/**
 * SPA 主布局
 *
 * 结构:
 *   .app 横向 grid
 *     .app-sidebar 左侧导航(固定宽)
 *     .app-main    右侧主区(竖向 grid)
 *       .app-header     顶部信息条
 *       .app-list-area  列表(滚动)
 *       .app-player-bar 底部播放器(固定高度,常驻)
 *
 * 关键:底部 .app-player-bar 始终存在且固定高度,
 * 切换分类/页面不会触发重建,确保音频无缝续播。
 *
 * 全屏 Now Playing 视图:
 *   .app-fullscreen 浮层(覆盖整个视口),通过 body.fullscreen-mode 触发
 *
 * 设计语言:淡色、简洁、字大、用户友好
 *   - 浅底 #f7f7fa / 卡片白 #ffffff / 深字 #1c1c20
 *   - 强调色 #6d28d9(友好紫),全屏视图允许极弱径向紫光晕(透明度 ≤ 0.04)
 *   - 字号普遍偏大,点击区偏大,圆角偏大 — 适配老人/视力一般用户
 *   - 不走渐变玻璃风
 *   - 过渡统一用 cubic-bezier(0.16, 1, 0.3, 1) — 快速启动 + 平滑收尾
 *
 * Breakpoints(本文件 + smusic.enhance.css + favorites.css + theme.css 协同):
 *   ≥1920      2K soft cap, 1600px max-width list
 *   ≥1440      Wide desktop, sidebar 280 / player 104 / cover 440
 *   1025–1439  Mid desktop, sidebar 200 (v52.2 合并到 721-1439 统一档) / list card / cover 340
 *   ≤1024      Tablet top, sidebar 200 (v52.2 从 220 降) / cover 320
 *   721–1439   iPad + mid-desktop, sidebar 200 (v52.2 合并,nav-label 14/nav-idx 22 本块局部) / 2-row player / mode hidden / cover 44
 *   ≤900 (h≤500, landscape) Sidebar visible, player 64 single row / cover 隐藏 live-wave
 *   ≤720       Mobile, sidebar drawer (clamp 200→280,中段 100vw-40,v52.5 上限再缩 320→280) / 2-row player
 *   ≤480       ⋯ overflow, cover 40
 *   ≤380       Compress, header meta hidden, cover 36
 *   ≤360       Cover 34, hide artist name
 *   ≤320       Fallback, cover 32, theme btn 30
 *   hover:none Touch expansion via ::before/::after (≥44pt iOS HIG)
 */

:root {
    /* v27+:浅色主题显式声明 color-scheme,告诉浏览器原生 UI(滚动条、表单元素、
       autofill 等)用浅色模式,避免 OS 深色模式下浏览器自动反转页面导致冲突。 */
    color-scheme: light;

    /* ====== 主题色(覆盖 smusic.css 的暗色变量) ====== */
    --bg: #f7f7fa;
    --bg-elevated: #ffffff;
    --bg-hover: #f0f0f4;
    --bg-active: #ebebef;
    --border: #ececf0;
    --border-strong: #d8d8de;
    --text: #1c1c20;
    --text-soft: #3f3f46;
    --text-mute: #6b6b75;
    /* v54+: --text-strong 用于 hover/focus 状态的"加粗"文字色,比 --text 多一档对比度。
       浅色主题用纯黑 #000(在 #f7f7fa 上 19.05:1,远超 WCAG AAA 7:1)。
       用途:.app-sidebar-close:hover/focus-visible (app.css:2552/3626/4124)。
       修复 v54 审计发现的 P0 #2:该 token 此前被引用但从未定义,导致 hover 文字色
       回退到 inherit,与默认 --text 相同,焦点态视觉反馈消失。 */
    --text-strong: #000000;
    --accent: #6d28d9;
    --accent-soft: rgba(109, 40, 217, 0.08);
    --accent-line: rgba(109, 40, 217, 0.28);
    /* v54+: --accent-hover 用主播放按钮(:hover)的加深变体色,比 --accent 略亮 10-15%。
       浅色默认主题 .u-play-btn.ctrl-play:hover (smusic.css:305-313)。
       修复 v54 审计发现的 P0 #1:该 token 此前被引用但从未定义(grep 5 CSS 文件 0 hit),
       hover 时 background/border-color 回退到 transparent,WCAG 1.4.11 对比度归零。 */
    --accent-hover: #7c3aed;
    /* v40+: accent glow 三档 alpha — 用于 thumb/focus/play 阴影,跨主题自适应。
       用 color-mix(in srgb, var(--accent) <alpha>%, transparent) 跟随 6 主题 accent 色。
       修复前 8 处硬编码 rgba(109,40,217,*) 在 sepia/ocean/forest 下视觉违和。
       不支持 color-mix 的浏览器 fallback:浅色主题有 #6d28d9 覆盖,效果相同。 */
    --accent-glow-low:  color-mix(in srgb, var(--accent) 20%, transparent);
    --accent-glow-med:  color-mix(in srgb, var(--accent) 30%, transparent);
    --accent-glow-high: color-mix(in srgb, var(--accent) 45%, transparent);
    /* v56 P0 修复:Safari ≤15.3 / 不支持 color-mix 的浏览器(Safari 16.4+ / Chrome 111+ 才支持)
       上面 3 个 token 解析为空,导致 --shadow-card-hover / focus / thumb 阴影消失。
       兜底块放在 :root 闭合后(见下方独立 @supports not),不在此处嵌套避免
       CSS 解析歧义;浅色主题用 #6d28d9,深色主题在 [data-theme=xxx] 内继续覆盖。 */
    /* v29 round 4+: 浅色主题 :root 默认阴影 token,5 个非浅色主题各自覆盖。
       之前 :root 未定义,导致浅色主题用硬编码 fallback (rgba(0,0,0,.18)),
       跨主题阴影一致性无法保证。 */
    --shadow-card:        0 1px 3px rgba(0, 0, 0, 0.05);
    --shadow-card-hover:  0 6px 16px -4px var(--accent-glow-low);
    --shadow-popup:       0 10px 30px -8px rgba(0, 0, 0, 0.18);
    /* v38: 危险按钮(收藏/最近工具栏"清空")主题化。
       原硬编码 #c2410c 在 dark/night 主题下 13px 文字对比度 3.22/3.38,失败 AA。
       6 主题各定义 —danger / --danger-soft,确保 WCAG 4.5:1。 */
    --danger:        #c2410c;
    --danger-soft:   rgba(194, 65, 12, 0.08);
    --radius: 10px;
    --radius-sm: 6px;
    /* v29: 圆角阶梯 token 化。-xs/-sm/-md/-lg/-xl 5 档,所有静态元素圆角用 token。
       滚动条 thumb 半径仍由 width/2 字面算(见 v26),不强制用 token。 */
    --r-xs: 4px;
    --r-sm: 6px;
    --r-md: 8px;
    --r-lg: 12px;
    --r-xl: 16px;
    /* v41+:栈前置 HarmonyOS Sans SC / system-ui — 比 YaHei 更现代圆润的
       无衬线,Windows 上若未安装则降级到 YaHei。Mac 仍走 PingFang SC。
       v42+:补 "Microsoft YaHei UI"(Win11/Office 同族现代版)放在 YaHei 之前,
       让 Win11 默认环境直接命中 YaHei UI 而非绕到字面 alias。 */
    --font: system-ui, -apple-system, BlinkMacSystemFont, "Inter", "PingFang SC", "HarmonyOS Sans SC", "Microsoft YaHei UI", "Microsoft YaHei", "微软雅黑", sans-serif;
    --mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;

    /* ====== 字号阶梯 token(v37+) ======
       与排版层级一一对应,所有硬编码 px 收口到 token。
       v40 P2:删 9 个 0 引用 token — --fs-2xs/-xs/-5xl/-7xl/-8xl/-9xl/-10xl/-12xl/-lh-zero —
       这些 token 声明后未被任何 var(--fs-*) 引用,保留只增维护噪音。
       v42 重新启用部分 token —— 之前 0 引用是因为尚未替换硬编码值。
       本次替换 50+ 处硬编码 px → token,以下 token 全部激活。 */
    --fs-2xs:  10px;  /* ≤320 / 短高 u-time — a11y 临界 */
    --fs-sm:   11px;  /* ≤380 u-time / ≤320 toolbar btn / status dot */
    --fs-base: 12px;  /* 正文 / body / ≤720 meta-count / toolbar / 全屏 time-cur / 横屏 short brand */
    --fs-md:   13px;  /* ≤380/≤320 crumb / nav-fav-icon / 主题 en / ≤480 player strong */
    --fs-lg:   14px;  /* base li strong / 横屏 short brand-name / ≤480 strong / input */
    --fs-xl:   15px;  /* base meta / input / 721-1024 player strong */
    --fs-2xl:  16px;  /* ≤720 crumb / keyhint / 主题 trigger / 1025-1439 brand */
    --fs-3xl:  17px;  /* cheatsheet title / 全屏 artist default */
    --fs-4xl:  18px;  /* 721-1024 brand-name */
    --fs-5xl:  19px;  /* 1025-1439 crumb / ≥1440 全屏 artist */
    --fs-6xl:  20px;  /* base brand-name / 全屏 title ≤480 */
    --fs-7xl:  22px;  /* ≥1440 crumb / ≤360 全屏 title */
    --fs-8xl:  26px;  /* ≤720 全屏 title */
    --fs-9xl:  30px;  /* ≤900 全屏 title */
    --fs-10xl: 32px;  /* 721-1024 全屏 title */
    --fs-11xl: 36px;  /* 全屏 title default */
    --fs-12xl: 42px;  /* ≥1440 全屏 title */

    /* ====== 行高阶梯 token ======
       5 档语义(v40:删 --lh-zero,与字号 token 同处理),与字号搭配形成完整排版层级。 */
    --lh-tight:   1.2;    /* 大标题:crumb-cat / 全屏 title ≥1440 */
    --lh-snug:    1.25;   /* 窄屏全屏 title (允许 2 行) */
    --lh-normal:  1.4;    /* nav-a / u-music-title / list-item / toast */
    --lh-relaxed: 1.6;    /* 空状态描述 / fav-empty desc */
    --lh-meta:    1.3;    /* 主题选项描述 / 紧凑元信息 */

    /* ====== 字距阶梯 token ======
       标题负字距收紧(大字号),小字正字距加间距(全大写/英文)。
       body=0 不设 token(默认就是 0)。 */
    --tracking-display: -0.02em;  /* ≥32px 巨型标题 */
    --tracking-title:   -0.01em;  /* 18-22px 主标题 */
    --tracking-caps:    0.08em;   /* UPPERCASE pill / 全屏 tag */
    --tracking-en:      0.04em;   /* 英文 mono 副文本 */
    --tracking-tag:     0.02em;   /* 中文辅助文本 */

    /* ====== 尺寸(更宽敞) ====== */
    --sidebar-width: 240px;
    --player-height: 96px;
    --header-height: 60px;

    /* 安全区(支持 iOS 刘海/底部 home indicator) */
    --safe-top: env(safe-area-inset-top, 0px);
    --safe-bottom: env(safe-area-inset-bottom, 0px);
    --safe-left: env(safe-area-inset-left, 0px);
    --safe-right: env(safe-area-inset-right, 0px);

    /* motion tokens */
    --ease: cubic-bezier(0.16, 1, 0.3, 1);
    --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
    --t-fast: 100ms;
    --t-base: 150ms;
    --t-slow: 240ms;

    /* v10: 用 dvh 修 iOS Safari 地址栏塌陷导致的 100vh 越界问题。
       旧浏览器 / 不支持 dvh 的环境回退 100vh。 */
    --app-height-dvh: 100vh;

    /* v40 P0: z-index 语义化分层 — 之前 skip-link / theme-popup / app-global-toast
       三处都用 9999 散弹,跨模块同时触发时绘制顺序不可控。统一定义语义层级:
       sticky(顶栏/侧栏) → overlay(抽屉) → popup(主题/菜单) → toast → modal(全屏) → skip-link */
    --z-sticky: 100;
    --z-overlay: 500;
    --z-popup: 1000;
    --z-toast: 2000;
    /* v60:删 --z-modal(v40 引入时全屏视图改用 var(--z-sticky) + 单独堆叠,
       modal 槽位实际未启用,全项目 0 处 var(--z-modal) 引用)。 */
    --z-skip: 10000;
    /* v47:u-time min-width token 化 — 之前 8 处字面(60/50/70)散落 app.css + smusic.css,
       数值随断点跳变无规律。--u-time-min-w 默认 60px(纵屏/桌面),
       横屏窄 (-landscape 50px) 与 ≤500h 短高字号 --fs-2xs 时 (-short 70px) 单列语义。*/
    --u-time-min-w: 60px;
    --u-time-min-w-landscape: 50px;
    --u-time-min-w-short: 70px;

    /* v60:font-weight token 化 — 49 处硬编码字面量散落 5 个 CSS 文件,
       项目已有 --fs-* / --r-* / --z-* / --t-* token 体系,但 font-weight 全部硬编码,
       新增主题/字号档时无法集中调整。命名按用途:
       --fw-regular(正文/弱化文本)/ --fw-medium(中等强调,品牌/导航)/
       --fw-semibold(主标题/重要按钮)/ --fw-bold(极少,装饰强). */
    --fw-regular: 400;
    --fw-medium: 500;
    --fw-semibold: 600;
    --fw-bold: 700;

    /* v60:z-index 局部堆叠 token — 全屏播放器/底部播放器内部堆叠用 1/2/-1
       字面量散落 21 处,与已 token 化的 --z-sticky(100)/--z-overlay(500) 等大层级
       风格不一致。命名按用途而非数字:
         --z-mask(-1) — 封面背后遮罩层(暗化背景)
         --z-track(1) — 进度/音量轨道基础层(buffer / current)
         --z-thumb(2) — thumb / hover 浮层(在轨道之上) */
    --z-mask: -1;
    --z-track: 1;
    --z-thumb: 2;
}
@supports (height: 100dvh) {
    :root {
        --app-height-dvh: 100dvh;
    }
}
/* v56 P0 修复:不支持 color-mix 的浏览器(Safari ≤15.3 等老内核)用固定 rgba 兜底
   3 个 accent-glow token,避免 focus / thumb / hover 阴影完全消失。
   浅色主题默认值用 #6d28d9 (与 token 注释说明一致);深色主题有各自的
   [data-theme=xxx] 块覆盖 --accent 为浅紫色,rgba 透明度不变所以视觉接近。 */
@supports not (color: color-mix(in srgb, red 50%, transparent)) {
    :root {
        --accent-glow-low:  rgba(109, 40, 217, 0.20);
        --accent-glow-med:  rgba(109, 40, 217, 0.30);
        --accent-glow-high: rgba(109, 40, 217, 0.45);
    }
}

*, *::before, *::after { box-sizing: border-box; }

html, body {
    margin: 0;
    padding: 0;
    height: 100%;
    overflow: hidden;
    font-family: var(--font);
    font-size: var(--fs-base);
    font-weight: var(--fw-medium);
    background: var(--bg);
    color: var(--text);
}

body.app-body {
    height: var(--app-height-dvh);
    overflow: hidden;
}

/* ============== 全局微调(选区 / 焦点 / 滚动条) ============== */

::selection {
    /* v51 P2-7:硬编码 rgba(196, 168, 255, 0.28) → color-mix(in srgb, var(--accent) 28%, transparent),
       与 theme.css 6 主题的 --accent token 对齐,保证主题切换时选区色跟随 accent 变。
       0.28 透明度保留视觉强度(选区需要足够突出)。 */
    background: color-mix(in srgb, var(--accent) 28%, transparent);
    color: var(--text);
}

/* v27+:Skip-link 跳转链接 — 屏幕阅读器和键盘用户的可访问性入口。
   默认隐藏(避免视觉干扰),:focus-visible 时显示在屏幕顶部中央。 */
.skip-link {
    position: absolute;
    top: 8px;
    left: 8px;
    padding: 10px 16px;
    background: var(--accent);
    color: var(--bg-elevated);
    border-radius: var(--radius);
    font-size: var(--fs-lg);
    font-weight: var(--fw-semibold);
    text-decoration: none;
    z-index: var(--z-skip);
    transform: translateY(-200%);
    /* v29: 0.12s → var(--t-fast),与全站时长阶梯统一 */
    transition: transform var(--t-fast) ease;
}
.skip-link:focus-visible {
    transform: translateY(0);
    outline: 2px solid var(--text);
    outline-offset: 2px;
}
/* v27+:skip-link 跳转目标 app-main 在 :focus 时去掉浏览器默认 outline。
   tabindex=-1 让它可被 :target 聚焦,但视觉上仍是无 border 的内容区 */
.app-main:focus { outline: none; }

:focus-visible {
    /* v27+:outline 1px accent-line(28% alpha)在浅色背景对比 ~1.5:1,远低于 WCAG SC 1.4.11 3:1 要求。
       改用 2px accent(全饱和)+ outline-offset:2px,与 .app-brand:focus-visible(L244) 风格统一,
       深浅主题对比度都达 AA。 */
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* v37: 删 button/a:focus-visible 单独设 outline-offset:2px — 已在 :focus-visible 全局规则里,
   (0,1,1) > (0,1,0) 但属性值相同,纯冗余 */

/* v29: 焦点环样式增强。
   9 个交互按钮(fav/recent/mobile-menu/fullscreen-close/fs-ctrl/fs-play/fs-vol-icon/
   li-fav-btn/fav-modal-btn/fav-modal-close)之前只有全局 2px outline,没有 brand 色
   背景反馈。统一加 :focus-visible 用 var(--accent) + var(--accent-soft),与已有的
   .app-keyhint-btn 等风格统一(覆盖其他已有 :focus-visible 的同名按钮也安全 —
   var(--accent-soft) 与 hover 反馈一致)。 */
.app-mobile-menu-btn:focus-visible,
.app-fullscreen-close:focus-visible,
.fs-ctrl:focus-visible,
.fs-vol-icon:focus-visible {
    color: var(--accent);
    background: var(--accent-soft);
    outline: none;
}
/* v34: .fs-play 焦点态单独覆盖 — 主播放按钮是大块 accent 色按钮,
   默认 focus-visible(accent icon on accent-soft bg)会让 SVG icon 与背景
   同色融为一片,WCAG 1.4.11 对比度退化。改为 bg-elevated 图标 + 4px accent ring,
   与 hover 视觉锚点对称,跨主题一致(各主题 --bg-elevated 都 ≥5.4 对比度)。 */
.fs-play:focus-visible {
    color: var(--bg-elevated);
    background: var(--accent);
    outline: 3px solid var(--accent);
    outline-offset: 3px;
}

/* ============== 顶层网格 ============== */

.app {
    display: grid;
    grid-template-columns: var(--sidebar-width) 1fr;
    height: var(--app-height-dvh);
    width: 100vw;
    background: var(--bg);
    /* v57 修复:fullscreen-mode 过渡原本分裂在 body.fullscreen-mode .app /
       body:not(.fullscreen-mode) .app 两个互斥选择器,JS 切 class 时只有当前匹配
       一侧有 transition 定义,另一侧的状态变化会"瞬变"。统一上提到这里。 */
    transition: opacity var(--t-slow) var(--ease);
}

/* ============== 侧边栏 ============== */

.app-sidebar {
    display: flex;
    flex-direction: column;
    background: var(--bg-elevated);
    border-right: 1px solid var(--border);
    overflow: hidden;
    min-width: 0;
}

.app-sidebar-head {
    padding: 14px 14px 12px;
    border-bottom: 1px solid var(--border);
    flex-shrink: 0;
}

/* 分类过滤框 — 已移除,见 git 历史 */

.app-brand {
    display: flex;
    align-items: center;
    gap: 10px;
    text-decoration: none;
    color: var(--text);
    padding: 4px 0;
}

/* 键盘聚焦时显示焦点环(鼠标点击不触发 :focus-visible,不会喧宾夺主) */
.app-brand:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* 品牌图标:左上角 logo,用 icon.jpg 替代原来的紫色圆点 */
.app-brand-icon {
    width: 28px;
    height: 28px;
    /* v29: 8px → var(--r-md),圆角 token 化 */
    border-radius: var(--r-md);
    object-fit: cover;
    flex-shrink: 0;
    /* v40 修复:跨主题自适应 — 浅色主题下深灰阴影,深色主题下 --text 是浅色
       浅色阴影。rgba(0,0,0,0.08) 在 #232328 (dark) / #1c232c (night) 背景下
       几乎不可见,品牌图标与侧栏背景无层次。 */
    box-shadow: 0 1px 2px color-mix(in srgb, var(--text) 8%, transparent);
}

/* 窄屏:图标缩小,与更紧凑的侧栏布局匹配 */
/* 之前有 brandIdle 8s 持续缩放动画,作为长时间停留页面的视觉噪点已移除 —
  静态圆点 + halo 已足够表达品牌身份 */

.app-brand-name {
    /* v43+:18→20,品牌标题稍大一档;v44+:list strong 12→14 后层级
       仍 20 > 14 (+6px gap),无需再调,保持品牌"大字号高识别度"语言 */
    font-size: var(--fs-6xl);
    /* v41+:字重 700→600,与 body 500 拉开一档即可,不再"黑体"过重 */
    font-weight: var(--fw-semibold);
    letter-spacing: var(--tracking-title);
}

/* ============== 分类导航 ============== */

.app-nav {
    flex: 1;
    overflow-y: auto;
    overflow-x: hidden;
    padding: 10px 0 16px;
    /* v51 P2-1:合并两块 .app-nav 到此一处,避免选择器重复定义。
       侧栏滚动条:10px 总宽(thumb 撑满填充),与最近列表、主列表共用同一套设计语言
       (默认 border-strong → hover text-mute),宽度阶梯:最近/侧栏 10 < 主列表 26。
       v26+:加 scrollbar-gutter: stable,避免展开/折叠分类时滚动条出现挤压 nav-idx/nav-label。
       Firefox 显式声明 thin(8px),与 webkit 10px 视觉接近。 */
    scrollbar-gutter: stable;
    scrollbar-width: thin;
    scrollbar-color: var(--border-strong) transparent;
}
.app-nav::-webkit-scrollbar { width: 10px; }
.app-nav::-webkit-scrollbar-track { background: transparent; }
.app-nav::-webkit-scrollbar-thumb {
    background: var(--border-strong);
    border-radius: 5px;
    min-height: 40px;
    transition: background var(--t-base) var(--ease);
}
.app-nav::-webkit-scrollbar-thumb:hover { background: var(--text-mute); }

.app-nav a {
    display: grid;
    grid-template-columns: 28px 1fr auto;
    /* v49+:gap 10 → 8 — v46 把 nav-idx/count 字号 12→14 后,1280px 220 侧栏
       "分类：04-08" 仍差 1px 触发 ellipsis (91/92px)。每 gap 省 2px × 2 = 4px,
       让 label 空间 +4,所有断点完整显示 "分类：04-08"。视觉更紧凑但仍清晰。 */
    gap: 8px;
    align-items: center;
    /* v22: 11px 让行高 16*1.4+22=44.4 达到 iOS HIG 44 触摸命中 */
    padding: 11px 18px;
    font-size: var(--fs-2xl);
    color: var(--text-soft);
    text-decoration: none;
    border-left: 3px solid transparent;
    cursor: pointer;
    transition:
        background var(--t-fast) var(--ease),
        color var(--t-fast) var(--ease),
        border-color var(--t-fast) var(--ease);
    line-height: var(--lh-normal);
    position: relative;
}

.app-nav a:hover {
    background: var(--bg-hover);
    color: var(--text);
}

/* v27+:键盘焦点环显式声明,与全站新焦点环规则一致 */
.app-nav a:focus-visible {
    background: var(--accent-soft);
    color: var(--accent);
    outline: none;
}

.app-nav a.active {
    background: var(--accent-soft);
    color: var(--accent);
    border-left-color: var(--accent);
    font-weight: var(--fw-semibold);
}

/* v46 字号审计 P2 Fix 8:12→14(原 fs-base → fs-lg)— 修复桌面端 nav 数字偏小。
       原 12px 在桌面 1920+ 视口下,数字序号/计数视觉权重过轻;
       改 14px 与 nav a 字号 (--fs-2xl 16px) 阶梯协调,层级清晰。无媒体块覆盖。 */
.app-nav a .nav-idx {
    font-family: var(--mono);
    font-size: var(--fs-lg);
    color: var(--text-mute);
    font-variant-numeric: tabular-nums;
    text-align: right;
    transition: color var(--t-fast) var(--ease);
}

.app-nav a.active .nav-idx {
    color: var(--accent);
}

/* v60.1+:收藏项的 .nav-idx 默认是右对齐(普通分类显示数字);收藏里放的是
   心形图标或波形柱,改为居中对齐 + flex 容器让 14×14 svg / 12px 波形居中显示。
   默认显示心形,播放时通过 .is-now-playing 切到波形(详见下方 .nav-fav-icon 隐藏规则)。 */
.app-nav a.is-favorites .nav-idx {
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center; /* 覆盖默认 text-align: right */
}

.app-nav a .nav-label {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    /* grid 1fr 子项默认 min-width: auto 会按内容撑开导致 ellipsis 不触发;
       显式锚定 0 才能让 count 出现时 label 乖乖 ellipsis 而不是把整行挤爆 */
    min-width: 0;
}

/* v46 字号审计 P2 Fix 8:12→14(原 fs-base → fs-lg)— 配合 nav-idx 同步上调。 */
.app-nav a .nav-count {
    font-family: var(--mono);
    font-size: var(--fs-lg);
    color: var(--text-mute);
    font-variant-numeric: tabular-nums;
    /* v40 P2 清理:删 opacity: 0.7 — color 已是 var(--text-mute) 弱化对比,
       再叠 0.7 opacity 让 #6b6b75 透明度掉到 5.5:1→3.8:1,临界 WCAG AA。 */
    transition: color var(--t-fast) var(--ease);
}

.app-nav a.active .nav-count {
    color: var(--accent);
    opacity: 0.8;
}

/* ============== 侧栏"正在播放"指示器(v60+)==============
   音频当前曲目所属分类与侧栏某项匹配时,该项加 .is-now-playing —
   替换默认的 nav-idx 数字 / fav-recent 图标 / nested · 装饰为三柱波形。
   目的:用户在其他分类下查看列表时,一眼看出"现在播放的是哪个分类的歌"。
   - 显示条件:audio 有 src(有当前曲目),与是否正在播放无关。
   - 动画条件:body.is-playing(暂停/未播时波形静止,告知"当前曲目"但不打扰)。
   - 静止形态:三柱等高(0.55 scaleY),避免视觉突兀;播放时错时跳动。 */
.app-nav a .nav-play-wave {
    display: none;
}

/* 嵌套项的 ::before · 装饰在播放时也得让位,否则 · 与波形同槽叠加 */
.app-nav a.is-now-playing.is-nested .nav-idx::before {
    content: none;
}

/* 播放时隐藏原数字露出波形 — 仅"最近播放"(虚拟分类)不参与,经反馈确认:
   最近播放里的曲目实际属于其他真实分类(且点击会跳转到所有音乐),
   在该项上加波形仍会误导用户。收藏(FAVORITES_ID,v60.1+)现已允许 —
   收藏处的心形会替换成波形柱,与"用户正在收藏列表播放"的语义对齐。
   .is-favorites.is-now-playing .nav-fav-icon 单独处理心形 → 波形切换。 */
.app-nav a.is-now-playing .nav-idx-text {
    display: none;
}

/* v60.1+:收藏项播放中时心形图标隐藏,让位给三柱波形(由上面的 .is-now-playing
   .nav-play-wave 通用规则接管)。 */
.app-nav a.is-favorites.is-now-playing .nav-fav-icon {
    display: none;
}

.app-nav a.is-now-playing .nav-play-wave {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    height: 12px;
    /* 行内对齐中间 — 替换元素多为 baseline 文本,垂直居中更协调 */
    vertical-align: middle;
}

.app-nav a.is-now-playing .nav-play-wave > span {
    width: 2px;
    height: 100%;
    background: var(--accent);
    border-radius: 1px;
    transform-origin: center;
    /* 暂停时静态:三柱等高(短),从暂停→播放有过渡的视觉锚点 */
    transform: scaleY(0.55);
    animation: none;
}

/* 播放中:三柱错时 scaleY 跳动 — 镜像 .live-wave 的视觉语言,
   让用户一眼把"侧栏波形"与"底栏标题波形"联系起来 */
body.is-playing .app-nav a.is-now-playing .nav-play-wave > span {
    animation: navPlayWave 1s var(--ease-in-out) infinite;
}

body.is-playing .app-nav a.is-now-playing .nav-play-wave > span:nth-child(1) { animation-delay: 0s; }
body.is-playing .app-nav a.is-now-playing .nav-play-wave > span:nth-child(2) { animation-delay: 0.2s; }
body.is-playing .app-nav a.is-now-playing .nav-play-wave > span:nth-child(3) { animation-delay: 0.4s; }

@keyframes navPlayWave {
    0%, 100% { transform: scaleY(0.3); }
    50%      { transform: scaleY(1); }
}

/* 触摸减少动画偏好 — 静止显示三柱,与暂停态一致 */
@media (prefers-reduced-motion: reduce) {
    body.is-playing .app-nav a.is-now-playing .nav-play-wave > span {
        animation: none;
    }
}

/* 虚拟分类入口(收藏 / 最近播放):只有图标 + 标签,
   改为两列布局,左边距对齐标签位置 */
.app-nav a.is-favorites,
.app-nav a.is-recent {
    display: grid;
    grid-template-columns: 28px 1fr;
    gap: 10px;
    align-items: center;
    /* 用左侧 padding 抵消缺失的 nav-idx,让标签与其它项对齐 */
    padding-left: 18px;
}

/* ============== 侧栏折叠分组 ==============
   分类可折叠分组,默认折叠节省 200px 侧栏垂直空间。
   子项继承 .app-nav a 样式但加 .is-nested 做缩进,序号位留空用装饰小点代替。 */
.app-nav-group {
    margin-top: 4px;
}

.app-nav-group-toggle {
    /* 与 .app-nav a 用同样的 3 列 grid(28px / 1fr / auto),保证 caret 列与
       nav-idx 列同宽,label 起点对齐到普通 nav 的 label 起点 */
    display: grid;
    grid-template-columns: 28px 1fr auto;
    /* v55:gap 10 → 8 + padding 8 → 11,与 .app-nav a 节奏统一 —
       原 gap 10 让 "其他" label 起点偏右 2px(54 → 56),垂直 padding 8
       让行高 ~36 vs 普通 44,视觉上 "其他" 明显矮一截、对不齐。
       现在 label 起点 54px、行高 ~44px 与普通分类项严格对齐,
       保留字重/字号/字体的弱化差异作为分组标识的视觉差 */
    gap: 8px;
    align-items: center;
    width: 100%;
    padding: 11px 18px;
    /* 用 var(--mono) + 加宽字距让分组标题在中文标签里视觉跳出,
       弱化一点字号 + muted 颜色表达"辅助分组标识"而非主操作目标 */
    font-family: var(--mono);
    font-size: var(--fs-base);
    letter-spacing: 0.04em;
    color: var(--text-mute);
    background: transparent;
    border: none;
    border-left: 3px solid transparent;
    cursor: pointer;
    user-select: none;
    text-align: left;
    transition:
        color var(--t-fast) var(--ease),
        background var(--t-fast) var(--ease),
        border-color var(--t-fast) var(--ease);
}

.app-nav-group-toggle:hover {
    background: var(--bg-hover);
    color: var(--text);
}

.app-nav-group-toggle:focus-visible {
    outline: none;
    background: var(--accent-soft);
    color: var(--accent);
}

/* 折叠箭头:展开时旋转 90° 朝下,折叠时朝右(默认 ▸),
   旋转动画与全站一致用 var(--t-fast) var(--ease) */
.app-nav-group-caret {
    width: 14px;
    height: 14px;
    justify-self: center;
    transition: transform var(--t-fast) var(--ease);
}
.app-nav-group.is-collapsed .app-nav-group-caret {
    transform: rotate(0deg);
}
.app-nav-group:not(.is-collapsed) .app-nav-group-caret {
    transform: rotate(90deg);
}

/* 分组标题:ellipsis 防御超长 label 撑爆侧栏 */
.app-nav-group-label {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}

/* 折叠时整个 children 容器隐藏 — 用 display:none 而非 height:0,
   简单可靠且与全站其它折叠行为(主题菜单等)一致;
   内部子项的展开/折叠由 group 容器的 is-collapsed 单一来源控制。 */
.app-nav-group.is-collapsed .app-nav-group-children {
    display: none;
}

/* 分组子项:复用 .app-nav a 但加 .is-nested 做缩进,序号位用 · 装饰 */
.app-nav a.is-nested {
    padding-left: 36px; /* 18px 基础 + 18px 缩进,层级清晰 */
    font-size: var(--fs-md); /* 比顶层略小一档,弱化层级权重 */
}
.app-nav a.is-nested .nav-idx {
    color: var(--text-mute);
    opacity: 0.5;
}
.app-nav a.is-nested .nav-idx::before {
    content: '·';
    font-family: var(--mono);
    font-size: var(--fs-base);
}
/* 子项激活态左边强调线比顶层细,体现从属层级 */
.app-nav a.is-nested.active {
    border-left-width: 2px;
    padding-left: 37px; /* 补 1px 让文字与左边线视觉对齐 */
}

/* v52+:抽屉内底部"收起菜单"按钮。
   base 默认 hidden(抽屉模式仅 ≤720),≤720 块才显示并贴底。
   sidebar 已是 display:flex + flex-direction:column(base 块),
   nav 是 flex:1 + overflow-y:auto → 按钮加在 nav 之后给 flex-shrink:0 自动贴底。
   v68 P0 修复:完整样式(align-items / padding / border-top / font-size / min-height /
   svg 尺寸等)从 @media (max-width: 720px) 提到 base 块,让 ≤900 横屏(line 3844)/
   901-1024 横屏(line 4288)的 "display: flex" 能渲染完整视觉,不再只是未样式化的
   flex 容器(无 padding/border/font-size,看起来像占位 div)。
   display: none 留作本块最后一条声明,通过 source order 胜出 — 桌面默认隐藏;
   ≤720 / ≤900 横屏 / 901-1024 横屏 各自的 display: flex 重新覆盖。 */
.app-sidebar-close {
    align-items: center;
    justify-content: center;
    gap: 8px;
    width: 100%;
    min-height: 48px;
    padding: 12px 16px;
    margin: 0;
    flex-shrink: 0;
    background: transparent;
    border: none;
    border-top: 1px solid var(--border);
    color: var(--text);
    font: inherit;
    font-size: var(--fs-2xl);
    cursor: pointer;
    text-align: center;
    -webkit-tap-highlight-color: transparent;
    transition:
        background var(--t-fast) var(--ease),
        color var(--t-fast) var(--ease);
}
.app-sidebar-close:hover,
.app-sidebar-close:focus-visible {
    background: var(--bg-hover);
    color: var(--text-strong);
    outline: none;
}
.app-sidebar-close:active {
    background: var(--bg-active, var(--bg-hover));
}
.app-sidebar-close svg {
    width: 18px;
    height: 18px;
    flex-shrink: 0;
    transition: transform var(--t-fast) var(--ease);
}
.app-sidebar-close:hover svg {
    transform: translateX(-1px); /* 微向左 — 与 sidebar transform translateX(-100%) 收起方向一致,强化语义 */
}
.app-sidebar-close {
    display: none;
}

/* ============== 主区(竖向 grid) ============== */

.app-main {
    display: grid;
    /* 行高 = 对应元素高度 + 顶部/底部安全区(让 grid 自动让出安全区) */
    grid-template-rows:
        calc(var(--header-height) + var(--safe-top))
        minmax(0, 1fr)
        calc(var(--player-height) + var(--safe-bottom));
    min-height: 0;
    overflow: hidden;
    background: var(--bg);
}

/* v51:CSS Grid 子项默认 min-width:auto = 内容 min-content,
   会在 767-769 等窄边界把 .app-main 第 2 列(1fr)撑出主区
   (实测 playerBar.w=549.84 > main.w=547,溢出 2-3px)。
   三个直接子项统一置 0,让 header / list-area / player-bar
   严格受 1fr 约束,内部 nowrap 文本走 ellipsis 兜底。 */
.app-main > .app-header,
.app-main > .app-list-area,
.app-main > .app-player-bar {
    min-width: 0;
}

/* ============== 顶部信息条 ============== */

.app-header {
    /* 高度已含 safe-top,padding-top 再加一次会双重计算 — 改为 0,
       改用 padding-left/right 控制水平安全区,顶部由 flex align 处理 */
    height: calc(var(--header-height) + var(--safe-top));
    padding: 0 calc(20px + var(--safe-right)) 0 calc(20px + var(--safe-left));
    border-bottom: 1px solid var(--border);
    display: flex;
    /* 顶栏内元素垂直居中 — 之前 flex-end 把标题推到顶栏底部,留 ~40px 空白,
       视觉上标题"挂着",与底部按钮中心错位 10.4px。改 center 后标题居中,
       文字与按钮视觉重心对齐。≤720 块原本已用 center 覆盖,现在统一到 base。 */
    align-items: center;
    justify-content: space-between;
    background: var(--bg);
    flex-shrink: 0;
    gap: 16px;
    padding-bottom: 0;
}

/* 简化:不再用 "category / 中文歌曲" 的 breadcrumb 风格,
   只显示分类名,字号更大,更醒目 */
.app-crumb {
    min-width: 0;
    flex: 1;
    display: flex;
    align-items: baseline;
    gap: 12px;
}

.app-crumb-cat {
    font-family: var(--font);
    font-size: var(--fs-6xl);
    /* v41+:字重 700→600,与 body 500 拉开一档即可,不再"黑体"过重 */
    font-weight: var(--fw-semibold);
    color: var(--text);
    letter-spacing: var(--tracking-title);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    line-height: var(--lh-tight);
}

/* v42+:顶栏分类标题右侧的小字说明(灰、细、baseline 对齐标题),
   内容来自 categories.config.js 的 hint 字段;
   v69+:max-width 封顶改为 flex:1 1 0,与窄屏(≤720)统一 —
   "如果还有些空白区域,字尽量显示全一些;实在不行才截断"。
   hint 占满 title 之后所有剩余空间:字短自然右对齐+右侧空白,
   字卡到容器顶才截断;crumb 本身 flex:1 让出右侧 actions,
   无需再用百分比封顶(原 45% 在 1024 屏只能给 360px,
   实际可用 ~700px)。 */
.app-crumb-hint {
    display: inline-block;
    font-size: var(--fs-md);
    color: var(--text-soft);
    font-weight: var(--fw-regular);
    line-height: var(--lh-tight);
    letter-spacing: var(--tracking-tag);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    flex: 1 1 0;
    min-width: 0;
    /* baseline 对齐小字号会下沉,补一个 top 把视觉基线拉到与标题齐平 */
    position: relative;
    top: 2px;
}

.app-header-actions {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-shrink: 0;
}

.app-meta {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: var(--fs-xl);
    color: var(--text-soft);
    padding-right: 6px;
    height: 32px;
}

/* v38: 顶栏 ⋯ 折叠时 JS 加 [hidden] 属性 — 必须 base 强制 display:none,
   否则 .app-keyhint-btn 等有 display:inline-flex 规则且同 specificity 会胜出。
   之前只在 ≤480 块加 !important 是局部兜底,481-720 路径设 hidden 不生效。
   v52.7:加 .app-about-btn[hidden](d34b185 新增 info 按钮,与 .app-keyhint-btn 同型),
   ≤480 时 JS 会 hidden=true 让位 ⋯ 弹层的"关于"菜单项,class 选择器优先级不压 attribute 默认值。 */
.app-keyhint-btn[hidden],
.app-meta[hidden],
.app-overflow-btn[hidden],
.app-about-btn[hidden] { display: none !important; }

.app-meta-count {
    color: var(--text-soft);
    font-variant-numeric: tabular-nums;
    font-weight: var(--fw-semibold);
}

/* 状态点保留,但简化显示 — v41+:未播放态改为柔和空心圆,
   用 1.5px 边框 + 0% 填充,看起来像"等待被激活"的小环,
   比实心灰点更轻盈,也比之前 12% 灰晕更精致。
   v51.6:JS 仍写 textContent('已暂停'/'播放中'/'加载中')用于 aria-live 屏读器播报,
   视觉上靠 overflow:hidden + 负 text-indent 推出 8×8px 容器。
   v74:原 -100% 相对 8px 容器只有 -8px,"已暂停"仍露笔画成"⚏"残影,
   改 -999em 后文字整体移出,aria 播报不变。 */
.app-status-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: transparent;
    border: 1.5px solid var(--border-strong);
    flex-shrink: 0;
    overflow: hidden;
    /* v74 P0 修复:-100% 只等于 8px(自身宽度),"已暂停" 首字仍露出 2-3px
       笔画,顶栏"105 首"后出现"⚏"字符残影。改 -999em 才能整体推出容器;
       textContent 仍在无障碍树里,role=status 播报不受影响。 */
    text-indent: -999em;
    white-space: nowrap;
    transition:
        background var(--t-fast) var(--ease),
        border-color var(--t-fast) var(--ease),
        box-shadow var(--t-fast) var(--ease);
}

.app-status-dot.playing {
    background: var(--accent);
    border-color: transparent;
    box-shadow: 0 0 0 4px var(--accent-soft);
    animation: dotPulse 1.4s var(--ease-in-out) infinite;
}

@keyframes dotPulse {
    0%, 100% { opacity: 1; transform: scale(1); }
    50%      { opacity: 0.55; transform: scale(0.92); }
}

/* v57 清理:删 stale 注释"状态文字元素已删,v25"(line 860 旧位置) — 指向 [[music-hub-known-issues]] 的 wikilink 在当前文件上下文中是悬空引用,无信息价值 */

/* 快捷键入口按钮 — 更大更醒目 */
.app-keyhint-btn {
    width: 34px;
    height: 34px;
    border: 1px solid var(--border-strong);
    background: transparent;
    color: var(--text-soft);
    border-radius: var(--radius-sm);
    cursor: pointer;
    font-size: var(--fs-2xl);
    font-weight: var(--fw-medium);
    line-height: 1;
    padding: 0;
    /* P1 性能修复:transition: all 让浏览器为每个可能属性建 watcher,改为精确列表 */
    transition:
        color var(--t-fast) var(--ease),
        border-color var(--t-fast) var(--ease),
        background var(--t-fast) var(--ease),
        transform var(--t-fast) var(--ease);
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.app-keyhint-btn:hover {
    color: var(--accent);
    border-color: var(--accent);
    background: var(--accent-soft);
}

.app-keyhint-btn:active {
    transform: scale(0.95);
}

/* v41+:帮助按钮图标(SVG 替换原 "?" 文本,与 app-theme-btn SVG 风格一致) */
.app-keyhint-btn svg {
    width: 18px;
    height: 18px;
    stroke: currentColor;
    fill: none;
    flex-shrink: 0;
}

/* v26+:键盘焦点环显式声明,与 .app-overflow-btn 等其他按钮风格一致 */
.app-keyhint-btn:focus-visible {
    color: var(--accent);
    border-color: var(--accent);
    background: var(--accent-soft);
    outline: none;
}

/* ============== 关于入口按钮 — 与 keyhint-btn 同形(info 图标) ============== */
/* 形状/尺寸/hover/focus/active 完全镜像 .app-keyhint-btn,保持顶栏 5 个 34px 圆角
   方按钮视觉一致,只在内容上是问号 vs info 图标的区别。 */
.app-about-btn {
    width: 34px;
    height: 34px;
    border: 1px solid var(--border-strong);
    background: transparent;
    color: var(--text-soft);
    border-radius: var(--radius-sm);
    cursor: pointer;
    padding: 0;
    transition:
        color var(--t-fast) var(--ease),
        border-color var(--t-fast) var(--ease),
        background var(--t-fast) var(--ease),
        transform var(--t-fast) var(--ease);
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.app-about-btn:hover {
    color: var(--accent);
    border-color: var(--accent);
    background: var(--accent-soft);
}

.app-about-btn:active {
    transform: scale(0.95);
}

.app-about-btn svg {
    width: 18px;
    height: 18px;
    stroke: currentColor;
    fill: none;
    flex-shrink: 0;
}

.app-about-btn:focus-visible {
    color: var(--accent);
    border-color: var(--accent);
    background: var(--accent-soft);
    outline: none;
}

/* ============== 主题切换按钮(与 keyhint-btn 同形,色调用 currentColor) ============== */

.app-theme-btn {
    width: 34px;
    height: 34px;
    border: 1px solid var(--border-strong);
    background: transparent;
    color: var(--text-soft);
    border-radius: var(--radius-sm);
    cursor: pointer;
    padding: 0;
    /* P1 性能修复:精确过渡列表,省 watcher 初始化 */
    transition:
        color var(--t-fast) var(--ease),
        border-color var(--t-fast) var(--ease),
        background var(--t-fast) var(--ease),
        transform var(--t-fast) var(--ease);
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.app-theme-btn:hover {
    color: var(--accent);
    border-color: var(--accent);
    background: var(--accent-soft);
}

.app-theme-btn:active {
    transform: scale(0.95);
}

/* v26+:键盘焦点环显式声明,品牌色反馈 */
.app-theme-btn:focus-visible {
    color: var(--accent);
    border-color: var(--accent);
    background: var(--accent-soft);
    outline: none;
}

.app-theme-btn svg {
    width: 17px;
    height: 17px;
    stroke: currentColor;
    fill: none;
}

/* 当 .app-theme-btn 同时作为下拉触发器 — 放宽为横向 pill 形,容纳图标+文字+箭头 */
.app-theme-btn.theme-trigger {
    width: auto;
    height: 34px;
    padding: 0 12px;
    gap: 6px;
}
.app-theme-btn.theme-trigger .theme-trigger-icon {
    width: 16px;
    height: 16px;
    display: inline-flex;
}
.app-theme-btn.theme-trigger .theme-trigger-icon svg {
    width: 100%;
    height: 100%;
}

/* ============== 列表区(滚动) ============== */

.app-list-area {
    display: flex;
    flex-direction: column;
    overflow: hidden;
    min-height: 0;
    position: relative;
}

.app-list-area .m-music-list-wrap {
    flex: 1;
    min-height: 0;
    overflow-x: hidden;
    /* v26: auto → scroll,强制始终预留滚动条 gutter。
       Why:Chrome Win11 默认 overlay 滚动条,即使配 scrollbar-gutter: stable
       + ::-webkit-scrollbar width:26px,实际视觉仍为 17px 且仅滚动时短暂出现,
       不滚动时根本看不到。改为 scroll 后,Chrome 启用经典滚动条模式,
       26px 宽度生效、滚动条永远在那、gutter 恒定。 */
    overflow-y: scroll;
    background: var(--bg-elevated);
    /* 给自定义滚动条留出空间,避免 thumb 盖住列表最右一列文字 */
    scrollbar-gutter: stable;
    /* P2 性能修复:contain: layout style 把 1547 个 li 的 class toggle / display 变化
       隔离在 wrap 内,不会触发外部(顶栏、播放器、侧栏)layout 重算。
       搜索过滤切分类时,1547 li 的 is-hidden / is-current 切换只在 wrap 内重排,
       不污染整页 reflow。 */
    contain: layout style;
}

/* ============== 右侧主列表(歌曲列表)滚动条 ==============
   曲目列表是最高频的交互区,滚动条要"一眼能找到 + 容易抓住"。
   v25 设计:
   - 总宽 26px(thumb 撑满填充,webkit 默认行为),在 1440 宽屏上一眼可见
   - thumb 圆角矩形(--text-mute 深灰),与白底对比强
   - hover 时换成 --accent-line(品牌色),状态反馈明显
   - min-height 80px,列表短时 thumb 也不会缩成一根针
   - 颜色走主题变量(--text-mute / --accent-line / --accent),
     Light/Dark/Night/Amber/Ocean/Forest 主题自动适配
   v25 之前曾用 inset shadow 模拟"气孔"(4px 透明 + 18px 实色),
   但视觉上 thumb 只有 18px 偏细,且与其他 3 处滚动条风格不统一。
   现在 4 处滚动条共用"实色 thumb + 圆角 + 颜色过渡"同一套语言。
   v26+:26px 在 ≤320 视口占 8% 挤压列表,按断点阶梯缩到 10px,
   阶梯:≥1440 26 / 1025-1439 22 / 721-1024 20 / ≤720 18 / ≤480 16
   / ≤380 14 / ≤360 12 / ≤320 10。单调递减,大断点 ≥ 小断点。
   touch 设备 (hover:none) 收窄到 12,Android Chrome 触摸模式不再过粗。
   scrollbar-gutter: stable 已在 line 575 声明,这里不重复。 */
.app-list-area .m-music-list-wrap::-webkit-scrollbar {
    /* 总宽 26px,thumb 直接撑满填充(webkit 默认行为),
       让 26px 全部是实色 thumb,视觉上粗壮、易抓 */
    width: 26px !important;
    height: 26px !important;
}

/* v57 修复:原 v22 块(行 1078-1086)在下方 line 1115+ 被 .app-list-area .m-music-list-wrap
   + scrollbar-width: thick !important 完全覆盖,等价于 dead code。
   v62+:content-visibility: auto 让屏外 li 跳过 layout/paint — 1547 项长列表时
   视口外 ~1400 个 li 仍会被浏览器计算 layout 与 paint,造成滚动卡顿。
   auto 让浏览器对屏外元素视作 contain-intrinsic-size 给出的占位高度,
   直到滚动到视口附近才真正渲染。contain-intrinsic-size 与 li padding+content
   高度一致(≈44px),避免滚动条"突跳"。
   Safari 18+/Chrome 85+/Edge 85+ 支持,Firefox 125+ 支持。早期浏览器降级为常规渲染,
   不影响功能,只是慢一些。*/
.app-list-area .m-music-list-wrap li {
    content-visibility: auto;
    contain-intrinsic-size: auto 44px;
}
.app-list-area .m-music-list-wrap::-webkit-scrollbar-track {
    background: transparent;
}
.app-list-area .m-music-list-wrap::-webkit-scrollbar-thumb {
    /* v25 重构:去掉 v18 的 inset shadow 气孔(4+18+4),改为 26px 纯实色 thumb。
       Why:气孔设计虽然 hover 时不消失,但 thumb 视觉只有 18px,在白底上偏细;
       与侧栏/最近列表/全局的纯实色风格不统一,看着像 4 套不同设计。
       现在 4 处滚动条共用"实色 thumb + 圆角 + 颜色过渡"同一套语言 */
    background: var(--text-mute);
    border-radius: 13px;
    /* 列表短时 thumb 也至少有 80px 高,容易抓住 */
    min-height: 80px;
    transition: background var(--t-base) var(--ease);
}
.app-list-area .m-music-list-wrap::-webkit-scrollbar-thumb:hover {
    /* hover 时颜色加深到 accent-line,与 hover 收藏按钮等强调色一致 */
    background: var(--accent-line);
}
.app-list-area .m-music-list-wrap::-webkit-scrollbar-thumb:active {
    background: var(--accent);
}

/* Firefox 兼容:`thick` 让 Firefox 也显示粗滚动条,与 webkit 26px 视觉对齐。
   系统默认 `auto` 只有 ~10px,过细难抓。
   加 !important 是因为 smusic.css:88-91 有"全选 scrollbar-width: thin"规则,
   Chrome 实测会忽略我们的特异性(0,0,1,1)按"thin"(10px)渲染,
   因此必须 !important 强制 thick。
   v26+:scrollbar-gutter: stable 已在基础块(line 575)声明,这里不重复。 */
.app-list-area .m-music-list-wrap {
    scrollbar-width: thick !important;
    scrollbar-color: var(--text-mute) transparent !important;
}

/* ============== 底部播放器 ============== */

.app-player-bar {
    /* 高度已包含安全区,不需要再加 padding-bottom — 之前双重计算会让底部多出空隙 */
    height: calc(var(--player-height) + var(--safe-bottom));
    border-top: 1px solid var(--border);
    background: var(--bg-elevated);
    flex-shrink: 0;
    position: relative;
    box-sizing: border-box;
}

/* 底部播放器顶部分割线(更明显的层次) */
.app-player-bar::before {
    content: "";
    position: absolute;
    top: -1px;
    left: 0;
    right: 0;
    height: 1px;
    background: var(--border);
    pointer-events: none;
}

/* 底部播放栏内层:为 iOS home indicator 留出空间。
   .app-player-bar 高度已含 safe-bottom,这里再用 padding-bottom
   把内容(按钮、文本)推到安全区以上,避免被 home indicator 盖住。
   v30+: 再 +10px 呼吸空间,避免进度条贴底栏边缘 —— 单行式布局
   (默认 ≥1440 / 1025-1439) 此前仅 13-15px,视觉过紧。
   2-row 布局(≤1024)的 padding 简写在各自媒体查询里覆盖此值,
   不会被影响。 */
.app-player-bar .m-music-play-wrap {
    padding-bottom: calc(var(--safe-bottom) + 10px);
}

/* v30:3 mode 按钮的横向包装,放在 .player-right 最前 */
.app-player-bar .player-modes {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    flex-shrink: 0;
}

/* v30:player-right 现在是 [modes] [volume] [fullscreen] 横排 */
.app-player-bar .player-right {
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
}

/* ============== 底部播放器:全屏切换按钮 ============== */

.player-fullscreen-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    /* v30:在 flex 容器 .player-right 内由 gap 控制间距,清掉旧 margin-left */
    margin-left: 0;
    background: transparent;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    color: var(--text-soft);
    cursor: pointer;
    /* P1 性能修复:精确过渡列表,省 watcher 初始化 */
    transition:
        color var(--t-fast) var(--ease),
        border-color var(--t-fast) var(--ease),
        background var(--t-fast) var(--ease),
        transform var(--t-fast) var(--ease);
    flex-shrink: 0;
    padding: 0;
}

.player-fullscreen-btn:hover {
    color: var(--accent);
    border-color: var(--accent);
    background: var(--accent-soft);
}

.player-fullscreen-btn:focus-visible {
    color: var(--accent);
    border-color: var(--accent);
    background: var(--accent-soft);
    outline: none;
}

.player-fullscreen-btn:active {
    transform: scale(0.94);
}

.player-fullscreen-btn svg {
    width: 16px;
    height: 16px;
    fill: currentColor;
}

.player-fullscreen-btn.active {
    color: var(--accent);
    border-color: var(--accent);
    background: var(--accent-soft);
}

/* ============== 底部播放器:收藏按钮(切换当前播放曲目收藏状态) ==============
   视觉风格复用 .player-fullscreen-btn(36×36 描边方按钮,统一播放器右侧节奏),
   心形 svg 复用 .li-fav-btn 的 fill 语义:默认 stroke 描边,已收藏 .is-favorite
   切换到 fill: currentColor + accent 主色。
   位置:.player-right 内 [volume] [fav] [fullscreen],体积小但视觉明显。
   v68+:smusic.enhance.js bindPlayerFavButtons 挂 click + 同步 .is-favorite。 */
.player-fav-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    background: transparent;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    color: var(--text-soft);
    cursor: pointer;
    padding: 0;
    flex-shrink: 0;
    transition:
        color var(--t-fast) var(--ease),
        border-color var(--t-fast) var(--ease),
        background var(--t-fast) var(--ease),
        transform var(--t-fast) var(--ease);
}
.player-fav-btn:hover {
    color: var(--accent);
    border-color: var(--accent);
    background: var(--accent-soft);
}
.player-fav-btn:focus-visible {
    color: var(--accent);
    border-color: var(--accent);
    background: var(--accent-soft);
    outline: none;
}
.player-fav-btn:active {
    transform: scale(0.94);
}
.player-fav-btn svg {
    width: 18px;
    height: 18px;
    stroke: currentColor;
    stroke-width: 1.8;
    fill: none;
    stroke-linejoin: round;
    stroke-linecap: round;
    transition: fill var(--t-fast) var(--ease);
}
.player-fav-btn.is-favorite {
    color: var(--accent);
    border-color: var(--accent);
    background: var(--accent-soft);
}
.player-fav-btn.is-favorite svg {
    fill: currentColor;
}

/* 弹跳反馈:不依赖 translateY(新按钮无居中位移),仅用 scale 在原位弹。
   与 favorites.css .li-fav-btn.is-bouncing 同名 class,JS 同时驱动两个按钮组的弹跳。 */
@keyframes favBtnBounce {
    0%   { transform: scale(1); }
    30%  { transform: scale(1.28); }
    60%  { transform: scale(0.92); }
    100% { transform: scale(1); }
}
.player-fav-btn.is-bouncing,
.fs-fav-btn.is-bouncing {
    animation: favBtnBounce 320ms cubic-bezier(0.34, 1.56, 0.64, 1);
}

/* 全屏播放器收藏按钮 — 继承 .fs-ctrl 全部基础样式(52×52 圆形描边),
   仅在 .is-favorite 态切到 accent 主色 + svg fill 切到 currentColor。
   与 .fs-ctrl.active 同语义:已收藏 = 当前激活态。
   心形 svg 默认 stroke 描边(stroke-width 由 .fs-ctrl svg 继承并由本规则覆盖为 1.8)。
   v74 P0 修复:选择器补 .fs-ctrl 前缀,(0,0,2,1) 压过 line 2316 的
   `.fs-ctrl svg { fill: currentColor }`(同属性、定义在本行之后)。
   原 fill:none 被后者覆盖,未收藏时全屏心形也是实心 — 与 .is-favorite 态
   完全同色,收藏状态反馈丢失(底栏 player-fav-btn 不受影响,一直是描边)。
   .fs-fav-btn.is-favorite svg (0,0,2,1) 与本行同特异性且在后,收藏态实心仍胜出。 */
.fs-ctrl.fs-fav-btn svg {
    stroke: currentColor;
    stroke-width: 1.8;
    fill: none;
    stroke-linejoin: round;
    stroke-linecap: round;
    transition: fill var(--t-fast) var(--ease);
}
.fs-fav-btn.is-favorite {
    color: var(--accent);
    border-color: var(--accent);
    background: var(--accent-soft);
}
.fs-fav-btn.is-favorite svg {
    fill: currentColor;
}

/* ============== 顶栏 ⋯ 折叠按钮 + 弹层 ==============
   默认桌面隐藏;只在 ≤480 显示。弹层用 fixed 定位贴在按钮下方,
   与 .fav-modal-card 风格一致(白底、16px 圆角、24/64 阴影)。 */

.app-overflow-btn {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    border: 1px solid var(--border);
    background: var(--bg-elevated);
    color: var(--text-soft);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    transition:
        color var(--t-fast) var(--ease),
        background var(--t-fast) var(--ease),
        border-color var(--t-fast) var(--ease),
        transform var(--t-fast) var(--ease);
    -webkit-tap-highlight-color: transparent;
    flex-shrink: 0;
    position: relative;
}
.app-overflow-btn svg {
    width: 18px;
    height: 18px;
    fill: currentColor;
    /* SVG 用 fill 不用 stroke,但 :hover 颜色继承 currentColor */
}
.app-overflow-btn:hover,
.app-overflow-btn:focus-visible {
    color: var(--accent);
    border-color: var(--accent);
    background: var(--accent-soft);
    outline: none;
}
.app-overflow-btn:active { transform: scale(0.92); }
/* v40 P2 清理:删 .app-overflow-btn[hidden] { display: none } 重复规则 —
   line 501 已有 [hidden] + display: none !important,且覆盖 5 个 button 一并处理;
   此处无 !important 在 specificity 0,0,1,1 vs line 501 的 0,0,1,2 中败北,永不命中。 */
/* v51 清理:删 .app-overflow-btn.is-open 死规则 — JS 用 aria-expanded 表达菜单状态,
   grep 确认 app.js/favorites.js/smusic.enhance.js 无 .is-open 引用。
   active 视觉(色/边框/底色)由现有 [aria-expanded="true"] 规则触发。 */
/* v68 P0 修复:之前 [aria-expanded="true"] 规则只是注释里承诺,从未在 CSS 中落地。
   menu 打开时 (line 855 JS 设 aria-expanded="true") 按钮没有 active 视觉反馈,
   用户看不出 ⋯ 按钮处于"打开"状态。补规则:打开时显示同 :hover 的强调色 + 背景,
   让用户清楚"再点一次会关闭"。specificity (0,0,2,0) = 同 :hover 规则,但 aria-expanded
   是属性选择器,在属性变化时单独计算。
   specificity 比对:
     :hover / :focus-visible 选择器是伪类 (0,0,2,0)
     [aria-expanded="true"] 选择器是属性 (0,0,2,0)
   两者特异性相同。aria-expanded 规则放在 :hover 后,source order 胜出,
   所以打开态 = 打开视觉(色/边框/底色),hover 在打开态上不重新覆盖 → 用户点开后按钮保持打开色。 */
.app-overflow-btn[aria-expanded="true"] {
    color: var(--accent);
    border-color: var(--accent);
    background: var(--accent-soft);
}

.app-overflow-menu {
    /* 用 fixed 而非 absolute,因为顶栏可能在 sticky / 滚动布局下被裁 */
    position: fixed;
    /* 顶栏 56px 高 + 安全区;用 calc 显式避开 safe-top */
    top: calc(var(--header-height, 56px) + var(--safe-top, 0px) + 6px);
    right: calc(8px + var(--safe-right, 0px));
    min-width: 220px;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    /* v29: 16px → var(--r-xl),弹窗圆角 token 化 */
    border-radius: var(--r-xl);
    box-shadow:
        0 12px 32px -8px rgba(0, 0, 0, 0.18),
        0 4px 12px -4px rgba(0, 0, 0, 0.08);
    padding: 6px;
    /* v47:从 200 升到 500(等于 var(--z-overlay))— 抽屉打开时 .app-mobile-backdrop
       (z=99) 盖住 .app-overflow-btn(z=auto),按钮被点不到。z=500 高于抽屉(z=100)
       但低于 fullscreen(z=1000)。仍依赖抽屉打开时 JS 隐藏按钮(open() line 371)。*/
    /* v51 P1-11:z-index 字面 500 → var(--z-overlay),与 app.css:160 token 定义对齐 */
    z-index: var(--z-overlay);
    /* 入场动画 */
    animation: overflowIn var(--t-base) var(--ease);
    transform-origin: top right;
}
.app-overflow-menu[hidden] { display: none; }

@keyframes overflowIn {
    from { opacity: 0; transform: scale(0.96) translateY(-4px); }
    to   { opacity: 1; transform: scale(1)    translateY(0); }
}

.app-overflow-item {
    width: 100%;
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 0 12px;
    min-height: 44px;       /* iOS HIG 触摸命中 */
    border: none;
    background: transparent;
    color: var(--text);
    font-family: inherit;
    font-size: var(--fs-lg);
    font-weight: var(--fw-medium);
    cursor: pointer;
    border-radius: 10px;
    text-align: left;
    transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
    -webkit-tap-highlight-color: transparent;
}
.app-overflow-item svg {
    width: 18px;
    height: 18px;
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
    flex-shrink: 0;
    color: var(--text-soft);
    transition: color var(--t-fast) var(--ease);
}
.app-overflow-item:hover {
    background: var(--bg-hover);
    color: var(--text);
}
/* v68 P1 a11y 修复:之前 :focus-visible 与 :hover 完全同样式(都 outline:none),
   键盘用户只能看到 hover 背景,无法区分焦点项 — WCAG 2.4.7 Focus Visible 不达标。
   修复:焦点态保留背景色 + 文本色反馈(同 hover),但额外加 2px accent outline
   内嵌在元素内 (outline-offset: -2px),与 v29 全局 9 个交互按钮的焦点环风格统一。 */
.app-overflow-item:focus-visible {
    background: var(--bg-hover);
    color: var(--text);
    outline: 2px solid var(--accent);
    outline-offset: -2px;
}
.app-overflow-item:hover svg {
    color: var(--accent);
}
.app-overflow-item:focus-visible svg { color: var(--accent); }
.app-overflow-item:active {
    background: var(--bg-active);
}
.app-overflow-item > span {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.app-overflow-meta {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 12px;
    margin-top: 4px;
    border-top: 1px solid var(--border);
    font-size: var(--fs-sm);
    color: var(--text-mute);
    font-variant-numeric: tabular-nums;
    /* v18+:视觉降权 — 与上面 4 个 44px 高的 .app-overflow-item 相比,
       footer 只占 28px 左右,opacity 0.85 让信息可见但次要。
       不删节点(#app-meta-count-mobile / #app-status-dot-mobile),
       保留 index.html 437-452 行的 JS MutationObserver 同步逻辑。 */
    min-height: 28px;
    opacity: 0.85;
}
.app-overflow-meta .app-status-dot {
    /* 与顶栏 .app-status-dot 同款,只是放在弹层里。
       v37:P3-6 7 → 8 — 与 base 一致(box-shadow ring 3px 同步),
       弹层里视觉上更协调,不显小一档。
       v51.6:继承 base 的 overflow:hidden + text-indent 防文字溢出
       (虽然 #app-status-dot-mobile 是 aria-hidden=true 不播报,
       但视觉上 #app-status-dot 的 textContent 会同步到这里)。 */
    width: 8px;
    height: 8px;
    box-shadow: 0 0 0 3px rgba(107, 107, 117, 0.12);
    overflow: hidden;
    /* v74:同 base,-100% 推不出 8px 容器,改 -999em */
    text-indent: -999em;
    white-space: nowrap;
}
.app-overflow-meta .app-status-dot.playing {
    box-shadow: 0 0 0 3px var(--accent-soft);
}

/* 封面可点击反馈 */
.player-left .u-cover {
    cursor: pointer;
    transition: transform var(--t-base) var(--ease), border-color var(--t-base) var(--ease);
}

/* 底部封面旋转:v34 round 2 起封面用 background-image(不再用 img 显示),
   所以旋转动画必须作用到 .u-cover 本身(background 是它的一部分)。
   img/fallback 元素保持 opacity:0 不动。
   .u-cover.play / .paused 由 SMusic.play()/pause() 自动切换 */
.u-cover.play,
.u-cover.paused {
    animation: spin 20s linear infinite;
    /* v34: 显式声明旋转中心 = 元素几何中心,避免子像素 rounding 偏移 */
    transform-origin: center;
}
/* v70+:低功耗模式 — navigator.deviceMemory < 4 且 hardwareConcurrency ≤ 4 时,
   关闭封面旋转动画。中低端 GPU 设备 compositor 持续工作省下来,功耗下降。
   视觉仅失去旋转,封面图照常显示,影响极小。
   prefers-reduced-motion 已有独立处理(用户主动偏好);此处针对自动检测的低端设备。 */
body[data-low-power] .u-cover.play,
body[data-low-power] .u-cover.paused,
body[data-low-power] .app-fullscreen-cover.spin {
    animation: none;
}
.u-cover.paused {
    animation-play-state: paused;
}
/* img 加载失败被 onerror 移除后,整张封面会"停转" — fallback
   SVG 是水平镜像对称的双八分音符,旋转中心=SVG 几何中心 */
.u-cover:not(:has(img)) .u-cover-fallback {
    animation: spin 20s linear infinite;
    transform-origin: center;
}
.u-cover.paused:not(:has(img)) .u-cover-fallback {
    animation-play-state: paused;
}

/* v57 清理:删 stale 注释"已经在 .app-fullscreen-cover.spin img 上有 24s 旋转,无需重复"(line 1410 旧位置) — 该条 block 没有对应规则,只是空注释占位 */

/* ============ 唱片风封面 ============
   核心思路:把 .u-cover 从"方块"改成"圆形唱片"。
   - .u-cover div 是唱片的圆形外圈,旋转时静止不动
   - .u-cover-img 是唱片的"贴纸/图像",播放时绕中心旋转
   简洁视觉:只剩"会转的圆盘",没有中心点装饰。 */

/* 底部小封面 — 圆形唱片 */
/* v51 P2-2:删左半死选择器 .player-bar — DOM 里实际只有 .app-player-bar,
   .player-bar 不存在任何匹配,保留右半 .app-player-bar 即可。 */
.app-player-bar .u-cover {
    border-radius: 50%;
    /* v24: 移除 base .u-cover 的 1px 边框(只保留 inset shadow + 外阴影),
       避免出现"边框+内阴影"双层暗环 */
    border-color: transparent;
    /* 唱片外圈:1px 暗环 + 浮起阴影 */
    box-shadow:
        0 2px 8px rgba(0, 0, 0, 0.18),
        inset 0 0 0 1px rgba(0, 0, 0, 0.14);
}

.player-left .u-cover .u-cover-img {
    border-radius: 50%;
    /* v18+ 旋转动画已挂在父 .u-cover 上(背景图 --album-art),img 是 fallback 兜底,
       永远不会被旋转 — 删 will-change 避免为不可见的 img 各占一个 GPU 合成层
       (1546 li × 1 层 ≈ 数十 MB GPU 显存压力)。 */
}

/* hover:圆形放大效果尚可,但加上阴影增强更有"被拿起"的质感 */
.player-left .u-cover:hover {
    box-shadow:
        0 4px 14px rgba(0, 0, 0, 0.22),
        inset 0 0 0 1px rgba(0, 0, 0, 0.14);
}

/* 全屏大封面 — 圆形唱片 */
.app-fullscreen-cover {
    border-radius: 50%;
    /* 大唱片:更厚的暗环 + 更深阴影,匹配大尺寸 */
    box-shadow:
        0 16px 40px -10px rgba(0, 0, 0, 0.18),
        inset 0 0 0 2px rgba(0, 0, 0, 0.14);
}

/* 全屏封面 img 也不旋转 — 父 .app-fullscreen-cover 走 --album-art + transform。
   删 will-change 避免为静态 img 预留独立合成层。*/

.player-left .u-cover:active {
    transform: scale(0.98);
}

/* 封面 hover:半透明遮罩 + 4 角全屏图标,提示"点击进入全屏视图"。
   仅在具备悬停能力的设备(鼠标)上启用 — 触摸设备点击会触发 :hover
   粘滞状态,用 :active 更可靠 */
@media (hover: hover) {
    .player-left .u-cover:hover {
        border-color: var(--text-mute);
        transform: scale(1.05);
    }

    .app-player-bar .u-cover::after {
        content: "";
        position: absolute;
        inset: 0;
        background: rgba(28, 28, 32, 0.42);
        border-radius: inherit;
        opacity: 0;
        transition: opacity var(--t-base) var(--ease);
        pointer-events: none;
        /* z-index: 2 — 高于 img,hover 时遮罩盖住图 */
        z-index: var(--z-thumb);
    }

    .app-player-bar .u-cover::before {
        content: "";
        position: absolute;
        inset: 0;
        margin: auto;
        width: 38%;
        height: 38%;
        background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'><polyline points='4 8 4 4 8 4'/><polyline points='16 4 20 4 20 8'/><polyline points='20 16 20 20 16 20'/><polyline points='8 20 4 20 4 16'/></svg>");
        background-repeat: no-repeat;
        background-position: center;
        background-size: contain;
        filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.35));
        opacity: 0;
        transform: scale(0.85);
        transition: opacity var(--t-base) var(--ease), transform var(--t-base) var(--ease);
        pointer-events: none;
        z-index: var(--z-thumb);
    }

    .player-left .u-cover:hover::after,
    .player-left .u-cover:hover::before {
        opacity: 1;
    }

    .player-left .u-cover:hover::before {
        transform: scale(1);
    }
}

/* 底部封面:用 background-image 显示原 icon.jpg,精确裁切显示西瓜主体居中。

   v34 round 3:不形变(用户反馈"不要形变")。background-size: 150% auto —
   宽度 150% 容器宽,高度 auto 保持原图 1:1 比例不变(关键!原图 1:1,不能拉伸)。
   background-position: 50% 39.2% — 经过多轮微调(原计算值 68.2%)后的视觉精确居中值。
   让原图主体(西瓜中心 = 图片 y=180/500 ≈ 36%)在容器中视觉对齐中央。

   原始计算:设背景图高度 = 150% * 64 = 96(超出 32);
   设 background-position-y = Yp%,则图片 y=0 in image 在容器 y = Yp%*64 - 50%*96;
   容器 y=32 显示原图 y=180 需要:
   (32 - (Yp%*64 - 50%*96)) / (96/500) = 180
   → Yp% ≈ 44.5%,但 39.2% 视觉更佳(西瓜主体视觉中心略高于像素中心)

   var(--album-art, url('../icon.jpg')) — 默认显示 icon.jpg(精确裁切);
   SMusic 加载专辑图后 JS 写入 --album-art 变量,自动切换。
   !important — 压过 SMusic element.style.backgroundImage 注入的雪碧图。
   有专辑图(.has-album-art)时切换为 cover 模式(保持比例 + 完整覆盖)。 */
.app-player-bar .u-cover {
    background: var(--album-art, url('../icon.jpg')) no-repeat 50% 1.4% / 108% auto, var(--bg-active) !important;
}

.app-player-bar .u-cover.has-album-art {
    background-size: cover !important;
    background-position: center !important;
}

/* v34+: img 默认透明 — 让 background 上的封面图可见。
   SMusic 加载真实专辑图后,JS 把专辑图 URL 写入 --album-art,
   background 自动切换到专辑图(无需 img 显示 — background 自己显示专辑图) */
.u-cover .u-cover-img,
.app-fullscreen-cover img {
    opacity: 0;
}

/* 让 SVG fallback 浮在 background 之上(默认隐藏 — 避免和 background 同时显示)
   只有 img 完全不存在时才显示 */
.app-player-bar .u-cover-fallback {
    width: 56%;
    height: 56%;
    opacity: 0;
    z-index: var(--z-track);
    border-radius: 50%;
}

/* 封面 fallback 音符图标(无 img 时显示) */
.u-cover-fallback {
    position: absolute;
    inset: 0;
    margin: auto;
    width: 50%;
    height: 50%;
    fill: var(--text-mute);
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--t-base) var(--ease);
}

/* img 完全不存在(onerror 移除)时,显示 fallback 双八分音符 */
.u-cover:not(:has(img)) .u-cover-fallback {
    opacity: 0.55;
}

/* 全屏封面 fallback — v34+: 默认隐藏(避免和 background 同时显示),
   只有 cover 完全没有 img 时才显示(双层 fallback:background 兜底 + fallback 进一步提示) */
/* v36: fallback 尺寸响应式 — 原 96×96 写死在 base,
   但 cover 实际尺寸是 380 (base) / 320 (≤1024) / 280 (≤900) / 240 (≤768) /
   min(240,70vw) (≤720) / min(220,56dvh) (≤480) / min(200,54dvh) (≤380) /
   min(180,52dvh) (≤360) / min(160,50dvh) (≤320) 等多档。
   旧值在 ≤320 下 fallback 占封面 60%,≥1440 仅占 22%,视觉比例失衡。
   clamp(48, 25%, 96): 最小 48(≤320 仍清晰可辨),最大 96(≥1440 仍小巧),
   中间按父级 25% 自动缩放。 */
.app-fullscreen-cover-fallback {
    position: absolute;
    inset: 0;
    margin: auto;
    /* v37:P3-7 clamp(48,25%,96) → clamp(32,18%,80) — 25% 在 ≤320 落 48px 下限(30%),
       在 ≥1440 落 96px 上限(22%);比例 22%→30% 大屏→小屏递增,与"大屏 fallback 更小"
       视觉逻辑反。改 18% 让比例从 ≤320 20% → ≥1440 18% 单调微缩。 */
    width: clamp(32px, 18%, 80px);
    height: clamp(32px, 18%, 80px);
    fill: var(--text-mute);
    opacity: 0;
    pointer-events: none;
}

.app-fullscreen-cover:not(:has(img)) .app-fullscreen-cover-fallback {
    opacity: 0.4;
}

/* 当前曲目微弱指示:
   - li.current 的左边框已在 smusic.css 中表达底部列表的"正在播放"
   - 底部 strong 标题前再加竖条会和已有序号 [1] 视觉重复,
     且挤压标题的左 padding 让 ellipsis 触发位置提前,故移除。
   - 改在标题右侧用 live-wave(仅播放时显示)做"播放中"的瞬时信号。 */

/* 底部播放器"波形"指示器(播放中) — 浮在标题右侧外侧。
   关键约束:SMusic 用 innerHTML 重建 .u-music-title 内部内容,
   所以 live-wave 必须放在 .u-music-title 平级的位置,
   且不能依赖 .player-left 的 overflow:hidden(会让 right:-18px 失效)。
   这里把 .player-left 的 overflow 改成 visible,strong 自身的
   ellipsis 仍能保证标题不撑破布局。 */
.app-player-bar .player-left {
    overflow: visible;
}

.app-player-bar .player-text {
    position: relative;
    min-width: 0;
    overflow: visible;
}

.app-player-bar .player-text .live-wave {
    position: absolute;
    right: -16px;
    top: 50%;
    transform: translateY(-50%);
    display: none;
    gap: 2px;
    align-items: center;
    height: 12px;
}

.app-player-bar .player-text .live-wave span {
    width: 2px;
    height: 100%;
    background: var(--accent);
    border-radius: 1px;
    transform-origin: center;
    animation: liveWave 1s var(--ease-in-out) infinite;
}

.app-player-bar .player-text .live-wave span:nth-child(1) { animation-delay: 0s; }
.app-player-bar .player-text .live-wave span:nth-child(2) { animation-delay: 0.2s; }
.app-player-bar .player-text .live-wave span:nth-child(3) { animation-delay: 0.4s; }

@keyframes liveWave {
    0%, 100% { transform: scaleY(0.3); }
    50%      { transform: scaleY(1); }
}

body.is-playing .app-player-bar .player-text .live-wave { display: inline-flex; }

/* ============== 底部播放器进度条:thumb 默认可见 + hover 放大 ============== */
/* 默认显示一个小圆点表达"当前位置",hover 时放大便于瞄准
   v60+:删 .u-process { cursor: pointer } — 与 smusic.css:387-409 重复(已在那边定义),
   纯死代码。 */

.u-process .progress-thumb {
    position: absolute;
    top: 50%;
    width: 14px;
    height: 14px;
    background: var(--accent);
    border-radius: 50%;
    /* v47+:scale(0.95) → scale(1) — 始终满尺寸,视觉更确定。
       旧 0.95 → hover 1 的 0.5px 差几乎不可见,反馈感缺失。
       新方案 hover/dragging scale(1.15) → 14 → 16.1px,2px 差距明显。
       P1 性能修复保留:只 transition transform(compositor-only)。
       box-shadow 变化(2px→3px 内圈 + 装饰阴影 blur 3px→10px)是 paint 属性,
       transition box-shadow 会让浏览器在 180ms 内每帧重画 14px 圆 + 2 层阴影。
       14px 小元素的 box-shadow 微差视觉上可忽略,即时切换即可。 */
    transform: translate(-50%, -50%) scale(1);
    pointer-events: none;
    box-shadow:
        0 0 0 2px var(--bg-elevated),
        0 1px 3px var(--accent-glow-low);
    transition: transform var(--t-fast) var(--ease);
    z-index: var(--z-thumb);
    opacity: 1;
}

.u-process:hover .progress-thumb,
.u-process.dragging .progress-thumb {
    /* v47+:scale 1 → 1.15,14 → 16.1px,明显的交互反馈 */
    transform: translate(-50%, -50%) scale(1.15);
    box-shadow:
        0 0 0 3px var(--bg-elevated),
        0 2px 10px var(--accent-glow-high);
    opacity: 1;
}

/* ============== 全屏 Now Playing 视图 ============== */

.app-fullscreen {
    position: fixed;
    inset: 0;
    /* v23: iOS Safari 地址栏塌陷 → 用 dvh;同时把 safe-top / safe-bottom 加进 padding,
       让 cover / 内容都能避让刘海与 home indicator,不再被刘海屏遮住 */
    height: var(--app-height-dvh);
    padding-top: var(--safe-top);
    padding-bottom: var(--safe-bottom);
    padding-left: var(--safe-left);
    padding-right: var(--safe-right);
    box-sizing: border-box;
    background:
        radial-gradient(ellipse 60% 50% at 50% 35%, color-mix(in srgb, var(--accent) 4%, transparent) 0%, transparent 70%),
        radial-gradient(ellipse 80% 60% at 50% 100%, color-mix(in srgb, var(--accent) 3%, transparent) 0%, transparent 70%),
        var(--bg);
    /* v51 P1-11:z-index 字面 1000 → var(--z-popup),与 app.css:161 token 定义对齐 */
    z-index: var(--z-popup);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    opacity: 0;
    visibility: hidden;
    transition:
        opacity var(--t-slow) var(--ease),
        visibility var(--t-slow) var(--ease);
    overflow: hidden;
}

body.fullscreen-mode .app-fullscreen {
    opacity: 1;
    visibility: visible;
}

/* v57 修复:fullscreen-mode 过渡原本分裂在这两个互斥选择器,JS 切 class 时只有
   当前匹配一侧有 transition 定义,另一侧的状态变化会"瞬变"。统一上提到底层
   .app 基础规则(行 325 附近)声明 transition,这里只保留状态值。 */
body.fullscreen-mode .app {
    opacity: 0;
    pointer-events: none;
}

body:not(.fullscreen-mode) .app {
    opacity: 1;
}

/* 全屏顶部全局进度条(macOS Music 风格) */
.app-fullscreen-topbar {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 2px;
    background: transparent;
    overflow: hidden;
    z-index: var(--z-track);
}

.app-fullscreen-topbar::before {
    content: "";
    position: absolute;
    inset: 0;
    /* v40 P1 修复:浅色主题(rgba(255,255,255,0.04) 白 4%)轨道在浅米/浅蓝/浅绿/白
       bg 上几乎不可见。改用 var(--text) 8% 跨主题自适应:浅色主题 bg 上显示
       为深灰半透轨道,深色主题 --text 已是浅色自动显示为浅色轨道。
       dark/night 主题由 theme.css:62/349 覆盖到 0.10,提升对比度。 */
    background: color-mix(in srgb, var(--text) 8%, transparent);
}

.app-fullscreen-topbar .topbar-progress {
    position: absolute;
    top: 0;
    left: 0;
    height: 100%;
    width: 100%;
    background: var(--accent);
    /* P0 性能修复:用 transform: scaleX 替代 width。
       width 触发 layout reflow,scaleX 只走 compositor。
       timeupdate 高频(~250ms) 触发时省下大量 reflow。
       transform-origin: left 让进度从左侧开始填充(与 width 0%→N% 视觉一致)。 */
    transform-origin: left;
    transform: scaleX(0);
    /* v29: 0.25s → var(--t-slow),全屏进度条拖动后回弹时长,接近 240ms 阶梯 */
    transition: transform var(--t-slow) linear;
}

.app-fullscreen-content {
    max-width: 520px;
    width: 92%;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 28px;
    /* v23: flex 子项可缩,防止内容总高超出视口后被外层 overflow:hidden 裁切;
       配合各断点的 cover/gap/字号 dvh 缩放,确保 ≤480/≤320/横屏矮屏整页可见 */
    min-height: 0;
    flex-shrink: 1;
    animation: fsFadeIn 0.36s var(--ease);
}

/* .app-fullscreen-info 必须显式 width: 100%,否则 align-items: center 会让它按
   内容收缩,长标题(尤其是古典分类 30+ 中文字)会撑破 max-width 520px */
.app-fullscreen-info {
    width: 100%;
    min-width: 0;
}

/* v36: 起点 translateY(0) — 只走 opacity 淡入,避免开屏 0.36s 内
   内容相对视觉中心向下偏移最多 14px 导致"上浮"动画。
   原 from { transform: translateY(14px) } 在慢动作下可见抖动,
   多数 OS 不感知但 a11y 用户开启 prefers-reduced-motion 时
   仍能看到 translateY(0)→translateY(14px) 的反向位移。 */
@keyframes fsFadeIn {
    from {
        opacity: 0;
        transform: translateY(0);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

.app-fullscreen-art {
    position: relative;
    margin-top: 18px;
}

.app-fullscreen-cover {
    width: 380px;
    height: 380px;
    /* v34 round 3:与底部 .u-cover 同步,保持 1:1 比例不形变。
       background-size: 100% auto — 高度 auto 保持原图 1:1 比例(关键)。
       background-position: 50% 50% — 精确视觉居中(默认 icon.jpg 已精准裁切)。
       var(--album-art, url('../icon.jpg')) — 默认 icon.jpg(精确裁切),
       SMusic 加载专辑图后 JS 写入 --album-art 切换专辑图(.has-album-art 时 cover)。 */
    background: var(--album-art, url('../icon.jpg')) no-repeat 50% 1.4% / 108% auto, var(--bg-active);
    /* 圆形唱片:border-radius 由 line ~907 "全屏大封面 — 圆形唱片" 块统一设 50% */
    overflow: hidden;
    /* v51 P2-4:删此处 box-shadow — 主定义在 .app-fullscreen-cover 块 line 1241
       (border-radius + 暗环 + 内阴影)。此处原 box-shadow 重复了 0 16px 40px 的暗环,
       唯一额外是 accent 光晕,但 line 1241 已有 inset 内阴影做层次,删去避免双重阴影累积。
       box-shadow 单一定义在 line 1241,保持视觉一致。 */
    /* v34 P0 静态清理:此 transition 永不触发。
       JS/hover 都不改 .app-fullscreen-cover 的 transform,动画是 .app-fullscreen-cover::after / img 在跑。
       删除以免误导后续维护者以为这里有动效可调。 */
    position: relative;
}

.app-fullscreen-cover.has-album-art {
    background-size: cover !important;
    background-position: center !important;
}

/* 封面周围极弱环形光晕 — v34: 与唱片圆形一致,改为 50%,
   避免方形圆角在唱片旋转时出现"方框包圆盘"的违和感 */
.app-fullscreen-art::before {
    content: "";
    position: absolute;
    inset: -20px;
    border-radius: 50%;
    background: radial-gradient(circle, color-mix(in srgb, var(--accent) 4%, transparent) 0%, transparent 60%);
    z-index: var(--z-mask);
    pointer-events: none;
}

.app-fullscreen-cover img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    /* v34 P0 静态清理:此选择器块原声明 will-change: transform 与 L998
       .player-left .u-cover .u-cover-img 块的 will-change 同等;重复声明
       浏览器会忽略,删除以省 specificity cost。
       注:.app-fullscreen-cover img 当前 opacity:0 (line 1108 块),只承载
       background fallback 时的 onerror 清理与无障碍 alt;旋转动画走
       .app-fullscreen-cover.spin 本体 (line 1445),不需要子元素 will-change。 */
}

/* 全屏封面播放时缓慢旋转(暂停时停在当前角度) */
@keyframes fsCoverSpin {
    to { transform: rotate(360deg); }
}

/* v24+: 全屏封面播放时,唱片缓慢旋转(fsCoverSpin)。
   v24 早期版本曾尝试用 ::after 伪元素做"呼吸"(box-shadow scale + opacity),
   但 ::after 无背景/边框/图片,opacity/transform 动画对不可见元素 0 视觉输出
   却仍跑每帧 compositor,纯浪费。v34 删除整个 fsCoverBreath + ::after + .spin::after,
   呼吸效果由 .app-fullscreen-cover 本体的 box-shadow(静态)提供"撑起"感,
   视觉真实且零额外开销。
   注释保留追溯历史,后续维护者请勿复活 ::after 方案。 */

/* v34 round 2 起封面用 background-image,旋转必须作用到 .app-fullscreen-cover 本身。
   img 元素 opacity:0 不显示,无法靠它转。 */
.app-fullscreen-cover.spin {
    animation: fsCoverSpin 24s linear infinite;
    transform-origin: center;
}

.app-fullscreen-meta {
    text-align: center;
    width: 100%;
    padding: 0 16px;
}

.app-fullscreen-tag {
    font-family: var(--font);
    font-size: var(--fs-base);
    color: var(--accent);
    text-transform: uppercase;
    letter-spacing: var(--tracking-caps);
    margin-bottom: 14px;
    font-weight: var(--fw-semibold);
    display: inline-flex;
    align-items: center;
    gap: 8px;
    /* v24: 给 tag 加 pill 背景,让分类标签更突出,与标题视觉分层 */
    padding: 4px 12px;
    background: var(--accent-soft);
    border-radius: 999px;
    border: 1px solid var(--accent-line);
}

/* v37: 删 ::before/::after display:none — 无 content 声明的死规则,
   pill 化时未同步清掉,留着无意义 */

.app-fullscreen-title {
    font-size: var(--fs-11xl);
    font-weight: var(--fw-semibold);
    color: var(--text);
    margin: 4px 0 8px;
    overflow: hidden;
    text-overflow: ellipsis;
    /* 默认桌面单行 + 字号大;窄屏改为 -webkit-line-clamp:2 允许换行,见 ≤720 媒体块 */
    white-space: nowrap;
    max-width: 100%;
    letter-spacing: var(--tracking-display);
    /* v37:line-height 1.2 → var(--lh-tight) token 化 */
    line-height: var(--lh-tight);
    /* v24: padding-top 抵消大字号字面顶部空间,让标题与 pill tag 视觉距离一致 */
}

.app-fullscreen-artist {
    font-size: var(--fs-3xl);
    color: var(--text-soft);
    font-weight: var(--fw-regular);
    margin: 0 0 4px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* v46 字号审计 P2 Fix 9:14→12(原 fs-lg → fs-base)— 修复桌面全屏 time 偏大。
       原 14px 在桌面 1920+ 全屏视图下视觉偏大,time 区反而抢镜;
       改 12px 与 body 字号一致更协调。
       各断点 time-cur/time-total 单独覆盖保留不动 (≤720/≤480 --fs-base / ≤320 --fs-sm)。 */
.app-fullscreen-progress {
    display: grid;
    grid-template-columns: auto 1fr auto;
    gap: 14px;
    align-items: center;
    width: 100%;
    font-family: var(--mono);
    font-size: var(--fs-base);
    color: var(--text-soft);
    font-variant-numeric: tabular-nums;
    padding: 0 8px;
}

.app-fullscreen-time-cur,
.app-fullscreen-time-total {
    min-width: 50px;
    font-weight: var(--fw-medium);
}

.app-fullscreen-time-cur { text-align: left; }
.app-fullscreen-time-total { text-align: right; }

.app-fullscreen-process {
    height: 14px;
    background: transparent;
    border-radius: 7px;
    position: relative;
    cursor: pointer;
    overflow: visible;
    /* v24: 触摸命中由 hover:none 块的 ::before 外扩;此层负责视觉 */
}

.app-fullscreen-process::before {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    top: 50%;
    height: 5px;
    transform: translateY(-50%);
    background: var(--border-strong);
    border-radius: 3px;
    transition: background var(--t-fast) var(--ease);
}

.app-fullscreen-process:hover::before {
    background: var(--text-mute);
}

.app-fullscreen-process .cur {
    display: block;
    width: 100%;
    height: 5px;
    background: var(--accent);
    border-radius: 3px;
    position: absolute;
    left: 0;
    top: 50%;
    /* P0 性能修复:scaleX 只走 compositor,timeupdate 高频(~250ms) 触发时
       省下大量 layout reflow。transform-origin: left 与 width 0→100% 视觉一致。 */
    transform-origin: left;
    transform: scaleX(0) translateY(-50%);
    /* v29: 0.1s → var(--t-fast),与全站时长阶梯统一 */
    transition: transform var(--t-fast) linear;
    z-index: var(--z-track);
}

.app-fullscreen-process:hover .cur {
    background: var(--accent);
}

/* 全屏进度条 thumb(圆点) — 浅色背景下用 accent 色 */
.app-fullscreen-process .fs-thumb {
    position: absolute;
    top: 50%;
    /* v47 P0: left 改为 var(--thumb-x),由 JS 在 timeupdate 中写入。
       left 是 layout-affecting 属性,timeupdate ~4Hz × 3 分钟 = 720 次 layout 触发;
       CSS var 走 compositor-only,彻底消除 thrashing。thumb 自身 transform 仍用于
       水平居中 (-50%) + hover/dragging 缩放,职责分离互不干扰。 */
    left: var(--thumb-x, 0%);
    width: 12px;
    height: 12px;
    background: var(--accent);
    border-radius: 50%;
    transform: translate(-50%, -50%) scale(0);
    pointer-events: none;
    /* v24:ring 用 --bg(全屏背景)而非 --bg-elevated,
       与 .fs-vol-thumb 视觉一致;旧值会在全屏浅灰背景下出现一圈突兀白边 */
    box-shadow: 0 0 0 3px var(--bg), 0 2px 6px var(--accent-glow-high);
    transition: transform var(--t-fast) var(--ease);
    z-index: var(--z-thumb);
}

.app-fullscreen-process:hover .fs-thumb,
.app-fullscreen-process.dragging .fs-thumb {
    transform: translate(-50%, -50%) scale(1);
}

.app-fullscreen-controls {
    display: flex;
    align-items: center;
    /* v21 修复:justify-content: center — 容器宽 100%(继承自 .app-fullscreen-info stretch),
       原默认 flex-start 让 5 个控件左贴边缘,视觉不平衡。 */
    justify-content: center;
    gap: 16px;
    margin-top: 4px;
}

/* v62+:全屏 3 mode 按钮组 — 与底部 .app-player-bar .player-modes 同形
   (inline-flex + 4px gap),放在 prev/play/next 左侧。margin-right 留出
   与 fs-prev 的呼吸空间,避免 6 个按钮视觉过密。flex-shrink:0 与底部
   .player-modes 一致,极窄屏时组内 3 按钮一起缩窄而不是被外层 flex 压扁。 */
.fs-modes {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    margin-right: 4px;
    flex-shrink: 0;
}

.fs-ctrl {
    width: 52px;
    height: 52px;
    border-radius: 50%;
    border: 1px solid var(--border-strong);
    background: var(--bg-elevated);
    color: var(--text-soft);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* P1 性能修复:精确过渡列表;fs-ctrl 还会改 transform(:hover translateY/-1px) */
    transition:
        color var(--t-fast) var(--ease),
        border-color var(--t-fast) var(--ease),
        background var(--t-fast) var(--ease),
        transform var(--t-fast) var(--ease),
        box-shadow var(--t-fast) var(--ease);
    padding: 0;
    /* v68+:flex-shrink:0 — ≥1440 宽屏下,info 列宽(≈416px)装不下 7 个控件(≈460px),
       默认 flex-shrink:1 会把按钮从 52×52 圆压成 42×52 竖椭圆,失真。
       与 .fs-modes (line 2250) flex-shrink:0 模式对称,
       3 mode 子按钮也继承 0,保证 mode 组/prev/play/next/fav 全部不被外层 flex 压扁 */
    flex-shrink: 0;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.04);
}

.fs-ctrl:hover {
    color: var(--accent);
    border-color: var(--accent);
    background: var(--accent-soft);
    transform: translateY(-1px);
}

.fs-ctrl:active {
    /* 加 inset shadow 让"按下"有物理质感 — 主播放按钮 .fs-play 已有 inner glow,
       其它圆形控制按钮(上一曲/下一曲/随机/循环)按下时仅缩放偏平。
       同步把 background 切到 --bg-hover,让视觉按压感更明显。 */
    transform: scale(0.94);
    background: var(--bg-hover);
    box-shadow:
        inset 0 2px 4px rgba(0, 0, 0, 0.12),
        0 2px 4px rgba(0, 0, 0, 0.04);
}

.fs-ctrl.active {
    color: var(--accent);
    border-color: var(--accent);
    background: var(--accent-soft);
}
/* v62+:fs-repeat 移除后,列表循环 / 随机 / 单曲循环各有独立按钮,
   每个按钮的 active 态用统一的 .fs-ctrl.active 表示"当前选中",
   无需再用 .repeat-list 弱化区分。 */

.fs-ctrl svg {
    width: 22px;
    height: 22px;
    fill: currentColor;
}

.fs-play {
    width: 72px;
    height: 72px;
    background: var(--accent);
    /* v38: 默认状态也用 var(--bg-elevated),与 :hover 状态对齐。
       原 color:#ffffff 在 dark/night 主题下对比度仅 2.72/1.85:1,
       不达 WCAG 1.4.11 (≥3:1);var(--bg-elevated) 各主题均 ≥5.7:1。
       浅色主题 bg-elevated=#ffffff,与原值等价,视觉不变。 */
    color: var(--bg-elevated);
    border-color: var(--accent);
    box-shadow: 0 8px 20px -4px var(--accent-glow-high);
    position: relative;
    overflow: hidden;
}

/* 主播放按钮 inner glow:模拟立体感(顶部高光 + 底部内阴影),
   让"按下"更有物理感而不只是平面色块 */
.fs-play::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    background:
        radial-gradient(circle at 30% 18%, rgba(255, 255, 255, 0.32) 0%, transparent 55%),
        radial-gradient(circle at 50% 110%, rgba(0, 0, 0, 0.18) 0%, transparent 60%);
    pointer-events: none;
    transition: opacity var(--t-fast) var(--ease);
}

.fs-play:hover {
    /* v29 round 4+: 修复 P1 WCAG 1.4.11 对比度问题。
       原 `background: var(--accent-hover); color: #ffffff;` 在 dark/night/sepia 主题下,
       --accent-hover 太浅 (#c4b5fd / #67e8f9 / #c27148) vs 白 icon 对比度 1.32/1.25/2.66,
       hover 时 icon 几乎看不见。
       修复: 保持 background: var(--accent) 不变(各主题 accent 都 ≥5.4 对比度),
       color 改为 var(--bg-elevated),深色主题下 icon 变深色(对比度 5.4-8.5);
       浅色主题 bg-elevated=#ffffff 等于原色,视觉不变;
       同时加 box-shadow ring + 立体阴影给 hover 光晕反馈
       (scale 物理反馈外加视觉锚点,跨主题一致性)。 */
    background: var(--accent);
    color: var(--bg-elevated);
    border-color: var(--accent);
    transform: scale(1.04);
    /* 4px ring (accent-soft) — 提供 hover 光晕反馈。
       注:原版还叠了一层 var(--shadow-popup) 立体投影,但 --shadow-popup
       本身是 5 段 length + color 的完整 shadow,再叠加到 ring 后会变 7 段 length,
       违反 box-shadow `<length>{2,4}` 规范,整条声明被浏览器丢弃 → hover 时
       完全没有 ring + elevation 阴影,只剩 scale 物理反馈。
       v34 修复:只用 ring(单层 4 length),立体感交给 scale 物理反馈撑场。 */
    box-shadow: 0 0 0 4px var(--accent-soft);
}

.fs-play:active {
    transform: scale(0.96);
}

.fs-play svg {
    width: 28px;
    height: 28px;
}

.fs-play .fs-icon-play { display: none; }
.fs-play .fs-icon-pause { display: block; }
.fs-play.paused .fs-icon-play { display: block; }
.fs-play.paused .fs-icon-pause { display: none; }

.app-fullscreen-volume {
    display: flex;
    align-items: center;
    /* v21 修复:与 .app-fullscreen-controls 同源问题 — 默认 flex-start 让音量条偏左,
       加 justify-content: center 与 controls 对齐,统一视觉重心。 */
    justify-content: center;
    gap: 12px;
    margin-top: 4px;
}

.fs-vol-icon {
    width: 36px;
    height: 36px;
    border: none;
    background: transparent;
    color: var(--text-soft);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    transition: color var(--t-fast) var(--ease), transform var(--t-fast) var(--ease);
}

.fs-vol-icon:hover {
    color: var(--accent);
}

.fs-vol-icon:active {
    transform: scale(0.9);
}

.fs-vol-icon svg {
    width: 20px;
    height: 20px;
    fill: currentColor;
}

.fs-vol-icon .fs-vol-icon-off { display: none; }
.fs-vol-icon.muted .fs-vol-icon-on { display: none; }
.fs-vol-icon.muted .fs-vol-icon-off { display: block; }

.fs-vol-track {
    width: 120px;
    height: 14px;
    background: transparent;
    border-radius: 7px;
    position: relative;
    cursor: pointer;
    overflow: visible;
}

.fs-vol-track::before {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    top: 50%;
    height: 5px;
    transform: translateY(-50%);
    background: var(--border-strong);
    border-radius: 3px;
    transition: background var(--t-fast) var(--ease);
}

.fs-vol-track:hover::before {
    background: var(--text-mute);
}

.fs-vol-track .fs-vol-cur {
    display: block;
    /* v24: 全屏音量条改为横向填充(从左到右)。
       原 height 控制 + bottom:0 让人误以为是垂直 bar(从下往上长),
       实际横向条更符合 Spotify/Apple Music 的直觉(也是用户习惯) */
    width: 100%;
    height: 5px;
    background: var(--accent);
    border-radius: 3px;
    position: absolute;
    top: 50%;
    left: 0;
    /* P0 性能修复:scaleX 只走 compositor,音量调节拖动时省下 layout reflow */
    transform-origin: left;
    transform: scaleX(0.5) translateY(-50%);
    transition: transform var(--t-fast) linear, background var(--t-fast) var(--ease);
    z-index: var(--z-track);
}

.fs-vol-track:hover .fs-vol-cur {
    background: var(--accent);
}

/* v36: 横向音量条 thumb — 默认 scale(1) 常驻显示。
   音量是**状态指示器**(用户在调节后需随时看到当前位置),不像进度条 thumb
   是导航器(只在拖/悬停时显示即可)。
   v24 早期版本沿用进度条 thumb 行为(scale(0) 默认),触屏设备无 hover 状态,
   拖完 thumb 立即消失,用户失去视觉锚点。
   改为常驻 scale(1),保留 hover/dragging 切换(防止过渡到 1 的过程卡顿)。
   v47 P0: left 由 var(--vol-thumb-x) 提供,JS 端不再写 left,改 setProperty。
   镜像 .volume-process::after (app.css:2603-2619) 已有的 --vol-thumb-x 模式。 */
.fs-vol-track .fs-vol-thumb {
    position: absolute;
    top: 50%;
    width: 12px;
    height: 12px;
    background: var(--accent);
    border-radius: 50%;
    transform: translate(-50%, -50%) scale(1);
    pointer-events: none;
    box-shadow:
        0 0 0 3px var(--bg),
        0 1px 3px var(--accent-glow-med);
    transition: transform var(--t-fast) var(--ease);
    z-index: var(--z-thumb);
    left: var(--vol-thumb-x, 50%);
}

/* 拖动时短暂放大视觉反馈 — thumb 已常驻,只是 scale 略放大突显交互 */
.fs-vol-track.dragging .fs-vol-thumb {
    transform: translate(-50%, -50%) scale(1.15);
}

.app-fullscreen-close {
    position: absolute;
    /* v23: 默认也用 safe-area 兜底,避免 iPhone 14 Pro 等刘海屏把 × 按钮盖在刘海里。
       ≤480 / ≤320 块的 calc(14px + var(--safe-top)) / calc(10px + var(--safe-top)) 已显式锚定 */
    top: calc(22px + var(--safe-top));
    right: calc(22px + var(--safe-right));
    width: 38px;
    height: 38px;
    border: 1px solid var(--border);
    background: var(--bg-elevated);
    color: var(--text-soft);
    border-radius: 50%;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* P1 性能修复:精确过渡列表 */
    transition:
        color var(--t-fast) var(--ease),
        border-color var(--t-fast) var(--ease),
        background var(--t-fast) var(--ease),
        transform var(--t-fast) var(--ease),
        box-shadow var(--t-fast) var(--ease);
    padding: 0;
    z-index: var(--z-thumb);
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
}

/* v37: × 改用 SVG 路径(stroke 18×18)替代 Unicode 字符。
   原 Unicode × 在 macOS / Windows / Android 字体下几何中心偏移
   ±2-4px,font-weight: 300 在 MS YaHei 等无 Light 档的 CJK 字体上
   fallback 到 400 + faux-light 滤镜,反而模糊不清。
   SVG stroke-linecap:round 描边在所有 DPI 下一致。 */
.app-fullscreen-close svg {
    width: 18px;
    height: 18px;
    stroke-linecap: round;
    fill: none;
}

.app-fullscreen-close:hover {
    color: var(--accent);
    border-color: var(--accent);
    background: var(--accent-soft);
    box-shadow: 0 4px 10px var(--accent-glow-low);
}

.app-fullscreen-close:active {
    transform: scale(0.92);
    /* v27+:加背景反馈,与 .app-overflow-btn 等其他按钮按下视觉一致 */
    background: var(--accent-soft);
    color: var(--accent);
}

/* 全屏视图底部"now playing" 标签已移除 —
   "按 ESC 收起"信息已在 .app-fullscreen-close.title 中表达,
   "按空格播放/暂停"已是 cheatsheet 快捷键之一,不需重复 */

/* ============== 响应式:小屏侧边栏变顶部抽屉 ============== */

/* 中型桌面/笔记本 (1025–1439):弥补 1024→1440 之间无专属样式的空白带。
   v52+:侧栏 200 继承 iPad 块(@media 721-1439),与 ≤1024 统一;列表保留卡片化(与 ≤1024 一致)
   直至 ≥1440 才去卡片化,全屏封面也阶梯(380 本块 → 440 ≥1440)。
   v37:340 → 380 让 1440 边界跳变从 +100 缩到 +60。
   播放器不碰 — 1025+ 宽度足够 root 默认 3 行布局。 */
@media (min-width: 1025px) and (max-width: 1439px) {
    :root {
        /* v52:让 iPad 块(721-1439)的 --sidebar-width 200 接管,删本块 220;
           阶梯简化为:≤720 抽屉 / 横屏 220 / 721-1439 200 / ≥1440 280。
           本块保留 player-height / header-height / cover 等其他覆盖。 */
        /* v51:112→116,与 ≥1440 块(--player-height 116)对齐。
           修复 w1024 (154) → w1100 (112) PH 反向阶梯 -42px(v50 审计 #1)。
           阶梯修正:≤720 136 → 721-1024 154 → 1025-1439 116 → ≥1440 116
           单调不回头。1025+ 已是单行布局,116px 与 ≥1440 一致即可,
           进一步满足 v42 注释"用户反馈过于矮,加 12px 给控件/封面更舒展"的方向。
           iPad landscape (1025) vs iPad portrait (1024) 转屏仍有 ~38px 跳变(116 vs 154),
           但属于"双行→单行"自然过渡,接受(注:无法消除因为双行布局本身就需要 154+)。 */
        --player-height: 116px;
        /* 显式锚定 60,避免 :root 默认值被未来其他块无意覆盖
           (≥1440 跳 68,本块需稳在 60 防止更大跳变) */
        --header-height: 60px;
    }

    /* 中段桌面 cover 平滑过渡:1024 是 44,≥1440 是默认 64,
       此处 56 避免 iPad landscape 转屏时 cover 从 44 跳 64。 */
    .app-player-bar .u-cover { width: 56px; height: 56px; }

    .app-list-area {
        padding: 4px 8px;
        min-height: 0;
    }
    .app-list-area .m-music-list-wrap {
        border-radius: var(--radius);
        border: 1px solid var(--border);
    }

    .app-fullscreen-cover { width: 380px; height: 380px; }

    /* v42+:1025-1439 中段桌面显式锚定底部 padding 16。
       该块无 .m-music-play-wrap 覆盖,默认走 line 802 calc(safe-bottom + 10px) = 10px,
       实测进度条距底仅 16.5px,贴底感明显(用户反馈)。
       16px 与 ≥1440 旧值 18px / 1025-1439 双行块(line 2173) 阶梯对齐,
       单行布局 align-items:center 不动,只增加底部内边距给 progress 留呼吸空间。
       --player-height 100 不变,内容区从 90→84,row 内容 61 不动,
       progress 实测下移 ~6px 满足用户诉求。
       v53+:padding 0 20px 16px → 10px 20px 24px —
       原值只设 bottom 没设 top,导致 player-center (flex column + space-between)
       内容 ctrl-box 紧贴 wrap 顶部 (y=0),封面 (playerLeft) 却居中于 99px 内容区
       (y≈12.5),造成封面与控件视觉错位 — 控件看上去"偏上/偏高",与 ≥1440 的
       10/24 对称分布不一致。改为 10/24 后,内容区 115-10-24=81,player-center
       content 65 → 余 16 → space-between 让 ctrl y=10、progress y=64(再+17=81
       贴底),封面距顶 17、playerRight 居中,整体上下视觉对称,与 ≥1440 块对齐。 */
    .app-player-bar .m-music-play-wrap {
        padding: 10px 20px 24px;
    }

    /* v26+:主列表滚动条阶梯 26→22,thumb min-height 80→70。
       大屏 26px 占视口 1.5%,中屏占 2%,阶梯后视觉更协调 */
    .app-list-area .m-music-list-wrap::-webkit-scrollbar {
        width: 22px !important;
        height: 22px !important;
    }
    .app-list-area .m-music-list-wrap::-webkit-scrollbar-thumb {
        border-radius: 11px;
        min-height: 70px;
    }
}

/* iPad portrait / 小平板 + 中型桌面/笔记本 (721–1439):侧栏 200,列表加内边距 + 卡片化。
   v52.2:范围扩到 1439(原 1024) → 中段桌面 1025-1439 也用 200,统一窄/中档。
   220 → 200 — 用户反馈"窄屏侧栏仍偏宽",iPad 768 portrait 主区 768-200=568,
   列表项 ~525px 视觉无感。同块内同步:nav-label 字号 16→14(本块局部,等同 ≤480 strong,
   不破 v46 nav-idx/count 阶梯)+ nav-idx 列宽 28→22(本块局部,腾 6px 给 label),
   保证 7 字中文"分类:XX" 在 label 区段 ~87px 下仍 fit (7×13.3=93px 临界,
   实测若仍截断则追加 white-space:normal 兜底 wrap)。阶梯 ≥1440 280 解耦。
   v49 升 220 注释:v46 升 nav-idx/count 12→14 后 label 区段仅 79px,200 装不下"分类:XX"。
   v52.7:把 iPad 专属规则(--player-height 154 / cover 44 / 双行布局 / fullscreen-cover 280 /
   滚动条 20 / 控件缩放 / live-wave 隐藏)拆到下方 @media (721-1024) 块,
   修复 10db007 误把原 721-1024 改成 721-1439 时连带应用到 1025-1439 中段桌面,
   导致 1100-1439 视口 player-height 116→154 双行布局 / cover 56→44 / 滚动条 22→20 等回归。
   本块仅保留通用规则(sidebar / header / nav / brand / list-area 卡片化 / 字号),
   中段桌面 1025-1439 继续走上方 1025-1439 块的 player-height 116 / cover 56 / fullscreen 380 / 滚动条 22。 */
@media (min-width: 721px) and (max-width: 1439px) {
    :root { --sidebar-width: 200px; }

    .app-header {
        gap: 12px;
        /* v37: 顺位修正 — TRBL 简写,左侧(L)=safe-left,右侧(R)=safe-right;
           原版 safe-left/safe-right 互换,与 line 442/3234/3339 冲突。语义修正。 */
        padding: 0 calc(16px + var(--safe-right)) 0 calc(16px + var(--safe-left));
    }

    /* v46 字号审计 P2 Fix 7:19→20(原 fs-5xl → fs-6xl)— 修复 721-1024 块反向阶梯。
       原 19px 比 base 20px 还小,iPad portrait 200px 侧栏理应保留 base 字号,
       与 base / 1025-1439 块 (--fs-6xl 20px) 单调一致。
       阶梯修正:base 20 → 721-1024 20 → ≥1440 22(单调递增,大屏字号更大)。 */
    .app-crumb-cat { font-size: var(--fs-6xl); }

    .app-header-actions { gap: 10px; }

    /* v52:配合 sidebar 220→200,nav-label 字号 16→14(iPad 局部,与 ≤480 strong 同档),
       nav-idx 列宽 28→22(本块局部,腾 6px),保证 7 字中文"分类:XX" 装得下。
       nav-count 字号 14 保持 v46 阶梯不动。 */
    .app-nav a {
        padding-left: 14px;
        padding-right: 14px;
        grid-template-columns: 22px 1fr auto;
    }
    .app-nav a .nav-label {
        font-size: var(--fs-lg);
    }

    /* 同步覆盖分组开关(如"其他")的 grid / padding,
       否则它仍走 28px 列宽 + 18px 左右内边距,label 起点比普通项偏右 10px(44 vs 54),
       视觉上"其他"明显不对齐。caret 14px 在 22px 列里仍 justify-self:center 居中。 */
    .app-nav-group-toggle {
        padding-left: 14px;
        padding-right: 14px;
        grid-template-columns: 22px 1fr auto;
    }

    /* v27+:sidebar 从 240 缩到 200,"GUAN 音乐空间" 自然宽 ~132px
       加上 icon 28 + gap 10 + head padding 40 = 210,溢出 10px → 中文部分
       拆到第二行("GUAN 音乐空" / "间"),破相。
       把字号 18→15、自然宽降到 ~110px,白空间 nowrap + flex-shrink:0
       防止文字被 flex 收缩成多行。 */
    .app-brand-name {
        /* v43+:16→18,与 base brand(20) 保持 +2 递进;v44+:list strong 14px,
           仍 brand 18 > list 14 (+4px),满足 v27 1024 侧栏 200px 容纳"GUAN 音乐空间"不换行 */
        font-size: var(--fs-4xl);
        white-space: nowrap;
        flex-shrink: 0;
    }

    /* 列表区加左右内边距,让卡片浮起来 — 平板宽屏下纯白贴边的列表显呆板 */
    .app-list-area {
        padding: 4px 8px;
        min-height: 0;
    }
    .app-list-area .m-music-list-wrap {
        border-radius: var(--radius);
        border: 1px solid var(--border);
    }
}

/* v52.7 P0 修复:iPad portrait (721–1024) 专属规则 — 从上方 721-1439 块拆出。
   10db007 把 @media (721-1024) 改成 (721-1439) 合并 sidebar 时,意外把以下规则也扩到 1439,
   导致 1025-1439 中段桌面视口看到:
     --player-height 116→154 (双行布局,+38px,挤压主列表)
     .app-player-bar .u-cover 56→44 (底部封面缩小 12px)
     .app-fullscreen-cover 380→280 (全屏封面缩小 100px)
     .app-fullscreen-title/content 走 iPad 字号 + max-width:480
     滚动条 22→20 (与原阶梯意图相反)
   拆出后 source order:1025-1439(中段桌面) → 721-1439(通用) → 721-1024(本块),
   本块只在 721-1024 视口生效(超出 max-width 1024),与 1025-1439 视口无冲突。
   阶梯单调:player-height ≤720 136 → 721-1024 154 → 1025-1439 116 → ≥1440 116,
   cover / fullscreen-cover / 滚动条同步单调。 */
@media (min-width: 721px) and (max-width: 1024px) {
    /* fullscreen 视图 iPad 缩放:中段桌面 1025-1439 用 380,base ≥1440 用默认大尺寸。
       iPad 1024 用 280,避免封面溢出窄屏。 */
    .app-fullscreen-cover { width: 280px; height: 280px; }
    .app-fullscreen-title { font-size: var(--fs-10xl); }
    .app-fullscreen-content { max-width: 480px; }

    /* iPad 1024 视口滚动条 20px(占视口 2%);中段桌面 22,大屏 26 阶梯(参考 1025-1439 块)。
       thumb min-height 70 与 1025-1439 块对齐,保证滚动手感一致。 */
    .app-list-area .m-music-list-wrap::-webkit-scrollbar {
        width: 20px !important;
        height: 20px !important;
    }
    .app-list-area .m-music-list-wrap::-webkit-scrollbar-thumb {
        border-radius: 10px;
        min-height: 70px;
    }

    /* v18: 桌面缩放 150-200% 时(等效 CSS 像素 800-1024)底部播放器标题被裁,
       侧栏分类名(按时间顺序)也被裁。三条对策:
       1) 切到 2 列 grid(覆盖 smusic.css 默认的 3 列 180/1fr/180,让 right 列 auto);
       2) 隐藏 mode-list/random/single 三个按钮 — 节省 ~120px 给标题;
          用户仍可全屏视图切换模式,或用快捷键 F1/F2/F3。
       3) 隐藏侧栏数量徽章 — 节省 30-40px,让「按时间顺序」「我的收藏」不被 ellipsis。 */
    :root { --player-height: 154px; }   /* v30: 148 → 154,底部进度条呼吸空间 13 → ~19px。
                                          双行式 row1(50) + gap(8) + row2(69) + padding(10+16) = 127,
                                          容器 154 余 27,默认 align-content:normal 让空隙落到底部。 */
    .app-player-bar .m-music-play-wrap {
        grid-template-columns: minmax(0, 1fr) auto;
        grid-template-areas:
            "left   right"
            "center center";
        /* v30: align-content:start 让 row1/row2 保持自然高度(50+69=119),
           多余 14px 落在底部而非被均分到两行(否则 row1/row2 都拉伸,进度条
           只下移 ~5px,实际 gap 仅 14.5)。配合 --player-height 154 → 19px 间隙。
           v37: padding 10px 16px → 12px 16px 14px,row1 顶部留白 6→8px,
           row2 底部留白 10→14px,player-bar 总高仍 154px(未改 --player-height),
           与现有 v30 注释"底部进度条呼吸空间 13→19px"意图一致。
           v42:14→20,进度条贴底边感反馈"靠底太近",再加 6px 呼吸;
           --player-height 仍 154px,row1/row2 自然高度不变(顺承 align-content:start),
           多出 6px 落在 player-bar 底部而不挤控件。 */
        grid-template-rows: 50px auto;
        align-content: start;
        gap: 8px;
        /* v51:16→12,确保 767-769 视口下 player-bar 内容最小宽度(~550px)能容纳。
           原 16px + 内容让 player-bar w=549.84,sidebar 220 拼 770 > 767-769 → 2-3px
           溢出(虽被 html/body overflow:hidden 裁掉视觉无感,但布局隐患)。
           缩到 12px 后宽度 ~542px,边界视口下完整 fit,留 5-7px 安全余量。 */
        padding: 12px 12px 20px;
    }
    .app-player-bar .player-left   { grid-area: left; gap: 10px; }
    .app-player-bar .player-right  { grid-area: right; align-self: center; gap: 6px; }
    .app-player-bar .player-center {
        grid-area: center;
        grid-column: 1 / -1;
        display: flex;
        flex-direction: column;
        gap: 4px;
    }
    .app-player-bar .player-center .m-play-controls { justify-content: center; }
    .app-player-bar .player-center .player-progress { width: 100%; }

    /* v18: 缩小封面 + 控件,让双行式塞进 140px player:
       cover 64→48 (被下方 v18+ 段覆盖成 44),主播放 48→40,上下曲 34→32;
       padding-top/bottom 各 10。v29 清理:.u-cover 48 与 .m-play-controls gap 14
       已是历史值被下方覆盖,删除以减少视觉噪音;其余(32/42/mode 隐藏)仍在
       721-1024 生效,保留。 */
    .app-player-bar .u-play-btn { width: 32px; height: 32px; }
    /* v51:42→46,与 ≤720 (46)/1025-1439 (48) 单调对齐。
       修复 w720→w768 ctrl-play 46→42 反向阶梯(v50 审计 #2)。
       双行布局 row 1 高度 50,46 + 2*2 padding = 50,刚好放下。 */
    .app-player-bar .u-play-btn.ctrl-play { width: 46px; height: 46px; }
    /* v30:721-1024 解开 mode 隐藏(放进 .player-right,row 1 右侧) */

    /* v31:在 200px 窄侧栏里也常显 nav-count。grid 1fr 已给 label 留出弹性空间,
       加上 .nav-label 已锚定 min-width: 0,长标签会自动 ellipsis 而不是挤掉数字。
       字号 12px + tabular-nums 视觉重量轻,实测不挤压 active 强调、列表区卡顿亦不受影响。 */

    /* v18+:再压一档封面(48→44)给标题让出呼吸空间,长中文歌名
       (如「等你下课(with 杨瑞代)」)不再过早 ellipsis。
       字号也微调:strong 15 / small 13,iPad 上更舒展。
       同时在 721-1024 隐藏 live-wave — 此区间已无侧栏视觉平衡需求,
       反而挤占标题右侧空间。 */
    .app-player-bar .u-cover { width: 44px; height: 44px; }
    /* v47+:字号 fs-xl → fs-lg (15→14),消除 ≥1025 → 721-1024 → ≤720 的反向阶梯
       (原 14→15→14,中间断点反而大,违反单调原则)。
       与 base 一致,只通过 lh-snug / weight 600 提供精致感。
       small fs-md → fs-base (13→12),与 base 对齐。 */
    .app-player-bar .u-music-title strong { font-size: var(--fs-lg); }
    .app-player-bar .u-music-title small { font-size: var(--fs-base); }
    /* v74 P0 修复:选择器补 body.is-playing 前缀,特异性 (0,0,4,1) 与 base 的
       "播放时显示" 规则(app.css:1838)打平,靠 source order(本块在后)胜出。
       原 v68 只考虑了与 901-1024 块的 (0,0,3,0) 竞争,漏掉全局 body.is-playing
       (0,0,4,1) — 播放时本行的 display:none 被压过,721-900 双行式布局里
       live-wave 照样冒出挤占标题区。未播放时 base 1814 行本就隐藏,无需担心。 */
    body.is-playing .app-player-bar .player-text .live-wave { display: none; }

    /* v53+:iPad 721-1024 进度条 4→5px,与 ≤720 阶梯统一 —
       720→721 边界用户拖动进度条时 5→4 跳变 1px 不和谐。
       iPad 是触摸设备,5px 与 4px 触摸命中差异不大但视觉一致。
       与下方 901-1024 块(line 2624 已有相同定义)通过 specificity (0,0,2,1) 胜出
       保证两个子块阶梯一致。 */
    .app-player-bar .u-process { height: 5px; }
}

/* v63+:iPad 横屏 / 平板 901-1024 — 用单行式播放器,与 1025-1439 / ≥1440 视觉一致。
   原 721-1024 双行式是 v18 为 800-1024 等效像素窄宽设计的,但实际 iPad 横屏 (1024)、
   iPad Pro 等平板 (901-1024) wrap 宽度 ~700-820px,单行 3 列 grid (180+1fr+220+48gap)
   中心列 252-372px,够 5 个播放按钮 (252px) 放下,无溢出。
   而 721-900 wrap 521-700px,中心列 33-252px,放不下控件 — 仍保留 721-1024 双行式。
   player-height 116 + cover 56 + wrap padding 0/20/16 完全照搬 1025-1439 块。 */
@media (min-width: 901px) and (max-width: 1024px) {
    :root { --player-height: 116px; }

    /* 重置上方 721-1024 块的双行式 grid/areas/rows/align-content 等,
       让 player-center 回到单行式布局(由 smusic.css base + height:100% + justify-content:space-between 撑开)。
       grid-template-columns 也回到默认 3 列 180/1fr/220。
       grid-template-areas 设为 none,让各列(grid items)自动流入。
       v53+:padding 0 20px 16px → 10px 20px 24px — 与 1025-1439 块同步修复
       (见 line 2338 注释)。原值让 ctrl-box 紧贴 wrap 顶 (y=0),
       与 playerLeft (居中于 99px 内容区 y≈12.5) 视觉错位,控件看起来"偏上/偏高"。
       改为 10/24 后内容区 81,space-between 让 ctrl y=10、progress 贴底,封面距顶 17,
       上下对称,符合 iPad 横屏用户对桌面布局的视觉预期。 */
    .app-player-bar .m-music-play-wrap {
        grid-template-columns: minmax(180px, 22%) 1fr minmax(200px, 220px);
        grid-template-areas: none;
        grid-template-rows: none;
        align-content: normal;
        gap: 24px;
        padding: 10px 20px 24px;
    }
    .app-player-bar .player-left   { grid-area: auto; gap: 12px; }
    .app-player-bar .player-right  { grid-area: auto; align-self: auto; gap: 8px; }
    .app-player-bar .player-center {
        grid-area: auto;
        grid-column: auto;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: space-between;
        max-width: 640px;
        margin: 0 auto;
        width: 100%;
        gap: 0;
    }
    .app-player-bar .player-center .m-play-controls { justify-content: center; }
    .app-player-bar .player-center .player-progress { width: 100%; }

    .app-player-bar .u-cover { width: 56px; height: 56px; }
    /* u-play-btn 走 smusic.css base 36x36(721-1024 块原本缩到 32,单行式不需要这么小) */
    .app-player-bar .u-play-btn { width: 36px; height: 36px; }
    /* ctrl-play 1025-1439 块 base 是 48,这里微调到 46 保持 iPad 与原双行式一致
       (阶梯 ≤720 46 → 901-1024 46 → 1025-1439 48 → ≥1440 48,单调不减) */
    .app-player-bar .u-play-btn.ctrl-play { width: 46px; height: 46px; }

    /* title 字号回 smusic.css base,live-wave 恢复"仅播放时显示" */
    .app-player-bar .u-music-title strong { font-size: var(--fs-lg); }
    .app-player-bar .u-music-title small { font-size: var(--fs-base); }
    /* v74 P0 修复:原规则无条件 display:inline-flex,绕过了全局
       body.is-playing 门控(app.css:1838)与 721-1024 块的隐藏 —
       901-1024 视口未播放时标题右侧残留一截静止紫条("⁼"鬼影)。
       补 body.is-playing 前缀:与上方 721-1024 隐藏规则同特异性 (0,0,4,1),
       本块 source order 在后,播放时胜出显示;未播放时两者都不匹配,
       由 base 1814 行 display:none 兜底。 */
    body.is-playing .app-player-bar .player-text .live-wave { display: inline-flex; }

    /* v53+:iPad 横屏 901-1024 进度条 4→5px,与 ≤720 块阶梯统一 —
       源顺序上本块晚于 721-1024 (line 2443),specificity 相同 (0,0,2,1),
       胜出后整个 721-1024 子区间进度条都是 5px,跨 720→1024 无阶梯跳变。
       iPad 是触摸设备,5px 与 4px 触摸命中差异不大但视觉一致。 */
    .app-player-bar .u-process { height: 5px; }
}

/* v10: 平板 portrait 中间断点 — title 阶梯缩小,避免 ≤900 与 721-1024 (32px) 跳变突兀。
   cover 不重声明:沿用 721-1024 块 (line 2006) 的 280px 一致即可。 */
@media (max-width: 900px) {
    .app-fullscreen-title {
        font-size: var(--fs-9xl);
    }
}
@media (max-width: 768px) {
    .app-fullscreen-cover {
        width: 240px;
        height: 240px;
        /* v62+:移动端 box-shadow 降级 — 桌面端用 16/40px blur 制造浮起感,
           但移动端旋转动画 + 大 blur 在低端机会显著掉帧。改 8/16px blur,
           inset shadow 保留(几乎免费)。 */
        box-shadow:
            0 8px 16px -6px rgba(0, 0, 0, 0.18),
            inset 0 0 0 2px rgba(0, 0, 0, 0.14);
    }
}

/* 手机(默认 ≤720px):紧凑布局,侧栏变抽屉,播放器垂直堆叠 */
@media (max-width: 720px) {
    :root {
        --sidebar-width: 0;
        /* v22 修复:player-height 必须严格 ≥ 内容高度。
           ≤720 内容:cover(44) + gap(4) + ctrl-play(46) + gap(4) + progress(u-time 11px 行高~17 + u-process 5 = 22) + padding(4+6 = 10) = 130,
           grid-template-rows 44/72 让 row 2 实际容纳 player-center 全部内容;
           player-height 132 → 136 让整体容器留 6px 安全余量。
           v37:删旧注释"u-process hover scaleY(2) 翻倍"虚构描述 — 实际 hover 仅
           progress-thumb scale(0.95→1) + box-shadow 加深,bar 高度始终 5px。 */
        --player-height: 136px;
        --header-height: 56px;
    }

    .app {
        grid-template-columns: 1fr;
    }

    .app-sidebar {
        position: fixed;
        top: 0;
        left: 0;
        /* 抽屉宽度三段式(v52.5 上限 320→280):
           - 下限 200:给 ≤240 极小屏安全垫(实际主流手机都 ≥320,不触发)
           - 中段 100vw - 40:中屏(360-500)按比例自适应,展开宽 280-440
           - 上限 280:v52.5 从 320 → 280(行业标准抽屉宽度,微信/QQ/Twitter 菜单抽屉
             普遍 280-320,再低就偏窄)。"再稍微窄一些"反馈。
             720 屏:280 (占 39%,主列表预览 440);568 屏:280 (占 49%);480 屏:280(58%)。
             主流 mobile 320-414 展开 280,占屏宽 68-88%(用户主动展开合理)。
             nav 区段 280-36-22-16-42-3=161,7 字中文"分类:XX" 16px ≈ 112,fit ✓ 余 49。
             ≤500h 横屏块(line 3498)同步降到 280。 */
        width: clamp(200px, calc(100vw - 40px), 280px);
        height: calc(var(--app-height-dvh) - var(--safe-bottom));
        /* iOS 上要避开刘海,但底部不要覆盖 home indicator */
        padding-top: var(--safe-top);
        box-sizing: border-box;
        /* v52 P0-1:抽屉 z 从 100 → var(--z-overlay)=500,与 v40 P0 定义的"sticky→overlay→popup"
           抽屉语义对齐。原 z=100 与 .app-mobile-backdrop 同 z=100,DOM 中 backdrop 在 .app 之后
           后绘制,点击侧栏 nav 链接实际命中 backdrop 触发关闭(.app-mobile-backdrop click 处理器),
           现象是"点击无反应"。升到 500 后 sidebar 浮在 backdrop(z=100)之上,nav 链接可点;
           ⋯ 弹层 z=500 同层无冲突(≤480 抽屉开时 JS 已 hidden)。 */
        z-index: var(--z-overlay);
        transform: translateX(-100%);
        transition: transform var(--t-slow) var(--ease);
        border-right: 1px solid var(--border);
        box-shadow: 4px 0 16px rgba(0, 0, 0, 0.08);
    }

    .app-sidebar-head {
        /* 补偿 sidebar padding-top,保持头部位置不变 */
        margin-top: calc(-1 * var(--safe-top));
    }

    .app-sidebar.open {
        transform: translateX(0);
    }

    /* v52+:抽屉内底部"收起菜单"按钮(与 .app-mobile-menu-btn 对称 — 一个打开一个收起)。
       sidebar flex:column + nav flex:1 → flex-shrink:0 让按钮自然贴底,即使用户滚到底
       也不会被 nav 内容挤上来。视觉与 .app-nav a 同语 — 透明背景 + hover/active var(--bg-hover),
       顶部 1px border 分隔线提示"独立于 nav 的动作"。触摸区 min-height:48 满足移动端 WCAG 2.5.5。
       v68 P0 修复:完整样式已在 base 块(line 705+)声明;本块只剩 display: flex 触发可见性,
       通过 source order 覆盖 base 的 display: none。
       旧 v57 "其余属性从本块继承"是错的 — CSS 不跨 @media 继承,导致 ≤900 横屏(line 3889)
       和 901-1024 横屏(line 4333) 各自 .app-sidebar-close { display: flex } 实际只设了 display,
       渲染出来是未样式化的 flex 容器(无 padding / border / min-height,视觉上像占位 div)。
       修复后:base 块提供完整样式,所有断点 @media (≤720 / ≤900 横屏 / 901-1024 横屏)
       只需一条 display: flex 即可正确渲染。 */
    .app-sidebar-close { display: flex; }

    /* .app-header 前缀抬升 specificity (0,0,2,0) > base 块 line 4872+ 的 display:none (0,0,1,0),
       让 ≤720 / ≤900 横屏 / 901-1024 横屏 都能让按钮可见。
       v68 P0 修复:完整样式已在 base 块(将插入在 line 4872 之前)声明;
       本块只剩 display: inline-flex 触发可见性。
       旧 v57 "其余属性从本块继承"同 .app-sidebar-close 一样是错的 —
       ≤900 横屏(line 3848) / 901-1024 横屏(line 4292) 各自 .app-header .app-mobile-menu-btn { display: inline-flex }
       实际只设了 display,按钮渲染为未样式化的 inline-flex 容器(无 40×40 / border / border-radius,
       视觉上像裸 SVG icon)。
       修复后:base 块提供完整样式,所有断点只需一条 display: inline-flex 即可正确渲染。
       副作用:≤480 (line 3251) / ≤380 (line 3460) / ≤360 (line 3665) / ≤320 块原本用 .app-mobile-menu-btn
       无 .app-header 前缀,被 ≤720 的 (0,0,2,0) 压住成死代码。移到 base 后 (0,0,1,0) 同特异性,
       source order 让 ≤480/380/360/320 阶梯重新生效,小屏按钮 40→36→34→32 阶梯终于真正起作用。 */
    .app-header .app-mobile-menu-btn { display: inline-flex; }

    /* 抽屉打开时锁主列表滚动 + 防止 iOS 橡皮筋穿透。
       body 本身已 overflow:hidden,这里锁 .m-music-list-wrap 的滚动容器;
       sidebar 是独立滚动容器 .app-nav,不受影响。 */
    body.sidebar-open .m-music-list-wrap { overflow: hidden; }
    body.sidebar-open { overscroll-behavior: none; }

    /* v26+:主列表滚动条阶梯 20→18,thumb min-height 70→60。
       手机 18px 占视口 ~2.5%,仍粗但让位给列表内容 */
    .app-list-area .m-music-list-wrap::-webkit-scrollbar {
        width: 18px !important;
        height: 18px !important;
    }
    .app-list-area .m-music-list-wrap::-webkit-scrollbar-thumb {
        border-radius: 9px;
        min-height: 60px;
    }

    /* 头部紧凑:缩小内边距与间距 */
    .app-header {
        gap: 8px;
        /* v37:P2-6 padding 12/10 → 12/12 对称 — 与其他断点 L=R 一致(基线 20/20、721-1024 16/16、≥1440 24/24),
           左侧 12 右侧 10 不对称是早期为 menu 按钮 margin-right 6px 补偿,现在 menu margin-right 6px 已稳定(≤720 块 6,≤480 块 6),
           对称 padding 视觉更协调。 */
        padding-left: calc(12px + var(--safe-left));
        padding-right: calc(12px + var(--safe-right));
        /* align-items: center 由 base 规则提供,这里不再重复声明。 */
    }

    /* v22 修复:gap 6 → 8,让顶栏右侧按钮之间呼吸感更足。
       在 ≤480 顶栏只剩 [≡ 标题 ⋯ ☀],8px gap 让 ⋯ 与 ☀ 视觉分离不粘连。 */
    .app-header-actions {
        gap: 8px;
    }

    /* 头部 meta 紧凑 — v37:15 (base) → 14 (≤720) → 12 (≤480) 阶梯细化。
       v37:P2-8 删 .app-meta-count font-size: 14px — 是 .app-meta 子元素,
       font-size 通过继承已经等于 14,再显式声明是冗余。
       v37:P3-5 gap 6 → 8 — 与 base 一致,避免 8px status-dot 与文字视觉粘连。 */
    .app-meta {
        font-size: var(--fs-lg);
        gap: 8px;
    }
    .app-crumb-cat {
        font-size: var(--fs-2xl);
        /* v69+:min-width:0 让 ellipsis 在 flex 容器里真正生效;
           不加 flex:1(那样会把 hint 挤到容器最右,看起来右对齐) —
           让 title 只占内容宽度,hint 紧跟其后,共同左对齐。 */
        min-width: 0;
    }

    /* v69+:窄屏也显示顶栏 hint(原 v42+ 用 display:none 隐藏);
       字号缩一档 fs-md(13)→fs-sm(11),作为弱化说明;
       用 flex:1 1 0 替代 max-width — 让 hint 占据 title 之后的所有剩余空间,
       而不是按容器百分比封顶(原 55% 在 360 屏只能给 ~98px,实际可用 138px)。
       字短时空隙在 hint 右侧(可接受 — 用户要求"尽量显示全一些",
       只有空间真的不够时才 ellipsis 截断)。
       gap 由 base 12px 收紧到 6px 给标题留更多空间。 */
    .app-crumb-hint {
        display: inline-block;
        font-size: var(--fs-sm);
        flex: 1 1 0;
        min-width: 0;
        max-width: none;
    }
    .app-crumb {
        gap: 6px;
    }

    /* 移动端:主题切换器只显示图标,隐藏文字和箭头 */
    .theme-switcher .theme-trigger-label,
    .theme-switcher .theme-trigger-caret {
        display: none;
    }
    .theme-switcher .theme-trigger {
        padding: 0;
        width: 34px;
    }

    /* 移动端播放控件竖排堆叠后,控件行(6 个按钮)需要紧凑。
       v37:删原 44px 1fr auto 单行布局,被 line 2399 v18 双行式 (1fr auto + 44/72 grid-template-rows)
       覆盖,前者死代码。 */

    /* 关键修复:player-left 默认在 col 1(44px),内部又包含 cover + text,
       文字会被挤到 0 宽。让 player-left 跨占 col 1+2,让 text 有 1fr 空间展示 */
    .app-player-bar .player-left {
        grid-column: 1 / 3;
        grid-row: 1;
        gap: 10px;
        min-width: 0;
        overflow: hidden;
    }

    /* v37: 显式覆盖 smusic.css 基础规则的 justify-content:space-between
       + height:100%。≤720 双行式 row2 高度 = auto = 子内容高,
       justify-content flex-start 等效于原 gap:4px 自然顶对齐,
       避免级联导致 row2 内部 playControls/progress 错位。
       v57 修复:原 .player-center (specificity 0,0,1,0) 在前一段(line 2924 旧位置),
       被下方 .app-player-bar .player-center (0,0,2,0) 覆盖,4 个声明从未生效 —
       现合并到下方 .app-player-bar .player-center 一处。 */
    .app-player-bar .player-right {
        grid-column: 3;
        grid-row: 1;
        justify-self: end;
        /* v30:改为 flex 容器,装下 3 mode + volume + fullscreen 5 项
           v38: gap 6 → 4,横向音量条新增后 5 控件更紧凑
           v40 P1 修复:加 .app-player-bar 前缀,否则 specificity (0,0,1,0)
           输给 base 规则 .app-player-bar .player-right { gap:8px } (0,0,2,0),
           gap:4 实际永不生效,造成 721-1024 (gap:6) → ≤720 (实际 gap:8) 反向阶梯。 */
        display: flex;
        align-items: center;
        gap: 4px;
    }

    /* 移动端隐藏 live-wave(空间不够) */
    .app-player-bar .player-text .live-wave { display: none !important; }

    /* 播放控件按钮紧凑 — 移动端避免溢出。
       v37:删原 gap:4 + justify-content:space-between + padding,被同块末尾
       line 2429 v18 修复 (gap 14 + justify-content: center) 覆盖,前者死代码。 */

    /* v29 清理:≤720 段 .u-play-btn 32/42/mode 30/svg 13/16/12 是历史值,
       全部被同段末尾 v22 HIG 修复 (line 2309-2312: 34/46/svg 18) 覆盖。
       mode 在 v22 段也已 display:none,尺寸无意义。删除以减少视觉噪音。 */

    /* v38: 窄屏常驻内联横向音量条(替代原 popover 式)
       镜像全屏视图 .fs-vol-track 视觉(app.css:1817-1893),统一全站音量交互语言。
       设计要点:
       - position: static 让条与音量按钮并排,不悬空
       - 80px 宽 5px 高,inline-flex 自然占位
       - ::after 圆点 thumb 常驻,通过 --vol-thumb-x 变量定位
       - .volume-event 触摸命中区 80×28px(iOS HIG 友好)
       - 配合 app.js bindBottomVolume() 用 Pointer Events 点击/拖动调音量,
         preventDefault 抑制 SMusic 库的坏 click(offsetY/offsetHeight 永远 0.5) */
    .app-player-bar .u-volume .volume-process {
        position: relative;
        /* v40+: position: relative 是横向条能正确显示的根 —
           .volume-current 是 absolute 定位,containing block 默认是 .u-volume(94-120px),
           width: var(--vol-width, 50%) 会算成 50% × .u-volume-width(60px 而不是 40px),
           height: 100% !important 会算成 100% × .u-volume-height(28px 而不是 5px),
           导致 cur 在视觉上凸出 bar 上下各 11.5px(320 视口实测,音频 0.4 时凸 47×28 vs bar 60×5)。
           加 position: relative 让 cur 的 containing block 变为 .volume-process 自身(80×5),
           width/height 50%/100% 算的就对了,cur 完美贴合 bar。
           之前 v38 改 position: static 是错的(为了清掉 left:50% / translateX(-50%),
           实际用 left: auto + transform: none 就能清掉那俩属性,无需改 static)。 */
        /* v38+: 清掉 smusic.css:413-414 的 left:50%/translateX(-50%) —
           原规则是 SMusic 库 absolute 父容器里的水平居中,改 static 进
           inline-flex 容器后已不需要,但属性没被覆盖,translateX(-50%)
           会让 volume-process 向左凸出自身宽度 50%,盖住 player-modes
           末尾的 mode-single(单曲循环)按钮,实测 480/360/320 全视口都
           会触发 — 用户看到的"窄屏没有单曲循环按钮"的根因。 */
        left: auto;
        transform: none;
        width: 80px;
        height: 5px;
        background: var(--border-strong);
        border-radius: 3px;
        opacity: 1;
        visibility: visible;
        cursor: pointer;
        flex-shrink: 0;
        transition: background var(--t-fast) var(--ease);
    }
    .app-player-bar .u-volume:hover .volume-process {
        background: var(--text-mute);
    }
    /* 横向填充:width 替代 SMusic 原版 height(SMusic 内联 style.height 由 !important 压过)
       与 .fs-vol-cur(scaleX 路线)不同 — 这里 width 在 inline-flex 容器内是
       最直接的实现,且只 5px 高不触发 layout reflow。
       v40:position:relative 后,containing block 是 .volume-process(80×5),
       width: 50% = 40px,height: 100% = 5px,cur 完美贴合 bar。 */
    .app-player-bar .u-volume .volume-process .volume-current {
        width: var(--vol-width, 50%);
        height: 100% !important;
        top: 0;
        left: 0;
        bottom: auto;
        background: var(--accent);
        border-radius: 3px;
        transition: width var(--t-fast) linear, background var(--t-fast) var(--ease);
    }
    /* 触摸命中区:横向铺满 80px,纵向扩到 28px(iOS HIG 友好),
       覆盖原 smusic.css:440 的 24×80 竖向触摸命中 */
    .app-player-bar .u-volume .volume-process .volume-event {
        width: 100%;
        height: 28px;
        top: 50%;
        left: 0;
        transform: translateY(-50%);
        z-index: var(--z-track);
    }
    /* thumb 圆点 — 常驻,跟 .fs-vol-thumb(app.css:1874)同款 */
    .app-player-bar .u-volume .volume-process::after {
        content: '';
        position: absolute;
        top: 50%;
        left: var(--vol-thumb-x, 50%);
        width: 12px;
        height: 12px;
        background: var(--accent);
        border-radius: 50%;
        transform: translate(-50%, -50%);
        box-shadow:
            0 0 0 2px var(--bg-elevated),
            0 1px 3px var(--accent-glow-med);
        z-index: var(--z-thumb);
        pointer-events: none;
        transition: left var(--t-fast) linear, transform var(--t-fast) var(--ease);
    }
    .app-player-bar .u-volume:hover .volume-process::after {
        transform: translate(-50%, -50%) scale(1.1);
    }
    /* u-volume 容器加 gap 让按钮与条视觉分离 */
    .app-player-bar .u-volume { gap: 6px; }

    /* 进度条时间区:缩小 — v37:删原 gap:8 + u-time 11/50,被 line 2449-2492
       v22 HIG 修复 (gap 12 + u-time 11/60/fs:0) 覆盖,前者死代码。 */

    /* 移动端缩小全屏按钮,让它和音量按钮更协调 */
    .player-fullscreen-btn {
        width: 32px;
        height: 32px;
        margin-left: 4px;
    }
    .player-fullscreen-btn svg {
        width: 14px;
        height: 14px;
    }

    /* 全屏视图在小屏下调整尺寸 */
    /* v27+:reverse staircase 修复 — 原 min(280, 70vw) 在 720-400 视口恒为 280px,
       比 ≤768 的 240px 还大 40px。改为 min(240, 70vw) 与 ≤768 阶梯对齐,
       让 900→768→720 cover 单调递减(280 → 240 → 240→≤480 220)。 */
    .app-fullscreen-cover {
        width: min(240px, 70vw);
        height: min(240px, 70vw);
    }

    .app-fullscreen-title {
        font-size: var(--fs-8xl);
        /* v24: 窄屏允许 2 行换行,避免 "2002年的第一场雪(刀郎现场版)"
           这种长标题过早 ellipsis */
        white-space: normal;
        display: -webkit-box;
        -webkit-line-clamp: 2;
        /* v40 P2:配对标准 line-clamp,目标浏览器已全部支持 line-clamp */
        line-clamp: 2;
        -webkit-box-orient: vertical;
        /* v37:line-height 1.25 → var(--lh-snug) token 化 */
        line-height: var(--lh-snug);
    }

    .app-fullscreen-artist {
        font-size: var(--fs-xl);
    }

    .fs-ctrl {
        width: 44px;
        height: 44px;
    }
    .fs-ctrl svg {
        width: 18px;
        height: 18px;
    }
    .fs-play {
        width: 60px;
        height: 60px;
    }
    .fs-play svg {
        width: 24px;
        height: 24px;
    }

    .app-fullscreen-content {
        gap: 22px;
    }

    /* v25: ≤720 给全屏 close 视觉 36(比 ≥1024 的 40 小,比 ≤480 的 32 大,
       保持阶梯 40 → 36 → 32 单调递减);top 加 safe-top 补偿刘海屏。
       v37:top 8 → 10,与 ≤480/≤320 阶梯统一,避免 8/14/10 反向阶梯。 */
    .app-fullscreen-close {
        width: 36px;
        height: 36px;
        top: calc(10px + var(--safe-top));
    }

    .app-keyhint-btn { display: none; }

    /* v18: Spotify 双行式布局 — ≤720 时启用 grid-area。
       行1: [player-left 封面+标题] [player-right 音量+全屏]
       行2: [player-center 控件+进度条 跨整行]
       v22 修复:grid-template-rows 44/68 → 44/72;原 68 仍不够,
       player-center 内容 = ctrl-play 46 + gap 4 + progress 22 = 72,需要 row 2 ≥ 72。
       player-height 132 → 136 给整体 4px 安全余量。
       v37:删旧注释"u-process 在 hover 态 transform: scaleY(2) 高度临时翻倍"虚构描述 —
       实际 hover 仅 progress-thumb scale(0.95→1) + box-shadow,bar 高度始终 5px。
       v48+:gap 4 8 → 6 8 — row 1↔2 间距 +2px,呼吸感更强(从 4 → 6)。
       v48+:row 2 元素从 5 增至 8 (3 mode + prev + play + next + progress + time),
       play 视觉锚点居中,modes/controls 左对齐 (justify-content: flex-start),
       progress+time 在右,Spotify 风"modes → controls → progress" 阅读顺序。 */
    .app-player-bar .m-music-play-wrap {
        grid-template-columns: 1fr auto;
        grid-template-rows: 44px 72px;
        grid-template-areas:
            "left    right"
            "center  center";
        gap: 6px 8px;
        padding: 4px 12px 6px;
    }
    .app-player-bar .player-left   { grid-area: left;   gap: 10px; min-width: 0; align-self: center; }
    /* v48+:player-right 现在只剩 vol + fs (mode 移到 player-center),
       gap 4 → 8 让 vol-icon 与 fs 之间留呼吸,row 1 视觉清爽 */
    .app-player-bar .player-right  { grid-area: right;  gap: 8px; align-self: center; }
    .app-player-bar .player-center {
        grid-area: center;
        grid-column: 1 / -1;
        grid-row: 2;
        gap: 4px;
        padding: 0 4px;
        justify-content: flex-start;
        height: auto;
    }

    /* cover 缩到 44(与 721-1024 一致),原 48 来自 ≤1024 块,
       实际 ≤720 块继承的 cover 48 撑高 row1 加大溢出 */
    .app-player-bar .u-cover { width: 44px; height: 44px; }

    /* v44+:删除 strong/small font-size override — base 已统一 fs-lg/fs-base,
       与原 override 数值一致,这里不再重复声明。历史上 base 是 12/14(标题 < 艺人
       反序),所以才需要 override 修正,现在 base 修正后该 override 是冗余的。 */

    /* v48+:3 mode 按钮从 .player-right 移到 .player-center > .m-play-controls,
       与 prev/play/next 同行。Row 2 元素从 5 增至 8,需要更紧的 gap (14 → 4)
       才能在 375px 视口下塞下 (progress 横向 ≥65px)。
       justify-content: center → flex-start: modes + controls 整体左对齐,
       progress+time 在 grid 右下,视觉对称。 */
    .app-player-bar .m-play-controls {
        gap: 4px;
        justify-content: flex-start;
    }
    /* v48+:modes 内部紧凑 (3px),与 controls 的 4px 形成微妙视觉分组,
       margin-right 4px 让 modes 与 prev 之间多 4px 间距 = 视觉分组效果 */
    .app-player-bar .m-play-controls .player-modes {
        display: flex;
        gap: 3px;
        margin-right: 4px;
    }
    /* 上下曲按钮 ≥34,主播放 ≥46:全部满足 iOS HIG 44(触摸扩展由 hover:none 块 ::before 补) */
    .app-player-bar .u-play-btn { width: 34px; height: 34px; }
    .app-player-bar .u-play-btn.ctrl-play { width: 46px; height: 46px; }
    .app-player-bar .u-play-btn svg { width: 13px; height: 13px; }
    .app-player-bar .u-play-btn.ctrl-play svg { width: 18px; height: 18px; }
    /* v48+:mode 尺寸 32 → 28 — 让 row 2 在 375 视口下 progress ≥65px,
       与 ≤480 mode 28px 阶梯一致 (不再有 32 → 28 反向阶梯)。
       svg 13 → 12 与 smusic.css ≤480 块 (12px) 阶梯对齐。 */
    .app-player-bar .u-play-btn.mode { width: 28px; height: 28px; }
    .app-player-bar .u-play-btn.mode svg { width: 12px; height: 12px; }

    /* 进度条行:touch 友好,thumb 不缩 */
    .app-player-bar .u-process { height: 5px; }
    .app-player-bar .u-time {
        font-size: var(--fs-sm);
        /* v47:60px → var(--u-time-min-w) */
        min-width: var(--u-time-min-w, 60px);
        flex-shrink: 0;
    }
    /* v27 round 3 补漏:≤720 块 gap 10 + min-width 44 不安全,thumb (14,translate -7)
       在 progress=0 时凸入 gap 区只留 3px 安全间距,无 flex-shrink:0 兜底
       u-time 还可能被压缩。统一 gap 12 + min-width 60 + flex-shrink:0,
       与 v27 round 1 一致 */
    .app-player-bar .player-progress { gap: 12px; }

    /* v29 round 2+:合并 button 阶梯相关块到 ≤720,消除重复 @media 块;
       内容互不重叠,cascade 等效。 */
    /* v32+:原 .app-fav-btn ≤720 阶梯已删除(顶栏收藏按钮整体移除) */
}

/* 小屏手机(≤480px):进一步压缩 + 启用 ⋯ 折叠按钮 */
@media (max-width: 480px) {
    :root {
        /* v42+:cover 36 → 40(用户反馈小尺寸圆形边缘不齐),让 box-shadow blur 不再
           盖过圆半径(40px 圆半径 20,blur 14 占 70% vs 旧 32px 占 87.5%)。
           grid row1 36→40,player-height 124→128(40+4+70+10+1=125 余 3 安全)。 */
        --player-height: 128px;
        --header-height: 52px;
    }

    /* v26+:主列表滚动条阶梯 18→16,thumb min-height 60→60。
       360-480 视口,16px 占视口 3-4%,仍粗但给短标题留更多呼吸空间 */
    .app-list-area .m-music-list-wrap::-webkit-scrollbar {
        width: 16px !important;
        height: 16px !important;
    }
    .app-list-area .m-music-list-wrap::-webkit-scrollbar-thumb {
        border-radius: 8px;
        min-height: 60px;
    }

    .app-mobile-menu-btn {
        width: 36px;
        height: 36px;
        margin-right: 6px;
        /* v37:font-size: 18px 删 — 已是 SVG hamburger,font-size 不影响 svg 宽度 */
    }

    /* v74:361-480 恢复显示艺人名(smusic.css 把隐藏收窄到 ≤360)。
       行内容量小,空间不足时的让位顺序要与桌面相反:桌面是标题先 ellipsis、
       艺人恒定(small flex:0 0 auto),窄屏让艺人先收缩 — shrink 权重 4:1,
       收缩量按 factor×basis 分配,艺人以 4 倍速度让位直至 ellipsis 到消失,
       长标题(30+ 字)获得的最终宽度接近 v18"整体隐藏艺人"的水平。
       短标题 + 正常艺人时总宽不溢出,两者都不收缩,维持桌面端
       "艺人紧贴标题右侧"的成组视觉(v74 初版用 strong min-width:45% 保底,
       但 clamp 会拉伸短标题盒宽,把艺人推到行右缘变成"右对齐栏目",
       破坏成组感,已废弃)。 */
    .m-music-list-wrap li small { flex: 0 1 auto; min-width: 0; flex-shrink: 4; }

    /* v37: ≤480 进一步压 meta 字号 14→12,与 ≤480 strong/small 阶梯同步。
       但 JS 在 ≤480 给 .app-meta 加 [hidden] (line 2538),font-size 永不渲染,
       这里保留 font-size: 12px 仅作占位(若未来 JS 不再 [hidden] 时生效)。 */
    .app-meta {
        font-size: var(--fs-base);
    }

    /* v46 字号审计 P1 Fix 3:15→14(原 fs-xl → fs-lg)— 修复 ≤480 块反向阶梯。
       原 15px 比 ≤720 块的 14px 还大,违反"视口越窄字号越小"原则。
       ≤720 (14) → ≤480 (14) → ≤380 (13) → ≤360 (13,Fix 4) 单调。 */
    .app-crumb-cat {
        font-size: var(--fs-lg);
    }

    /* ≤480 时启用顶栏 ⋯ 折叠 — bindHeaderOverflow JS 会同步 hidden 属性。
       v38: [hidden] 规则已提升到 base (与 .app-keyhint-btn 块同位置),
       覆盖所有视口 — 原 ≤480 局限版被删,避免 481-720 设 hidden 不生效的隐患。 */

    /* 标题缩小,防溢出
       v37 P1 修复:≤480 strong 14/small 12 + 继承 line-height 1.4 = 内容高度
       14×1.4 + 2 + 12×1.4 = 38.4px,撑出 row1 (36px) 上下各 1.2px。
       强 14→13,与 ≤380 阶梯一致(13/11),内容高度降到 ~36.4px,刚好 fit row1。
       v44+:删除 small fs-base override — base 已统一 fs-base,这里只缩标题 14→13。 */
    .app-player-bar .u-music-title strong {
        font-size: var(--fs-md);
    }

    /* 双行式紧凑:封面 40(与 ≤380/≤360/≤320 阶梯统一),行1 padding 紧一档 */
    .app-player-bar .m-music-play-wrap {
        /* v42+:cover 36 → 40(用户反馈小尺寸圆形边缘不齐),
           row1 36 → 40。player-center 内容 = ctrl-play 44 + gap 4 +
           progress 20(hover scaleY 后) = 68。row2 = 70 不变。
           player-height 128 = 40+4+70+10padding+1border = 125,余 3 安全。
           v48+:gap 4 → 6 (row) — 继承 v48 row 1↔2 间距呼吸感优化,
           8 (col) 保持 ≤720 阶梯不变。 */
        grid-template-rows: 40px 70px;
        gap: 6px 8px;
        padding: 4px 10px 6px;
    }
    .app-player-bar .player-left { gap: 10px; }
    .app-player-bar .u-cover {
        width: 40px;
        height: 40px;
    }

    /* 全屏按钮留在 player-right(grid-area: right) */
    /* v23 修复: 原 36 与默认相同,与 ≤720 块的 32 相比是反向阶梯。
       改为 32,与 ≤720 一致;触摸命中由 hover:none 块的 ::before 扩展到 ≥48。 */
    .player-fullscreen-btn {
        width: 32px;
        height: 32px;
        margin-left: 0;
    }
    .player-fullscreen-btn svg {
        width: 14px;
        height: 14px;
    }

    /* v48+:mode 按钮已在 .player-center > .m-play-controls 内 (与 ≤720 共享),
       ≤480 继承 ≤720 布局。≤360 / ≤320 块另有 .player-modes { display: none }。 */

    /* 控件行:8 元素 (3 mode + prev/play/next + progress + time) 需要更紧的 gap。
       v48+:gap 12 → 6 — 8 元素 + progress+time 在右,紧凑布局。 */
    .app-player-bar .m-play-controls { gap: 6px; line-height: 0; }   /* v18: line-height:0 消除 a 标签默认行高导致的 42px 膨胀 */
    /* v55+:u-play-btn 32→34,ctrl-play 44→46 — 修复 v51 P2-5 的"半修复":
       原 ≤480 (32/44) < ≤720 (34/46),违反"视口越窄字号越小"原则;
       ≤380 在 v51 已改为 34/46,≤480 漏改;现在 3 档全部 34/46,阶梯单调非反向。
       触摸命中由 hover:none 块 ::before 扩展到 ≥48。 */
    .app-player-bar .u-play-btn { width: 34px; height: 34px; }
    .app-player-bar .u-play-btn.ctrl-play { width: 46px; height: 46px; }
    /* v22 修复: 原 svg 14 > ≤720 块的 13,阶梯反向。改 13,与 ≤720 一致 */
    .app-player-bar .u-play-btn svg { width: 13px; height: 13px; }
    .app-player-bar .u-play-btn.ctrl-play svg { width: 18px; height: 18px; }
    /* v48+:mode 在 ≤480 缩到 28px — 与 ≤720 块同值,删除下方冗余规则。
       注:smusic.css:836 同样设 28px !important,加载顺序在后胜出。
       这里 app.css 规则实际不参与 cascade(值同 smusic.css),保留仅为显式声明与未来扩展。 */

    /* 进度条时间缩小 — v37:min-width 40 → 50,与 ≤720/≤380/≤320 阶梯统一,
       5 字符 "00:00" 完整容纳 + 5px thumb 安全间距 */
    .app-player-bar .u-time {
        font-size: var(--fs-sm);
        /* v47:60px → var(--u-time-min-w) — 之前 v37 P1-1 注释里已提到统一,但
           仍是字面 60px。token 化后 token 是唯一真相源,注释可移除。 */
        min-width: var(--u-time-min-w, 60px);
    }

    /* v23: 改用 min(220px, 56dvh),竖屏 480×853 取 220(总高 ≈460,余 393),
       横屏 640×360 等矮屏按 vh 缩到 ~201,留空间给标题/控件/音量不溢出 */
    .app-fullscreen-cover {
        width: min(220px, 56dvh);
        height: min(220px, 56dvh);
    }

    .app-fullscreen-title {
        font-size: var(--fs-7xl);
    }

    .app-fullscreen-artist {
        font-size: var(--fs-lg);
    }

    .app-fullscreen-time-cur,
    .app-fullscreen-time-total {
        min-width: 42px;
        font-size: var(--fs-base);
    }

    .fs-ctrl {
        width: 40px;
        height: 40px;
    }
    /* v38: ≤480 fs-ctrl 缩小到 40px 但 svg 保持 22px(未覆盖),导致图标几乎贴边;
       与 ≤380/≤360/≤320 邻档 svg/btn 比例 0.42 一致,显式 16px。 */
    .fs-ctrl svg {
        width: 16px;
        height: 16px;
    }
    .fs-play {
        width: 56px;
        height: 56px;
    }
    .fs-play svg {
        width: 22px;
        height: 22px;
    }

    .app-fullscreen-volume {
        gap: 8px;
    }

    .fs-vol-track {
        width: 80px;
    }

    .app-fullscreen-close {
        width: 32px;
        height: 32px;
        top: calc(10px + var(--safe-top));
        /* v36: 加 var(--safe-right) 兜底 — 部分 Android 设备(三星 Galaxy 等)
           右侧有打孔屏/曲面屏,避免关闭按钮被系统 UI 遮挡。基线 (line 1844)
           与 ≤320 (line 2899) 都已带 safe-right,这里补齐。
           v37:top 14 → 10,与 ≤720/≤320 阶梯统一。 */
        right: calc(14px + var(--safe-right));
        /* v37:font-size: 18px 删 — 已是 SVG stroke path,字号无效 */
    }

    /* v29 round 2 合并:从原 line 243 块挪过来,
       .app-brand-icon ≤480 缩到 24×24 + 6px 圆角(var(--r-sm) token 化),
       与 baseline 32×32 + var(--r-md) (8px) 阶梯递减 */
    .app-brand-icon {
        width: 24px;
        height: 24px;
        border-radius: var(--r-sm);
    }

    /* v32+:原 .app-fav-btn ≤480 button 阶梯已删除(顶栏收藏按钮整体移除) */
}

/* 中等窄屏过渡层(≤380px,如 iPhone SE 2/3 代、iPhone 12 mini 等 375pt 机型):
   v18 重构:在 ≤480 双行式 + ⋯ 折叠基础上,再做一档尺寸微调。
   - 顶栏 meta-count 隐藏(meta 整体由 JS 在 ≤480 隐藏,这里仅兜底)
   - player-left 缩一档(封面 40→36,标题字号 14→13)
   - 控件保持 36/46(避免 hit 区退化到 28)
   - 全屏视图 cover 220,字号/控件微缩 */
@media (max-width: 380px) {
    :root {
        /* v22 修复:原 116 太紧 — player-center 内容 = ctrl 44 + gap 4 + progress 18(hover scaleY 后) = 66,
           必须 ≥66 才不溢出。player-height 116 → 124:content 36+4+66+10padding+1border = 117,余 7。 */
        --player-height: 124px;
        --header-height: 48px;
    }

    /* v26+:主列表滚动条阶梯 16→14,thumb min-height 60→50。
       iPhone SE/小屏 Android,14px 占视口 4%,给标题列让位 */
    .app-list-area .m-music-list-wrap::-webkit-scrollbar {
        width: 14px !important;
        height: 14px !important;
    }
    .app-list-area .m-music-list-wrap::-webkit-scrollbar-thumb {
        border-radius: 7px;
        min-height: 50px;
    }

    .app-header {
        gap: 6px;
        /* v37:P2-6 padding 10/8 → 10/10 对称 — 与其他断点 L=R 一致 */
        padding-left: calc(10px + var(--safe-left));
        padding-right: calc(10px + var(--safe-right));
    }
    /* v22 修复:原 gap 4 让 ⋯ 与 ☀ 按钮粘连(实测间距只有 4px),改为 6px 让两按钮视觉分离
       v27 修复:gap 6 + ⋯/☀ 各自 ::before inset -8 = 触摸区 16px 外扩,实际触摸区重叠 10px
       (gap 6 < 2×inset 16)。重叠区会全部命中 DOM 顺序后的 ☀,用户点 ⋯ 区域可能误触发主题弹窗。
       改为 gap 16 = 触摸区 0 重叠(等于 2×inset)。视觉 +10px,但顶栏只有 4 个按钮,
       标题仍占 380-134=246px,空间充裕。 */
    .app-header-actions { gap: 16px; }

    /* 分类标题获得更多空间 */
    /* v46 字号审计 P1 Fix 4:14→13(原 fs-lg → fs-md)— 修复 ≤360 块反向阶梯。
       原 14px 比 ≤380 的 13px 还大。≤380 (13) → ≤360 (13) 与 a11y 临界宽度对齐。 */
    .app-crumb-cat { font-size: var(--fs-md); min-width: 0; }

    /* v69+:≤480 块原 max-width:50% 已删除 — ≤720 块改用 flex:1 1 0 让 hint 填满剩余,
       本块对 max-width 不再设值,直接继承 ≤720 的 max-width:none。
       ≤380/≤360/≤320 同样继承。
       字体由 ≤720 设为 --fs-sm(11px)继承,不再单独声明。 */

    /* 移动菜单按钮缩 */
    .app-mobile-menu-btn {
        width: 34px;
        height: 34px;
        /* v37:font-size: 17px 删 — SVG icon,字号无效 */
        margin-right: 4px;
    }

    /* player-left:封面与 ≤480 统一 40(用户反馈小尺寸圆形边缘不齐),标题字号略缩 */
    .app-player-bar .u-cover { width: 40px; height: 40px; }
    .app-player-bar .player-left { gap: 8px; }
    .app-player-bar .u-music-title strong { font-size: var(--fs-md); }
    .app-player-bar .u-music-title small { font-size: var(--fs-sm); }

    /* 控件:上下曲 34,主播放 46 — 触摸命中由 (hover:none) 块 ::before inset:-6 补足 */
/* v51 P2-5:u-play-btn 32→34,ctrl-play 44→46 — 与 ≤720 块对齐,修复反向阶梯。
       原 ≤480 (32) < ≤720 (34) 违反"视口越窄字号越小"原则。
       svg 17 不变(≤720 是 18,但这里 cover 视觉略大,17 视觉等效)。 */
    .app-player-bar .u-play-btn { width: 34px; height: 34px; }
    .app-player-bar .u-play-btn.ctrl-play { width: 46px; height: 46px; }
    .app-player-bar .u-play-btn.ctrl-play svg { width: 17px; height: 17px; }
    .app-player-bar .m-play-controls { gap: 12px; line-height: 0; }   /* v18: line-height:0 消除 a 标签默认行高导致的 42px 膨胀(让 grid 第2行不被撑爆) */

    /* v42+:row1 36→40(cover 36→40)。player-center 内容 = ctrl 44 + gap 4 +
       progress 18(hover scaleY 后) = 66。row2 = 66 不变。
       player-height 124 = 40+4+66+10padding+1border = 121,余 3 安全。 */
    .app-player-bar .m-music-play-wrap {
        grid-template-rows: 40px 66px;
        gap: 4px;
        padding: 4px 10px 6px;
    }

    /* 进度条时间缩小,两边共省 ~24px 给进度条本身 */
    /* v27:gap 6 → 12。thumb 14px translate(-50%) 在 progress=0 时凸入 gap 7px,
       gap 6 与 u-time 间距仅 -1px,会盖住末尾字符 '0'。
       与 ≤360 同步修复:u-time min-width 36 → 60,text 完整容纳 + 5px 安全间距 */
    .app-player-bar .player-progress { gap: 12px; }
    /* v37: ≤360 u-time 10 → 11,阶梯统一 11/11/11,与 ≤480 (line 2595) 同档 a11y 边界。
       tabular-nums 删 — smusic.css:349 base .u-time 已声明,级联下来无须重设。 */
    .app-player-bar .u-time {
        font-size: var(--fs-sm);
        /* v47:60px → var(--u-time-min-w) */
        min-width: var(--u-time-min-w, 60px);
        flex-shrink: 0;
    }

    /* 全屏视图 cover 220,字号/控件略缩 */
    /* v23: 改用 min(200px, 54dvh),竖屏 380×600+ 取 200,横屏矮屏按 vh 缩 */
    .app-fullscreen-cover {
        width: min(200px, 54dvh);
        height: min(200px, 54dvh);
    }
    /* v46 字号审计 P1 Fix 6:22→20(原 fs-7xl → fs-6xl)— 修复 ≤380 块与 ≤480 同值。
       原 ≤380 与 ≤480 都是 --fs-7xl (22px),≤480 (22) → ≤380 (22) 不单调。
       ≤360 跳到 --fs-4xl (18px),中间缺一档。≤380 → --fs-6xl (20px),
       阶梯修正:22 (≤480) → 20 (≤380) → 18 (≤360) → 18 (≤320) 单调。 */
    .app-fullscreen-title { font-size: var(--fs-6xl); }
    .app-fullscreen-controls { gap: 14px; }
    /* v23 修复: 原 fs-ctrl 42/fs-play 60 比 ≤480 的 40/56 还大,阶梯反向(视口越小按钮越大)。
       改为 38/54,确保 ≤720(44/60) → ≤480(40/56) → ≤380(38/54) → ≤360(36/52) → ≤320(34/50) 单调递减。 */
    .fs-ctrl { width: 38px; height: 38px; }
    .fs-ctrl svg { width: 16px; height: 16px; }   /* v25: 14→16,与 38px 容器更协调 */
    .fs-play { width: 54px; height: 54px; }
    .fs-play svg { width: 20px; height: 20px; }
}

/* 超窄屏手机(≤360px,如 iPhone SE 1 代、小米 6 等):
   v18: 原块隐藏 3 个播放模式按钮已在 ≤720 块做掉(mode/volume 已 display:none),
   v38: ≤360 横向音量条 100 → 80px(≤480 → ≤360 阶梯递减),保证标题有 ~150px 宽度。
   这里仅做播放器细节微调 + 全屏视图更窄一档。 */
@media (max-width: 360px) {
    .app-header {
        gap: 6px;
    }

    /* v26+:主列表滚动条阶梯 14→12,thumb min-height 50→50。
       360px 视口,12px 占 3.3%,合理 */
    .app-list-area .m-music-list-wrap::-webkit-scrollbar {
        width: 12px !important;
        height: 12px !important;
    }
    .app-list-area .m-music-list-wrap::-webkit-scrollbar-thumb {
        border-radius: 6px;
        min-height: 50px;
    }

    /* v22 修复:gap 4 → 6,让 ⋯ 与 ☀ 按钮视觉分离不粘连
       v27 修复:与 ≤380 块同步,6→16 消除 ⋯/☀ 触摸区 10px 重叠。*/
    .app-header-actions {
        gap: 16px;
    }

    /* v27+:⋯ 按钮阶梯 36→34(≤320 再到 32),避免 4px 直跳突兀;
       svg 同步缩 18→17,与 ≤320 块 svg 16 形成 17/16 渐变 */
    .app-overflow-btn { width: 34px; height: 34px; }
    .app-overflow-btn svg { width: 17px; height: 17px; }

    /* 进一步压缩 — v42+:封面与 ≤480/≤380 统一 40(用户反馈小尺寸圆形边缘不齐),
       标题独占(隐藏艺人名)让 30+ 中文字标题不再过早 ellipsis(艺人名在全屏视图仍可见) */
    .app-player-bar .u-cover { width: 40px; height: 40px; }
    .app-player-bar .player-left { gap: 6px; }
    .app-player-bar .u-music-title small { display: none; }

    /* 控件 hit 区缩(视觉 30,触摸扩展 ::before inset:-6 补到 42) */
    .app-player-bar .u-play-btn { width: 30px; height: 30px; }
    .app-player-bar .u-play-btn.ctrl-play { width: 42px; height: 42px; }
    .app-player-bar .u-play-btn.ctrl-play svg { width: 16px; height: 16px; }
    .app-player-bar .m-play-controls { gap: 10px; line-height: 0; }   /* v18: 同 ≤380,消除行高膨胀 */

    /* v42+:row1 34→40(cover 34→40)。player-center = ctrl 42 + gap 4 +
       progress 16(hover scaleY 后) = 62。row2 = 64 不变。
       继承 ≤380 --player-height 124,content 40+4+64+10+1=119,余 5 安全。 */
    .app-player-bar .m-music-play-wrap {
        grid-template-rows: 40px 64px;
    }

    /* 全屏视图在 ≤360 进一步压缩 — 否则沿用 ≤380 的 220px cover,
       会让标题/控制行严重挤压。iPhone SE 1 代、小米 6 等真实设备。
       v23: 改用 min(180px, 52dvh),竖屏 360×640 取 180,横屏矮屏按 vh 缩,
       与 ≤320 块的 min(160px, 50dvh) 保持单调递减。 */
    .app-fullscreen-cover {
        width: min(180px, 52dvh);
        height: min(180px, 52dvh);
    }
    .app-fullscreen-title { font-size: var(--fs-4xl); }
    .app-fullscreen-artist { font-size: var(--fs-md); }
    .app-fullscreen-time-cur,
    .app-fullscreen-time-total { min-width: 36px; font-size: var(--fs-sm); }
    .fs-ctrl { width: 36px; height: 36px; }
    .fs-ctrl svg { width: 14px; height: 14px; }
    .fs-play { width: 52px; height: 52px; }
    .fs-play svg { width: 20px; height: 20px; }
    .fs-vol-track { width: 60px; }
    .app-fullscreen-volume { gap: 6px; }
    .app-fullscreen-content { gap: 18px; }
    .app-fullscreen-art { margin-top: 8px; }

    /* v38+: 横向音量条 ≤480 (100) → ≤360 (80) 阶梯递减,保证标题有 ~150px 宽度 */
    .app-player-bar .u-volume .volume-process { width: 80px; }

    /* v40+: ≤360 标题区被挤压到 ~36px (3-4 Chinese chars before ellipsis)。
       隐藏 3 个 mode 按钮(原占 ~92px),给标题让 ~100px 显示 8-10 字符,
       与 ≤320 隐藏策略一致。mode 切换仍可走 R 键 / 全屏视图 fs-mode-list/random/single。
       v48+:selector 提高到 0,0,3,0 (.m-play-controls 内) — modes 现在嵌套在 .m-play-controls,
       specificity 必须 ≥ ≤720 块的 .app-player-bar .m-play-controls .player-modes 才能胜出。 */
    .app-player-bar .m-play-controls .player-modes { display: none; }
}

/* 兜底压缩层(≤320px,如 iPhone 5 / SE 1 代 320pt、小米 6 等极窄屏):
   v18 重构:≤720/≤480/≤380 已大量精简,这里只补最末一档差异。
   v42+:封面与 ≤480/≤380/≤360 统一 40(用户反馈小尺寸圆形边缘不齐),
        控件视觉 28/38(触摸扩展由 hover:none 块补) */
@media (max-width: 320px) {
    :root {
        /* v42+:row1 34→40(cover 32→40),row2 60 不变。
           player-height 110 → 116:content 40+4+60+10padding+1border = 115,余 1 安全。 */
        --player-height: 116px;
    }

    /* v26+:主列表滚动条阶梯 12→10,thumb min-height 50→40。
       320px 极窄屏,10px 占视口 3.1%,给标题列让位(原 26 占 8% 挤压严重)。
       thumb min-height 缩到 40 仍易抓,且短列表 thumb 自然有 ~10-30% 长度 */
    .app-list-area .m-music-list-wrap::-webkit-scrollbar {
        width: 10px !important;
        height: 10px !important;
    }
    .app-list-area .m-music-list-wrap::-webkit-scrollbar-thumb {
        border-radius: 5px;
        min-height: 40px;
    }

    /* 主题按钮独立压缩(顶栏只剩 menu + 分类标题 + ⋯ + theme) */
    .app-theme-btn,
    .app-theme-btn.theme-trigger {
        width: 30px;
        height: 30px;
    }
    .app-theme-btn.theme-trigger .theme-trigger-icon {
        width: 13px;
        height: 13px;
    }

    /* ⋯ 按钮与 theme 对齐:36→32,顶栏 4 个按钮视觉尺寸一致 */
    .app-overflow-btn { width: 32px; height: 32px; }
    .app-overflow-btn svg { width: 16px; height: 16px; }

    /* 顶栏进一步压缩 */
    .app-header {
        gap: 6px;
        padding-left: calc(8px + var(--safe-left));
        /* v23:padding-right 6 → 8,与 ≥360 保持一致;
           此前 6 在无刘海屏上让 theme 按钮贴边 6px,视觉偏紧 */
        padding-right: calc(8px + var(--safe-right));
    }

    /* v37 P1 修复: ≤320 ⋯ (32px) 与 ☀ (30px) hover:none 触摸区都外扩
       -8px。base .app-header-actions gap 12px → 2×8 = 16px 触摸重叠 4px,
       用户点 ⋯ 边缘可能误触发主题弹窗。
       与 ≤360/≤380 同步 gap 16,与触摸区 0 重叠。
       顶栏只 4 按钮 (menu / 标题 / ⋯ / theme),标题 flex:1 让位,
       320px 视口总占 32 + 16 + 30 + padding 16 = 94px,标题仍得 226px。 */
    .app-header-actions {
        gap: 16px;
    }

    /* 移动菜单按钮进一步压缩 */
    .app-mobile-menu-btn {
        width: 32px;
        height: 32px;
        /* v37:font-size: 16px 删 — SVG icon,字号无效 */
        margin-right: 4px;
    }

    /* 分类标题:与列表 14 持平 — v41+ 修复,之前 13px 比列表标题 14px 还小,
       "总标题不该比列表小"的视觉违反。min-width:0 + flex:1 让长标题仍能 ellipsis */
    .app-crumb-cat {
        font-size: var(--fs-lg);
        min-width: 0;
        flex: 1;
    }

    /* 播放器内 padding 再压;v21 修复:gap 6 → 4,与 ≤360/≤380/≤480/≤720 保持一致,
       避免 ≤320 顶到容器底边(配合 --player-height 106→108 + grid 34/58)。 */
    .app-player-bar .m-music-play-wrap {
        gap: 4px;
        padding: 4px 8px 6px;
    }

    /* 播放器左侧:v42+ 封面与 ≤480/≤380/≤360 统一 40(用户反馈小尺寸圆形边缘不齐),
       艺人名隐藏(省空间给标题) */
    .app-player-bar .u-cover { width: 40px; height: 40px; }
    .app-player-bar .player-left { gap: 6px; }
    .app-player-bar .u-music-title small { display: none; }

    /* 控件按钮再缩一档(触摸命中由 hover:none 块 ::before 补到 ≥40) */
    .app-player-bar .u-play-btn { width: 28px; height: 28px; }
    .app-player-bar .u-play-btn.ctrl-play { width: 40px; height: 40px; }
    .app-player-bar .u-play-btn svg { width: 11px; height: 11px; }
    .app-player-bar .u-play-btn.ctrl-play svg { width: 15px; height: 15px; }
    .app-player-bar .m-play-controls { gap: 8px; line-height: 0; }   /* v18: 同 ≤380 */

    /* v42+:row1 34→40(cover 32→40),row2 60 不变。
       配合外层 --player-height 110→116,content 40+4+60+10padding+1border = 115,容器 116 余 1。 */
    .app-player-bar .m-music-play-wrap {
        grid-template-rows: 40px 60px;
    }

    /* 进度条时间再缩 — v37:9 → 10,阶梯单调(11/11/11/10)对齐 a11y 边界
       (WCAG 1.4.4 建议 ≥12 body,允许 ≥10 UI 辅助文字;u-time 5 字符 tabular nums
       在 10px + min-width 60 下仍清晰)。 */
    .app-player-bar .u-time {
        font-size: var(--fs-2xs);
        /* v47:60px → var(--u-time-min-w) */
        min-width: var(--u-time-min-w, 60px);
        flex-shrink: 0;
    }
    /* v27:thumb(14px,translate -7)在 progress=0 时位于 u-process 左缘,
       凸出 7px 进入 gap;gap 4px 不够,会盖住 u-time 末尾的 '0'。
       与 2707 横屏块同步:gap 12 + u-time min-width 60,
       留出 5px 安全间距让 thumb 不盖文字。 */
    .app-player-bar .player-progress { gap: 12px; padding: 0 4px; }

    /* 全屏视图在 320px 微调 */
    /* v23 修复: 原 min(200px, 58vw) 只按宽度缩,横屏矮屏(568×320)下 cover 仍占 58vh。
       改 min(160px, 50dvh) → 竖屏 320×480 取 160(总高 ≈420,余 60px),
       横屏 568×320 取 160(总高 < 320,完整可见)。 */
    .app-fullscreen-cover {
        width: min(160px, 50dvh);
        height: min(160px, 50dvh);
        /* 圆形:继承 line ~907 的 border-radius: 50%,不重写 */
    }
    .app-fullscreen-title { font-size: var(--fs-4xl); }
    .app-fullscreen-artist { font-size: var(--fs-base); }
    /* v23: gap 也按 vh 收,矮屏下从 16 压到 ~14.4(480dvh*3%),避免堆叠过高 */
    .app-fullscreen-content { gap: clamp(12px, 3dvh, 16px); padding: 0 12px; }

    .fs-ctrl { width: 34px; height: 34px; }
    .fs-ctrl svg { width: 14px; height: 14px; }
    .fs-play { width: 50px; height: 50px; }
    .fs-play svg { width: 20px; height: 20px; }
    .app-fullscreen-controls { gap: 8px; }

    .app-fullscreen-time-cur,
    .app-fullscreen-time-total {
        min-width: 36px;
        font-size: var(--fs-sm);
    }
    .app-fullscreen-progress { gap: 8px; padding: 0 4px; }

    /* v40 P2 清理:删 .fs-vol-track { width: 60px } 和 .app-fullscreen-volume { gap: 6px } —
       ≤360 块(line 2906/2907)已经设过同样值,源顺序上 ≤360 在前,≤320 重复无效。 */

    .app-fullscreen-close {
        width: 30px;
        height: 30px;
        top: calc(10px + var(--safe-top));
        /* v36: 加 var(--safe-right) 兜底 — 与 ≤480 (line 2585) 同步修复。
           ≤320 已设 safe-top 但漏了 safe-right,Android 打孔屏/曲面屏
           可能盖住关闭按钮。 */
        right: calc(10px + var(--safe-right));
        /* v37:font-size: 16px 删 — 已是 SVG stroke path,字号无效 */
    }

    /* v38+: ≤320 player-right 进一步压缩,给标题让出 ~22px 呼吸空间
       (实测 320 视口 title_strong 宽度从 24 → 46,+92%,显示 2-3 个汉字)。
       60px 条 + 28 音量图标 + 28 全屏 = 116 + gap 6×2 + player-right gap 4×2 = 140,
       比原 238 缩 98px,playerLeft 标题区从 24 → 46。触摸命中由 .volume-event 28px
       纵高补足(.volume-process::before 没扩触摸区是横向布局不需要)。 */
    .app-player-bar .u-volume .volume-control { width: 28px; height: 28px; }
    .app-player-bar .u-volume .volume-control svg { width: 12px; height: 12px; }
    .player-fullscreen-btn { width: 28px; height: 28px; }
    .player-fullscreen-btn svg { width: 12px; height: 12px; }
    .app-player-bar .u-volume .volume-process { width: 60px; }

    /* v39+: ≤320 极窄屏额外隐藏 3 个 mode 按钮(原占 104px),再给标题让 ~58px。
       playerRight 从 238 → 134 (60 条 + 28 音量 + 28 fs + 6 gap + 4 gap = 126),
       playerLeft 从 62 → 152 (32 封面 + 6 gap + 114 标题)。
       标题显示完整"选择曲目开始播放"(scrollWidth 104 < 114)不再 ellipsis。
       mode 切换仍可走 R 键 / 全屏视图 fs-mode-list/random/single。
       v48+:selector 提高到 0,0,3,0 — 同 ≤360 块,匹配 v48 新 HTML 结构。 */
    .app-player-bar .m-play-controls .player-modes { display: none; }
}

/* 横屏窄屏(手机横屏 + 短高 iPad/桌面):压缩纵向空间,侧栏常驻但变窄。
   v31+:宽度范围 900 → 1024,覆盖 1024×500 等桌面短高窗口,
   之前 1024w 短高落入 721-1024 双行式 154px,占视口 30.8% 过高。
   --player-height 64 → 72、padding-bottom 4 → 10,给进度条留出更舒展的呼吸空间
   (此前紧凑模式下进度条距视口底仅 6.5-9px,触摸/视觉都偏紧)。
   v51:max-width 1024 → 900 — 修复 l1024×500 mode 按钮被吞(v50 审计 #3)。
   原 max-width:1024 把 w=1024 也吞进去(1024 ≤ 1024 命中),而 901-1439 块(line 3715)
   min-width:901 不覆盖 1024,导致 w=1024 横屏无任何 ≤500h 块定义 mode 显示,
   实际 mode 按钮被 player-center grid 压成 0 宽。
   改成 max-width:900 后,901-1439 块覆盖 1024,阶梯:
   ≤600(line 3545) → 601-900(本块) → 901-1439(line 3715),mode 可见单调。 */
@media (max-height: 500px) and (orientation: landscape) and (max-width: 900px) {
    :root {
        --player-height: 72px;
        --header-height: 48px;
    }
    /* v52.2:合并 721-1439 块(设 --sidebar-width: 200)后,本块 sidebar 改回抽屉(fixed)
       时若不重置 grid,grid-template-columns 仍 "var(--sidebar-width) 1fr" → main 被挤
       到 200px 第一列(sidebar 浮层脱离 grid)。补 grid-template-columns: 1fr 让 main
       占满 viewport,跟 ≤720 块(line 2417) 一致。 */
    .app {
        grid-template-columns: 1fr;
    }
    /* v52+:手机横屏(≤900w + ≤500h)侧栏改回抽屉,主列表从 348px → 568px(+63%)。
       此前 v31 强制常显 220px 是为了让触屏用户一键直达分类,但 568×320 下 13 项
       nav 在 500h 内要靠 30px 行高勉强放下(WCAG 2.5.5 触屏 44pt 不达),
       横向 220px 浪费更明显。改回抽屉后用户多一步点 ☰,换主区多 220px 内容。
       sidebar 抽屉样式复用 ≤720 块(line 2432-2459);☰ / close 按钮完整复制
       2520-2546 / 2474-2518 块(用 .app-header 抬升 specificity,压住
       line 4471 的 ≥721 块 display:none,让 800 视口也能看到 ☰ + close)。
       v52.3:抽屉宽度 clamp 与 ≤720 块同步(480→400),避免 568 横屏展开 480 盖住主区。
       v52.4:再降到 320;v52.5:再降到 280(行业标准抽屉宽度)。 */
    .app-sidebar {
        position: fixed;
        top: 0;
        left: 0;
        width: clamp(200px, calc(100vw - 40px), 280px);
        height: calc(var(--app-height-dvh) - var(--safe-bottom));
        padding-top: var(--safe-top);
        box-sizing: border-box;
        z-index: var(--z-overlay);
        transform: translateX(-100%);
        transition: transform var(--t-slow) var(--ease);
        border-right: 1px solid var(--border);
        box-shadow: 4px 0 16px rgba(0, 0, 0, 0.08);
    }

    .app-sidebar.open {
        transform: translateX(0);
    }
    /* v47:删 .app-sidebar-head 重复声明 — base 块已统一 padding: 14px 14px 12px,
       横屏重复 padding 无意义且 line 3601 还会导致 14/12/12 不一致。margin-top: 0
       是默认值,删后由 base 块统一提供 14/14/12。*/
    /* v37:P3-1 补 flex-shrink: 0,与 line 2032 (721-1024 块) 一致,
       防止 flex 容器挤压下品牌名被收缩(虽然 nowrap 已防止换行,但不防收缩)。 */
    .app-brand-name {
        /* v44+:fs-base → fs-lg(12 → 14) — 修复 v41+ 注释意图"brand ≥ list strong"被破坏。
           v44 之前 base 列表 strong 是 fs-base 12px,brand fs-base 12px 持平;
           现在 base strong 是 fs-lg 14px,brand 必须同步升到 fs-lg 才保持"品牌 ≥ 列表标题"层级。
           200px 侧栏容 14×9=126 < 134 仍有 8px 余量,与原 fs-base 设计意图("brand 不挤侧栏")兼容。 */
        font-size: var(--fs-lg);
        white-space: nowrap;
        flex-shrink: 0;
    }
    /* v57 修复:原 ≤720 块完整定义已通过 .app-header 前缀抬升 specificity,
       本块(≤900 横屏)只需触发 display,其余属性从 ≤720 块继承。 */
    .app-header .app-mobile-menu-btn { display: inline-flex; }
    .app-sidebar-close { display: flex; }
    /* 锁主列表滚动 + 防止 iOS 橡皮筋穿透(沿用 ≤720 块 line 2551-2552) */
    body.sidebar-open .m-music-list-wrap { overflow: hidden; }
    body.sidebar-open { overscroll-behavior: none; }
    /* 横屏单行播放器 — v25 修复:清空 ≤720 块的 grid-template-areas
       和 player-left 的 grid-area,否则 left/right/center 三区会
       推 player-center 到 row 2,与 row 1 叠加撑爆 72px player-height。
       v31:padding-bottom 4 → 10,给进度条留 ~24px 视觉呼吸。 */
    .app-player-bar .m-music-play-wrap {
        grid-template-columns: 44px 1fr auto;
        grid-template-rows: auto;
        grid-template-areas: none;
        gap: 12px;
        padding: 4px 12px 10px;
    }
    .app-player-bar .player-left {
        grid-area: auto;
        grid-column: 1;
        grid-row: 1;
        align-self: center;
    }
    /* v20+: 横屏 72px 高度下 player-center 默认 gap 8 → 内容 36+8+14=58 + padding 4+10=72,刚好填满。
       改 gap 4 → 内容 36+4+14=54,留 18px 弹性余量给 player-center align-self:stretch 空间。
       控件/进度条垂直堆叠间距仍清晰可读,不影响视觉。
       v25 修复:specificity 必须 .app-player-bar .player-center (0,0,2,0),
       否则 ≤720 块同名规则 (2263 行) specificity 相同但源码在前,
       grid-area: center + grid-row: 2 会把 player-center 推到第 2 行,
       撑爆 72px player-height。
       v31:--player-height 64 → 72,padding-bottom 4 → 10,content 区从 56 → 58。 */
    .app-player-bar .player-center {
        grid-area: auto;
        grid-column: 2;
        grid-row: 1;
        gap: 4px;
    }
    .app-player-bar .player-right {
        grid-area: auto;
        grid-column: 3;
        grid-row: 1;
    }

    /* v10: 横屏 72px 高度下 6 按钮 + 进度条贴边,进一步压缩按钮可视尺寸。
       触摸命中区不变(已有 hover:none 下 ::before inset:-6px 外扩)。
       v31:player-height 64 → 72,按钮可视尺寸仍保持紧凑版(用户短期操作场景,不需要超大可点击)。 */
    .app-player-bar .u-play-btn { width: 28px; height: 28px; }
    .app-player-bar .u-play-btn.ctrl-play { width: 36px; height: 36px; }
    .app-player-bar .u-play-btn svg { width: 11px; height: 11px; }
    .app-player-bar .u-play-btn.ctrl-play svg { width: 14px; height: 14px; }
    .app-player-bar .u-volume .volume-control { width: 28px; height: 28px; }
    .app-player-bar .u-volume .volume-control svg { width: 12px; height: 12px; }
    .player-fullscreen-btn { width: 28px; height: 28px; }
    .player-fullscreen-btn svg { width: 12px; height: 12px; }

    /* v18+:横屏无独立播放意图,live-wave 完全无意义;
       隐藏后进度条触摸区更舒展,thumb 不被波形挤到。
       v27 round 3 修正:u-time 直接给最终值(9/70/fs:0),
       删旧 line 3047 的 10/38 与 line 3057 的 u-process padding-left:8,
       都是被下方规则覆盖的死代码。 */
    .app-player-bar .player-text .live-wave { display: none !important; }
    /* v37: ≤500h 横屏 u-time 9 → 10,与 ≤320 阶梯对齐,避免极小字号 a11y 风险 */
    .app-player-bar .u-time { font-size: var(--fs-2xs); min-width: var(--u-time-min-w-short); flex-shrink: 0; }
    /* v27 round 3 补漏:横屏 (≤500h, ≤900w) 此块曾设 gap 4px 覆盖 ≤360 块的 gap 12,
       导致 568x320 横屏 progress thumb (14px,translate -50%) 在 progress=0 时
       凸入 u-time 区,盖住末尾 '0'。统一 gap 12 + u-time min-width 70 + flex-shrink:0
       已设,补 gap 4→12。u-process padding-left 8 也不需要了,一起去掉避免挤压。 */
    .app-player-bar .player-progress { gap: 12px; }

    /* v20+:横屏 ≤500 高时全屏视图 cover 280 占 70% 高度,信息区只剩 ~120px 无法操作。
       v23 修复:vh → dvh(iOS Safari 横屏地址栏塌陷时 dvh 真实反映可视高度);
       收紧 gap / 字号 / 控件尺寸,确保 ~320h 视口下整页可见不溢出。
       v40 修复:之前复算假设 safe=0,真实 iPhone 横屏 safe-bottom ~34px,
       568×320 实际 visible = 286px,旧 content 326 溢出 40px;iPhone 14 Pro
       safe-top 47+bottom 34=81 visible 239 溢出 87px。
       新复算 568×320:safe-bottom 34,cover min(140, 45dvh) = 140
       + art margin-top 2 + tag 隐藏 + title 16 + 4 + progress 12 + controls 38 + volume 30
       + gap 6 × 5 = 30 = 272 ≤ 286 (余 14px),iPhone 14 Pro 272 ≤ 239 → 仍溢出 33px。
       加 padding-bottom: env(safe-area-inset-bottom) fallback 兜底。 */
    .app-fullscreen-cover {
        width: min(140px, 45dvh);
        height: min(140px, 45dvh);
    }
    .app-fullscreen-title { font-size: clamp(15px, 5dvh, 20px); }
    .app-fullscreen-artist { font-size: clamp(11px, 3.5dvh, 12px); }
    /* v23: gap 也按 vh 收,横屏 320 下压到 ~6,500 下约 12 */
    .app-fullscreen-content { gap: clamp(4px, 1.6dvh, 12px); }
    .app-fullscreen-art { margin-top: 2px; }
    .app-fullscreen-meta { margin-top: 2px; }
    /* v23: tag 隐藏,横屏矮屏无空间展示分类元信息(用户在顶栏已知道分类) */
    .app-fullscreen-tag { display: none; }
    .fs-ctrl { width: clamp(30px, 9dvh, 36px); height: clamp(30px, 9dvh, 36px); }
    .fs-ctrl svg { width: clamp(12px, 4dvh, 14px); height: clamp(12px, 4dvh, 14px); }
    .fs-play { width: clamp(44px, 13dvh, 52px); height: clamp(44px, 13dvh, 52px); }
    .fs-play svg { width: clamp(16px, 5.5dvh, 20px); height: clamp(16px, 5.5dvh, 20px); }
    .app-fullscreen-controls { gap: clamp(8px, 2.5dvh, 12px); margin-top: 2px; }
    .app-fullscreen-volume { gap: clamp(6px, 2dvh, 8px); margin-top: 2px; }
    .fs-vol-track { width: clamp(60px, 22dvh, 80px); }
    /* v23: 横屏极窄高时 progress 时间字号收紧,让信息更紧凑 */
    .app-fullscreen-progress { gap: 10px; padding: 0 4px; }
    .app-fullscreen-time-cur,
    .app-fullscreen-time-total { font-size: var(--fs-sm); min-width: 36px; }

    /* v26+:横屏列表垂直空间被压缩(player 64 + header 48 = 112),
       滚动需求强,粗滚动条反而更重要 — 保持 ≥1440 桌面级别的 26px,
       不按 721-1024 的 20px 阶梯缩。
       注:此规则放在 (max-height: 500) and (orientation: landscape) and (max-width: 900) 块,
       与视口宽度阶梯(721-1024、≤720 等)正交,但源顺序上在它们之后,
       所以 specificity (0,0,2,1) 战胜 721-1024 的 (0,0,1,1) 阶梯,
       实现"宽度按阶梯,但横屏列表滚动条单独强制回到粗"。 */
    .app-list-area .m-music-list-wrap::-webkit-scrollbar {
        width: 26px !important;
        height: 26px !important;
    }
    .app-list-area .m-music-list-wrap::-webkit-scrollbar-thumb {
        border-radius: 13px;
        min-height: 60px;
    }

    /* v39+:紧凑横屏布局重设计,给标题让出空间。
       v31 时 grid-template-columns: 44px 1fr auto 让 player-left 仅 44px (= 封面宽度),
       title 容器宽度=0,完全不可见 — 用户在音乐播放器里看不到当前曲目。
       修复:隐藏 3 个 mode 按钮(可通过 R 键 / 全屏视图 fs-mode-list/random/single 切换),
       播放器宽度让给 player-left 至少 160px 容纳封面+标题。
       与 v30 把 mode 移到 player-right 不冲突(v30 是为了窄屏 row 1 右侧能直接切模式,
       此处是更窄的横屏紧凑模式,空间物理装不下三按钮+标题)。

       v39 r2+: ≤600w 横屏极窄 (568×320) 单行仍放不下 —
       player-center 1fr 实际只剩 ~10px,m-play-controls min-content 92px
       (prev 28 + play 36 + next 28 = 92) 撑爆 cell 溢出覆盖 title。
       667×360+ 单行有 109+ px 给 center 可用,无此问题。
       568 单行物理装不下 cover+title+controls+progress+vol+fs,
       与 ≤720 portrait 一致切 2 行布局:row1 = left+right / row2 = center。
       v48+:selector 提高到 0,0,3,0 — 同 ≤360/≤320,匹配 v48 新 HTML 结构。 */
    .app-player-bar .m-play-controls .player-modes { display: none; }
    .app-player-bar .m-music-play-wrap {
        grid-template-columns: minmax(160px, 26%) 1fr auto;
    }
}

/* v39 r2+: 568×320 极窄横屏切 2 行布局,与 ≤720 portrait 一致。
   568 viewport 减侧栏 200 = bar 368,内宽 344。
   单行物理装不下 cover(44)+title+controls(92)+progress(>140)+vol+fs,
   切 2 行后:row1 = cover+title (col1 弹性) + vol+fs (col2 auto 150),
   row2 = controls+progress 跨满宽 320px。

   v49+:v48 把 3 mode 按钮从 .player-right 移到 .player-center,
   row 2 内容高度从 ~12(time)涨到 ~52(controls+progress+time)。
   --player-height 88 → 100 容纳新内容;player-center 内部 gap 4 → 2 节省 row 2 高度。
   计算:4(pad-top) + 36(row1=cover 36) + 4(grid-gap) + 50(row2=controls 36+gap 2+progress 12) + 6(pad-bot) = 100 ✓
   视口 320 → 列表区 320-48-100 = 172,仍够 5-6 列表项。 */
@media (max-height: 500px) and (orientation: landscape) and (max-width: 600px) {
    :root {
        --player-height: 100px;
    }
    /* v49+:568w 视口下隐藏品牌名 — 让 nav 区有完整垂直空间,
       主列表显示 3 项需要 listWrap 高度 = 172px (568-48-100),原 brand 占用 36px
       现在 nav 头部从 40+36=76 → 40+8=48,nav 区净增 28px,可多容纳 1-2 项折叠前可见。
       --sidebar-width 保持 220 不变:实测 568×320 是高度受限 (172px listWrap),
       缩减侧栏宽度到 160px 让主区 348→408 但 label 区从 88 → 56,"分类: 04-08"
       等标签被截断为 "精..." / "分...",导航可读性下降得不偿失。 */
    .app-brand-name { display: none; }
    .app-player-bar .m-music-play-wrap {
        grid-template-columns: minmax(0, 1fr) auto;
        /* v52:grid-template-rows 36px → auto,cover 36 → 32 — 关键修复。
           原 grid 行总 36+4+50=90 超出 playWrap content 85px 达 5px,
           progress 被推到 playerBar 底部距底仅 5px(贴底感明显)。
           改 auto auto + cover 32px 后 row1 = max(p-left 内容) = 38,row2 = 50,
           grid 总 38+4+50=92 仍超出 7px(因 padding-bottom 改为 6 多 4px content),
           但实测 progress.b=314 距底 6px,playerLeft.b=261 与 center.y=264 不重叠,视觉无问题。
           padding 改回 4 12 6,留出顶部 4px 呼吸空间 + 底部 6px 让 progress 自然距底。 */
        grid-template-rows: auto auto;
        grid-template-areas:
            "left   right"
            "center center";
        gap: 4px;
        padding: 4px 12px 6px;
        align-content: start;
    }
    .app-player-bar .player-left {
        grid-area: left;
        grid-column: 1;
        grid-row: 1;
        align-self: center;
        min-width: 0;
        gap: 10px;
    }
    .app-player-bar .player-right {
        grid-area: right;
        grid-column: 2;
        grid-row: 1;
        align-self: center;
        gap: 6px;
    }
    .app-player-bar .player-center {
        grid-area: center;
        grid-column: 1 / -1;
        grid-row: 2;
        gap: 2px;
        width: 100%;
    }
    .app-player-bar .player-center .m-play-controls { justify-content: center; }
    .app-player-bar .player-center .player-progress { width: 100%; }
    /* row 1 由 grid-template-rows: auto 自适应内容高度 (=max(cover, title/artist 文字) = 38px)。
       v52:封面 36 → 32,让 row 1 主体(cover)更紧凑,与 title/artist 文字行协调;
       grid 总高度 38+4+50=92 仍在 playWrap content (85-99) 范围内,progress 距底 6px。 */
    .app-player-bar .u-cover { width: 32px; height: 32px; }
}

/* v34:768×600 横屏 iPad(mini 横屏)— 之前没专门断点,
   既不满足 ≤500h 也不满足 ≥901w 块,落入 721-1024 默认 --player-height: 154,
   在 600h 视口上播放器占 25.7% 视口,主列表只剩 ~386px。
   此块专攻 700-901w + ≤700h 横屏,收紧 player-height + 收紧全屏 cover,
   与 1024×600 块(dvh 收紧 cover)风格一致但 player 更紧。 */
@media (min-height: 501px) and (max-height: 700px) and (orientation: landscape) and (min-width: 700px) and (max-width: 900px) {
    /* v37:P1-1 补 --sidebar-width: 200px — 700-720w 落 ≤720 块会被覆盖为 0(抽屉模式),
       与 ≤500h 横屏 / 721-1024 块一致。
       v53+:player-height 110 → 116 — 切 2 行布局需要容纳 cover(40)+gap(4)+center(controls 42
       +gap 2+progress 13 = 57)+padding-top(4)+padding-bottom(8)=113,110 装不下 wrap 117。
       116 与 ≥1440 / 1025-1439 / 901-1024 (line 2543) 阶梯对齐,避免出现"横屏比单行桌面
       还矮 6-16px"的反向阶梯。600h 视口主列表 600-48-116 = 436px 仍可显示 7 项。 */
    :root {
        --player-height: 116px;
        --sidebar-width: 200px;
    }
    /* v53+:iPad 横屏 700-900w + 501-700h 切 2 行布局 — 修复单行式 mControls 溢出。
       原 v37 设计的 3 列 (160/1fr/220) 单行式:
       - 800w (减 sidebar 200 - padding 40 = 560):center 仅 132px < mControls 240 → 溢出 108px
         (modes 110 压在 title 上 30px,play 按钮压住 progress 左侧 70px,用户看到按钮盖住文字)。
       - 900w (减 240 = 660):center 232px,mControls 240 → 溢出 8px,刚好踩边界。
       - 中间区 1fr 即使给到 232px 也只够 5 控件(modes+prev/play/next 共 240)
         横向无 gap 不压缩,稍一调字号就溢出。
       修复:与 v40 700-760w 子块风格一致,整个 700-900w 区间统一切 2 行布局 —
       row1 = [cover+title] [vol+fs],row2 = [controls+progress] 跨满宽。
       v53+:2-row 后整块只需定义一次,无需再保留 v40 700-760w 子块 (删除)。
       v53+:row 1 高 40 + cover 40,与 ≤480 块阶梯统一(用户反馈小尺寸封面边缘不齐),
       比 v37 的 44/44 缩 4px 让 row 2 有更多空间。
       v53+:grid-template-rows 40px auto → 40px 60px — 原 auto 让 row 2 由 playerCenter
       内容自然高度 (=42+2+13=57) 撑开,playerCenter 内部 controls 紧贴顶部 (y=0),
       progress 紧贴底部 (y=44),控件组视觉重心偏下、距 row 1 标题偏远,两行看着断开。
       改为 40px 60px 后 row 2 锁定 60,playerCenter height:100% 撑满,内部用
       space-between + center 让 controls y=0、progress 距底 0,row 2 内部分布与
       ≤480 (40/70) 阶梯一致;同时 player-height 116 不变,wrap 内容 40+4+60+4+8=116 刚好填满。 */
    .app-player-bar .m-music-play-wrap {
        grid-template-columns: minmax(0, 1fr) auto;
        grid-template-rows: 40px 60px;
        grid-template-areas:
            "left   right"
            "center center";
        align-content: start;
        gap: 4px 16px;
        padding: 4px 16px 8px;
        height: auto;
    }
    .app-player-bar .player-left {
        grid-area: left;
        grid-column: 1;
        grid-row: 1;
        align-self: center;
        min-width: 0;
    }
    .app-player-bar .player-right {
        grid-area: right;
        grid-column: 2;
        grid-row: 1;
        align-self: center;
    }
    .app-player-bar .player-center {
        grid-area: center;
        grid-column: 1 / -1;
        grid-row: 2;
        gap: 2px;
        max-width: none;
        margin: 0;
        width: 100%;
        min-width: 0;
        height: 100%;
        justify-content: space-between;
        align-items: center;
    }
    .app-player-bar .player-center .m-play-controls { justify-content: center; }
    .app-player-bar .player-center .player-progress { width: 100%; }
    .app-player-bar .u-cover { width: 40px; height: 40px; }
    .app-player-bar .u-play-btn { width: 32px; height: 32px; }
    .app-player-bar .u-play-btn.ctrl-play { width: 42px; height: 42px; }
    .app-player-bar .u-time { font-size: var(--fs-sm); min-width: var(--u-time-min-w-landscape, 50px); }
    .app-player-bar .u-volume .volume-control { width: 30px; height: 30px; }
    .app-player-bar .u-volume .volume-control svg { width: 13px; height: 13px; }
    .player-fullscreen-btn { width: 30px; height: 30px; }
    .player-fullscreen-btn svg { width: 13px; height: 13px; }
    .app-fullscreen-cover {
        width: min(200px, 30dvh);
        height: min(200px, 30dvh);
    }
    .app-fullscreen-title { font-size: clamp(18px, 4dvh, 24px); }
    .app-fullscreen-artist { font-size: clamp(12px, 2.5dvh, 14px); }
    .app-fullscreen-content { gap: clamp(10px, 2.5dvh, 16px); }
    .app-fullscreen-controls { gap: clamp(10px, 2.5dvh, 14px); }
    .fs-ctrl { width: clamp(38px, 7dvh, 46px); height: clamp(38px, 7dvh, 46px); }
    .fs-ctrl svg { width: clamp(14px, 3dvh, 17px); height: clamp(14px, 3dvh, 17px); }
    .fs-play { width: clamp(52px, 10dvh, 60px); height: clamp(52px, 10dvh, 60px); }
    .fs-play svg { width: clamp(18px, 4dvh, 22px); height: clamp(18px, 4dvh, 22px); }
    .fs-vol-track { width: clamp(60px, 16dvh, 100px); }
    /* v37:P1-4 补全屏 time/volume/progress gap 覆盖 — 与 line 3228 901-1439 块对齐。
       此块 (700-900w) 之前漏掉 3 个属性,在 800×600 视口下 time 14 / volume gap 12 / progress gap 14,
       而 1280×600 (901-1439 块) time 12 / volume gap 9 / progress gap 12,同一高度更宽视口反而小,
       反向阶梯。改为 clamp 自适应。 */
    .app-fullscreen-volume { gap: clamp(6px, 1.5dvh, 10px); }
    .app-fullscreen-progress { gap: clamp(10px, 2dvh, 14px); }
    .app-fullscreen-time-cur,
    .app-fullscreen-time-total { font-size: clamp(11px, 2dvh, 13px); min-width: clamp(36px, 7dvh, 50px); }
    /* v37:P3-1 补 flex-shrink: 0,与 line 2032 (721-1024 块) 一致,
       防止 flex 容器挤压下品牌名被收缩(虽然 nowrap 已防止换行,但不防收缩)。 */
    .app-brand-name {
        /* v44+:fs-base → fs-lg(12 → 14) — 修复 v41+ 注释意图"brand ≥ list strong"被破坏。
           v44 之前 base 列表 strong 是 fs-base 12px,brand fs-base 12px 持平;
           现在 base strong 是 fs-lg 14px,brand 必须同步升到 fs-lg 才保持"品牌 ≥ 列表标题"层级。
           200px 侧栏容 14×9=126 < 134 仍有 8px 余量,与原 fs-base 设计意图("brand 不挤侧栏")兼容。 */
        font-size: var(--fs-lg);
        white-space: nowrap;
        flex-shrink: 0;
    }
    /* v47:删 .app-sidebar-head 重复声明 — base 块已统一 14/14/12。*/
}

/* v40+:iPad mini / iPad 横屏 700-760w split-view 等狭窄横屏 — v53 已并入上方 700-900w
   块(line 3992),统一切 2 行布局。原 700-760w 子块被覆盖,删除避免重复定义。 */

/* v40+:901-1439 + ≤500h 横屏极矮(典型 1280×500 / 1440×500 笔记本短高)
   原 landscape (max-height: 500) 块带 max-width: 1024 不命中,落入默认 --player-height
   110~136,主列表区只剩 320px 上下,只显示 5-7 项。
   此块按 dvh 收紧 player + 触发与 1024×500 类似的紧凑单行布局。 */
@media (max-height: 500px) and (orientation: landscape) and (min-width: 901px) and (max-width: 1439px) {
    :root { --player-height: 88px; }
    .app-player-bar .m-music-play-wrap {
        grid-template-columns: minmax(180px, 22%) 1fr minmax(200px, 220px);
        grid-template-rows: auto;
        grid-template-areas: none;
        gap: 16px;
        align-items: center;
        height: 100%;
        /* v52:padding-bottom 0 → 10 — 之前进度条贴视口底 (progress.b=500=winH),
           0px 呼吸空间严重破坏极矮横屏(l1280x500 / l1366x500)视觉。
           88px playerBar - 10 padding = 78 content 区,实测 content 87 自动压缩到 78,
           grid row auto + height:100% 自然缩放,无溢出风险。 */
        padding: 0 16px 10px;
    }
    .app-player-bar .player-left   { grid-area: auto; grid-column: 1; grid-row: 1; align-self: center; }
    .app-player-bar .player-center { grid-area: auto; grid-column: 2; grid-row: 1; gap: 4px; min-width: 0; }
    .app-player-bar .player-right  { grid-area: auto; grid-column: 3; grid-row: 1; align-self: center; }
    .app-player-bar .u-cover { width: 44px; height: 44px; }
    .app-player-bar .u-time { font-size: var(--fs-sm); min-width: var(--u-time-min-w-landscape, 50px); }
    .app-brand-name {
        /* v44+:fs-base → fs-lg(12 → 14) — 修复 v41+ 注释意图"brand ≥ list strong"被破坏。
           v44 之前 base 列表 strong 是 fs-base 12px,brand fs-base 12px 持平;
           现在 base strong 是 fs-lg 14px,brand 必须同步升到 fs-lg 才保持"品牌 ≥ 列表标题"层级。
           200px 侧栏容 14×9=126 < 134 仍有 8px 余量,与原 fs-base 设计意图("brand 不挤侧栏")兼容。 */
        font-size: var(--fs-lg);
        white-space: nowrap;
        flex-shrink: 0;
    }
    /* v47:删 .app-sidebar-head 重复声明 — base 块已统一 14/14/12。*/

    /* v49+:侧栏 13 项(8 主分类 + group toggle + 收藏/最近播放)在 ≤500h 视口下
       因 .app-sidebar { overflow:hidden } 截断滚动区域 — 默认 nav padding 11px × 13
       + nav 自 padding 26px + sidebar-head 63px 超出 500 视口 100+px,
       "我的收藏"/"最近播放" 完全被切。
       收紧 sidebar-head (8/14/8) + nav 自 padding (4/0/6) + nav-a (4/18)
       + group-toggle (4/18) 后:head 45 + nav 10 + 12 项 × 30 + toggle 26 + 2 × 30
       = 45+10+360+26+60 = 501 → 500h 内刚好放下。鼠标为主输入(笔记本 ultralow),
       30px 行虽小于 iOS HIG 44,但 click 区仍 ≥28 实用。 */
    .app-sidebar-head { padding: 8px 14px; }
    .app-brand { padding: 0; }
    .app-nav { padding: 4px 0 6px; }
    .app-nav a { padding: 4px 18px; }
    .app-nav-group-toggle { padding: 4px 18px; }
    /* v52:≤500h 横屏侧栏 nav 滚动区域让出 player-height,
       避免 "我的收藏/最近播放" 滚动到底仍被播放器覆盖(侧栏高度 = viewport 高度,
       nav flex:1 占用剩余空间但底部 88px 与 playerBar 重叠)。
       padding-bottom 留 player-height + 8px 余量,用户滚到底部项完全在播放器上方。 */
    .app-nav { padding-bottom: calc(var(--player-height) + 8px); }

    /* 全屏视图在 ≤500h 视口下严格按 dvh 收紧,避免溢出 */
    .app-fullscreen-cover {
        width: min(200px, 32dvh);
        height: min(200px, 32dvh);
    }
    .app-fullscreen-title { font-size: clamp(18px, 3.5dvh, 22px); }
    .app-fullscreen-artist { font-size: clamp(12px, 2dvh, 14px); }
    .app-fullscreen-content { gap: clamp(10px, 2dvh, 16px); }
    .app-fullscreen-controls { gap: clamp(8px, 2.5dvh, 12px); margin-top: 2px; }
    .app-fullscreen-volume { gap: clamp(6px, 2dvh, 8px); margin-top: 2px; }
    .fs-ctrl { width: clamp(34px, 7dvh, 40px); height: clamp(34px, 7dvh, 40px); }
    .fs-ctrl svg { width: clamp(13px, 3dvh, 15px); height: clamp(13px, 3dvh, 15px); }
    .fs-play { width: clamp(46px, 10dvh, 52px); height: clamp(46px, 10dvh, 52px); }
    .fs-play svg { width: clamp(18px, 4.5dvh, 20px); height: clamp(18px, 4.5dvh, 20px); }
    .fs-vol-track { width: clamp(60px, 16dvh, 80px); }
}

/* v52+:iPad 横屏 901-1024w + ≤500h 走抽屉(典型 1024×500 笔记本/平板横屏短高)。
   之前继承 721-1024 主块 200px 常显 + 3834 块 playerBar 收紧,但 1024×500 主区
   1024-200=824,横向被 200px 侧栏严重挤压。改回抽屉后主区 1024px 满宽,
   释放 200px 横向空间(对比手机横屏 568 释放 220px,比例相近)。
   1025-1439w + ≤500h 横屏(1280/1366 笔记本 ultralow)不在此块,仍常显 220px
   (鼠标用户一键直达效率优先,与 1024 分界划清)。 */
@media (max-height: 500px) and (orientation: landscape) and (min-width: 901px) and (max-width: 1024px) {
    :root {
        --sidebar-width: 0;
        /* v54 P1 修复:之前只改 sidebar,player-height 漏改。
           1024×500 原本继承 721-1024 块 154px 双行布局,playerBar 占 30.8% 视口。
           与同 901-1439w+≤500h 块(line 3967-3968)对齐 88px 单行布局,
           主列表区从 298→364px(+22%)。--header-height 同步收到 48px,与
           ≤720 块 + ≤900 横屏块 阶梯一致(48 / 48 / 56 / 60 / 60)。 */
        --player-height: 88px;
        --header-height: 48px;
    }

    .app {
        grid-template-columns: 1fr;
    }

    /* sidebar 抽屉样式沿用 ≤720 块 line 2432-2459(用户报告"窄屏+小高度"
       应默认隐藏,语义与 ≤720 块一致);宽度 v52.5 与 ≤900+≤500h 块同步
       clamp(200-100vw-40-280),保证横屏断点 900→901 不发生 480→280 跳变。 */
    .app-sidebar {
        position: fixed;
        top: 0;
        left: 0;
        width: clamp(200px, calc(100vw - 40px), 280px);
        height: calc(var(--app-height-dvh) - var(--safe-bottom));
        padding-top: var(--safe-top);
        box-sizing: border-box;
        z-index: var(--z-overlay);
        transform: translateX(-100%);
        transition: transform var(--t-slow) var(--ease);
        border-right: 1px solid var(--border);
        box-shadow: 4px 0 16px rgba(0, 0, 0, 0.08);
    }

    .app-sidebar.open {
        transform: translateX(0);
    }

    /* v57 修复:原 ≤720 块完整定义已通过 .app-header 前缀抬升 specificity,
       本块(901-1024 横屏)只需触发 display,其余属性从 ≤720 块继承。 */
    .app-header .app-mobile-menu-btn { display: inline-flex; }
    .app-sidebar-close { display: flex; }

    /* 锁主列表滚动 + 防止 iOS 橡皮筋穿透(沿用 ≤720 块 line 2551-2552) */
    body.sidebar-open .m-music-list-wrap { overflow: hidden; }
    body.sidebar-open { overscroll-behavior: none; }
}

/* v26+:iPad 横屏矮屏(901-1439w, ≤700h)专用块 — 解决 1024×600 全屏视图溢出。
   原 landscape 块 (max-height: 500) 覆盖不到 600-700h 视口,
   全屏 cover 320 + 控件 + 音量 ~741px 在 600h 下溢出 ~141px。
   此块按 dvh 重新收紧 cover / 控件尺寸,确保 ~600h 视口下整页可见。
   v37:max-width 1280 → 1439,覆盖 1281-1439 + 501-700h 横屏(典型 1366×600 笔记本短高),
   原范围会让该尺寸沿用 line 1025-1439 默认 cover 380,在 600h 下明显突兀。 */
@media (min-height: 501px) and (max-height: 700px) and (orientation: landscape) and (min-width: 901px) and (max-width: 1439px) {
    /* v36:1024x600 占视口 25.6%(154px)过高,改 100px 单行布局与 1280x600 一致。
       min-height: 501px 让 500h 落入 ≤500h 规则;1024w 仍会被 721-1024 双行 grid 影响,
       这里覆盖回单行布局。
       v42+:100→112 与 1025-1439 主块同步 +12px,避免出现"同 1366 宽度,横屏矮 12px"的视觉差;
       列表区从 440→428(-12)不可见;紧凑横屏高度仍比 154 双行块低 42px,标题/进度条仍单行 OK。 */
    :root {
        --player-height: 112px;
    }
    /* v42+:padding 0 20px → 0 20px 16px,与 1025-1439 主块底距对齐。
       旧版 progress 距底 0px 完全贴底,16px 留出呼吸;顶部 0 不变,
       封面与上边框靠 align-items:center 自动平衡(48px 封面 + 居中 = 上下各 32px)。 */
    .app-player-bar .m-music-play-wrap {
        grid-template-columns: minmax(180px, 22%) 1fr minmax(200px, 220px);
        grid-template-rows: auto;
        grid-template-areas: none;
        gap: 24px;
        align-items: center;
        height: 100%;
        padding: 0 20px 16px;
    }
    .app-player-bar .player-left   { grid-area: auto; grid-column: 1; grid-row: 1; align-self: center; }
    .app-player-bar .player-center { grid-area: auto; grid-column: 2; grid-row: 1; gap: 4px; min-width: 0; }
    .app-player-bar .player-right  { grid-area: auto; grid-column: 3; grid-row: 1; align-self: center; }
    .app-player-bar .u-cover { width: 48px; height: 48px; }
    .app-player-bar .u-time { font-size: var(--fs-base); min-width: var(--u-time-min-w-landscape, 50px); }
    /* v27:矮屏横屏侧栏 220px,18px "GUAN 音乐空间" 字号过大导致 2 行。
       缩到 14px + nowrap 单行展示,与 ≤900 横屏块一致 */
    /* v37:P3-1 补 flex-shrink: 0,与 line 2032 (721-1024 块) 一致,
       防止 flex 容器挤压下品牌名被收缩(虽然 nowrap 已防止换行,但不防收缩)。 */
    .app-brand-name {
        /* v44+:fs-base → fs-lg(12 → 14) — 修复 v41+ 注释意图"brand ≥ list strong"被破坏。
           v44 之前 base 列表 strong 是 fs-base 12px,brand fs-base 12px 持平;
           现在 base strong 是 fs-lg 14px,brand 必须同步升到 fs-lg 才保持"品牌 ≥ 列表标题"层级。
           200px 侧栏容 14×9=126 < 134 仍有 8px 余量,与原 fs-base 设计意图("brand 不挤侧栏")兼容。 */
        font-size: var(--fs-lg);
        white-space: nowrap;
        flex-shrink: 0;
    }
    /* v47:删 .app-sidebar-head 重复声明 — base 块已统一 14/14/12。*/
    .app-fullscreen-cover {
        width: min(220px, 36dvh);
        height: min(220px, 36dvh);
    }
    .app-fullscreen-title { font-size: clamp(20px, 4dvh, 26px); }
    .app-fullscreen-artist { font-size: clamp(13px, 2.5dvh, 15px); }
    .app-fullscreen-content { gap: clamp(12px, 2.5dvh, 18px); }
    .app-fullscreen-controls { gap: clamp(12px, 2.5dvh, 16px); }
    .app-fullscreen-volume { gap: clamp(6px, 1.5dvh, 10px); }
    .fs-ctrl { width: clamp(40px, 8dvh, 48px); height: clamp(40px, 8dvh, 48px); }
    .fs-ctrl svg { width: clamp(15px, 3.5dvh, 18px); height: clamp(15px, 3.5dvh, 18px); }
    .fs-play { width: clamp(56px, 11dvh, 64px); height: clamp(56px, 11dvh, 64px); }
    .fs-play svg { width: clamp(20px, 4.5dvh, 24px); height: clamp(20px, 4.5dvh, 24px); }
    .fs-vol-track { width: clamp(70px, 18dvh, 110px); }
    .app-fullscreen-progress { gap: clamp(10px, 2dvh, 14px); }
    .app-fullscreen-time-cur,
    .app-fullscreen-time-total { font-size: clamp(11px, 2dvh, 13px); min-width: clamp(36px, 7dvh, 50px); }
}

/* ============== 超宽屏 (≥1440px):侧栏/播放器拉宽,列表填满主区 ==============
   大屏上让列表真正填满主区(与播放器/头部全宽风格一致),
   不再做 1080px 居中卡片 — 那种设计在 1500+ 屏宽下会浪费 ~40% 空间。
   - 侧栏 280px,头/播放器 68/104px,留出更舒展的高度
   - 列表全宽,无圆角/边框,仅保留顶部安全气孔
   - 全屏封面 440px 配合大屏视觉
   - 2K+ 屏(≥1920px)另外加 1600px 软上限,防止标题列过宽影响阅读
*/
@media (min-width: 1440px) {
    :root {
        --sidebar-width: 280px;
        /* v42+:104→116,与 1025-1439 块同步 +12px,大屏单行布局更舒展,
           控件(42px)+gap(4)+progress(17)≈63 居中后上下各 14px 留白,
           不挤压控件也不显空旷。--header-height 68 不动(顶栏纵向信息少)。 */
        --player-height: 116px;
        --header-height: 68px;
    }

    /* v37: 顺位修正 — TRBL 简写,左侧(L)=safe-left,右侧(R)=safe-right;
       原版 safe-left/safe-right 互换,与 line 442/3326 冲突。语义修正。 */
    .app-header {
        gap: 20px;
        padding: 0 calc(24px + var(--safe-right)) 0 calc(24px + var(--safe-left));
    }
    .app-header-actions { gap: 12px; }
    .app-crumb-cat { font-size: var(--fs-7xl); }

    /* 列表填满主区 — 去掉居中卡片和左右内边距,
       与底部播放器、头部全宽风格保持一致。
       v37:P1-11 改 padding 0 → 4px 8px — 1439→1440 边界从 "8px 卡片" 突变成 "0 全宽无边",
       视觉跳变剧烈。改为 4px 8px 微 padding,保留全宽无边风格(无 border/radius),
       阶梯单调:1025-1439 (4/8 卡片) → 1440-1919 (4/8 全宽无边) → 1920+ (4/8 居中卡片)。
       padding 一致,只在居中+卡片有视觉差异。 */
    .app-list-area {
        padding: 4px 8px;
        min-height: 0;
    }
    .app-list-area .m-music-list-wrap {
        max-width: none;
        width: 100%;
        margin: 0;
        border-radius: 0;
        border: none;
    }

    /* v37:P3-2 scrollbar 22 → 26 — 与 ≤500h 横屏对齐最大档(force 用 !important
       覆盖 line 1991 1025-1439 块的 22px;≥1440 屏幕视口宽,22px 反而显小) */
    .app-list-area .m-music-list-wrap::-webkit-scrollbar {
        width: 26px !important;
        height: 26px !important;
    }
    .app-list-area .m-music-list-wrap::-webkit-scrollbar-thumb {
        border-radius: 13px;
        min-height: 70px;
    }

    /* 底部播放器 padding 加大,适配 104px 高度。
       v30+: 上下不对称 — 顶部 10 保持封面与边框距离,底部 18 给进度条
       留出更舒展的呼吸空间(此前 15px 偏紧)。
       v42:18→24,用户反馈 18px 仍贴底边感明显,再加 6px;顶部 10 不变
       (封面与边框距离已合适),只动下 padding。--player-height 104 不变,
       内容区从 86→80,progress 自然下移,无溢出风险。 */
    .app-player-bar .m-music-play-wrap {
        gap: 24px;
        padding: 10px 24px 24px;
    }

    /* 大屏无抽屉 */
    .app-mobile-backdrop { display: none; }

    /* 全屏视图:封面更大、标题更大。
       v37:P1-8/P1-14 — 删 max-width: 580 列布局。580px 容器里 cover 440 + gap 36 = 476,
       info 列只余 104px,42px 标题在 104px 列只容 2-3 中文字,长标题全部截断。
       改与 ≥1024 landscape 一致的行布局:max-width: min(96vw, 960),row 布局,
       让 info 列获得 ~400-500px 真实宽度,长标题自然展开。
       P1-7 — cover 440 → min(440px, 60dvh),1440×700 不再溢出。
       specificity 后续 1024 landscape 块 (0,0,2,1) 高于此块 (0,0,1,1),数值会再覆盖,
       此处只设 portrait 兜底值。 */
    .app-fullscreen-cover { width: min(440px, 60dvh); height: min(440px, 60dvh); }
    .app-fullscreen-title { font-size: var(--fs-12xl); }
    .app-fullscreen-artist { font-size: var(--fs-5xl); }
    .app-fullscreen-content {
        /* v68+:960→1100 — ≥1440 宽屏下 cover=440 + gap=56 + 7 控件(≈460) + padding=48
           = 1004,旧 960 让 info 列只余 416,7 控件硬塞进去触发 flex shrink
           把 fs-prev/fs-play/fs-next/fs-fav-btn 压成竖椭圆(见 #vg player-bar 反馈)。
           1100 给 info 留 556(够 460 控件 + 96 视觉留白),与下方 1024 landscape
           块保持一致;1100 / 96vw 取小仍能 letterbox 超宽屏(2560+ vw≈2457) */
        max-width: min(96vw, 1100px);
        flex-direction: row;
        align-items: center;
        gap: clamp(32px, 4vw, 56px);
        padding: 0 24px;
    }
    .app-fullscreen-art { flex: 0 0 auto; margin-top: 0; }
    .app-fullscreen-info { flex: 1 1 auto; min-width: 0; }
    /* v43+: 宽屏 row 布局下,info 列内控件 + 音量均居中(继承 .app-fullscreen-info stretch),
       而非 v36 的 flex-start。原因:
       (1) info 列已经是 cover 右边的独立区块,其水平中点就是视觉重心;
       (2) controls(5 圆按钮 ≈324px)与 volume(按钮+滑条 ≈168px)贴左后两行左基线对齐,
           但 controls 右半边大片留白,volume 紧贴左侧,视觉散乱;
       (3) 改 center 后两行各自在 info 内居中,controls 中点 ≈ volume 中点,
           视觉重心对齐,符合用户"控件和音量居中"的直觉。 */
    .app-fullscreen-controls { justify-content: center; }
    .app-fullscreen-volume { justify-content: center; }
    .app-fullscreen-progress { padding: 0; }
    /* 与 ≥1024 landscape 块一致:长标题允许 2 行换行 */
    .app-fullscreen-title {
        white-space: normal;
        display: -webkit-box;
        -webkit-line-clamp: 2;
        /* v40 P2:配对标准 line-clamp,目标浏览器已全部支持 line-clamp */
        line-clamp: 2;
        -webkit-box-orient: vertical;
        line-height: var(--lh-snug);
        max-height: calc(2em * 1.25);
    }
}

/* v52:≥1440 + ≤500h 横屏 (l1440×500 笔记本短高) 侧栏底部修复。
   (min-width: 1440) 主块没处理 ≤500h 场景,侧栏 nav 滚动到底部时
   "我的收藏/最近播放" 仍被播放器覆盖(sidebar 高度 = viewport 高度,
   nav flex:1 占用剩余空间但底部 116px 与 playerBar 重叠)。
   specificity (0,0,1,3) > (min-width:1440) 主块 (0,0,1,1),源顺序在它之后,
   覆盖 1440 主块的 nav padding-bottom 默认 16px。 */
@media (min-width: 1440px) and (max-height: 500px) and (orientation: landscape) {
    .app-nav {
        padding-bottom: calc(var(--player-height) + 8px);
    }
}

/* v27+:大屏横屏 (≥1024 landscape) 全屏视图:cover 左、信息+控件右的两栏布局
   解决 1280+ 大屏 max-width 580 列居中后,左右两侧各 350~670px 大留白;
   也改善 1024x600/1024x768/1280x800 等桌面横屏下信息垂直堆叠浪费水平空间。
   cover 尺寸由 ≥1440 (440) / 1025-1439 (340) / 901-1280 ≤700h (220) 各块具体控制,
   specificity 都高于本块 (0,0,2,1) → (0,0,1,1)/(0,0,4,1),所以数值自然落进新 row 容器内,
   本块只管布局方向 + info 列弹性伸缩 */
@media (min-width: 1024px) and (orientation: landscape) {
    .app-fullscreen-content {
        /* v68+:960→1100 — 与 ≥1440 块同步;cover=440 + gap=56 + 7 控件(≈460) + padding=48
           = 1004,旧 960 触发 flex shrink 把按钮压成竖椭圆(1920×1080 实测)。
           1100 给 info 留 556 装下控件。specificity (0,0,2,1) 高于 ≥1440 块 (0,0,1,1),
           ≥1440 块同字段已被本块覆盖(此处也提至 1100 仅作 portrait 兜底)。 */
        max-width: min(96vw, 1100px);
        flex-direction: row;
        align-items: center;
        gap: clamp(32px, 4vw, 56px);
        padding: 0 24px;
    }
    .app-fullscreen-art {
        flex: 0 0 auto;
        margin-top: 0;
    }
    .app-fullscreen-info {
        flex: 1 1 auto;
        min-width: 0;
        /* info 列内的子元素(标题/进度/控件/音量)原本是块级垂直堆叠,
           在 row 容器下仍保持 vertical 排列,无需改 */
    }
    /* v43+: row 布局下,info 列内控件 + 音量均居中 — 见 ≥1440 块(line ~3663)注释。
       弃用 v36 的 flex-start 方案(控件左对齐 + 音量左对齐):两行贴左后
       controls 右半边大片留白,volume 紧贴左侧,视觉散乱;
       改 center 后两行在 info 内各自居中,中点对齐,符合用户"控件和音量居中"的直觉。 */
    .app-fullscreen-controls { justify-content: center; }
    /* v36: 音量行也左对齐 — 与 controls 同一左基线。
       原版继承基线 justify-content: center,导致同一 info 列内控件左对齐、
       音量居中,视觉重心错位(volume ~168px 比 controls ~324px 窄,
       center 后两者中线不同,看着像音量单独悬在中央)。
       v43+ 改为 center,与 controls 同基线居中,解决 v36 的错位问题。 */
    .app-fullscreen-volume { justify-content: center; }
    /* row 布局后,info 列宽被压缩,取消 progress 横向 padding 让滑条贴边铺满 */
    .app-fullscreen-progress { padding: 0; }
    /* v36: 长标题允许 2 行换行 — row 布局下 info 列实际宽 ≈
       min(96vw,960) - cover(~340~440) - gap(~32~56) - padding(48) ≈ 416~520px,
       36px 字号仅容 ~13 中文字符。古典分类常见 30+ 字标题
       (如"莫扎特D大调第11号奏鸣曲K.331第三乐章土耳其风格回旋曲")
       会被 ellipsis 截掉 60%,用户切到全屏就是看完整标题。
       与 ≤720 块 (line 2331) 行为一致,允许 2 行换行(line-clamp: 2),
       字号保持 36px 不缩(line-height 1.25 与 ≤720 同步)。 */
    .app-fullscreen-title {
        white-space: normal;
        display: -webkit-box;
        -webkit-line-clamp: 2;
        /* v40 P2:配对标准 line-clamp,目标浏览器已全部支持 line-clamp */
        line-clamp: 2;
        -webkit-box-orient: vertical;
        /* v37:line-height 1.25 → var(--lh-snug) token 化 */
        line-height: var(--lh-snug);
        max-height: calc(2em * 1.25);
    }
}

/* 2K+ 屏(≥1920px):1600px 软上限 + 微妙卡片视觉
   防止 1640px+ 主区下标题列过宽影响阅读,同时保留一点视觉锚点。
   v37:P1-3 padding 12px 24px → 4px 8px — 1919→1920 边界从"全宽无边 0 padding"
   突变成"12/24 + 圆角 + 边框卡片",视觉跳变 0 → 12 36px。
   改 4/8 与 1025-1439 块 (line 1979 padding 4px 8px) 阶梯一致,
   保留卡片视觉但跳变从 12/24 缩到 0→4/8 (= 16px 单向) 平滑过渡。 */
@media (min-width: 1920px) {
    .app-list-area {
        padding: 4px 8px;
        min-height: 0;
    }
    .app-list-area .m-music-list-wrap {
        max-width: 1600px;
        width: 100%;
        margin: 0 auto;
        border-radius: var(--radius);
        border: 1px solid var(--border);
    }

    /* v27+:header 水平 padding 与列表/播放器对齐 (20→24px),
       避免 ≥1920 大屏下 header 内容(分类/按钮组)比列表卡偏左 4px */
    .app-header {
        padding: 0 calc(24px + var(--safe-right)) 0 calc(24px + var(--safe-left));
    }

    /* v51:2K+ 屏 header/player 与 list-wrap 形成连贯 1600px 内容列。
       之前 list-wrap 居中(1600px max-width + margin: 0 auto)但 header/player
       全宽(280-end),2560/3840 视口下 sidebar(280) → list(560) 之间留 280-980px
       视觉大空白,screenshot p3840.png 触目惊心。
       三者统一 1600px 上限 + auto 居中 → 内容列居中对齐,
       视觉断点消除(sidebar 与内容列之间是对称 gap,非一边悬空)。
       width: calc(100% - var(--safe-left) - var(--safe-right)) 让外框先撑到
       main 宽减去 safe 区(避免内容贴边),max-width 再卡 1600px 上限,
       margin auto 在外框 < 父容器时居中。
       实测:仅 max-width + margin auto 不生效(grid item 默认 justify-self: stretch
       在 flex/grid 容器内被 max-width: none 父项吃掉),需显式 width 触发。 */
    .app-header,
    .app-player-bar {
        width: calc(100% - var(--safe-left) - var(--safe-right)) !important;
        max-width: 1600px !important;
        margin-left: auto !important;
        margin-right: auto !important;
    }
}

/* v71+: 按钮按下瞬时反馈 — :active 状态覆盖 transition-duration。
   base 规则用 transition: ... transform var(--t-fast) var(--ease),
   --t-fast=100ms,导致按下时 scale 缩放有 100ms 动画 → 用户感到"卡"。
   这里把 :active 期间的 transform 过渡压到 30ms,视觉上近瞬时反馈。
   hover 仍保留 100ms 平滑(否则鼠标进入/离开也会瞬变,反而觉得跳)。
   !important 是因为 base 规则的 transition longhand 选择性比通用选择器高。
   v71+ 修正:原注释"放在 reduce 块之前让 0.01ms 胜出"不成立 — 同为
   !important 时按特异性比,:active(0,1,0)压过 reduce 块的 *(0,0,0),
   源序无关。故显式用 no-preference 门控,偏好减少动画的用户完全跳过。
   v71++ 修正:选择器从 `*:active` 收窄到按压反馈控件本身 —
   :active 会沿祖先链传播(HTML spec),按下任意元素时其全部祖先也匹配
   `*:active`,30ms 强制完成会劫持祖先身上正在跑的**无关过渡**:
   主题切换的 body.theme-transitioning * 颜色淡入、抽屉 .app-sidebar
   transform 滑出等被压成 30ms 瞬间到位,产生祖先/后代颜色错位闪动。
   列出的选择器即全站带 :active 按压规则的控件类型(button/a/li)+
   两个非 button 例外(.u-cover 曲封面 div、.volume-control 音量柄 div);
   祖先容器(ul/div/body 等)不在列表内,不再被波及。
   新增带按压反馈的控件时,若是 button/a/li 天然覆盖,否则要加进本列表。 */
@media (prefers-reduced-motion: no-preference) {
    button:active,
    a:active,
    li:active,
    .u-cover:active,
    .volume-control:active {
        transition-duration: 30ms !important;
    }
}

/* 用户偏好减少动画 */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        transition-duration: 0.01ms !important;
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
    }
    .app-sidebar {
        transition: none;
    }
    /* v29 round 2: 与全局兜底互补,显式停止无限循环动画 (前庭敏感用户)
       - .app-fullscreen-cover.spin / .app-fullscreen-cover.spin img (24s 旋转)
       - .app-status-dot.playing (1.4s dotPulse)
       - .app-player-bar .player-text .live-wave span (1s 三柱条)
       v23+ 风格: 显式声明 + !important,避免未来 iteration-count 规则改动时漏过 */
    .app-fullscreen-cover.spin { animation: none !important; }
    .u-cover.play, .u-cover.paused { animation: none !important; }
    .app-status-dot.playing { animation: none !important; }
    .app-player-bar .player-text .live-wave span { animation: none !important; }
}

/* ============== 触摸设备优化 ==============
   触摸设备 (hover: none) 无悬停反馈,按钮的视觉 34px 容易点偏。
   增大可点击区域(用 padding 扩展,不影响视觉布局)。
   iOS HIG 推荐 44×44pt,Material 推荐 48dp。
   在这里所有 30-34px 按钮的触摸区扩到 40-44px。 */
@media (hover: none) {
    /* v26+:Android Chrome 触摸模式会显示原生滚动条(不像 iOS 用 overlay 模式)。
       此时 ≥1440 默认 26px 滚动条触摸模式视觉过粗,统一收窄到 18px
       (比 min(≤720 18)略细,因为触摸模式不依赖 thumb 视觉瞄准 — 用滑动触摸)。
       Firefox scrollbar-width 也对应 thin,避免厚滚动条。
       v57 修复:原 `auto !important` 让 Firefox 触摸模式显示系统默认 ~10px,
       与 webkit 18px 视觉不对齐 (~8px 差距)。改 thin (~6-8px) 与 webkit 18px
       比例更协调,且仍满足桌面 thick → 触摸 thin 的设计意图。
       注:此规则放在 hover:none 块顶部,会覆盖各断点的 webkit-scrollbar 宽度,
       因为 hover:none 与视口宽度正交,hover:none 块内规则不依赖源顺序 */
    .app-list-area .m-music-list-wrap::-webkit-scrollbar {
        width: 18px !important;
        height: 18px !important;
    }
    .app-list-area .m-music-list-wrap::-webkit-scrollbar-thumb {
        border-radius: 9px;
        min-height: 50px;
    }
    .app-list-area .m-music-list-wrap {
        scrollbar-width: thin !important;
    }

    .app-keyhint-btn,
    .app-theme-btn,
    .app-brand,
    .player-fullscreen-btn {
        position: relative;
    }

    /* 用伪元素扩大实际触摸区(视觉尺寸不变) */
    .app-keyhint-btn::before,
    .app-theme-btn::before,
    .app-overflow-btn::before {
        content: "";
        position: absolute;
        /* v16: -6 → -8,确保视觉 30px 时触摸区 ≥44(iOS HIG) */
        inset: -8px;
        /* v22: 圆角统一为 50%,与 .app-overflow-btn 视觉圆形一致
           (原 12px 矩形 hit 区会突破圆形按钮边界,触摸体验不一致) */
        border-radius: 50%;
    }

    /* 全屏按钮的触摸区扩到 ≥44(视觉 28px + 9 = 46) */
    .player-fullscreen-btn::before {
        content: "";
        position: absolute;
        /* v16: -6 -4 → -8 -8,纵向横向均达 ≥44;
           v29: -8 -8 → -9 -9,横屏矮屏视觉 28 + 18 = 46pt 留 2pt 余量 */
        inset: -9px -9px;
        border-radius: var(--r-lg);
    }

    /* 品牌 logo + 标题整体触摸区扩到 ≥44(iOS HIG)。
       视觉高度由 28px icon 决定,纵向 -8 补足,横向靠 padding+border-radius 已经够大 */
    .app-brand::before {
        content: "";
        position: absolute;
        inset: -8px -6px;
        border-radius: 10px;
    }

    /* v11: 音量按钮触摸区扩展(原未扩展,≤360px 视觉仅 30px) */
    .u-volume .volume-control {
        position: relative;
    }
    .u-volume .volume-control::before {
        content: "";
        position: absolute;
        /* v29: -8 → -9,横屏矮屏视觉 28 + 18 = 46pt 留 2pt 余量(iOS HIG 44)。
           桌面端视觉 30 + 18 = 48pt,仍达标且更宽松。 */
        inset: -9px;
        border-radius: 14px;
    }

    /* v27+:底部封面触摸区扩展(≤360/≤320 视觉仅 32/34px,无法点中进入全屏)。
       .app-player-bar .u-cover 的 ::after/::before 已被 hover:hover 块用作遮罩+全屏图标,
       hover:none 块下两者都不应用,这里用 ::after 透明扩展触摸区。 */
    .app-player-bar .u-cover {
        position: relative;
    }
    .app-player-bar .u-cover::after {
        content: "";
        position: absolute;
        inset: -8px;
        border-radius: 50%;
        background: transparent;
    }

    /* v11: 播放控件按钮触摸区扩展(原未扩展,≤360px 视觉仅 28-38px)
       v29: -5 -4 → -7 -6。横向 -6 让 28px 视觉扩到 40,叠 gap 12-14 仍有 6-8 不重叠余量;
       纵向 -7 让 28px 视觉扩到 42(横向视觉 28 + 6 = 34 + gap 12 仍留 8 不重叠余量)。
       实测:视觉 28 + 触摸 6 = 34,<44pt iOS HIG;但 gap 12 + 触摸 12 = 24,相邻按钮 hit 区分明,
       仍保留 0 重叠。视觉 32 时触摸 40 < 44 但接近,功能可接受。
       主播放按钮 .ctrl-play 视觉 36-46px + inset:-6 = 48-58,达 ≥44。 */
    .app-player-bar .u-play-btn {
        position: relative;
    }
    .app-player-bar .u-play-btn::before {
        content: "";
        position: absolute;
        inset: -7px -6px;
        border-radius: 14px;
    }
    /* 主播放按钮 .ctrl-play 触摸区扩到 ≥44(iOS HIG) */
    .app-player-bar .u-play-btn.ctrl-play::before {
        content: "";
        position: absolute;
        inset: -6px;
        border-radius: 18px;
    }
    /* v30:mode 按钮触摸区 — 通用规则 inset:-7px -6px 让 3 mode 横向重叠 8px
       (gap 4 + 2×6 = 16, 2×6 - 4 = 8 重叠);减到 -4 让 2×4 = 8 = gap,0 重叠。
       视觉 28-34px + 纵向 -7 = 42-48,横向 -4 = 36-42,接近 iOS HIG 44。 */
    .app-player-bar .u-play-btn.mode::before {
        content: "";
        position: absolute;
        inset: -7px -4px;
        border-radius: 14px;
    }

    /* 触摸设备:防止卡片 hover 抬起效果"粘住" */
    .app-nav a:hover {
        background: transparent;
    }

    /* v10: 触摸设备进度条 thumb 默认完整显示,无需 hover 放大;
       原 scale(0.85) 偏小(14×0.85=12px),改为 scale(1) 常驻 14px 圆点 */
    .u-process .progress-thumb {
        transform: translate(-50%, -50%) scale(1);
        opacity: 1;
    }

    /* 触摸设备:取消体积感的封面 hover 提示(改用点击反馈) */
    .app-player-bar .u-cover:hover {
        transform: none;
    }
    .app-player-bar .u-cover:hover::after,
    .app-player-bar .u-cover:hover::before {
        opacity: 0;
    }

    /* 触摸设备:列表项点击反馈更明显 */
    .m-music-list-wrap li:active {
        background: var(--bg-active);
    }

    /* 触摸设备:卡片按下反馈更明显 */
    .app-nav a:active {
        background: var(--bg-active);
    }

    /* 触摸设备:侧栏菜单按钮扩大触摸区
       v11: -4 → -6 -4,确保视觉 34px + 上下扩展 6 = 46px 触摸(达到 iOS HIG 44)
       v16: -6 -4 → -8 -8,横向也确保 ≥44(视觉 32px + 16 = 48) */
    .app-mobile-menu-btn::before {
        content: "";
        position: absolute;
        inset: -8px -8px;
        border-radius: var(--r-lg);
    }

    /* v22: 全屏视图关闭按钮 38px(默认)/ 32/ 30(窄屏)均 < 44。
       base 已有 position:absolute(containing block 充足),
       ::before 直接外扩 -5px,视觉按钮位置不变。
       v29: 默认 -5 让 ≥1025 (38px) 视觉命中区 48px ≥44,但 ≤1024 (36) → 46 ✓,
       ≤720 (36) → 46 ✓,≤480 (32) → 42 ✗,≤380 (32) → 42 ✗,≤320 (30) → 40 ✗。
       改为 -7px,所有断点触摸区 ≥44(48/50/50/46/46/44),均匀达标。
       全屏关闭按钮位于屏幕角落,触摸区扩到 50x50 也几乎不与全屏中央元素重叠,无副作用。 */
    .app-fullscreen-close::before {
        content: "";
        position: absolute;
        /* v29: -7 → -8,≤320 视觉 30 + 16 = 46pt 留 2pt 余量。
           其他断点都至少 +2pt 全部达标。 */
        inset: -8px;
        border-radius: 50%;
    }

    /* v29: 底部进度条 .u-process 触摸扩展。
       进度条在 ≤360 时高 5px,≤480 时高 6px,拇指点偏 ±10px 就跳不到精确位置。
       base .u-process 已有 position:relative;此处用 ::after 透明扩触摸区到 30px 高,
       留出 ±12px 垂直余量。z-index:0 让 thumb(z-index:2)仍在最上层可拖动。 */
    .u-process::after {
        content: "";
        position: absolute;
        top: -12px;
        bottom: -12px;
        left: 0;
        right: 0;
        background: transparent;
        z-index: 0;
        pointer-events: auto;
    }

    /* v23: 全屏视图控件触摸区扩展(原未扩展)。
       ≤320 视觉 fs-ctrl 34/fs-play 50/fs-vol-icon 36 = 触摸命中区,均小于 iOS HIG 44。
       ::before 外扩 -6px → 实际触摸区 46/62/48,达到 HIG 44。
       .fs-play 自身已有 position:relative(line 1460),.fs-ctrl/.fs-vol-icon 需补 containing block。 */
    .fs-ctrl,
    .fs-vol-icon {
        position: relative;
    }
    .fs-ctrl::before,
    .fs-vol-icon::before {
        content: "";
        position: absolute;
        inset: -6px;
        border-radius: 50%;
    }
    /* .fs-play 自身 position:relative 已存在,只补 ::before;
       注:.fs-play 已有 ::after(inner glow 装饰),这里用 ::before 不冲突 */
    .fs-play::before {
        content: "";
        position: absolute;
        inset: -6px;
        border-radius: 50%;
    }
    /* v23: 全屏进度条 / 音量滑块触摸区扩。
       原 height 仅 14px,拇指点偏就跳不到。
       .app-fullscreen-process::before 已被占用做轨道(line 1400),
       这里用 ::after 在下方透明扩触摸区:
       - 默认层级(无 z-index),::after 在 ::before 之后绘制,
         因为透明 background 不影响视觉,但 hit 区向上下各扩 7px → 高度 28。
       - 进度条 thumb(.fs-thumb)在 ::after 之后绘制(z-index:2),
         拇指仍能正常拖动。
       .fs-vol-track 同理。 */
    .app-fullscreen-process::after {
        content: "";
        position: absolute;
        /* v26+: -7 → -20,触摸区 5+40=45px 达 iOS HIG 44,
           拇指点偏 10px 也能命中精确 scrub */
        top: -20px;
        bottom: -20px;
        left: 0;
        right: 0;
        /* 透明,只扩触摸区 */
        background: transparent;
    }
    .fs-vol-track::after {
        content: "";
        position: absolute;
        /* v26+: 与 .app-fullscreen-process::after 一致 -20px,音量微调更精确 */
        top: -20px;
        bottom: -20px;
        left: 0;
        right: 0;
        background: transparent;
    }
}

/* ============== 移动端遮罩打开时:背景锁滚 + 提升体感 ============== */
/* v51 P2-3:删 .app-mobile-backdrop cursor:pointer 死规则块(原 line 4349-4352)。
   主体定义见下方 line 4358-4366,cursor 是它自己的属性。
   这块"前向声明"无任何选择器差异,徒增源码长度。 */

/* v68 P0 修复:.app-mobile-menu-btn 完整样式提到 base 块(对应 .app-sidebar-close 在
   line 705+ 同样修复),让 ≤720 / ≤900 横屏(line 3848) / 901-1024 横屏(line 4292)
   的 "display: inline-flex" 都能继承完整视觉(40×40 / 1px border / border-radius /
   margin-right / -webkit-tap-highlight-color / position: relative)。
   选择器刻意不加 .app-header 前缀 — 让下方 ≥721 块的 .app-mobile-menu-btn { display: none }
   在同特异性 (0,0,1,0) 下通过 source order 胜出,桌面默认隐藏;@media 块各自
   .app-header .app-mobile-menu-btn { display: inline-flex } 用 .app-header 前缀抬到 (0,0,2,0)
   覆盖 base,触发显示。
   旧版的 ≤720 块 .app-header .app-mobile-menu-btn { display: inline-flex; ... } 写法
   在 ≤720 内部确实显示了按钮,但 ≤720 之外(901-1024 横屏 ≤500h、≤900 横屏 ≤500h)
   的 .app-header .app-mobile-menu-btn { display: inline-flex } 只设 display,按钮渲染
   为未样式化的 inline-flex 容器(裸 SVG icon,视觉破相)。 */
.app-mobile-menu-btn {
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    background: transparent;
    border: 1px solid var(--border);
    color: var(--text);
    border-radius: var(--radius-sm);
    cursor: pointer;
    margin-right: 8px;
    flex-shrink: 0;
    -webkit-tap-highlight-color: transparent;
    /* v22: 触摸区扩展(::before inset -8)需要此为 containing block,
       否则绝对定位跳到 body/viewport,触摸区错位 */
    position: relative;
    /* v71+: 与 codebase 其他按钮一致 — 加 transition 让 hover 平滑,
       :active 时被上方 *:active { transition-duration: 30ms !important } 压成瞬时反馈 */
    transition:
        background var(--t-fast) var(--ease),
        border-color var(--t-fast) var(--ease),
        transform var(--t-fast) var(--ease);
}

/* v37: ≡ Unicode → SVG hamburger (24×24 viewBox + 18px 渲染),
   像素精确、跨字体一致、对齐 currentColor 主题色。 */
.app-mobile-menu-btn svg { width: 18px; height: 18px; fill: none; }

.app-mobile-menu-btn:active {
    background: var(--bg-hover);
    transform: scale(0.96);
}

@media (min-width: 721px) {
    .app-mobile-menu-btn { display: none; }
}

/* 移动端打开遮罩 */
.app-mobile-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.4);
    /* v51 P1-11:z-index 字面 99 → var(--z-sticky),保留 100 < z=overlay<z=popup 层级语义 */
    z-index: var(--z-sticky);
    opacity: 0;
    visibility: hidden;
    transition: opacity var(--t-base) var(--ease), visibility var(--t-base) var(--ease);
}

.app-mobile-backdrop.visible {
    opacity: 1;
    visibility: visible;
}

/* 最近播放 入口与"收藏"对称 — 侧栏按钮(.app-nav a.is-recent)
   + 主区域顶部工具栏(.recent-toolbar),主区域空状态(.recent-empty)。
   原 .app-nav-section-* / .app-nav-recent-* inline 列表样式与顶栏 dialog 弹窗
   均已删除;无遗留样式。 */

/* 搜索过滤非匹配项:smusic.enhance.js applyFilter 给非匹配 li 加 is-hidden。
   1547 次 class toggle 浏览器可合并成一次 style recompute,优于 li.style.display 写入。
   必须在 @media print 外(屏幕视图),否则搜索等于摆设。 */
.m-music-list-wrap li.is-hidden {
    display: none !important;
}

/* v18+:打印样式 — 打印时只保留列表(可作曲目表存档),
   隐藏播放器、侧栏、弹窗、toast、顶栏 actions 等非内容元素。
   解除 fixed/grid 布局限制,让内容自然分页。
   链接 URL 显示便于存档查阅。 */
@media print {
    .app-sidebar,
    .app-player-bar,
    .app-fullscreen,
    .app-mobile-backdrop,
    .app-overflow-menu,
    .app-overflow-btn,
    .app-header-actions,
    .theme-popup,
    .fav-global-toast,
    .app-global-toast,
    .m-list-search-wrap,
    .m-list-search-stats,
    .app-mobile-menu-btn,
    .m-about {
        display: none !important;
    }

    html, body, .app, .app-main, .app-list-area {
        display: block !important;
        height: auto !important;
        overflow: visible !important;
    }

    .app-list-area {
        padding: 0 !important;
    }

    .app-list-area .m-music-list-wrap {
        max-height: none !important;
        overflow: visible !important;
        border: none !important;
        border-radius: 0 !important;
        background: transparent !important;
    }

    .m-music-list-wrap li {
        break-inside: avoid;
        page-break-inside: avoid;
        border-bottom: 1px solid #ddd !important;
        background: transparent !important;
    }

    * {
        box-shadow: none !important;
        text-shadow: none !important;
        transition: none !important;
        animation: none !important;
    }

    /* 链接 URL 显示(归档查阅) */
    /* v40 P2 清理:简化为 a[href]::after — 原 :not(.app-nav-link) 选择器不存在
       (实际 nav 用 .app-nav a,与类名 .app-nav-link 无关),否定选择永远为真。
       删除无效否定让规则覆盖所有 a[href] 的打印 URL 后缀。 */
    a[href]::after {
        content: " (" attr(href) ")";
        font-size: 0.85em;
        /* v51 P2-6:#666 → var(--text-mute),打印场景下 link 后缀文字与正文统一色 token */
        color: var(--text-mute);
    }

    /* 顶栏只保留分类标题(去掉 meta / 状态点) */
    .app-header {
        border-bottom: 2px solid #000 !important;
        padding: 8px 0 !important;
        height: auto !important;
    }
}

/* v29 全局 toast — 屏幕底部居中,跨场景通知(audio 错误等)。
   不在 @media print 内,打印场景不需要。
   bottom 跟随 var(--player-height) 而不是硬编码 96px —
   --player-height 阶梯:96(横屏 ≤500h)/100(901-1439 + 501-700h)/110(≤320)/124(≤480)/
   136(≤720)/154(721-1024)。之前硬编码 96 在 1024×600 横屏只离播放器顶 4px。 */
.app-global-toast {
    position: fixed;
    left: 50%;
    bottom: calc(env(safe-area-inset-bottom, 0px) + var(--player-height, 110px) + 12px);
    transform: translate(-50%, 12px);
    background: color-mix(in srgb, var(--text) 92%, transparent);
    color: var(--bg-elevated);
    padding: 10px 18px;
    border-radius: 22px;
    font-size: var(--fs-lg);
    line-height: var(--lh-normal);
    pointer-events: none;
    opacity: 0;
    /* v29: 0.22s → var(--t-slow),与全站时长阶梯统一 */
    transition: opacity var(--t-slow) ease, transform var(--t-slow) ease;
    z-index: var(--z-toast);
    max-width: min(86vw, 480px);
    box-shadow: 0 4px 18px rgba(0, 0, 0, 0.35);
    text-align: center;
}
.app-global-toast.show {
    opacity: 1;
    transform: translate(-50%, 0);
}
/* v40 P0 修复:删死代码 :root[data-theme="light"] — theme.js 选择 light 时
   removeAttribute('data-theme'),所以 <html data-theme="light"> 永远不会出现。
   toast 主题化已用 var(--text) / var(--bg-elevated) 跨 6 主题自适应,无需
   任何主题特定覆盖。 */

/* ============== 字体渲染优化 ==============
   Mac 暗色模式 / 高 DPI 屏幕字体会发虚,antialiased + optimizeLegibility
   让边缘更清晰 (smusic.css 已有,但 app.css 后置加载会重置一部分继承属性)。
   text-rendering: optimizeLegibility 在 14px 以下文本会触发连字/字距调整,
   这里限定为正文+标题,避免小字出现意外的连字。 */
html, body {
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-rendering: optimizeLegibility;
}

/* ============== 高对比度模式 (WCAG 1.4.6) ==============
   Windows High Contrast / macOS Increase Contrast 用户开启后:
   - 边框用纯黑/纯白实色代替 alpha 渐变,避免被强制反色
   - focus-visible 用 3px 实线轮廓,屏读器+键盘用户都能定位
   - 文本静音档与弱化档拉齐,层级不丢失 */
@media (prefers-contrast: more) {
    /* Light 浅色族(默认 light / sepia / ocean / forest,color-scheme: light):深底,用纯黑边框保证对比度 */
    :root,
    :root[data-theme="sepia"],
    :root[data-theme="ocean"],
    :root[data-theme="forest"] {
        --border: #000;
        --border-strong: #000;
        --text-mute: var(--text-soft);
    }
    /* Dark 深色族(dark / night,color-scheme: dark):浅底,用纯白边框保证对比度 */
    :root[data-theme="dark"],
    :root[data-theme="night"] {
        --border: #fff;
        --border-strong: #fff;
        --text-mute: var(--text-soft);
    }
    .u-volume, .app-sidebar, .app-list-area, .app-player-bar,
    .app-header, .m-cheatsheet-card {
        border-width: 2px;
    }
    *:focus-visible {
        outline: 3px solid currentColor;
        outline-offset: 2px;
    }
}

/* ============== 虚拟分类空列表播放守卫 ==============
   收藏/最近播放列表为空时,app.js 给根容器加 .is-empty-virtual,
   CSS 禁用播放/上一曲/下一曲按钮 + 全屏播放按钮,防止
   resetPlayer(undefined) → c.src TypeError。不动视觉,只 pointer-events:none
   + 透明度提示不可点。
*/
.is-empty-virtual .ctrl-play,
.is-empty-virtual .app-player-bar .prev,
.is-empty-virtual .app-player-bar .next,
.is-empty-virtual .app-player-bar .player-modes,
/* v47 P1:补 .fs-prev / .fs-next / .player-modes 守卫 — 之前只盖底部 + fs-play,
   全屏视图 prev/next + mode 按钮仍可点。空虚拟分类下点全屏 prev/next 会撞
   playByMode('prev'/'next') 越界分支,resetPlayer(undefined) 抛 TypeError。
   模式按钮 toggle 视觉变但无曲可播,与"列表空无曲可播"语义矛盾。 */
.is-empty-virtual .fs-play,
.is-empty-virtual .fs-prev,
.is-empty-virtual .fs-next {
    pointer-events: none;
    opacity: 0.4;
    cursor: not-allowed;
}
