/* =====================================================
   OKARA R — SPA app overrides
   Production SPA-specific tweaks on top of tokens.css + components.css
   ===================================================== */

/* SPA root takes over the page (replaces .mockup-frame from mockups).
   IMPORTANT: use `overflow-x: clip` (NOT `hidden`) — `hidden` silently
   promotes #app into a scrolling container which breaks
   `position: sticky` inside (sticky pins to the nearest scroll ancestor,
   and since #app rarely actually overflows, sticky never activates).
   `clip` cuts overflow without creating a scroll context.

   Responsive strategy:
     - Phone (default ≤767):    420px — single-thumb reach, original spec
     - Tablet (≥768):           100% (no cap) — fills the iPad screen
       fully. Content takes the whole width; playback bar separately
       shrinks + docks to bottom-LEFT (see .playback-bar block).
   --pb-max-width tracks the playback bar's own cap independently so
   tablets can have a content-full layout + a compact corner control. */
:root {
  --app-max-width: 420px;
  --pb-max-width: 420px;
}
#app {
  max-width: var(--app-max-width);
  margin: 0 auto;
  min-height: 100vh;
  min-height: 100dvh;
  background: var(--bg-gradient);
  background-attachment: fixed;
  position: relative;
  overflow-x: clip;
  padding-bottom: 96px; /* room for bottom playback bar */
}
@media (min-width: 768px) {
  :root {
    --app-max-width: 100%;
    --pb-max-width: 480px;
  }
}

/* When no playback bar (welcome/menu/recordings detail) the SPA can override per-view */
body {
  background: var(--bg-base);
  min-height: 100vh;
  min-height: 100dvh;
}

/* Bottom playback bar.
   Phone: centered, full container width — original layout.
   Tablet (≥768): dock to BOTTOM-LEFT, capped at --pb-max-width, with
     rounded corners so it floats above the fullscreen content. The
     #app container is now 100% wide on tablet, so a centered phone-
     sized bar in the middle would look orphaned; pinning to the
     corner reads as a true HUD control. */
.playback-bar {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  width: 100%;
  max-width: var(--pb-max-width);
  /* room for label below + iPhone home-bar safe area */
  padding-bottom: calc(8px + env(safe-area-inset-bottom));
}
@media (min-width: 768px) {
  .playback-bar {
    /* Dock bottom-left, leave a small inset from screen edge */
    left: calc(16px + env(safe-area-inset-left));
    right: auto;
    transform: none;
    border-radius: 16px 16px 0 0;
    box-shadow: 0 -4px 24px rgba(0, 0, 0, 0.45);
  }
}

/* Bottom-bar labels: tighten + allow wrap so 5 buttons fit on a 360px screen
   without 'PHÁT LẠ'/'QUA BÀI' running into each other. */
.pb-btn[data-label]::after {
  font-size: 9px;
  letter-spacing: 0.01em;
  max-width: 64px;
  text-align: center;
  white-space: normal;
  line-height: 1.05;
}

/* Hide playback bar entirely on welcome + sub-screens that override */
.no-bar .playback-bar { display: none; }
.no-bar #app { padding-bottom: env(safe-area-inset-bottom); }

/* Disconnected state — WSS closed / agent offline / session archived.
   Dim the whole bar + disabled buttons so the user can SEE that controls
   aren't live (the conn-banner above explains why). Disabled state is
   also wired at the DOM level via `disabled` attribute, so the click
   event never fires; this CSS is the visual reinforcement. */
.playback-bar.is-disconnected { opacity: 0.55; }
.playback-bar .pb-btn[disabled],
.playback-bar .pb-btn:disabled {
  cursor: not-allowed;
  pointer-events: none;
  filter: grayscale(0.4);
}

/* ─────────────── Sticky chrome (topbar + tabs) ───────────────
   Pins the toolbar + tab strip to the top regardless of scroll, and
   adds env(safe-area-inset-top) so iOS Dynamic Island / notch don't
   eat the menu button when the SPA is launched as PWA standalone. */
.sticky-header {
  position: sticky;
  top: 0;
  /* Bumped above the search-dropdown backdrop (z:25) so the search input
     stays crisp while the dim overlay only darkens the content area
     beneath the header. Using `isolation: isolate` so the stacking
     context is established here, not inherited from a transformed parent.
     When search dropdown is open, only the toolbar inside is on top —
     the tabs row is dimmed + disabled, see body.search-open rules below. */
  z-index: 50;
  isolation: isolate;
  /* The gradient backdrop fades through; we lay an opaque dark cover so
     scrolling content doesn't show through the sticky region. */
  background: linear-gradient(180deg,
    rgba(29, 13, 62, 0.96) 0%,
    rgba(58, 14, 84, 0.94) 80%,
    rgba(58, 14, 84, 0.88) 100%);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  padding-top: env(safe-area-inset-top);
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}
/* Tighter spacing inside the sticky header since the top inset already
   provides breathing room above. */
.sticky-header .top-toolbar { padding-top: 8px; padding-bottom: 6px; }
.sticky-header .tabs { padding-bottom: 0; }

/* When the unified search dropdown is open, the tabs row is dimmed (visual
   hint that focus is on the search input) but stays interactive — tapping
   a tab is a clear "leave the search" gesture, so we let it through.
   Without this, the dim + pointer-events:none combo turned every tab tap
   into a two-tap dance: tap-1 closed the dropdown via the backdrop, tap-2
   actually navigated. router.render() also closes the dropdown so this
   side-effect remains intact when the tap reaches the tab. */
body.search-open .sticky-header .tabs {
  opacity: 0.55;
  transition: opacity 0.15s var(--easing);
}
/* Playback bar stays click-disabled — tapping play/pause mid-typing is
   more likely accidental than intentional, so the strict gate makes sense
   here. */
body.search-open .playback-bar {
  opacity: 0.35;
  pointer-events: none;
  transition: opacity 0.15s var(--easing);
}

/* Top bar with back arrow + title (used for recordings, menu) */
.bar-title {
  flex: 1;
  font-size: 18px;
  font-weight: 800;
  padding-left: 6px;
  text-transform: uppercase;
  letter-spacing: 0.02em;
}

/* Make .tab clickable button look identical to anchor .tab */
button.tab {
  font-family: inherit;
  border: 1px solid var(--tab-border);
  border-bottom: none;
}
button.tab.active { border-color: transparent; }

/* Disabled tab look for Online (Phase 5+) */
.tab.disabled {
  opacity: 0.45;
  pointer-events: none;
  cursor: not-allowed;
}

/* Welcome / pair view (replaces old standalone welcome page) */
.welcome-view {
  display: flex;
  flex-direction: column;
  align-items: center;
  min-height: 100dvh;
  /* Pad past iOS notch / Dynamic Island when launched as PWA standalone.
     env() resolves to 0 on desktop / Android, so the base 24px is preserved. */
  padding: calc(24px + env(safe-area-inset-top)) 20px
           calc(20px + env(safe-area-inset-bottom));
  text-align: center;
  box-sizing: border-box;
}
.welcome-view .brand-stack {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin-top: 4vh;
  margin-bottom: 6vh;
}
/* Welcome screen brand mark — the combined OKARA + [R] SVG logo. Sized
   so the whole 412×70 wordmark + square fits comfortably on the typical
   ~360px mobile viewport (≈280px wide here gives ~48px tall). */
.welcome-view .welcome-brand-logo {
  width: 280px;
  max-width: 80%;
  height: auto;
  user-select: none;
  -webkit-user-drag: none;
  filter: drop-shadow(0 8px 24px rgba(0, 0, 0, 0.45));
  margin-bottom: 10px;
}
.welcome-view h1 {
  font-size: 24px;
  margin: 0 0 12px;
  font-weight: 700;
}
.welcome-view .lede {
  color: rgba(255, 255, 255, 0.85);
  font-size: 15px;
  line-height: 1.5;
  margin: 0 0 28px;
  padding: 0 12px;
}
.welcome-view .scan-visual {
  width: min(200px, 48vw);
  aspect-ratio: 1 / 1;
  margin: 0 auto;
  position: relative;
  border-radius: 24px;
  background: rgba(255, 255, 255, 0.04);
  border: 2px dashed rgba(255, 255, 255, 0.22);
  display: grid;
  place-items: center;
}
/* When the tile is the tap target (no separate "Quét QR" button), give
   it the press affordance: pointer cursor, lighter bg on hover, gentle
   pulse to draw attention, and a tactile press-shrink. */
.welcome-view .scan-visual.scan-visual-tap {
  transition: transform 120ms ease-out, background 200ms ease-out, border-color 200ms ease-out;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  animation: scanTilePulse 2.4s ease-in-out infinite;
}
.welcome-view .scan-visual.scan-visual-tap:hover {
  background: rgba(255, 216, 61, 0.08);
  border-color: rgba(255, 216, 61, 0.55);
}
.welcome-view .scan-visual.scan-visual-tap:active {
  transform: scale(0.96);
  background: rgba(255, 216, 61, 0.14);
}
.welcome-view .scan-visual.scan-visual-tap:focus-visible {
  outline: 2px solid var(--okara-gold);
  outline-offset: 4px;
}
@keyframes scanTilePulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(255, 216, 61, 0.0); }
  50%      { box-shadow: 0 0 28px 4px rgba(255, 216, 61, 0.18); }
}
.welcome-view .scan-visual::before,
.welcome-view .scan-visual::after,
.welcome-view .scan-visual > .br,
.welcome-view .scan-visual > .bl {
  content: "";
  position: absolute;
  width: 32px;
  height: 32px;
  border: 3px solid var(--okara-gold);
}
.welcome-view .scan-visual::before {
  top: -2px;
  left: -2px;
  border-right: none;
  border-bottom: none;
  border-radius: 12px 0 0 0;
}
.welcome-view .scan-visual::after {
  top: -2px;
  right: -2px;
  border-left: none;
  border-bottom: none;
  border-radius: 0 12px 0 0;
}
.welcome-view .scan-visual > .bl {
  bottom: -2px;
  left: -2px;
  border-right: none;
  border-top: none;
  border-radius: 0 0 0 12px;
}
.welcome-view .scan-visual > .br {
  bottom: -2px;
  right: -2px;
  border-left: none;
  border-top: none;
  border-radius: 0 0 12px 0;
}
.welcome-view .scan-visual .icon {
  font-size: 64px;
  opacity: 0.5;
}
.welcome-view .scan-visual .scan-line {
  position: absolute;
  left: 16px;
  right: 16px;
  height: 2px;
  background: linear-gradient(90deg, transparent, var(--okara-gold), transparent);
  box-shadow: 0 0 8px rgba(255, 216, 61, 0.6);
  animation: scan-sweep 2s ease-in-out infinite;
}
@keyframes scan-sweep {
  0%, 100% { top: 30px; opacity: 0.4; }
  50%      { top: 168px; opacity: 1; }
}
.welcome-view .pair-actions {
  width: 100%;
  max-width: 320px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.welcome-view .manual-entry {
  display: flex;
  gap: 8px;
}
.welcome-view .manual-entry input {
  flex: 1;
  height: 42px;
  border-radius: var(--radius-md);
  border: 1px solid rgba(255, 255, 255, 0.15);
  background: rgba(255, 255, 255, 0.06);
  color: #fff;
  padding: 0 14px;
  font-size: 14px;
  outline: none;
}
.welcome-view .manual-entry input::placeholder {
  color: rgba(255, 255, 255, 0.4);
}
.welcome-view .spinner {
  width: 48px;
  height: 48px;
  border: 3px solid rgba(255, 255, 255, 0.15);
  border-top-color: var(--okara-gold);
  border-radius: 50%;
  animation: spin 1s linear infinite;
  margin: 0 auto;
}
@keyframes spin { to { transform: rotate(360deg); } }
.welcome-view .err-msg {
  background: rgba(230, 57, 70, 0.16);
  border: 1px solid rgba(230, 57, 70, 0.45);
  color: #ffd8de;
  padding: 10px 14px;
  border-radius: var(--radius-md);
  font-size: 13px;
  margin-bottom: 14px;
}
.welcome-view footer {
  padding: 14px 0 4px;
  font-size: 11px;
  color: rgba(255, 255, 255, 0.5);
  letter-spacing: 0.06em;
}
.welcome-view footer .star { color: var(--okara-gold); }

/* ── Auth welcome (docs/AUTH_FLOW.md, compact single-screen layout) ──── */
.welcome-view .auth-stack {
  width: 100%;
  max-width: 360px;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 14px;
}
.welcome-view .auth-main {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
}
.welcome-view .auth-guest-btn {
  width: 100%;
  height: 50px;
  font-size: 15px;
  font-weight: 700;
}
.welcome-view .auth-countdown {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 6px;
}
.welcome-view .auth-countdown-bar {
  height: 4px;
  border-radius: 2px;
  background: rgba(255, 255, 255, 0.10);
  overflow: hidden;
}
.welcome-view .auth-countdown-fill {
  height: 100%;
  background: linear-gradient(90deg, var(--okara-gold), #ffe898);
  transition: width 900ms linear;
}
.welcome-view .auth-countdown-text {
  text-align: center;
  font-size: 12px;
  color: rgba(255, 255, 255, 0.55);
  font-variant-numeric: tabular-nums;
}
.welcome-view .scan-tap-hint {
  text-align: center;
  font-size: 13px;
  color: var(--text-secondary, rgba(255, 255, 255, 0.6));
  margin: 0;
  opacity: 0.85;
}

/* Telegram official widget — wrapper + slot.
   The script appends an iframe button into .auth-tg-slot. While waiting
   for the iframe to load (telegram.org round-trip), a hint sits above. */
.welcome-view .auth-tg-wrap {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  min-height: 50px;
  position: relative;
}
.welcome-view .auth-tg-slot-hint {
  font-size: 11px;
  color: rgba(255,255,255,0.5);
  font-style: italic;
  position: absolute;
  z-index: 0;
}
.welcome-view .auth-tg-slot {
  display: flex;
  justify-content: center;
  min-height: 40px;
  position: relative;
  z-index: 1;
}

.welcome-view .auth-tg-btn {
  width: 100%;
  height: 50px;
  border-radius: var(--radius-md);
  border: none;
  background: #229ED9;
  color: #fff;
  font-size: 15px;
  font-weight: 700;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: transform 120ms ease-out, filter 200ms ease-out, box-shadow 200ms;
  box-shadow: 0 4px 12px rgba(34, 158, 217, 0.32);
}
.welcome-view .auth-tg-btn:hover  { filter: brightness(1.08); }
.welcome-view .auth-tg-btn:active { transform: scale(0.98); }
.welcome-view .auth-tg-btn-icon {
  width: 22px;
  height: 22px;
  /* SVG ships with brand-blue fill; invert to white over our blue button. */
  filter: brightness(0) invert(1);
}

/* Login icon row — compact circular icons for Zalo + Telegram. Replaces
   the older full-width "Đăng nhập với Zalo" buttons in archive banner
   + welcome stack (2026-05-28). Both surfaces use this same row now,
   icon-only (no labels), to save vertical space. */
.login-icon-row {
  display: flex;
  gap: 14px;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
}
.login-icon {
  /* User request 2026-06-09 round 3: "bỏ luôn 2 đường tròn đi, để 2
     logo nho nhỏ thôi, hiệu ứng shadow một chút". Container is a wide
     tap target (the brand SVGs already have built-in whitespace padding
     so the visible glyph stays small inside this box) — no circle clip,
     no zoom, just a subtle drop-shadow on the glyph itself via the SVG
     filter so the soft drop sits on the artwork rather than on a
     coloured disc behind it. */
  width: 120px;
  height: 120px;
  background: transparent;
  border: none;
  display: grid;
  place-items: center;
  cursor: pointer;
  padding: 0;
  color: #fff;
  -webkit-tap-highlight-color: transparent;
  transition: transform 120ms ease-out, filter 200ms;
}
.login-icon svg {
  /* Generic-icon path (fallback when no brand SVG). */
  width: 48px;
  height: 48px;
  display: block;
}
/* OKARA brand SVGs have full colour baked in (Telegram cyan, Zalo
   navy + white outline) so we drop the colored circle background
   and let the icon stand on its own. The .login-icon button still
   provides the tap target + hover treatment. */
.login-icon-img {
  width: 100%;
  height: 100%;
  display: block;
  object-fit: contain;
  pointer-events: none;
  /* Soft drop on the glyph itself — filter:drop-shadow honours the
     SVG's alpha channel so the shadow traces the logo outline, not a
     bounding box. */
  filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.35));
}
/* Hover/active treatments target the inner image so the drop-shadow
   updates alongside the lift. The button itself is transparent. */
.login-icon:hover .login-icon-img  { transform: translateY(-2px); filter: drop-shadow(0 8px 14px rgba(0,0,0,0.42)) brightness(1.05); }
.login-icon:active .login-icon-img { transform: translateY(0);    filter: drop-shadow(0 3px 6px rgba(0,0,0,0.30)) brightness(0.95); }
.login-icon-img { transition: transform 120ms ease-out, filter 200ms; }
.login-icon.is-loading { opacity: 0.6; cursor: progress; }
/* Brand modifiers kept as no-op anchors — the SVG art carries the
   brand colour now. Reserved for future per-brand tweaks. */
.login-icon--zalo,
.login-icon--telegram { background: transparent; }

/* Zalo OAuth button — Zalo brand blue. Reused by:
   • welcome.js (auth stack, full width, primary CTA)
   • recordings.js archive banner (guest "Lưu bản ghi" CTA)
   Scope used to be `.welcome-view .auth-zalo-btn` but that meant archive
   banner usages fell through to browser defaults (looked huge + unstyled
   without the .welcome-view ancestor). Now global. */
.auth-zalo-btn {
  width: 100%;
  height: 44px;
  border-radius: var(--radius-md);
  border: none;
  background: #0068FF;
  color: #fff;
  font-size: 14px;
  font-weight: 700;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: transform 120ms ease-out, filter 200ms ease-out, box-shadow 200ms;
  box-shadow: 0 4px 12px rgba(0, 104, 255, 0.32);
}
/* Welcome adds top margin + slightly taller button for the primary
   landing-page CTA feel. */
.welcome-view .auth-zalo-btn {
  height: 50px;
  font-size: 15px;
  margin-top: 10px;
}
.auth-zalo-btn:hover  { filter: brightness(1.08); }
.auth-zalo-btn:active { transform: scale(0.98); }
.auth-zalo-btn-icon {
  width: 20px; height: 20px;
  filter: brightness(0) invert(1);
}
.welcome-view .auth-zalo-btn-icon { width: 22px; height: 22px; }

/* Guest as secondary text link when Telegram is the primary CTA. */
.welcome-view .auth-guest-link {
  width: 100%;
  height: 38px;
  background: transparent;
  border: none;
  color: rgba(255, 255, 255, 0.6);
  font-size: 13px;
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: rgba(255, 255, 255, 0.25);
}
.welcome-view .auth-guest-link:hover { color: rgba(255, 255, 255, 0.9); }

/* Toast container (above playback bar) */
.toast-container {
  position: fixed;
  left: 50%;
  bottom: calc(96px + env(safe-area-inset-bottom));
  transform: translateX(-50%);
  z-index: 60;
  display: flex;
  flex-direction: column;
  gap: 8px;
  pointer-events: none;
  max-width: 380px;
  width: calc(100% - 28px);
}
.toast {
  padding: 10px 14px;
  border-radius: var(--radius-md);
  font-size: 13px;
  font-weight: 600;
  background: rgba(0, 0, 0, 0.85);
  color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.1);
  backdrop-filter: blur(8px);
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.45);
  animation: toast-in 0.2s var(--easing);
  pointer-events: auto;
}
.toast.gold {
  background: linear-gradient(135deg, rgba(255, 216, 61, 0.95), rgba(217, 124, 10, 0.95));
  color: #3a1a00;
  border-color: rgba(255, 216, 61, 0.6);
}
.toast.red {
  background: rgba(230, 57, 70, 0.92);
  color: #fff;
  border-color: rgba(230, 57, 70, 0.6);
}
.toast.green {
  background: rgba(76, 175, 80, 0.92);
  color: #053912;
  border-color: rgba(76, 175, 80, 0.6);
}
@keyframes toast-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Connection banner — persistent, top of frame */
.conn-banner {
  background: rgba(230, 57, 70, 0.18);
  border-bottom: 1px solid rgba(230, 57, 70, 0.4);
  color: #ffd8de;
  font-size: 12px;
  text-align: center;
  padding: 6px 14px;
  font-weight: 600;
  letter-spacing: 0.04em;
}
.conn-banner.ok {
  background: rgba(76, 175, 80, 0.18);
  border-bottom-color: rgba(76, 175, 80, 0.4);
  color: #c6f0d2;
}
.conn-banner--archived {
  background: rgba(255, 216, 61, 0.14);
  border-bottom-color: rgba(255, 216, 61, 0.45);
  color: #ffe8a8;
  line-height: 1.35;
  font-weight: 600;
  padding: 8px 14px;
}

/* Recording mini player (sticky above playback bar) */
/* Pinned to viewport bottom edge, full width — same pattern as
   .playback-bar in the control view. No floating gap, no centering
   inset, no rounded corners flanked by air. The recordings archive
   view has no other bottom bar, so this is the operator's sole
   transport control and it should feel as anchored as the playback
   bar on the control tabs. */
.rec-mini-player {
  position: fixed;
  left: 0; right: 0;
  bottom: 0;
  /* Full viewport width — no max-width cap so wider phones / landscape
     get a player that fills the screen end-to-end like the playback bar. */
  z-index: var(--z-bottom-bar);
}
.rec-mini-player .body {
  background: rgba(0, 0, 0, 0.92);
  border: none;
  border-top: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 0;       /* flat edge against viewport bottom */
  padding: 10px 14px;
  padding-bottom: calc(10px + env(safe-area-inset-bottom));
  display: flex;
  align-items: center;
  gap: 12px;
  backdrop-filter: blur(8px);
}
.rec-mini-player .pb {
  width: 36px; height: 36px;
  border-radius: 50%;
  background: var(--okara-gold);
  border: none;
  display: grid; place-items: center;
  color: #3a1a00;
  flex-shrink: 0;
  cursor: pointer;
}
.rec-mini-player .close {
  width: 32px; height: 32px;
  border-radius: 50%;
  background: transparent;
  border: none;
  color: var(--text-secondary);
  display: grid; place-items: center;
  cursor: pointer;
}
/* Prev / Next nav buttons — flanking the play/pause. Disabled state
   greys out when the current track is the first / last in the list. */
.rec-mini-player .nav-btn {
  width: 36px; height: 36px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.14);
  color: #fff;
  display: grid; place-items: center;
  cursor: pointer;
  flex-shrink: 0;
  -webkit-tap-highlight-color: transparent;
  transition: background var(--duration-fast) var(--easing);
}
.rec-mini-player .nav-btn svg { fill: currentColor; display: block; }
.rec-mini-player .nav-btn:hover:not(:disabled) {
  background: rgba(255, 255, 255, 0.16);
}
.rec-mini-player .nav-btn:active:not(:disabled) { transform: scale(0.92); }
.rec-mini-player .nav-btn:disabled {
  opacity: 0.32;
  cursor: not-allowed;
}
/* Idle player — when nothing playing the player is shown but greyed
   so the layout doesn't jump when audio starts. */
.rec-mini-player--idle .body {
  background: rgba(0, 0, 0, 0.55);
}
.rec-mini-player--idle .pb {
  background: rgba(255, 255, 255, 0.10);
  color: rgba(255, 255, 255, 0.5);
  cursor: not-allowed;
}
.rec-mini-player .info { flex: 1; min-width: 0; }
.rec-mini-player .info .t {
  font-size: 13px;
  font-weight: 700;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.rec-mini-player .info .row {
  display: flex; align-items: center; gap: 8px;
  margin-top: 4px;
}
.rec-mini-player .info .row .time {
  font-size: 10px;
  font-family: var(--font-mono);
  color: var(--text-tertiary);
}
/* Scrub track — visible bar is 6px tall but the hit area is widened to 22px
   by transparent padding on a wrapper so finger taps register reliably on a
   phone. Using `box-sizing:content-box` (default) means the visible bar
   stays 6px while the wrapper grows. */
.rec-mini-player .info .row .track-hit {
  flex: 1;
  padding: 8px 0;
  cursor: pointer;
  touch-action: none;
  /* Negative margin keeps the row's visual height unchanged. */
  margin: -8px 0;
}
.rec-mini-player .info .row .track {
  width: 100%;
  height: 6px;
  background: rgba(255, 255, 255, 0.18);
  border-radius: 3px;
  overflow: hidden;
  position: relative;
}
.rec-mini-player .info .row .fill {
  height: 100%;
  background: linear-gradient(90deg, #ffd83d, #d97c0a);
  border-radius: 3px;
  transition: width 80ms linear;
  /* Faint inner glow so the boundary between fill + track is obvious even
     at low brightness or partial transparency. */
  box-shadow: 0 0 6px rgba(255, 216, 61, 0.5);
}

/* Volume modal (small slider sheet) */
.vol-sheet {
  display: flex; flex-direction: column;
  gap: 14px;
  padding-top: 6px;
}
.vol-sheet .vol-title {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--okara-gold);
  text-align: center;
}
.vol-sheet .vol-value {
  font-family: var(--font-mono);
  font-size: 36px;
  font-weight: 800;
  text-align: center;
  line-height: 1;
}
.vol-sheet input[type="range"] {
  appearance: none;
  -webkit-appearance: none;
  height: 6px;
  background: rgba(255, 255, 255, 0.12);
  border-radius: 3px;
  outline: none;
  margin: 12px 0;
}
.vol-sheet input[type="range"]::-webkit-slider-thumb {
  appearance: none;
  -webkit-appearance: none;
  width: 22px; height: 22px;
  border-radius: 50%;
  background: var(--okara-gold);
  border: 2px solid #fff;
  cursor: pointer;
  box-shadow: 0 1px 6px rgba(0, 0, 0, 0.4);
}
.vol-sheet input[type="range"]::-moz-range-thumb {
  width: 22px; height: 22px;
  border-radius: 50%;
  background: var(--okara-gold);
  border: 2px solid #fff;
  cursor: pointer;
}

/* E.4 — mobile EQ bottom sheet */
.sheet-title {
  font-size: 14px;
  font-weight: 800;
  margin-bottom: 6px;
  color: var(--okara-gold);
}
.sheet-hint {
  font-size: 12px;
  color: var(--text-tertiary);
  margin: 0 0 14px;
  line-height: 1.4;
}
.sheet-actions {
  margin-top: 18px;
  display: flex;
  justify-content: flex-end;
}
.sheet-actions .btn-ghost {
  background: transparent;
  border: 1px solid var(--card-border);
  color: var(--text-secondary);
  border-radius: 8px;
  padding: 8px 16px;
  font-size: 13px;
  cursor: pointer;
}
.eq-preset-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
}
.eq-preset-pill {
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--card-border);
  border-radius: 12px;
  padding: 12px;
  cursor: pointer;
  text-align: left;
  transition: background 120ms, border-color 120ms;
  color: var(--text-primary);
}
.eq-preset-pill strong {
  display: block;
  font-size: 15px;
  font-weight: 700;
  margin-bottom: 4px;
}
.eq-preset-pill .eq-preset-hint {
  font-size: 11px;
  color: var(--text-tertiary);
  line-height: 1.3;
}
.eq-preset-pill.is-active {
  background: rgba(255, 216, 61, 0.16);
  border-color: var(--okara-gold);
}
.eq-preset-pill:hover {
  background: rgba(255, 255, 255, 0.08);
}

/* Empty state for tabs */
.empty-state {
  padding: 60px 24px;
  text-align: center;
  color: var(--text-tertiary);
}
.empty-state .ic {
  font-size: 48px;
  opacity: 0.3;
  margin-bottom: 16px;
}
.empty-state .hint {
  font-size: 13px;
  line-height: 1.5;
  margin: 0;
}

/* Alphabet jump bar (singers) */
.alpha-bar {
  display: flex;
  gap: 4px;
  padding: 0 14px 8px;
  overflow-x: auto;
  scrollbar-width: none;
}
.alpha-bar::-webkit-scrollbar { display: none; }
.alpha-bar .letter {
  min-width: 26px;
  height: 26px;
  border-radius: 13px;
  background: rgba(255, 255, 255, 0.06);
  border: none;
  color: var(--text-secondary);
  font-size: 12px;
  font-weight: 700;
  display: grid;
  place-items: center;
  cursor: pointer;
  flex-shrink: 0;
}
.alpha-bar .letter.active {
  background: linear-gradient(135deg, #ffd83d, #d97c0a);
  color: #3a1a00;
}

/* Hide focus ring on touch-tapped elements but keep for keyboard */
button:focus:not(:focus-visible),
a:focus:not(:focus-visible) { outline: none; }
button:focus-visible,
a:focus-visible {
  outline: 2px solid var(--okara-gold);
  outline-offset: 2px;
}

/* ─────────── Profile (#/profile, Phase 5.B) ─────────── */
.profile-view {
  padding: 0 0 80px;        /* bottom breathing room — no playback bar here */
}

.profile-card {
  margin: 14px 14px 0;
  padding: 16px;
  background: var(--card-bg);
  border: 1px solid var(--card-border);
  border-radius: 14px;
  display: flex; gap: 14px; align-items: center;
}
.profile-avatar {
  width: 56px; height: 56px;
  border-radius: 50%;
  background: linear-gradient(135deg, #229ED9, #5536aa);
  display: grid; place-items: center;
  color: #fff; font-weight: 800; font-size: 20px;
  flex: 0 0 56px;
  box-shadow: 0 2px 10px rgba(0,0,0,0.3);
  letter-spacing: 0.02em;
}
.profile-avatar-img {
  object-fit: cover;
  background: rgba(255,255,255,0.05);
}
.profile-meta { flex: 1; min-width: 0; }
.profile-name {
  font-size: 17px; font-weight: 800; color: #fff;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.profile-provider-chip {
  display: inline-flex; align-items: center; gap: 6px;
  margin-top: 4px;
  padding: 3px 9px;
  background: rgba(34,158,217,0.18);
  border: 1px solid rgba(34,158,217,0.45);
  border-radius: 999px;
  font-size: 11px; color: #9ed6ef;
}
.profile-provider-chip::before {
  content: ""; width: 8px; height: 8px; border-radius: 50%;
  background: #4caf50;
}

.profile-stats {
  margin: 12px 14px 4px;
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 8px;
}
.profile-stat {
  padding: 10px;
  background: rgba(255,255,255,0.04);
  border: 1px solid var(--card-border);
  border-radius: 10px;
  text-align: center;
}
.profile-stat-n {
  font-size: 18px; font-weight: 800; color: var(--okara-gold);
  line-height: 1.1;
}
.profile-stat-l {
  font-size: 10px;
  color: var(--text-tertiary);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  margin-top: 2px;
}

.profile-subtabs {
  display: flex; gap: 8px;
  margin: 14px 14px 6px;
  padding: 4px;
  background: rgba(0,0,0,0.28);
  border-radius: 12px;
}
.profile-subtab {
  flex: 1;
  padding: 9px 12px;
  background: transparent;
  border: none;
  color: rgba(255,255,255,0.6);
  font-size: 13px; font-weight: 700;
  border-radius: 9px;
  cursor: pointer;
  transition: background 180ms, color 180ms;
}
.profile-subtab.active {
  background: var(--tab-active-bg);
  color: var(--tab-active-text);
}
.profile-subtab-content {
  min-height: 200px;
}

/* Subtle explainer at the top of the File tạm tab — tells the user that
   unsaved files are time-limited so the heart toggle has real stakes. */
.profile-temp-hint {
  margin: 4px 18px 10px;
  font-size: 11px;
  color: var(--text-tertiary);
  letter-spacing: 0.02em;
  line-height: 1.4;
}

/* Session grouping in File tạm — sticky headers anchor recordings by
   karaoke-session date so users with many sessions can scan their library
   chronologically. */
.profile-temp-group + .profile-temp-group { margin-top: 12px; }
.profile-temp-group-header {
  position: sticky;
  top: 0;
  z-index: 5;
  margin: 0 14px 6px;
  padding: 6px 10px;
  background: linear-gradient(180deg, rgba(20, 22, 36, 0.95), rgba(20, 22, 36, 0.88));
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  border-bottom: 1px solid rgba(255, 216, 61, 0.18);
  font-size: 11px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.10em;
  color: var(--okara-gold);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.profile-temp-group-count {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: rgba(255, 255, 255, 0.55);
  text-transform: none;
}
.profile-temp-group-title {
  flex: 1; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.profile-temp-group-venue {
  font-weight: 500;
  color: rgba(255, 255, 255, 0.45);
  text-transform: none;
  letter-spacing: 0.02em;
}
.profile-temp-group-meta {
  display: flex; align-items: center; gap: 8px;
  flex-shrink: 0;
}
/* Time-remaining chip — "Còn X ngày" for the earliest-expiring rec in
   this group. Red when ≤3 days so the operator sees they should save soon. */
.profile-temp-group-remain {
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.04em;
  padding: 1px 6px;
  border-radius: 4px;
  background: rgba(255, 255, 255, 0.08);
  color: rgba(255, 255, 255, 0.6);
  text-transform: none;
}
.profile-temp-group-remain--urgent {
  background: rgba(230, 57, 70, 0.18);
  color: #f3a4ab;
  border: 1px solid rgba(230, 57, 70, 0.35);
}

/* Inline countdown chip on each File tạm row — shows days until auto-purge,
   or "Giữ vĩnh viễn" once the user ❤ it. Sits next to the sub-line meta. */
.temp-expires-chip {
  display: inline-block;
  margin-left: 8px;
  padding: 1px 6px;
  border-radius: 4px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.02em;
  vertical-align: middle;
  white-space: nowrap;
}
.temp-expires-chip--normal {
  background: rgba(255, 255, 255, 0.08);
  color: rgba(255, 255, 255, 0.6);
  border: 1px solid rgba(255, 255, 255, 0.12);
}
.temp-expires-chip--urgent {
  background: rgba(230, 57, 70, 0.18);
  color: #f3a4ab;
  border: 1px solid rgba(230, 57, 70, 0.4);
}
.temp-expires-chip--kept {
  background: rgba(255, 216, 61, 0.16);
  color: var(--okara-gold);
  border: 1px solid rgba(255, 216, 61, 0.35);
}
/* Wider tab strip to fit 4 sub-tabs (File tạm / Bản ghi / Bài tủ / Lịch sử)
   on narrow phones. Slightly tighter padding so labels don't get truncated. */
.profile-subtabs { gap: 2px; }
.profile-subtab { padding: 9px 6px; font-size: 12px; letter-spacing: 0.02em; }

/* Archive-mode auth CTA — appears between toolbar and recording list.
   Two states: known user (link to profile) | guest (Telegram widget). */
.archive-auth-banner {
  margin: 12px 14px;
  padding: 12px 14px;
  background: var(--card-bg);
  border: 1px solid rgba(255,216,61,0.22);
  border-radius: 12px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  text-decoration: none;
  color: inherit;
}
.archive-auth-banner--known {
  background: linear-gradient(135deg, rgba(255,216,61,0.18), rgba(34,158,217,0.10));
  border-color: rgba(255,216,61,0.38);
  cursor: pointer;
}
.archive-auth-banner--known:hover { filter: brightness(1.05); }
.archive-auth-banner-text { display: flex; flex-direction: column; gap: 2px; }
.archive-auth-banner-line1 {
  font-size: 14px; font-weight: 700; color: #fff;
}
.archive-auth-banner-line2 {
  font-size: 11.5px; color: rgba(255,255,255,0.75);
}
.archive-auth-banner-widget {
  display: flex; justify-content: center;
  min-height: 40px;
}

/* Profile-card stacked action buttons (QR scan + settings gear). */
.profile-card-actions {
  position: absolute;
  top: 10px;
  right: 10px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.profile-action-btn {
  width: 36px; height: 36px;
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.14);
  color: rgba(255, 255, 255, 0.7);
  display: grid; place-items: center;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background 180ms, transform 120ms;
}
.profile-action-btn:active { transform: scale(0.9); }
.profile-action-btn:hover  { background: rgba(255, 255, 255, 0.12); }
.profile-action-btn--primary {
  background: rgba(255, 216, 61, 0.16);
  border-color: rgba(255, 216, 61, 0.38);
  color: var(--okara-gold);
}
.profile-action-btn--primary:hover { background: rgba(255, 216, 61, 0.26); }
.profile-action-btn--logout {
  background: rgba(230, 57, 70, 0.12);
  border-color: rgba(230, 57, 70, 0.32);
  color: rgba(230, 57, 70, 0.85);
}
.profile-action-btn--logout:hover  { background: rgba(230, 57, 70, 0.22); }
.profile-action-btn--logout:active { color: #e63946; }
/* "Rời phòng" — orange/amber accent to distinguish from logout (red). It's
   destructive of the current session but not the account, so it gets
   a warning hue rather than danger red. */
.profile-action-btn--leave {
  background: rgba(255, 158, 36, 0.12);
  border-color: rgba(255, 158, 36, 0.36);
  color: rgba(255, 200, 100, 0.95);
}
.profile-action-btn--leave:hover  { background: rgba(255, 158, 36, 0.22); }
.profile-action-btn--leave:active { color: #ff9e24; }
.profile-action-btn svg { fill: currentColor; }

/* Swipe-reveal recording row: foreground sits over two action backgrounds
   (download on the right side revealed by swiping right, delete on the
   left side revealed by swiping left). */
/* Row outer wrapper — no horizontal margin so the item edges line up
   with the section header (the parent .profile-rec-list already adds
   the 14px screen-edge inset; the old extra 14px here made items
   "tụt 2 bên" relative to the header). */
.rec-swipe-wrap {
  position: relative;
  margin: 0 0 8px;
  border-radius: 12px;
  overflow: hidden;
}
.rec-swipe-fg {
  position: relative;
  z-index: 2;
  margin: 0 !important;
  /* Bump opacity vs the previous --card-bg (which was ~6% white) — was
     reading as nearly transparent on the page gradient. */
  background: rgba(28, 28, 42, 0.85);
  border: 1px solid rgba(255, 255, 255, 0.10);
  border-radius: 12px;
  touch-action: pan-y;          /* allow vertical scroll; horizontal we handle */
  transition: transform 220ms ease-out;
  will-change: transform;
  cursor: pointer;
}
/* Swipe-reveal action buttons — shrunk to ~1/3 the previous 88px width
   so the swipe gesture exposes a smaller hit target (less visual real
   estate stolen from the row on a tiny accidental swipe). */
.rec-swipe-action {
  position: absolute;
  top: 0; bottom: 0;
  width: 32px;
  display: grid; place-items: center;
  border: none;
  color: #fff;
  z-index: 1;
  cursor: pointer;
  text-decoration: none;
}
.rec-swipe-action svg { fill: currentColor; }
.rec-swipe-action--download {
  left: 0;
  background: linear-gradient(135deg, var(--okara-gold), #d97c0a);
  color: #3a1a00;
}
.rec-swipe-action--delete {
  right: 0;
  background: var(--okara-red);
}

/* Mini-player skip buttons. */
.rec-mini-player .pb-skip {
  background: rgba(255,255,255,0.08);
  color: rgba(255,255,255,0.85);
}
.rec-mini-player .pb-skip.is-disabled {
  opacity: 0.3;
  cursor: not-allowed;
}

/* ─── Custom confirm + bottom-sheet styles ─── */
.okara-sheet {
  background: linear-gradient(180deg, #2a0e4a 0%, #1a0a35 100%);
  border-radius: 16px 16px 0 0;
  padding: 18px 18px calc(18px + env(safe-area-inset-bottom));
  margin-top: auto;
  width: 100%;
  /* Capped at 640 on tablet so bottom sheets (EQ, recordings player)
     stay readable instead of stretching across the whole screen. */
  max-width: 640px;
  box-shadow: 0 -8px 32px rgba(0,0,0,0.5);
}
.okara-sheet-title {
  font-size: 16px; font-weight: 800; color: #fff;
  margin-bottom: 14px;
}
.okara-sheet-body { display: flex; flex-direction: column; gap: 12px; }
.okara-sheet-actions {
  display: flex; gap: 10px; margin-top: 18px;
}
.okara-sheet-actions .btn { flex: 1; }

/* Logout footer in the settings sheet — quiet by design (small + muted)
   so it doesn't compete with the Save action above. */
.okara-sheet-logout {
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px solid rgba(255, 255, 255, 0.08);
  text-align: center;
}
.okara-logout-link {
  background: transparent;
  border: none;
  color: rgba(230, 57, 70, 0.85);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.04em;
  padding: 6px 10px;
  cursor: pointer;
  text-transform: uppercase;
}
.okara-logout-link:active { color: #e63946; }

.okara-confirm {
  text-align: center;
}
.okara-confirm-title {
  font-size: 16px; font-weight: 800; color: #fff;
  margin-bottom: 8px;
}
.okara-confirm-body {
  font-size: 13px; color: rgba(255,255,255,0.7);
  line-height: 1.5; margin-bottom: 18px;
}
.okara-confirm-actions { display: flex; gap: 10px; }
.okara-confirm-actions .btn { flex: 1; }

.okara-field { display: flex; flex-direction: column; gap: 4px; }
.okara-field label {
  font-size: 11px; color: var(--text-tertiary);
  text-transform: uppercase; letter-spacing: 0.08em;
  font-weight: 700;
}
.okara-field input {
  height: 40px;
  background: rgba(255,255,255,0.06);
  border: 1px solid rgba(255,255,255,0.15);
  border-radius: 8px;
  color: #fff;
  padding: 0 12px;
  font-size: 14px;
}
.okara-field input:focus { outline: 1px solid var(--okara-gold); }
.okara-field-hint {
  margin: 2px 0 0;
  font-size: 11px; color: var(--text-tertiary);
}

/* QR-scan icon inside the profile-card. Pinned top-right of the card so
   it stays within the rounded boundary (no parent overflow) and remains
   discoverable as a per-card action. */
.profile-card {
  position: relative;
}
.profile-join-fab {
  position: absolute;
  top: 10px;
  right: 10px;
  width: 36px; height: 36px;
  border-radius: 10px;
  background: rgba(255, 216, 61, 0.16);
  border: 1px solid rgba(255, 216, 61, 0.35);
  color: var(--okara-gold);
  display: grid; place-items: center;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background 180ms, transform 120ms ease-out;
}
.profile-join-fab:hover  { background: rgba(255, 216, 61, 0.26); }
.profile-join-fab:active { transform: scale(0.92); }
.profile-join-fab svg { fill: currentColor; }

/* Saved-recording card in profile.Bản ghi (Phase 5.B). Richer than the
   in-session song-item — embeds an HTML5 audio player + action row. */
.profile-rec-list {
  display: flex; flex-direction: column;
  gap: 10px;
  padding: 0 14px;
}
.rec-card {
  padding: 12px 14px;
  background: var(--card-bg);
  border: 1px solid var(--card-border);
  border-radius: 12px;
  /* Bài Tủ swipe-fg has body+share button — flex aligns them horizontally. */
  display: flex; align-items: center; gap: 8px;
}
.rec-card-share {
  flex: 0 0 36px;
  width: 36px; height: 36px;
  border-radius: 50%;
  background: rgba(255,255,255,0.06);
  border: none;
  color: var(--okara-gold);
  display: grid; place-items: center;
  cursor: pointer;
  transition: background 120ms ease;
}
.rec-card-share:active { background: rgba(255,216,61,0.18); transform: scale(0.94); }
.rec-card-head { display: flex; align-items: flex-start; gap: 12px; }
.rec-card-icon {
  width: 38px; height: 38px; border-radius: 10px;
  background: linear-gradient(135deg, #ffd83d, #d97c0a);
  display: grid; place-items: center; color: #3a1a00;
  flex: 0 0 38px;
}
.rec-card-body { flex: 1; min-width: 0; }
.rec-card-title {
  font-size: 14.5px; font-weight: 700; color: #fff;
  line-height: 1.3;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rec-card-sub {
  font-size: 11.5px; color: var(--text-tertiary);
  margin-top: 3px;
}
.rec-card-player {
  width: 100%;
  margin: 10px 0 4px;
  height: 36px;
}
/* Now-playing row highlight in profile.Bản ghi list. */
.rec-card.is-playing {
  border-color: rgba(255, 216, 61, 0.55);
  background: linear-gradient(135deg, rgba(255,216,61,0.10), rgba(50,20,60,0.78));
}
.rec-card-play {
  border: none; cursor: pointer;
  transition: transform 120ms ease-out;
}
.rec-card-play:active { transform: scale(0.92); }

/* Profile's mini-player sits at the very bottom (no playback bar below
   in profile mode). Reset the rec-mini-player offset to safe-area only. */
.profile-mini-player {
  bottom: env(safe-area-inset-bottom);
}
.rec-card-actions {
  display: flex; gap: 4px;
  margin-top: 8px;
  border-top: 1px solid rgba(255,255,255,0.06);
  padding-top: 8px;
}
.rec-act {
  flex: 1;
  padding: 8px;
  background: transparent;
  border: none;
  color: var(--text-secondary);
  font-size: 12px; font-weight: 600;
  border-radius: 6px;
  cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center; gap: 4px;
  text-decoration: none;
}
.rec-act:hover { background: rgba(255,255,255,0.06); color: #fff; }
.rec-act.danger { color: var(--okara-red); }
.rec-act.danger:hover { background: rgba(230,57,70,0.12); }
.rec-act svg { width: 14px; height: 14px; }

/* ─────────── Boot splash ─────────── */
/* Covers the SPA at first paint, fades out once content is in place.
   Sits above #app via z-index; gone-state disables pointer-events so
   it doesn't trap taps after the fade animation completes. */
#okara-splash {
  position: fixed;
  inset: 0;
  z-index: 999;
  background: var(--bg-gradient);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 36px;
  opacity: 1;
  transition: opacity 360ms ease-out;
}
#okara-splash.hide {
  opacity: 0;
  pointer-events: none;
}
.okara-splash-logo {
  width: 200px;
  max-width: 60%;
  height: auto;
  filter: drop-shadow(0 8px 24px rgba(0,0,0,0.5));
  animation: splash-logo-pulse 1.8s ease-in-out infinite;
}
@keyframes splash-logo-pulse {
  0%, 100% { transform: scale(1); opacity: 1; }
  50%      { transform: scale(1.04); opacity: 0.85; }
}
.okara-splash-spinner {
  width: 36px;
  height: 36px;
  border: 3px solid rgba(255,255,255,0.18);
  border-top-color: var(--okara-gold);
  border-radius: 50%;
  animation: splash-spin 1s linear infinite;
}
@keyframes splash-spin { to { transform: rotate(360deg); } }

/* ─────── Profile sub-tab content: Bài tủ + Lịch sử ─────── */
/* Bài tủ + Lịch sử rows — no horizontal margin so they align with
   the section header (parent .profile-rec-list owns the 14px inset). */
.tu-card {
  margin: 0 0 10px;
  padding: 14px;
  background: rgba(28, 28, 42, 0.85);
  border: 1px solid rgba(255, 255, 255, 0.10);
  border-radius: 12px;
  display: flex; align-items: center; gap: 12px;
}
.tu-rank {
  width: 32px; height: 32px; border-radius: 50%;
  background: linear-gradient(135deg, #c5318c, #5536aa);
  color: #fff; font-weight: 800; font-size: 14px;
  display: grid; place-items: center;
  flex: 0 0 32px;
}
.tu-body { flex: 1; min-width: 0; }
.tu-body .t {
  font-size: 15px; font-weight: 800; color: #fff;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tu-body .s {
  font-size: 11.5px; color: var(--text-tertiary);
  margin-top: 3px;
}

.hist-item {
  margin: 0 0 8px;
  padding: 12px 14px;
  background: rgba(28, 28, 42, 0.85);
  border: 1px solid rgba(255, 255, 255, 0.10);
  border-radius: 10px;
  display: flex; align-items: center; gap: 12px;
}
.hist-item .info { flex: 1; min-width: 0; }
.hist-item .info .t {
  font-size: 14px; font-weight: 700; color: #fff;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.hist-item .info .s {
  font-size: 11px; color: var(--text-tertiary);
  margin-top: 2px;
}
.hist-item .rec-chip {
  padding: 5px 10px;
  background: rgba(255,216,61,0.12);
  border: 1px solid rgba(255,216,61,0.35);
  border-radius: 999px;
  font-size: 11px; color: var(--okara-gold);
  font-weight: 700;
  white-space: nowrap;
  cursor: pointer;
}
.hist-item .rec-chip:hover { background: rgba(255,216,61,0.22); }

/* ─── Share player (#/r/:id) ───────────────────────────────────────
 * Phone-portrait card for guest playback. Full-bleed brand gradient
 * background; centered card with hero title, big play button, scrub,
 * meta grid, download CTA.
 */
.share-page {
  /* User request 2026-06-10: chỉ card center, no page bg, no scroll.
     Fixed full-viewport overlay → card về giữa, transparent background,
     overflow hidden ngăn scroll page. Card tự cap chiều cao 95vh + scroll
     nội bộ nếu nội dung dài trên màn hình nhỏ — overflow trong card không
     lên page level. */
  position: fixed;
  inset: 0;
  padding: 16px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  overflow: hidden;
  z-index: 1;
}
.share-card {
  width: 100%;
  max-width: 420px;
  max-height: calc(100vh - 32px);
  overflow-y: auto;
  background: linear-gradient(180deg, rgba(28,28,42,0.92), rgba(20,20,29,0.88));
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 20px;
  padding: 22px 18px 24px;
  box-shadow: 0 14px 50px rgba(0,0,0,0.5);
}

.share-brand {
  display: flex; align-items: baseline; justify-content: space-between;
  margin-bottom: 18px;
}
.share-brand__logo {
  font-size: 18px; font-weight: 900;
  background: linear-gradient(90deg, #ffd83d, #d97c0a);
  -webkit-background-clip: text; background-clip: text; color: transparent;
  letter-spacing: 0.04em;
}
.share-brand__tag {
  font-size: 10px; font-weight: 700; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--text-tertiary);
}

.share-hero {
  text-align: center;
  margin-bottom: 18px;
}
.share-hero__title {
  font-size: 22px; font-weight: 900; color: #fff;
  line-height: 1.25;
  word-wrap: break-word;
}
.share-hero__singer {
  font-size: 13px; color: var(--okara-gold);
  margin-top: 4px;
  font-weight: 600;
}

.share-play-wrap {
  display: flex; justify-content: center;
  margin: 14px 0 18px;
}
.share-play-btn {
  width: 96px; height: 96px;
  border-radius: 50%;
  background: linear-gradient(135deg, #ffd83d, #d97c0a);
  color: #3a1a00;
  border: none;
  display: grid; place-items: center;
  cursor: pointer;
  box-shadow: 0 8px 24px rgba(217,124,10,0.45);
  transition: transform 120ms ease;
}
.share-play-btn:active { transform: scale(0.96); }

.share-scrub {
  display: flex; align-items: center; gap: 10px;
  margin-bottom: 22px;
}
.share-time {
  font-size: 11px; font-variant-numeric: tabular-nums;
  color: var(--text-tertiary);
  min-width: 38px;
  text-align: center;
}
.share-track-hit {
  flex: 1;
  padding: 10px 0;
  cursor: pointer;
  touch-action: none;
}
.share-track {
  height: 6px;
  background: rgba(255,255,255,0.10);
  border-radius: 3px;
  overflow: hidden;
}
.share-fill {
  height: 100%;
  background: linear-gradient(90deg, #ffd83d, #d97c0a);
  transition: width 120ms linear;
}

/* Owner attribution card — sits between scrub bar and meta grid.
   Avatar + display name read as a byline ("Ghi bởi: Phạm Tú"). */
.share-owner-card {
  display: flex; align-items: center; gap: 12px;
  margin-bottom: 14px;
  padding: 10px 12px;
  background: rgba(255,216,61,0.06);
  border: 1px solid rgba(255,216,61,0.18);
  border-radius: 12px;
}
.share-owner-avatar {
  width: 40px; height: 40px;
  border-radius: 50%;
  object-fit: cover;
  flex: 0 0 40px;
  background: rgba(255,255,255,0.08);
}
.share-owner-avatar--fallback {
  display: grid; place-items: center;
  font-size: 18px; font-weight: 800;
  background: linear-gradient(135deg, #ffd83d, #d97c0a);
  color: #3a1a00;
}
.share-owner-meta { flex: 1; min-width: 0; }
.share-owner-label {
  font-size: 10.5px; color: var(--text-tertiary);
  font-weight: 700; letter-spacing: 0.06em;
  text-transform: uppercase;
}
.share-owner-name {
  font-size: 14px; color: #fff; font-weight: 700;
  margin-top: 2px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.share-meta-grid {
  background: rgba(255,255,255,0.03);
  border: 1px solid rgba(255,255,255,0.06);
  border-radius: 12px;
  padding: 12px 14px;
  margin-bottom: 18px;
}
.share-meta-row {
  display: flex; justify-content: space-between; align-items: center;
  padding: 7px 0;
  border-bottom: 1px solid rgba(255,255,255,0.05);
  font-size: 13px;
}
.share-meta-row:last-child { border-bottom: none; }
.share-meta-label {
  color: var(--text-tertiary);
  font-weight: 600;
  text-transform: uppercase;
  font-size: 10.5px;
  letter-spacing: 0.06em;
}
.share-meta-value {
  color: #fff;
  font-weight: 700;
  text-align: right;
  max-width: 60%;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.share-actions { margin-top: 6px; }
.share-actions .btn { display: flex; align-items: center; justify-content: center; }

/* Green Download pill (2026-06-10) — reference image: bright green
   capsule with a lighter green icon tile inset on the left. Tile is
   a rounded square, glyph + label both white uppercase bold. */
.share-download-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 56px;
  padding: 0 28px;
  border: none;
  border-radius: 16px;
  /* Two-stop vertical gradient (lighter top → darker bottom) + bottom
     darker rim via inset shadow gives the button a raised "3D" look
     without resorting to a heavy bezel. Outer drop adds floating
     elevation. */
  background: linear-gradient(180deg, #5BCB68 0%, #3FB54B 55%, #2E9F3A 100%);
  color: #fff;
  cursor: pointer;
  box-shadow:
    0 6px 14px rgba(46, 159, 58, 0.42),
    inset 0 1px 0 rgba(255, 255, 255, 0.35),
    inset 0 -2px 0 rgba(0, 0, 0, 0.18);
  transition: transform 120ms ease-out, box-shadow 200ms ease-out, filter 200ms;
}
.share-download-btn:hover {
  filter: brightness(1.05);
  box-shadow:
    0 8px 20px rgba(46, 159, 58, 0.55),
    inset 0 1px 0 rgba(255, 255, 255, 0.40),
    inset 0 -2px 0 rgba(0, 0, 0, 0.18);
}
.share-download-btn:active {
  transform: translateY(1px);
  filter: brightness(0.96);
  box-shadow:
    0 3px 8px rgba(46, 159, 58, 0.40),
    inset 0 1px 0 rgba(255, 255, 255, 0.20),
    inset 0 -1px 0 rgba(0, 0, 0, 0.18);
}
.share-download-btn__label {
  font-size: 20px;
  font-weight: 800;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.share-loading {
  display: flex; flex-direction: column; align-items: center;
  padding: 60px 18px;
}
.share-loading__spinner {
  width: 36px; height: 36px;
  border: 3px solid rgba(255,255,255,0.10);
  border-top-color: var(--okara-gold);
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}
.share-loading__text {
  margin-top: 16px;
  font-size: 13px; color: var(--text-tertiary);
}

.share-error {
  text-align: center;
  padding: 40px 22px;
  background: linear-gradient(180deg, rgba(28,28,42,0.92), rgba(20,20,29,0.88));
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 20px;
  margin: max(40px, env(safe-area-inset-top)) auto 0;
  max-width: 420px;
  width: 100%;
}
.share-error__icon {
  width: 64px; height: 64px;
  margin: 0 auto 14px;
  border-radius: 50%;
  background: rgba(230,57,70,0.18);
  display: grid; place-items: center;
  color: var(--okara-red);
}
.share-error__title { font-size: 17px; font-weight: 800; color: #fff; }
.share-error__body { font-size: 13px; color: var(--text-secondary); margin-top: 6px; line-height: 1.5; }
