/* ============================================================
   MUSIC CUP · 音乐世界杯
   深色 · 玻璃拟态 · 紫粉品牌色
   ============================================================ */

/* 自托管字体（拉丁子集，中文使用系统字体：苹方 / MiSans / 雅黑） */
@font-face {
  font-family: "Anton";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/anton-400.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Space Grotesk";
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url("../fonts/space-grotesk.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  --bg: #08080d;
  --panel: rgba(255, 255, 255, .045);
  --panel-2: rgba(255, 255, 255, .08);
  --line: rgba(255, 255, 255, .1);
  --line-soft: rgba(255, 255, 255, .06);
  --grain-seed: 4d16eb0dbd0d;
  --txt: #f4f4f8;
  --dim: rgba(235, 235, 245, .58);
  --dim2: rgba(235, 235, 245, .34);
  --acc1: hsl(258 84% 64%);
  --acc2: hsl(304 78% 66%);
  --acc1-soft: hsl(258 84% 64% / .34);
  --acc2-soft: hsl(304 84% 64% / .26);
  --grad: linear-gradient(135deg, var(--acc1), var(--acc2));
  --r-lg: 26px;
  --r-md: 18px;
  --r-sm: 12px;
  --font-cn: -apple-system, "PingFang SC", "Noto Sans SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
  --font-en: "Space Grotesk", "SF Pro Display", -apple-system, sans-serif;
  --font-display: "Anton", "Space Grotesk", "Arial Narrow", sans-serif;
  --shadow-card: 0 18px 50px -18px rgb(0 0 0 / .65);
  --topbar-h: 58px;
  /* 内容列宽的唯一来源（三套，桌面档各不相同；窄屏都是视口宽）：
       --col-app  = .screen（抽签 / 小组赛 / 淘汰赛 / 冠军页）
       --col-home = .home（首页那一列，SEO 正文、防伪声明、语言链接跟着它走）
       --col-rank = .rk（排行榜）
     顶栏两端的按钮、首页正文宽度、通栏分割线这些都从这里取值算，
     别再各写各的数字——三档一改，跟着列走的元素全部同步。 */
  --col-app: 520px;
  --col-home: 520px;
  --col-rank: 560px;
  /* 顶栏两端按钮（返回 / 重新开始）与撤销引导气泡的横向起点。桌面上它们要跟内容列
     的两边对齐，而不是贴在屏幕两边（用户 2026-07-29 定，同首页左上语言 / 右上排行榜
     的做法）：按钮 42px 宽、图标 21px 居中，图标左缘要压在内容列的 20px 内边距上，
     所以按钮盒比列左缘再外扩 10.5px。窄屏（列宽 = 视口）退回原先贴边的 10px。
     顶栏与气泡是 fixed 定位，这里的 50% 都指视口宽的一半。 */
  --tb-inset: max(10px, calc(50% - var(--col-app) / 2 + 9.5px));
  /* 全站统一从这两个变量取安全区内边距，不直接写 env()——x.com 的 iOS 内置浏览器
     把网页放在低于状态栏、高于 Home 条的卡片式 webview 里，视觉上不会被系统栏遮挡，
     却照样把 env(safe-area-inset-*) 报成整屏值，顶栏与底部 CTA 双重加高。
     util.js 按 UA 检测到它就在 <html> 加 .no-safe-inset 整体归零（见下）。 */
  --safe-top: env(safe-area-inset-top);
  --safe-bottom: env(safe-area-inset-bottom);
}
html.no-safe-inset { --safe-top: 0px; --safe-bottom: 0px; }
/* 列宽的桌面两档（原先散在文件末尾「桌面端适配」里逐条写死，现在只改这里）。
   1100 档三套同宽是历史现状：.home 在这一档本就被 .screen 的 720px 覆盖。 */
@media (min-width: 760px) { :root { --col-app: 640px; --col-home: 560px; --col-rank: 640px; } }
@media (min-width: 1100px) { :root { --col-app: 720px; --col-home: 720px; } }

* { margin: 0; padding: 0; box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

html { color-scheme: dark; }

html { overflow-x: clip; }
body {
  background: var(--bg);
  color: var(--txt);
  font-family: var(--font-cn);
  font-size: 16px;
  line-height: 1.5;
  min-height: 100vh;
  min-height: 100dvh;
  overflow-x: clip;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
input { font: inherit; color: inherit; }
img { display: block; max-width: 100%; }
svg { display: block; }
::selection { background: var(--acc1-soft); }

.en { font-family: var(--font-en); }

/* ---------- 语言切换（仅首页左上角：🌐 + 当前语言名，点击弹语言列表） ---------- */
/* 与首页搜索框左缘严格对齐：内容列左缘 = 50% − 列宽/2 + .screen 的 20px 内边距。
   列宽分三档（.home 基础 520 / ≥760 是 560 / ≥1100 被 .screen 的 720 覆盖），
   三档的 left 因此写在各自的媒体查询里；移动端列宽=视口，左缘就是那 20px 内边距。 */
.lang-switch {
  position: fixed;
  top: calc(var(--safe-top) + 14px);
  left: max(20px, calc(50% - 240px));
  z-index: 50;
  display: inline-flex; align-items: center; gap: 6px;
  /* 透明的等高内衬：与右上角排行榜胶囊（padding 7 + 边框 1）同高，
     两颗按钮共用同一个 top 时中线才对齐 */
  padding: 8px 0;
  font-size: 12px; font-weight: 500; letter-spacing: .04em;   /* 字重与首页歌手 chip 一致 */
  color: rgba(244, 244, 250, .72);
  transition: transform .15s, color .2s, opacity .25s ease;
  user-select: none; -webkit-user-select: none;
}
.lang-switch svg { width: 15px; height: 15px; }
.lang-switch:active { transform: scale(.92); }
[dir="rtl"] .lang-switch { left: auto; right: max(20px, calc(50% - 240px)); }

/* 语言选择弹窗 */
.lang-box { width: min(88vw, 320px); }
.lang-list { display: grid; grid-template-columns: 1fr 1fr; gap: 9px; margin-top: 16px; max-height: min(58vh, 470px); overflow-y: auto; -webkit-overflow-scrolling: touch; }
.lang-item {
  min-height: 46px;
  padding: 0 10px;
  border-radius: 13px;
  background: var(--panel);
  border: 1px solid var(--line);
  font-size: 14px; font-weight: 600;
  color: var(--dim);
  transition: transform .15s, border-color .2s, color .2s;
}
.lang-item:active { transform: scale(.95); }
.lang-item.on {
  color: var(--txt);
  border-color: transparent;
  background: linear-gradient(var(--panel-2), var(--panel-2)) padding-box, var(--grad) border-box;
}

/* ---------- 氛围背景：极光渐变 + 颗粒 ---------- */
.ambient { position: fixed; inset: 0; z-index: -2; overflow: hidden; }
.blob {
  position: absolute;
  width: 72vmax; height: 72vmax;
  border-radius: 50%;
  filter: blur(70px);
  opacity: .5;
  transition: background 1.2s ease;
}
.b1 {
  top: -32vmax; left: -18vmax;
  background: radial-gradient(circle at 35% 35%, var(--acc1-soft), transparent 62%);
  animation: drift1 26s ease-in-out infinite alternate;
}
.b2 {
  bottom: -36vmax; right: -20vmax;
  background: radial-gradient(circle at 60% 60%, var(--acc2-soft), transparent 62%);
  animation: drift2 32s ease-in-out infinite alternate;
}
@keyframes drift1 { to { transform: translate(9vmax, 7vmax) rotate(24deg); } }
@keyframes drift2 { to { transform: translate(-8vmax, -6vmax) rotate(-18deg); } }

.grain {
  position: fixed; inset: 0; z-index: 90;
  pointer-events: none; opacity: .05;
  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='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='.6'/%3E%3C/svg%3E");
}

/* ---------- 顶栏 ---------- */
.topbar[hidden] { display: none; } /* display:flex 会盖过 hidden 属性的默认隐藏，需显式声明 */
.topbar {
  position: fixed; top: 0; left: 0; right: 0; z-index: 40;
  height: calc(var(--topbar-h) + var(--safe-top));
  /* 底色仍然通栏，只有左右两颗按钮收到内容列边上（--tb-inset 见文件开头） */
  padding: var(--safe-top) var(--tb-inset) 0;
  display: flex; align-items: center; gap: 4px;
  background: rgb(10 10 16 / .68);
  backdrop-filter: blur(20px) saturate(1.4);
  -webkit-backdrop-filter: blur(20px) saturate(1.4);
  border-bottom: 1px solid var(--line-soft);
}
.tb-btn {
  width: 42px; height: 42px;
  display: grid; place-items: center;
  border-radius: 50%;
  color: var(--dim);
  transition: color .2s, background .2s, transform .15s;
}
.tb-btn svg { width: 21px; height: 21px; }
.tb-btn:active { transform: scale(.9); background: var(--panel-2); color: var(--txt); }
.tb-btn.off { opacity: .25; pointer-events: none; }
.tb-mid { flex: 1; min-width: 0; text-align: center; line-height: 1.25; }
.tb-artist {
  display: block; font-weight: 700; font-size: 14px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.tb-phase { display: block; font-size: 11px; color: var(--dim); letter-spacing: .04em; }
.tb-prog {
  position: absolute; left: 0; right: 0; bottom: -2px; height: 2px;
  background: rgb(255 255 255 / .06);
}
.tb-prog i {
  display: block; height: 100%;
  background: var(--grad);
  border-radius: 2px;
  transition: width .5s cubic-bezier(.2, .8, .2, 1);
  box-shadow: 0 0 12px var(--acc1-soft);
}

/* 首次进淘汰赛时指向撤销键的一次性引导（见 app.js 的 maybeShowUndoTip）。
   挂在 #overlays 下、自己 fixed 定位，所以位置得跟着 .topbar 的常量手算：
   顶栏内容区高 --topbar-h、按钮 42px 垂直居中 → 按钮底边在 safe-area + 50px；
   气泡再下移 2px 留给箭头。横向同理：气泡左缘与顶栏左内边距同取 --tb-inset（桌面上
   两者一起跟着内容列走），加按钮半宽 21px 就是按钮中心，所以箭头中心要落在气泡内
   21px 处。改顶栏尺寸时这两处要一起改。
   z-index 45 夹在顶栏 40 与弹窗 70 之间：盖得住顶栏，盖不住任何弹窗。 */
.undo-tip {
  position: fixed; z-index: 45;
  top: calc(var(--safe-top) + 52px);
  left: var(--tb-inset);
  max-width: min(272px, calc(100vw - 32px));
  padding: 9px 14px;
  border-radius: var(--r-sm);
  background: var(--grad);
  color: #fff;
  font-size: 13px; font-weight: 600; line-height: 1.4;
  box-shadow: 0 14px 34px -12px rgb(0 0 0 / .8), 0 0 0 1px rgb(255 255 255 / .1);
  cursor: pointer;
  /* 缩放锚在箭头上（= 撤销键中心），入场看着像从那颗按钮里长出来的 */
  transform-origin: 21px top;
  animation: undoTipIn .34s cubic-bezier(.2, .8, .2, 1) both;
}
/* 指向撤销键的小箭头。定位用的是左边缘，而按钮中心要对齐的是箭头中心，
   所以这里减掉半个箭头宽：21 - 11/2 = 15.5px。 */
.undo-tip::before {
  content: ''; position: absolute;
  top: -5px; left: 15.5px;
  width: 11px; height: 11px;
  background: var(--acc1);
  border-radius: 2px;
  transform: rotate(45deg);
}
.undo-tip.out { animation: undoTipOut .3s ease both; }
[dir="rtl"] .undo-tip { left: auto; right: var(--tb-inset); transform-origin: calc(100% - 21px) top; }
[dir="rtl"] .undo-tip::before { left: auto; right: 15.5px; }
@keyframes undoTipIn { from { opacity: 0; transform: translateY(-7px) scale(.96); } }
@keyframes undoTipOut { to { opacity: 0; transform: translateY(-4px); } }

/* ---------- 布局骨架 ---------- */
#app { min-height: 100vh; min-height: 100dvh; display: flex; flex-direction: column; }
.screen {
  width: 100%; max-width: var(--col-app);
  margin: 0 auto;
  padding: 20px 20px calc(40px + var(--safe-bottom));
  flex: 1; display: flex; flex-direction: column;
  /* 注意：.screen 自身只做透明度动画——transform 会把内部 fixed 元素变成相对它定位 */
  animation: screenFade .4s ease both;
}
.screen.with-topbar { padding-top: calc(var(--topbar-h) + var(--safe-top) + 18px); }
.screen.with-cta { padding-bottom: calc(120px + var(--safe-bottom)); }
@keyframes screenFade { from { opacity: 0; } }
@keyframes screenIn { from { opacity: 0; transform: translateY(14px); } }
.phase-head, .draw-head, .stat-row { animation: screenIn .45s cubic-bezier(.2, .8, .2, 1) both; }

/* ---------- 通用元件 ---------- */
.pill {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 13px;
  border-radius: 999px;
  font-size: 12px; font-weight: 700; letter-spacing: .12em;
  color: var(--txt);
  background: var(--panel-2);
  border: 1px solid var(--line);
}
.pill.grad {
  background: var(--grad);
  border: 0;
  box-shadow: 0 4px 18px -6px var(--acc1-soft);
}
.grad-text {
  background: var(--grad);
  -webkit-background-clip: text; background-clip: text;
  color: transparent;
}
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 54px;
  padding: 0 28px;
  border-radius: 999px;
  font-size: 17px; font-weight: 700;
  transition: transform .16s, box-shadow .25s, opacity .2s, filter .2s;
  user-select: none; -webkit-user-select: none;
}
.btn svg { width: 20px; height: 20px; }
.btn:active { transform: scale(.96); }
.btn.primary {
  background: var(--grad);
  color: #fff;
  box-shadow: 0 10px 32px -10px var(--acc1-soft), inset 0 1px 0 rgb(255 255 255 / .25);
}
.btn.primary:disabled {
  opacity: .38; filter: saturate(.4);
  pointer-events: none;
  box-shadow: none;
}
.btn.ghost {
  background: var(--panel);
  border: 1px solid var(--line);
  color: var(--txt);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}
.btn.sm { min-height: 44px; padding: 0 20px; font-size: 15px; }

/* 底部固定操作条 */
.cta-bar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 30;
  padding: 14px 20px calc(16px + var(--safe-bottom));
  display: flex; justify-content: center; gap: 12px;
  background: linear-gradient(to top, rgb(8 8 13 / .96) 55%, transparent);
}
.cta-bar .btn { flex: 1; max-width: 480px; }

/* ============================================================
   首页
   ============================================================ */
/* margin-auto 居中：空间充足时垂直居中，内容超高时可正常从顶部滚动 */
.home {
  gap: 24px;
  max-width: var(--col-home);
  min-height: 100vh; min-height: 100dvh;
  padding-bottom: calc(12px + var(--safe-bottom)); /* 推荐区贴近屏幕底缘 */
}
.home > .hero { margin-top: auto; }
.home > .home-foot { margin-bottom: auto; } /* 推荐区被推到屏幕最底部 */
.hero { text-align: center; padding-top: 10px; }
@keyframes heroFloat { 50% { transform: translateY(-7px) rotate(-3deg); } }

/* 组合字标：MUSIC 匀排 + CUP 巨字渐变 + 奖杯徽章叠角 + 整体斜切 */
.logo-lockup {
  position: relative;
  display: inline-block;
  margin: 0 auto;
  direction: ltr;   /* 品牌字标恒为 LTR：阿语 RTL 下逐字母 <i> 会被镜像成 CISUM */
  transform: skewX(-6deg);
  font-family: var(--font-display);
  font-weight: 400;
  text-transform: uppercase;
  line-height: 1;
  filter: drop-shadow(0 28px 70px var(--acc1-soft));
}
.logo-music {
  display: flex; justify-content: space-between;
  font-size: clamp(23px, 6.4vw, 30px);
  color: var(--txt);
  padding: 0 .12em;
  margin-bottom: 5px;
}
.logo-music i { font-style: normal; }
.logo-cup {
  display: block;
  font-size: clamp(94px, 25vw, 122px);
  line-height: .9;
  letter-spacing: .015em;
  padding-right: .05em;
  background: var(--grad);
  -webkit-background-clip: text; background-clip: text;
  color: transparent;
}
.logo-badge {
  position: absolute; top: 8px; right: -38px;
  font-size: 48px; line-height: 1;
  transform: skewX(6deg) rotate(18deg);
  filter: drop-shadow(0 12px 26px rgb(0 0 0 / .55));
  animation: badgeFloat 5s ease-in-out infinite;
}
@keyframes badgeFloat {
  0%, 100% { transform: skewX(6deg) rotate(18deg); }
  50% { transform: skewX(6deg) rotate(10deg) translateY(-6px); }
}
.slogan {
  margin-top: 24px;
  font-size: clamp(20px, 5.6vw, 24px);
  font-weight: 900;
  letter-spacing: .22em;
  padding-left: .22em; /* 抵消末字字距，保持视觉居中 */
}
/* .22em 大字距只适合方块字短句；拉丁/西里尔/天城文长句按常规标题字距收紧 */
html[lang="en"] .slogan,
html[lang="es"] .slogan,
html[lang="fr"] .slogan,
html[lang="pt-BR"] .slogan,
html[lang="ru"] .slogan,
html[lang="id"] .slogan,
html[lang="de"] .slogan,
html[lang="hi"] .slogan {
  letter-spacing: .03em;
  padding-left: .03em;
}
/* 阿拉伯文是连写文字；泰文含上下组合音标，两者都不适合额外字距 */
html[lang="ar"] .slogan,
html[lang="th"] .slogan {
  letter-spacing: 0;
  padding-left: 0;
}
.tagline { margin-top: 8px; color: var(--dim); font-size: 14px; letter-spacing: .02em; }

.searchbox { position: relative; }
.search-field {
  display: flex; align-items: center; gap: 10px;
  height: 60px;
  padding: 0 20px;
  border-radius: 20px;
  background: var(--panel-2);
  border: 1px solid var(--line);
  /* 不加 backdrop-filter：WebKit 在毛玻璃容器内首次聚焦时光标会错位 */
  transition: border-color .25s, box-shadow .25s;
}
.searchbox.open .search-field,
.search-field:focus-within {
  border-color: var(--acc1-soft);
  box-shadow: 0 0 0 4px hsl(from var(--acc1) h s l / .12), 0 14px 40px -18px var(--acc1-soft);
}
.search-field svg { width: 21px; height: 21px; color: var(--dim); flex: none; }
.search-field input {
  flex: 1; min-width: 0;
  background: none; border: 0; outline: 0;
  font-size: 17px;
}
.search-field input::placeholder { color: var(--dim2); }
.sug {
  position: absolute; top: calc(100% + 10px); left: 0; right: 0; z-index: 20;
  border-radius: 20px;
  background: rgb(21 21 31 / .97);
  border: 1px solid var(--line);
  backdrop-filter: blur(24px) saturate(1.4);
  -webkit-backdrop-filter: blur(24px) saturate(1.4);
  box-shadow: var(--shadow-card);
  overflow: hidden;
  animation: sugIn .22s cubic-bezier(.2, .8, .2, 1) both;
}
@keyframes sugIn { from { opacity: 0; transform: translateY(-6px) scale(.985); } }
.sug:empty { display: none; }
.sug-item {
  display: flex; align-items: center; gap: 13px;
  width: 100%;
  padding: 14px 18px;
  text-align: left;
  transition: background .15s;
}
.sug-item:not(:last-child) { border-bottom: 1px solid var(--line-soft); }
.sug-item:active, .sug-item:hover { background: var(--panel-2); }
/* 建议头像：Apple Music 真艺人照，异步回填；取不到时保持音符占位（同 .noart 手法）。
   圆角方形与站内歌曲封面同风格（圆角随尺寸缩放：30px→8 / 36px→10 / 52px→15），
   2026-07-27 用户定，弃用艺人头像惯用的圆形。 */
.sug-art {
  flex: none; width: 42px; height: 42px;
  border-radius: 11px;
  overflow: hidden;
  display: flex; align-items: center; justify-content: center;
  background: var(--panel-2);
  color: var(--dim);
}
.sug-art img { width: 100%; height: 100%; object-fit: cover; display: block; }
.sug-art svg { width: 18px; height: 18px; }
.sug-meta { min-width: 0; text-align: left; }
.sug-name { display: block; font-weight: 700; font-size: 15px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sug-genre { display: block; font-size: 12px; color: var(--dim); }
.sug-empty { padding: 18px; text-align: center; color: var(--dim); font-size: 14px; }

.chips-wrap { text-align: center; }
.chips-label { font-size: 12px; color: var(--dim2); letter-spacing: .16em; margin-bottom: 12px; }
/* 阿拉伯/天城文是连写文字，泰文含组合音标；.16em 字距会破坏字形 → 归零 */
html[lang="ar"] .chips-label,
html[lang="hi"] .chips-label,
html[lang="th"] .chips-label { letter-spacing: 0; }
.chips { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px; }
.chip {
  padding: 7px 14px;
  border-radius: 999px;
  font-size: 12px; font-weight: 500;
  background: var(--panel);
  border: 1px solid var(--line);
  transition: transform .16s, background .2s, border-color .2s;
}
.chip:active { transform: scale(.94); }
.chip:hover { background: var(--panel-2); border-color: var(--acc1-soft); }

/* 曲库模式开关（纯文字，低调）：选中字亮 + 前置紫色勾，紧贴搜索框 */
.mode-pick { display: flex; justify-content: center; align-items: center; gap: 22px; margin-top: -12px; }
.mode-btn {
  padding: 3px 2px; border: 0; background: none;
  color: var(--dim2); font: inherit; font-size: 12px; cursor: pointer;
  transition: color .18s;
}
.mode-btn.on { color: var(--txt); }
.mode-btn.on::before { content: "✓"; margin-right: 4px; color: var(--acc1); font-size: 10px; }

.home-foot { text-align: center; font-size: 12px; color: var(--dim2); }

/* 首页底部：关于 / 玩法 / 隐私（弱化：纯文字、低对比） */
.home-links {
  width: 100%;
  /* gap 24 而不是原来的 40：三个入口并排，40 的话两条间隙就吃掉 80px，实测日 / 西 /
     法 / 葡 / 俄 / 印尼六种语言在 375px 上放不下。配合底部专用短标签（见 app.js 的
     tplHome），14 种语言在 320px 都排得下；wrap 仍留着兜更极端的字号放大。 */
  display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: 4px 24px;
}
.home-link {
  padding: 6px 2px;
  font: inherit; font-size: 12px; color: var(--dim2);
  text-decoration: none;
  background: none; border: 0; cursor: pointer;
  transition: color .18s;
}
.home-link:hover { color: var(--dim); }
.home-link:active { color: var(--txt); }

/* 三个面板 .brand-btn 里的文字只有这一档：标题 14px/700、副标题 11px。原先第一层
   名片是 13 + 10.5、二级面板的社交按钮是 15，同一类按钮两套字号。跟 .brand-ico
   一样，面板级别不要再各调各的。整行 CTA（.dl-btn）是另一档，见那边。 */
.f-meta { min-width: 0; text-align: start; }
.f-meta b {
  display: block;
  font-size: 14px; font-weight: 700; color: var(--txt);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.f-meta i {
  font-style: normal;
  font-size: 11px; font-weight: 400; color: var(--dim); line-height: 1.35;
  margin-top: 1px;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}

/* 泽卿聊AI 关注面板 */
.zeqing-panel {
  position: relative;
  width: min(90vw, 360px);
  padding: 24px 22px 22px;
  border-radius: var(--r-lg);
  background: rgb(19 19 28 / .98);
  border: 1px solid var(--line);
  box-shadow: var(--shadow-card);
  animation: riPop .35s cubic-bezier(.2, .9, .3, 1.15) both;
}
.zeqing-panel .modal-close { position: absolute; top: 10px; right: 10px; }
.zeqing-head { display: flex; align-items: center; gap: 13px; padding-right: 40px; }
.zeqing-head img { flex: none; width: 52px; height: 52px; border-radius: 15px; object-fit: cover; }
.zeqing-head b { font-size: 20px; font-weight: 900; }
.zeqing-desc {
  margin: 14px 0 20px;
  font-size: 13px; line-height: 1.7;
  color: var(--dim);
}
.zeqing-links { display: flex; gap: 11px; margin-bottom: 20px; }
/* 三个面板里的卡片（名片 / 社交入口 / App 下载）共用这一套盒子，间距与内边距跟
   .dl-btn 保持一致，面板级别只覆盖对齐方式与栅格，不再各调各的 gap 和 padding。 */
.brand-btn {
  flex: 1;
  display: flex; flex-direction: row; align-items: center; justify-content: center; gap: 10px;
  padding: 12px 12px;
  border-radius: 15px;
  background: var(--panel-2);
  border: 1px solid var(--line);
  text-decoration: none; color: var(--txt);
  transition: transform .16s, background .2s, border-color .2s;
}
.brand-btn:active { transform: scale(.95); }
.brand-btn:hover { background: rgb(255 255 255 / .12); border-color: var(--line); }
.brand-btn > span:last-child { font-size: 14px; font-weight: 700; }   /* 与 .f-meta b 同一档 */
/* 三个面板（支持我们 / Yuting / 泽卿聊AI）内容区的品牌与社交图标只有这一档尺寸。
   面板级别不许再各调各的——之前 aboutModal 36、zeqingModal 34、tiimerModal 42
   三种并存，就是三次局部覆盖攒出来的。顶部头图是另一档，见 .zeqing-head img。 */
.brand-ico {
  flex: none;
  width: 36px; height: 36px;
  border-radius: 10px;
  display: grid; place-items: center;
  color: #fff;
}
/* 各品牌图形的密度不同，svg 占容器的比例分别调过，让视觉大小看齐：小红书 .71
   （方块字最实）、Threads .65、X .53（细长字母，留白多才不显得比别的重）。
   改 .brand-ico 尺寸时这三个数要按比例一起改，否则视觉重量就散了。 */
.brand-img { object-fit: cover; } /* 抖音用真实 app 图标（自带黑底彩色音符） */
.brand-ico.xhs { background: #ff2442; }
.brand-ico.xhs svg { width: 25px; height: 25px; }
.brand-ico.threads { background: #000; }
.brand-ico.threads svg { width: 23px; height: 23px; }
.brand-ico.x { background: #000; }
.brand-ico.x svg { width: 19px; height: 19px; }
/* 泽卿面板 4 个社交按钮：抖音/小红书 一行、Instagram/Threads 一行（2×2 换行），内容左对齐（同 Yuting 面板） */
#zeqingModal .zeqing-links { flex-wrap: wrap; }
#zeqingModal .brand-btn { flex: 1 1 calc(50% - 5.5px); justify-content: flex-start; }
/* 视频号二维码那一块（.zeqing-qr）已从面板移除，样式一并删掉。译文按本项目对
   退役文案的惯例留在 js/i18n.js 里，见 tests/i18n.test.mjs 的退役清单。 */

/* Tiimer / 作者 Yuting 面板：整行 App 下载按钮 + 联系小字。
   这颗虽然是整行，但和三个面板里的名片、社交按钮属于同一族卡片，图标与文字
   都走同一档（36px / 14px / 11px），不再自成一格。 */
.dl-btn {
  display: flex; align-items: center; gap: 10px;
  width: 100%; box-sizing: border-box;
  padding: 12px 12px;
  border-radius: 15px;
  background: var(--panel-2);
  border: 1px solid var(--line);
  text-decoration: none; color: var(--txt);
  transition: transform .16s, background .2s, border-color .2s;
}
.dl-btn:active { transform: scale(.98); }
.dl-btn:hover { background: rgb(255 255 255 / .12); }
.dl-btn .brand-img { flex: none; width: 36px; height: 36px; border-radius: 10px; object-fit: cover; }
.dl-btn span { display: flex; flex-direction: column; line-height: 1.35; min-width: 0; }
.dl-btn span b { font-size: 14px; font-weight: 700; }
.dl-btn span i { font-size: 11px; color: var(--dim); font-style: normal; }
.contact-label { text-align: left; font-size: 12px; color: var(--dim); margin: 16px 0 12px; letter-spacing: .02em; }
/* Tiimer 面板：社交按钮左对齐。尺寸、gap、padding 都不在这里覆盖了 */
#tiimerModal .brand-btn { justify-content: flex-start; }

/* 首页 SEO 正文（.screen.home 之后的独立区块）：首屏是 100dvh 的 app 界面，
   这一段从第二屏开始——用户下滑才看到，爬虫拿到的是全权重可见正文 + FAQ。
   玩法介绍弹窗与底部入口已移除（2026-07-26，用户定），玩法说明只有这一份。 */
/* 列宽跟着首页那一列走（用户 2026-07-29 定）：桌面档 .home 会宽到 560 / 720，
   这块正文原先钉死 520px，于是文字比上面的搜索框窄了一圈、也比下面的防伪声明卡窄。
   三处统一取 --col-home，左右各 20px 内边距后，正文与搜索框严格同宽。 */
.home-seo {
  width: 100%; max-width: var(--col-home);
  margin: 0 auto;
  /* 底部留白交给紧跟其后的 .lang-links（它总是存在，服务端与客户端都渲染），
     这里只留到语言链接的间距 */
  padding: 44px 20px 30px;
  display: grid; gap: 14px;
  font-size: 13px; line-height: 1.7; color: var(--dim);
}
.home-seo h2 { margin: 0; font-size: 19px; line-height: 1.4; color: var(--txt); }
.home-seo h3 { margin: 14px 0 0; font-size: 15px; color: var(--txt); }
.home-seo h4 { margin: 0 0 3px; font-size: 13px; color: var(--txt); }
.home-seo p, .home-seo .faq-item, .home-seo .feature-item { margin: 0; }
.home-seo ol {
  margin: 0; padding: 0 0 0 1.35em;
  display: grid; gap: 10px;
}
[dir="rtl"] .home-seo ol { padding: 0 1.35em 0 0; }
.home-seo ol li::marker { color: var(--acc1); font-weight: 700; }
/* 官方身份区（2026-07-26，替代单句 official-note）：正文最末一块，上有细分隔线。
   位置是刻意的——仿冒站 musiccup.run / .net / .org 抄得走文案，抄不走域名、
   公司与双向互链的真实账号，所以这块的重点是可核验的事实，不是「官方」二字。 */
.home-seo .official-head {
  padding-top: 14px;
  border-top: 1px solid rgba(255, 255, 255, .07);
}
/* 身份区里的链接：链接行（官号 + 公司）与正文里的作者名超链接共用下划线样式 */
.home-seo a {
  color: inherit;
  text-decoration: underline; text-underline-offset: 3px;
  transition: color .18s;
}
.home-seo a:hover { color: var(--txt); }
.home-seo .official-links { display: flex; flex-wrap: wrap; gap: 5px 16px; }
.home-seo .official-links a { font-size: 12px; color: var(--dim2); }
.home-seo .official-links a:hover { color: var(--txt); }

/* 页面最底部的防伪声明卡（在 .home-seo 之后、语言链接之前） */
.official-notice {
  margin: 26px auto 30px;
  /* 卡片的描边要与搜索框描边同宽：列宽减去两侧各 20px 的内容内边距 */
  max-width: calc(var(--col-home) - 40px);
  width: calc(100% - 40px);
  padding: 13px 16px;
  border: 1px solid rgba(255, 200, 87, .32);
  border-radius: 14px;
  background: rgba(255, 200, 87, .05);
  font-size: 12.5px;
  line-height: 1.75;
  color: var(--dim);
  text-align: start;
}
.official-notice p { margin: 0; }
.official-notice .notice-head { margin-bottom: 3px; font-weight: 700; color: #ffd77a; }

/* 页面最底部的语言链接（home-view.js 的 langNav）：14 个语言版本的真 <a>，
   服务端 HTML 里就有。文字压到 12px / --dim2，与 .home-link 同一档——它是页脚
   而不是导航重点；换行靠 wrap，阿语等 RTL 页面由 flex 自动镜像，不用另写规则。
   居中的一排原生语言名同时也是给人看的：读不懂当前语言的访客不必先找左上角
   那颗按钮。 */
.lang-links {
  width: 100%; max-width: var(--col-home);
  margin: 0 auto;
  padding: 0 20px calc(56px + var(--safe-bottom));
  text-align: center;
}
.lang-links-title {
  margin: 0 0 9px;
  font-size: 11px; color: var(--dim2); letter-spacing: .06em; text-transform: uppercase;
}
.lang-links-list { display: flex; flex-wrap: wrap; justify-content: center; gap: 5px 16px; }
.lang-link {
  padding: 3px 2px;
  font-size: 12px; color: var(--dim2); text-decoration: none;
  transition: color .18s;
}
.lang-link:hover { color: var(--txt); }
.lang-link.on { color: var(--dim); }

/* 首页左上语言、右上排行榜滚过首屏后的淡出规则在 .rank-fab 那节
   （body.mc-scrolled，app.js 维护），不写在这里——
   tests/home-rank-entry.test.mjs 按「文件中第一个 .rank-fab 块」取样式做断言。 */

/* 支持我们面板：内容比另外两个面板长，面板自身可滚动（小屏 / 微信 webview 下必须） */
.about-panel { max-height: 100%; padding: 20px 22px; overflow-y: auto; -webkit-overflow-scrolling: touch; }
.about-panel .zeqing-desc { margin: 13px 0 16px; }
/* 作者与朋友两张名片：图标 + 两行文字，左对齐；X 账号那张独占一行。
   字号与间距不在这里覆盖，跟其余卡片同一档（见 .f-meta / .brand-btn）。 */
#aboutModal .brand-btn { justify-content: flex-start; }
.about-accounts { margin-bottom: 0; }
.about-panel .contact-label { margin: 15px 0 9px; }
/* 打赏：所有语言都有 Stripe 按钮；简体额外在按钮上方并排两张收款码 */
/* 二维码限宽 124px：小屏（SE 一类）下整块打赏区尽量不用滚动就能看全 */
.donate-qrs { display: grid; grid-template-columns: repeat(2, minmax(0, 124px)); justify-content: center; gap: 16px; }
.donate-qrs figure { margin: 0; text-align: center; }
.donate-qrs img {
  display: block; width: 100%; height: auto;
  border-radius: 12px;
  background: #fff; padding: 7px;
  box-sizing: border-box;
}
.donate-qrs figcaption { margin-top: 8px; font-size: 11.5px; color: var(--dim); }
/* 收款码与银行卡按钮之间的分隔说明（仅简体） */
.donate-or { text-align: center; font-size: 11.5px; color: var(--dim2); margin: 14px 0 10px; }
.donate-btn {
  display: flex; align-items: center; justify-content: center; gap: 9px;
  padding: 13px 16px;
  border-radius: 14px;
  border: 1px solid var(--line);
  background: rgb(255 255 255 / .08);
  color: var(--txt); font-size: 14px; font-weight: 700;
  text-decoration: none;
  transition: background .15s, transform .1s;
}
.donate-btn:hover { background: rgb(255 255 255 / .12); }
.donate-btn:active { transform: scale(.98); }
.donate-btn svg { flex: none; width: 19px; height: 19px; color: var(--dim); }

/* ---------- 加载 ---------- */
.screen.loading { max-width: var(--col-home); align-items: center; gap: 26px; min-height: 100vh; min-height: 100dvh; text-align: center; }
.screen.loading > .spinner { margin-top: auto; }
.screen.loading > div:last-child { margin-bottom: auto; }
.spinner { position: relative; width: 86px; height: 86px; }
.spinner::before, .spinner::after {
  content: ""; position: absolute; inset: 0;
  border-radius: 50%;
  border: 3px solid transparent;
}
.spinner::before {
  border-top-color: var(--acc1); border-right-color: var(--acc2);
  animation: spin 1s linear infinite;
}
.spinner::after {
  inset: 12px;
  border-bottom-color: var(--acc2);
  opacity: .55;
  animation: spin 1.6s linear infinite reverse;
}
.spinner .note { position: absolute; inset: 0; display: grid; place-items: center; }
.spinner .note svg { width: 30px; height: 30px; color: var(--txt); animation: heroFloat 2.6s ease-in-out infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
.loading-artist { font-size: 22px; font-weight: 900; }
.loading-tip { color: var(--dim); font-size: 14px; min-height: 22px; transition: opacity .3s; }

/* ============================================================
   抽签结果
   ============================================================ */
.draw-head { text-align: center; margin-bottom: 24px; }
.draw-head h2 { font-size: 30px; font-weight: 900; margin: 12px 0 6px; }
.draw-head .sub { color: var(--dim); font-size: 14px; }
.stat-row {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px;
  margin-bottom: 24px;
}
/* 纯淘汰赛的名单页没有「小组」这一格，两格均分 */
.stat-row.two { grid-template-columns: repeat(2, 1fr); }

/* 名单页：赛事规模切换 + 歌名过滤（自选模式曲库可达 200 首，靠滚动找歌不现实） */
.size-pick { display: flex; justify-content: center; flex-wrap: wrap; gap: 8px; margin-bottom: 18px; }
.size-btn {
  padding: 7px 15px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--panel);
  color: var(--dim2); font: inherit; font-size: 13px; cursor: pointer;
  transition: color .18s, border-color .18s;
}
.size-btn.on { color: var(--txt); border-color: var(--acc1); }
/* 放在页面最顶、并粘住顶栏下方。位置是关键：iOS 键盘弹出时会把被遮住的输入框顶上来，
   搜索框若排在标题/统计条之后（屏幕 60% 处）必被遮住，整页就会跳一下；放在最顶端
   则一直在键盘上方，浏览器无需滚动。底色必须不透明，否则滚动时卡片会透上来。 */
.lineup-search {
  position: sticky;
  top: calc(var(--topbar-h) + var(--safe-top) + 6px);
  z-index: 6;
  display: flex; align-items: center; gap: 9px;
  height: 44px; padding: 0 10px 0 16px; margin-bottom: 20px;
  border-radius: 14px;
  background-color: var(--bg);
  background-image: linear-gradient(var(--panel-2), var(--panel-2));
  border: 1px solid var(--line);
  box-shadow: 0 10px 22px -14px #000;
  transition: border-color .25s;
}
.lineup-search:focus-within { border-color: var(--acc1-soft); }
.lineup-search > svg { width: 18px; height: 18px; color: var(--dim); flex: none; }
.lineup-search input {
  flex: 1; min-width: 0;
  background: none; border: 0; outline: 0;
  /* 必须 ≥16px：iOS Safari / 微信 webview 对更小的输入框会自动放大整页，
     放大后页面比视口宽（能横向滚动）且不会自动还原，退回首页仍是放大状态。 */
  font-size: 16px; color: inherit;
}
.lineup-search input::placeholder { color: var(--dim2); }
/* 键盘打开时收起底部按钮：iOS 的 visual viewport 缩小后，fixed 元素仍按原视口算
   bottom: 0，滚动时会飘到内容中间。搜索时那颗按钮本来也用不上。 */
body.kb-open .cta-bar { display: none; }
/* iOS 原生清除按钮在 webview 里时有时无，自己画一个 */
.lineup-search input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; }
.lq-clear {
  flex: none; width: 28px; height: 28px; padding: 0;
  display: flex; align-items: center; justify-content: center;
  border: 0; border-radius: 50%;
  background: var(--panel-2); color: var(--dim);
  cursor: pointer;
}
.lq-clear[hidden] { display: none; }
.lq-clear svg { width: 14px; height: 14px; }
.lq-clear:active { background: var(--line); color: var(--txt); }
.lineup-note { margin: -6px 0 14px; text-align: center; font-size: 12.5px; color: var(--dim2); }
.lineup-note.busy::after {
  content: ""; display: inline-block; vertical-align: -1px; margin-left: 6px;
  width: 10px; height: 10px; border-radius: 50%;
  border: 1.5px solid var(--line); border-top-color: var(--acc1);
  animation: spin .7s linear infinite;
}
.stat {
  padding: 16px 8px 13px;
  text-align: center;
  border-radius: var(--r-md);
  background: var(--panel);
  border: 1px solid var(--line-soft);
}
.stat b {
  display: block;
  font-family: var(--font-display);
  font-size: 32px; font-weight: 400; line-height: 1.1;
  letter-spacing: .02em;
}
.stat span { font-size: 12px; color: var(--dim); }
.group-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.gcard {
  min-width: 0;
  border-radius: var(--r-md);
  background: var(--panel);
  border: 1px solid var(--line-soft);
  padding: 13px 13px 11px;
  animation: screenIn .5s cubic-bezier(.2, .8, .2, 1) both;
}
.gcard-head {
  display: flex; align-items: baseline; gap: 7px;
  margin-bottom: 10px;
}
.gcard-head b {
  font-family: var(--font-display);
  font-size: 22px; font-weight: 400; font-style: italic;
}
.gcard-head span { font-size: 11px; color: var(--dim2); letter-spacing: .1em; }
.gsong { display: flex; align-items: center; gap: 9px; padding: 4.5px 0; min-width: 0; }
.gsong img, .gsong .noart {
  flex: none;
  width: 30px; height: 30px;
  border-radius: 8px;
  object-fit: cover;
  background: var(--panel-2);
}
.gsong .noart { display: grid; place-items: center; color: var(--dim2); }
.gsong .noart svg { width: 14px; height: 14px; }
.gsong span {
  flex: 1; min-width: 0;
  font-size: 12.5px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* ============================================================
   阶段公共头部
   ============================================================ */
.phase-head { text-align: center; margin-bottom: 22px; }
.phase-head h2 {
  font-size: 34px; font-weight: 900;
  margin: 13px 0 5px;
  letter-spacing: .01em;
}
.phase-head h2 .en {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 1.12em;
  /* 合成斜体会让字形溢出行内盒、被 background-clip:text 截断，改用 transform 斜切 */
  display: inline-block;
  transform: skewX(-8deg);
  padding: 0 .06em;
}
.phase-head .sub { color: var(--dim); font-size: 14px; }
.phase-head .count {
  font-family: var(--font-en);
  color: var(--dim);
  font-size: 13px; letter-spacing: .1em;
  margin-top: 5px;
}

/* ============================================================
   小组赛：2×2 选歌卡
   ============================================================ */
.pick-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 13px; }
.scard {
  position: relative;
  min-width: 0;
  border-radius: var(--r-md);
  background: var(--panel);
  border: 1.5px solid var(--line-soft);
  overflow: hidden;
  cursor: pointer;
  transition: transform .18s cubic-bezier(.2, .8, .2, 1), border-color .2s, box-shadow .25s, background .2s;
  animation: screenIn .45s cubic-bezier(.2, .8, .2, 1) both;
}
.scard:active { transform: scale(.96); }
.scard .art { position: relative; background: var(--panel-2); }
/* 用 padding 撑正方形，不依赖 aspect-ratio：老 webview 会拿图片本身像素高度撑高卡片 */
.scard .art::before { content: ""; display: block; padding-top: 100%; }
.scard .art img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.scard .art .noart { position: absolute; inset: 0; display: grid; place-items: center; color: var(--dim2); }
.scard .art .noart svg { width: 34px; height: 34px; }
.scard .meta { padding: 10px 12px 12px; }
.scard .tname {
  font-size: 14.5px; font-weight: 700; line-height: 1.3;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
  min-height: 2.6em;
}
.scard .talbum {
  margin-top: 3px;
  font-size: 11.5px; color: var(--dim2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.play-btn {
  position: absolute; right: 8px; bottom: 8px; z-index: 3;
  width: 38px; height: 38px;
  display: grid; place-items: center;
  border-radius: 50%;
  color: #fff;
  background: rgb(12 12 18 / .55);
  border: 1px solid rgb(255 255 255 / .22);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  transition: transform .16s, background .2s;
}
.play-btn svg { width: 17px; height: 17px; }
.play-btn:active { transform: scale(.88); }
.play-btn.playing { background: var(--grad); border-color: transparent; }
/* 纯 CSS 圆环加载器（iOS 对 svg 元素做旋转动画会出现拉伸残影） */
.ldr {
  display: block;
  width: 15px; height: 15px;
  border-radius: 50%;
  border: 2.5px solid rgb(255 255 255 / .28);
  border-top-color: #fff;
  animation: spin .8s linear infinite;
}
.check {
  position: absolute; top: 9px; right: 9px; z-index: 3;
  width: 28px; height: 28px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--grad);
  color: #fff;
  font-size: 15px; font-weight: 900;
  opacity: 0; transform: scale(.4);
  transition: opacity .18s, transform .22s cubic-bezier(.34, 1.56, .64, 1);
  box-shadow: 0 4px 14px -4px var(--acc1-soft);
}
.scard.picked { border-color: transparent; box-shadow: 0 0 0 2px var(--acc1), 0 14px 38px -14px var(--acc1-soft); }
.scard.picked .check { opacity: 1; transform: scale(1); }
.scard.picked .art img { filter: saturate(1.12); }

/* ============================================================
   遗珠复活赛：3 列小卡
   ============================================================ */
/* 名单页排序切换：纯文字开关（按热度 | 按专辑），不做成胶囊标签——
   页面顶部已经有一排「32/16/8/4 强」的胶囊，再来一排会分不清哪个是赛制哪个是排序 */
.lineup-sort { display: flex; align-items: center; margin: -8px 0 14px; padding-left: 2px; }
.sort-switch { display: flex; align-items: center; gap: 9px; }
.sort-link {
  padding: 7px 2px;   /* 纯文字本身热区太小，靠上下留白把可点高度撑到 34px */
  border: 0; background: none;
  color: var(--dim2); font: inherit; font-size: 13px; cursor: pointer;
  transition: color .18s;
}
.sort-link.on { color: var(--txt); font-weight: 700; }
.sort-sep { color: var(--line); font-size: 12px; user-select: none; }
/* 抽签方式勾选，贴这行最右（RTL 下自动翻到最左） */
.sort-rand {
  margin-inline-start: auto;
  display: flex; align-items: center; gap: 6px;
  padding: 7px 2px;
  border: 0; background: none;
  color: var(--dim2); font: inherit; font-size: 13px; cursor: pointer;
  transition: color .18s;
}
.sort-rand .rbox {
  width: 15px; height: 15px;
  display: grid; place-items: center;
  border: 1.5px solid var(--line); border-radius: 5px;
  font-size: 10px; line-height: 1; color: transparent;
  transition: background .18s, border-color .18s, color .18s;
}
.sort-rand.on { color: var(--txt); }
.sort-rand.on .rbox { background: var(--grad); border-color: transparent; color: #fff; }

.wild-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
/* 专辑分组标题：占满整行网格 */
.wgroup {
  grid-column: 1 / -1;
  display: flex; align-items: baseline; gap: 8px;
  margin: 10px 2px 0;
  padding-bottom: 7px;
  border-bottom: 1px solid var(--line-soft);
}
.wgroup:first-child { margin-top: 0; }
.wgroup[hidden] { display: none; }
.wg-name {
  flex: 1; min-width: 0;
  font-size: 13px; font-weight: 700; line-height: 1.35;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.wg-year { flex: none; font-size: 11.5px; color: var(--dim2); letter-spacing: .04em; }
.wg-count {
  flex: none;
  min-width: 18px; padding: 1px 6px;
  border-radius: 999px;
  background: var(--panel);
  font-size: 10.5px; color: var(--dim2); text-align: center;
}
.wcard {
  position: relative;
  min-width: 0;
  border-radius: 14px;
  background: var(--panel);
  border: 1.5px solid var(--line-soft);
  overflow: hidden;
  cursor: pointer;
  transition: transform .16s, border-color .2s, box-shadow .2s;
  animation: screenIn .4s cubic-bezier(.2, .8, .2, 1) both;
}
.wcard:active { transform: scale(.94); }
/* 受邀场的名单不可改（见 app.js lineupCard），按压反馈也要一并撤掉，别暗示能点 */
.wcard.locked:active { transform: none; }
.wcard .art { position: relative; background: var(--panel-2); }
.wcard .art::before { content: ""; display: block; padding-top: 100%; }
.wcard .art img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.wcard .art .noart { position: absolute; inset: 0; display: grid; place-items: center; color: var(--dim2); }
.wcard .art .noart svg { width: 22px; height: 22px; }
.wcard .wname {
  padding: 7px 8px 9px;
  font-size: 11.5px; font-weight: 500; line-height: 1.3;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
  min-height: 2.9em;
}
.gbadge {
  position: absolute; top: 6px; left: 6px; z-index: 2;
  min-width: 20px; height: 20px;
  padding: 0 5px;
  display: grid; place-items: center;
  border-radius: 7px;
  font-family: var(--font-en);
  font-size: 11px; font-weight: 700; font-style: italic;
  color: rgb(255 255 255 / .88);
  background: rgb(12 12 18 / .58);
  border: 1px solid rgb(255 255 255 / .16);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}
.wcard .check { width: 24px; height: 24px; font-size: 13px; top: 6px; right: 6px; }
.wcard .play-btn { width: 32px; height: 32px; right: 6px; bottom: 6px; }
.wcard .play-btn svg { width: 14px; height: 14px; }
.wcard.picked { border-color: transparent; box-shadow: 0 0 0 2px var(--acc1), 0 12px 30px -12px var(--acc1-soft); }
.wcard.picked .check { opacity: 1; transform: scale(1); }
.wild-counter {
  font-family: var(--font-en);
  font-weight: 700; font-size: 15px;
  transition: transform .2s;
}
.wild-counter.bump { animation: bump .3s; }
@keyframes bump { 40% { transform: scale(1.25); } }

/* ============================================================
   淘汰赛：对决
   ============================================================ */
.duel-wrap { flex: 1; display: flex; flex-direction: column; justify-content: center; }
.duel {
  position: relative;
  display: grid; grid-template-columns: 1fr 1fr; gap: 14px;
  align-items: stretch;
}
.dcard {
  position: relative;
  min-width: 0;
  border-radius: var(--r-lg);
  background: var(--panel);
  border: 1.5px solid var(--line-soft);
  overflow: hidden;
  cursor: pointer;
  transition: transform .22s cubic-bezier(.2, .8, .2, 1), border-color .2s, box-shadow .3s, opacity .3s, filter .3s;
  animation: duelIn .5s cubic-bezier(.2, .8, .2, 1) both;
}
.dcard:nth-child(3) { animation-delay: .08s; }
@keyframes duelIn { from { opacity: 0; transform: translateY(22px) scale(.96); } }
.dcard:active { transform: scale(.965); }
.dcard .art { position: relative; background: var(--panel-2); }
.dcard .art::before { content: ""; display: block; padding-top: 100%; }
.dcard .art img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.dcard .art .noart { position: absolute; inset: 0; display: grid; place-items: center; color: var(--dim2); }
.dcard .art .noart svg { width: 44px; height: 44px; }
/* 淘汰赛的播放键：视觉仍是 38px，命中区靠这层透明外扩撑到 44（iOS HIG 的下限）。
   只给 .dcard 加——小组赛和自选页的按钮一样大，但那两页点偏只是切换一个可反悔的
   勾选，不值得为此牺牲卡片的可点面积；而这一页整张卡都是判胜热区，手指落在按钮外
   一圈就等于把这首歌判胜、对手当场出局（2026-07-28 用户反馈，见 undo-tip 那组注释）。
   代价是卡片右下角 44×44 不再判胜——可接受，想选这首歌的人点的是封面和歌名。 */
.dcard .play-btn::before { content: ''; position: absolute; inset: -3px; }
.dcard .meta { padding: 12px 13px 15px; }
.dcard .tname {
  font-size: 15.5px; font-weight: 900; line-height: 1.3;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
  min-height: 2.6em;
}
.dcard .talbum {
  margin-top: 4px;
  font-size: 11.5px; color: var(--dim2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.dcard.winner {
  border-color: transparent;
  box-shadow: 0 0 0 2.5px var(--acc1), 0 22px 60px -18px var(--acc1-soft);
  transform: scale(1.035);
  z-index: 2;
}
.dcard.loser { opacity: .32; filter: saturate(.15) brightness(.75); transform: scale(.94); }
.vs-badge {
  position: absolute; left: 50%; top: 50%; z-index: 5;
  transform: translate(-50%, -50%);
  width: 54px; height: 54px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: rgb(14 14 21 / .88);
  border: 1px solid var(--line);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  box-shadow: 0 10px 34px -8px rgb(0 0 0 / .7), 0 0 0 5px rgb(255 255 255 / .03);
  pointer-events: none;
}
.vs-badge b {
  font-family: var(--font-display);
  font-weight: 400; font-size: 18px;
  letter-spacing: .03em;
  display: inline-block;
  transform: skewX(-8deg);
  padding: 0 .06em;
  background: var(--grad);
  -webkit-background-clip: text; background-clip: text;
  color: transparent;
}
.duel-hint { text-align: center; color: var(--dim2); font-size: 12.5px; margin-top: 22px; letter-spacing: .06em; }

/* ---------- 站点字标 MUSICCUP.APP ----------
   页面被截图/录屏分享出去时认得出来源。画法与分享图 share.js drawWordmark2 一致 */
.wordmark {
  direction: ltr;   /* 品牌字标恒为 LTR：阿语 RTL 下不镜像成 PPA.PUCCISUM */
  text-align: center;
  font-family: var(--font-display);
  font-weight: 400;
  line-height: 1;
  letter-spacing: .04em;
  user-select: none; -webkit-user-select: none;
}
/* 合成斜体会让字形溢出行内盒、被 background-clip:text 截断，与分享图一样改用斜切 */
.wordmark b, .wordmark i {
  display: inline-block;
  font-weight: 400; font-style: normal;
  transform: skewX(-8deg);
}
.wordmark b {
  /* 横向渐变而不是全站 --grad 的 135deg：字标很扁，斜切渐变在这里只会让色带走位，
     且分享图 canvas 侧同样是横跨文字的水平渐变，两边要对得上 */
  background: linear-gradient(90deg, var(--acc1), var(--acc2));
  -webkit-background-clip: text; background-clip: text;
  color: transparent;
}
.wordmark i { color: var(--txt); }

/* 对战页底部：绝对定位在 .screen 已有的 40px 下内边距里——对战页要求一屏放得下，
   字标不能占据布局高度，否则矮机型（iPhone SE 那档）会被顶出滚动条 */
.duel-screen { position: relative; }   /* transform 会让内部 fixed 元素改基准，这里只用 relative */
.duel-brand {
  position: absolute;
  left: 0; right: 0;
  bottom: calc(11px + var(--safe-bottom));
  font-size: 15px;
  opacity: .85;
  pointer-events: none;   /* 不抢卡片的点击 */
}

/* 轮次过场 */
.round-intro {
  position: fixed; inset: 0; z-index: 70;
  display: grid; place-items: center;
  background: rgb(6 6 10 / .82);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  animation: fadeIn .25s both;
}
.round-intro.out { animation: fadeOut .3s both; }
@keyframes fadeIn { from { opacity: 0; } }
@keyframes fadeOut { to { opacity: 0; } }
.ri-box { text-align: center; animation: riPop .55s cubic-bezier(.2, .9, .3, 1.2) both; }
@keyframes riPop { from { opacity: 0; transform: scale(.7) translateY(16px); } }
.ri-box .pill { margin-bottom: 18px; }
.ri-title {
  font-size: clamp(44px, 13vw, 66px);
  font-weight: 900; line-height: 1.05;
  letter-spacing: .02em;
}
.ri-sub { margin-top: 12px; color: var(--dim); font-size: 15px; }

/* ============================================================
   冠军页
   ============================================================ */
.champ { align-items: center; text-align: center; }
/* 冠军页顶部用站点字标而不是皇冠：这一页是最常被截图外发的一屏 */
.champ-brand { font-size: 25px; margin-bottom: 12px; filter: drop-shadow(0 10px 30px var(--acc1-soft)); }
.champ-label { letter-spacing: .3em; font-size: 13px; color: var(--dim); margin-bottom: 18px; }
.champ-art-wrap { position: relative; width: min(64vw, 300px); margin-bottom: 22px; }
.champ-art-wrap::before {
  content: ""; position: absolute; inset: -14%;
  background: var(--grad);
  filter: blur(48px); opacity: .4;
  border-radius: 40px;
  z-index: -1;
  animation: glowPulse 3.6s ease-in-out infinite;
}
/* 只做透明度呼吸，不缩放——缩放 blur 元素会强制每帧重算模糊，拖垮帧率 */
@keyframes glowPulse { 50% { opacity: .62; } }
/* 礼花期间临时关掉封面光晕，减轻整页合成负担 */
.champ-art-wrap.mc-celebrating::before { display: none; }
.champ-art-wrap img, .champ-art-wrap .noart {
  width: 100%; aspect-ratio: 1;
  border-radius: 30px;
  object-fit: cover;
  border: 1px solid rgb(255 255 255 / .18);
  box-shadow: 0 30px 80px -20px rgb(0 0 0 / .8);
  animation: champIn .7s cubic-bezier(.2, .9, .3, 1.1) both;
}
.champ-art-wrap .noart { display: grid; place-items: center; color: var(--dim2); background: var(--panel-2); }
.champ-art-wrap .noart svg { width: 70px; height: 70px; }
/* 冠军封面右下角试听键：等封面入场动画走完再淡入 */
.champ-art-wrap .play-btn {
  right: 12px; bottom: 12px;
  width: 46px; height: 46px;
  animation: fadeIn .5s .45s both;
}
.champ-art-wrap .play-btn svg { width: 19px; height: 19px; }
/* 礼花期间同样卸掉按钮的毛玻璃（又一层每帧重算的 blur） */
.champ-art-wrap.mc-celebrating .play-btn {
  backdrop-filter: none; -webkit-backdrop-filter: none;
  background: rgb(12 12 18 / .72);
}
@keyframes champIn { from { opacity: 0; transform: scale(.82) rotate(-3deg); } }
.champ-title {
  font-size: clamp(28px, 8vw, 38px); font-weight: 900; line-height: 1.2;
  /* 双语连写名可能很长：两行截断加省略号 */
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; max-width: 92%;
}
.champ-album { color: var(--dim); font-size: 14px; margin: 8px 0 26px; }
.podium {
  width: 100%;
  display: grid; grid-template-columns: 1fr 1fr; gap: 11px;
  margin-bottom: 28px;
}
.pod {
  display: flex; align-items: center; gap: 11px;
  padding: 11px;
  border-radius: var(--r-md);
  background: var(--panel);
  border: 1px solid var(--line-soft);
  text-align: left;
  min-width: 0;
}
.pod.silver { grid-column: 1 / -1; background: linear-gradient(120deg, rgb(255 255 255 / .09), var(--panel) 60%); border-color: var(--line); }
.pod-art {
  position: relative; flex: none;
  display: block; padding: 0;
  width: 46px; height: 46px;
  border-radius: 11px; overflow: hidden;
  background: var(--panel-2);
  transition: transform .16s;
}
button.pod-art:active { transform: scale(.92); }
.pod img, .pod .noart { width: 100%; height: 100%; object-fit: cover; }
.pod .noart { display: grid; place-items: center; color: var(--dim2); }
.pod .noart svg { width: 20px; height: 20px; }
/* 小图整块即播：常驻半透明遮罩 + 播放图标，明示可点 */
.play-ico {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  color: #fff;
  background: rgb(8 8 14 / .36);
  transition: background .2s;
}
/* 亮色封面上光靠白图标会糊掉，补一层投影 */
.play-ico svg { width: 16px; height: 16px; filter: drop-shadow(0 1px 3px rgb(0 0 0 / .55)); }
.play-ico .ldr { width: 13px; height: 13px; border-width: 2px; }
.play-ico.playing { background: rgb(8 8 14 / .26); }
.pod-meta { min-width: 0; }
.pod-rank { font-size: 10.5px; letter-spacing: .14em; color: var(--dim); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pod-name { font-size: 13.5px; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.champ-actions { width: 100%; display: flex; flex-direction: column; gap: 11px; }
/* 「再来一场」与「和好友一起玩」并排同款（同色同高同字号），但**不对半分宽**：
   后者在 14 种语言里都更长（"Mit Freunden spielen" / "Играть с друзьями"…），
   对半分时实测 7 种语言要折成两行；按 .8 : 1.2 分配后 375px 屏上全部单行。
   span 仍留两行的 line-clamp 兜底，将来新增语言最坏也只是折行，不会溢出或撑高按钮。 */
.champ-more { display: flex; gap: 11px; }
.champ-more .btn { min-width: 0; padding: 0 8px; font-size: 14px; gap: 5px; }
.champ-more .btn:first-child { flex: .8; }
.champ-more .btn:last-child { flex: 1.2; }
.champ-more .btn svg { flex: none; width: 16px; height: 16px; }
.champ-more .btn span {
  min-width: 0; text-align: center; line-height: 1.2;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
/* 320~359px 的老机型（iPhone SE 一代等）横排怎么调都塞不下法/德/俄，直接竖排 */
@media (max-width: 359px) {
  .champ-more { flex-direction: column; }
  .champ-more .btn:first-child, .champ-more .btn:last-child { flex: none; }
}

/* 冠军页底部的排行榜入口：只有这位歌手在公开榜上时才会出现（见 app.js 的
   maybeShowChampRank），跟随内容、不吸底。

   分割线要通到屏幕两侧（用户 2026-07-29 定），所以整块用负 margin 抵掉 .screen
   的左右 padding，行内再用同样的 20px 补回来——线是整宽的，头像仍与上面的按钮
   左对齐。底部那 -40px 抵掉 .screen 收尾的 40px，让头像上下的留白一样宽（各
   16px）；safe-area 不在抵消范围内，那是系统 home indicator 的地盘，不算间距。
   这三个数字跟着 .screen 的 padding 走，改那边记得回来对一遍。 */
.champ-rank-slot {
  align-self: stretch;
  margin: 26px -20px -40px;
}
.champ-rank {
  position: relative;
  display: flex; align-items: center; gap: 12px;
  padding: 16px 20px;
  /* 边框只留 1px 的占位高度，颜色让给下面那条通栏的伪元素线（见 ::before） */
  border-top: 1px solid var(--line);
  border-top-color: transparent;
  color: var(--txt); text-decoration: none;
  -webkit-tap-highlight-color: transparent;
  /* 数据是异步来的，淡入进场，别在页面上「啪」地弹出一行 */
  animation: screenIn .45s cubic-bezier(.2, .8, .2, 1) both;
}
/* 桌面上 .screen 有 max-width，上面那 -20px 的负 margin 只把线铺到内容列边缘；
   线要真的通到屏幕两侧（用户 2026-07-29 定），所以改由一条居中的 100vw 伪元素来画。
   溢出视口的部分由 html/body 的 overflow-x: clip 裁掉，横向不会多出滚动条。
   窄屏上列宽 = 视口，与原先的整宽边框效果相同。 */
.champ-rank::before {
  content: '';
  position: absolute;
  top: -1px; left: 50%;
  width: 100vw; height: 1px;
  transform: translateX(-50%);
  background: var(--line);
}
.champ-rank:active { opacity: .6; }
/* 圆角矩形，与排行榜列表里的 .rk-art 同款（用户 2026-07-29 定）：点进去看到的
   就是那张榜，头像的形状也跟那边保持一条口径，别在冠军页自成一格切成圆的。 */
.champ-rank-art {
  flex: none; width: 44px; height: 44px;
  border-radius: 10px; overflow: hidden;
  background: var(--panel-2);
}
.champ-rank-art img { width: 100%; height: 100%; object-fit: cover; }
.champ-rank-art .noart { width: 100%; height: 100%; display: grid; place-items: center; color: var(--dim2); }
.champ-rank-art .noart svg { width: 18px; height: 18px; }
/* .champ 整屏居中，这一行的文字要显式靠边——歌手名长时才不会在两行之间跳 */
.champ-rank-text {
  flex: 1; min-width: 0;
  font-size: 15px; font-weight: 600; line-height: 1.35; text-align: left;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
/* 比「再来一场 / 和好友一起玩」里那两个 16px 的图标再大 4px，是有意的（用户
   2026-07-29 反馈「明显更小」）：箭头是个细字形，同样的框里墨迹只有那两个撑满
   图标的一半多，按框对齐反而看着小一圈。要的是视觉等重，不是数值相等。 */
.champ-rank > svg { flex: none; width: 20px; height: 20px; color: var(--dim); }
[dir="rtl"] .champ-rank-text { text-align: right; }
[dir="rtl"] .champ-rank > svg { transform: scaleX(-1); }

/* ============================================================
   分享图弹窗
   ============================================================ */
.modal {
  position: fixed; inset: 0; z-index: 80;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  /* 顶部为关闭按钮预留独立区域，避免与图片重叠 */
  padding: calc(68px + var(--safe-top)) 20px 20px;
  background: rgb(5 5 9 / .88);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  animation: fadeIn .25s both;
}
/* 分享弹窗：关闭按钮改为悬浮在分享图上（它自带底色 + 描边，压在图上依然看得清），
   顶部因此不再为它整块留白，省下的高度全给对阵图——带小红书气泡时可用高度更紧张，
   上方那块空白格外显眼。其余弹窗（支持我们、玩法、确认框）仍走上面的 68px 留白。 */
.modal.share-modal { padding-top: calc(30px + var(--safe-top)); padding-bottom: 0; }
/* 按钮行贴底的口径对齐小组赛的底部操作条 .cta-bar（16px + 安全区）：弹窗自己那 20px
   下内边距叠上按钮行的 10px 后比小组赛多出 14px，看着浮在半空 */
.share-modal .share-actions { padding-bottom: calc(16px + var(--safe-bottom)); }
/* 弹窗打开时锁住背后正文的滚动：.modal 只是 fixed 覆盖层，页面本身照旧能滚，
   iOS 上滚动条会从遮罩后面透出来（还能隔着模糊层看到正文在动）。
   不支持 :has 的老内核退回现状，锁不住而已，不影响功能。 */
html:has(#overlays .modal), html:has(#overlays .modal) body { overflow: hidden; }
.modal-close {
  position: absolute; top: calc(14px + var(--safe-top)); right: 14px;
  z-index: 10;
  width: 42px; height: 42px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--panel-2);
  border: 1px solid var(--line);
  color: var(--txt);
}
.modal-close svg { width: 19px; height: 19px; }
[dir="rtl"] .modal-close { right: auto; left: 14px; }
[dir="rtl"] .zeqing-panel .modal-close { left: 10px; }
[dir="rtl"] .zeqing-head { padding-right: 0; padding-left: 40px; }
.share-stage {
  width: 100%; max-width: 400px;
  flex: 1; min-height: 0;
  display: flex; align-items: center; justify-content: center;
}
.share-stage img {
  max-width: 100%; max-height: 100%;
  border-radius: 18px;
  border: 1px solid var(--line);
  box-shadow: 0 30px 90px -20px rgb(0 0 0 / .9);
  animation: champIn .5s cubic-bezier(.2, .9, .3, 1.05) both;
}
.share-loading { text-align: center; color: var(--dim); font-size: 14px; display: grid; gap: 18px; justify-items: center; }
.share-actions {
  position: relative;   /* 小红书引导气泡（.xhs-tip）的定位上下文 */
  flex: none;
  width: 100%; max-width: 400px;
  display: flex; gap: 11px;
  padding: 18px 0 calc(10px + var(--safe-bottom));
}
/* 两个按钮严格等高：显式锁 44（= 分享按钮 sm 高度），避免 ghost 的 1px border + min-height
   在微信/iOS webview 里被当作 content-box 而多出 2px 高，与分享按钮不齐 */
.share-actions .btn { flex: 1; height: 44px; box-sizing: border-box; }
.share-actions .btn.ghost { flex: 0 0 auto; padding: 0 22px; }
/* 非简体那行（2026-07-29 用户定）：安卓是「圆形下载钮 + 分享图片 + 链接已复制」，
   iOS 少掉下载钮只剩后两颗。下载钮只留图标省出的宽度，正好让三颗排进一行。
   两颗长按钮等分剩余宽度、内边距收到 12px——13 种语言在 375 宽屏下逐一量过不截断。 */
/* 内边距 8px 而非 12：最紧的两条是西语 Copiar URL（135）与俄语 Копировать（140），
   只差 1–6px，收内边距比为这点差距去动文案划算（收 2px 左右各让出 4px）。 */
.share-actions.copy-row .btn { flex: 1 1 0; min-width: 0; padding: 0 8px; }
/* 切到「已复制」时对勾弹一下——这个动作才是「刚刚复制好了」的提示（用户自己没做
   任何事，静止的「已复制」是看不出来的）。复用小红书那条「✓ 已复制话题」的回弹曲线。 */
.share-actions .btn.is-copied svg { animation: xhsPop .32s cubic-bezier(.2, .9, .3, 1.45) both; }
/* 小红书分享入口（仅简体+移动端，见 app.js openShare）：
   红色小红书圆钮 + 安卓的圆形下载钮，与 44px 的分享长条等高 */
.btn-circle {
  flex: none;
  width: 44px; height: 44px;
  border-radius: 50%;
  display: grid; place-items: center;
  color: #fff;
  transition: transform .16s;
}
.btn-circle:active { transform: scale(.92); }
.btn-circle.xhs { background: #ff2442; }   /* 小红书品牌红，与 .brand-ico.xhs 同色 */
.btn-circle.xhs svg { width: 30px; height: 30px; }   /* 字标横排三字，画得比通用图标大才够清楚 */
.btn-circle.dim { background: var(--panel); border: 1px solid var(--line); color: var(--txt); }
.btn-circle.dim svg { width: 20px; height: 20px; }
/* 微信/国产内核的简体分支：长按提示紧贴图片下方，下一行小红书圆钮 + 内联气泡
   （气泡在这里改为流内布局并常驻，复制后不关闭，见 app.js copyXhsTopic） */
.wx-hint.mid { padding: 14px 0 0; color: var(--txt); }   /* 与下方气泡文字同为白色 */
.share-actions.inline-tip { align-items: center; }
.share-actions.inline-tip .xhs-tip { position: static; flex: 1; }
/* 简体标准分支（iOS Safari / Chrome 等）：气泡改流内排布、独占按钮行上方一行，
   按钮行因此变高，flex 的分享图（.share-stage 让出高度）自动缩小上移，不再被气泡压住下缘。
   上方留白比 row-gap 大，让气泡靠按钮行、离分享图远一点；气泡移除时 JS 会摘掉 has-tip，
   布局回到无气泡时的原样。只在带气泡时开 wrap——其余语言的保存/分享长按钮不受影响。 */
.share-actions.has-tip { flex-wrap: wrap; row-gap: 16px; padding-top: 18px; }
.share-actions.has-tip .xhs-tip { position: static; order: -1; flex: 0 0 100%; }
/* 引导气泡：微信分支内联在按钮行内，标准分支独占上方一行；常驻，点击整条复制话题，
   复制成功切「已复制」动画后由 JS 移除 */
.xhs-tip {
  position: absolute;
  bottom: calc(100% + 4px);
  left: 0; right: 0;   /* 撑满按钮行宽度 */
  box-sizing: border-box;
  display: flex; align-items: center; gap: 10px;
  padding: 10px 14px;
  border-radius: 14px;
  background: rgb(16 16 24 / .92);
  border: 1px solid var(--line);
  color: var(--txt);
  font-size: 13px; line-height: 1.6; text-align: left;
  opacity: 0; transform: translateY(6px);
  transition: opacity .35s, transform .35s;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  z-index: 5;
}
.xhs-tip.show { opacity: 1; transform: none; }
.xhs-tip:active { transform: scale(.98); }
.xhs-tip svg { flex: none; width: 17px; height: 17px; opacity: .75; }
.xhs-tip.copied { justify-content: center; cursor: default; }
.xhs-tip .xhs-copied {
  display: inline-flex; align-items: center; gap: 6px;
  font-weight: 700;
  animation: xhsPop .32s cubic-bezier(.2, .9, .3, 1.45) both;
}
@keyframes xhsPop {
  from { transform: scale(.6); opacity: 0; }
  to { transform: none; opacity: 1; }
}
.wx-hint {
  flex: none;
  color: var(--dim);
  font-size: 13.5px;
  padding: 18px 0 calc(14px + var(--safe-bottom));
}
.share-actions:has(+ .save-hint) { padding-bottom: 8px; }
.save-hint {
  flex: none;
  color: var(--dim2);
  font-size: 12.5px;
  padding-bottom: calc(14px + var(--safe-bottom));
}

/* ---------- 确认弹窗 ---------- */
.confirm-box {
  width: min(88vw, 340px);
  padding: 26px 22px 20px;
  border-radius: var(--r-lg);
  background: rgb(19 19 28 / .96);
  border: 1px solid var(--line);
  text-align: center;
  animation: riPop .35s cubic-bezier(.2, .9, .3, 1.15) both;
}
.confirm-box h4 { font-size: 18px; font-weight: 900; margin-bottom: 8px; }
/* 新功能告知：蒙层比其它弹窗淡得多——公告不该把首页整个盖住，让人看得见背后的站 */
#newsModal {
  background: rgb(5 5 9 / .5);
  backdrop-filter: blur(5px);
  -webkit-backdrop-filter: blur(5px);
}
.news-box { width: min(88vw, 360px); }
.news-item {
  display: flex; align-items: flex-start; gap: 11px;
  padding: 14px;
  margin-bottom: 14px;
  border-radius: 12px;
  background: var(--panel);
  border: 1px solid var(--line-soft);
  text-align: left;
}
.news-ico { flex: none; font-size: 19px; line-height: 1.35; }
/* 必须比 .confirm-box p 更具体：那条在本段之后定义、同优先级，会把 margin 覆盖回 22px，
   于是每块下方凭空多出一行空白 */
.news-box .news-item p { margin: 0; font-size: 13.5px; line-height: 1.65; color: var(--dim); }
.news-box .row { margin-top: 20px; }
.confirm-box p { color: var(--dim); font-size: 14px; margin-bottom: 22px; }
.confirm-box .row { display: flex; gap: 10px; }
/* 左右内边距比通用 .btn 小得多：并排两颗按钮宽度由 flex 决定，28px 的留白只会挤掉文字空间，
   长译文（俄/法/德等）因此被折成两行 */
.confirm-box .btn { flex: 1; min-height: 48px; font-size: 15px; padding: 0 12px; }

/* ---------- 和好友一起玩：漂浮按钮 / 邀请弹窗 / 受邀横幅 ---------- */
/* 只用在抽签、名单页：悬在 cta-bar 上方靠右（冠军页改成 .champ-more 里的实体按钮）。
   右缘与小组卡片严格对齐 = 50% + 列宽/2 − .screen 的 20px 内边距，列宽跟着
   .screen 的 max-width 分三档（520 / ≥760 是 640 / ≥1100 是 720）；
   移动端列宽=视口，右缘就是那 20px 内边距。 */
.fab-invite {
  position: fixed; right: max(20px, calc(50% - 240px)); bottom: calc(84px + var(--safe-bottom)); z-index: 31;
  display: inline-flex; align-items: center; gap: 7px;
  padding: 10px 15px;
  border-radius: 999px;
  background: rgb(16 16 24 / .88);
  border: 1px solid var(--line);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  font-size: 13px; font-weight: 700;
  color: var(--txt);
  box-shadow: var(--shadow-card);
  transition: transform .15s;
  animation: fabIn .4s cubic-bezier(.2, .9, .3, 1.15) .25s both;
}
.fab-invite svg { width: 17px; height: 17px; }
.fab-invite:active { transform: scale(.94); }
/* 名单没选满时置灰不可点：还没有可分享的开局 */
.fab-invite:disabled { opacity: .4; pointer-events: none; }
[dir="rtl"] .fab-invite { right: auto; left: max(20px, calc(50% - 240px)); }
@media (min-width: 760px) {
  .fab-invite { right: calc(50% - 300px); }                       /* .screen 640 → 640/2 − 20 */
  [dir="rtl"] .fab-invite { right: auto; left: calc(50% - 300px); }
  .lang-switch { left: calc(50% - 260px); }                       /* .home  560 → 560/2 − 20 */
  [dir="rtl"] .lang-switch { left: auto; right: calc(50% - 260px); }
  /* .rank-fab 的同款分档写在它自己那节（本段之后定义，写这里会被基础规则覆盖） */
}
@media (min-width: 1100px) {
  /* 这一档 .home 的 max-width 也被 .screen 的 720px 覆盖，两者列宽相同 */
  .fab-invite { right: calc(50% - 340px); }
  [dir="rtl"] .fab-invite { right: auto; left: calc(50% - 340px); }
  .lang-switch { left: calc(50% - 340px); }
  [dir="rtl"] .lang-switch { left: auto; right: calc(50% - 340px); }
}
body.kb-open .fab-invite { display: none; }   /* 名单页键盘弹起时随 cta-bar 一起让位 */
@keyframes fabIn { from { opacity: 0; transform: translateY(10px) scale(.9); } }

/* 邀请弹窗复用 news-box 的卡片排版与 .news-ico emoji（设计稿里的灰色圆点辨识度太低） */
.invite-box { position: relative; }
.invite-box .modal-close { position: absolute; top: 12px; right: 12px; width: 36px; height: 36px; }
[dir="rtl"] .invite-box .modal-close { right: auto; left: 12px; }
.invite-box h4 { margin: 6px 0 16px; padding: 0 30px; }
.invite-fb-hint { color: var(--dim); font-size: 12.5px; margin-bottom: 8px; }
.invite-fallback {
  width: 100%;
  margin-bottom: 4px;
  padding: 10px 12px;
  border-radius: 12px;
  background: var(--panel);
  border: 1px solid var(--line);
  color: var(--dim);
  /* 必须 ≥16px：复制失败后这个框会被 focus，iOS 对更小的可聚焦控件自动放大整页
     且不还原，放大后全站 fixed 元素（顶栏 / cta-bar / fab）拖动时都会飘（同
     .lineup-search input 的教训） */
  font-size: 16px; line-height: 1.5;
  resize: none;
  -webkit-user-select: text; user-select: text;
}

/* 受邀落地页顶栏：仍是常规 flex 三段式（左返回 / 中横幅 / 右重开），只换背景与中间文案，
   品牌字走展示字体渐变。别改回 display:block——好友在落地第一屏也需要退出入口。 */
.topbar.tb-invite {
  /* 与页面底部主按钮同一条渐变，两端呼应；不透明底色下不需要毛玻璃与分隔线 */
  background: var(--grad);
  backdrop-filter: none; -webkit-backdrop-filter: none;
  border-bottom-color: transparent;
}
/* 渐变底上 var(--dim) 系的灰会发浑，图标与副标题改用带透明度的白 */
.topbar.tb-invite .tb-btn { color: rgb(255 255 255 / .82); }
.topbar.tb-invite .tb-btn:active { background: rgb(255 255 255 / .16); color: #fff; }
/* 375px 屏减去左右各 42px 的按钮只剩约 270px：德语等长译文必须省略，不许把顶栏撑高 */
.ib-title, .ib-sub { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ib-title { display: block; font-size: 12.5px; font-weight: 800; letter-spacing: .01em; color: #fff; }
.ib-brand {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 400;
  letter-spacing: .05em;
  padding-inline: 2px;
  color: #fff;
}
.ib-sub { display: block; margin-top: 1px; font-size: 11px; color: rgb(255 255 255 / .78); }

/* ---------- Toast ---------- */
.toast {
  position: fixed; left: 50%; bottom: calc(96px + var(--safe-bottom)); z-index: 99;
  transform: translate(-50%, 16px);
  max-width: 82vw;
  padding: 11px 20px;
  border-radius: 999px;
  background: rgb(24 24 34 / .95);
  border: 1px solid var(--line);
  box-shadow: var(--shadow-card);
  font-size: 14px; font-weight: 500;
  opacity: 0;
  transition: opacity .3s, transform .35s cubic-bezier(.2, .8, .2, 1);
  pointer-events: none;
  text-align: center;
}
.toast.show { opacity: 1; transform: translate(-50%, 0); }

/* ============================================================
   桌面端适配
   ============================================================ */
@media (min-width: 760px) {
  /* 列宽（.screen / .home / .rk）统一由文件开头的 --col-* 三档变量给，这里不再重复 */
  .pick-grid { grid-template-columns: repeat(4, 1fr); gap: 14px; }
  .scard .tname { font-size: 13.5px; }
  .wild-grid { grid-template-columns: repeat(4, 1fr); }
  .group-grid { grid-template-columns: repeat(3, 1fr); }
  .duel { gap: 26px; }
  .duel-wrap { max-width: 620px; margin: 0 auto; width: 100%; }
  .vs-badge { width: 62px; height: 62px; }
  .vs-badge b { font-size: 19px; }
  .dcard .tname { font-size: 17px; }
  .champ-actions { flex-direction: row; }
  .champ-actions > .btn { flex: 1; }
  /* 三段并排时 champ-more 里挤了两颗按钮，等分会比手机上还窄（760px 档实测 294px，
     法/德/俄又要折行），多给一点 */
  .champ-actions > .champ-more { flex: 1.4; }
  .podium { grid-template-columns: 1.2fr 1fr 1fr; }
  .pod.silver { grid-column: auto; }
  .cta-bar .btn { max-width: 400px; }
  .scard:hover { border-color: var(--line); transform: translateY(-3px); }
  .dcard:hover:not(.winner):not(.loser) { transform: translateY(-4px); border-color: var(--line); }
  .wcard:hover:not(.locked) { border-color: var(--line); transform: translateY(-2px); }
}

@media (min-width: 1100px) {
  .pick-grid { gap: 16px; }
}

/* ============================================================
   排行榜页（rank.html）+ 首页入口
   ============================================================ */
/* 首页排行榜入口（用户定）：位置在右上角，样式是歌手 chip 同款金边胶囊——
   padding / 圆角 / 字号字重与 .chip 一致，金色描边 + 微金底 + 纯白字突出入口。 */
/* 与首页搜索框右缘严格对齐（胶囊描边对齐搜索框描边）：内容列右缘 = 50% + 列宽/2
   − .screen 的 20px 内边距，取值与左上角 .lang-switch 逐档镜像（.home 基础 520 /
   ≥760 是 560 / ≥1100 被 .screen 的 720 覆盖）；移动端列宽=视口，右缘就是那 20px。 */
.rank-fab {
  position: fixed;
  top: calc(var(--safe-top) + 14px);
  right: max(20px, calc(50% - 240px));
  z-index: 50;
  display: inline-flex; align-items: center; gap: 5px;
  padding: 7px 14px;
  border-radius: 999px;
  font-size: 12px; font-weight: 500; letter-spacing: .04em;   /* 字重与语言切换、首页歌手 chip 一致 */
  color: var(--txt);   /* 比左侧语言切换更亮：纯白字，突出入口 */
  background: rgba(255, 214, 116, .07);
  border: 1px solid rgba(255, 214, 116, .4);
  text-decoration: none;
  transition: transform .15s, color .2s, opacity .25s ease;
  user-select: none; -webkit-user-select: none;
}
.rank-fab:active { transform: scale(.92); }
/* 两颗 fixed 按钮滚过首屏（进入 SEO 正文）后淡出，免得悬浮压在正文标题上；
   body.mc-scrolled 由 app.js 的 scroll 监听维护。 */
body.mc-scrolled .lang-switch, body.mc-scrolled .rank-fab { opacity: 0; pointer-events: none; }
[dir="rtl"] .rank-fab { right: auto; left: max(20px, calc(50% - 240px)); }
/* 分档必须写在基础规则之后：上面「桌面端适配」那两段媒体查询在本节之前，
   同优先级下会被 .rank-fab 的基础 right 覆盖回去 */
@media (min-width: 760px) {
  .rank-fab { right: calc(50% - 260px); }                         /* .home 560 → 560/2 − 20 */
  [dir="rtl"] .rank-fab { right: auto; left: calc(50% - 260px); }
}
@media (min-width: 1100px) {
  .rank-fab { right: calc(50% - 340px); }                         /* .screen 720 → 720/2 − 20 */
  [dir="rtl"] .rank-fab { right: auto; left: calc(50% - 340px); }
}

/* 底部固定分享条要占一行高度，内容列多留出这段 padding 才不会被盖住尾巴
   （26px 渐隐区 + 48px 按钮 + 14px 底距 + 视觉缓冲，实测 116px 瓷砖行不再被压） */
.rk { max-width: var(--col-rank); padding-top: 0; padding-bottom: calc(116px + var(--safe-bottom)); }   /* 顶部 safe-area 由常驻的 .rk-head 自己扛 */

/* 榜单页固定底部的「分享」长条（设计稿：渐变胶囊贴内容列宽度，居中悬浮）。
   渐变下衬一层页底色渐隐，滚动经过的内容不会跟按钮直接打架。 */
.rk-share-bar {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 40;
  display: flex; justify-content: center;
  padding: 26px 20px calc(14px + var(--safe-bottom));
  background: linear-gradient(to top, rgba(8, 8, 13, .92) 55%, transparent);
  pointer-events: none;               /* 渐隐层不挡列表点击，按钮自己恢复交互 */
}
/* 宽度口径照抄小组赛底部操作条里的按钮（.cta-bar .btn，用户 2026-07-29 定）：
   窄屏铺满、最宽 480px，760 档起收到 400px（那一档写在本页末尾的媒体查询里）。 */
.rk-share-btn {
  pointer-events: auto;
  width: 100%;
  max-width: 480px;
  height: 48px;
  font-size: 15px;
}
/* 顶栏常驻（用户定）：下滑时钉在页顶不消失，样式**照抄主应用小组抽签页的
   .topbar**（上面 147 行那套半透明底 + 毛玻璃 + 细底边），不另创视觉。
   区别仅在定位：这里用 sticky 留在 .rk 的布局流里，与内容列对齐。 */
.rk-head {
  display: flex; align-items: center; gap: 8px;
  position: sticky;
  top: 0;
  z-index: 45;                     /* 在列表之上、分享弹窗(1000+)之下 */
  margin: 0 -20px;                 /* 铺满容器左右 padding，行内容不被边缘切断 */
  padding: calc(var(--safe-top) + 8px) 20px 8px;
}
/* 底色与底边要通栏（用户 2026-07-29 定，与主应用 fixed 的 .topbar 观感一致），
   但返回键与标题仍留在内容列里——所以拉宽的是这层背景，不是 .rk-head 自己的盒子。
   居中的 100vw 比可见宽多出一个滚动条宽度，两侧溢出的部分由 html/body 的
   overflow-x: clip 裁掉，不会多出横向滚动条。z-index 负值把它压在自身内容之下；
   .rk-head 有 z-index 建立了层叠上下文，不会漏到页面其它元素后面去。 */
.rk-head::before {
  content: '';
  position: absolute; z-index: -1;
  top: 0; bottom: 0; left: 50%;
  width: 100vw;
  transform: translateX(-50%);
  box-sizing: border-box;
  background: rgb(10 10 16 / .68);            /* ↓ 四行与 .topbar 逐字一致 */
  backdrop-filter: blur(20px) saturate(1.4);
  -webkit-backdrop-filter: blur(20px) saturate(1.4);
  border-bottom: 1px solid var(--line-soft);
}
/* 返回键图标与下方内容左缘对齐（抵消按钮内边距） */
.rk-back { flex: none; color: var(--dim); margin-left: -11px; }
[dir="rtl"] .rk-back { margin-left: 0; margin-right: -11px; }
.rk-title { flex: 1; min-width: 0; display: flex; align-items: baseline; gap: 10px; }
.rk-logo {
  font-family: var(--font-display);
  font-size: 21px; letter-spacing: .06em;
  display: inline-block;
  transform: skewX(-8deg);
  background: var(--grad);
  -webkit-background-clip: text; background-clip: text;
  color: transparent;
  white-space: nowrap;
}
/* Anton 字面高（21px 时上伸 18.2px、几乎无下伸），基线对齐后中文字块视觉偏低 → 上移 2px 做视觉居中 */
.rk-title .rk-h { position: relative; top: -2px; font-size: 19px; font-weight: 800; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* 数据截止时间：与 h1「排行榜」同色（继承 --txt），字号取其 50%（19px→9.5px）。
   底部与「MUSIC CUP 排行榜」视觉对齐（用户要求）：三者共用 .rk-title 的基线对齐，
   但 h1 为了配合 Anton 字标做了 top:-2px 的光学上移，这段小字没跟上，于是字形下缘
   反而比标题低出来一点。实测（PingFang SC / Anton，2026-07-26，逻辑像素）：
   字标 37.91、「排行榜」37.94、本行 38.58 —— 中文界面下沉 0.6px；拉丁界面的 h1
   没有下伸部，同样的 -2px 全额生效，下沉扩大到 1.9px。
   -1px 是两种字系的折中（中文剩 0.4px 上浮、拉丁剩 0.9px 下沉，都在半像素级）；
   要做到逐字系精确得按 h1 文本有无 CJK 下伸部分别取值，只有 JS 能判断，不值当。 */
.rk-cutoff { position: relative; top: -1px; font-size: 9.5px; font-weight: 400; white-space: nowrap; flex: none; }

/* 文字型 Tab（歌曲/歌手）+ 语言区下拉 */
.rk-topbar { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 16px 0 14px; }
.rk-tabs2 { display: flex; gap: 20px; min-width: 0; }
.rk-tab2 {
  font-size: 24px; font-weight: 900; line-height: 1.2;
  color: var(--dim2);
  padding: 2px 0;
  transition: color .2s;
  white-space: nowrap;
}
.rk-tab2.on { color: var(--txt); }
/* 这个壳是列表页的 <h1>（见 admin-music-data.js 的 listHtml）。标签从 div 换成 h1
   后要压掉浏览器默认的 2em / bold，否则里面那颗语区按钮会被撑大；margin/padding
   已由第 48 行的全局 reset 归零。视觉与换标签前逐像素相同。 */
.rk-region-wrap { position: relative; flex: none; font: inherit; }
.rk-region {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 7px 12px 7px 15px;
  border: 1px solid var(--line);
  border-radius: 999px;
  font-size: 13px; font-weight: 600;
  color: var(--txt);
  transition: background .2s, transform .15s;
}
.rk-region:active { transform: scale(.95); }
.rk-region svg { width: 14px; height: 14px; color: var(--dim); }
.rk-menu {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 30;
  min-width: 132px;
  padding: 5px;
  background: #15151f;
  border: 1px solid var(--line);
  border-radius: 14px;
  box-shadow: var(--shadow-card);
  animation: sugIn .18s cubic-bezier(.2, .8, .2, 1) both;
  /* 语区可能有十几个，超高就在菜单内滚动，不把页面顶长 */
  max-height: min(56vh, 380px);
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;   /* 滚到底不带着整页一起滚（微信 webview 尤其明显） */
}
[dir="rtl"] .rk-menu { right: auto; left: 0; }
.rk-menu-item {
  display: block; width: 100%;
  padding: 10px 13px;
  border-radius: 9px;
  text-align: left;
  font-size: 14px;
  color: var(--dim);
}
[dir="rtl"] .rk-menu-item { text-align: right; }
.rk-menu-item.on { color: var(--txt); font-weight: 700; background: var(--panel); }
.rk-menu-item:active, .rk-menu-item:hover { background: var(--panel-2); }

.rk-search { margin-bottom: 8px; }
.rk-search .search-field { height: 48px; border-radius: 14px; background: var(--panel); }
/* 必须 ≥16px：iOS Safari / 微信 webview 对更小的输入框会在聚焦时自动放大整页，
   放大后页面比视口宽（能横向滚动）且不会自动还原，退回首页仍是放大状态。 */
.rk-search .search-field input { font-size: 16px; }

/* 扁平行列表：无容器底色、无分隔线，间距分行 */
.rk-row {
  display: flex; align-items: center; gap: 12px;
  padding: 9px 4px;
  cursor: pointer;
  /* 行标签从 div 换成 <a>（SEO 真内链，admin-music-data.js 的 rowOpen）：压掉
     UA 的链接蓝与下划线，:visited 的紫也一并被 inherit 盖住。div 兜底形态下这
     两条就是默认值，视觉与改前逐像素一致。 */
  color: inherit; text-decoration: none;
}
.rk-num {
  flex: none;
  min-width: 22px;
  text-align: center;
  font-size: 14px; font-weight: 600;
  color: var(--dim2);
}
.rk-num.top { font-weight: 800; }
.rk-num.t1 { color: #ffd166; }
.rk-num.t2 { color: #cfd6e4; }
.rk-num.t3 { color: #e8a87c; }
.rk-art {
  flex: none;
  width: 46px; height: 46px;
  border-radius: 10px;
  overflow: hidden;
  background: var(--panel-2);
  display: block;
}
.rk-art img { width: 100%; height: 100%; object-fit: cover; }
.rk-art .noart { width: 100%; height: 100%; display: grid; place-items: center; color: var(--dim2); }
.rk-art .noart svg { width: 18px; height: 18px; }
.rk-main { flex: 1; min-width: 0; }
.rk-main > b {
  display: block;
  font-size: 15px; font-weight: 700;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.rk-main > i {
  display: block;
  font-style: normal;
  font-size: 12px; color: var(--dim);
  margin-top: 1px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* 歌手行右侧：检索量条形图（log 缩放，纯视觉不标数） */
.rk-bar { flex: 0 0 32%; height: 9px; display: block; }
.rk-bar i {
  display: block; height: 100%;
  border-radius: 999px;
  background: var(--acc1);
  min-width: 10px;
  transition: width .4s cubic-bezier(.2, .8, .2, 1);
}
/* 扁平试听键：描边圆 */
.play-btn.rk-play {
  position: static;
  flex: none;
  width: 36px; height: 36px;
  background: transparent;
  border: 1.5px solid rgb(255 255 255 / .32);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
.play-btn.rk-play svg { width: 14px; height: 14px; }
.play-btn.rk-play.playing { background: var(--acc1); border-color: var(--acc1); }

.rk-empty { padding: 44px 20px; text-align: center; color: var(--dim); font-size: 14px; }
.rk-loading { padding: 60px 0; display: grid; gap: 18px; justify-items: center; color: var(--dim); font-size: 14px; }

/* 启动遮罩（2026-07-29）。排行榜为 SEO 把正文做进了服务端 HTML，浏览器会先把那份
   语义文本画出来，等脚本跑完 render() 再整体重绘 #app——中间那段（从首页点进来约
   200ms，冷启动约 650ms）用户看到的是一屏裸文本然后硬切。这层遮罩把它盖掉，观感
   回到静态化之前的「先背景色、再出榜单」，而爬虫那一侧的正文一个字没动。
   只有 .rk-booting 时才显示，那个 class 由 rank.html 的内联脚本挂——不执行 JS 的
   爬虫因此完全看不到它。z-index 压过 .rk-head(45)，但让在 .grain(90) 之下，颗粒
   质感不断档；.ambient 那层 blob(-2) 会被盖住，是暗色渐变，察觉不到。 */
.rk-boot { display: none; }
.rk-booting .rk-boot {
  display: grid; place-items: center;
  position: fixed; inset: 0; z-index: 60;
  background: var(--bg);
  /* 兜底：JS 挂了就三秒后自己让开。宁可露出那份裸文本（它可读、链接也能点），
     也好过把用户永久锁在一个 loading 上。 */
  animation: rk-boot-bail .3s ease 3s forwards;
}
/* spinner 迟到 300ms 再淡入：多数情况下脚本早接管完了，一个只来得及转半圈的
   spinner 是凭空多一次视觉事件，比什么都不显示更碍眼。 */
.rk-boot-in { opacity: 0; animation: rk-boot-in .25s ease .3s forwards; }
@keyframes rk-boot-in { to { opacity: 1; } }
@keyframes rk-boot-bail { to { opacity: 0; visibility: hidden; } }

/* 无限滚动底部：.on 三点跳动；.done 自然到底彻底隐藏；.capped 到 150 上限显示文案 */
.rk-more { display: none; padding: 20px 0 30px; justify-content: center; align-items: center; }
.rk-more.on, .rk-more.capped { display: flex; }
.rk-more.done { display: none; }
.rk-more.on .rk-more-cap { display: none; }         /* 加载中只显示三点 */
.rk-more.capped .rk-more-dots { display: none; }    /* 到上限只显示文案 */
.rk-more-cap { display: none; font-size: 12px; letter-spacing: .04em; color: var(--dim2); }
.rk-more.capped .rk-more-cap { display: inline; }
.rk-more-dots { display: inline-flex; gap: 6px; }
.rk-more-dots i {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--acc1, #e8a87c);
  animation: rkDot 1s ease-in-out infinite both;
}
.rk-more-dots i:nth-child(2) { animation-delay: .16s; }
.rk-more-dots i:nth-child(3) { animation-delay: .32s; }
@keyframes rkDot {
  0%, 80%, 100% { transform: scale(.5); opacity: .4; }
  40% { transform: scale(1); opacity: 1; }
}

/* ---------- 歌曲详情页（沿用冠军页视觉：大封面光晕 + 大标题） ---------- */
.rk-song { display: flex; flex-direction: column; align-items: center; text-align: center; padding-top: 22px; }
/* 歌手名（用户 2026-07-29 定）：它是这一页通往歌手详情页的唯一入口，而原先与上面那行
   「专辑 · 年份」同款灰字，读不出可点。试过 chip（胶囊边框 + panel 底）嫌重，试过灰字
   缀一个 > 箭头，最终定成品牌紫的纯文字——颜色本身就是站内的可点信号（--acc1 是 logo
   与主按钮那支紫），不额外占横向空间，也不跟首页那排「一键开赛」的 .chip 抢视觉。
   没有图标就没有 RTL 镜像问题，阿语这行只是文字方向跟着页面走。 */
.rk-s-artist {
  padding: 0; margin-bottom: 20px;
  color: var(--acc1); font-size: 13.5px;
  transition: opacity .2s;
}
.rk-s-artist:hover, .rk-s-artist:active { opacity: .7; }
.rk-s-art { position: relative; width: min(61vw, 285px); margin-bottom: 16px; }   /* 用户定：封面缩 5%，瓷砖上移 */
.rk-s-art::before {
  content: ""; position: absolute; inset: -14%;
  background: var(--grad);
  filter: blur(48px); opacity: .38;
  border-radius: 40px;
  z-index: -1;
}
.rk-s-art .rk-art {
  width: 100%; height: auto; aspect-ratio: 1;
  border-radius: 30px;
  border: 1px solid rgb(255 255 255 / .18);
  box-shadow: 0 30px 80px -20px rgb(0 0 0 / .8);
}
.rk-s-art .rk-art .noart svg { width: 60px; height: 60px; }
.rk-s-art .play-btn.rk-play {
  position: absolute; right: 12px; bottom: 12px;
  width: 48px; height: 48px;
  background: rgb(12 12 18 / .55);
  border: 1px solid rgb(255 255 255 / .25);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}
.rk-s-art .play-btn.rk-play svg { width: 19px; height: 19px; }
.rk-s-art .play-btn.rk-play.playing { background: var(--acc1); border-color: var(--acc1); }
[dir="rtl"] .rk-s-art .play-btn.rk-play { right: auto; left: 12px; }
.rk-s-title { font-size: clamp(26px, 7.5vw, 34px); font-weight: 900; line-height: 1.2; }
.rk-s-album { color: var(--dim); font-size: 13.5px; margin: 8px 0 2px; min-height: 20px; }
.rk-s-stats { width: 100%; display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; }
.rk-s-stats .stat b { font-size: 26px; }
.rk-s-stats .stat span {
  display: block;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  padding: 0 4px;
}

/* ---------- 歌手详情页 ---------- */
.rk-artistv { padding-top: 8px; }
.rk-a-head { display: flex; align-items: center; gap: 16px; margin: 10px 0 16px; }
.rk-a-head .rk-art { width: 96px; height: 96px; border-radius: 22px; }
.rk-a-head .rk-art .noart svg { width: 34px; height: 34px; }
.rk-a-meta { min-width: 0; }
.rk-a-meta h1 {
  font-size: 26px; font-weight: 900; line-height: 1.25;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rk-a-meta p { color: var(--dim); font-size: 12.5px; margin-top: 4px; }
/* 歌手页的开赛入口（用户 2026-07-29 定）：跟着名字与名次排在头像右侧，不独占一行。
   这一列在最窄的 375 屏能给按钮 223px（375 − 40 页面左右内边距 − 96 头像 − 16 gap），
   14px/700 下量过 14 种语言的「选出我的冠军歌」，只有法语 247px 与葡语 240px 超线，
   这两种在字典里就写不含「歌」的短说法，其余 11 种用完整版（日/韩的说法本来就含
   「曲 / 곡」）。设计基准 375 起，不再为 320 屏做取舍；下面那个 359px 断点留作老机型
   兜底，让整块落成竖排、按钮拿到整行宽度。
   **改这里的字号、padding、gap 或那几条译文，都要回来重量一遍这 14 个宽度。** */
/* min-height 必须一起写：.btn 基类是 min-height: 54px，只设 height 压不下去，
   按钮会顶着 54px 把这一列撑得比头像还高（96px）。 */
.rk-cup {
  min-height: 34px; height: 34px;
  /* 7px 不是随手填的：这一列的三行合起来要正好等于头像的 96px，才跟头像上下齐平
     （32.5 名字 + 4+18.75 名次 + 7+34 按钮 = 96.25）。改任何一行的字号或行高，
     这个数都要重新算。
     按钮从 36 降到 34 是用户 2026-07-29 定的（原先离上面那行名次太挤）；省下的 2px
     不是留白掉，而是加到这里的 margin-top 上——只压高度并不会让它离上面更远，
     两处一起动才既松了间距、又保住与头像齐平那条。 */
  margin-top: 7px;
  padding: 0 14px;
  font-size: 14px; white-space: nowrap;
}
/* 曲库模式：三颗按钮竖排，文案与首页那一排同一批 key（14 语言现成） */
.cup-box h4 {
  margin-bottom: 2px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cup-box > p { color: var(--dim); font-size: 12.5px; margin-bottom: 16px; }
.cup-modes { display: flex; flex-direction: column; gap: 9px; }
/* 320~359px 的老机型：头像右侧那一列只剩 142px，葡语与法语的按钮文案放不下，
   整块改竖排——头像与文字上下摆，按钮拿到整行宽度。 */
@media (max-width: 359px) {
  .rk-a-head { flex-direction: column; align-items: flex-start; gap: 12px; }
  .rk-cup { width: 100%; }
}

/* 歌手详情页的两个 tab（歌手信息 · 歌迷分布）。沿用列表页那对文字 tab 的视觉，
   只是小一号。歌曲页不分 tab——地图直接接在瓷砖下面。
   tab 与下方内容的间距统一成 22px，与「歌迷分布」到地图的距离一致；
   此前歌单只隔 2px，贴着 tab 显得挤（用户实翻）。 */
.rk-dtabs { margin: 18px 0 0; justify-content: flex-start; width: 100%; }
.rk-dtabs .rk-tab2 { font-size: 17px; }
.rk-dtabs + .rk-list,
.rk-dtabs + .rk-geo,
.rk-dtabs + .rk-s-stats { margin-top: 22px; }

/* ---------- 粉丝城市分布地图（歌手页 / 歌曲页，绘制见 js/rank-geo.js） ----------
   歌曲页是居中版式（.rk-song 是 flex column + center），地图要占满整宽，
   所以这里的文字一律显式左对齐，不跟着父级居中。 */
.rk-geo { width: 100%; margin: 22px 0 4px; text-align: start; }
.rk-geo:empty { display: none; }
.rk-geo-title { font-size: 14px; font-weight: 700; color: var(--dim); margin-bottom: 10px; }
.rk-geo-stage {
  position: relative;
  border-radius: var(--r-md);
  background: rgba(255, 255, 255, .028);
  border: 1px solid var(--line-soft);
  overflow: hidden;
}
/* touch-action: none 常驻——平移是环绕的，没放大时也要能四向拖（用户定），
   所以单指、双指都由我们自己处理。地图只占屏幕两成高，页面上下都有余地滚。 */
.rk-geo-canvas { display: block; width: 100%; touch-action: none; cursor: grab; }
.rk-geo-canvas:active { cursor: grabbing; }
.rk-geo-reset {
  position: absolute; inset-inline-end: 8px; bottom: 8px;
  display: none; padding: 5px 10px; border-radius: 999px;
  background: rgba(12, 12, 20, .82); border: 1px solid var(--line);
  color: var(--dim); font-size: 11.5px; line-height: 1.4;
}
.rk-geo-stage.zoomed .rk-geo-reset { display: block; }
.rk-geo-tip {
  position: absolute; transform: translate(-50%, -100%);
  display: none; align-items: baseline; gap: 6px;
  padding: 5px 9px; border-radius: 9px;
  background: rgba(12, 12, 20, .92); border: 1px solid var(--line);
  font-size: 12px; white-space: nowrap; pointer-events: none;
}
.rk-geo-tip.on { display: flex; }
.rk-geo-tip b { font-weight: 700; }
.rk-geo-tip i { font-style: normal; color: var(--acc2); font-variant-numeric: tabular-nums; }
.rk-geo-legend {
  display: flex; align-items: center; gap: 7px;
  margin-top: 9px; font-size: 11.5px; color: var(--dim2);
}
.rk-geo-legend i {
  flex: 0 0 74px; height: 6px; border-radius: 3px;
  background: linear-gradient(90deg, hsl(258 72% 46%), hsl(312 94% 68%));
}
.rk-geo-count { margin-inline-start: auto; }
/* 前 10 名按行填充成两列：1 2 / 3 4 / …（用户指定的顺序） */
.rk-geo-top {
  list-style: none; margin: 12px 0 0; padding: 0;
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 2px 14px;
}
.rk-geo-city {
  display: flex; align-items: baseline; gap: 8px;
  padding: 5px 6px; border-radius: 8px; cursor: pointer;
  font-size: 13px; line-height: 1.5;
}
.rk-geo-city.on { background: rgba(255, 255, 255, .07); }
.rk-geo-city b {
  flex: 0 0 1.35em; color: var(--dim2); font-weight: 700; font-size: 11.5px;
  font-variant-numeric: tabular-nums; text-align: end;
}
.rk-geo-city span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rk-geo-city em {
  margin-inline-start: auto; font-style: normal; color: var(--dim);
  font-variant-numeric: tabular-nums;
}
.rk-geo-note {
  margin-top: 14px; text-align: center;
  font-size: 12px; color: var(--dim2);
}
/* 收尾的品牌字标，与淘汰赛底部同一个（.wordmark 提供字形与渐变）；
   这里是文档流里的收尾，不像对战页那样绝对定位吸底。
   50px = 原先那句小字占掉的 32px（14px 上边距 + 18px 行高）加上字标自己的
   18px。2026-07-30 歌手页删掉小字后按用户要求让字标停在原位，别改小。 */
.rk-geo-brand { margin-top: 50px; font-size: 15px; opacity: .85; }

@media (min-width: 760px) {
  .rk-bar { flex-basis: 36%; }
  /* 底部分享条与小组赛底部操作条同宽（用户 2026-07-29 定），分档一并照抄 */
  .rk-share-btn { max-width: 400px; }
}

/* ---------- 无障碍 ---------- */
:focus-visible { outline: 2px solid var(--acc1); outline-offset: 3px; border-radius: 6px; }
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; }
}

/* ---------- 排行榜实体页的服务端正文（SEO） ----------
   爬虫与无 JS 环境可见；JS 启动后 render() 整体重绘 #app 原地接管。 */
.rk-seo{max-width:640px;margin:0 auto;padding:28px 20px;color:var(--txt)}
.rk-seo h1{font-size:24px;margin:0 0 10px}
.rk-seo p{opacity:.75;line-height:1.6;margin:0 0 14px}
.rk-seo-nav{display:flex;flex-wrap:wrap;gap:6px 10px;margin-bottom:18px}
.rk-seo-nav a,.rk-seo-list a{color:var(--txt);text-decoration:none;border-bottom:1px solid rgba(255,255,255,.25)}
.rk-seo-list{margin:0;padding-left:1.5em;line-height:1.9}
/* ================================================================
   MUSIC CUP · 音乐品味分析面板 v2 — 6维度详细布局
   ================================================================ */

.taste-panel { margin: 28px 0 8px; text-align: center; max-width: 600px; margin-left: auto; margin-right: auto; }
.taste-header { margin-bottom: 12px; }
.taste-icon { font-size: 28px; display: block; margin-bottom: 4px; }
.taste-title { font-size: 18px; font-weight: 700; letter-spacing: .04em; margin: 0 0 2px; color: #fff; }
.taste-subtitle { font-size: 11px; font-weight: 500; color: rgba(235,235,245,.45); margin: 0 0 8px; }

/* ---- 总结句 ---- */
.taste-summary { font-size: 13px; font-weight: 600; line-height: 1.6; color: rgba(255,255,255,.85); margin: 0 0 18px; padding: 12px 16px; background: rgba(255,255,255,.04); border-radius: 12px; border: 1px solid rgba(255,255,255,.06); }

/* ---- 单个维度 ---- */
.taste-dim { background: rgba(255,255,255,.025); border: 1px solid rgba(255,255,255,.06); border-radius: 14px; padding: 16px; margin-bottom: 10px; text-align: left; }
.taste-dim-head { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; flex-wrap: wrap; }
.taste-dim-icon { font-size: 20px; line-height: 1; }
.taste-dim-label { font-size: 11px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase; color: rgba(235,235,245,.4); }
.taste-dim-primary { font-size: 15px; font-weight: 700; background: var(--grad, linear-gradient(135deg, hsl(258 84% 64%), hsl(304 78% 66%))); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; flex: 1; }
.taste-dim-bpm { font-size: 11px; font-weight: 600; color: rgba(235,235,245,.35); background: rgba(255,255,255,.06); padding: 2px 8px; border-radius: 6px; white-space: nowrap; }

/* ---- 占比进度条 ---- */
.taste-bars { margin-bottom: 8px; }
.taste-bar-row { display: flex; align-items: center; gap: 8px; margin-bottom: 5px; }
.taste-bar-label { font-size: 11px; color: rgba(235,235,245,.55); width: 42%; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.taste-bar-track { flex: 1; height: 5px; background: rgba(255,255,255,.06); border-radius: 3px; overflow: hidden; }
.taste-bar-fill { display: block; height: 100%; border-radius: 3px; background: var(--grad, linear-gradient(135deg, hsl(258 84% 64%), hsl(304 78% 66%))); transition: width .6s ease; }
.taste-bar-pct { font-size: 11px; font-weight: 600; color: rgba(235,235,245,.35); width: 30px; text-align: right; flex-shrink: 0; }

/* ---- 维度描述 ---- */
.taste-dim-detail { font-size: 12px; line-height: 1.55; color: rgba(235,235,245,.38); margin: 0; }

/* ---- 免责声明 ---- */
.taste-disclaimer { margin: 16px 0 0; font-size: 10px; color: rgba(235,235,245,.22); letter-spacing: .03em; }

/* ---- 响应式 ---- */
@media (min-width: 600px) {
  .taste-dim { padding: 18px 20px; }
  .taste-bar-label { width: 30%; font-size: 12px; }
  .taste-dim-detail { font-size: 13px; }
}
