/* SEO-only block — visible to crawlers + screen readers, off-screen visually.
 * Standard sr-only pattern (Tailwind / Bootstrap). Don't use display:none —
 * Google may skip elements that are display:none for the "primary content"
 * heuristic, but absolutely-positioned + clipped is fully indexable. */
.ivr-seo-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* iVRadio — Globe of Internet Radio. Scoped under .iv-radio-globe-root so
   nothing leaks to other ivuniverse-web routes when the user navigates away.
   ──
   Token philosophy (refactored 2026-05-27): the radio is an *always-dark*
   surface (planetarium / cosmic feel), but it MUST inherit from the canonical
   iVUniverse design system in `app/globals.css`. So we:
     1. Re-import the brand + radii + motion + status tokens unchanged.
     2. Re-define surface + ink + shadow tokens for the always-dark cosmic
        palette, overriding the design system's light defaults inside this
        scope only. (This is what `.dark` does globally; the radio is a
        permanent dark surface, so we don't need the dark class on <html>.)
     3. Map local `--ivr-*` aliases to the canonical tokens so the existing
        ~150 rules below keep working without rewriting every selector.
   Updating brand colors, radii, or motion in `globals.css` now propagates
   here automatically — single source of truth, "calm + dense + actionable"
   per CLAUDE_DESIGN_BRIEF_V2.md. */

.iv-radio-globe-root {
  /* ── 1. Canonical iVUniverse brand (from globals.css) ─── */
  /* --iv-yellow, --iv-purple, --iv-gradient, --iv-success/danger/warning/info,
     --iv-radius-*, --iv-ease, --iv-duration-* all inherit unchanged. */

  /* ── 2. Always-dark cosmic surface (overrides design system light defaults) ─── */
  --iv-bg:              #06050d;            /* deep space canvas */
  --iv-surface:         rgba(13, 10, 29, 0.72);
  --iv-surface-raised:  rgba(20, 14, 42, 0.86);
  --iv-border:          rgba(255, 255, 255, 0.08);

  --iv-text:            #faf8ff;
  --iv-text-primary:    #faf8ff;            /* slightly cooler than #F5F5F5 for the cosmic mood */
  --iv-text-secondary:  #a39cc4;
  --iv-text-tertiary:   #5e587a;
  --iv-muted:           #a39cc4;

  --iv-shadow-sm:  0 1px 3px rgba(0,0,0,0.40);
  --iv-shadow-md:  0 6px 24px rgba(0,0,0,0.40);
  --iv-shadow-lg:  0 24px 64px rgba(0,0,0,0.50);
  --iv-glow-yellow: 0 0 24px rgba(247,215,20,0.25);
  --iv-glow-purple: 0 0 24px rgba(107,63,160,0.30);

  /* ── 3. Radio-specific token aliases (so legacy --ivr-* rules keep working) ─── */
  --ivr-bg:     var(--iv-bg);
  --ivr-bg-2:   #0d0a1d;
  --ivr-panel:  var(--iv-surface);
  --ivr-line:   var(--iv-border);
  --ivr-ink:    var(--iv-text-primary);
  --ivr-dim:    var(--iv-text-secondary);
  --ivr-faint:  var(--iv-text-tertiary);

  /* Brand purple variants kept local (design system has --iv-purple only) */
  --iv-purple-d: #4a2873;
  --iv-purple-l: #8b5cf6;

  /* ── Genre swatches (radio-domain, not part of canonical design system) ─── */
  --g-classical: #d4a056;
  --g-jazz:      #1fb37a;
  --g-electronic:#6b5cff;
  --g-indie:     #ff7849;
  --g-mixed:     var(--iv-yellow);
  --g-news:      #94a4b5;
  --g-soulfunk:  #e54e78;
  --g-rockpop:   #5cb8ff;
  --g-world:     #c45cff;
  --g-hiphop:    #ff5c8d;

  position: fixed; inset: 0;
  z-index: 50;
  background: var(--iv-bg);
  color: var(--iv-text-primary);
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow: hidden;
  letter-spacing: -0.01em;
}
.iv-radio-globe-root * { box-sizing: border-box; margin: 0; padding: 0; }

.iv-radio-globe-root #iv-stars { position: absolute; inset: 0; z-index: 0; pointer-events: none; }

.iv-radio-globe-root #iv-map {
  position: absolute; top: 0; left: 0; right: 0; bottom: 0; z-index: 1;
  /* Mobile perf 2026-05-29: explicit touch-action: none on the wrapper
     ensures iOS Safari doesn't reserve the 300ms double-tap-zoom window
     before letting MapLibre's gesture handlers run. MapLibre sets this on
     its inner canvas; setting it on the wrapper too is belt-and-braces
     against any future child element accidentally claiming gestures. */
  touch-action: none;
  -webkit-tap-highlight-color: transparent;
  user-select: none;
  -webkit-user-select: none;
}

/* List view: appears in place of the globe when the user toggles to List mode.
   The globe stays mounted (cheap to keep) but is hidden so audio + state
   survive the toggle. */
.iv-radio-globe-root .ivr-list {
  position: absolute;
  top: 130px; left: 0; right: 0;
  bottom: 0;
  z-index: 1;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  /* Cap content width so rows don't stretch across ultrawide displays
     (audit 2026-05-29: at >1400 px the middle space between station info
     and tag column reads as wasted whitespace). Max 1280 px centred. */
  padding: 0 28px 140px;
  display: none;
  transition: bottom 0.6s cubic-bezier(0.2, 0.9, 0.2, 1);
}
/* Desktop: when player slides in, shrink list viewport to stop at player top —
   eliminates row-behind-player overlap at every scroll position. */
.iv-radio-globe-root:has(.ivr-player.visible) .ivr-list {
  bottom: 143px;
}
.iv-radio-globe-root .ivr-list-row,
.iv-radio-globe-root .ivr-list-count {
  max-width: 1280px;
  margin-left: auto;
  margin-right: auto;
}
.iv-radio-globe-root.mode-list .ivr-list { display: block; padding-top: 76px; }
.iv-radio-globe-root.mode-list #iv-map { opacity: 0; pointer-events: none; }
.iv-radio-globe-root.mode-list .ivr-nudge { display: none; }

/* List-view filter input — founder 2026-05-29. Visible only in list mode,
   sticks to the top of the list scroll, full-width on mobile, capped on
   wide screens. Simple search icon + clear-x button. */
.iv-radio-globe-root .ivr-list-search { display: none; }
.iv-radio-globe-root.mode-list .ivr-list-search {
  /* v2.42 (2026-06-02) — wider pill + flex-wrap on narrow widths so search
     + sort never overlap on mobile. Founder feedback: "All should be
     bautiful and very well placed along".
     Desktop: single row, wider (max 800px) so the sort + search both
     breathe without competing for pixels.
     Narrow (<480px): wraps to two rows — search on top, sort below. */
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  position: absolute;
  top: 130px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 10;
  margin: 0;
  padding: 10px 14px;
  width: min(800px, calc(100% - 24px));
  background: rgba(13, 10, 29, 0.92);
  backdrop-filter: blur(24px) saturate(180%);
  -webkit-backdrop-filter: blur(24px) saturate(180%);
  border: 1px solid var(--ivr-line);
  border-radius: var(--iv-radius-pill);
  box-shadow: 0 4px 24px rgba(0, 0, 0, 0.35), 0 12px 32px 8px rgba(6, 5, 13, 0.7);
}
/* Search input takes priority on a row; sort sits to the right.
   When the pill wraps on narrow viewports, both children flex to 100%
   so each gets a full row. */
.iv-radio-globe-root.mode-list .ivr-list-search > .ivr-list-search-icon,
.iv-radio-globe-root.mode-list .ivr-list-search > #iv-list-search-input,
.iv-radio-globe-root.mode-list .ivr-list-search > .ivr-list-search-clear {
  flex-shrink: 0;
}
.iv-radio-globe-root.mode-list .ivr-list-search > #iv-list-search-input {
  flex: 1 1 200px;
  min-width: 0;
}
.iv-radio-globe-root .ivr-list-search-icon {
  flex: 0 0 16px;
  color: var(--ivr-dim);
}
.iv-radio-globe-root #iv-list-search-input {
  flex: 1 1 auto;
  min-width: 0;
  background: transparent;
  border: 0;
  outline: 0;
  color: var(--ivr-ink);
  font-size: 14px;
  font-weight: 400;
  padding: 4px 0;
  caret-color: var(--iv-yellow);
}
.iv-radio-globe-root #iv-list-search-input::placeholder {
  color: var(--ivr-dim);
  opacity: 0.7;
}
.iv-radio-globe-root #iv-list-search-input::-webkit-search-cancel-button {
  -webkit-appearance: none;
  appearance: none;
}
.iv-radio-globe-root .ivr-list-search-clear {
  flex: 0 0 22px;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: 0;
  background: rgba(255, 255, 255, 0.06);
  color: var(--ivr-ink);
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background var(--iv-duration-sm) var(--iv-ease);
}
.iv-radio-globe-root .ivr-list-search-clear:hover {
  background: rgba(255, 255, 255, 0.12);
}
.iv-radio-globe-root .ivr-list-search.has-value {
  border-color: var(--iv-yellow);
  box-shadow: 0 0 0 1px var(--iv-yellow);
}

/* v2.40 + v2.42 — list-view sort dropdown. Sits inside the same glass pill
   as the search input. Native <select> styled to read like an iv-yellow
   chip so the user immediately sees it's an interactive control. */
.iv-radio-globe-root .ivr-list-sort {
  flex: 0 0 auto;
  appearance: none;
  -webkit-appearance: none;
  background: rgba(247, 215, 20, 0.10);
  color: var(--iv-yellow);
  border: 1px solid rgba(247, 215, 20, 0.30);
  border-radius: var(--iv-radius-pill);
  padding: 7px 30px 7px 14px;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.01em;
  cursor: pointer;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'><path d='M1 1l4 4 4-4' stroke='%23F7D714' stroke-width='1.6' fill='none' stroke-linecap='round'/></svg>");
  background-repeat: no-repeat;
  background-position: right 12px center;
  background-size: 10px 6px;
  transition: all var(--iv-duration-sm) var(--iv-ease);
}
.iv-radio-globe-root .ivr-list-sort:hover,
.iv-radio-globe-root .ivr-list-sort:focus {
  border-color: var(--iv-yellow);
  background-color: rgba(247, 215, 20, 0.18);
  outline: 0;
}
.iv-radio-globe-root .ivr-list-sort option {
  background: #0d0a1d;
  color: var(--ivr-ink);
  font-weight: 500;
}
/* Narrow viewports — make sort row to its own line below the search so
   neither competes for horizontal space. */
@media (max-width: 520px) {
  .iv-radio-globe-root.mode-list .ivr-list-search {
    width: calc(100% - 16px);
    border-radius: 16px;
    padding: 8px 10px;
  }
  .iv-radio-globe-root .ivr-list-sort {
    flex: 1 1 100%;
    text-align: center;
    background-position: right 14px center;
  }
  /* Sort wraps to its own row at this width — taller pill needs more list clearance */
  .iv-radio-globe-root.mode-list .ivr-list {
    padding-top: 116px;
  }
}

.iv-radio-globe-root .ivr-list-row {
  display: grid;
  /* +1 column (56px) for .lr-time local-time tag — founder 2026-05-28. */
  grid-template-columns: 36px minmax(0, 1fr) 110px 110px 56px 40px 38px;
  align-items: center;
  gap: 14px;
  padding: 12px 14px;
  border-radius: var(--iv-radius-lg);
  cursor: pointer;
  transition: background var(--iv-duration-sm) var(--iv-ease),
              transform var(--iv-duration-sm) var(--iv-ease);
  color: var(--ivr-ink);
  margin-bottom: 4px;
  border: 1px solid transparent;
}
.iv-radio-globe-root .ivr-list-row:hover {
  background: rgba(255,255,255,0.04);
  border-color: var(--ivr-line);
  transform: translateX(2px);
}
.iv-radio-globe-root .ivr-list-row.playing {
  background: linear-gradient(90deg, rgba(247,215,20,0.10) 0%, rgba(247,215,20,0.02) 100%);
  border-color: rgba(247,215,20,0.4);
  box-shadow: 0 0 0 1px rgba(247,215,20,0.18) inset;
}
/* Filter behaviour differs between views:
 *   globe → pins stay positionally consistent; non-matching dim to 30% so
 *           the user keeps geographic context.
 *   list  → list IS the filter result; non-matching rows are hidden entirely. */
.iv-radio-globe-root .ivr-list-row.dimmed { display: none; }

/* List-view match-count banner — only visible when in list mode. */
.iv-radio-globe-root .ivr-list-count {
  display: none;                  /* shown only in mode-list via below */
  font-size: 11.5px;
  font-family: 'JetBrains Mono', monospace;
  color: var(--ivr-dim);
  letter-spacing: 0.04em;
  padding: 8px 14px 12px;
  position: sticky;
  top: 0;
  background: linear-gradient(to bottom, var(--iv-bg) 60%, transparent);
  z-index: 1;
}
.iv-radio-globe-root .ivr-list-count.hidden { display: none !important; }
.iv-radio-globe-root .ivr-list-count .num {
  color: var(--iv-yellow);
  font-weight: 600;
  font-size: 13px;
}
.iv-radio-globe-root.mode-list .ivr-list-count { display: block; }

.iv-radio-globe-root .ivr-list-row .lr-dot {
  width: 14px; height: 14px;
  border-radius: 50%;
  margin: 0 auto;
  box-shadow: 0 0 0 2px rgba(255,255,255,0.9), 0 0 10px currentColor;
}
.iv-radio-globe-root .ivr-list-row .lr-name {
  font-size: 14px;
  font-weight: 600;
  letter-spacing: -0.01em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.iv-radio-globe-root .ivr-list-row .lr-sub {
  font-size: 11.5px;
  color: var(--ivr-dim);
  margin-top: 2px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.iv-radio-globe-root .ivr-list-row .lr-listens {
  color: var(--iv-yellow);
  font-family: 'JetBrains Mono', monospace;
  font-size: 10.5px;
}
.iv-radio-globe-root .ivr-list-row .lr-tag {
  display: inline-block;
  padding: 3px 10px;
  border-radius: var(--iv-radius-pill);
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  background: rgba(255,255,255,0.06);
  color: var(--ivr-ink);
  border: 1px solid var(--ivr-line);
  white-space: nowrap;
}
.iv-radio-globe-root .ivr-list-row .lr-tag.lr-tag-genre { color: var(--ivr-ink); }
.iv-radio-globe-root .ivr-list-row .lr-tag.lr-tag-mood {
  color: var(--iv-purple-l);
  background: rgba(139,92,246,0.08);
  border-color: rgba(139,92,246,0.2);
}
.iv-radio-globe-root .ivr-list-row .lr-play {
  width: 38px; height: 38px;
  border-radius: 50%;
  background: rgba(255,255,255,0.06);
  border: 1px solid var(--ivr-line);
  color: var(--ivr-ink);
  display: flex; align-items: center; justify-content: center;
  transition: all var(--iv-duration-sm) var(--iv-ease);
  cursor: pointer;
}
.iv-radio-globe-root .ivr-list-row:hover .lr-play { background: var(--iv-yellow); color: #000; border-color: var(--iv-yellow); }
.iv-radio-globe-root .ivr-list-row .lr-play svg { width: 16px; height: 16px; }

/* Heart icon for favourites (iVGU+ feature) */
.iv-radio-globe-root .ivr-list-row .lr-fav {
  width: 36px; height: 36px;
  background: transparent;
  border: 0;
  color: var(--ivr-dim);
  display: flex; align-items: center; justify-content: center;
  cursor: pointer;
  transition: all var(--iv-duration-sm) var(--iv-ease);
  padding: 0;
}
.iv-radio-globe-root .ivr-list-row .lr-fav svg { width: 18px; height: 18px; transition: all var(--iv-duration-sm) var(--iv-ease); }
.iv-radio-globe-root .ivr-list-row .lr-fav:hover { color: var(--iv-yellow); transform: scale(1.1); }
.iv-radio-globe-root .ivr-list-row .lr-fav.faved {
  color: var(--iv-yellow);
}
.iv-radio-globe-root .ivr-list-row .lr-fav.faved svg {
  fill: var(--iv-yellow);
  stroke: var(--iv-yellow);
  filter: drop-shadow(0 0 6px rgba(247,215,20,0.5));
}
/* Anonymous: dim the heart icon (still clickable; will toast a sign-in nudge) */
.iv-radio-globe-root.auth-off .ivr-list-row .lr-fav {
  opacity: 0.35;
}
.iv-radio-globe-root.auth-off .ivr-list-row .lr-fav:hover { opacity: 0.65; }

/* Submit-a-station link — small pill in the header, next to view toggle */
.iv-radio-globe-root .ivr-submit-link {
  flex: 0 0 auto;
  display: flex; align-items: center; gap: 6px;
  background: rgba(13, 10, 29, 0.72);
  backdrop-filter: blur(20px) saturate(150%);
  -webkit-backdrop-filter: blur(20px) saturate(150%);
  border: 1px solid var(--ivr-line);
  border-radius: var(--iv-radius-pill);
  padding: 7px 12px;
  color: var(--ivr-dim);
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-decoration: none;
  cursor: pointer;
  transition: all var(--iv-duration-sm) var(--iv-ease);
  pointer-events: auto;
}
.iv-radio-globe-root .ivr-submit-link:hover {
  color: #000;
  background: var(--iv-yellow);
  border-color: var(--iv-yellow);
}
@media (max-width: 640px) {
  .iv-radio-globe-root .ivr-submit-link span { display: none; }
  .iv-radio-globe-root .ivr-submit-link { padding: 7px 9px; }
}

/* View-mode toggle (Globe / List) */
.iv-radio-globe-root .ivr-view-toggle {
  flex: 0 0 auto;
  display: flex; gap: 2px;
  background: rgba(13, 10, 29, 0.72);
  backdrop-filter: blur(20px) saturate(150%);
  -webkit-backdrop-filter: blur(20px) saturate(150%);
  border: 1px solid var(--ivr-line);
  border-radius: var(--iv-radius-pill);
  padding: 3px;
  pointer-events: auto;
}
.iv-radio-globe-root .ivr-view-tab {
  background: transparent;
  border: 0;
  color: var(--ivr-dim);
  font: inherit;
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 5px 10px;
  border-radius: var(--iv-radius-pill);
  cursor: pointer;
  transition: all var(--iv-duration-sm) var(--iv-ease);
  display: flex; align-items: center; gap: 6px;
}
.iv-radio-globe-root .ivr-view-tab svg { opacity: 0.7; }
.iv-radio-globe-root .ivr-view-tab:hover { color: var(--ivr-ink); }
.iv-radio-globe-root .ivr-view-tab:hover svg { opacity: 1; }
.iv-radio-globe-root .ivr-view-tab.active {
  background: rgba(255,255,255,0.08);
  color: var(--ivr-ink);
}
.iv-radio-globe-root .ivr-view-tab.active svg { opacity: 1; }
.iv-radio-globe-root .maplibregl-canvas-container::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  /* Soft vignette only — no purple cast. Founder direction 2026-05-27:
     water blue, land natural. The vignette keeps the cosmic frame; the
     per-layer overrides in darkenMapStyle() do the rest. */
  background:
    radial-gradient(ellipse 60% 70% at 50% 50%, transparent 0%, rgba(6,5,13,0.55) 100%);
  z-index: 2;
}
/* Cosmic-natural canvas filter: keep colours saturated so the blue oceans
   and green land actually read on screen. Slight dim to keep the cosmic
   frame, but not so much that everything looks washed-grey. */
.iv-radio-globe-root .maplibregl-canvas {
  filter: saturate(1.05) brightness(0.92) contrast(1.08);
}
/* ──────────────────────────────────────────────────────────────────────
   Mobile perf 2026-05-29 (Tier 1 — surgical):
   On coarse-pointer devices both the radial-gradient vignette and the
   canvas color-matrix filter force a full-frame compositor pass on every
   render (~7 MP canvas × every move event). On mid-range phones this
   single pair of rules costs more than the 1239 markers combined.
   Drop both on mobile — the underlying NASA imagery is already beautiful
   and the cosmic frame survives via the existing dark page background
   and the natural fall-off at the globe silhouette.
   ────────────────────────────────────────────────────────────────────── */
@media (pointer: coarse), (max-width: 768px) {
  .iv-radio-globe-root .maplibregl-canvas-container::after {
    display: none;
  }
  .iv-radio-globe-root .maplibregl-canvas {
    filter: none;
  }
}

.iv-radio-globe-root .ivr-header {
  position: absolute; top: 0; left: 0; right: 0;
  padding: 22px 28px 18px; z-index: 20; pointer-events: none;
  display: flex; align-items: center; gap: 16px;
  background: linear-gradient(to bottom, rgba(6,5,13,0.92), rgba(6,5,13,0));
}
/* v2.7 (2026-05-30 night) — logo is now an <a> nav back to /dashboard
   (authed) or / (anon). text-decoration off so it doesn't look like a
   default browser link. Tiny opacity bump on hover signals it's clickable. */
.iv-radio-globe-root .ivr-logo { font-weight: 700; font-size: 16px; letter-spacing: -0.01em; color: var(--ivr-ink); display: flex; align-items: center; gap: 10px; pointer-events: auto; text-decoration: none; cursor: pointer; transition: opacity 0.15s ease; }
.iv-radio-globe-root .ivr-logo:hover { opacity: 0.82; }
/* v2.9 — canonical IV ribbon mark. The PNG ribbon brings its own colour +
   silhouette; we keep the soft yellow glow so the brand still feels alive
   on the cosmic background, drop the round gradient + circle clip. */
.iv-radio-globe-root .ivr-logo .mark {
  width: 32px; height: 32px;
  display: inline-flex; align-items: center; justify-content: center;
  filter: drop-shadow(0 0 14px rgba(247, 215, 20, 0.30));
  flex: 0 0 auto;
}
.iv-radio-globe-root .ivr-logo .mark img {
  width: 100%; height: 100%;
  object-fit: contain;
  display: block;
  /* The master PNG is rendered at high DPI but with crisp edges — let the
     browser bilinear-scale rather than pixel-snapping for retina sharpness. */
  image-rendering: auto;
  -webkit-user-drag: none;
  user-select: none;
}
.iv-radio-globe-root .ivr-logo .name { font-weight: 700; letter-spacing: -0.02em; }
.iv-radio-globe-root .ivr-logo .name span { color: var(--ivr-dim); font-weight: 400; }

.iv-radio-globe-root .ivr-header .ivr-spacer { flex: 1; }
.iv-radio-globe-root .ivr-header .ivr-count { font-family: 'JetBrains Mono', monospace; font-size: 11px; color: var(--ivr-dim); pointer-events: auto; letter-spacing: 0.04em; display: flex; align-items: center; gap: 14px; }
.iv-radio-globe-root .ivr-count .num { color: var(--iv-yellow); font-weight: 600; }
.iv-radio-globe-root .ivr-count .you { display: flex; align-items: center; gap: 6px; color: var(--ivr-ink); font-family: 'Inter', sans-serif; font-size: 11px; font-weight: 500; opacity: 0; transition: opacity 0.4s ease; }
.iv-radio-globe-root .ivr-count .you.ready { opacity: 1; }
.iv-radio-globe-root .ivr-count .you .dot-here { width: 7px; height: 7px; border-radius: 50%; background: var(--iv-purple-l); box-shadow: 0 0 8px var(--iv-purple-l); animation: ivrYouHere 2.2s ease-in-out infinite; }
@keyframes ivrYouHere { 0%, 100% { opacity: 0.55; transform: scale(0.9); } 50% { opacity: 1; transform: scale(1.15); } }

.iv-radio-globe-root .ivr-genres {
  position: absolute; top: 76px; left: 0; right: 84px;
  z-index: 19;
  padding: 0 28px;
  display: flex; gap: 8px;
  overflow-x: auto;
  scrollbar-width: none;
  /* Fade right edge so the user knows there's more to scroll.
     Mask-image works in all modern browsers. The fade kicks in when content
     overflows; for short rows it's invisible.
     Right: 84px reserves space for the floating search icon (38px wide
     + 28px right margin + 18px breathing room). Audit 2026-05-29 fix:
     previously the row went to `right: 0` and the search icon clipped
     the rightmost mood chip. */
  -webkit-mask-image: linear-gradient(to right, #000 0%, #000 calc(100% - 28px), transparent 100%);
          mask-image: linear-gradient(to right, #000 0%, #000 calc(100% - 28px), transparent 100%);
}
.iv-radio-globe-root .ivr-genres::-webkit-scrollbar { display: none; }
/* Search — floating top-right, collapses to icon when blurred */
.iv-radio-globe-root .ivr-search {
  position: absolute;
  top: 78px;
  right: 28px;
  z-index: 21;
  display: flex; align-items: center;
  background: rgba(13, 10, 29, 0.85);
  backdrop-filter: blur(20px) saturate(160%);
  -webkit-backdrop-filter: blur(20px) saturate(160%);
  border: 1px solid var(--ivr-line);
  border-radius: var(--iv-radius-pill);
  transition: width 0.28s cubic-bezier(0.2, 0.8, 0.2, 1), box-shadow 0.2s ease;
  width: 38px;
  height: 38px;
  overflow: hidden;
}
.iv-radio-globe-root .ivr-search.expanded {
  width: 340px;
  box-shadow: 0 6px 24px rgba(0,0,0,0.4), 0 0 0 1px rgba(255,255,255,0.05) inset;
}
.iv-radio-globe-root .ivr-search-toggle {
  flex: 0 0 38px;
  height: 38px;
  background: transparent;
  border: 0;
  color: var(--ivr-ink);
  display: flex; align-items: center; justify-content: center;
  cursor: pointer;
  padding: 0;
}
.iv-radio-globe-root .ivr-search-toggle:hover { color: var(--iv-yellow); }
.iv-radio-globe-root .ivr-search-input {
  flex: 1; min-width: 0;
  background: transparent;
  border: 0; outline: 0;
  color: var(--ivr-ink);
  font: inherit;
  font-size: 13px;
  padding: 0 8px 0 0;
  height: 38px;
  opacity: 0;
  transition: opacity 0.18s ease 0.05s;
}
.iv-radio-globe-root .ivr-search-input::placeholder { color: var(--ivr-faint); }
.iv-radio-globe-root .ivr-search.expanded .ivr-search-input { opacity: 1; }
.iv-radio-globe-root .ivr-search-clear {
  flex: 0 0 30px;
  height: 38px;
  background: transparent;
  border: 0;
  color: var(--ivr-dim);
  display: none;
  align-items: center; justify-content: center;
  cursor: pointer;
}
.iv-radio-globe-root .ivr-search-clear:hover { color: var(--ivr-ink); }
.iv-radio-globe-root .ivr-search.has-value .ivr-search-clear { display: flex; }

/* Mobile: search drops below the genre row, takes full width when expanded */
@media (max-width: 640px) {
  .iv-radio-globe-root .ivr-search {
    top: calc(env(safe-area-inset-top) + 110px);
    right: 12px;
  }
  .iv-radio-globe-root .ivr-search.expanded { width: calc(100vw - 24px); }
}

/* Filter axis tabs (Genre / Mood) — sit at the start of the chip row */
.iv-radio-globe-root .ivr-filter-tabs {
  flex: 0 0 auto;
  display: flex; gap: 2px;
  background: rgba(13, 10, 29, 0.72);
  backdrop-filter: blur(20px) saturate(150%);
  -webkit-backdrop-filter: blur(20px) saturate(150%);
  border: 1px solid var(--ivr-line);
  border-radius: var(--iv-radius-pill);
  padding: 3px;
  margin-right: 4px;
}
.iv-radio-globe-root .ivr-filter-tab {
  position: relative;
  background: transparent;
  border: 0;
  color: var(--ivr-dim);
  font: inherit;
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 5px 12px;
  border-radius: var(--iv-radius-pill);
  cursor: pointer;
  transition: all var(--iv-duration-sm) var(--iv-ease);
  white-space: nowrap;
}
.iv-radio-globe-root .ivr-filter-tab:hover { color: var(--ivr-ink); }
.iv-radio-globe-root .ivr-filter-tab.active {
  background: var(--iv-yellow);
  color: #000;
  box-shadow: 0 2px 10px rgba(247,215,20,0.25);
}
.iv-radio-globe-root .ivr-filter-tab-dot {
  position: absolute; top: 4px; right: 6px;
  width: 5px; height: 5px;
  border-radius: 50%;
  background: var(--iv-purple-l);
  box-shadow: 0 0 6px var(--iv-purple-l);
}

.iv-radio-globe-root .ivr-chip { flex: 0 0 auto; padding: 8px 14px; background: rgba(13, 10, 29, 0.72); backdrop-filter: blur(20px) saturate(150%); -webkit-backdrop-filter: blur(20px) saturate(150%); border: 1px solid var(--ivr-line); border-radius: var(--iv-radius-pill); font-size: 12px; font-weight: 500; color: var(--ivr-dim); cursor: pointer; user-select: none; transition: all 0.2s cubic-bezier(0.2, 0.8, 0.2, 1); white-space: nowrap; display: flex; align-items: center; gap: 7px; }
.iv-radio-globe-root .ivr-chip-count { opacity: 0.5; margin-left: 3px; font-size: 10px; }
.iv-radio-globe-root .ivr-chip:hover { color: var(--ivr-ink); border-color: rgba(255,255,255,0.2); transform: translateY(-1px); }
.iv-radio-globe-root .ivr-chip.active { color: #000; background: var(--iv-yellow); border-color: var(--iv-yellow); font-weight: 600; box-shadow: 0 4px 20px rgba(247,215,20,0.35); }
.iv-radio-globe-root .ivr-chip .swatch { width: 7px; height: 7px; border-radius: 50%; box-shadow: 0 0 6px currentColor; }

/* Founder 2026-05-29 v3: "pins are too big. Needs to be beautiful and
   adequate sizing with our posture, especially considering the growing
   number of channels." With 1238 stations clustered heavily in Europe
   + Americas, the previous 14 px pin + glow caused visual overflow.
   New baseline 9 px (≈35 % smaller), with the JS pin-renderer applying
   a CSS variable `--iv-pin-scale` based on zoom (small at world view,
   full size at city zoom) — see `pinScaleForZoom()` in app.js. Stations
   stay discoverable; the disc breathes. */
.iv-radio-globe-root .pin {
  --iv-pin-scale: 1;
  width: calc(9px * var(--iv-pin-scale));
  height: calc(9px * var(--iv-pin-scale));
  border-radius: 50%;
  cursor: pointer;
  position: relative;
  transition: transform 0.2s cubic-bezier(0.2, 0.8, 0.2, 1),
              width 0.3s cubic-bezier(0.2, 0.8, 0.2, 1),
              height 0.3s cubic-bezier(0.2, 0.8, 0.2, 1);
  box-shadow: 0 0 0 1.5px rgba(255,255,255,0.92), 0 0 10px currentColor;
}
.iv-radio-globe-root .pin:hover { transform: scale(1.45); }
.iv-radio-globe-root .pin::before {
  content: '';
  position: absolute;
  inset: -2px;
  border-radius: 50%;
  border: 1px solid currentColor;
  opacity: 0;
  animation: ivrPulseRing 3s ease-out infinite;
}
@keyframes ivrPulseRing { 0% { opacity: 0.5; transform: scale(0.85); } 100% { opacity: 0; transform: scale(2.8); } }
/* Founder 2026-05-29: "Bigger cinematic pulse on currently-playing pin."
   Scaled the now-playing pin and its rings up so the user can instantly
   see WHICH station is on-air from a wide overview. Compared to the
   previous subtle bob, this version reads like a strobe-light beacon. */
.iv-radio-globe-root .pin.playing {
  transform: scale(1.65);
  box-shadow:
    0 0 0 3px var(--iv-yellow),
    0 0 0 7px rgba(247,215,20,0.55),
    0 0 22px rgba(247,215,20,0.85),
    0 0 44px rgba(247,215,20,0.55);
  animation: ivrPlayingBob 2.1s ease-in-out infinite;
  z-index: 5;
}
@keyframes ivrPlayingBob {
  0%, 100% { transform: scale(1.65); box-shadow: 0 0 0 3px var(--iv-yellow), 0 0 0 7px rgba(247,215,20,0.55), 0 0 22px rgba(247,215,20,0.85), 0 0 44px rgba(247,215,20,0.55); }
  50%      { transform: scale(1.9);  box-shadow: 0 0 0 4px var(--iv-yellow), 0 0 0 9px rgba(247,215,20,0.7),  0 0 32px rgba(247,215,20,1.0),  0 0 64px rgba(247,215,20,0.7); }
}
/* Founder 2026-05-28 (Rohan PDFs Phase 1, top item): "pulsing glow or ripple
   emanating from the station location to indicate broadcast." Concentric
   rings expand outward at a slower cadence than the normal ::before ring,
   layered with a stronger yellow halo. Disabled in dragging state so the
   ring doesn't strobe during user pan/zoom.
   2026-05-29: bigger expansion + brighter borders. */
.iv-radio-globe-root .pin.playing::after {
  content: '';
  position: absolute;
  inset: -10px;
  border-radius: 50%;
  border: 2.5px solid var(--iv-yellow);
  opacity: 0;
  pointer-events: none;
  animation: ivrPlayingRipple 2.6s ease-out infinite;
  filter: drop-shadow(0 0 6px rgba(247,215,20,0.6));
}
@keyframes ivrPlayingRipple {
  0%   { opacity: 0.85; transform: scale(0.7); border-width: 3px; }
  60%  { opacity: 0.35; }
  100% { opacity: 0;    transform: scale(3.4); border-width: 0.5px; }
}
/* Second slower ripple — stacks on top of the first for a clear "this one
   is on-air right NOW" beacon. Phase offset by 1.3 s so the two rings
   don't pulse together. */
.iv-radio-globe-root .pin.playing::before {
  content: '';
  position: absolute;
  inset: -6px;
  border-radius: 50%;
  border: 1.5px solid var(--iv-yellow);
  opacity: 0;
  pointer-events: none;
  animation: ivrPlayingRipple 2.6s ease-out infinite 1.3s;
  filter: drop-shadow(0 0 4px rgba(247,215,20,0.5));
}
/* During drag, kill the ripple so it doesn't visually drift across the pan. */
.iv-radio-globe-root.dragging .pin.playing::after { animation: none; opacity: 0; }
.iv-radio-globe-root .pin.dimmed { opacity: 0.12; }
.iv-radio-globe-root .pin.dimmed::before { display: none; }
/* Founder 2026-05-28: when a genre/mood/favourites filter is active, non-
   matching pins should be HIDDEN entirely (not dimmed). We target the
   maplibregl-marker wrapper so the click hit-area also disappears. */
.iv-radio-globe-root .maplibregl-marker.filter-hidden {
  display: none !important;
}

/* ── Popularity tiers ────────────────────────────────────────────────────────
   The JS pinStyleFor() classifies stations by listen count:
   • hot    : top ~10-20% — trending. Bright pulse + chunky halo (set in JS).
   • warm   : upper-middle — louder halo
   • normal : baseline
   • cold   : ≤2 listens — muted, no extra pulse so it visually fades back.
   Halo/glow is applied inline by JS so it scales with the dynamic diameter.
   The CSS here drives the OUTER pulse ring (::before) — hot pins get a
   faster, more obvious second pulse so trending reads instantly. */
.iv-radio-globe-root .pin.pin-tier-hot::before  { animation-duration: 1.8s; opacity: 0; border-width: 2px; }
.iv-radio-globe-root .pin.pin-tier-hot::after   { content:''; position:absolute; inset:-5px; border-radius:50%; border:1.5px solid currentColor; opacity:0; animation: ivrPulseRing 1.8s ease-out infinite 0.6s; }
.iv-radio-globe-root .pin.pin-tier-warm::before { animation-duration: 2.4s; }
.iv-radio-globe-root .pin.pin-tier-cold::before { display: none; }
.iv-radio-globe-root .pin.pin-tier-cold        { opacity: 0.7; }

/* Seamless globe interaction: while the user drags / zooms the globe,
   pause the continuous pulse animations on every pin and the you-marker.
   14 simultaneous keyframe rings compounded with MapLibre's per-frame
   marker re-projection during drag caused visible stutter. Pausing
   compositing-heavy animations during movement keeps the audio thread
   un-starved and the globe smooth. */
.iv-radio-globe-root.dragging .pin::before,
.iv-radio-globe-root.dragging .pin.playing,
.iv-radio-globe-root.dragging .you-marker::after,
.iv-radio-globe-root.dragging .ivr-toast .dot {
  animation-play-state: paused;
}
/* Also disable the hover-scale transitions during drag — they trigger
   layout on every marker the cursor passes over. */
.iv-radio-globe-root.dragging .pin { transition: none; }

/* Back-hemisphere occlusion — MapLibre HTML markers are 2D overlays so
   pins on the far side of the globe would otherwise ghost through. JS adds
   the `.behind` class to any marker whose lat/lng vector has a negative
   dot product against the camera-centre vector (computed each frame via
   rAF). Founder 2026-05-28: pins still bleeding through. Root cause: the
   200ms opacity transition kept restarting as the auto-rotate fired every
   frame, so opacity never settled at 0. Fix: drop the transition (binary
   hide) + add visibility:hidden + !important so nothing inside the pin
   tree can race past the rule. */
.iv-radio-globe-root .maplibregl-marker.behind {
  opacity: 0 !important;
  visibility: hidden !important;
  pointer-events: none !important;
  transition: none !important;
}
.iv-radio-globe-root .maplibregl-marker:not(.behind) {
  opacity: 1;
  visibility: visible;
}
/* While the user is dragging, drop the cross-fade so the pin pop is
   instant — keeps the dragging hand-feel snappy. */
.iv-radio-globe-root.dragging .maplibregl-marker { transition: none; }

/* Mobile globe-pan smoothness (founder report 2026-05-29).
   REGRESSION NOTE: A previous version of this rule set
   `transform: translate3d(0,0,0)` here. That OVERRODE MapLibre's
   own per-marker `transform: translate(x,y)` which positions every
   pin on the globe. The result: every marker collapsed to the
   origin (0,0) and drifted with the GPU layer instead of staying
   glued to geographic coordinates (founder video 15:29). Reverted.

   The ONLY safe property here is `will-change: transform` — a pure
   hint to the browser; it never overrides any other property.
*/
.iv-radio-globe-root .maplibregl-marker {
  will-change: transform;
}
@media (pointer: coarse) {
  .iv-radio-globe-root .maplibregl-marker {
    transition: none !important;
  }
}

/* Podcast pin — square corners + thin outline to differentiate from live
   radio stations. Same color/genre scheme so the rest of the UI logic
   (filtering, popularity sizing) keeps working without branching. */
.iv-radio-globe-root .pin.pin-podcast {
  border-radius: 4px;
  outline: 1.5px dashed currentColor;
  outline-offset: 2px;
}
.iv-radio-globe-root .pin.pin-podcast::before {
  border-radius: 4px;
  border-style: dashed;
}

/* Offline station — desaturated, no pulse ring. Health probe sets this when
   the latest probe within the last hour returned unreachable. */
.iv-radio-globe-root .pin.offline {
  background: #4a4661 !important;
  color: #4a4661 !important;
  box-shadow: 0 0 0 2px rgba(255,255,255,0.4), 0 0 6px rgba(74,70,97,0.6) !important;
  animation: none;
}
.iv-radio-globe-root .pin.offline::before { display: none; }
.iv-radio-globe-root .ivr-list-row.offline { opacity: 0.55; }
.iv-radio-globe-root .ivr-list-row.offline .lr-dot {
  background: #4a4661 !important;
  color: #4a4661 !important;
  box-shadow: 0 0 0 2px rgba(255,255,255,0.4) !important;
}
.iv-radio-globe-root .ivr-list-row.offline::after {
  content: 'offline';
  font-family: 'JetBrains Mono', monospace;
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: #ff9999;
  margin-left: 6px;
}

.iv-radio-globe-root .you-marker { width: 12px; height: 12px; border-radius: 50%; background: var(--iv-purple-l); border: 2px solid #fff; box-shadow: 0 0 0 4px rgba(139, 92, 246, 0.25), 0 0 22px rgba(139, 92, 246, 0.7); pointer-events: none; position: relative; }
.iv-radio-globe-root .you-marker::after { content: ''; position: absolute; inset: -8px; border-radius: 50%; border: 1.5px solid var(--iv-purple-l); opacity: 0; animation: ivrYouPulse 3s ease-out infinite; }
@keyframes ivrYouPulse { 0% { opacity: 0.7; transform: scale(0.6); } 100% { opacity: 0; transform: scale(2.2); } }

.iv-radio-globe-root .pin-label { position: absolute; bottom: 24px; left: 50%; transform: translateX(-50%) translateY(4px); background: rgba(13, 10, 29, 0.94); backdrop-filter: blur(14px); border: 1px solid var(--ivr-line); color: var(--ivr-ink); padding: 5px 11px; border-radius: var(--iv-radius-md); font-size: 11px; font-weight: 500; white-space: nowrap; pointer-events: none; opacity: 0; transition: opacity var(--iv-duration-sm) var(--iv-ease), transform var(--iv-duration-sm) var(--iv-ease); }
.iv-radio-globe-root .pin:hover .pin-label { opacity: 1; transform: translateX(-50%) translateY(0); }
.iv-radio-globe-root .pin-label .city { color: var(--ivr-dim); font-weight: 400; margin-left: 5px; }
.iv-radio-globe-root .pin-label .listens {
  color: var(--iv-yellow);
  font-size: 10px;
  font-family: 'JetBrains Mono', monospace;
  margin-left: 5px;
  letter-spacing: 0.04em;
}

.iv-radio-globe-root .ivr-player { position: absolute; left: 50%; bottom: 28px; transform: translateX(-50%) translateY(140%); width: min(780px, calc(100% - 36px)); background: linear-gradient(135deg, var(--iv-surface-raised) 0%, var(--iv-surface) 100%); backdrop-filter: blur(28px) saturate(180%); -webkit-backdrop-filter: blur(28px) saturate(180%); border: 1px solid rgba(255,255,255,0.1); border-radius: var(--iv-radius-2xl); padding: 16px 20px; z-index: 20; display: flex; align-items: center; gap: 16px; box-shadow: var(--iv-shadow-lg), 0 0 0 1px rgba(255,255,255,0.04) inset; transition: transform 0.6s cubic-bezier(0.2, 0.9, 0.2, 1); }
.iv-radio-globe-root .ivr-player.visible { transform: translateX(-50%) translateY(0); }
.iv-radio-globe-root .pl-art { width: 52px; height: 52px; border-radius: var(--iv-radius-lg); background: var(--ivr-bg-2); border: 1px solid var(--ivr-line); flex: 0 0 auto; display: flex; align-items: center; justify-content: center; overflow: hidden; position: relative; }
.iv-radio-globe-root .pl-art .core { width: 14px; height: 14px; border-radius: 50%; box-shadow: 0 0 14px currentColor, 0 0 0 4px rgba(255,255,255,0.06); }
.iv-radio-globe-root .pl-art .bars { position: absolute; display: flex; align-items: flex-end; gap: 2px; height: 18px; opacity: 0; transition: opacity 0.3s; }
.iv-radio-globe-root .ivr-player.playing .pl-art .core { display: none; }
.iv-radio-globe-root .ivr-player.playing .pl-art .bars { opacity: 1; }

/* Graceful "connecting" state — founder 2026-05-29: track-switch must feel
   like loading, not failing. While the audio element is opening the
   upstream + buffering, the player chip breathes softly so the listener
   knows we're working without any alarming language. Removed automatically
   by onPlay() in app.js when audio starts. */
.iv-radio-globe-root .ivr-player.connecting .pl-art .core {
  animation: ivrConnectingPulse 1.4s ease-in-out infinite;
}
.iv-radio-globe-root .ivr-player.connecting .pl-art {
  /* Faint rotating ring around the art tile suggests progress without
     looking like an error spinner */
  position: relative;
}
.iv-radio-globe-root .ivr-player.connecting .pl-art::after {
  content: '';
  position: absolute;
  inset: -3px;
  border-radius: var(--iv-radius-lg);
  border: 2px solid transparent;
  border-top-color: var(--iv-yellow);
  border-right-color: rgba(247, 215, 20, 0.45);
  animation: ivrConnectingSpin 1.1s linear infinite;
  pointer-events: none;
  opacity: 0.85;
}
.iv-radio-globe-root .ivr-player.connecting.playing .pl-art::after {
  /* Belt-and-braces — once playing class lands the spinner stops, even
     if class removal racing edge case */
  display: none;
}
@keyframes ivrConnectingPulse {
  0%, 100% { transform: scale(0.85); opacity: 0.55; }
  50%      { transform: scale(1.15); opacity: 1; }
}
@keyframes ivrConnectingSpin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}
.iv-radio-globe-root .pl-art .bar { width: 3px; background: var(--iv-yellow); border-radius: 1px; animation: ivrBars 0.9s ease-in-out infinite; }
.iv-radio-globe-root .pl-art .bar:nth-child(1) { height: 50%; animation-delay: 0s; }
.iv-radio-globe-root .pl-art .bar:nth-child(2) { height: 100%; animation-delay: 0.12s; }
.iv-radio-globe-root .pl-art .bar:nth-child(3) { height: 70%; animation-delay: 0.24s; }
.iv-radio-globe-root .pl-art .bar:nth-child(4) { height: 90%; animation-delay: 0.36s; }
@keyframes ivrBars { 0%, 100% { transform: scaleY(0.35); } 50% { transform: scaleY(1); } }
.iv-radio-globe-root .pl-info { flex: 1; min-width: 0; }
/* Founder 2026-05-28 — "PROPERLY MAKE SURE AMAZING": dedicated top row in
   the player showing the broadcaster's local time. Big monospace digits +
   city name, yellow-tinted to match the IV brand, gentle pulse on tick. */
.iv-radio-globe-root .pl-localrow {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin-bottom: 4px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.iv-radio-globe-root .pl-localrow[hidden] { display: none; }
.iv-radio-globe-root .pl-localrow .pl-clock-glyph {
  font-size: 13px;
  filter: drop-shadow(0 0 6px rgba(255, 215, 70, 0.4));
}
.iv-radio-globe-root .pl-localrow .pl-clock {
  font-family: 'JetBrains Mono', monospace;
  font-size: 17px;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--iv-yellow, #f7d714);
  font-variant-numeric: tabular-nums;
  text-shadow: 0 0 12px rgba(255, 215, 70, 0.25);
}
.iv-radio-globe-root .pl-localrow .pl-clock-city {
  font-size: 12px;
  font-weight: 500;
  color: var(--ivr-text-secondary, rgba(250, 248, 255, 0.78));
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.iv-radio-globe-root .pl-localrow .pl-clock-city::before {
  content: '· in ';
  color: var(--ivr-faint, rgba(255,255,255,0.35));
  text-transform: none;
  letter-spacing: 0;
  margin-right: 2px;
}
.iv-radio-globe-root .pl-name { font-size: 15px; font-weight: 600; color: var(--ivr-ink); letter-spacing: -0.015em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.iv-radio-globe-root .pl-meta { font-size: 11.5px; color: var(--ivr-dim); margin-top: 3px; display: flex; align-items: center; gap: 8px; font-weight: 400; min-width: 0; overflow: hidden; }
/* v2.7-fix (2026-05-30 night — founder report "some stations Genre placement
   on the player is misplaced"). The geo span used to greedily take row width
   and squeeze the genre/track/time pills off the right edge (or onto a half-
   visible second line via white-space collapse). Now: geo shrinks with
   ellipsis, the pills NEVER shrink and stay in stable left→right order:
   geo · GENRE · TRACK · TIME. nowrap was removed from .pl-meta because flex
   children handle line-breaking themselves — the prior nowrap+flex combo
   created a layout where some children visually drifted depending on
   inline-size. */
.iv-radio-globe-root .pl-meta > #iv-plGeo { color: var(--ivr-ink); font-weight: 500; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 0 1 auto; }
.iv-radio-globe-root .pl-meta .geo { color: var(--ivr-ink); font-weight: 500; }
.iv-radio-globe-root .pl-meta .sep { color: var(--ivr-faint); flex: 0 0 auto; }
.iv-radio-globe-root .pl-meta .genre-tag { padding: 2px 8px; border-radius: var(--iv-radius-pill); background: rgba(255,255,255,0.06); font-size: 9.5px; font-family: 'JetBrains Mono', monospace; text-transform: uppercase; letter-spacing: 0.12em; color: var(--ivr-ink); flex: 0 0 auto; white-space: nowrap; }
/* Local-time chip in the player meta. Founder direction 2026-05-28 — surfaces
   the cultural cue "what time is it where the station is broadcasting", ticked
   every 30 s by updateLocalTimeDisplay(). */
.iv-radio-globe-root .pl-meta .pl-localtime { padding: 2px 8px; border-radius: var(--iv-radius-pill); background: rgba(255, 215, 70, 0.10); border: 1px solid rgba(255, 215, 70, 0.25); font-size: 10px; font-family: 'JetBrains Mono', monospace; color: var(--ivr-ink); white-space: nowrap; font-variant-numeric: tabular-nums; flex: 0 0 auto; }
.iv-radio-globe-root .pl-meta .pl-localtime[hidden] { display: none; }
/* Track-name chip — surfaces in-band ID3 metadata when the broadcaster sends
   it. Hidden when no metadata received (most stations don't embed track info). */
.iv-radio-globe-root .pl-meta .pl-track { padding: 2px 8px; border-radius: var(--iv-radius-pill); background: rgba(120, 80, 200, 0.12); border: 1px solid rgba(140, 100, 220, 0.28); font-size: 10.5px; color: var(--ivr-ink); max-width: 240px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 0 1 auto; }
.iv-radio-globe-root .pl-meta .pl-track[hidden] { display: none; }
/* Local-time cell in list rows — small monospaced tag at the row's right edge. */
.iv-radio-globe-root .ivr-list-row .lr-time { font-family: 'JetBrains Mono', monospace; font-size: 11px; color: var(--ivr-dim); text-align: right; min-width: 44px; font-variant-numeric: tabular-nums; opacity: 0.85; }
.iv-radio-globe-root .pl-volume { display: flex; align-items: center; gap: 9px; flex: 0 0 auto; padding: 0 4px 0 8px; border-left: 1px solid var(--ivr-line); margin-left: 4px; }
.iv-radio-globe-root .pl-volume svg { width: 15px; height: 15px; color: var(--ivr-dim); flex: 0 0 auto; }
.iv-radio-globe-root .pl-volume input[type="range"] { -webkit-appearance: none; appearance: none; width: 76px; height: 4px; border-radius: var(--iv-radius-pill); background: linear-gradient(to right, var(--iv-yellow) 0%, var(--iv-yellow) var(--vol, 8%), rgba(255,255,255,0.12) var(--vol, 8%), rgba(255,255,255,0.12) 100%); outline: none; cursor: pointer; }
.iv-radio-globe-root .pl-volume input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 14px; height: 14px; border-radius: 50%; background: var(--ivr-ink); border: 2px solid var(--iv-yellow); cursor: pointer; box-shadow: 0 0 8px rgba(247,215,20,0.4); transition: transform 0.1s ease; }
.iv-radio-globe-root .pl-volume input[type="range"]::-webkit-slider-thumb:hover { transform: scale(1.2); }
.iv-radio-globe-root .pl-volume input[type="range"]::-moz-range-thumb { width: 14px; height: 14px; border-radius: 50%; background: var(--ivr-ink); border: 2px solid var(--iv-yellow); cursor: pointer; box-shadow: 0 0 8px rgba(247,215,20,0.4); }
.iv-radio-globe-root .pl-controls { display: flex; gap: 7px; flex: 0 0 auto; }
.iv-radio-globe-root .ivr-btn { width: 38px; height: 38px; background: rgba(255,255,255,0.06); border: 1px solid var(--ivr-line); border-radius: 50%; color: var(--ivr-ink); cursor: pointer; display: flex; align-items: center; justify-content: center; transition: all var(--iv-duration-sm) var(--iv-ease); }
.iv-radio-globe-root .ivr-btn:hover { background: rgba(255,255,255,0.12); transform: scale(1.05); }
.iv-radio-globe-root .ivr-btn:active { transform: scale(0.95); }
.iv-radio-globe-root .ivr-btn.play { width: 44px; height: 44px; background: linear-gradient(135deg, var(--iv-yellow) 0%, #ffd84a 100%); border-color: var(--iv-yellow); color: #000; box-shadow: 0 4px 18px rgba(247,215,20,0.4); }
.iv-radio-globe-root .ivr-btn.play:hover { background: linear-gradient(135deg, #ffd84a 0%, var(--iv-yellow) 100%); }
.iv-radio-globe-root .ivr-btn svg { width: 16px; height: 16px; }
.iv-radio-globe-root .ivr-btn.play svg { width: 18px; height: 18px; }
/* Founder 2026-05-28: player heart button. Faved state = filled red heart
   so the listener sees at a glance whether the playing station is saved. */
.iv-radio-globe-root .ivr-btn.ivr-btn-fav.faved { background: rgba(220, 38, 38, 0.18); border-color: rgba(220, 38, 38, 0.5); color: #ef4444; }
.iv-radio-globe-root .ivr-btn.ivr-btn-fav.faved svg { fill: #ef4444; stroke: #ef4444; }
.iv-radio-globe-root .ivr-btn.ivr-btn-fav:hover { color: #ef4444; }
/* Founder 2026-05-28 ultrathink: micro-bounce on Next/Prev so the press
   feels acknowledged even when nothing else happens (single-station filter,
   debounce drop). 220ms total — short enough not to feel "slow", long
   enough to register. Replays cleanly via class toggle + reflow. */
@keyframes ivrBtnPress {
  0%   { transform: scale(1);    box-shadow: 0 0 0 0 rgba(247, 215, 20, 0.4); }
  35%  { transform: scale(0.88); box-shadow: 0 0 0 0 rgba(247, 215, 20, 0.0); }
  70%  { transform: scale(1.08); box-shadow: 0 0 0 8px rgba(247, 215, 20, 0.18); }
  100% { transform: scale(1);    box-shadow: 0 0 0 12px rgba(247, 215, 20, 0); }
}
.iv-radio-globe-root .ivr-btn.ivr-btn-pressed {
  animation: ivrBtnPress 220ms var(--iv-ease, ease) forwards;
}
/* Empty-filter state on the Prev/Next buttons — dim them so the user can
   see at a glance that there's no station to advance to. Pointer-events
   stay on so the click still surfaces the helpful toast. */
.iv-radio-globe-root .ivr-btn.ivr-btn-empty {
  opacity: 0.35;
  cursor: default;
}
/* Favourites filter tab — heart-shaped, slightly distinct from Genre/Mood. */
.iv-radio-globe-root .ivr-filter-tab-fav { display: inline-flex; align-items: center; }
.iv-radio-globe-root .ivr-filter-tab-fav.active { background: rgba(220, 38, 38, 0.15); border-color: rgba(220, 38, 38, 0.4); color: #ef4444; }
.iv-radio-globe-root .ivr-filter-tab-fav.active svg { fill: #ef4444; }

.iv-radio-globe-root .ivr-nudge { position: absolute; left: 50%; bottom: 110px; transform: translateX(-50%); font-size: 13px; color: var(--ivr-dim); z-index: 19; text-align: center; pointer-events: none; transition: opacity 0.5s ease; font-weight: 400; }
.iv-radio-globe-root .ivr-nudge .key { display: inline-block; padding: 2px 7px; background: rgba(255,255,255,0.08); border: 1px solid var(--ivr-line); border-radius: 5px; font-size: 11px; font-family: 'JetBrains Mono', monospace; color: var(--ivr-ink); margin: 0 2px; }
.iv-radio-globe-root .ivr-nudge.hidden { opacity: 0; }
.iv-radio-globe-root .ivr-nudge-mobile { display: none; }
.iv-radio-globe-root .ivr-nudge-desktop { display: inline; }
@media (max-width: 640px) {
  .iv-radio-globe-root .ivr-nudge-mobile { display: inline; }
  .iv-radio-globe-root .ivr-nudge-desktop { display: none; }
}

.iv-radio-globe-root .ivr-toast { position: absolute; top: 140px; left: 50%; transform: translateX(-50%) translateY(-6px); background: rgba(13, 10, 29, 0.94); backdrop-filter: blur(20px); border: 1px solid var(--ivr-line); border-radius: var(--iv-radius-pill); padding: 9px 16px; font-size: 12px; font-weight: 500; color: var(--ivr-ink); z-index: 22; opacity: 0; pointer-events: none; transition: opacity 0.25s ease, transform 0.25s ease; white-space: nowrap; }
.iv-radio-globe-root .ivr-toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
.iv-radio-globe-root .ivr-toast .dot { display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: var(--iv-yellow); margin-right: 7px; transform: translateY(-1px); animation: ivrPulseDot 1.4s ease-in-out infinite; }
@keyframes ivrPulseDot { 0%, 100% { opacity: 0.6; } 50% { opacity: 1; box-shadow: 0 0 12px var(--iv-yellow); } }

.iv-radio-globe-root .maplibregl-ctrl-attrib { background: rgba(6,5,13,0.7) !important; color: var(--ivr-faint) !important; font-size: 10px !important; }
.iv-radio-globe-root .maplibregl-ctrl-attrib a { color: var(--ivr-dim) !important; }
.iv-radio-globe-root .maplibregl-ctrl-bottom-right .maplibregl-ctrl-group { background: transparent !important; box-shadow: none !important; }
.iv-radio-globe-root .maplibregl-ctrl-bottom-right .maplibregl-ctrl button { background: rgba(13, 10, 29, 0.7) !important; border-radius: 50% !important; width: 32px !important; height: 32px !important; border: 1px solid var(--ivr-line) !important; margin: 4px !important; }
.iv-radio-globe-root .maplibregl-ctrl-bottom-right .maplibregl-ctrl button:hover { background: rgba(255,255,255,0.08) !important; }
.iv-radio-globe-root .maplibregl-ctrl-icon { filter: invert(0.85); }

/* ── v2.8 Broadcast-waves loader ───────────────────────────────────────────
   Founder direction 2026-05-30 night: replace the orbit+label splash with a
   richer "broadcast waves" animation. Concentric outward-bursting rings, a
   pulsing yellow core, slow radar sweep, EQ bars, eyebrow + title + sub.
   100% CSS — no JS dependency. Static-asset cached so the splash renders
   before app.js parses. Visually evokes "radio waves leaving a tower". */
.iv-radio-globe-root .ivr-splash {
  position: absolute; inset: 0;
  background: radial-gradient(ellipse at 50% 40%, #0d0a1d 0%, #06050d 72%);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 32px;
  z-index: 100;
  transition: opacity 0.8s ease;
}
.iv-radio-globe-root .ivr-splash.gone { opacity: 0; pointer-events: none; }

/* The animation stage — 220px circle holding rings + radar + core + EQ. */
.iv-radio-globe-root .ivl-stage {
  position: relative;
  width: 220px; height: 220px;
  display: grid; place-items: center;
}

/* Slow-sweeping radar arm (a conic gradient quadrant rotating once per 4s). */
.iv-radio-globe-root .ivl-radar {
  position: absolute; inset: 0;
  border-radius: 50%;
  background: conic-gradient(from 0deg,
    rgba(247,215,20,0.0) 0deg,
    rgba(247,215,20,0.0) 270deg,
    rgba(247,215,20,0.22) 350deg,
    rgba(247,215,20,0.05) 360deg);
  filter: blur(0.5px);
  animation: ivlRadar 4s linear infinite;
}

/* Four concentric broadcast-wave rings expanding outward with fading edges. */
.iv-radio-globe-root .ivl-ring {
  position: absolute; inset: 50% 50% auto auto;
  width: 32px; height: 32px;
  margin: -16px -16px 0 0;
  border-radius: 50%;
  border: 1.5px solid rgba(247,215,20,0.55);
  opacity: 0;
  animation: ivlRing 2.4s cubic-bezier(0, 0.55, 0.45, 1) infinite;
}
.iv-radio-globe-root .ivl-ring.r1 { animation-delay: 0s; }
.iv-radio-globe-root .ivl-ring.r2 { animation-delay: 0.6s; }
.iv-radio-globe-root .ivl-ring.r3 { animation-delay: 1.2s; }
.iv-radio-globe-root .ivl-ring.r4 { animation-delay: 1.8s; }

/* Pulsing yellow core — the broadcast source. */
.iv-radio-globe-root .ivl-core {
  position: relative;
  width: 28px; height: 28px;
  border-radius: 50%;
  background: radial-gradient(circle at 30% 30%, #ffe96b 0%, #f7d714 60%, #c9ad06 100%);
  box-shadow:
    0 0 14px rgba(247,215,20,0.6),
    0 0 36px rgba(247,215,20,0.35),
    inset 0 0 8px rgba(255,255,255,0.4);
  animation: ivlCore 1.6s ease-in-out infinite;
}

/* EQ bars below the stage — 5 dancing bars suggesting live audio. */
.iv-radio-globe-root .ivl-eq {
  position: absolute;
  bottom: -28px; left: 50%;
  transform: translateX(-50%);
  display: flex; align-items: flex-end; gap: 4px;
  width: 64px; height: 18px;
}
.iv-radio-globe-root .ivl-eq span {
  width: 3px;
  border-radius: 2px;
  background: linear-gradient(to top, #f7d714, #ffe96b);
  opacity: 0.85;
  animation: ivlEq 1.1s ease-in-out infinite;
}
.iv-radio-globe-root .ivl-eq span:nth-child(1) { animation-delay: 0.00s; }
.iv-radio-globe-root .ivl-eq span:nth-child(2) { animation-delay: 0.15s; }
.iv-radio-globe-root .ivl-eq span:nth-child(3) { animation-delay: 0.30s; }
.iv-radio-globe-root .ivl-eq span:nth-child(4) { animation-delay: 0.45s; }
.iv-radio-globe-root .ivl-eq span:nth-child(5) { animation-delay: 0.60s; }

/* Caption block under the stage. */
.iv-radio-globe-root .ivl-cap {
  display: flex; flex-direction: column; align-items: center;
  gap: 6px;
  text-align: center;
  margin-top: 16px;
}
.iv-radio-globe-root .ivl-eyebrow {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.32em;
  color: var(--iv-yellow);
  font-weight: 600;
  opacity: 0.85;
}
.iv-radio-globe-root .ivr-splash .label {
  font-size: 22px;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--ivr-ink);
}
.iv-radio-globe-root .ivl-sub {
  font-size: 12px;
  color: var(--ivr-dim);
  font-weight: 400;
  letter-spacing: 0.01em;
  max-width: 260px;
  line-height: 1.4;
}

@keyframes ivlRing {
  0%   { width: 32px;  height: 32px;  margin: -16px -16px 0 0; opacity: 0; border-color: rgba(247,215,20,0.85); }
  10%  { opacity: 0.9; }
  100% { width: 220px; height: 220px; margin: -110px -110px 0 0; opacity: 0; border-color: rgba(247,215,20,0.0); }
}
@keyframes ivlRadar  { to { transform: rotate(360deg); } }
@keyframes ivlCore {
  0%, 100% { transform: scale(1);    box-shadow: 0 0 14px rgba(247,215,20,0.6), 0 0 36px rgba(247,215,20,0.35), inset 0 0 8px rgba(255,255,255,0.4); }
  50%      { transform: scale(1.15); box-shadow: 0 0 22px rgba(247,215,20,0.8), 0 0 52px rgba(247,215,20,0.5),  inset 0 0 8px rgba(255,255,255,0.55); }
}
@keyframes ivlEq {
  0%, 100% { height: 22%; }
  50%      { height: 100%; }
}

/* Honour reduced-motion preferences — the loader still shows but stops
   animating. Apple HIG + WCAG 2.1 SC 2.3.3. */
@media (prefers-reduced-motion: reduce) {
  .iv-radio-globe-root .ivl-radar,
  .iv-radio-globe-root .ivl-ring,
  .iv-radio-globe-root .ivl-core,
  .iv-radio-globe-root .ivl-eq span {
    animation: none;
  }
  .iv-radio-globe-root .ivl-ring { opacity: 0.35; }
}

/* Legacy spin keyframe kept — used by other UI elements (turnstile re-check
   loops, etc.) — DON'T DROP this even though .orbit has been removed. */
@keyframes ivrSpin { to { transform: rotate(360deg); } }

/* ── Submit-a-Station page ──────────────────────────────────────────────
   Note: this is a separate route (/radio-globe/submit) and is NOT inside
   .iv-radio-globe-root, so it sets its own scoped --iv-* token overrides
   to keep the cosmic dark surface consistent across the radio routes. */
.ivr-submit-root {
  /* Always-dark cosmic surface — same overrides as .iv-radio-globe-root */
  --iv-bg:              #06050d;
  --iv-surface:         rgba(13, 10, 29, 0.72);
  --iv-border:          rgba(255, 255, 255, 0.08);
  --iv-text-primary:    #faf8ff;
  --iv-text-secondary:  #a39cc4;
  --iv-text-tertiary:   #5e587a;

  position: fixed; inset: 0;
  background: radial-gradient(ellipse at 40% 0%, rgba(107,63,160,0.18) 0%, var(--iv-bg) 60%);
  color: var(--iv-text-primary);
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
  -webkit-font-smoothing: antialiased;
  overflow-y: auto;
  padding: 48px 24px calc(48px + env(safe-area-inset-bottom));
  display: flex; justify-content: center; align-items: flex-start;
}
.ivr-submit-card {
  width: 100%; max-width: 640px;
  background: var(--iv-surface);
  border: 1px solid var(--iv-border);
  border-radius: var(--iv-radius-2xl);
  padding: 36px 36px 32px;
  backdrop-filter: blur(28px) saturate(160%);
  -webkit-backdrop-filter: blur(28px) saturate(160%);
  box-shadow: 0 24px 80px rgba(0,0,0,0.5);
}
.ivr-submit-back {
  display: inline-block;
  color: var(--iv-text-secondary);
  text-decoration: none;
  font-size: 12px;
  margin-bottom: 18px;
  transition: color var(--iv-duration-sm) var(--iv-ease);
}
.ivr-submit-back:hover { color: var(--iv-yellow); }
.ivr-submit-title {
  font-size: 28px;
  font-weight: 700;
  letter-spacing: -0.02em;
  margin: 0 0 8px;
}
.ivr-submit-sub {
  color: var(--iv-text-secondary);
  font-size: 13.5px;
  line-height: 1.5;
  margin: 0 0 24px;
}
.ivr-submit-sub a { color: var(--iv-yellow); text-decoration: none; }
.ivr-submit-sub a:hover { text-decoration: underline; }

.ivr-submit-form { display: flex; flex-direction: column; gap: 14px; }
.ivr-submit-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
}
.ivr-submit-field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.ivr-submit-field.full { grid-column: 1 / -1; }
.ivr-submit-field > span {
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--iv-text-primary);
}
.ivr-submit-field > span small {
  font-weight: 400;
  font-size: 11px;
  color: var(--iv-text-secondary);
  margin-left: 6px;
}
.ivr-submit-field input,
.ivr-submit-field select {
  background: rgba(255,255,255,0.04);
  border: 1px solid rgba(255,255,255,0.1);
  border-radius: var(--iv-radius-md);
  padding: 11px 13px;
  color: var(--iv-text-primary);
  font: inherit;
  font-size: 14px;
  outline: none;
  transition: border-color var(--iv-duration-sm) var(--iv-ease),
              background var(--iv-duration-sm) var(--iv-ease);
  width: 100%;
  min-width: 0;
}
.ivr-submit-field input:focus,
.ivr-submit-field select:focus {
  border-color: var(--iv-yellow);
  background: rgba(247,215,20,0.04);
}
.ivr-submit-field input::placeholder { color: var(--iv-text-tertiary); }
.ivr-submit-field select { appearance: none; -webkit-appearance: none; padding-right: 32px;
  background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="%23a39cc4" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg>');
  background-repeat: no-repeat;
  background-position: right 12px center;
  cursor: pointer;
}

.ivr-submit-turnstile { margin: 4px 0; }
.ivr-submit-actions {
  display: flex; gap: 10px;
  margin-top: 8px;
}
.ivr-submit-btn {
  flex: 0 0 auto;
  padding: 11px 22px;
  background: linear-gradient(135deg, var(--iv-yellow) 0%, #ffd84a 100%);
  color: #000;
  border: 0;
  border-radius: var(--iv-radius-pill);
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.01em;
  cursor: pointer;
  text-decoration: none;
  display: inline-flex; align-items: center; justify-content: center;
  box-shadow: 0 4px 18px rgba(247,215,20,0.3);
  transition: transform var(--iv-duration-sm) var(--iv-ease),
              box-shadow var(--iv-duration-sm) var(--iv-ease);
}
.ivr-submit-btn:hover { transform: translateY(-1px); box-shadow: 0 6px 22px rgba(247,215,20,0.45); }
.ivr-submit-btn:disabled { opacity: 0.6; cursor: not-allowed; transform: none; box-shadow: none; }
.ivr-submit-btn-ghost {
  background: transparent;
  color: var(--iv-text-secondary);
  border: 1px solid rgba(255,255,255,0.1);
  box-shadow: none;
}
.ivr-submit-btn-ghost:hover {
  color: var(--iv-text-primary);
  border-color: rgba(255,255,255,0.25);
  transform: none; box-shadow: none;
}

.ivr-submit-error {
  padding: 10px 14px;
  background: rgba(220, 38, 38, 0.12);          /* tinted from --iv-danger */
  border: 1px solid rgba(220, 38, 38, 0.3);
  border-radius: var(--iv-radius-md);
  color: #fda4af;
  font-size: 13px;
}

.ivr-submit-success {
  text-align: center;
  padding: 16px 0 8px;
}
.ivr-submit-success-icon {
  width: 56px; height: 56px;
  margin: 0 auto 16px;
  background: linear-gradient(135deg, var(--iv-yellow) 0%, #ffd84a 100%);
  border-radius: 50%;
  color: #000;
  font-size: 28px;
  font-weight: 700;
  line-height: 56px;
  box-shadow: 0 0 28px rgba(247,215,20,0.35);
}
.ivr-submit-success h2 { font-size: 22px; margin: 0 0 8px; font-weight: 700; }
.ivr-submit-success p { color: var(--iv-text-secondary); font-size: 14px; line-height: 1.5; margin: 0 0 12px; max-width: 480px; margin-inline: auto; }
.ivr-submit-success .ivr-submit-actions { justify-content: center; margin-top: 24px; }
.ivr-submit-probe {
  font-family: 'JetBrains Mono', monospace;
  font-size: 11px !important;
  color: var(--iv-text-tertiary) !important;
}

@media (max-width: 640px) {
  .ivr-submit-root { padding: 28px 16px calc(28px + env(safe-area-inset-bottom)); }
  .ivr-submit-card { padding: 24px 22px; border-radius: var(--iv-radius-xl); }
  .ivr-submit-title { font-size: 22px; }
  .ivr-submit-grid { grid-template-columns: 1fr; gap: 12px; }
}

/* ── Achievement chip + panel (iVGU+ gamification) ────────────────────── */
.iv-radio-globe-root .ivr-me-chip {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: 'Inter', sans-serif;
  font-size: 11px;
  font-weight: 500;
  margin-left: 4px;
  cursor: pointer;
  user-select: none;
  pointer-events: auto;
}
.iv-radio-globe-root.auth-off .ivr-me-chip { display: none !important; }
.iv-radio-globe-root .ivr-me-chip .me-pill {
  padding: 4px 8px;
  border-radius: var(--iv-radius-pill);
  background: rgba(13, 10, 29, 0.55);
  border: 1px solid var(--ivr-line);
  color: var(--ivr-ink);
  white-space: nowrap;
  transition: transform var(--iv-duration-sm) var(--iv-ease),
              background var(--iv-duration-sm) var(--iv-ease);
}
.iv-radio-globe-root .ivr-me-chip:hover .me-pill { background: rgba(13, 10, 29, 0.85); transform: translateY(-1px); }
.iv-radio-globe-root .ivr-me-chip:active .me-pill { transform: scale(0.95); opacity: 0.8; }
.iv-radio-globe-root .ivr-me-chip .me-pill-ach {
  background: linear-gradient(135deg, rgba(247,215,20,0.18) 0%, rgba(247,215,20,0.06) 100%);
  border-color: rgba(247,215,20,0.4);
  color: var(--iv-yellow);
}

.iv-radio-globe-root .ivr-ach-panel {
  position: absolute;
  top: 76px;
  right: 16px;
  z-index: 30;
  width: min(380px, calc(100% - 32px));
  background: rgba(13, 10, 29, 0.94);
  backdrop-filter: blur(24px) saturate(170%);
  -webkit-backdrop-filter: blur(24px) saturate(170%);
  border: 1px solid rgba(255,255,255,0.1);
  border-radius: var(--iv-radius-xl);
  padding: 18px 18px 14px;
  box-shadow: var(--iv-shadow-lg);
  color: var(--ivr-ink);
}
.iv-radio-globe-root .ivr-ach-head { font-size: 14px; font-weight: 700; margin: 0 0 12px; letter-spacing: -0.01em; }
.iv-radio-globe-root .ivr-ach-list { list-style: none; padding: 0; margin: 0; }
.iv-radio-globe-root .ivr-ach-list li {
  display: grid;
  grid-template-columns: 20px 1fr auto;
  gap: 10px;
  align-items: center;
  padding: 7px 0;
  font-size: 12.5px;
  border-bottom: 1px solid rgba(255,255,255,0.06);
}
.iv-radio-globe-root .ivr-ach-list li:last-child { border-bottom: 0; }
.iv-radio-globe-root .ivr-ach-list .ach-icon { font-size: 14px; color: var(--ivr-faint); }
.iv-radio-globe-root .ivr-ach-list li.unlocked .ach-icon { color: var(--iv-yellow); text-shadow: 0 0 8px rgba(247,215,20,0.5); }
.iv-radio-globe-root .ivr-ach-list li.unlocked .ach-label { color: var(--ivr-ink); font-weight: 600; }
.iv-radio-globe-root .ivr-ach-list li.locked .ach-label { color: var(--ivr-dim); }
.iv-radio-globe-root .ivr-ach-list .ach-prog {
  font-family: 'JetBrains Mono', monospace;
  font-size: 10.5px;
  color: var(--ivr-faint);
  letter-spacing: 0.04em;
}
.iv-radio-globe-root .ivr-ach-list li.unlocked .ach-prog { color: var(--iv-yellow); }
.iv-radio-globe-root .ivr-ach-close {
  position: absolute; top: 10px; right: 14px;
  width: 28px; height: 28px;
  background: transparent; border: 0;
  color: var(--ivr-dim);
  font-size: 22px; line-height: 1;
  cursor: pointer; border-radius: 50%;
  transition: all var(--iv-duration-sm) var(--iv-ease);
}
.iv-radio-globe-root .ivr-ach-close:hover { background: rgba(255,255,255,0.08); color: var(--ivr-ink); }

@media (max-width: 640px) {
  .iv-radio-globe-root .ivr-me-chip .me-pill { padding: 3px 6px; font-size: 10px; }
  .iv-radio-globe-root .ivr-ach-panel { top: calc(env(safe-area-inset-top) + 92px); right: 12px; left: 12px; width: auto; }
}

/* ── Episode picker panel (Phase 3, podcasts) ─────────────────────────────
   Opens when the listener taps a podcast pin or list row. Shows the latest
   10 episodes resolved from RSS via /api/radio-globe/podcast-episodes/[id].
   Sized + positioned like the achievements + playlists panels so they all
   share the same visual language. */
.iv-radio-globe-root .ivr-episode-panel {
  position: absolute;
  top: 76px;
  right: 16px;
  z-index: 30;
  width: min(440px, calc(100% - 32px));
  max-height: calc(100dvh - 180px);
  display: flex; flex-direction: column;
  background: rgba(13, 10, 29, 0.94);
  backdrop-filter: blur(24px) saturate(170%);
  -webkit-backdrop-filter: blur(24px) saturate(170%);
  border: 1px solid rgba(255,255,255,0.1);
  border-radius: var(--iv-radius-xl);
  padding: 18px 18px 10px;
  box-shadow: var(--iv-shadow-lg);
  color: var(--ivr-ink);
}
.iv-radio-globe-root .ivr-ep-head {
  margin-bottom: 12px;
  padding-right: 28px;     /* room for the close × */
}
.iv-radio-globe-root .ivr-ep-title {
  font-size: 16px;
  font-weight: 700;
  letter-spacing: -0.015em;
  color: var(--ivr-ink);
}
.iv-radio-globe-root .ivr-ep-sub {
  font-size: 11.5px;
  color: var(--ivr-dim);
  margin-top: 2px;
}
.iv-radio-globe-root .ivr-ep-list {
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  margin: 0 -4px;
  padding: 0 4px 8px;
}
.iv-radio-globe-root .ivr-ep-loading {
  text-align: center;
  font-size: 12px;
  color: var(--ivr-dim);
  padding: 24px 8px;
}
.iv-radio-globe-root .ivr-ep-row {
  display: grid;
  grid-template-columns: 28px minmax(0, 1fr) 32px;
  align-items: center;
  gap: 10px;
  padding: 10px 8px;
  border-bottom: 1px solid rgba(255,255,255,0.06);
  cursor: pointer;
  transition: background var(--iv-duration-sm) var(--iv-ease);
}
.iv-radio-globe-root .ivr-ep-row:last-child { border-bottom: 0; }
.iv-radio-globe-root .ivr-ep-row:hover,
.iv-radio-globe-root .ivr-ep-row:focus-visible {
  background: rgba(255,255,255,0.04);
  outline: none;
}
.iv-radio-globe-root .ivr-ep-row.is-last {
  background: linear-gradient(90deg, rgba(247,215,20,0.10) 0%, rgba(247,215,20,0.02) 100%);
}
.iv-radio-globe-root .ivr-ep-pos {
  font-family: 'JetBrains Mono', monospace;
  font-size: 11px;
  text-align: center;
  color: var(--ivr-faint);
}
.iv-radio-globe-root .ivr-ep-row.is-last .ivr-ep-pos { color: var(--iv-yellow); }
.iv-radio-globe-root .ivr-ep-body { min-width: 0; }
.iv-radio-globe-root .ivr-ep-row-title {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: -0.005em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.iv-radio-globe-root .ivr-ep-row-meta {
  font-size: 10.5px;
  color: var(--ivr-dim);
  margin-top: 2px;
  font-family: 'JetBrains Mono', monospace;
  letter-spacing: 0.04em;
}
.iv-radio-globe-root .ivr-ep-resume {
  color: var(--iv-yellow);
  font-weight: 600;
}
.iv-radio-globe-root .ivr-ep-play {
  width: 30px; height: 30px;
  border-radius: 50%;
  background: rgba(255,255,255,0.06);
  border: 1px solid var(--ivr-line);
  color: var(--ivr-ink);
  display: flex; align-items: center; justify-content: center;
  cursor: pointer;
  transition: all var(--iv-duration-sm) var(--iv-ease);
}
.iv-radio-globe-root .ivr-ep-row:hover .ivr-ep-play {
  background: var(--iv-yellow);
  color: #000;
  border-color: var(--iv-yellow);
}

@media (max-width: 640px) {
  .iv-radio-globe-root .ivr-episode-panel {
    top: calc(env(safe-area-inset-top) + 92px);
    right: 12px; left: 12px;
    width: auto;
  }
  .iv-radio-globe-root .ivr-ep-row { padding: 12px 8px; }   /* bigger touch area */
}

/* ── Playlists panel (iVGU+) ──────────────────────────────────────────────
   Parallel surface to the achievements panel; both anchor to the upper
   right corner. Uses the canonical iv-radius / iv-shadow / iv-duration
   tokens for visual consistency with the rest of the radio. */
.iv-radio-globe-root .ivr-pl-chip { background: transparent; border: 0; padding: 0; cursor: pointer; }
.iv-radio-globe-root .ivr-pl-chip .me-pill {
  background: rgba(13, 10, 29, 0.55);
  border: 1px solid var(--ivr-line);
  color: var(--ivr-ink);
  padding: 4px 10px;
  border-radius: var(--iv-radius-pill);
  font-size: 11px;
  font-weight: 500;
  white-space: nowrap;
  transition: transform var(--iv-duration-sm) var(--iv-ease),
              background var(--iv-duration-sm) var(--iv-ease);
}
.iv-radio-globe-root .ivr-pl-chip:hover .me-pill {
  background: rgba(13, 10, 29, 0.85);
  transform: translateY(-1px);
}

.iv-radio-globe-root .ivr-playlists-panel {
  position: absolute;
  top: 76px;
  right: 16px;
  z-index: 30;
  width: min(420px, calc(100% - 32px));
  max-height: calc(100dvh - 180px);
  overflow: hidden;
  display: flex; flex-direction: column;
  background: rgba(13, 10, 29, 0.94);
  backdrop-filter: blur(24px) saturate(170%);
  -webkit-backdrop-filter: blur(24px) saturate(170%);
  border: 1px solid rgba(255,255,255,0.1);
  border-radius: var(--iv-radius-xl);
  padding: 18px 16px 14px;
  box-shadow: var(--iv-shadow-lg);
  color: var(--ivr-ink);
}
.iv-radio-globe-root .ivr-pl-head {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 10px;
  padding-right: 28px;       /* room for the absolute close × */
}
.iv-radio-globe-root .ivr-pl-title { font-size: 14px; font-weight: 700; letter-spacing: -0.01em; }
.iv-radio-globe-root .ivr-pl-newbtn {
  background: var(--iv-yellow);
  color: #000;
  border: 0;
  padding: 5px 12px;
  border-radius: var(--iv-radius-pill);
  font: inherit;
  font-size: 11.5px;
  font-weight: 600;
  cursor: pointer;
  transition: transform var(--iv-duration-sm) var(--iv-ease),
              box-shadow var(--iv-duration-sm) var(--iv-ease);
  box-shadow: 0 2px 10px rgba(247,215,20,0.25);
}
.iv-radio-globe-root .ivr-pl-newbtn:hover {
  transform: translateY(-1px);
  box-shadow: 0 4px 14px rgba(247,215,20,0.4);
}
.iv-radio-globe-root .ivr-pl-list {
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  margin: 0 -4px;
  padding: 0 4px;
}
.iv-radio-globe-root .ivr-pl-empty {
  text-align: center;
  padding: 24px 16px 12px;
  color: var(--ivr-dim);
}
.iv-radio-globe-root .ivr-pl-empty-icon {
  font-size: 32px;
  color: var(--ivr-faint);
  margin-bottom: 8px;
}
.iv-radio-globe-root .ivr-pl-empty-text {
  font-size: 12.5px;
  line-height: 1.55;
}
.iv-radio-globe-root .ivr-pl-row {
  display: grid;
  grid-template-columns: 36px 1fr 32px 32px;
  align-items: center;
  gap: 10px;
  padding: 9px 4px;
  border-bottom: 1px solid rgba(255,255,255,0.06);
}
.iv-radio-globe-root .ivr-pl-row:last-child { border-bottom: 0; }
.iv-radio-globe-root .ivr-pl-play {
  width: 30px; height: 30px;
  border-radius: 50%;
  background: rgba(255,255,255,0.06);
  border: 1px solid var(--ivr-line);
  color: var(--ivr-ink);
  display: flex; align-items: center; justify-content: center;
  cursor: pointer;
  transition: all var(--iv-duration-sm) var(--iv-ease);
}
.iv-radio-globe-root .ivr-pl-row:hover .ivr-pl-play {
  background: var(--iv-yellow); color: #000; border-color: var(--iv-yellow);
}
.iv-radio-globe-root .ivr-pl-info { min-width: 0; }
.iv-radio-globe-root .ivr-pl-name {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: -0.005em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.iv-radio-globe-root .ivr-pl-meta {
  font-size: 11px;
  color: var(--ivr-dim);
  margin-top: 2px;
  font-family: 'JetBrains Mono', monospace;
  letter-spacing: 0.04em;
}
.iv-radio-globe-root .ivr-pl-iconbtn {
  width: 30px; height: 30px;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 50%;
  color: var(--ivr-dim);
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: all var(--iv-duration-sm) var(--iv-ease);
}
.iv-radio-globe-root .ivr-pl-iconbtn:hover {
  background: rgba(255,255,255,0.08);
  color: var(--ivr-ink);
  border-color: var(--ivr-line);
}
.iv-radio-globe-root .ivr-pl-iconbtn.ivr-pl-danger:hover {
  color: var(--iv-danger);
  border-color: rgba(220,38,38,0.4);
  background: rgba(220,38,38,0.08);
}

/* Playlist editor sub-panel */
.iv-radio-globe-root .ivr-pl-editor {
  z-index: 31;             /* above the list panel if both happen to render */
  max-height: calc(100dvh - 140px);
}
.iv-radio-globe-root .ivr-pl-name-input {
  width: 100%;
  background: rgba(255,255,255,0.04);
  border: 1px solid var(--ivr-line);
  border-radius: var(--iv-radius-md);
  color: var(--ivr-ink);
  font: inherit;
  font-size: 14px;
  padding: 10px 12px;
  outline: none;
  margin-bottom: 8px;
  transition: border-color var(--iv-duration-sm) var(--iv-ease),
              background var(--iv-duration-sm) var(--iv-ease);
}
.iv-radio-globe-root .ivr-pl-name-input:focus {
  border-color: var(--iv-yellow);
  background: rgba(247,215,20,0.04);
}
.iv-radio-globe-root .ivr-pl-name-input::placeholder { color: var(--ivr-faint); }
.iv-radio-globe-root .ivr-pl-pickhint {
  font-size: 10.5px;
  color: var(--ivr-dim);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  margin-bottom: 6px;
}
.iv-radio-globe-root .ivr-pl-pickbox {
  flex: 1; min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  border: 1px solid var(--ivr-line);
  border-radius: var(--iv-radius-md);
  padding: 4px;
}
.iv-radio-globe-root .ivr-pl-pickrow {
  display: grid;
  grid-template-columns: 18px 1fr auto;
  gap: 10px;
  align-items: center;
  padding: 7px 8px;
  border-radius: var(--iv-radius-sm);
  cursor: pointer;
  font-size: 12.5px;
  transition: background var(--iv-duration-sm) var(--iv-ease);
}
.iv-radio-globe-root .ivr-pl-pickrow:hover { background: rgba(255,255,255,0.04); }
.iv-radio-globe-root .ivr-pl-pickrow.on {
  background: linear-gradient(90deg, rgba(247,215,20,0.10) 0%, rgba(247,215,20,0.02) 100%);
}
.iv-radio-globe-root .ivr-pl-pickrow input[type="checkbox"] {
  accent-color: var(--iv-yellow);
  width: 14px; height: 14px;
  cursor: pointer;
}
.iv-radio-globe-root .ivr-pl-pickname {
  font-weight: 500;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.iv-radio-globe-root .ivr-pl-pickgeo {
  font-size: 10.5px;
  color: var(--ivr-dim);
  font-family: 'JetBrains Mono', monospace;
  letter-spacing: 0.04em;
  white-space: nowrap;
}
.iv-radio-globe-root .ivr-pl-actions {
  display: flex; justify-content: flex-end; gap: 8px;
  margin-top: 12px;
}
.iv-radio-globe-root .ivr-pl-cancelbtn,
.iv-radio-globe-root .ivr-pl-savebtn {
  padding: 8px 16px;
  border-radius: var(--iv-radius-pill);
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  transition: all var(--iv-duration-sm) var(--iv-ease);
}
.iv-radio-globe-root .ivr-pl-cancelbtn {
  background: transparent;
  border: 1px solid var(--ivr-line);
  color: var(--ivr-dim);
}
.iv-radio-globe-root .ivr-pl-cancelbtn:hover { color: var(--ivr-ink); border-color: rgba(255,255,255,0.25); }
.iv-radio-globe-root .ivr-pl-savebtn {
  background: var(--iv-yellow);
  border: 1px solid var(--iv-yellow);
  color: #000;
  box-shadow: 0 4px 14px rgba(247,215,20,0.3);
}
.iv-radio-globe-root .ivr-pl-savebtn:hover { transform: translateY(-1px); box-shadow: 0 6px 18px rgba(247,215,20,0.45); }

@media (max-width: 640px) {
  .iv-radio-globe-root .ivr-playlists-panel {
    top: calc(env(safe-area-inset-top) + 92px);
    right: 12px; left: 12px;
    width: auto;
  }
  .iv-radio-globe-root .ivr-pl-name { font-size: 12.5px; }
  .iv-radio-globe-root .ivr-pl-meta { font-size: 10.5px; }
  .iv-radio-globe-root .ivr-pl-pickrow { padding: 8px; }     /* bigger tap target */
}

/* Sign-in CTA (anonymous only) + user chip (signed-in only).
   The .auth-on / .auth-off classes are set on the root by the server-side
   page based on the iv_session cookie — toggling visibility is a CSS-only
   concern, no JS branching needed. */
.iv-radio-globe-root .ivr-signin-cta {
  position: absolute;
  bottom: 24px;
  left: 28px;
  z-index: 19;
  display: none;
  align-items: center; gap: 8px;
  padding: 11px 18px;
  background: rgba(13, 10, 29, 0.92);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border: 1px solid rgba(247, 215, 20, 0.35);
  border-radius: var(--iv-radius-pill);
  color: var(--ivr-ink);
  font-size: 13px;
  font-weight: 600;
  text-decoration: none;
  transition: all var(--iv-duration-sm) var(--iv-ease);
  pointer-events: auto;
}
.iv-radio-globe-root.auth-off .ivr-signin-cta { display: inline-flex; }
.iv-radio-globe-root .ivr-signin-cta:hover {
  border-color: var(--iv-yellow);
  background: rgba(247,215,20,0.12);
  transform: translateY(-2px);
  box-shadow: 0 4px 16px rgba(247,215,20,0.15);
}
.iv-radio-globe-root .ivr-signin-cta svg { color: var(--iv-yellow); }

.iv-radio-globe-root .ivr-user-chip {
  position: absolute;
  bottom: 24px;
  left: 28px;
  z-index: 19;
  display: none;
  align-items: center; gap: 7px;
  padding: 8px 14px;
  background: rgba(13, 10, 29, 0.7);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border: 1px solid var(--ivr-line);
  border-radius: var(--iv-radius-pill);
  color: var(--ivr-ink);
  font-size: 12px;
  font-weight: 500;
  pointer-events: auto;
}
.iv-radio-globe-root.auth-on .ivr-user-chip { display: inline-flex; }
.iv-radio-globe-root .ivr-user-chip .user-dot {
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--iv-yellow);
  box-shadow: 0 0 6px var(--iv-yellow);
}
.iv-radio-globe-root .ivr-user-chip .user-name {
  max-width: 200px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ── Mobile polish ──────────────────────────────────────────────────────── */
@media (max-width: 640px) {
  /* Header: respect notch (env(safe-area-inset-top)), tighter padding, hide
     the · iVUniverse subtitle to keep the brand line short.
     Founder 2026-05-28: iPhone Safari screenshot showed "Vadodara, India"
     clipped to "Vadodara, Ind" because the count had nowrap and overflowed
     the viewport right-edge. Fix: let the header shrink + ellipsis cleanly. */
  .iv-radio-globe-root .ivr-header {
    padding: calc(env(safe-area-inset-top) + 14px) 16px 10px;
    gap: 10px;
    overflow: hidden;                /* don't bleed past the viewport */
  }
  .iv-radio-globe-root .ivr-logo { font-size: 14px; gap: 8px; flex: 0 0 auto; }
  .iv-radio-globe-root .ivr-logo .mark { width: 26px; height: 26px; }
  .iv-radio-globe-root .ivr-logo .name span { display: none; } /* hide " · iVUniverse" */
  .iv-radio-globe-root .ivr-count {
    font-size: 10px;
    gap: 8px;
    min-width: 0;                    /* allow inner ellipsis */
    overflow: hidden;
  }
  .iv-radio-globe-root .ivr-count .you {
    font-size: 10px;
    min-width: 0;
    flex: 1 1 auto;
    overflow: hidden;
  }
  .iv-radio-globe-root .ivr-count .you #iv-youCity {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 140px;                /* fits "Vadodara, India" comfortably */
    display: inline-block;
  }
  /* Hide the "X live · X on globe" tally on phones; the "you" chip and the
     count of pins on the map are signal enough. Tally remains on tablet+. */
  .iv-radio-globe-root .ivr-count > span:first-child { display: none; }

  /* Genre row: tighter, sits just below the header. Scroll snaps for momentum. */
  .iv-radio-globe-root .ivr-genres {
    padding: 0 8px 0 16px;
    top: calc(env(safe-area-inset-top) + 68px);
    gap: 6px;
    -webkit-overflow-scrolling: touch;
    scroll-snap-type: x proximity;
  }
  .iv-radio-globe-root .ivr-chip { padding: 7px 12px; font-size: 11px; scroll-snap-align: start; }
  /* Last chip snaps to right edge so no trailing gap shows */
  .iv-radio-globe-root .ivr-genres .ivr-chip:last-child { scroll-snap-align: end; }

  /* v2.8 Broadcast-waves loader: scale the stage down on phones to keep
     enough headroom for the caption + iVUniverse branding above the fold. */
  .iv-radio-globe-root .ivl-stage { width: 168px; height: 168px; }
  .iv-radio-globe-root .ivl-core { width: 22px; height: 22px; }
  .iv-radio-globe-root .ivl-ring {
    width: 24px; height: 24px;
    margin: -12px -12px 0 0;
  }
  /* Rings need a matching keyframe override so the final extent matches the
     smaller stage — otherwise the ring would burst past the visible circle. */
  .iv-radio-globe-root .ivl-ring { animation-name: ivlRingMobile; }
  @keyframes ivlRingMobile {
    0%   { width: 24px;  height: 24px;  margin: -12px -12px 0 0; opacity: 0; border-color: rgba(247,215,20,0.85); }
    10%  { opacity: 0.9; }
    100% { width: 168px; height: 168px; margin: -84px -84px 0 0; opacity: 0; border-color: rgba(247,215,20,0.0); }
  }
  .iv-radio-globe-root .ivl-eq { width: 54px; height: 16px; bottom: -22px; }
  .iv-radio-globe-root .ivr-splash .label { font-size: 20px; }
  .iv-radio-globe-root .ivl-sub { font-size: 11px; max-width: 220px; }

  /* Player: bottom sheet feel, safe-area for home-indicator, 44px tap targets
     per Apple HIG. Volume slider hidden on phones (hardware volume is enough
     once playing); the volume icon remains as a visual cue.
     Founder 2026-05-28: iPhone Safari real-device screenshot showed the
     station name and broadcaster chip getting clipped to "B.." and "Mu" on
     small viewports because 4 control buttons + art + gaps left almost no
     room for pl-info. Solution: wrap the player on mobile — art + name on
     row 1 (info gets full available width), controls on row 2 spread evenly
     across the sheet. Reads natural, never truncates, taps stay HIG-sized. */
  .iv-radio-globe-root .ivr-player {
    left: 12px; right: 12px;
    bottom: calc(env(safe-area-inset-bottom) + 16px);
    width: auto;
    padding: 12px 14px 10px;
    gap: 12px 12px;
    border-radius: var(--iv-radius-xl);
    transform: translateY(160%);
    flex-wrap: wrap;
  }
  .iv-radio-globe-root .ivr-player.visible { transform: translateY(0); }
  .iv-radio-globe-root .pl-art { width: 48px; height: 48px; border-radius: var(--iv-radius-lg); order: 0; flex: 0 0 auto; }
  .iv-radio-globe-root .pl-info { order: 1; flex: 1 1 calc(100% - 60px); min-width: 0; }
  .iv-radio-globe-root .pl-controls {
    order: 2;
    flex: 1 1 100%;
    justify-content: space-between;
    gap: 4px;
    padding-top: 2px;
    border-top: 1px solid rgba(255,255,255,0.05);
    margin-top: 2px;
  }
  .iv-radio-globe-root .pl-name { font-size: 14.5px; }
  .iv-radio-globe-root .pl-meta { font-size: 11px; gap: 6px; }
  .iv-radio-globe-root .pl-meta .genre-tag { font-size: 9px; padding: 2px 6px; }
  .iv-radio-globe-root .pl-meta .pl-track { max-width: 160px; }
  .iv-radio-globe-root .pl-volume { display: none; }
  .iv-radio-globe-root .ivr-btn { width: 44px; height: 44px; }            /* HIG min */
  .iv-radio-globe-root .ivr-btn.play { width: 48px; height: 48px; }
  .iv-radio-globe-root .ivr-btn svg { width: 18px; height: 18px; }
  .iv-radio-globe-root .ivr-btn.play svg { width: 20px; height: 20px; }

  /* Nudge: no keyboard on phone — hide the "▶ key" hint */
  .iv-radio-globe-root .ivr-nudge { bottom: 95px; font-size: 12px; padding: 0 24px; }
  .iv-radio-globe-root .ivr-nudge .key { display: none; }

  /* Toast: room for the notch */
  .iv-radio-globe-root .ivr-toast {
    top: calc(env(safe-area-inset-top) + 108px);
    font-size: 11px;
    padding: 8px 14px;
  }

  /* Pin labels: hover doesn't fire on touch — show on tap instead. The pin's
     own click handler still tunes in, this is a brief visual confirm. */
  .iv-radio-globe-root .pin-label { font-size: 10px; bottom: 22px; padding: 4px 9px; }
  .iv-radio-globe-root .pin:active .pin-label { opacity: 1; transform: translateX(-50%) translateY(0); }

  /* Bigger native-style scrollbar hidden on iOS Safari */
  .iv-radio-globe-root .ivr-genres::-webkit-scrollbar { display: none; }

  /* View toggle: keep just the icons on phones (compact) */
  .iv-radio-globe-root .ivr-view-tab span { display: none; }
  .iv-radio-globe-root .ivr-view-tab { padding: 6px 8px; }

  /* Sign-in CTA / user chip: hide on phones — the player owns the bottom edge.
     Tap the user-avatar (future, when nav arrives) to access account. */
  .iv-radio-globe-root .ivr-signin-cta,
  .iv-radio-globe-root .ivr-user-chip { display: none !important; }

  /* List view: simpler row layout on phones — drop genre/mood tag columns */
  .iv-radio-globe-root .ivr-list {
    padding: 0 12px calc(env(safe-area-inset-bottom) + 16px);
    top: calc(env(safe-area-inset-top) + 118px);
    transition: bottom 0.6s cubic-bezier(0.2, 0.9, 0.2, 1);
  }
  /* When player is visible, physically shrink list viewport to stop at player top —
     eliminates overlap at every scroll position, not just at the end of the list. */
  .iv-radio-globe-root:has(.ivr-player.visible) .ivr-list {
    bottom: calc(env(safe-area-inset-bottom) + 177px);
  }
  /* Search + sort bar: reanchor to match the mobile list top, span full width */
  .iv-radio-globe-root.mode-list .ivr-list-search {
    top: calc(env(safe-area-inset-top) + 118px);
    left: 8px;
    right: 8px;
    transform: none;
    width: auto;
  }
  .iv-radio-globe-root .ivr-list-row {
    /* 4 cols: dot | info | fav | play — keeps all actions in one row on mobile.
       order swaps fav before play because DOM has play before fav. */
    grid-template-columns: 28px minmax(0, 1fr) 34px 34px;
    gap: 10px;
    padding: 10px 12px;
  }
  .iv-radio-globe-root .ivr-list-row .lr-fav { order: 1; }
  .iv-radio-globe-root .ivr-list-row .lr-play { order: 2; }
  .iv-radio-globe-root .ivr-list-row .lr-tag { display: none; }
  /* Local-time chip stays only in the player on phones to save horizontal room. */
  .iv-radio-globe-root .ivr-list-row .lr-time { display: none; }
  .iv-radio-globe-root .ivr-list-row .lr-name { font-size: 13.5px; }
  .iv-radio-globe-root .ivr-list-row .lr-sub { font-size: 11px; }
  .iv-radio-globe-root .ivr-list-row .lr-play { width: 34px; height: 34px; }
  .iv-radio-globe-root .ivr-list-row .lr-play svg { width: 14px; height: 14px; }
  /* List-view count banner: JS inserts it as a sibling of #iv-list (normal flow)
     so it ends up at y≈0 and bleeds through the header's transparent gradient.
     Pull it out of normal flow and anchor it just below the single-row pill. */
  .iv-radio-globe-root.mode-list .ivr-list-count {
    position: absolute;
    top: calc(env(safe-area-inset-top) + 168px);
    left: 16px; right: 16px;
    z-index: 2;
    background: none;
    padding: 4px 0 6px;
  }
  .iv-radio-globe-root.mode-list .ivr-list { padding-top: 100px; }
}

/* ── Narrow phones — pill wraps to two rows, count + list shift down ────── */
@media (max-width: 520px) {
  .iv-radio-globe-root.mode-list .ivr-list-count {
    top: calc(env(safe-area-inset-top) + 204px);
  }
  .iv-radio-globe-root.mode-list .ivr-list { padding-top: 136px; }
}

/* ── Tiny phones (iPhone SE, foldables in folded mode) ─────────────────── */
@media (max-width: 380px) {
  .iv-radio-globe-root .ivr-count > span:first-child { display: none; }   /* drop "14 live · 14 on globe" — keep only "you" chip */
  .iv-radio-globe-root .ivr-count .you #iv-youCity { max-width: 110px; }  /* tighter ellipsis at iPhone SE */
  .iv-radio-globe-root .pl-art { width: 40px; height: 40px; }
  .iv-radio-globe-root .ivr-btn { width: 40px; height: 40px; }            /* slight relax for tight phones */
  .iv-radio-globe-root .ivr-btn.play { width: 44px; height: 44px; }
  /* On iPhone SE, info column already shares row 1 with art via flex-wrap;
     ensure the controls row stays comfortable even at 320px-ish foldables. */
  .iv-radio-globe-root .pl-controls { gap: 2px; }
}

/* ── Touch-only refinements (any pointer that's coarse) ─────────────────── */
@media (pointer: coarse) {
  .iv-radio-globe-root .ivr-btn:hover { transform: none; }                /* no hover bounce on touch */
  .iv-radio-globe-root .pin:hover { transform: none; }
  .iv-radio-globe-root .ivr-chip:hover { transform: none; }
  /* Make the pin's tappable area larger than its visual circle */
  .iv-radio-globe-root .pin {
    box-shadow:
      0 0 0 2px rgba(255,255,255,0.92),
      0 0 14px currentColor,
      0 0 0 8px transparent; /* invisible 8px extra hit area */
  }
}

/* ── Landscape phones: keep header but slim player ─────────────────────── */
@media (max-width: 900px) and (orientation: landscape) and (max-height: 500px) {
  .iv-radio-globe-root .ivr-header { padding: calc(env(safe-area-inset-top) + 8px) 16px 6px; }
  .iv-radio-globe-root .ivr-genres { top: calc(env(safe-area-inset-top) + 44px); }
  .iv-radio-globe-root .ivr-player { bottom: calc(env(safe-area-inset-bottom) + 8px); padding: 10px 14px; }
  .iv-radio-globe-root .ivr-nudge { display: none; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   Auth Prompt Card — the first step in the two-step auth UX
   ═══════════════════════════════════════════════════════════════════════════
   Founder direction 2026-05-28 (final): "on click of favourites, in the
   page only it should show to use this feature login / sign up, which he
   then clicks on and the page open with his will."

   Renders inline on /radio-globe. Anonymous tap on ♥ Fav → this card
   appears above the player with a heart icon, "Save favourites" headline,
   one-line description, and [Sign up] [Sign in] buttons. User chooses
   when (and which mode) to proceed → that tap opens the iframe sheet
   below. No surprise navigation, no auto-popup, no music interruption.
   ───────────────────────────────────────────────────────────────────────── */
.iv-radio-globe-root .ivr-auth-prompt {
  position: absolute;
  left: 50%;
  bottom: 140px;
  transform: translateX(-50%) translateY(20px);
  z-index: 25;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.32s var(--iv-ease, ease), transform 0.32s var(--iv-ease, ease);
  width: min(420px, calc(100vw - 24px));
}
.iv-radio-globe-root .ivr-auth-prompt[hidden] { display: none; }
.iv-radio-globe-root .ivr-auth-prompt.show {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
  pointer-events: auto;
}
/* The card itself — premium feel: gradient surface, subtle yellow border
   tint, drop-shadow + edge-lit inset, centered content. */
.iv-radio-globe-root .ivr-auth-prompt-card {
  position: relative;
  padding: 22px 20px 18px;
  background:
    radial-gradient(ellipse 110% 80% at 50% 0%, rgba(247, 215, 20, 0.06), transparent 60%),
    linear-gradient(180deg, rgba(13, 10, 29, 0.96) 0%, rgba(20, 14, 40, 0.96) 100%);
  backdrop-filter: blur(26px) saturate(170%);
  -webkit-backdrop-filter: blur(26px) saturate(170%);
  border: 1px solid rgba(255,255,255,0.10);
  border-radius: 22px;
  box-shadow:
    0 24px 60px rgba(0,0,0,0.55),
    0 0 0 1px rgba(255,255,255,0.04) inset,
    0 -1px 0 rgba(247,215,20,0.10) inset;
  color: var(--ivr-ink);
  text-align: center;
  overflow: hidden;
}
/* Subtle top-edge yellow ribbon — IV brand accent without being loud */
.iv-radio-globe-root .ivr-auth-prompt-card::before {
  content: '';
  position: absolute;
  inset: 0 0 auto 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(247,215,20,0.6), transparent);
  pointer-events: none;
}
/* Glowing heart icon — center-top, pulse animation */
.iv-radio-globe-root .ivr-auth-prompt-heart {
  width: 48px;
  height: 48px;
  margin: 0 auto 12px;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 40%, rgba(239, 68, 68, 0.18), rgba(220, 38, 38, 0.05));
  border: 1px solid rgba(239, 68, 68, 0.35);
  color: #ef4444;
  display: flex;
  align-items: center;
  justify-content: center;
  filter: drop-shadow(0 0 16px rgba(239, 68, 68, 0.45));
  animation: ivrAuthHeartPulse 2.4s ease-in-out infinite;
}
@keyframes ivrAuthHeartPulse {
  0%, 100% { transform: scale(1);    box-shadow: 0 0 0 0   rgba(239, 68, 68, 0.35); }
  50%      { transform: scale(1.05); box-shadow: 0 0 0 10px rgba(239, 68, 68, 0); }
}
.iv-radio-globe-root .ivr-auth-prompt-title {
  /* Founder 2026-05-28: "text size should be amazing, proper for all
     types of phones." Fluid typography via clamp() — scales smoothly
     from 15px on iPhone SE 375 → 19px on iPhone 14 Pro Max 430+ → 20px
     on small tablets. No media-query jumps; reads beautifully on every
     device. Min protects readability, max protects elegance. */
  font-size: clamp(15px, 4.2vw, 20px);
  font-weight: 700;
  letter-spacing: -0.018em;
  color: var(--ivr-ink);
  line-height: 1.32;
  margin: 0 14px 18px;
  text-wrap: balance;             /* keep the line breaks visually even */
}
.iv-radio-globe-root .ivr-auth-prompt-actions {
  display: flex;
  gap: 8px;
  justify-content: center;
}
.iv-radio-globe-root .ivr-auth-prompt-btn {
  font: inherit;
  /* Fluid button label — 13px on tight phones up to 14.5px on wide */
  font-size: clamp(13px, 3.5vw, 14.5px);
  font-weight: 700;
  padding: clamp(10px, 2.6vw, 12px) clamp(18px, 5.5vw, 26px);
  border-radius: var(--iv-radius-pill, 999px);
  cursor: pointer;
  border: 1px solid rgba(255,255,255,0.12);
  transition: all 0.18s var(--iv-ease, ease);
  white-space: nowrap;
  letter-spacing: 0.005em;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-width: 112px;
  justify-content: center;
}
.iv-radio-globe-root .ivr-auth-prompt-btn .hint {
  font-size: 10px;
  font-weight: 700;
  padding: 2px 7px;
  border-radius: var(--iv-radius-pill, 999px);
  background: rgba(0,0,0,0.18);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  opacity: 0.7;
}
.iv-radio-globe-root .ivr-auth-prompt-btn.primary {
  background: linear-gradient(135deg, var(--iv-yellow, #f7d714) 0%, #ffd84a 100%);
  color: #000;
  border-color: var(--iv-yellow, #f7d714);
  box-shadow: 0 6px 18px rgba(247,215,20,0.35), 0 0 0 1px rgba(247,215,20,0.4) inset;
}
.iv-radio-globe-root .ivr-auth-prompt-btn.primary:hover {
  transform: translateY(-1px);
  box-shadow: 0 10px 26px rgba(247,215,20,0.42), 0 0 0 1px rgba(247,215,20,0.55) inset;
}
.iv-radio-globe-root .ivr-auth-prompt-btn.ghost {
  background: rgba(255,255,255,0.06);
  color: var(--ivr-ink);
}
.iv-radio-globe-root .ivr-auth-prompt-btn.ghost:hover {
  background: rgba(255,255,255,0.12);
  border-color: rgba(255,255,255,0.22);
}
/* × close — founder 2026-05-28: "x is breaking the flow". Now a
   ghost dismiss in the top-right corner: no circle background, no
   border, just a 14px stroke that fades into the card. Hit-area
   stays 32px (HIG-friendly tap target) via padding even though the
   visual mark is tiny. Hover/active reveals a subtle backdrop. */
.iv-radio-globe-root .ivr-auth-prompt-close {
  position: absolute;
  top: 8px;
  right: 8px;
  background: transparent;
  border: 0;
  color: rgba(255, 255, 255, 0.35);
  width: 32px;
  height: 32px;
  border-radius: 50%;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  transition: color 0.15s ease, background 0.15s ease;
  z-index: 1;
}
.iv-radio-globe-root .ivr-auth-prompt-close:hover,
.iv-radio-globe-root .ivr-auth-prompt-close:focus-visible {
  color: rgba(255, 255, 255, 0.85);
  background: rgba(255, 255, 255, 0.06);
  outline: none;
}
/* Mobile: anchor above the player, slightly tighter padding. Fluid type
   above already handles the size scaling — these overrides just lock the
   card's position + spacing for narrow viewports. */
@media (max-width: 640px) {
  .iv-radio-globe-root .ivr-auth-prompt {
    /* Player chip on mobile is ~180px tall when visible; sit just above */
    bottom: calc(env(safe-area-inset-bottom) + 200px);
    left: 12px;
    right: 12px;
    transform: translateY(20px);
    width: auto;
  }
  .iv-radio-globe-root .ivr-auth-prompt.show { transform: translateY(0); }
  .iv-radio-globe-root .ivr-auth-prompt-card { padding: 22px 18px 18px; border-radius: 20px; }
  .iv-radio-globe-root .ivr-auth-prompt-heart { width: 46px; height: 46px; margin-bottom: 12px; }
  .iv-radio-globe-root .ivr-auth-prompt-actions { gap: 10px; }
  .iv-radio-globe-root .ivr-auth-prompt-btn { min-width: 0; flex: 1 1 auto; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   Inline Auth Sheet — keeps the music playing on iOS Safari
   ═══════════════════════════════════════════════════════════════════════════
   Founder direction 2026-05-28: "when click fav new tab opened. Music
   stopped. How to keep it running."

   Root cause: iOS Safari backgrounds the original tab whenever window.open
   spawns a new tab, and aggressively pauses HTML5 <audio> in backgrounded
   tabs. There is no JS workaround for that platform behaviour.

   Fix: never open a new tab. Show /auth/register (or /auth/login) inside
   an iframe overlay ON THE SAME PAGE. The audio element keeps playing
   because the page itself never unloads, never backgrounds. Same-origin
   iframe is enabled by:
   • proxy.ts CSP frame-ancestors 'self' for /auth/*
   • next.config.ts X-Frame-Options: SAMEORIGIN for /auth/*

   ───────────────────────────────────────────────────────────────────────── */
.iv-radio-globe-root .ivr-auth-sheet {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
  animation: ivrSheetFade 0.2s ease;
}
.iv-radio-globe-root .ivr-auth-sheet[hidden] { display: none; }
@keyframes ivrSheetFade {
  from { opacity: 0; }
  to   { opacity: 1; }
}
.iv-radio-globe-root .ivr-auth-sheet-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(6, 5, 13, 0.82);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  cursor: pointer;
}
.iv-radio-globe-root .ivr-auth-sheet-panel {
  position: relative;
  width: min(560px, calc(100vw - 24px));
  height: min(760px, calc(100dvh - 80px));
  background: var(--iv-surface, #100a23);
  border: 1px solid rgba(255,255,255,0.10);
  border-radius: var(--iv-radius-xl, 18px);
  overflow: hidden;
  box-shadow: 0 30px 90px rgba(0,0,0,0.65), 0 0 0 1px rgba(255,255,255,0.04) inset;
  display: flex;
  flex-direction: column;
  animation: ivrSheetSlide 0.34s cubic-bezier(0.2, 0.9, 0.2, 1);
}
@keyframes ivrSheetSlide {
  from { transform: translateY(16px); opacity: 0; }
  to   { transform: translateY(0);    opacity: 1; }
}
.iv-radio-globe-root .ivr-auth-sheet-header {
  flex: 0 0 auto;
  padding: 14px 16px 12px;
  border-bottom: 1px solid rgba(255,255,255,0.06);
  display: flex;
  align-items: center;
  gap: 12px;
  background: linear-gradient(180deg, rgba(247,215,20,0.04), transparent);
}
.iv-radio-globe-root .ivr-auth-sheet-title {
  min-width: 0;
  flex: 1 1 auto;
}
.iv-radio-globe-root .ivr-auth-sheet-title strong {
  display: block;
  font-size: 13.5px;
  font-weight: 700;
  color: var(--ivr-ink);
  letter-spacing: -0.01em;
}
.iv-radio-globe-root .ivr-auth-sheet-title .sub {
  display: block;
  font-size: 10.5px;
  color: var(--ivr-dim);
  margin-top: 2px;
  font-weight: 500;
}
.iv-radio-globe-root .ivr-auth-sheet-title .sub .live-dot {
  display: inline-block;
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--iv-yellow, #f7d714);
  box-shadow: 0 0 8px var(--iv-yellow, #f7d714);
  animation: ivrPulseDot 1.4s ease-in-out infinite;
  margin-right: 6px;
  vertical-align: 1px;
}
.iv-radio-globe-root .ivr-auth-sheet-tabs {
  display: flex;
  gap: 2px;
  background: rgba(255,255,255,0.04);
  padding: 3px;
  border-radius: var(--iv-radius-pill);
  flex: 0 0 auto;
}
.iv-radio-globe-root .ivr-auth-sheet-tab {
  background: transparent;
  border: 0;
  color: var(--ivr-dim);
  font: inherit;
  font-size: 11.5px;
  font-weight: 600;
  padding: 6px 12px;
  border-radius: var(--iv-radius-pill);
  cursor: pointer;
  transition: all var(--iv-duration-sm, 0.18s) var(--iv-ease, ease);
  white-space: nowrap;
}
.iv-radio-globe-root .ivr-auth-sheet-tab.active {
  background: var(--iv-yellow, #f7d714);
  color: #000;
  box-shadow: 0 2px 10px rgba(247,215,20,0.25);
}
.iv-radio-globe-root .ivr-auth-sheet-close {
  background: rgba(255,255,255,0.06);
  border: 1px solid rgba(255,255,255,0.1);
  color: var(--ivr-ink);
  width: 30px;
  height: 30px;
  border-radius: 50%;
  cursor: pointer;
  font-size: 18px;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  transition: background 0.15s ease;
}
.iv-radio-globe-root .ivr-auth-sheet-close:hover {
  background: rgba(255,255,255,0.12);
}
.iv-radio-globe-root .ivr-auth-sheet-iframe {
  flex: 1 1 auto;
  border: 0;
  width: 100%;
  height: 100%;
  background: var(--iv-surface, #100a23);
  display: block;
}
.iv-radio-globe-root .ivr-auth-sheet-loading {
  position: absolute;
  inset: 60px 0 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  color: var(--ivr-dim);
  font-size: 12px;
  pointer-events: none;
  background: var(--iv-surface, #100a23);
  z-index: 1;
  transition: opacity 0.3s ease;
}
.iv-radio-globe-root .ivr-auth-sheet-loading.gone {
  opacity: 0;
  pointer-events: none;
}
.iv-radio-globe-root .ivr-auth-sheet-loading .orbit {
  width: 36px; height: 36px;
  border-radius: 50%;
  border: 2px solid rgba(255,255,255,0.08);
  border-top-color: var(--iv-yellow, #f7d714);
  border-right-color: var(--iv-purple-l, #a78bfa);
  animation: ivrSpin 1.2s linear infinite;
}
/* Mobile: full-screen sheet — feels native, no awkward floating panel */
@media (max-width: 640px) {
  .iv-radio-globe-root .ivr-auth-sheet-panel {
    width: 100vw;
    height: 100vh;
    height: 100dvh;            /* dynamic viewport h on Safari iOS */
    border-radius: 0;
    border: 0;
  }
  .iv-radio-globe-root .ivr-auth-sheet-header {
    padding: calc(env(safe-area-inset-top) + 12px) 14px 10px;
  }
  .iv-radio-globe-root .ivr-auth-sheet-title strong { font-size: 13px; }
  .iv-radio-globe-root .ivr-auth-sheet-title .sub { font-size: 10px; }
  .iv-radio-globe-root .ivr-auth-sheet-tab { padding: 6px 10px; font-size: 11px; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   Phase 2 — Web Audio reactivity (amplitude-driven pulse)
   ═══════════════════════════════════════════════════════════════════════════
   --ivr-amp (0–1) and --ivr-bass (0–1) are set by an FFT loop in app.js
   while a station is playing. The playing pin's outer ripple breathes in
   step with the music; the player chip glow brightens on bass kicks.
   Soft, premium — never strobing, never jarring. Disable via
   iv-no-reactivity class (a11y / epilepsy safety per Rohan PDF). */
.iv-radio-globe-root .pin.playing {
  --ivr-amp:  0;
  --ivr-bass: 0;
}
.iv-radio-globe-root .pin.playing::after {
  /* Inherit existing ripple (ivrPlayingRipple) but ADD an amplitude scalar.
     CSS scale composes multiplicatively — 1 + amp×0.6 means the pulse halo
     opens an extra 60 % at peak. */
  --ivr-pulse-extra: calc(1 + var(--ivr-amp) * 0.55);
  transform: translate(-50%, -50%) scale(var(--ivr-pulse-extra));
}
.iv-radio-globe-root .ivr-player.visible.playing {
  /* Bass kicks intensify the yellow glow around the player chip. */
  box-shadow:
    var(--iv-shadow-lg, 0 18px 50px rgba(0,0,0,0.55)),
    0 0 calc(28px + var(--ivr-bass, 0) * 60px) rgba(247, 215, 20, calc(0.10 + var(--ivr-bass, 0) * 0.18));
  transition: box-shadow 80ms linear;
}
.iv-radio-globe-root.iv-no-reactivity .pin.playing::after,
html.iv-no-reactivity .iv-radio-globe-root .pin.playing::after {
  transform: translate(-50%, -50%) scale(1);
}
html.iv-no-reactivity .iv-radio-globe-root .ivr-player.visible.playing {
  box-shadow: var(--iv-shadow-lg, 0 18px 50px rgba(0,0,0,0.55));
}

/* ═══════════════════════════════════════════════════════════════════════════
   Phase 4 — Low-end / mobile graceful degrade
   ═══════════════════════════════════════════════════════════════════════════
   Devices with ≤4 cores on a phone viewport (or ≤2 GB deviceMemory hint)
   get this class on <html>. We shed the heaviest visual effects: the
   playing-pin ripple animation, the player-chip outer glow, and the
   blurred backdrops on prompt cards. Catalog growth + low-end + 30+ FPS
   target = these go first. Music quality, controls, list, and tap targets
   are unaffected — only the polish layer dims. */
html.iv-low-end .iv-radio-globe-root .pin.playing::after { animation: none; opacity: 0.35; }
html.iv-low-end .iv-radio-globe-root .ivr-player { backdrop-filter: none; -webkit-backdrop-filter: none; }
html.iv-low-end .iv-radio-globe-root .ivr-genres { -webkit-mask-image: none; mask-image: none; }
html.iv-low-end .iv-radio-globe-root .ivr-auth-prompt-card,
html.iv-low-end .iv-radio-globe-root .ivr-auth-sheet-backdrop {
  backdrop-filter: none; -webkit-backdrop-filter: none;
}

/* ─── Contribute v2 — non-blocking drawer (2026-05-30 PM redesign) ──────────
   Founder feedback on v1 modal: "Its a popup. Really bad experience. I was
   seeing a simple button on the player. Clean and beautiful."

   v2 architecture:
   • Tiny heart button INLINE in player controls (.ivr-btn-contrib)
   • Mobile: bottom sheet (slides up, ~75vh max, music + globe stay visible
     above it, swipe-down or tap-outside dismisses)
   • Desktop: floating card BOTTOM-RIGHT 400px wide, doesn't block globe
   • No backdrop dim, no z-index battle with player
   • Animation: spring slide-in, snap closed
*/

/* Player-inline Contribute heart button — same size as other player btns */
/* v2.4 (2026-05-30 night) — match heart + share visual language.
   Color inherits from the button (white, like its siblings); the
   subtle iV-yellow only appears on hover to signal "contribute". */
.iv-radio-globe-root .ivr-btn-contrib svg { opacity: 0.85; }
.iv-radio-globe-root .ivr-btn-contrib:hover svg { color: #F7D714; opacity: 1; }

/* The drawer/card itself */
.iv-radio-globe-root .ivr-contrib-drawer {
  position: fixed;
  z-index: 1000;
  background: linear-gradient(180deg, #0f0b22 0%, #0a0717 100%);
  color: #fff;
  border: 1px solid rgba(247,215,20,0.18);
  box-shadow: 0 24px 80px rgba(0,0,0,0.5), 0 0 0 1px rgba(247,215,20,0.06) inset;
  font-family: inherit;
  /* desktop default: floating card bottom-right */
  width: 380px;
  bottom: 96px;            /* above the player chip */
  right: 16px;
  border-radius: 18px;
  padding: 16px 18px 14px;
  transform: translateY(20px); opacity: 0;
  transition: transform 0.24s cubic-bezier(0.2, 0.9, 0.3, 1), opacity 0.18s ease;
  pointer-events: none;
}
.iv-radio-globe-root .ivr-contrib-drawer.open {
  transform: translateY(0); opacity: 1;
  pointer-events: auto;
}
/* Mobile: bottom sheet */
@media (max-width: 720px) {
  .iv-radio-globe-root .ivr-contrib-drawer {
    width: auto;
    left: 0; right: 0;
    bottom: 0;
    border-radius: 18px 18px 0 0;
    padding: 8px 18px max(18px, env(safe-area-inset-bottom));
    transform: translateY(100%);
    border-bottom: 0;
  }
  .iv-radio-globe-root .ivr-contrib-drawer.open { transform: translateY(0); }
}

.iv-radio-globe-root .ivr-contrib-handle {
  display: none;  /* mobile only */
  width: 36px; height: 4px;
  background: rgba(255,255,255,0.18);
  border-radius: 2px;
  margin: 6px auto 8px;
}
@media (max-width: 720px) {
  .iv-radio-globe-root .ivr-contrib-handle { display: block; }
}

.iv-radio-globe-root .ivr-contrib-head {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 6px;
}
.iv-radio-globe-root .ivr-contrib-head h3 {
  font-size: 15px; font-weight: 700; margin: 0;
  color: #F7D714; letter-spacing: -0.005em;
}
.iv-radio-globe-root .ivr-contrib-x {
  width: 28px; height: 28px; border-radius: 50%;
  background: rgba(255,255,255,0.06); color: rgba(255,255,255,0.7);
  border: 0; cursor: pointer; display: grid; place-items: center;
  transition: background 0.15s, color 0.15s;
}
.iv-radio-globe-root .ivr-contrib-x:hover {
  background: rgba(255,255,255,0.12); color: #fff;
}
/* v2.9 — refined Contribute blurb. Lead phrase gets the yellow accent so
   the "initiative for an ever-growing ecosystem" framing reads as the
   primary message; the supporting line stays muted. Tightened line-height
   for the two-line treatment. */
.iv-radio-globe-root .ivr-contrib-blurb {
  font-size: 12.5px; line-height: 1.5;
  color: rgba(255,255,255,0.62); margin: 0 0 14px;
  letter-spacing: 0.005em;
}
.iv-radio-globe-root .ivr-contrib-blurb-lead {
  display: block;
  color: var(--iv-yellow);
  font-weight: 600;
  font-size: 12.5px;
  letter-spacing: 0.015em;
  margin-bottom: 2px;
}

/* BIG amount row — currency symbol + number input */
.iv-radio-globe-root .ivr-contrib-amount-row {
  display: flex; align-items: center; gap: 6px;
  background: rgba(255,255,255,0.04);
  border: 1px solid rgba(247,215,20,0.16);
  border-radius: 12px;
  padding: 10px 14px;
  margin-bottom: 10px;
  transition: border-color 0.15s;
}
.iv-radio-globe-root .ivr-contrib-amount-row:focus-within {
  border-color: #F7D714;
}
.iv-radio-globe-root .ivr-contrib-currency {
  font-size: 22px; font-weight: 700; color: #F7D714;
  font-variant-numeric: tabular-nums;
}
.iv-radio-globe-root .ivr-contrib-amount-row input {
  flex: 1; min-width: 0;
  background: transparent; border: 0; outline: 0;
  color: #fff; font-size: 22px; font-weight: 700;
  font-variant-numeric: tabular-nums;
  font-family: inherit;
  -moz-appearance: textfield;
}
.iv-radio-globe-root .ivr-contrib-amount-row input::-webkit-outer-spin-button,
.iv-radio-globe-root .ivr-contrib-amount-row input::-webkit-inner-spin-button {
  -webkit-appearance: none; margin: 0;
}

/* Quick-amount chips */
.iv-radio-globe-root .ivr-contrib-chips {
  display: flex; gap: 6px; margin-bottom: 12px; flex-wrap: wrap;
}
.iv-radio-globe-root .ivr-contrib-chip {
  background: rgba(255,255,255,0.05); color: rgba(255,255,255,0.75);
  border: 1px solid rgba(255,255,255,0.1);
  border-radius: 999px;
  padding: 6px 12px; font-size: 11.5px; font-weight: 600;
  cursor: pointer; font-family: inherit;
  transition: all 0.12s ease;
}
.iv-radio-globe-root .ivr-contrib-chip:hover {
  background: rgba(247,215,20,0.1); border-color: rgba(247,215,20,0.35);
  color: #fff;
}
.iv-radio-globe-root .ivr-contrib-chip.on {
  background: #F7D714; border-color: #F7D714; color: #0a0717;
}

/* Rails — two equal buttons */
.iv-radio-globe-root .ivr-contrib-rails {
  display: grid; grid-template-columns: 1fr 1fr; gap: 8px;
}
.iv-radio-globe-root .ivr-contrib-rail {
  display: flex; align-items: center; gap: 9px;
  padding: 10px 12px; border-radius: 11px;
  background: rgba(255,255,255,0.05);
  color: #fff; border: 1px solid rgba(255,255,255,0.1);
  cursor: pointer; text-align: left;
  font-family: inherit; transition: all 0.12s ease;
}
.iv-radio-globe-root .ivr-contrib-rail:hover:not(:disabled) {
  background: rgba(247,215,20,0.1); border-color: rgba(247,215,20,0.4);
}
.iv-radio-globe-root .ivr-contrib-rail:disabled { opacity: 0.5; cursor: wait; }
.iv-radio-globe-root .ivr-contrib-glyph {
  width: 30px; height: 30px; border-radius: 50%;
  background: rgba(247,215,20,0.22); color: #F7D714;
  display: grid; place-items: center;
  font-weight: 700; font-size: 15px;
  flex: 0 0 30px;
}
.iv-radio-globe-root .ivr-contrib-rail strong { display: block; font-size: 12px; line-height: 1.2; }
.iv-radio-globe-root .ivr-contrib-rail small {
  display: block; font-size: 10px;
  color: rgba(255,255,255,0.5); margin-top: 1px;
}

/* QR panel — desktop UPI flow */
.iv-radio-globe-root .ivr-contrib-qr {
  margin-top: 12px; padding: 14px;
  background: #fff; border-radius: 12px;
  text-align: center;
}
.iv-radio-globe-root .ivr-contrib-qr-svg svg {
  width: 100%; max-width: 240px; height: auto; display: block; margin: 0 auto;
}
.iv-radio-globe-root .ivr-contrib-qr-cap {
  font-size: 11px; color: #0a0717; margin: 8px 0 0; font-weight: 600;
}

.iv-radio-globe-root .ivr-contrib-error {
  font-size: 12px; color: #ff9a9a;
  background: rgba(255,80,80,0.08); border-radius: 8px;
  padding: 8px 10px; margin: 10px 0 0;
}
.iv-radio-globe-root .ivr-contrib-limits {
  font-size: 10.5px;
  color: rgba(255,255,255,0.4);
  margin: -4px 0 10px;
  text-align: center;
  letter-spacing: 0.2px;
}
.iv-radio-globe-root .ivr-contrib-limits span {
  color: rgba(247,215,20,0.7); font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* ─── v2.2 (2026-05-30 night) — "I have paid" attestation flow ──────
   Drawer becomes a 4-view state machine: entry → paying → attest →
   thanks. Each ivr-contrib-view is hidden by default; JS toggles the
   drawer's data-view attribute to reveal one view at a time. */
.iv-radio-globe-root .ivr-contrib-view { display: none; }
.iv-radio-globe-root .ivr-contrib-drawer[data-view="entry"]  .ivr-contrib-view[data-view="entry"],
.iv-radio-globe-root .ivr-contrib-drawer[data-view="paying"] .ivr-contrib-view[data-view="paying"],
.iv-radio-globe-root .ivr-contrib-drawer[data-view="attest"] .ivr-contrib-view[data-view="attest"],
.iv-radio-globe-root .ivr-contrib-drawer[data-view="thanks"] .ivr-contrib-view[data-view="thanks"] {
  display: block;
}

/* Optional email — entry view. Same visual language as amount row. */
.iv-radio-globe-root .ivr-contrib-email-row {
  background: rgba(255,255,255,0.04);
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 11px;
  padding: 9px 12px;
  margin-bottom: 12px;
  transition: border-color 0.15s;
}
.iv-radio-globe-root .ivr-contrib-email-row:focus-within {
  border-color: rgba(247,215,20,0.45);
}
.iv-radio-globe-root .ivr-contrib-email-row input {
  width: 100%;
  background: transparent; border: 0; outline: 0;
  color: #fff; font-size: 12.5px;
  font-family: inherit;
}
.iv-radio-globe-root .ivr-contrib-email-row input::placeholder {
  color: rgba(255,255,255,0.4);
}

/* v2.21 — auto-fill UX for logged-in users (founder direction 2026-06-01):
   "for logged in users form fills should be automatic from the database."
   When the user is logged in (.auth-on class on root), hide the email
   input rows in both the Contribute drawer entry view AND the UPI
   attestation view. Server resolves email from session userId, so the
   field is redundant — and a redundant field is friction. */
.iv-radio-globe-root.auth-on .ivr-contrib-email-row,
.iv-radio-globe-root.auth-on .ivr-contrib-attest-field--email,
.iv-radio-globe-root.auth-on #iv-contribAttestEmail {
  display: none !important;
}
/* The Attest view groups label + input — if it's a parent label, hide
   the whole label too. Defensive selector for any wrapping structure. */
.iv-radio-globe-root.auth-on label[for="iv-contribAttestEmail"] {
  display: none !important;
}

/* "Paying" view — pay-blurb + I-have-paid CTA + cancel link */
.iv-radio-globe-root .ivr-contrib-pay-blurb {
  font-size: 12px; color: rgba(255,255,255,0.65);
  margin: 12px 0 10px; text-align: center; line-height: 1.4;
}

/* Primary CTA used on paying + attest views */
.iv-radio-globe-root .ivr-contrib-primary {
  width: 100%;
  display: inline-flex; align-items: center; justify-content: center;
  gap: 8px;
  background: #F7D714; color: #0a0717;
  border: 1px solid #F7D714;
  border-radius: 11px;
  padding: 11px 14px;
  font-size: 13px; font-weight: 700;
  cursor: pointer; font-family: inherit;
  transition: filter 0.12s ease, transform 0.06s ease;
  margin-top: 4px;
}
.iv-radio-globe-root .ivr-contrib-primary:hover:not(:disabled) {
  filter: brightness(1.05);
}
.iv-radio-globe-root .ivr-contrib-primary:active { transform: translateY(1px); }
.iv-radio-globe-root .ivr-contrib-primary:disabled { opacity: 0.55; cursor: wait; }

/* Inline ghost link — "Cancel / change amount", "Done" */
.iv-radio-globe-root .ivr-contrib-link {
  display: block; width: 100%;
  background: transparent; border: 0;
  color: rgba(255,255,255,0.55); font-family: inherit;
  font-size: 11.5px; text-align: center;
  padding: 10px 0 2px;
  cursor: pointer;
  text-decoration: underline; text-underline-offset: 3px;
}
.iv-radio-globe-root .ivr-contrib-link:hover { color: rgba(255,255,255,0.85); }

/* Attestation form fields */
.iv-radio-globe-root .ivr-contrib-field {
  margin-bottom: 10px;
}
.iv-radio-globe-root .ivr-contrib-field label {
  display: block;
  font-size: 11px; font-weight: 600;
  color: rgba(255,255,255,0.7);
  margin-bottom: 5px;
  letter-spacing: 0.15px;
}
.iv-radio-globe-root .ivr-contrib-optional {
  font-weight: 500;
  color: rgba(255,255,255,0.4);
  letter-spacing: 0;
}
.iv-radio-globe-root .ivr-contrib-field input {
  width: 100%;
  background: rgba(255,255,255,0.04);
  border: 1px solid rgba(255,255,255,0.1);
  border-radius: 10px;
  color: #fff; font-size: 13px;
  font-family: inherit;
  padding: 9px 11px;
  outline: 0;
  transition: border-color 0.12s;
}
.iv-radio-globe-root .ivr-contrib-field input:focus {
  border-color: rgba(247,215,20,0.55);
}
.iv-radio-globe-root .ivr-contrib-field input::placeholder {
  color: rgba(255,255,255,0.35);
}
.iv-radio-globe-root .ivr-contrib-field small {
  display: block;
  font-size: 10.5px; color: rgba(255,255,255,0.4);
  margin-top: 5px; line-height: 1.4;
}
.iv-radio-globe-root .ivr-contrib-fineprint {
  font-size: 10.5px; line-height: 1.45;
  color: rgba(255,255,255,0.4);
  margin: 10px 0 0;
}

/* Thanks view — soft tick + headline */
.iv-radio-globe-root .ivr-contrib-success {
  display: grid; place-items: center;
  width: 64px; height: 64px;
  margin: 4px auto 14px;
  border-radius: 50%;
  background: rgba(64, 220, 130, 0.14);
  color: #4adb8c;
}
.iv-radio-globe-root .ivr-contrib-thanks-h {
  text-align: center;
  font-size: 16px; font-weight: 700;
  color: #fff; margin: 0 0 6px;
  letter-spacing: 0.1px;
}

/* ─── v2.3 (2026-05-30 night) — UPI app chooser (mobile only) ────────
   The single upi:// deeplink on iOS surfaces only ONE app (whichever
   claimed the scheme first — for the founder, IDFC FIRST Digital
   Rupee). We show explicit app cards instead, each launching the
   right app via its registered scheme. Desktop hides this block and
   uses the QR — desktop users pay by scanning with their phone. */
.iv-radio-globe-root .ivr-contrib-upi-picker {
  display: none; /* mobile-only — see @media below */
  margin: 0 0 14px;
}
.iv-radio-globe-root .ivr-contrib-upi-apps {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
  margin: 8px 0;
}
.iv-radio-globe-root .ivr-contrib-upi-app {
  display: flex; align-items: center; gap: 10px;
  padding: 11px 12px;
  background: rgba(255,255,255,0.05);
  border: 1px solid rgba(255,255,255,0.1);
  border-radius: 11px;
  cursor: pointer;
  font-family: inherit;
  color: #fff;
  text-align: left;
  transition: all 0.12s ease;
  min-height: 44px; /* iOS tap target */
}
.iv-radio-globe-root .ivr-contrib-upi-app:hover,
.iv-radio-globe-root .ivr-contrib-upi-app:active {
  background: rgba(247,215,20,0.1);
  border-color: rgba(247,215,20,0.4);
}
.iv-radio-globe-root .ivr-contrib-upi-glyph {
  width: 28px; height: 28px; border-radius: 50%;
  display: grid; place-items: center;
  font-size: 12px; font-weight: 700;
  flex: 0 0 28px;
  /* Neutral background — we keep brand colours subtle so we don't
     misrepresent third-party brands. iV-yellow accent on hover instead. */
  background: rgba(255,255,255,0.08);
  color: rgba(255,255,255,0.92);
  letter-spacing: -0.2px;
}
/* Subtle per-app tint (very low opacity — readable but neutral) */
.iv-radio-globe-root .ivr-contrib-upi-glyph[data-app="gpay"]    { background: rgba(255,255,255,0.14); }
.iv-radio-globe-root .ivr-contrib-upi-glyph[data-app="phonepe"] { background: rgba(133, 95, 215, 0.28); }
.iv-radio-globe-root .ivr-contrib-upi-glyph[data-app="paytm"]   { background: rgba(0, 186, 242, 0.24); }
.iv-radio-globe-root .ivr-contrib-upi-glyph[data-app="bhim"]    { background: rgba(247, 138, 13, 0.24); }
.iv-radio-globe-root .ivr-contrib-upi-glyph[data-app="other"]   { background: rgba(247,215,20,0.18); color: #F7D714; }
.iv-radio-globe-root .ivr-contrib-upi-name {
  font-size: 12.5px; font-weight: 600;
  color: #fff;
  letter-spacing: 0.1px;
}
.iv-radio-globe-root .ivr-contrib-upi-hint {
  font-size: 10.5px; line-height: 1.4;
  color: rgba(255,255,255,0.4);
  margin: 6px 0 0; text-align: center;
}
/* MOBILE: show app picker, hide QR. The QR is desktop-only because
   scanning your own phone screen with your own phone is pointless. */
@media (max-width: 720px) {
  .iv-radio-globe-root .ivr-contrib-upi-picker { display: block; }
  .iv-radio-globe-root .ivr-contrib-drawer[data-view="paying"] .ivr-contrib-qr { display: none; }
}

/* ╔══════════════════════════════════════════════════════════════════════════╗
   ║  SYNC SOUND — Listen Together Phase 2 (2026-06-02)                        ║
   ║                                                                            ║
   ║  Loaded for every render, but the DOM nodes only exist when the           ║
   ║  IV_RADIO_SYNC_SOUND_ENABLED server flag is true. Styles are scoped       ║
   ║  to .iv-radio-globe-root so they cannot leak to any other page.           ║
   ║  Follows the Contribute drawer's bottom-sheet / floating-card pattern     ║
   ║  for visual consistency.                                                  ║
   ╚══════════════════════════════════════════════════════════════════════════╝ */

/* ── Sync button on the player chip ──────────────────────────────────────── */
.iv-radio-globe-root .ivr-btn-sync {
  position: relative;
  opacity: 0.55;
  transition: opacity 0.15s, box-shadow 0.18s;
}
.iv-radio-globe-root .ivr-btn-sync:hover,
.iv-radio-globe-root .ivr-btn-sync:focus-visible {
  opacity: 1;
  box-shadow: 0 0 0 2px rgba(247,215,20,0.35);
}
.iv-radio-globe-root .ivr-btn-sync.on {
  opacity: 1;
  color: #F7D714;
}
.iv-radio-globe-root .ivr-btn-sync.on svg {
  filter: drop-shadow(0 0 6px rgba(247,215,20,0.5));
}
.iv-radio-globe-root .ivr-btn-sync-badge {
  position: absolute;
  top: -4px; right: -6px;
  min-width: 22px;
  padding: 1px 5px;
  font-size: 9.5px;
  font-weight: 700;
  line-height: 1.4;
  color: #0a0717;
  background: #F7D714;
  border-radius: 9px;
  border: 1.5px solid #0a0717;
  letter-spacing: 0.02em;
  pointer-events: none;
}

/* ── Sync drawer (reuses ivr-contrib-drawer base styles) ─────────────────── */
.iv-radio-globe-root .ivr-sync-view {
  display: none;
}
.iv-radio-globe-root .ivr-sync-drawer[data-view="off"]    .ivr-sync-view[data-view="off"],
.iv-radio-globe-root .ivr-sync-drawer[data-view="active"] .ivr-sync-view[data-view="active"],
.iv-radio-globe-root .ivr-sync-drawer[data-view="nudge"]  .ivr-sync-view[data-view="nudge"] {
  display: block;
}

.iv-radio-globe-root .ivr-sync-code-row {
  text-align: center;
  margin: 6px 0 10px;
}
.iv-radio-globe-root .ivr-sync-code {
  display: inline-block;
  font-family: 'SFMono-Regular', Menlo, Consolas, monospace;
  font-size: 22px;
  font-weight: 700;
  letter-spacing: 0.12em;
  color: #F7D714;
  padding: 6px 12px;
  background: rgba(247,215,20,0.08);
  border: 1px solid rgba(247,215,20,0.22);
  border-radius: 10px;
}
.iv-radio-globe-root .ivr-sync-actions {
  display: flex;
  gap: 8px;
  justify-content: center;
  margin-bottom: 12px;
}
.iv-radio-globe-root .ivr-sync-action {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 6px 12px;
  font-size: 12px;
  font-weight: 600;
  color: rgba(255,255,255,0.86);
  background: rgba(255,255,255,0.06);
  border: 1px solid rgba(255,255,255,0.12);
  border-radius: 8px;
  cursor: pointer;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.iv-radio-globe-root .ivr-sync-action:hover {
  background: rgba(247,215,20,0.12);
  color: #F7D714;
  border-color: rgba(247,215,20,0.3);
}

.iv-radio-globe-root .ivr-sync-qr {
  display: flex;
  justify-content: center;
  margin: 8px 0 12px;
  min-height: 200px;
}
.iv-radio-globe-root .ivr-sync-qr img {
  border-radius: 12px;
  background: #0a0717;
  padding: 6px;
  display: block;
  max-width: 200px;
  height: auto;
}

.iv-radio-globe-root .ivr-sync-count {
  font-size: 12px;
  color: rgba(255,255,255,0.62);
  text-align: center;
  margin: 4px 0 10px;
}
.iv-radio-globe-root .ivr-sync-count #iv-syncCount {
  color: #F7D714;
  font-weight: 700;
}

.iv-radio-globe-root .ivr-sync-members {
  list-style: none;
  padding: 0;
  margin: 0 0 12px;
  max-height: 160px;
  overflow-y: auto;
}
.iv-radio-globe-root .ivr-sync-member {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 8px;
  border-radius: 8px;
  font-size: 12.5px;
  color: rgba(255,255,255,0.78);
}
.iv-radio-globe-root .ivr-sync-member + .ivr-sync-member {
  margin-top: 2px;
}
.iv-radio-globe-root .ivr-sync-member-dot {
  width: 8px; height: 8px;
  border-radius: 50%;
  background: #4ade80;
  box-shadow: 0 0 6px rgba(74,222,128,0.6);
  flex-shrink: 0;
}
.iv-radio-globe-root .ivr-sync-member-name {
  flex: 1;
}
.iv-radio-globe-root .ivr-sync-kick {
  font-size: 10.5px;
  font-weight: 600;
  color: rgba(255,255,255,0.5);
  background: transparent;
  border: 1px solid rgba(255,255,255,0.15);
  border-radius: 6px;
  padding: 2px 8px;
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s;
}
.iv-radio-globe-root .ivr-sync-kick:hover {
  color: #ff6b6b;
  border-color: rgba(255,107,107,0.4);
}

.iv-radio-globe-root .ivr-sync-end,
.iv-radio-globe-root .ivr-sync-leave {
  width: 100%;
}
.iv-radio-globe-root .ivr-sync-end {
  background: linear-gradient(180deg, rgba(255,107,107,0.18), rgba(255,107,107,0.06));
  border-color: rgba(255,107,107,0.4);
  color: #ff8a8a;
}

/* ── Non-blocking toast ──────────────────────────────────────────────────── */
.ivr-sync-toast {
  position: fixed;
  bottom: 24px;
  left: 50%;
  transform: translateX(-50%) translateY(8px);
  padding: 8px 16px;
  font-size: 12.5px;
  font-weight: 600;
  color: #0a0717;
  background: #F7D714;
  border-radius: 18px;
  box-shadow: 0 8px 24px rgba(0,0,0,0.4);
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.2s, transform 0.2s;
  z-index: 1200;
}
.ivr-sync-toast.on {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}
