/* ==========================================================================
   墨竹印象 · 唱片页版式（唯一权威）

   为什么单独一个文件：stage.css 是叠出来的——同一个 .deck__platter 被声明过 25 次，
   .stage__info 的定位方案换过 3 套，还有一个 max-width:860px 的媒体查询没闭合、
   把后面十几条规则吞了进去。版式这种东西经不起这样叠。

   所以这里做一件事：**把"页面怎么排"从"物件怎么画"里拿出来，单独定死一套栅格。**
   stage.css 继续负责唱片、刻纹、圆标、歌词动画；页面结构（引首 / 画心 / 底轴、
   三栏、间距、字号）只认这个文件。

   改之前的实测问题（1600×900）：
     · 全页出现 9 个互不相同的对齐边：32 / 96 / 214 / 567 / 665 / 1204 / 1310 / 1568 / 1584
     · 左栏与盘面间距 131px，右栏与盘面间距 269px —— 三栏不是一套栅格
     · 盘面中心 y=378，另外两栏中心 y=432：圆心比它们高 54px（.deck 上挂着 translateY(-6vh)）
     · 曲名用 Helvetica Neue 发丝体，而 DESIGN.md 规定曲名一律宋体
     · 进度条 96→1230、控制键 1310→1568、时间 32 与 1246 —— 四段各自为政

   两条布局原则（照 DESIGN.md 的手卷结构）：
     1. 一条外边距。引首、画心、底轴共用同一个左右边界，页面上只有两个对齐边。
     2. 一处用力。这一页只有唱片是有体积的物件，其余全是墨底上的字——
        不加卡片、不加玻璃、不加面板描边。
   ========================================================================== */

.stage {
  /* 全页唯一的左右边界（2026-10-10 取消页内覆盖：它写的是 clamp(24px,…)，
     比 tokens.css 里那条还小 4px，把整档抬起后的边距又拽回去了一点） */
  --stage-pad: clamp(28px, 4.2vw, 76px);
  /* 一屏的宽度上限：1500 → 1700。
     2560 视口下实测左右各空 530px（合计 1060px 死白），是"不饱满"的主要来源。
     1700 之后左右各 430px，且中栏上限同步抬到 380 —— 歌词行更长、唱片更靠右，
     三栏的疏密关系不变（栏间相等这条仍然成立）。 */
  --stage-max: 1700px;
  /* 盘面直径：高度与宽度两侧同时收住，宽屏不溢出、矮屏不顶到字、窄屏不出横向滚动
     （82vw 这一项是手机必须的：只用 vh 时 430px 宽的屏幕上盘面 396px 把版面撑破）
     ⚠ 这里的值在第 1058 行被覆盖（那一处才是生效值），改大小请改那一条。 */
  --disc-size: clamp(260px, min(46vh, 82vw), 520px);

  padding-inline: var(--stage-pad);
  /* 靛蓝的地：DESIGN.md 02 的底是 #060C1B，河水青与朱砂只各留一口气。
     这条必须 !important：stage.css 里同属性有 6 条 !important 的历史值。

     ⚠ 文件末尾还有一条 .stage 的 background（它更靠后 → 实际生效的是那一条）。
     我在 2026-10-10 一度把"顶端天光"加在这里 —— 那是死代码，已改到末尾那条里。
     改背景请改末尾那条。 */
}
/* 撤掉"封面整屏高斯模糊"那一层：它带来的是每帧重绘（性能注释里已经关过一次），
   而颜色关系现在由上面这层靛蓝渐变换来。--stage-cover 由 stage.js 写入，此处不再消费。 */
.stage::before { background-image: none !important; }
.stage::after {
  background: radial-gradient(130% 100% at 50% 45%, rgba(2, 10, 20, 0) 0%, rgba(2, 10, 20, 0.5) 100%) !important;
}

/* 引首、画心、底轴共用同一个盒子 —— 页面上只剩两个对齐边 */
.stage__top,
.stage__body,
.stage__bottom {
  width: 100%;
  max-width: var(--stage-max);
  margin-inline: auto;
}

/* ───────────────────────────────────────────────────────────── 引首 */

.stage__top {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 18px 0 10px;
}
.stage__close {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 6px 10px 6px 4px;
  border-radius: 99px;
  font-size: var(--fs-tiny);
  letter-spacing: 0.14em;
  color: var(--paper-faint);
  transition: color var(--dur-fast), background var(--dur-fast);
}
.stage__close .ico { width: 15px; height: 15px; }
.stage__close:hover { color: var(--paper); background: rgba(242, 245, 248, 0.05); }
.stage__top-rule { width: 1px; height: 13px; background: var(--line-strong); flex: none; }
.stage__brand { display: flex; align-items: center; gap: 8px; font-size: var(--fs-tiny); white-space: nowrap; }
.stage__brand-cn {
  font-family: var(--font-cn-display);
  letter-spacing: 0.22em;
  color: var(--paper-dim);
}
.stage__brand-dot { color: var(--seal); }
.stage__brand-ip { color: var(--river); letter-spacing: 0.16em; }
/* 右上角是这一页唯一的"仪表读数"：把文件的参数交给等宽字，数字右对齐 */
.stage__quality {
  margin-left: auto;
  /* min-width: 0 必须写：不然这行不可压缩的字会把整个页首的最小宽度顶到 460px，
     窄屏上连带把引首/画心/底轴三条一起撑宽（实测 430px 屏撑到 462px）。 */
  min-width: 0;
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.06em;
  text-align: right;
  color: var(--paper-ghost);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ───────────────────────────────────────────────────────────── 画心 */

.stage__body {
  display: grid;
  /* 盘面自己定宽（auto），信息栏上下限收住，歌词吃掉剩余 —— 不再用 calc 猜盘面多大
     2026-10-10：中栏 340 → 380，栏间 100 → 112，歌词栏下限 280 → 320。
     ⚠ 下限这一条是**实测反馈改的**（普琼在 1440×900 窗口下"看不出效果"）：
       1440 上盘面从 504 长到 522，正好把歌词栏从 331 挤到 300 —— 全场只有"缩小"是看得见的，
       于是"放大"的感觉被吃掉了。给歌词栏一个 320 的地板，盘面要长就从中栏的余量里拿。 */
  grid-template-columns: auto minmax(240px, 380px) minmax(320px, 1fr);
  /* 画心占满引首与底轴之间的全部高度：歌词栏因此是一整根通高的文字栏，
     页面上下不再留两条空带（宽屏上那两块空地是原来最明显的"没排完"） */
  grid-template-rows: minmax(0, 1fr);
  column-gap: clamp(40px, 5vw, 112px);
  align-items: center;
  min-height: 0;
  /* 底距留一点呼吸，左右归零（旧规则里有 32px 内边距，会把栅格挤窄 64px） */
  padding: 0 0 clamp(6px, 1.4vh, 20px);
}

/* ─────────────────────────────────────────────── 唱盘（画心的主角） */

.deck {
  position: relative;
  transform: none !important; /* 撤掉 translateY(-6vh)：它把圆心顶高 54px，和另外两栏错开 */
  /* 必须显式声明 max-content：留 auto 时，唱盘对所在栅格行的高度贡献只算进内边距
     （实测 126.9px），盘面的 353px 被漏掉 —— 后果是窄屏下行高不够，
     盘面直接压到下面的信息区与歌词栏上。这条修的就是那个重叠。 */
  height: max-content;
}

/* 三脚架已按用户要求移除（DOM 也删了）。原来那套"两条前腿从盘底两侧起、
   后腿居中短一档"的几何记在 DESIGN.md 的唱片页一节，要恢复照它写回即可。
   腿拿掉之后唱片是"悬"在空间里的，重量交给 .deck__shadow 那层投影。 */
.deck__legs { display: none !important; }

/* ═══════════ 黑胶盘体与唱臂：已按用户要求移除 ═══════════

   他在截图上用红圈标出的正是这两处 —— 黑胶盘体（沟槽、盘边金线、盘体投影）与右侧唱臂，
   原话"保留人物，佛塔，路，其余的删掉"。画心（tools/make-label-art.py 生成的那张）本身
   就是主角，套上唱片壳反而把它压成"盘上的一张圆标"。

   ⚠ **一个必须一起处理的连带项**：盘体原本在转（`spin` 26s），画心为了保持正立做了
   **反向自转**（`spin-back` 26s），净效果静止。只停盘体而不管画心，画心就会自己转起来
   （26s 一圈，肉眼 immediately 能看出歪）—— 所以这两条动画必须一起停。

   这些都是**覆盖**，不是删除：盘面与唱臂的原始样式都还在 stage.css 里。
   要恢复唱片壳，把下面这一整块删掉即可。 */
.deck__disc {
  background: none !important;    /* 沟槽 repeating-radial-gradient + 主光 + 盘体渐变 */
  box-shadow: none !important;    /* 鎏金盘边、外溢光、盘体投影、内阴影 */
  animation: none !important;     /* 与下面 .deck__label 的反向自转成对 */
}
.deck__disc::before { display: none !important; }   /* 银环（盘面那道清楚的边） */
.deck__disc::after { display: none !important; }    /* 压片接缝 */
.deck__platter { filter: none !important; }         /* 盘体落在背景上的 drop-shadow */
.deck__platter::after { display: none !important; } /* 金属盘那道缓转的流光 */
/* 盘体背后那两团柔光（青 + 朱砂，`.deck__platter::before`）也去掉：
   它是给黑胶做背光的，唱片没了以后就只剩下一坨**圆形**光斑 ——
   用户刚要求"圆圈删掉"，留着它画心外面又套了一个更圆的圈。
   放大截图里那道弧就是这么来的。 */
.deck__platter::before { display: none !important; }
.deck__arm { display: none !important; }            /* 唱臂：支点、银杆、朱砂唱针 */
.deck__spindle { display: none !important; }        /* 盘心轴孔：没有盘就不该有孔 */
.deck__shadow { display: none !important; }         /* 盘体那块接地椭圆影 */
.deck__disc .deck__label { animation: none !important; } /* 反向自转，见上面的连带项 */
/* 盘体没了之后，画心就是主角：把它放大到原盘壳的分量（396 × 1.24 ≈ 491），
   并撤掉圆标那几道 1px 亮边（stage.css 里 946/1065/1106 三处各写了一道）——
   画心自带暗角，边缘原本就能溶进夜色；留一圈亮边反而又把它框回"盘上的一张圆标"。

   **圆形裁切也去掉了**（用户："圆圈删掉"）：唱片没了之后，圆是最后一件"唱片遗物"，
   留着它这幅画就还是"盘上的一张圆标"。现在改成**圆角矩形**——一幅挂着的画，
   与用户给的那张参考图的圆角一致。三处圆角必须一起改，少一处就会被外层的圆裁掉：
   外层 .deck__disc（它带 border-radius:50% + overflow:hidden，会剪掉画心的四角）、
   圆标本体、以及里面那张图。半径用百分比，窄屏缩小后圆角比例才不会走样。 */
.deck__disc {
  inset: -12% !important;
  border-radius: 4% !important;
}
.deck__disc .deck__label {
  inset: 0 !important;
  border-radius: 4% !important;
  box-shadow: none !important;
}
.deck__disc .deck__label > img.deck__label-photo {
  border-radius: 4% !important;
  /* 边缘再柔一道（用户："边缘柔化"）。画心自己已经把四条边渐隐到舞台底色了，
     这层 mask 是**兜底**：换素材、换底色、或画心被裁剪时，边界仍然不会显形。
     只吃最外 3%，不碰画面内容 —— 塔冠的日月在 y≈6%，再放宽就会把它也吃掉。 */
  -webkit-mask-image:
    linear-gradient(to right, transparent 0, #000 3%, #000 97%, transparent 100%),
    linear-gradient(to bottom, transparent 0, #000 3%, #000 97%, transparent 100%);
  mask-image:
    linear-gradient(to right, transparent 0, #000 3%, #000 97%, transparent 100%),
    linear-gradient(to bottom, transparent 0, #000 3%, #000 97%, transparent 100%);
  -webkit-mask-composite: source-in;
  mask-composite: intersect;
}

/* ═══════════ 盘体不许再裁圆（"这个还在"的那个圆）═══════════

   真凶是 stage.css:1077：

     .deck__platter { border-radius: 50% !important; overflow: hidden; ... }

   那是留给**黑胶盘体**的：圆盘 + 把盘上的一切剪成圆的。本文件前面把盘体的
   background / box-shadow / 伪元素全撤了，却漏了这两行 —— 于是"盘"看不见了，
   **裁切还在**：圆标本该是"挂着的一幅画"（铺满 570px、圆角 4%，见上面那条），
   却被盘体那个 **460px 的圆**剪成圆盘，四角连画心一起切掉。

   实测指纹：截图里那道圆的直径正好 690 设备像素 = 460 CSS = `--disc-size` ——
   与盘体同尺寸（圆标本体是 570，画心的暗角是柔的，两者都给不出这道硬边）。

   盘体已经不画任何东西了，把圆角与裁切一起归零：让圆标按它自己的圆角矩形显示。 */
.deck__platter {
  border-radius: 0 !important;
  overflow: visible !important;
}

/* 照片圆标（brand.labelArt='photo'，默认）的盒子必须**自己不画底**。

   stage.css:2247 给 `.deck__label` 铺了一层哑面纸（radial-gradient 白纸 + 印刷内圈亮线），
   注释里写明那是给**矢量圆标**（disc-face.svg，自带 800×800 不透明底）的纸感。
   照片模式用不着它，而且有害：画心 disc-label-photo.png 的左右各 20%、上下各 3%
   本就是透明的（tools/make-label-art.py 的 EDGE_X / EDGE_Y，为的是压在什么底色上都自然），
   白纸会从这些透明处透出来，在暗舞台上读成一道亮边。

   注：这不是那个"圆"的成因（圆是上面的盘体裁切），是同一张图上另一处会露馅的地方 —
   补上它，画心的透明边才真的落在舞台底色上。 */
.deck__disc .deck__label.deck__label--photo {
  background: none !important;
  box-shadow: none !important;
  border: none !important;
}

/* ═══════════ 画心（佛塔那张）已按用户要求下线 ═══════════

   用户原话："佛塔从我的界面里帮我去除，不要了"（附的图就是画心本身那张）。
   画心的主体就是那座佛塔（覆钵 + 十三天金顶 + 台座），去掉佛塔等于去掉整幅。

   这里只关**显示**，什么都不删：
     · 素材 web/assets/disc-label-photo.png 保留；
     · 生成它的 tools/make-label-art.py 保留（想改塔的形再重出图就走它）；
     · stage.js 的 mountPhotoLabel() / mountVectorLabel() 两套挂载逻辑保留；
     · config.json 的 brand.labelArt（photo/vector）保留。
   要回退：删掉下面这一行即可（矢量圆标同被这一行盖住 —— 它中间也是一座白塔）。

   注：本文件前面那些"黑胶遗物"的清理（盘体 / 唱臂 / 三脚架 / 圆标外那圈经幡）
   都还在原位，撤掉这里不会把它们带回来。 */
.deck__disc .deck__label { display: none !important; }

/* ═══════════ 唱片页新画：红袍行者 + 悬浮的经书 ═══════════

   用户给的图（"请帮我这个图放在播放器里"）—— 原图是白底截图，
   抠底脚本 tools/make-player-art.py，产出 web/assets/player-art.png。

   挂在 .deck__platter 里、.deck__disc **之外**：stage.js 每次换曲都会重写
   #disc 的 innerHTML，画放在 disc 里会被连着换掉（它是一张静态的画，不跟曲目走）。

   尺寸：高度跟 --disc-size（全页唯一的唱盘量纲）走，取 1.28 倍 ——
   图是竖构图（938×1296，约 0.72），**按高度定尺寸**最稳：定宽度的话，
   窄屏上会先撞到栅格宽度、再被 object-fit 压扁，比例就走了。
   1.28 让它的高度与原来那张圆标（570 = 460×1.24）基本齐平。
   位置绝对居中在盘心；pointer-events 关掉，它只是画，不参与交互。 */
.deck__figure {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  /* 尺寸**只用高度**定，宽度交给 contain 按比例算 —— 一个数就够。
     （用宽度定会被栏宽卡住、或让图按自身 1400px 原尺寸渲染；上一版"太大了"就是后者：
       正则清理时把 height 那行一起删掉了。） */
  height: min(34vh, 310px);
  width: auto;
  max-width: none;
  /* ⚠ object-fit 与 max-width 这两条是修一个实拍的 bug：
     app.css 里有全局 `img { max-width: 100% }`，它把宽度压到列宽（396），
     而 height 是按 56vh 算的（504）—— 一张正方形的图于是被**纵向拉长**成 396×504 显示。
     加 contain 之后按原比例放进这个盒子，尺寸由高度定，宽度自动。
     封面放大到能当主角时，这两条必须一起留。 */
  object-fit: contain;               /* 抠图无矩形底：contain 保比例 */
  border-radius: 0;
  /* 一扇亮着的窗：暖色画在深蓝页上，靠这圈暖光与环境"接上"，而不是硬生生贴上去 */
  /* 微风滤镜 + 投影：滤镜链要写在一起，后写的 drop-shadow 作用在位移之后 */
  /* 微风改由**素材**承担（tools/make-breeze.py 烘的循环 WebP）。
     这里不再挂 CSS 滤镜：两套动效叠着会打架，而且 CSS 滤镜只能让整张图受风。 */
  filter: drop-shadow(0 18px 44px rgba(0, 0, 0, 0.5));
  /* max-width 保持全局那条 100%（不写成 none）：otherwise 放大后它会溢出到右侧栏上，
     而这一步只是修比例，不是改版式。 */
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
}

/* ═══════════ 封面上的经幡（DOM，会飘）═══════════

   用户："可以替换成这个吗？有动画的吗？那就特别好" —— 他要旗上带六字真言**并且会飘**。
   所以旗不能画进 PNG：静态图飘不起来，字也不该由生成脚本硬写进图。
   这里做成 DOM：字体 / 正字 / 飘动全部复用页面已有那套（garland-flutter + Noto Sans Tibetan），
   字仍是蓝 ཨོཾ་ 、白 མ་ 、红 ཎི་ 、绿 པདྨེ་ 、黄 ཧཱུྃ།（与标题那串同源，不自己编）。

   摆位：沿一条**斜线**（左上近、右下远），与封面里石径的走向一致；
   旗身朝下、随距离略小略淡 —— 和标题那串旗同一套风、同一套重力规矩。
   绳是一条 rotate 过的 1px 线，角度按首末两面旗的连线算：atan2(58, 41) ≈ 54.7°。 */
.cover-flags {
  position: absolute;
  inset: 0;
  pointer-events: none;
  --cfs: clamp(11px, 1.7vh, 19px);     /* 旗的边长：跟视口高走。8 面、收在封面内（原来 15–26px 会溢出整页）*/
}
.cover-flags::before {                  /* 绳 */
  content: "";
  position: absolute;
  left: 8%;
  top: 12%;
  width: 63%;                           /* √(38² + 46²) */
  height: 1px;
  background: linear-gradient(90deg, rgba(210, 220, 236, 0) 0%, rgba(210, 220, 236, 0.5) 12%, rgba(210, 220, 236, 0.5) 88%, rgba(210, 220, 236, 0) 100%);
  transform-origin: 0 50%;
  transform: rotate(54.7deg);
}
.cflag {
  position: absolute;
  /* --x / --y / --z 由 HTML 内联：位置按深度倒数插值、--z 是 1/深度（尺寸与不透明度都用它） */
  left: var(--x);
  top: var(--y);
  width: calc(var(--cfs) * var(--z));
  aspect-ratio: 1;
  display: grid;
  place-items: center;
  background: var(--c);
  color: var(--t);
  opacity: calc(0.34 + var(--z) * 0.62);      /* 越远越淡（z 越小越淡）*/
  font-family: "Noto Sans Tibetan", var(--font-tibetan);
  font-weight: 700;
  font-size: calc(var(--cfs) * 0.48 * var(--z));
  line-height: 1;
  -webkit-text-stroke: 0.2px currentColor;
  /* 顶边挂在绳上、底部飘（与标题/盘面那两串同一条规矩）；
     相位逐面错开：风从近端走到远端 */
  transform-origin: top center;
  /* 侧转：旗面沿着"朝画里走"的方向略微转过去（foreshortening）。
     字仍保持近垂直，不然藏文一歪就读不出来。 */
  transform: perspective(420px) rotateY(-26deg) rotate(-5deg);
  animation: garland-flutter 2.4s cubic-bezier(0.37, 0, 0.63, 1) infinite;
  animation-delay: calc((1 - var(--z)) * -2.2s);   /* 越远相位越滞后：风从近端走向远端 */
}

/* 背景里那尊放大的红袍行者：**也已按用户要求去除**（"背景里的融合人物去除"）。
   它曾经是 .deck__walker：height 1.7×--disc-size、top -0.22×--disc-size、opacity 0.26、
   saturate(.86) brightness(.9)、上下两端各用一段 linear-gradient 溶边（mask-composite: intersect）。
   为什么拿掉：画心重做之后，画里已经有一个同样的行者在往塔走（见 tools/make-label-art.py），
   背景再放一个同名同形的巨大影子 = 同一句话说两遍，视觉上打架，也把唱片压住了。
   参数记在这里与 DESIGN.md，要恢复照写回即可；素材 web/assets/stage-walker.png **保留**，
   它是画心里那个人物的来源，别删。 */

.deck__platter {
  width: var(--disc-size) !important;
  height: var(--disc-size) !important;
}

/* 三脚架：已移除（DOM 一起删了）。
   之前那套几何是"两条前腿从盘底两侧起、各向后收 21°，后腿居中短一档，腿长按盘径 46%"，
   完整参数记在 DESIGN.md 的唱片页一节，将来要恢复照它写回即可。
   腿拿掉之后唱片是悬在空间里的，重量改由 .deck__shadow 那层投影承担
   （见 stage.css 末尾的"黑胶材质"一节）。 */

/* 唱臂：支点必须在盘外 —— 这是唱盘的结构，不是装饰。
   原来的 left/right 按"盘宽 30% + 靠右 2%"放，实测支点落在盘面内部、
   唱针停在半径 50% 的位置，于是整条臂读起来是"悬在盘上"。
   这里按几何重放：
     支点 = 盘心向右 1.13R、向上 0.70R（约 1.32R 的距离，在盘外）
     唱针落下 = 盘心向右 0.76R、向下 0.37R（约 0.85R，压在最外圈沟槽上）
   rotate(-26deg) 是抬起（停播），rotate(2deg) 是落下（播放），见 stage.css。 */
.deck__arm {
  left: 82.5%;
  right: auto;
  top: 6%;
  width: 30%;
  max-width: none;
  transform: rotate(-30deg); /* 抬起（停播）时退到盘外更远一点 */
}
/* 落针角度：实测唱针因此落在半径 0.88R 处（最外圈沟槽），支点在 1.44R（盘外）。
   原实现的 2° 让唱针深入到 0.78R，读起来像"停在了唱片中间"。 */
body.is-playing .deck__arm { transform: rotate(-3deg); }

/* ─────────────────────────────────────────────────────── 圆标取景 */

/* 1.65 倍放大是之前定下的（让塔填满中央圆标），保留。
   但取景中心取的是画面几何中心（origin 50% 52%），而塔的重心在 y≈62%——
   于是取景偏上：塔基与白色台座被圆标下沿切掉，圆标里露出来的
   是一横条白台 + 一道赭红墙带，读不出"这里是一座塔"。
   只把取景中心移到塔身上（不动放大倍数），塔尖、塔瓶、塔基、台座就都在圆内。
   （这条要 !important：stage.css 里 .deck__label > svg 的 transform-origin
   有 5 轮历史值，最后一个在文件更后面。） */
.deck__label > svg,
.deck__label > img {
  transform-origin: 50% 62% !important;
}

/* ─────────────────────────────────────── 标题上方的经幡（信息栏里的那一小串）

   用户："所有歌曲播放，标题上显示经幡"。五面、五色五字，与盘面那串同一套，
   横着挂在一条绳上、各自飘（相位逐面错开，风从一端走到另一端）。
   尺寸跟标题同一个 vw 节奏（clamp 14–21px），罗马字注音省掉：那个大小印出来是噪点。 */
/* 标题 + 经幡的包裹层：收紧到标题宽度，经幡才可能"在标题上方居中"而不是在整栏里居中 */
.stage__title-block { width: fit-content; }

.tgarland {
  /* 尺寸跟标题走（同一个 vw 节奏），不是固定 12px —— 用户："经幡小了点，测量字数"。
     12px 时藏文字只有 7px，白旗上那个 མ 几乎印不出来（"缺少字"就是这么来的）。 */
  --tflag: clamp(20px, 2.1vw, 32px);   /* 跟随标题一起放大（2026-10-10 普琼要求「经幡再大一点」：15/1.6/24 → 20/2.1/32，约 +33%） */
  position: relative;
  display: flex;
  align-items: flex-start;
  justify-content: center; /* 旗在绳上居中 */
  gap: calc(var(--tflag) * 0.85);
  height: calc(var(--tflag) + 3px);
  margin-bottom: 10px;
  /* 绳：一条两端渐隐的细线，宽度铺满标题（与盘面那串同一种做法） */
  background-image: linear-gradient(90deg, transparent, var(--line-strong) 12%, var(--line-strong) 88%, transparent);
  background-repeat: no-repeat;
  background-size: 100% 1px;
  background-position: 0 0;
}
.tflag {
  position: relative;
  width: var(--tflag);
  height: var(--tflag);
  flex: none;
  background: var(--f-c, #f2f5f8);
  color: var(--f-t, #f2f5f8);
  display: grid;
  place-items: center;
  opacity: 0.92;
  transform-origin: top center; /* 顶边挂在绳上，底部飘（与盘面那串同一规矩） */
  animation: garland-flutter 2.4s cubic-bezier(0.37, 0, 0.63, 1) infinite;
  animation-delay: var(--f-d, 0s);
}
/* stage.css:1199 有一条 `.stage__info, .stage__info * { color: rgba(240,248,252,.94) !important }`，
   标题块正好在 .stage__info 里 —— 五个字全被刷成近白，白旗上那面等于隐形
   （用户报的"缺少字"就是这个）。权重比它高（两个类）+ !important 抢回来。 */
.stage__title-block .tflag,
.stage__title-block .tflag-syl { color: var(--f-t, #f2f5f8) !important; }

.tflag-syl {
  font-family: "Noto Sans Tibetan", var(--font-tibetan);
  font-weight: 700;
  font-size: calc(var(--tflag) * 0.62);
  line-height: 1;
  -webkit-text-stroke: 0.2px currentColor;
}

/* ───────────────────────────────────────────────────────── 曲目信息栏 */

.stage__info {
  position: static;
  transform: none;
  max-width: none;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  text-align: left;
  min-width: 0;
  /* 间距全部交给下面的 margin 定死；gap 必须归零——
     stage.css 的基础规则给了 12px gap，会和 margin 叠成两套间距 */
  gap: 0 !important;
}
/* 曲名：细体几何无衬线（按用户给的参考图）。
   Avenir Next 是本机最接近参考的几何无衬线（Ultra Light 一档），中文交给苹方 Ultralight。
   注意这里没有用 --font-thin 令牌：那个令牌同时被侧栏导航与分类标签用着（那些地方要 300 的
   字重），而 Avenir Next 没有 300 这一档，改了会把导航一起变细。所以曲名单独声明字体栈。
   细体字要立得住，靠的是"够大 + 字距拉开"，不是靠加粗——参考图里那行大标题就是这么做的。 */
.stage__title {
  font-family: "Avenir Next", "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", sans-serif;
  font-weight: 200;
  font-size: var(--fs-display);
  line-height: 1.14;
  letter-spacing: 0.05em;
  color: var(--paper) !important;
  margin: 0 0 var(--sp-3);      /* 原先写死 14px —— 不在 4px 基准上，归到 12 */
  text-wrap: balance;
  -webkit-font-smoothing: antialiased;
}
.stage__artist {
  display: flex;
  align-items: center;
  gap: var(--sp-2);          /* 10 → 8（4px 基准）*/
  flex-wrap: wrap;
  padding: 0;
  margin: 0 0 24px;
  font-family: var(--font-cn-ui);
  font-size: var(--fs-lead);   /* 13 → 16：整档抬起（用户要求"在放大"）*/
  font-weight: 400;
  letter-spacing: 0.05em;
  color: var(--paper-dim) !important;
  align-self: stretch;
}
/* 原来艺人行尾部拖一条渐隐横线，和"正在播放"的字一起看像是没画完的下划线 —— 撤掉，
   改用一道竖细线分隔艺人 / 专辑（比"·"更像排过版） */
.stage__artist::after { display: none !important; }
.stage__artist .stage__artist-sep {
  width: 1px;
  height: 11px;
  flex: none;
  background: var(--line-strong);
}
.stage__artist .stage__album { color: var(--paper-faint) !important; }

.stage__specs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2);
  margin: 0 0 var(--sp-6);      /* 28 → 32：竖向节奏 12 / 24 / 32 都是 4 的倍数 */
}
/* 徽标：只有"音质档位"这一枚用品牌色，其余一律退成安静的等宽字面。
   原来是六个等重胶囊并排，读起来像说明书参数表。
   （以下 !important 是压 stage.css 里三轮"徽标分色"的历史值：999px 圆角、
   按序号上色等。等那份文件清理时一起删。） */
.stage__specs .spec-chip {
  font-family: var(--font-mono);
  font-size: var(--fs-small);  /* 11 → 13：11px 低于舒适下限 */
  letter-spacing: 0.06em !important;
  padding: 4px 8px;             /* 9 → 8 */
  border-radius: 4px !important;
  border: 1px solid var(--line) !important;
  color: var(--paper-faint) !important;
  background: none !important;
}
/* 无损 / 高音质 → 河水青（"状态"色）；高解析 → 银白（最珍贵的一档留给朱砂实心按钮） */
.stage__specs .spec-chip--lossless,
.stage__specs .spec-chip--hq {
  color: var(--river) !important;
  border-color: var(--line-bamboo) !important;
  background: rgba(57, 211, 196, 0.07) !important;
}
.stage__specs .spec-chip--hires {
  color: var(--silver) !important;
  border-color: rgba(228, 235, 240, 0.3) !important;
  background: rgba(228, 235, 240, 0.06) !important;
}
/* 来源（在线曲目）：不给颜色，它是"从哪来"，不是"多好" */
.stage__specs .spec-chip--online {
  color: var(--paper-ghost) !important;
  border-color: var(--line) !important;
  background: none !important;
}

.stage__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0;
}
.stage__actions .actbtn {
  white-space: nowrap;
  padding: 8px 15px;
  font-size: var(--fs-body);        /* 12 → 14：与艺人同步抬一档 */
  letter-spacing: 0.08em;
  color: var(--paper-faint) !important;
  background: none !important;
  border-color: var(--line-strong) !important;
}
.stage__actions .actbtn:hover { color: var(--paper) !important; border-color: var(--paper-ghost) !important; }
/* 喜欢是这排唯一的朱砂（DESIGN.md 03：朱砂是动词） */
.stage__actions .actbtn:first-child {
  color: #f6e6e1 !important;
  border-color: transparent !important;
  background: linear-gradient(180deg, rgba(206, 68, 52, 0.9), rgba(158, 38, 27, 0.9)) !important;
  box-shadow: 0 4px 14px rgba(190, 50, 39, 0.28), inset 0 1px 0 rgba(255, 255, 255, 0.16);
}

/* ───────────────────────────────────────────────────────────── 歌词栏 */

.stage__lyrics-wrap {
  position: relative;
  transform: none;
  width: auto;
  align-self: stretch;
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: clamp(300px, 46vh, 520px);
  /* 画心的分界线：这一栏是"另一卷"，一根细线把它立住，
     也让宽屏上右侧那片空不再是"没排完"。
     内边距保持 30 上下：线要读成"这一栏的左边框"，离字太远就成了一条孤线。 */
  padding-left: clamp(20px, 2.2vw, 38px);
  border-left: 1px solid var(--line) !important;
}
/* 歌词栏的"底子"：不能是一块死黑。
   这一栏要陪人听完一整首，背后得有空间感。只用渐变（不引实时模糊、不铺噪点图，
   理由见 stage.css 的性能一节）。
   注意用**径向**而不是线性：线性渐变在栏的边界上会留下硬边，读起来就是一块面板
   （设计文档禁止在唱片页加面板），径向的衰减落在框内，看不出边界。
   三处光：左侧（唱片那一侧）一道冷光把两卷连起来、上方一层薄天光、底部一点余温。
   必须 display:block !important：stage.css 有一轮"标题区背后不许有装饰块"的
   强力重置，把这两栏的伪元素整个 display:none 掉了。 */
.stage__lyrics-wrap::before {
  content: "" !important;
  display: block !important;
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(72% 40% at -8% 48%, rgba(146, 200, 216, 0.09), rgba(146, 200, 216, 0) 68%),
    radial-gradient(86% 30% at 34% 4%, rgba(122, 152, 194, 0.075), rgba(122, 152, 194, 0) 72%),
    radial-gradient(70% 26% at 52% 100%, rgba(190, 50, 39, 0.05), rgba(190, 50, 39, 0) 74%) !important;
}
.stage__lyrics-head,
.stage__lyrics {
  position: relative;
  z-index: 1;
  max-width: 34em;
  /* 用户："帮我一动(移)一下旁边" + 一个向右的箭头 —— 文字整体右移一点。
     用 padding-left 而不是 margin：行宽仍受上面那条 34em 约束，只是整块往右挪，
     所以每行的字数不变（不会因为移位把行拉长、反而更难读）。 */
  padding-left: var(--sp-5);
}
.stage__lyrics-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex: none;
  padding-bottom: 12px;
}
.stage__lyrics-label {
  font-family: var(--font-cn-display);
  font-size: var(--fs-small);
  letter-spacing: 0.24em;
  color: var(--paper-faint);
}
.lyrics-tools { display: flex; align-items: center; gap: 2px; padding: 0; }
.lyrics-tools .chipbtn {
  padding: 3px 8px;
  border: 0;
  border-bottom: 1px solid transparent;
  border-radius: 0;
  background: none;
  font-size: var(--fs-micro);
  letter-spacing: 0.14em;
  color: var(--paper-faint);
}
.lyrics-tools .chipbtn:hover { color: var(--paper-dim); }
.lyrics-tools .chipbtn.is-on {
  color: var(--paper);
  background: none;
  border-bottom-color: var(--seal);
}
.stage__lyrics {
  flex: 1;
  min-height: 0;
  /* 下内边距里含表头行的高度：歌词是"整栏居中"，而表头占了栏顶 38px，
     不补这一下，歌词的视觉中心会比唱片圆心低 19px（实测）。 */
  padding: 14px 16px 52px 0;
  /* 歌词短的时候把它摆在栏的中间，和唱片的圆心落在同一条水平线上；
     长的时候照常从中间往外滚（safe 保证溢出时不会把开头切掉）。 */
  display: flex;
  flex-direction: column;
  justify-content: safe center;
}

/* 歌词当前行：DESIGN.md 05 说"放大 4% + 一条朱砂短线"。
   之前被改成放大 14% 并染成 #ff8172（一个不在色板里的珊瑚色），
   既盖掉了"墨顺着句子染过去"的 --p 填色，也把朱砂用滥了。
   下面这几条把层次还给歌词自己（暗的过去行 / 亮的当前行），
   并压掉 stage.css 里 4 轮 !important 的歌词配色。
   亮度比之前各抬一档：在近黑的底上，0.38 的白读起来是一片灰雾。

   ⚠ 2026-10-10 更正（这次改版一路追下来才看清，记在这里免得下次又改错地方）：
   **歌词的颜色在本项目是三层接力，下面这几行只是第一层，实际生效的是最后那层。**
     ① 本层（版式层，2 个类）：0.48 / 0.56 —— 只在"没有主题层"时成立；
     ② web/css/theme-yf-font.css（主题层，最后加载，3~4 个类）：
        非当前句 --yf-t3（0.68 白）、当前句 --yf-accent-text（#f0837a）—— 压过 ①；
     ③ 本文件末尾 `#stageLyricsPane .lyric`（**ID 选择器**，2026-10-07 为
        「非当前行也要读得清」而写）：普通行 0.74、邻句 0.86、署名 0.82 —— 压过 ②。
   所以：**想改歌词明暗，改 ③（那一节有完整来龙去脉），别改这里。**
   （我这一轮曾在 ① 把 0.48 抬到 0.58 —— 实测证明是死代码，已改回原值。） */
.stage__lyrics .lyric { position: relative; color: rgba(214, 224, 232, 0.48) !important; }
.stage__lyrics .lyric.is-current { color: var(--paper) !important; }
.stage__lyrics .lyric.is-near { color: rgba(242, 245, 248, 0.56) !important; }
.stage__lyrics .lyric--plain { color: var(--paper-dim) !important; }
.stage__lyrics .lyric--idle { color: var(--paper-ghost) !important; }
.stage__lyrics .lyric--credit { color: rgba(147, 160, 173, 0.6) !important; }

/* ── 歌词的特效：正在唱的这一句背后有一圈光 ──────────────────────
   整页只有唱片是"物件"，所以歌词这边不该再出现第二个物体——
   让当前句自己被光照到就够了：一圈暖光（酥油灯的色温）+ 一点河水青，
   亮度随 --p（这一句唱了几成）从 0.4 走到 1，唱满时最亮。
   它跟着行高走，不需要 JS 参与——发光层挂在行上，行滚到哪儿它到哪儿。 */
.stage__lyrics .lyric.is-current::after {
  content: "";
  position: absolute;
  /* 光只铺到句子的长度附近，不铺满整栏（铺满就成了一条横带，读起来像表格高亮） */
  inset: -0.45em 24% -0.45em -1.5em;
  z-index: -1;
  border-radius: 40px;
  background:
    radial-gradient(62% 130% at 14% 50%, rgba(255, 206, 156, 0.22), rgba(255, 206, 156, 0) 74%),
    radial-gradient(86% 150% at 38% 50%, rgba(57, 211, 196, 0.1), rgba(57, 211, 196, 0) 76%);
  opacity: calc(0.45 + var(--p, 0) * 0.55);
  pointer-events: none;
}
/* 唱过的那一截更亮：把 --p 的对比拉开一点，让"墨顺句子染过去"这件事看得见 */
.stage__lyrics .lyric.is-current { text-shadow: 0 0 26px rgba(242, 245, 248, 0.3); }

/* ───────────────────────────────────────────────────────────── 底轴 */

.stage__bottom {
  display: grid;
  /* 时间贴在进度条两端、控制键靠右 —— 一整行仪表，不再是四条各自为政的边 */
  grid-template-columns: auto minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 18px;
  padding: 16px 0 26px;
}
.stage__time {
  font-family: var(--font-mono);
  font-size: var(--fs-tiny);
  font-variant-numeric: tabular-nums;
  color: var(--paper-faint);
}
.stage__time:last-of-type { text-align: right; }
.track--stage { height: 20px; }
.track--stage .track__rail { height: 3px; background: rgba(242, 245, 248, 0.14); border-radius: 2px; }
/* 进度是"正在发生"，归河水青（DESIGN.md 03）；原实现用了朱砂，
   于是朱砂同时表示"播放键 / 喜欢 / 进度"，一个颜色说三件事就不响了。 */
.track--stage .track__fill { background: linear-gradient(90deg, var(--river-deep), var(--river)); border-radius: 2px; }
.track--stage .track__thumb {
  width: 11px;
  height: 11px;
  background: var(--paper);
  box-shadow: 0 0 0 1px var(--river), 0 2px 8px rgba(0, 0, 0, 0.6);
}
.stage__ctrl { display: flex; align-items: center; gap: 10px; }
.stage__ctrl .iconbtn { width: 36px; height: 36px; color: var(--paper-ghost); }
.stage__ctrl .iconbtn:hover { color: var(--paper); }
.stage__ctrl .iconbtn.is-on { color: var(--river); }
/* 播放键：这一页唯一的实心色块，也是唯一的"动词" */
.playbtn--lg {
  width: 54px;
  height: 54px;
  margin-inline: 4px;
  background: radial-gradient(120% 120% at 34% 26%, var(--seal-bright), var(--seal) 62%, var(--seal-deep));
  color: var(--paper);
  box-shadow: 0 10px 28px -10px rgba(190, 50, 39, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.22);
}
.playbtn--lg:hover { transform: scale(1.04); }
.playbtn--lg .ico { width: 23px; height: 23px; }
.playbtn--lg.is-buffering::after { border-color: rgba(4, 7, 15, 0.35); border-top-color: var(--paper); }

/* ───────────────────────────────────────────────── 关掉歌词时的版式 */

/* 状态类必须写在断点之前：它只有一个用途 —— 在**宽屏**上把第三栏（歌词）去掉，
   让盘与信息居中。窄屏本来就没有第三栏，这里不能再去指定列数。
   踩过的坑（2026-09-18，回归脚本抓到的）：`.stage__body.is-lyric-off` 是两个类（0,2,0），
   比 720 断点里那条一栏规则 `.stage__body`（0,1,0）权重高 —— 于是在 430px 下，
   点过一次歌词开关之后，版式被掰回"盘 + 信息"两栏：画心被切掉 144px、
   信息栏右溢出 100px、标题经幡最后两面被压掉。回归里就是这么红的。
   修法：列数只由断点决定，状态类不再碰 `grid-template-columns`。 */
.stage__body.is-lyric-off {
  column-gap: clamp(40px, 6vw, 120px);
  justify-content: center;
  max-width: none !important;
  margin: 0 !important;
}
/* 只在"本来就该有三栏"的宽度上才收掉第三栏 */
@media (min-width: 1181px) {
  .stage__body.is-lyric-off { grid-template-columns: auto minmax(240px, 380px); }
}
.stage__body.is-lyric-off .stage__lyrics-wrap { display: none !important; }

/* ───────────────────────────────────────────────────────────── 窄屏 */

/* 1180 以下：盘与信息并排，歌词整行落到下面（给足高度，别只剩四五句） */
@media (max-width: 1180px) {
  .stage__body {
    grid-template-columns: auto minmax(0, 1fr);
    /* 两行都按内容高度：不这样写的话，基础规则里那条 minmax(0,1fr)
       会把第一行压成"剩下的空间"，盘面（高于那一行）就压到歌词栏上——
       实测 1024×768 下盘面 460px 压进歌词栏，两者重叠。 */
    grid-template-rows: auto auto;
    column-gap: clamp(24px, 4vw, 44px);
    row-gap: 22px;
    /* 顶对齐而不是居中：内容比可视区高时（1024×768 就会），
       居中会把溢出分到上下两边，而上边那一截是滚不到的——
       实测盘面顶部被切掉 1px 且无法滚回。 */
    align-items: start;
    align-content: start;
    overflow-y: auto;
    padding-bottom: 16px;
  }
  .stage__lyrics-wrap {
    grid-column: 1 / -1;
    min-height: 0;
    height: min(46vh, 420px);
    padding-left: 0;
    padding-top: 16px;
    border-left: 0 !important;
    border-top: 1px solid var(--line) !important;
  }
  .stage__lyrics { padding: 12px 12px 12px 0; }
}

/* 720 以下：一列，全部居中，控制键换到独立一行 */
@media (max-width: 720px) {
  /* 页首折成两行：第一行 收起（只留箭头）+ 署名，第二行让给参数读数。
     "收起/墨竹印象/普琼"在窄屏上曾经各折成两行，整条页首读不成样子。 */
  .stage__top { flex-wrap: wrap; row-gap: 6px; }
  .stage__close { padding: 6px; }
  .stage__close span { display: none; }
  .stage__top-rule { display: none; }
  .stage__quality { flex: 1 1 100%; text-align: left; }
  .stage__body {
    grid-template-columns: minmax(0, 1fr);
    justify-items: center;
    text-align: center;
  }
  .stage__info { align-items: center; text-align: center; }
  .stage__artist { align-self: center; justify-content: center; }
  .stage__specs,
  .stage__actions { justify-content: center; }
  .stage__title { font-size: 28px; }
  .stage__lyrics-wrap { height: 38vh; padding-top: 14px; }
  .stage__lyrics { padding-left: 0; }
  .stage__lyrics-head { justify-content: center; }
  .stage__lyrics-label { display: none; }
  .stage__bottom {
    grid-template-columns: auto minmax(0, 1fr) auto;
    gap: 12px;
    padding: 12px 0 18px;
  }
  .stage__ctrl { grid-column: 1 / -1; justify-content: center; }
}


/* ═══════════ 唱片页那层"近黑"：按用户要求去除 ═══════════

   原话："黑色都去除"、"破坏背景了"。指的是页面底那层不透光的近黑（#1a1820 + 一层暗角）——
   它把画心压成"嵌在一块黑框里"，而且暗角又把边缘再压回近黑一圈。
   现在底色提到**与画心天空同一族的深墨蓝**（#0d1526）：画心不再是黑框，
   页面与画连成同一片夜色；暗角整层撤掉。
   （背景原值写在 stage.css 的"唱片页：抬亮 + 去压黑"一节，这里只做覆盖。） */
.stage { background: #0d1526 !important; }
.stage::after { background: none !important; }

/* 尊重系统的"减少动态效果"：关掉微风，保留投影 —— 这不是可选项，
   前庭功能敏感的用户开着这个开关时，画面的持续起伏会让他们不适。 */
@media (prefers-reduced-motion: reduce) {
  .deck__figure { filter: drop-shadow(0 18px 44px rgba(0, 0, 0, 0.5)); }   /* 动画是素材，不用降级 */
}


/* ═══════════ 横排歌词（单行逐句）═══════════

   用户："歌词横着会怎么样" → "做"。默认横排：画面下方一行，只显示当前唱到的那句。
   位置：贴在底栏之上（底栏高约 100px），宽度给到 72vw 上限 900px，
   字号 clamp(20–30px) —— 这是"远处也读得清"的量级，也是竖排 15px 先天做不到的。
   「整篇」切回竖排那一栏时，这条自动让位（见下面的 is-lyric-line 开关）。 */
.stage__lyric-line {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  bottom: clamp(104px, 13vh, 150px);
  width: min(72vw, 900px);
  text-align: center;
  pointer-events: none;
  z-index: 3;
  font-family: var(--font-cn-ui);
  font-size: clamp(20px, 2.1vw, 30px);
  line-height: 1.5;
  letter-spacing: 0.02em;
  color: var(--paper-dim);
  text-wrap: balance;
}
.stage__lyric-line span { display: inline-block; transition: opacity 0.22s ease; }

/* 横排模式下：竖排那一栏收起，三栏相应收成两栏（否则右侧会空出一整栏） */
.stage.is-lyric-line .stage__lyrics-wrap { display: none !important; }
@media (min-width: 1181px) {
  .stage.is-lyric-line .stage__body {
    grid-template-columns: auto minmax(240px, 380px);
    justify-content: center;
  }
}


/* 歌词右移（用户："帮我一动(移)一下旁边"，附一个向右箭头）。
   ⚠ 必须写在文件末尾：前面 `.stage__lyrics` 里有一条 `padding:` 简写，
   同选择器下它会把我写在前面的 `padding-left` 复位 —— 第一次就是这么失效的（实测左缘没动）。 */
.stage__lyrics { padding-left: var(--sp-7); }   /* 24 → 48：24px 用户看不出变化，加大到肉眼可分 */


/* ═══════════ 歌词字体：跟参考图一致 ═══════════

   用户给了参考图（"AI 智能体 / 定制搭建与调写"）：等线、笔画匀细、字距略开的几何无衬线。
   做法就是**和大标题用同一副字**（Avenir Next → Helvetica Neue → 苹方），
   于是整页只有一种字体气质：标题与歌词是同一副嗓子，只是大小不同。

   三条必须照顾到的：
     1. **藏文歌词要让开** —— `.stage__lyrics.is-tibetan .lyric` 用的是藏文字体（在前面的规则里），
        这条写在末尾，不显式排除就会把藏文盖掉；
     2. **当前唱到的那句要保住 500 字重** —— 它靠字重加粗做强调（`color: #e0574a; font-weight: 500`），
        我如果用 300 压过去，强调句反而比别的行还细 —— 所以只对"非当前句"设 300；
     3. **细体在小字号上会显薄**：所以取 300 而不是标题的 200，字距也只开到 0.04em（再开就散了）。 */
.stage__lyrics:not(.is-tibetan) .lyric,
.stage__lyrics:not(.is-tibetan) .lyric--idle,
.stage__lyrics:not(.is-tibetan) .lyric--plain {
  font-family: "Avenir Next", "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", var(--font-cn-ui);
  letter-spacing: 0.04em;
}
.stage__lyrics:not(.is-tibetan) .lyric:not(.is-current) { font-weight: 300; }

/* 当前唱到那一句的**字重**也要显式压回来：stage.css 里有两条都作用于 `.lyric.is-current` 的
   font-weight（后者 600 赢了前者 500），所以"唱到的那句"原本是半粗 —— 与参考图的"细"相反。
   这里用更高权重（四个类）把它定成 500：强调靠朱砂色，不靠加粗。 */
/* ⚠ 必须带 !important：stage.css 里那条把当前句设成 600 的规则用了 !important
   （实测：我这条四个类、无 !important 的规则压不过它）。同强度 + 更靠后 → 这条赢。 */
.stage__lyrics:not(.is-tibetan) .lyric.is-current { font-weight: 500 !important; }

/* ═══════════ 微风封面（雪碧图 + JS 逐帧播放）—— ⚠ 当前未使用 ═══════════

   ⚠ **2026-09-22 起停用**：用户原话「动画去除」，起因是他发现**穿帮** ——
   逐帧位移是"把一块区域里的像素整体推走"，在这张**硬边平涂**的插画上会把图形撕出缺口
   （马的尾/后腿处少一块、袍摆边缘与雪面错位）。所以画心改回静态 <img>（见 index.html）。
   这条规则与上面的整套说明**留着是为了恢复**：把 index.html 里的 <img> 换回
   div#breezeFigure + 那段推进 background-position-x 的内联脚本即可，不必重写。

   起因：APP（WKWebView）里播放音乐时封面完全不动。同一份 animated WebP
   在 Chrome 里正常，说明素材没问题，是图片自带的动画解码在 WKWebView 不可靠。
   于是把 12 帧横排成一张雪碧图，用内联脚本推进 background-position-x
   （走普通重绘，与歌词刷新同一条路径，APP 里有效）。

   尺寸：.deck__figure 原本是 <img>，靠 height + object-fit: contain 定比例。
   换成 div 后没有固有比例，必须用 aspect-ratio 补上（单帧 720×428）。
   background-size 取 1200% —— 12 帧横向铺满，background-position-x
   按 i/11 推进即落在 0%…100%。两个数必须和 index.html 脚本里的 N 保持一致。

   2026-09-22 换素材：画心从「土路上的行者与狗」换成**雪原上的行者与驮马**
   （用户给的图 → tools/path-snow-source.png；经 tools/make-snow-cut.py 裁切、
   make-breeze.py 烘帧、make-breeze-sheet.py 拼图）。单帧由 720×362 变成 720×428，
   所以下面 aspect-ratio 跟着改了 —— **换素材后这两个数必须重新对齐**，否则整幅画会被压扁
   （或拉长）。旧素材（土路版）备份在 _temp/art-backup/。 */
.deck__figure--sheet {
  aspect-ratio: 720 / 428;
  width: auto;
  background-image: url("../assets/path-monk-breeze-sheet.webp?v=2");
  background-repeat: no-repeat;
  background-size: 1200% 100%;
  background-position: 0 0;
}


/* ══════════════════════════════════════════════════════════════════
   氛围底（2026-10-05 普琼要求：舞台页"背景要饱满、高端大气"）

   诊断：原来 .stage 只有一层近黑渐变 + 两层 10% 透明度的色晕 —— 在这个亮度下
   10% 几乎看不见，整页读起来就是"一块黑板"。现在改成：**用当前曲目的封面当光源**
   （虚化、去饱和、压暗），罩一层冷色 + 暗角把字压住，最后加一层极淡颗粒。

   三层各司其职：ambient=颜色与光，::after=对比与暗角，grain=质感。
   颗粒是"高级感"的关键：纯色底再干净也有塑料感，噪点让它像纸/胶片。
   ══════════════════════════════════════════════════════════════════ */
.stage { isolation: isolate; }

.stage__wash { position: absolute; inset: 0; z-index: 0; overflow: hidden; pointer-events: none; }

.stage__ambient { position: absolute; inset: -8%; display: block; }

.stage__ambient-layer {
  position: absolute;
  inset: 0;
  display: block;
  background-position: center;
  background-size: cover;
  /* 虚化半径要盖住放大带来的边缘，所以同时 scale 放大把毛边推出画面 */
  filter: blur(64px) saturate(0.52) brightness(0.46) contrast(1.08);
  transform: scale(1.22);
  opacity: 0;
  transition: opacity 1.1s ease;
}
.stage__ambient-layer.is-on { opacity: 0.9; }

/* 冷色罩 + 暗角：不是为了好看，是为了让白字在任何封面上都压得住 */
.stage__wash::after {
  content: '';
  position: absolute;
  inset: 0;
  background:
    radial-gradient(120% 88% at 26% 26%, rgba(201, 168, 106, 0.10), transparent 58%),
    radial-gradient(140% 130% at 50% 52%, transparent 26%, rgba(3, 6, 12, 0.86) 100%),
    linear-gradient(180deg, rgba(5, 9, 18, 0.58), rgba(3, 5, 11, 0.8));
}

/* 颗粒：inline SVG 噪点，不引外部图、不占请求 */
.stage__grain {
  position: absolute;
  inset: 0;
  display: block;
  opacity: 0.055;
  mix-blend-mode: overlay;
  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.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 200px 200px;
}

/* 引首/画心/底轴必须在氛围底之上 */
.stage__top, .stage__body, .stage__bottom { position: relative; z-index: 2; }

@media (prefers-reduced-motion: reduce) { .stage__ambient-layer { transition: none; } }

/* ── 无歌词时的「接下来」队列（把原来空着的右半边填成有用的内容）── */
.stage__lyrics .upnext { display: flex; flex-direction: column; gap: 2px; width: 100%; }

.upnext__head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  padding-bottom: 10px; margin-bottom: 6px;
  border-bottom: 1px solid rgba(242, 245, 248, 0.08);
}
.upnext__title { font-size: var(--fs-small); letter-spacing: 0.02em; }
.upnext__count, .upnext__no, .upnext__who, .upnext__when { color: var(--paper-faint); font-size: var(--fs-tiny); }

.upnext__row {
  display: grid; grid-template-columns: 22px minmax(120px, 1fr) auto 44px;
  align-items: center; gap: 10px; padding: 9px 8px;
  border: 0; border-radius: 8px; background: none; color: inherit; font: inherit;
  text-align: left; cursor: pointer; transition: background 0.15s ease;
}
.upnext__row:hover { background: rgba(242, 245, 248, 0.05); }
.upnext__row:focus-visible { outline: 2px solid var(--seal); outline-offset: -2px; }
.upnext__no, .upnext__when { font-variant-numeric: tabular-nums; }
.upnext__name { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.upnext__song { font-size: var(--fs-small); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.upnext__who { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.upnext__foot {
  margin-top: 12px; padding-top: 10px;
  border-top: 1px solid rgba(242, 245, 248, 0.08);
  font-size: var(--fs-tiny); color: var(--paper-faint); line-height: 1.6;
}


/* ══════════════════════════════════════════════════════════════════
   构图收口（2026-10-05 第二轮，普琼："背景看怎么设计饱满"）

   实测（1806×1000 视口）：
     · 画心区 1500×841，但插画只有 440×440、信息栏只有 254px 高，
       两个都居中 → 页面上下各空 200px，中栏空得最多（只占 30% 高度）。
     · 歌词栏 539×827 本来就占满，真正要补的是**竖向**。

   所以三件事：① 插画放大（把它当主角，而不是缩在中间）；
   ② 中栏改成铺满整条带，底部挂「接下来」；③ 氛围底提亮并给左三分之一加暖光。
   ══════════════════════════════════════════════════════════════════ */

/* ① 插画：440 → 560（上限放宽到 620）
   2026-10-10（普琼："整体放大……更饱满"）：上限 620 → 680，vh 系数 56 → 58。
   ⚠ 这一条是**最终生效值**（写在文件后段，覆盖第 37 行那条基础声明），
   改盘面大小请改这里；第 37 行那条留着是它"在同一个选择器里先声明一次"的历史。 */
.stage { --disc-size: clamp(320px, min(58vh, 88vw), 680px); }

/* ② 中栏铺满：标题块靠上、接下来靠下，中间用留白撑开 */
.stage__info {
  align-self: stretch;
  display: flex;
  flex-direction: column;
  justify-content: center;
  height: 100%;
}
.stage__info .stage__upnext {
  margin-top: auto;
  padding-top: clamp(14px, 3vh, 34px);
}
.stage__upnext[hidden] { display: none; }

/* 中栏的「接下来」做成紧凑版：只要 3 首、字号小一档、不要头部的计数 */
.stage__upnext .upnext { gap: 0; }
.stage__upnext .upnext__head { padding-bottom: 8px; margin-bottom: 2px; }
.stage__upnext .upnext__row { padding: 7px 6px; grid-template-columns: 20px minmax(90px, 1fr) auto; }
.stage__upnext .upnext__when + .upnext__when { display: none; }
.stage__upnext .upnext__foot { display: none; }

/* ③ 氛围底提亮：原来 brightness 0.46 太暗，颜色几乎读不出来 */
.stage__ambient-layer { filter: blur(64px) saturate(0.58) brightness(0.6) contrast(1.06); }
.stage__wash::after {
  background:
    /* 左三分之一的暖光：给插画一个"被光罩住"的落点，左半页不再是死黑 */
    radial-gradient(58% 62% at 20% 46%, rgba(201, 168, 106, 0.14), transparent 66%),
    radial-gradient(120% 88% at 72% 22%, rgba(96, 132, 186, 0.10), transparent 60%),
    radial-gradient(140% 130% at 50% 52%, transparent 30%, rgba(3, 6, 12, 0.82) 100%),
    linear-gradient(180deg, rgba(5, 9, 18, 0.46), rgba(3, 5, 11, 0.72));
}


/* ── 第三轮微调（2026-10-05）：看图后修两处 ──
   ① 中栏：上一版用 margin-top:auto 把「接下来」顶到最底部 → 标题与队列之间裂开一大段空。
      改成"整摞居中 + 区间固定的间距"，两块内容成一个整体。
   ② 氛围底：封面本身偏暗，叠加层又重，颜色仍读不出来 —— 提亮并压薄叠加层，
      但保留暗角（白字可读性靠它，不能一起撤）。 */
.stage__info { justify-content: center; }
.stage__info .stage__upnext { margin-top: clamp(26px, 7vh, 72px); }

.stage__ambient-layer { filter: blur(64px) saturate(0.62) brightness(0.78) contrast(1.04); }
.stage__ambient-layer.is-on { opacity: 0.96; }
.stage__wash::after {
  background:
    radial-gradient(58% 62% at 18% 48%, rgba(201, 168, 106, 0.16), transparent 68%),
    radial-gradient(120% 88% at 74% 20%, rgba(96, 132, 186, 0.12), transparent 62%),
    radial-gradient(150% 135% at 50% 52%, transparent 34%, rgba(3, 6, 12, 0.72) 100%),
    linear-gradient(180deg, rgba(5, 9, 18, 0.3), rgba(3, 5, 11, 0.56));
}

/* ══════════════════════════════════════════════════════════════════════════
   歌词工具条：把「看不见」改成「看得见」（2026-10-07 用户反馈「显示出来」）

   现状：这几个按钮（跟随 / 整篇 / 回到当前 / 早 0.5s / 晚 0.5s）平时用的是最暗的
   --paper-faint，而且 app.css 里那条 `.chipbtn { color: rgba(236,245,250,.85) !important }`
   又把它们统一压成一档半透明白 —— 结果一排在深色底上几乎看不清，用户圈出来要求提亮。

   做法：**保持原来无边框、靠字重与底色区分的设计**，只做三件事
     1. 静息态提到 --paper-dim（不悬停也读得清）
     2. 悬停/选中态用 --paper，并补一层极淡底色，让"能点"这件事看得出来
     3. 选中态（is-on）用朱砂色底线 + 一点朱砂底，和"未选中"区分开
   因为 app.css 那条是 !important，这里也必须 !important 才能生效。
   ══════════════════════════════════════════════════════════════════════════ */
.lyrics-tools .chipbtn {
  color: var(--paper-dim) !important;
  /* 字号从 --fs-micro（11px）提到 12.5px、内边距加宽：对比度够不代表"看得见"——
     11px 的白字在深底上依旧显得虚，用户圈出来正因为此。 */
  font-size: 12.5px !important;
  padding: 5px 11px !important;
  letter-spacing: 0.12em !important;
  border-bottom: 1px solid transparent;
  border-radius: 7px 7px 0 0;
  transition: color 0.16s ease, background 0.16s ease, border-color 0.16s ease;
}
.lyrics-tools .chipbtn:hover {
  color: var(--paper) !important;
  background: rgba(236, 245, 250, 0.08) !important;
}
.lyrics-tools .chipbtn.is-on {
  color: var(--paper) !important;
  background: rgba(190, 50, 39, 0.18) !important;
  border-bottom-color: var(--seal) !important;
}

/* ══════════════════════════════════════════════════════════════════════════
   歌词正文：非当前行也必须读得清（2026-10-07 用户「显示出来」/「还是有问题」）

   上一轮我修错了对象（改的是工具条），用户指的是**歌词正文**：
   除当前句外其余行是 rgba(214,224,232,0.48) —— 48% 的白，合成到近黑底上
   对比度只有 3.97；署名行（作词/作曲）更只有 3.22，看着就是一片灰雾。

   ⚠ 为什么必须用 ID 选择器：theme-yf-font.css 最后加载，而且它用
   `.stage__lyrics:not(.is-tibetan) .lyric--plain` 这种写法把特异性抬到 3 个类，
   我上一版写的 `.stage__lyrics .lyric`（2 个类）**根本压不过它**（实测仍是 0.68 的白）。
   一个 ID 的选择器胜过任何纯类选择器，所以这里一律以 #stageLyricsPane 起头。

   层次仍然保留：当前句（主题的珊瑚色）> 邻句 0.86 > 普通行 0.74 > 署名 0.82 压暗一档。
   ══════════════════════════════════════════════════════════════════════════ */
#stageLyricsPane .lyric {
  color: rgba(222, 232, 242, 0.74) !important;
}
#stageLyricsPane .lyric.is-near {
  color: rgba(240, 246, 252, 0.86) !important;
}
/* 当前句沿用主题的强调色（珊瑚/朱砂档），这里显式写回，避免被上面的基础规则盖掉 */
#stageLyricsPane .lyric.is-current {
  color: var(--yf-accent-text, var(--paper)) !important;
}
#stageLyricsPane .lyric--credit {
  color: rgba(186, 198, 212, 0.82) !important;
}
#stageLyricsPane .lyric--idle,
#stageLyricsPane .lyric--plain {
  color: var(--paper-dim) !important;
}

/* ══════════════════════════════════════════════════════════════════════════
   藏语歌词里，中文行也必须用可可字体（2026-10-07 用户「歌词保留可可字体」）

   病因：stage.css 里 `.stage__lyrics.is-tibetan .lyric { font-family: var(--font-tibetan) }`
   —— 只要歌词里出现藏文，**整栏**就被换成藏文字体栈（Kailasa/Noto Serif Tibetan…）。
   而那个栈里没有中文字形，于是双语歌词里的**中文翻译行掉到 Songti SC（宋体）**，
   可可字体就这么丢了。实测（CDP getPlatformFontsForNode）：中文行渲染成 Songti SC ✗

   做法：改成可可字体栈 —— 它的顺序本来就是「可可 → 藏文 → 中文兜底」，
   所以中文字形命中可可，藏文字形可可里没有、自然落到藏文字体，两边都对。
   （--yf-keke-stack: "白无常可可体", "BWCKKT", var(--font-tibetan), "PingFang SC", sans-serif）
   ══════════════════════════════════════════════════════════════════════════ */
#stageLyricsPane .lyric,
#stageLyricsPane .lyric--tibetan,
#stageLyricsPane.is-tibetan .lyric,
#stageLyricsPane.is-tibetan .lyric--tibetan {
  font-family: var(--yf-keke-stack) !important;
}

/* ══════════════════════════════════════════════════════════════════════════
   舞台暖光挪位置、变淡（2026-10-07 用户「底色出现色差」）

   实测（在用户截图上按像素取色，基准是舞台底色 rgb(13,21,38)）：
     「喜欢」按钮内      (168,51,41)  朱砂实心，设计如此
     「歌词」按钮内      (20,21,38) → 红通道 +7
     「歌词」按钮内右侧  (30,23,38) → 红通道 +17   ← 用户圈的就是这块
     「文件信息」按钮内  (14,21,38) → 红通道 +1（正常）

   原因不是 bug，是上面 .stage 那条氛围渐变里的第二层：
     radial-gradient(46% 42% at 76% 74%, rgba(190,50,39,.09), transparent 60%)
   它的中心（x 76% / y 74%）正好落在播放按钮那一排上，于是「歌词」按钮周围
   比别处红一档，看上去像"底色有色差"。

   做法：**保留这口气**（DESIGN.md 02：河水青与朱砂各留一口气），但
     · 中心从 76%/74% 挪到 88%/86%（移到右下角，离开按钮区）
     · 半径放大到 64%/56%、淡出拉到 78%（过渡更长，不会有可见的边）
     · 透明度 .09 → .055（更淡，只当环境光）
   ══════════════════════════════════════════════════════════════════════════ */
/* ⚠ 这是**实际生效**的 .stage 背景（同样的选择器、写在最后 → 压过文件开头那条）。
   2026-10-10 加第一层："顶端天光"。起因是视觉复核时反复指出"最空的是页面上半部"
   （插画与歌词之上那片）。它是纯渐变——不是面板、不是卡片，
   符合 DESIGN.md 05「其余全是墨底上的字」；没有它，上半屏读起来是"没排完"。

   下面两层不动（第二层的位置是 2026-10-05 为修"歌词按钮旁边发红"专门挪到 88%/86% 的，
   别挪回去）。 */
.stage {
  background:
    radial-gradient(120% 46% at 50% -6%, rgba(57, 211, 196, 0.11), transparent 66%),
    radial-gradient(58% 46% at 30% 26%, rgba(57, 211, 196, 0.13), transparent 62%),
    radial-gradient(64% 56% at 88% 86%, rgba(190, 50, 39, 0.065), transparent 78%),
    linear-gradient(165deg, #0a1020 0%, #060c1b 54%, #04070f 100%) !important;
}
/* ── 层级：正在唱的那句比其余句大 1.14 倍（2026-10-10）──────────────
   普琼："大小很单一"。实测全部文字挤在 14.6~20.2px 之间，只有曲名 54.8px 一个大数，
   中间没有过渡档 —— 所以读起来"平"。这一条补上"正在唱"的尺寸档：
   尺寸 + 亮度 + 后面那圈光三层一起上，其余行保持安静。
   ⚠ 必须写在这个文件的末尾（ID 选择器），否则压不过 theme-yf-font.css 里的主题层。 */
#stageLyricsPane .lyric.is-current { font-size: calc(var(--fs-lead) * 1.14) !important; }

/* ── 手机（窄屏）：盘面按屏宽收敛，黑胶必须留在屏内（2026-10-12）─────────
   问题：上面第 1069 行那条最终生效值用的是 `88vw`，而**黑胶 `.deck__disc`
   是盘面的 1.24 倍**。在 390px 手机上实测：
     盘面 --disc-size = 88vw = 343px，黑胶 = 343 × 1.24 = 426px > 屏宽 390px
   → 黑胶左右各被裁掉 18px，整页读起来"盘面大得离谱、塞不进屏幕"。
   （这正是当初那句"盘面尺寸被 vh 撑着"的真因：不是 vh，而是 vw 系数没算上 1.24 倍。）

   修法：按"黑胶 ≤ 92vw"反推盘面上限 → 盘面 ≤ 74vw（74 × 1.24 ≈ 92vw）。
   下限同时从 320px 降到 240px —— 否则 360px 这类窄机会被下限顶回 320px，
   黑胶 397px 依旧溢出，等于没修。
   vh 系数 58 → 52：竖屏手机上高度本来宽裕，压低一点把竖向空间还给标题与按钮。

   ⚠ 桌面**完全不受影响** —— 整条规则在 max-width:720px 内，
     1440px 下 --disc-size 仍是 58vh = 522px（实测值），与你之前要求的
     "整体放大、更饱满"一致。手机上想再大/再小，只改这一行的 74vw。 */
@media (max-width: 720px) {
  .stage { --disc-size: clamp(240px, min(52vh, 74vw), 460px); }

  /* ── 插画：这才是手机上真正占满视线的那块（2026-10-12 第二次修正）─────────
     ⚠ 上一轮只改了 --disc-size（盘面/黑胶），结果用户在手机上"看不出任何区别" ——
       因为肉眼看到的插画根本不是盘面，而是 .deck__figure：
       它**只用高度定尺寸**（height: min(34vh, 310px)），宽度交给 720/428 的固有比例算
       （见第 274 行的说明）。390px 手机上：34vh = 287px → 宽 = 287 × 1.682 = 483px，
       比屏幕还宽 93px，左右各被裁掉约 46px —— 实测 rect = 483×287@-51。
     基准不是拍脑袋来的，而是量自桌面 1960×717 的实拍：
     桌面插画 411px 正好落进第一栏 416px 里（99%）—— **桌面端插画从不超出它所在的栏**。
     而手机端 54vw 时插画 355px > 单栏 326px（109%），这正是"手机与电脑不统一"的
     唯一可量化差异（结构、字号档、按钮尺寸三点量下来本来就是一致的）。
     按同一种关系反推：49vw × (720/428) = 82.4vw ≤ 手机单栏（390px 下 326px = 83.6vw），
     插画于是与桌面一样"装在栏内"，不再溢出。
     想让插画更大/更小，只改这一个 49vw。 */
  .deck__figure { height: min(34vh, 310px, 49vw); }

  /* ── 画心区可滚动：让被挤到屏幕外的歌词能滚上来（2026-10-12 第三次修正）──
     ⚠ 这是"手机上歌词一个字都看不到"的真正原因，与尺寸无关：
       440×856 实测——画心区(.stage__body) 只有 **649px**（y 83→732），
       而里面装着 插画 216 + 标题块 268 + 歌词带 321 + 间隙 ≈ **861px**，
       超出 212px；歌词带 y 721→1046 直接压到底部播放条(.stage__bottom y 732→856)底下，
       而页面不可滚动 → 6 行歌词**一行都读不到**，只看到歌词带的头部控件
       （跟随/整篇/早0.5s/晚0.5s —— 用户截图里正是这一行）。
     实测过：只压字号/压插画都无效（缩掉的空间会被栅格吃掉，歌词带仍从 y≈707 起），
     要"一屏硬塞"必须改栅格行定义，风险大。这里只放开滚动，
     **不改任何尺寸与排列**；实测滚到底后 5/6 行歌词完整落在播放条之上。 */
  .stage__body { overflow-y: auto; -webkit-overflow-scrolling: touch; }
}

/* ── 舞台页避让手机安全区（刘海 / 灵动岛）（2026-10-12）──────────────
   实测（用户 iPhone 截图，430 CSS 视口）：唱片页的内容从 **y=0** 就开始，
   顶栏「墨竹印象 · 普琼」与下面那行 FLAC 参数被系统状态栏（时间/5G/电量）
   直接压住；iPhone 顶部安全区约 59px，这段高度整段被吃掉了。

   项目里其它页面早就处理过这件事 —— app.css 里
     `.rail  { padding-top: max(env(safe-area-inset-top), 12px) }`
     `.dock  { padding-bottom: max(env(safe-area-inset-bottom), 6px) }`
   只有这个全屏的舞台页漏了，现在按同一套写法补上。

   ⚠ env() 在桌面浏览器与无刘海设备上恒为 **0**，所以桌面端像素不变；
     这条不加媒体查询也是这个原因（加了反而会在横屏/平板上漏掉）。 */
.stage__top { padding-top: calc(18px + env(safe-area-inset-top, 0px)); }
