/* ================= 手机端全屏（2026-10 用户要求 ✓）=================
   手机上琴键只占屏幕三四成 ✗ 想看清每个键得来回滚 ✗
   点「全屏」按钮 → 整块面板铺满屏幕 ✓ 并且**尽量横过来** ✓

   ★ 为什么一定要横屏 ✗（2026-10 用户补充说明 ✓）：
     竖屏手机宽只有 ~390px ✗ "88 键全见"意味着每键 7px ✗ 根本没法看 ✗
     横过来宽变成 ~800px ✓ 每键 15px ✓ 才真的能用 ✓

   按钮本身只在**触摸设备**上出现 ✓（2026-10 用户要求"PC 端不要显示"✓）

   ★ 为什么从 max-width 改成 pointer:coarse ✗（用户实测反馈后改的 ✓）
     原来按 `max-width: 760px` ✗ → PC 用户把窗口拉窄到 600px ✗
       这个按钮就冒出来了 ✗ 而 PC 根本不缺屏幕空间 ✗ 全屏/横屏纯属添乱 ✗（用户原话：PC 端完全没必要 ✓）
     而"是不是手机"的**正确判据是输入方式** ✗ 不是屏幕宽度 ✓：
       (pointer: coarse) = 主要输入方式是**手指** ✗ 正好等于"手机 / 平板"✓
       PC（鼠标 / 触摸板）报 pointer: fine ✗ 自动不显示 ✓
       带触屏的 Windows 本：主指针仍是鼠标 ✗ 也不显示 ✓（符合"PC 端不要"✓）
     不支持这条媒体查询的老浏览器 → 判定为 false ✗ 同样不显示 ✓
       （"不显示"是安全的一侧 ✓ 多显示才是问题 ✗ 所以这个默认值选对了 ✓） */

/* ★ 2026-10：按钮改成所有设备都显示 ✓（用户改主意了 ✓）
   原因很实际 ✗：有了「特效模式」之后，全屏的含义变成了"把特效钢琴窗口铺满整屏"✓
   电脑屏幕虽大 ✗ 但标签栏 + 书签栏 + 任务栏一占 ✗ 特效区还是不够高 ✓
   所以交给用户自己决定 ✗ 不再按 pointer:coarse 只在触摸设备显示 ✓ */
/* ★ 全屏按钮：**电脑端也显示**（2026-10 用户最终决定 ✓ 前面来回改过几次 ✗ 这是结论 ✓）
   用户的理由 ✓：有了「特效模式」之后全屏确实有用 ✓
   而且明确说了只要"把 #pianoScroll 撑到 100% 宽"这么简单一件事 ✓
   → 所以按钮所有设备都显示 ✗ 由用户自己决定用不用 ✓ */
.piano-fs { display: inline-flex; }

/* 全屏 / 伪全屏时的面板
   ★ 为什么这几条用 !important ✗：
     首页有 `.hp-stage .piano-panel { margin:0; padding:0; border:0 }`（page-index.css ✓）
     与这里**特异度相同** ✗ 谁后加载谁赢 ✗
     而"铺满屏幕"是硬需求 ✓ 被盖掉就整块透明/带边距 ✗ 全屏等于没全屏 ✗
     → 把它标成不可覆盖 ✓ 只在这几个属性上 ✓ 不影响普通样式 ✓ */
.piano-panel:fullscreen,
.piano-panel:-webkit-full-screen,
.piano-panel.is-faux-full {
    position: fixed !important;      /* 伪全屏靠这个 ✓ 原生全屏时浏览器自己接管 ✓ 无副作用 ✓ */
    top: 0 !important; right: 0 !important; bottom: 0 !important; left: 0 !important;
    z-index: 99999 !important;
    margin: 0 !important;
    border: 0 !important;
    border-radius: 0 !important;
    box-shadow: none !important;
    background: #1c2820 !important;  /* 深底 ✓ 琴键的白色/绿色在深底上最清楚 ✓ */
    overflow: auto;
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: 10px 0 !important;
}

/* 伪全屏时锁住页面滚动 ✗ 否则手指一划整页跟着动 ✓ 琴反而跑出视野 ✓ */
body.piano-fs-open { overflow: hidden; }

/* 全屏里琴键视口别再限宽 ✓ 铺满整屏 ✓ */
.piano-panel:fullscreen .piano-scroll,
.piano-panel:-webkit-full-screen .piano-scroll,
.piano-panel.is-faux-full .piano-scroll {
    max-width: none !important;
    border-radius: 0 !important;
}

/* ★ 全屏 +「特效模式」：让**那个黑框**（音符下落区）吃掉整屏剩余高度 ✓✓
   —— 2026-10 用户要求 ✓："全屏之后主要看的就是特效钢琴那个框，让它全屏就行了"✓
   做法 ✗：把 .piano-scroll 变成**纵向 flex** ✗ 画布 flex:1 自动吃掉除琴键外的所有高度 ✓
   关键 ✗：画布的 height 必须改成 auto ✗
     它平时写死 240px（手机 150px ✓）✗ 不改的话它会长不大 ✗ 全屏就白搭 ✓
   min-height 140px 是兜底 ✗ 万一屏幕特别矮（横屏手机 ✓）也不至于把画布压没 ✓
   量尺寸不用管 ✓：进出全屏会触发 fullscreenchange → applyZoom → 我们的重算钩子 ✓
     另外 resize（全屏必然改变视口 ✓）还有一道防抖重算 ✓ 双保险 ✓ */
.piano-panel:fullscreen .piano-scroll,
.piano-panel:-webkit-full-screen .piano-scroll,
.piano-panel.is-faux-full .piano-scroll {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
}
.piano-panel:fullscreen .pk-roll,
.piano-panel:-webkit-full-screen .pk-roll,
.piano-panel.is-faux-full .pk-roll {
    flex: 1 1 auto;
    height: auto;
    min-height: 140px;
    /* ★ 高度比例上限（2026-10 ✓ 用户要求"高度比例设置好，看起来更舒服"✓）
       不设上限的话 ✗ 在 4K 竖屏上画布能长到 1500px ✗
       而琴键只有不到 200px ✗ 比例失衡会很怪 ✓
       62vh 是照着钢琴卷的常规观感选的 ✗
         （下落区 ≈ 琴键高度的 3~4 倍 ✓ 再高就"空得像块黑板"了 ✓）
       剩下的空间不会被浪费 ✓ 面板是 justify-content:center ✗
         整块钢琴窗会自动垂直居中 ✓ 上下留白反而更透气 ✓ */
    max-height: 62vh;
    margin-bottom: 10px;
}

/* ★★ 全屏里的文字要改成浅色 ✗（2026-10 用户实测 ✓）
   全屏时面板底色是深绿 #1c2820（上面那条 ✓）✗ 而工具条的文字/标签默认是深色 ✗
   → 黑字配黑底 ✗ 用户原话"黑色和黑色它就融合在一起，看不清了"✓
   这里把全屏范围内所有**可见文字**统一切成浅色 ✗
   ★ 注意只在 :fullscreen / .is-faux-full 范围内生效 ✗ 普通页面一个像素都不变 ✓ */
.piano-panel:fullscreen,
.piano-panel:-webkit-full-screen,
.piano-panel.is-faux-full,
.piano-panel.is-fs {
    color: #e4ede8;
}
/* ★★ .is-fs 是**由 JS 打的**全屏标记 ✗（2026-10 第三次修 ✓）
   为什么要它 ✗：整套全屏样式原本挂在 `:fullscreen` 伪类上 ✗
     但用户按 **F11** 全屏时 ✗ 浏览器不会给任何元素加这个伪类 ✗✗
     → 宽度卡在 992px ✗ 圆角不归零 ✗ 文字不变浅 ✗ 画布不撑高 ✗ 一条都没生效 ✓
   现在 JS 三种情况（原生 / 伪全屏 / F11 ✓）都打 `.is-fs` ✗ CSS 认它就行 ✓ */
.piano-panel.is-fs .piano-scroll {
    /* ★★ 用户的原话（2026-10 ✓ 说得最清楚的一次 ✓）：
       "我就是想当我点击全屏之后，<div class="piano-scroll">这个地方的宽度给它
        设置成百分之百，就是全屏的宽度，然后里面的钢琴键以及它的高度，
        按比例给它进行显示、放大或者增高，就是这么一个简单的意思。"✓
       → 所以这里就两件事 ✗ 都写死 ✓：
         ① 宽度 100% ✓（max-width 必须显式解除 ✗ 它原本卡着 992px ✓）
         ② 高度交给 flex ✗ 画布吃掉剩余高度 ✗ 琴键按 --pk-w 比例自动变高 ✓
       （--pk-w 是缩放按钮控制的 ✗ 全屏时会自动顶到"88 键正好铺满"✗
         所以琴键宽度和高度都是**按比例**来的 ✓ 不用另写 ✓） */
    width: 100% !important;
    max-width: none !important;
    border-radius: 0 !important;
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
}
/* ★ 全屏里**不显示**放大/缩小按钮（2026-10 用户明确要求 ✓ 手机端同样 ✓）
   为什么合理 ✗：全屏的缩放是自动贴合到"88 键铺满"的 ✗ 用户再手动调只会打架 ✓
   （而且这两个按钮在标签页里本来就不够用 ✗ 全屏后弹窗式地改尺寸更让人困惑 ✓）
   ★ 用 !important ✗：按钮带着 .tool-btn 的 display 规则 ✗ 不写会盖不住 ✓ */
.piano-panel.is-fs #pianoZoomIn,
.piano-panel.is-fs #pianoZoomOut,
.piano-panel.is-fs #pianoWide,
.piano-panel.is-fs #pianoNarrow {
    display: none !important;
}
.piano-panel.is-fs .pk-roll {
    flex: 1 1 auto;
    height: auto;
    min-height: 140px;
    max-height: 62vh;
    margin-bottom: 10px;
}
.piano-panel.is-fs .piano-bar,
.piano-panel.is-fs .piano-bar .tool-opt,
.piano-panel.is-fs .piano-bar .tool-opt > span,
.piano-panel.is-fs .piano-bar .tool-stat,
.piano-panel.is-fs .fret-chords-label,
.piano-panel.is-fs .fret-chords-hint,
.piano-panel.is-fs .piano-credit,
.piano-panel.is-fs .staff-title,
.piano-panel.is-fs .piano-staff-empty,
.piano-panel.is-fs .piano-nav-range {
    color: #e4ede8 !important;
}
.piano-panel.is-fs .piano-time { color: #a9c2b6 !important; }
.piano-panel.is-fs .piano-bar .tool-btn:not(.primary) {
    background: rgba(255, 255, 255, .09);
    border-color: rgba(255, 255, 255, .22);
    color: #e9f1ec;
}
.piano-panel.is-fs .piano-bar .tool-select,
.piano-panel.is-fs .piano-bar .tool-num {
    background: rgba(255, 255, 255, .10);
    border-color: rgba(255, 255, 255, .24);
    color: #eef4f0;
}
.piano-panel.is-fs .chord-chip {
    background: rgba(255, 255, 255, .10);
    border-color: rgba(255, 255, 255, .20);
    color: #e9f1ec;
}
/* 面板本身也要铺满 ✗ 伪全屏那条已经做了 ✗ 这里给 F11 兜一道 ✓ */
.piano-panel.is-fs {
    background: #1c2820 !important;
    border-radius: 0 !important;
    box-shadow: none !important;
}
.piano-panel:fullscreen .piano-bar,
.piano-panel:fullscreen .piano-bar .tool-opt,
.piano-panel:fullscreen .piano-bar .tool-opt > span,
.piano-panel:fullscreen .piano-bar .tool-stat,
.piano-panel:fullscreen .fret-chords-label,
.piano-panel:fullscreen .fret-chords-hint,
.piano-panel:fullscreen .piano-credit,
.piano-panel:fullscreen .staff-title,
.piano-panel:fullscreen .piano-staff-empty,
.piano-panel:fullscreen .piano-nav-range,
.piano-panel:-webkit-full-screen .piano-bar,
.piano-panel:-webkit-full-screen .piano-bar .tool-opt,
.piano-panel:-webkit-full-screen .piano-bar .tool-opt > span,
.piano-panel:-webkit-full-screen .piano-bar .tool-stat,
.piano-panel:-webkit-full-screen .fret-chords-label,
.piano-panel:-webkit-full-screen .fret-chords-hint,
.piano-panel:-webkit-full-screen .piano-credit,
.piano-panel:-webkit-full-screen .staff-title,
.piano-panel:-webkit-full-screen .piano-staff-empty,
.piano-panel:-webkit-full-screen .piano-nav-range,
.piano-panel.is-faux-full .piano-bar,
.piano-panel.is-faux-full .piano-bar .tool-opt,
.piano-panel.is-faux-full .piano-bar .tool-opt > span,
.piano-panel.is-faux-full .piano-bar .tool-stat,
.piano-panel.is-faux-full .fret-chords-label,
.piano-panel.is-faux-full .fret-chords-hint,
.piano-panel.is-faux-full .piano-credit,
.piano-panel.is-faux-full .staff-title,
.piano-panel.is-faux-full .piano-staff-empty,
.piano-panel.is-faux-full .piano-nav-range {
    color: #e4ede8 !important;
}
/* 时间码用次要色 ✓ 主次分明 ✓ 全浅反而没有层次 ✓ */
.piano-panel:fullscreen .piano-time,
.piano-panel:-webkit-full-screen .piano-time,
.piano-panel.is-faux-full .piano-time {
    color: #a9c2b6 !important;
}
/* 次要按钮：深底上要有边界 ✗ 不然看着像一行纯文字 ✓ */
.piano-panel:fullscreen .piano-bar .tool-btn:not(.primary),
.piano-panel:-webkit-full-screen .piano-bar .tool-btn:not(.primary),
.piano-panel.is-faux-full .piano-bar .tool-btn:not(.primary) {
    background: rgba(255, 255, 255, .09);
    border-color: rgba(255, 255, 255, .22);
    color: #e9f1ec;
}
.piano-panel:fullscreen .piano-bar .tool-btn:not(.primary):hover,
.piano-panel:-webkit-full-screen .piano-bar .tool-btn:not(.primary):hover,
.piano-panel.is-faux-full .piano-bar .tool-btn:not(.primary):hover {
    background: rgba(255, 255, 255, .17);
}
/* 下拉框 / 数字框：深底 + 浅字 ✓ */
.piano-panel:fullscreen .piano-bar .tool-select,
.piano-panel:fullscreen .piano-bar .tool-num,
.piano-panel:-webkit-full-screen .piano-bar .tool-select,
.piano-panel:-webkit-full-screen .piano-bar .tool-num,
.piano-panel.is-faux-full .piano-bar .tool-select,
.piano-panel.is-faux-full .piano-bar .tool-num {
    background: rgba(255, 255, 255, .10);
    border-color: rgba(255, 255, 255, .24);
    color: #eef4f0;
}
/* 常用和弦的小按钮：深底上换个配色 ✓ 不然白底黑字在全屏里很跳 ✗ */
.piano-panel:fullscreen .chord-chip,
.piano-panel:-webkit-full-screen .chord-chip,
.piano-panel.is-faux-full .chord-chip {
    background: rgba(255, 255, 255, .10);
    border-color: rgba(255, 255, 255, .20);
    color: #e9f1ec;
}

/* 工具条让开刘海与底部横条（iPhone 安全区 ✓） */
.piano-panel:fullscreen .piano-bar,
.piano-panel:-webkit-full-screen .piano-bar,
.piano-panel.is-faux-full .piano-bar {
    padding-left: max(12px, env(safe-area-inset-left));
    padding-right: max(12px, env(safe-area-inset-right));
}

/* ★ 全屏里**不要谱子** ✗（2026-10 用户明确要求 ✓）
   横屏之后高度被压得很扁 ✗ 再塞一行五线谱会把琴挤没 ✗
   而且全屏的诉求就是"看清整台琴" ✓ 谱子是额外信息 ✓ 该让位 ✓
   同理收掉键盘操作提示 ✗ —— 手机上根本插不了键盘 ✓ */
.piano-panel:fullscreen .piano-staff,
.piano-panel:-webkit-full-screen .piano-staff,
.piano-panel.is-faux-full .piano-staff,
.piano-panel:fullscreen .piano-hint,
.piano-panel:-webkit-full-screen .piano-hint,
.piano-panel.is-faux-full .piano-hint {
    display: none !important;
}

/* 工具条在横屏里收紧一点 ✓ 给琴留出更多高度 ✓ */
.piano-panel:fullscreen .piano-bar,
.piano-panel:-webkit-full-screen .piano-bar,
.piano-panel.is-faux-full .piano-bar {
    flex-wrap: nowrap;
    overflow-x: auto;
    padding-top: 6px;
    padding-bottom: 6px;
}

/* ★★ 自转横屏（退路 ✓）
   iPhone 上 screen.orientation.lock('landscape') **不存在** ✗
   （Safari 只允许 video 全屏 ✗ 更别说锁方向 ✗）
   于是把整块面板**旋转 90°** ✓ 视觉上就是横屏 ✓ 触控坐标浏览器会自动映射 ✓
   做法就是那个经典配方 ✓：
     · 尺寸按"横屏"给：宽 = 100vh（屏的高 ✓）、高 = 100vw（屏的宽 ✓）
     · 旋转基点在左上角 ✓
     · 再沿本地 -y 平移一个自身高度 ✓ 正好铺满整屏 ✓
   （用 JS 只在"真的拿不到系统横屏"时才加这个类 ✓ 不盲目转 ✗） */
.piano-panel.is-faux-land {
    width: 100vh !important;
    height: 100vw !important;
    transform-origin: 0 0 !important;
    transform: rotate(90deg) translateY(-100%) !important;
    overflow: hidden;
    justify-content: center;
}
/* 自转横屏时琴键视口铺满"横屏宽度" ✓ */
.piano-panel.is-faux-land .piano-scroll { max-width: none !important; }



/* ==========================================================================
   工具页样式（所有小工具共用）
   路径：/static/css/page-tool.css
   依赖：/static/css/style.css（基础排版与配色）
   ========================================================================== */

/* ---------- 通用容器 ---------- */
.tool-page,
.tools-index {
    max-width: 1100px;
    margin: 0 auto;
    padding: 0 0 40px;
}

/* ---------- 标题区 ---------- */
.tool-hero {
    margin: 18px 0 20px;
    padding: 0 0 16px;
    border-bottom: 1px solid #e8ece9;
}

.tool-title {
    margin: 0 0 10px;
    font-size: 24px;
    line-height: 1.35;
    color: #1f2d24;
    font-weight: 700;
}

.tool-title i {
    color: #2e7d32;
    margin-right: 6px;
}

.tool-intro {
    margin: 0;
    font-size: 14px;
    line-height: 1.9;
    color: #5a6b60;
}

/* ---------- 工具本体 ---------- */
.tool-body {
    margin: 22px 0 30px;
}

.tool-panel {
    background: #fff;
    border: 1px solid #e8ece9;
    border-radius: 12px;
    padding: 18px;
    box-shadow: 0 1px 3px rgba(0, 0, 0, .04);
}

.tool-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px;
}

.tool-col {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.tool-col-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    margin-bottom: 8px;
    font-size: 13px;
    font-weight: 600;
    color: #37493e;
}

.tool-col-head i {
    color: #2e7d32;
    margin-right: 4px;
}

.tool-col-actions {
    display: flex;
    gap: 6px;
}

.tool-textarea {
    width: 100%;
    min-height: 260px;
    padding: 12px;
    font-family: Consolas, Menlo, "Courier New", monospace;
    font-size: 13px;
    line-height: 1.7;
    color: #2b3a31;
    background: #fbfcfb;
    border: 1px solid #dfe6e1;
    border-radius: 8px;
    resize: vertical;
    box-sizing: border-box;
}

.tool-textarea:focus {
    outline: none;
    border-color: #2e7d32;
    background: #fff;
}

.tool-textarea.out {
    background: #f6f9f7;
}

/* ---------- 按钮 ---------- */
.tool-btn {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 6px 12px;
    font-size: 13px;
    color: #2e7d32;
    background: #fff;
    border: 1px solid #cfe0d3;
    border-radius: 6px;
    cursor: pointer;
    transition: all .15s;
}

.tool-btn:hover {
    background: #eef6f0;
    border-color: #2e7d32;
}

.tool-btn.tiny {
    padding: 4px 10px;
    font-size: 12px;
}

.tool-btn.primary {
    color: #fff;
    background: #2e7d32;
    border-color: #2e7d32;
}

.tool-btn.primary:hover {
    background: #256428;
}

/* ---------- 文件选择 ---------- */
.tool-file {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-top: 10px;
    flex-wrap: wrap;
}

.tool-file-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 12px;
    font-size: 13px;
    color: #37493e;
    background: #f4f7f5;
    border: 1px dashed #c4d4c9;
    border-radius: 6px;
    cursor: pointer;
}

.tool-file-btn:hover {
    border-color: #2e7d32;
    color: #2e7d32;
}

.tool-file-hint {
    font-size: 12px;
    color: #93a49a;
}

/* ---------- 格式选择 ---------- */
.tool-formats {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 16px;
    padding-top: 14px;
    border-top: 1px dashed #e4ebe6;
}

.tool-format {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 7px 14px;
    font-size: 13px;
    color: #37493e;
    background: #f7faf8;
    border: 1px solid #dfe6e1;
    border-radius: 20px;
    cursor: pointer;
    transition: all .15s;
}

.tool-format:hover {
    border-color: #a9cdb4;
}

.tool-format input {
    margin: 0;
}

.tool-format:has(input:checked) {
    color: #fff;
    background: #2e7d32;
    border-color: #2e7d32;
}

/* ---------- 选项行 ---------- */
.tool-options {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 18px;
    margin-top: 14px;
    font-size: 13px;
    color: #4a5c51;
}

.tool-opt {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.tool-opt input[type="number"] {
    width: 62px;
    padding: 5px 8px;
    border: 1px solid #dfe6e1;
    border-radius: 6px;
    font-size: 13px;
}

.tool-opt input[type="text"] {
    width: 130px;
    padding: 5px 8px;
    border: 1px solid #dfe6e1;
    border-radius: 6px;
    font-size: 13px;
}

.tool-stat {
    margin-left: auto;
    font-size: 12px;
    color: #7c8f84;
}

/* ---------- 正文区块 ---------- */
.tool-doc,
.tool-faq,
.tool-related {
    margin: 0 0 26px;
    padding: 20px 22px;
    background: #fff;
    border: 1px solid #e8ece9;
    border-radius: 12px;
}

.tool-doc h2,
.tool-faq h2,
.tool-related h2 {
    margin: 0 0 14px;
    font-size: 18px;
    color: #1f2d24;
}

.tool-doc h3 {
    margin: 18px 0 8px;
    font-size: 15px;
    color: #2e7d32;
}

.tool-doc h3:first-of-type {
    margin-top: 4px;
}

.tool-doc p {
    margin: 0 0 10px;
    font-size: 14px;
    line-height: 1.95;
    color: #4a5c51;
}

.tool-faq dl {
    margin: 0;
}

.tool-faq dt {
    margin-top: 16px;
    font-size: 14px;
    font-weight: 600;
    color: #1f2d24;
}

.tool-faq dt:first-child {
    margin-top: 0;
}

.tool-faq dd {
    margin: 6px 0 0;
    font-size: 14px;
    line-height: 1.9;
    color: #5a6b60;
}

/* ---------- 相关工具 ---------- */
.tool-related ul {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: 12px;
    margin: 0;
    padding: 0;
    list-style: none;
}

.tool-related li a {
    display: block;
    padding: 12px 14px;
    background: #f8fbf9;
    border: 1px solid #e4ebe6;
    border-radius: 8px;
    text-decoration: none;
    transition: all .15s;
}

.tool-related li a:hover {
    border-color: #2e7d32;
    background: #eef6f0;
}

.tool-related strong {
    display: block;
    margin-bottom: 5px;
    font-size: 14px;
    color: #2e7d32;
}

.tool-related span {
    display: block;
    font-size: 12px;
    line-height: 1.7;
    color: #6b7d72;
}

.tool-back {
    margin: 0;
    font-size: 14px;
}

.tool-back a {
    color: #2e7d32;
    text-decoration: none;
}

.tool-back a:hover {
    text-decoration: underline;
}

/* ---------- 工具列表页 ---------- */
.tool-group {
    margin-bottom: 34px;
}

/* 分组标题：左侧色条作为视觉锚点，后面跟一个数量胶囊 */
.tool-group h2 {
    display: flex;
    align-items: center;
    gap: 9px;
    margin: 0 0 16px;
    padding-left: 12px;
    font-size: 17px;
    line-height: 1.3;
    color: #1f2d24;
    border-left: 4px solid #2e7d32;
}

.tool-group-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 22px;
    height: 20px;
    padding: 0 7px;
    border-radius: 999px;
    background: #eef7ef;
    color: #4e7a58;
    font-size: 11px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

/* 一行三列。
   原来写的是 auto-fill + minmax(280px, 1fr)，在 1100px 的容器里会自动排成
   **4 列**，屏幕再宽一点甚至 5 列 —— 一行的张数随宽度忽多忽少，节奏不稳。
   改成 repeat(3, 1fr) 写死三列：每行固定三张、卡片宽度一致。
   ⚠️ 写死列数必须配降级断点，否则窄屏会被挤扁 —— 见紧随其后的两条 @media。 */
.tool-cards {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 14px;
    margin: 0;
    padding: 0;
    list-style: none;
}

@media (max-width: 900px) {
    .tool-cards {
        grid-template-columns: repeat(2, 1fr);
    }
}

@media (max-width: 560px) {
    .tool-cards {
        grid-template-columns: 1fr;
    }
}

/* 卡片：图在上、文在下。
   外壳观感沿用 /wiki/ 的图片卡（圆角 / 描边 / 悬浮抬升 / 图片微放大）；
   标题的白字压封面则照搬首页 .music-card 的做法（见下面 .tool-card h3）。
   原来是「左侧一条竖向渐变 + 整块文字」的文字卡，顶部有实拍图后把那根色条去掉了
   （3px 的条压在图片左边缘会把图切得很脏），视觉锚点改由图本身承担。 */
.tool-card a {
    position: relative;
    /* 纵向 flex：图区固定高度 + 文区撑开剩余空间。
       配合 .tool-card-more 的 margin-top:auto，同一行各卡的「立即使用 →」落在同一条线上。 */
    display: flex;
    flex-direction: column;
    height: 100%;
    background: #fff;
    border: 1px solid #e8ece9;
    border-radius: 16px;
    text-decoration: none;
    overflow: hidden;
    transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease;
}

/* 图区：固定 16:9。
   ⚠️ 高度必须固定 —— 图片高度一旦随内容变化，同一行三张卡就会被彼此拉高，
      又回到「高矮不齐」的老问题上。 */
.tool-card-thumb {
    position: relative;
    display: block;
    flex: none;              /* 不许被压扁：图区高度只由 aspect-ratio 决定 */
    aspect-ratio: 16 / 9;
    overflow: hidden;
    background: #eef4ef;
    z-index: 1;              /* 图区自成一个图层，标题（.tool-card-body z-index:2）压在它上面 */
}

/* 蒙版：全幅压一层极淡的薄纱 + 底部加深。
   数值与首页 .music-card .card-img::before **完全一致** ——
   白字直接压在照片上是看不清的（亮部尤其糊），这层是白色标题可读性的保证。 */
.tool-card-thumb::before {
    content: "";
    position: absolute;
    top: 0; right: 0; bottom: 0; left: 0;   /* 老浏览器不认 inset ✗ 先给等价的 ✓ 下一行覆盖 ✓ */
    inset: 0;
    pointer-events: none;
    z-index: 3;
    background: linear-gradient(180deg,
        rgba(10, 22, 12, .12) 0%,
        rgba(10, 22, 12, .16) 50%,
        rgba(10, 22, 12, .45) 100%);
}
.tool-card-thumb img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform .4s ease;
}
.tool-card a:hover .tool-card-thumb img {
    transform: scale(1.06);
}

/* 文区 */
.tool-card-body {
    position: relative;
    z-index: 2;              /* 压在 .tool-card-thumb（z-index:1）之上，标题才能浮在照片上 */
    display: flex;
    flex-direction: column;
    flex: 1;
    padding: 14px 18px 16px;
}

.tool-card a:hover {
    border-color: #a5d6a7;
    box-shadow: 0 12px 26px -12px rgba(46, 125, 50, .4);
    transform: translateY(-3px);
}

/* 标题：白字压在封面底部 —— 和首页 .music-card .card-body h3 同一套做法。
   用负上边距把 h3 整体提上去盖住图片下沿：
     标题顶边 = 图片底边 + 文区 padding-top(14px) + margin-top(-70px) = 图片底边 - 56px
     再配合 min-height:44px + align-items:flex-end，文字底边正好落在图片底边上方 12px。
   ⚠️ 这个 -70px 和 .tool-card-body 的 padding-top 是绑定的，改其中一个必须同步改另一个。
   另外：工具名最长 10 来个字、卡片宽约 357px，一行放得下，所以不用做两行截断。 */
.tool-card h3 {
    display: flex;
    align-items: flex-end;
    margin: -70px 0 18px;
    min-height: 44px;
    font-size: 16px;
    font-weight: 600;
    line-height: 1.3;
    color: #fff;
    text-shadow: 0 1px 3px rgba(0, 0, 0, .45);
    overflow: hidden;
}

.tool-card p {
    margin: 0 0 12px;
    font-size: 13px;
    line-height: 1.8;
    color: #6b7d72;
}

/* 「立即使用 →」：默认收紧，hover 时箭头滑出去一点，暗示"点进去" */
.tool-card-more {
    display: inline-flex;
    align-items: center;
    align-self: flex-start;   /* 别被 flex 的 stretch 拉满整行宽度 */
    margin-top: auto;         /* 推到卡底，同一行的这条链接就对齐了 */
    gap: 5px;
    font-size: 12px;
    color: #93a49a;
    transition: gap .18s ease, color .18s ease;
}

.tool-card a:hover .tool-card-more {
    gap: 10px;
    color: #2e7d32;
}

.tool-empty {
    padding: 30px;
    text-align: center;
    color: #8a9a90;
}

/* ---------- 格式详解（长尾落地页入口，仅主工具页显示） ---------- */
.tool-variants {
    border-left: 3px solid #2e7d32;
}

/* ---------- 开发中占位（工具骨架，仅本地可见） ---------- */
.tool-wip {
    padding: 52px 20px;
    text-align: center;
}

.tool-wip-icon {
    margin-bottom: 16px;
    font-size: 40px;
    color: #c4d4c9;
}

.tool-wip h2 {
    margin: 0 0 10px;
    font-size: 18px;
    color: #37493e;
}

.tool-wip p {
    max-width: 560px;
    margin: 0 auto 8px;
    font-size: 13px;
    line-height: 1.9;
    color: #7c8f84;
}

.tool-wip .tool-wip-note {
    margin-top: 14px;
    font-size: 12px;
    color: #a3b3a9;
}

/* ---------- 工具卡片上的「开发中」徽标 ----------
   原来跟在标题后面（margin-left），现在压在封面右上角当角标 ——
   图片卡里标题只留一行字更清爽，状态用角标反而更显眼。 */
.tool-card-badge {
    position: absolute;
    top: 10px;
    right: 10px;
    z-index: 4;              /* 要压在图区蒙版（z-index:3）之上，否则会被薄纱罩住 */
    padding: 2px 9px;
    font-size: 11px;
    font-weight: 600;
    line-height: 1.6;
    color: #6b5c1e;
    background: rgba(253, 246, 221, .95);
    border: 1px solid #f0e3b0;
    border-radius: 999px;
}

/* ---------- 本地开发提示条 ---------- */
.tool-devnotice {
    margin: 0 0 20px;
    padding: 10px 14px;
    font-size: 13px;
    line-height: 1.8;
    color: #6b5c1e;
    background: #fdf6dd;
    border: 1px solid #f0e3b0;
    border-radius: 8px;
}

/* ---------- 偏移控制条 ---------- */
.shift-bar {
    align-items: center;
}

.shift-label {
    margin-right: 4px;
    font-size: 13px;
    font-weight: 600;
    color: #37493e;
}

.shift-label i {
    margin-right: 4px;
    color: #2e7d32;
}

.shift-unit {
    font-size: 12px;
    color: #93a49a;
    margin-right: 2px;
}

/* ---------- 下拉选择（工具选项） ---------- */
.tool-select {
    padding: 5px 10px;
    font-size: 13px;
    color: #37493e;
    background: #f7faf8;
    border: 1px solid #dfe6e1;
    border-radius: 6px;
    cursor: pointer;
}

.tool-select:focus {
    outline: none;
    border-color: #2e7d32;
}

/* ---------- 可选提示文字 ---------- */
.opt-hint {
    font-size: 12px;
    color: #93a49a;
}

/* ---------- 校验报告 ---------- */
.check-report {
    margin: 0 0 26px;
    padding: 18px 20px;
    background: #fff;
    border: 1px solid #e8ece9;
    border-radius: 12px;
}

.check-report h3 {
    margin: 0 0 12px;
    font-size: 16px;
    color: #1f2d24;
}

.check-report h3 i {
    margin-right: 5px;
    color: #2e7d32;
}

.check-body {
    font-size: 13px;
    color: #5a6b60;
}

.check-summary {
    margin: 0 0 8px;
    font-size: 14px;
    color: #37493e;
}

.check-summary strong {
    color: #c0392b;
}

.check-chips {
    margin: 0 0 10px;
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

.check-chip {
    display: inline-block;
    padding: 2px 10px;
    font-size: 12px;
    border-radius: 12px;
}

.check-chip.err  { color: #b02a1f; background: #fdecea; border: 1px solid #f5c6c0; }
.check-chip.warn { color: #8a6d1e; background: #fdf6dd; border: 1px solid #f0e3b0; }
.check-chip.info { color: #5a6b60; background: #f2f5f3; border: 1px solid #e0e7e2; }

.check-info {
    margin: 0 0 10px;
    font-size: 12px;
    color: #8a9a90;
}

.check-ok {
    margin: 0 0 8px;
    font-size: 14px;
    color: #2e7d32;
}

.check-ok i {
    margin-right: 5px;
}

.check-list {
    margin: 0;
    padding: 0;
    list-style: none;
    max-height: 380px;
    overflow-y: auto;
}

.check-item {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 8px;
    padding: 7px 0;
    border-bottom: 1px dashed #eef2ef;
}

.check-item:last-child {
    border-bottom: none;
}

.check-tag {
    flex: none;
    padding: 1px 8px;
    font-size: 11px;
    border-radius: 4px;
}

.check-item.err  .check-tag { color: #b02a1f; background: #fdecea; }
.check-item.warn .check-tag { color: #8a6d1e; background: #fdf6dd; }
.check-item.info .check-tag { color: #5a6b60; background: #f2f5f3; }

.check-line {
    flex: none;
    font-size: 12px;
    color: #93a49a;
}

.check-item code {
    flex: 1;
    min-width: 160px;
    padding: 2px 6px;
    font-family: Consolas, Menlo, monospace;
    font-size: 12px;
    color: #4a5c51;
    background: #f8faf9;
    border-radius: 4px;
    word-break: break-all;
}

/* ---------- 统计面板（歌词统计等工具用） ---------- */
.stat-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
    gap: 10px;
    padding: 14px;
    background: #f8fbf9;
    border: 1px solid #e4ebe6;
    border-radius: 10px;
}

.stat-cell {
    text-align: center;
    padding: 8px 4px;
    background: #fff;
    border: 1px solid #eef2ef;
    border-radius: 8px;
}

.stat-cell b {
    display: block;
    font-size: 20px;
    line-height: 1.3;
    color: #2e7d32;
    font-weight: 700;
}

.stat-cell span {
    display: block;
    margin-top: 3px;
    font-size: 11px;
    color: #8a9a90;
}

.stat-lists .check-report h3 {
    font-size: 15px;
}

/* ---------- 结果展示（和弦/音阶查询等工具用） ---------- */
.tool-result {
    margin: 18px 0 0;
}

.result-head {
    margin: 0 0 10px;
    font-size: 15px;
    font-weight: 600;
    color: #1f2d24;
}

.result-table {
    width: 100%;
    margin: 0 0 14px;
    border-collapse: collapse;
    font-size: 13px;
    background: #fff;
    border: 1px solid #e4ebe6;
    border-radius: 10px;
    overflow: hidden;
}

.result-table th,
.result-table td {
    padding: 9px 12px;
    text-align: center;
    border-bottom: 1px solid #eef2ef;
}

.result-table th {
    width: 92px;
    text-align: left;
    font-weight: 600;
    color: #5a6b60;
    background: #f8fbf9;
}

.result-table td {
    font-family: Consolas, Menlo, monospace;
    font-size: 15px;
    color: #2e7d32;
}

.result-table-rows th {
    width: auto;
    text-align: center;
}

.result-table tr:last-child th,
.result-table tr:last-child td {
    border-bottom: none;
}

/* ---------- 节拍器 ---------- */
.metro-bar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 14px;
    margin: 18px 0 0;
    padding: 16px 18px;
    background: #f8fbf9;
    border: 1px solid #e4ebe6;
    border-radius: 10px;
}

.beat-dots {
    display: flex;
    align-items: center;
    gap: 10px;
}

.beat-dot {
    display: block;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: #dfe6e1;
    transition: background .05s, transform .05s;
}

.beat-dot.first {
    border: 2px solid #a9cdb4;
}

.beat-dot.on {
    background: #2e7d32;
    transform: scale(1.25);
}

.beat-dot.first.on {
    background: #c0392b;
    border-color: #c0392b;
}

.tool-range {
    flex: 1;
    min-width: 140px;
    accent-color: #2e7d32;
    cursor: pointer;
}

.tool-num {
    width: 74px !important;
}

.tuning-label {
    margin-left: 6px;
}

.tuning-btns {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

/* ---------- 乐理小测验 ---------- */
.quiz-card {
    margin: 18px 0 0;
    padding: 22px;
    background: #fff;
    border: 1px solid #e8ece9;
    border-radius: 12px;
}

.quiz-q {
    margin: 0 0 18px;
    font-size: 17px;
    line-height: 1.7;
    color: #1f2d24;
    font-weight: 600;
}

.quiz-options {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: 10px;
}

.quiz-opt {
    padding: 13px 12px;
    font-family: Consolas, Menlo, monospace;
    font-size: 16px;
    color: #2b3a31;
    background: #f8fbf9;
    border: 1px solid #dfe6e1;
    border-radius: 8px;
    cursor: pointer;
    transition: all .12s;
}

.quiz-opt:hover:not(:disabled) {
    border-color: #2e7d32;
    background: #eef6f0;
}

.quiz-opt:disabled {
    cursor: default;
    opacity: .75;
}

.quiz-opt.right {
    color: #fff;
    background: #2e7d32;
    border-color: #2e7d32;
    opacity: 1;
}

.quiz-opt.bad {
    color: #fff;
    background: #c0392b;
    border-color: #c0392b;
    opacity: 1;
}

.quiz-feedback {
    margin: 14px 0 0;
    font-size: 14px;
    min-height: 20px;
}

.quiz-feedback.right { color: #2e7d32; }
.quiz-feedback.bad   { color: #c0392b; }

/* ---------- 合唱分行列表 ---------- */
.duet-list {
    margin: 14px 0 0;
}

.duet-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 7px 10px;
    border-bottom: 1px dashed #eef2ef;
    border-left: 3px solid transparent;
}

.duet-row.duet-m {
    border-left-color: #2e7d32;
    background: #f6fbf7;
}

.duet-row.duet-f {
    border-left-color: #c2185b;
    background: #fdf6f9;
}

.duet-row.duet-b {
    border-left-color: #1565c0;
    background: #f5f9fe;
}

.duet-ts {
    flex: none;
    min-width: 62px;
    font-family: Consolas, Menlo, monospace;
    font-size: 12px;
    color: #93a49a;
}

.duet-noTs {
    min-width: 0;
    width: 0;
}

.duet-text {
    flex: 1;
    font-size: 14px;
    color: #2b3a31;
    word-break: break-all;
}

.duet-role {
    flex: none;
    min-width: 66px;
    padding: 4px 10px;
    font-size: 12px;
    color: #5a6b60;
    background: #f2f5f3;
    border: 1px solid #e0e7e2;
    border-radius: 6px;
    cursor: pointer;
    transition: all .12s;
}

.duet-role:hover {
    color: #2e7d32;
    border-color: #2e7d32;
}

/* ---------- ABC 记谱法编辑器 ---------- */
.abc-input {
    min-height: 180px;
    font-family: Consolas, Menlo, monospace;
    font-size: 13px;
}

.abc-paper-wrap {
    padding: 12px;
    background: #fff;
    border: 1px solid #e4ebe6;
    border-radius: 10px;
    overflow-x: auto;
}

.abc-paper {
    min-height: 120px;
}

.abc-paper svg {
    max-width: 100%;
    height: auto;
}

/* abcjs 自带的播放控件容器（这里只用作音频宿主，隐藏其 UI） */
.abc-audio {
    height: 0;
    overflow: hidden;
}

.abc-audio .abcjs-inline-audio {
    display: none;
}

/* ---------- 五线谱预览 ---------- */
.score-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    margin: 0 0 10px;
    padding-bottom: 10px;
    border-bottom: 1px dashed #eef2ef;
}

.score-sep {
    flex: 1;
}

.score-zoom {
    min-width: 46px;
    text-align: center;
    font-size: 12px;
    color: #5a6b60;
    font-variant-numeric: tabular-nums;
}

.score-paper-wrap {
    min-height: 180px;
    padding: 10px;
    background: #fff;
    border: 1px solid #e4ebe6;
    border-radius: 10px;
    overflow-x: auto;
}

.score-paper svg {
    display: block;
}

.score-empty {
    margin: 0;
    padding: 60px 20px;
    text-align: center;
    font-size: 14px;
    color: #93a49a;
}

/* ---------- 敏感词过滤 ---------- */
.wf-custom-label {
    display: block;
    margin: 12px 0 6px;
    font-size: 13px;
    color: #5a6b60;
}

.wf-custom {
    min-height: 68px;
}

.wf-output {
    min-height: 220px;
    max-height: 460px;
    padding: 12px 14px;
    font-size: 14px;
    line-height: 1.9;
    color: #2b3a31;
    background: #fbfcfb;
    border: 1px solid #e4ebe6;
    border-radius: 10px;
    white-space: pre-wrap;
    word-break: break-word;
    overflow: auto;
}

.wf-hit {
    padding: 1px 2px;
    color: #b3261e;
    background: #ffe8e6;
    border-bottom: 2px solid #f2a09a;
    border-radius: 3px;
}

.wf-top {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    margin-top: 14px;
}

.wf-top-label {
    font-size: 13px;
    color: #5a6b60;
    margin-right: 2px;
}

.wf-chip {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 3px 9px;
    font-size: 13px;
    color: #2b3a31;
    background: #fff2f0;
    border: 1px solid #f4cdc8;
    border-radius: 20px;
}

.wf-n {
    color: #b3261e;
    font-size: 12px;
}

/* ---------- 简繁转换 / 拼音注音 ---------- */
.zh-preview-wrap {
    margin-top: 14px;
}

.zh-preview-head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 10px;
    margin-bottom: 6px;
}

.zh-note {
    font-size: 12px;
    color: #93a49a;
}

.zh-preview {
    max-height: 260px;
    padding: 12px 14px;
    font-size: 14px;
    line-height: 1.9;
    background: #fbfcfb;
    border: 1px solid #e4ebe6;
    border-radius: 10px;
    white-space: pre-wrap;
    word-break: break-word;
    overflow: auto;
}

.zh-plain {
    color: #2b3a31;
}

.zh-none {
    color: #93a49a;
}

.zh-hit {
    padding: 0 1px;
    color: #b3261e;
    background: #ffe8e6;
    border-radius: 3px;
}

.py-output {
    min-height: 220px;
    max-height: 460px;
    padding: 12px 14px;
    font-size: 14px;
    line-height: 1.9;
    color: #2b3a31;
    background: #fbfcfb;
    border: 1px solid #e4ebe6;
    border-radius: 10px;
    white-space: pre-wrap;
    word-break: break-word;
    overflow: auto;
}

.py-line {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    margin-bottom: 4px;
}

.py-cell {
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    min-width: 1.6em;
}

.py-empty {
    min-width: 0.6em;
}

.py-r {
    font-size: 11px;
    font-weight: 400;
    font-style: normal;
    line-height: 1.3;
    color: #5b8c5a;
    white-space: nowrap;
}

.py-c {
    font-size: 17px;
    font-style: normal;
    line-height: 1.45;
    color: #2b3a31;
}

/* ---------- LRC 在线编辑器 ---------- */
.ed-audio {
    width: 100%;
    margin-bottom: 6px;
}

.ed-speed,
.ed-actions,
.ed-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
}

.ed-speed {
    margin: 4px 0 10px;
}

.ed-actions {
    margin-top: 8px;
}

.ed-toolbar {
    margin-bottom: 10px;
    padding-bottom: 10px;
    border-bottom: 1px dashed #eef2ef;
}

.ed-list {
    max-height: 520px;
    margin-top: 4px;
    overflow-y: auto;
    border: 1px solid #e4ebe6;
    border-radius: 10px;
    background: #fff;
}

.ed-row {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 5px 10px;
    border-left: 3px solid transparent;
    border-bottom: 1px dashed #f1f5f2;
    cursor: pointer;
}

.ed-row:last-child {
    border-bottom: 0;
}

.ed-row.playing {
    background: #f2f9f4;
}

.ed-row.active {
    border-left-color: #2e7d32;
    background: #eaf6ed;
}

.ed-row.untimed .ed-time {
    color: #c2cdc6;
}

.ed-idx {
    flex: none;
    min-width: 26px;
    font-size: 12px;
    color: #a8b5ac;
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.ed-time {
    flex: none;
    padding: 2px 6px;
    font-family: Consolas, Menlo, monospace;
    font-size: 12px;
    color: #2e7d32;
    background: #f2f5f3;
    border: 1px solid #e0e7e2;
    border-radius: 5px;
    cursor: pointer;
}

.ed-time:hover {
    border-color: #2e7d32;
}

.ed-text {
    flex: 1;
    font-size: 14px;
    color: #2b3a31;
    word-break: break-all;
}

.ed-nudge {
    display: none;
    flex: none;
    gap: 3px;
}

.ed-row.active .ed-nudge {
    display: inline-flex;
}

.ed-row.active .ed-nudge .tool-btn {
    padding: 2px 6px;
    font-size: 11px;
}

.ed-x {
    color: #b3261e;
}

.ed-empty {
    margin: 0;
    padding: 40px 16px;
    text-align: center;
    font-size: 14px;
    color: #93a49a;
}

/* ---------- ID3 标签编辑器 ---------- */
.id3-info {
    margin-bottom: 10px;
    padding: 10px 12px;
    background: #fbfcfb;
    border: 1px solid #e4ebe6;
    border-radius: 10px;
}

.id3-info p {
    margin: 0 0 4px;
    font-size: 13px;
    line-height: 1.7;
}

.id3-info p:last-child {
    margin-bottom: 0;
}

.id3-ok {
    color: #2e7d32;
}

.id3-warn {
    color: #b3261e;
}

.id3-dim {
    color: #7c8b81;
}

.id3-form {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 8px 12px;
    margin-bottom: 4px;
}

.id3-field label {
    display: block;
    margin-bottom: 3px;
    font-size: 12px;
    color: #5a6b60;
}

.id3-field code {
    color: #a8b5ac;
    font-size: 11px;
}

.tool-input {
    width: 100%;
    padding: 7px 10px;
    font-size: 14px;
    color: #2b3a31;
    background: #fff;
    border: 1px solid #e0e7e2;
    border-radius: 8px;
    box-sizing: border-box;
}

.tool-input:focus {
    outline: none;
    border-color: #2e7d32;
}

.id3-lyrics {
    min-height: 110px;
}

.id3-extra {
    display: grid;
    grid-template-columns: minmax(0, 220px) minmax(0, 1fr);
    gap: 16px;
    margin-top: 16px;
}

.id3-cover-box {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    margin-top: 6px;
    padding: 10px;
    min-height: 120px;
    background: #fbfcfb;
    border: 1px solid #e4ebe6;
    border-radius: 10px;
}

.id3-cover-img {
    max-width: 100%;
    max-height: 200px;
    border-radius: 6px;
}

.id3-frame-list {
    max-height: 300px;
    margin-top: 6px;
    overflow: auto;
    border: 1px solid #e4ebe6;
    border-radius: 10px;
    background: #fff;
}

.id3-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 13px;
}

.id3-table td {
    padding: 4px 8px;
    border-bottom: 1px dashed #f1f5f2;
    vertical-align: top;
}

.id3-fid {
    width: 62px;
}

.id3-fid code {
    color: #2e7d32;
    font-size: 12px;
}

.id3-fsize {
    width: 70px;
    color: #a8b5ac;
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.id3-fval {
    color: #2b3a31;
    word-break: break-all;
}

/* ---------- 音频元数据批量导出 ---------- */
.me-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    margin-bottom: 10px;
}

.me-table-wrap {
    margin-top: 10px;
    overflow-x: auto;
    border: 1px solid #e4ebe6;
    border-radius: 10px;
    background: #fff;
}

.me-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 13px;
    white-space: nowrap;
}

.me-table th,
.me-table td {
    padding: 6px 10px;
    text-align: left;
    border-bottom: 1px dashed #f1f5f2;
}

.me-table thead th {
    position: sticky;
    top: 0;
    z-index: 1;
    font-weight: 600;
    color: #3f5147;
    background: #f7faf8;
    border-bottom: 1px solid #e4ebe6;
    cursor: pointer;
    user-select: none;
}

.me-table thead th:hover {
    color: #2e7d32;
}

.me-table tbody tr:hover {
    background: #fafcfb;
}

.me-table tbody tr.me-off {
    color: #b9c4bc;
}

.me-table tbody tr.me-off .me-file {
    color: #b9c4bc;
}

.me-file {
    color: #2b3a31;
    font-weight: 500;
}

.me-sel {
    width: 34px;
    text-align: center;
}

.me-th {
    white-space: nowrap;
}

/* ---------- 响应式 ---------- */
@media (max-width: 760px) {
    .tool-grid {
        grid-template-columns: 1fr;
    }

    .tool-textarea {
        min-height: 200px;
    }

    .tool-panel,
    .tool-doc,
    .tool-faq,
    .tool-related {
        padding: 14px;
        border-radius: 10px;
    }

    .tool-title {
        font-size: 19px;
    }

    .tool-stat {
        width: 100%;
        margin-left: 0;
    }
}

/* ============================================================
   v1.1 视觉修订（2026-10-07）
   ------------------------------------------------------------
   1) 原生 radio / checkbox 一直裸露在外，和胶囊标签叠在一起，
      视觉上就像"没做样式"。这里把它们视觉隐藏，改由胶囊本身的
      选中态 + 对勾来表达，同时保留键盘可达性与焦点环。
   ============================================================ */

.tool-formats .tool-format input,
.tool-options .tool-format input {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: 0;
    opacity: 0;
    pointer-events: none;
}

.tool-format:has(input:focus-visible) {
    outline: 2px solid #2e7d32;
    outline-offset: 2px;
}

/* 选中态加一个对勾，比单纯变色更明确 */
.tool-format:has(input:checked)::before {
    content: "\2713";
    margin-right: 1px;
    font-size: 12px;
    font-weight: 700;
    line-height: 1;
}

.tool-format {
    position: relative;
    user-select: none;
}

.tool-format:has(input:checked) {
    box-shadow: 0 1px 3px rgba(46, 125, 50, .28);
}

/* ---------- 检查报告：条目卡片化、按严重度着色 ---------- */
.check-report {
    border-radius: 14px;
    box-shadow: 0 1px 2px rgba(31, 45, 36, .04);
}

.check-list {
    margin: 0;
    padding: 0;
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.check-item {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 9px 12px;
    background: #fbfcfb;
    border: 1px solid #eef2ef;
    border-left: 3px solid #cfd8d2;
    border-radius: 8px;
    line-height: 1.6;
}

.check-item.error,
.check-item.bad {
    border-left-color: #c0392b;
    background: #fdf6f5;
}

.check-item.warn,
.check-item.warning {
    border-left-color: #e0a800;
    background: #fffbf0;
}

.check-item.info {
    border-left-color: #2f6fb0;
    background: #f5f9fe;
}

.check-tag {
    flex: none;
    padding: 2px 8px;
    font-size: 12px;
    color: #fff;
    background: #9aa8a0;
    border-radius: 20px;
}

.check-item.error .check-tag,
.check-item.bad .check-tag {
    background: #c0392b;
}

.check-item.warn .check-tag,
.check-item.warning .check-tag {
    background: #d99b00;
}

.check-item.info .check-tag {
    background: #2f6fb0;
}

.check-line {
    flex: none;
    font-family: Consolas, Menlo, monospace;
    font-size: 12px;
    color: #93a49a;
}

.check-chip {
    padding: 3px 10px;
    font-size: 12px;
    border-radius: 20px;
    border: 1px solid #e0e7e2;
    background: #f7faf8;
    color: #37493e;
}

.check-ok {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 0 0 6px;
    padding: 10px 12px;
    font-size: 14px;
    color: #2e7d32;
    background: #f2f9f4;
    border: 1px solid #cfe6d6;
    border-radius: 8px;
}

.check-summary strong {
    font-size: 16px;
}

/* ============================================================
   v1.1 视觉修订（续）
   3) 打轴器行列表：更像"编辑器"，选中行更醒目、时间戳更像按钮
   4) 合唱分行的"全部设为"控件组：做成分段控件
   5) id3 编辑器的两个外层容器之前漏了样式
   ============================================================ */

.ed-list {
    border-radius: 12px;
    box-shadow: inset 0 0 0 1px rgba(228, 235, 230, .6);
}

.ed-row {
    padding: 7px 12px;
    transition: background .12s, border-color .12s;
}

.ed-row:hover {
    background: #f7faf8;
}

.ed-row.active {
    border-left-color: #2e7d32;
    background: #eef8f0;
    box-shadow: inset 0 0 0 1px #cfe6d6;
}

.ed-row.active .ed-idx {
    color: #2e7d32;
    font-weight: 600;
}

.ed-time {
    min-width: 68px;
    padding: 3px 8px;
    font-size: 12.5px;
    letter-spacing: .2px;
    transition: all .12s;
}

.ed-time:hover {
    color: #fff;
    background: #2e7d32;
    border-color: #2e7d32;
}

.ed-nudge {
    padding-left: 4px;
    border-left: 1px dashed #e4ebe6;
}

.ed-row.active .ed-nudge .tool-btn {
    padding: 3px 7px;
    font-family: Consolas, Menlo, monospace;
}

/* ---------- 合唱分行：把"全部设为"做成分段控件 ---------- */
.tool-options {
    margin-top: 14px;
    padding: 10px 12px;
    gap: 8px;
    background: #f9fbfa;
    border: 1px solid #e8ece9;
    border-radius: 12px;
}

.tool-options > .tool-btn {
    min-width: 62px;
    padding: 6px 12px;
    border-radius: 8px;
}

.tool-options > .tool-btn:hover {
    color: #2e7d32;
    border-color: #2e7d32;
    background: #fff;
}

.tool-options .tool-stat {
    flex: 1 1 100%;
    margin-top: 2px;
}

/* ---------- id3 编辑器漏掉的两个容器 ---------- */
.id3-cover,
.id3-frames {
    min-width: 0;
}

.id3-cover > .shift-label,
.id3-frames > .shift-label {
    display: inline-block;
    margin-bottom: 2px;
}

/* 窄屏时封面与帧列表改为上下排列 */
@media (max-width: 760px) {
    .id3-extra {
        grid-template-columns: 1fr;
    }

    .id3-form {
        grid-template-columns: 1fr;
    }
}

/* ============================================================
   v1.3：工具页的「格式详解」区块（长尾词入口）
   ------------------------------------------------------------
   这是长尾页唯一的站内入口，必须显眼；但也不能排成一长列
   占掉整屏，所以用自适应网格、2 列排布。
   ============================================================ */

.tool-variants {
    border: 1px solid #d8e6dc;
    border-left: 4px solid #2e7d32;
    background: linear-gradient(180deg, #f7fbf8 0%, #ffffff 60%);
}

.tool-variants h2 {
    display: flex;
    align-items: center;
    gap: 8px;
}

.tool-variants h2::after {
    content: "";
    flex: 1;
    height: 1px;
    background: #e4ebe6;
}

.tool-variants ul {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px;
    margin: 0;
    padding: 0;
    list-style: none;
}

.tool-variants li {
    margin: 0;
}

.tool-variants li a {
    display: flex;
    flex-direction: column;
    gap: 4px;
    height: 100%;
    padding: 12px 14px;
    text-decoration: none;
    background: #fff;
    border: 1px solid #e4ebe6;
    border-radius: 10px;
    transition: border-color .15s, box-shadow .15s, transform .15s;
}

.tool-variants li a:hover {
    border-color: #2e7d32;
    box-shadow: 0 2px 10px rgba(46, 125, 50, .12);
    transform: translateY(-1px);
}

.tool-variants li a strong {
    font-size: 14px;
    font-weight: 600;
    color: #1f2d24;
    line-height: 1.5;
}

.tool-variants li a strong::after {
    content: " \2192";
    color: #7fb98a;
}

.tool-variants li a span {
    font-size: 12.5px;
    line-height: 1.7;
    color: #6b7c72;
}

@media (max-width: 760px) {
    .tool-variants ul {
        grid-template-columns: 1fr;
    }
}

/* ============================================================
   在线乐器 · 彩色键盘（钢琴 / 吉他 / 贝斯 / 鼓 共用）
   ------------------------------------------------------------
   琴键用 flex + 绝对定位：白键均分宽度，黑键按 --at（左侧白键数）
   定位到两个白键的交界处。黑键不参与 flex 布局，所以不会挤走白键。
   ============================================================ */

.piano-panel {
    padding: 18px 18px 22px;
    /* 白键宽度（固定）：琴键尺寸、视口上限都由它推导，改一个值即可整体缩放 */
    --pk-w: 46px;
}

.piano-bar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 12px;
    margin-bottom: 10px;
}

.piano-vol {
    display: flex;
    align-items: center;
    gap: 8px;
}

.piano-vol .tool-range {
    width: 110px;
}

/* ---------- MIDI 播放控制（播放/暂停 + 进度条 + 时间） ---------- */
.piano-seek-wrap {
    display: flex;
    align-items: center;
    gap: 8px;
    flex: 1 1 220px;          /* 占满剩余宽度：进度条短了不好拖 */
    min-width: 180px;
}
.piano-seek-wrap .piano-time {
    font-variant-numeric: tabular-nums;
    font-size: 12px;
    color: #6b8a6b;
    white-space: nowrap;
}
.piano-seek {
    flex: 1 1 auto;
    width: auto;
    min-width: 120px;
}
.piano-seek:disabled { opacity: .5; }

.piano-check {
    display: flex;
    align-items: center;
    gap: 6px;
}

/* 左右滚动按钮：手机上没有它就只能靠手指蹭滚动条，很难用。
   88 键必须横向浏览，所以这两个按钮是移动端的刚需。 */
.piano-nav {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 0 0 10px;
}

.piano-nav-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    padding: 0;
    border: 1px solid #d7e3da;
    border-radius: 50%;
    background: #fff;
    color: #3f6b4a;
    font-size: 14px;
    cursor: pointer;
    transition: background .16s, border-color .16s, transform .12s;
}

.piano-nav-btn:hover {
    background: #f2f8f3;
    border-color: #a5d6a7;
}

.piano-nav-btn:active { transform: scale(.94); }

/* ★ 2026-10-20（用户要求）：钢琴显示区的**宽度放大 / 缩小**按钮 ✓
   只动 --pk-max（视口宽）✗ 高度交给 boot-ratio 的 16:9 ✓
   用符号 − / + ✗ 不用 fa 图标（这套 Font Awesome 子集里可能没字形 ✗ 会变方块 ✓） */
.piano-nav-btn.pk-wbtn {
    font-size: 20px;
    font-weight: 600;
    line-height: 1;
    font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
    user-select: none;
}
/* 手机 / 窄屏：显示区本来就是 100% 宽（min(100%, …) 兜住）⇒ 加减没有意义 ✓ 直接藏起来 */
@media (max-width: 900px) {
    .piano-nav-btn.pk-wbtn { display: none; }
}

/* 滚到两端时箭头置灰，避免点了没反应让人以为是坏的 */
.piano-nav-btn:disabled {
    opacity: .38;
    cursor: default;
    transform: none;
    background: #f6f8f6;
    border-color: #e6ebe7;
    color: #9aab9f;
}

.piano-nav-range {
    font-size: 12px;
    font-variant-numeric: tabular-nums;
    color: #8a9c90;
    min-width: 84px;
    text-align: center;
}

.piano-hint {
    margin: 0 0 12px;
    font-size: 12px;
    line-height: 1.7;
    color: #93a49a;
}

/* ============================================================
   ★★★ 2026-10-21 十轮（用户要求）：**主页面「简谱设置」条**
   ------------------------------------------------------------
   用户原话：「每小节的拍数、调号这两个…给它拿出来，放到主页面里面」
             「刚开始导入 MIDI 的时候就检查一下哪些地方疑似转调，
              然后标出来多少小节转调什么，不对可以手动改」
   ⇒ 原来 `.piano-hint`（电脑键盘提示）那一行换成这条 ✓
   结构：标题 + 三个下拉 + 开关 + 按钮 + 提示 + 转调段列表（自动换行 ✓）
   ★ 配色跟这个页面其它控件一致（白底 / 淡绿边 / 深绿字）✓
   ============================================================ */
.pk-keybar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px 14px;
    margin: 0 0 12px;
    padding: 8px 12px;
    font-size: 12px;
    line-height: 1.6;
    color: #5b6f63;
    background: #f6faf7;
    border: 1px solid #dbe8de;
    border-radius: 8px;
}

/* ★ [hidden] 必须显式写：上面 display:flex 会盖掉浏览器默认的 display:none ✗ */
.pk-keybar[hidden] { display: none; }

.pk-kb-title {
    font-weight: 600;
    color: #2e7d32;
}

.pk-kb-item {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    white-space: nowrap;
}

.pk-kb-item select {
    max-width: 24ch;
    padding: 3px 6px;
    font-size: 12px;
    color: #2f4a3a;
    background: #fff;
    border: 1px solid #cfe0d3;
    border-radius: 5px;
}

.pk-kb-chk { gap: 4px; cursor: pointer; }

.pk-kb-btn {
    padding: 4px 10px !important;
    font-size: 12px !important;
}

.pk-kb-hint {
    font-size: 11px;
    color: #8a9c92;
}

.pk-kb-hint.pk-kb-ok { color: #2e7d32; }

/* 转调段：一行一个"胶囊"，多了自动换行 ✓ */
.pk-modlist {
    flex: 1 0 100%;
    display: flex;
    flex-wrap: wrap;
    gap: 6px 10px;
    margin: 2px 0 0;
    padding: 0;
    list-style: none;
}

.pk-modlist[hidden] { display: none; }

.pk-mod-item {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 3px 8px 3px 10px;
    font-size: 11px;
    background: #fff;
    border: 1px solid #dbe8de;
    border-radius: 999px;
}

.pk-mod-base { background: #f0f7f2; }

.pk-mod-bar { color: #5b6f63; }

.pk-mod-item select {
    padding: 2px 4px;
    font-size: 11px;
    color: #2f4a3a;
    background: #fff;
    border: 1px solid #cfe0d3;
    border-radius: 4px;
}

.pk-mod-tag { color: #93a49a; }

/* 全屏 / 仿全屏里不占地方（跟原来的 .piano-hint 一样 ✓）*/
.piano-panel:fullscreen .pk-keybar,
.piano-panel:-webkit-full-screen .pk-keybar,
.piano-panel.is-faux-full .pk-keybar {
    display: none !important;
}

/* 琴键视口 = 琴体外壳。深色渐变 + 内阴影模拟真实钢琴的木框/铁排，
   让白键"陷"在里面，比纯白背景更有层次。 */
.piano-scroll {
    position: relative;
    overflow-x: auto;
    overflow-y: hidden;
    padding: 14px 12px 12px;
    /* 视口 = "钢琴的显示区域"，宽是**固定**的（见上面缩放的说明）：
       992px = 21 个白键 × 46px + 26px 内边距，窄屏就占满宽度。
       琴键宽度由 --pk-w 控制（缩放按钮改它），所以同一块区域里
       可以塞进 21 个白键（默认）到 52 个白键（缩到最小，88 键全见）。 */
    max-width: min(100%, var(--pk-max, 992px));
    margin-left: auto;
    margin-right: auto;
    border-radius: 12px;
    background: linear-gradient(180deg, #55655a 0%, #414f46 55%, #2f3a34 100%);
    box-shadow:
        inset 0 2px 6px rgba(0, 0, 0, .45),
        0 2px 6px rgba(0, 0, 0, .12);
    scrollbar-width: thin;
    scrollbar-color: #5b6b60 transparent;
    scroll-behavior: smooth;
    /* 隐藏横向滚动条在部分浏览器下会让滚动失效，这里保留细滚动条 */
    -webkit-overflow-scrolling: touch;
}

.piano-scroll::-webkit-scrollbar { height: 8px; }
.piano-scroll::-webkit-scrollbar-track { background: transparent; }
.piano-scroll::-webkit-scrollbar-thumb {
    background: #5b6b60;
    border-radius: 4px;
}
.piano-scroll::-webkit-scrollbar-thumb:hover { background: #6f8175; }

/* ================= 特效模式：下落音符画布（2026-10 新增 ✓）=================
   默认 display:none ✗ —— 关着的时候**连高度都不占** ✓
     不然每个钢琴页都会白多一块空白 ✗（首页、作品页、工具页都用这个片段 ✓）
   打开后由 tool-piano.js 给 .piano-scroll 加 is-roll ✓ 才显示 ✓

   背景做成"由上到下渐亮"✗ 是有意的 ✓：
     上方深 ✗ 音符从暗处落下来 ✓ 越往下越亮 ✓ 落到琴键那一瞬间最亮 ✓
     这是 SeeMusic / Synthesia 那类特效钢琴的通用观感 ✓（纵深感的来源 ✓）

   pointer-events: none ✗ 很重要 ✓：
     这块画布压在琴键上方 ✓ 绝不能**截住用户的手指** ✗
     琴键照常点、照常横向滑（滑动由 .piano-keys 的 touch-action 接管 ✓）
     画布只管显示 ✓ 一个字都不接收输入 ✓ */
.pk-roll {
    display: none;
    width: 100%;                 /* JS 会覆盖成真实像素宽（keysEl.scrollWidth ✓） */
    height: 240px;
    margin-bottom: 6px;
    border-radius: 10px;
    /* ★ 背景（2026-10 用户反馈"一片黑色，不太好看"后重做 ✓）
       旧版：一条接近纯黑的竖直渐变（.96 → .74 ✓）✗ 看久了像块黑布 ✓
       新版：**两层叠起来** ✗
         ① 底层：竖直渐变 ✗ 但把亮度整体抬起来一点（.97 → .82 ✓）色调偏深青绿 ✓
            和站内的绿主调是一家人 ✓ 不再是死黑 ✓
         ② 上层：一段 **从下方中央往上的径向微光** ✗
            就像"光线是从琴键那边打上来的"✓ 这是纵深感的来源 ✓
            而且它天然把视线引到命中的那一区域 ✓（音符下落的地方最亮 ✓）
       ★ 两层都是纯 CSS ✗ 零运行时成本 ✗ 不占 canvas 的一个像素 ✓ */
    background:
        radial-gradient(130% 85% at 50% 118%,
            rgba(58, 128, 98, .34) 0%,
            rgba(30, 70, 54, .18) 42%,
            rgba(0, 0, 0, 0) 68%),
        linear-gradient(180deg,
            rgba(15, 26, 21, .97) 0%,
            rgba(24, 42, 33, .94) 55%,
            rgba(38, 60, 48, .84) 100%);
    box-shadow:
        inset 0 -2px 12px rgba(0, 0, 0, .5),
        inset 0 1px 0 rgba(255, 255, 255, .05);
    pointer-events: none;
}

/* 打开开关才显示（类由 tool-piano.js 维护 ✓ 与 localStorage 里的记忆同步 ✓） */
.piano-scroll.is-roll .pk-roll { display: block; }

/* ============================================================
 * 2026-10-20：演奏放大模式（连 MIDI 键盘 / 开始录制时自动进入 ✓）
 * ------------------------------------------------------------
 * 用户要求：连了 MIDI 或开录制 ⇒ 钢琴主体适当放大
 * 做法：只放宽**视口** + 琴键高一点点 ✓
 *   · 宽度 = 用户选的宽度（--pk-max，默认 992）**再 + 168px**
 *     ⇒ 用户按过「+ / −」后，这里在**他的宽度基础上**继续放大 ✓
 *   · 卷帘高度**不手改** ✗：boot-ratio.js 按 16:9 自动算（宽度一变它自己长高）
 *     手写 height 会被它覆盖（实测 360 → 476）
 *   · 琴键高度 5.2 → 6.0 倍键宽（演奏时更好按）
 * ⇒ 退出时删掉 body.pk-focus 就完全还原 ✓
 * ============================================================ */
body.pk-focus .piano-scroll {
    max-width: min(100%, calc(var(--pk-max, 992px) + 168px));
}
body.pk-focus .piano-keys {
    height: calc(var(--pk-w, 46px) * 6);
}
@media (max-width: 900px) {
    body.pk-focus .piano-keys { height: calc(var(--pk-w, 46px) * 5.2); }
}

/* 小屏上落得矮一点 ✗ 240px 会把琴键挤出屏幕 ✗ */
@media (max-width: 760px) {
    .pk-roll { height: 150px; }
}

/* 工具栏里的颜色选择：跟浏览器默认宽度对齐就行 ✓ */
.piano-roll-color .tool-select {
    min-width: 84px;
}

.piano-keys {
    position: relative;
    display: flex;
    /* ★ 高度跟着键宽走（等比）：240 / 46 ≈ 5.2，接近真实钢琴的长宽比（1:6.4）。
       这样"缩小"时琴键是**整体变小**，而不是又细又长的一条。
       默认 --pk-w = 46px → 239px，跟以前写死的 240px 视觉上一致。
       （黑键高度是 60%，跟着这个高度一起缩。） */
    height: calc(var(--pk-w, 46px) * 5.2);
    /* ★★★ 2026-10 性能：把整块键床**提成独立合成层**（用户平板实测"键盘渲染一卡一卡"✓）
       为什么 ✗：播放时琴键要横向跟随音区（JS 直接改 #pianoScroll 的 scrollLeft ✓）
         而键盘有 **88 个键** ✗ 每个键都带：渐变底 + 两层阴影 + 1~2 个伪元素（渐变 + inset 阴影）
         ⇒ 每次滚动，新露出来的那一整片都要**重新光栅化** ✗
           密集段落每秒要滚十几次 ⇒ 平板上就是肉眼可见的"一卡一卡" ✓✓
       做法 ✓：translateZ(0) ⇒ 浏览器把整块键床**烤一次** ✗ 之后滚动只是移动这一层 ✓
         （与 midee 的 KeyboardRenderer 把静态键盘烘焙成一张 RenderTexture 是同一思路 ✓
           它是 Pixi 的 RenderTexture ✗ 我们没有构建步骤 ⇒ 用"合成层"达到同样效果 ✓）
       ★ 只影响合成 ✗ 不改任何颜色/尺寸/位置 ✗ 观感一模一样 ✓
       ★ 万一某台设备上有副作用（层太大 ⇒ 占显存 ✓）⇒ **删掉这一行就完全还原** ✓ */
    transform: translateZ(0);
    /* ★ 关键：宽度必须等于"所有白键之和"，不能是视口宽度。
       黑键用 left: 百分比 定位（值是白键序号 / 白键总数），百分比的分母正是这里。
       若这里被撑成视口宽（992px）而琴键总宽是 2392px，黑键间距会被压到 1/2.4、
       互相重叠挤成一堆，而键盘右半边一个黑键都没有。 */
    width: max-content;
    min-width: 0;
    /* ★ 关键：必须是 manipulation，不能是 none。
       原来写 none 是为了让 pointerdown 拖出滑奏，但它同时把**触摸滚动也禁掉了** ——
       键盘宽 760px 而手机屏只有 360px，结果一半琴键在手机上永远够不到。
       manipulation = 允许 pan-x / pan-y / 双指缩放，只禁掉双击缩放，正好是"可点按表面"要的值：
         · 轻点琴键 → 照常发声（pointerdown 不受 touch-action 影响）
         · 横向拖动 → 滚动 .piano-scroll 看别的音区
         · 纵向拖动 → 正常上下滚页面（写 pan-x 会把页面纵向滚动也锁死，所以不用它）
       浏览器接管手势时会发 pointercancel，keyboard-core.js 已监听它把音放掉，不会卡音。 */
    touch-action: manipulation;
    user-select: none;
    -webkit-user-select: none;
}

/* ---------- 白键 ---------- */
/* 立体感的来源：顶部纯白 → 底部略灰的渐变（键面受光），
   加一条下边缘阴影（键的前立面），再加左右分隔线。
   只有 .pk-white 参与 flex，黑键是绝对定位不占位。 */
.pk-white {
    position: relative;
    /* ★★★ 2026-10 Firefox 修复（用户实测：换 Firefox 后琴键"挤压错乱"✓）
       原来只写一条 `flex: 0 0 var(--pk-w, 58px)` ✗
         ⇒ 键宽**全押在"flex 简写里塞 var()"这一条**上 ✓
       Firefox 下它算不对 ✗ 键宽就退化成"由内容（音名文字）决定"✗
         ⇒ 有标签的键宽、没标签的键被压扁 ✓ 宽窄不一 ✓
         ⇒ 连带 `.piano-keys` 的总宽也算错 ⇒ 黑键的百分比分母跟着错 ✗
           黑键全挤到左边 ✓（两个症状是同一个根 ✓）
       现在拆开写，并用 width + min-width + max-width **三重钉死**：
         ⇒ 哪怕浏览器忽略 flex 那条 ✗ 键宽也一定是 --pk-w ✓
         ⇒ 与 tool-piano.js 里"容器宽 = 键数 × 键宽"严丝合缝 ✓
           （黑键的 left 百分比全靠这个分母 ✓）
       ★ min-width 不能再写常数 ✗ 它必须**跟着 --pk-w 一起走** ✓
         （原写法 14px 是给"缩到最小时"让路的 ✗ 现在直接等于键宽 ✗ 更准 ✓） */
    flex: 0 0 auto;
    width: var(--pk-w, 58px);
    min-width: var(--pk-w, 58px);
    max-width: var(--pk-w, 58px);

    margin: 0;
    padding: 0;
    background: linear-gradient(180deg, #ffffff 0%, #ffffff 78%, #eef2ef 100%);
    border: 0;
    border-left: 1px solid #dde4df;
    border-radius: 0 0 5px 5px;
    box-shadow:
        0 3px 0 #cdd6d0,                    /* 键的前立面 */
        0 4px 5px rgba(0, 0, 0, .28);       /* 落在琴体上的投影 */
    cursor: pointer;
    /* ★ 只给 transform 做过渡 ✗ 原来还写了 background / box-shadow ✗✗
       那两个属性能量到"不可动画"的渐变与阴影上 ✗ 浏览器只能逐帧重画 ✗
       （见下面 .pk-white::before 的说明 ✓） */
    transition: transform .05s;
}

.pk-white:first-child { border-left: 0; }

/* ★★★ 2026-10-16（第三十七轮 · 用户要求 ✓）：**白键的立体感**（与成片同一套配方 ✓）
   用户原话：「导出来的钢琴立体感不够……白键黑键都不好看，没有立体感，
             不真实的钢琴，**就像画出来的**」✓
   ★ 为什么放 ::after（不去改 background ✗）：
     成片那边 `measure` 量的就是**背景渐变**（视频的底色从它来 ✓）
     背景一改 ⇒ 成片底色跟着变 ✗ —— 而这里的立体是"叠上去的一层"✓ 不该进底色 ✓
     ⇒ 因此：网页用伪元素叠 ✓ 成片用**同一套叠色**画（fx-frame.js 的 drawKeys ✓）
   四层 ✓：左倒角亮 / 右槽暗 / 顶亮 / 底略暗（低透明度 ⇒ 主题色还透得出来 ✓）*/
.pk-white::after {
    content: "";
    position: absolute;
    top: 0; right: 0; bottom: 0; left: 0;
    inset: 0;
    pointer-events: none;
    border-radius: inherit;
    background:
        linear-gradient(90deg, rgba(255,255,255,.50) 0%, rgba(255,255,255,0) 7%,
                                rgba(0,0,0,0) 82%, rgba(0,0,0,.13) 97%, rgba(0,0,0,.22) 100%),
        linear-gradient(180deg, rgba(255,255,255,.55) 0%, rgba(255,255,255,0) 10%,
                                rgba(0,0,0,0) 72%, rgba(0,0,0,.075) 100%);
}

/* ★★ 按下态的性能改法（2026-10 · 用户反馈"播放时手机电脑发烫" ✓）
   原来：.pk-white.is-on 直接换 background 与 box-shadow，
         并且 transition 里带上了这两个属性 ✗✗
   问题：linear-gradient 与 box-shadow（尤其 inset ✗）**都无法交给 GPU 合成** ✗，
        每帧都要 CPU 重新光栅化整个键 ✗。
        MIDI 密集段落每秒切十几次高亮 ✗ → 每秒几百次重绘 ✗ → 手机/电脑一起发烫 ✓
   改法：把"按下时的样子"**预先画在 ::before 上** ✓ 切换时只改 opacity ✓
        opacity 是纯合成属性 ✗ 不触发布局也不触发重绘 ✓ 走 GPU ✓
        （::before 自己那一次光栅化只做一次 ✓ 之后切多少次都是免费的 ✓）
   注意：不能用 ::after ✗ —— 黑键的 ::after 拿去做竖向高光了 ✗ */
.pk-white::before {
    content: "";
    position: absolute;
    /* ★ 兼容（2026-10 ✓）：老浏览器不认 inset（Safari<14.1 / Chrome<87 / FF<66 ✓）
       先给一份等价声明 ✗ 下一行的 inset 会覆盖它 ✓ 观感一点不变 ✓ */
    top: 0; right: 0; bottom: 0; left: 0;
    inset: 0;
    pointer-events: none;
    opacity: 0;
    border-radius: inherit;
    background: linear-gradient(180deg, #d4ecd5 0%, #a5d6a7 60%, #8bc98e 100%);
    box-shadow: inset 0 2px 6px rgba(27, 94, 32, .22);
    transition: opacity .04s linear;
}
.pk-white.is-on::before,
.pk-white:active::before { opacity: 1; }

/* 按下的琴键：整块变绿 + 真的往下沉（transform 缩短键身，
   配合 shadow 变浅，视觉上"被按下去了"） */
/* 按下态：只改 transform ✗ 不碰 background / box-shadow ✗
   （按下时"变绿 + 凹陷"的视觉在 ::before 上用 opacity 切 ✓ 见上）
   transform 是合成属性 ✗ 88 个键一起动也不会重绘 ✓ */
.pk-white.is-on,
.pk-white:active {
    transform: translateY(2px);
}

/* 每个 C 键左侧加一道浅分隔，方便数八度 */
.pk-white.pk-c {
    border-left: 2px solid #b2c4b8;
}

/* 音名标签：默认不显示，勾选「显示音名」后才出现
   （52 个白键全写满会非常吵，所以只在 C 键上显示，见 tool-piano 的 show-names 逻辑）
   ★★★ 2026-10-17（第五十六轮 · 用户要求）：**白键音名改成黑色** ✓
     用户原话：「白键上面显示的音名，给它用黑色字体显示吧」✓
     改前：`#5c7a63`（灰绿）/ C 键 `#2f5d3a`（深绿）⇒ 白键上看着发灰、不够清楚 ✗
     改后：`#111` / C 键 `#000` ⇒ 白底黑字，一眼就读得出来 ✓
     ★ 成片会**自动跟上**：fx-frame 的 measure 读的就是这里的 computed color ✓
       （见 fx-frame 的 `lbl.color` ✓ 所以只改 CSS 就够 ✓）*/
.pk-label {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 6px;
    /* ★★★ 2026-10-19（第七十三轮 · 用户要求）：**音名字号缩小** ✓
      用户原话：「音名数字要再缩小一点，缩小一点之后更好看」✓
      改前：普通白键 11px / C 键 12.5px ⇒ 感觉有点大 ✗
      改后：普通白键 9px / C 键 10px ✓ 更精致 ✓ 成片也自动跟着 ✓ */
    font-size: 9px;
    line-height: 1;
    font-weight: 600;
    color: #111111;
    text-align: center;
    pointer-events: none;
    opacity: 0;
    transition: opacity .12s;
}

.piano-keys.show-names .pk-label {
    opacity: 1;
}

.piano-keys.show-names .pk-white.pk-c .pk-label {
    color: #000000;
    font-size: 10px;
}

/* 电脑模式按钮：开着时高亮（与工具页其它按钮同一套配色 ✓）
   ★ 2026-10-17（第六十轮）：用户要求"键盘字母默认不显示，点电脑模式才显示" ✓ */
#pianoPcMode.is-on {
    background: rgba(127, 224, 168, .20);
    border-color: rgba(127, 224, 168, .55);
    color: #c9ffe0;
    box-shadow: 0 0 0 1px rgba(127, 224, 168, .18) inset;
}
#pianoPcMode.is-on i { color: #9ff0c4; }
/* hidden 一定要压得住 .pk-keymap 自己的 display（这里没设 ⇒ 默认就够 ✓ 但显式写上更稳 ✓）*/
.pk-keymap[hidden] { display: none !important; }

/* ============================================================
 * ★★★ 2026-10-17（第五十九轮 · 用户要求）：**电脑键盘 ↔ 琴键 对照**
 *  用户原话：「电脑键盘这个按动 88 个键……网上热门的那种钢琴他们是怎么个
 *           对应对照法，你可以给它抄过来，然后在这个钢琴区域下方给它做一个
 *           这种介绍，然后给它一一对应起来」✓
 * ------------------------------------------------------------
 *  两块东西（都由 tool-piano 的 buildKeymap() 生成 ✓）：
 *    ① `.pk-keymap`  = 琴键下方那张表（键帽 kbd + 音名 ✓ 按半音顺序 ✓）
 *    ② `.pk-cap`     = 直接钉在琴键上的 8px 小键帽（一眼就对上 ✓）
 *  ★ 字号为什么是 8px ✗：88 键全显示时白键约 19px（46 × 0.41 ✓）
 *    8px 的大写字母刚好放得下 —— 再大就会两个字母撞在一起 ✓
 *  ★ 手机不显示键帽（只留下方表 ✓）：窄屏键更小，钉上去反而糊 ✗
 * ============================================================ */
.pk-keymap {
    margin: 8px 2px 2px;
    padding: 8px 10px 9px;
    border: 1px solid rgba(127, 224, 168, .18);
    border-radius: 8px;
    background: rgba(10, 20, 15, .5);
    font-size: 11px;
    color: #cfe6d8;
}
.pk-km-head {
    display: flex; gap: 10px; align-items: baseline; flex-wrap: wrap;
    font-weight: 700; opacity: .95;
}
.pk-km-o { font-weight: 500; opacity: .68; font-size: 10.5px; }
.pk-km-o b { color: #9ff0c4; }
.pk-km-row { display: flex; flex-wrap: wrap; gap: 5px; margin: 7px 0 6px; }
.pk-km {
    display: inline-flex; align-items: center; gap: 4px;
    padding: 2px 6px 2px 4px;
    border-radius: 6px;
    background: rgba(255, 255, 255, .07);
    border: 1px solid rgba(255, 255, 255, .11);
}
.pk-km.is-black {
    background: rgba(0, 0, 0, .40);
    border-color: rgba(255, 255, 255, .17);
    color: #eaf6ee;
}
.pk-km kbd {
    display: inline-block; min-width: 15px; text-align: center;
    padding: 1px 3px; border-radius: 4px;
    background: #f4fbf6; color: #0d1a13;
    font: 700 10px/1.25 ui-monospace, Consolas, monospace;
    box-shadow: 0 1px 0 rgba(0, 0, 0, .35);
}
.pk-km.is-black kbd { background: #dfe9e3; }
.pk-km i { font-style: normal; opacity: .82; font-size: 10.5px; }
.pk-km-tip { opacity: .62; font-size: 10.5px; line-height: 1.5; }

/* 琴键上的小键帽（白键底部 24px ⇒ 让开 .pk-label 的 6px ✓ 两者不打架）*/
.pk-cap {
    position: absolute;
    left: 0; right: 0;
    bottom: 24px;
    text-align: center;
    font: 700 8px/1 ui-monospace, Consolas, monospace;
    color: rgba(20, 70, 40, .62);
    letter-spacing: 0;
    pointer-events: none;
}
.pk-black .pk-cap { bottom: 9px; color: rgba(226, 246, 234, .80); }
@media (max-width: 760px) {
    .pk-cap { display: none; }
}


/* ---------- 黑键 ---------- */
/* 比白键短（62%）、窄（绝对宽度），带一层高光模拟抛光面 */
.pk-black {
    position: absolute;
    top: 0;
    height: 60%;
    width: calc(var(--pk-w, 58px) * 0.55);   /* 与白键宽度成比例：以前用百分比，键少时会被压得很窄 */
    margin: 0;
    padding: 0;
    transform: translateX(-50%);      /* left 是白键交界线，自身要居中 */
    background: linear-gradient(180deg, #63746a 0%, #3a4840 45%, #222c26 100%);
    border: 0;
    border-radius: 0 0 4px 4px;
    box-shadow:
        inset 0 -2.5px 0 rgba(0, 0, 0, .34),   /* ★ 第三十七轮：键的厚度（底沿 ✓ 与成片一致）*/
        inset 0 1px 0 rgba(255, 255, 255, .07),/* ★ 顶面一点点受光 ✓ */
        0 2px 0 #0e1611,                    /* 黑键前立面 */
        0 4px 6px rgba(0, 0, 0, .45);
    cursor: pointer;
    z-index: 2;
    transition: transform .05s;      /* ★ 同白键：去掉 background / box-shadow ✗ */
}

/* 高光：★ 2026-10-16（第三十七轮 · 用户要求 ✓）原来是一大块方块 ⇒「黑键上面有一点点
   白色泛白的东西」✗ 现在改成**一条窄的**反光（15% 宽 ✓）⇒ 像抛光面 ✓ 不像贴纸 ✓ */
.pk-black::after {
    content: "";
    position: absolute;
    left: 20%;
    width: 15%;
    top: 5%;
    height: 35%;
    border-radius: 2px;
    background: linear-gradient(180deg, rgba(255, 255, 255, .17) 0%, rgba(255, 255, 255, 0) 100%);
    pointer-events: none;
}

/* ★ 黑键按下态：同白键做法 —— 视觉预画在 ::before ✓ 切换只改 opacity ✓
   黑键的 ::after 已经被竖向高光占了 ✗ 所以这里用 ::before ✓ */
.pk-black::before {
    content: "";
    position: absolute;
    top: 0; right: 0; bottom: 0; left: 0;   /* 同 .pk-white::before ✗ 给不认 inset 的老浏览器一份等价声明 ✓ */
    inset: 0;
    pointer-events: none;
    opacity: 0;
    border-radius: inherit;
    background: linear-gradient(180deg, #43a047 0%, #2e7d32 55%, #1b5e20 100%);
    box-shadow: inset 0 2px 5px rgba(0, 0, 0, .3);
    transition: opacity .04s linear;
    /* ★ 不设 z-index ✗：伪元素本来就画在"父元素底色之上" ✓，
       而 ::after 在 ::before 之后绘制 ✓ → 竖高光自然压在上面 ✓
       （写成 -1 会把按下色藏到键底色**下面** ✗ 完全看不见 ✗） */
}
.pk-black.is-on::before,
.pk-black:active::before { opacity: 1; }

/* ★ 注意：按下态要保留 translateX(-50%)，否则黑键会偏移半格
   只改 transform ✗ 背景/阴影交给 ::before 的 opacity ✓ */
.pk-black.is-on,
.pk-black:active {
    transform: translateX(-50%) translateY(2px);
}

/* ============================================================
 * ★★★ 2026-10-15 新增（用户："轮指的时候琴键也要一闪一闪的"✓）
 * ------------------------------------------------------------
 * 症状 ✓：同一个音连着来好几个（轮指 / 连击 ✓ 一个音上有好几个叠着的音 ✓）
 *   高亮那边的逻辑是"**计数减到 0 才灭**"（因为同一音高确实可能重叠 ✓ 不能一松就灭 ✓）
 *   ⇒ 轮指那几个音之间，计数**一直是 ≥1** ⇒ 琴键**一直亮着、根本不闪** ✓✓
 *   （用户原话："得得得得得 ⇒ 那个键就一直按着 ✗ 不太好看"✓）
 * 做法 ✓：计数 >1 时给键重加一次 `.is-pulse` ⇒ 播一段很短的"灭 → 亮"动画 ✓
 *   · 动画画在 **::before** 上（"按下色"就是那一层 ✓ 与 .is-on 同一处 ✓）
 *   · 0.16 秒 ✗ 刚好是"闪一下"的手感 ✓
 *     ★ 不能用 transition 代替 ✗ transition 做不到"同一个值重新播一次" ✓
 *   · 每颗只改 opacity ⇒ 纯合成（GPU）✗ 不重排、不重绘 ✓
 *     ⇒ 88 个键一起闪也不掉帧 ✓
 * ★ 重播动画必须 **先移除类 → 强制重排 → 再加回**（见 tool-piano.js 的 retrigKey ✓）
 *   只换类名而不重排 ⇒ 浏览器认为"没变"⇒ 动画**不会重新开始** ✓ 这是 CSS 动画的固有行为 ✓
 * ★ 尊重系统"减少动态效果"⇒ 这类用户不给它闪 ✓（功能一点不受影响 ✓）
 * ============================================================ */
.pk-white.is-pulse::before,
.pk-black.is-pulse::before { animation: pkKeyPulse .16s ease-out 1; }
@keyframes pkKeyPulse {
    0%   { opacity: 0; }
    45%  { opacity: 1; }
    100% { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
    .pk-white.is-pulse::before,
    .pk-black.is-pulse::before { animation: none; }
}

/* ---------- 窄屏 ---------- */
@media (max-width: 760px) {
    .piano-panel { padding: 14px 12px 16px; }
    .piano-keys {
        height: calc(var(--pk-w, 46px) * 2.96);   /* 窄屏本来就矮：136 / 46，同样跟着键宽等比缩 */
        /* ★★ 2026-10 删掉原 `min-width: 640px`（用户 Firefox 实测后重做 ✓）
           它想表达的是"窄屏别把键缩得太小 ✗ 保持可点 ✓ 靠横向滚动浏览 ✓"✓
           但 min-width 会**压过**宽宽（JS 写进来的那行 ✓）
             ⇒ 容器 640 ✗ 白键总和只有 520 ⇒ 两者不等 ✗
             ⇒ 黑键的 left 百分比（分母 = 容器宽 ✓）算错 ✗ 全挤到左边 ✓✓
           现在这条规则**折算成键宽下限** ✗ 搬进 tool-piano.js 的 applyZoom 里 ✓
             （等价换算：640 / 52 ≈ 12.3px 一个白键 ✓）
           ⇒ 效果完全一样（可见总宽都是 640 ✓）✗ 但容器宽与白键总和**恒等** ✓
             黑键永远对齐 ✓✓ */
    }
    .piano-scroll { padding: 10px 8px 9px; }
    .piano-vol .tool-range { width: 80px; }
    .piano-hint { font-size: 11px; }
    .piano-nav { justify-content: space-between; }
}

/* 超窄屏：控制条换行更紧凑，避免按钮被挤成两行 */
@media (max-width: 420px) {
    .piano-bar { gap: 8px; }
    .piano-bar .tool-btn { padding: 8px 12px; font-size: 13px; }
    .piano-keys { height: calc(var(--pk-w, 46px) * 2.7); }   /* 124 / 46 */
}


/* ============================================================
   在线乐器栏目 /instruments/
   ============================================================ */

.inst-grid {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 18px;
    margin: 22px 0 36px;
}

/* 卡片配色全部走 --accent / --accent2，由模板按乐器注入（见 temp/instruments.php）。
   这样四个乐器各有主色，网格里一眼能区分，加新乐器也不用改 CSS。 */
.inst-card {
    --accent: #2e7d32;
    --accent2: #66bb6a;
    position: relative;
    display: flex;
    flex-direction: column;
    padding: 28px 20px 20px;
    background: #fff;
    border: 1px solid #e8ece9;
    border-radius: 16px;
    text-align: center;
    overflow: hidden;
    transition: transform .2s ease, box-shadow .2s ease, border-color .2s ease;
}

/* 顶部彩条：本乐器主色的渐变 */
.inst-card::before {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    height: 4px;
    background: linear-gradient(90deg, var(--accent) 0%, var(--accent2) 100%);
}

/* 右上角淡色光晕：给纯白卡片加一点空气感 */
.inst-card::after {
    content: "";
    position: absolute;
    right: -46px;
    top: -46px;
    width: 140px;
    height: 140px;
    border-radius: 50%;
    background: radial-gradient(circle, var(--accent2) 0%, rgba(255, 255, 255, 0) 70%);
    opacity: .14;
    pointer-events: none;
    transition: opacity .24s ease;
}

.inst-card:hover {
    border-color: var(--accent2);
    box-shadow: 0 14px 30px -10px rgba(0, 0, 0, .22);
    transform: translateY(-4px);
    text-decoration: none;
}

.inst-card:hover::after { opacity: .26; }

/* 图标：圆角方形比纯圆形更现代，实色渐变比淡色底更有存在感 */
.inst-icon {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 62px;
    height: 62px;
    margin: 0 auto 14px;
    border-radius: 18px;
    background: linear-gradient(140deg, var(--accent) 0%, var(--accent2) 100%);
    color: #fff;
    font-size: 26px;
    box-shadow: 0 10px 18px -8px var(--accent);
    transition: transform .22s ease;
}

.inst-card:hover .inst-icon { transform: scale(1.06) rotate(-3deg); }

.inst-tag {
    display: inline-block;
    margin: 0 auto 10px;
    padding: 3px 11px;
    border-radius: 999px;
    background: #f2f8f2;                                  /* 老浏览器回退（不支持 color-mix 时） */
    background: color-mix(in srgb, var(--accent2) 14%, #fff);
    color: var(--accent);
    font-size: 11px;
    font-weight: 500;
    letter-spacing: .02em;
}

.inst-card h2 {
    margin: 0 0 7px;
    font-size: 19px;
    letter-spacing: .01em;
    color: #1f2d24;
}

.inst-card .inst-hook {
    margin: 0 0 10px;
    font-size: 12.5px;
    color: var(--accent);
    font-weight: 600;
    line-height: 1.5;
}

.inst-card .inst-short {
    margin: 0 0 16px;
    padding-top: 12px;
    border-top: 1px dashed #eef3ef;
    font-size: 12px;
    line-height: 1.75;
    color: #7a8f80;
    text-align: left;
    flex: 1;
}

/* 行动按钮：胶囊形 + 悬停时箭头滑出一点，暗示"点进去" */
.inst-go {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 9px 0;
    border-radius: 999px;
    background: var(--accent);
    color: #fff;
    font-size: 13px;
    font-weight: 500;
    transition: gap .2s ease, filter .2s ease;
}

.inst-card:hover .inst-go {
    gap: 11px;
    filter: brightness(1.1);
}

/* ---------- 栏目页的正文区块 ---------- */
.inst-section {
    margin: 0 0 28px;
}

.inst-section h2 {
    margin: 0 0 12px;
    font-size: 18px;
    color: #1f2d24;
    padding-left: 10px;
    border-left: 4px solid #2e7d32;
}

.inst-section p {
    margin: 0 0 10px;
    font-size: 14px;
    line-height: 1.95;
    color: #4a5f52;
}

/* ---------- 跨乐器对比表 ---------- */
.inst-table-wrap {
    overflow-x: auto;
    margin-top: 12px;
}

.inst-table {
    width: 100%;
    min-width: 620px;
    border-collapse: collapse;
    font-size: 13px;
}

.inst-table th,
.inst-table td {
    padding: 10px 12px;
    text-align: left;
    border-bottom: 1px solid #eef3ef;
    vertical-align: top;
}

.inst-table thead th {
    background: #f6faf7;
    color: #2e7d32;
    font-weight: 600;
    white-space: nowrap;
}

.inst-table tbody th {
    color: #1f2d24;
    font-weight: 600;
    white-space: nowrap;
}

.inst-table tbody td {
    color: #5a6b60;
    line-height: 1.7;
}

.inst-table tbody tr:hover {
    background: #fafdfb;
}

@media (max-width: 900px) {
    .inst-grid {
        grid-template-columns: repeat(2, 1fr);
    }
}

@media (max-width: 520px) {
    .inst-grid {
        grid-template-columns: 1fr;
    }
    .inst-card .inst-short {
        text-align: center;
    }
}

/* ============================================================
   乐器 · 指板（吉他 / 贝斯共用）
   ------------------------------------------------------------
   布局：整块指板是一个纵向 flex，每行 = 一根弦；
   行内是「弦名 + 13 个品格格」，品格用 flex 均分宽度。
   品格格之间画右边界，看起来就是琴品。
   ============================================================ */

.fret-panel {
    padding: 16px 16px 20px;
}

.fret-bar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 12px;
}

.fret-vol {
    display: flex;
    align-items: center;
    gap: 8px;
}

.fret-vol .tool-range {
    width: 100px;
}

.fret-check {
    display: flex;
    align-items: center;
    gap: 6px;
}

/* ---------- 和弦预设 ---------- */
.fret-chords {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    margin: 12px 0 0;
}

.fret-chords-label {
    font-size: 12px;
    color: #93a49a;
}

.chord-chip {
    padding: 5px 14px;
    font-size: 13px;
    font-family: inherit;
    color: #2e7d32;
    background: #f2f8f2;
    border: 1px solid #cfe0d3;
    border-radius: 999px;
    cursor: pointer;
    transition: all .14s;
}

.chord-chip:hover {
    background: #e8f5e9;
    border-color: #a5d6a7;
}

.chord-chip:active {
    background: #2e7d32;
    color: #fff;
}

.fret-chords-hint {
    font-size: 11px;
    color: #b3c2b8;
}

.fret-hint {
    margin: 10px 0 12px;
    font-size: 12px;
    line-height: 1.7;
    color: #93a49a;
}

/* ---------- 缩放（缩小 / 放大）的配套规则 ----------
   ★ 视口宽度是**固定**的"显示区域"（见 .piano-scroll 的 max-width），
     只有琴键宽度 --pk-w 变，高度也跟着等比变（见 .piano-keys）。
     这里只管一件事：键太窄时音名标签放不下 —— 先收小、再小就藏起来。 */
.piano-keys.is-zoomed .pk-label { font-size: 9px; bottom: 6px; }
/* 缩得更小时标签已经放不下了（52 个叠在一起），干脆不显示 */
.piano-keys.is-tiny .pk-label { display: none; }

/* “打开 MIDI 文件”：用 label 冒充按钮，把 file input 藏起来 */
.piano-midi-open { cursor: pointer; }
.piano-midi-open input[type="file"] { display: none; }

/* 钢琴音色加载进度条（第 70 轮 · 2026-10-19） */
.piano-load-progress {
    position: relative;
    margin: 12px 0 0;
    padding: 8px 12px;
    background: rgba(40, 60, 40, 0.9);
    border: 1px solid #4a6a4a;
    border-radius: 6px;
    overflow: hidden;
}
.piano-load-progress-bar {
    display: block;
    position: absolute;
    left: 0; top: 0; bottom: 0;
    width: 0%;
    background: linear-gradient(90deg, #3a5a3a 0%, #5a8a5a 100%);
    transition: width 0.3s ease;
    z-index: 0;
}
.piano-load-progress-text {
    position: relative;
    z-index: 1;
    font-size: 11px;
    color: #c8e6c8;
    display: block;
}

/* 钢琴页底部的采样音源署名（CC BY 3.0 要求署名 + 许可链接） */
.piano-credit {
    margin: 12px 0 0;
    font-size: 11px;
    line-height: 1.6;
    color: #b3c2b8;
}
.piano-credit a { color: #8ba394; }

/* ---------- 指板本体 ---------- */
.fret-scroll {
    overflow-x: auto;
    overflow-y: hidden;
    padding-bottom: 6px;
    scrollbar-width: thin;
}

.fret-grid {
    display: flex;
    flex-direction: column;
    min-width: 560px;
    /* 指板上要用 pointerdown 横向扫弦，必须关掉浏览器的触摸滚动接管 */
    touch-action: none;
    user-select: none;
    -webkit-user-select: none;
}

.fret-row {
    display: flex;
    align-items: stretch;
}

/* 弦名（空弦音）：固定宽度，保证各行品格横向对齐 */
.fret-tune {
    flex: 0 0 36px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 11px;
    font-weight: 600;
    color: #6d8a72;
    background: #f6faf7;
    border-radius: 4px 0 0 4px;
    margin: 1px 0;
}

/* 一品数字行：不要左右内边距，否则会和下面的品格错位 */
.fret-nums {
    margin-bottom: 3px;
}

.fret-nums .fret-tune {
    background: transparent;
}

.fret-num {
    flex: 1 1 0;
    min-width: 30px;
    text-align: center;
    font-size: 10px;
    color: #b3c2b8;
    line-height: 1.6;
}

/* 品位记号点（3/5/7/9 品一个点，12 品两个点）用更深的颜色提示 */
.fret-num.fret-dot {
    color: #8fa895;
    font-weight: 600;
}

.fret-num.fret-dot2 {
    color: #2e7d32;

/* ============================================================
   ★★ 手机端全屏精简（2026-10 用户实测后要求 ✓）
   ------------------------------------------------------------
   用户的意思 ✓：手机屏就这么大 ✗ 全屏后如果把工具条 / 谱表 / 提示语 /
     左右导航 / 和弦 全部展开 ✗ 页面会被撑得很难看 ✓
     → 全屏时**只留琴键区**（.piano-scroll ✗ 内部含落下式画布 + 琴键 ✓）
     → 并且**默认打开特效模式**（由 JS 的 fsAutoRoll 处理 ✓ 见 tool-piano.js）
   做法 ✓：
     · 所有规则都加了三重全屏前缀（.is-fs / :fullscreen / .is-faux-full ✓）
       —— 覆盖 JS 打类 ✓ 原生全屏 ✓ iOS 伪全屏 ✓ 三种情况 ✓
     · 只在 max-width:768px 下生效 ✗ **标签页/桌面端一个像素都不变** ✓
     · ★ "退出全屏"按钮被 JS 从工具条里**摘出来**挂到面板根上 ✗
       所以这里要**单独放行**它 ✗ 否则用户进得去出不来 ✓
       （display:none 的祖先会连后代一起吞 ✗ position:fixed 也救不了 ✓）
   ============================================================ */
/* ★★ 2026-10 改：**不再限定窄屏**✗ 手机与电脑一视同仁 ✓
   （用户第二次明确要求原话 ✓："不管手机端还是电脑端 ✗ 就只显示这一个框架"✓）
   所以下面这组规则**取消了 @media 包裹** ✗ 全尺寸生效 ✓ */
.piano-panel.is-fs,
.piano-panel:fullscreen,
.piano-panel:-webkit-full-screen,
.piano-panel.is-faux-full {
        padding: 0 !important;
        border-radius: 0 !important;
        box-shadow: none !important;
        background: #1c2820 !important;
        height: 100vh !important;
        min-height: 100vh !important;
        max-height: 100vh !important;
        overflow: hidden !important;
        display: flex !important;
        flex-direction: column !important;
    }

    /* 只留琴键区与"退出全屏"按钮 ✗ 其余直接子元素一律不显示 ✓ */
    .piano-panel.is-fs > *:not(.piano-scroll):not(#pianoFullscreen),
    .piano-panel:fullscreen > *:not(.piano-scroll):not(#pianoFullscreen),
    .piano-panel.is-faux-full > *:not(.piano-scroll):not(#pianoFullscreen) {
        display: none !important;
    }

    /* 琴键区铺满整屏 ✗ 琴键贴底 ✗ 画布吃满上方 ✓ */
    .piano-panel.is-fs .piano-scroll,
    .piano-panel:fullscreen .piano-scroll,
    .piano-panel.is-faux-full .piano-scroll {
        flex: 1 1 auto !important;
        width: 100% !important;
        max-width: none !important;
        height: 100% !important;
        min-height: 0 !important;
        border-radius: 0 !important;
        padding: 8px 6px 10px !important;
        justify-content: flex-end;
    }

    /* 落下式画布：全屏时尽量高 ✓ */
    .piano-panel.is-fs .pk-roll,
    .piano-panel:fullscreen .pk-roll,
    .piano-panel.is-faux-full .pk-roll {
        flex: 1 1 auto !important;
        height: auto !important;
        max-height: none !important;
        min-height: 110px !important;
        margin-bottom: 8px !important;
    }

    /* ============================================================
       "退出全屏"按钮（用户 2026-10 明确要求 ✓ 说得最具体的一次 ✓）
       ★ 位置 ✗：**框架左侧**（贴左边 ✗ 垂直居中偏上 ✗ 不挡琴键 ✓）
       ★ 常态 ✗：**半透明**（opacity .28 ✓ 不干扰看谱 ✓）
       ★ 悬停 ✗：鼠标移上去就**实体显示**（opacity 1 ✗ 底色加深 ✓）
       ★ 内容 ✗：就 **A5** 两个字（用户原话 ✓）✗
         用 ::before 生成 ✗ 同时把里面的图标隐藏掉 ✓
         （这样不用 JS 去改按钮的 innerHTML ✗ 退出后自然恢复原样 ✓）
       ============================================================ */
    .piano-panel.is-fs #pianoFullscreen,
    .piano-panel:fullscreen #pianoFullscreen,
    .piano-panel:-webkit-full-screen #pianoFullscreen,
    .piano-panel.is-faux-full #pianoFullscreen {
        display: inline-flex !important;
        position: fixed;
        left: calc(10px + env(safe-area-inset-left, 0px));
        top: 50%;
        transform: translateY(-60%);
        right: auto !important;
        bottom: auto !important;
        z-index: 2147483000;
        width: 46px;
        height: 46px;
        padding: 0 !important;
        margin: 0 !important;
        align-items: center;
        justify-content: center;
        border-radius: 13px !important;
        background: rgba(10, 16, 12, .55) !important;
        border: 1px solid rgba(255, 255, 255, .30) !important;
        color: #fff !important;
        /* ★ 常态半透明 ✗ 悬停变实体 ✓ 过渡让变化不突兀 ✓ */
        opacity: .28;
        transition: opacity .18s ease, background .18s ease, transform .18s ease;
        /* ★★ 2026-10 删掉 backdrop-filter（用户实测"横屏全屏卡 ✗ 不全屏流畅"✓）
           它是手机的**头号杀手**✗ 语义是"把背后内容每帧重新合成并模糊"✓
           全屏时背后正是那块**巨大且每帧都在变**的落下式画布
             ⇒ 每帧模糊一整块画布 ⇒ GPU 跪 ⇒ 一卡一卡 ✓✓
           ★ 按钮本身有 rgba(10,16,12,.55) 的实底 ✗ 去掉模糊**观感几乎无差**✓ */
        cursor: pointer;
        font-weight: 700;
        letter-spacing: .5px;
    }
    /* 里面的图标让位给 A5 两个字 ✓ */
    .piano-panel.is-fs #pianoFullscreen > i,
    .piano-panel:fullscreen #pianoFullscreen > i,
    .piano-panel:-webkit-full-screen #pianoFullscreen > i,
    .piano-panel.is-faux-full #pianoFullscreen > i {
        display: none !important;
    }
    .piano-panel.is-fs #pianoFullscreen::before,
    .piano-panel:fullscreen #pianoFullscreen::before,
    .piano-panel:-webkit-full-screen #pianoFullscreen::before,
    .piano-panel.is-faux-full #pianoFullscreen::before {
        content: "A5";
        font-size: 15px;
        line-height: 1;
        color: #fff;
    }
    /* ★ 鼠标移上去（或手指按住 ✓）：实体显示 ✓ */
    .piano-panel.is-fs #pianoFullscreen:hover,
    .piano-panel:fullscreen #pianoFullscreen:hover,
    .piano-panel:-webkit-full-screen #pianoFullscreen:hover,
    .piano-panel.is-faux-full #pianoFullscreen:hover,
    .piano-panel.is-fs #pianoFullscreen:focus-visible,
    .piano-panel:fullscreen #pianoFullscreen:focus-visible {
        opacity: 1;
        background: rgba(10, 16, 12, .82) !important;
        border-color: rgba(255, 255, 255, .55) !important;
    }
/* ★ 上面这组规则已取消 @media 包裹 ✗ 这个右花括号是它原来的收尾 ✗ 已不需要 ✓ */

/* ★★ 全屏时被 JS 打上 .is-fs-hide 的元素一律不显示 ✓（2026-10 ✓）
   为什么改成 JS 打类 ✗ 而不用 `> *:not(.piano-scroll)`：
     ★ 实测（HTML 解析器逐层扫描 ✗ 不是猜 ✓）：#pianoScroll 在 .piano-panel
       里的**第 2 层**✗ 不是直接子元素 ✗
       ⇒ 上一版那个直接子选择器**一条也命中不到**✗ 所以"改了没效果"✓✓
   JS 的做法 ✓：从 #pianoScroll 沿祖先链往上 ✗ 把每一层的兄弟节点都打上这个类 ✓
     （见 tool-piano.js 的 fsHideSiblings() ✓ 不管嵌几层都对 ✓）
   ★ 必须带全屏前缀 ✗ 否则退出全屏后万一残留这类 ✗ 会把页面元素藏掉 ✓
     （退出时 JS 也会把类摘干净 ✗ 这里只是双保险 ✓） */
.piano-panel.is-fs .is-fs-hide,
.piano-panel:fullscreen .is-fs-hide,
.piano-panel:-webkit-full-screen .is-fs-hide,
.piano-panel.is-faux-full .is-fs-hide {
    display: none !important;
}

/* ============================================================
   ★★ 钢琴页深色模式（2026-10 新增 ✓ 用户要求 ✓）
   ------------------------------------------------------------
   用户原话 ✓："手机浏览器开夜间模式后 ✗ 这个站**非常好看**✗
     音条有淡淡的透明感 ✗ 背景不是纯黑 ✗ 是**很透明的淡绿荧光感**✗
     再配合粒子 ✗ 视觉上真的非常好"✓
   为什么之前只有"浏览器强制深色"才好看 ✗：
     本站主色是**一整套绿**✗ style.css 里早就有深色块（底色 #10160f ✓ 字 #d9e6d9 ✓）
     而 **page-tool.css（钢琴页）一处深色都没写**✓✓
     ⇒ 钢琴页只能靠浏览器自动反色 ✗ 结果不可控（有的浏览器发灰 ✗ 有的发蓝 ✓）
   做法 ✓：**照抄站里那套配色**（墨绿底 ✗ 淡绿字 ✗ 不用纯黑）✗ 让钢琴页也参与进来 ✓
     · 底 #10160f（比纯黑"亮"一点 ✗ 有空气感 ✓ ← 这就是用户要的"透明绿荧幕"✓）
     · 卡片/控件 #162013 ✗ 边框 #24352a ✗ 次要文字 #8fb38f ✓
     · ★ 琴键也要压暗 ✗ 否则白键在深色里会"刺眼"（一屏 88 个白块 ✗ 很扎眼 ✓）
     · ★ 音条的"更透明"由 JS 那边配合（见 tool-piano.js 的 rollDark ✓）
   全部带 !important ✗ 与站里既有写法一致（基础规则权重更高 ✓）
   ============================================================ */
@media (prefers-color-scheme: dark) and (min-width: 999999px) {  /* ★ 夜间模式暂时停用（2026-10 ✓ 用户要求）✗ 恢复就删掉 and (min-width: 999999px) ✓ */
    .piano-panel {
        background: #10160f !important;
        color: #d9e6d9 !important;
    }
    /* 工具条按钮 / 下拉 / 数字框 */
    .piano-bar .tool-btn:not(.primary) {
        background: #162013 !important;
        border-color: #24352a !important;
        color: #d9e6d9 !important;
    }
    .piano-bar .tool-select,
    .piano-bar .tool-num {
        background: #162013 !important;
        border-color: #24352a !important;
        color: #d9e6d9 !important;
    }
    .piano-bar .tool-btn:not(.primary):hover {
        background: #1d2a1e !important;
    }
    /* 时间 / 统计 / 提示 / 鸣谢：次要文字用暗一档的绿 ✓ */
    .piano-time,
    .piano-bar .tool-stat,
    .piano-hint,
    .piano-credit,
    .staff-title,
    .piano-nav-range {
        color: #8fb38f !important;
    }
    /* ★ 琴键压暗（关键 ✗ 不然一屏白块太刺眼 ✓）
       保留"上亮下暗"的立体感 ✗ 只是整体降到深绿灰 ✓ */
    .pk-white {
        background: linear-gradient(180deg, #e8f5ea 0%, #f6fdf8 78%, #d6ebdb 100%) !important;
        border-left-color: #1a241a !important;
        box-shadow:
            0 3px 0 #16200f,
            0 4px 5px rgba(0, 0, 0, .55) !important;
    }
    .pk-white.pk-c {
        background: linear-gradient(180deg, #e6f2e9 0%, #f6fbf7 78%, #dcebdf 100%) !important;
    }
    .pk-black {
        background: linear-gradient(180deg, #0b100b 0%, #131a13 100%) !important;
    }
    .pk-label {
        color: #a9c2a9 !important;
    }
    /* 按下态（原来在浅色下是"变亮"✗ 深色里改成"提亮一档绿"✓） */
    .pk-white.is-on {
        background: linear-gradient(180deg, #d4ecd5 0%, #a5d6a7 60%, #8bc98e 100%) !important;
    }
    /* 常用和弦片 / 谱表卡片 */
    .chord-chip {
        background: #162013 !important;
        border-color: #24352a !important;
        color: #d9e6d9 !important;
    }
    .piano-staff {
        background: #131c15 !important;
        border-color: #24352a !important;
    }
    /* 音符颜色下拉的选项（部分浏览器会继承系统色 ✗ 写死更稳 ✓） */
    .piano-bar .tool-select option {
        background: #162013 !important;
        color: #d9e6d9 !important;
    }
}

/* ============================================================
   ★ 琴键配色修正（2026-10 ✓ 用户要求："夜间模式下白键还是白的 / 绿白的"✓）
   ------------------------------------------------------------
   上一版把白键压成深绿灰 ✗ 用户反馈：
     "黑键本来就黑不用管 ✗ 白键变成黑色很怪"✓
   ⇒ 现在改成**绿白**：保留上亮下暗的立体感 ✗ 整体是浅色 ✓
   ⇒ 白键上的音名要用**深色**字（白键上必须深字才看得清 ✓）
   ★ 写在文件**最末**✗ 与前面的深色规则同权重时**后写的赢**⇒ 不必去改那两处重复 ✓
   ============================================================ */
html[data-theme="dark"] .pk-white {
    background: linear-gradient(180deg, #e8f5ea 0%, #f6fdf8 78%, #d6ebdb 100%) !important;
    border-left-color: #cfdcd2 !important;
    box-shadow: 0 3px 0 #9fb3a4, 0 4px 5px rgba(0, 0, 0, .5) !important;
}
html[data-theme="dark"] .pk-white.pk-c {
    background: linear-gradient(180deg, #e6f2e9 0%, #f6fbf7 78%, #dcebdf 100%) !important;
}
html[data-theme="dark"] .pk-white.is-on,
html[data-theme="dark"] .pk-white:active {
    background: linear-gradient(180deg, #c9e6cf 0%, #a8d8b4 78%, #8ec79c 100%) !important;
}
html[data-theme="dark"] .pk-label { color: #3a5a42 !important; }
html[data-theme="dark"] .pk-white.pk-c .pk-label { color: #2f5a3a !important; }
html[data-theme="dark"] .pk-black {
    background: linear-gradient(180deg, #1b241c 0%, #0d120d 100%) !important;
}

@media (prefers-color-scheme: dark) and (min-width: 999999px) {  /* ★ 夜间模式暂时停用（2026-10 ✓ 用户要求）✗ 恢复就删掉 and (min-width: 999999px) ✓ */
    .pk-white {
        background: linear-gradient(180deg, #e8f5ea 0%, #f6fdf8 78%, #d6ebdb 100%) !important;
        border-left-color: #cfdcd2 !important;
        box-shadow: 0 3px 0 #9fb3a4, 0 4px 5px rgba(0, 0, 0, .5) !important;
    }
    .pk-white.pk-c { background: linear-gradient(180deg, #e6f2e9 0%, #f6fbf7 78%, #dcebdf 100%) !important; }
    .pk-white.is-on,
    .pk-white:active { background: linear-gradient(180deg, #c9e6cf 0%, #a8d8b4 78%, #8ec79c 100%) !important; }
    .pk-label { color: #3a5a42 !important; }
    .pk-white.pk-c .pk-label { color: #2f5a3a !important; }
    .pk-black { background: linear-gradient(180deg, #1b241c 0%, #0d120d 100%) !important; }
}

/* ============================================================
   ★★ 工具 / 乐器页深色总覆盖（2026-10 手写 ✓）
   ------------------------------------------------------------
   背景 ✗ 本文件里有 **40+ 处浅底**（#fff / #f6f9f7 / #f8fbf9 / #fbfcfb / #f7faf8 …）
     逐个枚举不现实 ✗ 但它们**集中在少数类族**（box / card / panel / item / row / chip …）
   ⇒ 用**属性选择器通配** `[class*="box"]` 一类写法一次覆盖 ✓
   ★ 排除按钮与标签 ✗ 否则会把主按钮 / 徽章的颜色一起压掉 ✓
   ★ 文字同理 ✓：`#2e7d32` 也有 40+ 处 ✗ 用"继承 + 常用标签"两招一起收 ✓
   写在文件**最末**✗ 同权重时后写的赢 ✓ 不必回头改前面那些重复 ✓
   ============================================================ */
html[data-theme="dark"] .tool-panel,
html[data-theme="dark"] .tool-panel .tool-sec,
html[data-theme="dark"] .tool-panel .tool-block,
html[data-theme="dark"] .tool-panel [class*="box"]:not([class*="btn"]):not([class*="tag"]),
html[data-theme="dark"] .tool-panel [class*="card"]:not([class*="btn"]),
html[data-theme="dark"] .tool-panel [class*="panel"]:not([class*="btn"]),
html[data-theme="dark"] .tool-panel [class*="wrap"]:not([class*="btn"]),
html[data-theme="dark"] .tool-panel [class*="item"]:not([class*="btn"]):not([class*="tag"]),
html[data-theme="dark"] .tool-panel [class*="list"]:not([class*="btn"]),
html[data-theme="dark"] .tool-panel [class*="row"]:not([class*="btn"]),
html[data-theme="dark"] .tool-panel [class*="field"]:not([class*="btn"]),
html[data-theme="dark"] .tool-panel [class*="out"]:not([class*="btn"]),
html[data-theme="dark"] .tool-panel [class*="preview"]:not([class*="btn"]),
html[data-theme="dark"] .tool-panel [class*="result"]:not([class*="btn"]),
html[data-theme="dark"] .tool-panel [class*="editor"]:not([class*="btn"]),
html[data-theme="dark"] .tool-panel [class*="stat"]:not([class*="btn"]) {
    background: #1a261c !important;
    border-color: #2a3f2e !important;
    color: #dff3e4 !important;
}
/* 表格 / 代码块 / 预格式（工具页很常见 ✓） */
html[data-theme="dark"] .tool-panel table,
html[data-theme="dark"] .tool-panel thead th,
html[data-theme="dark"] .tool-panel th,
html[data-theme="dark"] .tool-panel td,
html[data-theme="dark"] .tool-panel pre,
html[data-theme="dark"] .tool-panel code {
    background: #16201a !important;
    border-color: #2a3f2e !important;
    color: #dff3e4 !important;
}
html[data-theme="dark"] .tool-panel thead th,
html[data-theme="dark"] .tool-panel th { background: #1e2b20 !important; color: #a8e6b8 !important; }
/* 文字：标题提亮 ✗ 正文次之 ✗ 链接绿白 ✓ */
html[data-theme="dark"] .tool-panel h1,
html[data-theme="dark"] .tool-panel h2,
html[data-theme="dark"] .tool-panel h3,
html[data-theme="dark"] .tool-panel h4,
html[data-theme="dark"] .tool-panel strong,
html[data-theme="dark"] .tool-panel b { color: #ecf9f0 !important; }
html[data-theme="dark"] .tool-panel,
html[data-theme="dark"] .tool-panel p,
html[data-theme="dark"] .tool-panel li,
html[data-theme="dark"] .tool-panel label,
html[data-theme="dark"] .tool-panel span,
html[data-theme="dark"] .tool-panel small,
html[data-theme="dark"] .tool-panel em { color: #dff3e4 !important; }
html[data-theme="dark"] .tool-panel a { color: #a8e6b8 !important; }
html[data-theme="dark"] .tool-panel a:hover { color: #cdf5d8 !important; }
/* 图标保持亮绿 ✗ 别被上面的 span 规则带灰 ✓ */
html[data-theme="dark"] .tool-panel i[class*="fa"],
html[data-theme="dark"] .tool-panel .tool-icon,
html[data-theme="dark"] .tool-panel [class*="icon"] { color: #86efac !important; }
/* 状态色别压掉（工具页有"通过/警告/错误"三态 ✓） */
html[data-theme="dark"] .tool-panel .err,
html[data-theme="dark"] .tool-panel .error { color: #ff9c92 !important; }
html[data-theme="dark"] .tool-panel .warn,
html[data-theme="dark"] .tool-panel .warning { color: #ffd479 !important; }
html[data-theme="dark"] .tool-panel .ok,
html[data-theme="dark"] .tool-panel .pass { color: #8ce0a2 !important; }

@media (prefers-color-scheme: dark) and (min-width: 999999px) {  /* ★ 夜间模式暂时停用（2026-10 ✓ 用户要求）✗ 恢复就删掉 and (min-width: 999999px) ✓ */
    .tool-panel,
    .tool-panel .tool-sec,
    .tool-panel .tool-block,
    .tool-panel [class*="box"]:not([class*="btn"]):not([class*="tag"]),
    .tool-panel [class*="card"]:not([class*="btn"]),
    .tool-panel [class*="panel"]:not([class*="btn"]),
    .tool-panel [class*="wrap"]:not([class*="btn"]),
    .tool-panel [class*="item"]:not([class*="btn"]):not([class*="tag"]),
    .tool-panel [class*="list"]:not([class*="btn"]),
    .tool-panel [class*="row"]:not([class*="btn"]),
    .tool-panel [class*="field"]:not([class*="btn"]),
    .tool-panel [class*="out"]:not([class*="btn"]),
    .tool-panel [class*="preview"]:not([class*="btn"]),
    .tool-panel [class*="result"]:not([class*="btn"]),
    .tool-panel [class*="editor"]:not([class*="btn"]),
    .tool-panel [class*="stat"]:not([class*="btn"]) {
        background: #1a261c !important;
        border-color: #2a3f2e !important;
        color: #dff3e4 !important;
    }
    .tool-panel table, .tool-panel thead th, .tool-panel th, .tool-panel td,
    .tool-panel pre, .tool-panel code {
        background: #16201a !important; border-color: #2a3f2e !important; color: #dff3e4 !important;
    }
    .tool-panel thead th, .tool-panel th { background: #1e2b20 !important; color: #a8e6b8 !important; }
    .tool-panel h1, .tool-panel h2, .tool-panel h3, .tool-panel h4,
    .tool-panel strong, .tool-panel b { color: #ecf9f0 !important; }
    .tool-panel, .tool-panel p, .tool-panel li, .tool-panel label,
    .tool-panel span, .tool-panel small, .tool-panel em { color: #dff3e4 !important; }
    .tool-panel a { color: #a8e6b8 !important; }
    .tool-panel a:hover { color: #cdf5d8 !important; }
    .tool-panel i[class*="fa"], .tool-panel .tool-icon, .tool-panel [class*="icon"] { color: #86efac !important; }
    .tool-panel .err, .tool-panel .error { color: #ff9c92 !important; }
    .tool-panel .warn, .tool-panel .warning { color: #ffd479 !important; }
    .tool-panel .ok, .tool-panel .pass { color: #8ce0a2 !important; }
}

/* ============================================================
   ★★★ 钢琴琴键最终保险（2026-10 ✓ 用户要求"白键要绿白" ✓ 放文件最末一定赢 ✓）
   ------------------------------------------------------------
   ★ 为什么还要再来一道 ✗：白键的"脸"就在 `.pk-white` 本体上（page-tool.css:2552 ✓）
     而按下态画在 `.pk-white::before`（opacity 切换 ✓ 见 2578 行 ✓）
     两处都必须同时是**绿白** ✗ 否则按下时会跳成另一种颜色 ✓
   ★ 写在最末 + !important ⇒ 一定压得住前面所有重复的深色规则 ✓
   ============================================================ */
html[data-theme="dark"] .pk-white {
    background: linear-gradient(180deg, #e8f5ea 0%, #f6fdf8 78%, #d6ebdb 100%) !important;
    border-left-color: #cfdcd2 !important;
    box-shadow: 0 3px 0 #9fb3a4, 0 4px 5px rgba(0, 0, 0, .5) !important;
}
html[data-theme="dark"] .pk-white::before {
    background: linear-gradient(180deg, #d4ecd5 0%, #a5d6a7 60%, #8bc98e 100%) !important;
    box-shadow: inset 0 2px 6px rgba(27, 94, 32, .22) !important;
}
html[data-theme="dark"] .pk-white.pk-c {
    border-left-color: #b2c4b8 !important;
}
html[data-theme="dark"] .pk-label { color: #2f5a3a !important; }
html[data-theme="dark"] .pk-black {
    background: linear-gradient(180deg, #1b241c 0%, #0d120d 100%) !important;
}

@media (prefers-color-scheme: dark) and (min-width: 999999px) {  /* ★ 夜间模式暂时停用（2026-10 ✓ 用户要求）✗ 恢复就删掉 and (min-width: 999999px) ✓ */
    .pk-white {
        background: linear-gradient(180deg, #e8f5ea 0%, #f6fdf8 78%, #d6ebdb 100%) !important;
        border-left-color: #cfdcd2 !important;
        box-shadow: 0 3px 0 #9fb3a4, 0 4px 5px rgba(0, 0, 0, .5) !important;
    }
    .pk-white::before {
        background: linear-gradient(180deg, #d4ecd5 0%, #a5d6a7 60%, #8bc98e 100%) !important;
        box-shadow: inset 0 2px 6px rgba(27, 94, 32, .22) !important;
    }
    .pk-white.pk-c { border-left-color: #b2c4b8 !important; }
    .pk-label { color: #2f5a3a !important; }
    .pk-black { background: linear-gradient(180deg, #1b241c 0%, #0d120d 100%) !important; }
}
html[data-theme="dark"] .piano-panel {
        background: #10160f !important;
        color: #d9e6d9 !important;
    }html[data-theme="dark"] /* 工具条按钮 / 下拉 / 数字框 */
    .piano-bar .tool-btn:not(.primary) {
        background: #162013 !important;
        border-color: #24352a !important;
        color: #d9e6d9 !important;
    }html[data-theme="dark"] .piano-bar .tool-select, html[data-theme="dark"] .piano-bar .tool-num {
        background: #162013 !important;
        border-color: #24352a !important;
        color: #d9e6d9 !important;
    }html[data-theme="dark"] .piano-bar .tool-btn:not(.primary):hover {
        background: #1d2a1e !important;
    }html[data-theme="dark"] /* 时间 / 统计 / 提示 / 鸣谢：次要文字用暗一档的绿 ✓ */
    .piano-time, html[data-theme="dark"] .piano-bar .tool-stat, html[data-theme="dark"] .piano-hint, html[data-theme="dark"] .piano-credit, html[data-theme="dark"] .staff-title, html[data-theme="dark"] .piano-nav-range {
        color: #8fb38f !important;
    }html[data-theme="dark"] /* ★ 琴键压暗（关键 ✗ 不然一屏白块太刺眼 ✓）
       保留"上亮下暗"的立体感 ✗ 只是整体降到深绿灰 ✓ */
    .pk-white {
        background: linear-gradient(180deg, #e8f5ea 0%, #f6fdf8 78%, #d6ebdb 100%) !important;
        border-left-color: #1a241a !important;
        box-shadow:
            0 3px 0 #16200f,
            0 4px 5px rgba(0, 0, 0, .55) !important;
    }html[data-theme="dark"] .pk-white.pk-c {
        background: linear-gradient(180deg, #e6f2e9 0%, #f6fbf7 78%, #dcebdf 100%) !important;
    }html[data-theme="dark"] .pk-black {
        background: linear-gradient(180deg, #0b100b 0%, #131a13 100%) !important;
    }html[data-theme="dark"] .pk-label {
        color: #a9c2a9 !important;
    }html[data-theme="dark"] /* 按下态（原来在浅色下是"变亮"✗ 深色里改成"提亮一档绿"✓） */
    .pk-white.is-on {
        background: linear-gradient(180deg, #d4ecd5 0%, #a5d6a7 60%, #8bc98e 100%) !important;
    }html[data-theme="dark"] /* 常用和弦片 / 谱表卡片 */
    .chord-chip {
        background: #162013 !important;
        border-color: #24352a !important;
        color: #d9e6d9 !important;
    }html[data-theme="dark"] .piano-staff {
        background: #131c15 !important;
        border-color: #24352a !important;
    }html[data-theme="dark"] /* 音符颜色下拉的选项（部分浏览器会继承系统色 ✗ 写死更稳 ✓） */
    .piano-bar .tool-select option {
        background: #162013 !important;
        color: #d9e6d9 !important;
    }


    font-weight: 700;
}

/* ---------- 品格格 ---------- */
.fret-cell {
    position: relative;
    flex: 1 1 0;
    min-width: 30px;
    height: 34px;
    margin: 1px 0;
    padding: 0;
    background: #fff;
    border: none;
    border-right: 1px solid #dfe8e2;    /* 琴品 */
    cursor: pointer;
    transition: background .06s;
}

.fret-cell:hover {
    background: #f3f8f4;
}

/* 空弦格（0 品）用浅灰底区分，并且左边界画粗一点当"琴枕" */
.fret-cell.fret-open {
    background: #f8fbf9;
    border-left: 2px solid #b9c9bf;
    border-right: 2px solid #b9c9bf;
}

/* ★ 顺级和弦行（2026-10 新增 ✓ 配合"选调 + 循环弹唱"）
   复用 .chord-chip 的外观 ✓ 只是排成一整行 ✓ 每个上面是大写的和弦名、下面是级数 ✓ */
.fret-degree-row {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    align-items: center;
}

.fret-degree-row .chord-degree {
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    min-width: 46px;
    line-height: 1.15;
    padding: 4px 9px;
}

.fret-degree-row .chord-degree b {
    font-weight: 700;
    font-size: 13px;
}

/* 级数用罗马数字小字标出 —— Ⅰ Ⅳ Ⅴ 一眼就知道功能，比光看和弦名有用 ✓ */
.fret-degree-row .chord-degree i {
    font-style: normal;
    font-size: 10px;
    opacity: .62;
}

/* 正在循环弹唱的那个和弦：描个边 + 轻呼吸，让"现在响的是它"一目了然 ✓ */
.chord-chip.is-looping {
    outline: 2px solid #2e7d32;
    outline-offset: 1px;
    animation: chord-breathe 1.38s ease-in-out infinite;
}

@keyframes chord-breathe {
    0%, 100% { box-shadow: 0 0 0 0 rgba(46, 125, 50, .35); }
    50%      { box-shadow: 0 0 0 5px rgba(46, 125, 50, 0); }
}

/* 尊重"减少动效"偏好 ✓ 循环标记不闪也没关系 ✓ 描边足够提示了 ✓ */
@media (prefers-reduced-motion: reduce) {
    .chord-chip.is-looping { animation: none; }
}

.fret-cell.is-on {
    background: linear-gradient(180deg, #c8e6c9 0%, #a5d6a7 100%);
    box-shadow: inset 0 2px 5px rgba(27, 94, 32, .25);
}

/* ★ 点过的格子**常驻**高亮（2026-10 加 ✓）
   原来的 .is-on 是"按住才亮"✓ 松手 60ms 就褪掉 ✗ 用户反馈"点一下绿了、过会儿又没了"✗
   这里用单独的类 ✓ 点一下亮 ✓ 再点一下灭 ✓ 中间不管鼠标去哪都亮着 ✓
   对比 .is-on 稍微深一点 ✓ 这样"刚弹响的音"和"标记过的音"能区分开 ✓ */
.fret-cell.is-locked {
    background: linear-gradient(180deg, #a5d6a7 0%, #81c784 100%);
    box-shadow: inset 0 2px 6px rgba(27, 94, 32, .35), 0 0 0 1px rgba(46, 125, 50, .35);
}

/* 锁定的格子即使悬停也保持绿色（否则鼠标划过会被 :hover 的浅底盖掉） */
.fret-cell.is-locked:hover {
    background: linear-gradient(180deg, #a5d6a7 0%, #81c784 100%);
}

/* 音名标签：默认隐藏，勾选「标出音名」后显示 */
.fret-label {
    position: absolute;
    top: 0; right: 0; bottom: 0; left: 0;   /* 老浏览器不认 inset ✗ 先给等价的 ✓ 下一行覆盖 ✓ */
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 9px;
    color: #8fa895;
    pointer-events: none;
    opacity: 0;
    transition: opacity .12s;
}

.fret-grid.show-names .fret-label {
    opacity: 1;
}

@media (max-width: 760px) {
    .fret-cell {
        height: 30px;
        min-width: 26px;
    }
    .fret-tune {
        flex: 0 0 30px;
        font-size: 10px;
    }
    .fret-num {
        min-width: 26px;
    }
    .fret-vol .tool-range {
        width: 70px;
    }
}

/* ============================================================
   乐器 · 打击垫（架子鼓）
   ============================================================ */

/* ================= 俯视鼓组图（2026-10 新增 ✓ 架子鼓图形化）=================
   为什么要图形 ✗：打击垫再怎么排也只是"十个方块"✗ 看着不像乐器 ✗
     鼓是最讲究**空间感**的乐器——哪只手打哪件、从左到右什么顺序 ✗
     一张俯视图能把这件事讲清楚 ✓ 一排方块讲不了 ✗
   实现要点 ✓：
     · 纯椭圆 + 径向渐变 ✓ 零第三方代码 ✓ 零授权风险 ✓
     · 高亮用 **animation** 而不是 transition ✗
       —— 琴键那次的教训：transition 频率一高就掉帧发热 ✗
          animation 只在敲的那一下跑 ✓ 且 transform/filter 都是合成属性 ✓ 不触发重排 ✓
     · 尊重 prefers-reduced-motion ✓ 不动的动画照常发声 ✓ */
.drum-kit {
    margin: 16px 0 12px;
    display: flex;
    justify-content: center;
}

.drum-kit svg {
    width: 100%;
    max-width: 720px;
    height: auto;
    display: block;
    filter: drop-shadow(0 6px 14px rgba(22, 62, 38, .10));
}

.drum-piece {
    cursor: pointer;
    outline: none;
}

/* 鼓圈 / 镲片：默认按鼓面画 ✓ 镲片和拍手各自覆盖 ✓ */
.drum-piece .drum-rim {
    fill: url(#dkDrum);
    stroke: #a3b3ab;
    stroke-width: 2;
}

.drum-piece[data-kind="cymbal"] .drum-rim {
    fill: url(#dkCymbal);
    stroke: #a8802a;
}

.drum-piece[data-kind="beat"] .drum-rim {
    fill: url(#dkClap);
    stroke: #8fb39c;
}

/* 内圈：鼓是"鼓面的边"✓ 镲片是"车削纹"✓ */
.drum-piece .drum-face {
    fill: rgba(255, 255, 255, .46);
    stroke: #cdd9d3;
    stroke-width: 1;
}

.drum-piece[data-kind="cymbal"] .drum-face {
    fill: rgba(255, 255, 255, .16);
    stroke: rgba(255, 255, 255, .40);
}

/* 镲片的"杯"（中间凸起）✓ 没有它就像个盘子 ✗ */
.drum-piece .drum-bell {
    fill: url(#dkBell);
    stroke: rgba(120, 88, 20, .45);
    stroke-width: 1;
}

/* 鼓面高光 ✓ 让鼓看起来是"鼓着"的 ✓ */
.drum-piece .drum-gloss {
    fill: rgba(255, 255, 255, .70);
}

/* 底鼓压深一点 ✓ 它是整套鼓里最大最深的那件 ✓ */
.drum-piece-kick .drum-rim {
    fill: url(#dkKick);
    stroke: #8d9c95;
    stroke-width: 2.5;
}

.drum-piece-kick .drum-face {
    fill: rgba(255, 255, 255, .34);
}

/* 件名：小字压在每件下方 ✓ 不挡图形 ✓ */
.drum-svg-name {
    text-anchor: middle;
    font-size: 12px;
    fill: #6b7d73;
    pointer-events: none;
    user-select: none;
}

.drum-piece:hover .drum-rim {
    stroke: #2e7d32;
}

/* 键盘 Tab 能聚焦 ✓ 聚焦时描一圈虚线 ✓ 不依赖鼠标 ✓ */
.drum-piece:focus-visible .drum-rim {
    stroke: #2e7d32;
    stroke-width: 3;
    stroke-dasharray: 6 3;
}

/* 敲击反馈：抖一下 + 亮一下 ✓ */
.drum-piece.is-hit .drum-rim,
.drum-piece.is-hit .drum-face {
    animation: drum-hit .24s ease-out;
}

@keyframes drum-hit {
    0%   { transform: scale(1);    filter: brightness(1); }
    28%  { transform: scale(.955); filter: brightness(1.22); }
    100% { transform: scale(1);    filter: brightness(1); }
}

/* ★ SVG 里 transform 的默认原点是画布原点 ✗ 直接 scale 会让图形"飞走"✗
   必须把原点改成各自图形的中心 ✓ 这是 SVG 动画最容易踩的坑 ✓ */
.drum-piece .drum-rim,
.drum-piece .drum-face,
.drum-piece .drum-bell,
.drum-piece .drum-gloss {
    transform-box: fill-box;
    transform-origin: center;
}

@media (prefers-reduced-motion: reduce) {
    .drum-piece.is-hit .drum-rim,
    .drum-piece.is-hit .drum-face { animation: none; }
}


.drum-panel {
    padding: 16px 16px 20px;
}

.drum-bar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 12px;
}

.drum-grid {
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    gap: 10px;
    /* 打击垫上要连续点击与多指同按，关掉浏览器的触摸滚动接管 */
    touch-action: none;
    user-select: none;
    -webkit-user-select: none;
}

.drum-pad {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 3px;
    min-height: 86px;
    padding: 8px 4px;
    font-family: inherit;
    color: #2b3a31;
    background: #f6faf7;
    border: 1px solid #dbe7de;
    border-radius: 10px;
    cursor: pointer;
    transition: all .08s;
}

.drum-pad:hover {
    background: #eef6f0;
    border-color: #a5d6a7;
}

.drum-pad.is-on {
    background: linear-gradient(180deg, #c8e6c9 0%, #a5d6a7 100%);
    border-color: #66bb6a;
    transform: scale(.96);
    box-shadow: inset 0 3px 8px rgba(27, 94, 32, .3);
}

.drum-name {
    font-size: 13px;
    font-weight: 600;
    pointer-events: none;
}

/* GM 编号默认隐藏，勾选后显示 —— 它的作用是让人能对照电子鼓 / MIDI 键位 */
.drum-gm {
    font-size: 10px;
    color: #8fa895;
    pointer-events: none;
    opacity: 0;
    transition: opacity .12s;
}

.drum-grid.show-gm .drum-gm {
    opacity: 1;
}

/* 按鼓件类型给一点底色差异，扫一眼就能区分镲、鼓、军鼓 */
.drum-pad.drum-kick {
    background: #eef4f0;
}

.drum-pad.drum-snare {
    background: #f4f8f2;
}

.drum-pad.drum-crash,
.drum-pad.drum-ride,
.drum-pad.drum-hh,
.drum-pad.drum-hho {
    background: #f8fbf9;
    border-style: dashed;
}

@media (max-width: 760px) {
    .drum-grid {
        grid-template-columns: repeat(3, 1fr);
        gap: 8px;
    }
    .drum-pad {
        min-height: 68px;
    }
    .drum-name {
        font-size: 12px;
    }
}

/* ============================================================
   工具页嵌入模式（?embed=1）—— 供歌曲页 iframe 嵌入（在线钢琴等）
   ------------------------------------------------------------
   嵌入页只有一个工具本体，没有导航 / 标题 / 说明 / FAQ，所以：
     · 去掉外壳留白与卡片边框，让琴体直接占满 iframe
     · 把琴键放大（更高更宽），配合底部横向滚动条浏览完整音区
   ============================================================ */
.tool-embed-body { background: #fff; }
.tool-embed { max-width: none; padding: 0; margin: 0; }
.tool-embed .tool-body { margin: 0; }
.tool-embed .piano-panel {
    padding: 12px 12px 16px;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    background: #fff;
}
/* 嵌入页与工具页共用同一套琴键尺寸（--pk-w 定义在 .piano-panel 上） */
.tool-embed .pk-label { font-size: 12px; bottom: 8px; }
/* 嵌入页省掉一行键盘说明，整体高度刚好塞进歌词页 iframe，不再出现内部滚动条 */
.tool-embed .piano-hint { display: none; }
/* 现场谱子：嵌入页里贴在琴键下面，留一点上间距即可（开关打开才出现） */
.tool-embed .piano-staff { margin: 10px 0 6px; }
.tool-embed .piano-staff .staff-box { margin: 0; }
.tool-embed .piano-notes .pn-chip { min-width: 30px; padding: 2px 5px; }
/* 底部进度条（横向滚动条）做得更明显，方便左右拖动 */
.tool-embed .piano-scroll { padding-bottom: 16px; }
.tool-embed .piano-scroll::-webkit-scrollbar { height: 12px; }
.tool-embed .piano-scroll::-webkit-scrollbar-thumb { background: #8aa392; border-radius: 6px; }
.tool-embed .piano-scroll::-webkit-scrollbar-thumb:hover { background: #75907f; }

/* ================= 一次性性能提醒（2026-10 用户要求 ✓）=================
   首次开启「特效模式」或「显示谱子」时弹一个小卡片 ✗ 点"我知道了"永久关掉 ✓
   ★ 为什么用 fixed 而不是塞进工具栏 ✗：
     工具栏在窄屏会换行 ✗ 提示挤在里面更难看见 ✓
     fixed 浮在右下角 ✗ 不占布局 ✗ 也不会把琴键顶动 ✓
   ★ 层级要够高但别打架 ✗：全屏面板是 99999 ✗ 所以给 100000 ✓
     （它本身也放在面板里面 ✗ 全屏时照样看得见 ✓）
   ★ 为什么 [hidden] 要单独写一条 ✗：
     下面给了 .piano-advice 一个 display 相关的能力 ✗ 不明确写的话
     某些浏览器里 hidden 的默认样式会被覆盖掉 ✗ 卡片就会一直挂着 ✓ */
.piano-advice {
    position: fixed;
    right: 18px;
    bottom: 18px;
    z-index: 100000;
    max-width: min(92vw, 390px);
    animation: advice-in .26s ease-out;
}
.piano-advice[hidden] { display: none !important; }

.piano-advice-card {
    background: #fff;
    border: 1px solid #d6e3da;
    border-radius: 12px;
    box-shadow: 0 10px 30px rgba(20, 45, 30, .22);
    padding: 14px 16px 12px;
}

.piano-advice-title {
    font-weight: 700;
    font-size: 14px;
    color: #1d3a2a;
    margin-bottom: 6px;
}

.piano-advice-body {
    margin: 0 0 12px;
    font-size: 13px;
    line-height: 1.65;
    color: #4a5f54;
}

.piano-advice .tool-btn { width: 100%; }

/* 小屏上让它占满下方 ✗ 免得卡片挤在角落里看不清 ✓ */
@media (max-width: 560px) {
    .piano-advice { left: 12px; right: 12px; bottom: 12px; max-width: none; }
}

@keyframes advice-in {
    from { opacity: 0; transform: translateY(10px); }
    to   { opacity: 1; transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
    .piano-advice { animation: none; }
}

/* ============================================================
   MANUAL DARK MODE (2026-10)
   ------------------------------------------------------------
   与上面的 @media (prefers-color-scheme: dark) 逐条同源，
   只是触发条件换成 html[data-theme=dark]（右侧按钮 + js.js 控制）。
   本块由 tools 脚本生成，请勿手改：改了 @media 那块后重跑脚本即可。
   注意：规则必须放在顶层（前缀加在选择器上），
   否则声明块里套规则属于非法结构，会被浏览器整块忽略。
   ============================================================ */

/* ============================================================
   日间模式「还原」规则（2026-10 ✓ 修一个真实 bug ✓）
   ------------------------------------------------------------
   ★ 症状 ✓：手机系统是深色时 ✗ 点夜间按钮切到"日间"**关不掉**✓
     原因 ✗：原来"日间"= 移除 data-theme ✗
       而 `@media (prefers-color-scheme: dark)` 依然生效 ⇒ 页面还是深色 ✓
   ★ 修法 ✓：JS 显式写 `data-theme="light"`✗
     这里补一组 `html[data-theme="light"]` 的还原规则 ✗ 把主要界面显式变回浅色 ✓
     ★ 权重 ✓：`html[data-theme="light"] .x` = (0,2,1)✗
       高于 `@media` 里的 `.x` = (0,1,1)⇒ **稳赢**✗ 不需要动那些 @media 块 ✓
   ★ 只还原"确定知道原值"的表面 ✗ 黑键本来就是黑的 ✗ 不碰 ✓
   ============================================================ */
html[data-theme="light"] body { background: #f5faf5 !important; color: #1e2e1e !important; }
html[data-theme="light"] .header_wrap { background: #fff !important; border-bottom-color: #e8f5e9 !important; box-shadow: 0 2px 12px rgba(0,0,0,.04) !important; }
html[data-theme="light"] .music-card,
html[data-theme="light"] .tool-card,
html[data-theme="light"] .tool-panel,
html[data-theme="light"] .piano-panel,
html[data-theme="light"] .article-main,
html[data-theme="light"] .lyric-main,
html[data-theme="light"] .pkl-table,
html[data-theme="light"] .pkl-work,
html[data-theme="light"] .hub-card,
html[data-theme="light"] .hp-card,
html[data-theme="light"] .artist-list-box,
html[data-theme="light"] .fav-panel,
html[data-theme="light"] .recommend-box,
html[data-theme="light"] .lang-dropdown {
    background: #fff !important;
    border-color: #e3e9e4 !important;
    color: #1e2e1e !important;
}
html[data-theme="light"] .pk-white {
    background: linear-gradient(180deg, #ffffff 0%, #ffffff 78%, #eef2ef 100%) !important;
    border-left-color: #dde4df !important;
    box-shadow: 0 3px 0 #cdd6d0, 0 4px 5px rgba(0, 0, 0, .28) !important;
}
html[data-theme="light"] .pk-white::before {
    background: linear-gradient(180deg, #d4ecd5 0%, #a5d6a7 60%, #8bc98e 100%) !important;
    box-shadow: inset 0 2px 6px rgba(27, 94, 32, .22) !important;
}
html[data-theme="light"] .pk-white.pk-c { border-left-color: #b2c4b8 !important; }
html[data-theme="light"] .pk-label { color: #6b7a6e !important; }
html[data-theme="light"] .fav-fab {
    background: #fff !important;
    color: #2e7d32 !important;
    border-color: #cfe0d3 !important;
}
html[data-theme="light"] .fav-fab:hover { background: #f2f8f3 !important; color: #1b5e20 !important; }
html[data-theme="light"] a { color: #2e7d32 !important; }
html[data-theme="light"] a:hover { color: #1b5e20 !important; }

/* ============================================================
   ★★★★ 2026-10 关键修复：声明 color-scheme（白键为什么"改了还是黑"的答案 ✓）
   ------------------------------------------------------------
   症状 ✓：夜间模式下钢琴白键**怎么改都是黑的**✗ 换了几次颜色都没用 ✓
   真因 ✓：手机浏览器（Chrome / Edge 的"夜间模式 / 强制深色"✓）
     在页面**没有声明 color-scheme** 时 ✗ 会**自动把浅色元素反色成深色**✓✓
     ⇒ 我把白键设成**绿白**✗ 浏览器又把它**反成黑色**⇒ 看到的永远是黑的 ✓
     ⇒ 这解释了"改对了却看着没变"✓（不是 CSS 没生效 ✗ 是被浏览器翻过去了 ✓）
   修法 ✓（标准做法 ✗ 就这三行 ✓）：
     · `:root { color-scheme: light dark; }` ⇒ 明确告诉浏览器"两套我自己处理"✓
       ⇒ **浏览器从此不再强行反色**✓✓
     · 手动切到夜间 / 日间时 ✗ 同步把 color-scheme 也切过去 ✓
       这样连**表单控件 / 滚动条 / 默认底色**都会跟着对 ✓
   ★ 放在每个 CSS 文件里 ✗ 因为有些页面只加载其中某一个 ✓
   ============================================================ */
:root { color-scheme: light dark; }
html[data-theme="dark"] { color-scheme: dark; }
html[data-theme="light"] { color-scheme: light; }

/* ============================================================
   ★★★★ 琴键专属修正（2026-10 ✓ 用户找到真凶后加 ✓）
   ------------------------------------------------------------
   真凶 ✓（用户截图给出的那条 ✓）：
     html[data-theme="dark"] button:not(.primary) { background: rgb(30,43,32) !important }
     特异性 **(0,3,1)** —— 而白键就是 `<button class="pk-white">`✗ 被它一并染色 ✓
     ⇒ 高于我写白键的 (0,2,1) ⇒ **我怎么改 .pk-white 都会被它压掉**✓✓
       （这就是"改了无数遍都没成功"的真正原因 ✓）
   修法（两步 ✓ 都做了 ✓）：
     ① 那 4 条按钮规则已改成 `button:not(.primary):not(.pk-white):not(.pk-black)`
        ⇒ 琴键**不再被那条规则碰到**✓（根因修复 ✓）
     ② 这里再用**同权重 (0,3,1) + 写在更后**钉一道保险 ✓
        ⇒ 即使将来又有人写宽泛的 button 规则 ✗ 这里也压得住 ✓
   ★ 还顺手修了一个隐患 ✓：`html[data-theme="dark"] .tool-panel span`（0,3,1）
     会压掉我的 `.pk-label`（0,2,1）⇒ 白键上的**音名会变浅色**✗ 在绿白键上看不清 ✓
   ============================================================ */
html[data-theme="dark"] .piano-keys .pk-label,
html[data-theme="dark"] .piano-keys .pk-white .pk-label,
html[data-theme="dark"] .piano-keys .pk-white.pk-c .pk-label {
    color: #2f5a3a !important;
}
html[data-theme="dark"] .piano-keys .pk-white,
html[data-theme="dark"] .piano-keys button.pk-white {
    background: linear-gradient(180deg, #e8f5ea 0%, #f6fdf8 78%, #d6ebdb 100%) !important;
    border-left-color: #cfdcd2 !important;
    color: #2f5a3a !important;
}
html[data-theme="dark"] .piano-keys .pk-white::before {
    background: linear-gradient(180deg, #d4ecd5 0%, #a5d6a7 60%, #8bc98e 100%) !important;
}
html[data-theme="dark"] .piano-keys .pk-black {
    background: linear-gradient(180deg, #1b241c 0%, #0d120d 100%) !important;
}

@media (prefers-color-scheme: dark) and (min-width: 999999px) {  /* ★ 夜间模式暂时停用（2026-10 ✓ 用户要求）✗ 恢复就删掉 and (min-width: 999999px) ✓ */
    .piano-keys .pk-label,
    .piano-keys .pk-white .pk-label,
    .piano-keys .pk-white.pk-c .pk-label { color: #2f5a3a !important; }
    .piano-keys .pk-white,
    .piano-keys button.pk-white {
        background: linear-gradient(180deg, #e8f5ea 0%, #f6fdf8 78%, #d6ebdb 100%) !important;
        border-left-color: #cfdcd2 !important;
        color: #2f5a3a !important;
    }
    .piano-keys .pk-white::before {
        background: linear-gradient(180deg, #d4ecd5 0%, #a5d6a7 60%, #8bc98e 100%) !important;
    }
    .piano-keys .pk-black { background: linear-gradient(180deg, #1b241c 0%, #0d120d 100%) !important; }
}

/* ============================================================
   ★★ 特效钢琴：弹奏区背景加"绿色荧光感"（2026-10 ✓ 用户要求 ✓）
   ------------------------------------------------------------
   用户原话 ✓："把这个特效钢琴弹奏的背景 ✗ 改得更有绿色荧光感觉一点"✓
   做法 ✓：给画布 `.pk-roll` 加一层**绿色荧光渐变**✗
     · 底部（琴键那侧 ✓）最亮 ✗ 像"从琴键往上透出的光"✓
     · 往上逐渐变暗 ✗ 收进深绿 ✗ 有纵深 ✓
     · 再叠一层很淡的径向光 ✗ 让中间有"聚光"的感觉 ✓
   ★ 为什么写在 `.pk-roll` 上 ✓：
     画布本身**没有背景色**（它只画音条 / 水印 / 微尘 ✓）
     ⇒ 背景色写它身上 ✗ 内容自然压在这层光之上 ✓✓
   ★ 只在特效模式出现 ✓：`.piano-scroll.is-roll .pk-roll { display: block }`
     ⇒ 关掉特效模式时画布不显示 ✗ 这层光也跟着不见 ✓ 不会影响普通弹奏 ✓
   ★ 这一层**不分深色/浅色**（用户要的是"弹奏区更好看"✓）
     而弹奏区本身就是深色底 ✗ 所以浅色模式下也这么显示 ✓
   ============================================================ */
.piano-scroll .pk-roll,
.piano-scroll.is-roll .pk-roll {
    background:
        radial-gradient(170% 150% at 50% 50%,
            rgba(74, 222, 128, .26) 0%,
            rgba(52, 178, 104, .13) 42%,
            rgba(20, 60, 36, .05) 72%,
            rgba(0, 0, 0, 0) 100%),
        linear-gradient(180deg,
            #0b1410 0%,
            #101d15 38%,
            #14281a 68%,
            #1a3a24 100%);
}
/* 全屏里也保持同一层光 ✓（全屏有自己的高度规则 ✗ 但背景不冲突 ✓） */
.piano-panel.is-fs .pk-roll,
.piano-panel:fullscreen .pk-roll,
.piano-panel.is-faux-full .pk-roll {
    background:
        radial-gradient(170% 150% at 50% 50%,
            rgba(74, 222, 128, .28) 0%,
            rgba(52, 178, 104, .14) 42%,
            rgba(20, 60, 36, .05) 72%,
            rgba(0, 0, 0, 0) 100%),
        linear-gradient(180deg,
            #0b1410 0%,
            #101d15 38%,
            #14281a 68%,
            #1a3a24 100%);
}


/* ★★★ 2026-10 用户要求：**手机端隐藏“全屏”按钮** ✓
   用户原话：“这个全屏按钮也给它隐藏掉，不是删掉，是隐藏掉” ✓
   ★ 为什么手机上不该有它 ✗：
     · 手机上习惯竖着看 ✗ 全屏横过来反而要扭转手机 ✗
     · 工具条里按钮一多 ✗ 手机上会挤成两行 ✗ 更难按 ✓
   ★ 为什么是“隐藏”不是“删掉” ✓：
     按钮仍留在 DOM 里 ✗ JS 照旧能拿到（判空的地方照常跳过 ✓）
     ⇒ **桌面端行为一个像素都不变** ✓ 将来想恢复只要删掉这一段 ✓
   ★ 断点 768px ✗ 与 JS 里的 `(max-width: 768px)` 完全一致 ✓
     （也同 tool-piano.js 里“窄屏不算全屏”的修复保持一致 ✓）✓ */
@media (max-width: 768px) {
    .piano-panel #pianoFullscreen {
        display: none !important;
    }
}


/* ★★★ 【特效钢琴 V2 优化层 · 由 _t/build-online.js 生成】★★★ */
/*
 * 来源：_t/piano2/page-tool2.css（V2 原型副本里手工维护的那份 ✓）
 * 生成：node _t/build-online.js ⇒ 自动把 body.v2-route 前缀去掉后追加到这里 ✓
 * ★ 别手改这一段 ✗ 改了下次重跑会被覆盖 ✓ 要改就改副本那份 ✓
 *
 * 这些类都由 static/js/tool-piano.js 在运行时建出来 ✓（页面 HTML 不用改 ✓）：
 *   .pk-strip-wrap / .pk-strip / .pk-strip-tile   音条带（画一次 + 只位移 ✓）
 *   .pk-hitline                                    命中线 + 键盘上沿那条泛光带
 *   .pk-sparks / .pk-spark                         落键火星（WAAPI ✓ 合成器 ✓）
 *   .pk-dust-wrap / .pk-dust                       背景微尘（CSS 动画 ✓ 每帧 0 JS）
 *   --pk-wm / --pk-wm-size                         水印（JS 用 toDataURL 挂上 ✓）
 *   --bar-b / --bar-o / --glow-h / --glow-b / --glow-bsec / --glow-anim
 *                                                  音条亮度·不透明度 / 泛光带旋钮
 *
 * ★ 层次（必须与老路完全一致 ✗ 否则观感就变了 ✓）：
 *   画布(z1 背景) → 微尘(z1 靠 DOM 顺序压上) → 条带(z2) → 命中线(z3) → 火星(z5)
 *
 * ★★ 性能红线（踩过 ✗ 别再踩 ✓）：
 *   · 每帧在动的层（.pk-strip / .pk-spark）**只许动 transform / opacity**
 *   · filter / mask / mix-blend-mode 一律挂在**不动的那层**
 *     （.pk-strip-wrap ✓ 当年把 blend 挂在位移层上 ⇒ 直接掉到 0.5fps ✓）
 */

/* V2 路线增量样式 —— 一律挂在 下 ✗ 不污染原版 page-tool.css
 * 生成器：_t/build-piano2.js ✓ 这个文件手工维护 ✓ 重新生成不会覆盖它 ✓ */
/* ============================================================
 * ★ 浏览器兼容备注（2026-10 ✓ 与 static/js/tool-piano.js 里的「兼容层」配套 ✓）
 * ------------------------------------------------------------
 * 这一整套 V2 样式**大量依赖 CSS 变量**（--pk-wm / --pk-wm-size / --pk-wm-x /
 *   --bar-b / --bar-o / --glow-h / --glow-b / --glow-bsec / --glow-anim …）
 *   ⇒ IE11 **完全没有 CSS 变量** ⇒ 玻璃区背景 / 水印 / 泛光带都出不来 ✓
 *   ⇒ 但 IE11 也**没有 Web Audio** ✗ 本来就弹不响 ✗ 所以 IE11 不在支持范围内 ✓
 *
 * 已经做好的退路 ✓（都写在 static/css/page-tool.css 的对应规则里）：
 *   · inset           → 前面先写一份等价的 top/right/bottom/left（Safari<14.1 ✓）
 *   · color-mix       → 前面先写一份纯色 background（见 .inst-go 那段 ✓）
 *   · 全屏态           → 由 JS 统一打 .is-fs 类 ✗ **不依赖** :fullscreen /
 *                       :-webkit-full-screen / :-moz-full-screen ✓（F11 也认 ✓）
 *   · 现代语法（:is / :where / clamp / dvh …）→ 本文件一条都没用 ✓ 不用退 ✓
 *
 * 已知「老浏览器只是不好看、但功能全在」的点 ✓（不硬修 ✗ 记在这里 ✓）：
 *   · flex 的 gap（Safari<14.1 / Chrome<84 / FF<63）⇒ 工具栏控件间距变 0 ✗
 *     没法用 @supports 区分（gap:1px 在只支持 grid-gap 的浏览器里也返回 true ✓）
 *     ⇒ 只有那一档浏览器会看到「控件贴着」✗ 布局 / 弹奏 / 播放全不受影响 ✓
 * ============================================================ */



/* ============================================================
 * ★★★ 第 1 步：水印 = 一层 CSS 背景（不再是画布内容）
 * ------------------------------------------------------------
 * 原来（每帧）：canvas 里 drawImage 一张整屏离屏图
 *   ⇒ 2400×240 @dpr1.86 ≈ 200 万像素的 alpha 混合 ✗ 每秒 1.2 亿像素 ✓
 * 现在（每帧）：0 ✗ 合成器直接复用这一层背景 ✓✓
 *
 * 分层顺序（从上到下 ✗ 这正是 CSS 背景的合成顺序 ✓）：
 *   ① 水印瓦片      var(--pk-wm)     ← JS 用 toDataURL 挂上来 ✓
 *   ② 绿色荧光径向  从琴键往上透出的光
 *   ③ 深绿纵向渐变  收进背景
 * ⇒ 与原来完全一致（原来水印也压在渐变之上、音条之下 ✓）
 *
 * ★ 变量来自 tool-piano2.js 的 applyRollWmLayer()
 *   --pk-wm      : url("data:image/png;base64,…") 或 none（文字被清空时 ✓）
 *   --pk-wm-size : "<step>px <mh>px"（瓦片尺寸 = 平铺步长 ✓）
 *   --pk-wm-x    : "<X>px"（★ 2026-10 新增 ✓ 让**当前可见区的中心**正对一张水印的中心 ✓
 *                  用户要求"文字要显示在背景居中"✗ 老写法从 0 起铺 ✗
 *                  可见区一变就偏 ✗ 长文字实测"贴着右边被切掉"✓
 *                  值由 tool-piano2.js 的 rollWmPlace 算 ✗ 并随横向滚动补偿 ✓）
 *   ★ 只在 尺寸/文字/主题 变化（+ 滚动）时才重设 ✗ 不是每帧 ✓
 *
 * ★ 这一层不分深色/浅色主题（弹奏区本来就是深色 ✗ 与原版一致 ✓）
 * ============================================================ */
.piano-scroll .pk-roll,
.piano-scroll.is-roll .pk-roll {
    background-image:
        var(--pk-wm, none),
        radial-gradient(170% 150% at 50% 50%,
            hsla(var(--bh, 152), 22%, 96%, var(--pk-glowA, .26)) 0%,
            hsla(var(--bh, 152), 20%, 90%, var(--pk-glowA2, .13)) 55%,
            hsla(var(--bh, 152), 18%, 82%, var(--pk-glowA3, .05)) 82%,
            rgba(0, 0, 0, 0) 100%),
        linear-gradient(180deg,
            hsla(var(--bh, 145), 30%, 6%, 1) 0%,
            hsla(var(--bh, 145), 29%, 9%, 1) 38%,
            hsla(var(--bh, 145), 33%, 12%, 1) 68%,
            hsla(var(--bh, 145), 38%, 16%, 1) 100%);
    background-size:
        var(--pk-wm-size, auto),
        auto,
        auto;
    background-repeat: repeat-x, no-repeat, no-repeat;
    /* ★★ 2026-10 改（用户：背景文字要"按长度整体缩放 + 居中"✓）
       原来写死 `0` ✗ ⇒ 水印网格从元素左边缘起铺 ✗ 与中心对不上 ✓
         ⇒ 水印一宽（长文字 ✓）就"贴着一边被切掉"✓
       现在 ✗ 起点由 JS 算（--pk-wm-x ✓ 见 tool-piano2.js 的 rollWmPlace ✓）
         ⇒ **可见区的中心**上必定压着一张水印的中心 ✓✓
         长文字任何宽度、任何滚动位置都居中 ✗ 永不切边 ✓
       ★ 兜底 0px ✗：JS 还没跑时与老行为一致 ✓ */
    background-position: var(--pk-wm-x, 0px) center, center, center;
}

/* 全屏 / 伪全屏同一套 ✗ 只是荧光略强（照抄原版的 .28/.14 ✓）
   ★★ 2026-10-15：与上面一样改成跟随主题色相 + 可调强度 ✓ */
.piano-panel.is-fs .pk-roll,
.piano-panel:fullscreen .pk-roll,
.piano-panel.is-faux-full .pk-roll {
    background-image:
        var(--pk-wm, none),
        radial-gradient(170% 150% at 50% 50%,
            hsla(var(--bh, 152), 22%, 96%, var(--pk-glowA, .26)) 0%,
            hsla(var(--bh, 152), 20%, 90%, var(--pk-glowA2, .13)) 55%,
            hsla(var(--bh, 152), 18%, 82%, var(--pk-glowA3, .05)) 82%,
            rgba(0, 0, 0, 0) 100%),
        linear-gradient(180deg,
            hsla(var(--bh, 145), 30%, 6%, 1) 0%,
            hsla(var(--bh, 145), 29%, 9%, 1) 38%,
            hsla(var(--bh, 145), 33%, 12%, 1) 68%,
            hsla(var(--bh, 145), 38%, 16%, 1) 100%);
    background-size:
        var(--pk-wm-size, auto),
        auto,
        auto;
    background-repeat: repeat-x, no-repeat, no-repeat;
    background-position: var(--pk-wm-x, 0px) center, center, center;
}

/* ★★ 2026-10-15 新增（用户："可以给它增加一个上传图片来当做这个背景的效果"✓）
   分层见 page-tool2.css 里那段注释 ✓ 这里只是**去掉 v2-route 前缀的同一份** ✓ */
.pk-roll.pk-has-bg {
    background-image:
        var(--pk-wm, none),
        radial-gradient(170% 150% at 50% 50%,
            hsla(var(--bh, 152), 22%, 96%, var(--pk-glowA, .26)) 0%,
            hsla(var(--bh, 152), 20%, 90%, var(--pk-glowA2, .13)) 55%,
            rgba(0, 0, 0, 0) 100%),
        linear-gradient(180deg,
            hsla(var(--bh, 145), 32%, 5%, var(--pk-bgdim, .62)) 0%,
            hsla(var(--bh, 145), 32%, 5%, var(--pk-bgdim, .62)) 100%),
        var(--pk-bg, none);
    background-size:
        var(--pk-wm-size, auto),
        auto,
        auto,
        cover;
    background-repeat: repeat-x, no-repeat, no-repeat, no-repeat;
    background-position: var(--pk-wm-x, 0px) center, center, center, center;
}
.piano-panel.is-fs .pk-roll.pk-has-bg,
.piano-panel:fullscreen .pk-roll.pk-has-bg,
.piano-panel.is-faux-full .pk-roll.pk-has-bg {
    background-image:
        var(--pk-wm, none),
        radial-gradient(170% 150% at 50% 50%,
            hsla(var(--bh, 152), 22%, 96%, var(--pk-glowA, .26)) 0%,
            hsla(var(--bh, 152), 20%, 90%, var(--pk-glowA2, .13)) 55%,
            rgba(0, 0, 0, 0) 100%),
        linear-gradient(180deg,
            hsla(var(--bh, 145), 32%, 5%, var(--pk-bgdim, .62)) 0%,
            hsla(var(--bh, 145), 32%, 5%, var(--pk-bgdim, .62)) 100%),
        var(--pk-bg, none);
    background-size:
        var(--pk-wm-size, auto),
        auto,
        auto,
        cover;
    background-repeat: repeat-x, no-repeat, no-repeat, no-repeat;
    background-position: var(--pk-wm-x, 0px) center, center, center, center;
}



/* ============================================================
 * ★★★ 第 2 步：落键火星（粒子）搬 CSS
 * ------------------------------------------------------------
 * 原来（每帧 · 在画布里）：每颗火星一次 drawImage
 *   ✗ 池子上限 140 ⇒ 一帧最多 140 次绘制（账本里占 11.9%）
 *   ✗ 而且完全在主线程上跑（主线程一卡 ✗ 粒子和画面一起停）
 * 现在：每颗火星 = 一个 <i> 元素
 *   ✓ 轨迹在**生成时**用 JS 一次算好（复刻原物理 ✓ 见 tool-piano2.js 的 cssSparkPath）
 *   ✓ 交给 WAAPI 播（transform / opacity ⇒ **合成器线程** ✗ 主线程不参与 ✓✓）
 *   ✓ 主线程卡顿时粒子照样飞 ✓ 而画布那边则完全不画火星了 ✓
 *
 * ★ 软光图不用图片 —— 直接用 CSS 径向渐变（stops 与画布版 V0 配方**逐档相同** ✓）
 *   ⇒ 零网络请求 ✗ 零 data-URI ✗ 而且矢量 ⇒ 任何尺寸都清晰 ✓✓
 * ★ --h 是色相（每颗按音符颜色给 ✗ 由 JS 写行内样式 ✓）
 * ★ 尺寸 = scale(sz/48) ✗ 盒子固定 48×48，全靠 transform 缩放 ✓（合成器干活 ✓）
 *   margin 负半宽 ⇒ translate 可直接写"火星中心点"✓ 与画布 drawImage(x-sz/2,…) 等价 ✓
 * ============================================================ */
.pk-sparks {
    position: absolute;
    left: 0; top: 0;
    overflow: hidden;              /* 与画布一样：飞出边界就裁掉 ✓ */
    pointer-events: none;
    z-index: 3;                    /* 压在画布之上（原版火星也是最后画 ✗ 在最前面 ✓）*/
    contain: layout style;         /* 这层内部不影响外部布局 ✓ 帮浏览器省事 ✓ */
}
.pk-spark {
    position: absolute;
    left: 0; top: 0;
    width: 48px; height: 48px;
    margin: -24px 0 0 -24px;       /* ★ 让 translate 直接写"中心点"✓ 不必每帧减半宽 ✓ */
    opacity: 0;
    background-repeat: no-repeat;
    background-size: 100% 100%;
    /* 与 tool-piano2.js 里 makeSparkSprites() 的 V0 配方逐档一致 ✓ 观感不变量 ✓ */
    background-image: radial-gradient(circle at 50% 50%,
        hsla(var(--h, 100), 100%, 94%, 1)    0%,
        hsla(var(--h, 100),  99%, 74%, .92) 34%,
        hsla(var(--h, 100),  96%, 60%, .40) 62%,
        hsla(var(--h, 100),  94%, 52%, .10) 86%,
        hsla(var(--h, 100),  90%, 45%, 0)  100%);
    /* ★ 不写 will-change ✗ 也不逐颗提升图层：
       WAAPI 的 transform/opacity 动画走浏览器自带的"合成动画"路径 ✓
       140 个常驻 will-change 反而会逼出 140 个图层 ✗ 低配上更危险 ✓ */
}
/* 保证 z-index 生效：滚动层当定位祖先 ✗ 画布自己起一层 ✓ */
.piano-scroll { position: relative; }
.pk-roll { position: relative; z-index: 1; }

/* ============================================================
 * ★★★ 第 3 步：音条「画一次 + 只位移」
 * ------------------------------------------------------------
 * 层次（**必须与老路完全一致** ✗ 否则观感就变了 ✓）
 *   微尘(画布 z1) → 音条(条带 z2) → 命中线(z3) → 火星(z4) ✓
 *
 *   .pk-strip-wrap   只负责【裁切 + 渐隐】✗ 静态 ✗ 每帧不动 ✓
 *   .pk-strip        只负责【位移】✗ 每帧一次 transform ⇒ 合成器干活 ✓
 *   .pk-strip-tile   一张分块画布 ✗ 画一次就不再动 ✓
 *   .pk-hitline      原来画在画布里的那 2px 亮线（搬过来才不会被音条盖住 ✓）
 *
 * ★ 渐隐为什么能搬过来 ✗ 因为老路那个 alpha 是**可分离**的 ✓
 *     老路：alpha = 固定(n) × (0.72 + 0.28(1 - y/rollH))
 *     现在：块里烤的是【固定(n)】✗ mask 乘上【1 → 0.72】✓✓
 *   ⇒ 逐像素等价 ✓（y 越往上越小 ⇒ mask 越亮 ✗ 与公式同向 ✓）
 * ============================================================ */
.pk-strip-wrap {
    position: absolute;
    left: 0; top: 0;
    overflow: hidden;              /* 音条只在画布那块里出现 ✓ */
    z-index: 2;
    pointer-events: none;
    /* ★★ 2026-10 新增：音条「亮度」与「不透明度」两个旋钮
       ★ 为什么非得挂在这一层 ✗：
           这层**内容不变 ✗ 每帧也一动不动** ⇒ 浏览器只需算一次 ✓
         绝对不能挂到 .pk-strip 上 ✗ 它每帧都在 transform
           ⇒ 那就是当年"卡到 0.5fps"那个错 ✗✗ 记住这条 ✓
       ★ 变量由 TUNE 写进 body（见 tool-piano2.js 的 a6TuneVars ✓）
         默认都是 1 ⇒ 观感与加这两个旋钮之前**完全一致** ✓ */
    filter: brightness(var(--bar-b, 1));
    opacity: var(--bar-o, 1);
    -webkit-mask-image: linear-gradient(to bottom, rgba(0,0,0,1) 0%, rgba(0,0,0,.72) 100%);
            mask-image: linear-gradient(to bottom, rgba(0,0,0,1) 0%, rgba(0,0,0,.72) 100%);
}
.pk-strip {
    position: absolute;
    left: 0; top: 0;
    width: 100%;
    will-change: transform;        /* ★ 整首歌只有这一层在动 ⇒ 合成层就是它 ✓ */
}
/* ★★★ 2026-10-20（用户要求「弹奏时音条从上面落下来」）：
   实时音条层 —— 只有"手动弹的音"画在这上面 ✓
   · 与 .pk-strip-wrap 同层（z-index:2），但 DOM 在它后面 ⇒ 压在上面 ✓
   · 命中线(3) / 火星(5) 仍然更高 ⇒ 落键瞬间的辉光会盖住条子 ✓ 观感正确 ✓
   · pointer-events:none ⇒ 绝不挡鼠标弹琴 ✓ */
.pk-live {
    position: absolute;
    left: 0; top: 0;
    z-index: 2;
    pointer-events: none;
}
/* 录制中：按钮变红 + 圆点呼吸（只影响提示 ✓ 不影响任何布局 ✓） */
.tool-btn.is-rec {
    color: #ff6b6b;
    border-color: rgba(255, 107, 107, .55);
}
.tool-btn.is-rec i { animation: pkRecPulse 1s ease-in-out infinite; }
@keyframes pkRecPulse {
    0%, 100% { opacity: 1; }
    50%      { opacity: .35; }
}
/* 没录到东西时，"播放录制 / 下载"两个按钮是灰的 ✓ */
.tool-btn[disabled] { opacity: .42; cursor: not-allowed; }

/* ★ 2026-10-20：节能 / 标准 模式按钮（选中的那个高亮 ✓） */
.tool-btn[aria-pressed="true"] {
    background: rgba(90, 200, 140, .18);
    border-color: rgba(90, 200, 140, .55);
    color: #bff3d6;
}

/* ============================================================
 * 2026-10-20：性能 / 内存（悬浮球已撤 ⇒ 设置进「⚙ 参数」✓）
 * ------------------------------------------------------------
 * ① 参数面板底部：开关 + 节能/标准（选中变绿）
 * ② 勾「在页面显示」⇒ 左侧绿色小面板 #memMonitor
 *    配色：深墨绿玻璃 + 荧光绿点缀（比纯黑更耐看 ✓）
 * ============================================================ */
#tunePanel .tp-perf {
    margin-top: 10px;
    padding-top: 9px;
    border-top: 1px dashed rgba(120, 200, 160, .25);
}
#tunePanel .tp-perf-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    font-weight: 600;
    font-size: 12px;
}
#tunePanel .tp-perf-modes {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 7px;
}
#tunePanel .tp-perf-hint { opacity: .62; font-size: 11px; }
#tunePanel .mem-mode { min-width: 64px; }
/* 「性能」这一页的 tab 按钮（样式照 .tp-tab，但独立类名 ⇒ 不被 tune-panel 的 0/1/2 逻辑覆盖 ✓） */
#tunePanel .tp-tab-mem {
    flex: 0 0 auto;
    cursor: pointer;
    padding: 4px 12px;
    border-radius: 999px;
    background: transparent;
    color: #9fdcbb;
    border: 1px solid #2f6b4a;
    font: 12px system-ui, "Microsoft YaHei", sans-serif;
}
#tunePanel .tp-tab-mem:hover { background: #24402e; }
#tunePanel .tp-tab-mem.on {
    background: #2f6b4a;
    color: #ffffff;
    border-color: #7fe0a8;
}
/* ★ 选中的那个：明显变绿 ✓ */
#tunePanel .mem-mode[aria-pressed="true"] {
    background: #2f855a;
    border-color: #4fbf86;
    color: #fff;
    box-shadow: inset 0 0 0 2px rgba(80, 200, 140, .25);
}

/* ---------------- 页面上的绿色小监控面板 ---------------- */
#memMonitor {
    position: fixed;
    left: 12px;
    top: 50%;
    transform: translateY(-50%);
    z-index: 9996;
    width: 150px;
    padding: 11px 12px 10px;
    border-radius: 14px;
    background: linear-gradient(160deg, rgba(22, 54, 38, .94), rgba(10, 26, 19, .94));
    border: 1px solid rgba(104, 222, 161, .32);
    box-shadow: 0 14px 34px rgba(0, 0, 0, .42), inset 0 1px 0 rgba(255, 255, 255, .06);
    color: #dff9ec;
    font: 12px/1.35 system-ui, "Microsoft YaHei", sans-serif;
    /* ★★★ 2026-10-21 十一轮（用户：电脑很烫）：**删掉 backdrop-filter** ✓
       为什么 ✗：这块底已经是 94% 不透明的深绿渐变 ⇒ 背后糊一层 8px 毛玻璃**根本看不出来**
         可它逼着浏览器**每一帧**都对身后那块（正在跑动画的钢琴）重新做一次高斯模糊 ✗
         实测这种「常驻 + 全帧率 + 大区域」的 backdrop-filter 是笔记本发热大户 ✓
       ⇒ 删掉后肉眼无差别，GPU 每帧少一次全区域模糊 ✓ */
    pointer-events: none;       /* 绝不挡琴键 / 点击 ✓ */
}
#memMonitor[hidden] { display: none; }
.mm-top {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 11px;
    color: #9fd9b9;
    letter-spacing: .3px;
}
.mm-dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: #56e39b;
    box-shadow: 0 0 10px rgba(86, 227, 155, .8);
    animation: mmPulse 2.2s ease-in-out infinite;
}
@keyframes mmPulse {
    0%, 100% { opacity: 1; }
    50%      { opacity: .45; }
}
.mm-mode {
    margin-left: auto;
    font-size: 10px;
    padding: 1px 6px;
    border-radius: 999px;
    background: rgba(86, 227, 155, .14);
    color: #8ff0c0;
    border: 1px solid rgba(86, 227, 155, .25);
}
.mm-big { margin-top: 5px; display: flex; align-items: baseline; gap: 3px; }
.mm-big b {
    font-size: 22px;
    font-weight: 700;
    color: #eafff4;
    letter-spacing: .3px;
    font-variant-numeric: tabular-nums;
}
.mm-big i { font-style: normal; font-size: 11px; color: #8fd6ae; }
.mm-sub { margin-top: 2px; font-size: 10.5px; color: #8fb9a5; font-variant-numeric: tabular-nums; }
.mm-bar {
    margin-top: 8px;
    height: 4px;
    border-radius: 3px;
    background: rgba(255, 255, 255, .08);
    overflow: hidden;
}
.mm-bar i {
    display: block;
    height: 100%;
    width: 0%;
    border-radius: 3px;
    background: linear-gradient(90deg, #2fbf77, #7cf0b4);
    transition: width .5s ease;
}

/* ============================================================
 * 2026-10-20：录制「下载」前的文件名弹窗
 * ★ 只做一件事：让用户改文件名 ✓ 不碰其它设置 ✓
 * ============================================================ */
.pk-name-mask {
    position: fixed;
    inset: 0;
    z-index: 100001;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(0, 0, 0, .5);
}
.pk-name-box {
    width: min(420px, 92vw);
    padding: 18px 18px 14px;
    border-radius: 14px;
    background: #192027;
    border: 1px solid rgba(255, 255, 255, .14);
    box-shadow: 0 18px 60px rgba(0, 0, 0, .55);
    color: #e9eef5;
}
.pk-name-title { font-size: 15px; font-weight: 600; margin-bottom: 4px; }
.pk-name-input {
    width: 100%;
    box-sizing: border-box;
    margin: 8px 0 14px;
    padding: 9px 11px;
    border-radius: 9px;
    border: 1px solid rgba(255, 255, 255, .18);
    background: #0f1419;
    color: #fff;
    font-size: 14px;
    outline: none;
}
.pk-name-input:focus { border-color: rgba(120, 200, 255, .65); }
.pk-name-actions { display: flex; gap: 10px; justify-content: flex-end; }
.pk-strip-tile {
    position: absolute;
    left: 0;
    display: block;
}
.pk-hitline {
    position: absolute;
    z-index: 3;
    background: rgba(255, 255, 255, .16);
    pointer-events: none;
}
.pk-sparks { z-index: 5; }   /* 火星在最上面 ✓（原来上面还压着光柱层 ✗ 已删 ✓）*/

/* ============================================================
 * ★ 火焰层：已按用户要求**整块删除**（2026-10 ✓ 说"没效果、看着也不爽"）
 *   被删掉的 CSS：.pk-fire-tile / .pk-fire-v
 *   被删掉的 JS ：FIRE_ON / fireEnsure / fireClipPath / fireApplyClip …
 *   老实现完整存在 _t/patch-step7-fire.orig.js ✗ 想捡回来就翻那个 ✓
 * ============================================================ */

/* ============================================================
 * ★ 琴键光芒（**光柱**）：用户说不要 ⇒ **整块删除** ✓（2026-10）
 *   删掉的 CSS：.pk-beams / .pk-beam / .pk-beam.on
 *   删掉的 JS ：beamInit / beamLayout / beamVars / beamTake / beamSet / beamClear
 *               + light() 里那两根桥 A6BEAM / A6BEAMV（一起删干净 ✓）
 *   老实现全量存档：_t/patch-step7-beam.orig.js ✗ 想捡回来翻那个 ✓
 *
 *   ★ 实测教训（留在这儿 ✗ 以后再加"贴视频 / 贴纹理 / 发光叠加"的层要记得）：
 *     千万别把 mix-blend-mode 写在"每帧都在位移"的层上
 *     ⇒ 浏览器每帧重算整块背景 ⇒ 60fps 直接掉到 0.5fps（页面像卡死 ✓）
 * ============================================================ */

/* ============================================================
 * ★★ 键盘上沿那条"泛光带"（.pk-hitline 的加高版）—— **先留着** ✓
 *   用户 2026-10 原话（很重要 ✗ 别删错 ✓）：
 *     · "钢琴上面一点点，它就有一个泛光的东西"  ← 就是这一条 ✓
 *     · "它是要弄成那种……像**闪电**形式的，然后一直在这里**缠绕转动** ✓"
 *     · "现在是**一道光芒** ⇒ 这样不好看 ✓"      ← 当前状态 ✓ 待改造 ✓
 *     · "这个光带就先给他留着吧" ⇒ **先不删** ✓（等闪电版做好再替换 ✓）
 *
 *   ★ 现在怎么做（别改它）：把原来那 2px 的命中线**加高 + 向上淡出的渐变**
 *     ⇒ 底边还是压在画布下沿 ✗ 一行 JS 都不用加 ✓
 *     ★ 纯静态元素 ⇒ 零开销 ✓（绝不能加 filter: blur ✗ 那是性能地雷 ✓）
 *
 *   ★★ 2026-10 新增：高度 + 呼吸感（**都是旋钮** ✗ 变量由 TUNE 写进 body ✓）
 *     --glow-h     高度 px（默认 30 ✓ 原先写死 58 ✗ 用户嫌太高 ⇒ 才有这个旋钮 ✓）
 *     --glow-b     呼吸幅度 0~1（0 = 不呼吸 ✓）
 *     --glow-bsec  呼吸周期 秒（越大越慢 ✓）
 *     --glow-anim  动画名 ✗ 幅度 = 0 时 JS 会把它设成 none ⇒ 动画**彻底不跑** ✓
 *       （不是"跑了但看不见"✗ 那是白烧电 ✓）
 *     ★ 呼吸只动 opacity + transform: scaleY ✗ 全是合成器包办的属性 ✓
 *       每帧 0 JS ✗ 也不重排（scaleY 不改变布局 ✓）
 *     ★ transform-origin 锚在**底边** ⇒ 看着是"从琴键那儿往上呼"✗ 最自然 ✓
 *     ★ 呼吸周期 ⚠ 别小于 1 秒 ✗ 那就不叫呼吸了 ✗ 像抽搐 ✓
 *
 *   ★★ 以后做"闪电"版的方向（记在这儿免得忘 ✓）：
 *     ① canvas 上**烤**几条分叉电弧（画一次 ✓ 成本为零 ✓ 还能带辉光 ✓）
 *     ② 放进独立层 ✗ 只动 transform（循环平移 / 轻微旋转）⇒ 合成器干活 ✓ 每帧 0 JS
 *     ③ 想"缠绕转动"⇒ 叠两层**不同速度**的电弧 ✗ 偶尔来一次 opacity 闪 ✓
 *     ④ 千万别用 SVG 滤镜（feTurbulence）/ filter: blur ✗ 性能直接崩 ✓
 * ============================================================ */
.pk-hitline {
    height: var(--glow-h, 30px);
    /* ★ 底边永远贴在"画布下沿 + 2px"那条命中线上 ✓
       高度一变 ⇒ 元素整体往上长 ✗ 底边不动 ✓（与原来 58/-56 的关系一致 ✓） */
    margin-top: calc(2px - var(--glow-h, 30px));
    transform-origin: 50% 100%;
    animation-name: var(--glow-anim, pkGlowBreath);
    animation-duration: var(--glow-bsec, 4.8s);
    animation-timing-function: ease-in-out;
    animation-iteration-count: infinite;
    background: linear-gradient(to top,
        hsla(var(--bh, 145), 100%, 88%, .60)   0%,
        hsla(var(--bh, 145),  98%, 74%, .28)  26%,
        hsla(var(--bh, 145),  95%, 66%, .10)  58%,
        transparent                          100%);
}
/* ★ 呼吸：亮 → 淡 → 亮 ✗ 同时高度微微收一下（scaleY ✓ 底边锚住不动 ✓）
   幅度全由 --glow-b 定 ✗ 0 就是纹丝不动 ✓ */
@keyframes pkGlowBreath {
    0%, 100% { opacity: 1;    transform: scaleY(1); }
    50%      { opacity: calc(1 - var(--glow-b, .35) * .55);
               transform: scaleY(calc(1 - var(--glow-b, .35) * .18)); }
}
/* ★ 尊重系统"减少动态效果"✗ 这类用户直接不给呼吸 ✓ */
@media (prefers-reduced-motion: reduce) {
    .pk-hitline { animation-name: none; }
}

/* ============================================================
 * ★★★ 第 4 步：微尘搬 CSS
 * ------------------------------------------------------------
 * ★ 层次为什么是 z-index:1（与画布同层 ✗ 靠 DOM 顺序压在它上面 ✓）
 *   画布 .pk-roll 自己带着**不透明的绿色渐变背景**
 *   ⇒ 微尘若放 z0（画布下面）会被那层背景整个盖住 ✗✗ 一顆都看不见
 *   ⇒ 必须放在【画布之上 ✗ 条带之下】：
 *       画布(z1 背景) → 微尘(z1 ✗ 后出现 ⇒ 压上) → 条带(z2) → 命中线(z3) → 火星(z4)
 *   ⇒ 这正好等于老路"微尘最先画 ✗ 然后才是音条"的层次 ✓✓
 * ============================================================ */
.pk-dust-wrap {
    position: absolute;
    left: 0; top: 0;
    overflow: hidden;
    z-index: 1;
    pointer-events: none;
    contain: layout style;
}
.pk-dust {
    position: absolute;
    left: 0; top: 0;
    background-repeat: no-repeat;
    background-size: 100% 100%;
    /* 与 makeDustSprite() 的 stops 逐档相同：1 → .34@40% → 0 */
    background-image: radial-gradient(circle at 50% 50%,
        rgba(255, 255, 255, 1)   0%,
        rgba(255, 255, 255, .34) 40%,
        rgba(255, 255, 255, 0)  100%);
}
/* 老版遇到"减少动效"是直接不画 ✓ 这里整层关掉 ✓ 等价 ✓ */
@media (prefers-reduced-motion: reduce) {
    .pk-dust-wrap { display: none !important; }
}

/* ★★★ 【特效钢琴 V2 优化层 END】★★★ */


/* ★★★ 2026-10-16（第二十六轮 · 用户要求）：**音名两档模式**
   · .names-c   ⇒ 只在 C 键（C1 C2 …C8）显示 ⇒ 其余白键的音名透明度归 0 ✓
   · .names-all ⇒ 52 个白键全显示 ✓
   · 不显示     ⇒ 连 .show-names 都不加 ✓
   ★ 用 opacity 而不是 display:none ✗：不重排布局 ⇒ 切换时琴键一点都不抖 ✓
   ★ 这三条写在**文件最末**，与前面同权重时后写的赢 ✓（本文件一贯做法 ✓）
   ★ 成片（fx-frame）量的是**计算样式**⇒ 网页怎么显示，片子就怎么画 ✓ 两边永远一致 ✓*/
.piano-keys.show-names.names-c .pk-white:not(.pk-c) .pk-label,
.piano-keys.show-names .pk-black .pk-label { opacity: 0; }
.piano-keys.show-names.names-all .pk-white .pk-label { opacity: 1; }
.piano-keys.show-names.names-c .pk-white.pk-c .pk-label { opacity: 1; }

/* ★★★ 2026-10-16（第二十六轮 · 用户要求）：**音色下拉优化**
   原来它就是一个浏览器默认样子的 <select> ✗ 摆在署名那一堆小字里很突兀 ✓
   现在：与 ⚙ 参数面板的控件同一套观感（深底 / 圆角 / 同色描边 / 聚焦时亮一圈 ✓）
   ★ 只用 CSS ✗ 不动 JS（选项仍是 manifest 给的 ✓ 只有一个音色时整行还是隐藏 ✓）*/
/* ★ 2026-10-20 晚：「关于我们」第一版样式（深色半透明块）已**废弃** ✗
   用户反馈「按钮不好看 / 展开像一个小白块 / 配色字体都不顺眼」⇒ 整套重写
   ★ 新样式在**文件最末**（第二版 · 同一张卡里折叠 ✓ 见那里的长注释）
   ★ 放最末是刻意的：与 body.v2-route 那套浅色覆盖同权重时，后写的赢 ✓ */

.piano-voice { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
/* ★★★ 2026-10-22（P1 · 踩到的坑 ✗）：`display:flex` 会盖掉浏览器给 [hidden] 的
   `display:none` ⇒ 上面那条 `wrap.hidden = true` **一直没生效**，音色那一行永远露着 ✗
   现在卡片接管后必须真的能藏起来 ✓（!important 是必要的：这里要压过 .piano-voice ✓）*/
.piano-voice[hidden] { display: none !important; }
.piano-voice i { opacity: .75; font-size: 12px; }
.piano-voice select {
    appearance: none;
    -webkit-appearance: none;
    min-width: 190px;
    padding: 5px 30px 5px 11px;
    border: 1px solid rgba(127, 224, 168, .35);
    border-radius: 7px;
    background-color: rgba(9, 18, 13, .85);
    background-image: linear-gradient(45deg, transparent 50%, #7fe0a8 50%),
                      linear-gradient(135deg, #7fe0a8 50%, transparent 50%);
    background-position: right 14px center, right 9px center;
    background-size: 5px 5px, 5px 5px;
    background-repeat: no-repeat;
    color: #d8ffe9;
    font: 12.5px/1.4 system-ui;
    cursor: pointer;
    transition: border-color .15s, box-shadow .15s;
}
.piano-voice select:hover { border-color: rgba(127, 224, 168, .65); }
.piano-voice select:focus {
    outline: none;
    border-color: #7fe0a8;
    box-shadow: 0 0 0 2px rgba(127, 224, 168, .18);
}
.piano-voice-hint { opacity: .55; font-size: 11px; }
/* ★★★ 2026-10-19（第七十四轮 · 用户要求）：音色旁边的「力度」下拉 + MIDI 力度平衡 ✓
   ★ 复用 .piano-voice select 那套皮肤（只缩窄一点 ✗ 别让两个下拉都 190px 宽 ✓）*/
.piano-voice select.piano-vel { min-width: 96px; }
.piano-voice .piano-vel-auto {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    cursor: pointer;
    user-select: none;
    color: inherit;
    opacity: .85;
}
.piano-voice .piano-vel-auto input[type=checkbox] {
    width: 14px;
    height: 14px;
    margin: 0;
    accent-color: #3a8159;
    cursor: pointer;
}
.piano-voice .piano-vel-auto:hover { opacity: 1; }

/* ★★★ 2026-10-16（第二十九轮 · 用户要求）：**键盘上的辉光**开关
   body.pk-plainkey（由 tool-piano 的 a6TuneVars 打/撤 ✓）⇒
     ① 键盘上沿那条泛光带（.pk-hitline）整条不显示 ✓
     ② 卷帘底部那层绿由 --pk-glowA* 归 0 关掉（见 a6TuneVars ✓）
     ③ 落键光柱由 GL 层那边读 keyPlain 关掉（见 pk-gl.js ✓）
   ★ 写在本文件最末 ✗ 与前面同权重时后写的赢 ✓（本文件一贯做法 ✓）*/
/* （第二十九轮撤销：泛光带**不再**受那个开关影响 ✓ 见 tune-panel 的 keyPlain ✓）*/

/* ★★★ 2026-10-16（第三十二轮 · 用户要求）：**C 键那条缝**改细 + 改中性色
   用户原话：「C1 和 B 这个钢琴缝有点大，那个衔接的缝那里有点绿色的感觉，
             感觉衔接得不是特别好」✓
   · 原来是 2px（见上面 .pk-white 那段注释「C 键是 2px」✗）⇒ 现在统一 1px ✓
   · 颜色原来跟主题走（浅绿 #b2c4b8 / 深绿 #2f5a3a 那些 ✗）⇒ 现在中性灰 ✓
   写在本文件最末 + !important ✗ 因为前面好几处 `html[data-theme=...]` 规则
   权重都比它高（同权重也是后写的赢 ⇒ 这里一定压得住 ✓）*/
.pk-white.pk-c,
html[data-theme="dark"] .pk-white.pk-c,
html[data-theme="light"] .pk-white.pk-c {
    border-left-width: 1px !important;
    border-left-color: #cfd8d3 !important;
}
html[data-theme="dark"] .pk-white.pk-c { border-left-color: #3a4a41 !important; }

/* ============================================================
 * ★★★ 2026-10-16（第三十二轮 · 用户要求）：**开场白预览层**（页面里的）
 * 用户原话：①「最好可以弄一个预览的效果给我看一下」
 *           ②「开启之后，就在钢琴窗口这里前 10 秒钟也能看到」✓
 * 结构（tool-piano 的 introBuild 建 ✓）：
 *   .pk-intro（整层） → .pk-intro-curtain（幕布）+ .pk-intro-box（标题/线/副标题）
 * ★ 只盖**卷帘**：bottom 等于键盘高度（= --pk-w × 5.2 ✓ 与 .piano-keys 同一条算式 ✓）
 *   ⇒ 键盘永远露着 ⇒ 音条照常落（与成片一致 ✓）
 * ★ 尺寸跟着 --pk-w 走 ⇒ 缩放琴键时预览字也**等比**变 ✓（与成片按卷帘高算同一个道理 ✓）
 * ★ 两个变量由 JS 写：--pk-intro-dim（幕布不透明度 ✓）/ --pk-intro-size（字号比例 ✓）
 * ============================================================ */
.pk-intro {
    position: absolute;
    left: 0; right: 0; top: 0;
    bottom: calc(var(--pk-w, 46px) * 5.2);
    z-index: 6;
    pointer-events: none;
    opacity: 0;
    visibility: hidden;
    transition: opacity .16s linear;
    overflow: hidden;
}
.pk-intro-curtain {
    position: absolute;
    top: 0; right: 0; bottom: 0; left: 0;
    inset: 0;
    background: #070d0a;
    opacity: var(--pk-intro-dim, 1);
}
.pk-intro-box {
    position: absolute;
    left: 50%; top: 50%;
    transform: translate(-50%, -50%);
    width: 92%;
    text-align: center;
    text-shadow: 0 2px 10px rgba(0, 0, 0, .75);
}
.pk-intro-title {
    /* ★ 第五十八轮：字号由 JS 算（A6PianoIntroFit ✓ 与成片同一个数 ✓）
       · 有 --pk-intro-title-px ⇒ 用它（自动缩放到框内 ✓ 长名字不超屏 ✓）
       · 没有（还没 fill 过）⇒ 退回老算式（仅占位一瞬 ✓ 不会真的画出来）*/
    font: 700 var(--pk-intro-title-px, calc(var(--pk-w, 46px) * 0.676)) / 1.15 var(--pk-intro-font, "Microsoft YaHei", system-ui, sans-serif);
    color: rgba(255, 255, 255, .98);
    letter-spacing: .02em;
}
.pk-intro-line {
    width: 46%;
    height: max(2px, calc(var(--pk-w, 46px) * .05));
    margin: .5em auto .42em;
    background: rgba(127, 224, 168, .85);
    border-radius: 2px;
}
.pk-intro-sub {
    /* ★ 第五十八轮：同上（JS 算好 ⇒ 与成片一致 ✓ 长副标题也不会撑出去 ✓）*/
    font: 500 var(--pk-intro-sub-px, calc(var(--pk-w, 46px) * 0.286)) / 1.3 var(--pk-intro-font, "Microsoft YaHei", system-ui, sans-serif);
    color: rgba(255, 255, 255, .72);
}
@media (max-width: 760px) {
    .pk-intro-line { width: 64%; }
}

/* ============================================================
 * ★★★ 2026-10-16（第三十四轮 · 用户要求）：**背景图更通透** ✓
 * 用户原话：「有没有什么办法可以让这个背景图变得更加透彻透亮一点？
 *           虽然我说页面这个背景图整体会压暗一点点，但我希望压暗的时候
 *           也不影响它的通透感」✓
 * 原来：一张**平板色**的压暗层（一层均匀 hsla ✓）⇒ 整张图被"蒙了一层灰"✗
 *       ⇒ 亮部也被压平 ⇒ 看着糊 ✓
 * 现在：**两段式**
 *   ① 中心**少压**（保留中间那块的透亮 ✓ 观感上"光是从中间透出来"✓）
 *   ② 四周**多压**（vignette ⇒ 观感更聚焦、也更"高级"✓）
 * ★ 总暗度不变 ✗（仍由 --pk-bgdim 管着 ✓ 你拖那个滑块照样有效 ✓）
 * ★ 只改"怎么压"✗ 不改"压多少" ✓ 所以不会突然变亮/变暗 ✓
 * ============================================================ */
.pk-has-bg .pk-roll {
    background-image:
        radial-gradient(120% 78% at 50% 42%,
            hsla(var(--bh, 152), 30%, 7%, calc(var(--pk-bgdim, .62) * .62)) 0%,
            hsla(var(--bh, 152), 32%, 5%, calc(var(--pk-bgdim, .62) * .88)) 58%,
            hsla(var(--bh, 152), 34%, 4%, var(--pk-bgdim, .62)) 100%),
        var(--pk-bg);
    background-size: cover, cover;
    background-position: center, center;
    background-repeat: no-repeat, no-repeat;
}

/* ============================================================
   ★★★ 2026-10-16（第三十八轮 · 用户要求）：**把键盘上方那条缝关掉** ✓
   用户原话：「在我们泛光带键盘上方的这个缝这里……增加一个叫**关闭**，
             就是这个缝隙给它关了，不要这个缝隙，那个范围内直接和这个键盘相连，
             就是这个 10 多 px 的缝隙就不需要了」✓
   ------------------------------------------------------------
   那条缝的**唯一来源**就在这里：`.pk-roll { margin-bottom: 6px }`（见上面）
     + canvas 是行内块时基线留的那 1~2px ⇒ 量出来 8~10px ✓
   ⇒ `gapFill = 3` 时 tool-piano.js 给 body 挂 `.pk-nogap`（见 a6TuneVars ✓）
   ★ 只关 margin / 底部圆角 ✗ **不动**卷帘高度、键盘高度、任何坐标 ✓
     ⇒ 成片里 fx-frame 的 measure() 量到的 keyTop-rollH 也跟着变 0
     ⇒ 网页与成片**仍然是同一套坐标** ✓（这是本项目的铁律 ✓）
   ★ 底部圆角一起关：不然缝没了、圆角还在 ⇒ 两角会漏出背景色 ✓
   ============================================================ */
body.pk-nogap .pk-roll {
    margin-bottom: 0 !important;
    border-bottom-left-radius: 0 !important;
    border-bottom-right-radius: 0 !important;
    /* ★ 投影也去掉：紧贴键盘时那条 inset 阴影会在接缝处压出一道假黑边 ✓ */
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .05) !important;
}
/* 键盘自己也别带外边距（有些主题规则会加 ✓ 一起压平 ✓）*/
body.pk-nogap .piano-keys {
    margin-top: 0 !important;
}

/* ============================================================
 * ★★★ 2026-10-17（第五十四轮 · 用户要求）：**缝的自定义颜色** ✓
 * 用户原话：「键盘上的缝，改成 关闭 / 自定义颜色（点击调色盘选色）/ 跟随主题」✓
 * ------------------------------------------------------------
 * 为什么用 `.pk-roll::after` ✗
 *   那条缝不是"一个元素"，它就是 `.pk-roll` 的 `margin-bottom`（露出来的是父元素底色 ✓）
 *   ⇒ 想单独给它上色，只能在这个 margin 区域**盖一层** ✓
 *   `top:100%` 正好落在 margin 里：不挡卷帘内容、不挡键盘、滚动时跟着卷帘走 ✓
 * 三档怎么落：
 *   · gapFill=1 跟随主题 ⇒ tool-piano **不写** --pk-gap-color ⇒ 这里 transparent
 *                        ⇒ 还是老样子（露父元素底色 / 有图时成片叠图 ✓）
 *   · gapFill=2 自定义   ⇒ 变量 = TUNE.gapColor ⇒ 这层铺上那个色 ✓
 *   · gapFill=3 关闭     ⇒ margin 已经是 0（.pk-nogap）⇒ 这层高度 12px 会伸到键盘上 ✗
 *                        但变量同样没写 ⇒ transparent ⇒ **完全看不见** ✓ 不会遮键盘 ✓
 * ============================================================ */
body.v2-route .piano-scroll .pk-roll::after,
body.v2-route .piano-scroll.is-roll .pk-roll::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    top: 100%;
    height: 14px;                       /* 缝实测 6~10px ⇒ 14 够盖满 ✓ */
    background: var(--pk-gap-color, transparent);
    pointer-events: none;
    z-index: 0;
}


/* ============================================================================
   ★★★ 2026-10-19（第七十七轮 · 用户要求）：**整页绿白配色** ✓
   用户原话：「整个主页面还有这些按钮这些东西，换一套配色 …
             换成绿白配色 … 除了这个钢琴窗口这些不要动」✓
   ----------------------------------------------------------------------------
   改了：页面底 / 面板外壳 / 顶部工具条 / 工具按钮 / 提示文字 / 翻看箭头 /
         署名 / 音色·力度下拉 / 页面滚动条
   **没改**：`.piano-scroll`（钢琴外壳）/ `.pk-roll`（卷帘）/ `.piano-keys`（键盘）
             —— 那是"钢琴窗口"，一个像素都不动 ✓
   ★ 全部挂在 `body.v2-route:not(.fx-stage)` 下：
     · `v2-route` = 工具页（其它页面不受影响 ✓）
     · `:not(.fx-stage)` = **导出预览舞台**（fx-export 会加 fx-stage）
       舞台里有自己的深色渐变，绝不能被这套浅色盖掉 ✓
   ★ 写在文件最末 ⇒ 同权重后写的赢（本文件一贯做法 ✓）
   ============================================================================ */
body.v2-route:not(.fx-stage) {
    /* ★ 第七十八轮（用户反馈「渐变太明显/不美观」）：收成**几乎纯白**，只留一丝绿意 ✓
       去掉顶部那团 1200px 的绿色光晕（那是显脏的主因 ✗）*/
    background: linear-gradient(180deg, #f7faf8 0%, #f1f5f3 100%) !important;
    background-attachment: fixed !important;
}

/* 面板外壳（只是那张白卡片的边框/圆角/阴影；里面的钢琴窗口不动 ✓） */
body.v2-route:not(.fx-stage) .tool-panel.piano-panel {
    background: #ffffff !important;
    border: 1px solid #e9f1ec !important;
    border-radius: 16px !important;
    box-shadow: 0 1px 2px rgba(16, 60, 40, .04),
                0 14px 34px -24px rgba(16, 60, 40, .18) !important;
}

/* 顶部工具条：白底 + 一条极细的绿灰边（不再整条铺浅绿 ✗ 显脏） */
body.v2-route:not(.fx-stage) .piano-bar {
    background: #ffffff;
    border: 1px solid #edf3ef;
    border-radius: 12px;
    padding: 10px 12px;
    margin-bottom: 12px;
    box-shadow: 0 1px 2px rgba(16, 60, 40, .03);
}

/* 工具按钮 */
body.v2-route:not(.fx-stage) .tool-btn {
    border-radius: 8px;
    border-color: #dbe8e0;
    color: #33705a;
    background: #ffffff;
    box-shadow: 0 1px 1px rgba(16, 60, 40, .03);
    transition: background .16s, border-color .16s, box-shadow .16s, transform .12s;
}
body.v2-route:not(.fx-stage) .tool-btn:hover {
    background: #f2faf5;
    border-color: #a8d5bd;
    transform: translateY(-1px);
    box-shadow: 0 3px 10px -8px rgba(16, 60, 40, .45);
}
body.v2-route:not(.fx-stage) .tool-btn.primary {
    background: linear-gradient(180deg, #329a66, #2f855a);
    border-color: #2f855a;
    color: #ffffff;
    box-shadow: 0 3px 10px -7px rgba(47, 133, 90, .5);
}
body.v2-route:not(.fx-stage) .tool-btn.primary:hover {
    background: linear-gradient(180deg, #359f6b, #329a66);
}
body.v2-route:not(.fx-stage) .tool-btn:disabled {
    transform: none;
    box-shadow: none;
    opacity: .55;
}

/* 提示 / 时间 / 音域 / 署名 */
body.v2-route:not(.fx-stage) .piano-hint { color: #4b5563; }
body.v2-route:not(.fx-stage) .piano-seek-wrap .piano-time { color: #4b5563; }
body.v2-route:not(.fx-stage) .piano-nav-range { color: #6b7280; }
body.v2-route:not(.fx-stage) .piano-credit { color: #6b7280; }
body.v2-route:not(.fx-stage) .piano-credit a { color: #2b9159; }

/* 左右翻看箭头 */
body.v2-route:not(.fx-stage) .piano-nav-btn {
    border-color: #dfeae3;
    color: #33705a;
    background: #ffffff;
    box-shadow: 0 1px 2px rgba(16, 60, 40, .05);
}
body.v2-route:not(.fx-stage) .piano-nav-btn:hover {
    background: #f4faf7;
    border-color: #a8d5bd;
}

/* 音色 / 力度下拉：白底 + 绿边 + 深绿字（原来是深绿底 ✗ 浅页面上很突兀） */
body.v2-route:not(.fx-stage) .piano-voice select {
    background-color: #ffffff !important;
    background-image:
        linear-gradient(45deg, transparent 50%, #3f8f65 50%),
        linear-gradient(135deg, #3f8f65 50%, transparent 50%) !important;
    border-color: #dbe8e0 !important;
    color: #1f2937 !important;
    box-shadow: 0 1px 2px rgba(16, 60, 40, .04);
}
body.v2-route:not(.fx-stage) .piano-voice select:hover {
    border-color: #a8d5bd !important;
}
body.v2-route:not(.fx-stage) .piano-voice select:focus {
    border-color: #2f855a !important;
    box-shadow: 0 0 0 3px rgba(47, 133, 90, .14) !important;
}

/* 页面滚动条（只作用于 body 自己的滚动条 ⇒ 钢琴窗口那条横向滚动条不受影响 ✓） */
body.v2-route::-webkit-scrollbar { width: 12px; height: 12px; }
body.v2-route::-webkit-scrollbar-track { background: #f7faf8; }
body.v2-route::-webkit-scrollbar-thumb {
    background: #dbe8e1;
    border-radius: 8px;
    border: 3px solid #f7faf8;
}
body.v2-route::-webkit-scrollbar-thumb:hover { background: #c3d8cb; }

/* ============================================================================
   ★★★ 2026-10-19（第七十九轮）：**截图复查后的 6 处细节打磨**
   （用户：「仔细分析这个页面，看看还有什么地方值得美化」✓）
   ============================================================================ */

/* ① 踏板那个勾选框：浏览器默认是**蓝色** ✗（截图里一眼跳出来）⇒ 改绿 */
body.v2-route:not(.fx-stage) .piano-bar input[type="checkbox"] {
    accent-color: #2f855a;
    width: 16px;
    height: 16px;
    cursor: pointer;
}

/* ② 两条状态文字（"点一下琴键…" / "已载入 N 个音符…"）：
      原来是**裸文字 + margin-left:auto** ⇒ 一行放不下时左右各飞一个，像断掉的工具栏 ✗
      现在：小胶囊徽章，换行后从左边整齐排 ✓ */
body.v2-route:not(.fx-stage) .piano-bar .tool-stat {
    margin-left: 0;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 3px 10px;
    border-radius: 999px;
    background: #f3f9f6;
    border: 1px solid #e4efe9;
    color: #4b5563;
    font-size: 11.5px;
    line-height: 1.5;
    white-space: nowrap;
}
body.v2-route:not(.fx-stage) .piano-bar .tool-stat:empty { display: none; }
body.v2-route:not(.fx-stage) .piano-bar .tool-stat + .tool-stat { margin-left: 6px; }

/* ③ 键盘提示：从"普通段落"改成一条**提示条**（浅绿底 + 左侧绿竖条）
      原来一整行长文字飘在那里，和上下都没关系 ✗ */
body.v2-route:not(.fx-stage) .piano-hint {
    position: relative;
    margin: 0 0 12px;
    padding: 8px 12px 8px 15px;
    background: #f6fbf8;
    border: 1px solid #e8f2ec;
    border-radius: 10px;
    color: #4b5563;
}
body.v2-route:not(.fx-stage) .piano-hint::before {
    content: "";
    position: absolute;
    left: 0;
    top: 9px;
    bottom: 9px;
    width: 3px;
    border-radius: 0 3px 3px 0;
    background: #7fc79c;
}

/* ④ 音域导航：整行**居中** + 范围做成小胶囊
      原来贴在左边、右边一大片空，箭头也太淡 ✗ */
body.v2-route:not(.fx-stage) .piano-nav {
    justify-content: center;
    gap: 10px;
    margin: 2px 0 12px;
}
body.v2-route:not(.fx-stage) .piano-nav-range {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 96px;
    padding: 3px 12px;
    border-radius: 999px;
    background: #f3f9f6;
    border: 1px solid #e4efe9;
    color: #4b5563;
    font-size: 12px;
}

/* ⑤ 音色 / 力度 / 署名：与钢琴区之间加一条极淡的分隔（现在像"粘在卡片底部"）
      注意：只加边距和一条虚线，**不包新容器** ⇒ tool-piano 的绑定一点不受影响 ✓ */
body.v2-route:not(.fx-stage) #pianoVoiceWrap {
    margin-top: 14px;
    padding-top: 12px;
    border-top: 1px dashed #e6f0ea;
}
body.v2-route:not(.fx-stage) #pianoCredit {
    margin-top: 6px;
    line-height: 1.7;
}

/* ⑧ 音色 / 力度条：从"散着的一行"改成一条**浅底信息条**（与卡片同宽、同一套边角） */
body.v2-route:not(.fx-stage) #pianoVoiceWrap {
    margin-top: 16px;
    padding: 10px 14px;
    background: #fbfdfc;
    border: 1px solid #edf3ef;
    border-radius: 12px;
}
body.v2-route:not(.fx-stage) #pianoCredit {
    margin-top: 8px;
    line-height: 1.7;
}

/* ⑥ 音色分批加载的进度条：**重做**
      原来（第七十轮那版）：深绿大方块 + 整行宽 + 粗条 ⇒ 截图里像一条"水泥带" ✗
      现在：6px 细条 + 浅绿轨道 + 下方 10.5px 小字 + 限宽 420px ✓ */
body.v2-route:not(.fx-stage) .piano-load-progress {
    position: relative;
    margin: 12px 0 24px;
    padding: 0;
    max-width: 420px;
    height: 6px;
    background: #e9f3ed;
    border: 0;
    border-radius: 999px;
    overflow: visible;
}
body.v2-route:not(.fx-stage) .piano-load-progress-bar {
    position: absolute;
    left: 0;
    top: 0;
    height: 100%;
    width: 0;
    background: linear-gradient(90deg, #a7ddc2, #2f855a);
    border-radius: 999px;
    transition: width .25s;
}
body.v2-route:not(.fx-stage) .piano-load-progress-text {
    position: absolute;
    left: 0;
    top: 12px;
    font-size: 10.5px;
    color: #6b7280;
    white-space: nowrap;
}
body.v2-route:not(.fx-stage) .piano-load-progress[hidden] { display: none; }

/* ★ 2026-10-20（用户要求：三种模式都要看见「传输了多少 / 是否加载完成」）：
   进度条**副行** —— 由 audio-store-client.js 写入
     · 下载中：已传输 113.4 / 254.1 MB · 2.1 MB/s
     · 完成：  已就绪 · 254.1 MB（本机加密缓存）
   为什么单独一行 ✗：主行显示的是「样本数 x/480」（引擎解码进度），
   副行显示的是**字节数**（网络传输进度）—— 两者不是一回事 ✓ */
.piano-load-progress-sub {
    position: relative;
    z-index: 1;
    display: block;
    margin-top: 2px;
    font-size: 10.5px;
    line-height: 1.5;
    color: #a9d6b8;
    font-variant-numeric: tabular-nums;
}
body.v2-route:not(.fx-stage) .piano-load-progress-sub {
    position: absolute;
    left: 0;
    top: 26px;
    display: block;
    font-size: 10.5px;
    line-height: 1.5;
    color: #8a94a6;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}
/* 副行是 absolute ⇒ 会给下面的署名留出位置（否则会压字 ✗） */
body.v2-route:not(.fx-stage) .piano-load-progress {
    margin-bottom: 40px;
}

/* ⑦ 页面底部留白：左下三个按钮是 `position:fixed` ⇒ 会压住最后的署名行 ✗
      留 220px ⇒ 内容能滚到按钮上方，随你怎么看都不挡 ✓ */
body.v2-route:not(.fx-stage) .tool-page {
    padding-bottom: 220px;
}

/* ⑩ 署名：颜色再深一档（截图里 #6b7280 在白底上偏淡） */
body.v2-route:not(.fx-stage) #pianoCredit {
    margin-top: 8px;
    line-height: 1.7;
    color: #6b7280;
}

/* ⑪ 翻看箭头：边框/图标都加深一档（截图里几乎看不见 ✗） */
body.v2-route:not(.fx-stage) .piano-nav-btn {
    border-color: #cfe3d6;
    color: #3f8f65;
    background: #ffffff;
    box-shadow: 0 1px 3px rgba(16, 60, 40, .08);
}
body.v2-route:not(.fx-stage) .piano-nav-btn:hover {
    background: #eef8f2;
    border-color: #8fceaa;
    color: #2f855a;
}

/* ⑫ 音色条里「音色 | 力度」两组之间加一条细竖线（现在全靠图标区分，有点糊）
   ★ 第八十轮：改成挂 `.piano-vel-label` —— 原来那个 `fa-sliders` 图标在本仓库
      的 Font Awesome 里**没有字形**，显示成缺字方块 ✗ 干脆去掉图标 ✓ */
body.v2-route:not(.fx-stage) #pianoVoiceWrap > .piano-vel-label {
    margin-left: 12px;
    padding-left: 14px;
    border-left: 1px solid #e6f0ea;
}

/* ============================================================================
   ★★★ 2026-10-19（第八十一轮）：「精致化」细节 —— 全部改成引用 tokens
   + 交互手感（按下 / 焦点 / 数字对齐）
   ============================================================================ */

/* 页面呼吸：顶部多 4px、底部按按钮区留白 */
body.v2-route:not(.fx-stage) .tool-page {
    padding-top: 22px;
    padding-bottom: 220px;
}

/* 卡片内边距统一 20（原来 18 略挤） */
body.v2-route:not(.fx-stage) .tool-panel.piano-panel {
    padding: 20px;
    border-color: var(--c-line);
    box-shadow: var(--sh-1), var(--sh-3);
}

/* 工具条：间距更均匀（原来 12px 有点挤） */
body.v2-route:not(.fx-stage) .piano-bar {
    gap: 12px 14px;
    border-color: var(--c-line);
}

/* 按钮：统一 tokens + 按下手感 */
body.v2-route:not(.fx-stage) .tool-btn {
    border-color: var(--c-line-strong);
    color: var(--c-brand-down);
    border-radius: var(--r-sm);
    transition: background var(--dur) var(--ease),
                border-color var(--dur) var(--ease),
                box-shadow var(--dur) var(--ease),
                transform .1s var(--ease);
}
body.v2-route:not(.fx-stage) .tool-btn:active {
    transform: translateY(0) scale(.985);
}
body.v2-route:not(.fx-stage) .tool-btn.primary {
    background: linear-gradient(180deg, var(--c-brand-hover), var(--c-brand));
    border-color: var(--c-brand);
}
body.v2-route:not(.fx-stage) .tool-btn.primary:active {
    background: var(--c-brand-down);
}

/* 音色 / 力度那一行：垂直居中 + 均匀间距 + 下拉圆角统一 */
body.v2-route:not(.fx-stage) #pianoVoiceWrap {
    align-items: center;
    gap: 10px;
    background: var(--c-surface-2);
    border-color: var(--c-line);
    border-radius: var(--r-md);
}
body.v2-route:not(.fx-stage) .piano-voice select {
    border-radius: var(--r-sm);
    transition: border-color var(--dur) var(--ease),
                box-shadow var(--dur) var(--ease);
}
body.v2-route:not(.fx-stage) .piano-voice select.piano-vel {
    min-width: 96px;
}

/* 音域范围胶囊：数字等宽 + 稍加重 */
body.v2-route:not(.fx-stage) .piano-nav-range {
    font-weight: 500;
    background: var(--c-surface-2);
    border-color: var(--c-line);
}

/* 提示条：用 tokens，圆角/配色统一 */
body.v2-route:not(.fx-stage) .piano-hint {
    background: var(--c-surface-2);
    border-color: var(--c-line);
    border-radius: var(--r-md);
    color: var(--c-text-2);
}

/* 参数按钮（右下 ⚙）：跟品牌绿一致（内联 style 用 0,1,1 特异度压住 ✓） */
body.v2-route #tuneFab {
    background: var(--c-brand);
    box-shadow: var(--sh-2);
}
body.v2-route #tuneFab:hover {
    background: var(--c-brand-hover);
}

/* 状态胶囊：用 tokens */
body.v2-route:not(.fx-stage) .piano-bar .tool-stat {
    background: var(--c-surface-2);
    border-color: var(--c-line);
    color: var(--c-text-2);
}

/* ── 2026-10-20：音频转 MIDI 进度面板 ── */
/* ★ 2026-10-20（用户要求）：「更多功能」折叠区 —— 电脑模式/MIDI 键盘/录制/下载/音频转 MIDI */
.piano-more {
    margin: 8px 0 4px;
    padding: 10px 12px 6px;
    border: 1px solid #dfe9e3;
    border-radius: 10px;
    background: #fbfdfb;
}
.piano-more-row {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    align-items: center;
}
.piano-more-row .tool-btn { flex: 0 0 auto; }
@media (max-width: 720px) {
    .piano-more-row .tool-btn { flex: 1 1 auto; }
}
.a2m-opts {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
    margin: 10px 0 2px;
    font-size: 12px;
    color: #6b8375;
}
.a2m-opts select {
    margin-left: 4px;
    padding: 2px 6px;
    border: 1px solid #cfe0d6;
    border-radius: 6px;
    background: #fff;
    color: #2f5a44;
}
.a2m-opts button {
    padding: 2px 10px;
    border: 1px solid #bcd7c6;
    border-radius: 6px;
    background: #fff;
    color: #2f7a55;
    cursor: pointer;
}
.a2m-opts button:disabled { opacity: .45; cursor: default; }
.a2m-panel {
    margin: 0 0 12px;
    padding: 10px 12px;
    border: 1px solid #e2eee7;
    border-radius: 10px;
    background: #f7fbf9;
    color: #3f5a4a;
}
.a2m-top {
    display: flex;
    justify-content: space-between;
    gap: 10px;
    font-size: 12.5px;
    font-weight: 600;
}
.a2m-bar {
    height: 8px;
    margin: 7px 0;
    border-radius: 6px;
    background: #e5efe9;
    overflow: hidden;
}
.a2m-bar i {
    display: block;
    width: 0;
    height: 100%;
    background: linear-gradient(90deg, #54b37c, #2f855a);
    transition: width .3s;
}
.a2m-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 14px;
    font-size: 12px;
    color: #7c9386;
    font-variant-numeric: tabular-nums;
}
.a2m-ctl {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 10px;
    margin-top: 8px;
    font-size: 12px;
    color: #6b8375;
}
.a2m-ctl select {
    margin-left: 4px;
    padding: 2px 6px;
    border: 1px solid #cfe0d6;
    border-radius: 6px;
    background: #fff;
    color: #2f5a44;
}
.a2m-ctl button {
    padding: 2px 10px;
    border: 1px solid #d3b4b4;
    border-radius: 6px;
    background: #fff;
    color: #a14b4b;
    cursor: pointer;
}
.a2m-ctl button#a2mDownload { border-color: #bcd7c6; color: #2f7a55; }
.a2m-ctl button:disabled { opacity: .45; cursor: default; }
.a2m-hint { opacity: .75; }

/* ============================================================================
 * ★★★ 2026-10-20 晚 · 第二版「关于我们」（用户反馈后重做）
 * ----------------------------------------------------------------------------
 * 用户原话（第一版的问题）：
 *   「这个按钮不好看」「点开之后只显示一个小白块，看着也不太舒服」
 *   「主要是这个背景颜色、配色、字体颜色，这些都不太顺眼」
 * 原因：第一版是**深色半透明**（rgba(10,20,15,.55) 那套）⇒ 叠在这页的
 *   **浅色**底（body.v2-route 近白 #f7faf8 + 白卡）上就是一块发脏的灰绿 ✗
 *   summary 又是个小胶囊，展开后 body 只有 580px 宽 ⇒ 孤零零一小块 ✗
 * 第二版：
 *   ① **同一张卡**：收起 = 一行入口；展开 = 这张卡向下延展（不是另起一块 ✗）
 *   ② 入口有图标徽标 + 标题 + 副标题 + 右侧箭头（展开转 180° ✓）
 *   ③ 配色全用这页的浅色体系：白底 / 绿灰细边 / 深灰字 / 绿色只做点缀 ✓
 *   ④ 联系方式做成 chip，号码点一下就复制（脚本在 index.html 末尾那段 ✓）
 * ⑤ 深色主题（html[data-theme=dark]）另留一套兜底 ✓
 * ★ 写在文件最末 ⇒ 与 body.v2-route 那套同权重时后写的赢 ✓（本文件一贯做法）
 * ============================================================================ */

.piano-about {
    margin: 14px 0 0;
    background: #ffffff;
    border: 1px solid #e6f0ea;
    border-radius: 14px;
    box-shadow: 0 1px 2px rgba(16, 60, 40, .04);
    color: #4b5563;
    font-size: 12.5px;
    line-height: 1.7;
    overflow: hidden;
    transition: border-color .16s, box-shadow .16s;
}
.piano-about:hover { border-color: #cfe8db; box-shadow: 0 10px 26px -22px rgba(16, 60, 40, .5); }

/* ---------- 入口那一行 ---------- */
.piano-about > summary.pa-head {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 11px 14px;
    margin: 0;
    list-style: none;
    cursor: pointer;
    user-select: none;
    color: #1f2937;
    background: transparent;
    border: 0;
    outline: 0;
    transition: background .16s;
}
.piano-about > summary.pa-head::-webkit-details-marker { display: none; }
.piano-about > summary.pa-head::marker { content: ""; }
.piano-about > summary.pa-head:hover { background: #fbfefc; }
.piano-about > summary.pa-head:focus-visible { box-shadow: inset 0 0 0 2px #2f855a; border-radius: 13px; }
.piano-about[open] > summary.pa-head {
    background: #fcfefd;
    border-bottom: 1px solid #eef5f1;
}

.pa-ico {
    flex: 0 0 auto;
    width: 30px;
    height: 30px;
    display: grid;
    place-items: center;
    border-radius: 10px;
    background: #ecf7f1;
    color: #2f855a;
    font-size: 13px;
}
.pa-tt { display: flex; flex-direction: column; min-width: 0; line-height: 1.4; }
.pa-tt b { font-size: 13px; font-weight: 600; letter-spacing: .2px; color: #1f2937; }
.pa-tt em { font-style: normal; font-size: 11px; color: #8b9c93; letter-spacing: .2px; }
.pa-caret {
    margin-left: auto;
    color: #9bb3a8;
    font-size: 11px;
    transition: transform .2s ease, color .16s;
}
.piano-about[open] .pa-caret { transform: rotate(180deg); color: #2f855a; }

/* ---------- 展开后的内容（同一张卡 ✓ 不再另起一块） ---------- */
.piano-about-body { padding: 2px 18px 16px; }
.pa-sec { padding-top: 14px; }
.pa-sec + .pa-sec { margin-top: 13px; border-top: 1px solid #f2f7f4; }
.pa-sec > h5 {
    margin: 0 0 6px;
    font-size: 11.5px;
    font-weight: 600;
    letter-spacing: .6px;
    color: #2f855a;
}
.pa-sec > p { margin: 0; color: #4b5563; }

/* 功能列表：绿色小方块做点（不用浏览器默认圆点 ✓ 浅底上更干净） */
.pa-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.pa-list li { position: relative; padding-left: 15px; color: #6b7280; }
.pa-list li::before {
    content: "";
    position: absolute;
    left: 2px;
    top: .68em;
    width: 5px;
    height: 5px;
    border-radius: 2px;
    background: #7fc9a3;
}
.pa-list li > b { margin-right: 6px; color: #1f2937; font-weight: 600; }
.pa-list li > span { color: #6b7280; }

/* 联系方式：一行一个 chip（号码点一下复制 ✓ data-copy，脚本见 index.html 末尾） */
.pa-contact { list-style: none; margin: 0; padding: 0; display: grid; gap: 7px; }
.pa-contact > li {
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 7px 11px;
    background: #f8fcfa;
    border: 1px solid #eef5f1;
    border-radius: 10px;
    font-size: 12px;
    color: #4b5563;
}
.pa-contact i { width: 16px; text-align: center; color: #4f9c76; font-size: 13px; }
.pa-k { min-width: 36px; color: #8b9c93; }
.pa-copy {
    margin: -1px -5px;
    padding: 1px 5px;
    border-radius: 6px;
    color: #1f2937;
    font-weight: 600;
    letter-spacing: .3px;
    cursor: pointer;
    transition: background .15s, color .15s;
}
.pa-copy:hover { background: #e9f7ef; color: #2f855a; }
.pa-copy.is-ok { background: #e9f7ef; color: #2f855a; }
.pa-contact a { color: #2b9159; text-decoration: none; }
.pa-contact a:hover { text-decoration: underline; }
.pa-soon {
    padding: 0 6px;
    background: #fdf6e7;
    border: 1px solid #f3e4c2;
    border-radius: 6px;
    color: #9a7534;
    font-size: 11px;
}
.pa-fine {
    margin: 14px 0 0;
    padding-top: 12px;
    border-top: 1px solid #f2f7f4;
    font-size: 11px;
    line-height: 1.65;
    color: #9aa8a0;
}

/* ---------- 深色主题兜底（本页是浅色 v2-route ⇒ 仅其它上下文生效） ---------- */
html[data-theme="dark"] body:not(.v2-route) .piano-about {
    background: #10161c;
    border-color: #1f2c26;
    box-shadow: none;
    color: #b3c2b8;
}
html[data-theme="dark"] body:not(.v2-route) .piano-about > summary.pa-head:hover { background: #16201a; }
html[data-theme="dark"] body:not(.v2-route) .piano-about[open] > summary.pa-head {
    background: #141d18;
    border-bottom-color: #1f2c26;
}
html[data-theme="dark"] body:not(.v2-route) .pa-ico { background: rgba(47, 133, 90, .18); color: #7fe0a8; }
html[data-theme="dark"] body:not(.v2-route) .pa-tt b,
html[data-theme="dark"] body:not(.v2-route) .pa-list li > b,
html[data-theme="dark"] body:not(.v2-route) .pa-copy { color: #e6f2ea; }
html[data-theme="dark"] body:not(.v2-route) .pa-tt em,
html[data-theme="dark"] body:not(.v2-route) .pa-k,
html[data-theme="dark"] body:not(.v2-route) .pa-list li,
html[data-theme="dark"] body:not(.v2-route) .pa-list li > span,
html[data-theme="dark"] body:not(.v2-route) .pa-sec > p { color: #9db0a6; }
html[data-theme="dark"] body:not(.v2-route) .pa-sec + .pa-sec,
html[data-theme="dark"] body:not(.v2-route) .pa-fine { border-color: #1f2c26; }
html[data-theme="dark"] body:not(.v2-route) .pa-contact > li { background: #0e1512; border-color: #1f2c26; }

/* ============================================================================
 * ★★★ 2026-10-20 晚 · 工具栏「力度」下拉（跟在踏板后面 ✓）
 * 观感跟音色下拉同一套：白底 / 绿灰细边 / 深绿字 / 自绘小箭头（去掉系统丑箭头 ✓）
 * ========================================================================== */
body.v2-route:not(.fx-stage) .piano-velopt select,
.piano-velopt select {
    padding: 3px 24px 3px 9px;
    border: 1px solid #dbe8e0;
    border-radius: 7px;
    background-color: #ffffff;
    color: #33705a;
    font: 12px system-ui, "Microsoft YaHei";
    cursor: pointer;
    appearance: none;
    -webkit-appearance: none;
    background-image:
        linear-gradient(45deg, transparent 50%, #3f8f65 50%),
        linear-gradient(135deg, #3f8f65 50%, transparent 50%);
    background-position: calc(100% - 13px) 52%, calc(100% - 9px) 52%;
    background-size: 4px 4px, 4px 4px;
    background-repeat: no-repeat;
}
body.v2-route:not(.fx-stage) .piano-velopt select:hover,
.piano-velopt select:hover {
    border-color: #a8d5bd;
    background-color: #f6fbf8;
}
.piano-velopt select:focus-visible { outline: 2px solid #2f855a; outline-offset: 1px; }

/* ============================================================================
 * ★★★ 2026-10-20 晚 · 「两排简谱」那一块（内容由 piano-sheet.js 画 ✓）
 * ----------------------------------------------------------------------------
 * 位置由 JS 算（贴在琴键正上方 8px）⇒ 这里只管外观 + 给绝对定位一个参照物
 * ★ `.piano-panel` 加 position:relative 是为它服务的：
 *   里面原来的 .piano-scroll 本来就是 relative ⇒ 音条 / 琴键一格都不受影响 ✓
 * ========================================================================== */
.piano-panel { position: relative; }

.pk-sheet {
    position: absolute;
    height: 92px;
    /* ★★★ 2026-10-20 晚（用户指定层级）：图片/压暗层 → **简谱** → 音条 → 命中线 → 火星
       背景图和压暗层都画在 .pk-roll 的 background 里（z-index:1）⇒
       简谱同为 1 但在 DOM 里排在它后面 ⇒ 压在画布上 ✓
       音条 .pk-strip-wrap 是 2、命中线 .pk-hitline 是 3、火星 .pk-sparks 是 3
       ⇒ 下落音条一律从简谱**上面**经过 ✓ 简谱再也不会挡住正在落的音 ✓ */
    z-index: 1;
    pointer-events: none;            /* 不挡鼠标（拖音条 / 点琴键照旧 ✓） */
    /* ★ 没有底板 ✗ 也没有毛玻璃 ✗（用户："背景不要出现这种黑色的"）
       ⇒ 谱子像**印在背景图上**一样，只剩白数字 + 一层很淡的暗投影保证可读 ✓
       注：backdrop-filter 会另起一个层叠上下文 ⇒ 上面那个 z-index:1 就白写了 ✗ 所以一并去掉 */
    overflow: hidden;
    /* ★ 左右边缘渐隐：数字滚到边上不会"被刀切"（只是视觉遮罩 ⇒ 不改布局 ✓） */
    -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 4%, #000 96%, transparent 100%);
    mask-image: linear-gradient(90deg, transparent 0, #000 4%, #000 96%, transparent 100%);
}
.pk-sheet[hidden] { display: none; }
.pk-sheet canvas { display: block; width: 100%; height: 100%; }

/* ★ 2026-10-20 晚：简谱「谱头」角标（1=E♭ 小调 · 4/4 · ♩=72 · 第 3/148 小节）
   为什么宽度靠内容撑（不是 100%）：它要像「谱子上写的一行小字」
   ⇒ 拉一条通栏横线反而像加载条 ✓ 位置由 piano-sheet.js 跟着简谱条算 ✓
   颜色 .55 + 暗投影：背景图再亮也读得出，但绝不抢谱子的视线 ✓ */
.pk-sheet-meta {
    position: absolute;
    z-index: 1;
    pointer-events: none;
    font: 500 10.5px/1.5 system-ui, "Microsoft YaHei", sans-serif;
    letter-spacing: .04em;
    color: rgba(255, 255, 255, .55);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    text-shadow: 0 1px 2px rgba(0, 0, 0, .75);
    padding: 0 2px;
}
.pk-sheet-meta[hidden] { display: none; }

/* ============================================================
   2026-10-21 十轮：MIDI 状态栏带上曲名（用户要求）
   ------------------------------------------------------------
   「已载入 1476 个音符，时长 5:43 · 童年 - 罗大佑.mid」
   · 名字可能很长（API 地址里的作品 id / 中文文件名）⇒ 必须收得住
   · 完整路径挂在 title 上 ⇒ 鼠标停上去看全 ✓
   ★ 不能只写 max-width ✗：span 默认是 inline，max-width 对 inline 无效
     ⇒ 这里显式 inline-block（v2 那个 pill 的 padding / 圆角照旧 ✓）
   ★ :empty 要跟着隐藏（不然没载入曲子时右边会冒出一个空胶囊 ✗） */
#pianoMidiStat {
    display: inline-block;
    max-width: min(46ch, 40vw);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    vertical-align: middle;
}
#pianoMidiStat:empty { display: none; }

/* ============================================================
   ★★★ 2026-10-22（P1 · 用户要求）：**音色库 = 三张卡**（不再用下拉）
   ------------------------------------------------------------
   三种音色：内置单层 / Yamaha C5 V6.2（16 层）/ Salamander V3（16 层）
   · 同一张钢琴图（static/images/piano-default-bg.jpg）用不同滤镜区分三套
     （手上只有一张授权明确的图；等有更好的素材只换 background-image ✓）
   · 卡片状态：vc-builtin / vc-ready / vc-busy / vc-idle / vc-partial
   · 窄屏自动堆成一列（手机上三列会把按钮挤成两行 ✓）
   ============================================================ */

.voice-deck { margin: 10px 0 6px; }
.voice-deck-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin-bottom: 8px; }
.voice-deck-title { font-size: 13px; font-weight: 600; letter-spacing: .3px; }
.voice-deck-sub { font-size: 11.5px; opacity: .6; }

.voice-cards { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
@media (max-width: 820px) { .voice-cards { grid-template-columns: 1fr; } }

.voice-card {
    display: flex; gap: 12px; padding: 10px;
    border: 1px solid rgba(0, 0, 0, .09); border-radius: 12px;
    background: #fff;
    transition: border-color .15s, box-shadow .15s, transform .15s;
}
.voice-card.is-active {
    border-color: rgba(58, 129, 89, .55);
    box-shadow: 0 0 0 2px rgba(58, 129, 89, .12);
}
.voice-card.vc-ready, .voice-card.vc-builtin { cursor: pointer; }
.voice-card.vc-ready:hover, .voice-card.vc-builtin:hover { transform: translateY(-1px); }

.vc-art {
    position: relative; flex: 0 0 104px; width: 104px; height: 80px;
    border-radius: 8px; overflow: hidden;
    background-image: url("/static/images/piano-default-bg.jpg");
    background-size: cover; background-position: center;
}
.vc-art::after {
    content: ''; position: absolute; inset: 0;
    background: linear-gradient(180deg, rgba(0, 0, 0, 0) 42%, rgba(0, 0, 0, .58));
}
.vc-art-basic { filter: grayscale(.18) brightness(1.06); }
.vc-art-v62   { filter: saturate(1.15) contrast(1.06); }
.vc-art-v3    { filter: hue-rotate(-14deg) saturate(.82) brightness(.94); }

.vc-badge {
    position: absolute; left: 6px; bottom: 6px; z-index: 1;
    font-size: 10.5px; line-height: 1.6; padding: 0 7px; border-radius: 999px;
    background: rgba(0, 0, 0, .58); color: #eafff3; white-space: nowrap;
}
.voice-card.is-active .vc-badge { background: #3a8159; }

.vc-main { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: 5px; }
.vc-name { font-size: 12.5px; font-weight: 600; line-height: 1.35; }
.vc-meta { font-size: 11px; opacity: .62; line-height: 1.45; }

.vc-bar { height: 4px; border-radius: 2px; background: rgba(0, 0, 0, .07); overflow: hidden; }
.vc-bar > i { display: block; height: 100%; width: 0; background: #3a8159; transition: width .3s; }
.voice-card.vc-busy .vc-bar > i { background: #c9912b; }

.vc-actions { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; margin-top: 1px; }
.vc-btn {
    font: 11.5px/1 system-ui, sans-serif;
    padding: 6px 9px; border-radius: 7px;
    border: 1px solid rgba(0, 0, 0, .14); background: #fff; color: #24352c;
    cursor: pointer; transition: border-color .15s, background .15s;
}
.vc-btn:hover { border-color: rgba(58, 129, 89, .55); }
.vc-btn:disabled { cursor: default; }
.vc-btn.is-primary { background: #3a8159; border-color: #3a8159; color: #fff; }
.vc-btn.is-primary:hover { background: #347552; }
.vc-btn.is-on { background: rgba(58, 129, 89, .12); border-color: rgba(58, 129, 89, .4); color: #2f6b4a; }
.vc-btn.is-warn { color: #a3541c; border-color: rgba(163, 84, 28, .38); }
.vc-btn.is-off { opacity: .45; }
.vc-note { font-size: 11px; opacity: .55; }

.voice-deck-tip { font-size: 11.5px; margin: 7px 0 0; opacity: .75; }
.voice-deck-tip.is-err { color: #c0392b; opacity: 1; }

/* 深色主题（页面里 html[data-theme="dark"] 那套 ✓ 不新造变量） */
html[data-theme="dark"] .voice-card {
    background: rgba(9, 18, 13, .85);
    border-color: rgba(127, 224, 168, .18);
    color: #d8ffe9;
}
html[data-theme="dark"] .voice-card.is-active {
    border-color: rgba(127, 224, 168, .6);
    box-shadow: 0 0 0 2px rgba(127, 224, 168, .12);
}
html[data-theme="dark"] .vc-btn {
    background: rgba(9, 18, 13, .9);
    border-color: rgba(127, 224, 168, .3);
    color: #d8ffe9;
}
html[data-theme="dark"] .vc-btn.is-primary { background: #2f6b4a; border-color: #2f6b4a; color: #eafff3; }
html[data-theme="dark"] .vc-btn.is-on { background: rgba(127, 224, 168, .14); color: #a8f0c6; }
html[data-theme="dark"] .vc-bar { background: rgba(255, 255, 255, .12); }

