/* ── Vault.audio additions on top of the inherited theme ── */

/* ── Mobile foundations (safe-area + stack-height vars) ── */
/* NOTE: this :root stays UNLAYERED (outside @layer) so tokens always resolve
   regardless of cascade layers. Single source of truth for layout tokens. */
:root {
    --safe-top:    env(safe-area-inset-top, 0px);
    --safe-bottom: env(safe-area-inset-bottom, 0px);
    --safe-left:   env(safe-area-inset-left, 0px);
    --safe-right:  env(safe-area-inset-right, 0px);

    /* stack heights — HONEST values per band. Default 0 (tablet/desktop have no
       tab bar); the phone band (<=767px) overrides --tabbar-h to 56px below. */
    --tabbar-h: 0px;          /* phone band sets 56px; tablet/desktop keep 0 */
    --player-h: 0px;          /* JS sets to real bar height (offsetHeight) when a track is loaded */
    --player-h-loaded: 60px;  /* phone mini-player real height (reference) */

    /* composed bottom reservation — the ONE token every fixed bottom element uses */
    --bottom-stack: calc(var(--tabbar-h) + var(--player-h) + var(--safe-bottom));

    /* canonical breakpoints (reference values; @media queries still need literals) */
    --bp-phone: 767px;
    --bp-tablet: 1023px;
    --bp-desktop: 1024px;

    /* z-index scale — replaces all ad-hoc values across both stylesheets */
    --z-base: 0;
    --z-sticky: 30;
    --z-tabbar: 90;
    --z-fixed: 100;
    --z-drawer: 200;
    --z-overlay: 300;
    --z-modal: 400;
    --z-toast: 500;
}
/* Phone band restores the tab-bar reserve (tab bar only exists <=767px) */
@media (max-width: 767px), (min-width: 768px) and (max-width: 1024px) and (orientation: portrait) {
    :root { --tabbar-h: 56px; }
    /* Full-screen AI chat (T4.1): tab bar is hidden in an active conversation,
       so drop its reserve. UNLAYERED (like :root above) so it wins the cascade. */
    body.ai-chat-active { --tabbar-h: 0px; }
}

/* ── All vault.css rules live in the `responsive` cascade layer (declared last
   in style.css → wins over style.css `components` regardless of <link> order).
   :root token blocks above stay UNLAYERED so custom properties always resolve. ── */
@layer responsive {
/* Kill the 300ms tap delay + double-tap zoom on controls; keep momentum scroll */
button, .nav-item, .tab, a[role="button"] { touch-action: manipulation; }
/* Prevent overscroll chaining everywhere (also disables browser pull-to-refresh) */
html, body { overscroll-behavior-y: contain; }
/* iOS WebKit text auto-inflation: when a block (e.g. expanded research <details>)
   grows tall, Safari boosts font-size of its text runs. We size all type
   explicitly, so disable inflation to keep the research log + niches stable. */
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }

/* Center the auth screen (CSS in style.css targets #login-screen; we use #auth-screen) */
#auth-screen {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    min-height: 100dvh;
    /* black-translucent status bar draws under the iOS clock; safe insets keep
       the login box clear of the notch / status bar / landscape rounded corners. */
    padding: calc(var(--safe-top) + 20px) calc(var(--safe-right) + 16px)
             calc(var(--safe-bottom) + 20px) calc(var(--safe-left) + 16px);
    background:
        radial-gradient(ellipse at top, rgba(201, 168, 94, 0.08) 0%, transparent 50%),
        radial-gradient(ellipse at bottom, rgba(201, 168, 94, 0.04) 0%, transparent 60%),
        var(--bg);
}

/* Login/register forms */
.auth-form {
    display: flex;
    flex-direction: column;
    gap: 12px;
    margin-bottom: 20px;
}

#auth-screen .login-box {
    width: min(420px, 100% - 2rem);
}

/* Login: full vault.audio wordmark, hero-sized */
.login-logo {
    display: block;
    width: 300px;
    height: auto;
    margin: -8px auto 4px;
    filter: drop-shadow(0 16px 48px rgba(0, 0, 0, 0.6))
            drop-shadow(0 0 32px rgba(239, 68, 68, 0.18));
}
.login-brand {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 18px;
    margin: -4px auto 14px;
    filter: drop-shadow(0 16px 48px rgba(0, 0, 0, 0.6))
            drop-shadow(0 0 32px rgba(239, 68, 68, 0.18));
}
.login-mark {
    width: 96px;
    height: auto;
    display: block;
}
.login-wordmark {
    display: flex;
    flex-direction: column;
    line-height: 0.95;
    letter-spacing: -0.02em;
    font-family: -apple-system, BlinkMacSystemFont, 'Inter', 'Segoe UI', sans-serif;
}
.login-wordmark .bw-vault { font-size: 40px; font-weight: 800; color: var(--text-strong); }
.login-wordmark .bw-audio { font-size: 30px; font-weight: 400; color: #9ba0aa; margin-top: 4px; }
.login-wordmark .bw-dot { width: 8px; height: 8px; margin-right: 5px; }

#auth-screen .subtitle {
    text-align: center;
    margin-top: 0;
    margin-bottom: 28px;
}

#auth-screen .login-box {
    width: min(440px, 100% - 2rem);
    padding: 28px 36px 36px;
}

/* Sidebar: prominent logo at top — overrides default 64px topbar alignment.
   Sidebar nav starts below it. */
.sidebar .brand {
    /* safe-top: in PWA standalone mode the status-bar clock overlays the
       viewport, so push the logo below it (0 in a normal browser). */
    padding: calc(var(--safe-top) + 12px) 16px 14px;
    justify-content: center;
    border-bottom: 1px solid var(--border);
    height: auto;        /* override .brand height: var(--topbar-h) from style.css */
}
/* New brand: real PNG icon + HTML wordmark (so 'vault' shows white on dark sidebar) */
.sidebar .brand { gap: 12px; }
.brand-mark-img {
    width: 56px;
    height: auto;
    display: block;
    flex-shrink: 0;
    filter: drop-shadow(0 4px 10px rgba(0,0,0,0.5));
}
.brand-wordmark {
    display: flex;
    flex-direction: column;
    line-height: 0.95;
    letter-spacing: -0.02em;
    font-family: -apple-system, BlinkMacSystemFont, 'Inter', 'Segoe UI', sans-serif;
}
.bw-vault {
    font-size: 28px;
    font-weight: 800;
    color: var(--text-strong);
}
.bw-audio {
    font-size: 22px;
    font-weight: 400;
    color: #9ba0aa;
    display: inline-flex;
    align-items: center;
    margin-top: 2px;
}
.bw-dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--accent);
    display: inline-block;
    margin-right: 4px;
    margin-bottom: 2px;
}
/* (legacy ≤900px brand rules removed — folded into the 768–1023 tablet band) */

/* Settings tabs */
.settings-tab.hidden { display: none; }

/* Connection cards */
.connection-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 16px;
}

.connection-card {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 20px;
    display: flex;
    flex-direction: column;
    gap: 12px;
    transition: border-color 0.15s;
}
.connection-card:hover { border-color: var(--border-strong); }

.connection-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
}

.connection-logo {
    width: 36px; height: 36px;
    border-radius: 8px;
    background: var(--bg-input);
    display: flex; align-items: center; justify-content: center;
    font-weight: 800;
    color: var(--accent);
}

.connection-status {
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    padding: 4px 10px;
    border-radius: 999px;
}
.connection-status.connected { color: var(--success); background: rgba(33,208,122,0.12); }
.connection-status.disconnected { color: var(--text-dim); background: rgba(118,120,134,0.12); }
.connection-status.stub { color: var(--warning); background: rgba(255,176,84,0.12); }

.connection-meta {
    font-size: 12px;
    color: var(--text-dim);
    min-height: 16px;
}
.connection-meta b { color: var(--text); font-weight: 600; }

.connection-actions {
    display: flex;
    gap: 8px;
}

.connection-form {
    display: flex;
    flex-direction: column;
    gap: 8px;
    border-top: 1px solid var(--border);
    padding-top: 12px;
    margin-top: 4px;
}
.connection-form input { font-size: 13px; }
.connection-form .help {
    font-size: 11px;
    color: var(--text-dim);
    line-height: 1.4;
}

/* Per-service download / concurrent-stream settings inside a connection card
   (relocated here from Settings → Downloads). */
.connection-stream {
    border-top: 1px solid var(--border);
    padding-top: 12px;
    margin-top: 12px;
}
.conn-stream-block {
    display: flex;
    flex-direction: column;
    gap: 10px;
}
.conn-stream-head {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--text-dim);
}
.conn-stream-toggle {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    font-size: 13px;
    color: var(--text);
    line-height: 1.4;
    cursor: pointer;
}
.conn-stream-toggle input { accent-color: var(--accent); margin-top: 2px; flex: 0 0 auto; }
.conn-stream-slider > label {
    display: block;
    font-size: 13px;
    color: var(--text);
    margin-bottom: 6px;
}
.conn-stream-bk {
    font-size: 11px;
    color: var(--text-dim);
    font-weight: normal;
    margin-left: 4px;
}
.conn-stream-slider-row {
    display: flex;
    align-items: center;
    gap: 12px;
}
.conn-stream-slider-row input[type="range"] {
    flex: 1 1 auto;
    accent-color: var(--accent);
    min-width: 0;
}
.conn-stream-out {
    flex: 0 0 auto;
    min-width: 28px;
    text-align: center;
    font-size: 14px;
    font-weight: 700;
    color: var(--text);
    font-variant-numeric: tabular-nums;
}

/* Tidal OAuth code display */
.tidal-code-display {
    text-align: center;
    padding: 16px;
    background: var(--bg-input);
    border: 1px dashed var(--accent);
    border-radius: var(--radius-sm);
}
.tidal-code-display .code {
    font-family: 'SF Mono', Menlo, monospace;
    font-size: 24px;
    font-weight: 800;
    letter-spacing: 0.2em;
    color: var(--accent);
}
.tidal-link {
    color: var(--accent);
    font-weight: 700;
    text-decoration: underline;
    text-underline-offset: 2px;
}
.tidal-link:hover { color: var(--accent-hover); }

/* Admin */
.admin-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 10px 0;
    border-bottom: 1px solid var(--border);
    font-size: 13px;
    gap: 12px;
}
.admin-row:last-child { border-bottom: none; }
.admin-row .meta { color: var(--text-dim); font-size: 11px; }
.admin-row code {
    background: var(--bg-input);
    padding: 3px 8px;
    border-radius: var(--radius-sm);
    font-size: 12px;
    color: var(--accent);
    font-family: 'SF Mono', Menlo, monospace;
    word-break: break-all;
}

.invite-link-box {
    background: var(--bg-input);
    border: 1px solid var(--accent);
    border-radius: var(--radius-sm);
    padding: 12px;
    font-family: 'SF Mono', Menlo, monospace;
    font-size: 12px;
    color: var(--accent);
    word-break: break-all;
    cursor: pointer;
}
.invite-link-box:hover { background: var(--bg-card-hover); }

/* Admin tabs */
.admin-tab.hidden { display: none; }

/* Download preferences form rows */
.prefs-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 8px 0;
    border-bottom: 1px solid var(--border);
    font-size: 13px;
}
.prefs-row:last-child { border-bottom: none; }
.prefs-row > label {
    flex: 1;
    color: var(--text-mid);
    display: flex;
    align-items: center;
    gap: 8px;
}
.prefs-row > label > input[type="checkbox"] {
    width: auto;
    margin: 0;
    accent-color: var(--accent);
}
.prefs-row input[type="text"],
.prefs-row input[type="number"],
.prefs-row select {
    background: var(--bg-input);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    color: var(--text);
    padding: 8px 12px;
    font-size: 13px;
    min-width: 240px;
}
.prefs-row select { cursor: pointer; }
.prefs-row code {
    background: var(--bg-input);
    padding: 2px 6px;
    border-radius: var(--radius-sm);
    font-size: 11px;
    color: var(--accent);
    font-family: 'SF Mono', Menlo, monospace;
}

.prefs-tag-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
    gap: 8px;
}
.tag-chk {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 12px;
    background: var(--bg-input);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    font-size: 12px;
    color: var(--text-mid);
    cursor: pointer;
}
.tag-chk:has(input:checked) {
    border-color: var(--accent);
    color: var(--accent);
}
.tag-chk input { accent-color: var(--accent); }

/* Top search bar kind selector */
.topbar-search { display: flex; gap: 8px; align-items: center; }
.search-kind {
    width: 110px;
    padding: 10px 12px;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    color: var(--text);
    font-size: 12px;
    cursor: pointer;
}

/* Service badge on a card showing where the result came from */
.dl-card .src-badge {
    position: absolute;
    top: 8px; right: 8px;
    background: rgba(0,0,0,0.7);
    color: var(--accent);
    font-size: 9px; font-weight: 800;
    padding: 3px 7px;
    border-radius: 4px;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
}

/* "Not downloaded" tag on a saved-but-not-yet-downloaded AI playlist card —
   used both on Library cards (.dl-card) and the home Recent carousel (.recent-cover). */
.lib-notdl-badge {
    position: absolute;
    bottom: 8px; left: 8px;
    background: rgba(0,0,0,0.66);
    color: #fff;
    font-size: 9px; font-weight: 700;
    padding: 3px 7px;
    border-radius: 4px;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
}
.dl-card .lib-saved-dl { margin-top: 8px; width: 100%; }

/* Sidebar sub-items for service browse sections */
.nav-item-sub {
    padding-left: 22px;
    font-size: 12px;
}
.nav-item-sub .ic {
    width: 18px;
    text-align: center;
    display: inline-block;
    font-size: 14px;
    margin-right: 4px;
    opacity: 0.85;
}

/* Card with Download action overlaid */
.dl-card {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    overflow: hidden;
    transition: border-color 0.15s, transform 0.15s;
    display: flex; flex-direction: column;
}
.dl-card:hover { border-color: var(--accent); transform: translateY(-2px); }

/* ── My Library: sub-section tabs ───────────────────────────────────── */
.lib-tabs {
    display: flex; gap: 8px; flex-wrap: wrap;
    margin: 4px 0 16px;
}
.lib-tab {
    background: var(--bg-card); color: var(--text-dim);
    border: 1px solid var(--border); border-radius: 999px;
    padding: 6px 16px; font-size: 13px; font-weight: 600; cursor: pointer;
    transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.lib-tab:hover { color: var(--text); border-color: var(--accent); }
.lib-tab.active { background: var(--accent); color: #fff; border-color: var(--accent); }

/* ── My Library: multi-select ──────────────────────────────────────── */
.dl-card.lib-selectable { cursor: pointer; }
.dl-card.lib-selected { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent) inset; }
.dl-card .lib-check {
    position: absolute; top: 8px; left: 8px; z-index: 3;
    width: 24px; height: 24px; border-radius: 6px;
    background: rgba(0,0,0,0.55); border: 2px solid #fff;
    display: flex; align-items: center; justify-content: center;
    color: #fff; font-size: 14px; font-weight: 800;
}
.dl-card .lib-check.on { background: var(--accent); border-color: var(--accent); }

.lib-bulk-bar {
    position: sticky; bottom: 0; z-index: 20;
    display: flex; align-items: center; gap: 14px;
    margin-top: 16px; padding: 12px 16px;
    background: var(--bg-card); border: 1px solid var(--accent);
    border-radius: 10px;
}
.lib-bulk-bar.hidden { display: none; }
.lib-bulk-all { display: flex; align-items: center; gap: 6px; font-size: 13px; color: var(--text); cursor: pointer; }
.lib-bulk-count { font-size: 12px; color: var(--text-dim); }
.lib-bulk-spacer { flex: 1; }

/* ── Archives list (Library → Archives tab) ─────────────────────── */
.archives-list { display: flex; flex-direction: column; gap: 8px; margin-top: 4px; }
.arc-row {
    display: flex; align-items: center; gap: 12px;
    padding: 12px 14px;
    background: var(--bg-card);
    border: 1px solid var(--border, rgba(255,255,255,0.08));
    border-radius: 10px;
}
.arc-icon { font-size: 22px; line-height: 1; flex: 0 0 auto; opacity: 0.9; }
.arc-main { flex: 1 1 auto; min-width: 0; }
.arc-name {
    font-size: 14px; font-weight: 600; color: var(--text);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.arc-meta { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 3px; }
.arc-kind {
    font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em;
    padding: 2px 8px; border-radius: 999px;
    background: rgba(255,255,255,0.07); color: var(--text-dim);
}
.arc-kind-album    { background: rgba(96,165,250,0.16);  color: #93c5fd; }
.arc-kind-playlist { background: rgba(167,139,250,0.16); color: #c4b5fd; }
.arc-kind-track,
.arc-kind-tracks   { background: rgba(52,211,153,0.16);  color: #6ee7b7; }
.arc-size { font-size: 12px; color: var(--text-dim); }
.arc-progress { font-size: 12px; color: var(--accent); font-weight: 600; }
.arc-actions { flex: 0 0 auto; display: flex; gap: 8px; }
@media (max-width: 640px) {
    .arc-row { flex-wrap: wrap; }
    .arc-actions { width: 100%; }
    .arc-actions .btn { flex: 1; }
}
.dl-card .cover {
    position: relative;
    aspect-ratio: 1;
    background: var(--bg-input);
    overflow: hidden;
}
.dl-card .cover img { width: 100%; height: 100%; object-fit: cover; display: block; }
.dl-card .cover .hires {
    position: absolute; top: 8px; left: 8px;
    background: var(--accent); color: #1a1300;
    padding: 2px 6px; border-radius: 4px;
    font-size: 9px; font-weight: 800;
    letter-spacing: 0.05em;
}
.dl-card .dl-btn {
    position: absolute; right: 8px; bottom: 8px;
    width: 38px; height: 38px;
    border-radius: 50%;
    background: var(--accent); color: #1a1300;
    border: none; cursor: pointer;
    display: flex; align-items: center; justify-content: center;
    box-shadow: 0 4px 16px rgba(0,0,0,0.4);
    opacity: 0;
    transform: translateY(6px);
    transition: opacity 0.15s, transform 0.15s, background 0.15s;
    font-weight: 800;
    font-size: 14px;
}
.dl-card:hover .dl-btn { opacity: 1; transform: none; }
.dl-card .dl-btn:hover { background: var(--accent-hover); }

/* 🗑 remove-from-service button — top-right, danger-tinted. Reveal on hover
   like the download button; always faintly visible on touch (no hover). */
.dl-card .svc-del-btn {
    position: absolute; right: 8px; top: 8px;
    width: 30px; height: 30px;
    border-radius: 50%;
    background: rgba(0,0,0,0.55);
    color: #fff;
    border: 1px solid rgba(255,255,255,0.15);
    cursor: pointer;
    display: flex; align-items: center; justify-content: center;
    font-size: 13px; line-height: 1;
    opacity: 0;
    transform: translateY(-6px);
    transition: opacity 0.15s, transform 0.15s, background 0.15s, border-color 0.15s;
    -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
}
.dl-card:hover .svc-del-btn { opacity: 1; transform: none; }
.dl-card .svc-del-btn:hover {
    background: var(--danger, #e5484d); border-color: var(--danger, #e5484d);
}
.dl-card .svc-del-btn.busy { opacity: 0.5; pointer-events: none; }
@media (hover: none) {
    .dl-card .svc-del-btn { opacity: 0.8; transform: none; }
}

.dl-card .info {
    padding: 10px 12px 12px;
    min-width: 0;
}
.dl-card .title {
    font-size: 13px; font-weight: 600; color: var(--text);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.dl-card .artist {
    font-size: 12px; color: var(--text-dim);
    margin-top: 2px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.dl-card .meta {
    font-size: 11px; color: var(--text-dim); margin-top: 4px;
}

.dl-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
    gap: 16px;
}

.dl-card .cover.clickable { cursor: pointer; }

/* Alphabet sidebar for browse pages */
.browse-layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 56px;
    gap: 16px;
    align-items: start;
}
.browse-layout > .dl-grid { min-width: 0; }
.alphabet-rail {
    position: sticky;
    top: 12px;
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 8px 4px;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    max-height: calc(100vh - 24px);
    max-height: calc(100dvh - 24px);
    overflow-y: auto;
    user-select: none;
}
.alphabet-rail .ab-letter {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 24px;
    font-size: 12px;
    font-weight: 600;
    color: var(--text-dim);
    cursor: pointer;
    border-radius: 4px;
    transition: background 0.12s, color 0.12s;
}
.alphabet-rail .ab-letter:hover {
    background: var(--bg-input);
    color: var(--text);
}
.alphabet-rail .ab-letter.active {
    background: var(--accent);
    color: #000;
}
.alphabet-rail .ab-clear {
    height: 24px;
    font-size: 11px;
    color: var(--text-dim);
    cursor: pointer;
    text-align: center;
    border-radius: 4px;
    margin-bottom: 4px;
    border-bottom: 1px solid var(--border);
    padding-bottom: 4px;
}
.alphabet-rail .ab-clear:hover { color: var(--text); }
.alphabet-rail .ab-clear.disabled { opacity: 0.4; cursor: default; }

/* Detail page (album/artist/playlist) hero block */
.detail-hero {
    display: flex;
    gap: 28px;
    align-items: flex-end;
    padding: 28px;
    background: linear-gradient(135deg, rgba(201,168,94,0.12) 0%, rgba(0,0,0,0) 60%), var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    margin-bottom: 24px;
}
.detail-hero .cover-art {
    width: 200px; height: 200px;
    border-radius: var(--radius);
    object-fit: cover;
    box-shadow: var(--shadow-elev);
    flex-shrink: 0;
    background: var(--bg-input);
}
.detail-hero .cover-art.artist { border-radius: 50%; }
.detail-hero .meta-block { flex: 1; min-width: 0; }
.detail-hero .label-text {
    font-size: 11px; text-transform: uppercase; letter-spacing: 0.1em;
    color: var(--accent); font-weight: 700;
}
.detail-hero h2 {
    font-size: 32px; font-weight: 800; letter-spacing: -0.02em;
    margin: 4px 0 6px;
    color: var(--text-strong);
    word-wrap: break-word;
}
.detail-hero .sub {
    color: var(--text-mid); font-size: 13px; margin-bottom: 16px;
}
.detail-hero .actions { display: flex; gap: 8px; flex-wrap: wrap; }

/* Track list */
.track-list {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 6px 0;
}
.track-row {
    display: grid;
    grid-template-columns: 32px 36px 1fr auto auto auto;
    gap: 14px;
    align-items: center;
    padding: 8px 18px;
    transition: background 0.12s;
}
.track-row:hover { background: var(--bg-card-hover); }
.track-row + .track-row { border-top: 1px solid var(--border); }
.track-row .num {
    text-align: right;
    color: var(--text-dim);
    font-size: 13px;
    font-variant-numeric: tabular-nums;
}
.track-row .tr-cover {
    width: 36px; height: 36px;
    border-radius: var(--radius-sm);
    object-fit: cover;
    background: var(--bg-input);
}
.track-row .tr-info { min-width: 0; }
.track-row .tr-title {
    font-size: 13px; font-weight: 500; color: var(--text);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.track-row .tr-artist {
    font-size: 12px; color: var(--text-dim);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.track-row .tr-dur {
    color: var(--text-dim);
    font-size: 12px;
    font-variant-numeric: tabular-nums;
    min-width: 40px;
    text-align: right;
}
.track-row .tr-hires {
    background: var(--accent); color: #1a1300;
    font-size: 9px; font-weight: 800;
    padding: 2px 6px; border-radius: 4px;
    letter-spacing: 0.05em;
}
.track-row .tr-dl {
    width: 32px; height: 32px;
    border-radius: 50%;
    background: var(--bg-input); color: var(--text-mid);
    border: 1px solid var(--border);
    cursor: pointer;
    display: flex; align-items: center; justify-content: center;
    font-weight: 800;
    font-size: 13px;
    transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.track-row .tr-dl:hover { background: var(--accent); color: #1a1300; border-color: var(--accent); }

/* Right-side drawer for user detail */
.drawer.hidden { display: none; }
.drawer-backdrop {
    position: fixed; inset: 0;
    background: rgba(0,0,0,0.6);
    -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
    z-index: 999;
}
.drawer-panel {
    position: fixed; top: 0; right: 0; bottom: 0;
    width: min(640px, 90vw);
    background: var(--bg);
    border-left: 1px solid var(--border);
    box-shadow: var(--shadow-elev);
    z-index: 1000;
    display: flex; flex-direction: column;
    animation: slideInRight 0.18s ease;
}
@keyframes slideInRight { from { transform: translateX(20px); opacity: 0; } to { transform: none; opacity: 1; } }

.drawer-head {
    display: flex; align-items: center; justify-content: space-between;
    padding: 18px 24px;
    border-bottom: 1px solid var(--border);
}
.drawer-head h3 { color: var(--text-strong); font-size: 16px; margin: 0; }
.drawer-body {
    flex: 1; overflow-y: auto;
    padding: 20px 24px;
}

/* Stat mini-cards inside drawer */
.mini-stats {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 10px;
    margin-bottom: 18px;
}
.mini-stat {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 12px 14px;
}
.mini-stat .lbl {
    font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em;
    color: var(--text-dim); font-weight: 700;
    margin-bottom: 4px;
}
.mini-stat .val {
    font-size: 18px; font-weight: 800; color: var(--text-strong);
    letter-spacing: -0.01em;
}

/* Tag chips */
.chip {
    display: inline-block;
    background: var(--bg-card-hover);
    border: 1px solid var(--border);
    color: var(--text-mid);
    font-size: 11px;
    padding: 3px 8px;
    border-radius: 999px;
    margin: 2px;
}
.chip.accent { color: var(--accent); border-color: rgba(201,168,94,0.4); background: var(--accent-soft); }

/* Disk bar */
.disk-bar {
    height: 6px;
    background: var(--border);
    border-radius: 3px;
    overflow: hidden;
    margin-top: 4px;
}
.disk-bar .fill {
    height: 100%;
    background: var(--accent);
}
.disk-bar .fill.warn { background: var(--warning); }
.disk-bar .fill.danger { background: var(--danger); }

/* ── Artist card fallback (no picture) ──────── */
.dl-card .cover.artist-cover { border-radius: 50%; overflow: hidden; }
.dl-card .cover.artist-cover img { border-radius: 50%; }
.artist-initial {
    width: 100%; height: 100%;
    display: flex; align-items: center; justify-content: center;
    font-size: 56px; font-weight: 800;
    color: var(--accent);
    background: linear-gradient(135deg, rgba(212,175,55,0.10), rgba(212,175,55,0.02));
    text-transform: uppercase;
    user-select: none;
}

/* ── Adaptive scrollable sidebar ──────────── */
.sidebar { overflow: hidden; }
.sidebar .brand { flex: 0 0 auto; }
.sidebar-scroll {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    overflow-x: hidden;
    scrollbar-width: thin;
    scrollbar-color: var(--border-strong, #333) transparent;
}
.sidebar-scroll::-webkit-scrollbar { width: 6px; }
.sidebar-scroll::-webkit-scrollbar-thumb {
    background: var(--border-strong, #333);
    border-radius: 3px;
}
.sidebar-scroll::-webkit-scrollbar-track { background: transparent; }
.sidebar-bottom {
    flex: 0 0 auto;
    border-top: 1px solid var(--border);
    background: var(--bg-elev);
}
.sidebar-bottom .nav-section { padding-bottom: 8px; }

/* ── Collapsible nav sections ─────────────── */
.nav-section.collapsible > h4 {
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: space-between;
    user-select: none;
    transition: color 0.15s;
}
.nav-section.collapsible > h4:hover { color: var(--text-strong); }
.nav-section.collapsible > h4 .chev {
    font-size: 11px;
    color: var(--text-dim);
    transition: transform 0.18s ease;
    margin-left: 6px;
}
.nav-section.collapsible.collapsed > h4 .chev { transform: rotate(-90deg); }
.nav-section.collapsible > .nav-list {
    overflow: hidden;
    max-height: 800px;
    transition: max-height 0.22s ease, opacity 0.18s ease, margin 0.18s ease;
    opacity: 1;
}
.nav-section.collapsible.collapsed > .nav-list {
    max-height: 0;
    opacity: 0;
    margin: 0;
    pointer-events: none;
}

/* ── Downloads page ──────────────────────── */
.downloads-list { display: flex; flex-direction: column; gap: 10px; }
.dl-row {
    display: grid;
    grid-template-columns: 56px 1fr auto;
    gap: 14px;
    align-items: center;
    padding: 12px 14px;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius);
}
.dl-row .dl-cover {
    width: 56px; height: 56px;
    background: var(--bg-input);
    border-radius: var(--radius-sm);
    overflow: hidden;
}
.dl-row .dl-cover img { width: 100%; height: 100%; object-fit: cover; }
.dl-title { font-weight: 700; color: var(--text-strong); font-size: 14px; }
.dl-sub { font-size: 12px; color: var(--text-mid); margin-top: 2px; }
.dl-svc {
    display: inline-block;
    background: var(--bg-input);
    color: var(--accent);
    text-transform: uppercase;
    font-size: 10px;
    font-weight: 800;
    padding: 1px 6px;
    border-radius: 4px;
    margin: 0 4px;
    letter-spacing: 0.04em;
}
.dl-error {
    margin-top: 6px;
    font-size: 11px;
    color: var(--danger);
    word-break: break-word;
}
.dl-progress {
    margin-top: 8px;
    width: 100%;
    height: 5px;
    background: var(--bg-input);
    border-radius: 999px;
    overflow: hidden;
}
.dl-progress-fill {
    height: 100%;
    background: var(--accent);
    transition: width 0.4s ease;
}
.dl-actions { display: flex; align-items: center; gap: 8px; }
.dl-badge {
    display: inline-block;
    font-size: 10px;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    padding: 4px 10px;
    border-radius: 999px;
}
.dl-badge.badge-ok { color: var(--success); background: rgba(33,208,122,0.12); }
.dl-badge.badge-info { color: var(--accent); background: var(--accent-soft); }
.dl-badge.badge-warn { color: var(--warning, #f0b95e); background: rgba(240,185,94,0.12); }
.dl-badge.badge-err { color: var(--danger); background: rgba(239,68,68,0.12); }
.dl-badge.badge-neutral { color: #3ba59c; background: rgba(59,165,156,0.12); }
.btn-xs { padding: 4px 10px; font-size: 14px; line-height: 1; }

/* Calm "⚠" that replaces scary red error text in the Downloads list. */
.dl-btn-warn {
    color: var(--warning, #f0b95e) !important;
    font-size: 15px; line-height: 1;
}
.dl-btn-warn:hover { background: rgba(240,185,94,0.15); }

/* Download-errors modal: list of tracks that didn't download + reasons. */
#dl-errors-modal .zip-modal-body { gap: 8px; max-height: 60vh; max-height: 60dvh; overflow-y: auto; }
.dlerr-note {
    font-size: 12px; color: var(--text-dim);
    background: rgba(240,185,94,0.10);
    border: 1px solid rgba(240,185,94,0.40);
    border-radius: 8px; padding: 8px 10px;
}
.dlerr-count { font-size: 12px; color: var(--text-dim); margin: 2px 0; }
.dlerr-row {
    display: flex; flex-direction: column; gap: 2px;
    padding: 8px 10px; border-radius: 8px; background: var(--bg-input);
}
.dlerr-track { font-size: 13px; color: var(--text); }
.dlerr-artist { color: var(--text-dim); }
.dlerr-reason { font-size: 11px; color: var(--warning, #f0b95e); word-break: break-word; }
.dlerr-empty { font-size: 13px; color: var(--text-dim); padding: 6px 2px; }

/* Library card delete-button — sits top-right of cover, only visible on hover */
.lib-del-btn {
    position: absolute;
    top: 6px;
    left: 6px;
    width: 28px;
    height: 28px;
    border-radius: 50%;
    background: rgba(0, 0, 0, 0.75);
    color: var(--danger);
    border: 1px solid rgba(255, 255, 255, 0.12);
    font-size: 18px;
    font-weight: 800;
    line-height: 1;
    cursor: pointer;
    opacity: 0;
    transition: opacity 0.15s, background 0.15s, transform 0.12s;
    z-index: 2;
}
.dl-card:hover .lib-del-btn { opacity: 1; }
.lib-del-btn:hover {
    background: var(--danger);
    color: #fff;
    transform: scale(1.08);
}

/* "Move to folder" on a loose track card — sits left of the delete button. */
.lib-move-btn {
    position: absolute; top: 6px; right: 40px;
    width: 28px; height: 28px; border-radius: 50%;
    background: rgba(0, 0, 0, 0.75); color: var(--text-strong);
    border: 1px solid rgba(255, 255, 255, 0.12);
    font-size: 14px; line-height: 1; cursor: pointer; opacity: 0;
    transition: opacity 0.15s, background 0.15s, transform 0.12s; z-index: 2;
    display: flex; align-items: center; justify-content: center;
}
.dl-card:hover .lib-move-btn { opacity: 1; }
.lib-move-btn:hover { background: var(--accent); color: #fff; transform: scale(1.08); }

/* Create-folder card in My Folders */
.folder-create-card .cover { border: 2px dashed var(--border-strong, var(--border)); background: transparent; }

/* Folder picker dialog (download / move) */
.folder-pick-modal { max-width: 460px; width: 92vw; }
.folder-pick-list { display: flex; flex-direction: column; gap: 6px; padding: 4px 2px 6px; max-height: 60vh; max-height: 60dvh; overflow-y: auto; }
.folder-pick-item {
    display: flex; align-items: center; gap: 10px; text-align: left;
    padding: 11px 12px; border: 1px solid var(--border); border-radius: 10px;
    background: var(--bg-card); color: var(--text-strong); font-size: 14px;
    cursor: pointer; transition: border-color .12s, background .12s;
}
.folder-pick-item:hover { border-color: var(--accent); background: var(--bg-card-hover); }
.folder-pick-item.fp-last { border-color: var(--accent); }
.folder-pick-item .fp-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.folder-pick-item .fp-count { color: var(--text-dim); font-size: 12px; }
.folder-pick-item.fp-new { color: var(--accent); border-style: dashed; }

/* Library detail — file list */
.dl-card.clickable { cursor: pointer; }
.dl-card.clickable:hover { border-color: var(--accent-dim, var(--accent)); }
.dl-row.dl-row-clickable { cursor: pointer; transition: border-color 0.12s; }
.dl-row.dl-row-clickable:hover { border-color: var(--border-strong); }

.lib-files { margin-top: 20px; }
.lib-files-table {
    width: 100%;
    border-collapse: collapse;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    overflow: hidden;
}
.lib-files-table thead th {
    text-align: left;
    font-size: 11px;
    font-weight: 800;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-dim);
    padding: 12px 16px;
    background: var(--bg-elev);
    border-bottom: 1px solid var(--border);
}
.lib-files-table tbody td {
    padding: 11px 16px;
    border-bottom: 1px solid var(--border);
    font-size: 13px;
    color: var(--text);
    vertical-align: middle;
}
.lib-files-table tbody tr:last-child td { border-bottom: none; }
.lib-files-table tbody tr:hover { background: var(--bg-card-hover); }
.lf-num { color: var(--text-dim); font-variant-numeric: tabular-nums; }
.lf-name { word-break: break-word; }
.lf-size {
    text-align: right;
    color: var(--text-mid);
    font-variant-numeric: tabular-nums;
    font-family: 'SF Mono', Menlo, monospace;
    font-size: 12px;
}
.lf-dur {
    text-align: right;
    color: var(--text-dim);
    font-variant-numeric: tabular-nums;
    font-size: 12px;
}

/* Phone track list — Spotify/Qobuz/Tidal-style rows. Rendered (JS-gated) only
   at ≤767px in place of the desktop table: title (1-line ellipsis) + artist
   subtitle, duration on the right, no format/size. */
.lib-mobile-list {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    overflow: hidden;
}
.lmt-row {
    display: grid;
    grid-template-columns: 40px 1fr auto;
    align-items: center;
    gap: 12px;
    width: 100%;
    background: transparent;
    border: 0;
    border-bottom: 1px solid var(--border);
    padding: 9px 14px;
    min-height: 58px;
    text-align: left;
    cursor: pointer;
    color: var(--text);
    -webkit-tap-highlight-color: transparent;
}
.lmt-row:last-child { border-bottom: 0; }
.lmt-row:active { background: var(--bg-card-hover); }
.lmt-lead {
    position: relative;
    width: 40px; height: 40px;
    display: flex; align-items: center; justify-content: center;
    flex: 0 0 40px;
}
.lmt-num { color: var(--text-dim); font-size: 14px; font-variant-numeric: tabular-nums; }
.lmt-cover { width: 40px; height: 40px; border-radius: 5px; object-fit: cover; background: var(--bg-input); }
.lmt-main { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.lmt-title {
    font-size: 15px; font-weight: 600; color: var(--text); line-height: 1.25;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.lmt-artist {
    font-size: 13px; color: var(--text-dim); line-height: 1.2;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.lmt-dur { color: var(--text-dim); font-size: 13px; font-variant-numeric: tabular-nums; flex: 0 0 auto; }
.lmt-disc {
    font-size: 11px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
    color: var(--text-dim); padding: 12px 14px 6px;
}
/* Now-playing: an animated equalizer replaces the number/cover; title accents. */
.lmt-eq { display: none; gap: 2px; align-items: flex-end; height: 16px; }
.lmt-eq i {
    width: 3px; background: var(--accent); border-radius: 1px;
    transform-origin: bottom; animation: lmtEq 0.9s ease-in-out infinite;
}
.lmt-eq i:nth-child(1) { height: 40%;  animation-delay: 0s; }
.lmt-eq i:nth-child(2) { height: 100%; animation-delay: 0.2s; }
.lmt-eq i:nth-child(3) { height: 65%;  animation-delay: 0.4s; }
@keyframes lmtEq { 0%, 100% { transform: scaleY(0.4); } 50% { transform: scaleY(1); } }
.lmt-row.playing .lmt-num, .lmt-row.playing .lmt-cover { display: none; }
.lmt-row.playing .lmt-eq { display: flex; }
.lmt-row.playing .lmt-title { color: var(--accent); }
.lf-ext-badge {
    display: inline-block;
    font-size: 10px;
    font-weight: 800;
    letter-spacing: 0.06em;
    padding: 3px 8px;
    border-radius: 4px;
    background: var(--accent-soft);
    color: var(--accent);
}
.lf-playcol { width: 36px; text-align: center; }
.lf-play-btn {
    width: 26px; height: 26px;
    border-radius: 50%;
    border: 1px solid var(--border-strong);
    background: transparent;
    color: var(--text-mid);
    font-size: 10px;
    cursor: pointer;
    transition: all 0.12s;
    display: inline-flex; align-items: center; justify-content: center;
    line-height: 1;
    padding-left: 1px;
}
.lf-play-btn:hover {
    background: var(--accent);
    border-color: var(--accent);
    color: #fff;
}
.lf-play-btn.playing {
    background: var(--accent);
    border-color: var(--accent);
    color: #fff;
    box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.18);
}

/* ── Bottom Player Bar ──────────────────── */
.player-bar {
    position: fixed;
    bottom: 0;
    left: var(--sidebar-w);
    right: 0;
    height: 74px;
    background: var(--bg-elev);
    border-top: 1px solid var(--border);
    display: grid;
    /* Spotify-style three columns: middle one has an explicit px-range so
       the controls have real width on wide screens (the previous `auto`
       collapsed the column to ~150px because pb-progress's width:100% was
       circular against an auto-sized parent). Now: middle floors at 360px
       and caps at 720px (don't stretch ridiculously on 4K); side columns
       split the remainder equally with min:0 so middle wins width-budget. */
    grid-template-columns: minmax(0, 1fr) minmax(360px, 720px) minmax(0, 1fr);
    align-items: center;
    gap: 16px;
    padding: 10px 24px;
    z-index: 80;
    box-shadow: 0 -8px 32px rgba(0, 0, 0, 0.35);
}
/* (legacy ≤900px player-bar rules removed — tablet band sets left:64px + meta
   visible; phone band ≤767 sets the mini-player geometry) */

.pb-track {
    display: flex; align-items: center; gap: 12px; min-width: 0;
}
.pb-cover {
    width: 52px; height: 52px;
    background: var(--bg-input);
    border-radius: 8px;
    overflow: hidden;
    flex-shrink: 0;
}
.pb-cover img { width: 100%; height: 100%; object-fit: cover; }
.pb-meta { min-width: 0; }
.pb-title {
    font-size: 13px; font-weight: 700; color: var(--text-strong);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pb-artist {
    font-size: 12px; color: var(--text-mid); margin-top: 2px;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.pb-controls {
    display: flex; flex-direction: column; align-items: stretch; gap: 6px;
    /* stretch (default for column-flex) lets pb-progress fill the column
       width — was 'center' which collapsed pb-progress to its content size. */
    min-width: 0;
    width: 100%;
}
.pb-buttons {
    display: flex; align-items: center; justify-content: center; gap: 14px;
}
.pb-btn {
    background: transparent;
    border: none;
    color: var(--text-mid);
    width: 30px; height: 30px;
    border-radius: 50%;
    cursor: pointer;
    font-size: 13px;
    transition: color 0.12s, background 0.12s;
}
.pb-btn:hover { color: var(--text-strong); background: var(--bg-card-hover); }
.pb-btn:disabled { opacity: 0.4; cursor: default; }
.pb-play {
    width: 36px; height: 36px;
    border-radius: 50%;
    border: 2px solid var(--accent);
    background: var(--accent);
    color: #fff;
    cursor: pointer;
    font-size: 14px;
    line-height: 1;
    transition: transform 0.1s, box-shadow 0.12s;
}
.pb-play:hover { transform: scale(1.06); box-shadow: 0 0 0 4px rgba(239, 68, 68, 0.2); }

/* Inline SVG transport/expand/close icons (no emoji). Center the glyph inside
   the round buttons and size it so it never overflows the 30/36px hit areas.
   Scoped to .player-bar so the same markup behaves on desktop AND phone. */
.player-bar .pb-btn, .player-bar .pb-play {
    display: inline-flex; align-items: center; justify-content: center; padding: 0;
}
.player-bar .pb-btn svg { width: 18px; height: 18px; display: block; }
.player-bar .pb-play svg { width: 20px; height: 20px; display: block; }

.pb-progress {
    display: flex; align-items: center; gap: 10px;
    width: 100%;
    /* Drop max-width — let the progress bar use the full middle column
       (capped at 720px upstream by the grid template). */
}
.pb-time { font-size: 11px; color: var(--text-dim); min-width: 36px; text-align: center; font-variant-numeric: tabular-nums; }
.pb-bar {
    flex: 1; height: 4px;
    background: var(--bg-input);
    border-radius: 999px;
    overflow: hidden;
    cursor: pointer;
}
.pb-bar:hover { height: 6px; }
.pb-bar-fill {
    height: 100%;
    width: 0%;
    background: var(--accent);
    border-radius: 999px;
    transition: width 0.15s linear;
}

.pb-right {
    display: flex; align-items: center; gap: 12px;
    justify-self: end;   /* hug the right edge of the 1fr cell so it doesn't stretch and shift the centred controls */
}
.pb-vol { display: flex; align-items: center; gap: 8px; color: var(--text-mid); }
.pb-vol svg { width: 16px; height: 16px; }
.pb-vol input[type="range"] {
    width: 90px;
    -webkit-appearance: none;
    appearance: none;
    height: 4px;
    background: var(--bg-input);
    border-radius: 999px;
    outline: none;
}
.pb-vol input[type="range"]::-webkit-slider-thumb {
    -webkit-appearance: none;
    width: 12px; height: 12px;
    background: var(--accent);
    border-radius: 50%;
    cursor: pointer;
}
.pb-vol input[type="range"]::-moz-range-thumb {
    width: 12px; height: 12px;
    background: var(--accent);
    border-radius: 50%;
    cursor: pointer;
    border: none;
}
.pb-close {
    width: 28px; height: 28px;
    border-radius: 50%;
    background: transparent;
    border: 1px solid var(--border);
    color: var(--text-dim);
    cursor: pointer;
    font-size: 16px;
    line-height: 1;
}
.pb-close:hover { color: var(--danger); border-color: var(--danger); }

/* AirPlay button (Safari): tinted when currently routed to a wireless target. */
.pb-airplay.active { color: var(--accent); }

/* AirPlay in the full-screen Now Playing top row (grouped with the quality badge). */
.np-top-actions { display: flex; align-items: center; gap: 10px; }
.np-act-btn {
    background: none; border: 0; color: var(--text-strong);
    min-width: 44px; min-height: 44px; cursor: pointer;
    display: flex; align-items: center; justify-content: center;
}
.np-act-btn svg { width: 24px; height: 24px; }
.np-airplay.active { color: var(--accent); }

/* ── Full-screen Now Playing (slide-up sheet over the same #player-audio) ──
   Base rules — the overlay is only ever opened on phone (matchMedia-gated),
   so these never affect desktop layout (it stays translateY(100%)/hidden). */
/* ────────────────────────────────────────────────────────────────────
   Now Playing — premium full-screen overlay (ambient art + SVG controls)
   ──────────────────────────────────────────────────────────────────── */
.now-playing {
    position: fixed; inset: 0; height: 100dvh; z-index: 100;
    background: var(--bg); overflow: hidden;
    transform: translateY(100%); transition: transform .28s cubic-bezier(.32,.72,0,1);
    overscroll-behavior: contain; visibility: hidden;
}
.now-playing.open { transform: translateY(0); visibility: visible; }

/* Ambient backdrop = the album art, blurred + dimmed; scaled up to hide edges. */
.np-bg {
    position: absolute; inset: 0; z-index: 0;
    background: var(--bg) center/cover no-repeat;
    filter: blur(48px) saturate(1.4) brightness(.45);
    transform: scale(1.3);
    transition: background-image .4s ease, opacity .4s ease;
}
.now-playing.no-art .np-bg { opacity: 0; }
.np-bg-overlay {
    position: absolute; inset: 0; z-index: 1; pointer-events: none;
    background: linear-gradient(180deg,
        rgba(12,13,16,.35) 0%, rgba(12,13,16,.85) 60%, #0c0d10 100%);
}

/* Foreground content stack. */
.np-shell {
    position: relative; z-index: 2;
    height: 100%; width: 100%;
    display: flex; flex-direction: column; align-items: center;
    padding: calc(var(--safe-top) + 10px) 24px calc(var(--safe-bottom) + 28px);
    max-width: 520px; margin: 0 auto;
}

/* Top row: collapse left, hi-res quality badge right. */
.np-top { width: 100%; display: flex; align-items: center; justify-content: space-between; flex: 0 0 auto;
    padding-top: var(--safe-top); padding-left: var(--safe-left); padding-right: var(--safe-right); }
.np-collapse { background:none;border:0;color:var(--text-strong);min-width:44px;min-height:44px;cursor:pointer;
    display:flex;align-items:center;justify-content:center;margin-left:-8px; }
.np-collapse svg { width: 26px; height: 26px; }
.np-quality {
    font-size: 11px; font-weight: 700; letter-spacing: .02em; line-height: 1;
    color: var(--text-strong); white-space: nowrap;
    padding: 6px 10px; border-radius: 999px;
    background: rgba(239,68,68,.14); border: 1px solid rgba(239,68,68,.35);
    box-shadow: 0 0 0 1px rgba(0,0,0,.15) inset;
}
.np-quality[hidden] { display: none; }

/* Album art. */
.np-art {
    position: relative; width: min(78vw, 360px); aspect-ratio: 1;
    border-radius: 16px; overflow: hidden;
    margin: auto 0; flex: 0 1 auto;
    box-shadow: 0 24px 60px rgba(0,0,0,.6);
    background: linear-gradient(135deg, #20232a, #15171c);
}
.np-art img { width: 100%; height: 100%; object-fit: cover; display: block; }
.now-playing.no-art .np-art img { display: none; }
.np-art-ph {
    position: absolute; inset: 0; display: none; align-items: center; justify-content: center;
    color: var(--text-dim);
    background: linear-gradient(135deg, #23262e 0%, #15171c 100%);
}
.np-art-ph svg { width: 30%; height: 30%; opacity: .55; }
.now-playing.no-art .np-art-ph { display: flex; }

/* Info row: text left, heart right. */
.np-info {
    width: 100%; display: flex; align-items: center; gap: 14px;
    margin: 22px 0 14px; flex: 0 0 auto;
}
.np-info-text { min-width: 0; flex: 1; }
.np-title { font-size: 22px; font-weight: 800; color: var(--text-strong); line-height: 1.2;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.np-artist { font-size: 15px; color: var(--text-mid); margin-top: 5px;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.np-album { font-size: 13px; color: var(--text-dim); margin-top: 2px;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Like (heart): outline by default, fills accent + pops when liked. */
.np-like {
    flex: 0 0 auto; background:none;border:0;cursor:pointer;
    width:44px;height:44px;display:flex;align-items:center;justify-content:center;
    color: var(--text-mid); transition: color .15s, transform .12s;
}
.np-like svg { width: 26px; height: 26px; transition: transform .18s cubic-bezier(.34,1.56,.64,1); }
.np-like:hover { color: var(--text-strong); }
.np-like.liked { color: var(--accent); }
.np-like.liked svg { fill: var(--accent); }
.np-like.pop svg { animation: np-heart-pop .32s cubic-bezier(.34,1.56,.64,1); }
@keyframes np-heart-pop { 0% { transform: scale(1); } 45% { transform: scale(1.32); } 100% { transform: scale(1); } }
.np-like:active svg { transform: scale(.88); }
.np-like:disabled { opacity: .5; cursor: default; }

/* Scrubber. */
.np-scrub { width: 100%; flex: 0 0 auto; margin-bottom: 26px; }
.np-bar {
    position: relative; height: 4px; border-radius: 3px;
    background: rgba(255,255,255,.16); cursor: pointer;
    touch-action: none;
}
.np-bar-fill { height: 100%; border-radius: 3px; background: var(--accent); width: 0%; pointer-events: none; }
.np-bar-thumb {
    position: absolute; top: 50%; left: 0; width: 10px; height: 10px; border-radius: 50%;
    background: #fff; transform: translate(-50%, -50%) scale(0);
    box-shadow: 0 1px 4px rgba(0,0,0,.5); pointer-events: none;
    transition: transform .12s ease;
}
.np-bar:hover .np-bar-thumb,
.np-bar.dragging .np-bar-thumb { transform: translate(-50%, -50%) scale(1); }
.np-bar.dragging .np-bar-thumb { transform: translate(-50%, -50%) scale(1.4); }
.np-times { display: flex; justify-content: space-between; margin-top: 8px; }
.np-time { font-size: 12px; color: var(--text-dim); font-variant-numeric: tabular-nums; }

/* Transport. */
.np-controls { display: flex; align-items: center; justify-content: center; gap: 36px; flex: 0 0 auto; }
.np-btn {
    background:none;border:0;color:var(--text-strong);cursor:pointer;
    width:48px;height:48px;display:flex;align-items:center;justify-content:center;
    transition: transform .1s, color .15s;
}
.np-btn svg { width: 30px; height: 30px; }
.np-btn:hover { color: var(--text-strong); }
.np-btn:active { transform: scale(.9); }
.np-btn:disabled { opacity: .35; cursor: default; }
.np-play {
    width: 64px; height: 64px; border-radius: 50%; border: 0; cursor: pointer;
    background: linear-gradient(135deg, #ff5a5a, #ef4444); color: #fff;
    display: flex; align-items: center; justify-content: center;
    box-shadow: 0 8px 24px rgba(239,68,68,.4);
    transition: transform .1s;
}
.np-play svg { width: 30px; height: 30px; }
.np-play:active { transform: scale(.93); }
/* Expand button reuses .pb-btn sizing/colour; just give the glyph some room. */
.pb-expand { font-size: 15px; }

/* Reserve space at bottom of content area when player visible — driven by the
   composed --bottom-stack (tabbar + REAL player height + safe-bottom), honest
   per band instead of a magic 90px. */
body:has(.player-bar:not(.hidden)) .content {
    padding-bottom: calc(var(--bottom-stack) + 40px);
}

/* ============================================================
   ── vault.audio v2 redesign ────────────────────────────────
   ============================================================ */

/* ── Sidebar groups + service rows ─────────── */
.nav-group { padding: 14px 14px 4px; }
.nav-group-label {
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.14em;
    color: var(--text-dim);
    padding: 0 6px 10px;
    text-transform: uppercase;
}

.nav-service { margin-bottom: 2px; }
.nav-service-head {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 10px;
    border-radius: var(--radius-sm);
    cursor: pointer;
    color: var(--text-mid);
    transition: background 0.12s, color 0.12s;
}
.nav-service-head:hover { background: var(--bg-card-hover); color: var(--text-strong); }
.nav-service .ns-name {
    flex: 1;
    font-size: 13.5px;
    font-weight: 600;
    letter-spacing: 0.01em;
}
.nav-service.disconnected .ns-name { color: var(--text-dim); }
.nav-service.disconnected .ns-status {
    color: var(--accent); font-size: 10px; font-weight: 800; text-transform: uppercase; letter-spacing: 0.08em;
}
.nav-service.connected .ns-status { display: none; }
.nav-service-soon .ns-status {
    color: var(--text-dim); font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em;
}
.nav-service-soon { opacity: 0.55; pointer-events: none; }

.ns-chev {
    color: var(--text-dim);
    transition: transform 0.18s ease;
    font-size: 16px;
    line-height: 1;
}
.nav-service.open .ns-chev { transform: rotate(90deg); color: var(--accent); }
.nav-service.disconnected .ns-chev { display: none; }

/* Service brand-mark badges — hold an official SVG/PNG glyph inside */
.ns-ic {
    width: 28px; height: 28px;
    border-radius: 7px;
    display: inline-flex; align-items: center; justify-content: center;
    flex-shrink: 0;
    background: var(--bg-input);
    position: relative;
    overflow: hidden;
}
.ns-ic svg {
    width: 18px;
    height: 18px;
    display: block;
}
.ns-ic img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    display: block;
}

/* Qobuz — official favicon (vinyl-Q mark, black on white). Sit on white
   backdrop so the black mark reads properly. */
.ns-ic-qobuz {
    background: #ffffff;
    padding: 2px;
}

/* TIDAL — flat black square, white mark */
.ns-ic-tidal {
    background: #000000;
    color: #ffffff;
}

/* Spotify — green circle with white sound waves (the real brand look) */
.ns-ic-spotify {
    background: #1DB954;
    color: #ffffff;
    border-radius: 50%;
}
.ns-ic-spotify svg { width: 22px; height: 22px; }

/* Apple Music — red rounded square, white note */
.ns-ic-apple {
    background: linear-gradient(135deg, #FA2D48 0%, #FA5773 100%);
    color: #ffffff;
}
.ns-ic-apple svg { width: 19px; height: 19px; }

/* YouTube Music — red rounded square, white play (NOT a circle) */
.ns-ic-yt {
    background: #FF0000;
    color: #ffffff;
}
.ns-ic-yt svg { width: 20px; height: 20px; }

/* Bandcamp — teal square, white mark */
.ns-ic-bandcamp {
    background: #1da0c3;
    color: #ffffff;
}
.ns-ic-bandcamp svg { width: 20px; height: 20px; }

/* Bandcamp collection toolbar (search + format + download-all) */
.bandcamp-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 12px;
    margin-bottom: 18px;
}
.bandcamp-toolbar #bandcamp-search {
    flex: 1 1 220px;
    min-width: 0;
    padding: 9px 12px;
    background: var(--bg-input);
    border: 1px solid var(--border);
    border-radius: 10px;
    color: var(--text);
    font-size: 14px;
}
.bandcamp-toolbar .bandcamp-fmt {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    color: var(--text-dim);
}
.bandcamp-toolbar .bandcamp-fmt select {
    padding: 8px 10px;
    background: var(--bg-input);
    border: 1px solid var(--border);
    border-radius: 10px;
    color: var(--text);
    font-size: 13px;
}

/* Bandcamp collection footer: count + "show more" + infinite-scroll sentinel */
.bandcamp-footer {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 12px;
    padding: 20px 0 8px;
}
.bandcamp-footer .bandcamp-count {
    font-size: 12px;
    color: var(--text-dim);
}

/* Active vault-style item highlight (red bar on left of selected sub-item) */
.nav-service-body {
    list-style: none;
    max-height: 0;
    overflow: hidden;
    transition: max-height 0.22s ease, opacity 0.18s ease;
    opacity: 0;
    padding: 0 10px 0 40px;
}
.nav-service.open .nav-service-body { max-height: 400px; opacity: 1; padding-top: 4px; padding-bottom: 6px; }

.nav-service .nav-item-sub {
    list-style: none;
    padding: 6px 10px;
    font-size: 13px;
    color: var(--text-mid);
    cursor: pointer;
    border-radius: 6px;
    position: relative;
    transition: background 0.12s, color 0.12s;
}
.nav-service .nav-item-sub:hover { background: var(--bg-card-hover); color: var(--text-strong); }
.nav-service .nav-item-sub.active {
    color: var(--accent);
    font-weight: 700;
    background: transparent;
}
.nav-service .nav-item-sub.active::before {
    content: '';
    position: absolute;
    left: -8px; top: 50%;
    width: 3px; height: 18px;
    background: var(--accent);
    border-radius: 2px;
    transform: translateY(-50%);
}

/* ── Sidebar storage strip + Server Status block ── */
.sidebar-storage {
    margin: 8px 14px 10px;
    padding: 10px 12px;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius);
}
.ss-row {
    display: flex; align-items: center; justify-content: space-between;
    font-size: 11px;
    color: var(--text-mid);
}
.ss-row .ss-label { font-weight: 700; letter-spacing: 0.02em; }
.ss-row .ss-value { color: var(--text-strong); font-weight: 600; font-size: 11px; }
.ss-bar { height: 4px; background: var(--bg-input); border-radius: 999px; margin: 6px 0 4px; overflow: hidden; }
.ss-bar-fill { height: 100%; background: var(--accent); border-radius: 999px; transition: width 0.4s; }
.ss-bar-fill.warn { background: var(--warning); }
.ss-bar-fill.danger { background: var(--danger); }
.ss-row-pct { color: var(--accent); font-weight: 800; font-size: 10px; }

.sidebar-server {
    margin: 0 14px 14px;
    padding: 10px 12px;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    display: flex;
    align-items: center;
    gap: 10px;
    cursor: pointer;
    transition: border-color 0.12s;
}
.sidebar-server:hover { border-color: var(--border-strong); }
.srv-mark {
    width: 28px; height: 28px;
    border-radius: 7px;
    background: var(--bg-input);
    display: flex; align-items: center; justify-content: center;
    flex-shrink: 0;
}
.srv-dot {
    width: 8px; height: 8px;
    border-radius: 50%;
    background: var(--text-dim);
    display: inline-block;
}
.srv-dot.online { background: var(--success); box-shadow: 0 0 0 3px rgba(33,208,122,0.18); }
.srv-meta { flex: 1; min-width: 0; }
.srv-title { font-size: 11px; color: var(--text-dim); font-weight: 700; }
.srv-state { font-size: 13px; color: var(--text-strong); font-weight: 700; }

/* ── Topbar (revamped) ─────────────────────── */
.topbar { gap: 24px; }
.topbar-search { position: relative; max-width: 520px; }
.topbar-search-ic {
    position: absolute;
    left: 14px; top: 50%;
    width: 16px; height: 16px;
    transform: translateY(-50%);
    color: var(--text-dim);
    pointer-events: none;
}
.topbar-search input {
    padding-left: 38px !important;
    background: var(--bg-card) !important;
    border-radius: 999px !important;
}
.topbar-search input:focus { background: var(--bg-card-hover) !important; }
.topbar-search .search-kind {
    background: transparent;
    border: none;
    color: var(--text-dim);
    font-size: 12px;
    cursor: pointer;
}

.topbar-controls { display: flex; align-items: center; gap: 12px; position: relative; }

.icon-circle {
    width: 38px; height: 38px;
    border-radius: 50%;
    background: var(--bg-card);
    border: 1px solid var(--border);
    color: var(--text-mid);
    display: inline-flex; align-items: center; justify-content: center;
    cursor: pointer;
    transition: background 0.12s, color 0.12s, border-color 0.12s;
    position: relative;
}
.icon-circle:hover { background: var(--bg-card-hover); color: var(--text-strong); border-color: var(--border-strong); }
.icon-circle svg { width: 17px; height: 17px; }
.bell-dot {
    position: absolute;
    top: 1px; right: 1px;
    min-width: 16px; height: 16px;
    padding: 0 4px;
    border-radius: 999px;
    background: var(--accent);
    color: #fff;
    font-size: 10px;
    font-weight: 800;
    line-height: 16px;
    text-align: center;
    box-shadow: 0 0 0 2px var(--bg-elev);
}

/* ── Notification dropdown panel (desktop bell) ── */
.notif-panel {
    position: absolute;
    top: 56px; right: 0;
    width: 360px; max-width: 92vw;
    background: var(--bg-card);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius);
    box-shadow: var(--shadow-elev);
    z-index: 100;
    overflow: hidden;
}
.notif-panel.hidden { display: none; }
.notif-panel-head {
    display: flex; align-items: center; justify-content: space-between;
    padding: 12px 14px; border-bottom: 1px solid var(--border);
}
.notif-panel-head b { font-size: 14px; color: var(--text-strong); }
.notif-panel-actions { display: flex; gap: 6px; }
.notif-panel-actions button {
    background: transparent; border: none; color: var(--accent);
    font-size: 12px; font-weight: 700; cursor: pointer; padding: 2px 4px;
}
.notif-panel-actions button:hover { color: var(--accent-hover); }
.notif-panel-list { max-height: 60vh; max-height: 60dvh; overflow-y: auto; }
.notif-empty { padding: 24px 14px; text-align: center; color: var(--text-dim); font-size: 13px; }

/* ── Notification rows (shared by desktop panel + mobile sheet) ── */
.notif-row {
    display: flex; gap: 10px; padding: 11px 14px;
    border-bottom: 1px solid var(--border);
    cursor: pointer; transition: background 0.1s;
}
.notif-row:last-child { border-bottom: none; }
.notif-row:hover { background: var(--bg-card-hover); }
.notif-row-unread { background: rgba(239,68,68,0.06); }
.notif-ic {
    flex-shrink: 0; width: 30px; height: 30px; border-radius: 8px;
    background: var(--bg-input); display: flex; align-items: center;
    justify-content: center; font-size: 15px; color: var(--text-mid);
}
.notif-row-err .notif-ic { color: var(--danger); background: rgba(239,68,68,0.12); }
.notif-main { min-width: 0; flex: 1; }
.notif-title {
    font-size: 13px; font-weight: 700; color: var(--text-strong);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.notif-body {
    font-size: 12px; color: var(--text-mid); margin-top: 2px;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.notif-time { font-size: 11px; color: var(--text-dim); margin-top: 3px; }

/* ── Settings → Notifications (push) ── */
.notif-set-sub { font-size: 13px; color: var(--text-mid); margin-bottom: 12px; max-width: 520px; }
.notif-set-actions { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
.notif-set-note { font-size: 12px; color: var(--text-dim); margin-top: 10px; max-width: 520px; line-height: 1.5; }
.notif-devices { display: flex; flex-direction: column; gap: 8px; max-width: 520px; }
.notif-device {
    display: flex; align-items: center; justify-content: space-between;
    padding: 10px 12px; background: var(--bg-card); border: 1px solid var(--border);
    border-radius: 8px; font-size: 13px; color: var(--text-strong);
}
.notif-toggles { display: flex; flex-direction: column; gap: 12px; max-width: 520px; }
.notif-toggle { display: flex; align-items: center; gap: 10px; font-size: 14px; color: var(--text-strong); cursor: pointer; }
.notif-toggle input { width: 18px; height: 18px; accent-color: var(--accent); cursor: pointer; }

.user-pill {
    /* Desktop topbar shows just the circular initials avatar at the far right;
       the name/email text + chevron are hidden. The whole pill stays clickable
       (→ toggleUserMenu) and remains the last element in .topbar-controls. */
    display: flex;
    align-items: center;
    background: transparent;
    border: 0;
    border-radius: 999px;
    padding: 0;
    cursor: pointer;
    transition: opacity 0.12s;
}
.user-pill:hover { opacity: 0.85; }
.user-pill .avatar {
    width: 36px; height: 36px;
    border-radius: 50%;
    background: var(--accent);
    color: #fff;
    font-weight: 800;
    font-size: 13px;
    display: inline-flex; align-items: center; justify-content: center;
    border: 2px solid var(--accent);
}
.user-pill-text { display: none; }
.user-pill-text b { font-size: 13px; font-weight: 700; color: var(--text-strong); }
.user-pill-text span { font-size: 11px; color: var(--text-dim); }
.user-pill-chev { display: none; }

.user-menu {
    position: absolute;
    top: 56px; right: 0;
    background: var(--bg-card);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius);
    box-shadow: var(--shadow-elev);
    min-width: 180px;
    padding: 6px 0;
    z-index: 100;
}
.user-menu-item {
    display: block;
    padding: 10px 16px;
    font-size: 13px;
    color: var(--text);
    cursor: pointer;
    transition: background 0.1s, color 0.1s;
}
.user-menu-item:hover { background: var(--bg-card-hover); color: var(--accent); }
.user-menu-item-danger { color: var(--danger); }
.user-menu-item-danger:hover { color: var(--danger); background: rgba(239,68,68,0.10); }
.user-menu-sep { height: 1px; background: var(--border); margin: 4px 0; }

/* ── Dashboard layout ──────────────────────── */
.dashboard { padding: 24px 28px 80px; display: flex; flex-direction: column; gap: 22px; }

.kpi-grid {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 16px;
}
@media (max-width: 1180px) { .kpi-grid { grid-template-columns: repeat(2, 1fr); } }

.kpi-card {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 18px 20px;
    display: flex;
    gap: 14px;
    position: relative;
    transition: border-color 0.12s;
}
.kpi-card:hover { border-color: var(--border-strong); }
.kpi-ic {
    width: 44px; height: 44px;
    border-radius: 12px;
    background: var(--bg-input);
    display: inline-flex; align-items: center; justify-content: center;
    color: var(--text-mid);
    flex-shrink: 0;
}
.kpi-ic svg { width: 22px; height: 22px; }
.kpi-body { flex: 1; min-width: 0; }
.kpi-label { font-size: 11px; color: var(--text-dim); font-weight: 700; letter-spacing: 0.02em; }
.kpi-value {
    font-size: 28px;
    line-height: 1.05;
    font-weight: 800;
    color: var(--text-strong);
    margin: 4px 0 2px;
    letter-spacing: -0.01em;
}
.kpi-value-sync { font-size: 22px; color: var(--success); }
.kpi-value-sync.sync-busy { color: var(--accent); }
.kpi-value-sync.sync-warn { color: var(--warning); }
.kpi-unit { font-size: 12px; color: var(--text-mid); }
.kpi-meta { margin-top: 6px; font-size: 11px; color: var(--text-mid); font-weight: 700; }
.kpi-meta-warn { color: var(--warning); }
.kpi-meta-bad  { color: var(--danger); }
.kpi-corner {
    position: absolute;
    top: 16px; right: 18px;
    width: 22px; height: 22px;
    color: var(--success);
}
.kpi-corner svg { width: 100%; height: 100%; }

/* ── Two-column dash layout ──────────────── */
.dash-cols {
    display: grid;
    grid-template-columns: 1fr 360px;
    gap: 16px;
}
.dash-cols-bottom { grid-template-columns: 1fr 360px; }
@media (max-width: 1100px) {
    .dash-cols, .dash-cols-bottom { grid-template-columns: 1fr; }
}

.dash-card {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 18px 20px;
    /* Critical for the grid layout: without this, the inner recent-carousel
       (which has overflow-x:auto) refuses to shrink below its content width
       and pushes the right-hand Queue card off-screen. */
    min-width: 0;
}
.dash-card-head {
    display: flex; align-items: center; justify-content: space-between;
    margin-bottom: 14px;
}
.dash-card-head h3 { font-size: 16px; font-weight: 800; color: var(--text-strong); letter-spacing: -0.01em; }
.dash-card-link { font-size: 12px; color: var(--accent); font-weight: 700; cursor: pointer; }
.dash-card-link:hover { color: var(--accent-hover); }

/* Recent Albums — wrapping grid on desktop/tablet (section grows TALLER instead
   of scrolling left/right). Mobile (≤767px) restores the horizontal carousel. */
.recent-carousel {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;   /* centre the covers within the section */
    gap: 16px;
    padding-bottom: 6px;
}
.recent-carousel::-webkit-scrollbar { height: 6px; }
.recent-carousel::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 3px; }
.recent-card {
    flex: 0 0 150px;
    width: 150px;          /* hard cap — flex-basis alone has been overridden by browser/extension CSS in the wild */
    max-width: 150px;      /* belt + braces */
    min-width: 0;           /* lets ellipsis kick in on flex children */
    scroll-snap-align: start;
    cursor: pointer;
    overflow: hidden;       /* anything escaping the card never affects siblings */
}
.recent-cover {
    width: 150px; height: 150px;
    background: var(--bg-input);
    border-radius: var(--radius);
    overflow: hidden;
    margin-bottom: 8px;
    transition: transform 0.18s;
    position: relative;     /* so dl-progress-pill positions inside the cover */
}
.recent-card:hover .recent-cover { transform: translateY(-3px); }
.recent-cover img { width: 100%; height: 100%; object-fit: cover; }
.recent-title {
    font-size: 13px; font-weight: 700; color: var(--text-strong);
    /* Wrap long titles onto a 2nd line, clamp at 2 with ellipsis (was
       single-line ellipsis; user requested wrap-or-truncate for long names). */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    line-clamp: 2;
    overflow: hidden;
    word-break: break-word;     /* break inside a giant unspaced token */
    line-height: 1.25;
    min-height: 2.5em;          /* reserves 2 lines so cards align */
}
.recent-artist {
    font-size: 12px; color: var(--text-mid); margin-top: 2px;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.recent-tech {
    font-size: 11px; color: var(--text-dim); margin-top: 4px; letter-spacing: 0.02em;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.recent-empty { color: var(--text-dim); font-size: 13px; padding: 40px 0; text-align: center; width: 100%; }

/* Downloads queue side */
.dash-queue { display: flex; flex-direction: column; gap: 12px; }
.dash-queue-empty { color: var(--text-dim); font-size: 12px; padding: 12px 0; text-align: center; }
.dash-queue-row { display: flex; gap: 10px; align-items: center; }
.dqr-cover {
    width: 42px; height: 42px;
    background: var(--bg-input);
    border-radius: 8px;
    overflow: hidden;
    flex-shrink: 0;
}
.dqr-cover img { width: 100%; height: 100%; object-fit: cover; }
.dqr-meta { flex: 1; min-width: 0; }
.dqr-title { font-size: 12px; font-weight: 700; color: var(--text-strong);
              overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dqr-sub { font-size: 11px; color: var(--text-dim);
            overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dqr-bar {
    height: 3px; background: var(--bg-input); border-radius: 999px;
    margin-top: 4px; overflow: hidden;
}
.dqr-bar-fill { height: 100%; background: var(--accent); transition: width 0.4s; }
.dqr-pct { font-size: 11px; font-weight: 800; color: var(--accent); flex-shrink: 0; }

.dash-queue-foot {
    display: flex; justify-content: space-between; align-items: center;
    margin-top: 14px; padding-top: 12px;
    border-top: 1px solid var(--border);
}
.dash-queue-count { font-size: 11px; color: var(--accent); font-weight: 700; }

/* Now Playing card */
.dash-now { padding: 18px 20px 14px; }
.dash-now-tools { display: flex; gap: 6px; }
.icon-mini {
    width: 28px; height: 28px;
    border-radius: 50%;
    background: transparent;
    border: none;
    color: var(--text-dim);
    cursor: pointer;
    font-size: 14px;
}
.icon-mini:hover { color: var(--text-strong); background: var(--bg-card-hover); }
.icon-mini:disabled { opacity: 0.5; cursor: default; }

.now-row {
    display: grid;
    grid-template-columns: 56px 1fr auto auto;
    gap: 16px;
    align-items: center;
}
.now-cover {
    width: 56px; height: 56px;
    background: var(--bg-input);
    border-radius: 8px;
    overflow: hidden;
}
.now-cover img { width: 100%; height: 100%; object-fit: cover; }
.now-title { font-size: 13px; font-weight: 700; color: var(--text-strong); }
.now-artist { font-size: 12px; color: var(--text-mid); }
.now-album { font-size: 11px; color: var(--text-dim); margin-top: 1px; }
.now-quality { font-size: 10px; color: var(--accent); letter-spacing: 0.04em; margin-top: 3px; font-weight: 700; }
.now-ctl { display: flex; align-items: center; gap: 6px; }
.now-btn {
    background: transparent; border: none; color: var(--text-mid);
    width: 32px; height: 32px; border-radius: 50%; cursor: pointer; font-size: 18px;
}
.now-btn:hover { color: var(--text-strong); }
.now-btn:disabled { opacity: 0.55; cursor: default; }
.now-play {
    width: 40px; height: 40px;
    border-radius: 50%;
    background: transparent;
    color: var(--accent);
    border: 2px solid var(--accent);
    cursor: pointer;
    font-size: 14px;
    display: inline-flex; align-items: center; justify-content: center;
}
.now-play:disabled { opacity: 0.65; cursor: default; }
.now-vol { display: flex; align-items: center; gap: 8px; color: var(--text-mid); }
.now-vol svg { width: 16px; height: 16px; }
.now-vol-bar { width: 80px; height: 4px; background: var(--bg-input); border-radius: 999px; overflow: hidden; }
.now-vol-fill { height: 100%; background: var(--accent); border-radius: 999px; }

.now-progress {
    display: flex; align-items: center; gap: 10px;
    margin-top: 12px;
}
.now-progress-time { font-size: 11px; color: var(--text-dim); min-width: 36px; text-align: center; }
.now-progress-bar { flex: 1; height: 4px; background: var(--bg-input); border-radius: 999px; overflow: hidden; }
.now-progress-fill { height: 100%; background: var(--accent); border-radius: 999px; }

/* Server Status card */
.dash-srv .srv-grid {
    display: flex; flex-direction: column; gap: 10px;
}
.srv-grid-row {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    padding: 4px 0;
}
.srv-grid-ic {
    width: 32px; height: 32px; border-radius: 8px;
    background: var(--bg-input);
    color: var(--text-mid);
    display: inline-flex; align-items: center; justify-content: center;
    margin-right: 8px;
}
.srv-grid-ic svg { width: 16px; height: 16px; }
.srv-grid-meta { flex: 1; }
.srv-grid-label { font-size: 11px; color: var(--text-dim); }
.srv-grid-val { font-size: 13px; color: var(--text-strong); font-weight: 700; }
.srv-online { display: inline-flex; align-items: center; gap: 6px; color: var(--success); }

/* ── Library: downloading-in-progress pill + view modes + tiles ── */
.dl-progress-pill, .recent-cover .dl-progress-pill {
    position: absolute;
    top: 6px; right: 6px;
    background: var(--accent);
    color: #fff;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.3px;
    padding: 3px 7px;
    border-radius: 999px;
    box-shadow: 0 2px 6px rgba(0,0,0,0.4);
    pointer-events: none;
    animation: dlpulse 1.6s ease-in-out infinite;
}
@keyframes dlpulse {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0.6; }
}
/* Terminal "finished with errors" — static (no pulse), amber so it reads as
   "done with a skipped track", not "still downloading". */
.dl-progress-pill.dl-progress-pill-warn {
    background: var(--warning, #f0b95e);
    color: #1a1a1a;
    animation: none;
}

/* View-mode switch in detail hero */
.view-switch {
    display: inline-flex;
    gap: 2px;
    background: var(--bg-input);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 2px;
    margin-left: auto;
}
.view-switch .view-btn {
    background: transparent; border: 0; color: var(--text-dim);
    padding: 4px 10px; border-radius: 6px; cursor: pointer;
    font-size: 14px; line-height: 1;
}
.view-switch .view-btn:hover { color: var(--text); }
.view-switch .view-btn.active {
    background: var(--accent); color: #fff;
}

/* Tile grid for compact / large detail views */
.lib-tiles { display: grid; gap: 14px; padding: 16px 0; }
/* Disc separators for multi-disc box sets */
.lib-disc-head {
    grid-column: 1 / -1;
    font-size: 13px; font-weight: 700; letter-spacing: .04em;
    text-transform: uppercase; color: var(--accent, #ef4444);
    padding: 10px 2px 2px; border-bottom: 1px solid var(--border);
}
.lf-disc-row td.lf-disc {
    font-size: 12px; font-weight: 700; letter-spacing: .04em;
    text-transform: uppercase; color: var(--accent, #ef4444);
    padding: 14px 8px 6px; background: transparent;
}
.lib-tiles-compact { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); }
.lib-tiles-large   { grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); }
.lib-tile {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: 10px;
    overflow: hidden;
    cursor: pointer;
    transition: transform 0.12s ease, border-color 0.12s ease;
}
.lib-tile:hover {
    transform: translateY(-2px);
    border-color: var(--accent);
}
.lib-tile-cover {
    position: relative;
    aspect-ratio: 1 / 1;
    background: var(--bg-input);
    overflow: hidden;
}
.lib-tile-cover img {
    width: 100%; height: 100%; object-fit: cover; display: block;
}
.lib-tile-play {
    position: absolute;
    inset: 0;
    display: flex; align-items: center; justify-content: center;
    color: #fff; font-size: 36px;
    opacity: 0;
    background: rgba(0,0,0,0.35);
    transition: opacity 0.12s ease;
}
.lib-tile:hover .lib-tile-play { opacity: 1; }
.lib-tile-title {
    padding: 8px 10px 2px;
    font-size: 13px; font-weight: 600; color: var(--text-strong);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.lib-tile-meta {
    padding: 0 10px 10px;
    font-size: 11px; color: var(--text-dim);
}
.lib-tiles-large .lib-tile-title { font-size: 14px; padding: 10px 12px 2px; }
.lib-tiles-large .lib-tile-meta  { font-size: 12px; padding: 0 12px 12px; }

/* ── Downloads: in-flight expansion panel ── */
.dl-inflight {
    margin-top: 10px;
    padding: 10px 12px;
    background: var(--bg-input);
    border: 1px solid var(--border);
    border-radius: 8px;
}
.dl-inflight-head {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 12px;
    margin-bottom: 8px;
}
.dl-inflight-label {
    font-size: 11px; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.6px;
}
.dl-inflight-speed {
    font-size: 14px; font-weight: 700; color: var(--accent);
    font-variant-numeric: tabular-nums;
}
.dl-inflight-hint {
    font-size: 11px; color: var(--text-dim); font-style: italic;
}
.dl-inflight-files {
    display: flex; flex-direction: column; gap: 6px;
}
.dl-inflight-file {
    display: grid;
    grid-template-columns: 1fr 100px 130px;
    gap: 10px;
    align-items: center;
}
.dl-if-name {
    font-size: 12px; color: var(--text);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dl-if-bar {
    height: 4px;
    background: var(--bg-card);
    border-radius: 2px;
    overflow: hidden;
}
.dl-if-fill {
    height: 100%;
    background: var(--accent);
    transition: width 0.3s ease;
}
.dl-if-sz {
    font-size: 11px; color: var(--text-dim);
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* ── Zip & Download modal + toast ── */
.zip-modal { position: fixed; inset: 0; z-index: 9999; display: flex; align-items: center; justify-content: center; }
.zip-modal.hidden { display: none !important; }   /* defeat any other display: rule */
.zip-modal-backdrop {
    position: absolute; inset: 0; z-index: 1;
    background: rgba(0,0,0,0.55);
}
.zip-modal-panel {
    position: relative; z-index: 2;       /* explicit > backdrop, defends against future siblings */
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: 12px;
    width: min(560px, 92vw);
    max-height: 90vh;
    max-height: 90dvh;
    overflow-y: auto;
    padding: 24px;
    box-shadow: 0 30px 80px rgba(0,0,0,0.45);
    color: var(--text);                   /* explicit color in case parent inherits something dark */
}
.zip-modal-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px; }
.zip-modal-head h3 { font-size: 18px; font-weight: 800; color: var(--text-strong); }
.zip-modal-head-actions { display: flex; gap: 8px; }
.zip-modal-body { display: flex; flex-direction: column; gap: 14px; }
.zip-fn { font-size: 13px; color: var(--text); font-family: 'SF Mono', Menlo, monospace; word-break: break-all; }
.zip-bar { height: 10px; background: var(--bg-input); border-radius: 5px; overflow: hidden; }
.zip-bar-fill { height: 100%; background: var(--accent); transition: width 0.4s ease; }
.zip-stats { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 24px; }
.zip-stats > div { display: flex; justify-content: space-between; align-items: baseline; padding: 8px 0; border-bottom: 1px solid var(--border); }
.zip-stats .lbl { font-size: 11px; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.6px; }
.zip-stats .val { font-size: 14px; color: var(--text-strong); font-weight: 700; font-variant-numeric: tabular-nums; }
.zip-error { padding: 10px 12px; background: rgba(255,80,80,0.12); border-left: 3px solid var(--danger); color: var(--danger); font-size: 13px; border-radius: 4px; }
.zip-done { display: flex; justify-content: flex-end; gap: 8px; padding-top: 6px; }

/* Download modal: direct copyable link for external download managers */
.dlm-or { font-size: 12px; color: var(--text-dim); margin-bottom: 8px; }
.dlm-link {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 12px;
    color: var(--text-mid);
    background: var(--bg-input);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 10px 12px;
    word-break: break-all;
    line-height: 1.5;
    user-select: all;
}
.dlm-link.dlm-link-ready { cursor: pointer; color: var(--text-strong); }
.dlm-link.dlm-link-ready:hover { border-color: var(--accent); color: var(--accent); }

.zip-toast {
    position: fixed;
    right: 16px; bottom: 16px;
    z-index: 9998;
    background: var(--bg-card);
    border: 1px solid var(--accent);
    color: var(--text-strong);
    padding: 10px 14px;
    border-radius: 8px;
    font-size: 13px;
    font-weight: 700;
    cursor: pointer;
    box-shadow: 0 10px 30px rgba(0,0,0,0.45);
    animation: zipPulse 2.2s ease-in-out infinite;
}
.zip-toast.hidden { display: none; }
/* On phones the minimised toast must clear the bottom tab bar (and the
   mini-player when a track is loaded) so it never covers navigation. */
@media (max-width: 767px), (min-width: 768px) and (max-width: 1024px) and (orientation: portrait) {
    .zip-toast {
        left: 12px; right: 12px;
        bottom: calc(var(--bottom-stack) + 12px);
    }
}
@keyframes zipPulse {
    0%, 100% { box-shadow: 0 10px 30px rgba(0,0,0,0.45); }
    50%      { box-shadow: 0 10px 30px rgba(239,68,68,0.45); }
}

/* ──────────── AI Assistant chat ──────────── */

.ai-chat-shell {
    display: grid;
    grid-template-columns: 260px 1fr;
    gap: 16px;
    height: calc(100vh - 220px);
    height: calc(100dvh - 220px);   /* dvh tracks the dynamic viewport (iOS URL bar) */
    min-height: 480px;
}
/* Active-chat name in the compact phone header — desktop keeps title+subtitle. */
.ai-mobile-chat-title { display: none; }
/* Phone-only iMessage-style chrome — hidden on desktop/tablet (shown in phone band). */
.ai-mobile-back, .ai-mobile-menu-btn, .ai-mobile-menu, .ai-mobile-cam, .ai-chips-toggle, .ai-credit-bottom { display: none; }

/* VAULT coin mark — a struck-through "V" (like the ₽ sign). Used wherever the
   internal currency balance is shown. */
.ai-coin {
    display: inline-block; font-weight: 800; text-decoration: line-through;
    text-decoration-thickness: 2px; color: var(--accent); transform: translateY(-0.5px);
}
/* Per-operation "tokens · cost" line inside the chat thread (replaces header strip). */
.ai-cost-line {
    align-self: center; margin: 2px 0 4px; padding: 2px 10px;
    font-size: 11px; color: var(--text-dim); opacity: 0.8;
}
.ai-chat-sidebar {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 12px;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.ai-chat-sidebar-head {
    font-size: 11px;
    color: var(--text-dim);
    text-transform: uppercase;
    letter-spacing: 0.6px;
    padding: 4px 8px;
}
.ai-chat-list { display: flex; flex-direction: column; gap: 4px; }
.ai-chat-list-item {
    padding: 8px 10px;
    border-radius: 6px;
    cursor: pointer;
    font-size: 13px;
    color: var(--text);
    border: 1px solid transparent;
    transition: background 0.12s, border 0.12s;
    position: relative;
}
.ai-chat-list-item:hover { background: var(--bg-input); }
.ai-chat-list-item.active { background: var(--bg-input); border-color: var(--accent); color: var(--text-strong); }
.ai-chat-list-item .acli-title {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    line-height: 1.3;
    padding-right: 18px;
}
.ai-chat-list-item .acli-meta { font-size: 10px; color: var(--text-dim); margin-top: 3px; }
.ai-chat-list-item .acli-del {
    position: absolute;
    top: 4px;
    right: 4px;
    width: 28px;
    height: 28px;
    border-radius: 50%;
    background: transparent;
    color: var(--text-dim);
    border: none;
    font-size: 17px;
    line-height: 26px;
    cursor: pointer;
    padding: 0;
    opacity: 0;
    transition: opacity 0.15s, background 0.15s, color 0.15s;
    /* Защита от случайного перехвата клика родительским div'ом на
       устройствах без hover. Без stopPropagation+visibility юзер на
       мобиле тыкал в невидимый × и попадал в onclick родителя —
       вместо удаления грузил историю диалога. */
    z-index: 2;
}
.ai-chat-list-item:hover .acli-del { opacity: 1; }
.ai-chat-list-item .acli-del:hover {
    background: var(--danger, #e55);
    color: #fff;
}
/* На touch-устройствах :hover не срабатывает — показываем × всегда,
   иначе кнопка невидима и tap уходит на родительский onclick
   (открытие диалога вместо удаления). См. bug fix 2026-05-29. */
@media (hover: none) {
    .ai-chat-list-item .acli-del { opacity: 0.6; }
    .ai-chat-list-item .acli-del:active {
        opacity: 1;
        background: var(--danger, #e55);
        color: #fff;
    }
}

.ai-chat-main {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    display: flex;
    flex-direction: column;
    overflow: hidden;
}
.ai-chat-thread {
    flex: 1;
    overflow-y: auto;
    padding: 20px 24px;
    display: flex;
    flex-direction: column;
    gap: 14px;
}
.ai-chat-empty {
    margin: auto;
    max-width: 540px;
    text-align: center;
    color: var(--text-dim);
}
.ai-chat-empty h3 { color: var(--text-strong); font-size: 22px; margin-bottom: 10px; }
.ai-chat-empty p { font-size: 14px; line-height: 1.5; margin-bottom: 14px; }
.ai-chat-empty ul { list-style: none; padding: 0; display: flex; flex-direction: column; gap: 6px; font-size: 13px; }
.ai-chat-empty li {
    padding: 8px 12px;
    background: var(--bg-input);
    border-radius: 6px;
    font-style: italic;
    cursor: pointer;
}
.ai-chat-empty li:hover { background: rgba(201,168,94,0.12); color: var(--text); }

.ai-msg {
    display: flex;
    gap: 12px;
    align-items: flex-start;
    animation: aiMsgIn 0.2s ease-out;
}
@keyframes aiMsgIn {
    from { opacity: 0; transform: translateY(4px); }
    to { opacity: 1; transform: translateY(0); }
}
.ai-msg-avatar {
    width: 32px; height: 32px;
    border-radius: 50%;
    background: var(--bg-input);
    color: var(--text-strong);
    display: flex; align-items: center; justify-content: center;
    font-size: 12px;
    font-weight: 700;
    flex-shrink: 0;
}
.ai-msg-user .ai-msg-avatar { background: var(--accent); color: #000; }
.ai-msg-assistant .ai-msg-avatar { background: rgba(201,168,94,0.18); color: var(--accent); }
.ai-msg-body {
    flex: 1;
    min-width: 0;
    background: var(--bg-input);
    padding: 10px 14px;
    border-radius: 10px;
    font-size: 14px;
    line-height: 1.55;
    white-space: pre-wrap;
    word-wrap: break-word;
    color: var(--text);
}
.ai-msg-user .ai-msg-body { background: rgba(201,168,94,0.10); color: var(--text-strong); }

.ai-tool-pill {
    display: inline-block;
    background: rgba(255,255,255,0.04);
    border: 1px solid var(--border);
    border-radius: 999px;
    padding: 2px 10px;
    font-size: 11px;
    color: var(--text-dim);
    margin: 2px 4px 2px 0;
    font-family: monospace;
}
.ai-msg-loading {
    display: flex; align-items: center; gap: 10px;
    color: var(--text-dim);
    font-size: 13px;
    font-style: italic;
}
.ai-msg-loading::before {
    content: '●';
    animation: aiDots 1.2s steps(3) infinite;
}
@keyframes aiDots {
    0%   { content: '·  '; }
    33%  { content: '·· '; }
    66%  { content: '···'; }
    100% { content: '·  '; }
}

.ai-chat-input {
    display: flex;
    gap: 10px;
    padding: 12px 16px;
    border-top: 1px solid var(--border);
    background: var(--bg-card);
}
.ai-chat-input textarea {
    flex: 1;
    background: var(--bg-input);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 10px 12px;
    color: var(--text);
    font-family: inherit;
    font-size: 14px;
    resize: none;
    min-height: 44px;
    max-height: 200px;
}
.ai-chat-input textarea:focus { outline: none; border-color: var(--accent); }
.ai-chat-input button[type="submit"] { align-self: flex-end; }
.ai-chat-input textarea:disabled { opacity: 0.5; cursor: not-allowed; }

/* Compact icon Send button (replaces the "Send"/"Отправить" text — saves space). */
.ai-send-btn {
    flex: 0 0 auto;
    width: 44px;
    height: 44px;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 10px;
    position: relative;
}
.ai-send-btn svg { display: block; transform: translateX(-1px); }   /* optical centering of the paper-plane */
.ai-send-btn:disabled { cursor: default; }
/* Busy state: hide the icon and spin a ring in its place. */
.ai-send-btn.busy { opacity: 1; }
.ai-send-btn.busy svg { visibility: hidden; }
.ai-send-btn.busy::after {
    content: "";
    position: absolute;
    width: 16px; height: 16px;
    border: 2px solid rgba(255, 255, 255, 0.35);
    border-top-color: #fff;
    border-radius: 50%;
    animation: ai-send-spin 0.7s linear infinite;
}
@keyframes ai-send-spin { to { transform: rotate(360deg); } }

/* "Chat finished — start a new one" bar (after a playlist is created). */
.ai-chat-closed {
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    padding: 12px 16px;
    border-top: 1px solid var(--border);
    background: var(--bg-card);
    font-size: 13px; color: var(--text-dim);
}

/* Per-chat cumulative cost stat in the header. */
.ai-conv-cost {
    font-size: 12px; color: var(--text-dim);
    padding: 3px 9px; border: 1px solid var(--border); border-radius: 999px;
    white-space: nowrap; align-self: center;
}

/* "This chat is getting expensive" banner, sits above the composer. */
.ai-cost-banner {
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    flex-wrap: wrap;
    padding: 10px 14px;
    border-top: 1px solid var(--warning, #f0b95e);
    background: color-mix(in srgb, var(--warning, #f0b95e) 12%, transparent);
    font-size: 13px; color: var(--text);
}
.ai-cost-banner .acb-text { flex: 1; min-width: 180px; }
.ai-cost-banner .acb-actions { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.ai-cost-banner .acb-x {
    background: none; border: none; color: var(--text-dim);
    font-size: 18px; line-height: 1; cursor: pointer; padding: 0 4px;
}
.ai-cost-banner .acb-x:hover { color: var(--text); }

/* Markdown rendering inside assistant messages — quiet, no harsh colours.
   The whole point is that text reads as natural prose with light structural
   hints, not a coloured wall of bold accents. */
.ai-msg-md { white-space: normal; }
.ai-msg-md .md-p { margin: 0 0 8px 0; line-height: 1.6; }
.ai-msg-md .md-p:last-child { margin-bottom: 0; }
.ai-msg-md .md-h {
    color: var(--text-strong);
    margin: 12px 0 4px 0;
    font-weight: 600;
    letter-spacing: -0.005em;
}
.ai-msg-md h3.md-h { font-size: 14px; }
.ai-msg-md h4.md-h { font-size: 13px; }
.ai-msg-md h5.md-h { font-size: 12px; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.6px; font-weight: 600; }
.ai-msg-md .md-strong { color: var(--text-strong); font-weight: 600; }
.ai-msg-md em { color: var(--text); font-style: italic; opacity: 0.85; }
.ai-msg-md .md-code {
    background: rgba(255,255,255,0.06);
    color: var(--text-strong);
    padding: 1px 5px;
    border-radius: 3px;
    font-family: 'SF Mono', Menlo, monospace;
    font-size: 0.88em;
}
.ai-msg-md .md-pre {
    background: rgba(0,0,0,0.25);
    border: 1px solid var(--border);
    padding: 8px 12px;
    border-radius: 6px;
    overflow-x: auto;
    margin: 6px 0;
}
.ai-msg-md .md-pre code {
    background: none; color: var(--text); padding: 0;
    font-family: 'SF Mono', Menlo, monospace;
    font-size: 12px;
}
.ai-msg-md .md-list { margin: 4px 0 8px 0; padding-left: 20px; }
.ai-msg-md .md-list li { margin: 2px 0; line-height: 1.55; }
.ai-msg-md ul.md-list li::marker { color: var(--text-dim); }
.ai-msg-md ol.md-list li::marker { color: var(--text-dim); }
.ai-msg-md .md-link { color: var(--text-strong); text-decoration: underline; text-decoration-color: var(--border); text-underline-offset: 2px; }
.ai-msg-md .md-link:hover { text-decoration-color: var(--text); }

/* AI Playlist card (in chat or library) */
.ai-playlist-card {
    background: linear-gradient(135deg, rgba(201,168,94,0.10) 0%, rgba(0,0,0,0) 70%), var(--bg-card);
    border: 1px solid var(--accent);
    border-radius: var(--radius);
    padding: 16px;
    margin-top: 8px;
    display: flex;
    flex-direction: column;
    gap: 12px;
}
.ai-playlist-card .ap-title {
    font-size: 16px;
    font-weight: 700;
    color: var(--text-strong);
}
.ai-playlist-card .ap-desc { font-size: 13px; color: var(--text-dim); }
.ai-playlist-card .ap-hint {
    font-size: 12px;
    padding: 6px 10px;
    background: rgba(201,168,94,0.06);
    border: 1px dashed var(--border);
    border-radius: 6px;
    color: var(--text);
}
.ai-playlist-card .ap-tracks {
    display: flex; flex-direction: column;
    max-height: 260px;
    overflow-y: auto;
    border: 1px solid var(--border);
    border-radius: 6px;
}
.ai-playlist-card .ap-track {
    padding: 6px 10px;
    font-size: 12px;
    border-bottom: 1px solid var(--border);
    display: flex;
    justify-content: space-between;
    gap: 8px;
}
.ai-playlist-card .ap-track:last-child { border-bottom: none; }
.ai-playlist-card .ap-track .apt-title { color: var(--text); flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ai-playlist-card .ap-track .apt-artist { color: var(--text-dim); font-size: 11px; flex-shrink: 0; }
/* Smoke #14 Bug E: research playlists show a rich per-track description.
   Stack the title-row + description vertically when present. */
.ai-playlist-card .ap-track.ap-track-rich {
    flex-direction: column;
    align-items: stretch;
    gap: 3px;
}
.ai-playlist-card .ap-track.ap-track-rich .apt-main {
    display: flex;
    justify-content: space-between;
    gap: 8px;
    align-items: baseline;
}
.ai-playlist-card .ap-track .apt-desc {
    color: var(--text-dim);
    font-size: 11px;
    line-height: 1.45;
    white-space: normal;
}
.ai-playlist-card .ap-actions {
    display: flex; gap: 8px; justify-content: flex-end;
}
.ai-playlist-card.downloaded { opacity: 0.7; border-color: var(--success, #4ade80); }

/* Playlist card head: title + stats badges */
.ai-playlist-card .ap-head {
    display: flex; justify-content: space-between; align-items: center; gap: 12px;
}
.ai-playlist-card .ap-stats {
    display: flex; gap: 6px; font-size: 11px; font-weight: 600;
    font-variant-numeric: tabular-nums;
}
.ai-playlist-card .ap-stat { padding: 2px 8px; border-radius: 999px; border: 1px solid var(--border); }
.ai-playlist-card .ap-stat-ok { color: #4ade80; border-color: rgba(74,222,128,0.4); }
.ai-playlist-card .ap-stat-q  { color: var(--accent); border-color: var(--accent); }
.ai-playlist-card .ap-stat-x  { color: var(--danger, #e55); border-color: rgba(229,85,85,0.4); }
.ai-playlist-card .ap-stat-total { color: var(--text-dim); padding: 2px 4px; }

/* Section labels between playable / needs / unresolved */
.ai-playlist-card .ap-section-label {
    font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px;
    color: var(--text-dim); margin-top: 6px; padding: 0 2px;
}
.ai-playlist-card .ap-section-q  { color: var(--accent); }
.ai-playlist-card .ap-section-x  { color: var(--danger, #e55); }

/* Per-track service badge in playable section */
.ai-playlist-card .ap-track .apt-svc {
    font-size: 9px; text-transform: uppercase; padding: 1px 5px;
    border-radius: 3px; background: rgba(255,255,255,0.05);
    color: var(--text-dim); margin-left: 6px;
}

/* Detail-view top bar: Back + Edit */
.ap-detail-bar { display: flex; gap: 8px; align-items: center; margin-bottom: 14px; }

/* ── AI playlist editor (drag-reorder + delete) ───────────────────────── */
.ape-editor {
    background: var(--bg-card); border: 1px solid var(--border);
    border-radius: var(--radius); padding: 16px;
    display: flex; flex-direction: column; gap: 12px;
}
.ape-editor .ape-head {
    display: flex; align-items: center; gap: 12px;
}
.ape-editor .ape-title-h {
    font-size: 16px; font-weight: 800; color: var(--text-strong);
    flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Editable playlist name in the editor (2026-05-29). Looks like the title
   until focused, then reads as a field. */
.ape-editor .ape-title-input {
    flex: 1; min-width: 0;
    font-size: 16px; font-weight: 800; color: var(--text-strong);
    background: transparent;
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    padding: 4px 8px;
    transition: border-color 0.12s, background 0.12s;
}
.ape-editor .ape-title-input:hover { border-color: var(--border); }
.ape-editor .ape-title-input:focus {
    outline: none; border-color: var(--accent); background: var(--bg-card);
}
.ape-editor .ape-count { font-size: 12px; color: var(--text-dim); flex-shrink: 0; }
.ape-editor .ape-hint { font-size: 12px; color: var(--text-dim); }
.ape-editor .ape-list {
    display: flex; flex-direction: column;
    border: 1px solid var(--border); border-radius: 8px; overflow: hidden;
}
.ape-track {
    display: flex; align-items: center; gap: 10px;
    padding: 8px 10px; border-bottom: 1px solid var(--border);
    background: var(--bg-card); font-size: 13px;
}
.ape-track:last-child { border-bottom: none; }
.ape-track.ape-dragging {
    opacity: 0.92; background: var(--bg-card-hover);
    box-shadow: 0 6px 18px rgba(0,0,0,0.45); border-radius: 6px;
}
.ape-list.ape-dragging-active { cursor: grabbing; }
.ape-handle {
    flex-shrink: 0; cursor: grab; color: var(--text-dim);
    font-size: 18px; line-height: 1; user-select: none;
    /* critical: a touch-drag on the handle must NOT scroll the page */
    touch-action: none;
    min-width: 28px; min-height: 28px;
    display: flex; align-items: center; justify-content: center;
}
.ape-handle:active { cursor: grabbing; }
.ape-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.ape-main .ape-title {
    color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ape-main .ape-artist { color: var(--text-dim); font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ape-track .apt-svc {
    flex-shrink: 0; font-size: 9px; text-transform: uppercase; padding: 1px 5px;
    border-radius: 3px; background: rgba(255,255,255,0.05); color: var(--text-dim);
}
.ape-del {
    flex-shrink: 0; background: none; border: 0; cursor: pointer;
    color: var(--text-dim); font-size: 15px; line-height: 1;
    min-width: 32px; min-height: 32px; border-radius: 6px;
}
.ape-del:hover { color: var(--danger, #e55); background: rgba(229,85,85,0.1); }
.ape-bar { display: flex; gap: 8px; justify-content: flex-end; flex-wrap: wrap; }

/* Mobile: bigger touch targets + sticky action bar above the tab bar */
@media (max-width: 767px), (min-width: 768px) and (max-width: 1024px) and (orientation: portrait) {
    .ape-handle { min-width: 44px; min-height: 44px; font-size: 22px; }
    .ape-del { min-width: 40px; min-height: 40px; font-size: 17px; }
    .ape-track { padding: 6px 8px; }
    .ape-bar {
        position: sticky;
        bottom: calc(var(--bottom-stack) + 8px);   /* +player-h: was missing → sat behind mini-player */
        background: var(--bg-elev); border: 1px solid var(--border);
        border-radius: 12px; padding: 8px; z-index: 50; justify-content: stretch;
    }
    .ape-bar .btn { flex: 1; }
}

/* Needs-confirmation entries */
.ai-playlist-card .ap-needs {
    display: flex; flex-direction: column; gap: 10px;
    border: 1px dashed var(--accent); border-radius: 6px; padding: 8px;
}
.ai-playlist-card .ap-needs-entry { display: flex; flex-direction: column; gap: 4px; }
.ai-playlist-card .ap-needs-q { font-size: 12px; color: var(--text); }
.ai-playlist-card .ap-cands { display: flex; flex-direction: column; gap: 4px; }
.ai-playlist-card .ap-cand-btn {
    text-align: left; cursor: pointer; border: 1px solid var(--border);
    background: rgba(0,0,0,0.18); border-radius: 6px; padding: 6px 10px;
    font-family: inherit; color: var(--text); transition: background 0.12s, border-color 0.12s;
}
.ai-playlist-card .ap-cand-btn:hover {
    background: rgba(201,168,94,0.10); border-color: var(--accent);
}
.ai-playlist-card .ap-cand-btn:disabled { cursor: default; opacity: 0.5; }
.ai-playlist-card .ap-cand-line { font-size: 12px; }
.ai-playlist-card .ap-cand-meta {
    font-size: 10px; color: var(--text-dim); margin-top: 2px;
    font-variant-numeric: tabular-nums;
}
.ai-playlist-card .ap-cand-svc {
    text-transform: uppercase; color: var(--accent); font-weight: 600;
}

/* Unresolved entries */
.ai-playlist-card .ap-unresolved {
    display: flex; flex-direction: column; gap: 2px;
    border: 1px solid var(--border); border-radius: 6px; padding: 6px 10px;
    background: rgba(0,0,0,0.18);
}
.ai-playlist-card .ap-unresolved-row {
    display: flex; justify-content: space-between; gap: 8px;
    font-size: 11px;
}
.ai-playlist-card .ap-unresolved-row .apr-title { color: var(--text-dim); }
.ai-playlist-card .ap-unresolved-row .apr-reason {
    color: var(--danger, #e55); font-style: italic; font-size: 10px; flex-shrink: 0;
}

/* Read-only candidate (finalized playlist — no interaction) */
.ai-playlist-card .ap-cand-btn.ap-cand-static {
    cursor: default; opacity: 0.7;
}
.ai-playlist-card .ap-cand-btn.ap-cand-static:hover {
    background: rgba(0,0,0,0.18); border-color: var(--border);
}

/* "Похоже на…" suggestion for an otherwise-unresolved track */
.ai-playlist-card .ap-unresolved-row.ap-unresolved-sugg {
    flex-direction: column; align-items: stretch; gap: 4px;
}
.ai-playlist-card .apr-main { display: flex; justify-content: space-between; gap: 8px; }
.ai-playlist-card .apr-suggest {
    display: flex; align-items: center; flex-wrap: wrap; gap: 6px;
    font-size: 11px; color: var(--text);
}
.ai-playlist-card .apr-suggest-label { color: var(--text-dim); }
.ai-playlist-card .apr-suggest .apr-take,
.ai-playlist-card .apr-suggest .apr-skip {
    font-size: 11px; padding: 2px 8px; margin-left: 2px;
}

/* Soft-gate warning above the download button */
.ai-playlist-card .ap-dl-warn {
    font-size: 11px; color: var(--accent);
    background: rgba(201,168,94,0.10);
    border: 1px solid var(--accent); border-radius: 6px;
    padding: 6px 10px; margin-bottom: 6px;
}

/* Live progress card shown while a turn is running */
.ai-progress-card {
    background: var(--bg-input);
    border: 1px solid var(--accent);
    border-radius: 10px;
    padding: 10px 14px;
    display: flex;
    flex-direction: column;
    gap: 8px;
    font-size: 13px;
    color: var(--text);
}
.aipc-head { display: flex; align-items: center; gap: 10px; }
.aipc-spinner {
    display: inline-block;
    width: 14px; height: 14px;
    border: 2px solid rgba(201,168,94,0.25);
    border-top-color: var(--accent);
    border-radius: 50%;
    animation: aipcSpin 0.9s linear infinite;
    flex-shrink: 0;
}
@keyframes aipcSpin { to { transform: rotate(360deg); } }
.aipc-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.aipc-elapsed {
    font-variant-numeric: tabular-nums;
    font-size: 11px;
    color: var(--text-dim);
    flex-shrink: 0;
}
.aipc-tools { display: flex; flex-wrap: wrap; gap: 4px; }
.aipc-tool-pill {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 999px;
    font-size: 11px;
    border: 1px solid var(--border);
    background: rgba(0,0,0,0.18);
}
.aipc-tool-pending {
    color: var(--accent);
    border-color: var(--accent);
    animation: aipcPulse 1s ease-in-out infinite;
}
@keyframes aipcPulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.55; } }
.aipc-tool-ok  { color: var(--text-dim); }
.aipc-tool-err { color: var(--danger, #e55); border-color: var(--danger, #e55); }
.aipc-done-mark {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 14px; height: 14px;
    font-size: 13px;
    line-height: 1;
    color: var(--text-strong);
    flex-shrink: 0;
}
.ai-progress-card.aipc-success { border-color: var(--border); }
.ai-progress-card.aipc-error { border-color: var(--danger, #e55); }

/* ── Global modal dialog (vaultConfirm / vaultPrompt / vaultAlert) ── */
.vmodal {
    position: fixed; inset: 0;
    z-index: 10000;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
    animation: vmodalFadeIn 0.12s ease-out;
}
.vmodal.hidden { display: none; }
.vmodal-backdrop {
    position: absolute; inset: 0;
    background: rgba(0,0,0,0.62);
    -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
}
.vmodal-panel {
    position: relative;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 22px 24px 18px 24px;
    width: 100%;
    max-width: 460px;
    box-shadow: 0 24px 60px rgba(0,0,0,0.55);
    display: flex;
    flex-direction: column;
    gap: 14px;
    animation: vmodalPanelIn 0.18s ease-out;
}
@keyframes vmodalFadeIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes vmodalPanelIn {
    from { opacity: 0; transform: translateY(8px) scale(0.98); }
    to   { opacity: 1; transform: translateY(0) scale(1); }
}
.vmodal-title {
    font-size: 16px;
    font-weight: 700;
    color: var(--text-strong);
    margin: 0;
    letter-spacing: -0.005em;
}
.vmodal-body {
    font-size: 13.5px;
    color: var(--text);
    line-height: 1.55;
    white-space: pre-wrap;
    word-wrap: break-word;
    max-height: 50vh;
    max-height: 50dvh;
    overflow-y: auto;
}
.vmodal-input {
    width: 100%;
    background: var(--bg-input);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 10px 12px;
    color: var(--text);
    font-family: inherit;
    font-size: 14px;
}
.vmodal-input:focus { outline: none; border-color: var(--accent); }
.vmodal-input.hidden { display: none; }
.vmodal-actions {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
    margin-top: 4px;
}
.vmodal.vmodal-danger .vmodal-confirm {
    background: var(--danger, #e55);
    border-color: var(--danger, #e55);
    color: #fff;
}
.vmodal.vmodal-danger .vmodal-confirm:hover { filter: brightness(1.08); }

/* Share modal. .vmodal-body defaults to white-space:pre-wrap (meant for the
   plain-text confirm message in vaultConfirm); our HTML content has source
   indentation + newlines that pre-wrap renders as ragged text and blank
   vertical gaps — reset to normal flow so it lays out cleanly. */
.share-modal .vmodal-body { white-space: normal; overflow: visible; }
.share-modal .vmodal-body > p { margin: 0 0 14px; }
.share-modal .share-svc-row .btn { flex: 0 0 auto; }
.share-modal .share-result label { font-weight: 600; }
.aipc-meta { font-size: 11px; color: var(--text-dim); font-variant-numeric: tabular-nums; }

/* Library detail file rows: "Artist — Title" — artist subtly dimmed */
.lf-name-artist { color: var(--text-strong); font-weight: 600; }
.lf-name-sep { color: var(--text-dim); }

/* AI clarifying-question chips (rendered when agent calls ask_user_choice) */
.ai-choices {
    margin-top: 10px;
    padding: 10px 12px;
    background: rgba(255,255,255,0.03);
    border: 1px solid var(--border);
    border-radius: 8px;
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.ai-choices-q {
    font-size: 13px;
    color: var(--text-strong);
    font-weight: 600;
}
.ai-choices-row {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}
.ai-choice-chip {
    background: var(--bg-input);
    border: 1px solid var(--border);
    color: var(--text);
    padding: 6px 12px;
    border-radius: 999px;
    font-size: 12px;
    font-family: inherit;
    cursor: pointer;
    transition: background 0.12s, border-color 0.12s, color 0.12s;
}
.ai-choice-chip:hover {
    background: rgba(201,168,94,0.12);
    border-color: var(--accent);
    color: var(--text-strong);
}
.ai-choice-chip-own {
    background: transparent;
    color: var(--text-dim);
    border-style: dashed;
}
.ai-choice-chip-own:hover {
    color: var(--text);
}

/* Admin AI pool list */
.ai-pool-row {
    display: grid;
    grid-template-columns: 1fr 100px 110px 110px 120px;
    gap: 12px;
    padding: 10px 12px;
    border: 1px solid var(--border);
    border-radius: 6px;
    margin-bottom: 6px;
    font-size: 12px;
    align-items: center;
}
.ai-pool-row .apr-label { font-weight: 700; color: var(--text-strong); }
.ai-pool-row .apr-meta { color: var(--text-dim); font-variant-numeric: tabular-nums; }
.ai-pool-row .apr-actions { display: flex; gap: 6px; justify-content: flex-end; }

/* ===== PHONE input/target hygiene (<=767px) ===== */
@media (max-width: 767px), (min-width: 768px) and (max-width: 1024px) and (orientation: portrait) {
    /* iOS zooms when an input has font-size < 16px on focus — force >=16px */
    input, textarea, select { font-size: 16px; }
    /* Minimum 44x44 hit area on icon controls */
    .pb-btn, .pb-play, .icon-circle, .pb-close { min-width: 44px; min-height: 44px; }
    /* Remove grey iOS tap flash; rely on :active states for feedback */
    button, .nav-item, .tab, a[role="button"] { -webkit-tap-highlight-color: transparent; }
}

/* Bottom tab bar — phone only */
.tabbar { display: none; }

/* Mobile top header — phone only */
.m-header { display: none; }

/* Mobile search controls — phone only */
.m-search-bar, .m-search-kinds { display: none; }

/* AI chat "Conversations" toggle — phone only */
.m-ai-convos { display: none; }
.m-ai-link   { display: none; }   /* phone-only AI Chat <-> AI Playlists cross-links */

/* Interface-language segmented control (Settings → Account) */
.lang-seg {
    display: inline-flex; gap: 4px; padding: 4px;
    background: var(--bg-input); border: 1px solid var(--border); border-radius: 10px;
}
.lang-seg-btn {
    padding: 8px 18px; border: 0; background: transparent;
    color: var(--text-dim); font: inherit; font-weight: 600; font-size: 13px;
    border-radius: 7px; cursor: pointer; transition: background .15s, color .15s;
}
.lang-seg-btn:hover { color: var(--text); }
.lang-seg-btn.active { background: var(--accent); color: #fff; }

/* Bottom-sheet dialog (profile/notifications — opened on phone only) */
dialog.sheet { margin: auto auto 0; width: 100%; max-width: 640px; border: 0;
    border-radius: 18px 18px 0 0; padding: 0 0 var(--safe-bottom); background: var(--bg-elev);
    color: var(--text); box-shadow: 0 -8px 40px rgba(0,0,0,.5); }
dialog.sheet::backdrop { background: rgba(0,0,0,.5); }
.sheet-grab { width: 40px; height: 4px; border-radius: 2px; background: var(--border); margin: 10px auto 4px; }
.sheet-body { padding: 8px 16px 16px; display: flex; flex-direction: column; gap: 4px; }
.sheet-user { display: flex; align-items: center; gap: 12px; padding: 8px 4px 14px; }
.sheet-user b { display: block; color: var(--text-strong); }
.sheet-user span { font-size: 12px; color: var(--text-dim); }
.sheet-storage { padding: 8px 4px 14px; border-bottom: 1px solid var(--border); margin-bottom: 6px; }
.sheet-row { text-align: left; padding: 15px 8px; background: none; border: 0; border-radius: 8px;
    color: var(--text-strong); font-size: 16px; cursor: pointer; }
.sheet-row:active { background: var(--bg-card-hover); }
.sheet-row-danger { color: var(--accent); }
.sheet-row-cancel { color: var(--text-dim); text-align: center; }

/* Browse hub — service segmented control + category rows (phone tab only) */
.browse-seg { display: flex; gap: 8px; overflow-x: auto; padding: 4px 0 12px; -webkit-overflow-scrolling: touch; }
.browse-seg-btn { flex: 0 0 auto; padding: 8px 16px; border-radius: 18px; border: 1px solid var(--border);
    background: var(--bg-card-hover); color: var(--text-mid); font-weight: 700; font-size: 13px; cursor: pointer; }
.browse-seg-btn.active { background: var(--accent); color: #fff; border-color: var(--accent); }
.browse-cats { display: flex; flex-direction: column; gap: 2px; }
.browse-cat-row { display: flex; align-items: center; justify-content: space-between;
    padding: 14px 12px; background: none; border: 0; border-bottom: 1px solid var(--border);
    color: var(--text-strong); font-size: 15px; cursor: pointer; }
.browse-cat-row svg { width: 18px; height: 18px; color: var(--text-dim); }

/* ===== PHONE (<=767px) ===== */
@media (max-width: 767px), (min-width: 768px) and (max-width: 1024px) and (orientation: portrait) {
    /* Global overflow guard — stop desktop-width content from pushing the page
       sideways. MUST use `clip`, not `hidden`: on Android Chromium, overflow-x:
       hidden on the root html/body turns them into scroll containers and kills
       the root-scroller optimisation → finger-scroll stops working (iOS WebKit
       tolerates it, hence "works on iPhone, dead on Galaxy"). `clip` clips the
       overflow WITHOUT creating a scroll container, so the document keeps
       scrolling. `hidden` stays first as a fallback for pre-clip browsers. */
    html, body { max-width: 100vw; overflow-x: hidden; overflow-x: clip; }
    .page, .page-header, .page-actions { max-width: 100%; min-width: 0; }
    .page-header { flex-wrap: wrap; gap: 10px; }
    /* Header action buttons must wrap on phone — the AI chat has three
       (Диалоги / AI-плейлисты / Новый чат) which overflowed in one nowrap row. */
    .page-actions { flex-wrap: wrap; }
    img, .dl-card, .lib-card { max-width: 100%; }

    /* Login / auth screen — desktop is a fixed 440px box; shrink to phone width */
    #auth-screen { padding: 20px; }
    #auth-screen .login-box { width: 100%; max-width: 400px; padding: 24px 20px 28px; }
    .login-brand { gap: 12px; }
    .login-mark { width: 64px; }
    .login-wordmark .bw-vault { font-size: 30px; }
    .login-wordmark .bw-audio { font-size: 22px; }

    /* Reserve space for the bottom tab bar + (conditional) mini-player + home indicator */
    .content {
        padding: 16px calc(var(--safe-right) + 14px)
                 calc(var(--bottom-stack) + 16px) calc(var(--safe-left) + 14px);
        /* Override the base `overflow-x: hidden` (which promotes overflow-y to
           `auto`, making .content a competing scroll container). `clip` keeps
           horizontal clipping but lets the DOCUMENT own vertical scrolling —
           the phone scroll model is "document scrolls", no inner scrollers. */
        overflow-x: clip;
    }

    /* Collapse the desktop grid to a single content column. Use minmax(0, 1fr)
       NOT 1fr: 1fr == minmax(auto, 1fr), whose `auto` min is min-content, and
       `.content` uses `overflow-x: clip` (not a scroll container, so it does NOT
       get the implicit min-width:0 that `hidden` would). A non-shrinkable wide
       child (e.g. the AI chat's 3-button .page-actions row) then BLOWS OUT the
       track → the whole page becomes wider than the viewport and gets clipped on
       the right. minmax(0, 1fr) lets the track shrink to the viewport. */
    #app {
        grid-template-columns: minmax(0, 1fr);
        grid-template-rows: 1fr;
        grid-template-areas: "content";
    }
    .content { min-width: 0; }   /* belt-and-suspenders against grid blowout */
    .sidebar { display: none; }
    .topbar  { display: none; }
    .content { grid-area: content; }

    .tabbar {
        display: flex;
        position: fixed; left: 0; right: 0; bottom: 0;
        height: calc(var(--tabbar-h) + var(--safe-bottom));
        padding-bottom: var(--safe-bottom);
        background: var(--bg-elev);
        border-top: 1px solid var(--border);
        z-index: 90;                      /* above player (80), below Now Playing (100) */
    }
    .tabbar .tab {
        flex: 1; display: flex; flex-direction: column; align-items: center;
        justify-content: center; gap: 3px;
        background: none; border: 0; cursor: pointer;
        color: var(--text-dim); font-size: 10px; font-weight: 600;
        min-height: 44px;
    }
    .tabbar .tab svg { width: 22px; height: 22px; }
    .tabbar .tab[aria-current="page"] { color: var(--accent); }
    body.kb-open .tabbar { transform: translateY(100%); }  /* hide while typing */
    /* T4.1 — full-screen AI chat: hide the tab bar in an active conversation so
       the thread gets ~56px + reserve back. A back chevron in the AI header
       (.ai-mobile-back) replaces tab-bar navigation. */
    body.ai-chat-active .tabbar { display: none; }

    .m-header {
        display: flex; align-items: center; gap: 10px;
        position: sticky; top: 0; z-index: 60;
        padding: calc(var(--safe-top) + 8px) 4px 8px;
        background: var(--bg);
        margin: -16px -14px 12px;            /* bleed to screen edges over .content padding */
        padding-left: calc(var(--safe-left) + 14px);
        padding-right: calc(var(--safe-right) + 14px);
    }
    .m-header-brand { display: flex; align-items: center; }
    .m-header-brand .bw-vault { font-size: 18px; font-weight: 800; color: var(--text-strong); }
    .m-header-brand .bw-audio { font-size: 15px; color: #9ba0aa; display:inline-flex; align-items:center; }
    .m-header-actions { margin-left: auto; display: flex; align-items: center; gap: 8px; }
    .m-back-btn { background:none;border:0;color:var(--text-strong);min-width:40px;min-height:40px;cursor:pointer; }
    .m-back-btn svg { width: 22px; height: 22px; }
    .m-chip { display:flex;align-items:center;gap:5px;background:var(--bg-card-hover);border:1px solid var(--border);
              border-radius:14px;padding:5px 9px;color:var(--text-strong);font-size:12px;font-weight:700;cursor:pointer; }
    .m-chip svg { width: 15px; height: 15px; }
    /* Mobile header menu/settings button — replaces the old initials avatar.
       Inherits .icon-circle styling; opens the profile sheet (profile, settings,
       admin, logout). */
    .m-profile-btn { width: 38px; height: 38px; }

    /* Mobile search: sticky field + segmented kind selector */
    .m-search-bar { display: flex; align-items: center; gap: 8px; position: sticky;
        top: calc(var(--safe-top) + 52px); z-index: 40; background: var(--bg);
        border: 1px solid var(--border); border-radius: 12px; padding: 10px 12px; margin-bottom: 10px; }
    .m-search-bar svg { width: 18px; height: 18px; color: var(--text-dim); flex: 0 0 auto; }
    .m-search-bar input { flex: 1; background: none; border: 0; color: var(--text-strong); font-size: 16px; outline: none; }
    .m-search-kinds { display: flex; gap: 6px; overflow-x: auto; margin-bottom: 14px; }
    .m-search-kinds button { flex: 0 0 auto; padding: 7px 14px; border-radius: 16px; border: 1px solid var(--border);
        background: var(--bg-card-hover); color: var(--text-mid); font-weight: 700; font-size: 13px; }
    .m-search-kinds button.active { background: var(--accent); color: #fff; border-color: var(--accent); }

    /* ── 5.1 Dashboard: 2×2 KPIs, single-column, pan-x carousel ── */
    /* Compact KPI cards on phone — desktop fonts/padding waste space. */
    .kpi-grid { grid-template-columns: repeat(2, 1fr); gap: 8px; }
    .kpi-card { padding: 10px; gap: 9px; }
    .kpi-ic { width: 34px; height: 34px; border-radius: 9px; }
    .kpi-ic svg { width: 17px; height: 17px; }
    .kpi-label { font-size: 10px; }
    .kpi-value { font-size: 18px; margin: 2px 0 1px; }
    .kpi-value-sync { font-size: 15px; }
    .kpi-unit { font-size: 11px; }
    .kpi-meta { margin-top: 3px; font-size: 10px; }
    .kpi-corner { top: 9px; right: 9px; width: 16px; height: 16px; }
    .dash-cols { grid-template-columns: 1fr; }
    /* Phone: keep the horizontal carousel (desktop/tablet use a wrapping grid). */
    .recent-carousel {
        display: flex;
        flex-wrap: nowrap;                       /* phone: single scrolling row */
        justify-content: flex-start;             /* start at left, not centred */
        grid-template-columns: none;
        overflow-x: auto;
        scroll-snap-type: x mandatory;
        scrollbar-width: thin;
        touch-action: pan-x;                     /* never fight page gestures */
    }

    /* ── 5.2 Grids 2-col + hover-only actions always visible on touch ── */
    .dl-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
    /* hover-only controls are invisible on touch — force them visible on phone.
       Real selectors (grep-confirmed): .dl-card .dl-btn, .lib-del-btn,
       .lib-tile-play, .result-card .cover-overlay. */
    .dl-card .dl-btn,
    .lib-del-btn,
    .lib-tile-play,
    .result-card .cover-overlay { opacity: 1 !important; }

    /* ── 5.3 Alphabet rail → horizontal chip strip ── */
    .browse-layout { grid-template-columns: 1fr; }      /* drop the 56px rail column */
    .alphabet-rail {
        position: static; max-height: none; flex-direction: row; overflow-x: auto;
        gap: 4px; padding: 4px 0 10px; -webkit-overflow-scrolling: touch; touch-action: pan-x;
    }
    .alphabet-rail .ab-letter { flex: 0 0 auto; min-width: 34px; height: 34px; }

    /* ── 5.4 Downloads rows reflow (DOM-diff .dl-* hooks untouched) ── */
    /* cover | meta on row 1; the action cluster (badge + pause/retry/delete)
       wraps onto a full-width row 2 so it never gets pushed off the right edge. */
    .dl-row { grid-template-columns: 48px 1fr; gap: 8px 12px; align-items: start; }
    /* The cover is a fixed 56px on desktop — shrink it to MATCH the mobile column,
       otherwise the 56px image spills out of the 48px track and overlaps the
       title (the "обложка налезает на текст" bug). */
    .dl-row .dl-cover { width: 48px; height: 48px; }
    .dl-meta { min-width: 0; }   /* let the title/sub column shrink + ellipsis */
    /* per-file rows: base is 1fr 100px 130px — drop the fixed cols so they fit */
    .dl-inflight-file { grid-template-columns: 1fr auto; gap: 6px; }
    /* real size/percent column is .dl-if-sz (plan's .dlf-size/.dlf-speed don't exist) */
    .dl-inflight-file .dl-if-sz { font-size: 11px; }
    .dl-row .dl-actions { grid-column: 1 / -1; flex-wrap: wrap; justify-content: flex-start; gap: 6px; margin-top: 2px; }

    /* ── 5.5 AI chat: conversations drawer + flex layout (replaces calc(100vh-220px)) + keyboard ── */
    .ai-chat-shell { grid-template-columns: 1fr; display: flex; flex-direction: column; min-height: 0; }
    .ai-chat-shell { height: calc(100dvh - var(--tabbar-h) - var(--player-h) - var(--safe-bottom) - 120px); }
    .ai-chat-sidebar {
        position: fixed; top: 0; left: 0; bottom: 0; width: min(80vw, 320px); z-index: 95;
        background: var(--bg-elev); transform: translateX(-100%); transition: transform .25s;
        padding-top: var(--safe-top);
    }
    .ai-chat-sidebar.open { transform: translateX(0); box-shadow: 0 0 40px rgba(0, 0, 0, .5); }
    .ai-chat-main { flex: 1; min-height: 0; }
    .ai-chat-input { position: sticky; bottom: 0; transform: translateY(calc(-1 * var(--kb, 0px))); background: var(--bg); }
    .m-ai-convos { display: inline-flex; }
    .m-ai-link  { display: inline-flex; }   /* phone-only AI Chat <-> AI Playlists links */

    /* Keep every AI-chat bubble/card within the phone width (was "разъехался"):
       a flex item's default min-width:auto refuses to shrink below its content,
       so a non-wrapping action row / a flex:1 textarea / a fixed-width artist
       column pushed the whole column past the screen edge. */
    .ai-chat-main { min-width: 0; }
    .ai-chat-thread { padding: 14px 12px; overflow-x: hidden; }   /* clip stray horizontal overflow inside the thread */
    .ai-msg, .ai-msg > * { min-width: 0; max-width: 100%; }
    .ai-msg-body, .ai-msg-md { min-width: 0; max-width: 100%; overflow-wrap: anywhere; word-break: break-word; }
    .ai-msg-md .md-pre, .ai-msg-md .md-code { white-space: pre-wrap; word-break: break-word; }
    .ai-chat-input textarea { min-width: 0; }   /* flex:1 textarea must be allowed to shrink */
    /* Hard-cap the whole AI subtree to the phone width (the onboarding cards were
       inheriting a blown-out width and getting clipped on the right). */
    #page-ai-chat, .ai-chat-shell, .ai-chat-main, .ai-chat-thread,
    .ai-chat-empty, .ai-onboard-grid, .ai-onboard-card { min-width: 0; max-width: 100%; }
    .ai-onboard-card { overflow-wrap: anywhere; }
    .ai-playlist-card { min-width: 0; max-width: 100%; }
    .ai-playlist-card .ap-head,
    .ai-playlist-card .ap-actions { flex-wrap: wrap; }
    .ai-playlist-card .ap-actions { justify-content: flex-start; }
    .ai-playlist-card .ap-track .apt-artist {
        flex-shrink: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    }

    /* ── AI chat: Telegram-style bubbles (phone only) ──────────────────────
       Drop avatars and lay each message out as a column aligned to its sender,
       so plain text becomes a chat bubble (right=you, left=AI) while rich AI
       content (proposal/progress cards, choice chips) stays full width. */
    #page-ai-chat .ai-chat-thread { gap: 4px; padding: 12px 10px; }
    #page-ai-chat .ai-msg { gap: 0; align-items: flex-start; }
    #page-ai-chat .ai-msg-avatar { display: none; }
    #page-ai-chat .ai-msg-content {
        display: flex; flex-direction: column; align-items: flex-start; gap: var(--chat-gap, 6px);
    }
    #page-ai-chat .ai-msg-user .ai-msg-content { align-items: flex-end; }
    /* Native cadence: tight within a speaker cluster, wider between speakers. */
    #page-ai-chat .ai-msg-user + .ai-msg-assistant,
    #page-ai-chat .ai-msg-assistant + .ai-msg-user { margin-top: 8px; }

    /* Plain-text bubble. width:auto + max-width makes it hug its content. */
    #page-ai-chat .ai-msg-body {
        width: auto; max-width: 86%;
        padding: 7px 11px;
        border-radius: 16px;
        background: var(--bg-input);
        font-size: var(--chat-fs-body, 14px);
        line-height: var(--chat-lh-body, 1.4);
    }
    #page-ai-chat .ai-msg-assistant .ai-msg-body { border-bottom-left-radius: 5px; border: 1px solid var(--border); }   /* tail + hairline edge */
    #page-ai-chat .ai-msg-user .ai-msg-body {
        max-width: 82%;
        background: var(--accent); color: #fff;                                       /* #000→#fff: contrast on red */
        border-bottom-right-radius: 5px;                                              /* tail */
    }

    /* Rich AI content must NOT be squeezed into a bubble — keep it full width. */
    #page-ai-chat .ai-progress-card,
    #page-ai-chat .ai-playlist-card,
    #page-ai-chat .ape-editor,
    #page-ai-chat .ai-choices,
    #page-ai-chat .ai-msg-content > div[style*="margin-top"] { align-self: stretch; width: 100%; max-width: 100%; }

    /* Attached photo: right-aligned, rounded, compact. */
    #page-ai-chat .ai-msg-user .ai-msg-img { align-self: flex-end; }
    #page-ai-chat .ai-msg-img img { max-width: 64%; border-radius: 14px; }

    /* ── Composer: rounded "pill" with inline icons + round send ──
       The bar sits on the dark page (--bg) so the pill is a clearly lighter,
       outlined capsule (bg-input ≈ card bg was nearly invisible). */
    #page-ai-chat .ai-chat-input {
        gap: 2px;
        margin: 8px 10px calc(8px + var(--safe-bottom));   /* bottom margin clears the home indicator */
        padding: 4px 5px 4px 6px;
        background: #20242c;
        border: 1px solid #353b45;
        border-radius: 24px;
        border-top: 1px solid #353b45;        /* override the base border-top */
        align-items: center;
        box-shadow: 0 2px 10px rgba(0,0,0,0.25);
    }
    /* When the keyboard is up the home-indicator area is covered by the keyboard,
       so drop the safe-area bottom margin — otherwise it leaves a gap (and a
       detached caret) between the composer and the keyboard. */
    body.kb-open #page-ai-chat .ai-chat-input { margin-bottom: 8px; }
    #page-ai-chat .ai-chat-input textarea {
        background: transparent; border: none; border-radius: 0;
        padding: 8px 6px; min-height: 22px; max-height: 132px;
        line-height: 1.35; align-self: center;
        font-size: 16px;   /* iOS auto-zooms (and breaks the keyboard math) when an
                              input is <16px on focus — the base .ai-chat-input
                              textarea is 14px and beat the generic 16px rule. */
    }
    #page-ai-chat .ai-chat-input textarea:focus { border: none; outline: none; }
    #page-ai-chat .ai-attach-btn {
        flex: 0 0 auto; width: 34px; height: 34px; padding: 0; margin: 0;
        display: inline-flex; align-items: center; justify-content: center;
        border-radius: 50%; background: transparent; border: none;
        font-size: 17px; align-self: center;
    }
    #page-ai-chat .ai-send-btn {
        flex: 0 0 auto; width: 36px; height: 36px; border-radius: 50%;
        align-self: center; margin: 0;
    }

    /* ── Compact one-row header: drop subtitle, show the active chat name ── */
    #page-ai-chat .page-title,
    #page-ai-chat .page-subtitle { display: none; }
    #page-ai-chat .ai-head-titles { min-width: 0; flex: 1; }
    #page-ai-chat .ai-mobile-chat-title { display: none; }   /* center is now the "Vault AI" menu */
    /* T4.1 — phone back chevron: replaces tab-bar navigation now that the tab bar
       is hidden in active chat. Calls goBack() (history). */
    #page-ai-chat .ai-mobile-back {
        display: inline-flex; align-items: center; justify-content: center;
        flex: 0 0 auto; width: 36px; height: 36px; margin-right: 2px; padding: 0;
        background: none; border: 0; cursor: pointer;
        color: var(--text-strong); font-size: 30px; line-height: 1;
    }
    /* ── iMessage-style top bar: [ ‹ back ] [ Vault AI ▾ (centered menu) ] [ 📷 ] ── */
    #page-ai-chat .page-header { flex-wrap: nowrap; align-items: center; gap: 6px; }
    #page-ai-chat .ai-head-titles {
        flex: 1 1 auto; min-width: 0; display: flex;
        align-items: center; justify-content: center; position: relative;
    }
    /* Centered translucent "Vault AI" → dropdown trigger */
    #page-ai-chat .ai-mobile-menu-btn {
        display: inline-flex; align-items: center; gap: 4px;
        background: none; border: 0; cursor: pointer; padding: 6px 10px;
        color: var(--text); opacity: 0.7; font: inherit; font-size: 16px; font-weight: 600;
        border-radius: 10px;
    }
    #page-ai-chat .ai-mobile-menu-btn:active { background: rgba(255,255,255,0.06); }
    #page-ai-chat .ai-mobile-menu-btn .amm-caret { font-size: 11px; opacity: 0.8; }
    /* Dropdown menu (our palette) */
    #page-ai-chat .ai-mobile-menu {
        display: flex; flex-direction: column;
        position: absolute; top: calc(100% + 6px); left: 50%; transform: translateX(-50%);
        min-width: 200px; z-index: var(--z-overlay, 300);
        background: var(--bg-elev); border: 1px solid var(--border-strong);
        border-radius: 14px; padding: 6px; gap: 2px;
        box-shadow: 0 12px 40px rgba(0,0,0,0.55);
    }
    #page-ai-chat .ai-mobile-menu[hidden] { display: none; }
    #page-ai-chat .ai-mobile-menu button {
        display: block; width: 100%; text-align: left;
        background: none; border: 0; cursor: pointer; color: var(--text);
        font: inherit; font-size: 15px; padding: 11px 12px; border-radius: 9px;
    }
    #page-ai-chat .ai-mobile-menu button:active { background: var(--bg-card-hover); }
    /* Right: camera; hide the old text/icon action buttons (moved into the menu). */
    #page-ai-chat .page-actions { flex: 0 0 auto; }
    #page-ai-chat .page-actions .m-ai-convos,
    #page-ai-chat .page-actions .m-ai-link,
    #page-ai-chat .page-actions #ai-new-chat-btn { display: none; }
    #page-ai-chat .ai-mobile-cam {
        display: inline-flex; align-items: center; justify-content: center;
        width: 38px; height: 38px; padding: 0; border-radius: 10px;
        background: none; border: 0; cursor: pointer; color: var(--text);
    }
    #page-ai-chat .ai-mobile-cam:active { background: rgba(255,255,255,0.06); }
    /* Header chips removed on phone (balance → bottom bar, cost → in-thread line)
       so "Vault AI" centers and we reclaim the row. */
    #page-ai-chat .page-actions #ai-credit-chip,
    #page-ai-chat .page-actions .ai-conv-cost { display: none; }
    /* Bottom VAULT balance — slim right-aligned line just above the composer. */
    #page-ai-chat .ai-credit-bottom {
        display: flex; justify-content: flex-end; align-items: center; gap: 5px;
        margin: 0 12px 4px; font-size: 13px; font-weight: 600; color: var(--text-mid);
    }
    #page-ai-chat .ai-credit-bottom .ai-coin { font-size: 15px; }
    #page-ai-chat .ai-credit-bottom.is-over { color: var(--danger); }
    #page-ai-chat .ai-credit-bottom.is-low  { color: #f59e0b; }

    /* ── ＋ params sheet: hide the inline chip bar; ＋ in composer toggles it.
       Shown in normal flow right above the composer (it already sits before the
       form in the DOM), as a rounded panel that lifts the composer. ── */
    #page-ai-chat #ai-chat-chips {
        display: none;   /* shown only when body.ai-chips-open */
        flex-wrap: wrap; gap: 8px;
        margin: 0 8px 6px;
        padding: 10px; border-radius: 16px;
        background: var(--bg-elev); border: 1px solid var(--border-strong);
        box-shadow: 0 -6px 24px rgba(0,0,0,0.4);
    }
    body.ai-chips-open #page-ai-chat #ai-chat-chips { display: flex; }
    #page-ai-chat .ai-chips-toggle { display: inline-flex; }   /* ＋ visible on phone */
    #page-ai-chat #ai-attach-btn { display: none; }            /* 📎 moved to the top camera */
    body.ai-chips-open #page-ai-chat .ai-chips-toggle { background: var(--accent); color: #fff; }

    /* ── iOS standalone-PWA keyboard fix (WebKit #237851 + iOS 26 #297779): in a
       Home-Screen PWA the keyboard does NOT resize the viewport and visualViewport
       reports nothing, so a position:fixed + overflow:hidden layout gets bodily
       PANNED by WebKit on focus (header slides off-screen). Fix = NORMAL document
       flow so WebKit's native "scroll focused field into view" scrolls the PAGE
       (real scroll) instead of panning a fixed layer. Header is sticky (stays
       visible); composer is the last flow item in a ≥100dvh column. On focus we
       add a bottom spacer (--kb-est) so there's real scroll room for WebKit to
       lift the composer above the keyboard, since we cannot read its height. ── */
    body.ai-chat-active .m-header { display: none; }   /* one header — chat controls only */

    /* Un-lock the document + un-constrain the shell so the page can scroll. */
    html.ai-chat-lock, html.ai-chat-lock body { overflow-x: clip; overflow-y: visible; height: auto; overscroll-behavior-y: contain; }
    body.ai-chat-active #app { display: block; min-height: 100dvh; }
    body.ai-chat-active .content { overflow: visible; padding: 0; min-height: 100dvh; }

    #page-ai-chat:not(.hidden) {
        position: static;
        display: flex; flex-direction: column;
        min-height: 100dvh;
        margin: 0; padding: 0; background: var(--bg);
    }
    body.kb-open .tabbar { transform: translateY(100%); }   /* hide while typing */
    body.ai-chat-active .player-bar { transform: translateY(150%); pointer-events: none; }

    #page-ai-chat .page-header {
        position: sticky; top: 0; z-index: 50;
        flex: 0 0 auto; margin: 0;
        padding: calc(var(--safe-top) + 8px) 12px 8px;
        border-bottom: 1px solid var(--border); background: var(--bg);
    }
    #page-ai-chat .ai-chat-shell {
        flex: 1 1 auto; min-height: 0; height: auto; margin: 0;
        display: flex; flex-direction: column;
    }
    #page-ai-chat .ai-chat-main {
        flex: 1 1 auto; min-height: 0; border: none; border-radius: 0; background: transparent;
        display: flex; flex-direction: column;
    }
    /* Thread grows; it does NOT scroll on its own — the document scrolls, which is
       what lets WebKit reveal the composer above the keyboard in standalone. */
    #page-ai-chat .ai-chat-thread { flex: 1 1 auto; min-height: 0; overflow: visible; }
    #page-ai-chat .ai-chat-input { position: static; transform: none; bottom: auto; }
    /* When the keyboard is up, reserve room below the composer so the document has
       something to scroll into → WebKit lifts the focused composer above the kb. */
    body.kb-open #page-ai-chat .ai-chat-shell { padding-bottom: var(--kb-est, 300px); }

    /* ════════════════════════════════════════════════════════════════════
       Mobile chat type system (design workflow 2026-05-29): step text down a
       notch from desktop, tighten rhythm, de-monospace the verbose log, and
       compact cards — all scoped to #page-ai-chat so desktop is untouched and
       NO feature is removed. One 4-step scale drives every role.
       ════════════════════════════════════════════════════════════════════ */
    #page-ai-chat {
        --chat-fs-body: 14px;  --chat-fs-sec: 12.5px;  --chat-fs-meta: 11px;  --chat-fs-micro: 10.5px;
        --chat-lh-body: 1.4;   --chat-lh-sec: 1.42;    --chat-lh-tight: 1.35; --chat-gap: 6px;
    }
    /* Markdown sub-elements */
    #page-ai-chat .ai-msg-md .md-p { font-size: var(--chat-fs-body); line-height: var(--chat-lh-sec); margin: 0 0 6px; }
    #page-ai-chat .ai-msg-md .md-p:last-child { margin-bottom: 0; }
    #page-ai-chat .ai-msg-md h3.md-h { font-size: 14px; }
    #page-ai-chat .ai-msg-md h4.md-h { font-size: 13px; }
    #page-ai-chat .ai-msg-md h5.md-h { font-size: 11px; letter-spacing: 0.5px; }
    #page-ai-chat .ai-msg-md .md-h { margin: 9px 0 3px; }
    #page-ai-chat .ai-msg-md .md-list { margin: 3px 0 6px; padding-left: 17px; }
    #page-ai-chat .ai-msg-md .md-list li { font-size: var(--chat-fs-body); line-height: 1.42; margin: 1px 0; }
    #page-ai-chat .ai-msg-md .md-code { font-size: 0.82em; padding: 0 4px; }
    #page-ai-chat .ai-msg-md .md-pre { padding: 6px 9px; margin: 5px 0; }
    #page-ai-chat .ai-msg-md .md-pre code { font-size: 11.5px; }
    /* Progress / status card */
    #page-ai-chat .ai-progress-card { font-size: var(--chat-fs-sec); padding: 8px 11px; gap: 6px; border-radius: 12px; }
    #page-ai-chat .aipc-head { gap: 8px; }
    #page-ai-chat .aipc-spinner { width: 12px; height: 12px; border-width: 2px; }
    #page-ai-chat .aipc-elapsed, #page-ai-chat .aipc-meta { font-size: var(--chat-fs-meta); }
    #page-ai-chat .aipc-tool-pill { font-size: 10.5px; padding: 1px 7px; }
    #page-ai-chat .aipc-tools { gap: 3px; }
    /* Verbose log — kill the terminal look: proportional, muted, left-rail timeline */
    #page-ai-chat .rcl-verbose > summary { font-size: 11px; padding: 3px 0; }
    #page-ai-chat .rcl-verbose-body {
        font-family: inherit; font-size: var(--chat-fs-micro); line-height: var(--chat-lh-tight);
        padding: 5px 8px 5px 10px; max-height: 180px; color: var(--text-dim);
        border-radius: 8px; border-left: 2px solid var(--border);
    }
    #page-ai-chat .rcl-ts { font-size: 9.5px; font-variant-numeric: tabular-nums; margin-right: 5px; opacity: 0.6; }
    #page-ai-chat .rcl-agent { color: var(--text-dim); font-size: 10px; }
    #page-ai-chat .rcl-finding-slug, #page-ai-chat .rcl-dur { font-size: 9.5px; }
    #page-ai-chat .research-chat-log { font-family: inherit; font-size: var(--chat-fs-micro); line-height: var(--chat-lh-tight); }
    /* Research header + target pill */
    #page-ai-chat .research-chat-head { font-size: var(--chat-fs-sec); margin-bottom: 6px; }
    #page-ai-chat .research-chat-target { font-size: 10.5px; padding: 1px 7px; margin-left: 6px; }
    /* Album / track vote cards — chat-attachment scale */
    #page-ai-chat .album-vote-card, #page-ai-chat .track-vote-card { gap: 10px; padding: 8px; margin: 5px 0; border-radius: 10px; }
    #page-ai-chat .album-vote-card .ac-cover { width: 44px; height: 44px; }
    #page-ai-chat .album-vote-card .ac-cover-blank { font-size: 18px; }
    #page-ai-chat .album-vote-card .ac-title { font-size: var(--chat-fs-sec); line-height: 1.3; margin-bottom: 2px; }
    #page-ai-chat .album-vote-card .ac-key, #page-ai-chat .track-vote-card .ac-album-line { font-size: var(--chat-fs-meta); margin-bottom: 3px; }
    #page-ai-chat .track-vote-card .ac-description { font-size: var(--chat-fs-meta); line-height: 1.4; margin-top: 3px; }
    /* Vote buttons — square tap targets */
    #page-ai-chat .vote-btn { font-size: 15px; padding: 0; width: 34px; height: 34px; display: inline-flex; align-items: center; justify-content: center; }
    #page-ai-chat .vote-actions { gap: 6px; }
    /* Bulk actions + counter */
    #page-ai-chat .research-bulk-actions { padding: 9px 10px; margin-top: 8px; gap: 6px; border-radius: 10px; font-size: var(--chat-fs-sec); }
    #page-ai-chat .vote-counter { font-size: var(--chat-fs-sec); }
    #page-ai-chat .vote-counter strong { font-size: 14px; }
    /* Choice cards (ask_user_choice) */
    #page-ai-chat .ai-choices { padding: 8px 10px; gap: 6px; border-radius: 10px; margin-top: 8px; }
    #page-ai-chat .ai-choices-q { font-size: var(--chat-fs-sec); }
    #page-ai-chat .ai-choices-row { gap: 6px; }
    #page-ai-chat .ai-choice-chip { font-size: var(--chat-fs-meta); padding: 6px 12px; min-height: 34px; }
    /* Cost line — lowest meta */
    #page-ai-chat .ai-cost-line { font-size: var(--chat-fs-micro); margin: 1px 0 3px; opacity: 0.75; }

    /* ── 5.6 Settings/Admin: scrollable tab strips + single-column forms ── */
    .tabs { overflow-x: auto; flex-wrap: nowrap; -webkit-overflow-scrolling: touch; }
    .tabs .tab { flex: 0 0 auto; }
    /* collapse the inline-styled multi-col admin forms to single column */
    #admin-users form, #admin-pool form, #admin-ai-pool form {
        grid-template-columns: 1fr !important; display: flex !important; flex-direction: column;
    }
    /* .pool-row / .prefs-control don't exist — use real names (.ai-pool-row, .prefs-row input/select) */
    .ai-pool-row { grid-template-columns: 1fr !important; gap: 6px; }
    .prefs-row { flex-direction: column; align-items: stretch; gap: 6px; }
    .prefs-row input, .prefs-row select { min-width: 0; width: 100%; }
    .lib-files-table { display: block; overflow-x: auto; }   /* let the wide table scroll */
    .connection-grid { grid-template-columns: 1fr; }

    /* ── Admin: full mobile pass (phone). Desktop/tablet untouched. ───────── */
    /* Sticky tab strip so navigation stays reachable down a long tab. */
    #page-admin #admin-tabs {
        position: sticky; top: 0; z-index: 30;
        background: var(--bg); margin: 0 0 14px; padding: 6px 0;
    }
    /* Stat cards: two per row. */
    #admin-stats .stats-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
    #page-admin .settings-section { padding: 14px; margin-bottom: 14px; }

    /* Wide admin tables → stacked, labelled cards (no horizontal scroll).
       Each <td> carries data-label (added in the JS renderers). */
    .admin-tab .lib-files-table,
    .admin-tab .lib-files-table tbody,
    .admin-tab .lib-files-table tr,
    .admin-tab .lib-files-table td { display: block; width: auto; }
    .admin-tab .lib-files-table { overflow: visible; }       /* override the 5.6 scroll */
    .admin-tab .lib-files-table thead { display: none; }     /* labels move into cells */
    .admin-tab .lib-files-table tr {
        border: 1px solid var(--border); border-radius: 10px;
        padding: 8px 12px; margin-bottom: 10px; background: var(--bg-card);
    }
    .admin-tab .lib-files-table td {
        border: none; padding: 5px 0;
        display: flex; justify-content: space-between; gap: 12px;
        text-align: right; font-size: 13px;
    }
    .admin-tab .lib-files-table td::before {
        content: attr(data-label);
        color: var(--text-dim); font-weight: 600; text-align: left;
        flex: 0 0 auto;
    }
    /* First cell = card title (no label, prominent, full width). */
    .admin-tab .lib-files-table td:first-child {
        display: block; text-align: left; font-weight: 700;
        color: var(--text-strong); font-size: 14px;
        padding-bottom: 6px; margin-bottom: 4px; border-bottom: 1px solid var(--border);
    }
    .admin-tab .lib-files-table td:first-child::before { display: none; }

    /* Account-pool + AI-pool rows: stack the fixed multi-col grid into a card. */
    #admin-pool .pool-row,
    #admin-ai-pool .ai-pool-row {
        display: flex !important; flex-direction: column; align-items: stretch; gap: 8px;
    }
    #admin-pool .pool-row .dl-badge { align-self: flex-start; }
    #admin-pool .pool-row input[type="number"] { width: 90px; align-self: flex-start; }
    #admin-pool .pool-row > span:last-child { flex-wrap: wrap; }   /* action buttons wrap */

    /* Users: drop the action cluster below the user info, full width. */
    #admin-users .admin-row { flex-wrap: wrap; }
    #admin-users .admin-row > div:last-child {
        width: 100%; justify-content: flex-start; flex-wrap: wrap;
    }

    /* ── 5.7 user-drawer + vault-modal → bottom sheets (modal JS unchanged) ── */
    .drawer-panel {
        top: auto; left: 0; right: 0; bottom: 0; width: 100%;
        max-height: 90dvh; border-radius: 16px 16px 0 0; animation: slideUp .25s;
        padding-bottom: var(--safe-bottom);
    }
    @keyframes slideUp { from { transform: translateY(100%); } to { transform: translateY(0); } }
    .vmodal-panel { width: 100%; max-width: 100%; margin: 0 12px; border-radius: 16px; }
    .vmodal-body { max-height: 60dvh; }

    /* detail-hero overflows on phone — stack it (cover selector is .cover-art, grep-confirmed) */
    .detail-hero { flex-direction: column; align-items: flex-start; gap: 14px; }
    .detail-hero .cover-art, .detail-hero img { width: 140px; height: 140px; }
    .detail-hero h2 { font-size: 22px; }
    /* In a column flex the meta-block was shrinking to content width, squeezing
       the wrapped action buttons (Upscale/Zip/Delete) out of view — force full
       width so every action is visible and wraps cleanly on phones. */
    .detail-hero .meta-block { width: 100%; }
    .detail-hero .actions { width: 100%; }
    .detail-hero .actions .btn { flex: 0 1 auto; }

    /* ── Premium mini-player (collapsed bar) — matches the full-screen Now
       Playing redesign. Layout = rounded cover + meta (flex) | accent play |
       close, with a thin accent progress line along the very top edge. Tap the
       bar (anywhere but the buttons) to expand → Now Playing (JS, ≤767px). ── */
    .player-bar {
        left: 0; right: 0;
        bottom: calc(var(--tabbar-h) + var(--safe-bottom));   /* on top of the tab bar */
        height: 60px;
        padding: 7px calc(var(--safe-right) + 12px) 7px calc(var(--safe-left) + 12px);
        grid-template-columns: 1fr auto auto;
        gap: 10px;
        z-index: 85;                                           /* below tabbar (90), above content */
        position: fixed;                                       /* positioning context for the top strip */
        overflow: visible;
        cursor: pointer;                                       /* tap-to-expand affordance on the whole bar */
    }
    /* Rounded album thumb — premium, matches NP art corners. */
    .player-bar .pb-cover { width: 40px; height: 40px; border-radius: 8px; }
    .player-bar .pb-cover img { border-radius: 8px; object-fit: cover; }
    /* re-enable the label (hidden at 900px via .player-bar .pb-track .pb-meta) — match specificity */
    .player-bar .pb-track { gap: 10px; min-width: 0; }
    .player-bar .pb-track .pb-meta { display: block; min-width: 0; }
    .player-bar .pb-title {
        font-size: 13px; font-weight: 600; color: var(--text-strong);
        overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    }
    .player-bar .pb-artist {
        font-size: 11px; color: var(--text-dim); margin-top: 1px;
        overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    }
    /* Hide everything but the primary play + close: no prev/next, no volume,
       and no separate expand button (tap-to-expand replaces it on phone).
       NOTE: .pb-progress is NOT hidden — it's repositioned below as the top
       strip (a display:none parent would hide #pb-bar-fill too). */
    .player-bar .pb-vol,
    .player-bar #pb-prev,
    .player-bar #pb-next,
    .player-bar #pb-expand { display: none; }
    .player-bar .pb-time { display: none; }                   /* labels off — strip only */
    /* .pb-controls is column-flex (stretch, width:100%) on desktop — collapse
       it to a compact inline cell now that it only holds the play button */
    .player-bar .pb-controls { flex-direction: row; align-items: center;
        gap: 8px; width: auto; min-width: 0; }
    .player-bar .pb-buttons { gap: 0; }
    .player-bar .pb-right { gap: 8px; }
    /* Circular accent play/pause — the clear primary action. */
    .player-bar .pb-play {
        width: 38px; height: 38px; border: none;
        background: var(--accent); color: #fff;
    }
    .player-bar .pb-play svg { width: 20px; height: 20px; }
    /* Compact, subtle close on the right. */
    .player-bar .pb-close { width: 32px; height: 32px; }
    .player-bar .pb-close svg { width: 16px; height: 16px; }

    /* Thin accent progress line pinned to the very top edge of the mini-bar.
       Reuses #pb-bar-fill (already updated on timeupdate). The whole
       .pb-progress row is lifted out of flow and pinned to the top as a 2px
       strip; the time labels are hidden above so only the bar shows. */
    .player-bar .pb-progress {
        position: absolute; top: 0; left: 0; right: 0;
        gap: 0; padding: 0; width: auto;
        pointer-events: none;                                  /* don't intercept tap-to-expand */
    }
    .player-bar #pb-bar {
        height: 2px; border-radius: 0; background: var(--bg-input);
    }
    .player-bar #pb-bar:hover { height: 2px; }                 /* no desktop hover-grow on phone */
    .player-bar #pb-bar-fill { transition: width 0.15s linear; }
}

/* ════════════════════════════════════════════════════════════════════
   Storage breakdown page (#page-storage) — opened from the Library KPI.
   Summary tiles · SVG donut + legend · per-item proportional bars.
   ════════════════════════════════════════════════════════════════════ */

/* ── Summary row: 3 stat tiles ── */
.storage-summary {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 14px;
    margin-bottom: 20px;
}
.storage-tile {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 16px 18px;
}
.storage-tile-val {
    font-size: 24px;
    font-weight: 700;
    color: var(--text);
    line-height: 1.15;
    word-break: break-word;
}
.storage-tile-lbl {
    margin-top: 4px;
    font-size: 12px;
    color: var(--text-dim);
    text-transform: uppercase;
    letter-spacing: 0.06em;
}

/* ── Breakdown: donut card (left) + item list card (right) ── */
.storage-breakdown {
    display: grid;
    grid-template-columns: 280px 1fr;
    gap: 18px;
    align-items: start;
}
.storage-chart-card,
.storage-list-card {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 18px;
}
.storage-chart-card h3,
.storage-list-card h3 {
    margin: 0 0 14px;
    font-size: 14px;
    font-weight: 600;
    color: var(--text);
}

/* ── Donut + legend ── */
.storage-donut-wrap {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 16px;
}
.storage-donut { flex: 0 0 auto; }
.storage-donut-seg {
    transition: stroke-dasharray 0.4s ease;
}
.storage-donut-c1 {
    fill: var(--text);
    font-size: 15px;
    font-weight: 700;
}
.storage-donut-c2 {
    fill: var(--text-dim);
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.08em;
}
.storage-legend {
    width: 100%;
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.storage-legend-row {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 13px;
}
.storage-legend-dot {
    width: 11px; height: 11px;
    border-radius: 3px;
    flex: 0 0 auto;
}
.storage-legend-name { color: var(--text); font-weight: 500; }
.storage-legend-val {
    margin-left: auto;
    color: var(--text-dim);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* ── Per-item bar list ── */
.storage-list {
    display: flex;
    flex-direction: column;
    gap: 14px;
}
.storage-item {
    border-bottom: 1px solid var(--border);
    padding-bottom: 14px;
}
.storage-item:last-child { border-bottom: none; padding-bottom: 0; }
.storage-item.clickable { cursor: pointer; }
.storage-item-head {
    display: flex;
    align-items: center;
    gap: 12px;
}
.storage-item-cover {
    width: 44px; height: 44px;
    border-radius: var(--radius-sm);
    object-fit: cover;
    flex: 0 0 auto;
    background: var(--bg-elev);
}
.storage-item-cover-ph {
    display: flex; align-items: center; justify-content: center;
    color: var(--accent);
    font-size: 22px;
}
.storage-item-info { min-width: 0; flex: 1 1 auto; }
.storage-item-title {
    font-size: 14px; font-weight: 600; color: var(--text);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.storage-item-sub {
    margin-top: 4px;
    display: flex; align-items: center; gap: 8px;
    font-size: 12px;
}
.storage-item-svc { color: var(--text-dim); text-transform: capitalize; }
.storage-item-right { text-align: right; flex: 0 0 auto; }
.storage-item-size {
    font-size: 14px; font-weight: 700; color: var(--text);
    font-variant-numeric: tabular-nums;
}
.storage-item-files { font-size: 12px; color: var(--text-dim); }
.storage-item-bar {
    margin-top: 9px;
    height: 6px;
    background: var(--bg-elev);
    border-radius: 999px;
    overflow: hidden;
}
.storage-item-bar > span {
    display: block;
    height: 100%;
    background: var(--accent);
    border-radius: 999px;
    transition: width 0.4s ease;
}

/* ── Phone (≤767px): stack to single column, donut centered, bars full-width ── */
@media (max-width: 767px), (min-width: 768px) and (max-width: 1024px) and (orientation: portrait) {
    /* minmax(0,1fr): a bare 1fr track won't shrink below its content's
       min-content, so the three big-number tiles overflowed the screen. */
    .storage-summary { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
    .storage-tile { padding: 12px 10px; min-width: 0; }
    .storage-tile-val { font-size: 18px; word-break: break-word; }
    .storage-tile-lbl { font-size: 10px; }
    .storage-breakdown { grid-template-columns: 1fr; gap: 14px; }
    .storage-chart-card, .storage-list-card { padding: 14px; min-width: 0; }
    .storage-item-cover { width: 40px; height: 40px; }
    /* Let the long name truncate so the right-aligned value stays on-screen. */
    .storage-legend-row { min-width: 0; }
    .storage-legend-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
}

/* =====================================================================
   TABLET (768–1023px) — spec §11 (revised 2026-05-29).
   The previous 64px icon-rail clipped the sidebar: it only hid `.nav-item
   span`, but the live sidebar uses `.nav-group-label` (SERVICES/DOWNLOADS),
   `.ns-name` (Qobuz/TIDAL…), `.nav-item-sub` (Albums/Artists — bare text,
   no span) and `.sidebar-storage` (Storage) — none of which it hid, so they
   overflowed the 64px column and rendered half-cut on portrait iPad. A 64px
   rail also can't host the expandable service sub-nav.
   Fix: tablets inherit the FULL desktop sidebar (240px, labelled). 768px −
   240px = 528px content — comfortable, and labels never clip. Only add the
   status-bar safe-area inset so the brand clears the notch in standalone PWA.
   Landscape tablets are ≥1024px → desktop band; this block targets the
   portrait-tablet width range.
   ===================================================================== */
@media (min-width: 768px) and (max-width: 1023px) and (orientation: landscape) {
    /* Landscape tablet (rare — most landscape iPads are ≥1024 = desktop band):
       full desktop sidebar with status-bar safe-area. Portrait tablets use the
       phone shell (bottom tab bar) via the (orientation: portrait) queries above. */
    .sidebar { padding-top: var(--safe-top); }
}

/* ── Downloads pager ─────────────────────────────────────────────── */
.dl-pager {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 12px;
    margin: 16px 0 4px;
    flex-wrap: wrap;
}
.dl-pager-info { font-size: 13px; color: var(--text-dim); }

/* ── Storage: collapsed single-tracks summary ────────────────────── */
.storage-tracks-summary .st-caret { color: var(--text-dim); font-size: 11px; margin-left: 2px; }
.storage-tracks-sub { margin-left: 14px; border-left: 2px solid var(--border); padding-left: 8px; }

/* ── AI chat onboarding (interactive feature discovery) ───────────── */
.ai-onboard .ai-onboard-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 10px;
    margin-top: 14px;
    width: 100%;
    max-width: 680px;
}
.ai-onboard-card {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 3px;
    text-align: left;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 14px;
    cursor: pointer;
    font: inherit;
    color: inherit;
    transition: border-color .15s ease, transform .1s ease;
}
.ai-onboard-card:hover { border-color: var(--accent); transform: translateY(-1px); }
.ai-onboard-card:active { transform: translateY(0); }
.ai-onboard-ic { font-size: 22px; line-height: 1; }
.ai-onboard-tt { font-weight: 700; color: var(--text-strong); }
.ai-onboard-ds { font-size: 12px; color: var(--text-dim); }
.ai-onboard-ex { font-size: 12px; color: var(--accent); margin-top: 6px; }
@media (max-width: 560px) {
    .ai-onboard .ai-onboard-grid { grid-template-columns: 1fr; }
}

/* ── AI chat image attachment (multimodal) ───────────────────────── */
.ai-attach-btn {
    flex: 0 0 auto;
    align-self: flex-end;
    padding: 8px 10px;
    font-size: 18px;
    line-height: 1;
}
.ai-attach-btn:disabled { opacity: 0.4; cursor: not-allowed; }
.ai-image-preview {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 8px;
    margin: 0 0 6px;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    width: fit-content;
    max-width: 100%;
}
.ai-image-preview img {
    height: 56px;
    width: auto;
    max-width: 120px;
    object-fit: cover;
    border-radius: 6px;
}
.ai-image-remove {
    background: none;
    border: none;
    color: var(--text-dim);
    cursor: pointer;
    font-size: 15px;
    padding: 4px 6px;
}
.ai-image-remove:hover { color: var(--danger); }
.ai-msg-img img {
    max-width: 220px;
    max-height: 220px;
    border-radius: 8px;
    margin-bottom: 6px;
    border: 1px solid var(--border);
}
.ai-mic-btn.listening {
    color: #fff;
    background: var(--accent);
    animation: ai-mic-pulse 1.1s ease-in-out infinite;
}
@keyframes ai-mic-pulse {
    0%, 100% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.5); }
    50%      { box-shadow: 0 0 0 6px rgba(239, 68, 68, 0); }
}

/* ════════════════════════════════════════════════════════════════
   HOME dashboard redesign — stat tiles, cards & hand-rolled charts
   (mobile-first; desktop widens the grids via min-width queries)
   ════════════════════════════════════════════════════════════════ */

/* Hero stat tiles — 2×2 on phone, 4-across on desktop */
.home-hero {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 10px;
    margin-bottom: 14px;
}
.hstat {
    background: var(--bg-card, #16181d);
    border: 1px solid var(--border, rgba(255,255,255,0.07));
    border-radius: 14px;
    padding: 14px 16px;
}
.hstat-k { font-size: 12px; color: var(--text-dim, #8b8f98); font-weight: 600; }
.hstat-v { font-size: 26px; font-weight: 800; color: var(--text, #fff); line-height: 1.15; margin-top: 4px; }
.hstat-v.hstat-accent { color: var(--accent, #ef4444); }
.hstat-v.hstat-warn { color: #f6c352; }
.hstat-v.hstat-bad { color: #ef4444; }
.hstat-s { font-size: 11px; color: var(--text-dim, #8b8f98); margin-top: 2px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Quick actions */
.home-quick { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 16px; }
.qbtn {
    flex: 1 1 calc(50% - 8px);
    display: flex; align-items: center; justify-content: center; gap: 8px;
    padding: 12px; border-radius: 12px;
    background: var(--bg-card, #16181d);
    border: 1px solid var(--border, rgba(255,255,255,0.07));
    color: var(--text, #fff); font-size: 13px; font-weight: 600; cursor: pointer;
}
.qbtn:hover { border-color: var(--accent, #ef4444); }
.qbtn-ic { font-size: 16px; }

/* Segmented range toggle */
.seg { display: inline-flex; background: rgba(255,255,255,0.05); border-radius: 9px; padding: 2px; }
.seg button {
    border: 0; background: transparent; color: var(--text-dim, #8b8f98);
    font-size: 12px; font-weight: 600; padding: 5px 10px; border-radius: 7px; cursor: pointer;
}
.seg button.active { background: var(--accent, #ef4444); color: #fff; }

/* Cards grid */
.home-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 12px;
    margin: 14px 0;
}
.stat-card { padding: 14px 16px; }
.stat-card h4 { margin: 0 0 12px; font-size: 13px; color: var(--text-dim, #8b8f98); font-weight: 700; }
.stat-big .big-num { font-size: 34px; font-weight: 800; color: var(--text, #fff); line-height: 1; }
.stat-big .big-sub { font-size: 12px; color: var(--text-dim, #8b8f98); margin-top: 4px; }
.stat-big .big-sub.up { color: #34d399; }
.stat-big .big-sub.down { color: #ef4444; }
.chart-empty { font-size: 12px; color: var(--text-dim, #8b8f98); padding: 8px 0; }

/* Stacked bar + legend */
.stackbar { display: flex; height: 14px; border-radius: 7px; overflow: hidden; background: rgba(255,255,255,0.05); }
.sb-seg { height: 100%; }
.sb-legend { display: flex; flex-wrap: wrap; gap: 10px 14px; margin-top: 10px; }
.sb-leg { font-size: 12px; color: var(--text-dim, #8b8f98); display: inline-flex; align-items: center; gap: 5px; }
.sb-leg b { color: var(--text, #fff); }
.sb-dot { width: 9px; height: 9px; border-radius: 3px; display: inline-block; }
.seg-ok, .sb-dot.seg-ok   { background: #34d399; }
.seg-warn, .sb-dot.seg-warn { background: #f6c352; }
.seg-bad, .sb-dot.seg-bad  { background: #ef4444; }
.seg-hires, .sb-dot.seg-hires { background: #a78bfa; }
.seg-svc0, .sb-dot.seg-svc0 { background: #60a5fa; }
.seg-svc1, .sb-dot.seg-svc1 { background: #34d399; }
.seg-svc2, .sb-dot.seg-svc2 { background: #f6c352; }
.seg-svc3, .sb-dot.seg-svc3 { background: #f472b6; }

/* Leaderboard */
.lb { display: flex; flex-direction: column; gap: 8px; }
.lb-row { display: flex; align-items: center; gap: 10px; }
.lb-row.clickable { cursor: pointer; }
.lb-name { flex: 1 1 auto; min-width: 0; font-size: 13px; color: var(--text, #fff);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.lb-track { flex: 0 0 38%; height: 8px; background: rgba(255,255,255,0.06); border-radius: 4px; overflow: hidden; }
.lb-fill { height: 100%; background: var(--accent, #ef4444); border-radius: 4px; }
.lb-val { flex: 0 0 auto; font-size: 12px; color: var(--text-dim, #8b8f98); min-width: 40px; text-align: right; }

/* Volume rows */
.vol-row { display: flex; justify-content: space-between; font-size: 13px; padding: 4px 0; color: var(--text-dim, #8b8f98); }
.vol-row b { color: var(--text, #fff); }

/* Activity bars */
.activity-chart { min-height: 150px; }
.bars { display: flex; align-items: flex-end; gap: 4px; height: 140px; }
.bar-col { flex: 1 1 0; display: flex; flex-direction: column; align-items: center; justify-content: flex-end; height: 100%; gap: 4px; }
.bar-fill { width: 70%; min-height: 2px; background: linear-gradient(180deg, var(--accent, #ef4444), rgba(239,68,68,0.45));
    border-radius: 4px 4px 0 0; transition: height .25s; }
.bar-lbl { font-size: 9px; color: var(--text-dim, #8b8f98); white-space: nowrap; }

/* Heatmap */
.heatmap { overflow-x: auto; }
.hm-grid { display: grid; grid-auto-flow: column; grid-template-rows: repeat(7, 12px); gap: 3px; width: max-content; }
.hm-cell { width: 12px; height: 12px; border-radius: 3px; background: rgba(255,255,255,0.05); }
.hm-1 { background: rgba(239,68,68,0.30); }
.hm-2 { background: rgba(239,68,68,0.52); }
.hm-3 { background: rgba(239,68,68,0.74); }
.hm-4 { background: rgba(239,68,68,0.96); }

@media (min-width: 720px) {
    .home-hero { grid-template-columns: repeat(4, 1fr); }
    .home-grid { grid-template-columns: repeat(2, 1fr); }
    .qbtn { flex: 0 1 auto; padding: 12px 18px; }
}
@media (min-width: 1100px) {
    .home-grid { grid-template-columns: repeat(3, 1fr); }
}

/* ════════════════════════════════════════════════════════════════
   Quality Showdown — Duplicates tab + A/B compare modal
   ════════════════════════════════════════════════════════════════ */
.dupes-list { display: flex; flex-direction: column; gap: 12px; }
.dupe-group { padding: 14px 16px; }
.dupe-head { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.dupe-head h4 { margin: 0; font-size: 14px; color: var(--text, #fff); flex: 1; min-width: 0;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.dupe-badge { flex: 0 0 auto; font-size: 12px; font-weight: 800; color: #fff;
    background: var(--accent, #ef4444); border-radius: 999px; padding: 2px 9px; }
.dupe-copies { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 12px; }
.dupe-copy { display: flex; gap: 8px; align-items: center; flex: 1 1 220px; min-width: 0;
    background: rgba(255,255,255,0.04); border-radius: 10px; padding: 8px; }
.dupe-cover { position: relative; width: 44px; height: 44px; border-radius: 6px; overflow: hidden;
    background: var(--bg-input, #222); flex: 0 0 auto; }
.dupe-cover img { width: 100%; height: 100%; object-fit: cover; }
.dupe-cover .src-badge { position: absolute; bottom: 0; left: 0; font-size: 8px; padding: 1px 3px;
    background: rgba(0,0,0,0.7); border-radius: 0 4px 0 0; }
.dupe-meta { min-width: 0; }
.dupe-t { font-size: 12px; color: var(--text, #fff); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.dupe-s { font-size: 11px; color: var(--text-dim, #8b8f98); }

/* Compare modal */
.cmp-modal { border: 0; border-radius: 16px; padding: 0; width: 92vw; max-width: 920px;
    background: var(--bg-card, #16181d); color: var(--text, #fff); box-shadow: 0 20px 60px rgba(0,0,0,0.6);
    /* Center in the viewport regardless of UA dialog defaults / margin resets */
    position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); margin: 0;
    max-height: 90vh; max-height: 90dvh; overflow-y: auto; }
.cmp-modal::backdrop { background: rgba(0,0,0,0.66); }
.cmp-modal-head { display: flex; align-items: center; justify-content: space-between;
    padding: 14px 18px; border-bottom: 1px solid var(--border, rgba(255,255,255,0.08)); }
.cmp-modal-head h3 { margin: 0; font-size: 15px; }
.cmp-verdict { padding: 12px 18px; font-size: 14px; border-bottom: 1px solid var(--border, rgba(255,255,255,0.08)); }
.cmp-verdict.analyzing { color: var(--accent, #ef4444); animation: cmp-pulse 1.2s ease-in-out infinite; }
.cmp-verdict.done { color: var(--text, #fff); background: rgba(52,211,153,0.08); }
@keyframes cmp-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.55; } }
.cmp-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; padding: 16px 18px; }
.cmp-col { background: rgba(255,255,255,0.03); border: 1px solid var(--border, rgba(255,255,255,0.07));
    border-radius: 12px; padding: 12px; }
.cmp-col.winner { border-color: #34d399; box-shadow: 0 0 0 1px #34d399 inset; }
.cmp-chead { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
.cmp-cnt { font-size: 11px; color: var(--text-dim, #8b8f98); }
/* Building album spectrogram — one real strip per track, stitched left→right */
.cmp-spectro { display: flex; align-items: stretch; height: 150px; margin-bottom: 8px;
    background: #0c0c0f; border-radius: 8px; overflow: hidden; }
.csb { flex: 1 1 0; min-width: 1px; background-color: rgba(255,255,255,0.03);
    background-size: cover; background-position: center; background-repeat: no-repeat;
    transition: background-color .25s; }
.csb.on { background-color: transparent; }
.cmp-spec { width: 100%; height: 70px; object-fit: cover; border-radius: 8px; display: block;
    background: transparent; margin-bottom: 8px; }
.cmp-metrics { display: flex; flex-direction: column; gap: 6px; }
.cmp-row { display: flex; justify-content: space-between; font-size: 12px; color: var(--text-dim, #8b8f98); }
.cmp-row b { color: var(--text, #fff); }
.auth-badge { display: inline-block; font-size: 11px; font-weight: 700; padding: 3px 8px; border-radius: 6px; margin-bottom: 4px; }
.auth-badge.ok { background: rgba(52,211,153,0.18); color: #6ee7b7; }
.auth-badge.ups { background: rgba(246,195,82,0.18); color: #fcd770; }
.auth-badge.bad { background: rgba(239,68,68,0.18); color: #fca5a5; }
.dr-badge { font-size: 12px; font-weight: 700; padding: 1px 7px; border-radius: 6px; background: rgba(255,255,255,0.08); }
.dr-badge.dr-good { background: rgba(52,211,153,0.18); color: #6ee7b7; }
.dr-badge.dr-mid { background: rgba(246,195,82,0.18); color: #fcd770; }
.dr-badge.dr-bad { background: rgba(239,68,68,0.18); color: #fca5a5; }
.cmp-warn { font-size: 11px; color: #fca5a5; }
.cmp-incon { font-size: 12px; color: var(--text-dim, #8b8f98); font-style: italic; }
.cmp-actions { display: flex; gap: 8px; justify-content: flex-end; padding: 0 18px 16px; }
@media (max-width: 640px) {
    .cmp-cols { grid-template-columns: 1fr; }
    .cmp-actions { flex-direction: column; }
    .cmp-actions .btn { width: 100%; }
}

/* Upscale modal */
.ups-modal { max-width: 520px; }
.ups-body { padding: 16px 18px; display: flex; flex-direction: column; gap: 12px; }
.ups-note { font-size: 12px; color: var(--text-dim, #8b8f98); line-height: 1.45;
    background: rgba(255,255,255,0.04); border-radius: 8px; padding: 10px 12px; }
.ups-rec { background: rgba(239,68,68,0.08); border: 1px solid rgba(239,68,68,0.3); border-radius: 10px; padding: 12px 14px; }
.ups-rec-line { font-size: 15px; color: var(--text, #fff); }
.ups-rec-line b { color: var(--accent, #ef4444); }
.ups-rec-sub { font-size: 12px; color: var(--text-dim, #8b8f98); margin-top: 3px; }
.ups-warn { font-size: 12px; color: #fcd770; }
.ups-blocked { font-size: 13px; color: #fca5a5; background: rgba(239,68,68,0.1); border-radius: 8px; padding: 12px 14px; }
.ups-done { font-size: 14px; color: #6ee7b7; }
.ups-adv summary { font-size: 13px; color: var(--text-dim, #8b8f98); cursor: pointer; padding: 4px 0; }
.ups-field { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 8px 0; }
.ups-field label { font-size: 13px; color: var(--text-dim, #8b8f98); }
.ups-field select { background: var(--bg-input, #222); color: var(--text, #fff);
    border: 1px solid var(--border, rgba(255,255,255,0.12)); border-radius: 7px; padding: 6px 8px; font-size: 13px; }
.ups-prog { height: 10px; background: rgba(255,255,255,0.08); border-radius: 5px; overflow: hidden; }
.ups-prog-fill { height: 100%; background: linear-gradient(90deg, var(--accent, #ef4444), #f6c352); transition: width .3s; }
.ups-prog-label { font-size: 13px; color: var(--accent, #ef4444); font-weight: 600; }

/* Compare conclusion (upscale A/B) */
.cmp-conclusion:empty { display: none; }
.cmp-concl { margin: 0 18px 12px; padding: 12px 14px; background: rgba(255,255,255,0.04); border-radius: 10px; }
.cmp-concl-title { font-size: 11px; font-weight: 700; color: var(--text-dim, #8b8f98); text-transform: uppercase; letter-spacing: .04em; margin-bottom: 6px; }
.cmp-concl-row { font-size: 13px; color: var(--text-dim, #8b8f98); }
.cmp-concl-row b { color: var(--text, #fff); }
.cmp-concl-bad { font-size: 13px; color: #fcd770; margin-top: 8px; line-height: 1.45; }
.cmp-concl-ok { font-size: 13px; color: #6ee7b7; margin-top: 8px; line-height: 1.45; }
.cmp-concl-note { font-size: 13px; color: var(--text-dim, #8b8f98); }

/* AI conversations drawer backdrop (phone) — tap outside to close */
.ai-sidebar-backdrop { position: fixed; inset: 0; z-index: 94; background: rgba(0,0,0,0.5);
    opacity: 0; pointer-events: none; transition: opacity .2s; }
.ai-sidebar-backdrop.show { opacity: 1; pointer-events: auto; }

/* ── Sticky-model badge (bug 2026-05-28: persist Opus across turns) ── */
.ai-sticky-model-badge {
    display: flex;
    justify-content: flex-start;
    padding: 0 4px;
}
.ai-sticky-model-badge:empty { display: none; }
.ai-sticky-pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 8px 4px 10px;
    background: linear-gradient(135deg, rgba(201,168,94,0.18), rgba(201,168,94,0.08));
    border: 1px solid rgba(201,168,94,0.45);
    border-radius: 999px;
    font-size: 12px;
    color: var(--accent);
    font-weight: 600;
}
.ai-sticky-close {
    background: transparent;
    border: 0;
    color: var(--accent);
    cursor: pointer;
    font-size: 14px;
    line-height: 1;
    padding: 0 2px;
    opacity: 0.7;
}
.ai-sticky-close:hover { opacity: 1; }

/* ── AI playlist v2 chip bar (above chat input) ───────────────── */
.ai-chip-bar {
    display: flex;
    gap: 6px;
    padding: 6px 4px 8px;
    flex-wrap: wrap;
    align-items: center;
}
.ai-chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    height: 28px;
    padding: 0 10px;
    border-radius: 999px;
    background: rgba(255,255,255,0.04);
    border: 1px solid var(--border);
    color: var(--text);
    font-size: 12px;
    cursor: pointer;
    transition: background 0.12s, border-color 0.12s, color 0.12s;
}
.ai-chip:hover { background: rgba(255,255,255,0.08); }
.ai-chip[aria-pressed="true"],
.ai-chip.active {
    background: var(--accent);
    color: #fff;
    border-color: var(--accent);
}
.ai-chip-icon { font-size: 14px; line-height: 1; }
.ai-chip-mobile-toggle { display: none; }   /* redundant with the full chip set; never shown */

/* Phone: show the FULL chip set (it wraps to 2 rows) instead of collapsing to a
   single bias toggle — users need cap / exclude / sources / research / count on
   mobile too. The chip bar already has flex-wrap: wrap. */

/* While an AI request is in flight, the conversation list is dimmed and
   pointer events are suppressed so the user can't switch mid-request. */
.ai-list-busy { opacity: 0.55; pointer-events: none; cursor: not-allowed; }
/* Tiny shake nudge when a switch is attempted during a request. */
@keyframes ai-shake-blocked {
    0%, 100% { transform: translateX(0); }
    25%      { transform: translateX(-3px); }
    75%      { transform: translateX(3px); }
}
.shake-blocked { animation: ai-shake-blocked 0.25s ease; }

/* ── AI playlist card: 👍/👎 + per-track 👎 ───────────────────── */
.ap-thumbs {
    display: flex;
    gap: 6px;
    padding: 8px 12px 0;
    justify-content: flex-end;
}
.ap-thumb {
    background: transparent;
    border: 1px solid var(--border);
    border-radius: 999px;
    min-width: 36px;
    height: 28px;
    padding: 0 8px;
    cursor: pointer;
    font-size: 14px;
    line-height: 1;
    color: var(--text);
    transition: background 0.12s, border-color 0.12s, transform 0.12s;
}
.ap-thumb:hover { background: rgba(255,255,255,0.08); transform: scale(1.05); }
.ap-thumb.active { background: var(--accent); border-color: var(--accent); color: #fff; }

/* Per-track 👎 — hidden until the row is hovered, low-noise. */
.apt-block {
    background: transparent;
    border: none;
    color: var(--text-dim);
    cursor: pointer;
    font-size: 12px;
    opacity: 0;
    transition: opacity 0.12s, color 0.12s;
    padding: 0 4px;
    margin-left: auto;
}
.ap-track:hover .apt-block,
.ap-track:focus-within .apt-block { opacity: 0.65; }
.apt-block:hover { opacity: 1; color: var(--danger, #e55); }
/* Blocked state — solid accent ✓; clickable to undo (no :disabled rule). */
.apt-block[data-blocked="1"] { opacity: 1; color: var(--accent); }
.apt-block[data-blocked="1"]:hover { color: var(--text); }

/* ▶ preview button (2026-05-29): stream a track without downloading, from the
   user's own account. Small, always visible, accent-tinted. Shared across AI
   playlist rows, the editor, research cards and search results. */
.apt-play {
    flex: 0 0 auto;
    width: 24px; height: 24px;
    display: inline-flex; align-items: center; justify-content: center;
    background: transparent;
    border: 1px solid var(--border);
    border-radius: 50%;
    color: var(--accent);
    cursor: pointer;
    font-size: 10px;
    line-height: 1;
    padding: 0;
    margin-right: 8px;
    vertical-align: middle;
    transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.apt-play:hover { background: var(--accent); color: #000; border-color: var(--accent); }
.apt-play.playing { background: var(--accent); color: #000; border-color: var(--accent); }
/* In the research/search title lines the button sits inline before text. */
.ac-title .apt-play, .info .title .apt-play { width: 22px; height: 22px; }
/* On touch (no hover), make the block button always visible but dim. */
@media (hover: none) {
    .apt-block { opacity: 0.55; }
    .apt-block[data-blocked="1"] { opacity: 1; }
}

/* v2 transparency line — shows what the agent understood (genre, bias,
   sub-tags, artist cap, pool size, optional relaxation note) above each
   AI-generated playlist card. Pre-v2 cards omit it. */
.ai-playlist-card .ap-transparency {
    font-size: 11px;
    color: var(--text-dim);
    padding: 6px 12px 0;
    letter-spacing: 0.02em;
}
.ai-playlist-card .ap-subtags { color: var(--text); opacity: 0.75; }
.ai-playlist-card .ap-relax { color: var(--accent); }

/* ── AI background indicator — floats on every page while a chat turn is in
   flight (except on ai-chat itself where the native UI shows progress). ── */
.ai-bg-indicator {
    position: fixed;
    right: 18px;
    bottom: calc(var(--bottom-stack) + 18px);   /* clears tab bar + player + safe-area on every band */
    z-index: 1050;
    display: flex;
    align-items: stretch;
    border-radius: 999px;
    background: var(--bg-elev-2, #1f1f24);
    box-shadow: 0 6px 24px rgba(0, 0, 0, 0.45);
    border: 1px solid var(--accent);
    overflow: hidden;
    color: var(--text);
    font-size: 12px;
    transition: opacity 0.2s, transform 0.2s;
}
.ai-bg-indicator.hidden { display: none; }
.ai-bg-open, .ai-bg-cancel {
    background: transparent;
    border: none;
    color: inherit;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    padding: 8px 12px;
    font: inherit;
    font-size: 12px;
}
.ai-bg-open { gap: 8px; padding-left: 14px; }
.ai-bg-open:hover { background: rgba(255, 255, 255, 0.06); }
.ai-bg-cancel {
    opacity: 0.65;
    border-left: 1px solid rgba(255, 255, 255, 0.08);
    padding: 8px 12px;
}
.ai-bg-cancel:hover { opacity: 1; color: var(--danger, #e55); background: rgba(255, 255, 255, 0.04); }
.ai-bg-dot {
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: var(--accent);
    animation: ai-bg-pulse 1.2s ease-in-out infinite;
}
@keyframes ai-bg-pulse {
    0%, 100% { opacity: 0.4; transform: scale(0.85); }
    50%      { opacity: 1;   transform: scale(1.15); }
}
@media (max-width: 640px) {
    .ai-bg-indicator { right: 12px; }   /* bottom handled by --bottom-stack on base */
}

/* ── AI playlist card: collapsible "Подробное описание" ──────── */
.ap-long {
    margin: 10px 12px 0;
    padding: 8px 10px;
    background: rgba(255, 255, 255, 0.03);
    border: 1px solid var(--border);
    border-radius: 8px;
}
.ap-long > summary {
    cursor: pointer;
    font-size: 12px;
    color: var(--text-dim);
    font-weight: 600;
    list-style: none;
    user-select: none;
    padding: 2px 0;
}
.ap-long > summary::-webkit-details-marker { display: none; }
.ap-long > summary::before {
    content: '▸ ';
    display: inline-block;
    transition: transform 0.15s;
    margin-right: 4px;
}
.ap-long[open] > summary::before { transform: rotate(90deg); }
.ap-long > summary:hover { color: var(--text); }
.ap-long-body {
    margin-top: 8px;
    padding-top: 8px;
    border-top: 1px solid var(--border);
    font-size: 13px;
    line-height: 1.55;
    color: var(--text);
    white-space: normal;
    word-wrap: break-word;
}

/* Horizontal rule inside markdown — the agent uses `---` to separate the
   "result" line from the per-track commentary in playlist explanations. */
.ai-msg-md .md-hr {
    border: none;
    border-top: 1px solid var(--border);
    margin: 14px 0;
    height: 0;
}

/* When the long description is open, render the body with the same paragraph
   spacing as the chat — line-height 1.55 already inherits from .ap-long-body. */
.ap-long-body.ai-msg-md > p,
.ap-long-body.ai-msg-md > * { margin-block: 6px; }
.ap-long-body.ai-msg-md > h3.md-h,
.ap-long-body.ai-msg-md > h4.md-h,
.ap-long-body.ai-msg-md > h5.md-h { margin-top: 14px; margin-bottom: 4px; }

/* ── perf-v1: skip layout/paint for off-screen pages ────────────
   29 .page-* divs stay mounted in DOM; navigation toggles .hidden.
   Without this, every style-recalc cost scales with the accumulated
   persistent DOM (Library + Service-browse + AI Playlists can hit
   tens of thousands of nodes). The browser keeps DOM + event bindings
   intact; it just skips rendering work until the page is shown again. */
.page.hidden {
    content-visibility: auto;
    contain-intrinsic-size: 1px 1000px;
}

/* perf-v1: phase milestone line under download title — visible without expanding */
.dl-phase {
    font-size: 11px;
    color: var(--text-dim);
    padding: 2px 0 0;
    letter-spacing: 0.01em;
}

/* perf-v1: per-track resolver ticker — shown during AI playlist build.
   Lives inside .ai-progress-card; cleared with the rest of the in-progress UI
   when the stream ends. Caps at 5 visible rows so a 50-track playlist won't
   blow up DOM during the gap between "building" and "done". */
.ai-resolver-ticker {
    margin: 6px 0;
    padding: 6px 10px;
    background: rgba(255, 255, 255, 0.03);
    border: 1px solid var(--border);
    border-radius: 8px;
    font-size: 12px;
    line-height: 1.5;
    max-height: 140px;
    overflow-y: auto;
}
.ai-resolver-ticker .art-head {
    color: var(--text-dim);
    font-weight: 600;
    margin-bottom: 4px;
}
.ai-resolver-ticker .art-row {
    display: flex;
    gap: 6px;
    align-items: center;
}
.ai-resolver-ticker .art-mark {
    color: var(--accent);
    width: 14px;
}
.ai-resolver-ticker .art-missing .art-mark { color: var(--text-dim); }
.ai-resolver-ticker .art-title {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.ai-resolver-ticker .art-svc {
    margin-left: auto;
    color: var(--text-dim);
    font-size: 11px;
}

/* perf-v1 D.5: global "downloads active" indicator — visible from any page.
   Stacks above the existing #ai-bg-indicator (which sits at bottom-right). */
.dl-bg-indicator {
    position: fixed;
    right: 18px;
    bottom: calc(var(--bottom-stack) + 80px);   /* stacks above ai-bg-indicator */
    z-index: 1050;
    background: var(--bg-elev-2, #1f1f24);
    border: 1px solid var(--accent);
    border-radius: 999px;
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4);
    color: var(--text);
}
.dl-bg-indicator.hidden { display: none; }
.dl-bg-open {
    background: transparent;
    border: none;
    color: inherit;
    padding: 8px 14px;
    cursor: pointer;
    font-size: 12px;
    display: inline-flex;
    align-items: center;
    gap: 6px;
}
.dl-bg-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--accent);
    animation: ai-bg-pulse 1.2s infinite ease-in-out;
}
.dl-bg-meta { color: var(--text-dim); font-size: 11px; }

@media (max-width: 640px) {
    .dl-bg-indicator { right: 12px; }   /* bottom handled by --bottom-stack on base */
}

/* perf-v1: opt-in browser Notification consent banner */
.dl-notify-banner {
    position: fixed;
    left: 50%;
    transform: translateX(-50%);
    bottom: calc(var(--bottom-stack) + 140px);
    z-index: 1100;
    background: var(--bg-elev-2);
    border: 1px solid var(--accent);
    border-radius: 12px;
    padding: 10px 14px;
    display: flex;
    gap: 8px;
    align-items: center;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
    font-size: 13px;
    max-width: 90vw;
}
.dl-notify-banner .dl-notify-text {
    flex: 1;
    min-width: 0;
}
@media (max-width: 640px) {
    .dl-notify-banner { flex-wrap: wrap; }   /* bottom handled by --bottom-stack on base */
}

/* ── C-scope: skeleton loaders ─────────────────────────────────────
   Replace plaintext "Loading…" with a shimmering grid of placeholder
   cards. Doesn't speed up the underlying fetch but kills the "blank
   screen" perception so pages feel responsive even on slow first paint.
   Skeleton dimensions match the real card grid so layout doesn't jump
   when content arrives. */
@keyframes sk-shimmer {
    0% { background-position: -200% 0; }
    100% { background-position: 200% 0; }
}

.sk-shimmer {
    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: sk-shimmer 1.6s linear infinite;
    border-radius: 6px;
}

.sk-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
    gap: 16px;
    padding: 8px 0;
}

.sk-card {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.sk-card .sk-cover {
    aspect-ratio: 1 / 1;
    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: sk-shimmer 1.6s linear infinite;
    border-radius: 8px;
}
.sk-card .sk-line-1,
.sk-card .sk-line-2 {
    height: 12px;
    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: sk-shimmer 1.6s linear infinite;
    border-radius: 4px;
}
.sk-card .sk-line-2 { width: 60%; }

/* Album/detail hero skeleton — for openLibraryDetail */
.sk-hero {
    display: grid;
    grid-template-columns: 200px 1fr;
    gap: 24px;
    padding: 24px;
    align-items: start;
}
.sk-hero .sk-cover {
    width: 200px;
    height: 200px;
    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: sk-shimmer 1.6s linear infinite;
    border-radius: 12px;
}
.sk-hero .sk-meta {
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding-top: 12px;
}
.sk-hero .sk-meta .sk-title {
    width: 60%;
    height: 24px;
    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: sk-shimmer 1.6s linear infinite;
    border-radius: 6px;
}
.sk-hero .sk-meta .sk-sub {
    width: 40%;
    height: 14px;
    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: sk-shimmer 1.6s linear infinite;
    border-radius: 4px;
}

@media (max-width: 640px) {
    .sk-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: 12px;
    }
    .sk-hero {
        grid-template-columns: 1fr;
        text-align: center;
    }
    .sk-hero .sk-cover {
        width: 100%;
        max-width: 280px;
        height: auto;
        aspect-ratio: 1 / 1;
        margin: 0 auto;
    }
}

/* perf-v1 C-scope: invisible spacer at the bottom of a windowed grid;
   IntersectionObserver fires when it scrolls into view to load the next
   chunk of items. */
.sg-sentinel {
    grid-column: 1 / -1;
    height: 1px;
    background: transparent;
    pointer-events: none;
}

/* perf-v2: Library "select all in library" banner — appears only when the
   user has selected every visible card AND more items exist server-side.
   Clicking the primary button fetches up to 10k ids and adds them to the
   selection set, which lets bulk-delete/bulk-zip operate over the full
   library without forcing it all to render. */
.lib-select-all-banner {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 14px;
    margin: 0 0 12px;
    background: rgba(201, 168, 94, 0.10);
    border: 1px solid var(--accent);
    border-radius: 8px;
    font-size: 13px;
    color: var(--text);
}
.lib-select-all-banner > span { flex: 1; }
@media (max-width: 640px) {
    .lib-select-all-banner { flex-wrap: wrap; }
    .lib-select-all-banner > span { flex-basis: 100%; }
}

/* perf-v2: "Recently from AI" strip — moved out of the interleaved grid
   so it doesn't disappear once the user scrolls past the first page of
   real downloads. Sits between the tab bar and the paged grid. */
.lib-saved-ai-wrap { margin-bottom: 16px; }
.lib-saved-ai-header {
    font-size: 12px;
    color: var(--text-dim, #8a8e94);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    margin: 0 0 8px;
}
.lib-saved-ai-strip {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
    gap: 12px;
}
@media (max-width: 640px) {
    .lib-saved-ai-strip { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Mobile: push notify() toasts below the mobile header (logo + status row)
 * so success/error messages do not overlap the brand mark on phones. The
 * notify() helper sets inline `top: 20px; right: 20px`, so we need
 * !important to override. */
@media (max-width: 640px) {
    .notify-toast {
        top: calc(env(safe-area-inset-top, 0px) + 56px) !important;
        right: 12px !important;
        left: 12px !important;
        max-width: none !important;
    }
}

/* ── Phase 3: Deep Research page ───────────────────────────────
 * 2-column layout: history sidebar (left) + report detail (right).
 * Task 7 ships the skeleton; Task 8 fills the detail pane with
 * markdown + per-finding album cards. */
.research-layout {
    display: grid;
    grid-template-columns: 320px 1fr;
    gap: 16px;
    min-height: 60vh;
    min-height: 60dvh;
}
@media (max-width: 720px) {
    .research-layout { grid-template-columns: 1fr; }
}
.research-history {
    border-right: 1px solid var(--border);
    padding-right: 12px;
}
@media (max-width: 720px) {
    .research-history { border-right: none; padding-right: 0; }
}
.research-history-row {
    padding: 8px 10px;
    margin-bottom: 4px;
    border-radius: var(--radius-sm, 6px);
    cursor: pointer;
    transition: background 0.1s ease;
}
.research-history-row:hover { background: var(--bg-card-hover); }
.research-history-row .rh-q {
    font-weight: 500;
    font-size: 14px;
    color: var(--text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.research-history-row .rh-meta {
    display: flex;
    gap: 8px;
    font-size: 11px;
    color: var(--text-dim);
    margin-top: 2px;
}
.rh-status {
    padding: 1px 6px;
    border-radius: 4px;
    background: var(--bg-card);
    text-transform: lowercase;
}
.rh-status-done { color: var(--success); }
.rh-status-failed,
.rh-status-cost_capped { color: var(--danger); }
.rh-status-quota_exceeded { color: var(--warning); }
.research-detail { padding: 8px; }
.research-empty {
    text-align: center;
    padding: 40px 16px;
    color: var(--text-dim);
}
.research-empty .ico { font-size: 48px; margin-bottom: 12px; }
.research-empty h3 { color: var(--text); margin-bottom: 8px; }
.research-history-empty {
    padding: 16px;
    color: var(--text-dim);
    text-align: center;
    font-size: 13px;
}

/* "New research" modal — native <dialog> with form inside. */
.research-modal {
    border: 1px solid var(--border-strong);
    border-radius: var(--radius, 10px);
    background: var(--bg-card);
    color: var(--text);
    padding: 0;
    max-width: 520px;
    width: 92vw;
    box-shadow: var(--shadow-elev);
}
.research-modal::backdrop {
    background: rgba(0, 0, 0, 0.55);
}
.research-modal-body {
    display: flex;
    flex-direction: column;
    gap: 12px;
    padding: 20px;
}
.research-modal-body h3 {
    margin: 0;
    font-size: 18px;
    font-weight: 700;
    color: var(--text-strong);
}
.research-modal-hint {
    margin: 0;
    color: var(--text-dim);
    font-size: 13px;
}
.research-modal-body textarea {
    width: 100%;
    background: var(--bg-input);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm, 6px);
    padding: 10px 12px;
    color: var(--text);
    font: inherit;
    resize: vertical;
    min-height: 90px;
}
.research-modal-body textarea:focus {
    outline: none;
    border-color: var(--accent);
}
.research-modal-actions {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
    margin-top: 4px;
}

/* Phase 3 Task 8 — research stream + report renderer */
.research-stream { padding: 16px; }
.research-stream-title { font-weight: 600; font-size: 16px; margin-bottom: 12px; }
.research-progress { font-family: ui-monospace, monospace; line-height: 1.7; }
.pr-row { padding: 2px 0; }
.pr-row-niche { padding-left: 20px; }
.pr-row-done { color: var(--success); }
.pr-row-running { color: var(--accent); }
.pr-row-err { color: var(--danger); }
.pr-row-warn { color: var(--warning); }
.research-stream-hint { margin-top: 20px; font-size: 13px; }

.research-tonight {
    background: var(--bg-card);
    padding: 12px 16px;
    border-radius: var(--radius-sm);
    margin: 12px 0;
    border-left: 3px solid var(--accent);
}
.research-tonight h4 {
    margin: 0 0 4px;
    font-size: 12px;
    color: var(--text-dim);
    text-transform: uppercase;
    letter-spacing: 0.05em;
}
.research-tonight-album { font-weight: 600; font-size: 15px; margin-bottom: 4px; }
.research-tonight p { margin: 0; color: var(--text-dim); font-size: 14px; }

.research-report-head {
    margin-bottom: 16px;
    padding-bottom: 12px;
    border-bottom: 1px solid var(--border-strong);
}
.research-report-head .rr-query { font-size: 18px; font-weight: 600; margin-bottom: 6px; }
.research-report-head .rr-meta {
    font-size: 12px;
    display: flex;
    align-items: center;
    gap: 6px;
}

.album-card-research {
    display: flex;
    gap: 12px;
    background: var(--bg-card);
    border-radius: var(--radius-sm);
    padding: 10px;
    margin: 8px 0;
    align-items: flex-start;
}
.album-card-research .ac-cover {
    width: 64px;
    height: 64px;
    border-radius: 4px;
    object-fit: cover;
    flex-shrink: 0;
}
.album-card-research .ac-cover-blank {
    background: var(--bg-card-hover);
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--text-dim);
    font-size: 24px;
}
.album-card-research .ac-body { flex: 1; min-width: 0; }
.album-card-research .ac-title { font-weight: 600; font-size: 14px; margin-bottom: 4px; }
.album-card-research .ac-key { font-size: 12px; color: var(--text-dim); margin-bottom: 6px; }
.album-card-research .ac-actions {
    display: flex;
    gap: 6px;
    flex-wrap: wrap;
    align-items: center;
}
.album-card-research .ac-action-badge {
    font-size: 11px;
    padding: 2px 8px;
    border-radius: 4px;
    background: var(--bg-card-hover);
    color: var(--text-dim);
}
.album-card-missing { color: var(--text-dim); font-style: italic; }

/* Phase 3.5: research bubble inside ai-chat thread */
.research-chat-bubble .ai-msg-content { padding-right: 4px; }
.research-chat-head { font-weight: 600; margin-bottom: 8px; }
.research-chat-q { color: var(--text-dim); font-weight: 400; }
.research-chat-target {
    margin-left: 8px;
    font-size: 11px;
    padding: 2px 8px;
    background: var(--bg-card-hover);
    border-radius: var(--radius-sm);
    color: var(--text-dim);
}
.research-chat-log {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    line-height: 1.6;
    font-size: 13px;
}
.rcl-row { padding: 1px 0; }
.rcl-niche { padding-left: 16px; }
.rcl-done { color: var(--success); }
.rcl-running { color: var(--accent); }
.rcl-err { color: var(--danger); }
.rcl-warn { color: var(--warning); }

/* Phase 3.5 Task 7: verbose tool-call log inside research chat bubble. */
.rcl-verbose { margin-top: 8px; }
.rcl-verbose > summary {
    cursor: pointer;
    font-size: 12px;
    color: var(--text-dim);
    padding: 4px 0;
    user-select: none;
}
.rcl-verbose-body {
    max-height: 240px;
    overflow-y: auto;
    background: var(--bg-card);
    border-radius: var(--radius-sm);
    padding: 6px 10px;
    font-family: ui-monospace, monospace;
    font-size: 12px;
    line-height: 1.7;
}
.rcl-verbose-body .rcl-row {
    padding: 1px 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.rcl-ts { color: var(--text-dim); margin-right: 6px; font-size: 11px; }
.rcl-agent { color: var(--accent); }
.rcl-arg, .rcl-result { color: var(--text-dim); }
.rcl-dur { color: var(--text-dim); opacity: 0.7; }
.rcl-finding { color: var(--success); }
.rcl-finding-icon { display: inline-block; width: 14px; color: var(--success); font-weight: 700; }
.rcl-finding-slug { color: var(--text-dim); margin-left: 6px; font-size: 10px; opacity: 0.7; }
.rcl-resolve-icon { color: var(--success); margin-right: 4px; }
.rcl-resolve-badge { color: var(--success); font-weight: 600; }
.rcl-resolve-miss { color: var(--warning); font-style: italic; }
.rcl-extract { color: var(--accent); }
.rcl-extract-icon { display: inline-block; width: 14px; }
.rcl-track-added { color: var(--success); padding-left: 14px; }
.rcl-track-icon { display: inline-block; width: 12px; color: var(--success); }
.rcl-track-name { font-weight: 500; }

.research-chat-report { margin-top: 12px; }
.research-chat-report:empty { display: none; }

/* ── Phase 3.5 Task 8: voting album cards + bulk actions ─────────────── */
.album-vote-card {
    display: flex;
    gap: 12px;
    background: var(--bg-card);
    border-radius: var(--radius-sm);
    padding: 10px;
    margin: 8px 0;
    align-items: flex-start;
    transition: background 0.1s ease;
}
.album-vote-card[data-vote="up"] { background: var(--bg-card-hover); border-left: 3px solid var(--success); }
.album-vote-card[data-vote="down"] { opacity: 0.5; border-left: 3px solid var(--danger); }
.album-vote-card .ac-cover { width: 56px; height: 56px; border-radius: 4px; object-fit: cover; flex-shrink: 0; }
.album-vote-card .ac-cover-blank { background: var(--bg-card-hover); display: flex; align-items: center; justify-content: center; color: var(--text-dim); font-size: 22px; }
.album-vote-card .ac-body { flex: 1; min-width: 0; }
.album-vote-card .ac-title { font-weight: 600; font-size: 14px; margin-bottom: 4px; }
.album-vote-card .ac-key { font-size: 12px; color: var(--text-dim); }
.album-vote-card .ac-unresolved { color: var(--warning); font-size: 13px; margin-left: 4px; }

.vote-actions { display: flex; gap: 4px; align-items: center; flex-shrink: 0; }
.vote-btn {
    background: transparent;
    border: 1px solid var(--border, #2a2a2a);
    border-radius: var(--radius-sm);
    padding: 4px 10px;
    cursor: pointer;
    font-size: 18px;
    line-height: 1;
    transition: all 0.1s ease;
}
.vote-btn:hover { background: var(--bg-card-hover); }
.vote-btn-up.vote-btn-active { background: var(--success); border-color: var(--success); }
.vote-btn-down.vote-btn-active { background: var(--danger); border-color: var(--danger); }

.research-bulk-actions {
    display: flex;
    gap: 8px;
    align-items: center;
    padding: 12px;
    margin-top: 12px;
    background: var(--bg-card);
    border-radius: var(--radius-sm);
    border-top: 1px solid var(--border-strong, var(--border));
}
.vote-counter { margin-right: auto; font-size: 14px; }
.vote-counter strong { color: var(--success); font-size: 16px; }

/* Phase 4 — track-card variant. Mostly inherits .album-vote-card. */
.track-vote-card .ac-album-line {
    font-size: 12px;
    color: var(--text-dim);
    margin-bottom: 6px;
}
.track-vote-card .ac-description {
    font-size: 13px;
    color: var(--text-strong, var(--text));
    line-height: 1.5;
    margin-top: 4px;
    white-space: pre-wrap;
}
} /* ── end @layer responsive ── */

/* ── Hi-Res / quality audit panel (2026-05-29) ───────────────────────── */
.qa-panel { max-width: 760px; width: 100%; }
.qa-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.qa-sub { font-size: 12px; color: var(--text-dim); margin: 4px 0 10px; }
.qa-progress { font-size: 13px; color: var(--text); margin-bottom: 6px; }
.qa-summary { font-size: 13px; font-weight: 700; color: var(--text-strong); margin-bottom: 8px; min-height: 18px; }
.qa-filter { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--text-dim); margin-bottom: 8px; cursor: pointer; }
.qa-list { max-height: 56vh; max-height: 56dvh; overflow-y: auto; display: flex; flex-direction: column; gap: 2px; }
.qa-list.qa-only-problems .qa-row:not(.qa-problem) { display: none; }
.qa-row {
    display: grid;
    grid-template-columns: 22px minmax(0,1fr) auto auto auto;
    gap: 10px; align-items: center;
    padding: 6px 8px; border-radius: var(--radius-sm);
    font-size: 13px;
}
.qa-row:nth-child(odd) { background: rgba(255,255,255,0.02); }
.qa-row.qa-problem { background: rgba(229,72,77,0.10); }
.qa-icon { text-align: center; }
.qa-main { min-width: 0; overflow: hidden; }
.qa-title { display: block; color: var(--text-strong); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.qa-artist { display: block; font-size: 11px; color: var(--text-dim); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.qa-verdict { font-size: 12px; color: var(--text); white-space: nowrap; }
.qa-specs { font-size: 11px; color: var(--text-dim); font-variant-numeric: tabular-nums; white-space: nowrap; }
.qa-dr { font-size: 10.5px; color: var(--text-dim); white-space: nowrap; }
.qa-dr.qa-dr-bad { color: var(--danger, #e5484d); }
@media (max-width: 640px) {
    .qa-row { grid-template-columns: 20px minmax(0,1fr) auto; }
    .qa-specs, .qa-dr { display: none; }
}
