/* ==========================================================================
   歌星图 · 全站样式
   首页、选歌页、导入弹层、计算中画面、结果页和分享海报用同一套星图风格：深夜蓝底、象牙白字、金色点缀，
   不用霓虹发光和荧光色。中文一律用系统黑体（--sans），大数字和装饰性的拉丁字用系统拉丁衬线（--latin），
   只用系统自带字体。按页面分段：共用 → 首页 → 选歌页 → 导入弹层 → 计算中 → 结果页各章 → 分享层 → 合拍页 → 窄屏与动效偏好。
   页面现在的样子见 docs/product.md，每一轮为什么这么改见 docs/plans/2026-09-23-gameplay-v2-plan.md。
   ========================================================================== */

:root {
  color-scheme: dark;
  /* 七级字号（第二十八轮）：数字、大标题、标题、小标题、正文、辅助、注脚；新加文字只从这里挑 */
  --fs-number: 112px;
  --fs-display: 44px;
  --fs-title: 32px;
  --fs-heading: 21px;
  --fs-body: 16px;
  --fs-support: 14px;
  --fs-note: 12px;
  /* 章节节奏：分隔星到这一章第一行字、这一章最后一行到下一颗分隔星，各章一样（2026-09-28） */
  --chapter-top: 44px;
  --chapter-bottom: 40px;
  /* 底色：页面是 --night，往下几档是更深的衬底、卡片和输入框 */
  --night: #070a17;
  --night-2: #0b1024;
  --bg-deep: #05070f;
  --bg-card: #0d1329;
  --bg-input: #0a0f22;
  --ivory: #f1e9d8;
  --ivory-2: rgba(241, 233, 216, .74);
  --mist: #8e93ad;
  /* 只给线条和装饰：在夜蓝底上对比度只有 3.3:1，小字用 --mist */
  --faint: #5c6282;
  --gold: #d8bf8a;
  --gold-hi: #f3e2b5;
  /* 文案里标了金色、又要更暖一些的字用它（原来只给「热歌榜」，2026-10-01 起这几个字不再上页面）；海报上是 src/poster.js 的 HOT，两处要同值 */
  --hot: #ecc988;
  --line: rgba(216, 191, 138, .14);
  --hair: rgba(216, 191, 138, .24);
  --hair-strong: rgba(216, 191, 138, .42);
  /* 字体统一（第三十二轮）：中文一律用系统黑体。iPhone 和多数安卓手机本来就没有中文宋体，写了也显示成黑体；
     电脑上装的宋体又常常只有一档粗细。大数字和装饰性的拉丁字用系统自带的拉丁衬线体，夹在里面的中文退到黑体 */
  --latin: "Cormorant Garamond", "Cormorant", "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, "PingFang SC", "Hiragino Sans GB", "Noto Sans SC", "Microsoft YaHei UI", serif;
  --sans: "PingFang SC", "Hiragino Sans GB", "Noto Sans SC", "Microsoft YaHei UI", system-ui, sans-serif;
  /* 链接这类要一个字一个字核对的内容才用等宽字体 */
  --code: ui-monospace, "SF Mono", "Cascadia Mono", Consolas, "Courier New", monospace;
  /* 章节分隔：一条两头淡出的金线，正中一颗四角星 */
  --divider-star: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' viewBox='-11 -11 22 22'%3E%3Cdefs%3E%3CradialGradient id='c'%3E%3Cstop offset='0' stop-color='%23fffaf0'/%3E%3Cstop offset='.2' stop-color='%23f3e2b5' stop-opacity='.7'/%3E%3Cstop offset='.5' stop-color='%23d8bf8a' stop-opacity='.16'/%3E%3Cstop offset='1' stop-color='%23d8bf8a' stop-opacity='0'/%3E%3C/radialGradient%3E%3ClinearGradient id='h' x1='-11' x2='11' y1='0' y2='0' gradientUnits='userSpaceOnUse'%3E%3Cstop offset='0' stop-color='%23f3e2b5' stop-opacity='0'/%3E%3Cstop offset='.5' stop-color='%23f3e2b5'/%3E%3Cstop offset='1' stop-color='%23f3e2b5' stop-opacity='0'/%3E%3C/linearGradient%3E%3ClinearGradient id='v' x1='0' x2='0' y1='-11' y2='11' gradientUnits='userSpaceOnUse'%3E%3Cstop offset='0' stop-color='%23f3e2b5' stop-opacity='0'/%3E%3Cstop offset='.5' stop-color='%23f3e2b5'/%3E%3Cstop offset='1' stop-color='%23f3e2b5' stop-opacity='0'/%3E%3C/linearGradient%3E%3C/defs%3E%3Ccircle r='7' fill='url(%23c)'/%3E%3Cpath d='M-11 0H11' stroke='url(%23h)' stroke-width='.6'/%3E%3Cpath d='M0-11V11' stroke='url(%23v)' stroke-width='.6'/%3E%3C/svg%3E");
  --divider-line: linear-gradient(90deg, rgba(216, 191, 138, 0), rgba(216, 191, 138, .3) 20%, rgba(216, 191, 138, .3) 80%, rgba(216, 191, 138, 0));
  /* 星光：一个亮核、一圈淡晕、横竖两道两头渐隐的细星芒（第九轮，取代原来的胖四角星） */
  --glint:
    radial-gradient(closest-side, #fffaf0 0 14%, rgba(243, 226, 181, .55) 24%, rgba(216, 191, 138, .14) 50%, rgba(216, 191, 138, 0) 100%),
    linear-gradient(90deg, rgba(243, 226, 181, 0), rgba(243, 226, 181, .75) 50%, rgba(243, 226, 181, 0)) 50% 50% / 100% 1px no-repeat,
    linear-gradient(0deg, rgba(243, 226, 181, 0), rgba(243, 226, 181, .75) 50%, rgba(243, 226, 181, 0)) 50% 50% / 1px 100% no-repeat;
}

/* ==========================================================================
   共用：页面骨架、顶栏、按钮、提示条、缺封面的占位
   ========================================================================== */

* { box-sizing: border-box; }
html { min-height: 100%; background: var(--bg-deep); }
body {
  min-width: 0;
  min-height: 100%;
  margin: 0;
  overflow-x: hidden;
  background: var(--bg-deep);
  color: var(--ivory);
  font-family: var(--sans);
  font-size: var(--fs-body);
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
}
body.share-open,
body.reference-open,
body.import-open { overflow: hidden; }

button, input, textarea, select { font: inherit; }
/* 文案里的格式（第十四轮）：审阅稿写 **加粗**、==金色==，行首行尾 -> <- 调对齐，由 src/copy-format.js 排成下面几种元素 */
copy-b { font-weight: bolder; }
copy-gold { color: var(--gold-hi); }
/* 注音（2026-10-02）：审阅稿里生僻字后面写“（pái chàng）”，拼音用注脚字号、淡色，不加粗 */
copy-pinyin { color: var(--mist); font-size: var(--fs-note); font-weight: 400; letter-spacing: .02em; }
copy-line { display: block; }
copy-line[data-align="left"] { text-align: left; }
copy-line[data-align="center"] { text-align: center; }
copy-line[data-align="right"] { text-align: right; }
button { color: inherit; }
button:focus-visible,
input:focus-visible,
textarea:focus-visible,
select:focus-visible {
  outline: 2px solid var(--gold-hi);
  outline-offset: 3px;
}
/* 藏起来的一律不占位，个别元素不再各写一遍 */
[hidden] { display: none !important; }
/* 只给读屏软件看的字：页面上不显示，也不占位置 */
.visually-hidden {
  width: 1px;
  height: 1px;
  position: absolute;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.app-shell {
  width: min(100%, 480px);
  min-height: 100vh;
  min-height: 100svh;
  margin: 0 auto;
  position: relative;
  /* overflow: clip 要 iOS 16；更早的系统用 hidden 兜底，只是顶栏不再吸顶 */
  overflow: hidden;
  overflow: clip;
  background: var(--night);
  box-shadow: 0 0 0 1px var(--line), 0 24px 80px rgba(0, 0, 0, .65);
}
/* 侧框线 */
.app-shell::before {
  content: "";
  position: fixed;
  z-index: 90;
  inset: 0 auto 0 50%;
  width: min(100%, 480px);
  transform: translateX(-50%);
  border-inline: 1px solid rgba(216, 191, 138, .12);
  pointer-events: none;
}
/* 胶片噪点：去塑料感 */
.app-shell::after {
  content: "";
  position: fixed;
  z-index: 240;
  inset: 0 auto 0 50%;
  width: min(100%, 480px);
  transform: translateX(-50%);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='0.55'/%3E%3C/svg%3E");
  opacity: .05;
  pointer-events: none;
}
.screen {
  min-height: 100vh;
  min-height: 100svh;
  position: relative;
  background: var(--night);
  animation: screen-in 280ms cubic-bezier(.2, .8, .2, 1);
}
/* 切屏、打开计算中画面时焦点交给整屏（给读屏软件定位），不画焦点框 */
.screen:focus,
.loading-layer:focus { outline: none; }
@keyframes screen-in {
  from { opacity: .01; transform: translateY(12px); }
  to { opacity: 1; transform: none; }
}
@keyframes pulse {
  50% { opacity: .28; transform: scale(.92); }
}
@keyframes spin { to { transform: rotate(360deg); } }

.topbar {
  min-height: 68px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: max(14px, env(safe-area-inset-top, 0px)) 22px 12px;
  border-bottom: 1px solid var(--line);
}

/* 按钮：主按钮是星图图名框（.button--acid），次要按钮是同一副图名框的轻版（.button）：一道细线、不填底、两头不带星，
   和主按钮并排或上下排时看得出是一套（2026-10-04 用户：为什么只给一个单独的光秃秃的文本框；原来是金色细线的长方框）。
   小按钮（.mini-button）照旧是金色细框。按钮字居中（第二十八轮）：原来左字右箭头，箭头去掉以后左对齐没有理由了。
   框是一张九宫格切的 SVG（border-image），四个凹角不拉伸，按钮多宽多高都一样 */
.button {
  width: 100%;
  min-height: 54px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  border: 14px solid transparent;
  border-radius: 0;
  border-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='40' height='40' viewBox='0 0 40 40'%3E%3Cpath d='M10.7 .7H29.3A10 10 0 0 0 39.3 10.7V29.3A10 10 0 0 0 29.3 39.3H10.7A10 10 0 0 0 .7 29.3V10.7A10 10 0 0 0 10.7 .7Z' fill='%23080b1c' fill-opacity='.55' stroke='%23d8bf8a' stroke-opacity='.75' stroke-width='1'/%3E%3C/svg%3E") 14 fill / 14px stretch;
  padding: 0 4px;
  background: none;
  color: var(--gold-hi);
  font: 600 var(--fs-body) var(--sans);
  letter-spacing: .14em;
  cursor: pointer;
  transition: transform 140ms ease, filter 140ms ease;
}
.button:active { transform: translateY(1px); filter: brightness(1.12); }
.button:disabled { opacity: .34; cursor: not-allowed; transform: none; box-shadow: none; }
.mini-button {
  min-height: 44px;
  border: 1px solid var(--gold);
  border-radius: 3px;
  padding: 0 14px;
  background: transparent;
  color: var(--gold-hi);
  font: 600 var(--fs-support) var(--sans);
  letter-spacing: .1em;
  cursor: pointer;
}
.mini-button:disabled { cursor: wait; opacity: .5; }
/* 主按钮是星图上的图名框（2026-09-30 用户选）：四角往里凹的双线金框，底是夜色，字是金色，两头各一颗小星。
   不用整块金色色块。框是一张九宫格切的 SVG（border-image），四个角不拉伸，按钮多宽多高都一样 */
.button--acid {
  border: 14px solid transparent;
  border-radius: 0;
  border-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='40' height='40' viewBox='0 0 40 40'%3E%3Cpath d='M10.7 .7H29.3A10 10 0 0 0 39.3 10.7V29.3A10 10 0 0 0 29.3 39.3H10.7A10 10 0 0 0 .7 29.3V10.7A10 10 0 0 0 10.7 .7Z' fill='%23080b1c' fill-opacity='.9' stroke='%23f3e2b5' stroke-width='1.4'/%3E%3Cpath d='M13.2 5H26.8A8.2 8.2 0 0 0 35 13.2V26.8A8.2 8.2 0 0 0 26.8 35H13.2A8.2 8.2 0 0 0 5 26.8V13.2A8.2 8.2 0 0 0 13.2 5Z' fill='none' stroke='%23d8bf8a' stroke-opacity='.55' stroke-width='.7'/%3E%3C/svg%3E") 14 fill / 14px stretch;
  padding: 0 4px;
  background: none;
  color: var(--gold-hi);
  font-weight: 700;
  letter-spacing: .32em;
}
.button--acid::before,
.button--acid::after {
  content: "";
  flex: 0 0 auto;
  width: 9px;
  height: 9px;
  background: var(--gold-hi);
  clip-path: polygon(50% 0, 60% 40%, 100% 50%, 60% 60%, 50% 100%, 40% 60%, 0 50%, 40% 40%);
}
.button--acid::after { margin-left: -.32em; }
.button--acid:active { box-shadow: none; filter: brightness(1.08); }
/* 两个按钮并排时（导入确认列表）：主按钮的字距收一点、两头的星挨近一点，半宽里排得下一行（2026-10-04：导入卡片里「查看结果」折成了两行）。
   375 宽以下连两头的星也不放。分享层原来也是两个并排，2026-10-10 拿掉「下载图片」，只剩「复制配文」 */
.import-actions .button--acid { gap: 8px; letter-spacing: .16em; }
.import-actions .button--acid::after { margin-left: -.16em; }
@media (max-width: 374px) {
  .import-actions .button { letter-spacing: .08em; }
  .import-actions .button--acid::before,
  .import-actions .button--acid::after { display: none; }
  /* 换海报样式的三个按钮：窄屏上每个四个字原来折成两行（2026-10-04 自查），边距和字距收一点 */
  .share-style { padding: 0 8px; letter-spacing: .04em; }
}
.mini-button--acid {
  border-color: var(--gold-hi);
  background: rgba(243, 226, 181, .08);
  color: var(--gold-hi);
  letter-spacing: .08em;
}
.text-button {
  min-height: 44px;
  display: block;
  margin: 12px auto 0;
  border: 0;
  border-bottom: 1px solid var(--hair-strong);
  padding: 0 4px;
  background: transparent;
  color: var(--mist);
  font: 500 var(--fs-support) var(--sans);
  letter-spacing: .1em;
  cursor: pointer;
}
.text-button--small { min-width: 44px; margin: 0; font-size: var(--fs-note); }
/* 顶栏和浮层的返回、关闭键只画图标，不带圈（2026-10-04 全站统一，原来结果页、分享层、你与好友的歌单那一页带一道细圈）。
   按钮 44px 宽、图标在正中，左右各空 12px：放在顶栏头一个时往左挪 12px，图标对齐 22px 的左边线；关闭键往右挪 12px */
.icon-button {
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  border: 0;
  border-radius: 50%;
  padding: 0;
  background: transparent;
  color: var(--ivory-2);
  font: 500 20px/1 var(--latin);
  cursor: pointer;
}
.icon-button:active { background: rgba(241, 233, 216, .06); color: var(--ivory); }
.topbar > .icon-button:first-child { margin-left: -12px; }
/* 音乐开关（2026-10-05 用户：需要保留一个让用户打开/关闭音乐的按钮）：顶栏最右边，首页在右上金角里；和返回键一样只画图标、不带圈。
   开着时三根竖条跟着拍子跳：一拍 0.8 秒，和 BGM 是同一个拍子，脚本按音乐时间对齐起点（--beat-delay）；左边那根打在拍上，
   中间那根打在半拍上，右边那根两拍一轮；亮起来都是先 60 毫秒涨上去再落回，不是一下跳上去。关着时竖条压平、一道斜线。
   说的是音乐在不在响 */
.topbar > .music-toggle:last-child,
.topbar-end > .music-toggle:last-child { margin-right: -12px; }
.topbar-end { display: flex; align-items: center; gap: 4px; }
.music-toggle { color: var(--gold-hi); }
.music-toggle--corner {
  position: absolute;
  z-index: 4;
  top: calc(max(22px, calc(env(safe-area-inset-top, 0px) + 8px)) + 4px);
  right: 26px;
}
.music-bars { width: 20px; height: 18px; overflow: visible; }
.music-bar,
.music-slash { fill: none; stroke: currentColor; stroke-linecap: round; }
/* 关着时竖条留六成高、暗一点，看得出是音乐，一道斜线压在上面；原来压到三成，只剩三个点，认不出是什么（2026-10-05 自查） */
.music-bar {
  stroke-width: 1.6;
  transform-box: fill-box;
  transform-origin: 50% 100%;
  transform: scaleY(.62);
  opacity: .7;
  transition: transform .3s cubic-bezier(.2, .7, .3, 1), opacity .2s ease;
}
.music-slash { stroke-width: 1.3; transition: opacity .2s ease; }
.music-toggle.is-on .music-slash { opacity: 0; }
.music-toggle.is-on .music-bar { transform: scaleY(.8); opacity: 1; }
.music-toggle.is-playing .music-bar { animation: music-on-beat var(--beat, .8s) linear var(--beat-delay, 0s) infinite; }
.music-toggle.is-playing .music-bar:nth-child(2) { animation-name: music-off-beat; }
.music-toggle.is-playing .music-bar:nth-child(3) { animation: music-two-beats calc(var(--beat, .8s) * 2) linear var(--beat-delay-2, 0s) infinite; }
@keyframes music-on-beat {
  0% { transform: scaleY(1); animation-timing-function: cubic-bezier(.2, .7, .3, 1); }
  70%, 92% { transform: scaleY(.4); animation-timing-function: cubic-bezier(.4, 0, .6, 1); }
  100% { transform: scaleY(1); }
}
@keyframes music-off-beat {
  0%, 42% { transform: scaleY(.5); animation-timing-function: cubic-bezier(.4, 0, .6, 1); }
  50% { transform: scaleY(.95); animation-timing-function: cubic-bezier(.2, .7, .3, 1); }
  92%, 100% { transform: scaleY(.5); }
}
@keyframes music-two-beats {
  0% { transform: scaleY(.9); animation-timing-function: cubic-bezier(.2, .7, .3, 1); }
  40%, 46% { transform: scaleY(.35); animation-timing-function: cubic-bezier(.4, 0, .6, 1); }
  50% { transform: scaleY(.65); animation-timing-function: cubic-bezier(.2, .7, .3, 1); }
  85%, 96% { transform: scaleY(.35); animation-timing-function: cubic-bezier(.4, 0, .6, 1); }
  100% { transform: scaleY(.9); }
}
@media (prefers-reduced-motion: reduce) {
  .music-toggle.is-playing .music-bar { animation: none; }
}
/* 图标（2026-09-28）：index.html 开头那张 SVG 图标表，写成 <i class="icon"><svg><use href="#icon-名字"></use></svg></i>；
   大小跟字号走，颜色跟字色走，外面那层 <i> 让原来按图标字体写的字号、颜色、转动规则照样生效 */
.icon-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
.icon { width: 1em; height: 1em; display: inline-block; flex: none; line-height: 1; vertical-align: -.0625em; }
.icon svg { width: 100%; height: 100%; display: block; fill: currentColor; }

/* 轻提示（2026-10-04 用户：光秃秃的文本框）：一块带四个金角的夜色小牌，左边一颗金星，从下面浮上来，不画成像按钮的框。
   四个金角画在背景上（字是整块换的，角不能挂在里面）；原来是一道圆角的亮金框，看着像一个按钮 */
.toast {
  min-width: 220px;
  max-width: calc(100% - 44px);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  position: fixed;
  /* 浮在所有弹层上面（2026-10-06）：原来是 75，分享层（120）、导入弹层（110）一开，复制配文成功、存图失败这几句提示被压在底下看不见 */
  z-index: 130;
  left: 50%;
  bottom: 116px;
  transform: translateX(-50%);
  border: 1px solid var(--hair);
  padding: 12px 18px;
  background:
    linear-gradient(var(--gold), var(--gold)) left top / 9px 1px no-repeat,
    linear-gradient(var(--gold), var(--gold)) left top / 1px 9px no-repeat,
    linear-gradient(var(--gold), var(--gold)) right top / 9px 1px no-repeat,
    linear-gradient(var(--gold), var(--gold)) right top / 1px 9px no-repeat,
    linear-gradient(var(--gold), var(--gold)) left bottom / 9px 1px no-repeat,
    linear-gradient(var(--gold), var(--gold)) left bottom / 1px 9px no-repeat,
    linear-gradient(var(--gold), var(--gold)) right bottom / 9px 1px no-repeat,
    linear-gradient(var(--gold), var(--gold)) right bottom / 1px 9px no-repeat,
    rgba(11, 16, 36, .97);
  background-origin: border-box;
  box-shadow: 0 18px 44px rgba(0, 0, 0, .55);
  color: var(--ivory);
  font: 500 var(--fs-support)/1.5 var(--sans);
  letter-spacing: .04em;
  text-align: left;
  pointer-events: none;
  animation: toast-in 320ms cubic-bezier(.2, .8, .2, 1);
}
.toast::before {
  content: "";
  width: 11px;
  height: 11px;
  flex: none;
  background: var(--gold-hi);
  clip-path: polygon(50% 0, 60% 40%, 100% 50%, 60% 60%, 50% 100%, 40% 60%, 0 50%, 40% 40%);
}
@keyframes toast-in {
  from { opacity: 0; transform: translate(-50%, 12px); }
}
/* 刚出现、字还没写进去的那一下（给读屏软件读，src/app.js 的 showToast）：先不显出空框 */
.toast--pending { opacity: 0; }
/* 带按钮的提示（「清空了N首 撤销」）：按钮要能点 */
.toast--action {
  justify-content: flex-start;
  gap: 12px;
  padding-block: 4px;
  padding-right: 8px;
  pointer-events: auto;
  text-align: left;
}
.toast__action {
  margin-left: auto;
  min-width: 44px;
  min-height: 44px;
  border: 0;
  padding: 0 4px;
  background: transparent;
  color: var(--gold-hi);
  font: 600 var(--fs-support) var(--sans);
  cursor: pointer;
}

/* 缺封面的占位（2026-09-28 全站统一）：深蓝一块，左上角一个淡淡的衬线首字。封面图叠在上面，加载失败就藏掉、露出首字。
   格子多大由各处的 --cover-size 定，首字跟着缩放 */
.cover-blank {
  display: grid;
  overflow: hidden;
  background: linear-gradient(145deg, #2a3150, #141a33);
  color: rgba(241, 233, 216, .36);
  font: 400 calc(var(--cover-size, 44px) * .4)/1 var(--latin);
}
.cover-blank > * { grid-area: 1 / 1; }
.track-cover-fallback {
  align-self: start;
  justify-self: start;
  padding: calc(var(--cover-size, 44px) * .08) 0 0 calc(var(--cover-size, 44px) * .11);
}
.track-cover-image {
  width: 100%;
  height: 100%;
  display: block;
  object-fit: cover;
  color: transparent;
}

/* ==========================================================================
   首页（2026-09-30，参照杉浦康平的宇宙图）：满版的夜空、斜穿的银河、右边一角外圈是五线谱的盖图、左下一把北斗
   （2026-10-02），都画在整页的画布上（src/cover-art.js），只是装饰。四个角各一个金角，不画整圈的框。
   左上竖排「歌星图鉴」字标（src/wordmark.js），旁边竖排一句副标题；最下面是唯一的按钮。
   ========================================================================== */

.landing {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--bg-deep);
}

/* 整页的夜空画布垫在最底下 */
.landing-sky {
  position: absolute;
  z-index: 0;
  inset: 0;
  animation: landing-fade 1.6s ease .1s both;
}
.landing-sky canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  pointer-events: none;
}
@keyframes landing-fade {
  from { opacity: 0; }
}

/* 四个金角：每个角横竖两道细金线，躲开刘海和底部横条 */
.landing-frame {
  --corner: 30px;
  position: absolute;
  z-index: 2;
  inset: max(22px, calc(env(safe-area-inset-top, 0px) + 8px)) 22px max(22px, calc(env(safe-area-inset-bottom, 0px) + 8px));
  background:
    linear-gradient(var(--gold), var(--gold)) left top / var(--corner) 1.4px no-repeat,
    linear-gradient(var(--gold), var(--gold)) left top / 1.4px var(--corner) no-repeat,
    linear-gradient(var(--gold), var(--gold)) right top / var(--corner) 1.4px no-repeat,
    linear-gradient(var(--gold), var(--gold)) right top / 1.4px var(--corner) no-repeat,
    linear-gradient(var(--gold), var(--gold)) left bottom / var(--corner) 1.4px no-repeat,
    linear-gradient(var(--gold), var(--gold)) left bottom / 1.4px var(--corner) no-repeat,
    linear-gradient(var(--gold), var(--gold)) right bottom / var(--corner) 1.4px no-repeat,
    linear-gradient(var(--gold), var(--gold)) right bottom / 1.4px var(--corner) no-repeat;
  opacity: .85;
  pointer-events: none;
  animation: landing-fade 1.2s ease both;
}

/* 朋友发来合拍链接时首页多一条邀请提示；首页的画布上有星，垫一层不透明的底，星不会从字后面透出来。
   和卡片同一副框（2026-10-04）：1px 细线、四个金角，原来是一道圆角的亮金框 */
.invite-banner {
  display: block;
  position: relative;
  z-index: 3;
  /* 右边让出右上金角里的音乐开关那一列（2026-10-05），原来左右都是 30 px，开关压在它右上角 */
  margin: max(40px, calc(env(safe-area-inset-top, 0px) + 28px)) 74px 0 30px;
  border: 1px solid var(--hair);
  padding: 12px 14px;
  background: var(--night);
  color: var(--ivory);
  font: 700 var(--fs-note)/1.6 var(--sans);
  /* 两行时只在标点处换行、长短匀开，不让“一次”“间的联系”单独掉一行（2026-10-06 在 360 下看到） */
  word-break: keep-all;
  overflow-wrap: anywhere;
  text-wrap: balance;
}

/* 字标竖排在左上，高度跟着屏幕高度走：一个字的边长约为字标高度的 0.3；SVG 四周留了光晕的边。
   变量不能像普通属性那样先写 vh 再写 svh 兜底：后一条照样生效，不认 svh 的 iOS 15.0 到 15.3 上算出来是无效值，
   所以认 svh 时才换成 svh（2026-10-04） */
.hero-copy {
  --wordmark-height: clamp(300px, 58vh, 500px);
  position: relative;
  z-index: 3;
  display: flex;
  align-items: flex-start;
  /* 字标 SVG 右边自带十来 px 的边，再隔 2 px，副标题离字的笔画 11 到 16 px */
  gap: 2px;
  padding: max(50px, calc(env(safe-area-inset-top, 0px) + 38px)) 22px 0 18px;
}
@supports (height: 1svh) {
  .hero-copy { --wordmark-height: clamp(300px, 58svh, 500px); }
}
.invite-banner:not([hidden]) + .hero-copy { padding-top: 18px; }
/* 320×568 这类矮屏上，邀请提示占了 60 多 px：字标矮一截，按钮和下面两个金角留在第一屏里（2026-10-04，原来整页多出 23 px） */
@media (max-height: 620px) {
  .invite-banner:not([hidden]) + .hero-copy { --wordmark-height: clamp(250px, calc(58vh - 70px), 500px); }
  @supports (height: 1svh) {
    .invite-banner:not([hidden]) + .hero-copy { --wordmark-height: clamp(250px, calc(58svh - 70px), 500px); }
  }
}
.hero-title { margin: 0; line-height: 0; }
.hero-wordmark { display: block; }
.hero-wordmark .wordmark {
  display: block;
  height: var(--wordmark-height);
  /* 四个字竖排一列：字标连同四周的边是 1240 × 4330（src/wordmark.js 的 wordmarkBox 加 WORDMARK_PAD） */
  width: calc(var(--wordmark-height) * .2864);
  filter: drop-shadow(0 0 16px rgba(3, 4, 11, .85));
  animation: landing-rise .7s cubic-bezier(.2, .8, .3, 1) .12s both;
}
/* 字标的动效（2026-10-04 改，用户：字里的星要等好几秒才亮起来，整个画面看起来非常的廉价）。时间写在 src/wordmark.js 的 HOME_WORDMARK：
   1. 一进来金色虚线 0.9 秒从“歌”的流星连到“鉴”的星（0.3 秒起，匀速，好和星对上），连到哪颗哪颗亮起来，之后一直亮着；
   2. 一道光沿虚线从“歌”跑到“鉴”，经过哪颗哪颗闪一下。2026-10-05 起跟着 BGM 的拍子，两小节一趟（src/bgm-score.js），
      由 src/wordmark.js 的 driveWordmark 每一帧摆，这里只留它们不动时的样子；
   3. 光晕一直轻轻明暗，几颗快慢不同。
   说的是四首歌连成一个星座。原来一轮 10 秒整屏重放、星要等虚线连到才亮、每轮末尾一起暗下去，作废 */
.hero-wordmark .wm-reveal { animation: wm-draw var(--wm-draw, .9s) linear var(--wm-start, .3s) both; }
@keyframes wm-draw {
  from { stroke-dashoffset: var(--wm-len); }
  to { stroke-dashoffset: 0; }
}
.hero-wordmark .wm-starset { transform-box: fill-box; transform-origin: center; animation: wm-ignite .8s var(--on) both; }
@keyframes wm-ignite {
  0% { opacity: .35; transform: scale(.6); animation-timing-function: cubic-bezier(.2, .7, .3, 1); }
  40% { opacity: 1; transform: scale(1.2); animation-timing-function: cubic-bezier(.4, 0, .2, 1); }
  100% { opacity: 1; transform: scale(1); }
}
/* 跑过的光、星闪的那一下由脚本摆：闪的时候以星为中心放大 */
.hero-wordmark .wm-flare { transform-box: fill-box; transform-origin: center; }
.hero-wordmark .wm-glow { animation: wm-twinkle var(--tw) ease-in-out infinite alternate; }
@keyframes wm-twinkle { to { opacity: .45; } }
@media (prefers-reduced-motion: reduce) {
  .hero-wordmark .wm-reveal,
  .hero-wordmark .wm-starset,
  .hero-wordmark .wm-glow { animation: none; }
}
/* 副标题竖排贴着字标右边，和第一个字齐头，和字标算一组（2026-10-02）。原来比第一个字低一截、隔开 16 px，
   盖图进来以后它正好夹在字标和盖图正中间，离两边都是 30 px 左右，看着哪边都不挨（用户：平白出现，突兀）。
   字标 SVG 四周留了 120/4330 的边（src/wordmark.js 的 WORDMARK_PAD），“歌”最上面一横又比字格低 40 左右，
   往下错开这两段，“每”字的顶和那一横齐（放大看过，只错开边时高出 4 px） */
.hero-note {
  margin: calc(var(--wordmark-height) * 160 / 4330) 0 0;
  writing-mode: vertical-rl;
  color: var(--ivory);
  font: 600 var(--fs-body)/1 var(--sans);
  letter-spacing: .5em;
  text-shadow: 0 0 10px rgba(3, 4, 11, .9);
  animation: landing-rise .7s cubic-bezier(.2, .8, .3, 1) .26s both;
}
@keyframes landing-rise {
  from { opacity: 0; transform: translateY(18px); }
}

/* 字标和按钮之间留给北斗的地方：北斗画在整页的画布上，这里只占住高度 */
.landing-stage {
  flex: 1 1 auto;
  min-height: 24px;
  position: relative;
  z-index: 1;
}

.landing-actions {
  position: relative;
  z-index: 3;
  padding: 6px 44px max(48px, calc(env(safe-area-inset-bottom, 0px) + 38px));
  animation: landing-rise .7s cubic-bezier(.2, .8, .3, 1) .52s both;
}
.landing-actions .button--acid { min-height: 58px; font-size: var(--fs-body); }
.landing-actions .button--acid:focus-visible { outline: 2px solid var(--gold-hi); outline-offset: 3px; }

/* 网站备案号（2026-10-10）：法规要它标在主页底部正中、链接到备案管理系统。手机上首页不挂，挂在结果页最后一行（用户选）；
   电脑打开时才挂：屏宽超过 480 px，页面成了屏幕正中的一列（平板、横过来的手机也算）。阿里云写明目前只要求电脑端的网站底部挂。
   落在两个下金角之间：外面这一层高度为 0，压在金角那道横线的正中，字在里面上下居中，字的正中就和横线齐，
   不靠位移去凑（画布量亮星该让开哪里时会扣掉位移）。跟着四个金角一起淡入、不升起 */
.landing-beian {
  display: none;
  position: absolute;
  z-index: 3;
  right: 0;
  bottom: calc(max(22px, calc(env(safe-area-inset-bottom, 0px) + 8px)) + .7px);
  left: 0;
  align-items: center;
  justify-content: center;
  height: 0;
  animation: landing-fade 1.2s ease both;
}
@media (min-width: 481px) {
  .landing-beian { display: flex; }
}
/* 首页和结果页那一行同一个样子：注脚字号的淡色字；字四周多留一圈，手指好按，看着位置不变 */
.beian-link {
  padding: 10px 14px;
  color: var(--mist);
  font: 400 var(--fs-note)/1 var(--sans);
  letter-spacing: .06em;
  text-decoration: none;
  white-space: nowrap;
  text-shadow: 0 0 8px rgba(3, 4, 11, .9);
}
.beian-link:active { color: var(--ivory); }
.beian-link:focus-visible { outline: 1px solid var(--gold-hi); outline-offset: -4px; }

/* ==========================================================================
   选歌页：顶栏（「←」、标题、导入歌单）、搜歌框（右端换一批或清除）、封面长廊、歌曲列表、底部已选栏。
   整屏正好一屏高，只有歌曲列表自己滚（2026-09-28）；列表底边停在已选栏上沿。
   2026-10-04 用户嫌头尾两块不好看，重排：导入歌单进顶栏，换一批进搜歌框，搜歌框和已选栏用结果页卡片那副框
   ========================================================================== */

.selector {
  height: 100vh;
  height: 100svh;
  display: flex;
  flex-direction: column;
  background:
    radial-gradient(92% 34% at 78% 7%, rgba(216, 191, 138, .1), transparent 66%),
    var(--night);
}
/* 整屏不滚，顶栏不用吸顶，也不画底线。「←」的图标对着 22px 的左边线 */
.selector-bar {
  min-height: 64px;
  flex: none;
  justify-content: flex-start;
  gap: 2px;
  border-bottom: 0;
  padding: max(10px, env(safe-area-inset-top, 0px)) 22px 2px;
}
.selector-title {
  min-width: 0;
  flex: 1;
  overflow: hidden;
  font: 700 var(--fs-heading)/1.2 var(--sans);
  letter-spacing: .06em;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* 导入歌单：顶栏右边的次要按钮，金色细框；框高 36px，手指点的范围上下各多出 4px */
.quick-import-trigger {
  min-height: 36px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: none;
  position: relative;
  border: 1px solid var(--hair-strong);
  border-radius: 3px;
  padding: 0 12px;
  background: transparent;
  color: var(--gold-hi);
  font: 600 var(--fs-support) var(--sans);
  letter-spacing: .08em;
  white-space: nowrap;
  cursor: pointer;
}
.quick-import-trigger::before { content: ""; position: absolute; inset: -4px 0; }
.quick-import-trigger i { font-size: 16px; }
.quick-import-trigger:active { background: rgba(243, 226, 181, .08); }

/* 已选栏高 68px、离屏幕底边至少 12px：面板底下让出这么多，列表的底边正好停在已选栏上沿 */
.input-panel {
  min-height: 0;
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  padding: 6px 22px calc(68px + max(12px, env(safe-area-inset-bottom, 0px)));
}
.input-panel > * { flex: none; }

/* 搜歌时选歌页顶上的原图条：点缩略图看大图 */
.screenshot-reference {
  display: grid;
  grid-template-columns: minmax(52px, auto) 1fr auto;
  align-items: center;
  gap: 10px;
  margin-bottom: 15px;
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 8px;
  background: var(--bg-card);
}
.reference-thumbnails {
  max-width: 116px;
  display: flex;
  gap: 6px;
  overflow-x: auto;
  scrollbar-width: none;
}
.reference-thumbnails::-webkit-scrollbar { display: none; }
.reference-thumb {
  width: 52px;
  height: 52px;
  flex: 0 0 52px;
  position: relative;
  overflow: hidden;
  border: 1px solid var(--hair-strong);
  border-radius: 4px;
  padding: 0;
  background: var(--bg-deep);
}
.reference-thumb img { width: 100%; height: 100%; object-fit: cover; }
/* 缩略图角上的序号 */
.reference-thumb span,
.upload-preview__image span {
  min-width: 18px;
  height: 18px;
  display: grid;
  place-items: center;
  position: absolute;
  right: 3px;
  bottom: 3px;
  border-radius: 4px;
  padding: 0 4px;
  background: rgba(5, 7, 15, .88);
  color: var(--gold-hi);
  font: 700 var(--fs-note) var(--sans);
}
.reference-thumb.is-active,
.upload-preview__image.is-active { border-color: var(--gold-hi); }
.reference-copy { min-width: 0; }
.reference-copy small { display: block; color: var(--gold); font: 700 var(--fs-note) var(--sans); letter-spacing: .14em; }
.reference-remove {
  min-width: 44px;
  min-height: 44px;
  border: 0;
  padding: 0 10px;
  background: transparent;
  color: var(--ivory-2);
  font: 700 var(--fs-note) var(--sans);
}

/* 搜歌框（2026-10-04）：和结果页星图下的搜歌框同一副框，1px 细线、四个金角；外框贴 22px，图标和字从 39px 起。
   右端没输入时是「换一批」，输入了字换成清除键 */
.search-box {
  min-height: 52px;
  display: flex;
  align-items: center;
  position: relative;
  border: 1px solid var(--hair);
  background: rgba(9, 12, 28, .86);
  transition: border-color 160ms ease;
}
.search-box:focus-within { border-color: var(--hair-strong); }
.search-box:focus-within .corner { border-color: var(--gold-hi); }
.search-box__field {
  min-width: 0;
  flex: 1;
  display: flex;
  align-items: center;
  gap: 10px;
  padding-left: 16px;
  color: var(--gold);
  cursor: text;
}
.search-box__field > i { flex: none; font-size: 20px; }
/* 输入框的字不能小于 16px：再小 iPhone 聚焦时会自动放大整页 */
.search-box input {
  width: 100%;
  min-width: 0;
  min-height: 44px;
  border: 0;
  outline: 0;
  padding: 0 6px 0 0;
  background: transparent;
  color: var(--ivory);
  font-size: var(--fs-body);
  -webkit-appearance: none;
  appearance: none;
}
.search-box input::placeholder { color: var(--mist); }
/* 浏览器自带的清除叉不用，右端自己放一个，和「换一批」换着出现 */
.search-box input::-webkit-search-cancel-button { display: none; -webkit-appearance: none; }
/* 换一批：前面一道竖的细线和输入的地方隔开 */
.refresh-discovery {
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: none;
  position: relative;
  border: 0;
  padding: 0 15px 0 14px;
  background: transparent;
  color: var(--gold-hi);
  font: 600 var(--fs-support) var(--sans);
  letter-spacing: .06em;
  white-space: nowrap;
  cursor: pointer;
}
.refresh-discovery::before {
  content: "";
  width: 1px;
  height: 20px;
  position: absolute;
  left: 0;
  top: calc(50% - 10px);
  background: var(--hair);
}
.refresh-discovery i { color: var(--gold); font-size: 16px; }
.refresh-discovery:active { color: var(--ivory); }
.search-clear {
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  flex: none;
  margin-right: 4px;
  border: 0;
  padding: 0;
  background: transparent;
  color: var(--mist);
  cursor: pointer;
}
.search-clear i { font-size: 16px; }
.search-clear:active { color: var(--ivory); }

/* 封面长廊（2026-10-04 用户：原来可以直接左右滑动专辑封面选歌）：和下面的列表列同样的歌。
   左右滑翻歌，点框里那张选上；正中的取景框（三个金角，第四个角是加选按钮）和框下的歌名不动，封面从框里转过去。
   封面多大由 --cf-cover 定，JS 照它排：离正中一张的让开取景框，再往外一张比一张挨得紧、小、暗 */
/* 长廊自成一层（2026-10-06 用户：这个图层怎么在上面）：框下的歌名、取景框和加号的层级只在长廊里面比，
   已选歌曲面板从底栏往上展开时整块盖住长廊。原来它们和底栏在同一层里比，50 压过底栏的 29，歌名浮在面板表头上 */
.coverflow-stage {
  --cf-cover: 80px;
  height: calc(var(--cf-cover) + 62px);
  flex: none;
  position: relative;
  isolation: isolate;
  overflow: hidden;
  margin: 6px -22px 2px;
  touch-action: pan-y;
  cursor: grab;
  -webkit-user-select: none;
  user-select: none;
}
.coverflow-stage.is-dragging { cursor: grabbing; }
/* 封面不放进同一个立体空间里排前后（2026-10-05）：换一批翻牌时，侧过来的牌有一半凸到前面，会从正中那张前面穿过去。
   每张封面的 transform 自带 perspective(900px)，消失点还是长廊正中，看上去和原来一样；谁压着谁按 z-index 定 */
.coverflow-rail {
  position: absolute;
  inset: 0;
}
/* 封面平时藏着，排到正中左右 4 张以内才显出来、才放图 */
.coverflow-card {
  width: var(--cf-cover);
  height: var(--cf-cover);
  position: absolute;
  left: calc(50% - var(--cf-cover) / 2);
  top: 12px;
  visibility: hidden;
  border: 0;
  padding: 0;
  background: transparent;
  cursor: pointer;
  will-change: transform;
}
.coverflow-card .song-cover {
  --cover-size: var(--cf-cover);
  width: 100%;
  height: 100%;
  border: 1px solid rgba(241, 233, 216, .14);
  border-radius: 3px;
  box-shadow: 0 14px 26px rgba(0, 0, 0, .46);
}
/* 长廊里相邻的封面互相压着：缺封面时首字放在正中，免得被挡住半个 */
.coverflow-card .track-cover-fallback { align-self: center; justify-self: center; padding: 0; }
/* 离正中越远越暗：JS 写 --dim（0 到 1），盖一层夜色 */
.coverflow-card::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 3px;
  background: rgba(5, 7, 15, .64);
  opacity: var(--dim, 0);
  pointer-events: none;
}
.coverflow-card.is-picked .song-cover { border-color: rgba(243, 226, 181, .7); }
/* 牌背（2026-10-05 用户：聚到中间以后一下子变成几张图片瞬间切换，看着凌乱）：换一批时封面翻过去露出来，
   一摞只露出一样的牌背，新的一批在牌背底下换好，发开时一张张翻过来。平时藏着，显隐由洗牌的动画管。
   夜色底、四个金角、正中一颗金色星芒和一道细圈：每首歌都是一颗星，翻开才知道是哪首 */
.coverflow-card__back {
  position: absolute;
  inset: 0;
  border: 1px solid rgba(241, 233, 216, .14);
  border-radius: 3px;
  background:
    linear-gradient(var(--gold), var(--gold)) left 5px top 5px / 8px 1px no-repeat,
    linear-gradient(var(--gold), var(--gold)) left 5px top 5px / 1px 8px no-repeat,
    linear-gradient(var(--gold), var(--gold)) right 5px top 5px / 8px 1px no-repeat,
    linear-gradient(var(--gold), var(--gold)) right 5px top 5px / 1px 8px no-repeat,
    linear-gradient(var(--gold), var(--gold)) left 5px bottom 5px / 8px 1px no-repeat,
    linear-gradient(var(--gold), var(--gold)) left 5px bottom 5px / 1px 8px no-repeat,
    linear-gradient(var(--gold), var(--gold)) right 5px bottom 5px / 8px 1px no-repeat,
    linear-gradient(var(--gold), var(--gold)) right 5px bottom 5px / 1px 8px no-repeat,
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cpolygon fill='%23f3e2b5' points='50,0 60,40 100,50 60,60 50,100 40,60 0,50 40,40'/%3E%3C/svg%3E") center / 24% no-repeat,
    radial-gradient(circle closest-side, transparent 60%, rgba(216, 191, 138, .34) 61.5%, rgba(216, 191, 138, .34) 63%, transparent 64.5%),
    var(--bg-card);
  opacity: 0;
  pointer-events: none;
}
/* 发出去还没翻开的牌：封面图还没到，牌背朝上等着，图到了再翻开（2026-10-05 用户：很多专辑封面老是显示不出来） */
.coverflow-card.is-face-down .coverflow-card__back { opacity: 1; }
/* 取景框：比封面四周各宽 6px */
.coverflow-finder {
  width: calc(var(--cf-cover) + 12px);
  height: calc(var(--cf-cover) + 12px);
  position: absolute;
  z-index: 50;
  left: calc(50% - var(--cf-cover) / 2 - 6px);
  top: 6px;
  pointer-events: none;
}
.coverflow-finder .corner { border-color: var(--gold-hi); }
/* 框右下角的加选按钮：和列表里的加选按钮同一种，小一号；手指点的范围四周各多出 8px。
   写两个类名，压过后面列表那条 .add-button 的 44px */
.add-button.coverflow-pick {
  width: 30px;
  height: 30px;
  display: grid;
  place-items: center;
  position: absolute;
  z-index: 51;
  left: calc(50% + var(--cf-cover) / 2 - 9px);
  top: calc(var(--cf-cover) + 3px);
  padding: 0;
  background: var(--night);
}
.coverflow-pick i { font-size: 13px; }
.coverflow-pick::before { content: ""; position: absolute; inset: -8px; }
.coverflow-pick .add-star { width: 16px; height: 16px; }
.coverflow-pick:active { transform: scale(.92); }
.coverflow-caption {
  max-width: calc(100% - 96px);
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 8px;
  position: absolute;
  z-index: 50;
  left: 50%;
  top: calc(var(--cf-cover) + 36px);
  margin: 0;
  overflow: hidden;
  transform: translateX(-50%);
  white-space: nowrap;
  pointer-events: none;
}
.coverflow-caption strong,
.coverflow-caption small { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
/* 换一批洗牌时（2026-10-04）：框下的歌名和框角的加号先淡出，新的一批发开以后再淡入 */
.coverflow-caption,
.add-button.coverflow-pick { transition: opacity 180ms ease; }
.coverflow-stage.is-shuffling .coverflow-caption,
.coverflow-stage.is-shuffling .coverflow-pick { opacity: 0; pointer-events: none; }
.coverflow-caption strong { flex: 0 1 auto; color: var(--ivory); font: 600 var(--fs-support)/1.45 var(--sans); letter-spacing: .02em; }
.coverflow-caption small { flex: 0 2 auto; color: var(--mist); font: var(--fs-note)/1.45 var(--sans); }
.coverflow-caption small:empty { display: none; }
/* 屏幕矮：封面小一号，高度留给列表 */
@media (max-height: 700px) {
  .coverflow-stage { --cf-cover: 68px; }
}

/* 歌曲列表：占满搜索框下面剩下的高度，自己滚 */
.search-results {
  min-height: 0;
  flex: 1 1 auto;
  display: grid;
  align-content: start;
  gap: 4px;
  overflow-y: auto;
  margin-top: 8px;
  padding: 0 0 24px;
  overscroll-behavior-y: contain;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
  scrollbar-color: rgba(216, 191, 138, .22) transparent;
  scroll-padding-block: 45%;
}

/* 一首歌一行：封面、歌名和歌手、右边的加减按钮。选歌列表、已选列表、导入确认列表的歌名和歌手都是 16/12 */
.song-row {
  min-height: 68px;
  display: grid;
  grid-template-columns: 44px 1fr auto;
  align-items: center;
  gap: 12px;
  padding: 11px 12px;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: var(--bg-card);
}
.search-results .song-row {
  min-height: 76px;
  grid-template-columns: 58px minmax(0, 1fr) 46px;
  gap: 12px;
  border: 1px solid transparent;
  border-radius: 4px;
  padding: 8px 7px;
  background: transparent;
  transition: background-color 160ms ease, border-color 160ms ease;
}
.search-results .song-row.is-browse-active {
  border-color: rgba(216, 191, 138, .2);
  background: linear-gradient(100deg, rgba(216, 191, 138, .12), rgba(241, 233, 216, .025));
}
.song-cover {
  --cover-size: 44px;
  width: 44px;
  height: 44px;
  position: relative;
  border-radius: 4px;
}
.search-results .song-cover {
  --cover-size: 58px;
  width: 58px;
  height: 58px;
  border-radius: 4px;
  box-shadow: 0 9px 22px rgba(0, 0, 0, .2);
}
.song-meta { min-width: 0; }
.song-meta strong,
.song-meta small { display: block; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.song-meta strong { color: var(--ivory); font: 600 var(--fs-body)/1.4 var(--sans); letter-spacing: .02em; }
.song-meta small { margin-top: 4px; color: var(--mist); font: var(--fs-note) var(--sans); }
.search-results .song-meta small { margin-top: 6px; }
.add-button,
.remove-button {
  width: 44px;
  height: 44px;
  border: 1px solid var(--hair-strong);
  border-radius: 50%;
  background: transparent;
  font: 20px var(--sans);
  cursor: pointer;
}
/* 加选按钮（2026-10-02）：没选时一道细金圈、一个细加号；选上以后圈里点亮一颗金色星芒，
   和主按钮两头、字标上同一种细星芒，底下一团很淡的暖光。原来实心浅金圆底加深色的勾，用户嫌太塑料 */
.add-button { border-color: rgba(216, 191, 138, .6); color: var(--gold-hi); font-size: 16px; }
.add-button.is-added {
  border-color: var(--gold-hi);
  background: radial-gradient(closest-side, rgba(243, 226, 181, .22), rgba(243, 226, 181, .06) 70%, rgba(243, 226, 181, 0));
}
.add-star {
  display: block;
  width: 24px;
  height: 24px;
  margin: auto;
  background: var(--gold-hi);
  clip-path: polygon(50% 0, 60% 40%, 100% 50%, 60% 60%, 50% 100%, 40% 60%, 0 50%, 40% 40%);
}
.remove-button { border-color: transparent; color: var(--mist); }

/* 列表底部：往下刷时的载入中、刷到底、刷到一半断了的重试；平时什么都不写 */
.discovery-sentinel {
  min-height: 62px;
  display: grid;
  place-items: center;
  margin-top: 6px;
  color: var(--mist);
  font: 600 var(--fs-note) var(--sans);
}
.discovery-sentinel__status { display: inline-flex; align-items: center; gap: 8px; }
.discovery-sentinel__status i {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--gold-hi);
  box-shadow: 0 0 0 4px rgba(243, 226, 181, .12);
  animation: pulse 1s steps(2, end) infinite;
}
.discovery-sentinel__retry {
  min-height: 42px;
  border: 0;
  padding: 0 12px;
  background: transparent;
  color: var(--gold);
  font: 700 var(--fs-note) var(--sans);
  cursor: pointer;
}
/* 列表里的状态（2026-10-04 用户：错误界面像根本没好好做过的半成品）：一幅小星图、一句话，有重试再加一个按钮，排在列表正中。
   没连上是两颗星中间一道断开的虚线；没搜到、已选歌曲还空着是一颗暗星；搜索中三颗星依次亮。
   原来是一个虚线框，字和按钮挤在框的上沿，下面空着一大片 */
.search-results[data-state] { align-content: center; padding-bottom: 56px; }
.list-state {
  display: grid;
  justify-items: center;
  gap: 16px;
  padding: 24px 12px;
  text-align: center;
}
/* 两行排得一样长，标点不放到行首，只在词和词之间折行（2026-10-06 自查：原来“歌单链接”的“接”、“你与好友”的“友”掉到第二行）。
   词和词之间的折行位置由页面脚本按分词插进去（src/app.js 的 wordWrapMarkup）；一个词长过一行时才在字中间折 */
.list-state p { max-width: 17em; margin: 0; color: var(--ivory-2); font: 500 var(--fs-body)/1.6 var(--sans); letter-spacing: .04em; word-break: keep-all; overflow-wrap: anywhere; line-break: strict; text-wrap: balance; }
.list-state .mini-button { min-width: 128px; margin-top: 4px; }
.list-state__art {
  width: 132px;
  height: 16px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  position: relative;
}
.list-state__art i {
  width: 14px;
  height: 14px;
  flex: none;
  background: var(--gold-hi);
  clip-path: polygon(50% 0, 60% 40%, 100% 50%, 60% 60%, 50% 100%, 40% 60%, 0 50%, 40% 40%);
}
/* 断开的虚线：从两颗星各引出一段，正中留出 16px 的缺口 */
.list-state__art--broken::before,
.list-state__art--broken::after {
  content: "";
  width: calc(50% - 30px);
  height: 1px;
  position: absolute;
  top: 50%;
  background: repeating-linear-gradient(90deg, rgba(216, 191, 138, .62) 0 4px, transparent 4px 8px);
}
.list-state__art--broken::before { left: 22px; }
.list-state__art--broken::after { right: 22px; }
.list-state__art--alone { width: 14px; }
.list-state__art--alone i { background: var(--faint); }
.list-state__art--loading { width: 60px; }
.list-state__art--loading i { width: 10px; height: 10px; opacity: .22; animation: list-state-blink 1.2s ease-in-out infinite; }
.list-state__art--loading i:nth-child(2) { animation-delay: .2s; }
.list-state__art--loading i:nth-child(3) { animation-delay: .4s; }
@keyframes list-state-blink {
  30% { opacity: 1; }
  60%, 100% { opacity: .22; }
}
/* 已选歌曲还空着：面板里不用排到正中，上下留白就够 */
.selected-list .list-state { padding: 28px 12px; }

/* 底部已选栏（2026-10-04）：和搜歌框同一副框，1px 细线、四个金角。左边最近选的三首叠成一摞、已选首数，点开已选歌曲；
   右边主按钮。外框和上面的内容一样贴 22px，框里的东西从 39px 起：1px 框线加 16px 内边距 */
.selector-dock {
  width: min(calc(100% - 44px), 436px);
  position: fixed;
  z-index: 29;
  inset: auto max(22px, calc((100vw - 436px) / 2)) max(12px, env(safe-area-inset-bottom));
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  border: 1px solid var(--hair);
  background: rgba(9, 12, 28, .94);
  box-shadow: 0 18px 40px rgba(0, 0, 0, .5);
  -webkit-backdrop-filter: blur(18px);
  backdrop-filter: blur(18px);
}
.selection-section {
  display: flex;
  flex-direction: column-reverse;
  min-width: 0;
  z-index: 29;
  border: 0;
  padding: 0;
  pointer-events: none;
}
.selection-toggle {
  width: 100%;
  min-height: 68px;
  display: flex;
  align-items: center;
  gap: 10px;
  border: 0;
  padding: 0 8px 0 16px;
  background: transparent;
  color: var(--ivory);
  text-align: left;
  cursor: pointer;
  pointer-events: auto;
}
.selection-toggle [data-selected-label] { min-width: 0; font: 600 var(--fs-support) var(--sans); white-space: nowrap; }
.selection-toggle [data-selected-label] b { margin-right: 3px; color: var(--gold-hi); font: 600 var(--fs-title)/1 var(--latin); font-variant-numeric: lining-nums; }
.selection-toggle > i { flex: none; color: var(--gold); font-size: 14px; transition: transform 160ms ease; }
.selection-toggle[aria-expanded="true"] > i { transform: rotate(180deg); }
/* 最近选的三首叠成一摞：新选的在最右、压在最上面，早选的往左歪；一首没选时是一个虚线空位 */
.dock-stack { display: flex; flex: none; align-items: center; }
.dock-stack .song-cover {
  --cover-size: 30px;
  width: 30px;
  height: 30px;
  flex: none;
  border: 1px solid rgba(7, 10, 23, .9);
  border-radius: 3px;
  box-shadow: 0 4px 10px rgba(0, 0, 0, .45);
}
.dock-stack .song-cover + .song-cover { margin-left: -16px; }
.dock-stack .song-cover:nth-last-child(2) { transform: rotate(-6deg); }
.dock-stack .song-cover:nth-last-child(3) { transform: rotate(-12deg); }
.dock-stack:empty::before {
  content: "";
  width: 30px;
  height: 30px;
  border: 1px dashed var(--hair-strong);
  border-radius: 3px;
}
/* 选上一首歌时飞进那一摞的封面（只在动的时候有） */
.fly-cover {
  position: fixed;
  z-index: 80;
  margin: 0;
  pointer-events: none;
  will-change: transform;
}
/* 已选歌曲面板：从已选栏上沿往上展开 */
.selection-content {
  max-height: min(54vh, 470px);
  max-height: min(54svh, 470px);
  position: absolute;
  left: -1px;
  right: -1px;
  bottom: calc(100% + 8px);
  overflow-y: auto;
  border: 1px solid var(--hair);
  background: rgba(11, 16, 36, .985);
  box-shadow: 0 -24px 50px rgba(0, 0, 0, .4);
  pointer-events: auto;
}
.selection-tools {
  min-height: 48px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 12px 0 16px;
  border-bottom: 1px solid var(--line);
  color: var(--ivory-2);
  font: 700 var(--fs-note) var(--sans);
}
.selection-tools .text-button { margin: 0; }
.selected-list { display: grid; gap: 8px; padding: 10px; }
.selected-list .song-row { grid-template-columns: 30px 44px 1fr auto; padding: 9px 10px; }
.song-index { color: var(--gold); font: 700 var(--fs-note) var(--sans); }

/* 右边的按钮四周和底栏的框留出空隙，图名框不贴着底栏的上下边和顶上那道进度线（2026-10-02 用户：和上面的底线贴上了）；
   框自己就是分隔，不再画竖线 */
.selector-footer {
  z-index: 30;
  display: flex;
  align-items: center;
  padding: 8px 8px 8px 0;
}
.selector-dock .selector-footer .button {
  width: auto;
  min-width: 68px;
  min-height: 50px;
  justify-content: center;
  gap: 7px;
  padding: 0 14px;
  box-shadow: none;
}
.selector-dock .selector-footer .button i { font-size: 20px; }
/* 主按钮必须带文字标签，纯箭头无法传达下一步 */
.selector-dock [data-calculate-label] {
  max-width: min(38vw, 150px);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font: 600 var(--fs-body)/1 var(--sans);
  letter-spacing: .04em;
}
/* 选够 10 首的进度：已选栏顶边一道细金线跟着首数变长；够 10 首按钮能点了，这道线收起（2026-10-04：满格的金线压在框上太重） */
.intake-progress {
  height: 2px;
  position: absolute;
  inset: -1px 0 auto;
  overflow: hidden;
  margin: 0;
  background: rgba(216, 191, 138, .1);
  transition: opacity 240ms ease;
}
.intake-progress.is-ready { opacity: 0; }
.intake-progress i {
  display: block;
  width: 0%;
  height: 100%;
  border-radius: 2px;
  background: linear-gradient(90deg, var(--gold), var(--gold-hi));
  transition: width 240ms ease;
}
.intake-progress.is-ready i { background: var(--gold-hi); }

/* ==========================================================================
   导入弹层：歌单链接、截图识别两个页签，识别画布、确认列表和原图大图层。
   外框贴 22px，带框的卡片里面的字从 39px 起
   ========================================================================== */

.import-sheet {
  position: fixed;
  z-index: 110;
  inset: 0 max(0px, calc((100vw - 480px) / 2));
  overflow-y: auto;
  background:
    radial-gradient(110% 54% at 14% -8%, rgba(243, 226, 181, .09), transparent 58%),
    radial-gradient(86% 46% at 104% 28%, rgba(216, 191, 138, .13), transparent 64%),
    var(--night);
  color: var(--ivory);
  overscroll-behavior: contain;
}
.import-sheet__header {
  min-height: 72px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  position: sticky;
  z-index: 4;
  top: 0;
  border-bottom: 1px solid rgba(241, 233, 216, .08);
  padding: max(12px, env(safe-area-inset-top, 0px)) 22px 10px;
  /* 整个弹层一起滚，列表会从标题栏底下经过：标题栏不透明，和下面的页签一行一样（不靠模糊，iOS 18 以前的 Safari 和微信不认不带前缀的 backdrop-filter） */
  background: var(--night);
}
.import-sheet__header strong {
  color: var(--ivory);
  font: 700 var(--fs-heading)/1 var(--sans);
  letter-spacing: .06em;
}
/* 关闭键的图标对齐 22px 的右边线 */
.import-sheet__header .icon-button { margin-right: -12px; }
.import-mode-switch {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px;
  position: sticky;
  z-index: 3;
  top: 72px;
  margin: 0;
  padding: 12px 22px 8px;
  /* 吸顶时底下的网址和框线不能从页签后面透出来：这一行不透明，渐隐挪到行下面 */
  background: var(--night);
}
.import-mode-switch::after {
  content: "";
  height: 18px;
  position: absolute;
  inset: 100% 0 auto;
  background: linear-gradient(180deg, var(--night), transparent);
  pointer-events: none;
}
.import-mode-switch button {
  min-height: 46px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  border: 1px solid rgba(241, 233, 216, .1);
  border-radius: 4px;
  background: rgba(241, 233, 216, .025);
  color: var(--mist);
  font: 600 var(--fs-support) var(--sans);
  cursor: pointer;
}
.import-mode-switch button i { font-size: 18px; }
.import-mode-switch button.is-active {
  border-color: rgba(243, 226, 181, .72);
  background: rgba(243, 226, 181, .1);
  color: var(--gold-hi);
  box-shadow: inset 0 0 0 1px rgba(243, 226, 181, .08);
}
.import-sheet__body {
  min-height: calc(100vh - 138px);
  min-height: calc(100svh - 138px);
  padding: 12px 22px max(40px, env(safe-area-inset-bottom));
}

/* ---------- 歌单链接 ---------- */
.bulk-import {
  margin: 2px 0 0;
  position: relative;
  border: 1px solid var(--hair);
  border-radius: 4px;
  padding: 16px;
  background: rgba(11, 16, 36, .78);
}
/* 链接框（2026-10-04）：框里粘贴链接。「读取歌单」是框下面一整行的主按钮（2026-10-06 用户选）。
   读取中和读不了时，按钮下面用选歌页列表里的那种状态：一幅小星图加一句话，居中排（2026-10-06 用户选） */
.link-field {
  border: 1px solid var(--hair-strong);
  border-radius: 4px;
  background: var(--bg-input);
  transition: border-color 160ms ease;
}
.link-field:focus-within { border-color: var(--gold-hi); }
.link-field textarea {
  width: 100%;
  min-height: 76px;
  display: block;
  resize: none;
  border: 0;
  outline: 0;
  padding: 11px;
  background: transparent;
  color: var(--ivory);
  font: var(--fs-body)/1.5 var(--sans);
}
.link-field textarea::placeholder { color: var(--mist); }
.link-import__read { margin-top: 12px; }
.link-import__state { padding: 28px 4px 12px; }
.link-import__results { display: grid; gap: 12px; margin-top: 14px; border-top: 1px solid var(--line); padding-top: 14px; }
/* 「《歌单名》共N首，读到了M首」是一句话，按正文大小排 */
.link-import__summary { color: var(--ivory); font: 700 var(--fs-body)/1.5 var(--sans); word-break: keep-all; overflow-wrap: anywhere; text-wrap: balance; }

/* ---------- 截图识别 ---------- */
.import-empty-state {
  display: grid;
  gap: 18px;
  padding-top: 6px;
}
/* 上传区（2026-10-04）：和卡片同一副框，1px 细线、四个金角；中间一道细金圈里一个图片图标，和选歌页的加选按钮同一种圈。
   原来是虚线框加一块歪着的实心金方块，右上角还有一团光圈 */
.import-sheet .upload-zone {
  min-height: 286px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0;
  position: relative;
  border: 1px solid var(--hair);
  padding: 30px 22px;
  background: rgba(9, 12, 28, .86);
  text-align: center;
  cursor: pointer;
}
.upload-zone__icon {
  width: 64px;
  height: 64px;
  display: grid;
  place-items: center;
  position: relative;
  border: 1px solid rgba(216, 191, 138, .6);
  border-radius: 50%;
  color: var(--gold-hi);
  font: 26px/1 var(--sans);
  transition: background-color 150ms ease, border-color 150ms ease;
}
.import-sheet .upload-zone:active .upload-zone__icon {
  border-color: var(--gold-hi);
  background: radial-gradient(closest-side, rgba(243, 226, 181, .22), rgba(243, 226, 181, .06) 70%, rgba(243, 226, 181, 0));
}
.upload-zone strong {
  position: relative;
  margin: 24px 0 0;
  color: var(--ivory);
  font: 700 var(--fs-heading)/1.2 var(--sans);
  letter-spacing: .04em;
}
.upload-preview {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 10px;
  margin: 2px 0 0;
  border: 1px solid rgba(241, 233, 216, .1);
  border-radius: 4px;
  padding: 12px 16px;
  background: rgba(11, 16, 36, .84);
}
.upload-preview__list {
  grid-column: 1;
  display: flex;
  gap: 8px;
  overflow-x: auto;
  padding-bottom: 2px;
  scrollbar-width: thin;
  scrollbar-color: rgba(243, 226, 181, .32) transparent;
}
.upload-preview__image {
  width: 68px;
  height: 68px;
  flex: 0 0 68px;
  position: relative;
  overflow: hidden;
  border: 1px solid var(--hair-strong);
  border-radius: 4px;
  padding: 0;
  background: var(--bg-deep);
  cursor: zoom-in;
}
.upload-preview__image img { width: 100%; height: 100%; display: block; object-fit: cover; }
/* 「移除」比别的细框按钮再淡一档 */
.upload-preview .mini-button { border-color: rgba(241, 233, 216, .14); color: var(--ivory-2); }

.ocr-workspace { margin-top: 12px; }
.ocr-status {
  min-height: 68px;
  display: grid;
  grid-template-columns: 34px minmax(0, 1fr) auto;
  align-items: center;
  gap: 11px;
  border: 1px solid rgba(241, 233, 216, .11);
  border-radius: 4px 4px 0 0;
  padding: 11px 16px;
  background: var(--bg-card);
}
/* 状态栏左边的小星图（2026-10-06）：和全站的状态一个样子。识别中三颗星依次亮；出错是两头两颗星、中间一道断开的虚线；
   平时只亮正中一颗。原来是转圈的小圆标 */
.ocr-status__mark {
  width: 34px;
  height: 30px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  position: relative;
}
.ocr-status__mark i {
  width: 8px;
  height: 8px;
  flex: none;
  background: var(--gold-hi);
  clip-path: polygon(50% 0, 60% 40%, 100% 50%, 60% 60%, 50% 100%, 40% 60%, 0 50%, 40% 40%);
  opacity: 0;
}
.ocr-status__mark i:nth-child(2) { width: 12px; height: 12px; opacity: 1; }
.ocr-status[data-state="running"] .ocr-status__mark i { width: 8px; height: 8px; opacity: .22; animation: list-state-blink 1.2s ease-in-out infinite; }
.ocr-status[data-state="running"] .ocr-status__mark i:nth-child(2) { animation-delay: .2s; }
.ocr-status[data-state="running"] .ocr-status__mark i:nth-child(3) { animation-delay: .4s; }
.ocr-status[data-state="error"] .ocr-status__mark i { opacity: 1; }
.ocr-status[data-state="error"] .ocr-status__mark i:nth-child(2) { opacity: 0; }
.ocr-status[data-state="error"] .ocr-status__mark::before,
.ocr-status[data-state="error"] .ocr-status__mark::after {
  content: "";
  width: 5px;
  height: 1px;
  position: absolute;
  top: 50%;
  background: repeating-linear-gradient(90deg, rgba(216, 191, 138, .62) 0 2px, transparent 2px 4px);
}
.ocr-status[data-state="error"] .ocr-status__mark::before { left: 10px; }
.ocr-status[data-state="error"] .ocr-status__mark::after { right: 10px; }
.ocr-status p,
.ocr-status strong,
.ocr-status small { display: block; min-width: 0; margin: 0; }
.ocr-status strong { color: var(--ivory); font-size: var(--fs-support); }
.ocr-status small { margin-top: 4px; color: var(--mist); font: var(--fs-note)/1.45 var(--sans); }
.ocr-status__actions { display: grid; grid-template-columns: 44px; gap: 6px; }
.ocr-tool-button {
  min-width: 44px;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  border: 1px solid rgba(241, 233, 216, .14);
  border-radius: 4px;
  padding: 0 12px;
  background: rgba(241, 233, 216, .035);
  color: var(--ivory-2);
  font: 700 var(--fs-note) var(--sans);
  white-space: nowrap;
  transition: border-color 160ms ease, background-color 160ms ease, color 160ms ease, transform 160ms ease;
}
.ocr-tool-button i { color: var(--gold-hi); font-size: 16px; }
.ocr-tool-button:active { transform: scale(.97); }
.ocr-tool-button:active { border-color: rgba(243, 226, 181, .34); background: rgba(243, 226, 181, .06); color: var(--ivory); }
.ocr-tool-button:disabled { opacity: .35; pointer-events: none; }
.ocr-status__actions .ocr-tool-button { width: 44px; padding: 0; }
.ocr-status__actions .ocr-tool-button span {
  width: 1px;
  height: 1px;
  position: absolute;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}
/* 识别画布：歌单区域和逐行的框在 src/app.js 的 drawOcrCanvas 里用金色画 */
.ocr-canvas-frame {
  min-height: 220px;
  position: relative;
  overflow: hidden;
  border: 1px solid rgba(241, 233, 216, .11);
  border-top: 0;
  border-radius: 0 0 4px 4px;
  background: var(--bg-deep);
}
.ocr-canvas-frame canvas {
  width: 100%;
  max-height: 64vh;
  max-height: 64svh;
  display: block;
  object-fit: contain;
}
.ocr-workspace.is-confirming .ocr-status,
.ocr-workspace.is-confirming .ocr-canvas-frame { display: none; }

/* ---------- 确认列表：链接读出来的歌和截图识别出来的歌共用 ---------- */
.ocr-confirm {
  scroll-margin-top: 148px;
  padding-top: 4px;
}
.ocr-confirm__heading {
  display: grid;
  padding: 0 0 12px;
}
.ocr-confirm__title {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}
/* 「《歌单名》共N首，读到了M首」是一句话，按正文大小排 */
.ocr-confirm__title strong { color: var(--ivory); font: 700 var(--fs-body)/1.4 var(--sans); }
.ocr-confirm__title span { color: var(--ivory-2); font: var(--fs-note) var(--sans); }
/* 「查看原图」「重新识别」在列表上面并排，各占一半（2026-10-04） */
.ocr-confirm__actions {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
  margin-bottom: 12px;
}
.ocr-tool-button:focus-visible {
  outline: 2px solid var(--gold-hi);
  outline-offset: 2px;
}
/* 确认列表（2026-10-04 用户：按钮怎么就孤零零的放在左边）：表头右边一个「全选」，它的圈和每一行的勾排成一列；
   左边写已勾选几首（歌单链接那张没全勾上时才写） */
.checklist {
  overflow: hidden;
  border: 1px solid rgba(241, 233, 216, .1);
  border-radius: 4px;
  background: rgba(7, 10, 23, .46);
}
.checklist__head {
  min-height: 48px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  border-bottom: 1px solid rgba(241, 233, 216, .075);
  padding: 0 10px 0 12px;
}
.checklist__count { color: var(--ivory-2); font: var(--fs-note) var(--sans); }
.checklist__all {
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  margin-left: auto;
  position: relative;
  color: var(--ivory-2);
  font: 700 var(--fs-note) var(--sans);
  letter-spacing: .1em;
  cursor: pointer;
}
.checklist__all:active .ocr-result-row__check { transform: scale(.94); }
.ocr-result-row {
  min-height: 68px;
  position: relative;
  display: grid;
  grid-template-columns: 48px minmax(0, 1fr) 32px;
  align-items: center;
  gap: 11px;
  border-bottom: 1px solid rgba(241, 233, 216, .075);
  padding: 9px 10px;
  background: transparent;
  transition: background-color 150ms ease, opacity 150ms ease;
}
.ocr-result-row:last-child { border-bottom: 0; }
/* 歌单整份列出来，最多 3,000 行（2026-10-10）：屏幕外的行先不排版、不画，滑到附近再画；封面本来就是滑到才下载 */
.link-import__results .ocr-result-row { content-visibility: auto; contain-intrinsic-size: auto 68px; }
.ocr-result-row:not(.is-unmatched) { cursor: pointer; }
.ocr-result-row:not(.is-unmatched):active { background: rgba(241, 233, 216, .035); }
.ocr-result-row:has(.ocr-result-row__input:checked) { background: rgba(243, 226, 181, .045); }
.ocr-result-row__input {
  width: 1px;
  height: 1px;
  position: absolute;
  overflow: hidden;
  opacity: 0;
}
.ocr-result-row__cover { --cover-size: 48px; width: 48px; height: 48px; border-radius: 4px; }
.ocr-result-row__copy,
.ocr-result-row__copy strong,
.ocr-result-row__copy small { display: block; min-width: 0; }
.ocr-result-row__copy strong,
.ocr-result-row__copy small { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ocr-result-row__copy strong { color: var(--ivory); font: 600 var(--fs-body)/1.4 var(--sans); letter-spacing: .02em; }
.ocr-result-row__copy small { margin-top: 5px; color: var(--ivory-2); font: var(--fs-note) var(--sans); }
/* 勾选的圈（2026-10-04 用户：选歌界面都改了，这个不改吗）：和选歌页的加选按钮同一种。
   没勾是一道细金圈；勾上以后圈里点亮一颗金色星芒，底下一团很淡的暖光。原来是实心金圆底配深色的勾 */
.ocr-result-row__check {
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  justify-self: end;
  border: 1px solid rgba(216, 191, 138, .5);
  border-radius: 50%;
  transition: border-color 150ms ease, background-color 150ms ease, transform 150ms ease;
}
.ocr-result-row__check::before,
.ocr-result-row__check::after { content: ""; grid-area: 1 / 1; }
.ocr-result-row__check::after {
  width: 16px;
  height: 16px;
  background: var(--gold-hi);
  clip-path: polygon(50% 0, 60% 40%, 100% 50%, 60% 60%, 50% 100%, 40% 60%, 0 50%, 40% 40%);
  opacity: 0;
  transform: scale(.4);
  transition: opacity 150ms ease, transform 180ms ease;
}
.ocr-result-row__input:checked + .ocr-result-row__check,
.ocr-result-row__input:indeterminate + .ocr-result-row__check {
  border-color: var(--gold-hi);
  background: radial-gradient(closest-side, rgba(243, 226, 181, .22), rgba(243, 226, 181, .06) 70%, rgba(243, 226, 181, 0));
}
.ocr-result-row__input:checked + .ocr-result-row__check::after { opacity: 1; transform: none; }
/* 表头的「全选」只勾上一部分时，圈里一道短横 */
.ocr-result-row__input:indeterminate + .ocr-result-row__check::before {
  width: 12px;
  height: 2px;
  border-radius: 1px;
  background: var(--gold-hi);
}
.ocr-result-row__input:focus-visible + .ocr-result-row__check { outline: 2px solid var(--gold-hi); outline-offset: 2px; }
.ocr-result-row:active .ocr-result-row__check { transform: scale(.94); }
.ocr-result-row.is-unmatched {
  grid-template-columns: 30px minmax(0, 1fr) auto;
  background: rgba(216, 191, 138, .025);
}
.ocr-result-row__alert {
  width: 30px;
  height: 30px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: rgba(216, 191, 138, .1);
  color: var(--gold);
}
.ocr-result-row__alert i { font-size: 17px; }
.ocr-result-row__search {
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  border: 0;
  padding: 0 2px 0 10px;
  background: transparent;
  color: var(--gold-hi);
  font: 700 var(--fs-note) var(--sans);
  white-space: nowrap;
}
.ocr-result-row__search i { font-size: 14px; transition: transform 150ms ease; }
/* 确认列表下面并排两个按钮（2026-10-04 用户：一个是导入当前选择，另一个才是直接计算）：「查看结果」「加入已选」，钉在弹层底部，
   列表再长也在第一屏里（2026-09-28）。这一排垫上底色，上沿一小段渐隐，滚动时列表里的歌从按钮后面淡出去，不从按钮之间透出来；
   和屏幕底边之间那道缝用一层不带虚化的影子垫上。截图识别的列表直接放在夜色上，垫夜色；歌单链接的列表在卡片里，垫卡片的颜色
   （rgba(11, 16, 36, .78) 叠在夜色上） */
.import-actions {
  --pad: var(--night);
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
  position: sticky;
  z-index: 2;
  bottom: max(12px, env(safe-area-inset-bottom, 0px));
  padding-top: 6px;
  background: var(--pad);
  box-shadow: 0 max(12px, env(safe-area-inset-bottom, 0px)) 0 0 var(--pad);
}
.import-actions::before {
  content: "";
  height: 22px;
  position: absolute;
  inset: auto 0 100%;
  background: linear-gradient(rgba(7, 10, 23, 0), var(--pad));
  pointer-events: none;
}
.ocr-confirm > .import-actions { margin-top: 14px; }
/* 列表翻到底时按钮回到原位，下面只剩卡片 16px 内边距；带底部安全区的 iPhone 上垫的那条更高，按钮下面多留出差的那一截，垫条不伸出卡片 */
.link-import__results > .import-actions {
  --pad: #0a0f21;
  margin-bottom: max(0px, calc(env(safe-area-inset-bottom, 0px) - 16px));
}

/* ---------- 截图大图层 ---------- */
.reference-layer {
  position: fixed;
  z-index: 112;
  inset: 0 max(0px, calc((100vw - 480px) / 2));
  overflow-y: auto;
  background: var(--bg-deep);
  color: var(--ivory);
}
.reference-layer__body { padding: 22px 22px max(34px, env(safe-area-inset-bottom)); }
.reference-layer__gallery { display: grid; gap: 18px; }
.reference-layer__gallery figure { scroll-margin-top: 82px; margin: 0; }
.reference-layer__gallery img { display: block; width: 100%; border: 1px solid var(--hair-strong); border-radius: 4px; }

/* 分享层和截图大图层共用的顶栏：左边标题，右边关闭 */
.share-layer > header,
.reference-layer > header {
  min-height: 68px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  position: sticky;
  z-index: 2;
  top: 0;
  border-bottom: 1px solid var(--line);
  padding: max(14px, env(safe-area-inset-top, 0px)) 22px 12px;
  background: rgba(5, 7, 15, .94);
  color: var(--gold);
  font: 600 var(--fs-note) var(--sans);
  letter-spacing: .3em;
}
.share-layer > header .icon-button,
.reference-layer > header .icon-button { margin-right: -12px; }

/* ==========================================================================
   计算中（2026-09-24 第七轮）：一块星图铭牌，十二颗星穿在一条金线上；上面掠过所选歌的封面
   ========================================================================== */

.loading-layer {
  position: fixed;
  z-index: 80;
  inset: 0 max(0px, calc((100vw - 480px) / 2));
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background:
    radial-gradient(100% 50% at 50% 30%, rgba(58, 42, 110, .3), rgba(5, 7, 15, 0) 70%),
    rgba(5, 7, 15, .97);
  color: var(--ivory);
}
/* 翻检你的收藏：所选封面横向掠过 */
.loading-covers {
  width: min(100%, 340px);
  overflow: hidden;
  margin-bottom: 26px;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 18%, #000 82%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 18%, #000 82%, transparent);
}
.loading-covers__rail {
  display: flex;
  gap: 10px;
  width: max-content;
  animation: loading-shuffle 9s linear infinite;
}
.loading-cover {
  --cover-size: 52px;
  width: 52px;
  height: 52px;
  flex: 0 0 auto;
  border-radius: 4px;
  box-shadow: 0 6px 16px rgba(0, 0, 0, .4);
}
.loading-cover:nth-child(odd) { transform: rotate(-2deg); }
.loading-cover:nth-child(even) { transform: rotate(1.6deg); }
@keyframes loading-shuffle {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}
.exe-window {
  width: min(84%, 330px);
  position: relative;
  border: 1px solid var(--hair);
  padding: 20px 20px 16px;
  background: rgba(11, 16, 36, .82);
}
/* 四角金色折角 */
.exe-window::before,
.exe-window::after,
.exe-body::before,
.exe-body::after {
  content: "";
  width: 10px;
  height: 10px;
  position: absolute;
  border: 0 solid var(--gold);
  pointer-events: none;
}
.exe-window::before { top: -1px; left: -1px; border-width: 1px 0 0 1px; }
.exe-window::after { top: -1px; right: -1px; border-width: 1px 1px 0 0; }
.exe-body::before { bottom: -1px; left: -1px; border-width: 0 0 1px 1px; }
.exe-body::after { bottom: -1px; right: -1px; border-width: 0 1px 1px 0; }
.exe-titlebar {
  display: flex;
  align-items: center;
  color: var(--ivory);
  font: 600 var(--fs-body)/1.4 var(--sans);
  letter-spacing: .08em;
}
.exe-body { padding: 18px 0 0; }
/* 算了 10 秒还没出结果（2026-10-10 用户）：窗下面淡淡浮出一句，挂在窗外面，不把窗顶上去；结果出来就收起 */
.exe-note {
  position: absolute;
  top: calc(100% + 18px);
  right: 0;
  left: 0;
  margin: 0;
  color: var(--ivory-2);
  font: 400 var(--fs-support)/1.6 var(--sans);
  letter-spacing: .04em;
  text-align: center;
  opacity: 0;
  visibility: hidden;
  transition: opacity .8s ease, visibility 0s linear .8s;
}
.exe-note.is-shown {
  opacity: 1;
  visibility: visible;
  transition: opacity .8s ease;
}
/* 读完了几首、还剩几首（2026-10-12）：排在那一句下面一行，同一种字；数字等宽，跳数时整行不晃 */
.exe-note--progress {
  top: calc(100% + 18px + 1.6em);
  font-variant-numeric: tabular-nums;
}
/* 冷门指数下面北斗报节气（2026-10-11 用户：冷门指数像硬插进来的，换掉那道细线）：星和北斗画在画布上（src/term-dial.js），
   八个节气的名字是网页上的字，由脚本按星的位置摆在星外侧（--x、--y）：左边三个贴星的左边，右边三个贴右边，下面两个写在星下面。
   颜色从左头的暖金到右头的淡蓝（--tone-rgb），你那一档名字金色、大一号，北斗转到时慢慢变过去。计算中画面是同一副，不写名字 */
.term-dial {
  position: relative;
  /* 北斗转到斗柄朝上时，斗柄尖也要在画布里 */
  height: 236px;
  margin: 18px 0 0;
}
.term-dial__sky { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
.term-dial__names { margin: 0; padding: 0; list-style: none; }
.term-dial li {
  position: absolute;
  left: var(--x, 0);
  top: var(--y, 0);
  transform: translate(-50%, 0);
  color: rgba(var(--tone-rgb, 243, 226, 181), .8);
  font: 400 var(--fs-note)/1 var(--sans);
  letter-spacing: .06em;
  white-space: nowrap;
  transition: color .5s ease, font-size .5s ease;
}
.term-dial li[data-align="right"] { transform: translate(-100%, -50%); }
.term-dial li[data-align="left"] { transform: translate(0, -50%); }
.term-dial li.is-you { color: var(--gold-hi); font-size: var(--fs-support); font-weight: 700; letter-spacing: .08em; }
/* 计算中：北斗一直转，转过的星亮一下；算完转回斗柄朝上，八颗一起亮。不写名字、不标你那一档（2026-10-02、10-06 用户） */
.term-dial--scan { height: 150px; margin: 4px 0 0; }

/* ==========================================================================
   结果页（2026-09-25 第十六轮按章节排，2026-09-29 冷门指数和分享挪到最后）：Ⅰ 你歌单里的星座 → 档案 → Ⅱ 音乐身份证 → 合拍 → 分享与邀请。
   ========================================================================== */

.results {
  background:
    radial-gradient(120% 30% at 88% 0%, rgba(58, 42, 110, .34), rgba(7, 10, 23, 0) 64%),
    radial-gradient(110% 22% at 0% 38%, rgba(34, 48, 104, .3), rgba(7, 10, 23, 0) 66%),
    radial-gradient(110% 20% at 100% 72%, rgba(58, 42, 110, .22), rgba(7, 10, 23, 0) 66%),
    var(--night);
  color: var(--ivory);
}
.topbar--result {
  position: relative;
  z-index: 5;
  border-bottom-color: transparent;
}
/* ---------- 章节 ----------
   章与章之间一颗分隔星；分隔星到第一行字都是 --chapter-top。
   每一章的标题都用 .chapter-title（第一章、缘分卡、档案、身份证、合拍）。
   小标签 .act-kicker 只有回传链接打开的合拍页在用，2026-10-06 那一页删掉，跟着删 */
.act { padding: var(--chapter-top) 22px var(--chapter-bottom); }
.act--pair,
.act--idcard,
.insight-section {
  background-image: var(--divider-star), var(--divider-line);
  background-size: 22px 22px, 100% 1px;
  background-position: 50% 0, 0 11px;
  background-repeat: no-repeat;
}
.chapter-title {
  color: var(--ivory);
  font: 700 var(--fs-heading)/1.35 var(--sans);
  letter-spacing: .06em;
  text-wrap: balance;
}

/* ---------- Ⅰ 你歌单里的星座：星图、星图下的搜歌框 ---------- */
.act--stories { padding-top: 0; }
/* 缘分卡上方的标题和「歌「星」档案」同一种章标题（2026-09-29 用户定，原来是小标签），离卡片也是 18px */
.act--stories > .fate-heading { margin: 36px 0 18px; }
.sky { margin: 0 -22px; }
/* 标题下面整块留给星图（2026-09-29）：星图区加高，挪开标题以后球还是原来那么大 */
.sky-stage {
  height: clamp(560px, 168vw, 700px);
  position: relative;
  overflow: hidden;
  isolation: isolate;
  background: radial-gradient(140% 60% at 50% 112%, rgba(34, 48, 104, .5) 0%, rgba(7, 10, 23, 0) 62%);
}
/* 暗角；下沿淡进页面的夜色，不留一道亮边（第三十一轮） */
.sky-stage::after {
  content: "";
  position: absolute;
  z-index: 1;
  inset: 0;
  background:
    linear-gradient(180deg, rgba(7, 10, 23, 0) 80%, var(--night) 100%),
    radial-gradient(120% 90% at 50% 50%, rgba(0, 0, 0, 0) 55%, rgba(2, 3, 9, .62) 100%);
  pointer-events: none;
}
/* 左右拖动、两指缩放交给星图，上下滑照常滚动页面；页面别处照样能双指放大 */
.sky-canvas {
  position: absolute;
  z-index: 0;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  touch-action: pan-y;
  cursor: grab;
}
.sky-canvas:active { cursor: grabbing; }
.sky-head {
  position: absolute;
  z-index: 2;
  inset: 0 0 auto;
  padding: 18px 22px 28px;
  /* 顶上透明，和顶栏接得上；中间压暗一点，外圈的环从字后面穿过去也看得清。星不会转到字底下（src/star-chart.js 按字的底边放球心） */
  background: linear-gradient(180deg, rgba(7, 10, 23, 0) 0%, rgba(7, 10, 23, .6) 24%, rgba(7, 10, 23, .5) 70%, rgba(7, 10, 23, 0) 100%);
  pointer-events: none;
}
.sky-title { margin: 14px 0 0; }
/* 大数字右边一列：上面“个星官”，下面一行小字写连起了几首歌（2026-10-05 用户：说明写成三行太挤，把数据挪到别处）。
   这一列是行内块，基线跟着它最后一行走：有小字时小字和大数字落在一条基线上，没有时“个星官”落在基线上 */
.sky-count { margin: 10px 0 0; }
.sky-count b {
  color: var(--gold-hi);
  font: 600 var(--fs-number)/.84 var(--latin);
  font-variant-numeric: lining-nums tabular-nums;
  letter-spacing: .01em;
}
.sky-count b.is-word { font: 700 var(--fs-display)/1.15 var(--sans); letter-spacing: .14em; }
.sky-count__side { display: inline-block; margin-left: 10px; }
.sky-count [data-sky-unit] { display: block; color: var(--ivory); font: 500 var(--fs-body)/1 var(--sans); letter-spacing: .12em; }
.sky-count__linked { display: block; margin-top: 8px; color: var(--ivory-2); font: var(--fs-support)/1.2 var(--sans); letter-spacing: .04em; white-space: nowrap; }
.sky-line { max-width: 24em; margin: 10px 0 0; color: var(--ivory-2); font: var(--fs-support)/1.7 var(--sans); letter-spacing: .02em; }
/* 手势提示（2026-10-04 用户：那你要给提示啊）：浮在星图下沿、搜歌框上面，一行注脚字，垫一圈夜色，压在星上也读得清。
   手机上写两根手指怎么用，电脑上写鼠标怎么用；看到星图时淡入，第一次拖动或缩放以后淡出（src/star-chart.js） */
.sky-hint {
  position: absolute;
  z-index: 2;
  left: 22px;
  right: 22px;
  bottom: 50px;
  margin: 0;
  color: var(--ivory-2);
  font: var(--fs-note)/1.5 var(--sans);
  letter-spacing: .08em;
  text-align: center;
  text-wrap: balance;
  text-shadow: 0 0 6px var(--night), 0 0 2px var(--night);
  pointer-events: none;
  opacity: 0;
  transition: opacity .6s ease;
}
.sky-hint.is-shown { opacity: 1; }
.sky-hint__mouse { display: none; }
@media (hover: hover) and (pointer: fine) {
  .sky-hint__touch { display: none; }
  .sky-hint__mouse { display: inline; }
}
@media (prefers-reduced-motion: reduce) {
  .sky-hint { transition: none; }
}

/* 星图下的卡片：星表的样子，四角金色折角，每行至少 44 像素。
   平时只有搜歌框，一道金色细线托着字（第二十轮）；搜到的歌、单看一首歌的连接情况排在框下面 */
.sky-card {
  position: relative;
  z-index: 3;
  margin: -34px 22px 0;
  border: 1px solid var(--hair);
  padding: 14px 16px 12px;
  background: rgba(9, 12, 28, .86);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  touch-action: pan-y pinch-zoom;
}
/* 带框卡片的四个金角，全页同一副（2026-09-29）：星图下的搜歌框、缘分卡、画像卡、年份时间轴、歌不够时的提示。
   卡片自己要 position: relative，框线是 1px 的 --hair，底色 rgba(9, 12, 28, .86) */
.corner { position: absolute; width: 10px; height: 10px; border: 0 solid var(--gold); pointer-events: none; }
.corner--tl { top: -1px; left: -1px; border-width: 1px 0 0 1px; }
.corner--tr { top: -1px; right: -1px; border-width: 1px 1px 0 0; }
.corner--bl { bottom: -1px; left: -1px; border-width: 0 0 1px 1px; }
.corner--br { bottom: -1px; right: -1px; border-width: 0 1px 1px 0; }
/* 工具键浮在右上角，标题绕着它排：只有第一行让出工具键的宽，长标题第二行起用整张卡的宽（2026-10-06：360 宽上标题挤在一窄列里，
   最后一个字单独掉一行）。工具键往下少占 12 px，第二行就不再让 */
.sky-card__head { display: flow-root; margin-bottom: 4px; }
.sky-card__title { padding-top: 6px; text-wrap: pretty; }
.sky-card__name { min-width: 0; color: var(--gold-hi); font: 700 var(--fs-heading)/1.3 var(--sans); letter-spacing: .06em; overflow-wrap: anywhere; }
.sky-card__meta { display: inline-block; margin-left: 10px; color: var(--mist); font-size: var(--fs-note); letter-spacing: .04em; }
/* 点开星座时标题是一整句故事（第十二轮），字小一号；只在标点处折行，「两次，一首也没唱」不拆成「两 / 次」 */
.sky-card__name--story { font-size: var(--fs-body); line-height: 1.45; letter-spacing: .02em; word-break: keep-all; text-wrap: balance; }
.sky-card__tools { float: right; display: flex; gap: 2px; margin: 0 -10px -12px 8px; }
/* 宽 34、高 40：右上角三个键少占一点第一行，360 宽上十个字的标题能排在一行里（2026-10-06） */
.sky-tool {
  width: 34px;
  height: 40px;
  display: grid;
  place-items: center;
  border: 0;
  padding: 0;
  background: none;
  color: var(--gold);
  font: 500 26px/1 var(--latin);
  cursor: pointer;
}
.sky-tool[data-sky-close] { color: var(--faint); }
.sky-rows { display: grid; margin: 0; padding: 0; list-style: none; }
.sky-rows button {
  width: 100%;
  min-height: 44px;
  display: flex;
  align-items: center;
  border: 0;
  padding: 8px 0;
  background: none;
  color: var(--ivory);
  text-align: left;
  cursor: pointer;
}
.sky-row__num { flex: 0 0 auto; width: calc(var(--sky-num, 1) * 1em + 8px); color: var(--gold); font: 600 var(--fs-support)/1 var(--sans); }
.sky-row__name { max-width: 62%; overflow: hidden; font: 500 var(--fs-support)/1.3 var(--sans); letter-spacing: .04em; text-overflow: ellipsis; white-space: nowrap; }
.sky-row__lead { flex: 1; min-width: 16px; margin: 0 10px; border-bottom: 1px dotted rgba(216, 191, 138, .32); transform: translateY(-4px); }
.sky-row__who { max-width: 34%; overflow: hidden; color: var(--mist); font-size: var(--fs-note); text-overflow: ellipsis; white-space: nowrap; }
.sky-rows button:active .sky-row__name,
.sky-rows button:focus-visible .sky-row__name { color: var(--gold-hi); }
.sky-card__note { margin: 0 0 4px; color: var(--mist); font: var(--fs-note)/1.7 var(--sans); }
/* 星图搜歌没搜到（2026-10-06）：列表状态的小一号 */
.sky-card__state { gap: 10px; padding: 14px 8px 6px; }
.sky-card__state p { font-size: var(--fs-support); }
/* 「还有N首」「收起」（2026-10-04 用户：点开应该可以看到完整名单，需要可以展开、收起）：金色虚下划线表示能点，
   和缘分卡的「全部歌曲」一样；上下留够手指点的高度 */
/* 能展开的「还有N首」全站一个样子（2026-10-04）：星图卡片和画像卡、你与好友的歌单里的故事都是金色虚下划线的字 */
.sky-card__more,
.story-more {
  display: block;
  margin: 2px 0 0;
  border: 0;
  padding: 12px 0;
  background: none;
  color: var(--gold);
  font: 500 var(--fs-support)/1.4 var(--sans);
  letter-spacing: .06em;
  text-decoration: underline dotted rgba(216, 191, 138, .55);
  text-decoration-thickness: 1px;
  text-underline-offset: 4px;
  cursor: pointer;
}
.sky-card__more:active,
.story-more:active { color: var(--gold-hi); }
.sky-card__more:focus-visible,
.story-more:focus-visible { outline: 1px solid var(--gold-hi); outline-offset: 2px; }
.sky-card button:focus-visible { outline: 1px solid var(--gold); outline-offset: 3px; }
.sky-search {
  min-height: 44px;
  display: flex;
  align-items: center;
  gap: 10px;
  border-bottom: 1px solid var(--hair);
  color: var(--gold);
}
.sky-search i { flex: none; font-size: 18px; }
.sky-search input {
  width: 100%;
  min-width: 0;
  border: 0;
  padding: 10px 0;
  background: none;
  color: var(--ivory);
  font: 500 var(--fs-body)/1.4 var(--sans);
  letter-spacing: .04em;
  -webkit-appearance: none;
  appearance: none;
}
.sky-search input::placeholder { color: var(--mist); }
.sky-search input:focus { outline: none; }
.sky-search:focus-within { border-bottom-color: var(--gold); }
.sky-search input::-webkit-search-cancel-button { filter: sepia(1) saturate(2) hue-rotate(5deg); }
.sky-card__body { margin-top: 10px; }
.sky-card__body:empty { display: none; }
.sky-links { display: grid; margin: 4px 0 0; padding: 0; list-style: none; }
.sky-links button {
  width: 100%;
  min-height: 44px;
  display: grid;
  gap: 3px;
  border: 0;
  border-bottom: 1px dotted rgba(216, 191, 138, .32);
  padding: 10px 24px 10px 0;
  background: none;
  color: var(--ivory);
  text-align: left;
  cursor: pointer;
  position: relative;
}
.sky-links button::after {
  content: "\203A";
  position: absolute;
  top: 50%;
  right: 2px;
  color: var(--gold);
  font: 500 22px/1 var(--latin);
  transform: translateY(-55%);
}
.sky-links strong { font: 500 var(--fs-support)/1.55 var(--sans); letter-spacing: .03em; word-break: keep-all; overflow-wrap: anywhere; }
.sky-links small { color: var(--mist); font: var(--fs-note)/1.5 var(--sans); }
.sky-links button:active strong,
.sky-links button:focus-visible strong { color: var(--gold-hi); }

/* ---------- Ⅰ 缘分卡（第三十四轮）：星座之外的联系横着排一行，一次看一张，下一张露出一截。
   卡片外沿贴 22px、里面的字从 39px 起；四角金色折角，和星图下的搜歌框同一副框 ---------- */
.fate-rail {
  display: flex;
  gap: 12px;
  margin: 0 -22px;
  padding: 2px 22px 6px;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scroll-padding: 0 22px;
  scrollbar-width: none;
}
.fate-rail::-webkit-scrollbar { display: none; }
/* 电脑上用鼠标按住拖着滑：拖的时候关掉吸附、不选字，放手后吸附到最近的一张 */
@media (hover: hover) and (pointer: fine) {
  .fate-rail { cursor: grab; scroll-behavior: smooth; }
  .fate-rail.is-dragging { cursor: grabbing; scroll-snap-type: none; scroll-behavior: auto; user-select: none; }
}
.fate-rail:focus-visible { outline: 1px solid var(--gold); outline-offset: -1px; }
.fate-card {
  position: relative;
  flex: 0 0 min(300px, calc(100vw - 88px));
  min-height: 292px;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--hair);
  padding: 16px 16px 14px;
  background: rgba(9, 12, 28, .86);
  scroll-snap-align: start;
}
/* 没有正文的卡（同一天生日、两首歌的同一天发行这类）：画面和标题一起排在卡片正中，不在底下留一截空白（2026-09-29） */
.fate-card--no-body { justify-content: center; }
/* 标题只在标点处折行（和点开星座的那句一样），书名号里的歌名不拆开 */
.fate-card h3 { margin: 0; color: var(--ivory); font: 600 var(--fs-body)/1.6 var(--sans); letter-spacing: .02em; word-break: keep-all; overflow-wrap: anywhere; text-wrap: balance; }
.fate-card > h3 { margin-top: 16px; }
/* 正文直接跟在标题下面，每张都不折叠（2026-09-27 用户：不要有的弄加号展开有的又不弄） */
.fate-body { margin: 6px 0 0; color: var(--ivory-2); font: var(--fs-support)/1.8 var(--sans); text-wrap: pretty; }
/* 「全部歌曲」（2026-10-03 用户）：两首以上的歌的卡最下面都有一个，金色虚下划线和幕后群星的名字一样，表示能点；
   点了在卡片下面列出这张卡的歌。没有正文的卡跟在标题下面，不推到卡底 */
.fate-songs-toggle {
  align-self: flex-start;
  margin: auto 0 0;
  border: 0;
  padding: 14px 0 2px;
  background: none;
  color: var(--gold);
  font: 500 var(--fs-support)/1.5 var(--sans);
  letter-spacing: .06em;
  text-decoration: underline dotted rgba(216, 191, 138, .55);
  text-decoration-thickness: 1px;
  text-underline-offset: 4px;
  cursor: pointer;
}
.fate-card--no-body .fate-songs-toggle { margin-top: 0; padding-top: 10px; }
.fate-songs-toggle[aria-pressed="true"] { color: var(--gold-hi); text-decoration: underline solid var(--gold-hi); text-decoration-thickness: 1px; }
.fate-songs-toggle:focus-visible { outline: 1px solid var(--gold-hi); outline-offset: 2px; }
.fate-songs { margin: 14px 0 0; border-top: 1px solid var(--line); padding: 14px 0 0; }

/* 画面：高 150px。封面、星、细金线全卡统一 */
.fate-pic { position: relative; height: 150px; display: flex; align-items: center; justify-content: space-between; }
.fate-side { position: relative; z-index: 2; display: grid; justify-items: center; gap: 6px; }
.fate-cover { --cover-size: 64px; width: 64px; height: 64px; border-radius: 3px; box-shadow: 0 0 0 1px var(--hair); }
/* 采样、改编的原曲不在歌单里：暗一档的色块 */
.fate-cover.is-dim { background: linear-gradient(145deg, #232838, #10131f); }
.fate-star { display: inline-block; flex: none; width: 20px; height: 20px; background: var(--glint); }
/* 两张封面中间一颗星：封面和星都在画面正中，星和细金线对准封面中线 */
.fate-link { position: relative; flex: 1; display: flex; align-items: center; margin: 0 6px; }
.fate-link__line { flex: 1; height: 1px; background: linear-gradient(90deg, rgba(216, 191, 138, .15), rgba(216, 191, 138, .75)); transform-origin: left center; }
.fate-link__line + .fate-star + .fate-link__line { background: linear-gradient(90deg, rgba(216, 191, 138, .75), rgba(216, 191, 138, .15)); transform-origin: right center; }

/* 日历和门票（2026-09-29 第二次重画，日历用方向 A「夜」、门票用方向 B）：和卡片同一种夜蓝，金色细边，不用象牙色的纸。
   用户：纸放在专辑封面旁边突兀，门票太简陋。日历两张封面在两边，一根细线从纸后面穿过去，线从封面中心起 */
.fate-pic--calendar { padding: 0 2px; }
.fate-pic--calendar .fate-cover { --cover-size: 56px; width: 56px; height: 56px; }
.fate-thread { position: absolute; z-index: 1; left: 30px; right: 30px; top: 74px; height: 1px; background: rgba(216, 191, 138, .6); transform-origin: left center; }

/* 日历：手撕日历的一页。上面一道装订条，别着那颗星；撕开的地方是一道虚线；底下露出两层纸边，日子自己发一点暖光 */
.fate-calendar {
  position: relative;
  z-index: 3;
  flex: none;
  width: 88px;
  display: flex;
  flex-direction: column;
  filter: drop-shadow(0 10px 14px rgba(3, 5, 12, .6));
  transform-origin: 50% 0;
}
.fate-calendar__bind { position: relative; height: 14px; border: 1px solid rgba(216, 191, 138, .5); border-bottom: 0; border-radius: 3px 3px 0 0; background: linear-gradient(180deg, #2b3254, #171c35); }
.fate-calendar__bind .fate-star { position: absolute; top: 50%; left: 50%; width: 18px; height: 18px; margin: -9px 0 0 -9px; }
.fate-calendar__sheet {
  display: grid;
  justify-items: center;
  gap: 4px;
  padding: 12px 4px 10px;
  border: 1px solid rgba(216, 191, 138, .5);
  border-top: 1px dashed rgba(216, 191, 138, .32);
  border-radius: 0 0 3px 3px;
  background:
    radial-gradient(80% 60% at 50% 64%, rgba(243, 226, 181, .16), transparent 72%),
    linear-gradient(180deg, #1b2244, #0d1230);
  color: var(--ivory);
  box-shadow: 0 3px 0 -1px rgba(216, 191, 138, .28), 0 6px 0 -2px rgba(216, 191, 138, .14);
}
.fate-calendar__sheet small { color: var(--gold); font: 600 var(--fs-note)/1 var(--sans); letter-spacing: .03em; white-space: nowrap; }
.fate-calendar__sheet strong { font: 600 var(--fs-display)/1 var(--latin); font-variant-numeric: lining-nums; text-shadow: 0 0 16px rgba(243, 226, 181, .45); }

/* 同一天生日：两页叠着放。早的那页暗一些，压在左后方；晚的那页在右前方。
   前面那页往右下错开，后面那页的年份整行露在外面，日子也露出大半 */
.fate-days { position: relative; z-index: 3; flex: none; width: 132px; height: 140px; }
.fate-days .fate-calendar { position: absolute; width: 80px; }
.fate-days .fate-calendar__sheet strong { font-size: var(--fs-title); }
.fate-days .fate-calendar--old { top: 0; left: 0; rotate: -6deg; }
.fate-days .fate-calendar:not(.fate-calendar--old) { right: 0; bottom: 0; rotate: 3deg; }
.fate-calendar--old .fate-calendar__bind { border-color: rgba(216, 191, 138, .3); background: linear-gradient(180deg, #21274a, #12162d); }
.fate-calendar--old .fate-calendar__sheet {
  border-color: rgba(216, 191, 138, .3);
  background: linear-gradient(180deg, #161c3a, #0a0e26);
  box-shadow: 0 3px 0 -1px rgba(216, 191, 138, .18), 0 6px 0 -2px rgba(216, 191, 138, .09);
}
.fate-calendar--old .fate-calendar__sheet small { color: rgba(216, 191, 138, .7); }
.fate-calendar--old .fate-calendar__sheet strong { color: rgba(241, 233, 216, .72); text-shadow: none; }

/* 门票：一张横着的宽票，稍微歪一点放。票面正着印一排封面（最多 3 张）；最右一截副券沿齿孔撕了下来，
   往右下错开一点、歪一点，看得出检过票、入了场。票上一个字都不写（2026-10-06 用户：不要写字，重新设计一下），也不放星。
   正票和副券各是一截：撕开的那边是一排半个的齿孔（遮罩咬出来的小半圆），两头的半圆缺口各剩一半。
   遮罩分两层叠：外层管两头的缺口，里层的纸管齿孔，两层都透的地方才看得见 */
.fate-pic--ticket { display: block; }
.fate-ticket { --stub: 34px; --tear: 6px; position: absolute; z-index: 3; left: 4px; right: 4px; top: 29px; height: 92px; rotate: -2deg; filter: drop-shadow(0 10px 14px rgba(3, 5, 12, .6)); }
.fate-ticket__main,
.fate-ticket__stub { position: absolute; top: 0; bottom: 0; }
.fate-ticket__main {
  left: 0;
  right: calc(var(--stub) + var(--tear));
  -webkit-mask:
    radial-gradient(circle 7px at 100% 0, transparent 97%, #000) top / 100% 51% no-repeat,
    radial-gradient(circle 7px at 100% 100%, transparent 97%, #000) bottom / 100% 51% no-repeat;
  mask:
    radial-gradient(circle 7px at 100% 0, transparent 97%, #000) top / 100% 51% no-repeat,
    radial-gradient(circle 7px at 100% 100%, transparent 97%, #000) bottom / 100% 51% no-repeat;
}
.fate-ticket__stub {
  right: 0;
  width: var(--stub);
  transform: translate(3px, 2px) rotate(5deg);
  transform-origin: 0 50%;
  -webkit-mask:
    radial-gradient(circle 7px at 0 0, transparent 97%, #000) top / 100% 51% no-repeat,
    radial-gradient(circle 7px at 0 100%, transparent 97%, #000) bottom / 100% 51% no-repeat;
  mask:
    radial-gradient(circle 7px at 0 0, transparent 97%, #000) top / 100% 51% no-repeat,
    radial-gradient(circle 7px at 0 100%, transparent 97%, #000) bottom / 100% 51% no-repeat;
}
.fate-ticket__paper {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(70% 90% at 30% 50%, rgba(243, 226, 181, .1), transparent 72%),
    linear-gradient(170deg, #1d2447, #0e1331);
  box-shadow: inset 0 0 0 1px rgba(216, 191, 138, .55);
}
.fate-ticket__main .fate-ticket__paper {
  border-radius: 4px 0 0 4px;
  -webkit-mask:
    linear-gradient(#000 0 0) left / calc(100% - 2px) 100% no-repeat,
    radial-gradient(circle at 100% 50%, transparent 1.5px, #000 1.9px) right / 2px 5px repeat-y;
  mask:
    linear-gradient(#000 0 0) left / calc(100% - 2px) 100% no-repeat,
    radial-gradient(circle at 100% 50%, transparent 1.5px, #000 1.9px) right / 2px 5px repeat-y;
}
/* 副券上一片细斜线，是票根上防伪的底纹，不写座位号 */
.fate-ticket__stub .fate-ticket__paper {
  border-radius: 0 4px 4px 0;
  background:
    repeating-linear-gradient(-58deg, rgba(216, 191, 138, .2) 0 1px, transparent 1px 4px) center / calc(100% - 14px) calc(100% - 22px) no-repeat,
    linear-gradient(170deg, #1d2447, #0e1331);
  -webkit-mask:
    linear-gradient(#000 0 0) right / calc(100% - 2px) 100% no-repeat,
    radial-gradient(circle at 0 50%, transparent 1.5px, #000 1.9px) left / 2px 5px repeat-y;
  mask:
    linear-gradient(#000 0 0) right / calc(100% - 2px) 100% no-repeat,
    radial-gradient(circle at 0 50%, transparent 1.5px, #000 1.9px) left / 2px 5px repeat-y;
}
/* 印在票面上的封面：正着排成一排，在正票上居中 */
.fate-ticket__art { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; gap: 8px; }
.fate-ticket__art .fate-cover { --cover-size: 60px; width: 60px; height: 60px; border-radius: 2px; box-shadow: 0 0 0 1px rgba(216, 191, 138, .35); }

/* 三首以上的日历：三张封面排在上面，中间那张高一点；三根细线收到下面那页日历后面。门票的封面都印在票上，不用这一排 */
.fate-pic--fan { display: block; }
.fate-rays { position: absolute; z-index: 1; inset: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none; }
.fate-rays path { fill: none; stroke: rgba(216, 191, 138, .55); stroke-width: 1; vector-effect: non-scaling-stroke; }
.fate-fan__cover { position: absolute; z-index: 2; translate: -50% -50%; }
.fate-fan__cover--1 { top: 34px; left: 16%; }
.fate-fan__cover--2 { top: 24px; left: 50%; }
.fate-fan__cover--3 { top: 34px; left: 84%; }
.fate-fan__cover .fate-cover { --cover-size: 44px; width: 44px; height: 44px; }
.fate-pic--fan .fate-calendar { position: absolute; top: 58px; left: 50%; width: 80px; margin-left: -40px; }
.fate-pic--fan .fate-calendar__sheet { padding-top: 10px; }
.fate-pic--fan .fate-calendar__sheet strong { font-size: var(--fs-title); }

/* 加歌卡：三个虚线空位，每个空位一颗星；整张居中（2026-09-27 用户：按钮的排版应该居中） */
.fate-pic--slots { justify-content: center; gap: 14px; }
.fate-slot { width: 64px; height: 64px; display: grid; place-items: center; border: 1px dashed rgba(216, 191, 138, .35); border-radius: 3px; }
.fate-slot .fate-star { width: 16px; height: 16px; }
.fate-card--add { align-items: center; text-align: center; }
.fate-add-line { margin: 16px 0 14px; color: var(--ivory-2); font: 500 var(--fs-body)/1.7 var(--sans); letter-spacing: .02em; word-break: keep-all; overflow-wrap: anywhere; text-wrap: balance; }
.fate-card--add .mini-button { align-self: center; }

/* ---------- 歌不够时（有效歌曲不到 10 首）：身份证上方一句提示加一个按钮，和上下两章各隔一段 ---------- */
.result-informal {
  position: relative;
  margin: 0 22px var(--chapter-bottom);
  border: 1px solid var(--hair);
  padding: 14px 16px;
  background: rgba(9, 12, 28, .86);
}
.result-informal p { margin: 0 0 12px; color: var(--ivory-2); font: var(--fs-support)/1.6 var(--sans); }

/* ---------- Ⅱ 音乐身份证（第三十一轮「星盘」）：右上角是首页那副刻度环出血，只是装饰，不标度数；卡片不带底色，和上下同一片夜色。
   「你的歌单冷门指数」下面是大数字和「/100」，再下面一句相当于哪颗行星到太阳的距离（2026-10-01）；
   冷门指数的颜色随指数由暖变冷（--tone 由 src/app.js 按冷门指数填），和那句话的间距固定。卡上的字都用黑体，只有冷门指数用衬线 ---------- */
.act--idcard {
  position: relative;
  padding: 0 0 var(--chapter-bottom);
}
.id-card {
  --tone: var(--gold-hi);
  position: relative;
  z-index: 1;
  isolation: isolate;
  padding: var(--chapter-top) 22px 0;
}
/* 刻度环画在画布上（src/star-chart.js 的 mountIdCardSky），在顶边和左边自己淡出。
   画布离分隔星 34px，环先隔一段空再淡入；底边最多到本章底边，没有星座的短卡片上，星不落到下面的按钮上 */
.id-card__sky { position: absolute; z-index: -1; top: 34px; right: 0; width: 100%; height: min(346px, calc(100% + var(--chapter-bottom) - 34px)); pointer-events: none; }
.id-card__label { margin: 0; color: var(--mist); font: 400 var(--fs-note)/1.5 var(--sans); letter-spacing: .3em; }
/* 章标题（2026-09-29）：和「歌「星」档案」一样，离下面「你的歌单冷门指数」18px */
.id-card__title { margin: 0 0 18px; }

/* 冷门指数 */
.id-card__reckon { position: relative; padding-bottom: 8px; }
.id-card__lede { margin: 0; color: var(--ivory-2); font: 400 var(--fs-body)/1.5 var(--sans); letter-spacing: .12em; }
.id-card__lede copy-gold { color: var(--hot); }
.id-card .score-lockup {
  display: flex;
  align-items: baseline;
  gap: 6px;
  margin: 14px 0 0;
  color: var(--tone);
  transition: transform 1.1s cubic-bezier(.2, .8, .2, 1) .1s, color 1.2s ease .1s;
}
/* 揭晓时数字从下面浮上来，颜色由暖变到自己的冷暖 */
.id-card__reckon:not(.is-revealed) .score-lockup { transform: translateY(14px); color: var(--gold-hi); }
.id-card .score-lockup strong { font: 400 var(--fs-number)/.8 var(--latin); font-variant-numeric: lining-nums; letter-spacing: -.03em; }
.id-card .score-unit { font: 400 var(--fs-heading)/1 var(--latin); letter-spacing: .02em; opacity: .7; }
/* 相当于哪颗行星到太阳的距离（2026-10-01）：和上面那句同一种字，行星名是金色字 */
.id-card__term { margin: 14px 0 0; color: var(--ivory-2); font: 400 var(--fs-body)/1.5 var(--sans); letter-spacing: .12em; }
.id-card .term-dial { margin-top: 16px; }
/* 算不出冷门指数（热度一首都没取到）：数字那几行换成一句话加「再量一次」（2026-09-29） */
/* 算不出冷门指数（2026-10-06）：和全站的状态一个样子，断开虚线的小星图、一句话、一个按钮；靠左排，和身份证上别的字对齐 */
.id-card .score-missing { justify-items: start; margin: 18px 0 0; padding: 4px 0 0; text-align: left; }
.id-card .score-missing .mini-button { min-width: 128px; margin-top: 0; }

/* 串起你歌单的（2026-09-28 取代音乐人格）：和上面隔一段留白，下面几行就是页尾的星表那一套 */
.id-card__links { margin-top: 56px; }
.id-card__links .star-table { margin-top: 12px; }
/* 名字很长时自己折行，右边「电影 2首」保持一行 */
.id-card__links .star-table__value { flex: none; white-space: nowrap; }
/* 最后一句在页面上默认删掉（审阅稿「删掉的板块」），海报上照旧 */
.id-card__hook { margin: 34px 0 0; color: var(--ivory-2); font: 400 var(--fs-support)/1.6 var(--sans); letter-spacing: .04em; }

/* 星章墙（2026-09-29 第二版）：14 个成就，每行 4 个。每个只画这颗星或这个星座的样子，不画外框的圆圈（用户：三个圈不好看），星按星等定大小。
   这份歌单点亮的，星和线亮金色，后面一片暖光；以前在这台手机上测的歌单点亮过的半亮；没点亮的淡淡的，名字调暗。
   每一枚是一个按钮，点了名字变金色，下面带框的地方写名字和来历 */
.id-card__badges { margin-top: 56px; }
/* 星章墙（2026-09-30 第三版）：按稀有程度分三组，每组一行小标题，每行 4 个；每个是一枚印章，画在小画布上（src/badge-art.js） */
.badge-wall { display: grid; gap: 18px; margin: 20px 0 0; }
.badge-tier__name { margin: 0 0 10px; color: var(--gold); font: 600 var(--fs-note)/1.4 var(--sans); letter-spacing: .3em; }
.badge-tier__list { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 14px 6px; margin: 0; padding: 0; list-style: none; }
.badge { position: relative; display: grid; justify-items: center; gap: 6px; width: 100%; min-width: 0; padding: 4px 0; border: 0; background: none; color: inherit; font: inherit; cursor: pointer; -webkit-tap-highlight-color: transparent; }
.badge:focus-visible { outline: 1px solid var(--gold); outline-offset: 2px; }
.badge__seal { display: block; width: 54px; height: 54px; }
.badge__name { color: var(--mist); font: 500 var(--fs-note)/1.4 var(--sans); letter-spacing: .04em; text-align: center; white-space: nowrap; opacity: .62; }
.badge.is-lit .badge__name { color: var(--ivory); opacity: 1; }
.badge[aria-pressed="true"] .badge__name { color: var(--gold-hi); opacity: 1; }

.badge-detail { position: relative; margin-top: 20px; padding: 14px 16px 16px; border: 1px solid var(--hair); background: rgba(9, 12, 28, .86); }
.badge-detail:empty { display: none; }
.badge-detail b { display: block; color: var(--gold); font: 600 var(--fs-note)/1.4 var(--sans); letter-spacing: .3em; }
/* 以前测的歌单点亮过的写一句；这份歌单是谁、哪几首歌点亮的不写（2026-10-04 用户：干脆直接删掉吧，不告诉他们） */
.badge-detail__lore { margin: 6px 0 0; color: var(--ivory-2); font: var(--fs-support)/1.8 var(--sans); text-wrap: pretty; }
/* 点亮条件：写成「点亮条件：……」，前面几个字淡一点，后面的条件是金色字，一眼看得出歌单里要有什么（2026-10-02 用户定）。
   排在最下面，上面一道细线和来历隔开（2026-10-04 用户：把点亮条件放下面） */
.badge-detail__rule { margin: 12px 0 0; padding-top: 10px; border-top: 1px solid var(--line); color: var(--gold-hi); font: 500 var(--fs-support)/1.6 var(--sans); text-wrap: pretty; }
.badge-detail__rule-label { color: var(--ivory-2); font-weight: 400; }
/* 「这台手机上已经点亮 9/14」：用户在工作台删了，删空时不占地方 */
/* 清空这台手机上以前的记录：只在有以前的记录时出现，靠左的一行小字按钮 */

/* 分享层上换海报样式（2026-09-30）：整片天、冷门指数、最亮的星座；不能选的不出现 */
.share-styles { display: flex; justify-content: center; gap: 8px; margin: 14px 0 0; }
.share-styles[hidden],
.share-style[hidden] { display: none; }
.share-style { min-height: 44px; padding: 0 14px; border: 1px solid var(--hair); border-radius: 3px; background: transparent; color: var(--mist); font: 500 var(--fs-support)/1 var(--sans); letter-spacing: .1em; cursor: pointer; }
.share-style[aria-checked="true"] { border-color: var(--gold-hi); background: rgba(243, 226, 181, .08); color: var(--gold-hi); }

/* ---------- 故事卡：结尾档案的画像卡和合拍一节共用 ---------- */
.playlist-stories { display: grid; gap: 24px; }
.playlist-story { min-width: 0; padding-top: 20px; border-top: 1px solid var(--line); }
.playlist-story h3 { margin: 0; color: var(--ivory); font: 600 var(--fs-body)/1.6 var(--sans); letter-spacing: .03em; overflow-wrap: anywhere; text-wrap: balance; }
.playlist-story > p { margin: 12px 0 20px; color: var(--ivory-2); font: var(--fs-body)/1.85 var(--sans); }
.playlist-story ul { display: grid; gap: 12px; margin: 0; padding: 0; list-style: none; }
.playlist-story li { display: flex; align-items: center; gap: 12px; min-width: 0; }
.story-cover { --cover-size: 44px; flex: 0 0 44px; width: 44px; height: 44px; border-radius: 3px; box-shadow: 0 0 0 1px var(--hair); }
.playlist-story li > span:last-child { min-width: 0; }
.playlist-story li strong,
.playlist-story li small { display: block; overflow-wrap: anywhere; }
.playlist-story li strong { color: var(--ivory); font: 500 var(--fs-support)/1.5 var(--sans); letter-spacing: .02em; }
.playlist-story li small { margin-top: 3px; color: var(--mist); font: var(--fs-note)/1.5 var(--sans); }
/* 展开的其余几首接着上面那几首排 */
.playlist-story ul + ul { margin-top: 12px; }
.playlist-story .story-more { margin-top: 4px; }
/* 合拍里标出这首歌是谁的：「你」「TA」「邀请人」「受邀人」 */
.story-owner {
  display: inline-block;
  margin-right: 6px;
  border: 1px solid var(--hair);
  border-radius: 2px;
  padding: 0 6px;
  color: var(--gold-hi);
  font: 500 var(--fs-note)/1.7 var(--sans);
  font-style: normal;
}

/* ---------- 合拍一节（朋友测完看到的）和合拍页共用 ---------- */
.act--pair > .button { width: 100%; margin-top: 22px; }
/* 「你与好友的歌单」是章标题，和「你的歌单联结成了星座」「歌「星」档案」一样（2026-09-29，原来是小标签） */
.act--pair > .chapter-title { margin: 0 0 22px; }
.pair-lines { display: grid; gap: 10px; margin: 0; padding: 0; list-style: none; }
/* 合拍先讲故事（2026-09-28）：第一条故事紧跟小标签，小标签自带一道线，故事上沿不再画第二道 */
.pair-body > .playlist-stories > .playlist-story:first-child { border-top: 0; padding-top: 0; }
.playlist-stories + .pair-lines { margin-top: 28px; }
/* 中文字之间不断行，只在逗号这类标点处折行，数字和“/100”不拆开；一段太长时 overflow-wrap 兜底，不撑出屏幕。
   balance 让两行差不多长（不认 text-wrap 的浏览器照常折行） */
.pair-lines li { position: relative; padding-left: 20px; color: var(--ivory); font: 500 var(--fs-body)/1.6 var(--sans); letter-spacing: .02em; word-break: keep-all; overflow-wrap: anywhere; text-wrap: balance; }
.pair-lines li::before {
  content: "";
  position: absolute;
  top: .4em;
  left: -2px;
  width: 14px;
  height: 14px;
  background: var(--glint);
}
/* 比对中、比对不了、链接过期（2026-10-06）：和全站的状态一个样子，排在这一节正中 */
.pair-state { padding: 20px 12px 28px; }
/* 邀请链接和回传链接：复制不成功时才摊开，让人长按手动复制 */
/* 复制没成功时，摊开的链接和配文上面一行说怎么办（2026-10-04）：原来用轻提示，浮在屏幕下面正好盖住要长按的链接 */
/* 2026-10-06 起和「长按图片保存」一个样子：居中，两头各一颗小星（原来是单独一行字压在框上面）。
   分享层里的段落另有一条字色字号，这里写成两个类名压过它 */
.copy-note,
.share-layer__body > .copy-note {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  margin: 12px 0 0;
  color: var(--mist);
  font: 500 var(--fs-note)/1.6 var(--sans);
  letter-spacing: .24em;
  text-align: center;
}
.copy-note::before,
.copy-note::after {
  content: "";
  width: 7px;
  height: 7px;
  flex: none;
  background: var(--gold);
  clip-path: polygon(50% 0, 60% 40%, 100% 50%, 60% 60%, 50% 100%, 40% 60%, 0 50%, 40% 40%);
}
.copy-note::after { margin-left: -.24em; }
/* 分享层里上下间距由分享层自己的行距管，和原来一样 */
.share-layer__body > .copy-note { margin: 0; }
.copy-note + .pair-link { margin-top: 6px; }
.share-layer__body > .copy-note + .share-copy-block { margin-top: -8px; }
/* 复制不成功时摊开的链接和配文（2026-10-04）：像一个只能看的输入框，和导入弹层的链接框同一种边和底，原来是虚线框 */
.pair-link {
  margin: 8px 0 0;
  border: 1px solid var(--hair-strong);
  border-radius: 4px;
  padding: 10px 12px;
  background: var(--bg-input);
  color: var(--gold-hi);
  font: var(--fs-note)/1.5 var(--code);
  overflow-wrap: anywhere;
  user-select: all;
}

/* ---------- 分享与邀请 ---------- */
.result-primary-actions { display: grid; gap: 10px; padding: 28px 22px var(--chapter-bottom); }
.pair-invite-note { margin: 2px 0 0; color: var(--mist); font: var(--fs-note)/1.6 var(--sans); }
/* 审阅稿里这句删成空的时候不占位，章末留白还是 40px；在工作台写回字就自己露出来 */
.pair-invite-note:empty { display: none; }

/* ---------- 档案（第二十二轮，2026-09-29 挪到第一章后面）：年份时间轴、画像卡、星表合成一段，只有一个标题，不折叠 ---------- */
.insight-section { padding: var(--chapter-top) 22px var(--chapter-bottom); }
.insight-heading { display: flex; align-items: flex-end; justify-content: space-between; gap: 12px; padding: 0 0 18px; }
.insight-heading h2 { max-width: 78%; margin: 0; }
/* 画像卡和时间轴卡之间的 14px 用 gap 给：没有画像卡（空着或在工作台删掉）时时间轴贴着标题，有的话和画像卡之间一样隔 14px */
.insight-section .act--portraits { display: flex; flex-direction: column; gap: 14px; margin: 0 0 22px; padding: 0; background: none; }
.story-portraits { display: grid; gap: 14px; }
.insight-section .story-portraits:empty { display: none; }
/* 和缘分卡、搜歌框同一副框：四个金角（.corner），1px 细线（2026-09-29 统一，原来只有左上、右下两个角） */
.story-portraits .playlist-story,
.portrait-timeline {
  position: relative;
  border: 1px solid var(--hair);
  padding: 18px 16px 16px;
  background: rgba(9, 12, 28, .86);
}
.story-portraits .playlist-story h3 { color: var(--ivory); font: 600 var(--fs-body)/1.5 var(--sans); }
.story-portraits .playlist-story > p { margin: 8px 0 14px; font-size: var(--fs-support); }
.story-portraits .playlist-story > p:last-child,
.story-portraits .story-chips:last-child { margin-bottom: 0; }
.story-chips { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 16px; }
.story-chips span { border: 1px solid var(--hair); border-radius: 999px; padding: 7px 12px; color: var(--ivory); font: 500 var(--fs-note)/1 var(--sans); letter-spacing: .06em; }
.portrait-timeline { display: flex; flex-direction: column; align-items: flex-start; }
/* 幕后群星（2026-10-02 用户定）：档案最后一张，和画像卡同一副框。按署名角色分页，页头是角色和人数，名字一路排下来；
   名字都是一种颜色：原来署了两首以上的是金色，页面上看不出它代表什么，2026-10-02 去掉 */
.credits-roll {
  position: relative;
  margin: 22px 0 0;
  border: 1px solid var(--hair);
  padding: 18px 16px 16px;
  background: rgba(9, 12, 28, .86);
}
.credits-roll__title { margin: 0; color: var(--ivory); font: 600 var(--fs-body)/1.5 var(--sans); letter-spacing: .03em; }
/* 每个职位一张卡，左右切换（2026-10-03 用户：起码会切换着看一下）：一次看一张，下一张露出一截、淡出，和缘分卡一样左右划，
   电脑上按住拖。最后一张后面垫一截，让它也能停到和别的卡一样的位置 */
.credits-roll__pages {
  display: flex;
  gap: 14px;
  margin: 12px -16px 0;
  padding: 0 0 2px 16px;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scroll-padding: 0 16px;
  scrollbar-width: none;
  -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 34px), transparent);
  mask-image: linear-gradient(90deg, #000 calc(100% - 34px), transparent);
}
.credits-roll__pages::-webkit-scrollbar { display: none; }
.credits-roll__pages::after { content: ""; flex: 0 0 28px; }
.credits-roll__pages:focus-visible { outline: 1px solid var(--gold); outline-offset: -1px; }
@media (hover: hover) and (pointer: fine) {
  .credits-roll__pages { cursor: grab; scroll-behavior: smooth; }
  .credits-roll__pages.is-dragging { cursor: grabbing; scroll-snap-type: none; scroll-behavior: auto; user-select: none; }
}
.credits-roll__page { flex: 0 0 calc(100% - 26px); min-width: 0; scroll-snap-align: start; }
/* 上面一排职位名：点了跳到那一张，滑到哪一张哪个亮 */
/* 职位加到 16 种以后先折成两行，用户嫌太挤（2026-10-03）：改成排一行、间距放宽，放不下的往右滑。
   和下面的职位卡一样贴到框边、右边淡出，滑开以后左边也淡出（is-scrolled）；末尾留出淡出的那一截，滑到头最后一个不被盖住 */
.credits-roll__tabs {
  display: flex;
  gap: 0 22px;
  margin: 12px -16px 0;
  padding: 0 0 0 16px;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-padding: 0 16px;
  scrollbar-width: none;
  -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 34px), transparent);
  mask-image: linear-gradient(90deg, #000 calc(100% - 34px), transparent);
}
.credits-roll__tabs.is-scrolled {
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 34px, #000 calc(100% - 34px), transparent);
  mask-image: linear-gradient(90deg, transparent, #000 34px, #000 calc(100% - 34px), transparent);
}
.credits-roll__tabs::-webkit-scrollbar { display: none; }
.credits-roll__tabs::after { content: ""; flex: 0 0 12px; }
@media (hover: hover) and (pointer: fine) {
  .credits-roll__tabs.is-dragging { scroll-behavior: auto; user-select: none; }
}
.credits-roll__tabs button {
  flex: none;
  margin: 0;
  border: 0;
  border-bottom: 1px solid transparent;
  padding: 8px 0 6px;
  background: none;
  color: var(--mist);
  font: 500 var(--fs-support)/1.4 var(--sans);
  letter-spacing: .06em;
  white-space: nowrap;
  cursor: pointer;
}
.credits-roll__tabs button[aria-pressed="true"] { border-bottom-color: var(--gold-hi); color: var(--gold-hi); }
.credits-roll__tabs button:focus-visible { outline: 1px solid var(--gold-hi); outline-offset: 2px; }
.credits-roll__role { display: flex; align-items: baseline; gap: 8px; margin: 0; padding-bottom: 6px; border-bottom: 1px solid var(--line); }
.credits-roll__role b { color: var(--gold-hi); font: 600 var(--fs-support)/1.4 var(--sans); letter-spacing: .12em; }
.credits-roll__role span { color: var(--mist); font: 400 var(--fs-note)/1.4 var(--sans); letter-spacing: .06em; }
/* 卡里的名字上下滑：一张卡最高 300 px、不高过半屏，底下淡出；滚动条是一道细金线 */
.credits-roll__names {
  display: flex;
  flex-wrap: wrap;
  align-content: flex-start;
  gap: 0 14px;
  max-height: 300px;
  max-height: min(300px, 45vh);
  margin: 6px 0 0;
  padding: 0 0 32px;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
  scrollbar-color: rgba(216, 191, 138, .45) transparent;
  -webkit-mask-image: linear-gradient(180deg, #000 calc(100% - 36px), transparent);
  mask-image: linear-gradient(180deg, #000 calc(100% - 36px), transparent);
}
.credits-roll__names::-webkit-scrollbar { width: 3px; }
.credits-roll__names::-webkit-scrollbar-thumb { border-radius: 2px; background: rgba(216, 191, 138, .45); }
.credits-roll__names::-webkit-scrollbar-track { background: transparent; }
/* 每个名字都能点，点了下面列出他参与的歌（2026-10-02 用户）：底下一道很淡的点线，看得出能点；点着的那个金色实线 */
.credits-name {
  margin: 0;
  border: 0;
  padding: 5px 0;
  background: none;
  color: var(--ivory-2);
  font: 400 var(--fs-support)/1.5 var(--sans);
  letter-spacing: .04em;
  text-align: left;
  overflow-wrap: anywhere;
}
button.credits-name {
  cursor: pointer;
  text-decoration: underline dotted rgba(216, 191, 138, .42);
  text-decoration-thickness: 1px;
  text-underline-offset: 4px;
}
button.credits-name[aria-pressed="true"] { color: var(--gold-hi); text-decoration: underline solid var(--gold-hi); text-decoration-thickness: 1px; }
button.credits-name:focus-visible { outline: 1px solid var(--gold-hi); outline-offset: 2px; }
/* 点一个名字：卡片下面列出他参与的每一首歌，封面、歌名、歌手，后面写他在这首歌里做什么 */
.credits-person { margin: 18px 0 0; border-top: 1px solid var(--line); padding: 14px 0 0; }
.credits-person__name { display: block; color: var(--gold-hi); font: 600 var(--fs-body)/1.5 var(--sans); letter-spacing: .04em; }
.credits-person__songs { display: grid; gap: 10px; margin: 10px 0 0; padding: 0; list-style: none; }
.credits-song { display: grid; grid-template-columns: 40px minmax(0, 1fr) auto; align-items: center; gap: 12px; }
.credits-song__cover { --cover-size: 40px; width: 40px; height: 40px; border-radius: 2px; box-shadow: 0 0 0 1px var(--hair); }
.credits-song__text { min-width: 0; }
.credits-song__text b { display: block; color: var(--ivory); font: 500 var(--fs-support)/1.4 var(--sans); overflow-wrap: anywhere; }
.credits-song__text span { display: block; margin-top: 2px; color: var(--mist); font: 400 var(--fs-note)/1.4 var(--sans); overflow-wrap: anywhere; }
.credits-song__roles { color: var(--gold); font: 400 var(--fs-note)/1.4 var(--sans); letter-spacing: .04em; white-space: nowrap; }
/* 标题只在标点处折行：「它们跨越了13年，/ 在你的歌单里相遇」 */
.timeline-span { margin: 0 0 20px; color: var(--ivory); font: 500 var(--fs-body)/1.6 var(--sans); letter-spacing: .04em; word-break: keep-all; overflow-wrap: anywhere; text-wrap: balance; }
/* 年份时间轴：一条底线，两头是起止年份，柱子画成一根根带星的细杆；不画刻度和网格（2026-09-28）。
   底线下面留出起止年份的位置（年份挂在底线下 26 像素） */
.year-axis {
  position: relative;
  align-self: stretch;
  height: 150px;
  margin: 18px 4px 30px;
  border-bottom: 1px solid var(--hair-strong);
}
.year-axis::before,
.year-axis::after {
  position: absolute;
  bottom: -26px;
  color: var(--gold);
  font: 600 var(--fs-note) var(--latin);
  letter-spacing: .08em;
}
.year-axis::before { content: attr(data-from); left: 0; }
.year-axis::after { content: attr(data-to); right: 0; }
.year-axis i {
  position: absolute;
  bottom: 0;
  width: 2px;
  background: linear-gradient(to top, rgba(216, 191, 138, .12), rgba(243, 226, 181, .85));
  transform: translateX(-50%);
}
.year-axis i::after {
  content: "";
  position: absolute;
  top: -3px;
  left: 50%;
  width: 6px;
  height: 6px;
  margin-left: -3px;
  border-radius: 50%;
  background: var(--gold-hi);
}
/* 收藏最密的那一年：柱顶一颗四角星，替掉那根柱子的小圆点（位置由 renderTimeline 写在行内）。
   用负边距对准柱子，不用 transform 左移：呼吸时以星的正中放大，原来那段左移跟着一起放大，
   星往左偏出 4 px，跑到柱子外面（2026-10-10 用户：一动起来都跑到柱子外面去了） */
.year-axis i.is-peak::after { display: none; }
.year-axis__peak {
  position: absolute;
  width: 24px;
  height: 24px;
  margin-left: -12px;
  background: var(--glint);
}
/* 歌最多的那一年：和起止年份一样挂在底线下，亮一档（2026-10-03 用户：现在要一个个数年份） */
.year-axis__year {
  position: absolute;
  bottom: -26px;
  color: var(--gold-hi);
  font: 600 var(--fs-note) var(--latin);
  letter-spacing: .08em;
  transform: translateX(-50%);
}
/* 歌单星表（第十轮）：名目在左、值在右，中间一行金色虚线，像星表的一行 */
.star-table { display: grid; margin: 4px 0 0; padding: 0; list-style: none; border-top: 1px solid var(--line); }
.star-table li {
  min-height: 46px;
  display: flex;
  align-items: center;
  border-bottom: 1px solid var(--line);
  padding: 10px 0;
}
/* 名目一般很短；身份证上放的是人名、作品名，太长时让它折行，不把整行撑出屏幕 */
.star-table__label { flex: 0 1 auto; min-width: 0; color: var(--mist); font: 500 var(--fs-support)/1.4 var(--sans); letter-spacing: .08em; overflow-wrap: anywhere; }
.star-table__lead { flex: 1; min-width: 16px; margin: 0 12px; border-bottom: 1px dotted rgba(216, 191, 138, .32); transform: translateY(-4px); }
.star-table__value { min-width: 0; color: var(--ivory); font: 600 var(--fs-body)/1.4 var(--sans); letter-spacing: .03em; text-align: right; overflow-wrap: anywhere; }

/* ---------- 页脚 ---------- */
.result-actions { display: grid; gap: 14px; padding: 0 22px 30px; }
.result-footer {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 12px;
  border-top: 1px solid var(--line);
  padding: 26px 22px 14px;
  color: var(--mist);
}
/* 网站备案号（2026-10-10 用户选）：结果页最后一行，在收尾的金色小星下面；手机上首页不挂，挂在这里。
   不在页脚那一块里，删掉“结果页页脚”也照样在；页面底下让出的那一截从页脚挪到这里 */
.result-beian {
  display: flex;
  justify-content: center;
  padding: 0 22px max(24px, env(safe-area-inset-bottom));
}
/* 百科来源放在页脚，单独一行，只出现这一次；小字用 --mist，对比度在 4.5:1 以上 */
.story-wiki-note { margin: 22px 0 0; color: var(--mist); font: var(--fs-note)/1.6 var(--sans); }
.result-footer .story-wiki-note { flex-basis: 100%; margin: 0 0 4px; text-align: center; }
.footer-star { width: 16px; height: 16px; background: var(--glint); opacity: .85; }

/* ---------- 结果页入场动效：只在 results--animated（有脚本、没设减少动态效果）时生效，静态时内容永远完整可读。
   每一章滚进屏幕时加 is-entered；重测时 src/app.js 把 is-entered 摘掉，再滚进来照样播 ---------- */
.results--animated .act,
.results--animated .result-informal {
  opacity: 0;
  transform: translateY(26px);
  /* 安全网：滚动观察器失灵时 6 秒后也必须能读到内容 */
  animation: act-safety .6s ease 6s forwards;
}
@keyframes act-safety {
  to { opacity: 1; transform: none; }
}
.results--animated .act.is-entered,
.results--animated .result-informal.is-entered {
  opacity: 1;
  transform: none;
  transition: opacity .6s ease, transform .62s cubic-bezier(.16, 1, .3, 1);
}
/* 档案整段不动（标题和星表本来就不动），只有时间轴的柱子从底线依次长出来 */
.results--animated .insight-section .act--portraits { opacity: 1; transform: none; animation: none; }
.results--animated .act--portraits .year-axis i {
  transform: translateX(-50%) scaleY(0);
  transform-origin: bottom;
  animation: bar-safety .5s ease 6.4s forwards;
}
@keyframes bar-safety {
  to { transform: translateX(-50%) scaleY(1); }
}
.results--animated .act--portraits.is-entered .year-axis i {
  transform: translateX(-50%) scaleY(1);
  transition: transform .7s cubic-bezier(.16, 1, .3, 1);
  transition-delay: calc(var(--i, 0) * 40ms);
}
/* 收藏最多那年的星光呼吸 */
.results--animated .act--portraits.is-entered .year-axis__peak { animation: peak-pulse 2.8s ease-in-out 1s infinite; }
@keyframes peak-pulse {
  0%, 100% { scale: 1; opacity: .85; }
  50% { scale: 1.35; opacity: 1; }
}
/* 缘分卡：进到第一章时细金线从一头画到另一头、日历翻一页、门票落下来；三首以上时收到日历后面的三根线跟着显出来。
   日历页的歪斜写在 rotate 上，翻页动画只动 transform，两样叠在一起不打架 */
.results--animated .act--stories .fate-link__line,
.results--animated .act--stories .fate-thread { transform: scaleX(0); }
.results--animated .act--stories .fate-calendar { transform: rotateX(-80deg); opacity: 0; }
.results--animated .act--stories .fate-ticket { transform: translateY(-12px); opacity: 0; }
.results--animated .act--stories .fate-rays { opacity: 0; }
.results--animated .act--stories.is-entered .fate-link__line,
.results--animated .act--stories.is-entered .fate-thread { animation: fate-draw .7s ease .2s forwards; }
.results--animated .act--stories.is-entered .fate-calendar { animation: fate-flip .6s cubic-bezier(.2, .7, .2, 1) .1s forwards; }
.results--animated .act--stories.is-entered .fate-ticket { animation: fate-drop .6s cubic-bezier(.2, .7, .2, 1) .1s forwards; }
.results--animated .act--stories.is-entered .fate-rays { animation: fate-fade .6s ease .25s forwards; }
/* 门票落下来时是整张的，两半齿孔对成一排圆孔；落稳以后副券从齿孔撕开，表示检票入场（2026-10-06） */
.results--animated .act--stories .fate-ticket__stub { transform: translateX(calc(-1 * var(--tear))); }
.results--animated .act--stories.is-entered .fate-ticket__stub { animation: fate-tear .5s cubic-bezier(.2, .7, .2, 1) .8s forwards; }
@keyframes fate-draw { to { transform: scaleX(1); } }
@keyframes fate-flip { to { transform: rotateX(0); opacity: 1; } }
@keyframes fate-drop { to { transform: translateY(0); opacity: 1; } }
@keyframes fate-tear { to { transform: translate(3px, 2px) rotate(5deg); } }
@keyframes fate-fade { to { opacity: 1; } }

/* ==========================================================================
   分享层：海报图、「下载图片」（实心）和「复制配文」（细框）
   ========================================================================== */

.share-layer {
  position: fixed;
  z-index: 120;
  inset: 0 max(0px, calc((100vw - 480px) / 2));
  overflow-y: auto;
  background: var(--bg-deep);
  color: var(--ivory);
  overscroll-behavior: contain;
}
.share-layer__body { display: grid; gap: 15px; padding: 22px 22px max(34px, env(safe-area-inset-bottom)); }
.share-layer__body img {
  width: 100%;
  display: block;
  border: 1px solid var(--hair);
  border-radius: 3px;
  box-shadow: 0 20px 50px rgba(0, 0, 0, .55);
}
.share-layer__body > p { margin: 0; color: var(--ivory-2); font: var(--fs-support)/1.6 var(--sans); }
/* 「长按图片保存」是海报的图注（2026-10-04 用户：为什么只给一个单独的光秃秃的文本框）：紧跟在图下面、居中，两头各一颗小星。
   原来排在换样式那一排和按钮中间，靠左，看着像按钮的标签 */
.share-layer__body > .share-hint {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  margin-top: -4px;
  color: var(--mist);
  font: 500 var(--fs-note)/1.6 var(--sans);
  letter-spacing: .24em;
}
.share-hint::before,
.share-hint::after {
  content: "";
  width: 7px;
  height: 7px;
  flex: none;
  background: var(--gold);
  clip-path: polygon(50% 0, 60% 40%, 100% 50%, 60% 60%, 50% 100%, 40% 60%, 0 50%, 40% 40%);
}
.share-hint::after { margin-left: -.24em; }
/* 只有「复制配文」一个按钮，占满一行（2026-10-10 拿掉「下载图片」） */
.share-actions { display: grid; grid-template-columns: 1fr; }
/* 配文平时不摊开，复制不成功时才亮出来让人手动复制 */
.share-copy-block {
  margin: 0;
  border: 1px solid var(--hair-strong);
  border-radius: 4px;
  padding: 12px 14px;
  background: var(--bg-input);
  color: var(--ivory-2);
  font: var(--fs-note)/1.7 var(--sans);
  white-space: pre-wrap;
  user-select: all;
  -webkit-user-select: all;
}

/* ==========================================================================
   窄屏、矮屏与动效偏好
   ========================================================================== */

@media (max-width: 380px) {
  /* 窄屏上冷门指数和星座数缩一档（用户没定，照旧） */
  .id-card .score-lockup strong { font-size: 84px; }
  .id-card .score-lockup strong[data-digits="3"] { font-size: 66px; }
  .sky-count b { font-size: 84px; }
}
@media (max-width: 374px) {
  /* 缘分卡的画面在这些屏上只有 240 px 上下：封面缩小，同一天生日的两页收窄一些 */
  .fate-pic--calendar .fate-cover { --cover-size: 48px; width: 48px; height: 48px; }
  .fate-thread { left: 26px; right: 26px; }
  .fate-days { width: 124px; height: 136px; }
  .fate-days .fate-calendar { width: 70px; }
  /* 门票窄了 30 px：封面小一档，三张照样排得下 */
  .fate-ticket__art { gap: 6px; }
  .fate-ticket__art .fate-cover { --cover-size: 52px; width: 52px; height: 52px; }
}
/* 360 宽以下不再另排（2026-10-06）：屏宽不到 360 的手机按 360 排版、整页等比缩小，见 index.html 开头那一段。
   原来这里给 320 宽另定了按钮字距、缘分卡封面、门票和冷门指数的字号 */
/* 横屏的手机：封面长廊让出高度，顶栏收窄，第一屏就能看到列表里的歌。
   只认真横屏（宽高比 3:2 以上，手机横过来都在 16:9 上下）：安卓微信弹出键盘时会把竖屏视口压到 480 以下，
   360 宽的手机还会压到比宽还矮，这时 orientation: landscape 也会命中；长廊不能跟着键盘一会儿有一会儿没有 */
@media (max-height: 480px) and (min-aspect-ratio: 3/2) {
  .coverflow-stage { display: none; }
  .selector-bar { min-height: 52px; padding-block: 4px; }
  .input-panel { padding-top: 4px; }
}
/* 已选栏在窄屏上（2026-10-04）：375 宽以下那一摞封面小一号。原来 320 宽连这一摞也不放，2026-10-06 起最窄按 360 排 */
@media (max-width: 380px) {
  .dock-stack .song-cover { --cover-size: 26px; width: 26px; height: 26px; }
  .dock-stack .song-cover + .song-cover { margin-left: -14px; }
  .dock-stack:empty::before { width: 26px; height: 26px; }
  .selection-toggle { gap: 8px; }
}
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    scroll-behavior: auto !important;
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
  .id-card .score-lockup { transition: none; }
}
