/*!
 * assets/css/base.css — 站点基础结构样式 (主题无关)
 * ------------------------------------------------------------------
 * 所有配色都走 CSS 变量, 由 themes/<id>/style.css 覆盖:
 *   --bg --bg2 --card --card2 --line --txt --dim --dim2 --accent --accent2
 *   --pink --ok --warn --shadow --hero-grad --nav-bg --stage-bg --radius
 */

/* ================================================================
   0. 变量默认值 (深色)
   ================================================================ */
:root {
  --nav-w: 236px;
  --queue-w: 320px;
  --foot-h: 88px;
  --topbar-h: 64px;
  --radius: 14px;
  --radius-sm: 9px;
  --pad: 22px;

  --bg: #101020;
  --bg2: #14142a;
  --card: #181828;
  --card2: #202030;
  --line: #282838;
  --nav-bg: #14142a;
  --stage-bg: #0b0b18;
  --txt: #ffffff;
  --dim: #a0a0b0;
  --dim2: #6b6b80;
  --accent: #6c5ce7;
  --accent2: #a29bfe;
  --pink: #fd79a8;
  --ok: #22d3a7;
  --warn: #ffb020;
  --shadow: 0 12px 34px rgba(0, 0, 0, .38);
  --hero-grad: linear-gradient(115deg, #3a2170 0%, #1a1440 42%, #101020 100%);
  --scroll-thumb: #33334a;

  /* ---- 彩虹色板 (五彩斑斓的来源) ----------------------------------
     8 个高饱和色相均匀铺满色轮, 主题可整体替换 (浅色主题用同色相的低饱和版)。
     用法: --rainbow 横向渐变 / --rainbow-d 斜向 / --c1..--c8 单色 */
  --c1: #ff3d71;
  --c2: #ff9f1a;
  --c3: #ffe14d;
  --c4: #3ddc84;
  --c5: #22d3ee;
  --c6: #4d8bff;
  --c7: #a855f7;
  --c8: #ff5ec4;
  --rainbow: linear-gradient(90deg, var(--c1), var(--c2), var(--c3), var(--c4), var(--c5), var(--c6), var(--c7), var(--c8));
  --rainbow-d: linear-gradient(135deg, var(--c1), var(--c2), var(--c3), var(--c4), var(--c5), var(--c6), var(--c7), var(--c8));
  --rainbow-soft: linear-gradient(90deg,
      color-mix(in srgb, var(--c1) 22%, transparent),
      color-mix(in srgb, var(--c4) 22%, transparent),
      color-mix(in srgb, var(--c6) 22%, transparent),
      color-mix(in srgb, var(--c8) 22%, transparent));
}

/* 顶部一条 2px 彩虹饰线, 贯穿顶栏 —— 整页"五彩斑斓"的第一眼来源 */
.topbar::after {
  content: '';
  position: absolute; left: 0; right: 0; bottom: -1px; height: 2px;
  background: var(--rainbow);
  opacity: .55;
  pointer-events: none;
}

* { box-sizing: border-box; }

html, body {
  height: 100%;
  margin: 0;
  padding: 0;
}

body {
  background: var(--bg);
  color: var(--txt);
  font: 14px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", "Microsoft YaHei",
        "PingFang SC", "Hiragino Sans GB", Roboto, "Helvetica Neue", Arial, sans-serif;
  overflow: hidden;
  -webkit-font-smoothing: antialiased;
}

a { color: inherit; text-decoration: none; }
button, input, select { font: inherit; color: inherit; }
button { cursor: pointer; border: 0; background: none; }
ul { list-style: none; margin: 0; padding: 0; }
h1, h2, h3, h4 { margin: 0; font-weight: 700; }
em { font-style: normal; }
img { display: block; max-width: 100%; }

::-webkit-scrollbar { width: 9px; height: 9px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--scroll-thumb); border-radius: 6px; }
::-webkit-scrollbar-thumb:hover { background: var(--accent); }

.hide { display: none !important; }

/* ================================================================
   1. 主框架: 三栏 + 底部播放条 + 可展开舞台面板
   ----------------------------------------------------------------
   网格三行:
     row1 = 内容区 (nav | main | queue)
     row2 = 舞台面板 (MV/频谱/歌词) —— 收起时高度 0, 展开时 --stage-h
     row3 = 底部播放条
   舞台面板必须占满整行并显式指定 grid-area, 否则会被网格自动布局
   丢进隐式行列, 出现与播放条重叠/错位 (这是早期版本的布局 bug)。
   ================================================================ */
:root {
  --stage-h: 0px;          /* 由 body.stage-open 改为 250px */
  --stage-head-h: 44px;    /* 面板头高度, 收起时只留这一条 */
}

#app {
  display: grid;
  height: 100%;
  /* 背景视频/图片是 body 的 fixed 层 (z-index:0), 这里必须抬到它上面,
     否则主题 CSS 没加载时视频会把整个界面盖住。 */
  position: relative;
  z-index: 1;
  grid-template-columns: var(--nav-w) minmax(0, 1fr) var(--queue-w);
  grid-template-rows: minmax(0, 1fr) auto var(--foot-h);
  grid-template-areas:
    "nav   main  queue"
    "stage stage stage"
    "foot  foot  foot";
}

/* ================================================================
   2. 左侧导航
   ================================================================ */
.nav {
  grid-area: nav;
  background: var(--nav-bg);
  border-right: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  min-height: 0;
  padding: 18px 0 10px;
}

.nav-brand {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 0 20px 20px;
  flex: none;
}

.nav-brand .logo {
  width: 38px;
  height: 38px;
  border-radius: 11px;
  display: grid;
  place-items: center;
  font-size: 20px;
  font-weight: 800;
  color: #fff;
  background: var(--rainbow-d);
  box-shadow: 0 6px 16px rgba(108, 92, 231, .4);
  overflow: hidden;
}

.nav-brand .logo img { width: 100%; height: 100%; object-fit: cover; }
/* 注意: HTML 用的是 bn-txt, 早期 CSS 误写成 nb-txt, 两个都留着以防回归 */
.nav-brand .bn-txt,
.nav-brand .nb-txt { min-width: 0; flex: 1 1 auto; }
.nav-brand .bn-txt b,
.nav-brand .nb-txt b { display: block; font-size: 16px; letter-spacing: .3px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nav-brand .bn-txt em,
.nav-brand .nb-txt em { display: block; font-size: 10px; color: var(--dim2); letter-spacing: 1.4px; }

.nav-scroll { flex: 1 1 auto; overflow-y: auto; min-height: 0; padding-bottom: 8px; }

.nav-group { padding: 12px 12px 4px; }
.nav-group > b {
  display: block;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 1.5px;
  color: var(--dim2);
  padding: 0 10px 8px;
  text-transform: uppercase;
}

.nv {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 9px 12px;
  border-radius: 10px;
  color: var(--dim);
  font-size: 13.5px;
  position: relative;
  transition: background .16s, color .16s;
}

.nv i { width: 20px; text-align: center; font-size: 15px; flex: none; }
.nv span { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nv em { font-size: 10px; color: var(--dim2); letter-spacing: .5px; }
.nv:hover { background: var(--card2); color: var(--txt); }
.nv.on { background: color-mix(in srgb, var(--accent) 20%, transparent); color: var(--txt); }
.nv.on::before {
  content: "";
  position: absolute; left: 0; top: 50%; transform: translateY(-50%);
  width: 3px; height: 18px; border-radius: 0 3px 3px 0; background: var(--rainbow);
}
.nv.on i { color: var(--c5); }
.nv .badge {
  min-width: 20px; height: 18px; padding: 0 6px; border-radius: 9px;
  background: var(--accent); color: #fff; font-size: 10.5px;
  display: grid; place-items: center; flex: none;
}

/* 歌单子项 */
.nav-pls { padding: 2px 12px 6px; }
.nav-pls a {
  display: block; padding: 6px 12px; border-radius: 8px;
  font-size: 12.5px; color: var(--dim2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.nav-pls a:hover { color: var(--accent2); background: var(--card2); }

.nav-foot {
  flex: none;
  padding: 10px 16px 0;
  border-top: 1px solid var(--line);
  margin-top: auto;
  font-size: 11.5px;
  color: var(--dim2);
  display: flex;
  flex-direction: column;   /* 光环在上, 「后台管理」链接在下 */
  gap: 8px;
  justify-content: space-between;
  align-items: stretch;
}
.nav-foot > a { align-self: flex-start; }
.nav-foot a:hover { color: var(--accent2); }

/* ================================================================
   3. 中间主区
   ================================================================ */
.main { grid-area: main; display: flex; flex-direction: column; min-width: 0; min-height: 0; }

.topbar {
  height: var(--topbar-h);
  flex: none;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 0 var(--pad);
  border-bottom: 1px solid var(--line);
  background: #ffffff4f;
  position: relative;
}

.tb-search {
  flex: 1 1 auto;
  max-width: 460px;
  height: 38px;
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 0 13px;
  border-radius: 10px;
  background: var(--card2);
  border: 1px solid transparent;
}
.tb-search:focus-within { border-color: var(--accent); }
.tb-search i { color: var(--dim2); font-size: 14px; }
.tb-search input {
  flex: 1 1 auto; border: 0; background: none; outline: none; min-width: 0; font-size: 13px;
}
.tb-search input::placeholder { color: var(--dim2); }

.seg { display: flex; gap: 2px; padding: 3px; border-radius: 9px; background: var(--card2); flex: none; }
.seg button {
  width: 30px; height: 26px; border-radius: 7px; color: var(--dim2);
  display: grid; place-items: center; font-size: 14px;
}
.seg button.on { background: var(--accent); color: #fff; }

.tb-icon {
  width: 34px; height: 34px; border-radius: 9px; flex: none;
  background: var(--card2); color: var(--dim);
  display: grid; place-items: center; font-size: 15px;
}
.tb-icon:hover { color: var(--txt); background: var(--card); }

.tb-user { margin-left: auto; display: flex; align-items: center; gap: 9px; flex: none; }
.tb-user .avatar {
  width: 34px; height: 34px; border-radius: 50%;
  background: linear-gradient(135deg, var(--accent), var(--pink));
  display: grid; place-items: center; color: #fff; font-weight: 700; font-size: 14px;
}
.tb-user span { font-size: 13px; color: #ff6b23; }

.scroll { flex: 1 1 auto; overflow-y: auto; min-height: 0; padding: var(--pad); }

/* Hero */
.hero {
  position: relative;
  height: 208px;
  border-radius: var(--radius);
  overflow: hidden;
  margin-bottom: 26px;
  background: var(--hero-grad);
  display: flex;
  align-items: center;
  padding: 0 34px;
}

/* 有背景图/背景视频时, Hero 不再用不透明渐变整块盖住 —— 否则站长设了
   背景视频却只能从主区缝隙里看到一点点。半透明后视频能透上来。 */
body.has-bgvideo .hero,
body.has-bgimage .hero {
  background: rgba(10, 10, 24, .42);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}
html[data-theme="light"] body.has-bgvideo .hero,
html[data-theme="light"] body.has-bgimage .hero {
  background: rgba(255, 255, 255, .46);
}

.hero-bg {
  position: absolute; inset: 0;
  background-image: linear-gradient(90deg, rgba(10, 10, 24, .94) 0%, rgba(10, 10, 24, .55) 48%, rgba(10, 10, 24, .2) 100%);
}
/* 同上: 有背景时把渐变遮罩整体减轻, 让视频透出来 (只保留左侧文字可读性) */
body.has-bgvideo .hero-bg,
body.has-bgimage .hero-bg {
  background-image: linear-gradient(90deg, rgba(10, 10, 24, .78) 0%, rgba(10, 10, 24, .34) 52%, rgba(10, 10, 24, .06) 100%);
}
html[data-theme="light"] body.has-bgvideo .hero-bg,
html[data-theme="light"] body.has-bgimage .hero-bg {
  background-image: linear-gradient(90deg, rgba(255, 255, 255, .82) 0%, rgba(255, 255, 255, .44) 52%, rgba(255, 255, 255, .12) 100%);
}
.hero-bg img { width: 100%; height: 100%; object-fit: cover; opacity: .55; }

/* Hero 收起按钮 (右上角): 嫌横幅占地方时点掉, 选择记在 localStorage */
.hero-close {
  position: absolute; top: 10px; right: 10px; z-index: 3;
  width: 28px; height: 28px; border-radius: 50%;
  display: grid; place-items: center;
  background: rgba(0, 0, 0, .32);
  color: rgba(255, 255, 255, .82);
  font-size: 12px; line-height: 1;
  border: 1px solid rgba(255, 255, 255, .16);
  transition: background .16s, color .16s, transform .16s;
}
.hero-close:hover { background: rgba(0, 0, 0, .55); color: #fff; transform: scale(1.08); }
html[data-theme="light"] .hero-close {
  background: rgba(0, 0, 0, .08);
  color: #5c6b7a;
  border-color: rgba(0, 0, 0, .1);
}
html[data-theme="light"] .hero-close:hover { background: rgba(0, 0, 0, .16); color: #2d3436; }

/* 收起态: 整块 Hero 不占位 */
.hero.hide { display: none; }

/* 收起后, 在顶栏放一个极小的「展开横幅」入口, 否则用户找不到恢复方式 */
.tb-hero-restore { display: none; }
body.hero-hidden .tb-hero-restore {
  display: grid; place-items: center;
  width: 34px; height: 34px; border-radius: 9px; flex: none;
  background: var(--card2); color: var(--dim); font-size: 13px;
}
body.hero-hidden .tb-hero-restore:hover { color: var(--txt); background: var(--card); }
.hero-txt { position: relative; z-index: 1; max-width: 58%; }
.hero-tag {
  display: inline-block; padding: 3px 10px; border-radius: 20px;
  background: rgba(255, 255, 255, .16); font-size: 10.5px; letter-spacing: 1.2px;
  margin-bottom: 12px;
}
.hero-txt h1 { font-size: 30px; line-height: 1.2; margin-bottom: 8px; }
.hero-txt h1 small { display: block; font-size: 12px; font-weight: 400; color: var(--dim); letter-spacing: 2px; margin-top: 6px; }
.hero-txt p { margin: 0 0 16px; font-size: 12.5px; color: var(--dim); }
.hero-txt p b { color: var(--accent2); }

.hero-play {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 10px 22px; border-radius: 24px;
  background: var(--rainbow-d); color: #fff; font-size: 13px; font-weight: 600;
  box-shadow: 0 8px 22px rgba(108, 92, 231, .45);
}
.hero-play:hover { filter: brightness(1.12); }
.hero-play span:first-child { font-size: 12px; }

/* Hero 按钮排 + 次要按钮 (HTML 一直在用, 早期漏了样式) */
.hero-btns { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.hero-ghost {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 10px 20px; border-radius: 24px;
  background: rgba(255, 255, 255, .14);
  color: var(--txt); font-size: 13px; font-weight: 600;
  border: 1px solid rgba(255, 255, 255, .22);
  transition: background .16s, border-color .16s;
}
.hero-ghost:hover { background: rgba(255, 255, 255, .24); border-color: rgba(255, 255, 255, .4); }

.hero-art {
  position: relative; margin-left: auto; flex: none;
  width: 146px; height: 146px; border-radius: 50%;
  background: var(--card); overflow: hidden;
  display: grid; place-items: center;
  box-shadow: 0 14px 40px rgba(0, 0, 0, .45);
}
.hero-art img { width: 100%; height: 100%; object-fit: cover; }
.hero-art .ph { font-size: 42px; color: var(--dim2); }
.hero-ring {
  position: absolute; inset: -9px; border-radius: 50%;
  border: 2px dashed color-mix(in srgb, var(--accent2) 55%, transparent);
  animation: spin 26s linear infinite;
  pointer-events: none;
}
/* 播放时封面缓慢旋转 —— 注意必须作用在内部 img 上,
   不能给 .hero-art 本身加 animation, 否则会覆盖它的节拍缩放 transform */
body.playing .hero-art img { animation: spin 14s linear infinite; }
body.playing .hero-art .ph { animation: spin 14s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }

/* ================================================================
   音乐动感联动 (由 KG.Viz 每帧写 body 上的 CSS 变量)
     --beat   低频节拍包络(已平滑)  驱动"跳动"
     --level  综合能量              驱动"呼吸"
     --glow   发光强度(0~1)
     --low / --mid / --high  三频段能量, 分层驱动
     --kick   节拍冲击(0~1 衰减)    短促脉冲
     --kickpx 冲击像素量  --scale 缩放倍数  --spin/--hue 旋转与色相
   注意: 这里只放"结构级"的通用联动, 具体质感由各主题覆盖。
   ================================================================ */

/* 播放圈呼吸: 低频缩放 + 中频发光 + kick 冲击外扩 */
.hero-art {
  transition: transform .09s linear, box-shadow .18s ease;
  transform: scale(calc(1 + var(--beat, 0) * .045 + var(--kick, 0) * .02));
  box-shadow:
    0 14px 40px rgba(0, 0, 0, .45),
    0 0 calc(var(--glow, 0) * 46px) color-mix(in srgb, var(--accent2) calc(var(--glow, 0) * 88%), transparent),
    0 0 calc(var(--glow, 0) * 14px) color-mix(in srgb, var(--accent) calc(var(--glow, 0) * 70%), transparent),
    0 0 calc(var(--kick, 0) * 56px) color-mix(in srgb, var(--accent2) calc(var(--kick, 0) * 45%), transparent);
}
/* 高频段点亮封面外圈细边 (主题可覆盖颜色; .hero-art 已是 position:relative) */
.hero-art::after {
  content: ''; position: absolute; inset: -6px; border-radius: inherit;
  pointer-events: none;
  border: 1px solid color-mix(in srgb, var(--accent2) calc(var(--high, 0) * 55%), transparent);
  opacity: calc(.22 + var(--high, 0) * .78);
  transition: opacity .12s linear;
}
body.playing .hero-ring {
  border-color: color-mix(in srgb, var(--accent2) calc(40% + var(--glow, 0) * 60%), transparent);
  box-shadow: 0 0 calc(var(--glow, 0) * 26px + var(--kick, 0) * 18px) color-mix(in srgb, var(--accent2) calc(var(--glow, 0) * 60%), transparent);
}

/* 播放按钮脉冲: 低频缩放 + 中频发光 + kick 凸起
   注意: 动效必须与下方 .fb.big 的基础定义合并成同一处,
   否则后定义的基础规则会覆盖这里的 transform/box-shadow。 */

/* 进度条已播放部分随节拍增亮 + kick 短促闪光 */
body.playing .bar {
  box-shadow:
    0 0 calc(var(--glow, 0) * 14px) color-mix(in srgb, var(--accent) calc(var(--glow, 0) * 80%), transparent),
    0 0 calc(var(--kick, 0) * 24px) color-mix(in srgb, var(--accent2) calc(var(--kick, 0) * 65%), transparent);
}
/* 侧栏 / 队列里的当前曲目卡片随节拍轻微起伏 (只在播放时) */
body.playing .q-wave i {
  animation-duration: calc(.9s - var(--beat, 0) * .55s);
}

/* 底部播放条封面: 跟随节拍轻微脉动 */
#fCover {
  transform: scale(calc(1 + var(--beat, 0) * .045));
  transition: transform .09s linear, box-shadow .18s ease;
}

/* 全屏背景可选的低频脉动 (由主题用 --beat 自行决定强度, 默认关闭) */
body.fx-bgbeat.playing::after {
  content: ''; position: fixed; inset: 0; pointer-events: none; z-index: 0;
  background: radial-gradient(circle at 50% 42%,
    color-mix(in srgb, var(--accent) calc(var(--beat, 0) * 16%), transparent), transparent 62%);
  transition: opacity .2s ease;
}

/* 无动画偏好时全部关掉 */
@media (prefers-reduced-motion: reduce) {
  .hero-art, .hero-art::after, .fb.big, #fCover, #dot { transform: none; }
  #rippleCv { display: none; }
  body.playing .hero-art img, body.playing .hero-art .ph,
  body.playing .hero-ring, .q-wave i { animation: none; }
  body.fx-bgbeat.playing::after { display: none; }
  /* 视频背景: 不受无障碍偏好影响 —— 站长显式配置的背景就该正常播放。
     历史事故: 这里曾写 display:none, 系统开了"减少动画效果"的用户完全看不到
     背景视频, 误判为功能失效; 之后改成"只暂停显示首帧", 用户又反馈"背景不能动了"。
     结论: 站长主动配置的内容不做无障碍降级, 只有页面自发的装饰动画才降级。 */
  .bg-video { animation: none; }
}

/* 区块 */
.sect { margin-bottom: 28px; }
.sect-head { display: flex; align-items: baseline; gap: 12px; margin-bottom: 14px; min-width: 0; }
.sect-head h2 { font-size: 17px; flex: none; }
.sect-head .more,
.sect-head .sect-more { margin-left: auto; font-size: 12px; color: var(--accent2); flex: none; }
.sect-head .cnt,
.sect-head .sect-sub { font-size: 12px; color: var(--dim2); flex: 0 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* 分类 chips */
.chips { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 16px; }
.chip {
  padding: 6px 14px; border-radius: 18px;
  background: var(--card2); color: var(--dim); font-size: 12.5px;
  border: 1px solid transparent;
}
.chip:hover { color: var(--txt); }
.chip.on { background: var(--rainbow-d); color: #fff; }
.chip b { font-weight: 600; margin-left: 5px; opacity: .7; font-size: 11px; }

/* 卡片行 */
.cards-row {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 152px;
  gap: 16px;
  overflow-x: auto;
  padding-bottom: 8px;
}

.card {
  position: relative;
  border-radius: var(--radius);
  background: var(--card);
  padding: 12px;
  transition: transform .18s, background .18s, box-shadow .18s;
}
.card:hover { transform: translateY(-5px); background: var(--card2); box-shadow: var(--shadow); }
.card .cv {
  position: relative; width: 100%; aspect-ratio: 1/1;
  border-radius: var(--radius-sm); overflow: hidden;
  background: var(--card2); margin-bottom: 10px;
}
.card .cv img { width: 100%; height: 100%; object-fit: cover; }
.card .cv .ph { width: 100%; height: 100%; display: grid; place-items: center; color: var(--dim2); font-size: 30px; }
.card .cplay {
  position: absolute; right: 8px; bottom: 8px;
  width: 36px; height: 36px; border-radius: 50%;
  background: var(--rainbow-d); color: #fff;
  display: grid; place-items: center; font-size: 15px;
  opacity: 0; transform: translateY(6px);
  transition: opacity .18s, transform .18s;
  box-shadow: 0 6px 18px rgba(0, 0, 0, .45);
}
.card:hover .cplay { opacity: 1; transform: none; }
.card b { display: block; font-size: 13px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.card em { display: block; font-size: 11.5px; color: var(--dim2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; margin-top: 3px; }

/* 卡片网格 (▦ 视图) */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(152px, 1fr));
  gap: 16px;
}
.card-grid.hide { display: none; }

/* ================================================================
   4. 音乐导航 (常用音乐站点)
   ----------------------------------------------------------------
   卡片按分类分组; 每张卡片的左侧图标底色按 nth-child 轮换彩虹色,
   所以一整片站点看起来是彩色的, 而不是统一的灰。
   ================================================================ */
#sitesBox { display: flex; flex-direction: column; gap: 22px; }

.sites-tip {
  padding: 26px 20px; border-radius: var(--radius);
  background: var(--card); color: var(--dim);
  font-size: 13px; text-align: center; line-height: 1.9;
}
.sites-tip a { color: var(--c5); text-decoration: underline; }

.site-group h3 {
  font-size: 13px; font-weight: 700; color: var(--dim);
  letter-spacing: .5px; margin-bottom: 10px;
  display: flex; align-items: center; gap: 8px;
}
.site-group h3::before {
  content: ''; width: 3px; height: 13px; border-radius: 2px;
  background: var(--rainbow); flex: none;
}

.site-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(184px, 1fr));
  gap: 12px;
}

.site-card {
  display: flex; align-items: center; gap: 11px;
  padding: 12px 14px; border-radius: var(--radius);
  background: var(--card); border: 1px solid var(--line);
  transition: transform .16s, border-color .16s, box-shadow .16s, background .16s;
  min-width: 0;
}
.site-card:hover {
  transform: translateY(-3px);
  border-color: color-mix(in srgb, var(--c5) 55%, transparent);
  box-shadow: 0 10px 26px rgba(0, 0, 0, .3), 0 0 0 1px color-mix(in srgb, var(--c5) 26%, transparent);
  background: var(--card2);
}

.sc-ico {
  width: 38px; height: 38px; border-radius: 11px; flex: none;
  display: grid; place-items: center;
  font-size: 17px; font-weight: 700; color: #fff;
  background: var(--c6);
  box-shadow: 0 4px 12px rgba(0, 0, 0, .28);
  overflow: hidden;
}
.sc-ico img { width: 100%; height: 100%; object-fit: cover; }
/* 8 色轮换 —— 每 8 个站点循环一次, 视觉上五彩斑斓 */
.site-grid .site-card:nth-child(8n+1) .sc-ico { background: var(--c1); }
.site-grid .site-card:nth-child(8n+2) .sc-ico { background: var(--c2); }
.site-grid .site-card:nth-child(8n+3) .sc-ico { background: var(--c3); color: #4a3a00; }
.site-grid .site-card:nth-child(8n+4) .sc-ico { background: var(--c4); }
.site-grid .site-card:nth-child(8n+5) .sc-ico { background: var(--c5); color: #06323a; }
.site-grid .site-card:nth-child(8n+6) .sc-ico { background: var(--c6); }
.site-grid .site-card:nth-child(8n+7) .sc-ico { background: var(--c7); }
.site-grid .site-card:nth-child(8n+8) .sc-ico { background: var(--c8); }

.sc-meta { min-width: 0; flex: 1 1 auto; }
.sc-meta b {
  display: block; font-size: 13px; font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sc-meta em {
  display: block; font-size: 11px; color: var(--dim2); margin-top: 2px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sc-go {
  flex: none; font-size: 13px; color: var(--dim2);
  opacity: 0; transform: translateX(-4px);
  transition: opacity .16s, transform .16s, color .16s;
}
.site-card:hover .sc-go { opacity: 1; transform: none; color: var(--c5); }

/* ================================================================
   5. 歌曲列表
   ================================================================ */
/* .tracks 是 <ul id="list"> 的外层类名, 必须清掉浏览器默认的项目符号与缩进 */
.tracks { list-style: none; margin: 0; padding: 0; }
#list { display: flex; flex-direction: column; gap: 2px; }
#list.hide { display: none; }

.th, .row {
  display: grid;
  grid-template-columns: 34px 44px minmax(0, 1fr) 66px 52px;
  align-items: center;
  gap: 12px;
  padding: 8px 12px;
  border-radius: var(--radius-sm);
}

.th {
  font-size: 10.5px; letter-spacing: 1.2px; color: var(--dim2);
  text-transform: uppercase; padding-bottom: 6px;
}

/* 空态行: 占满整行, 不被上表列宽切成碎块 */
.row.empty-row { display: block; padding: 0; }
.row.empty-row:hover { background: none; }

.row { cursor: pointer; transition: background .14s; }
.row:hover { background: var(--card); }
.row.on { background: color-mix(in srgb, var(--accent) 18%, transparent); }
.row.on .no { color: var(--accent2); }

.row .no { font-size: 12.5px; color: var(--dim2); text-align: center; font-variant-numeric: tabular-nums; }
.row .cv {
  width: 44px; height: 44px; border-radius: 8px; overflow: hidden;
  background: var(--card2); position: relative;
}
.row .cv img { width: 100%; height: 100%; object-fit: cover; }
.row .cv .ph { width: 100%; height: 100%; display: grid; place-items: center; color: var(--dim2); font-size: 16px; }

.row .mi { min-width: 0; }
.row .mi b { display: block; font-size: 13.5px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.row .mi em { display: block; font-size: 11.5px; color: var(--dim2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; margin-top: 2px; }

.row .dur { font-size: 12px; color: var(--dim2); text-align: right; font-variant-numeric: tabular-nums; }

.row .act { display: flex; gap: 4px; justify-content: flex-end; opacity: 0; transition: opacity .14s; }
.row:hover .act, .row.on .act { opacity: 1; }
.row .act button {
  width: 30px; height: 30px; border-radius: 8px;
  color: var(--dim2); display: grid; place-items: center; font-size: 14px;
}
.row .act button:hover { background: var(--card2); color: var(--txt); }
.row .act button.on { color: var(--pink); }

/* 紧凑密度 */
body.density-compact .row { padding: 4px 12px; }
body.density-compact .row .cv { width: 34px; height: 34px; }
body.density-compact .row .mi em { display: none; }
body.density-compact .th { padding-bottom: 2px; }

/* 空态 */
.empty { padding: 60px 20px; text-align: center; color: var(--dim2); }
.empty b { display: block; font-size: 15px; color: var(--dim); margin-bottom: 8px; }
.empty a { color: var(--accent2); }

/* ================================================================
   6. 舞台面板 (MV / 频谱 / 歌词)
   ================================================================ */
.stagepanels {
  grid-area: stage;          /* 必须显式指定, 否则会与 foot 重叠 */
  height: var(--stage-h);
  overflow: hidden;
  border-top: 1px solid var(--line);
  background: var(--bg2);
  display: flex;
  flex-direction: column;
  min-height: 0;
  transition: height .22s ease;
}
/* 展开/收起: 收起时只保留面板头 (44px), 由 body 上的类统一控制,
   避免 .collapsed 与 grid 行高互相打架 */
body.stage-open .stagepanels { --stage-h: 250px; }
.stagepanels.collapsed { --stage-h: var(--stage-head-h); }

.panel-hd {
  flex: none; height: var(--stage-head-h);
  display: flex; align-items: center; gap: 6px;
  padding: 0 16px; border-bottom: 1px solid var(--line);
  min-width: 0; overflow: hidden;
  position: relative;           /* #status / .ph-close 的绝对定位基准 */
  justify-content: center;      /* 让 .ph-tabs 整组水平居中 */
}
/* 收起时不显示分隔线, 视觉上更像一条工具条 */
.stagepanels.collapsed .panel-hd { border-bottom: 0; }

/* 三个切换按钮的包裹层: 作为整体居中, 内部仍按 flex 排列。
   早期版本三个按钮直接挂在 .panel-hd 里、靠 #status 的 margin-left:auto
   把它们顶到左边, 视觉上像"按钮跑到左边缘"。 */
.ph-tabs {
  display: flex; align-items: center; gap: 6px;
  flex: none; min-width: 0;
}
.ph-tab {
  padding: 6px 14px; border-radius: 8px; font-size: 12.5px;
  color: var(--dim2); display: flex; align-items: center; gap: 7px;
  flex: none; white-space: nowrap;
}
.ph-tab:hover { color: var(--txt); }
.ph-tab.on { background: var(--accent); color: #fff; }
/* 状态文字与收起按钮钉在右侧, 不参与居中, 也不会在窄屏把按钮挤走 */
.panel-hd #status {
  position: absolute; right: 52px; top: 50%; transform: translateY(-50%);
  font-size: 11.5px; color: var(--dim2);
  max-width: 40%; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
  pointer-events: none;
}
.panel-hd .ph-close {
  position: absolute; right: 14px; top: 50%; transform: translateY(-50%);
  width: 28px; height: 28px; border-radius: 7px; color: var(--dim2);
  display: grid; place-items: center; font-size: 15px;
  flex: none;
}
.panel-hd .ph-close:hover { background: var(--card2); color: var(--txt); }
.stagepanels.collapsed .panel-bd { display: none; }

.panel-bd { flex: 1 1 auto; position: relative; min-height: 0; }
.pane { position: absolute; inset: 0; display: none; }
.pane.on { display: block; }

/* MV / 封面
   注意: display 必须写在 #stage.on 上, 不能写在 #stage 上。
   ID 选择器 (1,0,0) 特异性高于 .pane (0,1,0), 若把 display:flex 挂在
   #stage 本身, 即使面板切到歌词/频谱, #stage 依然 display:flex 铺满
   .panel-bd, 把歌词整片盖住 (历史 bug: 歌词区中央出现"MV / 视频播放区"
   提示语、右上角出现 ▶ MV 按钮)。 */
#stage {
  background: var(--stage-bg);
  overflow: hidden;
}
#stage.on {
  display: flex; align-items: center; justify-content: center;
}
#stage video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; background: #000; }
#stage video.hide { display: none; }

/* 未播放时的提示语: 只在这一层显示, 与封面互斥 (否则两者会叠在一起) */
.ph-hint {
  position: absolute; inset: 0; z-index: 1;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 8px; color: var(--dim2); font-size: 13px; letter-spacing: .4px;
  text-align: center; padding: 0 20px;
}
.ph-hint b { font-size: 14px; color: var(--dim); letter-spacing: 1px; }
.ph-hint span { font-size: 12px; }

/* 封面光环 (第十八轮修正): 环形放射画布挂在左侧导航底部 .nav-foot 内,
   位于「后台管理」链接上方, 随音乐起伏。舞台里的封面本体不再放光环。 */
.nav-ring {
  position: relative;
  width: 180px;
  height: 180px;
  display: grid;
  place-items: center;
  margin: 0 auto 8px;
  overflow: hidden;
}
#ringCv {
  width: 100%; height: 100%;
  display: block;
  pointer-events: none;
}
#cover {
  width: 168px; height: 168px; border-radius: 10px; overflow: hidden;
  background: var(--card2);
  display: grid; place-items: center;
  box-shadow: 0 16px 44px rgba(0, 0, 0, .55); position: relative; z-index: 1;
}
#cover img { width: 100%; height: 100%; object-fit: cover; }
#cover .ph { font-size: 40px; color: var(--dim2); }

/* 封面兜底: 曲目没抓到封面时用站点 logo 顶上 (文字 logo 或图片 logo 都支持)。
   样式做成"低调的水印感" —— 它是占位, 不该抢过真封面。 */
.logo-fb {
  display: grid;
  place-items: center;
  width: 100%; height: 100%;
  background: var(--rainbow-d);
  color: #fff;
  font-weight: 800;
  font-size: 15px;
  letter-spacing: .5px;
  opacity: .82;
  user-select: none;
}
img.logo-fb { object-fit: cover; opacity: .9; }
#cover .logo-fb { font-size: 46px; }
.card .cv .logo-fb { font-size: 34px; }
.row .cv .logo-fb { font-size: 17px; }
.q-item .qi-cv .logo-fb { font-size: 13px; }
#fCover .logo-fb { font-size: 15px; }
#qCover .logo-fb { font-size: 15px; }
.hero-art .logo-fb { font-size: 46px; }

/* 视频背景 (后台「背景图 URL」填 mp4/webm/ogv 时由 player.js 挂上来)。
   fixed 全屏铺满 + 盖一层主题色遮罩, 保证前景文字对比度。 */
/* ---- 背景图 / 背景视频 ----
   视频与图片是 body 下的 fixed 层 (z-index:0), #app 抬到 z-index:1。
   但侧栏/队列/底栏用的是不透明 --nav-bg, 会把背景整块盖住 —— 只要设了
   背景, 就换成带透明度的同名色, 让背景真正透出来 (否则"设了背景但看不见")。 */
.bg-video {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 0;
  pointer-events: none;
  opacity: .55;
}

body.has-bgvideo,
body.has-bgimage {
  --nav-bg: rgba(20, 20, 42, .62);
}

body.has-bgvideo .nav,
body.has-bgimage .nav,
body.has-bgvideo .queue,
body.has-bgimage .queue,
body.has-bgvideo .foot,
body.has-bgimage .foot {
  backdrop-filter: none;      /* 毛玻璃在这种层叠下很贵, 且已有半透明底色 */
}

/* 亮色主题: 半透明白, 否则深色蒙层会压在浅色背景上 */
html[data-theme="light"] body.has-bgvideo,
html[data-theme="light"] body.has-bgimage {
  --nav-bg: rgba(255, 255, 255, .74);
}
/* 图片背景: 铺满不重复, 再压暗一档, 避免干扰前景 */
body.has-bgimage {
  background-size: cover !important;
  background-position: center !important;
  background-repeat: no-repeat !important;
  background-attachment: fixed !important;
}
@media (max-width: 900px) {
  /* 手机端视频背景: 保留显示(显示首帧), 但不自动播放 —— 由 player.js 判断。
     曾经这里写 display:none, 结果手机上看不到背景, 被当成功能坏了。 */
  .bg-video { opacity: .45; }
  body.has-bgimage { background-attachment: scroll !important; }
}
/* 互斥: 有封面时隐藏提示语; 无封面时隐藏空封面占位块 —— 两者永不叠加。
   has-cover 由 player.js 的 setCoverEl() 维护 (判定 #cover 内是否真的渲染出 img)。 */
#stage.has-cover .ph-hint { display: none; }
#stage:not(.has-cover) #cover { display: none; }
/* 兼容: 万一 JS 未执行, 用 :has() 兜一层, 至少不会把封面藏掉 */
#stage:has(#cover img) .ph-hint { display: none; }
.pane-badges { position: absolute; right: 12px; top: 12px; display: flex; gap: 8px; align-items: center; z-index: 2; }
#srctag {
  padding: 3px 10px; border-radius: 20px; font-size: 10.5px;
  background: rgba(0, 0, 0, .5); color: var(--dim);
}
#videoBtn {
  width: 30px; height: 30px; border-radius: 8px;
  background: rgba(0, 0, 0, .5); color: var(--dim);
  display: grid; place-items: center; font-size: 13px;
}
#videoBtn:hover { color: #fff; background: var(--accent); }
#loading {
  position: absolute; left: 50%; top: 50%; width: 34px; height: 34px;
  margin: -17px 0 0 -17px; border-radius: 50%;
  border: 3px solid rgba(255, 255, 255, .14); border-top-color: var(--accent);
  animation: spin .8s linear infinite; z-index: 3;
}
#loading.hide { display: none; }

/* 频谱 */
#vizwrap { background: var(--bg2); }
#viz { width: 100%; height: 100%; display: block; }

/* 歌词 */
#lyricwrap { background: var(--bg2); overflow: hidden; }
#lyric { height: 100%; overflow-y: auto; padding: 16px 30px 40px; text-align: center; }
.ly-line {
  font-size: 13.5px; color: var(--dim2); padding: 5px 0;
  transition: color .2s, transform .2s, font-size .2s;
}
.ly-line.on {
  font-size: 15.5px; font-weight: 600; transform: scale(1.02);
  background: var(--rainbow);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: var(--accent2);
}
.ly-line:hover { color: var(--dim); }
.ly-tip { color: var(--dim2); font-size: 12.5px; padding-top: 20px; }

/* ================================================================
   7. 右侧播放队列
   ================================================================ */
.queue {
  grid-area: queue;
  background: var(--nav-bg);
  border-left: 1px solid var(--line);
  display: flex; flex-direction: column; min-height: 0;
}

.q-head {
  flex: none; height: 56px;
  display: flex; align-items: center; gap: 10px; padding: 0 18px;
}
.q-head h3 { font-size: 15px; }
.q-head h3 em { font-size: 11px; color: var(--dim2); letter-spacing: 1px; margin-left: 5px; }
.q-acts { margin-left: auto; display: flex; gap: 6px; }
.q-acts button {
  padding: 5px 12px; border-radius: 8px; font-size: 12px;
  background: var(--card2); color: var(--dim);
}
.q-acts button:hover { color: var(--txt); }
.q-acts button.pri { background: var(--accent); color: #fff; }

.q-now {
  flex: none; margin: 0 14px 12px; padding: 14px;
  border-radius: var(--radius);
  background: var(--rainbow-d);
  display: flex; align-items: center; gap: 12px; color: #fff;
  box-shadow: 0 10px 26px rgba(72, 52, 168, .35);
}
.q-cover {
  width: 52px; height: 52px; border-radius: 10px; overflow: hidden; flex: none;
  background: rgba(255, 255, 255, .18); display: grid; place-items: center; font-size: 20px;
}
.q-cover img { width: 100%; height: 100%; object-fit: cover; }
.q-meta { min-width: 0; flex: 1 1 auto; }
.q-meta b { display: block; font-size: 13.5px; font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.q-meta em { display: block; font-size: 11.5px; opacity: .82; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; margin-top: 3px; }

.q-wave { display: flex; align-items: flex-end; gap: 3px; height: 22px; flex: none; }
.q-wave i { width: 3px; height: 6px; border-radius: 2px; background: rgba(255, 255, 255, .75); }
body.playing .q-wave i:nth-child(1) { animation: wv .9s ease-in-out infinite; }
body.playing .q-wave i:nth-child(2) { animation: wv .7s ease-in-out infinite .12s; }
body.playing .q-wave i:nth-child(3) { animation: wv 1.1s ease-in-out infinite .24s; }
body.playing .q-wave i:nth-child(4) { animation: wv .8s ease-in-out infinite .36s; }
@keyframes wv { 0%, 100% { height: 6px; } 50% { height: 22px; } }

.q-list { flex: 1 1 auto; overflow-y: auto; min-height: 0; padding: 0 8px; }

.q-item {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 10px; border-radius: 8px; cursor: pointer;
}
.q-item:hover { background: var(--card2); }
.q-item.on { background: color-mix(in srgb, var(--accent) 20%, transparent); }
.q-item .qi-cv {
  width: 36px; height: 36px; border-radius: 7px; overflow: hidden; flex: none;
  background: var(--card2); display: grid; place-items: center; color: var(--dim2); font-size: 14px;
}
.q-item .qi-cv img { width: 100%; height: 100%; object-fit: cover; }
.q-item .qi-meta { min-width: 0; flex: 1 1 auto; }
.q-item .qi-meta b { display: block; font-size: 12.5px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.q-item .qi-meta em { display: block; font-size: 11px; color: var(--dim2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.q-item .qi-dur { font-size: 11px; color: var(--dim2); flex: none; font-variant-numeric: tabular-nums; }

.q-foot {
  flex: none; height: 44px; padding: 0 16px;
  display: flex; align-items: center; gap: 10px;
  border-top: 1px solid var(--line); font-size: 11.5px; color: var(--dim2);
}
.q-foot b { color: var(--accent2); }
.q-foot button { margin-left: auto; color: var(--dim2); font-size: 14px; }
.q-foot button:hover { color: var(--accent2); }

/* ================================================================
   8. 底部播放条
   ================================================================ */
.foot {
  grid-area: foot;
  background: var(--nav-bg);
  border-top: 1px solid var(--line);
  display: grid;
  grid-template-columns: minmax(200px, 1fr) minmax(340px, 2fr) minmax(200px, 1fr);
  align-items: center;
  gap: 18px;
  padding: 0 var(--pad);
}

.f-info { display: flex; align-items: center; gap: 12px; min-width: 0; }
.f-cover {
  width: 52px; height: 52px; border-radius: 10px; overflow: hidden; flex: none;
  background: var(--card2); display: grid; place-items: center; color: var(--dim2); font-size: 18px;
  box-shadow: var(--shadow);
}
.f-cover img { width: 100%; height: 100%; object-fit: cover; }
.f-meta { min-width: 0; }
.f-meta b { display: block; font-size: 13.5px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.f-meta em { display: block; font-size: 11.5px; color: var(--dim2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; margin-top: 2px; }

/* 喜欢按钮 (HTML 用 class="f-like", 早期只写了 id 样式) */
.f-like {
  font-size: 15px; color: var(--dim2); flex: none;
  width: 30px; height: 30px; border-radius: 8px;
  display: grid; place-items: center;
  transition: color .16s, background .16s, transform .16s;
}
.f-like:hover { color: var(--pink); background: var(--card2); }
.f-like.on { color: var(--pink); }
.f-like:active { transform: scale(.9); }
#likeBtn { font-size: 15px; color: var(--dim2); flex: none; }
#likeBtn.on { color: var(--pink); }

.f-center { display: flex; flex-direction: column; align-items: center; gap: 4px; min-width: 0; }
.f-btns { display: flex; align-items: center; gap: 8px; justify-content: center; }
.fb {
  width: 34px; height: 34px; border-radius: 50%;
  color: var(--dim); display: grid; place-items: center; font-size: 15px;
}
.fb:hover { color: var(--txt); background: var(--card2); }
.fb.on { color: var(--accent2); }
.fb.big {
  width: 42px; height: 42px; background: var(--rainbow-d); color: #fff; font-size: 16px;
  /* 节拍联动: 低频缩放 + kick 凸起, 光晕随节拍增强 (变量由 KG.Viz 每帧写入)。
     幅度经过实测调整: --beat 在真实音乐里会被 AGC 拉到 0.4~1.0 的宽区间,
     所以 .16 的系数就能有明显但不过火的呼吸感。 */
  transform: scale(calc(1 + var(--beat, 0) * .16 + var(--kick, 0) * .07));
  transition: transform .09s linear, box-shadow .18s ease, filter .18s ease;
  box-shadow:
    0 6px 18px rgba(108, 92, 231, .45),
    0 0 calc(var(--beat, 0) * 34px) color-mix(in srgb, var(--accent) calc(var(--beat, 0) * 85%), transparent),
    0 0 calc(var(--kick, 0) * 38px) color-mix(in srgb, var(--accent2) calc(var(--kick, 0) * 70%), transparent);
}
.fb.big:hover { filter: brightness(1.12); color: #fff; }

/* 播放按钮的七彩水波纹由 assets/js/ripple.js 用 canvas 画 (#rippleCv)。
   canvas 绝对定位在 .f-btns 正中、按钮之下, 不参与布局也不吃点击。 */
.f-btns { position: relative; }
#rippleCv {
  position: absolute; left: 50%; top: 50%; width: 168px; height: 168px;
  transform: translate(-50%, -50%);
  pointer-events: none; z-index: 0;
}
.f-btns > .fb { position: relative; z-index: 1; }
.fb.sm { width: 30px; height: 30px; font-size: 13px; }

.f-prog { display: flex; align-items: center; gap: 11px; width: 100%; max-width: 560px; }
.f-prog span { font-size: 11px; color: var(--dim2); font-variant-numeric: tabular-nums; flex: none; min-width: 34px; text-align: center; }

/* 进度条轨道 (HTML 用 class="bar-wrap" 包住 #bar 与 #dot) */
.bar-wrap,
#prog {
  position: relative; flex: 1 1 auto; height: 16px; cursor: pointer;
  display: flex; align-items: center;
}
.bar-wrap::before,
#prog::before {
  content: ""; position: absolute; left: 0; right: 0; height: 4px;
  border-radius: 3px; background: var(--card2);
}
.bar-wrap .bar,
#bar {
  position: absolute; left: 0; height: 4px; width: 0;
  border-radius: 3px; background: var(--rainbow);
}
.bar-wrap .dot,
#dot {
  position: absolute; left: 0; top: 50%; width: 11px; height: 11px; margin-left: -5.5px;
  border-radius: 50%; background: #fff; box-shadow: 0 2px 6px rgba(0, 0, 0, .5);
  opacity: 0;
  /* 高频段驱动滑块尺寸, kick 时短促放大 (变量由 KG.Viz 每帧写入) */
  transform: translate(-50%, -50%) scale(calc(1 + var(--high, 0) * .32 + var(--kick, 0) * .5));
  transition: opacity .16s, transform .09s linear;
}
.bar-wrap:hover .dot,
#prog:hover #dot { opacity: 1; }

.f-right { display: flex; align-items: center; gap: 9px; justify-content: flex-end; }
#mute { width: 30px; height: 30px; border-radius: 8px; color: var(--dim); display: grid; place-items: center; font-size: 14px; }
#mute:hover { color: var(--txt); }

#vol {
  -webkit-appearance: none; appearance: none; width: 84px; height: 4px;
  border-radius: 3px; background: var(--card2); outline: none;
}
#vol::-webkit-slider-thumb {
  -webkit-appearance: none; width: 12px; height: 12px; border-radius: 50%;
  background: var(--accent); cursor: pointer;
}
#vol::-moz-range-thumb { width: 12px; height: 12px; border: 0; border-radius: 50%; background: var(--accent); }

#eqSel, #fxSel, #vizSel {
  height: 30px; padding: 0 9px; border-radius: 8px;
  background: var(--card2); border: 0; outline: none; font-size: 12px; color: var(--dim);
}
#fxSel { max-width: 108px; }
/* 频谱样式选择器: 放在播放按钮旁边, 窄屏时隐藏 (空间不够, 且窄屏看频谱的人少) */
#vizSel { max-width: 104px; }
@media (max-width: 760px) { #vizSel { display: none; } }

/* ================================================================
   9. 提示 / 自检弹层
   ================================================================ */
#toast {
  position: fixed; left: 50%;
  /* 底部播放条 + 收起状态的舞台面板头, 避免被它们盖住 */
  bottom: calc(var(--foot-h) + var(--stage-head-h) + 18px);
  transform: translateX(-50%) translateY(14px);
  padding: 10px 22px; border-radius: 24px;
  background: rgba(20, 20, 34, .96); color: #fff; font-size: 13px;
  box-shadow: var(--shadow); border: 1px solid var(--line);
  opacity: 0; pointer-events: none; transition: opacity .24s, transform .24s;
  z-index: 99; max-width: min(74vw, 560px); text-align: center;
}
#toast.on { opacity: 1; transform: translateX(-50%); }

.diag-box {
  position: fixed; right: 22px;
  bottom: calc(var(--foot-h) + var(--stage-head-h) + 18px);
  width: min(380px, calc(100vw - 44px)); max-height: 62vh; overflow-y: auto;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: 16px 18px; z-index: 98; font-size: 12.5px;
}
.diag-box.hide { display: none; }
.diag-box h4 { font-size: 13.5px; margin-bottom: 10px; }
.diag-box .st { display: flex; gap: 8px; padding: 5px 0; border-bottom: 1px dashed var(--line); }
.diag-box .st i { flex: none; }
.diag-box .st .ok { color: var(--ok); }
.diag-box .st .no { color: var(--warn); }
.diag-box .vd { margin-top: 10px; padding: 9px 11px; border-radius: 8px; background: var(--card2); }
.diag-box .x { position: absolute; right: 14px; top: 12px; color: var(--dim2); }

/* ================================================================
   10. 响应式 (桌面 → 平板 → 手机 H5)
   ----------------------------------------------------------------
   注意: 每一档断点重写 grid-template-areas 时, 必须把 stage 这一行
   一起写上, 否则舞台面板会失去 grid-area 而跟播放条重叠。
   ================================================================ */
@media (max-width: 1400px) { :root { --queue-w: 288px; } }

/* ---- 平板: 收起右侧队列 ---- */
@media (max-width: 1180px) {
  #app {
    grid-template-columns: var(--nav-w) minmax(0, 1fr);
    grid-template-areas:
      "nav   main"
      "stage stage"
      "foot  foot";
  }
  .queue { display: none; }
}

/* ---- 手机 H5: 单列, 隐藏左栏, 底部播放条重排 ---- */
@media (max-width: 900px) {
  :root {
    --pad: 14px;
    --foot-h: 92px;          /* 两行布局: 信息行 42px + 进度行 16px + 间距/内边距 */
    --stage-head-h: 40px;
    --radius: 12px;
  }

  #app {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas:
      "main"
      "stage"
      "foot";
    grid-template-rows: minmax(0, 1fr) auto var(--foot-h);
  }

  .nav { display: none; }
  body.stage-open .stagepanels { --stage-h: 210px; }

  /* 底部条: 手机改成两行两列 —— 上行左信息/右控制, 下行整行进度条。
     .foot 本身就是 grid, 这里只换列定义与区域划分。 */
  .foot {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
      "finfo  fright"
      "fprog  fprog";
    gap: 2px 12px;
    padding: 6px var(--pad);
    align-content: center;
  }
  .f-info  { grid-area: finfo; }
  .f-center{ grid-area: fprog; }
  .f-right { grid-area: fright; }
  .f-cover { width: 42px; height: 42px; border-radius: 9px; }
  .f-meta b { font-size: 12.5px; }
  .f-meta em { font-size: 11px; }
  .f-btns { gap: 4px; }
  .fb.sm { display: none; }          /* 手机上先收起模式/面板小按钮 */
  .fb.big { width: 40px; height: 40px; font-size: 15px; }
  .f-prog { max-width: none; gap: 8px; }
  .f-right #vol, .f-right #fxSel { display: none; }
  /* 窄屏上 nav 整个隐藏 (光环在 nav-foot 里, 跟着一起消失, 不占空间);
     #vizSel 保留 —— 动感样式切换在手机上仍然可用 */
  .f-right #vizSel { display: block; }

  /* 顶栏: 手机上让搜索框独占一行, 其余控件收窄 */
  .topbar { flex-wrap: wrap; gap: 8px; padding: 8px 10px; height: auto; min-height: 46px; }
  .tb-search { flex: 1 1 100%; max-width: none; order: 1; }
  .tb-search input { font-size: 13px; }
  .tb-user { order: 2; margin-left: auto; }
  #whoName { display: none; }           /* 昵称在小屏没位置, 只留头像 */
  .seg { order: 3; }
  .tb-icon { order: 4; }

  /* Hero 与卡片适配 */
  .hero { height: auto; min-height: 150px; padding: 20px; }
  .hero-txt { max-width: 100%; }
  .hero-txt h1 { font-size: 20px; }
  .hero-art { display: none; }
  .hero-btns { gap: 8px; }
  .hero-play, .hero-ghost { padding: 9px 16px; font-size: 12.5px; }

  .cards-row { grid-auto-columns: 132px; gap: 12px; }
  .card-grid { grid-template-columns: repeat(auto-fill, minmax(132px, 1fr)); gap: 12px; }

  /* 列表: 隐藏封面/操作列后, 网格要同步降为 3 列 (否则会留出 2 个空列) */
  .th, .row {
    grid-template-columns: 26px minmax(0, 1fr) 44px;
    gap: 8px;
    padding: 7px 8px;
  }
  .th .cv, .row .cv, .th .act, .row .act { display: none; }
  .row .dur { font-size: 11px; }

  /* 面板头: 窄屏时按钮组仍居中, 状态文字让位 (否则会压住最后一个按钮) */
  .panel-hd { padding: 0 10px; gap: 4px; }
  .ph-tabs { gap: 4px; }
  .ph-tab { padding: 5px 10px; font-size: 12px; }
  .panel-hd #status { right: 46px; max-width: 30%; }

  #toast { bottom: calc(var(--foot-h) + var(--stage-head-h) + 12px); max-width: 88vw; font-size: 12.5px; }
  .diag-box { right: 10px; left: 10px; width: auto; max-height: 52vh; }
}

/* ---- 小屏手机 ---- */
@media (max-width: 560px) {
  :root { --pad: 12px; }
  .hero-tag { font-size: 9.5px; padding: 2px 8px; }
  .hero-txt h1 { font-size: 18px; }
  .hero-txt p { font-size: 11.5px; }
  .sect-head h2 { font-size: 15px; }
  .card-grid { grid-template-columns: repeat(auto-fill, minmax(112px, 1fr)); }
  .f-btns .fb:not(.big) { display: none; }   /* 只留一个大播放键 */
  .f-center { align-items: stretch; }
  /* 面板头太窄: 状态文字会压住"歌词"按钮, 直接隐藏 (状态仍可从底部/歌词区感知) */
  .panel-hd #status { display: none; }
}
