/* =====================================================
   OKARA R — Components
   Match KaraRemoteHD visual: gradient tím-hồng-xanh + gold tabs + bottom controls
   ===================================================== */

/* ───────────────────────────────────
   STATUS BAR (iOS-style fake)
   ─────────────────────────────────── */
.status-bar {
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 22px 4px;
  font-size: 15px; font-weight: 600;
  color: var(--text-primary);
  letter-spacing: 0.02em;
}
.status-bar .right {
  display: flex; align-items: center; gap: 6px;
  font-size: 12px; opacity: 0.9;
}

/* ───────────────────────────────────
   TOP TOOLBAR — left pill (user / rec) + logo + search
   ─────────────────────────────────── */
.top-toolbar {
  display: flex; align-items: center; gap: 10px;
  padding: 6px 14px 12px;
}

/* Flex spacer pushes logo + search button hard against the right edge
   no matter how wide the left pill turns out. */
.toolbar-flex-spacer { flex: 1; min-width: 0; }
.top-toolbar.is-search-open .toolbar-flex-spacer { display: none; }

/* Left slot — back arrow, user pill, rec pill, or empty spacer. */
.toolbar-left {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  height: 36px;
  color: #fff;
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
}
.toolbar-left--spacer { width: 0; }
.top-toolbar.is-search-open .toolbar-left { display: none; }

/* Back arrow — small chevron button. Used by category/singer-detail. */
.back-arrow {
  width: 36px; height: 36px;
  justify-content: center;
}
.back-arrow svg { width: 22px; height: 22px; display: block; }

/* User pill: avatar + name. Tappable, leads to #/profile. */
.toolbar-user-pill {
  gap: 8px;
  padding: 2px 12px 2px 2px;
  background: rgba(255,255,255,0.12);
  border-radius: var(--radius-pill);
  max-width: 60vw;
  transition: background var(--duration-fast) var(--easing);
}
.toolbar-user-pill:active { background: rgba(255,255,255,0.22); }
.toolbar-user-avatar {
  width: 32px; height: 32px;
  border-radius: 50%;
  background: rgba(0,0,0,0.25);
  object-fit: cover;
  flex-shrink: 0;
  border: 1.5px solid rgba(255,255,255,0.45);
}
.toolbar-user-avatar--initials {
  display: grid; place-items: center;
  font-size: 12px; font-weight: 800;
  color: var(--okara-gold);
  background: rgba(0,0,0,0.35);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.toolbar-user-name {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: #fff;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 40vw;
}

/* Rec pill (guest): red blinking dot + THU ÂM. Leads to #/recordings. */
.toolbar-rec-pill {
  gap: 8px;
  padding: 0 14px 0 12px;
  background: rgba(230,57,70,0.18);
  border: 1px solid rgba(230,57,70,0.45);
  border-radius: var(--radius-pill);
  transition: background var(--duration-fast) var(--easing);
}
.toolbar-rec-pill:active { background: rgba(230,57,70,0.32); }
.toolbar-rec-dot {
  width: 10px; height: 10px;
  border-radius: 50%;
  background: #e63946;
  box-shadow: 0 0 0 2px rgba(230,57,70,0.35), 0 0 6px rgba(230,57,70,0.7);
  animation: toolbar-rec-blink 1.4s ease-in-out infinite;
}
.toolbar-rec-label {
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 0.08em;
  color: #fff;
}
@keyframes toolbar-rec-blink {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.35; }
}

/* Dual-purpose top-left action button (Profile / Recordings views).
   Live session → "Điều khiển" (return to control tab). Idle → "Quét QR"
   (open camera to pair). Visually distinct from user/REC pills so the
   operator immediately reads it as a context action, not an identity. */
.toolbar-action-pill {
  gap: 6px;
  padding: 0 12px;
  border-radius: var(--radius-pill);
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 0.04em;
  border: none;
  cursor: pointer;
  text-decoration: none;
  transition: background var(--duration-fast) var(--easing);
}
.toolbar-action-pill svg { display: block; }
.toolbar-action-label {
  white-space: nowrap;
}
/* Live session → soft gold pill, evokes the playback bar's gradient. */
.toolbar-action-pill--control {
  background: linear-gradient(135deg, rgba(255,216,61,0.95), rgba(217,124,10,0.92));
  color: #3a1a00;
  box-shadow: 0 2px 8px rgba(255,216,61,0.32);
}
.toolbar-action-pill--control:active {
  background: linear-gradient(135deg, rgba(255,216,61,1), rgba(217,124,10,1));
}
/* Idle (no live session) → neutral pill with QR icon, less attention-grabbing. */
.toolbar-action-pill--scan {
  background: rgba(255,255,255,0.14);
  color: #fff;
  border: 1px solid rgba(255,255,255,0.22);
}
.toolbar-action-pill--scan:active {
  background: rgba(255,255,255,0.24);
}

/* OKARA wordmark — now lives on the right, snug against the search btn. */
.top-brand-logo {
  flex: 0 0 auto;
  height: 26px;
  width: auto;
  object-fit: contain;
  user-select: none;
  -webkit-user-drag: none;
  filter: drop-shadow(0 1px 2px rgba(0,0,0,0.35));
}
.top-toolbar.is-search-open .top-brand-logo { display: none; }

/* Default toolbar state: input collapsed, only the magnifying-glass icon
   is visible at the right. Tapping the glass adds .is-search-open which
   reveals the inline input + close-X (left pill + logo hide to make room). */
.search-input-wrap {
  flex: 1;
  background: var(--search-bg);
  border-radius: var(--radius-pill);
  height: 40px;
  display: none;                 /* collapsed by default */
  align-items: center;
  padding: 0 6px 0 16px;
  box-shadow: 0 2px 8px rgba(0,0,0,0.25);
}
.top-toolbar.is-search-open .search-input-wrap { display: flex; }
.search-input-wrap input {
  flex: 1;
  background: transparent;
  border: none;
  outline: none;
  font-size: 15px;
  color: var(--search-text);
}
.search-input-wrap input::placeholder { color: rgba(0,0,0,0.4); }

/* Magnifying-glass button — collapsed default. Hidden when input is open. */
.search-glass-btn {
  width: 40px; height: 40px;
  background: var(--search-bg);
  border: none;
  border-radius: 50%;
  display: grid; place-items: center;
  color: var(--search-icon);
  cursor: pointer;
  box-shadow: 0 2px 8px rgba(0,0,0,0.25);
  transition: transform var(--duration-fast) var(--easing);
  flex-shrink: 0;
}
.search-glass-btn:active { transform: scale(0.93); }
.search-glass-btn svg { width: 22px; height: 22px; display: block; fill: currentColor; }
.top-toolbar.is-search-open .search-glass-btn { display: none; }

/* X close button inside the expanded input row — replaces the legacy
   .search-icon-btn that used to live inside the pill. */
.search-close-btn {
  width: 32px; height: 32px;
  background: transparent;
  border: none;
  display: grid; place-items: center;
  color: var(--search-icon);
  cursor: pointer;
  flex-shrink: 0;
}
.search-close-btn svg { width: 18px; height: 18px; display: block; fill: currentColor; }

/* Legacy alias kept so other call sites referencing .search-icon-btn don't
   break — still used by the recordings reload button etc. */
.search-icon-btn {
  width: 32px; height: 32px;
  background: transparent;
  border: none;
  display: grid; place-items: center;
  color: var(--search-icon);
  cursor: pointer;
}
.search-icon-btn svg { width: 20px; height: 20px; display: block; }

/* ───────────────────────────────────
   TABS — horizontal scroll, rounded-top, active gold
   ─────────────────────────────────── */
.tabs {
  display: flex;
  gap: 4px;
  padding: 0 14px;
  align-items: flex-end;
  overflow-x: auto;
  scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }
.tab {
  position: relative;
  padding: 10px 18px;
  background: var(--tab-inactive-bg);
  color: var(--tab-inactive-text);
  border-radius: var(--radius-tab);
  border: 1px solid var(--tab-border);
  border-bottom: none;
  font-size: 14px;
  font-weight: 700;
  white-space: nowrap;
  cursor: pointer;
  min-height: 38px;
  letter-spacing: 0.01em;
  text-transform: none;
}
.tab.active {
  background: var(--tab-active-bg);
  color: var(--tab-active-text);
  padding-top: 12px;
  box-shadow: 0 -2px 6px rgba(255, 216, 61, 0.25);
  text-shadow: 0 1px 0 rgba(255,255,255,0.2);
}
.tab .tab-badge {
  position: absolute;
  top: -6px; right: -4px;
  min-width: 18px; height: 18px;
  padding: 0 5px;
  border-radius: 9px;
  background: var(--okara-red);
  color: #fff;
  font-size: 10px;
  font-weight: 700;
  display: grid; place-items: center;
  border: 1.5px solid rgba(0,0,0,0.3);
  z-index: 2;
}

/* ───────────────────────────────────
   LIST ITEM — song / singer / etc.
   ─────────────────────────────────── */
.song-list {
  padding: 14px 14px 0;
  display: flex; flex-direction: column;
  gap: 8px;
}
/* Currently-playing row — distinct visual mark so the operator can spot
   which file the player is on while scrolling a long list. Gold left
   border + subtle gradient background.  */
.song-item.is-playing {
  background: linear-gradient(90deg, rgba(255, 216, 61, 0.14), rgba(255, 216, 61, 0));
  border-left: 3px solid var(--okara-gold);
  padding-left: calc(var(--song-item-pl, 14px) - 3px);
}
.song-item.is-playing .song-title {
  color: var(--okara-gold);
}

.song-item {
  display: flex; align-items: center; gap: 12px;
  background: var(--card-bg);
  border: 1px solid var(--card-border);
  border-radius: var(--radius-lg);
  padding: 14px 16px;
  cursor: pointer;
  transition: all var(--duration-fast) var(--easing);
}
.song-item:hover, .song-item:active {
  background: var(--card-bg-active);
}
.song-info { flex: 1; min-width: 0; }
.song-title {
  font-size: 16px;
  font-weight: 800;
  color: var(--text-primary);
  text-transform: uppercase;
  letter-spacing: 0.01em;
  line-height: 1.25;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.song-singer {
  font-size: 12px;
  font-weight: 600;
  color: var(--text-singer);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-top: 4px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.song-item-action {
  width: 36px; height: 36px;
  border-radius: 50%;
  background: linear-gradient(135deg, #1a0820, #0d0413);
  border: none;
  color: #fff;
  display: grid; place-items: center;
  flex-shrink: 0;
  cursor: pointer;
}
.song-item-action svg { width: 14px; height: 14px; display: block; }

/* Heart save button — dim outline by default, gold-filled when saved.
   Phase 5.B: only shown for authenticated (Telegram-linked) users. */
.rec-save-btn {
  color: rgba(255, 255, 255, 0.4) !important;
  transition: color 180ms, transform 120ms ease-out, filter 180ms;
}
.rec-save-btn:hover { color: rgba(255, 255, 255, 0.7) !important; }
.rec-save-btn:active { transform: scale(0.88); }
.rec-save-btn.is-saved {
  color: var(--okara-red) !important;
  filter: drop-shadow(0 0 6px rgba(230,57,70,0.5));
}
.rec-save-btn.is-saved:hover { filter: drop-shadow(0 0 9px rgba(230,57,70,0.7)); }
.rec-save-btn svg { fill: currentColor; }

/* Priority button — đưa bài lên đầu queue (Selected list only) */
.song-item-priority {
  width: 36px; height: 36px;
  border-radius: 50%;
  background: linear-gradient(135deg, rgba(255,216,61,0.18), rgba(217,124,10,0.04));
  border: 1px solid rgba(255,216,61,0.32);
  color: var(--okara-gold);
  display: grid; place-items: center;
  flex-shrink: 0;
  cursor: pointer;
  transition: all var(--duration-fast) var(--easing);
}
.song-item-priority svg { width: 18px; height: 18px; display: block; }
.song-item-priority:hover, .song-item-priority:active {
  background: linear-gradient(135deg, #ffd83d, #d97c0a);
  border-color: var(--okara-gold);
  color: #3a1a00;
  transform: translateY(-1px);
}
.song-item.priority {
  border-color: rgba(255, 216, 61, 0.4);
  background: linear-gradient(135deg, rgba(80, 50, 30, 0.6), rgba(30, 14, 42, 0.78));
}
.song-item .priority-badge {
  background: linear-gradient(135deg, #ffd83d, #d97c0a);
  color: #3a1a00;
  font-size: 9px;
  font-weight: 700;
  padding: 3px 6px;
  border-radius: 4px;
  letter-spacing: 0.04em;
  margin-right: 8px;
}

/* ───────────────────────────────────
   BOTTOM PLAYBACK BAR — circular filled buttons (no border)
   + text label dưới mỗi nút (matching KaraRemoteHD pattern)
   ─────────────────────────────────── */
.playback-bar {
  position: fixed; bottom: 0; left: 0; right: 0;
  max-width: var(--pb-max-width, 420px);
  margin: 0 auto;
  background: var(--bar-bg);
  border-top: 1px solid var(--bar-border);
  display: flex;
  justify-content: space-around;
  align-items: flex-start;
  padding: 10px 8px 8px;
  padding-bottom: calc(8px + env(safe-area-inset-bottom));
  z-index: var(--z-bottom-bar);
}

/* ───────────────────────────────────
   NOW-PLAYING STATUS BAR (sits on top of .playback-bar)
   3 zones: left state label · middle marquee · right "Đã chọn" chip.
   Lives as a child of .playback-bar (which is position:fixed), anchored
   via bottom:100% so it tracks the parent's top edge without height math.
   ─────────────────────────────────── */
.now-playing-bar {
  position: absolute;
  left: 0; right: 0;
  bottom: 100%;
  height: 30px;
  overflow: hidden;
  background: rgba(0, 0, 0, 0.82);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  border: none;
  user-select: none;
}
/* Marquee viewport — fills the whole bar; content scrolls beneath the
   absolute-positioned label (left) and chip (right). */
.npb-marquee {
  position: absolute; inset: 0;
  display: flex; align-items: center;
  overflow: hidden;
  padding-left: 130px;
}
.npb-track {
  display: flex; align-items: center;
  gap: 40px;
  white-space: nowrap;
  /* animation-duration is set inline by JS (varies with text length so the
     speed stays ~50 px/s regardless of title length). */
  animation: npb-scroll linear infinite;
  flex-shrink: 0;
  will-change: transform;
}
@keyframes npb-scroll {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}
.npb-text {
  color: rgba(255, 255, 255, 0.92);
  font-size: 12px; font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.npb-text .singer { color: var(--okara-gold); font-weight: 600; }
.npb-text .cta { color: var(--okara-gold); font-weight: 800; }
.npb-logo {
  height: 14px; width: auto;
  opacity: 0.7;
  display: inline-block;
  vertical-align: middle;
}
.npb-bullet {
  width: 4px; height: 4px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.4);
  display: inline-block;
}
/* Pause animation when the player itself is paused — gives a clear
   visual cue that nothing is moving forward. */
.now-playing-bar--paused .npb-track { animation-play-state: paused; }

/* LEFT label — solid #000 100%, with 28px right-edge fade so marquee text
   sliding under it disappears gradually (not a hard cut). */
.npb-label {
  position: absolute;
  left: 0; top: 0; bottom: 0;
  z-index: 2;
  padding: 0 28px 0 14px;
  display: flex; align-items: center;
  gap: 8px;
  pointer-events: none;
  font-size: 11px; font-weight: 800;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: #fff;
  background: linear-gradient(
    to right,
    #000 0,
    #000 calc(100% - 28px),
    rgba(0, 0, 0, 0) 100%
  );
}
.npb-dot {
  width: 8px; height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
}
.now-playing-bar--playing .npb-dot {
  background: #6dffa0;
  box-shadow: 0 0 0 2px rgba(109, 255, 160, 0.25), 0 0 6px rgba(109, 255, 160, 0.7);
  animation: npb-pulse 1.4s ease-in-out infinite;
}
.now-playing-bar--paused .npb-dot {
  background: #ffd83d;
  box-shadow: 0 0 0 2px rgba(255, 216, 61, 0.25);
}
.now-playing-bar--ended .npb-dot {
  background: #e63946;
  box-shadow: 0 0 0 2px rgba(230, 57, 70, 0.35);
}
@keyframes npb-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.4; }
}

/* "Đã chọn" chip — sibling of .now-playing-bar inside .playback-bar.
   2× the status-bar height (60px) so the top half pokes UP above the
   status bar (status bar stays 30px), giving a bigger tap target.
   Bottom 30px overlaps the status-bar's right side so the marquee text
   under it still gets the left-edge-fade "eaten by chip" effect.
   Top-left is rounded; right edge is flush with the viewport.

   CONCENTRIC GEOMETRY: the chip's TL fillet and the inner
   .npb-queue-btn pill share the SAME arc center — both curves read as
   nested concentric circles. Tuning (after icon-only refactor):
     • Icon-only button is ~28 px tall × ~52 px wide; corner radius 14
       (pill = h/2).
     • chip padding-left = 12 (= top/bottom slack to button) so vertical
       and horizontal insets match → arc centers coincide.
     • chip TL radius   = 14 (btn corner) + 12 (inset) = 26 px.
     • fade tail = 12 px so the transparent ramp lives entirely in the
       gap to the left of the button.
   Compared to the wider text variant: padding-left 16→12, radius
   30→26, fade 16→12. The chip overall shrinks by ~30 px width with
   the text dropped + tighter button. */
.npb-queue {
  position: absolute;
  right: 0;
  bottom: 100%;          /* top of playback-bar = bottom of chip */
  height: 60px;          /* 2× now-playing-bar height (30px) */
  z-index: 3;            /* above .now-playing-bar so its bg eats the marquee */
  padding: 0 10px 0 12px;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  text-decoration: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  /* Element switched from <a> to <button> for the popup toggle — reset
     the user-agent button styling (default 2px gray outset border, system
     font color, focus outline) so the chip renders identically to its
     previous link form. */
  border: 0;
  outline: none;
  color: inherit;
  font: inherit;
  appearance: none;
  -webkit-appearance: none;
  border-top-left-radius: 26px;  /* 14 (btn) + 12 (inset) — concentric */
  background: linear-gradient(
    to left,
    #000 0,
    #000 calc(100% - 12px),
    rgba(0, 0, 0, 0) 100%
  );
}
.npb-queue:focus-visible .npb-queue-btn {
  /* Keyboard focus indicator — only when navigated via keyboard, not
     after a tap. Subtle gold dot underneath the pill rather than a ring
     around the whole chip (which previously read as a stray border). */
  box-shadow: 0 2px 10px rgba(255, 216, 61, 0.6);
}
/* Inner pill — icon + count badge. Compact since the text "Đã chọn"
   was dropped (icon-only); padding shrunk from 7px 14px to 6px 10px so
   the chip overall fits comfortably without dominating the bar. */
.npb-queue-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px;
  border-radius: 999px;
  background: linear-gradient(135deg, #ffd83d, #d97c0a);
  color: #3a1a00;
  box-shadow: 0 2px 10px rgba(255, 216, 61, 0.32);
  transition: transform var(--duration-fast) var(--easing);
}
.npb-queue-btn svg { display: block; fill: currentColor; }
.npb-queue:active .npb-queue-btn { transform: scale(0.94); }
/* Notification-style count badge — red bg + white text. Bumped from
   10→13 px so the song count is glanceable at TV/operator distance. */
.npb-queue-count {
  background: #e63946;
  color: #fff;
  border-radius: 12px;
  padding: 2px 8px;
  font-size: 13px;
  font-weight: 800;
  min-width: 22px;
  text-align: center;
  line-height: 1.25;
  box-shadow: 0 1px 3px rgba(230, 57, 70, 0.4);
}
/* Queue empty: badge fades to neutral so a "0" doesn't read as alarming. */
.npb-queue[data-count="0"] .npb-queue-count {
  background: rgba(255, 255, 255, 0.18);
  color: rgba(58, 26, 0, 0.5);
  box-shadow: none;
}
.pb-btn {
  width: 42px; height: 42px;
  border-radius: 50%;
  border: none;                /* NO BORDER — đồng nhất fill */
  background: linear-gradient(135deg, #ffd83d, #d97c0a);
  color: #3a1a00;              /* icon đen warm, dùng currentColor trong SVG */
  display: grid; place-items: center;
  cursor: pointer;
  flex-shrink: 0;
  position: relative;
  transition: all var(--duration-fast) var(--easing);
  box-shadow: 0 2px 10px rgba(255, 216, 61, 0.32);
  margin-bottom: 18px;          /* room for label below */
}
/* Text label dưới button (KaraRemoteHD pattern) */
.pb-btn[data-label]::after {
  content: attr(data-label);
  position: absolute;
  top: calc(100% + 5px);
  left: 50%;
  transform: translateX(-50%);
  font-size: 10px;
  font-weight: 700;
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  white-space: nowrap;
  pointer-events: none;
}
.pb-btn:hover, .pb-btn:active {
  transform: scale(0.96);
  box-shadow: 0 2px 14px rgba(255, 216, 61, 0.5);
}
.pb-btn svg {
  width: 22px; height: 22px;
  display: block;
}
.pb-btn.fill-green {
  background: linear-gradient(135deg, #5cd96a, #2e8d3a);
  color: #053912;
  box-shadow: 0 2px 10px rgba(76, 175, 80, 0.35);
}
.pb-btn.fill-red {
  background: linear-gradient(135deg, #ff4255, #c20a18);
  color: #fff;
  box-shadow: 0 2px 10px rgba(230, 57, 70, 0.4);
}
.pb-btn.large {
  width: 56px; height: 56px;
  box-shadow: 0 4px 16px rgba(255, 216, 61, 0.45);
}
.pb-btn.large svg { width: 28px; height: 28px; }

/* Vocal toggle button — button style GIỮ NGUYÊN như nút thường
   Chỉ ICON COLOR đổi theo state:
   - Default (Karaoke / tách lời) — icon dark như các nút khác (#3a1a00)
   - .active (Nghe hát / có lời ca sĩ) — icon SÁNG (white) + glow */
.pb-btn.vocal-toggle {
  /* Inherit base .pb-btn — gold filled, dark icon. Không override. */
}
/* Icon khuôn mặt cô gái — to hơn so với các icon khác cho rõ */
.pb-btn.vocal-toggle svg {
  width: 30px; height: 30px;
}
.pb-btn.vocal-toggle.active {
  color: #ffffff;                            /* ca sĩ sáng lên → icon trắng */
}
.pb-btn.vocal-toggle.active svg {
  filter: drop-shadow(0 0 4px rgba(255, 255, 255, 0.85))
          drop-shadow(0 0 8px rgba(255, 255, 255, 0.4));
}

/* ───────────────────────────────────
   ICONS (SVG-friendly fallback emoji)
   ─────────────────────────────────── */
.ic { display: inline-block; font-style: normal; line-height: 1; }

/* ───────────────────────────────────
   GENERIC CARD
   ─────────────────────────────────── */
.card {
  background: var(--card-bg);
  border: 1px solid var(--card-border);
  border-radius: var(--radius-lg);
  padding: 18px;
  margin: 0 14px 14px;
}
.card-title {
  font-size: 11px;
  font-weight: 700;
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  margin-bottom: 12px;
}

/* Status dot */
.dot {
  display: inline-block;
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--text-tertiary);
  flex-shrink: 0;
}
.dot.ok    { background: var(--status-ok);    box-shadow: 0 0 0 3px rgba(76,175,80,0.18); }
.dot.warn  { background: var(--status-warn);  box-shadow: 0 0 0 3px rgba(255,216,61,0.18); }
.dot.error { background: var(--status-error); box-shadow: 0 0 0 3px rgba(230,57,70,0.18); }
.dot.recording {
  background: var(--okara-red);
  animation: pulse-record 1.4s ease-out infinite;
}
@keyframes pulse-record {
  0%, 100% { box-shadow: 0 0 0 0 rgba(230, 57, 70, 0.6); }
  50%      { box-shadow: 0 0 0 8px rgba(230, 57, 70, 0); }
}

/* Modal */
.overlay-backdrop {
  position: fixed; inset: 0;
  background: rgba(0,0,0,0.7);
  backdrop-filter: blur(4px);
  display: flex; align-items: flex-end; justify-content: center;
  z-index: var(--z-modal);
}
.bottom-sheet {
  max-width: 640px; width: 100%;
  background: linear-gradient(180deg, #2c1240 0%, #170828 100%);
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  padding: 14px 20px 28px;
  border-top: 2px solid var(--okara-gold);
}
.bottom-sheet-handle {
  width: 36px; height: 4px;
  background: rgba(255,255,255,0.2);
  border-radius: 2px;
  margin: 0 auto 14px;
}

/* ── Queue popup (Đã chọn toggle sheet) ──
   Backdrop sits JUST UNDER the playback-bar (z=29 vs z=30) so the chip +
   playback controls remain interactive — the chip itself toggles the
   popup closed on 2nd tap, so it must stay clickable. The sheet is a
   sibling positioned above everything (z=51), not a backdrop child. */
/* Backdrop mirrors .overlay-backdrop used by song-sheet modal — full
   viewport blur/dim, sits just below the sheet. z-modal lifts it above
   the playback-bar so the popup is a true modal (matches song-sheet UX). */
.queue-popup-backdrop {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal);
  background: rgba(0,0,0,0.55);
  backdrop-filter: blur(3px);
  transition: opacity .2s var(--easing);
}
/* Mirror .bottom-sheet.song-sheet exactly — anchored to viewport bottom,
   top-rounded corners only, full width up to 460px. Slides up from below
   on open. inset-inline + margin-inline-auto = orientation-change safe
   (no transform transition on resize). */
/* Inherits .bottom-sheet + .song-sheet base. These overrides:
   - position:fixed + inset-inline + bottom:0 → anchor to viewport bottom
   - flex column + capped height → scroll the list internally
   - safe-area-inset on padding-bottom → respects iPhone notch / nav bar
   - z-index just above the backdrop (which sits at var(--z-modal)) */
.queue-popup-sheet {
  position: fixed;
  inset-inline: 0;
  margin-inline: auto;
  bottom: 0;
  width: 100%;
  max-width: 460px;
  max-height: 68vh;
  display: flex;
  flex-direction: column;
  box-shadow: 0 -12px 40px rgba(0,0,0,0.55), 0 0 0 1px rgba(255,216,61,0.18);
  padding: 14px 20px calc(env(safe-area-inset-bottom, 0px) + 18px);
  z-index: calc(var(--z-modal) + 1);
  transition: opacity .2s var(--easing), transform .2s var(--easing);
  overflow: hidden;
}
.queue-popup-handle {
  width: 36px; height: 4px;
  background: rgba(255,255,255,0.2);
  border-radius: 2px;
  margin: 0 auto 14px;
  flex-shrink: 0;
}
/* "Đang phát" mini-card sits between header and list — analogous to the
   song-sheet's lyric meta block. */
.queue-popup-now {
  padding: 8px 10px 10px;
  margin-bottom: 8px;
  background: rgba(255,255,255,0.04);
  border-radius: var(--radius-md);
  border: 1px solid var(--card-border);
  flex-shrink: 0;
}
.queue-popup-now__label {
  font-size: 9px; font-weight: 800; letter-spacing: 0.12em;
  color: var(--okara-gold); text-transform: uppercase;
  margin-bottom: 2px;
}
.queue-popup-now__title {
  font-size: 14px; font-weight: 800; text-transform: uppercase;
  color: #fff;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.queue-popup-now__meta {
  font-size: 11px; color: var(--text-singer);
  margin-top: 2px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Header now matches .song-sheet__header — title left, close button right.
   The "N bài" count goes in the sub-line just like a song-sheet singer sub. */
.queue-popup-clear {
  display: inline-flex; align-items: center; gap: 4px;
  background: rgba(230,57,70,0.16);
  color: #ff8a93;
  border: none;
  border-radius: var(--radius-pill);
  padding: 4px 10px;
  font-size: 10px; font-weight: 700;
  cursor: pointer;
  align-self: flex-start;
  margin-bottom: 8px;
}
.queue-popup-clear:active { transform: scale(0.95); }
.queue-popup-list {
  flex: 1;
  overflow-y: auto;
  padding: 0 2px 4px;
  -webkit-overflow-scrolling: touch;
}
.queue-popup-empty {
  padding: 28px 18px 32px;
  text-align: center;
  color: var(--text-tertiary);
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  flex: 1;
}
.queue-popup-empty svg { opacity: 0.4; }
.queue-popup-empty__hint { font-size: 11px; font-style: italic; }
.queue-popup-row {
  display: flex; align-items: center; gap: 8px;
  padding: 8px;
  border-radius: var(--radius-md);
  margin: 4px 0;
  background: rgba(255,255,255,0.04);
}
.queue-popup-row__priority,
.queue-popup-row__remove {
  width: 32px; height: 32px;
  flex-shrink: 0;
  display: grid; place-items: center;
  border: none;
  border-radius: 50%;
  cursor: pointer;
}
.queue-popup-row__priority {
  background: rgba(255,216,61,0.15);
  color: var(--okara-gold);
}
.queue-popup-row__remove {
  background: rgba(230,57,70,0.15);
  color: #ff8a93;
}
.queue-popup-row__priority:active,
.queue-popup-row__remove:active { transform: scale(0.92); }
.queue-popup-row__info { flex: 1; min-width: 0; }
.queue-popup-row__title {
  font-size: 13px; font-weight: 700; color: #fff;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.queue-popup-row__singer {
  font-size: 11px; color: var(--text-singer);
  margin-top: 1px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* "ONLINE" badge next to a song title in the queue popup. Informational
   only — marks tracks that came from YouTube / SoundCloud / MixCloud
   (added via UP_SONG_WITH_URL=109). Removal works for online songs too
   now (rAgent's AIDL bind to IMediaService), so the badge is no longer
   a "this can't be removed" warning, just a glance-able source hint. */
.queue-popup-row__badge {
  display: inline-block;
  margin-left: 8px;
  padding: 1px 6px;
  font-size: 9px;
  font-weight: 800;
  letter-spacing: 0.06em;
  border-radius: var(--radius-pill);
  background: rgba(255, 216, 61, 0.18);
  color: var(--okara-gold);
  vertical-align: middle;
}

/* Chip "open" state — popup mounting below the chip is signal enough;
   no extra ring or border needed (added a 2px gold outline earlier; it
   read as chunky next to the rounded pill, removed 2026-05-28). */

/* Fly-to-queue: a small gold disc spawns at the tapped song row's centre
   and animates into the chip's inner pill. The CSS holds the visuals; JS
   sets the start position (left/top) and the destination via `transform`.
   Duration deliberately set to 850ms to cover the gateway pre-resolve +
   firmware queue-echo round-trip — by the time the disc lands, the real
   queue update has arrived from firmware, so the chip count animates
   simultaneously with the pulse. Perceived latency = 0.
   Eased to swoop briefly out + accelerate into the chip — feels like a
   small object being thrown rather than linearly dragged. */
.fly-to-queue {
  position: fixed;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: linear-gradient(135deg, #ffd83d, #d97c0a);
  color: #3a1a00;
  font-size: 22px;
  font-weight: 800;
  line-height: 32px;
  text-align: center;
  box-shadow: 0 6px 16px rgba(255, 216, 61, 0.55);
  pointer-events: none;
  /* Above the playback bar + queue chip so the disc is visible all the
     way to the moment it lands. */
  z-index: calc(var(--z-bottom-bar, 30) + 5);
  transition:
    transform 850ms cubic-bezier(0.34, 1.1, 0.5, 1),
    opacity   850ms cubic-bezier(0.7, 0, 0.95, 0.4);
  will-change: transform, opacity;
}

/* Chip spin transform-origin only — the actual rotation + scale
   animation is driven by JS via the Web Animations API in
   flyToQueueChip()'s cleanup. WAAPI lets us use `composite: 'add'`
   so multiple landings (user queues several songs back-to-back) stack
   on top of an in-flight spin — chip just keeps spinning + briefly
   bumps speed each time a new disc lands. Pointer-events stay on, so
   the chip remains tappable mid-spin. */
.npb-queue-btn {
  transform-origin: center center;
}

/* Badge generic */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 8px;
  border-radius: var(--radius-pill);
  font-size: 10px;
  font-weight: 700;
  background: rgba(255,255,255,0.08);
  color: var(--text-secondary);
  letter-spacing: 0.04em;
}
.badge.gold { background: linear-gradient(135deg, #ffd83d, #d97c0a); color: #3a1a00; }
.badge.green { background: rgba(76,175,80,0.18); color: var(--okara-green); }
.badge.red { background: rgba(230,57,70,0.18); color: var(--okara-red); }

/* Button generic */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: 8px;
  padding: 12px 18px;
  border-radius: var(--radius-md);
  border: none;
  font-size: 14px;
  font-weight: 700;
  cursor: pointer;
  transition: all var(--duration-fast) var(--easing);
}
.btn-primary {
  background: var(--tab-active-bg);
  color: #3a1a00;
}
.btn-secondary {
  background: rgba(255,255,255,0.08);
  color: #fff;
  border: 1px solid rgba(255,255,255,0.15);
}
.btn-ghost {
  background: transparent;
  color: var(--text-secondary);
}
.btn-block { width: 100%; }
.btn-large { padding: 16px 22px; font-size: 15px; }
.btn-red {
  background: var(--okara-red);
  color: #fff;
}
.btn-red:hover, .btn-red:active {
  background: #b71724;
}

/* Exit-from-archive confirmation sheet. Lives in #modal-root via openModal. */
.exit-confirm-sheet { max-width: 400px; }
.exit-confirm__title {
  font-size: 17px;
  font-weight: 800;
  color: var(--text-primary);
  margin-bottom: 8px;
  text-align: center;
}
.exit-confirm__body {
  font-size: 13px;
  color: var(--text-secondary);
  line-height: 1.5;
  margin-bottom: 18px;
  text-align: center;
}
.exit-confirm__actions {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* Mockup label (only for mockups themselves, visible top-right) */
.mockup-label {
  position: fixed;
  top: 10px; right: 10px;
  background: var(--okara-red);
  color: #fff;
  padding: 4px 10px;
  border-radius: var(--radius-pill);
  font-size: 10px;
  font-weight: 700;
  z-index: 100;
  font-family: var(--font-mono);
  letter-spacing: 0.04em;
}

/* Category tile (browse) */
.category-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
  padding: 14px;
}
.category-tile {
  background: var(--card-bg);          /* fallback — inline style adds a per-category gradient */
  border: 1px solid var(--card-border);
  border-radius: var(--radius-lg);
  padding: 18px 14px;
  text-align: left;
  display: flex; flex-direction: column; gap: 10px;
  color: var(--text-primary);          /* OVERRIDE browser default <button> dark color */
  cursor: pointer;
  font-family: inherit;
  font-size: inherit;
  text-decoration: none;               /* anchors used as tiles — no underline */
  min-height: 110px;
}
.category-tile .icon-big {
  width: 32px; height: 32px;
  color: var(--okara-gold);            /* monochrome gold */
  display: flex; align-items: center; justify-content: flex-start;
}
.category-tile .icon-big svg {
  width: 100%; height: 100%;
  fill: currentColor;
}
.category-tile .label {
  color: var(--text-primary);          /* white — explicit */
  font-size: 15px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  line-height: 1.2;
}
.category-tile .count {
  font-size: 11px;
  color: var(--text-tertiary);
  font-weight: 500;
}
.category-tile:hover {
  border-color: var(--okara-gold);
}
.category-tile:active { transform: scale(0.98); }

/* Slider */
.slider-row {
  display: flex; align-items: center; gap: 12px;
  margin-bottom: 12px;
}
.slider-row .slider-track {
  flex: 1;
  height: 6px;
  background: rgba(255,255,255,0.1);
  border-radius: 3px;
  position: relative;
}
.slider-row .slider-fill {
  position: absolute; left: 0; top: 0;
  height: 100%;
  border-radius: 3px;
  background: linear-gradient(90deg, var(--okara-orange), var(--okara-gold));
}
.slider-row .slider-knob {
  position: absolute;
  top: 50%; transform: translate(-50%, -50%);
  width: 18px; height: 18px;
  border-radius: 50%;
  background: var(--okara-gold);
  border: 2px solid #fff;
  box-shadow: 0 1px 6px rgba(0,0,0,0.4);
}
.slider-row .slider-value {
  width: 40px;
  text-align: right;
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
}

/* Toggle */
.toggle {
  display: flex; align-items: center; justify-content: space-between;
  padding: 10px 0;
}
.toggle-label .title { font-size: 14px; color: var(--text-primary); font-weight: 600; }
.toggle-label .desc { font-size: 12px; color: var(--text-tertiary); margin-top: 2px; }
.toggle-switch {
  width: 44px; height: 26px;
  border-radius: 13px;
  background: rgba(255,255,255,0.12);
  position: relative;
  transition: background var(--duration-fast) var(--easing);
  flex-shrink: 0;
}
.toggle-switch::after {
  content: ""; position: absolute;
  top: 2px; left: 2px;
  width: 22px; height: 22px;
  border-radius: 50%;
  background: #fff;
  transition: all var(--duration-fast) var(--easing);
}
.toggle-switch.on { background: var(--okara-gold); }
.toggle-switch.on::after { left: 20px; }

/* Avatar */
.avatar {
  width: 50px; height: 50px;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--okara-magenta), var(--okara-orange));
  display: grid; place-items: center;
  font-size: 18px;
  color: #fff;
  font-weight: 800;
  flex-shrink: 0;
}

/* =====================================================
   ADMIN SHELL (r.okara.vn/man)
   ===================================================== */
.admin-shell {
  display: grid;
  grid-template-columns: 220px 1fr;
  min-height: 100vh;
  background: var(--bg-gradient);
  background-attachment: fixed;
}
.sidebar {
  background: rgba(0,0,0,0.4);
  border-right: 1px solid rgba(255,255,255,0.06);
  padding: 18px 0;
  display: flex; flex-direction: column;
  position: sticky; top: 0;
  height: 100vh;
  overflow-y: auto;
}
.sidebar .brand-row {
  display: flex; align-items: center; gap: 10px;
  padding: 0 18px 18px;
  border-bottom: 1px solid rgba(255,255,255,0.06);
  margin-bottom: 12px;
}
.sidebar .brand-row .logo-disc-sm {
  width: 32px; height: 32px;
  border-radius: 50%;
  background: linear-gradient(180deg,#ffd83d,#d97c0a);
  display: grid; place-items: center;
  color: #3a1a00; font-weight: 900; font-size: 16px;
}
.sidebar .brand-row .brand-text {
  font-size: 16px; font-weight: 900; letter-spacing: 0.04em;
  line-height: 1.1;
}
.sidebar .brand-row .brand-text .accent { background: linear-gradient(180deg,#ffd83d,#d97c0a); -webkit-background-clip:text; -webkit-text-fill-color:transparent; }
.sidebar .brand-row .brand-sub {
  font-size: 9px; color: var(--text-tertiary);
  font-family: var(--font-mono); letter-spacing: 0.08em;
  margin-top: 2px;
}

.nav-section-title {
  font-size: 9px; font-weight: 700;
  color: var(--text-tertiary);
  text-transform: uppercase; letter-spacing: 0.12em;
  padding: 12px 18px 6px;
}
.nav-item {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 18px;
  color: var(--text-secondary);
  cursor: pointer;
  border-left: 3px solid transparent;
  font-size: 13px;
  font-weight: 500;
  text-decoration: none;
  transition: all var(--duration-fast) var(--easing);
}
.nav-item:hover { background: rgba(255,255,255,0.04); color: #fff; }
.nav-item.active {
  background: rgba(255,216,61,0.1);
  color: var(--okara-gold);
  border-left-color: var(--okara-gold);
  font-weight: 700;
}
.nav-item svg { width: 18px; height: 18px; flex-shrink: 0; }
.nav-item .nav-count {
  margin-left: auto;
  background: rgba(255,255,255,0.08);
  padding: 1px 7px;
  border-radius: 9px;
  font-size: 10px;
  font-weight: 700;
}
.nav-item.active .nav-count { background: var(--okara-gold); color: #3a1a00; }

.sidebar-footer {
  margin-top: auto;
  padding: 14px 18px;
  border-top: 1px solid rgba(255,255,255,0.06);
  font-size: 11px; color: var(--text-tertiary);
}
.sidebar-footer .user {
  display: flex; align-items: center; gap: 10px;
  margin-bottom: 6px;
}
.sidebar-footer .avatar-sm {
  width: 28px; height: 28px; border-radius: 50%;
  background: linear-gradient(135deg, var(--okara-magenta), var(--okara-orange));
  display: grid; place-items: center;
  font-size: 12px; font-weight: 800;
  color: #fff;
}
.sidebar-footer .uname { color: #fff; font-size: 12px; font-weight: 600; }
.sidebar-footer .urole { color: var(--text-tertiary); font-size: 10px; font-family: var(--font-mono); }

.admin-main {
  display: flex; flex-direction: column;
  min-height: 100vh;
  overflow-x: hidden;
}
.admin-topbar {
  display: flex; align-items: center; gap: 14px;
  padding: 14px 28px;
  background: rgba(0,0,0,0.25);
  border-bottom: 1px solid rgba(255,255,255,0.06);
  flex-shrink: 0;
}
.admin-topbar .page-title {
  font-size: 18px; font-weight: 800;
  letter-spacing: 0.02em;
}
.admin-topbar .crumbs {
  font-size: 11px; color: var(--text-tertiary);
  text-transform: uppercase; letter-spacing: 0.08em;
  margin-top: 2px;
}
.admin-topbar .topbar-actions {
  margin-left: auto;
  display: flex; gap: 10px; align-items: center;
}

.admin-content {
  padding: 24px 28px;
  flex: 1;
}

/* KPI cards */
.kpi-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 14px;
  margin-bottom: 24px;
}
.kpi-card {
  background: var(--card-bg);
  border: 1px solid var(--card-border);
  border-radius: var(--radius-lg);
  padding: 18px 20px;
}
.kpi-card .kpi-label {
  font-size: 10px; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.12em;
  color: var(--text-tertiary);
  margin-bottom: 8px;
}
.kpi-card .kpi-value {
  font-family: var(--font-mono);
  font-size: 32px; font-weight: 800;
  line-height: 1;
}
.kpi-card .kpi-delta {
  display: inline-flex; align-items: center; gap: 4px;
  margin-top: 8px;
  font-size: 11px;
  font-weight: 600;
}
.kpi-card .kpi-delta.up { color: var(--status-ok); }
.kpi-card .kpi-delta.down { color: var(--status-error); }
.kpi-card .kpi-delta.flat { color: var(--text-tertiary); }
.kpi-card .kpi-sub {
  font-size: 11px; color: var(--text-singer);
  margin-top: 6px;
  text-transform: uppercase; letter-spacing: 0.04em;
}
.kpi-card.gold { border-color: rgba(255,216,61,0.3); background: linear-gradient(135deg, rgba(255,216,61,0.12), rgba(45,30,12,0.6)); }
.kpi-card.gold .kpi-value { color: var(--okara-gold); }
.kpi-card.green { border-color: rgba(76,175,80,0.3); background: linear-gradient(135deg, rgba(76,175,80,0.12), rgba(12,40,18,0.6)); }
.kpi-card.green .kpi-value { color: var(--okara-green); }
.kpi-card.red { border-color: rgba(230,57,70,0.3); background: linear-gradient(135deg, rgba(230,57,70,0.12), rgba(45,12,20,0.6)); }
.kpi-card.red .kpi-value { color: var(--okara-red); }

/* Data table */
.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
  background: var(--card-bg);
  border: 1px solid var(--card-border);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
.data-table th {
  text-align: left;
  font-size: 10px; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.1em;
  color: var(--text-tertiary);
  padding: 12px 16px;
  background: rgba(0,0,0,0.25);
  border-bottom: 1px solid rgba(255,255,255,0.06);
}
.data-table td {
  padding: 12px 16px;
  border-bottom: 1px solid rgba(255,255,255,0.04);
  color: var(--text-secondary);
  vertical-align: middle;
}
.data-table tr:last-child td { border-bottom: none; }
.data-table tr:hover td { background: rgba(255,255,255,0.02); }
.data-table .mono { font-family: var(--font-mono); color: var(--text-primary); font-size: 12px; }

/* Log lines */
.log-stream {
  background: rgba(0,0,0,0.4);
  border: 1px solid var(--card-border);
  border-radius: var(--radius-lg);
  padding: 14px 18px;
  font-family: var(--font-mono);
  font-size: 11.5px;
  line-height: 1.7;
  max-height: 560px;
  overflow-y: auto;
}
.log-line {
  display: grid;
  grid-template-columns: 80px 60px 1fr;
  gap: 14px;
  padding: 2px 0;
  color: var(--text-secondary);
  word-break: break-all;
}
.log-line .ts { color: var(--text-tertiary); }
.log-line .lvl { font-weight: 700; text-transform: uppercase; }
.log-line.error .lvl { color: var(--okara-red); }
.log-line.warn  .lvl { color: var(--okara-gold); }
.log-line.info  .lvl { color: var(--okara-cyan); }
.log-line.debug .lvl { color: var(--text-tertiary); }
.log-line.error .msg { color: #ffd8de; }

/* Activity timeline mini chart bars */
.activity-chart {
  display: flex; align-items: flex-end;
  height: 80px; gap: 4px;
  padding: 12px 0;
}
.activity-chart .bar {
  flex: 1;
  background: linear-gradient(180deg, var(--okara-gold), var(--okara-orange));
  border-radius: 2px 2px 0 0;
  opacity: 0.85;
}

/* =====================================================
   VU METER (audio level visualization)
   ===================================================== */
.vu-meter {
  display: grid;
  grid-template-columns: 18px 1fr 56px;
  align-items: center;
  gap: 10px;
  margin-bottom: 8px;
}
.vu-meter .vu-label {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-tertiary);
  font-weight: 700;
}
.vu-meter .vu-bar {
  position: relative;
  height: 14px;
  background: rgba(0,0,0,0.55);
  border-radius: 7px;
  overflow: hidden;
}
.vu-meter .vu-fill {
  height: 100%;
  background: linear-gradient(90deg,
    #4caf50 0%,
    #4caf50 55%,
    #ffd83d 75%,
    #ff4255 92%,
    #c20a18 100%);
  border-radius: 7px;
  position: relative;
}
.vu-meter .vu-peak {
  position: absolute;
  top: 0; bottom: 0;
  width: 2px;
  background: #fff;
  box-shadow: 0 0 6px rgba(255,255,255,0.9);
  pointer-events: none;
}
.vu-meter .vu-value {
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 700;
  color: var(--text-primary);
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.vu-meter .vu-value.clip { color: var(--okara-red); }

/* dB scale below meter */
.vu-scale {
  display: flex; justify-content: space-between;
  font-family: var(--font-mono);
  font-size: 9px;
  color: var(--text-tertiary);
  padding: 2px 28px 0 28px;   /* align with bars */
  letter-spacing: -0.5px;
}
.vu-scale .mark {
  position: relative;
}
.vu-scale .mark.zero { color: var(--okara-red); font-weight: 700; }

/* =====================================================
   GAIN slider (50% - 300%)
   ===================================================== */
.gain-control {
  margin: 16px 0;
}
.gain-track {
  position: relative;
  height: 24px;
  background: rgba(0,0,0,0.4);
  border-radius: 12px;
  margin: 0 12px;
}
.gain-fill {
  position: absolute;
  top: 0; left: 0; bottom: 0;
  background: linear-gradient(90deg, #4caf50, #ffd83d, #ff4255);
  border-radius: 12px;
  opacity: 0.75;
}
.gain-knob {
  position: absolute;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 28px; height: 28px;
  border-radius: 50%;
  background: linear-gradient(180deg, #fff, #c0c0c0);
  border: 2px solid var(--okara-gold);
  box-shadow: 0 2px 8px rgba(0,0,0,0.5);
  cursor: grab;
}
.gain-marks {
  position: relative;
  margin: 6px 12px 0;
  height: 18px;
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--text-tertiary);
}
.gain-marks .mark {
  position: absolute;
  transform: translateX(-50%);
  text-align: center;
}
.gain-marks .mark.unity {
  color: var(--okara-gold);
  font-weight: 700;
}
.gain-marks .mark.unity::before {
  content: ""; display: block;
  width: 2px; height: 6px;
  background: var(--okara-gold);
  margin: 0 auto 2px;
}
.gain-marks .mark::before {
  content: ""; display: block;
  width: 1px; height: 4px;
  background: var(--text-tertiary);
  margin: 0 auto 2px;
}

.gain-presets {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 6px;
  margin-top: 14px;
}
.gain-preset {
  padding: 8px;
  border-radius: 8px;
  border: 1px solid rgba(255,255,255,0.1);
  background: rgba(255,255,255,0.04);
  color: var(--text-secondary);
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 700;
  cursor: pointer;
  text-align: center;
}
.gain-preset:hover { background: rgba(255,255,255,0.08); color: #fff; }
.gain-preset.active {
  background: linear-gradient(180deg, #ffd83d, #d97c0a);
  border-color: var(--okara-gold);
  color: #3a1a00;
}

/* "Reaction" buttons - effects */
.reactions {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  padding: 14px;
}
.reaction {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: 16px 8px;
  background: var(--card-bg);
  border: 1px solid var(--card-border);
  border-radius: var(--radius-lg);
  font-size: 11px;
  color: var(--text-secondary);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
.reaction .emoji { font-size: 32px; }
.reaction:hover, .reaction:active {
  border-color: var(--okara-gold);
  background: linear-gradient(135deg, rgba(255,216,61,0.15), rgba(217,124,10,0.05));
  color: var(--okara-gold);
  transform: scale(0.97);
}

/* ───────────────────────────────────
   SINGER GRID — 3-col card layout
   ───────────────────────────────────
   Mock visual:
     ┌──────┐ ┌──────┐ ┌──────┐
     │photo │ │photo │ │photo │
     │  3:4 │ │      │ │      │
     ├──────┤ ├──────┤ ├──────┤
     │ name │ │ ...  │ │ ...  │
     │count │ │      │ │      │
     └──────┘ └──────┘ └──────┘
   We stay at 3 cols on all phone widths so the card is finger-sized but
   not too small. On wider tablets that'd be cramped; mobile-first only.
*/
.singer-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px;
  padding: 14px;
}
.singer-card {
  display: flex; flex-direction: column;
  background: var(--card-bg);
  border: 1px solid var(--card-border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  text-decoration: none;
  color: inherit;
  transition: transform var(--duration-fast) var(--easing),
              border-color var(--duration-fast) var(--easing);
}
.singer-card:active { transform: scale(0.97); }
.singer-card:hover {
  border-color: var(--okara-gold);
}
.singer-card__photo {
  position: relative;
  aspect-ratio: 3 / 4;        /* matches the singers_b 190×246 source */
  overflow: hidden;
  background: #1a0a14;
}
.singer-card__photo-img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}
/* The fallback sits BEHIND the <img>. On 404 the img sets display:none and
   the gradient + initials below take over. */
.singer-card__fallback {
  position: absolute; inset: 0;
  display: grid; place-items: center;
}
.singer-card__initials {
  color: #fff;
  font-weight: 800;
  font-size: 28px;
  letter-spacing: 0.06em;
  text-shadow: 0 1px 2px rgba(0,0,0,0.4);
}
.singer-card__body {
  padding: 8px 10px 10px;
  display: flex; flex-direction: column;
  gap: 2px;
  min-width: 0;          /* lets text-overflow:ellipsis work on the name */
}
.singer-card__name {
  font-size: 13px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  line-height: 1.25;
}
.singer-card__meta {
  font-size: 10px;
  font-weight: 700;
  color: var(--okara-gold);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.singer-grid__sentinel {
  font-style: italic;
}

/* Skeleton placeholders — shown on the very first paint of the Singers
   tab while the API + image batch is in flight. They take the exact
   shape of a real .singer-card so the eventual data swap costs zero
   extra layout. The shimmer animation is purely cosmetic; it pauses
   when the tab isn't visible (page visibility API + animation-play-state). */
.singer-card--skeleton {
  pointer-events: none;
  background: var(--card-bg);
}
.skeleton {
  position: relative;
  overflow: hidden;
  background: linear-gradient(90deg,
    rgba(255,255,255,0.04) 0%,
    rgba(255,255,255,0.10) 50%,
    rgba(255,255,255,0.04) 100%);
  background-size: 200% 100%;
  animation: skeleton-shimmer 1.4s ease-in-out infinite;
  border-radius: var(--radius-sm, 6px);
}
.skeleton--line {
  height: 12px;
  margin: 4px 0;
}
.skeleton--line.short { width: 40%; }
@keyframes skeleton-shimmer {
  0%   { background-position: 100% 0; }
  100% { background-position: -100% 0; }
}
/* Pause shimmer when the page is hidden so we don't waste CPU. */
@media (prefers-reduced-motion: reduce) {
  .skeleton { animation: none; }
}

/* ───────────────────────────────────────────────────────────────────
   Singer detail (one singer → all their songs)
   ───────────────────────────────────────────────────────────────────
   Layout: hero strip on top (portrait + name + count), sort chip row,
   then the standard song-list with infinite-scroll sentinel.
*/
.singer-hero {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 16px 6px;
}
.singer-hero__photo {
  position: relative;
  width: 96px;
  height: 128px;             /* 3:4 portrait, mirrors the catalog card */
  border-radius: var(--radius-lg);
  overflow: hidden;
  flex-shrink: 0;
  background: #1a0a14;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.35);
  border: 1px solid var(--card-border);
}
.singer-hero__photo-img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}
.singer-hero__fallback {
  position: absolute; inset: 0;
  display: grid; place-items: center;
}
.singer-hero__initials {
  color: #fff;
  font-weight: 800;
  font-size: 30px;
  letter-spacing: 0.06em;
  text-shadow: 0 1px 2px rgba(0,0,0,0.4);
}
.singer-hero__info {
  flex: 1;
  min-width: 0;
  display: flex; flex-direction: column;
  gap: 6px;
}
.singer-hero__name {
  font-size: 22px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  line-height: 1.15;
  color: var(--text-primary);
  /* allow wrapping; clamp to 2 lines so long names don't shove the count out */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.singer-hero__count {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--okara-gold);
}

/* Sort row — three small pills, gold when active. */
.singer-detail__sort {
  display: flex;
  gap: 8px;
  padding: 10px 16px 6px;
  overflow-x: auto;
  scrollbar-width: none;
}
.singer-detail__sort::-webkit-scrollbar { display: none; }
.singer-detail__sort .chip {
  padding: 7px 14px;
  border-radius: 999px;
  background: var(--card-bg);
  border: 1px solid var(--card-border);
  color: var(--text-singer);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--duration-fast) var(--easing),
              color var(--duration-fast) var(--easing),
              border-color var(--duration-fast) var(--easing);
}
.singer-detail__sort .chip--active {
  background: linear-gradient(135deg, #ffd83d, #d97c0a);
  color: #3a1a00;
  border-color: transparent;
}

/* ───────────────────────────────────────────────────────────────────
   Song row action sheet (opened by clicking the ">" chevron) +
   lyric modal — both live in #modal-root as bottom-sheets.
   ─────────────────────────────────────────────────────────────────── */
.song-sheet { max-width: 460px; }
.song-sheet__header {
  padding: 4px 4px 14px;
  border-bottom: 1px solid var(--card-border);
  margin-bottom: 10px;
  display: flex;
  align-items: flex-start;
  gap: 10px;
}
.song-sheet__header-text { flex: 1; min-width: 0; }
.song-sheet__close {
  flex-shrink: 0;
  width: 32px; height: 32px;
  border-radius: 50%;
  background: rgba(255,255,255,0.06);
  border: 1px solid rgba(255,255,255,0.10);
  color: var(--text-secondary);
  display: grid; place-items: center;
  cursor: pointer;
  padding: 0;
  transition: background var(--duration-fast) var(--easing);
}
.song-sheet__close:hover, .song-sheet__close:active {
  background: rgba(255,255,255,0.12);
  color: var(--text-primary);
}
.song-sheet__title {
  font-size: 16px;
  font-weight: 800;
  color: var(--text-primary);
  line-height: 1.2;
  /* Allow wrapping; clamp to 2 lines so a really long title doesn't crowd
     the sheet, but still leaves room for the actions. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.song-sheet__sub {
  font-size: 12px;
  color: var(--okara-gold);
  margin-top: 4px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-weight: 700;
}
.song-sheet__actions {
  display: flex; flex-direction: column;
  gap: 4px;
}
.song-sheet__item {
  /* Reset button defaults so we get a clean row in the sheet. */
  display: grid;
  grid-template-columns: 28px 1fr 16px;
  align-items: center;
  gap: 14px;
  width: 100%;
  padding: 12px 8px;
  background: transparent;
  border: 0;
  border-radius: var(--radius-md);
  color: var(--text-primary);
  text-align: left;
  cursor: pointer;
  transition: background var(--duration-fast) var(--easing);
}
.song-sheet__item:hover:not(.is-disabled) { background: rgba(255,255,255,0.04); }
.song-sheet__item:active:not(.is-disabled) { background: rgba(255,255,255,0.07); }
.song-sheet__item.is-disabled {
  opacity: 0.4;
  cursor: not-allowed;
}
.song-sheet__ic {
  display: grid; place-items: center;
  color: var(--okara-gold);
}
.song-sheet__lbl-main {
  font-size: 14px;
  font-weight: 700;
}
.song-sheet__lbl-sub {
  font-size: 11px;
  color: var(--text-tertiary);
  margin-top: 2px;
}
.song-sheet__chev { color: var(--text-tertiary); }

/* Inline scrollable lyric area at the bottom of the song action sheet.
   The sheet itself is capped at 85vh and uses flex column so the lyric
   area can take the remaining height and scroll internally without
   pushing the action buttons off-screen. */
/* 68vh = ~20% shorter than the previous 85vh cap. Users reported the sheet
   was eating too much screen when the lyric loaded; lyric area still scrolls
   internally so we just give it less height to live in. */
.song-sheet { max-height: 68vh; display: flex; flex-direction: column; }
.song-sheet__lyric-section {
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px solid var(--card-border);
  flex: 1;
  min-height: 0;       /* lets the inner area actually shrink + scroll */
  display: flex; flex-direction: column;
}
.song-sheet__lyric-label {
  font-size: 11px;
  font-weight: 800;
  color: var(--okara-gold);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  margin-bottom: 8px;
  flex-shrink: 0;
}
.song-sheet__lyric {
  flex: 1;
  overflow: auto;
  -webkit-overflow-scrolling: touch;
  padding-right: 4px;  /* breathing room for the scrollbar on desktop */
}
.song-sheet__lyric-empty,
.song-sheet__lyric-loading {
  padding: 20px 4px;
  text-align: center;
  color: var(--text-tertiary);
  font-size: 12px;
  font-style: italic;
}
.song-sheet__lyric-err {
  padding: 12px 4px;
  color: var(--okara-red);
  font-size: 12px;
}
.song-sheet__lyric-meta {
  display: flex; flex-direction: column;
  gap: 2px;
  margin-bottom: 10px;
}
.song-sheet__lyric-comp {
  font-size: 13px;
  font-weight: 800;
  color: var(--text-primary);
}
.song-sheet__lyric-composer {
  font-size: 11px;
  color: var(--text-secondary);
}
.song-sheet__lyric-body {
  white-space: pre-wrap;
  font-family: inherit;
  font-size: 14px;
  line-height: 1.7;
  color: var(--text-primary);
  margin: 0;
  padding: 0 0 12px;
}

/* ─── Category detail hero (#/categories/:id) ───────────────────── */
.category-hero {
  display: flex; align-items: center; gap: 14px;
  padding: 18px 16px;
  margin: 14px 16px 0;
  border-radius: var(--radius-lg);
  border: 1px solid var(--card-border);
  box-shadow: 0 6px 20px rgba(0,0,0,0.35);
}
.category-hero__icon {
  width: 56px; height: 56px;
  display: grid; place-items: center;
  background: rgba(0,0,0,0.25);
  border-radius: var(--radius-md);
  color: var(--okara-gold);
  flex-shrink: 0;
}
.category-hero__info { flex: 1; min-width: 0; }
.category-hero__crumb {
  font-size: 10px;
  color: rgba(255,255,255,0.6);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 700;
}
.category-hero__name {
  font-size: 22px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  margin-top: 4px;
  color: #fff;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ───────────────────────────────────────────────────────────────────
   Unified-search dropdown
   ───────────────────────────────────────────────────────────────────
   Rendered into <body> as a top-level node so it overlays every view.
   Hidden by default; opens via .is-open. Backdrop covers the screen
   to capture taps-outside; the inner panel is anchored under the
   sticky header (we approximate via fixed top:64px which matches the
   toolbar's measured height on phone — adjust if RAGENT_UI changes
   that.) */
.search-dropdown {
  /* Full-viewport dim. The sticky-header (z:30) explicitly sits above us
     (z:25) so the search input + glass button remain crisp while content
     below them is darkened + blurred. The panel inside us is positioned
     to start below the header — see .search-dropdown__panel `top`. */
  position: fixed; inset: 0;
  background: rgba(0,0,0,0.0);
  pointer-events: none;
  opacity: 0;
  z-index: 25;
  transition: opacity 0.15s var(--easing);
}
.search-dropdown.is-open {
  background: rgba(0,0,0,0.55);
  backdrop-filter: blur(2px);
  pointer-events: auto;
  opacity: 1;
}
.search-dropdown__panel {
  position: absolute;
  /* Container is full-viewport so we offset the panel ourselves to clear
     the sticky-header (toolbar ~56px + tabs ~48px + notch). z-index above
     the dim so the panel itself isn't darkened. */
  top: calc(112px + env(safe-area-inset-top));
  left: 12px; right: 12px;
  z-index: 1;
  max-height: 70vh;
  background: linear-gradient(180deg, #2a1238 0%, #170828 100%);
  border: 1px solid var(--card-border);
  border-radius: var(--radius-lg);
  box-shadow: 0 14px 40px rgba(0,0,0,0.55);
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 6px 0 10px;
}
.search-dropdown__msg {
  padding: 24px 16px;
  text-align: center;
  color: var(--text-secondary);
  font-size: 13px;
  font-style: italic;
}
.search-dropdown__msg--err { color: var(--okara-red); }
/* Inline match highlight — Vietnamese-fold aware (handled JS-side). Light
   gold wash so it pops without screaming, and inherits color from the
   parent name/sub so highlighted text stays readable. */
.search-dropdown__hl {
  background: linear-gradient(180deg, rgba(255,216,61,0.0) 55%, rgba(255,216,61,0.45) 55%);
  color: inherit;
  font-weight: 800;
  padding: 0 1px;
  border-radius: 2px;
}

/* YouTube suggest row — Google-style: small magnifying glass + plain
   text suggestion. Compact compared to singer/song rows since each
   line is just a single string. */
.search-dropdown__row--suggest {
  padding: 8px 14px;
  gap: 10px;
}
.search-dropdown__suggest-icon {
  width: 22px; height: 22px;
  display: grid; place-items: center;
  color: var(--text-tertiary);
  flex-shrink: 0;
}
.search-dropdown__suggest-icon svg { fill: currentColor; }
.search-dropdown__row--suggest .search-dropdown__name {
  font-weight: 600;
  font-size: 14px;
}
.search-dropdown__section {
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--okara-gold);
  font-weight: 800;
  padding: 10px 16px 4px;
}
.search-dropdown__row {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 14px;
  text-decoration: none;
  color: inherit;
  cursor: pointer;
  border-radius: 8px;
  margin: 0 6px;
  transition: background var(--duration-fast) var(--easing);
}
.search-dropdown__row:hover,
.search-dropdown__row:active {
  background: rgba(255,255,255,0.06);
}
.search-dropdown__avatar {
  position: relative;
  width: 36px; height: 36px;
  border-radius: 50%;
  overflow: hidden;
  flex-shrink: 0;
  display: grid; place-items: center;
}
.search-dropdown__avatar img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}
.search-dropdown__avatar-initials {
  color: #fff; font-weight: 800;
  font-size: 13px;
  letter-spacing: 0.04em;
}
.search-dropdown__clef {
  width: 36px;
  text-align: center;
  font-size: 28px;        /* 𝄞 needs to be larger to read */
  line-height: 1;
  color: var(--okara-gold);
  flex-shrink: 0;
}
.search-dropdown__songcol {
  flex: 1; min-width: 0;
  display: flex; flex-direction: column;
  gap: 2px;
}
.search-dropdown__name {
  font-size: 14px; font-weight: 700;
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.search-dropdown__sub {
  font-size: 11px;
  color: var(--text-secondary);
  letter-spacing: 0.02em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.search-dropdown__row--singer .search-dropdown__sub {
  color: var(--okara-gold);
  text-transform: uppercase;
  font-weight: 700;
  letter-spacing: 0.05em;
  font-size: 10px;
}
.search-dropdown__badge {
  font-size: 9px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-tertiary);
  background: rgba(255,255,255,0.06);
  padding: 3px 6px;
  border-radius: 999px;
  white-space: nowrap;
}

.singer-detail__sentinel,
.song-list__sentinel {
  min-height: 56px;
  display: flex; align-items: center; justify-content: center;
  color: var(--text-tertiary);
  font-size: 11px;
  font-style: italic;
  padding: 12px;
}

/* ---------- Online tab (YouTube / SoundCloud / MixCloud) ---------- */

.online-body { display: flex; flex-direction: column; }

.online-source-row {
  display: flex;
  gap: 8px;
  padding: 14px 14px 0;
  overflow-x: auto;
  scrollbar-width: none;
}
.online-source-row::-webkit-scrollbar { display: none; }

.online-source-pill {
  padding: 8px 14px;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: none;
  cursor: pointer;
}
.online-source-pill:not(.is-active) {
  background: rgba(255,255,255,0.06);
  color: var(--text-secondary);
}
.online-source-pill svg { display: block; }

.online-thumb {
  width: 64px;
  height: 48px;
  flex-shrink: 0;
  border-radius: 6px;
  background-color: #000;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  display: grid;
  place-items: center;
  color: rgba(255,255,255,0.7);
}

.online-yougo {
  margin: 8px 12px 0;
  padding: 8px 12px;
  border-radius: 10px;
  background: rgba(255,216,61,0.12);
  border: 1px solid rgba(255,216,61,0.30);
  display: flex;
  align-items: center;
  gap: 10px;
}
.online-yougo__label {
  font-size: 11px;
  font-weight: 700;
  color: var(--okara-gold);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  flex-shrink: 0;
}
.online-yougo__msg {
  flex: 1;
  font-size: 11px;
  color: var(--text-secondary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.online-empty {
  padding: 40px 24px;
  text-align: center;
  color: var(--text-secondary);
  font-size: 13px;
}
.online-empty--loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
}

.online-spinner {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: 3px solid rgba(255,216,61,0.20);
  border-top-color: var(--okara-gold, #ffd83d);
  animation: online-spin 0.75s linear infinite;
}

/* Infinite-scroll sentinel — sits at the bottom of the results list.
   IntersectionObserver fires loadMoreResults() when this scrolls into
   view. Minimal styling so it doesn't compete with song rows. */
.online-load-sentinel {
  padding: 18px 16px;
  text-align: center;
  font-size: 12px;
  color: var(--text-secondary);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.online-load-sentinel.is-end {
  opacity: 0.55;
  font-style: italic;
}
@keyframes online-spin { to { transform: rotate(360deg); } }

/* Branded fallback thumbnails when the source doesn't expose an image URL. */
.online-thumb--brand-youtube    { background: linear-gradient(135deg, #e63946, #b71724); }
.online-thumb--brand-soundcloud { background: linear-gradient(135deg, #ff7700, #c25400); }
.online-thumb--brand-mixcloud   { background: linear-gradient(135deg, #52ACFF, #2b6fb3); }

/* When a new search is in flight but we still have stale results showing,
   dim the old list so the user knows it's about to be replaced. */
.song-list.is-stale {
  opacity: 0.55;
  pointer-events: none;
  transition: opacity 0.15s ease-out;
}

/* Per-row "sent" flash — 1.4s outline pulse confirming the tap registered. */
.online-row--sent {
  animation: online-row-flash 1.4s ease-out;
}
@keyframes online-row-flash {
  0%   { box-shadow: 0 0 0 0 rgba(255,216,61,0.85); border-color: var(--okara-gold, #ffd83d); }
  40%  { box-shadow: 0 0 0 6px rgba(255,216,61,0.0);  border-color: var(--okara-gold, #ffd83d); }
  100% { box-shadow: 0 0 0 0 rgba(255,216,61,0.0); }
}

/* ════════ Card kết quả YouTube — 16:11 ════════
   Ảnh 16:9 nguyên vẹn (không cắt, không bị che) + footer bên dưới. Tổng
   16:11. Chỉ TAG là đè lên ảnh; caption thì không. */
.online-card {
  position: relative;
  border-radius: 14px;
  overflow: hidden;
  background: rgba(255,255,255,0.04);
  border: 1px solid rgba(255,255,255,0.08);
  cursor: pointer;
  margin-bottom: 12px;
  transition: transform 0.12s ease-out, border-color 0.15s;
}
.online-card:active { transform: scale(0.985); }

.online-card-thumb {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;          /* ảnh giữ trọn tỉ lệ, footer nằm NGOÀI */
  background-size: cover;
  background-position: center;
  background-color: rgba(255,255,255,0.06);
}
/* Trình duyệt cũ không có aspect-ratio → padding-hack giữ đúng 16:9. */
@supports not (aspect-ratio: 16 / 9) {
  .online-card-thumb { height: 0; padding-bottom: 56.25%; }
}

.online-card-tags {
  position: absolute;
  top: 8px;
  display: flex;
  flex-direction: column;        /* REMIX nằm ngay dưới KARAOKE */
  align-items: flex-start;
  gap: 4px;
  z-index: 1;
}
.online-card-tags--left  { left: 8px; }
.online-card-tags--right { right: 8px; align-items: flex-end; }

.online-tag {
  display: inline-block;
  padding: 3px 7px;
  border-radius: 3px;            /* chữ nhật, bo rất nhẹ */
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.04em;
  line-height: 1.2;
  text-transform: uppercase;
  color: #fff;
  background: rgba(0,0,0,0.72);  /* đọc được trên mọi ảnh */
  backdrop-filter: blur(2px);
  white-space: nowrap;
}
.online-tag--karaoke { background: rgba(217,124,10,0.92); color: #fff; }
.online-tag--remix   { background: rgba(124,58,237,0.92); }
.online-tag--nam     { background: rgba(37,99,235,0.92); }
.online-tag--nu      { background: rgba(219,39,119,0.92); }
.online-tag--sc      { background: rgba(5,150,105,0.92); }

/* Thời lượng — góc dưới-phải của ảnh, quy ước YouTube. */
.online-card-dur {
  position: absolute;
  right: 8px;
  bottom: 8px;
  padding: 2px 6px;
  border-radius: 4px;
  font-size: 11px;
  font-weight: 600;
  color: #fff;
  background: rgba(0,0,0,0.78);
  line-height: 1.4;
}
.online-card-trending {
  position: absolute;
  left: 8px;
  bottom: 8px;
  padding: 2px 6px;
  border-radius: 4px;
  font-size: 10px;
  font-weight: 700;
  color: #3a1a00;
  background: var(--okara-gold, #ffd83d);
}

/* Footer — nền đặc, CHỈ mang tên bài đã làm sạch. */
.online-card-footer {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px;
  background: rgba(255,255,255,0.06);
}
.online-card-title {
  flex: 1;
  min-width: 0;
  font-size: 15px;
  font-weight: 600;
  color: var(--text-primary, #fff);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.online-card-more {
  flex-shrink: 0;
  width: 30px; height: 30px;
  display: grid; place-items: center;
  border: none;
  border-radius: 8px;
  background: transparent;
  color: var(--text-secondary, rgba(255,255,255,0.55));
  cursor: pointer;
}
.online-card-more:active { background: rgba(255,255,255,0.08); }

.online-card.online-row--sent { animation: online-row-flash 1.4s ease-out; }

/* ════════ Hàng bản ghi trong Hồ sơ ════════
   Bỏ nút tròn 44px; chạm cả hàng để phát. Chỗ trống dồn cho hai dòng chữ. */
.temp-file-row {
  align-items: center;
  min-height: 62px;              /* cao hơn để chứa đủ 2 dòng */
  cursor: pointer;
}
.temp-file-row.is-pending { opacity: 0.62; cursor: default; }
.temp-file-row.is-playing { border-color: var(--okara-gold, #ffd83d); }

/* Ca sĩ đứng cùng dòng với tên bài, nhạt hơn để tên bài vẫn nổi. */
.rec-singer {
  font-weight: 400;
  color: var(--text-secondary, rgba(255,255,255,0.55));
}

/* Dòng thông tin file — nhỏ, mờ, không tranh chỗ với tên bài. */
.rec-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  color: var(--text-secondary, rgba(255,255,255,0.5));
  font-variant-numeric: tabular-nums;
}
