/* ============================================================
   JJ Music Player — modern glass UI
   Accent colors (--a1/--a2) are recomputed from album artwork.
   ============================================================ */

:root{
  --a1:#7c5cff;             /* accent primary   */
  --a2:#00d4ff;             /* accent secondary */
  --a3:#ff4d9d;             /* accent tertiary  */

  --bg:#07070d;
  --bg-2:#0c0c16;
  --panel:rgba(255,255,255,.045);
  --panel-2:rgba(255,255,255,.07);
  --stroke:rgba(255,255,255,.09);
  --stroke-2:rgba(255,255,255,.16);

  --tx:#f2f3f8;
  --tx-2:rgba(242,243,248,.62);
  --tx-3:rgba(242,243,248,.38);

  --r-sm:10px; --r:16px; --r-lg:22px; --r-xl:30px;
  --bar-h:104px;
  --side-w:260px;
  --np-w:340px;
  --ease:cubic-bezier(.22,1,.36,1);
  --shadow:0 18px 50px -12px rgba(0,0,0,.75);
  --blur:saturate(180%) blur(22px);
}

*{box-sizing:border-box;margin:0;padding:0}
/* Author display rules would otherwise beat the UA rule for [hidden]. */
[hidden]{display:none !important}
html,body{height:100%}
body{
  font-family:'Outfit',-apple-system,BlinkMacSystemFont,'Segoe UI',system-ui,sans-serif;
  background:var(--bg); color:var(--tx);
  overflow:hidden;
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
}
svg{width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:1.9;
    stroke-linecap:round;stroke-linejoin:round;flex:none}
svg.fill{fill:currentColor;stroke:none}
button{font:inherit;color:inherit;background:none;border:none;cursor:pointer}
input,select{font:inherit;color:inherit}
::selection{background:var(--a1);color:#fff}

/* scrollbars */
*::-webkit-scrollbar{width:9px;height:9px}
*::-webkit-scrollbar-track{background:transparent}
*::-webkit-scrollbar-thumb{background:rgba(255,255,255,.11);border-radius:9px;border:2px solid transparent;background-clip:content-box}
*::-webkit-scrollbar-thumb:hover{background:rgba(255,255,255,.22);background-clip:content-box}

/* ---------- ambient art glow ---------- */
.ambient{
  position:fixed;inset:0;z-index:0;pointer-events:none;
  background:
    radial-gradient(58vw 58vw at 12% -8%,  color-mix(in oklab,var(--a1) 42%,transparent), transparent 62%),
    radial-gradient(52vw 52vw at 92% 8%,   color-mix(in oklab,var(--a2) 34%,transparent), transparent 60%),
    radial-gradient(64vw 64vw at 55% 112%, color-mix(in oklab,var(--a3) 26%,transparent), transparent 62%),
    var(--bg);
  filter:blur(20px) saturate(130%);
  opacity:.55;
  transition:background 1.1s var(--ease);
}
.ambient::after{
  content:'';position:absolute;inset:0;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='.35'/%3E%3C/svg%3E");
  opacity:.22;mix-blend-mode:overlay;
}

/* ---------- layout ---------- */
.app{
  position:relative;z-index:1;
  display:grid;
  grid-template-columns:var(--side-w) minmax(0,1fr) var(--np-w);
  height:100vh; height:100dvh;
  padding-bottom:var(--bar-h);
  transition:grid-template-columns .4s var(--ease);
}
.app.np-hidden{grid-template-columns:var(--side-w) minmax(0,1fr) 0px}

/* ---------- sidebar ---------- */
.sidebar{
  display:flex;flex-direction:column;gap:14px;
  padding:20px 14px;
  border-right:1px solid var(--stroke);
  background:linear-gradient(180deg,rgba(255,255,255,.05),rgba(255,255,255,.015));
  backdrop-filter:var(--blur);-webkit-backdrop-filter:var(--blur);
  overflow-y:auto;
}
.brand{display:flex;align-items:center;gap:11px;padding:2px 6px 10px}
.brand-mark{
  width:38px;height:38px;border-radius:12px;flex:none;
  background:linear-gradient(135deg,var(--a1),var(--a2));
  display:flex;align-items:flex-end;justify-content:center;gap:2.5px;padding:9px 0;
  box-shadow:0 8px 22px -6px color-mix(in oklab,var(--a1) 75%,transparent);
}
.eq-bar{width:3px;border-radius:2px;background:rgba(255,255,255,.95);height:6px;animation:eq 1.05s var(--ease) infinite}
.eq-bar:nth-child(2){animation-delay:.16s}
.eq-bar:nth-child(3){animation-delay:.32s}
.eq-bar:nth-child(4){animation-delay:.48s}
@keyframes eq{0%,100%{height:5px}50%{height:17px}}
body.paused .eq-bar{animation-play-state:paused}
.brand-text{display:flex;flex-direction:column;line-height:1.15;min-width:0}
.brand-text strong{font-size:15.5px;font-weight:700;letter-spacing:-.2px}
.brand-text small{font-size:10.5px;color:var(--tx-3);text-transform:uppercase;letter-spacing:.09em}
.brand .sidebar-close{display:none;margin-left:auto}

.upload-btn{
  display:flex;align-items:center;justify-content:center;gap:8px;
  padding:12px;border-radius:var(--r);font-weight:650;font-size:14px;
  background:linear-gradient(135deg,var(--a1),var(--a2));color:#fff;
  box-shadow:0 10px 26px -10px color-mix(in oklab,var(--a1) 90%,transparent);
  transition:transform .18s var(--ease),box-shadow .18s var(--ease),filter .18s;
}
.upload-btn:hover{transform:translateY(-2px);filter:brightness(1.08);box-shadow:0 16px 34px -12px color-mix(in oklab,var(--a1) 95%,transparent)}
.upload-btn:active{transform:translateY(0)}
.ghost-btn{
  display:flex;align-items:center;justify-content:center;gap:8px;
  padding:10px;border-radius:var(--r);font-size:13px;font-weight:500;color:var(--tx-2);
  border:1px dashed var(--stroke-2);transition:.18s var(--ease);
}
.ghost-btn:hover{color:var(--tx);border-color:var(--a2);background:var(--panel)}
.ghost-btn svg{width:17px;height:17px}

.nav{display:flex;flex-direction:column;gap:2px}
.nav-label{
  display:flex;align-items:center;justify-content:space-between;
  font-size:10.5px;text-transform:uppercase;letter-spacing:.11em;
  color:var(--tx-3);padding:12px 10px 6px;font-weight:600;
}
.mini-btn{width:22px;height:22px;border-radius:7px;display:grid;place-items:center;color:var(--tx-3);transition:.15s}
.mini-btn svg{width:14px;height:14px;stroke-width:2.4}
.mini-btn:hover{background:var(--panel-2);color:var(--a2)}
.nav-item{
  display:flex;align-items:center;gap:11px;
  padding:9px 11px;border-radius:11px;font-size:13.5px;font-weight:500;
  color:var(--tx-2);position:relative;transition:.16s var(--ease);text-align:left;
}
.nav-item svg{width:17px;height:17px}
.nav-item span{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.nav-item em{font-style:normal;font-size:11px;color:var(--tx-3);font-variant-numeric:tabular-nums}
.nav-item:hover{background:var(--panel);color:var(--tx)}
.nav-item.active{background:var(--panel-2);color:var(--tx)}
.nav-item.active::before{
  content:'';position:absolute;left:-14px;top:50%;transform:translateY(-50%);
  width:3px;height:19px;border-radius:0 3px 3px 0;background:linear-gradient(var(--a1),var(--a2));
}
.playlist-nav{display:flex;flex-direction:column;gap:2px;max-height:34vh;overflow-y:auto}
.pl-item{display:flex;align-items:center;gap:10px;padding:8px 11px;border-radius:11px;
  font-size:13px;color:var(--tx-2);transition:.15s;text-align:left;width:100%}
.pl-item .pl-dot{width:8px;height:8px;border-radius:3px;flex:none;background:linear-gradient(135deg,var(--a1),var(--a3))}
.pl-item span{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pl-item em{font-style:normal;font-size:11px;color:var(--tx-3)}
.pl-item:hover{background:var(--panel);color:var(--tx)}
.pl-item.active{background:var(--panel-2);color:var(--tx)}
.pl-empty{font-size:12px;color:var(--tx-3);padding:6px 11px;line-height:1.5}

.sidebar-foot{margin-top:auto;padding:14px 10px 4px;display:flex;flex-direction:column;gap:7px}
.storage-bar{height:4px;border-radius:4px;background:var(--panel-2);overflow:hidden}
.storage-bar span{display:block;height:100%;width:0%;border-radius:4px;
  background:linear-gradient(90deg,var(--a2),var(--a1));transition:width .6s var(--ease)}
.sidebar-foot small{font-size:10.5px;color:var(--tx-3);line-height:1.45}
.account-row{display:flex;align-items:center;gap:8px;margin-bottom:4px;min-width:0}
.account-name{font-size:12px;color:var(--tx-2);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

/* ---------- sign-in gate ---------- */
/* Sits over everything until Supabase answers. The app behind it is inert
   rather than absent, so nothing has to be torn down and rebuilt. */
.auth-gate{
  position:fixed;inset:0;z-index:400;display:grid;place-items:center;padding:24px;
  background:radial-gradient(1200px 700px at 50% -10%,rgba(124,92,255,.22),transparent 70%),var(--bg);
  overflow:auto;
}
.auth-gate[hidden]{display:none}
body.gated{overflow:hidden}
.auth-card{display:flex;flex-direction:column;align-items:center;gap:18px;width:100%;max-width:420px}
.auth-brand{display:flex;align-items:center;gap:11px;color:var(--tx)}
.auth-brand svg{
  width:34px;height:34px;flex:none;fill:none;stroke:currentColor;stroke-width:1.8;
  stroke-linecap:round;stroke-linejoin:round;color:var(--a1);
}
.auth-brand strong{display:block;font-size:17px;letter-spacing:-.01em}
.auth-brand small{display:block;font-size:11.5px;color:var(--tx-3);margin-top:1px}
.auth-mount{width:100%;display:flex;justify-content:center}
.auth-form{
  display:flex;flex-direction:column;gap:9px;width:100%;
  padding:22px;border-radius:16px;background:var(--panel);border:1px solid var(--stroke);
}
.auth-form label{
  font-size:11px;text-transform:uppercase;letter-spacing:.09em;
  color:var(--tx-3);font-weight:600;
}
.auth-form input{
  height:42px;padding:0 13px;border-radius:11px;font:inherit;font-size:14px;
  background:var(--panel-2);border:1px solid var(--stroke);outline:none;color:var(--tx);
  transition:border-color .15s;
}
.auth-form input:focus{border-color:var(--a2)}
/* The code is short and read off another screen — space it out so a digit
   is not mistaken for its neighbour while retyping. */
#authCode{letter-spacing:.42em;text-align:center;font-size:19px;font-family:ui-monospace,monospace}
.auth-form button[type=submit]{margin-top:3px;height:42px;justify-content:center}
.auth-form button[type=submit]:disabled{opacity:.55;cursor:default}
.auth-hint{font-size:11.5px;color:var(--tx-3);line-height:1.55}
.auth-alt{display:flex;justify-content:space-between;gap:10px;margin-top:2px}
.auth-alt button{
  background:none;border:0;padding:0;font:inherit;font-size:11.5px;cursor:pointer;
  color:var(--a2);text-decoration:underline;text-underline-offset:2px;
}
.auth-alt button:hover{color:var(--tx)}
.auth-alt button:disabled{opacity:.5;cursor:default;text-decoration:none}
.account-out{
  background:none;border:0;padding:0;font:inherit;font-size:11.5px;cursor:pointer;
  color:var(--tx-3);text-decoration:underline;text-underline-offset:2px;
}
.account-out:hover{color:var(--tx-2)}
.auth-error{
  width:100%;text-align:center;font-size:12.5px;color:var(--tx-2);line-height:1.6;
  padding:18px;border-radius:14px;background:var(--panel);border:1px solid var(--stroke);
}
.auth-error p{margin-bottom:12px}

/* ---------- topbar ---------- */
.main{display:flex;flex-direction:column;min-width:0;overflow:hidden;position:relative}
.topbar{
  display:flex;align-items:center;gap:12px;padding:16px 26px 10px;flex:none;
}
.topbar .menu-btn{display:none}
.search{
  flex:1;max-width:520px;display:flex;align-items:center;gap:9px;
  padding:0 13px;height:42px;border-radius:13px;
  background:var(--panel);border:1px solid var(--stroke);
  transition:.2s var(--ease);
}
.search:focus-within{border-color:color-mix(in oklab,var(--a2) 60%,transparent);background:var(--panel-2);
  box-shadow:0 0 0 4px color-mix(in oklab,var(--a2) 12%,transparent)}
.search svg{width:17px;height:17px;color:var(--tx-3);flex:none}
.search input{flex:1;background:none;border:none;outline:none;font-size:14px;min-width:0}
.search input::placeholder{color:var(--tx-3)}
.clear-search{display:grid;place-items:center;color:var(--tx-3);width:20px;height:20px;border-radius:6px}
.clear-search:hover{color:var(--tx);background:var(--panel-2)}
.clear-search svg{width:13px;height:13px}
.topbar-actions{display:flex;align-items:center;gap:8px;margin-left:auto}
.select{
  height:38px;padding:0 30px 0 12px;border-radius:11px;font-size:12.5px;
  background:var(--panel) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E") no-repeat right 9px center/13px;
  border:1px solid var(--stroke);appearance:none;cursor:pointer;outline:none;
}
.select:hover{background-color:var(--panel-2)}
.select option{background:#14141f;color:var(--tx)}

.icon-btn{
  width:38px;height:38px;border-radius:11px;display:grid;place-items:center;
  color:var(--tx-2);transition:.16s var(--ease);position:relative;flex:none;
}
.icon-btn:hover{background:var(--panel-2);color:var(--tx)}
.icon-btn:active{transform:scale(.93)}
.icon-btn.active{color:var(--a2)}
.icon-btn.active::after{content:'';position:absolute;bottom:5px;width:4px;height:4px;border-radius:50%;background:var(--a2)}

/* ---------- view head ---------- */
.view-head{display:flex;align-items:flex-end;justify-content:space-between;gap:16px;padding:10px 26px 14px;flex:none;flex-wrap:wrap}
.view-head h1{font-size:30px;font-weight:750;letter-spacing:-.9px;line-height:1.15}
.view-head p{font-size:13px;color:var(--tx-3);margin-top:3px}
.view-head-actions{display:flex;gap:8px}
.pill-btn{
  display:inline-flex;align-items:center;gap:7px;height:38px;padding:0 17px;
  border-radius:99px;font-size:13px;font-weight:600;color:var(--tx-2);
  background:var(--panel);border:1px solid var(--stroke);transition:.18s var(--ease);
}
.pill-btn svg{width:15px;height:15px}
.pill-btn:hover{background:var(--panel-2);color:var(--tx);transform:translateY(-1px)}
.pill-btn.primary{background:linear-gradient(135deg,var(--a1),var(--a2));color:#fff;border-color:transparent;
  box-shadow:0 8px 22px -10px color-mix(in oklab,var(--a1) 90%,transparent)}
.pill-btn.primary:hover{filter:brightness(1.08)}
.pill-btn.danger{color:#ff7a92}
.pill-btn.danger:hover{background:rgba(255,77,109,.14);border-color:rgba(255,77,109,.4)}

/* ---------- content ---------- */
.content{flex:1;overflow-y:auto;padding:0 26px 34px;scrollbar-gutter:stable}

/* grid cards */
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(168px,1fr));gap:18px}
.card{
  border-radius:var(--r-lg);padding:11px;cursor:pointer;position:relative;
  background:var(--panel);border:1px solid var(--stroke);
  transition:transform .22s var(--ease),background .22s,border-color .22s,box-shadow .22s;
}
.card:hover{transform:translateY(-5px);background:var(--panel-2);border-color:var(--stroke-2);box-shadow:var(--shadow)}
.card.playing{border-color:color-mix(in oklab,var(--a2) 62%,transparent);
  background:color-mix(in oklab,var(--a2) 9%,var(--panel))}
.card-art{
  position:relative;aspect-ratio:1;border-radius:15px;overflow:hidden;margin-bottom:11px;
  background:linear-gradient(135deg,#20203a,#12121f);display:grid;place-items:center;
}
.card-art img{width:100%;height:100%;object-fit:cover;display:block}
.card-art .ph{width:34%;height:34%;color:rgba(255,255,255,.2)}
.card-play{
  position:absolute;right:9px;bottom:9px;width:42px;height:42px;border-radius:50%;
  display:grid;place-items:center;color:#fff;
  background:linear-gradient(135deg,var(--a1),var(--a2));
  box-shadow:0 10px 24px -8px rgba(0,0,0,.85);
  opacity:0;transform:translateY(9px) scale(.86);transition:.24s var(--ease);
}
.card:hover .card-play,.card.playing .card-play{opacity:1;transform:none}
.card-play svg{width:17px;height:17px;margin-left:2px}
.card-play.pause svg{margin-left:0}
.card-title{font-size:13.5px;font-weight:600;line-height:1.3;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.card-sub{font-size:11.5px;color:var(--tx-3);margin-top:2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.card-badge{position:absolute;top:9px;left:9px;padding:3px 8px;border-radius:99px;font-size:10px;font-weight:700;
  background:rgba(0,0,0,.6);backdrop-filter:blur(8px);letter-spacing:.05em}
.card-fav{position:absolute;top:8px;right:8px;width:30px;height:30px;border-radius:50%;display:grid;place-items:center;
  background:rgba(0,0,0,.5);backdrop-filter:blur(8px);opacity:0;transition:.2s var(--ease)}
.card:hover .card-fav,.card-fav.on{opacity:1}
.card-fav svg{width:15px;height:15px}
.card-fav.on svg{fill:var(--a3);stroke:var(--a3)}

/* list rows */
.list{display:flex;flex-direction:column;gap:2px}
.list-head{
  display:grid;grid-template-columns:34px 1fr 22% 18% 62px 66px;gap:14px;align-items:center;
  padding:0 13px 9px;font-size:10.5px;text-transform:uppercase;letter-spacing:.1em;color:var(--tx-3);
  border-bottom:1px solid var(--stroke);margin-bottom:7px;font-weight:600;
}
.row{
  display:grid;grid-template-columns:34px 1fr 22% 18% 62px 66px;gap:14px;align-items:center;
  padding:8px 13px;border-radius:13px;cursor:pointer;transition:background .15s;position:relative;
}
.row:hover{background:var(--panel)}
.row.playing{background:color-mix(in oklab,var(--a2) 11%,transparent)}
.row.playing .row-title{color:var(--a2)}
.row-idx{font-size:12px;color:var(--tx-3);text-align:center;font-variant-numeric:tabular-nums}
.row:hover .row-idx-n{display:none}
.row-idx-play{display:none;width:15px;height:15px;margin:0 auto;color:var(--tx)}
.row:hover .row-idx-play{display:block}
.row.playing .row-idx-n{display:none}
.row.playing .row-bars{display:flex}
.row-bars{display:none;gap:2px;align-items:flex-end;height:14px;justify-content:center}
.row-bars i{width:2.5px;background:var(--a2);border-radius:2px;height:5px;animation:eq .95s var(--ease) infinite}
.row-bars i:nth-child(2){animation-delay:.18s}.row-bars i:nth-child(3){animation-delay:.36s}
body.paused .row-bars i{animation-play-state:paused}
.row-main{display:flex;align-items:center;gap:11px;min-width:0}
.row-art{width:40px;height:40px;border-radius:9px;object-fit:cover;flex:none;background:linear-gradient(135deg,#20203a,#12121f)}
.row-txt{min-width:0;display:flex;flex-direction:column}
.row-title{font-size:13.5px;font-weight:550;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.row-artist-sm{font-size:11.5px;color:var(--tx-3);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:none}
.row-cell{font-size:12.5px;color:var(--tx-2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.row-dur{font-size:12px;color:var(--tx-3);text-align:right;font-variant-numeric:tabular-nums}
.row-actions{display:flex;align-items:center;justify-content:flex-end;gap:2px}
.row-actions .icon-btn{width:30px;height:30px;opacity:0}
.row:hover .row-actions .icon-btn,.row-actions .icon-btn.on{opacity:1}
.row-actions svg{width:15px;height:15px}
.row-actions .on svg{fill:var(--a3);stroke:var(--a3)}

/* section groups */
.group-title{font-size:16px;font-weight:650;margin:26px 0 13px;letter-spacing:-.3px;display:flex;align-items:center;gap:9px}
.group-title:first-child{margin-top:4px}
.group-title small{font-size:11.5px;color:var(--tx-3);font-weight:500}

/* empty state */
.empty{display:flex;flex-direction:column;align-items:center;justify-content:center;
  text-align:center;padding:64px 20px;gap:14px;color:var(--tx-3);min-height:44vh}
.empty svg{width:52px;height:52px;opacity:.35;stroke-width:1.4}
.empty h3{font-size:19px;color:var(--tx-2);font-weight:600}
.empty p{font-size:13.5px;max-width:380px;line-height:1.6}

/* dropzone */
.dropzone{
  position:absolute;inset:0;z-index:40;display:none;place-items:center;padding:24px;
  background:color-mix(in oklab,var(--bg) 76%,transparent);backdrop-filter:blur(14px);
}
.dropzone.on{display:grid}
.dropzone-inner{
  border:2.5px dashed color-mix(in oklab,var(--a2) 65%,transparent);border-radius:var(--r-xl);
  padding:56px 68px;text-align:center;display:flex;flex-direction:column;align-items:center;gap:12px;
  background:color-mix(in oklab,var(--a2) 8%,transparent);animation:pop .3s var(--ease);
}
@keyframes pop{from{transform:scale(.94);opacity:0}to{transform:none;opacity:1}}
.dropzone-inner svg{width:46px;height:46px;color:var(--a2)}
.dropzone-inner h2{font-size:22px;font-weight:700}
.dropzone-inner p{font-size:13px;color:var(--tx-3);letter-spacing:.04em}

/* ---------- now playing ---------- */
.nowplaying{
  display:flex;flex-direction:column;gap:14px;padding:18px 18px 8px;overflow:hidden;
  border-left:1px solid var(--stroke);
  background:linear-gradient(180deg,rgba(255,255,255,.05),rgba(255,255,255,.015));
  backdrop-filter:var(--blur);-webkit-backdrop-filter:var(--blur);
}
.app.np-hidden .nowplaying{opacity:0;pointer-events:none;padding-left:0;padding-right:0}
.np-head{display:flex;align-items:center;justify-content:space-between;
  font-size:10.5px;text-transform:uppercase;letter-spacing:.12em;color:var(--tx-3);font-weight:600}
.np-head .icon-btn{width:28px;height:28px}
.np-head .icon-btn svg{width:15px;height:15px}

.art-stage{position:relative;display:grid;place-items:center;aspect-ratio:1;
  width:min(100%,42vh);margin:0 auto;flex:none}
.radial-canvas{position:absolute;inset:0;width:100%;height:100%}
.art-wrap{
  position:relative;width:70%;aspect-ratio:1;border-radius:22px;overflow:hidden;
  box-shadow:0 26px 60px -18px rgba(0,0,0,.9),0 0 0 1px rgba(255,255,255,.09);
  background:linear-gradient(135deg,#20203a,#101019);
  transition:transform .5s var(--ease);
}
body.playing-now .art-wrap{animation:breathe 5.5s ease-in-out infinite}
@keyframes breathe{0%,100%{transform:scale(1)}50%{transform:scale(1.032)}}
.art-wrap img{width:100%;height:100%;object-fit:cover;display:block}
.art-wrap img:not([src]),.art-wrap img[src=""]{display:none}
.art-placeholder{position:absolute;inset:0;display:grid;place-items:center;color:rgba(255,255,255,.16)}
.art-placeholder svg{width:32%;height:32%;stroke-width:1.3}
.art-wrap.has-art .art-placeholder{display:none}

.np-meta{text-align:center;padding:0 4px}
.np-meta h2{font-size:19px;font-weight:700;letter-spacing:-.4px;line-height:1.28;
  overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
.np-meta p{font-size:13px;color:var(--tx-2);margin-top:4px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.np-album{font-size:11.5px !important;color:var(--tx-3) !important;margin-top:2px !important}

.viz-shell{position:relative;flex:1;min-height:92px;border-radius:var(--r);overflow:hidden;
  background:rgba(0,0,0,.28);border:1px solid var(--stroke)}
.viz-shell canvas{width:100%;height:100%;display:block}
.viz-modes{position:absolute;top:7px;right:7px;display:flex;gap:3px;
  background:rgba(0,0,0,.5);backdrop-filter:blur(10px);padding:3px;border-radius:9px;opacity:0;transition:.2s}
.viz-shell:hover .viz-modes{opacity:1}
.viz-modes button{font-size:10px;padding:4px 8px;border-radius:6px;color:var(--tx-3);font-weight:600;transition:.15s}
.viz-modes button:hover{color:var(--tx)}
.viz-modes button.active{background:linear-gradient(135deg,var(--a1),var(--a2));color:#fff}

.np-extras{display:flex;flex-direction:column;gap:7px;padding-bottom:8px;flex:none}
.extra-row{display:flex;align-items:center;gap:10px;font-size:11.5px;color:var(--tx-3)}
.extra-row label{flex:none;width:82px}
.extra-row input[type=range]{flex:1}
.extra-row output{width:44px;text-align:right;font-variant-numeric:tabular-nums;color:var(--tx-2)}
.switch{position:relative;width:34px;height:19px;flex:none;margin-left:auto;cursor:pointer}
.switch input{position:absolute;opacity:0;width:0;height:0}
.switch span{position:absolute;inset:0;border-radius:99px;background:var(--panel-2);
  border:1px solid var(--stroke);transition:.2s var(--ease)}
.switch span::after{content:'';position:absolute;top:2px;left:2px;width:13px;height:13px;border-radius:50%;
  background:var(--tx-3);transition:.22s var(--ease)}
.switch input:checked+span{background:linear-gradient(135deg,var(--a1),var(--a2));border-color:transparent}
.switch input:checked+span::after{transform:translateX(15px);background:#fff}

/* ---------- range inputs ---------- */
input[type=range]{
  -webkit-appearance:none;appearance:none;background:none;height:16px;cursor:pointer;outline:none;
}
input[type=range]::-webkit-slider-runnable-track{height:4px;border-radius:4px;
  background:linear-gradient(90deg,var(--a2) var(--p,0%),rgba(255,255,255,.14) var(--p,0%))}
input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;width:12px;height:12px;border-radius:50%;
  background:#fff;margin-top:-4px;box-shadow:0 2px 8px rgba(0,0,0,.6);transition:transform .15s var(--ease)}
input[type=range]:hover::-webkit-slider-thumb{transform:scale(1.25)}
input[type=range]::-moz-range-track{height:4px;border-radius:4px;background:rgba(255,255,255,.14)}
input[type=range]::-moz-range-progress{height:4px;border-radius:4px;background:var(--a2)}
input[type=range]::-moz-range-thumb{width:12px;height:12px;border:none;border-radius:50%;background:#fff}

/* ---------- player bar ---------- */
.playerbar{
  position:fixed;left:0;right:0;bottom:0;height:var(--bar-h);z-index:60;
  display:grid;grid-template-columns:minmax(190px,1fr) minmax(420px,2.9fr) minmax(120px,0.7fr);
  align-items:center;gap:18px;padding:0 20px;
  background:color-mix(in oklab,var(--bg-2) 82%,transparent);
  backdrop-filter:var(--blur);-webkit-backdrop-filter:var(--blur);
  border-top:1px solid var(--stroke);
}
.pb-track{display:flex;align-items:center;gap:12px;min-width:0}
.pb-art{width:54px;height:54px;border-radius:12px;overflow:hidden;flex:none;position:relative;
  background:linear-gradient(135deg,#20203a,#12121f);display:grid;place-items:center;
  box-shadow:0 8px 20px -8px rgba(0,0,0,.8)}
.pb-art img{width:100%;height:100%;object-fit:cover;display:block}
.pb-art-fallback{font-size:19px;color:rgba(255,255,255,.2)}
.pb-info{min-width:0;display:flex;flex-direction:column;gap:2px}
.pb-info strong{font-size:13.5px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pb-info span{font-size:11.5px;color:var(--tx-3);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.heart.on svg{fill:var(--a3);stroke:var(--a3)}
.heart.on{color:var(--a3)}

.pb-center{display:flex;flex-direction:column;gap:5px;min-width:0}
.controls{display:flex;align-items:center;justify-content:center;gap:3px}
.seek-btn b{position:absolute;font-size:7.5px;font-weight:800;letter-spacing:-.3px;
  top:50%;left:50%;transform:translate(-50%,-38%)}
.play-btn{
  width:44px;height:44px;border-radius:50%;display:grid;place-items:center;margin:0 6px;flex:none;
  background:linear-gradient(135deg,var(--a1),var(--a2));color:#fff;
  box-shadow:0 8px 22px -8px color-mix(in oklab,var(--a1) 95%,transparent);
  transition:transform .16s var(--ease),filter .16s;
}
.play-btn svg{width:19px;height:19px;margin-left:2px}
.play-btn.is-playing svg{margin-left:0}
.play-btn:hover{transform:scale(1.07);filter:brightness(1.1)}
.play-btn:active{transform:scale(.95)}
#repeatBtn b{position:absolute;bottom:4px;right:6px;font-size:8.5px;font-weight:800;color:var(--a2)}

.scrub{display:flex;align-items:center;gap:11px}
.time{font-size:11px;color:var(--tx-3);font-variant-numeric:tabular-nums;width:42px;flex:none}
.time:last-child{text-align:right}
/* touch-action:none — without it a touch drag scrolls the page instead of
   reaching the pointermove handler that scrubs. */
.track-wrap{flex:1;position:relative;padding:9px 0;cursor:pointer;min-width:0;touch-action:none}
.track{position:relative;height:5px;border-radius:5px;background:rgba(255,255,255,.12);overflow:visible}
.buffered{position:absolute;left:0;top:0;height:100%;border-radius:5px;background:rgba(255,255,255,.09);width:0}
.played{position:absolute;left:0;top:0;height:100%;border-radius:5px;width:0;
  background:linear-gradient(90deg,var(--a1),var(--a2))}
.played .knob{
  position:absolute;right:-6.5px;top:50%;transform:translateY(-50%) scale(0);
  width:13px;height:13px;border-radius:50%;background:#fff;
  box-shadow:0 2px 9px rgba(0,0,0,.7);transition:transform .18s var(--ease);
}
.track-wrap:hover .knob,.track-wrap.dragging .knob{transform:translateY(-50%) scale(1)}
.ab-region{
  position:absolute;top:-2px;height:9px;border-radius:3px;
  background:color-mix(in oklab,var(--a3) 34%,transparent);
  border-left:2px solid var(--a3);border-right:2px solid var(--a3);
  pointer-events:none;transition:opacity .2s;
}
.ab-region.looping{animation:abpulse 1.7s ease-in-out infinite}
@keyframes abpulse{0%,100%{background:color-mix(in oklab,var(--a3) 30%,transparent)}
                   50%{background:color-mix(in oklab,var(--a3) 55%,transparent)}}
.marker{position:absolute;top:-13px;transform:translateX(-50%);cursor:grab;z-index:3;touch-action:none}
.marker:active{cursor:grabbing}
.marker i{display:block;width:2px;height:24px;background:var(--a3);margin:0 auto;border-radius:2px;
  box-shadow:0 0 8px color-mix(in oklab,var(--a3) 80%,transparent)}
.marker label{position:absolute;top:-15px;left:50%;transform:translateX(-50%);
  font-size:9px;font-weight:800;padding:1.5px 5px;border-radius:5px;background:var(--a3);color:#fff;
  letter-spacing:.05em;pointer-events:none}
.marker-b i,.marker-b label{background:var(--a2)}
.marker-b i{box-shadow:0 0 8px color-mix(in oklab,var(--a2) 80%,transparent)}
.hover-time{position:absolute;bottom:26px;transform:translateX(-50%);z-index:5;
  font-size:10.5px;padding:3px 7px;border-radius:6px;background:#000c;backdrop-filter:blur(8px);
  font-variant-numeric:tabular-nums;pointer-events:none;white-space:nowrap}

.pb-right{display:flex;align-items:center;justify-content:flex-end;gap:12px}

/* ---------- A-B controls ----------
   These live on the scrub row, immediately right of the timeline, because
   A and B are positions ON that timeline. Each chip is tinted to match its
   marker (A pink, B cyan) so the link reads at a glance. */
.scrub-row{display:flex;align-items:center;gap:14px;min-width:0}
.scrub-row .scrub{flex:1;min-width:0}
.ab-box{display:flex;align-items:center;gap:3px;flex:none}

.ab-btn{
  display:flex;align-items:center;gap:6px;height:28px;padding:0 9px 0 7px;
  border-radius:99px;transition:.16s var(--ease);
  border:1px solid transparent;background:var(--panel);
}
.ab-key{
  width:16px;height:16px;border-radius:50%;flex:none;display:grid;place-items:center;
  font-size:9.5px;font-weight:800;line-height:1;
  background:rgba(255,255,255,.13);color:var(--tx-2);transition:.16s var(--ease);
}
.ab-btn small{
  font-size:10px;color:var(--tx-3);font-variant-numeric:tabular-nums;
  letter-spacing:-.1px;transition:color .16s;
}
.ab-btn:hover{background:var(--panel-2);border-color:var(--stroke-2)}
.ab-btn:hover small{color:var(--tx-2)}

/* set state — chip adopts its marker's colour */
.ab-btn.a.set{background:color-mix(in oklab,var(--a3) 15%,transparent);
  border-color:color-mix(in oklab,var(--a3) 45%,transparent)}
.ab-btn.a.set .ab-key{background:var(--a3);color:#fff}
.ab-btn.a.set small{color:color-mix(in oklab,var(--a3) 55%,white)}
.ab-btn.b.set{background:color-mix(in oklab,var(--a2) 15%,transparent);
  border-color:color-mix(in oklab,var(--a2) 45%,transparent)}
.ab-btn.b.set .ab-key{background:var(--a2);color:#04121a}
.ab-btn.b.set small{color:color-mix(in oklab,var(--a2) 55%,white)}

.ab-toggle{
  display:flex;align-items:center;gap:5px;height:28px;padding:0 11px;border-radius:99px;
  font-size:11px;font-weight:650;color:var(--tx-3);transition:.18s var(--ease);
  background:var(--panel);border:1px solid transparent;
}
.ab-toggle svg{width:13px;height:13px}
.ab-toggle:disabled{opacity:.4;cursor:not-allowed}
.ab-toggle:not(:disabled):hover{background:var(--panel-2);color:var(--tx);border-color:var(--stroke-2)}
.ab-toggle.on{
  background:linear-gradient(135deg,var(--a3),var(--a1));color:#fff;border-color:transparent;
  box-shadow:0 5px 16px -6px color-mix(in oklab,var(--a3) 90%,transparent);
}
.ab-toggle.on svg{animation:abspin 2.4s linear infinite}
@keyframes abspin{to{transform:rotate(360deg)}}
.ab-more{width:26px;height:26px;border-radius:50%}
.ab-more svg{width:14px;height:14px}

/* hovering a chip flags the matching marker on the timeline */
.playerbar:has(.ab-btn.a:hover) .marker-a i{box-shadow:0 0 0 3px color-mix(in oklab,var(--a3) 40%,transparent)}
.playerbar:has(.ab-btn.b:hover) .marker-b i{box-shadow:0 0 0 3px color-mix(in oklab,var(--a2) 40%,transparent)}

.volume{display:flex;align-items:center;gap:7px}
.volume input[type=range]{width:88px}

/* ---------- A-B panel ---------- */
.ab-panel{
  position:fixed;right:20px;bottom:calc(var(--bar-h) + 16px);z-index:70;width:340px;
  max-width:calc(100vw - 24px);
  /* never run off the top on a short window */
  max-height:calc(100vh - var(--bar-h) - 32px);overflow-y:auto;
  padding:16px;border-radius:var(--r-lg);display:flex;flex-direction:column;gap:12px;
  background:color-mix(in oklab,var(--bg-2) 92%,transparent);
  backdrop-filter:var(--blur);-webkit-backdrop-filter:var(--blur);
  border:1px solid var(--stroke-2);box-shadow:var(--shadow);
  animation:slideUp .26s var(--ease);
}
@keyframes slideUp{from{transform:translateY(14px);opacity:0}to{transform:none;opacity:1}}
.ab-panel-head{display:flex;align-items:center;justify-content:space-between}
.ab-panel-head h3{font-size:15px;font-weight:700;letter-spacing:-.2px}
.ab-panel-head .icon-btn{width:26px;height:26px}
.ab-panel-head svg{width:14px;height:14px}
.ab-fields{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.ab-fields label{font-size:10.5px;color:var(--tx-3);text-transform:uppercase;letter-spacing:.09em;
  font-weight:600;display:flex;flex-direction:column;gap:5px}
.ab-field{display:flex;flex-direction:column;gap:5px}
.ab-field input{
  height:34px;padding:0 9px;border-radius:9px;font-size:12.5px;text-align:center;
  background:var(--panel);border:1px solid var(--stroke);outline:none;
  font-variant-numeric:tabular-nums;transition:.15s;width:100%;
}
.ab-field input:focus{border-color:var(--a2);background:var(--panel-2)}
.nudge{display:grid;grid-template-columns:repeat(4,1fr);gap:3px}
.nudge button{font-size:9.5px;padding:4px 0;border-radius:6px;background:var(--panel);
  color:var(--tx-3);border:1px solid var(--stroke);transition:.14s;font-weight:600}
.nudge button:hover{background:var(--panel-2);color:var(--a2);border-color:var(--stroke-2)}
.ab-row{display:flex;align-items:center;gap:10px;font-size:12px;color:var(--tx-2)}
.ab-row label{flex:none;width:118px;font-size:11.5px;color:var(--tx-3)}
.ab-row input[type=range]{flex:1}
.ab-row .select{height:32px;font-size:11.5px;flex:1}
.ab-row output{width:46px;text-align:right;font-variant-numeric:tabular-nums;font-size:11.5px}
.ab-loops{font-size:11px;color:var(--a3);font-weight:700;white-space:nowrap;font-variant-numeric:tabular-nums}
.ab-panel-actions{display:flex;gap:6px;flex-wrap:wrap}
.ab-panel-actions .pill-btn{height:32px;padding:0 12px;font-size:11.5px;flex:1;justify-content:center}
.ab-hint{font-size:10.5px;color:var(--tx-3);line-height:1.7;text-align:center}
kbd{display:inline-block;padding:1px 5px;border-radius:5px;background:var(--panel-2);
  border:1px solid var(--stroke);font-family:ui-monospace,monospace;font-size:10px;color:var(--tx-2)}

/* ---------- modal ---------- */
.modal-backdrop{
  position:fixed;inset:0;z-index:100;display:grid;place-items:center;padding:22px;
  background:rgba(0,0,0,.62);backdrop-filter:blur(9px);animation:fade .2s var(--ease);
}
@keyframes fade{from{opacity:0}to{opacity:1}}
.modal{
  width:min(500px,100%);max-height:82vh;overflow-y:auto;padding:22px;border-radius:var(--r-lg);
  background:color-mix(in oklab,var(--bg-2) 96%,transparent);
  border:1px solid var(--stroke-2);box-shadow:var(--shadow);animation:slideUp .26s var(--ease);
}
.modal h3{font-size:19px;font-weight:700;margin-bottom:5px;letter-spacing:-.3px}
.modal p.sub{font-size:13px;color:var(--tx-3);margin-bottom:16px}
.modal .field{display:flex;flex-direction:column;gap:6px;margin-bottom:14px}
.modal .field label{font-size:11px;text-transform:uppercase;letter-spacing:.09em;color:var(--tx-3);font-weight:600}
.modal .field input,.modal .field textarea{
  padding:11px 13px;border-radius:11px;background:var(--panel);border:1px solid var(--stroke);
  outline:none;font-size:14px;transition:.16s;resize:vertical;
}
.modal .field input:focus,.modal .field textarea:focus{border-color:var(--a2);background:var(--panel-2)}
.modal-actions{display:flex;gap:8px;justify-content:flex-end;margin-top:6px}
.pick-list{display:flex;flex-direction:column;gap:4px;max-height:320px;overflow-y:auto;margin-bottom:14px}
.pick-item{display:flex;align-items:center;gap:11px;padding:9px 11px;border-radius:11px;
  cursor:pointer;transition:.14s;text-align:left;width:100%}
.pick-item:hover{background:var(--panel)}
.pick-item .pl-dot{width:30px;height:30px;border-radius:9px;flex:none;display:grid;place-items:center;
  background:linear-gradient(135deg,var(--a1),var(--a3));font-size:13px}
.pick-item div{min-width:0;flex:1}
.pick-item strong{display:block;font-size:13.5px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pick-item small{font-size:11px;color:var(--tx-3)}

/* ---------- context menu ---------- */
.ctx-menu{
  position:fixed;z-index:120;min-width:210px;padding:6px;border-radius:14px;
  background:color-mix(in oklab,var(--bg-2) 96%,transparent);
  backdrop-filter:var(--blur);-webkit-backdrop-filter:var(--blur);
  border:1px solid var(--stroke-2);box-shadow:var(--shadow);animation:pop .14s var(--ease);
}
.ctx-item{display:flex;align-items:center;gap:10px;width:100%;padding:9px 11px;border-radius:9px;
  font-size:13px;color:var(--tx-2);text-align:left;transition:.12s}
.ctx-item svg{width:15px;height:15px}
.ctx-item:hover{background:var(--panel-2);color:var(--tx)}
.ctx-item.danger{color:#ff7a92}
.ctx-item.danger:hover{background:rgba(255,77,109,.14)}
.ctx-sep{height:1px;background:var(--stroke);margin:5px 7px}

/* ---------- toasts ---------- */
.toasts{position:fixed;left:50%;transform:translateX(-50%);bottom:calc(var(--bar-h) + 16px);
  z-index:130;display:flex;flex-direction:column;gap:8px;align-items:center;pointer-events:none}
.toast{
  display:flex;align-items:center;gap:9px;padding:10px 16px;border-radius:99px;font-size:13px;font-weight:500;
  background:color-mix(in oklab,var(--bg-2) 94%,transparent);
  backdrop-filter:var(--blur);border:1px solid var(--stroke-2);box-shadow:var(--shadow);
  animation:toastIn .3s var(--ease);white-space:nowrap;
}
.toast svg{width:15px;height:15px;color:var(--a2)}
.toast.out{animation:toastOut .3s var(--ease) forwards}
@keyframes toastIn{from{transform:translateY(14px);opacity:0}to{transform:none;opacity:1}}
@keyframes toastOut{to{transform:translateY(-8px);opacity:0}}

/* ---------- import HUD ---------- */
.import-hud{
  position:fixed;right:20px;bottom:calc(var(--bar-h) + 12px);z-index:110;
  display:flex;align-items:center;gap:13px;padding:13px 17px;border-radius:var(--r);min-width:250px;
  background:color-mix(in oklab,var(--bg-2) 94%,transparent);
  backdrop-filter:var(--blur);border:1px solid var(--stroke-2);box-shadow:var(--shadow);
  animation:slideUp .26s var(--ease);
}
.import-spin{width:24px;height:24px;border-radius:50%;flex:none;
  border:2.5px solid rgba(255,255,255,.13);border-top-color:var(--a2);animation:spin .8s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}
.import-hud strong{display:block;font-size:13px;font-weight:600}
.import-hud small{font-size:11px;color:var(--tx-3)}
.import-bar{height:3px;border-radius:3px;background:var(--panel-2);margin-top:6px;overflow:hidden;width:160px}
.import-bar span{display:block;height:100%;width:0;border-radius:3px;
  background:linear-gradient(90deg,var(--a1),var(--a2));transition:width .25s var(--ease)}

/* ---------- responsive ---------- */
@media (max-width:1180px){
  :root{--np-w:300px}
  .row{grid-template-columns:34px 1fr 24% 62px 66px}
  .list-head{grid-template-columns:34px 1fr 24% 62px 66px}
  .col-album{display:none}
}
@media (max-width:980px){
  .app{grid-template-columns:var(--side-w) minmax(0,1fr)}
  .nowplaying{
    position:fixed;right:0;top:0;bottom:var(--bar-h);width:min(340px,88vw);z-index:70;
    transform:translateX(101%);transition:transform .38s var(--ease);
    background:color-mix(in oklab,var(--bg-2) 96%,transparent);
  }
  .app:not(.np-hidden) .nowplaying{transform:none;opacity:1;pointer-events:auto;padding:18px 18px 8px}
  .app.np-hidden{grid-template-columns:var(--side-w) minmax(0,1fr)}
}
@media (max-width:820px){
  :root{--bar-h:142px}
  .topbar .menu-btn{display:grid}
  .sidebar{position:fixed;left:0;top:0;bottom:0;width:270px;z-index:90;
    transform:translateX(-102%);transition:transform .34s var(--ease);
    background:color-mix(in oklab,var(--bg-2) 97%,transparent)}
  body.side-open .sidebar{transform:none;box-shadow:var(--shadow)}
  body.side-open::after{content:'';position:fixed;inset:0;z-index:85;background:rgba(0,0,0,.55);backdrop-filter:blur(3px)}
  .brand .sidebar-close{display:grid}
  .app{grid-template-columns:minmax(0,1fr)}
  .app.np-hidden{grid-template-columns:minmax(0,1fr)}
  .topbar{padding:12px 16px 8px}
  .view-head{padding:6px 16px 12px}
  .view-head h1{font-size:24px}
  .content{padding:0 16px 30px}
  .grid{grid-template-columns:repeat(auto-fill,minmax(135px,1fr));gap:13px}

  .playerbar{
    grid-template-columns:1fr;grid-template-rows:auto auto;gap:6px;padding:9px 14px 12px;
    align-content:center;
  }
  .pb-track{grid-row:1;order:1}
  .pb-center{grid-row:2;order:3;grid-column:1}
  .pb-right{grid-row:1;order:2;justify-self:end;align-self:center}
  .pb-art{width:44px;height:44px}
  .volume{display:none}
  /* Stack the timeline and its A-B chips so neither gets squeezed. */
  .scrub-row{flex-direction:column;align-items:stretch;gap:4px}
  .ab-box{justify-content:center}
  .ab-btn{height:26px}
  .ab-toggle{height:26px}
  .ab-panel{right:10px;left:10px;width:auto;bottom:calc(var(--bar-h) + 8px)}
  .row{grid-template-columns:30px 1fr 56px 60px;gap:10px}
  .list-head{display:none}
  .col-album,.col-artist{display:none}
  .row-artist-sm{display:block}
  .row-actions .icon-btn{opacity:1}
}
@media (max-width:520px){
  .topbar-actions .select{display:none}
  .seek-btn{display:none}
  .grid{grid-template-columns:repeat(auto-fill,minmax(118px,1fr));gap:11px}
  .view-head-actions{width:100%}
  .view-head-actions .pill-btn{flex:1;justify-content:center}
}
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.01ms !important;animation-iteration-count:1 !important;
    transition-duration:.01ms !important}
}

/* ---------- saved A-B loops ---------- */
.ab-saved{display:flex;flex-direction:column;gap:7px;padding-top:2px;
  border-top:1px solid var(--stroke)}
.ab-saved-head{display:flex;align-items:center;justify-content:space-between;
  font-size:10.5px;text-transform:uppercase;letter-spacing:.09em;color:var(--tx-3);font-weight:600}
.loop-list{display:flex;flex-direction:column;gap:4px;max-height:150px;overflow-y:auto}
.loop-empty{font-size:11px;color:var(--tx-3);line-height:1.5;padding:2px 1px}
.loop-chip{
  display:flex;align-items:center;gap:9px;padding:7px 9px;border-radius:10px;
  background:var(--panel);border:1px solid var(--stroke);cursor:pointer;transition:.15s var(--ease);
}
.loop-chip:hover{background:var(--panel-2);border-color:var(--stroke-2)}
.loop-chip.on{border-color:color-mix(in oklab,var(--a3) 60%,transparent);
  background:color-mix(in oklab,var(--a3) 12%,var(--panel))}
.loop-chip .loop-bar{width:3px;align-self:stretch;border-radius:3px;flex:none;
  background:linear-gradient(var(--a3),var(--a2))}
.loop-chip .loop-txt{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px;text-align:left}
.loop-chip strong{font-size:12px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.loop-chip small{font-size:10px;color:var(--tx-3);font-variant-numeric:tabular-nums}
.loop-chip .loop-acts{display:flex;gap:1px;opacity:0;transition:.15s}
.loop-chip:hover .loop-acts{opacity:1}
.loop-acts button{width:22px;height:22px;border-radius:6px;display:grid;place-items:center;color:var(--tx-3)}
.loop-acts button:hover{background:var(--panel-2);color:var(--tx)}
.loop-acts button.del:hover{color:#ff7a92;background:rgba(255,77,109,.14)}
.loop-acts svg{width:13px;height:13px}

/* saved-loop cards in the library view */
.loop-card{
  display:flex;align-items:center;gap:13px;padding:11px 13px;border-radius:15px;cursor:pointer;
  background:var(--panel);border:1px solid var(--stroke);transition:.18s var(--ease);
}
.loop-card:hover{background:var(--panel-2);border-color:var(--stroke-2);transform:translateY(-2px)}
.loop-card .lc-art{width:48px;height:48px;border-radius:11px;object-fit:cover;flex:none;
  background:linear-gradient(135deg,#20203a,#12121f)}
.loop-card .lc-txt{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
.loop-card strong{font-size:13.5px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.loop-card .lc-track{font-size:11.5px;color:var(--tx-2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.loop-card .lc-range{font-size:11px;color:var(--tx-3);font-variant-numeric:tabular-nums;
  display:flex;align-items:center;gap:6px;flex-wrap:wrap}
.loop-card .lc-tag{padding:1.5px 7px;border-radius:99px;font-size:9.5px;font-weight:700;letter-spacing:.04em;
  background:color-mix(in oklab,var(--a3) 22%,transparent);color:color-mix(in oklab,var(--a3) 60%,white)}
.loop-card .lc-play{width:38px;height:38px;border-radius:50%;flex:none;display:grid;place-items:center;
  color:#fff;background:linear-gradient(135deg,var(--a3),var(--a1));
  box-shadow:0 8px 20px -8px color-mix(in oklab,var(--a3) 90%,transparent)}
.loop-card .lc-play svg{width:15px;height:15px;margin-left:2px}
.loop-card .lc-del{width:32px;height:32px;border-radius:9px;display:grid;place-items:center;
  color:var(--tx-3);opacity:0;transition:.15s}
.loop-card:hover .lc-del{opacity:1}
.loop-card .lc-del:hover{color:#ff7a92;background:rgba(255,77,109,.14)}
.loop-grid{display:flex;flex-direction:column;gap:8px}

/* ---------- Spotify account ---------- */
.sp-box{display:flex;flex-direction:column;gap:7px}
.sp-connect{
  display:flex;align-items:center;justify-content:center;gap:8px;width:100%;
  padding:10px;border-radius:var(--r);font-size:13px;font-weight:600;
  color:#0b1f14;background:linear-gradient(135deg,#1ed760,#12b954);
  transition:.18s var(--ease);
}
.sp-connect:hover{filter:brightness(1.08);transform:translateY(-1px)}
.sp-connect svg{width:17px;height:17px;stroke-width:2}
.sp-account{
  display:flex;align-items:center;gap:9px;padding:8px 10px;border-radius:var(--r);
  background:var(--panel);border:1px solid var(--stroke);
}
.sp-avatar{width:28px;height:28px;border-radius:50%;flex:none;object-fit:cover;
  background:linear-gradient(135deg,#1ed760,#12b954);display:grid;place-items:center;
  font-size:12px;font-weight:700;color:#0b1f14}
.sp-who{flex:1;min-width:0;display:flex;flex-direction:column;line-height:1.25}
.sp-who strong{font-size:12px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.sp-who small{font-size:10px;color:#1ed760;text-transform:uppercase;letter-spacing:.07em;font-weight:600}
.sp-who small.free{color:#ffb020}
.sp-actions{display:flex;gap:5px}
.sp-actions button{
  flex:1;padding:7px;border-radius:9px;font-size:11.5px;font-weight:600;color:var(--tx-2);
  background:var(--panel);border:1px solid var(--stroke);transition:.15s;
}
.sp-actions button:hover{background:var(--panel-2);color:var(--tx)}
.sp-actions button.danger:hover{color:#ff7a92;border-color:rgba(255,77,109,.4);background:rgba(255,77,109,.12)}
.sp-actions button:disabled{opacity:.5;cursor:not-allowed}

/* source badges */
.src-badge{
  display:inline-flex;align-items:center;gap:4px;padding:2px 7px;border-radius:99px;
  font-size:9.5px;font-weight:700;letter-spacing:.04em;flex:none;
  background:rgba(30,215,96,.16);color:#1ed760;
}
.src-badge svg{width:9px;height:9px;stroke-width:2.6}
.card-art .src-badge{position:absolute;bottom:9px;left:9px;
  background:rgba(0,0,0,.62);backdrop-filter:blur(8px)}
.row-title-wrap{display:flex;align-items:center;gap:7px;min-width:0}
.row-title-wrap .row-title{min-width:0}

/* visualizer disclosure for DRM audio */
.viz-sim{cursor:pointer;border:0;font-family:inherit;
  position:absolute;left:8px;bottom:8px;z-index:2;
  padding:3px 9px;border-radius:99px;font-size:9.5px;font-weight:600;letter-spacing:.03em;
  background:rgba(0,0,0,.55);backdrop-filter:blur(8px);color:var(--tx-3);
  pointer-events:none;
}

/* disabled controls when the backend can't support them */
.extra-row.unavailable{opacity:.42}
.extra-row.unavailable input,.extra-row.unavailable .switch{pointer-events:none}
.ab-row.unavailable{opacity:.42}
.ab-row.unavailable input{pointer-events:none}

/* setup instructions in the connect modal */
.sp-steps{display:flex;flex-direction:column;gap:9px;margin-bottom:16px}
.sp-step{display:flex;gap:10px;font-size:12.5px;color:var(--tx-2);line-height:1.55}
.sp-step b{
  flex:none;width:20px;height:20px;border-radius:50%;display:grid;place-items:center;
  background:var(--panel-2);border:1px solid var(--stroke);font-size:10.5px;color:var(--tx)
}
.sp-step code,.sp-note code{
  display:inline-block;padding:1px 6px;border-radius:6px;background:var(--panel-2);
  border:1px solid var(--stroke);font-family:ui-monospace,monospace;font-size:11px;
  word-break:break-all;color:var(--a2);
}
.sp-copy{display:flex;gap:6px;align-items:center;margin-top:4px}
.sp-copy input{
  flex:1;height:32px;padding:0 9px;border-radius:8px;font-size:11px;
  font-family:ui-monospace,monospace;background:var(--panel-2);
  border:1px solid var(--stroke);outline:none;color:var(--a2);
}
.sp-note{font-size:11.5px;color:var(--tx-3);line-height:1.6;
  padding:10px 12px;border-radius:11px;background:var(--panel);border:1px solid var(--stroke)}
.sp-note strong{color:var(--tx-2)}
/* Nothing carries a margin (see the reset), so stacked notes need their own. */
.sp-note + .sp-note,.sp-note + .field,.sp-note + .modal-actions{margin-top:12px}

/* the quieter of the two ways in, under the buttons */
.sp-alt{margin-top:14px;text-align:center;font-size:11.5px;color:var(--tx-3)}
.sp-alt button{
  background:none;border:0;padding:0;font:inherit;cursor:pointer;
  color:var(--a2);text-decoration:underline;text-underline-offset:2px;
}
.sp-alt button:hover{color:var(--tx)}

/* ---------- sidebar sections ---------- */
.section-toggle{
  display:flex;align-items:center;gap:5px;flex:1;
  font-size:10.5px;text-transform:uppercase;letter-spacing:.11em;
  color:var(--tx-3);font-weight:600;text-align:left;transition:color .15s;
}
.section-toggle:hover{color:var(--tx-2)}
.section-toggle .chev{width:12px;height:12px;stroke-width:2.6;
  transform:rotate(90deg);transition:transform .22s var(--ease)}
.nav.collapsed .section-toggle .chev{transform:rotate(0deg)}
.nav.collapsed .playlist-nav{display:none}

.pl-item .pl-mark{width:14px;height:14px;flex:none;color:#1ed760}
.pl-item .pl-mark svg{width:14px;height:14px}
.pl-item.liked .pl-dot{background:linear-gradient(135deg,#4500a0,#c4efd9)}
.sp-section-empty{font-size:11.5px;color:var(--tx-3);padding:6px 11px;line-height:1.5}

/* ---------- search scope switch ---------- */
.scope{display:flex;gap:2px;padding:2px;border-radius:9px;background:var(--panel-2);flex:none}
.scope button{
  padding:4px 10px;border-radius:7px;font-size:11px;font-weight:600;
  color:var(--tx-3);transition:.15s var(--ease);white-space:nowrap;
}
.scope button:hover{color:var(--tx)}
.scope button.active{background:var(--a1);color:#fff}
.scope button[data-scope="spotify"].active{background:#1ed760;color:#0b1f14}

/* ---------- remote (catalog) results ---------- */
.result-section{margin:22px 0 12px;display:flex;align-items:center;gap:9px}
.result-section:first-child{margin-top:2px}
.result-section h2{font-size:15px;font-weight:650;letter-spacing:-.2px}
.result-section .count{font-size:11.5px;color:var(--tx-3)}
.result-section .rule{flex:1;height:1px;background:var(--stroke)}
.card.remote .card-art,.row.remote .row-art{position:relative}
.row-add,.card-add{
  display:grid;place-items:center;border-radius:50%;color:#0b1f14;
  background:linear-gradient(135deg,#1ed760,#12b954);transition:.16s var(--ease);
}
.row-add{
  width:26px;height:26px;min-width:26px;min-height:26px;
  flex:0 0 26px;aspect-ratio:1;align-self:center;
}
.row-add svg{width:14px;height:14px;stroke-width:2.6}
.card-add{
  position:absolute;top:8px;left:8px;width:28px;height:28px;
  min-width:28px;min-height:28px;flex:none;aspect-ratio:1;opacity:0;
}
.card:hover .card-add{opacity:1}
.card-add svg{width:15px;height:15px;stroke-width:2.6}
.row-add:hover,.card-add:hover{filter:brightness(1.12);transform:scale(1.08)}
.searching{
  display:flex;align-items:center;gap:10px;padding:16px 2px;font-size:13px;color:var(--tx-3);
}
.searching .spin{width:16px;height:16px;border-radius:50%;flex:none;
  border:2px solid rgba(255,255,255,.14);border-top-color:#1ed760;animation:spin .8s linear infinite}
.search-cta{
  display:flex;align-items:center;gap:11px;padding:13px 15px;border-radius:15px;margin-top:14px;
  background:var(--panel);border:1px solid var(--stroke);cursor:pointer;transition:.16s var(--ease);
}
.search-cta:hover{background:var(--panel-2);border-color:#1ed760}
.search-cta svg{width:19px;height:19px;color:#1ed760;flex:none}
.search-cta div{flex:1;min-width:0}
.search-cta strong{display:block;font-size:13.5px;font-weight:600}
.search-cta small{font-size:11.5px;color:var(--tx-3)}

@media (max-width:820px){
  .scope button{padding:4px 7px;font-size:10px}
}

/* playlist rows carry their real artwork once synced */
.pl-item .pl-art{width:20px;height:20px;border-radius:6px;object-fit:cover;flex:none;
  background:linear-gradient(135deg,var(--a1),var(--a3))}
.pl-item.loading{opacity:.6}
.pl-item.loading em{animation:pulse 1.1s ease-in-out infinite}
@keyframes pulse{0%,100%{opacity:.4}50%{opacity:1}}

/* ---------- Now Playing stage tabs ---------- */
.np-tabs{display:flex;gap:3px;padding:3px;border-radius:11px;background:var(--panel);flex:none}
.np-tabs button{
  flex:1;padding:6px 0;border-radius:8px;font-size:11.5px;font-weight:600;
  color:var(--tx-3);transition:.16s var(--ease);
}
.np-tabs button:hover{color:var(--tx)}
.np-tabs button.active{background:linear-gradient(135deg,var(--a1),var(--a2));color:#fff}

/* ---------- dancer ---------- */
.dancer-stage{display:flex;flex-direction:column;gap:10px;flex:none}
/* Match the art stage's footprint; the SVG is taller than it is wide
   (viewBox 100x124) so size it by HEIGHT or squash-and-stretch clips. */
.dancer-mount{
  width:min(100%,42vh);aspect-ratio:1;margin:0 auto;flex:none;
  display:grid;place-items:center;padding:10px;
  border-radius:var(--r-lg);position:relative;overflow:hidden;
  background:radial-gradient(70% 70% at 50% 40%,
    color-mix(in oklab,var(--a1) 20%,transparent), transparent 70%), rgba(0,0,0,.22);
  border:1px solid var(--stroke);
}
.dancer-mount svg{height:100%;width:auto;max-width:100%;overflow:visible}
/* the floor shadow sells the bounce */
.dancer-mount::after{
  content:'';position:absolute;left:50%;bottom:9%;transform:translateX(-50%);
  width:42%;height:9px;border-radius:50%;
  background:radial-gradient(ellipse,rgba(0,0,0,.5),transparent 70%);
}
.av-photo{width:72%;aspect-ratio:1;border-radius:50%;overflow:hidden;
  box-shadow:0 18px 40px -14px rgba(0,0,0,.9),0 0 0 3px color-mix(in oklab,var(--a2) 55%,transparent)}
.av-photo-img{width:100%;height:100%;object-fit:cover;display:block}
.dancer-bar{display:flex;align-items:center;gap:8px;flex:none}
.dancer-bar .pill-btn{height:32px;padding:0 12px;font-size:11.5px;flex:1;justify-content:center}
.bpm-tag{
  font-size:10.5px;font-weight:700;letter-spacing:.05em;padding:6px 10px;border-radius:99px;
  background:var(--panel);border:1px solid var(--stroke);color:var(--tx-3);
  font-variant-numeric:tabular-nums;white-space:nowrap;
}
.bpm-tag.sim{color:#1ed760;border-color:rgba(30,215,96,.3)}

/* avatar picker */
.avatar-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-bottom:14px}
.avatar-tile{
  display:flex;flex-direction:column;align-items:center;gap:6px;padding:12px 6px;
  border-radius:15px;background:var(--panel);border:1px solid var(--stroke);
  cursor:pointer;transition:.16s var(--ease);
}
.avatar-tile:hover{background:var(--panel-2);transform:translateY(-2px)}
.avatar-tile.on{border-color:var(--a2);background:color-mix(in oklab,var(--a2) 12%,var(--panel))}
.avatar-tile svg{width:56px;height:68px}
.avatar-tile span{font-size:11px;font-weight:600;color:var(--tx-2)}

/* ---------- lyrics ---------- */
.lyrics-stage{display:flex;flex-direction:column;gap:8px;flex:1;min-height:0}
.nowplaying.stage-lyrics .viz-shell,
.nowplaying.stage-lyrics .np-extras,
.nowplaying.stage-dancer .np-extras{display:none}
.nowplaying.stage-lyrics .np-meta{padding-bottom:2px}
.lyrics-body{
  flex:1;min-height:0;overflow-y:auto;padding:14px 12px;border-radius:var(--r-lg);
  background:rgba(0,0,0,.22);border:1px solid var(--stroke);scroll-behavior:smooth;
}
.lyric-line{
  display:block;width:100%;text-align:left;padding:6px 9px;border-radius:9px;
  font-size:14px;line-height:1.45;color:var(--tx-3);transition:color .2s,background .2s;
  font-weight:500;
}
.lyrics-body.synced .lyric-line{cursor:pointer}
.lyrics-body.synced .lyric-line:hover{background:var(--panel);color:var(--tx-2)}
.lyric-line.past{color:rgba(242,243,248,.28)}
.lyric-line.active{
  color:var(--tx);font-weight:650;font-size:15.5px;
  background:color-mix(in oklab,var(--a2) 13%,transparent);
}
.lyric-line.blank{height:8px;padding:0}
.lyric-line .loop-line{
  float:right;opacity:0;font-size:9.5px;font-weight:700;letter-spacing:.05em;
  padding:2px 7px;border-radius:99px;background:var(--a3);color:#fff;margin-left:8px;
}
.lyrics-body.synced .lyric-line:hover .loop-line{opacity:1}
.lyrics-plain{white-space:pre-wrap;font-size:14px;line-height:1.75;color:var(--tx-2);padding:2px}
.lyrics-bar{display:flex;align-items:center;gap:8px;flex:none}
.lyrics-src{flex:1;min-width:0;font-size:10.5px;color:var(--tx-3);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.lyrics-acts{display:flex;gap:2px}
.lyrics-acts .icon-btn{width:30px;height:30px}
.lyrics-acts svg{width:15px;height:15px}
.lyrics-empty{
  display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;
  gap:11px;text-align:center;color:var(--tx-3);padding:20px;
}
.lyrics-empty svg{width:34px;height:34px;opacity:.35}
.lyrics-empty p{font-size:12.5px;line-height:1.6;max-width:230px}

/* ---------- karaoke ---------- */
.karaoke{
  position:fixed;inset:0;z-index:200;display:flex;flex-direction:column;
  background:var(--bg);overflow:hidden;animation:fade .28s var(--ease);
}
.k-bg{
  position:absolute;inset:-12%;background-size:cover;background-position:center;
  filter:blur(58px) saturate(150%);transform:scale(1.15);opacity:.5;
  transition:background-image .6s var(--ease);
}
.k-scrim{
  position:absolute;inset:0;
  background:
    radial-gradient(60vw 60vw at 20% 0%,  color-mix(in oklab,var(--a1) 30%,transparent), transparent 60%),
    radial-gradient(60vw 60vw at 85% 100%,color-mix(in oklab,var(--a2) 26%,transparent), transparent 60%),
    linear-gradient(180deg,rgba(4,4,10,.72),rgba(4,4,10,.9));
}
.karaoke > :not(.k-bg):not(.k-scrim){position:relative;z-index:1}

.k-head{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:20px 26px;flex:none}
.k-track{display:flex;align-items:center;gap:13px;min-width:0}
.k-art{width:52px;height:52px;border-radius:13px;flex:none;background-size:cover;background-position:center;
  background-color:rgba(255,255,255,.07);box-shadow:0 10px 26px -10px rgba(0,0,0,.9)}
.k-meta{display:flex;flex-direction:column;min-width:0}
.k-meta strong{font-size:16px;font-weight:700;letter-spacing:-.3px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.k-meta span{font-size:13px;color:var(--tx-2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.k-head-actions{display:flex;gap:6px;flex:none}

/* the lyric stage */
.k-stage{
  flex:1;min-height:0;overflow:hidden;position:relative;
  display:flex;flex-direction:column;align-items:center;justify-content:flex-start;
  padding:0 6vw;
  -webkit-mask-image:linear-gradient(180deg,transparent,#000 16%,#000 84%,transparent);
  mask-image:linear-gradient(180deg,transparent,#000 16%,#000 84%,transparent);
}
.k-lines{
  display:flex;flex-direction:column;align-items:center;gap:clamp(10px,1.6vh,22px);
  width:100%;will-change:transform;transition:transform .42s var(--ease);
}
.k-line{
  font-size:clamp(19px,3vw,40px);line-height:1.22;font-weight:750;letter-spacing:-.5px;
  text-align:center;max-width:22ch;color:#fff;opacity:.14;transform:scale(.94);
  transition:opacity .32s var(--ease),transform .32s var(--ease),color .32s var(--ease);
  cursor:pointer;
}
/* Lines just ahead stay readable so you can see what is coming; anything
   already sung recedes. That gradient of attention is what makes it feel
   like karaoke rather than a list with one row highlighted. */
.k-line.near{opacity:.4;transform:scale(.97)}
.k-line.done{opacity:.1}
.k-line.active{
  opacity:1;color:transparent;transform:scale(1.06);
  font-size:clamp(23px,3.7vw,50px);
  /* the sweep: --p is written every frame from the line's own progress */
  background-image:linear-gradient(90deg,
    var(--a2) 0%, var(--a1) var(--p,0%), rgba(255,255,255,.34) var(--p,0%));
  -webkit-background-clip:text;background-clip:text;
  filter:drop-shadow(0 4px 24px color-mix(in oklab,var(--a1) 45%,transparent));
}
.k-line.blank{height:0;padding:0;margin:0;gap:0}

/* instrumental countdown */
.k-gap{
  position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
  display:flex;gap:11px;justify-content:center;padding:6px 0;pointer-events:none;
}
.k-gap i{width:13px;height:13px;border-radius:50%;background:rgba(255,255,255,.2);
  animation:kdot 1.05s ease-in-out infinite}
.k-gap i:nth-child(2){animation-delay:.16s}
.k-gap i:nth-child(3){animation-delay:.32s}
@keyframes kdot{0%,100%{transform:scale(.7);background:rgba(255,255,255,.18)}
                50%{transform:scale(1.15);background:var(--a2)}}

.k-empty{
  margin:auto;display:flex;flex-direction:column;align-items:center;gap:14px;
  text-align:center;color:var(--tx-3);max-width:420px;
}
.k-empty svg{width:44px;height:44px;opacity:.35}
.k-empty h3{font-size:20px;color:var(--tx-2);font-weight:650}
.k-empty p{font-size:14px;line-height:1.6}
.k-plain{
  margin:auto;max-width:34ch;text-align:center;white-space:pre-wrap;
  font-size:clamp(16px,2vw,24px);line-height:1.9;color:rgba(255,255,255,.6);
  overflow-y:auto;max-height:100%;padding:4vh 0;
}

/* footer transport */
.k-foot{display:flex;flex-direction:column;gap:12px;padding:16px 26px 24px;flex:none}
.k-scrub{display:flex;align-items:center;gap:13px}
.k-track-wrap{flex:1;padding:8px 0;cursor:pointer;touch-action:none}
.k-bar{height:6px;border-radius:6px;background:rgba(255,255,255,.15);overflow:hidden}
.k-played{height:100%;border-radius:6px;width:0;background:linear-gradient(90deg,var(--a1),var(--a2))}
.k-controls{display:flex;align-items:center;justify-content:center;gap:10px;position:relative}
.k-hint{position:absolute;right:0;font-size:11px;color:var(--tx-3)}

@media (max-width:720px){
  .k-head{padding:14px 16px}
  .k-foot{padding:12px 16px 18px}
  .k-line{max-width:16ch}
  .k-hint{display:none}
}

/* ---------- lyric offset control ---------- */
.offset-ctl{
  display:flex;align-items:center;gap:2px;padding:2px;border-radius:99px;flex:none;
  background:var(--panel);border:1px solid var(--stroke);
}
.offset-ctl button{
  width:22px;height:22px;border-radius:50%;display:grid;place-items:center;
  font-size:13px;font-weight:700;line-height:1;color:var(--tx-3);transition:.14s var(--ease);
}
.offset-ctl button:hover{background:var(--panel-2);color:var(--tx)}
.offset-ctl span{
  min-width:52px;text-align:center;font-size:10px;font-weight:650;color:var(--tx-2);
  font-variant-numeric:tabular-nums;letter-spacing:-.2px;
}
.offset-ctl [data-offset-reset]{font-size:11px;color:var(--tx-3)}
.offset-ctl [data-offset-reset]:hover{color:#ff7a92}
.offset-ctl.sm{transform:scale(.92);transform-origin:left center}
.k-controls .offset-ctl{position:absolute;left:0;background:rgba(255,255,255,.06)}
@media (max-width:720px){ .k-controls .offset-ctl{display:none} }

/* ---------- lyric match picker ---------- */
.pick-results{display:flex;flex-direction:column;gap:4px;max-height:320px;overflow-y:auto;margin-bottom:14px}
.pick-empty{font-size:13px;color:var(--tx-3);padding:14px 2px;line-height:1.6}
.pick-row{
  display:flex;align-items:center;gap:11px;width:100%;padding:9px 11px;border-radius:11px;
  text-align:left;transition:.14s var(--ease);border:1px solid transparent;
}
.pick-row:hover{background:var(--panel)}
.pick-row.close{background:color-mix(in oklab,var(--a2) 8%,transparent);
  border-color:color-mix(in oklab,var(--a2) 30%,transparent)}
.pick-row.close:hover{background:color-mix(in oklab,var(--a2) 14%,transparent)}
.pick-delta{
  flex:none;min-width:48px;text-align:center;padding:3px 6px;border-radius:7px;
  font-size:10px;font-weight:700;font-variant-numeric:tabular-nums;letter-spacing:-.2px;
}
.pick-delta.good{background:rgba(30,215,96,.18);color:#1ed760}
.pick-delta.ok{background:rgba(255,176,32,.16);color:#ffb020}
.pick-delta.bad{background:rgba(255,77,109,.14);color:#ff7a92}
.pick-text{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px}
.pick-text strong{font-size:13px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pick-text small{font-size:11px;color:var(--tx-3);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pick-tag{
  flex:none;padding:2px 7px;border-radius:99px;font-size:9px;font-weight:800;letter-spacing:.05em;
  background:color-mix(in oklab,var(--a1) 22%,transparent);color:color-mix(in oklab,var(--a1) 45%,white);
}
.pick-tag.plain{background:rgba(255,255,255,.08);color:var(--tx-3)}

/* ============================================================
   Vinyl deck
   The record spins under a FIXED light source — that is what makes
   it read as a real object. Grooves are concentric, so rotation is
   only legible through the label artwork and the wear streak, which
   is exactly how a real record behaves.
   ============================================================ */
.vinyl-stage{display:flex;flex-direction:column;gap:10px;flex:none}
.nowplaying.stage-vinyl .np-extras{display:none}

.deck{
  position:relative;width:min(100%,42vh);aspect-ratio:1;margin:0 auto;flex:none;
  display:grid;place-items:center;
}
.platter{
  position:relative;width:88%;aspect-ratio:1;border-radius:50%;
  box-shadow:
    0 26px 60px -18px rgba(0,0,0,.95),
    0 0 0 1px rgba(255,255,255,.06),
    inset 0 0 60px rgba(0,0,0,.9);
}

/* --- the record --- */
.disc{
  position:absolute;inset:0;border-radius:50%;
  background:
    radial-gradient(circle at 50% 50%, #17171b 0 33%, #0c0c0f 33.4% 100%),
    #08080a;
  animation:vinyl-spin var(--spin-dur,1.8s) linear infinite;
  will-change:transform;
}
body:not(.playing-now) .disc{animation-play-state:paused}

/* concentric grooves plus one wear streak, so the spin stays visible */
.grooves{
  position:absolute;inset:0;border-radius:50%;
  background:
    repeating-radial-gradient(circle at 50% 50%,
      rgba(255,255,255,.055) 0 1px, rgba(0,0,0,0) 1px 3.5px),
    conic-gradient(from 210deg, rgba(255,255,255,.05) 0deg 8deg, transparent 8deg 360deg);
  -webkit-mask:radial-gradient(circle at 50% 50%, transparent 0 33.5%, #000 34% 99%, transparent 100%);
  mask:radial-gradient(circle at 50% 50%, transparent 0 33.5%, #000 34% 99%, transparent 100%);
  opacity:.85;
}

/* --- label: the album art --- */
.label{
  position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
  width:34%;aspect-ratio:1;border-radius:50%;overflow:hidden;
  display:grid;place-items:center;
  background:linear-gradient(135deg,var(--a1),var(--a3));
  box-shadow:0 0 0 1px rgba(0,0,0,.6), inset 0 0 18px rgba(0,0,0,.35);
}
.label-art{position:absolute;inset:0;background-size:cover;background-position:center}
.label.no-art .label-art{display:none}
.spindle{
  position:relative;width:7%;aspect-ratio:1;min-width:5px;border-radius:50%;
  background:radial-gradient(circle at 40% 35%, #4a4a52, #101014 70%);
  box-shadow:0 0 0 2px rgba(0,0,0,.55), inset 0 1px 1px rgba(255,255,255,.3);
}

/* --- fixed light: deliberately NOT rotated --- */
.sheen{
  position:absolute;inset:0;border-radius:50%;pointer-events:none;
  background:
    conic-gradient(from 0deg,
      transparent 0deg, rgba(255,255,255,.11) 16deg, transparent 40deg,
      transparent 150deg, rgba(255,255,255,.06) 176deg, transparent 205deg,
      transparent 320deg, rgba(255,255,255,.08) 344deg, transparent 360deg);
  mix-blend-mode:screen;opacity:.9;
}
.rim{
  position:absolute;inset:0;border-radius:50%;pointer-events:none;
  background:radial-gradient(circle at 50% 50%, transparent 0 96%,
    rgba(255,255,255,.16) 97%, rgba(0,0,0,.7) 100%);
  box-shadow:inset 0 0 30px rgba(0,0,0,.8);
}

/* --- tonearm ---
   Geometry in % of the square deck (x right, y down):
     pivot (78,12) · arm length 52 · platter centre (50,50) r=44 · label r≈15
   Solving |pivot + 52*u - centre| = r for the head to land on groove r.
   Audio starts inside the rim on a real record, so the sweep opens at 88%
   of the radius rather than right on the edge:
     r=38.7 (first groove) -> -9.2deg     r=17 (run-out) -> +17.5deg
   The arm rotates about the pivot it is actually drawn at; getting those
   two out of step is what put the stylus on the label before. */
.tonearm{position:absolute;inset:0;pointer-events:none}
.pivot{
  position:absolute;left:78%;top:12%;transform:translate(-50%,-50%);
  width:9%;aspect-ratio:1;min-width:16px;border-radius:50%;
  background:radial-gradient(circle at 38% 32%, #6c6c78, #23232a 72%);
  box-shadow:0 3px 9px rgba(0,0,0,.75), inset 0 1px 1px rgba(255,255,255,.35);
}
.arm{
  position:absolute;left:78%;top:12%;
  width:3.2px;height:52%;border-radius:4px;
  transform-origin:top center;
  transform:translateX(-50%) rotate(calc(-9.2deg + var(--track,0) * 26.7deg));
  transition:transform .8s var(--ease);
  background:linear-gradient(180deg,#c6c6d2,#8a8a96 55%,#616170);
  box-shadow:0 2px 8px rgba(0,0,0,.7);
}
.arm::before{                                   /* counterweight */
  content:'';position:absolute;top:-13px;left:50%;transform:translateX(-50%);
  width:11px;height:13px;border-radius:3px;
  background:linear-gradient(180deg,#54545f,#2e2e36);
  box-shadow:0 2px 5px rgba(0,0,0,.6);
}
.head{                                          /* headshell */
  position:absolute;bottom:-11px;left:50%;
  transform:translateX(-50%) rotate(-14deg);
  width:13px;height:17px;border-radius:3px 3px 2px 2px;
  background:linear-gradient(180deg,#eaeaf1,#9a9aa6);
  box-shadow:0 2px 6px rgba(0,0,0,.7);
}
.head::after{                                   /* stylus */
  content:'';position:absolute;bottom:-4px;left:50%;transform:translateX(-50%);
  width:2px;height:5px;border-radius:1px;background:#dcdce6;
}
body:not(.playing-now) .arm{transform:translateX(-50%) rotate(-34deg)}

/* Named apart from the generic `spin` used by the import spinner — sharing
   that name is what made an earlier edit clobber the wrong block. */
@keyframes vinyl-spin{to{transform:rotate(360deg)}}

.deck-bar{display:flex;align-items:center;gap:8px;flex:none}
.deck-hint{
  flex:1;min-width:0;font-size:11px;color:var(--tx-3);text-align:right;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}

@media (prefers-reduced-motion:reduce){ .disc{animation:none} }

/* --- scratching --- */
.disc{cursor:grab;touch-action:none}
.disc.scratching{cursor:grabbing;animation-play-state:paused}
/* the hand drives rotation directly while scratching, so kill the transition */
.disc.scratching{transition:none}
body.is-scratching{user-select:none}
body.is-scratching .tonearm{filter:drop-shadow(0 0 6px color-mix(in oklab,var(--a2) 60%,transparent))}

/* ---------- Connect / output device ---------- */
.connect-btn{position:relative}
.connect-btn.remote{color:#1ed760}
.connect-dot{
  position:absolute;right:6px;bottom:6px;width:6px;height:6px;border-radius:50%;
  background:#1ed760;box-shadow:0 0 0 2px var(--bg-2);
}
.connect-panel{
  position:fixed;right:20px;bottom:calc(var(--bar-h) + 16px);z-index:75;width:320px;
  max-width:calc(100vw - 24px);max-height:calc(100vh - var(--bar-h) - 32px);overflow-y:auto;
  padding:16px;border-radius:var(--r-lg);
  background:color-mix(in oklab,var(--bg-2) 94%,transparent);
  backdrop-filter:var(--blur);-webkit-backdrop-filter:var(--blur);
  border:1px solid var(--stroke-2);box-shadow:var(--shadow);
  animation:slideUp .26s var(--ease);
}
.connect-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:12px}
.connect-head h3{font-size:15px;font-weight:700;letter-spacing:-.2px}
.connect-head .icon-btn{width:26px;height:26px}
.connect-head svg{width:14px;height:14px}

.dev-group{margin-bottom:14px}
.dev-group:last-child{margin-bottom:0}
.dev-label{
  font-size:10px;text-transform:uppercase;letter-spacing:.11em;color:var(--tx-3);
  font-weight:600;margin-bottom:6px;display:flex;align-items:center;gap:6px;
}
.dev-row{
  display:flex;align-items:center;gap:11px;width:100%;padding:9px 10px;border-radius:11px;
  text-align:left;transition:.14s var(--ease);border:1px solid transparent;
}
.dev-row:hover:not(:disabled){background:var(--panel)}
.dev-row:disabled{opacity:.45;cursor:not-allowed}
.dev-row.on{background:color-mix(in oklab,#1ed760 12%,transparent);
  border-color:rgba(30,215,96,.35)}
.dev-row.on .dev-name{color:#1ed760}
.dev-icon{
  width:32px;height:32px;border-radius:9px;flex:none;display:grid;place-items:center;
  background:var(--panel-2);color:var(--tx-2);
}
.dev-row.on .dev-icon{background:rgba(30,215,96,.16);color:#1ed760}
.dev-icon svg{width:16px;height:16px}
.dev-text{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px}
.dev-name{font-size:13px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.dev-sub{font-size:11px;color:var(--tx-3);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.dev-bars{display:flex;gap:2px;align-items:flex-end;height:13px;flex:none}
.dev-bars i{width:2.5px;border-radius:2px;background:#1ed760;height:5px;animation:eq .95s var(--ease) infinite}
.dev-bars i:nth-child(2){animation-delay:.18s}
.dev-bars i:nth-child(3){animation-delay:.36s}
body.paused .dev-bars i{animation-play-state:paused}
.dev-note{
  font-size:11px;color:var(--tx-3);line-height:1.55;padding:9px 11px;border-radius:10px;
  background:var(--panel);border:1px solid var(--stroke);
}
.dev-note strong{color:var(--tx-2)}
.dev-tag{
  font-size:8.5px;font-weight:700;letter-spacing:.06em;padding:2px 6px;border-radius:99px;
  background:var(--panel-2);color:var(--tx-3);text-transform:none;letter-spacing:.02em;
}

/* ---------- Sonos room controls ---------- */
.sonos-room{border-radius:11px;border:1px solid transparent;transition:.15s var(--ease)}
.sonos-room.on{background:color-mix(in oklab,#1ed760 10%,transparent);border-color:rgba(30,215,96,.3)}
.sonos-room .dev-row{width:100%}
.sonos-room.on .dev-name{color:#1ed760}
.sonos-room.on .dev-icon{background:rgba(30,215,96,.16);color:#1ed760}
.room-controls{
  display:flex;align-items:center;gap:5px;padding:0 10px 9px 10px;
}
.room-btn{
  width:26px;height:26px;border-radius:7px;display:grid;place-items:center;flex:none;
  color:var(--tx-3);transition:.14s var(--ease);
}
.room-btn svg{width:13px;height:13px}
.room-btn:hover{background:var(--panel-2);color:var(--tx)}
.room-btn.link{margin-left:2px}
.room-btn.link.on{color:#1ed760;background:rgba(30,215,96,.14)}
.room-vol{flex:1;min-width:40px;height:14px}
.room-vol::-webkit-slider-runnable-track{height:3px;border-radius:3px;background:rgba(255,255,255,.16)}
.room-vol::-webkit-slider-thumb{-webkit-appearance:none;width:10px;height:10px;border-radius:50%;
  background:var(--tx-2);margin-top:-3.5px}
.room-vol-val{
  font-size:9.5px;color:var(--tx-3);font-variant-numeric:tabular-nums;
  width:20px;text-align:right;flex:none;
}
.sonos-room.output{border-color:rgba(30,215,96,.55);background:color-mix(in oklab,#1ed760 14%,transparent)}

/* ---------- YouTube video stage ---------- */
.video-stage{display:flex;flex-direction:column;gap:8px;flex:none}
.nowplaying.stage-video .np-extras{display:none}
.video-frame{
  width:100%;aspect-ratio:16/9;min-height:200px;border-radius:var(--r);overflow:hidden;
  background:#000;border:1px solid var(--stroke);
  box-shadow:0 18px 44px -16px rgba(0,0,0,.9);
}
.video-frame iframe{width:100%;height:100%;display:block;border:0}
.video-note{font-size:10.5px;color:var(--tx-3);line-height:1.5;text-align:center}
.scope button[data-scope="youtube"].active{background:#ff0033;color:#fff}
.src-badge.yt{background:rgba(255,0,51,.16);color:#ff4d6a}

/* ---------- video stage ---------- */
/* The visualiser is simulated for YouTube — there is no Web Audio access to
   a cross-origin iframe — so a fake spectrum under a real video is noise. */
.nowplaying.stage-video .viz-shell{display:none}
.video-bar{display:flex;gap:6px;flex:none}
.video-bar .pill-btn{flex:1;height:32px;padding:0 10px;font-size:11.5px;justify-content:center}
.video-bar .pill-btn svg{width:14px;height:14px}

/* ---------- theater mode ----------
   The iframe is never relocated: moving it in the DOM reloads the player and
   restarts the video. Earlier this promoted the video frame itself, but
   .nowplaying carries a backdrop-filter, which makes it the containing block
   for any fixed descendant — so the frame sized against a 340px panel and
   collapsed. Expanding the PANEL instead sidesteps that entirely: it is a
   direct child of .app, which creates no containing block, and the iframe
   just goes along for the ride. */
body.theater .nowplaying{
  position:fixed;inset:0;z-index:160;width:auto;max-width:none;
  backdrop-filter:none;-webkit-backdrop-filter:none;
  background:transparent;border:0;padding:0;opacity:1;transform:none;
  pointer-events:none;                     /* chrome below handles input */
}
/* Everything except the video steps aside. */
body.theater .np-head,
body.theater .np-tabs,
body.theater .np-meta,
body.theater .viz-shell,
body.theater .np-extras,
body.theater .art-stage,
body.theater .vinyl-stage,
body.theater .dancer-stage,
body.theater .lyrics-stage,
body.theater .video-bar{display:none !important}

/* Outranks [hidden]{display:none !important} so the tab selection cannot
   leave theater staring at nothing. */
body.theater .video-stage{
  display:block !important;
  position:absolute;top:64px;left:3vw;right:3vw;bottom:118px;
  pointer-events:auto;
  transition:right .28s var(--ease), bottom .28s var(--ease);
}
body.theater.theater-lyrics .video-stage{right:calc(3vw + 340px)}

body.theater .video-frame{
  width:100%;height:100%;aspect-ratio:auto;min-height:0;
  border-radius:14px;border-color:transparent;
  box-shadow:0 40px 120px -30px rgba(0,0,0,.95);
}

/* Backdrop lives on the body so it shares the root stacking context and can
   sit BELOW the app, rather than on top of it. */
body.theater::before{
  content:'';position:fixed;inset:0;z-index:100;pointer-events:none;
  background:radial-gradient(70vw 70vw at 50% 0%,
    color-mix(in oklab,var(--a1) 22%,transparent), transparent 60%), #05050a;
}
/* Lift the app (and with it the video) above that backdrop. */
body.theater .app{z-index:150}
/* ...but only the video should be up there. Everything else in the app
   would otherwise sit on top of the backdrop and bleed around the frame. */
body.theater .sidebar,
body.theater .main,
body.theater .playerbar{display:none}

/* Controls ride above the video; the layer itself is inert so clicks reach
   the player in the middle. */
.theater-chrome{position:fixed;inset:0;z-index:200;pointer-events:none}
.theater-chrome > *{pointer-events:auto}
.theater-scrim{display:none}
.theater-chrome > :not(.theater-scrim){z-index:1}

.theater-head{
  position:absolute;top:0;left:0;right:0;height:64px;
  display:flex;align-items:center;justify-content:space-between;gap:16px;padding:0 22px;
}
.theater-meta{display:flex;flex-direction:column;min-width:0;line-height:1.25}
.theater-meta strong{font-size:15px;font-weight:700;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.theater-meta span{font-size:12.5px;color:var(--tx-2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.theater-actions{display:flex;gap:5px;flex:none}

.theater-side{
  position:absolute;top:64px;bottom:118px;right:3vw;width:320px;z-index:161;
  border-radius:14px;overflow:hidden;
  background:rgba(255,255,255,.045);border:1px solid var(--stroke);
  backdrop-filter:var(--blur);
}
.tl-body{height:100%;overflow-y:auto;padding:14px 12px;scroll-behavior:smooth}
.tl-line{
  display:block;width:100%;text-align:left;padding:6px 9px;border-radius:9px;
  font-size:14px;line-height:1.45;color:var(--tx-3);font-weight:500;cursor:pointer;
  transition:color .2s,background .2s;
}
.tl-line:hover{background:var(--panel);color:var(--tx-2)}
.tl-line.past{color:rgba(242,243,248,.26)}
.tl-line.active{color:var(--tx);font-weight:650;background:color-mix(in oklab,var(--a2) 14%,transparent)}
.tl-empty{font-size:12.5px;color:var(--tx-3);text-align:center;padding:20px;line-height:1.6}

.theater-foot{
  position:absolute;left:0;right:0;bottom:0;height:118px;
  display:flex;flex-direction:column;gap:10px;justify-content:center;padding:0 22px;
}
.theater-scrub{display:flex;align-items:center;gap:12px}
.theater-track{flex:1;height:6px;border-radius:6px;background:rgba(255,255,255,.15);cursor:pointer;touch-action:none}
.theater-played{height:100%;border-radius:6px;width:0;background:linear-gradient(90deg,var(--a1),var(--a2))}
.theater-controls{display:flex;align-items:center;justify-content:center;gap:12px}

@media (max-width:900px){
  body.theater .video-stage{left:0;right:0;top:56px;bottom:104px}
  body.theater .video-frame{border-radius:0}
  body.theater.theater-lyrics .video-stage{right:0;bottom:calc(104px + 34vh)}
  .theater-side{right:0;left:0;width:auto;top:auto;height:34vh;bottom:104px;border-radius:14px 14px 0 0}
}
/* the output name, shown in the bar whenever sound is leaving another device */
.output-label{
  font-size:11px;font-weight:600;color:#1ed760;max-width:120px;flex:none;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-left:-4px;
}
@media (max-width:1180px){ .output-label{max-width:80px} }
@media (max-width:820px){ .output-label{display:none} }

/* ---------- resizable Now Playing panel ---------- */
.np-resizer{
  position:absolute;left:0;top:0;bottom:0;width:9px;margin-left:-4px;
  cursor:col-resize;z-index:5;touch-action:none;
}
.np-resizer::after{
  content:'';position:absolute;left:3px;top:0;bottom:0;width:2px;
  background:transparent;transition:background .15s;
}
.np-resizer:hover::after{background:color-mix(in oklab,var(--a2) 65%,transparent)}
body.resizing{cursor:col-resize;user-select:none}
body.resizing .np-resizer::after{background:var(--a2)}
/* the grid animates its columns, which fights a drag */
body.resizing .app{transition:none}
/* Only the docked panel needs a positioning context for the resizer. Applying
   it unconditionally beat the drawer rules below and pinned the panel back
   into the flow on every narrow screen. */
@media (min-width:981px){ .nowplaying{position:relative} }
@media (max-width:980px){ .np-resizer{display:none} }

/* the real-audio switch is an icon now, not a label */
.viz-sim{
  display:grid;place-items:center;width:26px;height:26px;padding:0;border-radius:50%;
  background:rgba(0,0,0,.55);color:var(--tx-2);
}
.viz-sim svg{width:14px;height:14px}
.viz-sim:hover{background:var(--a2);color:#04121a}
/* the contextual scope reads as "where you are", not another catalogue */
.scope button[data-scope="view"]{max-width:130px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.scope button[data-scope="view"].active{background:var(--panel-2);color:var(--tx);
  box-shadow:inset 0 0 0 1px var(--stroke-2)}

/* ============================================================
   MOBILE
   The phone layout is not the desktop layout squeezed down. Two
   things change shape:

   1. Now Playing stops being a column and becomes a sheet that
      slides up over the library, the way every phone player works.
   2. The player bar has two states. Closed, it is a mini bar —
      artwork, title, play — with the progress as a hairline on its
      top edge. Open, it grows into the full transport for the sheet
      above it, A-B chips and all.

   --bar-h drives the bar height, the sheet's bottom edge and every
   panel that floats above the bar, so switching states moves all of
   them together.
   ============================================================ */

:root{
  --sa-t:env(safe-area-inset-top,0px);
  --sa-r:env(safe-area-inset-right,0px);
  --sa-b:env(safe-area-inset-bottom,0px);
  --sa-l:env(safe-area-inset-left,0px);
  /* Height of the bottom tab bar. Zero everywhere it is not shown, so the
     player bar, the sheet and every floating panel can all offset by it
     unconditionally. */
  --tab-h:0px;
}

/* ---------- bottom tab bar (phones) ---------- */
.tabbar{
  display:none;
  position:fixed;left:0;right:0;bottom:0;z-index:65;
  height:var(--tab-h);padding-bottom:var(--sa-b);
  grid-template-columns:repeat(4,1fr);align-items:center;
  background:color-mix(in oklab,var(--bg-2) 94%,transparent);
  backdrop-filter:var(--blur);-webkit-backdrop-filter:var(--blur);
  border-top:1px solid var(--stroke);
}
.tabbar button{
  height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:4px;color:var(--tx-3);transition:color .16s var(--ease);
}
.tabbar svg{width:21px;height:21px;transition:transform .16s var(--ease)}
.tabbar span{font-size:10.5px;font-weight:600;letter-spacing:.01em;line-height:1}
.tabbar button.active{color:var(--tx)}
.tabbar button.active svg{color:var(--a2)}
.tabbar button:active svg{transform:scale(.9)}

/* ---------- Library hub ---------- */
.browse-list{display:flex;flex-direction:column;gap:6px}
.browse-row{
  display:flex;align-items:center;gap:14px;width:100%;padding:14px 14px;
  border-radius:var(--r);text-align:left;transition:.15s var(--ease);
  background:var(--panel);border:1px solid var(--stroke);
}
.browse-row:hover{background:var(--panel-2);border-color:var(--stroke-2)}
.browse-row:active{background:var(--panel-2)}
.browse-icon{
  width:42px;height:42px;border-radius:13px;flex:none;display:grid;place-items:center;
  background:linear-gradient(135deg,color-mix(in oklab,var(--a1) 30%,transparent),
                                    color-mix(in oklab,var(--a2) 22%,transparent));
  color:var(--tx);
}
.browse-icon svg{width:20px;height:20px}
.browse-txt{display:flex;flex-direction:column;min-width:0;flex:1;gap:2px}
.browse-txt strong{font-size:15px;font-weight:600;letter-spacing:-.2px}
.browse-txt small{font-size:12px;color:var(--tx-3)}
.browse-row em{
  font-size:12.5px;font-style:normal;color:var(--tx-3);font-variant-numeric:tabular-nums;
}
.browse-chev{width:17px;height:17px;color:var(--tx-3);flex:none}

/* Once the sidebar and the panel are both eating into a narrower window, the
   search pill's min-content width — the input plus four scope chips — is wider
   than the room left for it, and it pushes the sort/view/panel buttons off the
   right edge. min-width:0 lets it shrink; the chips scroll instead. Must stay
   above the 820 block, which overrides these to wrap the chips onto their own
   line once even a scrolling strip is too tight. */
@media (max-width:980px){
  .search{min-width:0}
  .scope{min-width:0;overflow-x:auto;scrollbar-width:none}
  .scope::-webkit-scrollbar{display:none}
}

@media (max-width:820px){
  /* The tab bar sits on the floor and carries the safe-area inset; the player
     bar stacks on top of it. When the sheet is up you are in the player, not
     browsing, so the tab bar leaves and the bar takes the inset back. */
  body{--tab-h:calc(60px + var(--sa-b));--bar-h:66px}
  body.np-open{--tab-h:0px;--bar-h:calc(166px + var(--sa-b))}
  .tabbar{display:grid}
  body.np-open .tabbar{display:none}

  .app{padding-bottom:calc(var(--bar-h) + var(--tab-h))}
  .playerbar{bottom:var(--tab-h)}
  /* .app carried z-index:1, which made it a stacking context and trapped the
     fixed sidebar inside it — so the drawer painted *under* the scrim meant
     to dim the page behind it, and under the player bar. It only needs to
     out-paint .ambient, and being a later positioned sibling already does
     that; dropping to auto lets drawer, scrim and bar order themselves. */
  .app{z-index:auto}

  /* ---- topbar ---- */
  .topbar{
    gap:8px;
    padding:calc(10px + var(--sa-t)) calc(14px + var(--sa-r)) 6px calc(14px + var(--sa-l));
  }
  /* min-width:0 lets the pill shrink; without it the scope chips set a
     min-content floor that shoved the action buttons off-screen. */
  .search{
    min-width:0;height:auto;min-height:44px;flex-wrap:wrap;
    padding:0 10px 0 12px;row-gap:0;
  }
  /* 16px is the threshold under which iOS zooms the page on focus. */
  .search input{font-size:16px;height:40px}
  /* The scope chips double the height of the pill, and most of the time you
     are not searching — so they unfold only once the field has focus or a
     query in it. That is also what the Search tab does: put focus here. */
  .scope{
    display:none;
    flex:1 1 100%;order:5;overflow-x:auto;scrollbar-width:none;
    justify-content:flex-start;
  }
  .search:focus-within .scope,
  .search:not(:has(#searchInput:placeholder-shown)) .scope{display:flex}
  .search:focus-within,
  .search:not(:has(#searchInput:placeholder-shown)){padding-bottom:7px}
  .scope::-webkit-scrollbar{display:none}
  .scope button{padding:6px 11px;font-size:11px;flex:none}
  .topbar-actions{gap:4px}

  /* ---- shell ---- */
  .view-head{padding:4px calc(16px + var(--sa-r)) 12px calc(16px + var(--sa-l))}
  .content{
    padding:0 calc(16px + var(--sa-r)) 26px calc(16px + var(--sa-l));
    scrollbar-gutter:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch;
  }
  .sidebar{
    width:min(300px,86vw);
    padding:calc(20px + var(--sa-t)) 14px calc(20px + var(--sa-b));
    padding-left:calc(14px + var(--sa-l));
    overscroll-behavior:contain;
  }
  /* The tab bar owns navigation now: Home and Library reach every view the
     Browse list held, so the drawer behind More is what is left — adding
     music, connecting Spotify, playlists, storage. */
  .topbar .menu-btn{display:none}
  #browseSection{display:none}

  /* ---- Now Playing as a bottom sheet ---- */
  .nowplaying{
    position:fixed;left:0;right:0;top:0;bottom:calc(var(--bar-h) + var(--tab-h));
    width:auto;z-index:70;
    padding:6px calc(16px + var(--sa-r)) 10px calc(16px + var(--sa-l));
    gap:10px;
    border-left:none;
    border-radius:0;
    background:color-mix(in oklab,var(--bg-2) 97%,transparent);
    transform:translateY(100%);
    transition:transform .34s var(--ease),opacity .2s var(--ease);
    /* The base panel clips its overflow, which on a short screen quietly
       cuts the title and the speed controls off the bottom. Let it scroll. */
    overflow-y:auto;
    overscroll-behavior:contain;
  }
  .app.np-hidden .nowplaying{
    transform:translateY(100%);opacity:0;pointer-events:none;
    padding-left:calc(16px + var(--sa-l));padding-right:calc(16px + var(--sa-r));
  }
  .app:not(.np-hidden) .nowplaying{transform:none;opacity:1;pointer-events:auto}

  /* a grab bar, so the sheet reads as dismissable */
  .np-head{position:relative;padding-top:12px;font-size:11px}
  .np-head::before{
    content:'';position:absolute;top:4px;left:50%;transform:translateX(-50%);
    width:38px;height:4px;border-radius:4px;background:var(--stroke-2);
  }
  /* the chevron points the way the sheet actually leaves */
  .np-head .icon-btn{width:38px;height:38px}
  .np-head .icon-btn svg{width:18px;height:18px;transform:rotate(90deg)}

  .np-tabs{gap:2px}
  .np-tabs button{padding:9px 0;font-size:12px}
  .art-stage{width:min(100%,34vh)}
  .np-meta h2{font-size:17px}
  .np-extras{padding-bottom:2px}

  /* Six 30px buttons and a nudge control do not share one 354px line on a
     phone. Let the bar wrap and give the actions a row of their own. */
  .lyrics-bar{flex-wrap:wrap;row-gap:6px}
  .lyrics-src{order:1;flex:1;min-width:0;text-align:right}
  .lyrics-acts{order:2;flex:1 1 100%;justify-content:space-between;gap:0}
  .lyrics-acts .icon-btn{width:38px;height:38px}
  .lyrics-acts svg{width:18px;height:18px}
  .offset-ctl.sm{transform:none}

  /* ---- player bar ---- */
  .playerbar{
    height:var(--bar-h);
    grid-template-columns:minmax(0,1fr) auto;
    grid-template-rows:auto;
    align-items:center;gap:0 10px;
    padding:0 calc(12px + var(--sa-r)) var(--sa-b) calc(12px + var(--sa-l));
  }
  .pb-track{grid-column:1;grid-row:1;gap:10px}
  .pb-art{width:44px;height:44px;border-radius:10px}
  .pb-center{grid-column:2;grid-row:1;gap:0}
  .pb-right{grid-column:3;grid-row:1}
  .volume{display:none}

  /* --- mini bar (sheet closed) --- */
  body:not(.np-open) .playerbar{padding-top:6px}
  /* the timeline becomes a hairline across the top edge */
  body:not(.np-open) .playerbar .scrub{position:absolute;left:0;right:0;top:0;gap:0}
  body:not(.np-open) .playerbar .time,
  body:not(.np-open) .playerbar .ab-box,
  body:not(.np-open) .playerbar .marker,
  body:not(.np-open) .playerbar .hover-time,
  body:not(.np-open) .playerbar .pb-right,
  body:not(.np-open) .playerbar .heart,
  body:not(.np-open) .playerbar .controls > :not(.play-btn):not(#nextBtn){display:none}
  body:not(.np-open) .playerbar .track-wrap{padding:4px 0}
  body:not(.np-open) .playerbar .track{height:3px;border-radius:0}
  body:not(.np-open) .playerbar .played{border-radius:0}
  body:not(.np-open) .playerbar .controls{gap:2px}
  body:not(.np-open) .playerbar .play-btn{width:42px;height:42px;margin:0}
  /* the mini bar itself is the handle that opens the sheet */
  body:not(.np-open) .pb-track{cursor:pointer}
  /* ...but when sound is leaving for another device, that has to stay
     visible — it is the only sign the phone is not the speaker. */
  body:not(.np-open) .playerbar:has(.connect-dot:not([hidden])) .pb-right{display:flex}
  body:not(.np-open) .playerbar:has(.connect-dot:not([hidden])) .connect-btn{display:grid}

  /* --- expanded (sheet open) --- */
  body.np-open .playerbar{
    grid-template-columns:minmax(0,1fr) auto;
    grid-template-rows:auto auto;
    row-gap:2px;padding-top:4px;
  }
  body.np-open .pb-track{grid-column:1;grid-row:1}
  body.np-open .pb-right{grid-column:2;grid-row:1;display:flex}
  body.np-open .pb-center{grid-column:1 / -1;grid-row:2;gap:2px}
  body.np-open .playerbar .scrub{position:static}
  body.np-open .pb-art{width:38px;height:38px}
  /* ±10s is how you work a loop; the full row fits once the buttons are
     sized for the sheet rather than for a desktop toolbar. */
  body.np-open .seek-btn{display:grid}
  body.np-open .controls{gap:0}
  body.np-open .controls .icon-btn{width:40px;height:40px}
  body.np-open .play-btn{margin:0 4px}
  .scrub-row{flex-direction:column;align-items:stretch;gap:2px}
  .ab-box{justify-content:center;gap:6px}
  .ab-btn,.ab-toggle{height:34px}
  .ab-btn{padding:0 12px 0 9px}
  .ab-btn small{font-size:11px}
  .ab-toggle{padding:0 14px;font-size:12px}
  .ab-box .ab-more{width:34px;height:34px}
  .ab-more svg{width:16px;height:16px}
  .time{width:38px}

  /* The markers reach up out of the timeline and, in a bar this shallow,
     straight into the transport row. Shorten the stems and drop the A/B
     flags — the chips directly below already name both points, in the
     same two colours.

     The padding is the point: the stem is 2px wide, which is a fine
     target for a cursor and none at all for a thumb. It widens the grab
     area to 32x34 without moving the line the eye follows, since the
     marker stays centred on its own position by translateX(-50%). */
  body.np-open .scrub{margin-top:4px}
  body.np-open .marker{top:-15px;padding:8px 15px 4px}
  body.np-open .marker i{height:18px}
  body.np-open .marker label{display:none}

  /* ---- floating surfaces become sheets ---- */
  .ab-panel,.connect-panel{
    left:calc(8px + var(--sa-l));right:calc(8px + var(--sa-r));width:auto;max-width:none;
    bottom:calc(var(--bar-h) + var(--tab-h) + 8px);
    max-height:calc(100dvh - var(--bar-h) - var(--tab-h) - 24px);
  }
  .modal-backdrop{padding:0;place-items:end center}
  .modal{
    width:100%;max-height:82dvh;padding:20px 18px calc(20px + var(--sa-b));
    border-radius:var(--r-lg) var(--r-lg) 0 0;
  }
  .ctx-menu{
    left:calc(8px + var(--sa-l)) !important;right:calc(8px + var(--sa-r));
    width:auto;min-width:0;max-width:none;
    top:auto !important;bottom:calc(var(--bar-h) + var(--tab-h) + 10px);
    padding-bottom:8px;
  }
  .ctx-item{padding:13px 12px;font-size:14px}
  .import-hud{
    left:calc(12px + var(--sa-l));right:calc(12px + var(--sa-r));min-width:0;
    bottom:calc(var(--bar-h) + var(--tab-h) + 10px);
  }
  .toasts{bottom:calc(var(--bar-h) + var(--tab-h) + 14px)}
  .import-bar{width:auto}
  .toast{max-width:calc(100vw - 32px);white-space:normal;text-align:center}

  /* ---- full-screen surfaces clear the notch and the home indicator ---- */
  .k-head{padding:calc(14px + var(--sa-t)) calc(16px + var(--sa-r)) 14px calc(16px + var(--sa-l))}
  .k-foot{padding:12px calc(16px + var(--sa-r)) calc(18px + var(--sa-b)) calc(16px + var(--sa-l))}
  .theater-head{
    height:calc(64px + var(--sa-t));padding-top:var(--sa-t);
    padding-right:calc(16px + var(--sa-r));padding-left:calc(16px + var(--sa-l));
  }
  .theater-foot{
    height:calc(118px + var(--sa-b));padding-bottom:var(--sa-b);
    padding-right:calc(16px + var(--sa-r));padding-left:calc(16px + var(--sa-l));
  }
  /* the 56/104 gutters are the small-screen ones set above; only the insets
     are new, so the video keeps sitting just under the translucent chrome */
  body.theater .video-stage{top:calc(56px + var(--sa-t));bottom:calc(104px + var(--sa-b))}
  body.theater.theater-lyrics .video-stage{bottom:calc(104px + var(--sa-b) + 34vh)}
  .theater-side{bottom:calc(104px + var(--sa-b))}
  .theater-controls .icon-btn,.k-controls .icon-btn{width:44px;height:44px}

  /* ---- touch targets ----
     Everything a finger has to land on gets sized for one. The sliders keep
     their thin visual track and grow only their hit box, so the look is
     unchanged while the grabbable area roughly doubles. */
  .icon-btn{width:42px;height:42px}
  .row-actions .icon-btn{width:36px;height:36px}
  .np-head .icon-btn,.ab-panel-head .icon-btn,.connect-head .icon-btn{border-radius:50%}
  .ab-panel-head .icon-btn,.connect-head .icon-btn{width:36px;height:36px}
  .ab-panel-head svg,.connect-head svg{width:16px;height:16px}
  .nav-item{padding-top:13px;padding-bottom:13px}
  .offset-ctl button{width:30px;height:30px;font-size:15px}
  .dev-row{padding:12px 10px}
  .mini-btn{width:34px;height:34px}
  .mini-btn svg{width:17px;height:17px}
  .nudge{gap:5px}
  .nudge button{font-size:11px;padding:10px 0}
  .ab-field input{height:42px;font-size:14px}
  .select,.ab-row .select{height:42px;font-size:13.5px}
  .pill-btn,.ab-panel-actions .pill-btn{height:42px}
  .modal .field input,.modal .field textarea{font-size:16px;padding:13px}

  input[type=range]{height:34px}
  input[type=range]::-webkit-slider-thumb{width:18px;height:18px;margin-top:-7px}
  input[type=range]::-moz-range-thumb{width:18px;height:18px}
  .switch{width:46px;height:28px}
  .switch span::after{width:20px;height:20px;top:3px;left:3px}
  .switch input:checked+span::after{transform:translateX(18px)}
  .extra-row{gap:12px}
}

/* ---- small phones ---- */
@media (max-width:430px){
  .grid{grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:12px}
  .view-head h1{font-size:22px}
  /* the index column costs more width than it earns at this size */
  .row{grid-template-columns:1fr 52px 40px;gap:8px;padding:7px 8px}
  .row-idx{display:none}
  .row-actions{gap:0}
  /* There is no right-click on a phone, so the ⋮ is the only way into the
     row menu and it stays. Favourite is an item inside that menu, so the
     heart is the one that can go. */
  .row-actions [data-act="fav"]{display:none}
  .ab-fields{grid-template-columns:1fr}
  .np-tabs button{font-size:11px}
}

/* ---- landscape phone: a full sheet would leave nothing to look at ---- */
@media (max-width:900px) and (max-height:480px){
  body{--tab-h:calc(46px + var(--sa-b));--bar-h:58px}
  body.np-open{--tab-h:0px;--bar-h:calc(58px + var(--sa-b))}
  /* no height for two stacked lines here — the label carries it alone */
  .tabbar button{flex-direction:row;gap:7px}
  .tabbar svg{width:17px;height:17px}
  body.np-open .playerbar{grid-template-rows:auto;padding-top:6px}
  body.np-open .pb-center{grid-column:2;grid-row:1}
  body.np-open .playerbar .scrub{position:absolute;left:0;right:0;top:0;gap:0}
  body.np-open .playerbar .time,
  body.np-open .playerbar .ab-box{display:none}
  body.np-open .playerbar .track-wrap{padding:4px 0}
  body.np-open .playerbar .track{height:3px;border-radius:0}
  .art-stage{width:min(100%,38vh)}
  .np-head{padding-top:8px}
  .np-tabs button{padding:7px 0}
  .np-meta h2{font-size:15px}
  .np-album{display:none}
  .viz-shell,.np-extras{display:none}
}

/* ============================================================
   TOUCH
   Anything revealed by :hover is unreachable on a touch screen, and
   a :hover transform sticks to whatever was tapped last. Neither is
   a width question — it is about the input device.
   ============================================================ */
@media (hover:none){
  /* affordances that were hover-only are simply always on */
  .card-play,.card-fav{opacity:1;transform:none}
  .row-actions .icon-btn{opacity:1}
  .played .knob{transform:translateY(-50%) scale(1)}

  /* ...and the lifts that would stick after a tap are dropped */
  .card:hover{transform:none;box-shadow:none}
  .pill-btn:hover,.upload-btn:hover,.ghost-btn:hover,.search-cta:hover{transform:none}
  .play-btn:hover{transform:none;filter:none}
  .row-add:hover,.card-add:hover{transform:none}
  .icon-btn:hover{background:none;color:var(--tx-2)}
  .row:hover{background:none}
  .row.playing:hover{background:color-mix(in oklab,var(--a2) 11%,transparent)}
  /* the pointer never leaves on touch, so this readout would never clear */
  .hover-time{display:none}

  /* keep the pressed feedback — that one is real on touch */
  .icon-btn:active{background:var(--panel-2);color:var(--tx)}
  .row:active{background:var(--panel)}
  .card:active{background:var(--panel-2)}

  /* no grey flash box on tap */
  *{-webkit-tap-highlight-color:transparent}
}

/* ============================================================
   SoundCloud and Apple Music
   ============================================================ */

/* The widget carries SoundCloud's own artwork and controls, so it is shown
   rather than hidden — and their terms require the uploader credited with a
   visible link back, which the caption below provides. */
.sc-stage{display:flex;flex-direction:column;gap:8px;flex:none;margin-bottom:12px}
.sc-frame{width:100%;height:166px;border-radius:12px;overflow:hidden;background:rgba(0,0,0,.25)}
.sc-frame iframe{width:100%;height:100%;display:block;border:0}
.sc-credit{font-size:11px;color:var(--tx-3);text-decoration:none;text-align:right}
.sc-credit:hover{color:var(--tx-2);text-decoration:underline}

.src-badge.sc{color:#ff5500}
.src-badge.am{color:#fa243c}
.scope button[data-scope="soundcloud"].active{background:#ff5500;color:#fff}
.scope button[data-scope="apple"].active{background:#fa243c;color:#fff}

/* Apple Music's setup has more to say than the others, because it is the
   only source with a bill and an expiry date attached. */
.am-box .am-expiry{font-size:11px;color:var(--tx-3)}
.am-box .am-expiry.soon{color:#ffb020}
.am-box .am-expiry.gone{color:#ff5a4a}
