/* ============================================================================
   视频转 GIF · 妄想天使工坊样式
   风格：贴纸 / 桌游风（源头：绝区零《妄想天使训练中》网页活动），
   Token 与组件配方严格遵循 DESIGN-REFERENCE.md —— 实色色块、粗描边、硬投影、
   零 UI 渐变。与同目录的 gifcompress / ncm2mp3 是同一套语言，改样式请三个页面一起看。

   本页专属的三块：
     ① 预览舞台（深紫底 + 金色取景框四角 + 裁剪框 + 外圈压暗）；
     ② 时间轴（深底槽 + 粉色选区 + 起止旗标 + 圆点滑块）；
     ③ 进度条（斜纹滚动填充）与结果相框（白框 + 贴纸角色）。
   ============================================================================ */

/* ---------- 品牌展示字体（印品鸿蒙体按本页文案裁的子集；正文走系统栈 0 字节） ---------- */
@font-face {
    font-family: '印品鸿蒙体';
    src: url('./fonts/yinpin-subset.woff2') format('woff2');
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}

/* ============================ 设计 Token ============================ */
:root {
    /* 背景（粉白奶油三阶） */
    --bg-page: #fdeef4;
    --bg-card: #fff9fc;
    --bg-card-2: #ffd9e9;
    --bg-deep: #ffe9f3;
    --bg-light: #fff7fb;
    --bg-ink: #262040;
    --ink: #221f33;

    /* 品牌色 */
    --primary: #ee77a9;
    --accent-pink: #ee77a9;
    --accent-gold: #f8e179;
    --gold-text: #c98a1b;
    --cta-stroke: #7a2e4d;
    --mint: #36ba82;
    --mint-soft: #d8f5ea;
    --mint-line: #9ad9be;
    --mint-deep: #1f7a55;
    --warn: #e5484d;

    /* 文字四阶 */
    --text-title: #3a3f63;
    --text-body: #4a4f6b;
    --text-sub: #6b7091;
    --text-dim: #9aa0bd;

    /* 描边 */
    --stroke-dark: #f0a2c0;

    /* 形状 */
    --r-sm: 10px;
    --r-md: 15px;
    --r-lg: 22px;
    --ease: ease-in-out;

    /* 字体 */
    --font-sans: system-ui, -apple-system, 'PingFang SC', 'HarmonyOS Sans SC', 'MiSans',
        'Microsoft YaHei UI', 'Microsoft YaHei', 'Source Han Sans SC', 'Noto Sans CJK SC',
        'Hiragino Sans GB', sans-serif;
    --font-brand: '印品鸿蒙体', var(--font-sans);
    --font-num: 'Baloo 2', 'Segoe UI', var(--font-sans);
    --font-mono: ui-monospace, 'Cascadia Mono', 'JetBrains Mono', Consolas, 'Courier New', monospace;
}

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

html {
    scrollbar-color: var(--stroke-dark) var(--bg-deep);
}

body {
    min-height: 100vh;
    font: 15px/1.7 var(--font-sans);
    background: var(--bg-page);
    color: var(--text-body);
    overflow-x: hidden;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

/* 单字重中文字体不做伪粗：需要强调用颜色 / 描边 / 字号 */
h1, h2, h3, h4, h5, h6, b, strong, legend {
    font-weight: 400;
}

a {
    color: inherit;
    text-decoration: none;
}

button,
input,
select {
    font-family: inherit;
}

img {
    display: block;
}

/* .player-controls / .progress-head 等自带 display，必须显式兜住 hidden 属性 */
[hidden] {
    display: none !important;
}

.num {
    font-family: var(--font-num);
    font-variant-numeric: tabular-nums;
}

::selection {
    background: var(--accent-pink);
    color: #fff;
}

:focus-visible {
    outline: 3px solid var(--primary);
    outline-offset: 2px;
}

/* ============================ 背景装饰层 ============================ */
.bg-deco {
    position: fixed;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    overflow: hidden;
}

/* 纯色圆 + blur(120px) 的柔光光斑（规范允许的唯一"柔"元素，不是 UI 渐变） */
.bg-blob {
    position: absolute;
    border-radius: 50%;
    filter: blur(120px);
    opacity: .5;
}

.b1 {
    width: 46vw;
    height: 46vw;
    left: -12vw;
    top: -14vw;
    background: #ffc2d8;
}

.b2 {
    width: 40vw;
    height: 40vw;
    right: -10vw;
    top: 24vh;
    background: #ffe9a8;
    opacity: .45;
}

.b3 {
    width: 42vw;
    height: 42vw;
    left: 24vw;
    bottom: -18vw;
    background: #b9f0dc;
    opacity: .38;
}

/* ANGELS 水印：PNG 当遮罩上色，避免再存一份彩色素材 */
.bg-logo {
    position: absolute;
    right: -4vw;
    bottom: -6vh;
    width: min(46vw, 620px);
    aspect-ratio: 1 / 1;
    background: #f7b7d2;
    opacity: .5;
    transform: rotate(-8deg);
    -webkit-mask: url('angel/img/mid_trans_logo.png') center/contain no-repeat;
    mask: url('angel/img/mid_trans_logo.png') center/contain no-repeat;
}

/* 半调网点 */
.bg-dots {
    position: absolute;
    inset: -20px;
    background-image: radial-gradient(rgba(238, 119, 169, .22) 1.5px, transparent 1.6px);
    background-size: 15px 15px;
    -webkit-mask-image: linear-gradient(180deg, #000, transparent 62%);
    mask-image: linear-gradient(180deg, #000, transparent 62%);
}

/* 斜排线（呼应首页字标的排线肌理） */
.bg-hatch {
    position: absolute;
    inset: 0;
    opacity: .5;
    background-image: repeating-linear-gradient(115deg,
            rgba(238, 119, 169, .13) 0 7px, transparent 7px 15px);
    -webkit-mask-image: radial-gradient(120% 80% at 50% 100%, #000, transparent 70%);
    mask-image: radial-gradient(120% 80% at 50% 100%, #000, transparent 70%);
}

/* 噪点质感层 */
.bg-noise {
    position: absolute;
    inset: 0;
    background: url('angel/img/common_noise.png') center/220px repeat;
    opacity: .05;
}

/* ---------- 漂浮小装饰（素材库贴纸 · 配方对齐登录页 .deco-layer） ----------
   两级分工：.deco-item 只负责鼠标视差位移（--mx/--my × --depth），::before 负责自身的漂浮摆动。
   必须拆成两层 —— 一个元素只能挂一份 transform 动画，合在一起 keyframes 会把视差顶掉。
   --edge-l / --edge-r：把贴纸推出正文版心（1180px 是 .panel-record 加宽后的最宽内容）外侧。 */
.deco-layer {
    position: absolute;
    inset: 0;
    --edge-l: max(16px, calc((100% - 1180px) / 2 - 68px));
    --edge-r: max(16px, calc((100% - 1180px) / 2 - 68px));
}

.deco-item {
    position: absolute;
    display: block;
    width: var(--w, 48px);
    height: var(--w, 48px);
    opacity: .94;
    transform: translate(calc(var(--mx, 0) * var(--depth, 12px)),
            calc(var(--my, 0) * var(--depth, 12px))) rotate(var(--rot, 0deg));
    transition: transform .4s ease-out;
}

.deco-item::before {
    content: '';
    display: block;
    width: 100%;
    height: 100%;
    background: center / contain no-repeat;
    filter: drop-shadow(3px 4px 0 rgba(122, 46, 77, .15));
    animation: deco-float 4.2s var(--ease) infinite alternate;
}

/* 单色四角星一律「遮罩 + 填色」，不另存彩色副本（同首页 hero-stars 配方） */
.d1 {
    top: 17%;
    left: var(--edge-l);
    --w: 40px;
    --depth: 24px;
    --rot: -12deg;
}

.d1::before {
    background-color: var(--accent-gold);
    -webkit-mask: url('angel/img/deco_star_2.png') center / contain no-repeat;
    mask: url('angel/img/deco_star_2.png') center / contain no-repeat;
}

.d2 {
    top: 9%;
    right: 2.6%;
    --w: 54px;
    --depth: 16px;
    --rot: 12deg;
}

.d2::before {
    background-image: url('angel/img/load_i_flower.png');
}

.d3 {
    top: 45%;
    left: 2.2%;
    --w: 44px;
    --depth: 34px;
    --rot: -14deg;
}

.d3::before {
    background-image: url('angel/img/load_i_heart.png');
    animation-duration: 5.4s;
    animation-delay: -1.1s;
}

.d4 {
    top: 41%;
    right: var(--edge-r);
    --w: 36px;
    --depth: 30px;
    --rot: 10deg;
}

.d4::before {
    background-color: var(--accent-pink);
    -webkit-mask: url('angel/img/mid_trans_star_3.png') center / contain no-repeat;
    mask: url('angel/img/mid_trans_star_3.png') center / contain no-repeat;
    animation-duration: 3.6s;
    animation-delay: -.7s;
}

.d5 {
    bottom: 15%;
    left: var(--edge-l);
    --w: 52px;
    --depth: 20px;
    --rot: 8deg;
}

.d5::before {
    background-image: url('angel/img/load_i_pin.png');
    animation-duration: 4.8s;
    animation-delay: -2s;
}

.d6 {
    bottom: 21%;
    right: 3.2%;
    --w: 42px;
    --depth: 18px;
    --rot: -12deg;
}

.d6::before {
    background-color: var(--mint);
    -webkit-mask: url('angel/img/mid_trans_star_1.png') center / contain no-repeat;
    mask: url('angel/img/mid_trans_star_1.png') center / contain no-repeat;
    animation-duration: 5.8s;
    animation-delay: -1.6s;
}

@keyframes deco-float {
    from { transform: translateY(-5px) rotate(-3deg); }
    to { transform: translateY(6px) rotate(4deg); }
}

/* 两侧留白不足时整层收起（同登录页：窄屏直接关掉 deco-layer）。
   门槛算给最坏情况：正文最宽 1180px（.panel-record 在 >=1000px 会加宽到 1180 并居中溢出），
   装饰自身外边界最多约 100px（含旋转撑大的包围盒）→ 视口需 >= 1180 + 2×110 = 1400px 才放得下。 */
@media (max-width: 1399.98px) {
    .deco-layer {
        display: none;
    }
}

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

    .deco-item::before {
        animation: none;
    }
}

/* ============================ 返回链接 ============================ */
.back-link {
    position: fixed;
    top: 22px;
    left: 24px;
    z-index: 40;
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 9px 20px;
    background: #fff;
    border: 2.5px solid var(--ink);
    border-radius: 999px;
    color: var(--text-title);
    font-size: 14px;
    box-shadow: 0 4px 0 rgba(34, 31, 51, .30);
    transition: transform .2s var(--ease), box-shadow .2s var(--ease), background .2s var(--ease), color .2s var(--ease);
}

.back-link .bl-ico {
    font-size: 16px;
    line-height: 1;
    transition: transform .2s var(--ease);
}

.back-link:hover {
    background: var(--accent-pink);
    color: #fff;
    transform: translateY(-2px);
    box-shadow: 0 6px 0 rgba(34, 31, 51, .30);
}

.back-link:hover .bl-ico {
    transform: translateX(-3px);
}

.back-link:active {
    transform: translateY(3px);
    box-shadow: 0 1px 0 rgba(34, 31, 51, .30);
}

/* ============================ 主体 ============================ */
.container {
    position: relative;
    z-index: 1;
    max-width: 940px;
    margin: 0 auto;
    padding: 84px 24px 56px;
}

/* ============================ 标题区 ============================ */
.hero {
    position: relative;
    text-align: center;
    margin-bottom: 26px;
    animation: fade-in-up .5s var(--ease) both;
}

.hero-badge {
    position: relative;
    display: inline-block;
    margin-bottom: 10px;
}

.hb-tile {
    display: grid;
    place-items: center;
    width: 78px;
    height: 78px;
    background: #fff;
    border: 3px solid var(--ink);
    border-radius: 20px;
    font-size: 38px;
    line-height: 1;
    box-shadow: 0 5px 0 rgba(34, 31, 51, .28);
    transform: rotate(-5deg);
    animation: float 1.6s var(--ease) infinite alternate;
}

.hb-star {
    position: absolute;
    background: var(--accent-gold);
    -webkit-mask: url('angel/img/deco_star_1.png') center/contain no-repeat;
    mask: url('angel/img/deco_star_1.png') center/contain no-repeat;
    animation: breath 1.4s var(--ease) infinite alternate;
}

.hb-star.s1 {
    width: 26px;
    height: 26px;
    right: -14px;
    top: -8px;
}

.hb-star.s2 {
    width: 16px;
    height: 16px;
    left: -10px;
    bottom: -4px;
    background: var(--mint);
    animation-delay: -.7s;
}

/* 双层描边字：::before 用透明填充的 text-stroke 只画外轮廓，压在实心字下面
   （比 paint-order 兼容性稳，也比 SVG 滤镜少一层依赖） */
.hero-title-wrap {
    position: relative;
    display: inline-block;
}

.hero-title {
    position: relative;
    z-index: 1;
    display: inline-block;
    font-family: var(--font-brand);
    /* 比 gifcompress 收一档：本页标题字数多（「视频转」+GIF），字号过大描边会把笔画糊住 */
    font-size: clamp(34px, 6vw, 56px);
    line-height: 1.14;
    letter-spacing: .01em;
    color: #fff;
    filter: drop-shadow(0 6px 0 var(--primary)) drop-shadow(0 12px 0 rgba(34, 31, 51, .10));
    transform: rotate(-1.5deg);
}

.hero-title::before {
    content: attr(data-text);
    position: absolute;
    inset: 0;
    z-index: -1;
    color: transparent;
    -webkit-text-stroke: 10px var(--ink);
}

.hero-title .ht-en {
    color: var(--accent-gold);
}

.hero-title .ht-cn {
    color: #fff;
}

.hero-tag {
    position: absolute;
    left: 100%;
    top: 4px;
    /* 标题自身 z-index:1 建了层叠上下文，会盖住 auto 的兄弟节点 → 标签必须抬到它之上 */
    z-index: 2;
    margin-left: -18px;
    padding: 3px 12px;
    background: var(--ink);
    border-radius: 999px;
    color: #fff;
    font-size: 10px;
    letter-spacing: .22em;
    transform: rotate(9deg);
    box-shadow: 0 3px 0 rgba(34, 31, 51, .18);
}

/* 分隔装饰：三颗四角星（素材库 deco_star_1.png 当遮罩上色，不另存彩色副本） */
.hero-stars {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 9px;
    margin: 14px auto 8px;
    animation: deco-sway 2.8s var(--ease) infinite alternate;
}

.hero-stars i {
    display: block;
    background: var(--accent-pink);
    -webkit-mask: url('angel/img/deco_star_1.png') center/contain no-repeat;
    mask: url('angel/img/deco_star_1.png') center/contain no-repeat;
}

.hero-stars i:nth-child(2) {
    width: 24px;
    height: 24px;
}

.hero-stars i:nth-child(1),
.hero-stars i:nth-child(3) {
    width: 13px;
    height: 13px;
    background: var(--mint);
}

.hero-stars i:nth-child(1) { transform: rotate(-8deg); }
.hero-stars i:nth-child(3) { transform: rotate(8deg); }

/* ============================ 内联图标（自绘 SVG，替代 emoji） ============================
   全部按贴纸风画：墨色粗描边 + 实色填充，24×24 viewBox，等比缩放到 14~20px。
   .i-f = 有填充的形（默认白，可由 .ico-xxx 改色）；.i-f2 = 第二个颜色的形；
   .i-s = 只描边的线；.i-p = 实心点。
   ------------------------------------------------------------------------- */
.ico {
    width: 20px;
    height: 20px;
    flex: none;
    display: block;
}

.ico .i-f,
.ico .i-f2,
.ico .i-s {
    stroke: var(--ink);
    stroke-width: 2.2;
    stroke-linejoin: round;
    stroke-linecap: round;
}

.ico .i-f { fill: #fff; }
.ico .i-f2 { fill: var(--accent-pink); }
.ico .i-s { fill: none; }
.ico .i-p { fill: var(--ink); }

/* 图标配色预设（只改填充色，描边始终是墨色） */
.ico-mint .i-f { fill: var(--mint-soft); }
.ico-gold .i-f { fill: var(--accent-gold); }
.ico-warn .i-f { fill: var(--warn); }
.ico-warn .i-s { stroke: #fff; }

/* 粉色实心按钮里：白填充 + 酒红描边，保证在白字按钮上依然清晰 */
.btn-primary .ico .i-f,
.btn-primary .ico .i-f2,
.btn-primary .ico .i-s {
    stroke: var(--cta-stroke);
}

.btn-primary .ico .i-f,
.btn-primary .ico .i-f2 {
    fill: #fff;
}

/* 小尺寸场景 */
.btn-sm .ico { width: 16px; height: 16px; }
.chip .ico { width: 16px; height: 16px; }
/* ⚠️ 必须写成 .btn.btn-mark：与 .btn 同为单类选择器时靠源序决胜负，
   而 .btn 在本文件后面 → 会被它的 gap:8px 覆盖掉。 */
.btn.btn-mark { gap: 6px; }
.modal-tip .ico { width: 30px; height: 30px; }
.speed-wrap .ico,
.vol-wrap .ico { width: 17px; height: 17px; }
.footer .ico {
    display: inline-block;
    vertical-align: -0.22em;
    width: 14px;
    height: 14px;
    margin-right: 3px;
}

.hero-sub {
    font-size: 14.5px;
    color: var(--text-sub);
}

.hero-sub b {
    color: var(--cta-stroke);
    box-shadow: inset 0 -.55em 0 rgba(248, 225, 121, .55);
}

.chips {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 10px;
    margin-top: 16px;
    list-style: none;
}

.chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 15px;
    border: 2px solid var(--ink);
    border-radius: 999px;
    font-size: 12.5px;
    color: var(--ink);
    box-shadow: 0 3px 0 rgba(34, 31, 51, .22);
    transition: transform .2s var(--ease);
}

.chip-mint { background: var(--mint-soft); }
.chip-gold { background: #fdf0c0; }
.chip-pink { background: var(--bg-card-2); }

.chip:nth-child(odd) { transform: rotate(-1.2deg); }
.chip:nth-child(even) { transform: rotate(1.2deg); }

.chip:hover {
    transform: translateY(-2px) rotate(0deg);
}

/* ============================ 面板（游戏面板配方） ============================ */
.panel {
    position: relative;
    margin-bottom: 18px;
    padding: 24px 22px 22px;
    background: var(--bg-card);
    border: 2px solid var(--stroke-dark);
    border-radius: var(--r-lg);
    box-shadow: 0 5px 0 rgba(34, 31, 51, .10);
    /* overflow:hidden 是为了把 .panel-bar 裁到圆角内 —— 5px 高的元素上写 20px 圆角会被
       CSS 的"等比缩放"压成 5px（半径不能超过高度），方角就会戳出面板的 20px 内圆角。
       靠父级裁切才是几何上正确的做法。 */
    overflow: hidden;
    /* 用 backwards 而非 both：延迟期间先隐藏，播完把 transform 交还给样式表
       （both/forwards 会永久占用 transform，把 hover 的上浮位移吃掉） */
    animation: fade-in-up .5s var(--ease) backwards;
    animation-delay: calc(var(--i, 0) * 80ms);
}

.panel-bar {
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    height: 5px;
    background: var(--accent-pink);
    border-radius: calc(var(--r-lg) - 2px) calc(var(--r-lg) - 2px) 0 0;
}

.panel-drop { --i: 1; }
/* 「算法分段」参数栏挪到预览区右侧后要给右栏腾地方：本面板单独加宽、居中溢出
   .container（其它面板宽度不受影响）。两处 min() 必须一致 —— 窄视口下它退化成
   「正好等于内容宽」，此时 margin-left 算出来是 0，不会横向撑出视口。 */
.panel-player {
    --i: 2;
    --player-w: min(1180px, calc(100vw - 48px));
}

/* 预览面板统一加宽（站长 2026-09-16：手动打点的窗口大小学习智能模式）。
   手动模式右栏（分析算法栏）隐藏，左列自动占满 —— 预览比算法模式更大，切模式不再忽大忽小。 */
.panel-player {
    width: var(--player-w);
    margin-left: calc((100% - var(--player-w)) / 2);
}
.panel-opts { --i: 4; }
/* 「渲染队列」单开一栏挂在最下面（紧跟动图参数） */
.panel-queue { --i: 5; }
/* 这两块是「被 unhide 才出现」的，错峰值给小一点，否则点了转换要等 300ms+ 才看到进度条 */
.panel-progress { --i: 1; }
.panel-result { --i: 1; }

.panel-head {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    margin-bottom: 14px;
}

.panel-title {
    display: flex;
    align-items: center;
    gap: 8px;
    font-family: var(--font-brand);
    font-size: 19px;
    color: var(--text-title);
}

.panel-title::before {
    content: '';
    width: 8px;
    height: 18px;
    border-radius: 4px;
    background: var(--accent-pink);
    transform: rotate(-8deg);
}

.panel-sub {
    font-size: 12.5px;
    color: var(--text-dim);
}

/* ============================ 拖放区 ============================ */
.dropzone {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 190px;
    padding: 26px 190px 26px 26px;
    background: var(--bg-light);
    border: 3px dashed var(--stroke-dark);
    border-radius: var(--r-md);
    cursor: pointer;
    transition: transform .2s var(--ease), border-color .2s var(--ease),
        background .2s var(--ease), box-shadow .2s var(--ease);
}

.dropzone:hover,
.dropzone:focus-visible {
    border-color: var(--primary);
    background: var(--bg-deep);
    transform: translateY(-2px);
    box-shadow: 0 6px 0 rgba(34, 31, 51, .12);
}

.dropzone.dragover {
    border-color: var(--primary);
    border-style: solid;
    background: var(--bg-card-2);
    transform: scale(1.01);
    box-shadow: 0 0 0 6px rgba(238, 119, 169, .22), 0 8px 0 rgba(34, 31, 51, .14);
}

.dz-body {
    text-align: center;
}

.dz-icon {
    font-size: 42px;
    line-height: 1;
    margin-bottom: 10px;
    filter: drop-shadow(0 4px 0 rgba(34, 31, 51, .16));
    transition: transform .2s var(--ease);
}

.dropzone:hover .dz-icon {
    transform: translateY(-3px) rotate(-6deg);
}

.dz-text {
    font-size: 16.5px;
    color: var(--text-title);
}

.dz-text b {
    color: var(--cta-stroke);
    box-shadow: inset 0 -.55em 0 rgba(248, 225, 121, .6);
}

.dz-hint {
    margin-top: 7px;
    font-size: 12.5px;
    color: var(--text-dim);
}

/* 站在框边、脚踩出画面的 Q 版立绘贴纸（不裁剪，允许出框 16px 落在面板留白里） */
.dz-mascot {
    position: absolute;
    right: 12px;
    bottom: -16px;
    width: 152px;
    transform: rotate(4deg);
    filter: drop-shadow(0 5px 0 rgba(34, 31, 51, .18));
    transition: transform .25s var(--ease);
    pointer-events: none;
}

.dropzone:hover .dz-mascot {
    transform: rotate(-2deg) translateY(-8px);
}

.dz-star {
    position: absolute;
    right: 178px;
    top: 16px;
    width: 22px;
    height: 22px;
    background: var(--accent-gold);
    -webkit-mask: url('angel/img/deco_star_2.png') center/contain no-repeat;
    mask: url('angel/img/deco_star_2.png') center/contain no-repeat;
    animation: breath 1.3s var(--ease) infinite alternate;
}

/* ============================ 预览区两列布局 ============================ */
/* 左列 = 舞台 / 波形 / 时间轴 / 播放控制；右列 = 算法分段参数栏。
   并排阈值取 1228px：面板宽度是 min(1180px, 100vw - 48px)，只有视口 ≥ 1228 时面板才真能
   撑到 1180 宽、左列才拿得到承诺的 838px。比这更窄就整条堆到预览区下方撑满一行
   （.auto-bar 内部是 grid 自适应列，撑满时自动回到多列横排的观感，不会退化）。 */
.player-body {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 18px;
}

.player-main {
    flex: 1 1 0;
    min-width: 0;
}

/* ============================ 预览舞台 ============================ */
/* ⚠️ 不要给 .stage 加 padding：script.js 的裁剪换算用 stage.clientWidth/Height
   当显示区域，video 又是 width/height:100%（相对内容盒），加 padding 会让两者错位。
   只描边（border 不进 clientWidth），几何天然一致。 */
.stage {
    position: relative;
    width: 100%;
    aspect-ratio: 16 / 9;
    background: var(--bg-ink);
    border: 3px solid var(--ink);
    border-radius: var(--r-md);
    box-shadow: 0 6px 0 rgba(34, 31, 51, .22);
    overflow: hidden;
}

.stage video {
    width: 100%;
    height: 100%;
    object-fit: contain;
    display: block;
}

/* 取景框四角：金色直角标记，纯装饰、不吃鼠标事件（拖拽裁剪要能穿透到 overlay） */
.vf-corner {
    position: absolute;
    width: 20px;
    height: 20px;
    border: 0 solid var(--accent-gold);
    pointer-events: none;
    z-index: 3;
    filter: drop-shadow(0 2px 0 rgba(34, 31, 51, .45));
}

.vf-corner.c1 {
    left: 8px;
    top: 8px;
    border-left-width: 3px;
    border-top-width: 3px;
}

.vf-corner.c2 {
    right: 8px;
    top: 8px;
    border-right-width: 3px;
    border-top-width: 3px;
}

.vf-corner.c3 {
    left: 8px;
    bottom: 8px;
    border-left-width: 3px;
    border-bottom-width: 3px;
}

.vf-corner.c4 {
    right: 8px;
    bottom: 8px;
    border-right-width: 3px;
    border-bottom-width: 3px;
}

.crop-overlay {
    position: absolute;
    inset: 0;
    z-index: 2;
    cursor: crosshair;
}

/* 裁剪框：薄荷绿虚线 + 外圈压暗（沿用原实现的 9999px spread 遮罩，被 .stage 裁掉） */
.crop-rect {
    position: absolute;
    pointer-events: none;
    border: 3px dashed var(--mint);
    background: rgba(54, 186, 130, .12);
    box-shadow: 0 0 0 9999px rgba(38, 32, 64, .62),
        inset 0 0 0 2px rgba(255, 255, 255, .55);
}

.crop-tip {
    position: absolute;
    left: 50%;
    bottom: 12px;
    z-index: 4;
    transform: translateX(-50%) rotate(-1deg);
    padding: 5px 16px;
    background: #fff;
    border: 2px solid var(--ink);
    border-radius: 999px;
    color: var(--text-title);
    font-size: 12px;
    white-space: nowrap;
    box-shadow: 0 3px 0 rgba(34, 31, 51, .28);
    pointer-events: none;
    transition: opacity .25s var(--ease);
}

.crop-tip.hidden {
    opacity: 0;
}

/* ============================ 时间轴 ============================ */
.timeline {
    position: relative;
    height: 34px;
    margin-top: 14px;
}

.tl-track {
    position: absolute;
    left: 0;
    right: 0;
    top: 13px;
    height: 8px;
    background: var(--bg-ink);
    border: 2px solid var(--ink);
    border-radius: 999px;
}

.tl-range {
    position: absolute;
    top: 13px;
    height: 8px;
    background: var(--accent-pink);
    border: 2px solid var(--cta-stroke);
    border-radius: 999px;
    pointer-events: none;
    box-shadow: 0 2px 0 rgba(34, 31, 51, .22);
}

.tl-marker {
    position: absolute;
    top: 4px;
    width: 7px;
    height: 26px;
    border: 2px solid var(--ink);
    border-radius: 4px;
    pointer-events: none;
    transform: translateX(-50%);
    box-shadow: 0 2px 0 rgba(34, 31, 51, .28);
    z-index: 1; /* 盖在红色播放游标上方，便于区分打点位置 */
}

.tl-marker-start { background: var(--mint); }
.tl-marker-end { background: var(--accent-gold); }

.timeline input[type="range"] {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    -webkit-appearance: none;
    appearance: none;
    background: transparent;
    cursor: pointer;
}

.timeline input[type="range"]::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    /* 播放游标：红色竖直细线样式（更精确，替代圆形滑块） */
    width: 6px;
    height: 28px;
    border-radius: 2px;
    background: var(--warn);
    border: 2px solid #7f1d1d;
    box-shadow: 0 0 0 2px #fff, 0 2px 0 rgba(34, 31, 51, .35);
}

.timeline input[type="range"]::-moz-range-thumb {
    width: 6px;
    height: 28px;
    border-radius: 2px;
    background: var(--warn);
    border: 2px solid #7f1d1d;
    box-shadow: 0 0 0 2px #fff, 0 2px 0 rgba(34, 31, 51, .35);
}

/* ============================ 播放控制条 ============================ */
.player-controls {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    margin-top: 12px;
}

.time-display {
    margin-left: 4px;
    font-size: 13.5px;
    color: var(--text-dim);
}

.time-display b {
    color: var(--cta-stroke);
    font-size: 15px;
}

.spacer {
    flex: 1;
}

.btn-mark {
    background: var(--bg-card-2);
}

.btn-mark b {
    color: var(--cta-stroke);
}

.vol-wrap,
.speed-wrap {
    display: flex;
    align-items: center;
    gap: 7px;
    font-size: 14px;
    color: var(--text-sub);
}

/* 跳帧倍率（音量右边）：小药丸里装「开关 + 帧数」，开关打开时整块转粉看着像"生效了" */
.frame-step {
    display: flex;
    align-items: center;
    gap: 7px;
    font-size: 14px;
    color: var(--text-sub);
}

/* 贴纸风小方钮：零渐变、硬边描边、选中画白勾 */
.fs-toggle input[type="checkbox"]:checked {
    background: var(--accent-pink);
}

.fs-toggle input[type="checkbox"]:checked::after {
    content: "";
    position: absolute;
    left: 3px;
    top: 0;
    width: 6px;
    height: 10px;
    border: solid #fff;
    border-width: 0 2px 2px 0;
    transform: rotate(45deg);
}

.fs-num {
    width: 50px;
    padding: 2px 6px;
    background: #fff;
    border: 2px solid var(--ink);
    border-radius: 6px;
    font-size: 12.5px;
    color: var(--text-title);
}

.fs-num:disabled {
    background: var(--bg-deep);
    border-color: var(--stroke-dark);
    color: var(--text-dim);
}

/* 「加入渲染队列」常驻在播放控制条行尾（margin-left:auto 顶到右边），做成**薄荷绿实底 + 硬投影**：
   与下方「渲染队列」面板同一色系，在周围一圈幽灵按钮里一眼就能看到。
   未选中分段 / 未打点时走 .btn:disabled 的灰度偏暗态。 */
.player-controls .queue-add {
    padding: 9px 22px;
    background: var(--mint);
    border-color: var(--cta-stroke);
    color: #fff;
    font-size: 13.5px;
    box-shadow: 0 4px 0 var(--cta-stroke);
}

.player-controls .queue-add:hover:not(:disabled) {
    transform: translateY(-2px);
    box-shadow: 0 6px 0 var(--cta-stroke);
}

.player-controls .queue-add:disabled {
    opacity: .5;
}

/* 手动打点区间入队时的重命名框：长在「加入渲染队列」按钮这一行
   （按钮自带 margin-left:auto，所以它跟到行尾、必要时换到下一行，不会挤动前面的控件） */
.player-controls .seg-rename {
    margin-left: 8px;
}

.vol-slider {
    width: 72px;
    height: 20px;
    -webkit-appearance: none;
    appearance: none;
    background: transparent;
    cursor: pointer;
}

.vol-slider::-webkit-slider-runnable-track {
    height: 6px;
    background: var(--bg-ink);
    border: 2px solid var(--ink);
    border-radius: 999px;
}

.vol-slider::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    margin-top: -7px;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: #fff;
    border: 3px solid var(--ink);
}

.vol-slider::-moz-range-track {
    height: 6px;
    background: var(--bg-ink);
    border: 2px solid var(--ink);
    border-radius: 999px;
}

.vol-slider::-moz-range-thumb {
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: #fff;
    border: 3px solid var(--ink);
}

.speed-select {
    padding: 7px 12px;
    background: #fff;
    border: 2.5px solid var(--ink);
    border-radius: 999px;
    color: var(--text-title);
    font-size: 13px;
    font-variant-numeric: tabular-nums;
    cursor: pointer;
    box-shadow: 0 3px 0 rgba(34, 31, 51, .22);
    transition: background .2s var(--ease), transform .2s var(--ease), box-shadow .2s var(--ease);
}

.speed-select:hover {
    background: var(--bg-card-2);
    transform: translateY(-2px);
    box-shadow: 0 5px 0 rgba(34, 31, 51, .22);
}

/* ============================ 参数区 ============================ */
.file-meta {
    margin-bottom: 16px;
    padding: 10px 15px;
    background: var(--bg-deep);
    border: 2px dashed var(--stroke-dark);
    border-radius: var(--r-md);
    font-size: 12.5px;
    line-height: 1.75;
    color: var(--text-sub);
    word-break: break-all;
}

.file-meta::before {
    content: '📄 ';
}

.grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
    gap: 14px 16px;
}

.field {
    display: flex;
    flex-direction: column;
    gap: 7px;
    min-width: 0;
    font-size: 12.5px;
    color: var(--text-sub);
    letter-spacing: .02em;
}

.field-wide {
    grid-column: 1 / -1;
}

.grid input[type="number"],
.grid input[type="text"] {
    width: 100%;
    padding: 9px 12px;
    background: #fff;
    border: 2px solid var(--ink);
    border-radius: var(--r-sm);
    color: var(--text-title);
    font-size: 14px;
    font-variant-numeric: tabular-nums;
    box-shadow: 0 3px 0 rgba(34, 31, 51, .16);
    transition: border-color .2s var(--ease), box-shadow .2s var(--ease), background .2s var(--ease);
}

.grid input[type="number"]:hover,
.grid input[type="text"]:hover {
    border-color: var(--primary);
}

.grid input[type="number"]:focus,
.grid input[type="text"]:focus {
    outline: none;
    background: var(--bg-light);
    border-color: var(--primary);
    box-shadow: 0 3px 0 rgba(238, 119, 169, .40);
}

/* ---- 比例预设（一排贴纸胶囊） ---- */
.ratio-row {
    grid-column: 1 / -1;
    display: flex;
    align-items: center;
    gap: 9px;
    flex-wrap: wrap;
}

.ratio-label {
    margin-right: 2px;
    font-size: 12.5px;
    color: var(--text-sub);
    letter-spacing: .02em;
}

.ratio-btn {
    padding: 6px 16px;
    background: #fff;
    border: 2.5px solid var(--ink);
    border-radius: 999px;
    color: var(--text-sub);
    font-size: 12.5px;
    line-height: 1.5;
    cursor: pointer;
    box-shadow: 0 3px 0 rgba(34, 31, 51, .22);
    transition: transform .2s var(--ease), box-shadow .2s var(--ease),
        background .2s var(--ease), color .2s var(--ease);
}

.ratio-btn:hover {
    background: var(--bg-card-2);
    color: var(--cta-stroke);
    transform: translateY(-2px);
    box-shadow: 0 5px 0 rgba(34, 31, 51, .22);
}

.ratio-btn.is-active {
    background: var(--accent-pink);
    border-color: var(--cta-stroke);
    color: #fff;
    transform: rotate(-2deg);
    box-shadow: 0 3px 0 var(--cta-stroke);
}

.ratio-btn.is-active:hover {
    transform: rotate(-2deg) translateY(-2px);
    box-shadow: 0 5px 0 var(--cta-stroke);
}

.ratio-btn:active {
    transform: translateY(3px);
    box-shadow: 0 0 0 transparent;
}

/* ---- 开关（DESIGN-REFERENCE 第四节配方） ---- */
.switch-row {
    display: flex;
    align-items: center;
    gap: 12px;
    cursor: pointer;
    user-select: none;
}

.switch-wide {
    grid-column: 1 / -1;
}

.switch-row input {
    position: absolute;
    opacity: 0;
    width: 0;
    height: 0;
}

.sw {
    position: relative;
    flex: none;
    width: 44px;
    height: 24px;
    background: #fff;
    border: 2.5px solid var(--ink);
    border-radius: 999px;
    box-shadow: 0 3px 0 rgba(34, 31, 51, .22);
    transition: background .2s var(--ease);
}

.sw::after {
    content: '';
    position: absolute;
    left: 2px;
    top: 2px;
    width: 15px;
    height: 15px;
    background: var(--text-dim);
    border-radius: 50%;
    transition: transform .2s var(--ease), background .2s var(--ease);
}

.switch-row input:checked + .sw {
    background: var(--accent-pink);
}

.switch-row input:checked + .sw::after {
    background: #fff;
    transform: translateX(19px);
}

.switch-row input:focus-visible + .sw {
    outline: 3px solid var(--primary);
    outline-offset: 2px;
}

.switch-txt {
    font-size: 14.5px;
    color: var(--text-title);
}

.switch-tag {
    padding: 2px 10px;
    background: var(--bg-deep);
    border: 2px solid var(--stroke-dark);
    border-radius: 999px;
    font-size: 11.5px;
    color: var(--text-sub);
}

.opt-note {
    display: flex;
    align-items: flex-start;
    gap: 9px;
    margin-top: 18px;
    padding: 10px 15px;
    background: var(--bg-deep);
    border: 2px dashed var(--stroke-dark);
    border-radius: var(--r-md);
    font-size: 12.5px;
    line-height: 1.7;
    color: var(--text-sub);
}

.opt-note span {
    flex: 1;
}

.opt-note .ico {
    width: 18px;
    height: 18px;
    margin-top: 1px;
}

.action-row {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    margin-top: 18px;
}

/* 算法分段模式下「一键转换」被收起（[hidden]），这一行就空了 —— 别让它继续占 18px 外边距。
   :has() 在旧浏览器上不生效时会退化成"留一小段空白"，不影响功能。 */
.action-row:not(:has(> .btn:not([hidden]))) {
    display: none;
}

/* ============================ 按钮 ============================ */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    background: #fff;
    border: 2.5px solid var(--ink);
    border-radius: 999px;
    padding: 10px 24px;
    font-size: 14px;
    color: var(--text-title);
    cursor: pointer;
    transition: transform .2s var(--ease), box-shadow .2s var(--ease),
        background .2s var(--ease), color .2s var(--ease);
}

.btn-primary {
    background: var(--accent-pink);
    color: #fff;
    box-shadow: 0 4px 0 var(--cta-stroke);
}

.btn-primary:hover:not(:disabled) {
    transform: translateY(-2px);
    box-shadow: 0 6px 0 var(--cta-stroke);
}

.btn-ghost {
    background: #fff;
    box-shadow: 0 4px 0 rgba(34, 31, 51, .28);
}

.btn-ghost:hover:not(:disabled) {
    background: var(--bg-card-2);
    transform: translateY(-2px);
    box-shadow: 0 6px 0 rgba(34, 31, 51, .28);
}

.btn:active:not(:disabled) {
    transform: translateY(4px);
    box-shadow: 0 0 0 transparent;
}

/* 禁用态 = 灰度 + 不可点（不改字色不改底色） */
.btn:disabled {
    filter: grayscale(1);
    cursor: not-allowed;
    opacity: .75;
}

.btn-sm {
    padding: 7px 16px;
    font-size: 13px;
}

.btn-lg {
    padding: 14px 34px;
    font-size: 16px;
    box-shadow: 0 5px 0 var(--cta-stroke);
}

.btn-lg:hover:not(:disabled) {
    box-shadow: 0 7px 0 var(--cta-stroke);
}

/* ============================ 进度 / 日志 ============================ */
.progress-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 12px;
    font-size: 13.5px;
    color: var(--text-sub);
}

#progressPct {
    flex: none;
    padding: 2px 13px;
    background: var(--ink);
    border-radius: 999px;
    color: #fff;
    font-size: 12.5px;
}

/* 深底槽 + 斜纹滚动填充（重复硬边条纹属图案，不是 UI 渐变） */
.progress-bar {
    position: relative;
    height: 18px;
    background: var(--bg-ink);
    border: 2.5px solid var(--ink);
    border-radius: 999px;
    overflow: hidden;
}

.progress-fill {
    height: 100%;
    width: 0;
    background-color: var(--accent-pink);
    background-image: repeating-linear-gradient(115deg,
            rgba(255, 255, 255, .26) 0 8px, transparent 8px 16px);
    border-radius: 999px;
    transition: width .3s var(--ease);
    animation: barber 1.1s linear infinite;
}

.log-box {
    margin-top: 14px;
}

.log-box summary {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 6px 16px;
    background: #fff;
    border: 2px solid var(--ink);
    border-radius: 999px;
    color: var(--text-title);
    font-size: 12.5px;
    cursor: pointer;
    user-select: none;
    list-style: none;
    box-shadow: 0 3px 0 rgba(34, 31, 51, .2);
    transition: background .2s var(--ease), transform .2s var(--ease);
}

.log-box summary::-webkit-details-marker {
    display: none;
}

.log-box summary:hover {
    background: var(--bg-card-2);
    transform: translateY(-2px);
}

.log-box[open] summary {
    background: var(--bg-card-2);
}

.log-box pre {
    margin-top: 10px;
    max-height: 200px;
    overflow: auto;
    padding: 12px 14px;
    background: var(--bg-ink);
    border: 2px solid var(--ink);
    border-radius: var(--r-sm);
    color: #ebebeb;
    font-family: var(--font-mono);
    font-size: 11.5px;
    line-height: 1.65;
    white-space: pre-wrap;
    word-break: break-all;
}

/* ============================ 结果 ============================ */
.result-body {
    display: flex;
    align-items: flex-start;
    gap: 26px;
    flex-wrap: wrap;
}

/* 白相框 + 硬投影 + 轻微歪斜（像贴在墙上的一张照片） */
.result-frame {
    position: relative;
    flex: none;
    max-width: 100%;
    padding: 10px;
    background: #fff;
    border: 3px solid var(--ink);
    border-radius: var(--r-md);
    box-shadow: 0 6px 0 rgba(34, 31, 51, .22);
    transform: rotate(-1deg);
    animation: frame-in .45s var(--ease) both;
}

.result-frame > img {
    display: block;
    max-width: 380px;
    max-height: 300px;
    border-radius: var(--r-sm);
    background: var(--bg-deep);
}

/* 转换成功后从相框右下角探出来的贴纸角色 */
/* 结果面板里的角色贴纸是"出框"的（right / bottom 各 -22px），而 .panel 为了裁粉条圆角
   加了 overflow:hidden —— 这里补 10px 下内边距，保证贴纸本体和它的硬投影都完整。 */
.panel-result {
    padding-bottom: 32px;
}

.result-mascot {
    position: absolute;
    right: -22px;
    bottom: -22px;
    width: 104px;
    transform: rotate(6deg);
    filter: drop-shadow(0 4px 0 rgba(34, 31, 51, .18));
    pointer-events: none;
    animation: float 1.8s var(--ease) infinite alternate;
}

.result-meta {
    flex: 1;
    min-width: 230px;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 16px;
    padding-top: 6px;
}

.result-meta p {
    font-size: 13px;
    line-height: 1.9;
    color: var(--text-sub);
    word-break: break-all;
}

/* ============================ 页脚 ============================ */
.footer {
    margin-top: 30px;
    padding-top: 18px;
    border-top: 2px dashed var(--stroke-dark);
    font-size: 12px;
    line-height: 1.95;
    color: var(--text-dim);
    text-align: center;
}

.footer p + p {
    margin-top: 4px;
}

.footer b {
    color: var(--cta-stroke);
}

.footer a {
    color: var(--cta-stroke);
    box-shadow: inset 0 -.5em 0 rgba(248, 225, 121, .5);
}

.footer a:hover {
    color: var(--primary);
}

/* ============================ Toast ============================ */
.toast-wrap {
    position: fixed;
    left: 50%;
    bottom: 34px;
    z-index: 60;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 10px;
    transform: translateX(-50%);
    pointer-events: none;
}

.toast {
    display: flex;
    align-items: center;
    justify-content: center;
    min-width: 300px;
    min-height: 64px;
    max-width: 78vw;
    padding: 0 34px;
    background: url('angel/img/guide_tip_bg.png') center/100% 100% no-repeat;
    color: #fff;
    font-family: var(--font-brand);
    font-size: 14.5px;
    text-align: center;
    text-shadow: 0 2px 0 rgba(122, 46, 77, .5);
    animation: fade-in-up .3s var(--ease) both;
}

.toast.leaving {
    animation: fade-out-down .25s var(--ease) both;
}

/* ============================ 提示 / 确认弹窗 ============================ */
.modal-mask {
    position: fixed;
    inset: 0;
    z-index: 80;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
    background: rgba(38, 32, 64, .55);
}

/* 白粉底 + 3px 墨框 + 22px 圆角 + 0 8px 硬投影 + 入场弹跳（DESIGN-REFERENCE 弹窗配方） */
.modal {
    position: relative;
    width: min(440px, 92vw);
    padding: 28px 26px 22px;
    background: var(--bg-card);
    border: 3px solid var(--ink);
    border-radius: var(--r-lg);
    box-shadow: 0 8px 0 rgba(34, 31, 51, .35);
    text-align: center;
    animation: modal-pop .34s var(--ease) both;
}

.modal::before {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    height: 5px;
    background: var(--accent-pink);
    border-radius: calc(var(--r-lg) - 3px) calc(var(--r-lg) - 3px) 0 0;
}

.modal-tip {
    display: grid;
    place-items: center;
    width: 60px;
    height: 60px;
    margin: 2px auto 14px;
    background: #fff;
    border: 3px solid var(--ink);
    border-radius: 18px;
    font-size: 28px;
    line-height: 1;
    box-shadow: 0 4px 0 rgba(34, 31, 51, .22);
    transform: rotate(-5deg);
}

.modal-title {
    font-family: var(--font-brand);
    font-size: 19px;
    color: var(--text-title);
}

.modal-desc {
    margin-top: 9px;
    font-size: 13.5px;
    line-height: 1.85;
    color: var(--text-sub);
    /* 文案里的 \n 换行保留 */
    white-space: pre-line;
}

.modal-foot {
    display: flex;
    justify-content: center;
    gap: 12px;
    margin-top: 22px;
}

/* ============================ 点击粒子 ============================ */
.fx-layer {
    position: fixed;
    inset: 0;
    z-index: 70;
    pointer-events: none;
    overflow: hidden;
}

.fx-click {
    position: absolute;
    width: 34px;
    height: 34px;
    margin: -17px 0 0 -17px;
    background: url('assets/click-effect.png') 0 0 no-repeat;
    background-size: 170px 34px;
    animation: fx-click .25s steps(5) forwards;
}

@keyframes fx-click {
    from { background-position: 0 0; }
    to { background-position: -170px 0; }
}

/* ============================ 动画 ============================ */
@keyframes fade-in-up {
    from { opacity: 0; transform: translateY(18px); }
    to { opacity: 1; transform: translateY(0); }
}

@keyframes fade-out-down {
    from { opacity: 1; transform: translateY(0); }
    to { opacity: 0; transform: translateY(10px); }
}

@keyframes float {
    from { transform: translateY(0) rotate(-5deg); }
    to { transform: translateY(-6px) rotate(-5deg); }
}

@keyframes breath {
    from { transform: scale(.86); }
    to { transform: scale(1.12); }
}

@keyframes deco-sway {
    from { transform: rotate(-4deg) translateY(0); }
    to { transform: rotate(4deg) translateY(-3px); }
}

@keyframes barber {
    from { background-position: 0 0; }
    to { background-position: 32px 0; }
}

@keyframes modal-pop {
    0% { opacity: 0; transform: translateY(16px) scale(.94); }
    60% { transform: translateY(-4px) scale(1.02); }
    100% { opacity: 1; transform: translateY(0) scale(1); }
}

@keyframes frame-in {
    from { opacity: 0; transform: rotate(3deg) translateY(14px); }
    to { opacity: 1; transform: rotate(-1deg) translateY(0); }
}


/* ============================ 算法分段（2026-09-14 新增） ============================ */

/* 模式切换：面板头右侧的贴纸胶囊对（与 .ratio-btn 同配方） */
.mode-switch {
    display: inline-flex;
    gap: 6px;
    margin-left: auto;
}

.mode-btn {
    padding: 5px 15px;
    background: #fff;
    border: 2.5px solid var(--ink);
    border-radius: 999px;
    color: var(--text-sub);
    font-size: 12.5px;
    line-height: 1.5;
    cursor: pointer;
    box-shadow: 0 3px 0 rgba(34, 31, 51, .22);
    transition: transform .2s var(--ease), box-shadow .2s var(--ease),
        background .2s var(--ease), color .2s var(--ease);
}

.mode-btn:hover {
    background: var(--bg-card-2);
    color: var(--cta-stroke);
    transform: translateY(-2px);
    box-shadow: 0 5px 0 rgba(34, 31, 51, .22);
}

.mode-btn.is-active {
    background: var(--accent-pink);
    border-color: var(--cta-stroke);
    color: #fff;
    transform: rotate(-2deg);
    box-shadow: 0 3px 0 var(--cta-stroke);
}

/* 时间轴上的分段色条层：盖在选区与滑块之间，pointer-events 只给色条本身 */
.seg-strip {
    position: absolute;
    inset: 0;
    pointer-events: none;
    /* 兜底：即使分段数据超界（left+width 本应在 JS 侧联动 clamp），色条也不会画到时间轴外面去。
       色条自身没有投影、`.seg-block` 只有 8px 高顶在 top:13px，裁切不会伤到它。 */
    overflow: hidden;
}

.seg-block {
    position: absolute;
    top: 13px;
    height: 8px;
    background: var(--accent-gold);
    border: 2px solid var(--cta-stroke);
    border-radius: 4px;
    opacity: .9;
    pointer-events: auto;
    cursor: pointer;
    transform: translateY(0);
    transition: transform .15s var(--ease), opacity .15s var(--ease);
}

.seg-block:hover {
    transform: translateY(-2px);
    opacity: 1;
}

.seg-block-mixed {
    background: var(--text-dim);
    border-color: var(--ink);
}

/* 算法分析栏（仅算法模式出现）—— 默认整条落在预览区下方；够宽（≥1228px）时并排到右侧。
   grid 的 auto-fit 让它兼顾两种形态：竖栏（内容宽 ~244px）只有 1 列、字段上下排；
   撑满一行时自动铺成多列，回到原来的流式横排观感。
   min-width:0 是必须的 —— flex item 默认 min-width:auto 会按内容的 min-content 撑宽，
   而这里的 select（最长 option 是「按键桥（推荐 · 需录制配对）」）会把它撑到 1400px 以上，
   直接把并排判定顶掉。 */
.auto-bar {
    flex: 0 0 100%;
    min-width: 0;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    align-content: start;
    gap: 11px;
    padding: 14px;
    background: var(--bg-deep);
    border: 2px dashed var(--stroke-dark);
    border-radius: var(--r-md);
}

.auto-bar > * {
    min-width: 0;
}

@media (min-width: 1228px) {
    .auto-bar {
        flex: 0 0 276px;
    }
}

/* 每个字段「标签在上、控件满宽在下」，否则长 option 会把 select 撑出格子 */
.auto-bar .auto-field {
    flex-direction: column;
    align-items: stretch;
    gap: 5px;
}

.auto-bar .auto-field select,
.auto-bar .auto-field input[type='number'] {
    width: 100%;
    min-width: 0;
}

.auto-bar > .btn {
    width: 100%;
}

/* 素材文件夹行（只在 算法分段 + 按键桥 出现，挂在算法选择正下方）：
   276px 的竖栏里按钮和说明挤一行会太窄，允许换行、说明吃掉剩余宽度 */
.material-row {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    min-width: 0;
}

.material-row .record-label {
    flex: 1;
    min-width: 0;
    font-size: 11.5px;
    line-height: 1.5;
    letter-spacing: 0;
}

/* 状态文字横跨整行（横条里原本靠 flex:1 撑满，留给「渲染队列」面板的批量状态继续用原规则 ——
   那块已经不是 .auto-bar 的子元素了，这里的选择器不会误伤它） */
.auto-bar .auto-status {
    grid-column: 1 / -1;
    min-width: 0;
}

.auto-field {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: 12.5px;
    color: var(--text-sub);
}

.auto-field input[type='number'] {
    width: 72px;
    padding: 6px 9px;
    background: #fff;
    border: 2px solid var(--ink);
    border-radius: var(--r-sm);
    color: var(--text-title);
    font-size: 13px;
    font-variant-numeric: tabular-nums;
    box-shadow: 0 2px 0 rgba(34, 31, 51, .16);
}

.auto-field input[type='number']:focus {
    outline: none;
    border-color: var(--primary);
    box-shadow: 0 2px 0 rgba(238, 119, 169, .40);
}

.auto-field select {
    padding: 6px 10px;
    background: #fff;
    border: 2px solid var(--ink);
    border-radius: var(--r-sm);
    color: var(--text-title);
    font-size: 13px;
    cursor: pointer;
    box-shadow: 0 2px 0 rgba(34, 31, 51, .16);
}

/* 状态提示（分析进度/结果、批量渲染进度都落在这里）：字号与颜色都比普通次要文字更实一点，
   它是这块面板的主要反馈位，不该淡到看不见 */
.auto-status {
    flex: 1;
    min-width: 200px;
    font-size: 13px;
    line-height: 1.65;
    color: var(--text-body);
}

/* 「分析状态小面板」：算法 / 参数有没有应用上 / 保留与排除段数 —— 三行一眼看全（站长要求） */
.analyze-panel {
    grid-column: 1 / -1;
    display: flex;
    flex-direction: column;
    gap: 5px;
    margin-top: 2px;
    padding: 10px 14px;
    background: #fff;
    border: 2px solid var(--stroke-dark);
    border-radius: var(--r-md);
    box-shadow: 0 3px 0 rgba(34, 31, 51, .08);
}

.ap-row {
    display: flex;
    align-items: baseline;
    gap: 10px;
    font-size: 13px;
    line-height: 1.55;
}

.ap-k {
    flex: 0 0 40px;
    font-size: 12.5px;
    color: var(--text-sub);
}

.ap-v {
    flex: 1;
    min-width: 0;
    color: var(--text-title);
}

.ap-v.is-ok {
    color: var(--mint-deep);
}

.ap-v.is-warn {
    color: var(--warn);
}

/* 明细行：一句链式算式（候选 → 合并 → 太短 → 切分 → 保留），比正文小一档、允许换行 */
.ap-detail {
    font-size: 12.5px;
    color: var(--text-sub);
    line-height: 1.6;
    word-break: break-all;
}

/* 分段结果卡片列表 */
.panel-seg {
    --i: 3;
}

.seg-list {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
}

.seg-card {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 8px 14px;
    background: #fff;
    border: 2px solid var(--ink);
    border-radius: 999px;
    cursor: pointer;
    transition: transform .2s var(--ease), box-shadow .2s var(--ease),
        background .2s var(--ease);
    box-shadow: 0 3px 0 rgba(34, 31, 51, .22);
}

.seg-card:hover {
    background: var(--bg-card-2);
    transform: translateY(-2px);
    box-shadow: 0 5px 0 rgba(34, 31, 51, .22);
}

.seg-card:active {
    transform: translateY(2px);
    box-shadow: 0 0 0 transparent;
}

.seg-no {
    padding: 1px 9px;
    background: var(--ink);
    border-radius: 999px;
    color: #fff;
    font-size: 11.5px;
}

.seg-time {
    font-size: 13px;
    color: var(--text-title);
}

.seg-dur {
    font-size: 12px;
    color: var(--text-sub);
}

.seg-score {
    font-size: 11.5px;
    color: var(--gold-text);
}

.seg-tag {
    padding: 1px 9px;
    background: var(--bg-card-2);
    border: 1.5px solid var(--stroke-dark);
    border-radius: 999px;
    font-size: 11px;
    color: var(--text-sub);
}

.seg-empty {
    margin-top: 4px;
    font-size: 13.5px;
    line-height: 1.8;
    /* 这是"没切出段落"时的解释文案，站长要求醒目些 —— 别用最浅那一档灰 */
    color: var(--text-body);
}

/* 小屏沿用既有断点：并排/堆叠交给 .player-body 的 flex-wrap 自动决定，这里只修面板宽度 */
@media (max-width: 640px) {
    .auto-status {
        min-width: 0;
    }

    .mode-switch {
        margin-left: 0;
    }

    /* 本断点下 .container 的左右内边距是 14px，加宽用的 48px 要跟着改成 28px */
    .panel-player {
        --player-w: min(1180px, calc(100vw - 28px));
    }
}


/* ============================ 算法分段 · 二期（选中/队列/波形/批量） ============================ */

/* 能量波形画布（需求 7）：贴在播放控制条上方 */
.wave-canvas {
    display: block;
    width: 100%;
    height: 64px;
    margin-top: 14px;
    background: var(--bg-ink);
    border: 2px solid var(--ink);
    border-radius: var(--r-sm);
    cursor: crosshair;
}

/* 卡片选中态（需求 3）：金色实底 + 微抬，与队列态区分 */
.seg-card-selected {
    background: var(--accent-gold);
    border-color: var(--cta-stroke);
    box-shadow: 0 4px 0 var(--cta-stroke);
    transform: translateY(-2px);
}

.seg-card-selected .seg-no {
    background: var(--cta-stroke);
}

.seg-card-selected .seg-time,
.seg-card-selected .seg-dur,
.seg-card-selected .seg-score {
    color: var(--ink);
}

/* 卡片入队态（需求 5）：薄荷绿描边 + 队名标签；选中时叠加金色（22:25 站长要求：入队+选中 → 黄色） */
.seg-card-queued {
    border-color: var(--mint-deep);
    background: var(--mint-soft);
    box-shadow: 0 3px 0 var(--mint-deep);
}

/* 同时选中 + 入队：选中态（金）优先，源序在 queued 之后覆盖其背景/边框 */
.seg-card-queued.seg-card-selected {
    background: var(--accent-gold);
    border-color: var(--cta-stroke);
    box-shadow: 0 4px 0 var(--cta-stroke);
}

.seg-tag-queue {
    background: var(--mint);
    border-color: var(--mint-deep);
    color: #fff;
    max-width: 120px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* 时间轴色条状态 */
.seg-block-queued {
    background: var(--mint);
    border-color: var(--mint-deep);
}

/* 段内重命名输入（需求 5） */
.seg-rename {
    display: inline-flex;
}

.seg-rename-input {
    width: 110px;
    padding: 3px 9px;
    background: #fff;
    border: 2px solid var(--ink);
    border-radius: 999px;
    color: var(--text-title);
    font-size: 12px;
}

.seg-rename-input:focus {
    outline: none;
    border-color: var(--primary);
}

/* 渲染队列条目：一行一段（加入一个就多一行；渲染中整行变成进度条） */
.queue-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-bottom: 14px;
}

.queue-row {
    position: relative;
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 9px 14px;
    background: #fff;
    border: 2px solid var(--ink);
    border-radius: var(--r-sm);
    font-size: 13px;
    color: var(--text-body);
    /* 进度填充要裁进圆角里 */
    overflow: hidden;
}

/* 整行进度条：铺满行的斜纹填充，宽度 = 本段渲染进度（与页面主进度条同一套斜纹语言） */
.queue-fill {
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: 0;
    background-color: var(--accent-pink);
    background-image: repeating-linear-gradient(115deg,
            rgba(255, 255, 255, .26) 0 8px, transparent 8px 16px);
    transition: width .3s var(--ease);
    animation: barber 1.1s linear infinite;
    pointer-events: none;
}

/* 行内文字压在填充之上 */
.queue-row > *:not(.queue-fill) {
    position: relative;
    z-index: 1;
}

.queue-no {
    display: grid;
    place-items: center;
    min-width: 24px;
    height: 24px;
    padding: 0 6px;
    background: var(--bg-deep);
    border: 2px solid var(--ink);
    border-radius: 999px;
    font-size: 12px;
    color: var(--text-title);
}

.queue-name {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--text-title);
}

/* 右侧主体：上面一行名称，下面一行标点信息（已完成再补渲染结果） */
.queue-main {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.queue-line {
    display: flex;
    align-items: center;
    gap: 12px;
    min-width: 0;
}

.queue-meta {
    font-size: 11.5px;
    color: var(--text-dim);
}

.queue-row.is-done .queue-meta {
    color: var(--cta-stroke);
}

.queue-state {
    padding: 3px 10px;
    background: var(--bg-deep);
    border: 2px solid var(--stroke-dark);
    border-radius: 999px;
    font-size: 12px;
    color: var(--text-sub);
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

.queue-del {
    padding: 2px 4px;
    background: transparent;
    border: 0;
    font-size: 14px;
    line-height: 1;
    color: var(--text-dim);
    cursor: pointer;
    transition: color .2s var(--ease);
}

.queue-del:hover:not(:disabled) {
    color: var(--warn);
}

.queue-del:disabled {
    opacity: .3;
    cursor: default;
}

/* 渲染中：粉色填充铺过之处压住白字，所以文字统一走酒红（白底/粉底上都读得清） */
.queue-row.is-rendering {
    border-color: var(--cta-stroke);
}

.queue-row.is-rendering .queue-name,
.queue-row.is-rendering .queue-no,
.queue-row.is-rendering .queue-meta,
.queue-row.is-rendering .queue-state {
    color: var(--cta-stroke);
}

.queue-row.is-rendering .queue-no,
.queue-row.is-rendering .queue-state {
    background: rgba(255, 255, 255, .86);
    border-color: var(--cta-stroke);
}

/* 已完成：整行转粉 —— 与页面主色同族，且和「待渲染」的中性白底一眼分得开。
   （配色沿革：起初两态共用薄荷绿 → 后来待渲染改中性 → 现在已完成改用粉色，
     站长 2026-09-15 定稿：未渲染 = 白，已渲染 = 粉。）
   骨架（封面 / 两行信息）两态始终一致，只是配色分开。 */
.queue-row.is-done {
    background: var(--bg-card-2);
    border-color: var(--stroke-dark);
}

.queue-row.is-done .queue-name {
    color: var(--cta-stroke);
}

/* 已完成：实底粉胶囊 + 白字 */
.queue-row.is-done .queue-state {
    background: var(--accent-pink);
    border-color: var(--cta-stroke);
    color: #fff;
}

/* 失败：红色标识，不阻塞后续段 */
.queue-row.is-fail {
    background: #ffe2e3;
    border-color: var(--warn);
}

.queue-row.is-fail .queue-state {
    background: var(--warn);
    border-color: var(--cta-stroke);
    color: #fff;
}

/* 批量队列条与结果列表（需求 5/6）—— 现挂在独立的「渲染队列」面板里，
   紧跟 panel-head（它自带 margin-bottom），所以这里不再加顶部间距 */
/* 队列操作栏（计数 + 一键批量渲染 + ZIP）
   ⚠️ 底色用浅金，**不能再用薄荷绿** —— 下面的队列条目本身就是薄荷绿底，
   两处撞在一起分不清"哪一层是操作栏、哪一层是队列"（站长 2026-09-15 指出）。 */
.batch-bar {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    margin-top: 12px;
    padding: 12px 16px;
    background: #fdf2cf;
    border: 2px solid var(--gold-text);
    border-radius: var(--r-md);
}

.batch-count {
    font-size: 13px;
    color: var(--gold-text);
    word-break: break-all;
}

/* 空队列时的提示文案弱化，别和「真有 N 段」的实信息抢眼 */
.batch-count.is-empty {
    color: var(--text-dim);
}

.batch-results {
    margin-top: 12px;
    margin-top: 14px;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

/* 悬停产物行时的动图预览：fixed 居中大图，pointer-events:none 不挡鼠标
   （z-index 介于 toast(60) 与 modal(80) 之间） */
.batch-peek {
    position: fixed;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    max-width: min(480px, 72vw);
    max-height: min(60vh, 480px);
    border: 3px solid var(--ink);
    border-radius: var(--r-md);
    background: var(--bg-ink);
    box-shadow: 0 8px 0 rgba(34, 31, 51, .30);
    z-index: 75;
    pointer-events: none;
}

/* 产物行用**绿色系**底（站长 2026-09-16）：这批是实打实的成品，
   与「队列条目」（白 = 待渲染 / 粉 = 已渲染）区分开，绿 = 成品在手 */
.batch-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 8px 14px;
    background: var(--mint-soft);
    border: 2px solid var(--mint-line);
    border-radius: var(--r-sm);
    font-size: 13px;
    color: var(--mint-deep);
}

.batch-name {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--mint-deep);
}

.batch-row .num {
    color: var(--mint-deep);
}

/* 预览面板两列：左 = 画面/时间轴/跳帧/控制条，右 = 「分析算法」栏（站长 2026-09-16）。
   窄视口（<1100px）折回单列，分析栏回到预览下方，避免把预览挤太窄 */
.player-cols {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 330px;
    gap: 14px;
    align-items: start;
}

.player-side .auto-bar {
    flex-direction: column;
    align-items: stretch;
}

.player-side .auto-field {
    width: 100%;
}

/* 手动打点 / 录制模式下右栏（算法栏 + 分析状态）整体隐藏 —— 2026-09-16 移除
   「分析状态」后，右栏在这两种模式下永远为空，两列布局别再留 330px 空列：
   grid 收成单列占满，aside 整个 display:none。用 :has() 跟随 autoBar 的 hidden
   属性自动切换（显隐仍由 setSegMode 单点控制，这里纯响应式）。 */
.player-cols:has(.player-side > .auto-bar[hidden]) {
    grid-template-columns: minmax(0, 1fr);
}

.player-side:has(> .auto-bar[hidden]) {
    display: none;
}

/* 控制条强制换行点：「＋当前段加入队列」固定落到结束点按钮的正下方一排 */
.ctl-break {
    flex-basis: 100%;
    height: 0;
}

/* 右列容器：纵向 flex + gap，让「分析算法栏」与「分析状态」小面板之间留出呼吸间距
   （2026-09-16 站长反馈两者黏在一起） */
.player-side {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

/* 右列里的「分析状态」小面板：内边距收窄一点，跟分析算法栏的密度一致 */
.player-side .panel {
    margin-bottom: 0;
    padding: 14px 16px;
}

@media (max-width: 1100px) {
    .player-cols {
        grid-template-columns: minmax(0, 1fr);
    }
}

.batch-dl {
    text-decoration: none;
    padding: 5px 14px;
}


/* ============================ 算法分段 · 三期（移出队列 / 结果预览与删除） ============================ */

/* 入队卡片悬停时出现的「✕ 移出」小按钮 */
.seg-unqueue {
    display: none;
    align-items: center;
    padding: 1px 9px;
    background: #fff;
    border: 2px solid var(--mint-deep);
    border-radius: 999px;
    color: var(--mint-deep);
    font-size: 11px;
    cursor: pointer;
    user-select: none;
}

.seg-card-queued:hover .seg-unqueue {
    display: inline-flex;
}

.seg-unqueue:hover {
    background: var(--warn);
    border-color: var(--warn);
    color: #fff;
}

/* 删除按钮 */
.batch-del:hover:not(:disabled) {
    background: var(--warn);
    border-color: var(--warn);
    color: #fff;
}



/* ============================ 按键辅助（六期） ============================ */
.seg-tag-nokey {
    background: var(--bg-ink);
    border-color: var(--ink);
    color: #fff;
}


/* ============================ 条码手动标记（六期） ============================ */
.barcode-overlay {
    position: absolute;
    inset: 0;
    z-index: 15;
    cursor: crosshair;
    background: rgba(15, 12, 24, .35);
}

.barcode-hint {
    position: absolute;
    top: 12px;
    left: 50%;
    transform: translateX(-50%);
    padding: 6px 16px;
    background: var(--accent-gold);
    border: 2px solid var(--ink);
    border-radius: 999px;
    font-size: 12px;
    color: var(--ink);
    white-space: nowrap;
    pointer-events: none;
}

.barcode-rect {
    position: absolute;
    border: 2px dashed var(--mint);
    background: rgba(54, 186, 130, .18);
    border-radius: 4px;
    pointer-events: none;
}


/* ============================ 七期：录制（方案 E） ============================ */
.panel-record {
    --i: 2;
}

/* 录制素材面板单独加宽、居中溢出容器（不动 .container，其它面板宽度不变）——
   屏幕预览要给足横向空间。两处 min() 必须一致：窄视口下 --w 退化成「正好等于内容宽」，
   margin-left 自然算成 0，不会把页面撑出横向滚动条。
   只在够宽的视口生效：小屏那档容器 padding 更小，继续按 48px 扣会溢出。 */
@media (min-width: 1000px) {
    .panel-record {
        --w: min(1180px, calc(100vw - 48px));
        width: var(--w);
        margin-left: calc((100% - var(--w)) / 2);
    }
}

.record-grid {
    display: grid;
    /* 左列（屏幕预览）吃掉大部分宽度：minmax(0,…) 是必须的 ——
       右列那串长文案/按钮的 min-content 会把等分列顶宽，预览反而变窄 */
    grid-template-columns: minmax(0, 1.75fr) minmax(0, 1fr);
    gap: 20px;
}

@media (max-width: 860px) {
    .record-grid {
        grid-template-columns: 1fr;
    }
}

.record-cell {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.record-label {
    font-size: 12px;
    letter-spacing: .08em;
    color: var(--text-dim);
}

.record-preview {
    width: 100%;
    background: #000;
    border: 2px solid var(--ink);
    border-radius: var(--r-sm);
    /* 没开始录制时的占位高度（有画面后按视频自身比例撑开，不会变形） */
    min-height: 320px;
}

.bridge-state {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 10px 14px;
    background: var(--bg-deep);
    border: 2px solid var(--ink);
    border-radius: var(--r-sm);
    font-size: 13px;
    color: var(--text-body);
}

.bridge-state .rich-dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    display: inline-block;
}

/* 「下载按键桥」：<a> 撑成按钮的样子（.btn 是 class 选择器，a 一样吃） */
.bridge-dl {
    margin: 9px 0 2px;
    width: 100%;
    justify-content: center;
}

.record-steps {
    margin: 4px 0 8px 1.2em;
    font-size: 13px;
    color: var(--text-sub);
}

.record-steps li {
    margin-bottom: 6px;
}

.record-actions {
    display: flex;
    flex-wrap: wrap;   /* 放不下整颗换行，不压缩按钮（站长 03:38：录完后关桥按钮被压变形） */
    gap: 10px;
}

.record-actions.rec-product {
    margin-top: 10px;   /* 与「关闭按键桥」那排留出呼吸感 */
    gap: 6px;           /* 右列 404px 里三颗按钮刚好一排放下（差 10px 就得换行变 2+1） */
}

.rec-product .btn {
    padding-left: 12px;
    padding-right: 12px;
}

.record-actions .btn {
    white-space: nowrap;   /* 按钮文字不换行 —— 以前被 flex 压成窄椭圆、文字竖排 */
}

/* 录制中的实时状态（停止按钮右边）：红点红字，一眼看到「正在录」 */
#recLive:not([hidden]) {
    color: var(--warn);
    font-family: var(--font-num);
    font-size: 13px;
}

#recLiveTime {
    font-variant-numeric: tabular-nums;
    color: var(--text-sub);
    font-size: 13px;
}

.record-actions .btn:disabled {
    filter: grayscale(1);
    opacity: .55;
    cursor: not-allowed;
}

/* ---- 按键灯条（录制中的实时按键反馈） ----
   一键一灯：每颗灯是键帽小方块，按下的瞬间「下沉变粉」，松开 180ms 后弹回。
   灯数上限 18，超了 FIFO 摘最旧的（键盘上常见键就那几十个，18 颗足够铺满两行又不撑爆面板）。 */
.key-lamp-bar {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 8px 10px;
    background: var(--bg-deep);
    border: 2px solid var(--stroke-dark);
    border-radius: var(--r-sm);
}

.key-lamp-cap {
    flex: 0 0 auto;
    padding-top: 5px;
    font-size: 12px;
    letter-spacing: .08em;
    color: var(--text-dim);
}

.key-lamp-track {
    flex: 1;
    min-width: 0;
    display: flex;
    /* 固定一行（3 颗灯上限由 JS 保证）：父盒不随按键变多而撑大 */
    flex-wrap: nowrap;
    overflow: hidden;
    height: 26px;
    gap: 6px;
}

.key-lamp {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 34px;
    height: 26px;
    padding: 0 8px;
    background: var(--bg-light);
    border: 2px solid var(--ink);
    border-radius: var(--r-sm);
    box-shadow: 0 2px 0 rgba(34, 31, 51, .15);
    font-family: var(--font-num);
    font-size: 12.5px;
    font-variant-numeric: tabular-nums;
    color: var(--text-sub);
    /* 动效只走 transform / opacity：亮灯的变色是瞬时切换，下沉用 transform 补 */
    transition: transform .12s var(--ease);
}

.key-lamp.lit {
    background: var(--primary);
    border-color: var(--ink);
    color: #fff;
    transform: translateY(1px) scale(.92);
    box-shadow: 0 1px 0 rgba(34, 31, 51, .25);
}

.record-meta {
    font-size: 12px;
    color: var(--text-dim);
    font-variant-numeric: tabular-nums;
}

/* 录制中的醒目条：浅红实底 + 硬边描边（贴纸风不给渐变），圆点脉冲只走 opacity/transform */
.rec-live {
    display: flex;
    align-items: center;
    gap: 9px;
    margin-top: 10px;
    padding: 9px 13px;
    background: #ffe2e3;
    border: 2px solid var(--warn);
    border-radius: var(--r-sm);
    font-size: 14px;
    color: var(--warn);
}

.rec-live-dot {
    width: 11px;
    height: 11px;
    border-radius: 50%;
    background: var(--warn);
    animation: recPulse 1s ease-in-out infinite;
}

@keyframes recPulse {
    0%,
    100% {
        opacity: 1;
        transform: scale(1);
    }

    50% {
        opacity: .3;
        transform: scale(.8);
    }
}

@media (prefers-reduced-motion: reduce) {
    .rec-live-dot {
        animation: none;
    }
}

.rec-live-time {
    margin-left: auto;
    font-size: 17px;
    font-variant-numeric: tabular-nums;
}

/* 录制中「停止录制」转红实底 —— 那一刻唯一该点的就是它 */
.btn.is-recording {
    background: var(--warn);
    border-color: var(--ink);
    color: #fff;
}

/* 置灰的链接按钮（<a> 没有 disabled，用 class 表达）：透明度压下去 + 点不动 */
.btn.is-off {
    opacity: .45;
    pointer-events: none;
}

/* 录制产物的下载按钮行（录完才出现）：右栏窄，允许换行 */
.record-dl {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    margin-top: 11px;
}

/* ---- 录制参数栏（分辨率 / 帧率 / 码率） ----
   与 .auto-bar 同一套：虚线浅底容器 + auto-fit 网格，字段「标签在上、控件满宽在下」。
   min-width:0 同属必需项 —— 长 option（「自动（按尺寸与帧率）」）会把 select 撑到 min-content
   那么宽，不夹住就把整条网格顶出面板。 */
.rec-opts {
    margin-top: 18px;
    padding: 14px;
    background: var(--bg-deep);
    border: 2px dashed var(--stroke-dark);
    border-radius: var(--r-md);
    min-width: 0;
}

.rec-opts-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    flex-wrap: wrap;
    margin-bottom: 11px;
}

.rec-opts-title {
    font-size: 13px;
    color: var(--text-title);
    letter-spacing: .04em;
}

/* 「实际生效值」读数：墨底胶囊，与滑块读数同一种游戏 UI 语言 */
.rec-actual {
    padding: 3px 12px;
    background: var(--bg-ink);
    border-radius: 999px;
    font-size: 12px;
    color: #fff;
}

.rec-opts-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 11px;
}

.rec-opts-grid > * {
    min-width: 0;
}

.rec-field {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 5px;
    min-width: 0;
    font-size: 12.5px;
    color: var(--text-sub);
}

.rec-field select,
.rec-field input[type='number'] {
    width: 100%;
    min-width: 0;
    padding: 6px 10px;
    background-color: #fff;
    border: 2px solid var(--ink);
    border-radius: var(--r-sm);
    color: var(--text-title);
    font-size: 13px;
    font-variant-numeric: tabular-nums;
    box-shadow: 0 2px 0 rgba(34, 31, 51, .16);
}

.rec-field select {
    cursor: pointer;
    appearance: none;
    padding-right: 30px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.6 6 6.4l5-4.8' fill='none' stroke='%23221f33' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 11px center;
}

.rec-field select:hover,
.rec-field input[type='number']:hover {
    border-color: var(--primary);
}

.rec-field select:focus,
.rec-field input[type='number']:focus {
    outline: none;
    border-color: var(--primary);
    box-shadow: 0 2px 0 rgba(238, 119, 169, .40);
}

.rec-field:has(select:disabled),
.rec-field:has(input:disabled) {
    filter: grayscale(1);
    opacity: .6;
}

/* 自定义尺寸独占一行：两个数字框挤在一格（~200px）里会窄到看不清 */
.rec-wh {
    grid-column: 1 / -1;
}

.rec-wh-inputs {
    display: flex;
    align-items: center;
    gap: 6px;
    min-width: 0;
    max-width: 320px;   /* 独占一行时别把两个数字框拉成 400px 宽 */
}

.rec-wh-inputs input {
    min-width: 0;
    flex: 1;
}

.rec-x {
    color: var(--text-dim);
}

.rec-sub {
    font-size: 11.5px;
    color: var(--text-dim);
}

.rec-note {
    margin-top: 13px;
}

.rec-note b {
    color: var(--text-body);
}


/* ============================ 结构补齐样式（16:06, 修复 DOM/CSS 不同步造成的裸元素） ============================ */

/* 隐藏类: 全站通用兜底（HTML hidden 属性失效的双保险） */
[hidden] {
    display: none !important;
}

/* ---- 录制参数面板 ---- */
.record-params {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    margin-top: 12px;
    padding: 12px 16px;
    background: var(--bg-deep, #f6f2ea);
    border: 2px solid var(--ink, #221f33);
    border-radius: 10px;
}

.record-params .auto-field {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 12.5px;
    color: var(--text-sub, #55506b);
}

.record-params select,
.record-params input[type='number'] {
    padding: 5px 9px;
    background: #fff;
    border: 2px solid var(--ink, #221f33);
    border-radius: 8px;
    font-size: 13px;
    color: var(--text-title, #221f33);
}

.record-params input[type='number'] {
    width: 76px;
}

.recLive {
    color: #e05555;
    font-weight: 700;
    animation: rec-blink 1s steps(2) infinite;
}

@keyframes rec-blink {
    50% { opacity: .25; }
}

.recLoadBtn, .recDownVideo, .recDownKeys, .bridgeShutdownBtn, .build-stamp {
    display: none;
}

/* ---- 分析状态小面板 ---- */
.analyzePanel .panel-title + p,
.analyzePanel p {
    font-size: 13.5px;
    color: var(--text-sub, #55506b);
    margin: 4px 0;
    padding-left: 1em;
}

/* ---- 渲染队列面板 ---- */
.queueSection[hidden] { display: none; }

#queueList {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
}

/* ---- 按键灯条 ---- */
#keyLampBar[hidden] { display: none; }

#keyLampTrack {
    display: flex;
    gap: 8px;
    flex-wrap: nowrap;
    overflow: hidden;
    /* 固定一行高（3 颗灯上限由 JS 保证）：父盒不随按键变多而撑大（站长 03:32） */
    height: 26px;
    align-items: center;
}

/* ---- 逐帧步进条 ---- */
/* ⚠️ 2026-09-16 恢复记录：这里原来有一条「#mp3Player / #mp3Wrapper / #mp3Kernel /
   #celebrate / #idolFloor 一律 position:fixed + display:none」的规则 —— 是撞车重建后
   为藏空壳加的（注释写"默认收起, 脚本可控显隐"，但那个显隐脚本并不存在），
   结果把 MP3 播放器、三主角散步层、celebrate 撒花全部永久藏死。
   现在这些 DOM 已恢复完整结构，显隐各有自己的机制（JS 的 hidden 属性 /
   prefers-reduced-motion 媒体查询 / .mp3-kernel 自身 display:none），本条规则删除。 */

/* ---- 庆祝彩带 / 吉祥物地面层 ---- */

/* ⚠️ 2026-09-16 恢复记录：上面那段「结构补齐样式」里，有一批 #mp3Shell / #mp3Screen /
   #mp3Cover / #mp3Charm / #mp3Progress* / #mp3Song* / #mp3Prev... / #mp3MiniBadge /
   #mp3MiniBtn / #mp3Kernel / #idolFloor 的 ID 规则是并行会话为「空壳 div 骨架」画的替代样式
   （当时的 DOM 里这些元素全是无图 div）。DOM 已恢复为 img 真结构，这些 ID 规则全部与之冲突
   （ID 特异性压过 .mp3-* 类规则：外壳被包上边框、挂件缩成 22px 圆点、缩小按钮/圆贴被永久
   display:none、#idolFloor 高度 120px 把 190px 的小人画布平整截断、#celebrate 缺 position）
   —— 已整批删除，MP3 播放器 / 小人层的样式回归 style.css 末尾那两段移植样式。 */


#celebrate {
    position: fixed;
    inset: 0;
    pointer-events: none;
    z-index: 50;
}

/* ---- 结构补齐样式 · 第二批（子元素/修饰类） ---- */
.recWhRow, .recFpsCustomRow, .recBitrateCustomRow {
    display: inline-flex;
    align-items: center;
    gap: 4px;
}

#recActual {
    font-size: 12px;
    color: var(--text-dim, #8a85a0);
    font-variant-numeric: tabular-nums;
}

#recLiveTime {
    font-variant-numeric: tabular-nums;
    color: var(--text-dim, #8a85a0);
}

#keyLampHint {
    font-size: 12px;
    color: var(--text-dim, #8a85a0);
}

#apAlgo, #apParams, #apResult, #apDetail {
    list-style: none;
}

#frameStepOn {
    accent-color: var(--accent-pink, #ee77a9);
}

#frameStepN {
    width: 60px;
}

/* ============================ 降级 / 偏好 ============================ */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: .01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .01ms !important;
    }

    .fx-layer {
        display: none;
    }

    .hb-tile,
    .result-mascot,
    .progress-fill {
        animation: none;
    }
}

/* 桌面优先；沿用本页原有的小屏断点，不主动扩展适配 */
@media (max-width: 640px) {
    .container {
        padding: 72px 14px 32px;
    }

    .stage {
        aspect-ratio: 4 / 3;
    }

    .dropzone {
        min-height: 0;
        padding: 24px;
    }

    .dz-mascot,
    .dz-star,
    .hero-tag {
        display: none;
    }

    .hero-title {
        font-size: 30px;
    }

    .hero-title::before {
        -webkit-text-stroke-width: 7px;
    }

    .player-controls {
        gap: 8px;
    }

    .spacer {
        display: none;
    }

    .result-body {
        gap: 18px;
    }

    .result-frame {
        transform: none;
    }

    .result-frame > img {
        max-width: 100%;
    }

    .result-mascot {
        display: none;
    }
}

/* ============================ 三主角散步层 ============================
   移植自首页训练房间（HomeMainPage.vue 的 .idol-floor 一套）。差异：
     · 改成 position:fixed 贴住浏览器窗口下边缘（首页是绝对定位在舞台地板线上）
     · 位移用 transform: translateX（首页改 left 百分比）→ 少一次重排，且不依赖容器宽度
   整层 pointer-events:none：小人纯装饰，点它等于点到它下面的东西。 */
.idol-floor {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    /* 高于画布（150×397/313 ≈ 190px）40px：Spine 动画的手臂/头发偶尔会画出
       等高视口上沿，首页同款做法是「容器高于画布且不裁剪」，这里保持一致 ——
       ⚠️ 不要加 overflow:hidden（加了就会把小人顶部平整截断） */
    height: 230px;
    z-index: 6;              /* 内容之上、toast(60)/modal(80) 之下 */
    pointer-events: none;
}

.idol-unit {
    position: absolute;
    left: 0;
    bottom: 0;
    width: 150px;
    text-align: center;
    user-select: none;
    -webkit-user-select: none;
    will-change: transform;
    /* 走路位移：时长由 JS 按距离算（约 7%/s 的散步速度，与首页同节奏） */
    transition: transform 1.2s linear;
}

.idol-unit .flipper {
    transition: transform .15s var(--ease);
}

.idol-clip {
    aspect-ratio: 313 / 397;
    width: 100%;
    overflow: hidden;
}

/* 初始藏在地板下，加载完成后 risen 升起来（同首页的入场动画） */
.idol-view {
    height: 100%;
    transform: translateY(108%);
}

.idol-unit.risen .idol-view {
    animation: idol-rise .65s var(--ease) both;
}

.idol-unit.risen:nth-child(2) .idol-view {
    animation-delay: .12s;
}

.idol-unit.risen:nth-child(3) .idol-view {
    animation-delay: .24s;
}

/* ============ 全部渲染完成的小庆祝 ============ */

/* 彩纸层：整屏覆盖但 pointer-events:none，z-index 低于弹窗（60）免得盖住对话框 */
.celebrate {
    position: fixed;
    inset: 0;
    z-index: 55;
    overflow: hidden;
    pointer-events: none;
}

.confetti {
    position: absolute;
    top: -16px;
    width: 9px;
    height: 14px;
    border: 1.5px solid var(--ink);
    border-radius: 2px;
    animation: confettiFall var(--dur, 2.2s) linear var(--delay, 0s) forwards;
}

@keyframes confettiFall {
    0% {
        opacity: 0;
        transform: translate3d(0, 0, 0) rotate(0deg);
    }

    12% {
        opacity: 1;
    }

    100% {
        opacity: 0;
        transform: translate3d(var(--dx, 0px), 102vh, 0) rotate(var(--rot, 360deg));
    }
}

/* 小人跳起：挂在 .idol-clip 上 —— 不能碰 .idol-unit（走路用 translateX）
   和 .flipper（镜像用 scaleX），那两个 transform 正被 JS 用着，会互相覆盖 */
.idol-clip.is-celebrating {
    animation: idolJump .55s var(--ease) 3;
}

@keyframes idolJump {
    0%,
    100% {
        transform: translateY(0);
    }

    45% {
        transform: translateY(-18px);
    }
}

@media (prefers-reduced-motion: reduce) {

    .confetti,
    .idol-clip.is-celebrating {
        animation: none;
    }
}

@keyframes idol-rise {
    from { transform: translateY(108%); }
    to { transform: translateY(0); }
}

.idol-view canvas {
    width: 100% !important;
    height: 100% !important;
}

/* 系统开了「减少动态效果」：整层不出现（JS 里也会因此不加载那 6MB 骨架资源） */
@media (prefers-reduced-motion: reduce) {
    .idol-floor {
        display: none;
    }
}


/* ============================ MP3 造型悬浮播放器 ============================
   整段搬自 src/components/Mp3MusicPlayer.vue 的 <style scoped> —— 该文件里没有任何
   url() 与 :deep()，选择器全部挂在 .mp3-* 前缀下，所以可以原样复用（静态页里不需要
   scoped 属性选择器）。组件那边改了样式要同步这边。
   ⚠️ 造型素材已复制到本目录 assets/mp3/（见 index.html 里的 src）。 */

/* ========== 容器 ========== */
.mp3-player {
  position: fixed !important;
  z-index: 99999 !important;
  isolation: isolate;
  user-select: none;
}

.mp3-wrapper {
  position: relative;
  width: 230px;
  height: 310px;
  overflow: hidden;
  cursor: grab;
}

.mp3-player.dragging {
  cursor: grabbing;
}

/* ========== 小挂件（挂在壳右上角的环上） ========== */
/* 链接点 = 外壳挂环右端中心（源坐标 x430,y93 → 显示 219.8,47.5）；
   定位 = 链接点 - (origin 分数 × 显示尺寸)：显示尺寸 37×104，origin(18.8%, 0.5%) */
.mp3-charm {
  position: absolute;
  left: 207px;
  top: 47px;
  width: 37px;
  height: 104px;
  transform-origin: 18.8% 0.5%;
  pointer-events: none; /* 纯装饰，事件全部穿透 */
  z-index: 7;           /* 壳(5)/控制区(6) 之上：挂件整体压在机身前面，不再被外壳挡住 */
  will-change: transform;
}
.mp3-player.dragging .mp3-shell {
  cursor: grabbing;
}

/* ========== 右上角缩小按钮 ========== */
/* 复用 .mp3-btn 的圆形紫边底子，钉在机身右上角（机身顶边 y≈25，挂环/挂件在 (210+, 40+)，
   此处 (184..208, 18..42) 压住角边但不碰挂件摆动区；层级压过挂件(7) */
.mp3-mini-btn {
  position: absolute;
  top: 18px;
  right: 22px;
  width: 22px;
  height: 22px;
  font-size: 13px;
  font-weight: bold;
  z-index: 8;
}

/* ========== 缩小态圆贴（三小只，可拖拽，点击恢复机身） ========== */
/* 缩小后机身 v-show 消失、根元素塌成圆贴自身大小；用 margin 把圆贴推到原缩小按钮的位置。
   ⚠️ margin-left/top 与脚本里的 BADGE_OFFSET_X/BADGE_OFFSET_Y 是同一组数，改一处必改另一处 */
.mp3-mini-badge {
  display: block;
  width: 90px;
  height: 90px;
  margin-top: 18px;    /* = BADGE_OFFSET_Y，与按钮同高 */
  margin-left: 118px;  /* = BADGE_OFFSET_X：230(机身宽) - 22(按钮右距) - 90(圆贴宽) */
  cursor: grab;
  transition: transform 0.2s ease;
}

.mp3-mini-badge:hover {
  transform: scale(1.06);
}

.mp3-mini-badge:active {
  transform: scale(0.96);
}

.mp3-player.dragging .mp3-mini-badge {
  cursor: grabbing;
}

/* ========== 外壳图（顶层，屏幕镂空透出封面） ========== */
.mp3-shell {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 5;
  /* 注意：此处不要加 drop-shadow —— 阴影会沿外壳轮廓生成一圈整齐的深色光晕，
     在深色背景下会被看成"半透明黑边"。素材自带描边，无需额外投影。 */
}

/* ========== 镂空屏（封面） ========== */
/* 屏幕镂空区实测为显示坐标 x[27,193] / y[36.8,179]，四边外扩 2px 保证铺满不留缝，
   溢出部分被上层外壳图（不透明区）遮住，视觉上正好贴合镂空边缘 */
.mp3-screen {
  position: absolute;
  left: 25px;
  top: 34px;
  width: 170px;
  height: 147px;
  border-radius: 17px;
  overflow: hidden;
  z-index: 1;
  background: #0d2426;
  cursor: pointer;
}

/* 封面：外层负责 hover 放大，内层负责播放时的律动，两者互不打断 */
.mp3-cover-wrap {
  width: 100%;
  height: 100%;
  transition: transform 0.35s ease;
}

.mp3-screen:hover .mp3-cover-wrap {
  transform: scale(1.04);
}

.mp3-cover {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transform-origin: center 60%;
}

/* 播放中：轻微律动（缩放恒 ≥1，不会在屏幕边缘露出底色） */
.mp3-cover.beating {
  animation: mp3-cover-beat 1.5s ease-in-out infinite;
}

@keyframes mp3-cover-beat {
  0% { transform: scale(1); }
  50% { transform: scale(1.055); }
  100% { transform: scale(1); }
}

/* 歌名条：短名居中，超出宽度时左对齐并跑马灯滚动 */
.mp3-song-strip {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 14px 8px 5px;
  background: linear-gradient(to top, rgba(13, 30, 32, 0.82), transparent);
  color: #fff;
  font-size: 10px;
  line-height: 1.3;
  white-space: nowrap;
  overflow: hidden;
  display: flex;
  justify-content: center;
  pointer-events: none;
}

/* 溢出时改为左起，保证滚动起点一致 */
.mp3-song-strip.overflowing {
  justify-content: flex-start;
}

.mp3-song-track {
  display: flex;
  gap: 34px;
  will-change: transform;
}

.mp3-song-track.scrolling {
  animation-name: mp3-marquee;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}

.mp3-song-text {
  flex: 0 0 auto;
}

@keyframes mp3-marquee {
  0% { transform: translateX(0); }
  100% { transform: translateX(var(--mp3-marquee-shift, -120px)); }
}

/* ========== 控制区 ========== */
/* 顶 192px（原 184，整体下移让进度条离屏幕远一点）
   左 78 / 右 26：外壳在控制区高度的安全边界约 x[9,211]，右端留足余量不再溢出 */
.mp3-controls {
  position: absolute;
  left: 78px;
  top: 192px;
  right: 26px;
  bottom: 8px;
  /* 外壳图 z-index:5，控制区必须压在壳身上方才可见（屏幕镂空区仅用于透出封面） */
  z-index: 6;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding-top: 2px;
  /* 控制区空白同样可以抓着拖动，光标与外壳保持一致 */
  cursor: grab;
}

/* 进度条：整体右移 8px 避开左侧贴纸装饰，宽度相应减到 108px（右端仍为 199，在安全区内） */
.mp3-progress {
  position: relative;
  width: 108px;
  margin-left: 8px;
  flex: 0 0 auto;
  height: 6px;
  border-radius: 3px;
  background: rgba(0, 0, 0, 0.28);
  cursor: pointer;
  touch-action: none;
  overflow: visible;
}

.mp3-progress-loaded {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  border-radius: 3px;
  background: rgba(255, 255, 255, 0.25);
}

.mp3-progress-played {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  border-radius: 3px;
  background: #ff8cc6;
}

.mp3-progress-thumb {
  position: absolute;
  top: 50%;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: #fff;
  border: 2px solid #523874;
  transform: translate(-50%, -50%);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
  pointer-events: none;
  transition: width 0.15s ease, height 0.15s ease;
}

.mp3-progress:hover .mp3-progress-thumb {
  width: 14px;
  height: 14px;
}

.mp3-time {
  width: 108px;
  margin-left: 8px;
  display: flex;
  justify-content: space-between;
  flex: 0 0 auto;
  margin-top: 3px;
  font-size: 9px;
  font-weight: bold;
  color: rgba(255, 255, 255, 0.92);
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
  font-variant-numeric: tabular-nums;
}

/* 按钮行 */
.mp3-btn-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px; /* 原 13px：三键靠拢 */
  flex: 0 0 auto;
  margin-top: 4px; /* 原 7px：整组上移一点，与上方时间行留出呼吸 */
}

.mp3-btn {
  border: 2px solid #523874;
  border-radius: 50%;
  background: #fff;
  color: #523874;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: 0 2px 0 #523874;
  transition: transform 0.12s ease, background 0.15s ease, color 0.15s ease;
  padding: 0;
}

.mp3-btn:hover {
  background: #ffe3f1;
}

.mp3-btn:active {
  transform: translateY(2px);
  box-shadow: 0 0 0 #523874;
}

.mp3-skip-btn {
  width: 27px;
  height: 27px;
  font-size: 11px;
}

.mp3-play-btn {
  width: 44px;
  height: 44px;
  font-size: 16px;
  background: #dd60a2;
  color: #fff;
}

.mp3-play-btn:hover {
  background: #e97ec2;
}

/* ========== APlayer 内核隐藏（仅用 API） ========== */
.mp3-kernel {
  display: none;
}

/* ⚠️ hidden 属性在 .mp3-btn(display:flex) / .mp3-mini-badge(display:block) 上会被 display 覆盖掉，
   而播放器的显隐一律走 hidden 属性（原 Vue 版的 v-show），这里兜一条强制规则 */
.mp3-player [hidden] {
    display: none !important;
}

/* ⚠️ 层级适配（静态页特有）：组件里 .mp3-player 是 z-index:99999 !important，用来压过站点一切；
   而本页的弹窗(80) / toast(60) 必须能盖住播放器 —— 否则「分析失败」这类确认弹窗会被机身挡住。
   这里降到 55：仍在页面内容(40 及以下)与散步层(6)之上，但在 toast/modal 之下。 */
.mp3-player {
    z-index: 55 !important;
}

/* 倍速 / 跳帧倍数两个下拉统一等宽（内容长度不同导致一宽一窄，看着不齐） */
.speed-select {
    min-width: 88px;
}

/* 控制条排2 的弹性占位：把「一键转换（智能模式下隐藏）+ ＋当前段加入队列」整组
   推到行尾 —— 占位符永远在场，两种模式下「加入队列」都贴最右（2026-09-16 站长指定） */
.ctl-spacer {
    flex: 1 1 0;
    height: 0;
}


/* ============================ 核心技替换（swap） ============================ */
/* 原生工作区（2026-09-16 站长指定去 iframe）：内置三层 + 目标色 + 合成预览 + 参考图吸色。
   配色全部走主题变量，与录制/智能分段面板同骨架。 */
.swap-grid {
    display: grid;
    grid-template-columns: 250px minmax(0, 1fr) 300px;
    gap: 16px;
    align-items: start;
}

.swap-col {
    min-width: 0;
}

.swap-label {
    font-size: 13px;
    font-weight: 800;
    color: var(--text-title);
    letter-spacing: .5px;
    margin: 2px 0 8px;
}

.swap-label:not(:first-child) {
    margin-top: 16px;
}

.swap-row {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 8px 0;
}

.btn.swap-mini {
    padding: 3px 10px;
    font-size: 12px;
    line-height: 1.5;
}

.swap-hex {
    font-family: Consolas, monospace;
    font-size: 12px;
    padding: 2px 9px;
    border: 1.5px solid var(--stroke-dark);
    background: #fff;
    border-radius: 999px;
    color: var(--text-body);
    white-space: nowrap;
}

.swap-color {
    width: 46px;
    height: 32px;
    padding: 0;
    border: 2.5px solid var(--ink);
    border-radius: 10px;
    background: #fff;
    cursor: pointer;
}

.swap-k {
    flex: 0 0 52px;
    font-size: 12px;
    color: var(--text-sub);
}

.swap-gain input[type="range"] {
    flex: 1;
    min-width: 60px;
    accent-color: var(--accent-pink);
}

.swap-gain b {
    flex: 0 0 42px;
    text-align: right;
    font-size: 12px;
    color: var(--text-body);
}

.swap-export {
    width: 100%;
    margin-top: 6px;
}

.swap-hint {
    font-size: 11.5px;
    line-height: 1.7;
    color: var(--text-sub);
    margin: 8px 0 0;
}

.swap-hint b {
    color: var(--cta-stroke);
}

.swap-stage-wrap {
    border: 2px solid var(--stroke-dark);
    border-radius: var(--r-md);
    background: repeating-conic-gradient(#f6e7ef 0 25%, #fff9fc 0 50%) 0 0 / 20px 20px;
    padding: 12px;
    display: flex;
    justify-content: center;
}

#swapStage {
    width: min(340px, 100%);
    height: auto;
    cursor: crosshair;
    border-radius: 10px;
}

.swap-ref-empty {
    border: 2px dashed var(--stroke-dark);
    border-radius: var(--r-md);
    background: var(--bg-light);
    padding: 16px 12px;
    display: grid;
    justify-items: center;
    gap: 8px;
    text-align: center;
    transition: background .15s var(--ease), border-color .15s var(--ease);
}

.swap-ref-empty.over {
    background: var(--bg-card-2);
    border-color: var(--accent-pink);
}

.swap-ref-box.over {
    outline: 2px dashed var(--accent-pink);
    outline-offset: 3px;
    border-radius: var(--r-md);
}

.swap-ref-scroll {
    overflow: auto;
    max-height: 330px;
    border: 2px solid var(--stroke-dark);
    border-radius: var(--r-md);
    background: repeating-conic-gradient(#f6e7ef 0 25%, #fff9fc 0 50%) 0 0 / 16px 16px;
}

#swapRefCanvas {
    display: block;
    cursor: crosshair;
}

.swap-zoom input[type="range"] {
    flex: 1;
    min-width: 60px;
    accent-color: var(--accent-pink);
}

.swap-zoom b {
    flex: 0 0 42px;
    text-align: right;
    font-size: 12px;
}

#swapEyedot {
    position: fixed;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    border: 3px solid #fff;
    outline: 2px solid var(--ink);
    pointer-events: none;
    z-index: 99;
    display: none;
    box-shadow: 0 2px 8px rgba(38, 32, 64, .35);
}

@media (max-width: 1080px) {
    .swap-grid {
        grid-template-columns: 1fr 1fr;
    }
    .swap-col-preview {
        grid-column: 1 / -1;
        order: -1;
    }
}

@media (max-width: 640px) {
    .swap-grid {
        grid-template-columns: 1fr;
    }
}

.btn.swap-reset {
    justify-self: start;
    margin-top: 2px;
}

/* ---- 内发光开关 ---- */
.swap-glowrow {
    margin-top: 10px;
}
.swap-switch {
    position: relative;
    display: inline-block;
    width: 44px;
    height: 24px;
    cursor: pointer;
}
.swap-switch input {
    opacity: 0;
    width: 0;
    height: 0;
}
.swap-switch i {
    position: absolute;
    inset: 0;
    border-radius: 999px;
    background: var(--bg-card-2, #ffd9e9);
    border: 1.5px solid var(--stroke-dark, #f0a2c0);
    transition: background .15s;
}
.swap-switch i::after {
    content: "";
    position: absolute;
    top: 2px;
    left: 2px;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    background: #fff;
    box-shadow: 0 1px 3px rgba(34, 31, 51, .25);
    transition: transform .15s;
}
.swap-switch input:checked + i {
    background: var(--accent-pink, #ee77a9);
}
.swap-switch input:checked + i::after {
    transform: translateX(20px);
}


/* ============================ GIF 编辑（gifedit） ============================ */
/* 与 swap 同一套贴纸语言：面板骨架复用 .panel，工作区配色全走主题变量。
   布局：拖放区（上）→ 预览 + 素材列表两列（中）→ 导出参数/产物（下）。 */
.ge-drop {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    margin-bottom: 16px;
    padding: 26px 16px;
    background: var(--bg-deep);
    border: 2.5px dashed var(--stroke-dark);
    border-radius: var(--r-md);
    cursor: pointer;
    text-align: center;
    transition: background .15s var(--ease), border-color .15s var(--ease), transform .2s var(--ease);
}

.ge-drop:hover,
.ge-drop:focus-visible {
    background: var(--bg-light);
    border-color: var(--accent-pink);
    outline: none;
    transform: translateY(-2px);
}

.ge-drop.over {
    background: var(--bg-card-2);
    border-color: var(--cta-stroke);
}

.ge-drop-icon {
    width: 40px;
    height: 40px;
    border-radius: 14px;
    background: var(--accent-pink);
    border: 2.5px solid var(--ink);
    box-shadow: 0 3px 0 rgba(34, 31, 51, .22);
    position: relative;
}

.ge-drop-icon::before {
    content: '';
    position: absolute;
    left: 50%;
    top: 50%;
    width: 16px;
    height: 16px;
    transform: translate(-50%, -50%);
    background: #fff;
    clip-path: polygon(0 38%, 38% 38%, 38% 0, 62% 0, 62% 38%, 100% 38%, 100% 62%, 62% 62%, 62% 100%, 38% 100%, 38% 62%, 0 62%);
}

.ge-drop-text {
    font-size: 13.5px;
    color: var(--text-body);
}

.ge-drop-text b {
    color: var(--cta-stroke);
}

.ge-drop-hint {
    font-size: 11.5px;
    color: var(--text-dim);
    letter-spacing: .02em;
}

/* 预览 + 素材列表两列 */
.ge-main {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 280px;
    gap: 16px;
    align-items: start;
    margin-bottom: 16px;
}

.ge-view {
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.ge-stage-wrap {
    position: relative;
    border: 2.5px solid var(--ink);
    border-radius: var(--r-md);
    background:
        repeating-conic-gradient(#3a3157 0 25%, #2c2547 0 50%) 0 0 / 22px 22px,
        var(--bg-ink);
    padding: 10px;
    box-shadow: 0 4px 0 rgba(34, 31, 51, .25);
}

#geStage {
    display: block;
    width: auto;
    height: auto;
    max-width: 100%;
    max-height: 52vh;
    margin: 0 auto;
    border-radius: 8px;
    background: #262040;
}

/* 取景框四角：主预览区同款金色装饰 */
.ge-vf-corner {
    position: absolute;
    width: 16px;
    height: 16px;
    border: 3px solid var(--accent-gold);
    pointer-events: none;
}

.ge-vf-corner.c1 { left: 3px; top: 3px; border-right: none; border-bottom: none; border-top-left-radius: 8px; }
.ge-vf-corner.c2 { right: 3px; top: 3px; border-left: none; border-bottom: none; border-top-right-radius: 8px; }
.ge-vf-corner.c3 { left: 3px; bottom: 3px; border-right: none; border-top: none; border-bottom-left-radius: 8px; }
.ge-vf-corner.c4 { right: 3px; bottom: 3px; border-left: none; border-top: none; border-bottom-right-radius: 8px; }

.ge-thumb-badge {
    position: absolute;
    left: 50%;
    bottom: 18px;
    transform: translateX(-50%);
    padding: 4px 14px;
    background: var(--accent-gold);
    border: 2px solid var(--ink);
    border-radius: 999px;
    font-size: 11.5px;
    color: var(--ink);
    pointer-events: none;
    box-shadow: 0 2px 0 rgba(34, 31, 51, .3);
}

.ge-stage-bar {
    display: flex;
    align-items: center;
    gap: 10px;
    font-size: 12.5px;
    color: var(--text-sub);
}

.ge-progress {
    position: relative;
    flex: 1;
    height: 12px;
    background: var(--bg-ink);
    border: 2px solid var(--ink);
    border-radius: 999px;
    cursor: pointer;
    overflow: hidden;
}

.ge-progress-loaded {
    position: absolute;
    inset: 0;
    background: rgba(255, 255, 255, .08);
}

.ge-progress-played {
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: 0;
    background: var(--accent-pink);
    transition: width .08s linear;
}

.ge-progress-thumb {
    position: absolute;
    top: 50%;
    left: 0;
    width: 14px;
    height: 14px;
    transform: translate(-50%, -50%);
    background: #fff;
    border: 3px solid var(--accent-pink);
    border-radius: 50%;
    box-shadow: 0 1px 4px rgba(34, 31, 51, .4);
    pointer-events: none;
}

/* 截取区间：两端灰色压暗层（中段亮 = 截取保留区） */
.ge-progress-zone {
    position: absolute;
    top: 0;
    bottom: 0;
    background: rgba(38, 32, 64, .55);
    pointer-events: none;
}

.ge-progress-zone.zs { left: 0; }
.ge-cur, .ge-dur { min-width: 52px; }

.ge-controls {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

.ge-lens {
    font-size: 12px;
    color: var(--gold-text);
    background: #fdf2cf;
    border: 2px solid var(--gold-text);
    border-radius: 999px;
    padding: 3px 12px;
    white-space: nowrap;
}

.ge-controls .spacer {
    flex: 1 1 0;
    height: 0;
}

/* 截取操作组（起点 / 终点 / 全片）：整组独占一行，不会被换行拆散。
   flex-basis 100% 就是「整组固定落在第二行」的开关 —— 想让它在宽屏时回到第一行右侧
   （能放下就不换行），把这一条改成 flex: 0 0 auto 即可。 */
.ge-marks {
    flex: 1 1 100%;
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

/* 素材列表（右列） */
.ge-side {
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.ge-side-head {
    font-size: 13px;
    font-weight: 800;
    color: var(--text-title);
    letter-spacing: .5px;
}

.ge-list {
    display: flex;
    flex-direction: column;
    gap: 6px;
    max-height: 320px;
    overflow: auto;
    padding-right: 2px;
}

.ge-item {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 7px 10px;
    background: #fff;
    border: 2px solid var(--stroke-dark);
    border-radius: var(--r-sm);
    cursor: pointer;
    transition: border-color .15s var(--ease), background .15s var(--ease), transform .15s var(--ease);
}

.ge-item:hover {
    border-color: var(--accent-pink);
    transform: translateX(-2px);
}

.ge-item.is-cur {
    border-color: var(--cta-stroke);
    background: var(--bg-card-2);
}

.ge-item.has-trim .ge-dot {
    background: var(--accent-gold);
}

.ge-item.is-error {
    opacity: .65;
    border-style: dashed;
}

.ge-dot {
    flex: 0 0 10px;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: var(--mint);
    border: 2px solid var(--ink);
}

.ge-item.is-loading .ge-dot,
.ge-item.is-error .ge-dot {
    background: var(--warn);
}

.ge-item-main {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 1px;
}

.ge-item-name {
    font-size: 12.5px;
    color: var(--text-title);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ge-item-meta {
    font-size: 11px;
    color: var(--text-dim);
}

.ge-item-del {
    flex: 0 0 auto;
    width: 22px;
    height: 22px;
    border: 2px solid var(--stroke-dark);
    border-radius: 50%;
    background: #fff;
    color: var(--text-dim);
    font-size: 13px;
    line-height: 1;
    cursor: pointer;
    transition: background .15s var(--ease), color .15s var(--ease), border-color .15s var(--ease);
}

.ge-item-del:hover {
    background: var(--warn);
    border-color: var(--warn);
    color: #fff;
}

.ge-hint {
    font-size: 11.5px;
    line-height: 1.7;
    color: var(--text-dim);
}

/* 导出参数：四行「标签 + 控件」，一行 = 一个字段组 */
.ge-opts {
    padding-top: 4px;
    border-top: 2px dashed var(--stroke-dark);
}

/* 外层只管纵向排布：一行一个 .ge-opt-row。
   ⚠️ 别退回「扁平网格 + 各组子项直接当格子」的写法 —— 各组占的格数不等（4/4/5/3 格），
   多出来的格子会把下一组的标签吸到上一行末尾（「帧率」被顶到第一行、「压缩预设」被顶到第二行）。 */
.ge-opt-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 12px;
}

/* 一个字段组 = 一行：标签列固定 76px，其后 4 个控件列等宽 —— 组与组之间不可能再串行 */
.ge-opt-row {
    display: grid;
    grid-template-columns: 76px repeat(4, minmax(0, 1fr));
    gap: 12px 14px;
    align-items: center;
}

.ge-opt-label {
    font-size: 12.5px;
    font-weight: 700;
    color: var(--text-body);
    letter-spacing: .04em;
}

.ge-opt-grid .auto-field {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 12.5px;
    color: var(--text-sub);
    cursor: pointer;
    white-space: nowrap;
}

.ge-opt-grid .auto-field input[type='number'] {
    width: 74px;
    padding: 6px 9px;
    background: #fff;
    border: 2px solid var(--ink);
    border-radius: var(--r-sm);
    color: var(--text-title);
    font-size: 13px;
    font-variant-numeric: tabular-nums;
}

.ge-opt-grid .auto-field input[type='number']:disabled {
    background: var(--bg-deep);
    color: var(--text-dim);
}

.ge-opt-grid input[type='radio'],
.ge-opt-grid input[type='checkbox'] {
    accent-color: var(--accent-pink);
    width: 15px;
    height: 15px;
}

.ge-opt-note {
    font-size: 11.5px;
    color: var(--text-dim);
}

/* 组内那几个永远为空的占位 note 直接不占格，否则会白吃一列（右侧留白忽宽忽窄） */
.ge-opt-note:empty {
    display: none;
}

/* 原生 checkbox 只当状态载体，贴纸风的开关本体是 .sw 自绘的 ——
   不把它藏起来，就会和自绘开关并排出现两个「开关」（站长 2026-09-16 报的冗余）。 */
.ge-fade-switch > input[type='checkbox'] {
    position: absolute;
    width: 0;
    height: 0;
    opacity: 0;
    pointer-events: none;
}

/* 原生的藏了，键盘聚焦时就把焦点环补给自绘开关，别让键盘用户看不到焦点 */
.ge-fade-switch > input:focus-visible + .sw {
    outline: 3px solid var(--primary);
    outline-offset: 2px;
}

.ge-fade-switch .sw {
    position: relative;
    width: 36px;
    height: 20px;
    border-radius: 999px;
    background: var(--bg-card-2);
    border: 1.5px solid var(--stroke-dark);
    transition: background .15s;
}

.ge-fade-switch .sw::after {
    content: '';
    position: absolute;
    top: 2px;
    left: 2px;
    width: 14px;
    height: 14px;
    border-radius: 50%;
    background: #fff;
    box-shadow: 0 1px 3px rgba(34, 31, 51, .25);
    transition: transform .15s;
}

.ge-fade-switch input:checked + .sw {
    background: var(--accent-pink);
}

.ge-fade-switch input:checked + .sw::after {
    transform: translateX(15px);
}

.ge-preset-note {
    margin: 10px 0 0;
    padding: 8px 14px;
    background: #fdf2cf;
    border: 2px solid var(--gold-text);
    border-radius: var(--r-sm);
    font-size: 12px;
    line-height: 1.7;
    color: var(--gold-text);
}

.ge-export-bar {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    margin-top: 14px;
}

/* 产物列表复用 .batch-results / .batch-row（主脚本智能分段同款），无需新样式 */

.ge-progress-zone.ze {
    background: rgba(38, 32, 64, .45);
}

/* 历史遗留收尾（2026-09-16 静态检查顺手补）：全局模式条面板的入场峰值与头部间距 */
.panel-modes {
    --i: 0;
}

.panel-modes .panel-head {
    margin-bottom: 0;
}

/* 按键桥状态点的基础色（setBridgeState 会按连接态改 inline background，这里是未连接默认色） */
.rich-dot-blue {
    background: var(--text-dim);
}

@media (max-width: 1080px) {
    .ge-main {
        grid-template-columns: 1fr;
    }
    .ge-opt-row {
        grid-template-columns: 76px minmax(0, 1fr) minmax(0, 1fr);
    }
}
