/* ============================================================================
   Radio Arcade — shared interaction & contrast polish
   ----------------------------------------------------------------------------
   Additive layer loaded AFTER the theme CSS in both layouts. It does NOT
   restyle a theme's personality (bevels, glows, hard shadows, corners). It
   layers the interaction and robustness the four themes share in common:

     • prefers-reduced-motion — kill non-essential motion.
     • opaque surfaces — keep the full-page visualizer canvas from bleeding
       through cards/panels and hurting text contrast.
     • request/loading feedback for htmx swaps (no spinners, just dim).
     • consistent press + focus affordances, guarded to pointer/hover devices.
     • explanatory empty states.

   Consumes a small token contract each theme defines in its :root:
     --pol-accent / --pol-accent-soft
     --pol-surface / --pol-surface-2
     --pol-text  / --pol-muted / --pol-border
     --pol-radius-sm / -md / -lg
     --pol-motion-fast / -mid
   ============================================================================ */

/* --- Respect users who prefer reduced motion ------------------------------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
  .station-card:hover,
  body.radio-mode .radio-station:hover,
  body.radio-mode .radio-prev:hover { transform: none; }
}

/* --- Opaque surfaces: keep the visualizer out of reading surfaces ----------- */
/* The canvas is fixed behind the app; these are the content-bearing panels.
   Forcing an opaque background (per theme token) guarantees the canvas cannot
   bleed through and drop text contrast. TV mode is exempt — it is a deliberate
   fullscreen immersive surface that wants the visualizer behind it. */
body:not(.tv-mode) .player,
body.radio-mode .radio-player,
body:not(.tv-mode) .player-display,
body.radio-mode .radio-player-display,
body:not(.tv-mode) .atc-controls,
body.radio-mode .radio-station,
.station-card,
.search-results,
.search-unavailable,
body:not(.tv-mode) table.tracks,
.stat-card {
  background: var(--pol-surface, var(--bg));
}

/* Inset "display well" surfaces read slightly differently than outer cards so
   the now-playing readout sits comfortably inside the player panel. */
body.radio-mode .radio-player-display,
body:not(.tv-mode) .player-display,
body:not(.tv-mode) .atc-controls {
  background: var(--pol-surface-2, var(--pol-surface, var(--bg)));
}

/* ----------------------------------------------------------------------------
   Loading / request feedback (htmx). htmx tags the initiating element with
   .htmx-request during a swap. We use a dim + gentle pulse instead of a
   spinner — no JS, no layout shift, and it reads as "this card is working".
   ---------------------------------------------------------------------------- */
.radio-station.htmx-request,
.btn.htmx-request,
.vbtn.htmx-request,
.station-card.htmx-request,
.track-row .btn.htmx-request {
  opacity: .55;
  pointer-events: none;
  cursor: wait;
  transform: translateY(1px);
  animation: pol-dim 1.1s ease-in-out infinite;
}

/* Keep active-station highlight readable while a neighbouring request runs. */
body.radio-mode .radio-station.active.htmx-request { opacity: .7; }

@keyframes pol-dim {
  0%, 100% { opacity: .55; }
  50%      { opacity: .32; }
}

/* ----------------------------------------------------------------------------
   Consistent, subtle press + hover for the tactile elements, guarded so it only
   applies on devices with a pointer/hover. Each theme keeps its own colour; we
   only unify the *direction* of the micro-interaction so the app feels like one
   product across the four skins.
   ---------------------------------------------------------------------------- */
@media (hover: hover) and (pointer: fine) {
  .btn:not(:disabled),
  .vbtn:not(:disabled),
  .radio-station:not(:disabled),
  .station-card,
  .tv-channel,
  .stat-card {
    transition: transform .12s ease, box-shadow .15s ease, border-color .15s ease,
                background .15s ease, opacity .15s ease;
  }
  .btn:not(:disabled):hover,
  .vbtn:not(:disabled):hover {
    transform: translateY(-1px);
  }
  .btn:not(:disabled):active,
  .vbtn:not(:disabled):active {
    transform: translateY(1px);
  }
}

/* ----------------------------------------------------------------------------
   Consistent focus affordance across every theme. Each theme sets its own
   colour; we make sure keyboard focus is always visible and offset enough to
   read against panels and the canvas.
   ---------------------------------------------------------------------------- */
a:focus-visible,
button:focus-visible,
select:focus-visible,
input:focus-visible,
.radio-station:focus-visible,
.station-card:focus-visible,
.tv-channel:focus-visible {
  outline: 3px solid var(--pol-accent, currentColor);
  outline-offset: 2px;
}

/* ----------------------------------------------------------------------------
   Empty states — a little explanation beats a bare "nothing here".
   ---------------------------------------------------------------------------- */
.empty-state,
td[colspan] {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: .35rem;
  text-align: center;
  color: var(--pol-muted, inherit);
}
.empty-state::before {
  content: "◦ ◦ ◦";
  font-size: .9em;
  letter-spacing: .35em;
  opacity: .6;
  display: block;
}
.empty-state .empty-hint {
  font-size: .86em;
  opacity: .8;
  font-style: normal;
  max-width: 34ch;
}

/* Keep column spacer cells from stretching the table. */
td[colspan] {
  padding: 1.4rem 1rem;
  font-style: italic;
  font-size: .9em;
}

/* A lone empty state inside a station grid should span the whole row. */
.radio-station-grid .empty-state,
.station-grid .empty-state {
  grid-column: 1 / -1;
}

/* ----------------------------------------------------------------------------
   Scoped corrective bits that improve cohesion without a second visual language.
   ---------------------------------------------------------------------------- */

/* Player transport: give the play/pause toggle a slightly larger target and a
   comfortable hit area; aligns with the radio-mode play/pause look. */
.player-controls .pbtn-playpause,
body.radio-mode .radio-playpause {
  touch-action: manipulation;
}

/* ATC status line reads like a live LED; keep tabular digits steady. */
#atc-status { font-variant-numeric: tabular-nums; }

/* Footer metrics: keep right-aligned and steady width so the every-5s refresh
   doesn't reflow the footer. */
.footer-metrics { white-space: nowrap; }

/* ----------------------------------------------------------------------------
   Floating wall clock — a small, draggable, always-on-top time widget appended
   to <body> by player.js (moved out of the player panel). Compact retro style
   derived from the theme tokens so it matches whatever skin is active without
   a second visual language. Position is persisted in localStorage by JS.
   ---------------------------------------------------------------------------- */
#floating-clock {
  position: fixed;
  z-index: 9999;
  top: 0.75rem;
  left: 0.75rem;
  padding: .18rem .34rem;
  font-family: var(--pol-clock-mono, "VT323", "Courier New", "Lucida Console", "DejaVu Sans Mono", monospace);
  font-variant-numeric: tabular-nums;
  font-size: .95rem;
  line-height: 1;
  letter-spacing: .1em;
  color: var(--pol-text, var(--fg, #e8e8f0));
  background: var(--pol-surface, var(--bg, #1a1a2e));
  border: 1px solid var(--pol-border, var(--dim, #444));
  box-shadow: 0 0 0 1px var(--pol-accent-soft, transparent), 0 2px 8px rgba(0, 0, 0, .35);
  white-space: nowrap;
  user-select: none;
  touch-action: none;
  cursor: move;
  transition: opacity var(--pol-motion-fast, .1s) ease;
}
/* Keep the widget from blocking the whole UI; only the widget itself is hit. */
#floating-clock:active { cursor: grabbing; }
#floating-clock.dragging { opacity: .75; }

/* ----------------------------------------------------------------------------
   ATC airport dropdown polish
   ----------------------------------------------------------------------------
   The airport <select> is the one fiddly control in the chatter strip. Give it
   a deliberate, consistent appearance on top of whatever theme is active: a
   custom chevron, a readable height, a clear focus ring, and a distinct
   "dropdown open" state. Native <option> popup styling is browser-limited, so
   the <select> shell carries the personality and the live/offline glyphs (added
   by player.js) carry the state.
   ---------------------------------------------------------------------------- */
.atc-airport {
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  position: relative;
  font-family: inherit;
  font-size: .78rem;
  line-height: 1.2;
  padding: .34rem 1.6rem .34rem .6rem;
  max-width: 17rem;
  min-height: 1.7rem;
  cursor: pointer;
  border: 1px solid var(--pol-border, var(--dim));
  border-radius: var(--pol-radius-sm, 4px);
  color: var(--pol-text, var(--fg, #e8e8f0));
  background-color: var(--pol-surface-2, var(--pol-surface, var(--bg)));
  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' fill='none' stroke='%23888' stroke-width='1.5' stroke-linecap='round'/></svg>");
  background-repeat: no-repeat;
  background-position: right .55rem center;
  background-size: .55rem;
  transition: border-color var(--pol-motion-fast, .12s) ease, background-color var(--pol-motion-fast, .12s) ease, box-shadow var(--pol-motion-fast, .12s) ease;
}
/* Hover + focus affordances (guarded to pointer/hover devices). */
.atc-airport:hover { border-color: var(--pol-accent-soft, var(--accent, #7aa2f7)); }
.atc-airport:focus-visible {
  outline: none;
  border-color: var(--pol-accent, var(--accent, #7aa2f7));
  box-shadow: 0 0 0 2px var(--pol-accent-soft, rgba(122,162,247,.35));
}
.atc-airport:active { background-color: var(--pol-surface-2, var(--pol-surface, var(--bg))); }
.atc-airport:disabled {
  opacity: .5;
  cursor: not-allowed;
}

/* Dropdown popup: best-effort option styling. Backgrounds on <option> work on
   Chromium; color works broadly. Keep it readable and compact. */
.atc-airport option {
  font-size: .8rem;
  color: var(--pol-text, #e8e8f0);
  background-color: var(--pol-surface-2, var(--pol-surface, var(--bg)));
  padding: .2rem .4rem;
}

/* Live/offline glyphs injected by player.js read as a status; make the live
   dot the only colored bit so the row stays scannable. The glyphs are the
   unicode ● (U+25CF) and ○ (U+25CB) followed by LIVE/offline text. */
.atc-airport option {
  font-variant-numeric: tabular-nums;
}