/* ============================================================
   band.css — band.dreamanual.com 页面样式
   ------------------------------------------------------------
   令牌层：tokens.css（与 my-app 同一套 --dt-* 令牌）

   🔴 分层铁律（2026-09-24 立，用户裁定「不要在有先例的情况下自创」）：
   §1 共享件 —— 声明值**逐字抄自** my-app 的 shell.css / login.css，
      每条都标了出处（文件:行号）。要改值先改 my-app 那一份、两站同批改；
      只改这里 = 两站开始漂移。逐条对照表见 doc/BAND-UI-ALIGN.md，
      对拍门禁见 tools/check-shared.cjs。
   §2 band 私有件 —— 只有 band 才有的东西。私有件可以**引用**共享件的值，
      但不得改写共享件的声明。
   §3 响应式

   ⚠️ band 只引 tokens.css、不引 my-app 的 shell.css（那是外壳，band 无外壳），
      所以本节 §1 是「同名同值的副本」。my-app 的 login.css 同理（它也不引 shell.css）。
   ============================================================ */

/* ============================================================
   §0 页面基底
   my-app 侧由 shell.css §2（带外壳的页）与 login.css §0（壳外页）各承担一份；
   band 无处承担，落地在此。四行 reset 逐字抄 shell.css:83-87。
   ============================================================ */
* { box-sizing: border-box; }
button, input, select, textarea { font-family: inherit; font-size: inherit; }
a { color: inherit; text-decoration: none; }
button { cursor: pointer; }

body {
  margin: 0;
  background: var(--dt-bg);
  color: var(--dt-text1);
  font-family: var(--font-base);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
}

/* ============================================================
   §0b band 私有令牌层（2026-09-24）
   ------------------------------------------------------------
   为什么单独一层：tokens.css 是 my-app 那份的**逐字节副本**，往里面加色会让两份分叉，
   而令牌层目前**没有门禁**（见 BAND-UI-ALIGN §8 第 1 条）。所以 band 自己的颜色落在这里。

   取值：Tailwind v4 的色阶。v4 换了色空间，官方文档给的是 oklch，
   下列 sRGB 值是用 Chrome 的 canvas 反解（fillStyle 落色 + getImageData）实换算出来的，
   不是手算或从 v3 的十六进制表抄的 —— v3 / v4 同名色**不是同一个色**
   （v3 blue-600 = #2563eb，v4 blue-600 = #155dfc）。

   🔴 语义纪律：每个令牌只背一个语义。`--band-accent` **只表示「正在播放的段落」**，
      `--band-req` **只表示「必填」**。别处一律仍走 `--dt-interact`（墨黑），
      否则这两个彩会很快渗到全站。 */
:root {
  --band-accent: #155dfc;   /* Tailwind v4 blue-600 —— 正在播放的段落（卡边框 + 段名） */
  --band-req:    #e7000b;   /* Tailwind v4 red-600  —— 必填标记（歌名后的 *） */

  /* 看谱页固定顶栏的高度。my-app 的 `--shell-topbar-*` 是外壳私有令牌（band 无外壳），
     故 band 自持一份。`-total` 把顶部安全区一起算进去，内容区留白与顶栏几何都取它，
     避免两处各算一遍。 */
  --band-topbar-h:     52px;
  --band-topbar-total: calc(var(--band-topbar-h) + env(safe-area-inset-top, 0px));

  /* 编辑页固定底栏（保存/取消/删除那一行）的高度。
     = `.btn` 的高 32 + 上下内距各 --sp-12。落成令牌是为了让「正文底部留白」与
     「栏体自身高度」出自**同一个来源** —— 两处各算一遍的话，哪天按钮档改高度，
     留白就会与栏体错位（`.page.edit-page` 的 padding-bottom 取它）。 */
  --band-editbar-h: calc(32px + var(--sp-12) * 2);
}

/* ============================================================
   §1 共享件（照抄 my-app，禁改值）
   ============================================================ */

/* ---- 卡片 · shell.css:373 ---- */
.card {
  padding: var(--pad-card-md) var(--pad-x-md);
  background: var(--dt-raised);
  border: 1px solid var(--dt-card-border);
  border-radius: var(--radius-lg);
}

/* ---- 表单控件 · shell.css:728-751 ----
   ⚠️ `.form-select` 不再单设：my-app 侧下拉就是 `select.form-input`（shell.css:752，
   words.css:306 记着那次统一），band 无 select 宿主 ⇒ 该规则删除（原为死规则）。
   多行输入在 my-app 是 `class="form-input {模块}-ftextarea"`（dates/places/shelf 三处同构），
   band 对应件见 §2 的 `.edit-ftextarea`。 */
.form-label {
  display: block;
  margin-bottom: var(--sp-6);
  font-size: var(--fs-caption);
  font-weight: var(--fw-medium);
  color: var(--dt-text2);
}
/* 必填标记 · shell.css:813
   ⚠️ 本件是**登记过的值偏离**：my-app 的 `.form-req` 取 `--dt-amber-text`（琥珀），
      band 取私有令牌 `--band-req`（Tailwind v4 red-600 = #e7000b），用户点名要红色。
      琥珀在 band 已被「置顶」占住语义（`.pin-mark` 与 `.btn-icon.is-on` 都是琥珀），
      必填标记再用琥珀，同一页里会出现两个意思共用一个色。
      `font-style: normal` 与 my-app 逐字相同（`<i>` 默认斜体，这条不能丢）。
      登记见 tools/shared.manifest.json。 */
.form-req { color: var(--band-req); font-style: normal; }
/* 提示行（「这里该填什么」）。⚠️ margin 必须写满四值：`<p>` 的 UA margin-block-end:1em
   会回流成第二份组间距（my-app 第 25 批实锤：漏抄那次有提示行的组多 12px） */
.form-hint { font-size: var(--fs-caption); color: var(--dt-text3); margin: var(--sp-6) 0 0; line-height: var(--lh-prose); }

/* ---- info 气泡（band 私有件，形态照抄 dreamanual.com css/common.css 的 .gc-switch-* 族）----
   编辑页各卡标题后的长注释收进 info 图标（用户 2026-09-24：悬停/点击展开），
   常驻的 form-hint 让卡片喧宾夺主。令牌 --dt-tooltip-bg / --dt-text-inv / --dt-text5
   band tokens.css 本就带全（整份副本的顺手收益）。定位行为在 public/js/tooltip.js。 */
.info-tip {
  position: relative; display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; margin-left: var(--sp-4); vertical-align: -3px;
  flex-shrink: 0; cursor: help; color: var(--dt-text3);
}
.info-tip:hover, .info-tip:focus-visible { color: var(--dt-text2); }
.info-tip svg { display: block; width: 14px; height: 14px; }
.info-popup {
  position: fixed; left: 0; top: 0; transform: scale(0.96);
  background: var(--dt-tooltip-bg); border-radius: var(--radius-sm);
  padding: var(--sp-10) var(--sp-12);
  width: min(280px, calc(100vw - 32px));
  opacity: 0; pointer-events: none; transition: all 0.2s ease; z-index: 10000;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  display: flex; flex-direction: column; gap: var(--sp-4);
}
/* 默认向下展开，箭头在顶部朝上指向触发元素；.info-above 时向上翻、箭头朝下 */
.info-popup::after {
  content: ""; position: absolute; bottom: 100%; left: var(--info-arrow-left, 4px);
  border: 5px solid transparent; border-bottom-color: var(--dt-tooltip-bg);
}
.info-popup.info-above::after {
  bottom: auto; top: 100%;
  border-bottom-color: transparent; border-top-color: var(--dt-tooltip-bg);
}
.info-popup-title { font-size: var(--fs-md); line-height: 22px; font-weight: var(--fw-semibold); color: var(--dt-text-inv); }
.info-popup-text { font-size: var(--fs-sm); line-height: 18px; color: var(--dt-text5); white-space: normal; word-break: break-word; }
.form-input {
  width: 100%; height: 32px; box-sizing: border-box; min-width: 0;
  border: 1px solid var(--dt-border); border-radius: var(--radius-md);
  background: var(--dt-raised); padding: 0 var(--sp-10);
  font-size: var(--fs-input); font-family: inherit; color: var(--dt-text1); outline: none;
  transition: border-color 0.15s;
}
.form-input:focus { border-color: var(--dt-focus); background-color: var(--dt-raised); box-shadow: none; }
.form-input::placeholder { color: var(--dt-text5); }
.form-group { margin-bottom: var(--sp-12); }   /* 原写 --gap-card，解析后同值（--gap-card: var(--sp-12)）⇒ 按共享件原文写 */

/* ---- 按钮 · shell.css:655-720 ----
   🔴 基座两条与旧版不同（本次修正）：
     ① `border: 1px solid transparent`（旧写 --dt-border ⇒ 全站按钮都自带一圈描边，
        那是 my-app 里不存在的形状；带描边的只有 `.btn-secondary`）；
     ② 基座不设 background / color（旧写了 --dt-raised + --dt-text1 ⇒ 裸 `.btn` 也是白底描边块）。
   ⇒ 原来用裸 `.btn` 的地方一律改 `.btn-secondary`。 */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-6);
  height: 32px; padding: 0 var(--sp-12);
  border-radius: var(--radius-md);
  font-size: var(--fs-body); font-weight: var(--fw-medium); font-family: inherit;
  cursor: pointer; transition: all 0.15s;
  border: 1px solid transparent;
}
.btn svg { width: 14px; height: 14px; stroke: currentColor; }
/* 禁用态：中性化（--dt-inset 底 + --dt-text4 字 + 无描边），第 92 批定案。
   旧写 `opacity: .5` 保留色相 ⇒ 危险件禁用后仍是红的，且字对比度掉到 1.69:1 */
.btn:disabled {
  opacity: 1; cursor: not-allowed;
  background: var(--dt-inset); color: var(--dt-text4); border-color: transparent;
}
.btn-primary { background: var(--dt-interact); color: #fff; }
.btn-secondary { background: var(--dt-raised); border-color: var(--dt-border); color: var(--dt-text2); }
.btn-danger { background: var(--dt-error-bg); color: var(--dt-error); }
/* 三条 hover 一律带 `:not(:disabled)` —— 缺了它禁用钮悬停会翻成实色 */
.btn-primary:hover:not(:disabled) { background: var(--dt-interact-bg); }
.btn-secondary:hover:not(:disabled) { border-color: var(--dt-interact); color: var(--dt-interact); }
.btn-danger:hover:not(:disabled) { background: var(--dt-error); color: #fff; }
/* 两段式击发确认态（文字按钮）：首点变红底警示，超时未续点则复位 */
.btn.confirming {
  background: var(--dt-error);
  border-color: var(--dt-error);
  color: var(--dt-text-inv);
}
.btn.confirming:hover { background: var(--dt-error); color: var(--dt-text-inv); }

/* ---- 纯图标按钮 · shell.css:564-638 ----
   无边框、无底色、低对比静默色；hover 才浮出淡底。
   🔴 hover 守卫必须写 `:where(:not(:disabled))`，不能写裸 `:not(:disabled)`：
   `:not()` 的参数计入特异性，裸写会抬到 (0,4,0) 盖掉下面 `.danger` / `.armed` 的同权重平衡。 */
.btn-icon {
  width: 26px; height: 26px; flex-shrink: 0;
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0;
  border: none; background: none; border-radius: var(--radius-sm);
  color: var(--dt-text4); cursor: pointer;
  font-family: inherit;
  transition: color 0.15s, background 0.15s;
}
.btn-icon svg { width: 15px; height: 15px; stroke: currentColor; }
.btn-icon:hover:where(:not(:disabled)) { color: var(--dt-interact); background: var(--dt-interact-a06); }
.btn-icon:disabled { color: var(--dt-text5); cursor: default; }
/* band 裁定（2026-09-24，用户点名「删除图标比其他图标颜色还深，没有保持统一」）：
   行内操作区的三枚钮取**同一个静默档**。
   共享件 shell.css:599-603 让破坏性变体比邻钮深一档（--dt-text3 / #64748b），
   理由是 --dt-text4（#94a3b8）在白底只有 2.6:1、不达 WCAG 非文本 3:1。
   但同一栏里的置顶/编辑钮本就在 text4、对比度完全相同 —— 只把删除加深，
   等于让破坏性操作在同一行里抢视觉焦点，且并没有解决那条可达性理由。
   `.danger` 仍然保留：它承载的是**红悬停**（.danger:hover）与 `.armed` 击发态，
   不是静默色。删除钮的这两档与共享件仍逐字对齐。 */
.btn-icon.danger { color: var(--dt-text4); }
.btn-icon.danger:hover:where(:not(:disabled)) { color: var(--dt-error); background: var(--dt-error-bg); }
.btn-icon.is-lg { width: 32px; height: 32px; border-radius: var(--radius-md); }
.btn-icon.is-lg svg { width: 18px; height: 18px; }
/* 两步击发的图标钮确认态：图标换成文字、撑成红底白字胶囊。写在 :hover 之后，
   否则 armed 的实红底会被 hover 的浅底盖掉 */
.btn-icon.armed {
  width: auto; padding: 0 var(--sp-12);
  background: var(--dt-error); color: #fff;
  font-size: var(--fs-body); font-weight: var(--fw-medium);
}
.btn-icon.armed:hover { background: var(--dt-error); color: #fff; }

/* ---- 胶囊 chip · shell.css:1146-1155 ----
   ⚠️ 共享件**刻意没有 hover**（book.css:253 记着第 82 批的全站收敛口径：
   「全站其他页的 chip 都没有 hover 语言（共享 .chip 也没有）」）。
   旧版 band 自加的 `.chip:hover` 已按此删除。 */
.chip {
  display: inline-flex; align-items: center; gap: var(--sp-5);
  height: 30px; padding: 0 var(--sp-11);
  border: 1px solid var(--dt-border); border-radius: 999px;
  background: var(--dt-raised); color: var(--dt-text2);
  font-size: var(--fs-caption); font-weight: var(--fw-medium);
  cursor: pointer; transition: all .15s; user-select: none; font-family: inherit;
}
.chip svg { width: 13px; height: 13px; }
.chip-row { display: flex; flex-wrap: wrap; gap: var(--sp-6); }
/* 选中态（共享件刻意不含）：取 dates.css:209 `.chip.on` 逐字同款 —— 浅灰底 + 深框 + 深字。
   ⚠️ 旧版 band 用 `.chip.active` + 墨黑描边（`--dt-interact`），层级比 my-app 重一档、
   名字也与 my-app 的 `.on` 不同 ⇒ 本次改名改值。 */
.chip.on { background: var(--dt-interact-surf); border-color: var(--dt-text2); color: var(--dt-text1); }

/* ---- 筛选栏 · shell.css:503-552 ----
   宿主写法 = `<div class="card filter-bar">`（places.html:241 同款）。
   ⚠️ 两处刻意偏离，已在 doc/BAND-UI-ALIGN.md 登记：
     ① z-index：my-app 取 `var(--shell-z-sidebar)`，那是 shell.css 私有的层级梯子（band 无外壳）
        ⇒ band 取本页顶栏同档 10；
     ② `padding 10 12 设在 card 自身`：band 的 `.card` 默认 --pad-card-md/--pad-x-md，
        故此处必须显式覆写 padding（my-app 那句注释说的就是这个坑）。 */
.filter-bar {
  display: flex; flex-direction: column; gap: var(--sp-8);
  padding: var(--sp-10) var(--sp-12);
  position: sticky; top: 0; z-index: 10;
}
.filter-row { display: flex; align-items: center; gap: var(--sp-8); flex-wrap: wrap; }
.filter-sep { width: 1px; height: 18px; background: var(--dt-border); flex-shrink: 0; }
.filter-bar .filter-search {
  flex: 1; min-width: 200px; height: 34px;
  border: 1px solid transparent; border-radius: var(--radius-md);
  background: transparent; padding: 0 var(--sp-12);
  font-size: var(--fs-body); color: var(--dt-text1); outline: none;
  display: flex; align-items: center; gap: var(--sp-8);
  transition: background-color 0.15s, border-color 0.15s;
}
.filter-bar .filter-search:hover { background: var(--dt-inset); }
.filter-bar .filter-search:focus-within { background: var(--dt-raised); border-color: var(--dt-focus); }
.filter-bar .filter-search svg { width: 16px; height: 16px; stroke: var(--dt-text4); flex-shrink: 0; }
.filter-bar .filter-search input {
  flex: 1; border: none; background: none; outline: none;
  font-size: var(--fs-input); color: var(--dt-text1); font-family: inherit;
}
.filter-bar .filter-search input::placeholder { color: var(--dt-text4); }
.filter-bar .filter-plain {
  height: 34px; width: auto; flex-shrink: 0;
  border: none; background-color: transparent;
  padding: 0 var(--sp-8);
  border-radius: var(--radius-md);
  color: var(--dt-text2); font-size: var(--fs-input); font-family: inherit;
  display: inline-flex; align-items: center; gap: var(--sp-6);
  cursor: pointer;
  transition: color 0.15s, background-color 0.15s;
}
.filter-bar .filter-plain:hover { background-color: var(--dt-inset); color: var(--dt-text1); }
.filter-bar .filter-plain:focus {
  border: none; background-color: var(--dt-inset); color: var(--dt-text1); outline: none;
}
.filter-bar .filter-plain svg { width: 16px; height: 16px; stroke: currentColor; flex-shrink: 0; }
.filter-bar .btn { height: 34px; }

/* ---- 空态四件套 · shell.css:488-501 ----
   规范原文：新页面一律直接用这四个类，不要再自造 `.xx-empty`（dates.css:128、places.css:47
   都记着各自那套自造空态退役的过程）。 */
.empty-state {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--sp-6); padding: 32px var(--sp-20); text-align: center;
}
.empty-state-icon {
  display: flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; margin-bottom: 2px;
  border-radius: var(--radius-full);
  background: var(--dt-inset); color: var(--dt-text4);
}
.empty-state-icon svg { width: 20px; height: 20px; stroke: currentColor; }
.empty-state-title { font-size: var(--fs-title); font-weight: var(--fw-medium); color: var(--dt-text2); }
.empty-state-hint { font-size: var(--fs-caption); color: var(--dt-text4); }
.empty-state .btn { margin-top: var(--sp-6); }

/* ---- 页面 FAB · shell.css:442-462 ----
   全站唯一定义。my-app 注释原文（shell.css:414-441）：新页面一律复用此类，
   模块 CSS 只写页面变体。🔴 主功能按钮三原则，判定依据是「页面是否需要滚动」：
     ① 单一主功能 + 长列表 / 需要滚动 → 全端统一右下角 FAB（加 `.always`），
        桌面**不另留标题行按钮**（代表：shelf / book 账户·预算·分类 / market / places）
     ② 单一主功能 + 整页不需要滚动 → 桌面放标题行第二列
     ③ 多个功能 / 次要低频 → 逐页具体分析
   ⚠️ 原文（shell.css:437）：**同一动作禁止在标题行和 FAB 上各留一份入口**。
   🔴 由此歌单页标题行的「新建歌曲」按钮本次**删除**：速记就是新增歌曲（PRD D16），
      它和 FAB 是同一个动作，不是「两个功能不同的入口」——二稿那个说法已作废。
   ⚠️ 两处**登记过的偏离**（因 band 无外壳）：bottom 去掉 tabbar 那一项；z-index 取本页档 30。
      见 tools/shared.manifest.json。 */
.page-fab {
  display: none;
  position: fixed; right: 16px;
  bottom: calc(env(safe-area-inset-bottom) + 24px);
  width: 50px; height: 50px;
  align-items: center; justify-content: center;
  border: none; border-radius: 50%;
  background: var(--dt-interact); color: #fff;
  box-shadow: 0 6px 20px rgba(15, 23, 42, .28);
  cursor: pointer;
  z-index: 30;    /* > 本页吸顶筛选栏 10 */
  transition: transform .12s ease;
}
.page-fab:active { transform: scale(.94); }
.page-fab svg { stroke: currentColor; }
/* 主区（移动端）显形；桌面默认隐藏，把入口交回标题行 */
@media (max-width: 768px) { .page-fab { display: flex; } }
/* 全端常驻变体：桌面右下 24/28（对齐 places / market 基线） */
@media (min-width: 769px) {
  .page-fab.always { display: flex; right: 24px; bottom: 28px; }
}

/* ---- 列表行骨架 · shell.css:1070-1090 ----
   `.list-row` 本体（无悬停底色、靠 border-bottom 分隔、横向内距 0）留给「流水/归档型」列表；
   band 的歌单行是**整行可点 + 悬停底色**，属 .dts-row / .tx-item / .notif-row 那一族
   （见我 §2 的 `.song-row`）。这里只用与行族无关的四个内件。 */
.list-row-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.list-row-name { font-size: var(--fs-body); font-weight: var(--fw-medium); color: var(--dt-text1); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.list-row-sub { font-size: var(--fs-caption); color: var(--dt-text3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.list-row-ops { flex: 0 0 auto; display: flex; align-items: center; gap: 2px; }

/* ---- 登录页 · login.css:20-181 ----
   登录页是壳外独立入口，my-app 用**独立文件**划作用域；band 单文件 ⇒ 用 `.login-body` 作用域。
   下列声明值逐字抄 login.css，含该页独有的两档：
     · 输入框 36px（共享层是 32px）· 提交钮 36px 全宽（共享层 32px）
     · 输入框 focus 带 3px 光晕（共享层是 box-shadow: none） */
.login-body {
  display: flex; align-items: center; justify-content: center;
  min-height: 100vh; padding: 24px;
  background: var(--dt-bg);
}
.login-card {
  width: 100%; max-width: 380px;
  padding: var(--pad-card-lg) var(--pad-x-lg);
  background: var(--dt-raised);
  border: 1px solid var(--dt-card-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
}
.login-title {
  /* 标题自带「标题→首个输入框」那一档间距（第 102 批删掉 `.login-sub` 副标题后补的），
     少了它两个盒子相邻塌陷成 0，标题会贴住「共享口令」标签 */
  margin: var(--gap-card) 0 var(--gap-section);
  font-size: var(--fs-heading); font-weight: var(--fw-semibold);
  line-height: var(--lh-tight); text-align: center; color: var(--dt-text1);
}
.login-body .form-input { height: 36px; }
.login-body .form-input:focus { box-shadow: 0 0 0 3px var(--dt-focus-light); }
.login-body .btn { width: 100%; height: 36px; }

/* 错误 / 提示条 · login.css:134-159 —— 旧版 band 是裸文字（`.login-error`），
   与全站「成条」的写法不一致 */
.login-alert {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-6);
  margin-bottom: var(--gap-card);
  padding: var(--sp-8) var(--sp-10);
  border-radius: var(--radius-md);
  font-size: var(--fs-caption);
  line-height: var(--lh-prose);
}
.login-alert[hidden] { display: none; }
.login-alert.is-error {
  background: #fef2f2;
  border: 1px solid #fecaca;
  color: #b91c1c;
}
.login-alert.is-info {
  background: var(--dt-inset);
  border: 1px solid var(--dt-border);
  color: var(--dt-text2);
}

/* ============================================================
   §2 band 私有件
   ============================================================ */

/* ---------- 页面容器与页头 ---------- */
.page {
  max-width: 760px;
  margin: 0 auto;
  padding: var(--sp-20) var(--sp-16) var(--sp-24);
  display: flex;
  flex-direction: column;
  gap: var(--gap-section);
}

/* 页头几何抄 shell.css:317-321（grid 两列 + 标题/描述显式落第一列），
   `.page-title` 字号字重抄 shell.css:323-328。 */
.page-head {
  width: 100%;
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
}
/* 标题恒在第 1 列 = 页面左栏。少了这一条，只要头部第一个元素不是标题（比如返回钮在前），
   标题就会被 grid 自动落到第 2 列（auto）贴到页面右端 */
.page-head > .page-title { grid-column: 1; }
/* 子页头部（返回钮在前）：图标与标题同一行，标题缩进跟随图标。
   🔴 is-sub 必须显式把标题放回第 2 列：上一条只保证「不在最右列」，
   同一行排布时它会把标题拉回第 1 列、与返回图标重叠。特异性 (0,3,0) 高过 (0,2,0)。
   🔴 返回钮的 justify-self 不能省：grid 项默认 stretch 会撑满剩余空间，
   实测点击区曾达 632px，点它右边的空白也会跳走 */
.page-head.is-sub { grid-template-columns: auto 1fr; column-gap: var(--sp-12); }
.page-head.is-sub > .page-back { justify-self: start; }
.page-head.is-sub > .page-title { grid-column: 2; }

.page-title {
  font-size: var(--fs-page);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-tight);
  color: var(--dt-text1);
}
.page-back {
  display: inline-flex; align-items: center; gap: var(--sp-6);
  font-size: var(--fs-body); color: var(--dt-text2);
  padding: var(--sp-4) 0;
}
/* 纯图标形态：尺寸与静默色取 `.btn-icon.is-lg`（32×32、radius-md、--dt-text4、
   hover 到交互色 + a06 淡底）。触屏档放大到 40（见 §3）。
   宽高写死 ⇒ 必须同时归零 padding，否则内容被挤出边框 */
.page-back.is-icon {
  justify-content: center; width: 32px; height: 32px; padding: 0;
  border-radius: var(--radius-md);
  color: var(--dt-text4);
}
.page-back.is-icon:hover { color: var(--dt-interact); background: var(--dt-interact-a06); }
.page-back.is-icon:active { background: var(--dt-inset); }

/* ---------- 歌单列表 ---------- */
/* 列表承载卡：padding 与 my-app 四处同尺规（book .tx-group:455 / dates .dts-list-card:61 /
   places .plc-list-card:318 / shelf .shf-hist-card:125 —— 都是 `--sp-6 --sp-8`），
   my-app 的写法是 `<div class="card 模块-list-card">`，band 同构。 */
.song-list { padding: var(--sp-6) var(--sp-8); }

/* 整行可点的列表行。my-app 侧这一族**尚未上提共享件** —— book `.tx-item` /
   dates `.dts-row` / notifications `.notif-row` 是三份同构实现，band 是第四处。
   声明值逐字抄 dates `.dts-row`（dates.css:63-81，最新一版）：
   悬停底色关进 `@media (hover: hover)`，触屏改由 `:active` 承担 ——
   触屏没有真 hover，`:hover` 会「粘」在上次点过的行上，看着像选中态。 */
.song-row {
  display: flex; align-items: center; gap: var(--sp-12);
  padding: var(--sp-12) var(--sp-8);
  border-bottom: 1px solid var(--dt-border);
  cursor: pointer;
  transition: background .12s;
}
.song-row:last-of-type { border-bottom: none; }
@media (hover: hover) {
  .song-row:hover { background: var(--dt-interact-a04); }
}
.song-row:active { background: var(--dt-inset); }

/* 行尾只读列（主唱签 + 置顶标记），形态同 dates `.dts-row-ops`（dates.css:110）：
   flex 0 0 auto ⇒ 宽度恒等于内容宽，没有内容时不影响布局 */
.song-side { flex: 0 0 auto; display: inline-flex; align-items: center; gap: var(--sp-8); }
.song-tags { display: inline-flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--sp-5); }

/* 只读小签。形态（display / 字号 / 行高 / 圆角 / 内距）逐字抄 my-app places 管理列表里的
   公开/隐藏徽标 `.plc-badge`（places.css:42；places-map.js:192 原注「管理页表格仍是双徽标」
   ⇒ 那个徽标就是用户看到的参照物，2026-09-24 用户裁定「一样」）。
   配色另取中性档：`.plc-badge` 自己不写配色（由 `.plc-badge-public/-private` 两个变体给），
   而本件没有公开/私密那类二态语义 ⇒ 取 places 分类小签 `.plc-mcat-tag`（places.css:339）
   的 `--dt-inset` + `--dt-text2`。
   ⚠️ 旧的 `white-space: nowrap` 已删：`.plc-badge` 没有这条，用户要的是「一样」。
      `.song-tags` 是 `inline-flex` 且外层 `.song-side` 是 `flex: 0 0 auto`（宽度恒等于内容），
      标签不会被挤压 ⇒ nowrap 本来也不生效。
   ⚠️ 旧版是自造的 `padding: 2px 8px` + `--radius-full`，与 my-app 的 sp-4/sp-9 不同值。
   🔒 已登记进 tools/shared.manifest.json（`.singer-tag ⇄ .plc-badge`），改了就报红。 */
.singer-tag {
  display: inline-block;
  font-size: var(--fs-caption); line-height: 1;
  border-radius: 999px;
  padding: var(--sp-4) var(--sp-9);
  background: var(--dt-inset); color: var(--dt-text2);
}

/* 置顶标记（只显示、不可点）。形态同 dates `.dts-pin`（dates.css:115-116）：
   inline-flex + 15px + 琥珀色。旧版是 14px + 墨黑交互色（`.pin-mark` 定义在歌名行内）。

   🔴 它**只在窄屏出现**（见 §3）：桌面上行尾那枚置顶钮（下一条 `.is-on`）已经在表达
   同一个状态，两处都画就是同一条信息标记两遍 —— 用户点名的毛病正是「重复标记」，
   从粗描边换成第二个图钉同样算重复。每个断点只留一处。 */
.pin-mark { flex: 0 0 auto; display: none; color: var(--dt-amber-text); }
.pin-mark svg { width: 15px; height: 15px; }

/* 行内图标钮的「已置顶」语义态：琥珀色（同 my-app `.dts-pin` 的琥珀语义）。
   手法与共享件的 `.btn-icon.danger` 同构 —— 语义分档，不是尺寸分档 */
.btn-icon.is-on { color: var(--dt-amber-text); }
.btn-icon.is-on:hover:where(:not(:disabled)) { color: var(--dt-amber-text); background: var(--dt-amber-bg); }

/* 编辑页窄屏提示（「本端不做这件事」的说明）；
   旧版借用 `.empty-tip`（空态件），语义不符且该件已随共享件收口退役。
   `display: none` 与形状写在**同一条**里（旧版在 §2 尾另有一条同选择器的 `display:none`，
   同一类名两处定义是台账里的旧账，本次合并）；窄屏只在 `.edit-page.is-edit` 上开（见 §3）。 */
.edit-mobile-note {
  display: none;
  margin: 0; padding: var(--sp-24) var(--sp-16);
  text-align: center; font-size: var(--fs-caption); color: var(--dt-text3);
}

/* ---------- 看谱页 ---------- */
/* 上留白让开固定顶栏；下留白给常驻底栏。
   🔴 选择器必须写 `.page.song-page`（两类名），不能只写 `.song-page`：
      §3 的 `@media (max-width: 768px) { .page { padding: 简写 } }` 排在本节**之后**，
      同特异性下后到者胜 —— 只写一个类名时，窄屏那条 `padding` 简写会把这里的
      `padding-top` 一起重置回 16px，固定顶栏就会压住正文（实测：内容顶 16 / 顶栏高 52）。 */
.page.song-page { padding-top: calc(var(--band-topbar-total) + var(--sp-16)); padding-bottom: calc(var(--band-bottom-h, 88px) + var(--sp-16)); }

/* 顶栏（PRD D19）：固定顶部、下沿一条**全宽分隔线**。
   几何与手法抄 my-app `.app-topbar`（shell.css:92-104）：height 走令牌、顶部安全区由
   padding 承担、分隔线就是 `border-bottom`（横贯视口，不缩在 760px 内容列内）。
   ⚠️ 旧版是 `position: sticky` 且宽度被 `.page` 那一列夹住，画不出全宽线。
   三段：左返回钮 / 中两行信息 / 右两枚胶囊（胶囊形态与内容都不变，见 PRD D13）。 */
.chart-topbar {
  position: fixed; top: 0; left: 0; right: 0;
  height: var(--band-topbar-total);
  padding: env(safe-area-inset-top, 0px) var(--sp-16) 0;
  display: flex; align-items: center; gap: var(--sp-8);
  background: var(--dt-bg);
  border-bottom: 1px solid var(--dt-border);
  z-index: 12;
}
/* 中间信息块：两行左对齐（第一行歌名 / 第二行歌手 · 原唱 · 拍号） */
.chart-info { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.chart-title {
  font-size: var(--fs-title); font-weight: var(--fw-semibold); line-height: var(--lh-tight);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.chart-sub {
  font-size: var(--fs-caption); color: var(--dt-text3); line-height: var(--lh-tight);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* 窄高双行胶囊（PRD D13：52×42）—— 形态与值都不变，它同时是面板入口 */
.pill {
  flex: 0 0 auto; width: 52px; height: 42px;
  border: 1px solid var(--dt-border-str); border-radius: var(--radius-md);
  background: var(--dt-raised); color: var(--dt-text2);
  font-family: inherit; text-align: center; line-height: 1.3;
  padding: 2px 0; cursor: pointer; transition: all .15s;
}
.pill:hover { border-color: var(--dt-interact-acc); }
.pill .pill-top { display: block; font-size: var(--fs-body); font-weight: var(--fw-medium); color: var(--dt-text1); }
.pill .pill-sub { display: block; font-size: var(--fs-caption); color: var(--dt-text3); }
.pill.open { border-color: var(--dt-interact); background: var(--dt-interact-surf); }
.pill.open .pill-sub { color: var(--dt-interact); }

/* 段落卡片流。
   🔴 卡片壳由**共享件 `.card`** 承担 —— 宿主写成 `<article class="card section-card">`
      （见 chart.js）。旧版只挂了 `.section-card`，而这条规则只设内距 ⇒ 段落其实是
      平铺文字、没有卡片（用户点名的「歌词区用卡片形式展示」）。
   内距覆盖与 `.song-list` 同手法：挂上 `.card` 再压内距。 */
.section-list { display: flex; flex-direction: column; gap: var(--gap-card); }
.section-card { padding: var(--pad-card-sm) var(--pad-x-md); }

/* 段落标签行：整体比正文**浅一档**（PRD D19「段名取浅一档」）。
   旧值 = head: text3 / name: text2，名字紧挨着 15px 的歌词（text1）时读起来混。 */
.section-head {
  display: flex; align-items: baseline; justify-content: space-between;
  margin-bottom: var(--sp-6);
  font-size: var(--fs-caption); color: var(--dt-text4);
}
.section-name { font-weight: var(--fw-medium); color: var(--dt-text3); }
.section-time { font-variant-numeric: tabular-nums; }

/* 播放态取蓝（PRD D19）。`--band-accent` = Tailwind v4 blue-600，**只表示这一件事**，
   别处一律仍走 `--dt-interact` 墨黑（见 §0b 的语义纪律）。
   ⚠️ 该态随 M2 上线（要有音频才可能进入播放）。 */
.section-card.playing { border-color: var(--band-accent); background: var(--dt-raised); }
.section-card.playing .section-name { color: var(--band-accent); }
.section-card.standby { opacity: 0.72; }
.section-card.standby .section-name::after { content: ' · 预备进入'; color: var(--dt-text3); }

/* 和弦歌词逐字对齐 */
.ch-body { font-size: 15px; line-height: 1.7; }
.ch-cell { display: inline-block; vertical-align: bottom; min-width: 1em; white-space: nowrap; }
.ch-chord {
  display: block; height: 16px;
  font-family: var(--font-mono); font-size: 12px; font-weight: var(--fw-medium);
  color: var(--dt-interact); line-height: 16px;
}
.ch-lyr { display: block; }
.ch-linebreak { height: var(--sp-8); }
/* 「只写和弦、不写歌词」的行（前奏/间奏，源文本如 `[G] [F] [Em] [C]`）：
   和弦横排成一行，**不产出歌词格** —— 所以这种卡片不再白占一条空的歌词轨道。
   🔴 间距只由这里的 gap 给，源文本里那个空格不参与 —— 否则 `[G] [F]` 与 `[G][F]`
      会渲染出两种间距。`.ch-chord` 本是字格里的 `display: block` 内件，
      在 flex 容器里直接充当 flex 项（height 16px 照旧生效）。 */
.ch-chord-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-10); }
/* 和弦显隐（PRD D18）。开关挂在段落流的宿主上：`.section-list.hide-chords`。
   🔴 用 `visibility: hidden + height: 0`，**不用 `display: none`**：
      `.ch-chord` 是 `display: block`，它的文字宽度参与 `.ch-cell`（inline-block）的收缩计算。
      `display: none` 会让「弦比词宽」的那些格变窄 ⇒ 歌词整体左移、**逐字对齐被重排**。
      visibility 保留布局宽度（歌词一格不移）、height 0 塌掉那 16px
      ⇒ 卡片同步变紧凑。两个结果都在 smoke 里各有一条断言：
      ① 隐藏前后每个歌词字符的 x 坐标逐个相同；② 卡片高度变小。 */
.section-list.hide-chords .ch-chord { visibility: hidden; height: 0; }

/* ---------- 底栏（播放条常驻贴底 + 调节区在上方） ---------- */
.chart-bottom {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 20;
  background: var(--dt-raised);
  border-top: 1px solid var(--dt-border);
}
.chart-bottom-inner {
  max-width: 760px; margin: 0 auto;
  padding: var(--sp-8) var(--sp-16) calc(var(--sp-8) + env(safe-area-inset-bottom, 0px));
}

.adj-panel {
  padding: var(--sp-10) 0;
  border-bottom: 1px solid var(--dt-border);
  margin-bottom: var(--sp-8);
}
.adj-head {
  display: flex; align-items: center; gap: var(--sp-8);
  margin-bottom: var(--sp-10);
}
.adj-head .chip-row { flex: 1; min-width: 0; }
/* 面板头部的读数行（速度面板用它放「原 72 · 1.33×」）。
   调性面板的同一位置是主唱标签行 —— 两者都是「这块面板现在按什么算」，
   都占满左侧剩余宽度，收起钮始终在右端同一落点。 */
.adj-note { flex: 1; min-width: 0; font-size: var(--fs-caption); color: var(--dt-text3); }
/* 收起钮：取 `.btn-icon` 的静默色与 hover 语义，尺寸按底栏触屏档放大到 32 方钮 */
.adj-collapse {
  flex: 0 0 auto; width: 32px; height: 32px;
  border: none; background: none; border-radius: var(--radius-sm);
  color: var(--dt-text4);
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0; cursor: pointer;
}
.adj-collapse:hover { color: var(--dt-interact); background: var(--dt-interact-a06); }

.adj-row {
  display: flex; align-items: center; gap: var(--sp-10);
}
.stepper { display: flex; align-items: center; gap: var(--sp-8); flex: 1; }
/* 步进钮 40px（PRD D12「触屏按钮 40px 以上」）；形状取共享件的控件档：
   border 1px --dt-border + radius-md + 白底 */
.step-btn {
  width: 40px; height: 40px; flex: 0 0 auto;
  border: 1px solid var(--dt-border); border-radius: var(--radius-md);
  background: var(--dt-raised); color: var(--dt-text1);
  font-family: inherit; cursor: pointer; user-select: none;
  display: inline-flex; align-items: center; justify-content: center;
}
.step-btn:hover { border-color: var(--dt-interact); color: var(--dt-interact); }
.step-btn:active { background: var(--dt-inset); }
.step-value { flex: 1; text-align: center; min-width: 0; }
.step-main { font-size: var(--fs-heading); font-weight: var(--fw-medium); line-height: var(--lh-tight); }
.step-main small { font-size: var(--fs-caption); font-weight: var(--fw-normal); color: var(--dt-text3); }
.step-hint { margin-top: 2px; font-size: var(--fs-caption); color: var(--dt-text3); }
.step-value input {
  width: 72px; text-align: center;
  border: none; border-bottom: 1px solid var(--dt-border-str);
  background: transparent; font-size: var(--fs-heading); font-weight: var(--fw-medium);
  font-family: inherit; color: var(--dt-text1); outline: none; padding: 0;
}

/* 底栏控制行（PRD D19 的第二行）。
   播放控件整组 `flex: 1`，把右端的和弦显隐钮推到行尾（D18「播放器右侧」）。
   旧版的 `#playerSlot` 占位（灰点 + 「还没有音频」提示）已退役：没音频时
   控件**显示但进禁用态**（D30，2026-09-24 用户裁定），提示挪到时间读数的位置。
   M1.5 只有那枚和弦钮是真的；M2 换成进度条 + `[后退5s][播放][前进5s]`。 */
.player-row { display: flex; align-items: center; gap: var(--sp-10); }

/* ---- 音频播放器（M2-a）----
   进度条是 band 私有件：my-app 只有展示型进度条，没有可拖动的媒体条。
   轨道这组形态取自 `words.css:98-101` 的 `.overview-progress-bar`（`--dt-inset` 底 + 圆头端点）；
   `book.css:844` 那条是 2px 方角 —— my-app 内部这两档本就不统一，band 取圆头那档，
   与全站按钮/胶囊的圆角语言一致。高度取 6px 细线：它是可拖的媒体条，不是展示型进度。
   手柄（knob）是 band 特有的：16px 白底 + 交互色描边，手指按得住。 */
.audio-bar {
  padding-bottom: var(--sp-8);
  margin-bottom: var(--sp-8);
  border-bottom: 1px solid var(--dt-border);
}
/* 版本行：`.chip-row` 空着也有高度对齐问题 ⇒ 只在有内容时才留缝（不用 hidden，
   那会被 `.chip-row { display: flex }` 盖掉 —— `[hidden]` 是 UA 样式，作者样式优先） */
.audio-vers:not(:empty) { margin-bottom: var(--sp-10); }
.audio-add { display: flex; gap: var(--sp-8); margin-top: var(--sp-8); }
.audio-progress {
  position: relative; height: 28px;               /* 视觉 6px，触控区 28px */
  display: flex; align-items: center;
  cursor: pointer; touch-action: none;
}
/* 没音频的禁用态（D30）：压透明度 + 拦指针。按钮侧走 `.btn-icon:disabled`（共享件已有）。 */
.audio-progress.is-disabled { opacity: .4; pointer-events: none; cursor: default; }
.audio-progress-track,
.audio-progress-fill {
  position: absolute; height: 6px; border-radius: var(--radius-full);
}
.audio-progress-track { left: 0; right: 0; background: var(--dt-inset); }
.audio-progress-fill { left: 0; width: 0; background: var(--dt-interact); }
.audio-progress-knob {
  position: absolute; top: 50%; left: 0;
  width: 16px; height: 16px; margin-top: -8px; margin-left: -8px;
  border-radius: var(--radius-full);
  background: #fff; border: 2px solid var(--dt-interact);
  pointer-events: none;
}
.audio-time {
  margin-top: var(--sp-4); text-align: right;
  font-size: var(--fs-caption); color: var(--dt-text3);
  font-variant-numeric: tabular-nums;
}
.player-ctrls { flex: 1; min-width: 0; display: flex; align-items: center; gap: var(--sp-10); }

/* 开关类图标钮的「开」态（PRD D19「开关态一律用颜色区分，图标形不变」）：
   取交互墨黑 + a06 淡底。手法抄 my-app `.topbar-icon-btn.current`（shell.css:195）。
   ⚠️ 不复用 `.btn-icon.is-on` —— 那一枚是**置顶**语义、取琥珀（见 §2 上方），
      两个意思两档色，别混。 */
.btn-icon.is-current { color: var(--dt-interact); background: var(--dt-interact-a06); }
.btn-icon.is-current:hover:where(:not(:disabled)) { color: var(--dt-interact); background: var(--dt-interact-a12); }

/* ---------- 编辑页（电脑端） ---------- */
/* 两个固定条：顶栏（返回 + 页名）与底栏（保存/取消/删除）。
   用户 2026-09-24 点名：谱面一长就得来回滚到底去按「保存」，段落排序还得先把卡片拖到眼前。
   两处都取 `position: fixed`，宽度收在**与正文同一列**（`max-width` 与 `.page` 同值 760px、
   `margin: 0 auto` 居中）—— 编辑页是表单页，看谱页那种横贯视口的全宽分隔线在这里没有承载物。
   🔴 fixed 元素不能在同时给了 `left/right` 的情况下留 `width: 100%`：
      那会变成「宽 = 视口、左对齐」，`margin: auto` 失效（分配不到剩余空间）。
      `.page-head` 自己带 `width: 100%` ⇒ 这里必须显式 `width: auto` 让它回到可收缩档。 */
.edit-page .page-head,
.edit-page .edit-actions {
  position: fixed; left: 0; right: 0;
  width: auto; max-width: 760px; margin: 0 auto;
}
.edit-page .page-head {
  top: 0;
  height: var(--band-topbar-total);
  padding: env(safe-area-inset-top, 0px) var(--sp-16) 0;
  background: var(--dt-bg);
  border-bottom: 1px solid var(--dt-border);
  z-index: 12;
}
.edit-actions {
  display: flex; gap: var(--sp-8); justify-content: flex-end;
  bottom: 0;
  padding: var(--sp-12) var(--sp-16) calc(var(--sp-12) + env(safe-area-inset-bottom, 0px));
  background: var(--dt-raised);
  border-top: 1px solid var(--dt-border);
  z-index: 20;
}
/* 正文两头让开两个固定条。🔴 必须写 `.page.edit-page`（双类名）：
   §3 的 `@media (max-width: 768px) { .page { padding: 简写 } }` 排在本节**之后**，
   同特异性下后到者胜 —— 只写一个类名时窄屏那条会把 padding-top 一起重置回去。 */
.page.edit-page {
  padding-top: calc(var(--band-topbar-total) + var(--sp-16));
  padding-bottom: calc(var(--band-editbar-h) + var(--sp-16));
}

.edit-grid {
  display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 4fr);
  gap: var(--gap-block); align-items: start;
}
.edit-sections { display: flex; flex-direction: column; gap: var(--gap-list); margin-bottom: var(--sp-8); }
/* 段落编辑器 = 「段落」卡里的**嵌套小卡片**。
   三档值全部取 my-app 既有档（settings.css:206-235，第 112 批「卡内小卡片」的判据）：
     底色 --dt-inset（块级浅底的既有档）· 圆角 --radius-md（比父卡 --radius-lg 小一档才同心）·
     内距 --pad-card-sm / --pad-x-sm（「紧凑卡片」那两个 token 的语义）。
   结构两行：头部（拖拽手柄 + 段名 + 上移/下移/删）+ 正文多行输入。
   🔴 旧版是三列 grid（96px 固定段名 / 文本域 / 竖排工具列）：段名「预副歌B」刚卡在 96px 上，
      文本域被右列挤窄，而且整块没有边界 —— 拖拽排序没有可承载的「一张卡」。 */
.section-editor {
  display: flex; flex-direction: column; gap: var(--sp-8);
  background: var(--dt-inset);
  border-radius: var(--radius-md);
  padding: var(--pad-card-sm) var(--pad-x-sm);
}
/* 拖动态：浅底卡片没有描边，浮起只能由阴影表达。取 --shadow-lg
   （与 my-app `.account-card.dragging` 同档：opacity .85 + 阴影 + 加深描边，
   本件无描边故只留前两项）。 */
.section-editor.dragging { opacity: .85; box-shadow: var(--shadow-lg); }
.section-editor-head { display: flex; align-items: center; gap: var(--sp-6); }
.section-editor-head .form-input { flex: 1; min-width: 0; height: 28px; }
/* 拖拽手柄。🔴 它**不是按钮**（不进无障碍树，用 aria-hidden）：
   键盘用户走右端的上移/下移钮，那两枚同时是键盘可达路径与「只挪一格」的轻操作。
   整卡不给 draggable —— 段名是 input、正文是 textarea，整卡可拖会让文本没法选中
   （my-app 的分类卡能整卡拖，是因为它里面没有输入框）。 */
.drag-handle {
  flex: 0 0 auto; width: 22px; height: 28px;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--dt-text4); cursor: grab; touch-action: none;
}
.drag-handle:hover { color: var(--dt-interact); }
.drag-handle:active { cursor: grabbing; }
.drag-handle svg { width: 16px; height: 16px; stroke: currentColor; }
/* 行内操作钮改横排（旧版竖排三枚，占掉 86px 宽的一整列） */
.section-tools { display: flex; gap: 2px; flex: 0 0 auto; }
/* 多行输入：my-app 对应件是各模块的 `.{模块}-ftextarea`（places.css:75 那类：
   归零高度 + resize + 行高），band 的处境还要等宽字体（谱面是等宽排的）。
   🔴 高度**不写死**：由 edit.js 的 autosize() 按内容算（下限 88px、上限 320px，
      到顶才转内部滚动）。旧版 min-height 64px（约三行）在真实谱面前不够 ——
      副歌一段就有 20 行（开发库 song #3 实测），打开编辑得手动往下拖五、六次才看得全。
      上限存在的理由：单段不该独占整屏，超出后在框内滚（顶栏底栏都固定着，首尾行仍看得见）。 */
.edit-ftextarea {
  height: auto; min-height: 88px; max-height: 320px; overflow-y: auto;
  resize: none;
  font-family: var(--font-mono); font-size: 13px; line-height: var(--lh-body);
  padding: var(--sp-8) var(--sp-10);
}

.singer-rows { display: flex; flex-direction: column; gap: var(--sp-8); }
.singer-row {
  display: flex; align-items: center; gap: var(--sp-8);
}
.singer-row .form-input { width: 88px; height: 28px; }
.singer-add { display: flex; gap: var(--sp-8); margin-top: var(--sp-8); }
.singer-add .form-input { width: 140px; }

/* 预览卡里的段落：卡内行靠 border-bottom 分隔、无圆角（同列表行族的手法） */
.preview-body .section-card { border: none; border-bottom: 1px solid var(--dt-border); border-radius: 0; padding: var(--sp-8) 0; }
.preview-body .section-card:last-child { border-bottom: none; }

/* ---------- 图标（lucide；数据源 public/js/icons.js，白名单 tools/icons.manifest.json）
   尺寸由 HTML 上的 width/height 或 CSS 给，颜色一律 currentColor ---------- */
.btn > svg, .chip > svg, .btn-icon > svg, .page-back > svg, .adj-collapse > svg, .step-btn > svg {
  flex: 0 0 auto; display: block;
}

/* ============================================================
   §3 响应式
   ⚠️ 原先 list.js 里另有一处 768（`NARROW`，只用于空态文案分档），已随空态
      文案统一而删除 ⇒ 这个断点现在**只有本处一份**。
   ============================================================ */
@media (max-width: 768px) {
  .page { padding: var(--sp-16) var(--sp-12) var(--sp-24); }
  .list-row-ops { display: none; }  /* 手机只读：行内管理入口隐藏（PRD D3） */
  .pin-mark { display: inline-flex; } /* 管理钮没了 ⇒ 置顶状态改由只读标记承担（一处换一处） */

  /* 编辑表单窄屏隐藏 —— **只挡「改已有歌」**（`.edit-page.is-edit`）。
     🔴 `/song/new` 是速记落点（PRD D16：速记就是新增歌曲），窄屏必须照常显示表单，
        否则点 FAB 跳过去只看到一句「编辑功能在电脑端进行」，速记无从下手。
        所以这里按**页面身份**分档，而不是一律隐藏 —— 旧的 `.desktop-only` 一刀切已删
        （它唯一的宿主「新建歌曲」按钮也随 FAB 上线而删除）。 */
  .edit-page.is-edit .edit-grid,
  .edit-page.is-edit .edit-actions { display: none !important; }
  .edit-page.is-edit .edit-mobile-note { display: block; }
  .edit-grid { grid-template-columns: 1fr; }

  .page-back.is-icon { width: 40px; height: 40px; } /* 触屏档：图标钮放大到拇指尺寸 */
}
