/*
 * 锐影爆款裂变 — 样式
 *
 * 一套皮肤 = 一个变量块。五套：dreammini（默认）/ light / sakura / plain / ink。
 * 语义编码跨皮肤不变：暖色=参考素材、冷紫蓝=AI 生成、绿=真实可信、红=出错。
 * 深浅之间只调明度与饱和度，色相不换——换了运营对着两套界面得记两套含义。
 *
 * 🔴 所有色值经 e2e-tests/test_skin_tokens.py 实算，五套 × 十几组配对全部达标
 *    （文本 4.5:1、控件边框 3:1），并断言同一皮肤内 ok/accent/warn/err 两两不同色。
 *    **改任何一个 hex 都要跑那套测试**：python3 -m pytest e2e-tests/test_skin_tokens.py -q
 *
 * ⚠️ 不要用 opacity 压淡文字。opacity 是合成属性，不折进 computed color，
 *    浏览器里量对比度的脚本和纯解析 CSS 的脚本都看不见它——
 *    这里踩过一次，两处文字被压到 3.3–4.4 而两套测试全绿。层级用字号做。
 *
 * 变量集是旧版的超集：旧名（--gen / --faint / --radius / --real-soft …）全部保留，
 * 让既有的一百多个选择器继续成立；--faint 刻意赋成与 --muted 同值——
 * 它原来那档"比 muted 更淡"在浅底上做不到 4.5:1，层级改用字号与字重做。
 *
 * @author sunhongliang
 */

/* ========== 默认皮肤：Dream Mini 黑 ==========
   放在裸 :root 而不是 [data-theme=dreammini]：JS 没跑起来或挂了，
   页面仍是完整可读的默认皮肤，不会出现变量未定义的裸样式。 */
.shot-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 12px; margin: 16px 0; }
.shot-card { border: 1px solid var(--rule-strong); border-radius: 12px; padding: 14px; background: var(--panel-2); }
.shot-card h4 { margin: 0 0 10px; }
.shot-card h4 small { color: var(--muted); font-weight: normal; display: block; }
.shot-card p { white-space: pre-wrap; overflow-wrap: anywhere; max-height: 140px; overflow: auto; }
.shot-preview { display: block; max-width: 100%; height: 350px; margin: 12px auto; }
.shot-edit-dialog { width: min(740px, calc(100vw - 40px)); max-height: 85vh; overflow-y: auto; padding: 24px; border: 1px solid var(--rule-strong); border-radius: 16px; background: var(--panel); color: var(--ink); }
.shot-edit-dialog::backdrop { background: rgba(0, 0, 0, .65); }
.shot-edit-dialog fieldset { margin: 16px 0; border: 1px solid var(--rule-strong); border-radius: 10px; padding: 14px; }
.shot-edit-dialog label { display: block; margin: 8px 0; }
.shot-edit-dialog textarea, .shot-edit-dialog input { width: 100%; box-sizing: border-box; margin-top: 6px; }
.shot-workbench { width: min(1200px, calc(100vw - 32px)); max-height: 90vh; overflow: auto; padding: 24px; border: 1px solid var(--rule-strong); border-radius: 16px; background: var(--panel); color: var(--ink); }
.shot-workbench::backdrop { background: rgba(0, 0, 0, .7); }
.shot-workbench header { display: flex; justify-content: space-between; align-items: start; gap: 16px; }
.shot-workbench h2 { margin-top: 0; }
.shot-workspace { display: grid; grid-template-columns: 180px minmax(0, 1fr); gap: 24px; }
.shot-workspace nav { display: flex; flex-direction: column; gap: 10px; }
.shot-workspace nav button { text-align: left; white-space: normal; }
.shot-workspace nav small { display: block; margin-top: 6px; }
.shot-workspace [aria-current="true"] { outline: 2px solid var(--accent); }
.workbench-compare, .workbench-notes { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 20px; }
.workbench-compare video { display: block; width: 100%; height: 300px; object-fit: contain; background: #000; margin: 10px 0; }
.workbench-compare video[hidden] { display: none; }
.workbench-versions { display: flex; flex-wrap: wrap; gap: 8px; max-height: 130px; overflow-y: auto; padding: 3px; }
.workbench-versions button { text-align: left; }
.workbench-versions small { display: block; }
.workbench-versions [aria-pressed="true"] { outline: 2px solid var(--accent); }
.workbench-notes p { white-space: pre-wrap; overflow-wrap: anywhere; }
.shot-workbench .hint { font-size: 14px; }
@media (max-width: 720px) {
  .shot-workbench { padding: 16px; }
  .shot-workspace, .workbench-compare, .workbench-notes { grid-template-columns: 1fr; }
  .shot-workspace nav { flex-direction: row; overflow-x: auto; padding: 4px; }
  .shot-workspace nav button { flex: 0 0 130px; }
  .workbench-compare video { height: 260px; }
}
:root {
    color-scheme: dark;

    --paper: #0a0a0a;
    --panel: #141418;
    --panel-2: #19191f;
    --panel-3: #24242c;
    --ink: #f1f2f6;
    --muted: #a3a7b2;
    --faint: #a3a7b2;
    --rule: #292933;
    --rule-soft: #202027;
    --rule-strong: #666b78;

    --accent: #65d7ec;
    --accent-ink: #04232c;
    --accent-soft: rgba(101, 215, 236, .13);

    --brand-a: #75d6e8;
    --brand-b: #b99cff;
    /* 🔴 brand-* 与 fill-* 不是一回事，别合并：
       brand-* 是**当字用**的（侧栏产品名的渐变字），底色是 --panel；
       fill-* 是**当底用**的（主按钮的渐变填充），上面压的是白字。
       深色皮肤下这两件事要的亮度正好相反——brand 必须够亮、fill 必须够暗，
       合成一组的话，按钮上的白字和侧栏上的产品名必有一头看不清 */
    --fill-a: #6949cc;
    --fill-b: #205d91;

    --ref: #ff9147;
    --ref-soft: #3a2517;
    --gen: #a78bfa;            /* 旧名 = 方案 / AI 产出，与 --ai 同值 */
    --gen-soft: rgba(167, 139, 250, .16);

    /* --ok 必须与 --accent 不同色：同色时「通过」与「进行中」两个 pill 完全无法区分 */
    --ok: #4ade80;
    --ok-soft: rgba(74, 222, 128, .13);

    --real: #2ee6c5;
    --real-soft: rgba(46, 230, 197, .20);
    --ai: #a78bfa;
    --ai-soft: rgba(167, 139, 250, .16);
    --ai-stripe: rgba(167, 139, 250, .30);
    --warn: #f0b429;
    --warn-soft: rgba(240, 180, 41, .13);
    --err: #ff6b60;
    --err-soft: rgba(255, 107, 96, .13);

    --mono: ui-monospace, SFMono-Regular, "SF Mono", "JetBrains Mono",
        "Cascadia Mono", Consolas, "Liberation Mono", monospace;
    --sans: "PingFang SC", "HarmonyOS Sans SC", "Source Han Sans SC",
        "Noto Sans CJK SC", "Microsoft YaHei", system-ui, -apple-system, sans-serif;
    --serif: "Songti SC", "STSong", "Source Han Serif SC",
        "Noto Serif CJK SC", SimSun, serif;
    --ui: var(--sans);

    --r-card: 14px;
    --r-ctl: 9px;
    --r-pill: 999px;
    --radius: var(--r-ctl);    /* 旧名 */

    --on-ink: var(--accent-ink);
    --ink-hover: #85e3f2;
    --field: #101014;
    --warn-line: rgba(240, 180, 41, .4);
    --err-line: rgba(255, 107, 96, .4);
    --real-ink: #8bf2de;
    --ai-ink: #cbb8fd;
    --w-head: 600;
    --track-ls: 0;
}

/* ========== 极简白：信息密度最高，给内部运营长时间盯 ========== */
/* ========== Lumio 浅：三期 3.7 界面改版的默认皮肤 ==========
   参考站是浅底白卡 + 粉橙渐变主按钮。品牌粉 #ff5f6d 只够做"面"（按钮底、选中态），
   做"字"时对比度不足 4.5:1，所以 --accent（链接、焦点环、选中文字）用压深的 #c8323f，
   渐变那两个色单独放在 --brand-a / --brand-b 里，只用在填充上。 */
:root[data-theme="lumio"] {
    color-scheme: light;
    --paper: #f7f7f8;  --panel: #ffffff;  --panel-2: #fbfbfc;  --panel-3: #f3f3f5;
    --ink: #1f2126;    --muted: #5f646d;  --faint: #5f646d;
    --rule: #ececef;   --rule-soft: #f2f2f4;  --rule-strong: #8b8f98;
    --accent: #c8323f; --accent-ink: #ffffff; --accent-soft: #fff0ec;
    /* 参考站那套粉橙（#ff5f6d/#ff8e53）压在白底上只有 2.3~3.0，白字和产品名都糊，按同色相压深到 4.6 */
    --brand-a: #ec0015; --brand-b: #cf4700;
    --fill-a: #ec0015;  --fill-b: #cf4700;
    --ref: #b45309;    --ref-soft: #fdf3e6;
    --gen: #4b3fb0;    --gen-soft: #eceaff;
    --ok: #1f7a43;     --ok-soft: #e8f7ee;
    --real: #1f7a43;   --real-soft: #fdeee0;
    --ai: #4b3fb0;     --ai-soft: #eceaff;  --ai-stripe: rgba(75, 63, 176, .18);
    --warn: #8a6100;   --warn-soft: #fff6e0;
    --err: #c0392b;    --err-soft: #fdecec;
    --on-ink: #ffffff; --ink-hover: #a32733; --field: #ffffff;
    --warn-line: #f0dfae; --err-line: #f3c6c0;
    --real-ink: #8a4b12;  --ai-ink: #4b3fb0;
    --r-card: 16px; --r-ctl: 10px; --r-pill: 999px;
}

:root[data-theme="light"] {
    color-scheme: light;
    --paper: #f6f7f9;  --panel: #ffffff;  --panel-2: #f6f7f9;  --panel-3: #eef0f4;
    --ink: #14171c;    --muted: #5f6773;  --faint: #5f6773;
    --rule: #dfe3e9;   --rule-soft: #eef0f4;  --rule-strong: #8a94a2;
    --accent: #2e5be8; --accent-ink: #ffffff; --accent-soft: #e9eeff;
    --brand-a: #2e5be8; --brand-b: #067a52;
    --fill-a: #2e5be8;  --fill-b: #067a52;
    --ref: #e8590c;    --ref-soft: #fdf0e7;
    --gen: #2e5be8;    --gen-soft: #eaefff;
    --ok: #067a52;     --ok-soft: #e6f7f0;
    --real: #067a52;   --real-soft: #d9f2e8;
    --ai: #2e5be8;     --ai-soft: #dfe7ff;  --ai-stripe: rgba(46, 91, 232, .22);
    --warn: #b45309;   --warn-soft: #fdf6e7;
    --err: #b42318;    --err-soft: #fdf0ef;
    --on-ink: #ffffff; --ink-hover: #2449c4; --field: #ffffff;
    --warn-line: #f1d9a8; --err-line: #f3c2bd;
    --real-ink: #06614a;  --ai-ink: #1c3fa8;
    --r-card: 8px; --r-ctl: 6px; --r-pill: 999px;
}

/* ========== 樱花落：粉只用在小面积强调，底是极淡暖白 ========== */
:root[data-theme="sakura"] {
    color-scheme: light;
    --paper: #fdf6f7;  --panel: #ffffff;  --panel-2: #fbeef1;  --panel-3: #f7e4e9;
    --ink: #3d2d33;    --muted: #7a5f68;  --faint: #7a5f68;
    --rule: #f0dde2;   --rule-soft: #f7eaee;  --rule-strong: #9f8790;
    --accent: #a8325c; --accent-ink: #ffffff; --accent-soft: #fce9ee;
    /* 紫端原来是 #9b7fc4，白底上 3.37 不够，压到 4.6 */
    --brand-a: #a8325c; --brand-b: #8665b8;
    --fill-a: #a8325c;  --fill-b: #8665b8;
    --ref: #b5713f;    --ref-soft: #f8ece2;
    --gen: #9b7fc4;    --gen-soft: #f0e9f8;
    --ok: #3f6b4e;     --ok-soft: #e6f2ea;
    --real: #3f6b4e;   --real-soft: #dfeee3;
    --ai: #9b7fc4;     --ai-soft: #ece3f6;  --ai-stripe: rgba(155, 127, 196, .26);
    --warn: #8a5a1e;   --warn-soft: #fbf1e4;
    --err: #a8323f;    --err-soft: #fbebee;
    --on-ink: #ffffff; --ink-hover: #8d2a4d; --field: #ffffff;
    --warn-line: #eddcc2; --err-line: #f0ccd3;
    --real-ink: #2e4f3a;  --ai-ink: #5e4680;
    --r-card: 16px; --r-ctl: 10px; --r-pill: 999px;
}

/* ========== 简约风：无彩色，强调即纯墨，全靠 1px 线分区 ========== */
:root[data-theme="plain"] {
    color-scheme: light;
    --paper: #f4f3f1;  --panel: #fbfaf9;  --panel-2: #efeeeb;  --panel-3: #e7e6e2;
    --ink: #1c1c1a;    --muted: #63625e;  --faint: #63625e;
    --rule: #e0dfdb;   --rule-soft: #ebeae7;  --rule-strong: #8e8d88;
    --accent: #1c1c1a; --accent-ink: #fbfaf9; --accent-soft: #e6e5e1;
    --brand-a: #1c1c1a; --brand-b: #63625e;
    --fill-a: #1c1c1a;  --fill-b: #63625e;
    --ref: #a8764f;    --ref-soft: #f0eae4;
    --gen: #5a6b8c;    --gen-soft: #e8ebf0;
    --ok: #4e6a52;     --ok-soft: #e6ebe7;
    --real: #4e6a52;   --real-soft: #e2e8e3;
    --ai: #5a6b8c;     --ai-soft: #e0e4ea;  --ai-stripe: rgba(90, 107, 140, .20);
    --warn: #7a5f34;   --warn-soft: #f0ece3;
    --err: #96453c;    --err-soft: #f2e8e6;
    --on-ink: #fbfaf9; --ink-hover: #35352f; --field: #ffffff;
    --warn-line: #ddd5c5; --err-line: #e0cdc9;
    --real-ink: #33422f;  --ai-ink: #384556;
    --r-card: 3px; --r-ctl: 2px; --r-pill: 3px;   /* 连 pill 都是方角，这是它的态度 */
    --track-ls: .02em;
}

/* ========== 水墨：宣纸底、宋体、朱砂做印 ========== */
:root[data-theme="ink"] {
    color-scheme: light;
    --paper: #f0ece2;  --panel: #f8f5ee;  --panel-2: #e9e4d8;  --panel-3: #e1dbcc;
    --ink: #1f1e1a;    --muted: #56534b;  --faint: #56534b;
    --rule: #d6d0c0;   --rule-soft: #e4dfd2;  --rule-strong: #938d7c;
    --accent: #a8332a; --accent-ink: #f8f5ee; --accent-soft: #f0dfd9;
    --brand-a: #a8332a; --brand-b: #3d5a6b;
    --fill-a: #a8332a;  --fill-b: #3d5a6b;
    --ref: #94612e;    --ref-soft: #eee3d0;
    --gen: #3d5a6b;    --gen-soft: #dfe6ea;
    --ok: #55703f;     --ok-soft: #e6ead9;
    --real: #55703f;   --real-soft: #dfe4d3;
    --ai: #3d5a6b;     --ai-soft: #d9e2e6;  --ai-stripe: rgba(61, 90, 107, .22);
    --warn: #7d5122;   --warn-soft: #efe6d5;
    /* --err 不能用朱砂：朱砂已经是 --accent，同色时「进行中」与「失败」分不出 */
    --err: #7a2e2e;    --err-soft: #eddedb;
    --on-ink: #f8f5ee; --ink-hover: #8c2a22; --field: #fdfbf6;
    --warn-line: #dccfb4; --err-line: #dfc3bb;
    --real-ink: #35441f;  --ai-ink: #233947;
    --ui: var(--serif);
    --r-card: 2px; --r-ctl: 2px; --r-pill: 2px;
    --track-ls: .01em;
}

/* ========== 基础 ========== */
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
    background: var(--paper);
    color: var(--ink);
    font-family: var(--ui);
    font-size: 14px;
    line-height: 1.65;
    letter-spacing: var(--track-ls);
    -webkit-font-smoothing: antialiased;
}
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
[hidden] { display: none !important; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 2px; }
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after { transition: none !important; animation: none !important; }
}

.mono { font-family: var(--mono); font-size: 12.5px; font-variant-numeric: tabular-nums; letter-spacing: 0; }
.muted { color: var(--muted); }
.small { font-size: 12px; }
.ok { color: var(--ok); }
.err { color: var(--err); }
.warn { color: var(--warn); background: var(--warn-soft); padding: 8px 10px;
        border-radius: var(--r-ctl); border-left: 3px solid var(--warn); line-height: 1.6; }
.wrap { white-space: normal; max-width: 420px; }

/* ========== 外壳：左导航 + 顶栏 ========== */
.shell { display: flex; min-height: 100vh; }
.side {
    width: 216px; flex: 0 0 216px;
    background: var(--panel); border-right: 1px solid var(--rule);
    padding: 18px 12px 14px; position: sticky; top: 0; height: 100vh;
    box-sizing: border-box; display: flex; flex-direction: column;
}
.shell > .body { flex: 1; min-width: 0; }   /* min-width:0 不能省，否则宽表格会把左导航挤没 */

.brand { display: flex; align-items: center; gap: 10px; padding: 0 6px 4px; text-decoration: none; }
.brand:hover { text-decoration: none; }
.brand-mark {
    width: 36px; height: 36px; flex: 0 0 36px; display: grid; place-items: center;
    border-radius: 11px; color: #fff; font-size: 20px; font-weight: 600;
    background: linear-gradient(135deg, var(--fill-a), var(--fill-b));
}
.brand .n {
    display: block; font-size: 16px; font-weight: var(--w-head); letter-spacing: .01em;
    color: var(--ink);
}
/* 渐变字只在支持时启用，且必须有兜底色：background-clip 不支持时整个产品名会消失 */
@supports (-webkit-background-clip: text) or (background-clip: text) {
    .brand .n {
        background: linear-gradient(96deg, var(--brand-a), var(--brand-b));
        -webkit-background-clip: text; background-clip: text; color: transparent;
    }
}
.brand .ver { display: block; font-size: 11.5px; color: var(--muted); margin-top: 1px; }
.nav-icon { width: 20px; text-align: center; font-size: 18px; color: var(--accent); }
.side-note { color: var(--muted); font-size: 12px; line-height: 1.8; padding: 0 8px; margin: 22px 0; }

.tabs { display: flex; flex-direction: column; gap: 1px; margin-top: 18px; }
.tabs a {
    display: flex; align-items: center; gap: 9px;
    padding: 8px 10px; color: var(--muted); border-radius: var(--r-ctl);
    font-size: 13.5px; position: relative;
    transition: background .13s, color .13s;
}
.tabs a:hover { text-decoration: none; color: var(--ink); background: var(--panel-2); }
.tabs a.on { color: var(--ink); background: var(--accent-soft); font-weight: var(--w-head); }
.tabs a.on::before {
    content: ""; position: absolute; left: -12px; top: 7px; bottom: 7px; width: 2px;
    background: var(--accent); border-radius: 0 2px 2px 0;
}
.nav-gap { flex: 1; }

/* 当前登录人。字段只用 /api/admin/me 已有的：登录名 + 租户名 + 是否平台管理员。
   显示名与角色名都不在这个接口里（分别要 user:manage 才拿得到），不做。 */
.me { display: flex; align-items: center; gap: 9px; padding: 7px 8px; border-radius: var(--r-ctl); }
.me .av {
    width: 26px; height: 26px; flex: 0 0 26px; border-radius: var(--r-pill);
    background: var(--accent); color: var(--accent-ink);
    font-size: 12px; display: grid; place-items: center; font-weight: var(--w-head);
}
.me .who-n { font-size: 13px; color: var(--ink); }
.me .who-t { font-size: 11.5px; color: var(--muted); }

.top {
    display: flex; align-items: center; gap: 12px;
    height: 60px; padding: 0 26px;
    background: var(--panel); border-bottom: 1px solid var(--rule);
    position: sticky; top: 0; z-index: 5;
}
.who { margin-left: auto; display: flex; align-items: center; gap: 12px; }
.side-toggle { display: none; font-size: 18px; line-height: 1; }
.breadcrumb { display: flex; align-items: center; gap: 9px; font-size: 12px; color: var(--muted); white-space: nowrap; }
.breadcrumb b { color: var(--ink); font-weight: 500; }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }

/* 皮肤选择器。
   🔴 五个色点必须带 class="link"：test_ui.py 的对比度门禁用
   document.querySelector("button:not(.link)") 取"主按钮"，色点在 DOM 里排在业务按钮之前，
   不带 .link 就会被选中一个装饰色块，门禁的取样点被换掉。 */
.skins { display: flex; align-items: center; gap: 5px; }
.skins button.link {
    width: 24px; height: 24px; padding: 0; border-radius: var(--r-pill);
    border: 1px solid var(--rule-strong); cursor: pointer;
    transition: transform .12s;
}
.skins button.link:hover { transform: scale(1.1); }
.skins button.link[aria-checked="true"] {
    box-shadow: 0 0 0 2px var(--panel), 0 0 0 3.5px var(--accent);
}
/* 🔴 必须带 .skins 前缀：色点是 <button class="link">，而下面「按钮」一节的
   button.link { background: none } 特异性是 (0,1,1)，会盖过裸类名的 (0,1,0)，
   五个色点就成了五个一模一样的空圈——皮肤在界面上根本选不了。
   这里用 .skins .sk-xxx 拿到 (0,2,0) 压过它。
   色板刻意写死不跟随皮肤：它表示的是「那一套长什么样」，与当前皮肤无关 */
.skins .sk-lumio     { background: linear-gradient(135deg, #f7f7f8 50%, #ec0015 50%); }
.skins .sk-dreammini { background: linear-gradient(135deg, #141418 50%, #65d7ec 50%); }
.skins .sk-light     { background: linear-gradient(135deg, #ffffff 50%, #2e5be8 50%); }
.skins .sk-sakura    { background: linear-gradient(135deg, #fdf6f7 50%, #a8325c 50%); }
.skins .sk-plain     { background: linear-gradient(135deg, #fbfaf9 50%, #1c1c1a 50%); }
.skins .sk-ink       { background: linear-gradient(135deg, #f0ece2 50%, #a8332a 50%); }

.dot { width: 8px; height: 8px; border-radius: 50%; background: var(--muted); display: inline-block; }
.dot.ok { background: var(--ok); }
.dot.bad { background: var(--err); }

/* 窄屏：导航收起来，用顶栏按钮拉出。
   只做位移——收起时仍在 DOM 里，键盘 Tab 顺序和读屏由 app.js 的 syncSideA11y() 处理
   （设 tabindex="-1" + aria-hidden）。两者缺一不可。 */
@media (max-width: 860px) {
    .side {
        position: fixed; left: 0; top: 0; z-index: 20;
        transform: translateX(-100%); transition: transform .18s ease;
        box-shadow: 0 0 24px rgba(0, 0, 0, .12);
    }
    .side.open { transform: none; }
    .side-toggle { display: inline-block; }
}

/* ========== 页面骨架 ========== */
.page { max-width: 1240px; margin: 0 auto; padding: 22px; }
.page.narrow { max-width: 1080px; }

@media (min-width: 1000px) {
    .form.split {
        display: grid; grid-template-columns: 1fr 1fr; gap: 0 22px;
        align-items: start;
    }
    .form.split > fieldset:nth-of-type(1) { grid-column: 1; grid-row: 1; }
    .form.split > fieldset:nth-of-type(2) { grid-column: 2; grid-row: 1 / span 2; }
    .form.split > fieldset:nth-of-type(3) { grid-column: 1; grid-row: 2; }
    .form.split > .actions,
    .form.split > .err { grid-column: 1 / -1; }
}
.bar { display: flex; align-items: center; gap: 14px; margin-bottom: 16px; }
.bar h2 { margin: 0; font-size: 19px; font-weight: var(--w-head); }
.bar .btn, .bar .back { margin-left: auto; }
.bar .back { margin-left: 0; margin-right: 4px; color: var(--muted); }
h3 { font-size: 13.5px; font-weight: var(--w-head); margin: 0 0 11px; color: var(--ink); }
.empty { color: var(--muted); padding: 40px 0; text-align: center; }

/* ========== 按钮 / 表单 ========== */
button, .btn {
    font: inherit; font-family: var(--ui); font-size: 13px;
    padding: 6px 14px; border-radius: var(--r-ctl);
    border: 1px solid var(--accent); background: var(--accent); color: var(--accent-ink);
    cursor: pointer; display: inline-block; line-height: 1.5;
    transition: filter .13s, background .13s;
}
button:hover, .btn:hover { background: var(--ink-hover); border-color: var(--ink-hover); text-decoration: none; }
button:disabled { opacity: 0.5; cursor: default; }
button.secondary { background: var(--panel); color: var(--ink); border-color: var(--rule-strong); }
button.secondary:hover { background: var(--panel-2); border-color: var(--rule-strong); }
button.link { border: 0; background: none; color: var(--accent); padding: 0; }
button.link:hover { background: none; }
button.link.danger { color: var(--err); }
.file-btn { cursor: pointer; }

.form fieldset {
    border: 1px solid var(--rule); border-radius: var(--r-card);
    background: var(--panel); padding: 14px 18px 8px; margin: 0 0 14px;
}
.form legend { padding: 0 6px; font-weight: var(--w-head); font-size: 13px; }
.form label { display: block; margin-bottom: 10px; font-size: 13px; color: var(--muted); }
.form label b { color: var(--err); font-weight: 400; }
.form input:not([type=file]), .form textarea, .form select {
    display: block; width: 100%; margin-top: 4px;
    font: inherit; font-family: var(--ui); font-size: 14px; color: var(--ink);
    padding: 6px 9px; border: 1px solid var(--rule-strong);
    border-radius: var(--r-ctl); background: var(--field);
}
.form input[type=file] { display: block; margin-top: 4px; font-size: 13px; }
/* 焦点用 :focus-visible 且保留可见环：只换边框色的话，键盘用户在 14 个字段的表单里会丢焦点 */
.form input:focus-visible, .form textarea:focus-visible, .form select:focus-visible {
    outline: none; border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}
.form .hint { display: block; margin-top: 3px; font-size: 11.5px; color: var(--muted); line-height: 1.5; }
.form .row2 { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.form .actions { display: flex; align-items: center; gap: 12px; }
.form.compact label { margin-bottom: 8px; }
.form.compact button { margin-top: 4px; }

/* ========== 表格 ========== */
.list { width: 100%; border-collapse: collapse; background: var(--panel);
        border: 1px solid var(--rule); border-radius: var(--r-card); overflow: hidden; }
.list th, .list td { text-align: left; padding: 9px 12px; border-bottom: 1px solid var(--rule-soft); vertical-align: top; }
.list td:last-child, .list th:last-child { white-space: nowrap; }
.list th { font-weight: 400; color: var(--muted); font-size: 11.5px; background: var(--panel-2); white-space: nowrap; }
.list tr:last-child td { border-bottom: 0; }
.list tbody tr { transition: background .12s; }
.list tbody tr:hover { background: var(--panel-2); }

/* 分段表：短内容的几列不该折行。
   中文任意两字之间都能断行，窄列里「取原片 0.0s–5.0s」会被从「取」和「原片」
   中间劈开、「镜头1」会劈成两行、「0.0s–2.4s」会在破折号处断掉——
   读起来像另一个词，而这张表是运营判断「改哪一段」的依据。
   只钉短列：来源列整格仍可折行（素材 id 很长），提示词列本来就该折。 */
#dPlan td:nth-child(1),      /* # */
#dPlan td:nth-child(2),      /* 角色 */
#dPlan td:nth-child(4),      /* 成片位置 */
#dPlan td:nth-child(6) {     /* 预览 */
    white-space: nowrap;
}
/* 来源列里那句「取原片 x–y」自己不折，但它前面的素材 id 该折还是折 */
#dPlan td:nth-child(3) .muted.small { white-space: nowrap; }
/* 点轨道上某一段时，分段表格里对应那行跟着亮起来 */
.list tbody tr.on { background: var(--accent-soft); box-shadow: inset 2px 0 0 var(--accent); }
.list.small td { padding: 6px 10px; font-size: 12.5px; }
.tags { margin-top: 4px; }
.tags span { display: inline-block; font-size: 11px; color: var(--muted); border: 1px solid var(--rule);
             border-radius: var(--r-pill); padding: 0 6px; margin: 0 4px 3px 0; }
.clip { font-size: 12.5px; padding: 2px 0; }
.clip i { color: var(--muted); font-style: normal; }

/* ========== 状态 ========== */
.status {
    display: inline-block; font-size: 12px; padding: 1px 9px; border-radius: var(--r-pill);
    border: 1px solid var(--rule); color: var(--muted); background: var(--panel-2); white-space: nowrap;
}
.status.run { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
.status.wait { border-color: var(--warn-line); color: var(--warn); background: var(--warn-soft); }
.status.ok { border-color: var(--ok); color: var(--ok); background: var(--ok-soft); }
.status.err { border-color: var(--err-line); color: var(--err); background: var(--err-soft); }

/* ========== 详情 ========== */
.detail .bar h2 { margin-right: 4px; }
/* 详情页顶行的那串「任务号 · 出片方式 · 派生自 · 同批」可以很长，
   不让它换行的话会把标题挤成两行、「← 作品」也被压断（1440 宽实测）。
   让它自己占一行，标题和返回保持原样 */
.detail .bar { flex-wrap: wrap; }
/* h2 不能加 flex:none。那等于 flex:0 0 auto、按 max-content 撑着且不许收缩，
   而标题是商品名（「金豆芽儿童山楂汁」这种），375px 窄屏上一行放不下又断不了行，
   整页就被撑出横向滚动条。默认的可收缩让中文自然换行才对 */
.detail .bar .back, .detail .bar .status { flex: none; }
.detail .bar #dAuto { flex: 1 1 100%; margin-left: 0; }
.steps {
    list-style: none; margin: 0 0 18px; padding: 0;
    display: flex; gap: 2px; counter-reset: s;
}
.steps li {
    flex: 1; padding: 7px 10px; font-size: 12px; color: var(--muted);
    border-bottom: 2px solid var(--rule-soft); counter-increment: s; position: relative;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.steps li::before {
    content: counter(s); font-family: var(--mono); font-size: 10px; margin-right: 6px;
    display: inline-grid; place-items: center; width: 15px; height: 15px;
    border: 1px solid var(--rule-strong); border-radius: var(--r-pill); vertical-align: -3px;
}
.steps li.done::before { content: "✓"; color: var(--ok); border-color: var(--ok); background: var(--ok-soft); }
.steps li.cur { color: var(--ink); font-weight: var(--w-head); border-bottom-color: var(--accent); }
.steps li.cur::before { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.steps li.err { color: var(--err); font-weight: var(--w-head); border-bottom-color: var(--err); }
.steps li.err::before { background: var(--err-soft); border-color: var(--err); color: var(--err); }

.notice { padding: 11px 14px; border-radius: var(--r-card); margin-bottom: 16px;
          background: var(--panel); border: 1px solid var(--rule); }
.notice.wait { background: var(--warn-soft); border-color: var(--warn-line); }
.notice.err { background: var(--err-soft); border-color: var(--err-line); }
.notice button { margin-left: 12px; padding: 3px 11px; }
.note-warn { margin-top: 6px; color: var(--warn); font-weight: var(--w-head); }

.block { background: var(--panel); border: 1px solid var(--rule); border-radius: var(--r-card);
         padding: 16px 18px; margin-bottom: 12px; }
.block .list { border: 0; border-radius: 0; }
.card { background: var(--panel); border: 1px solid var(--rule); border-radius: var(--r-card); padding: 18px; }

.kv { display: grid; grid-template-columns: max-content 1fr; gap: 5px 16px; margin: 0; }
.kv dt { color: var(--muted); font-size: 12.5px; }
.kv dd { margin: 0; }

/* 详情页的分栏（三期 3.7 改成两栏，做法见下面的 .work-two）
   ——————————————
   二期这里是单栏，当时的判断是：七个 block 在 DOM 里交错（#bProduct 第 4、#bVideo 第 9），
   靠纯 CSS 分列，同一行的高度由较高者决定，而 #bVideo 装着 9:16 竖版播放器，
   会把同行左栏的下方撑出大片空白；要做干净就得包一层容器 div，
   而那是改 HTML，越过了 PRD 1.3「第二期不做逐页重画」。
   三期 PRD 3.7 明确要「左边播放器、右边分段和操作」，于是就包了那层容器：
   只把 #bVideo 和 #bPlan 两块放进 .work-two，其余 block 仍在下面顺排——
   交错的问题就不存在了。
   09-21 又把上传从左栏挪到右栏顶上：右栏是 .side-right，里面上传在上、分段在下。 */

/* 分段清单（方案卡片里的那种，与轨道上的 .track .seg 不是一回事）。
   限定在 .timeline 下：裸写 .seg 会漏到轨道上，给每段轨道多加一条虚线底边和网格间距 */
.timeline { margin-top: 10px; border-left: 2px solid var(--rule); }
.timeline.ref { border-left-color: var(--ref); }
.timeline.gen { border-left-color: var(--gen); }
.timeline .seg { display: grid; grid-template-columns: 110px 44px 1fr; gap: 2px 10px; padding: 5px 0 5px 12px;
       border-bottom: 1px dashed var(--rule-soft); font-size: 13px; }
.timeline .seg:last-child { border-bottom: 0; }
.timeline .seg .t { color: var(--muted); white-space: nowrap; }
.timeline .seg .role { font-size: 11px; color: var(--muted); align-self: start; padding-top: 2px; white-space: nowrap; }
.timeline.ref .seg .role { color: var(--ref); }
.timeline.gen .seg .role { color: var(--gen); }
.timeline .seg .vo { grid-column: 3; color: var(--muted); font-size: 12.5px; }

.variants { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 14px; }
.variant { border: 1px solid var(--rule); border-radius: var(--r-card); padding: 13px 15px;
           display: flex; flex-direction: column; background: var(--panel); }
.variant.chosen { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.variant.dim { opacity: 0.55; }
/* 预计耗时与用量（PRD 3.2）。用 --muted 压卡片底色，静态门禁里已有这一对 */
.variant .est { margin: 8px 0 0; font-size: 12px; color: var(--muted); }
.variant header { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.variant .idx { font-family: var(--mono); font-size: 12px; color: var(--gen); background: var(--gen-soft);
                width: 20px; height: 20px; display: inline-flex; align-items: center;
                justify-content: center; border-radius: 50%; }
.variant .tag { font-size: 11px; color: var(--gen); border: 1px solid var(--gen);
                border-radius: var(--r-pill); padding: 0 6px; }
.variant header .muted { margin-left: auto; }
.variant .angle { margin: 0 0 6px; color: var(--muted); font-size: 13px; }
.variant .timeline { flex: 1; }
.variant button { margin-top: 12px; align-self: flex-start; }

/* 三期 3.7：详情页两栏——左播放器、右分段与操作。
   flex 不是 grid：没出片时左边整块是 hidden，display:none 的 flex 子项不占位、右栏自动铺满；
   grid 固定两列的话左边会空着一大块。min-width:0 是给右栏的，
   不写的话里面那张分段表会把栏撑出去，整页横向滚 */
.work-two { display: flex; align-items: flex-start; gap: 14px; }
/* 340 = 播放器 300 + 两边内边距，成片信息摞在播放器下面。
   左栏自己能滚（max-height + overflow-y）：矮屏上左栏可能高于视口，整块 sticky 时
   下面那截会一直待在视口外。让左栏在自己那一屏里滚，播放器始终在眼前。
   （上传原来也摞在这下面，看完片得在左栏里往下滚才找得到，09-21 挪去了右栏顶上）
   overflow 只加在 sticky 元素**自己**身上不影响它粘住（破坏 sticky 的是祖先上的 overflow） */
.work-two .side-video {
    /* 🔴 top 要让开吸顶的顶栏。.top 是 position:sticky; top:0; height:50px; z-index:5，
       背景不透明——按 top:14px 粘的话，卡片上边框和「成片」标题会被顶栏整段吃掉；
       配上下面这个 overflow 之后更糟：顶部那 36px 变成永久遮挡带，
       左栏内部怎么滚都补不回来（09-21 审查在 1366×768 实测：h3 只剩 3px 露在顶栏下沿）。
       64 = 顶栏 50 + 上留白 14；限高再多减一个下留白 14 */
    flex: 0 0 340px; position: sticky; top: 64px;
    max-height: calc(100vh - 78px); overflow-y: auto;
}
/* 右栏：上传在上、分段在下。flex 属性给容器，min-width:0 防那张宽表把栏撑出去 */
.work-two .side-right { flex: 1 1 0; min-width: 0; }
.work-two .side-plan { overflow-x: auto; }
@media (max-width: 1100px) {
    .work-two { display: block; }
    /* 单栏时别再限高，否则左栏里套一个滚动条，页面里两层滚动 */
    .work-two .side-video { position: static; max-height: none; overflow-y: visible; }
}

.video-wrap { display: grid; grid-template-columns: 300px 1fr; gap: 22px; align-items: start; }
/* 两栏布局里播放器独占左栏，信息摞在它下面，不再与之并排。
   🔴 必须关在 min-width 查询里：这条是 (0,2,0)，而窄屏降级那两段
   （max-width:1100 / 760 里的 .video-wrap）只有 (0,1,0)，压不过它。
   裸写的话手机上播放器会被钉死在 300px、右边留一条白，
   而 761~1100px 区间的成片卡高度直接翻倍（09-21 审查实测 637 → 1231） */
@media (min-width: 1101px) {
    .work-two .video-wrap { grid-template-columns: 300px; }
}
.video-wrap video { width: 300px; aspect-ratio: 9 / 16; background: #000;
                    border-radius: var(--r-ctl); border: 1px solid var(--rule); }
/* 上传三样在右栏的 #bUpload 里（09-21 从左栏挪过去）；表单和下面的上传结果之间留点空 */
#bUpload .form + .kv { margin-top: 16px; }

/* ========== 登录 ========== */
/* 登录页（三期 3.7 改版）。
   🔴 柔光背景画在 .login 上，不画在 body 上：E2E 的对比度门禁把 body 当必测取样点，
   一见到 background-image 就判"算不准"并放弃这个点，门禁会在最关键的一格静默失效。
   两团光用 --fill-a / --fill-b（主按钮那套渐变色）着色，跟着皮肤走；
   低透明度铺在 --paper 上，浅色皮肤是一层淡晕，深色皮肤才接近参考站那种夜色。 */
.login {
    /* 满屏而不是 100vh-50px：那 50px 是旧版顶栏的高度，而登录时整个壳（含顶栏）是收起的，
       减掉之后底部会露出一条没有晕染的白带（box-sizing 全局 border-box，padding 不会撑出滚动条） */
    min-height: 100vh; display: flex; align-items: center; justify-content: center;
    padding: 24px;
    /* 两条 background 是**给 color-mix() 兜底**的：不认识 color-mix 的浏览器会整条丢弃第二句，
       退回上面这句用现成软色的版本；认识的用下面那句，浓淡跟着主按钮那套渐变走。
       不写兜底的话老浏览器上登录页就是一片纯色，不难看但也白写了 */
    background:
        radial-gradient(60% 55% at 18% 12%, var(--accent-soft), transparent 70%),
        radial-gradient(55% 50% at 85% 88%, var(--gen-soft), transparent 70%);
    background:
        radial-gradient(60% 55% at 18% 12%, color-mix(in srgb, var(--fill-a) 22%, transparent), transparent 70%),
        radial-gradient(55% 50% at 85% 88%, color-mix(in srgb, var(--fill-b) 18%, transparent), transparent 70%);
}
.login-card {
    width: 360px; max-width: 100%; background: var(--panel); border: 1px solid var(--rule);
    border-radius: 18px; padding: 32px 30px 24px; box-shadow: 0 18px 48px rgba(0, 0, 0, .10);
}
.lg-head { text-align: center; margin-bottom: 22px; }
/* 首字当图标用：内部系统没有 logo 文件，与其摆一个占位图，不如用产品名的第一个字 */
.lg-mark {
    display: inline-grid; place-items: center; width: 44px; height: 44px; border-radius: 13px;
    background: linear-gradient(135deg, var(--fill-a), var(--fill-b));
    color: #fff; font-size: 20px; font-weight: 600; margin-bottom: 12px;
}
.login-card h1 { font-size: 20px; margin: 0 0 4px; font-weight: var(--w-head); }
.login-card .sub { margin: 0; font-size: 13px; color: var(--muted); }
.login-card label { display: block; font-size: 13px; color: var(--muted); margin-bottom: 14px; }
.login-card input { display: block; width: 100%; margin-top: 5px; font: inherit; font-family: var(--ui);
                    color: var(--ink); background: var(--field); padding: 9px 11px;
                    border: 1px solid var(--rule-strong); border-radius: var(--r-ctl); }
.login-card input:focus-visible { outline: none; border-color: var(--accent);
                                  box-shadow: 0 0 0 3px var(--accent-soft); }
.login-card button { width: 100%; margin-top: 6px; }
.login-card .err { min-height: 1.4em; margin: 10px 0 0; font-size: 13px; text-align: center; }
.lg-foot { margin: 6px 0 0; font-size: 12px; color: var(--muted); text-align: center; }

@media (max-width: 760px) {
    .top { gap: 14px; padding: 0 14px; }
    .page { padding: 14px; }
    .form .row2 { grid-template-columns: 1fr; }
    .video-wrap { grid-template-columns: 1fr; }
    .video-wrap video { width: 100%; }
    .steps { flex-wrap: wrap; }
    .steps li { flex-basis: 33%; }
}

/* ========== 素材缩略图 ========== */
/* 固定框而不是让图片决定行高：素材长宽比各异，不固定的话表格每行高低不齐 */
.thumb {
    position: relative; display: block; width: 72px; height: 96px;
    border: 1px solid var(--rule); border-radius: var(--r-ctl); overflow: hidden;
    background: var(--panel-3);
}
.thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.thumb.no-thumb img { display: none; }
.thumb.no-thumb::after {
    content: '无预览'; position: absolute; inset: 0;
    display: flex; align-items: center; justify-content: center;
    font-size: 11px; color: var(--muted);
}
.thumb .play {
    position: absolute; right: 3px; bottom: 3px;
    color: #fff; font-size: 10px; line-height: 1;
    background: rgba(0, 0, 0, 0.65); border-radius: 2px; padding: 2px 4px;
}

/* 表单上方的说明条 */
.note {
    margin: 0 0 14px; padding: 10px 13px; border-radius: var(--r-ctl);
    background: var(--gen-soft); border-left: 3px solid var(--gen);
    color: var(--ink); font-size: 13px; line-height: 1.6;
}
.note.warn { background: var(--warn-soft); border-left-color: var(--warn); color: var(--warn); }
.note a { text-decoration: underline; }

/* ========== 合成轨道：宽度即时长，颜色即来源 ==========
   🔴 AI 段用斜纹、真实段用实色，不是装饰：实算两色在红绿色盲下的距离，
   plain 只有 50、ink 58；两者的段底块明度比在五套里是 1.01–1.23，
   正常视觉也几乎只能靠色相分。形态冗余是唯一可靠的区分手段。 */
.ruler { display: flex; height: 13px; margin-top: 10px; padding: 0 1px; }
.ruler i {
    /* flex 由 JS 按每格实际秒数写在行内——最后一格通常不足一个整步长。
       这里不写 flex，免得和行内值表达相反的意图 */
    font-style: normal; font-family: var(--mono); font-size: 10px;
    color: var(--muted); border-left: 1px solid var(--rule); padding-left: 3px;
    /* flex item 默认 min-width:auto，缩不到标签文字以下，格子一多就把刻度尺撑破轨道 */
    min-width: 0; overflow: hidden;
}

.track {
    display: flex; height: 46px; margin: 3px 0 8px;
    border: 1px solid var(--rule); border-radius: var(--r-ctl); overflow: hidden;
    background: var(--panel-2);
}
.track .seg {
    display: flex; flex-direction: column; justify-content: center;
    padding: 0 8px; min-width: 0; overflow: hidden;
    border-right: 1px solid var(--panel);
    font-size: 12px; line-height: 1.35; cursor: pointer;
    transition: filter .13s;
}
.track .seg:hover { filter: brightness(1.06); }
.track .seg:last-child { border-right: 0; }
.track .seg b { font-weight: var(--w-head); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* 不用 opacity 压淡：opacity 是合成属性，不折进 computed color，
   量对比度的工具（浏览器里的、脚本里的）都看不见它。
   实测这一处曾因 opacity:.8 在五套皮肤下全部落到 3.45–4.38，而两套测试都是绿的 */
.track .seg i { font-style: normal; font-size: 11px; }
.track .seg.real { background: var(--real-soft); color: var(--real-ink); }
.track .seg.photo { background: var(--real-soft); color: var(--real-ink); border-bottom: 3px solid var(--real); }
.track .seg.ai {
    color: var(--ai-ink);
    background-color: var(--ai-soft);
    background-image: repeating-linear-gradient(135deg, var(--ai-stripe) 0 5px, transparent 5px 11px);
}
.track .seg.on { box-shadow: inset 0 0 0 2px var(--accent); }

.legend { display: flex; align-items: center; gap: 6px; margin: 0 0 12px; font-size: 12px; color: var(--muted); }
.legend .k { width: 12px; height: 10px; border-radius: 2px; display: inline-block; }
.legend .k.real { background: var(--real-soft); border: 1px solid var(--real); }
.legend .k.photo { background: var(--real-soft); border: 1px solid var(--real); border-bottom-width: 3px; margin-left: 10px; }
.legend .k.ai { background-color: var(--ai-soft); border: 1px solid var(--ai); margin-left: 10px;
                background-image: repeating-linear-gradient(135deg, var(--ai-stripe) 0 4px, transparent 4px 8px); }
.legend .muted { margin-left: auto; }

.err.box {
    padding: 12px 14px; border-radius: var(--r-ctl);
    background: var(--err-soft); border-left: 3px solid var(--err);
}

/* 列表里的素材构成条：宽度即真实素材占比。
   AI 那半边同样上斜纹——16px 高也不能退化成纯色编码 */
.mix { display: inline-flex; align-items: center; gap: 7px; }
.mix .bar {
    width: 64px; height: 8px; border-radius: var(--r-pill);
    background-color: var(--ai-soft); overflow: hidden; display: inline-block;
    background-image: repeating-linear-gradient(135deg, var(--ai-stripe) 0 3px, transparent 3px 6px);
    border: 1px solid var(--rule);
}
.mix .bar b { display: block; height: 100%; background: var(--real); }

.list tr.row-block td { background: var(--err-soft); }
.flags { margin: 6px 0 0; padding-left: 18px; font-size: 12px; }
.flags.block { color: var(--err); }
.flags.review { color: var(--muted); }

/* 局部重跑：换素材面板与重跑历史 */
.rerun-panel { margin-top: 12px; padding: 13px 15px; border: 1px solid var(--rule);
               border-radius: var(--r-card); background: var(--panel-2); }
.rerun-panel h4 { margin: 0 0 8px; font-size: 13px; }
.rerun-panel .row { display: flex; gap: 8px; align-items: flex-end; flex-wrap: wrap; }
.rerun-panel label { display: flex; flex-direction: column; gap: 3px; font-size: 12px; color: var(--muted); }
.rerun-panel select, .rerun-panel input {
    padding: 5px 7px; font-size: 13px; font-family: var(--ui); color: var(--ink);
    background: var(--field); border: 1px solid var(--rule-strong); border-radius: var(--r-ctl);
}
.rerun-panel .hint { font-size: 12px; color: var(--muted); margin-top: 6px; }
.rerun-hist { margin-top: 10px; font-size: 12px; }
.rerun-hist li { color: var(--muted); margin: 2px 0; }
.rerun-hist li b { color: var(--ink); font-weight: var(--w-head); }
.seg-changed { color: var(--accent); font-size: 11px; margin-left: 4px; }

/* 商品画面检测（第二期 4.2）。
   三态靠文字+颜色双编码：ink 与 sakura 下 --ok 与 --err 在红绿色盲下几乎同色，
   光靠颜色分不出，所以理由一律挂在 title 上（E2E 钉着这一点）。 */
.seg-suspect { color: var(--err); font-size: 11px; margin-left: 4px; cursor: help; }
.seg-note { color: var(--muted); font-size: 11px; margin-left: 4px; cursor: help; }
/* 通过态：比"未检查"淡一档但仍要读得出。
   原来用 --rule（边框色）只有 1.3:1；改用 --muted 后又加过 opacity:.75，
   实测把五套皮肤压到 3.34–4.21 全部不达标，而 opacity 不进 computed color、测不出来。
   层级改用字号做（11px vs 正文 14px），不再压明度 */
.seg-ok { color: var(--muted); font-size: 11px; margin-left: 4px; cursor: help; }

/* ==================================================================
   三期 3.7 界面改版：左侧分组导航 + 创作三步 + 作品墙 + 卡片网格
   全部用上面那套语义变量，换皮肤时一起变
   ================================================================== */

/* 侧栏分组标题 */
.nav-grp { font-size: 12px; color: var(--muted); padding: 14px 12px 4px; }
.tabs a { border-radius: var(--r-ctl); }

/* 左下角账号菜单 */
.me { position: relative; }
.me-who { flex: 1; min-width: 0; }
.me-more { margin-left: auto; padding: 4px 6px; color: var(--muted); }
.me-menu {
    position: absolute; left: 8px; right: 8px; bottom: calc(100% + 6px);
    background: var(--panel); border: 1px solid var(--rule); border-radius: var(--r-ctl);
    box-shadow: 0 8px 24px rgba(0, 0, 0, .12); padding: 6px; z-index: 20;
}
.me-menu a, .me-menu button {
    display: block; width: 100%; text-align: left; padding: 9px 12px;
    border-radius: 8px; font-size: 13px; color: var(--ink); background: none; border: 0;
}
.me-menu a:hover, .me-menu button:hover { background: var(--panel-3); }
.me-menu .sep { height: 1px; background: var(--rule); margin: 4px 0; }

/* 页头 */
.page-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; margin-bottom: 20px; flex-wrap: wrap; }
.page-head h2 { margin: 0 0 2px; font-size: 20px; }
.page-head .sub { color: var(--muted); margin: 0; font-size: 13px; }

/* DreamMini 的深色工作台、青蓝 / 紫色强调，沿用现有创作流程。 */
.create-page { padding-top: 28px; }
.creation-intro { padding: 4px 0 26px; }
.creation-intro .eyebrow { color: var(--accent); font-size: 12px; margin: 0 0 10px; letter-spacing: .08em; }
.creation-intro h1 { margin: 0 0 12px; font-size: clamp(23px, 2.5vw, 32px); font-weight: 600; line-height: 1.45; }
.creation-intro > p:not(.eyebrow) { margin: 0; color: var(--muted); font-size: 13px; line-height: 1.8; max-width: 760px; }
.creation-path { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-top: 18px; }
.creation-path span { padding: 5px 10px; border: 1px solid var(--rule); border-radius: 7px; color: var(--muted); background: var(--panel); font-size: 11px; }
.creation-path i { color: var(--muted); font-size: 12px; font-style: normal; }

@media (max-width: 600px) {
    .top { padding: 0 12px; gap: 8px; }
    .breadcrumb > span { display: none; }
    .who { gap: 8px; }
    .skins { gap: 4px; }
    .skins button.link { width: 19px; height: 19px; }
    .page { padding: 16px 12px; }
    .creation-intro { padding-bottom: 20px; }
    .head-ops { justify-content: flex-start; width: 100%; }
    .model-pick { flex-wrap: wrap; white-space: normal; }
    .model-pick select { min-width: 0; max-width: 240px; }
    .card { padding: 14px 12px; }
    .card-h { flex-wrap: wrap; }
    .play { grid-template-columns: 1fr; }
}

/* 卡片 */
.card {
    background: var(--panel); border: 1px solid var(--rule); border-radius: var(--r-card);
    padding: 18px 20px; margin-bottom: 14px;
}
.card.hot { border-color: var(--accent-soft); background: var(--panel-2); }
.card-h { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 12px; }
.card-h b { font-size: 15px; display: flex; align-items: center; }
.card-h .tip { color: var(--muted); font-size: 12px; margin-left: 8px; font-weight: 400; }
.step {
    display: inline-grid; place-items: center; width: 20px; height: 20px; border-radius: 50%;
    border: 1.5px solid var(--ink); font-size: 12px; margin-right: 6px; font-weight: 600; flex: none;
}

/* 主按钮与通栏按钮 */
.btn.pri { background: linear-gradient(135deg, var(--fill-a), var(--fill-b)); color: #fff; border-color: transparent; }
.btn.pri:hover { filter: brightness(1.05); }
.big {
    display: block; width: 100%; padding: 15px; border-radius: 14px; font-size: 16px; font-weight: 600;
    text-align: center; background: linear-gradient(135deg, var(--fill-a), var(--fill-b));
    color: #fff; border: 0; cursor: pointer;
}
/* 🔴 必须显式写 hover：全局的 button:hover{background:var(--ink-hover)} 权重是 (0,1,1)，
   压得过裸类名 .big 的 (0,1,0)——鼠标一放，渐变底就被换成一块纯色，而 color:#fff 还留着。
   dreammini 下那个纯色是亮薄荷，白字对比度只有 1.44，字直接看不见了（09-21 审查实测六套皮肤）。
   登录按钮和"生成视频"都是 .big，这一条两处受益 */
.big:hover { background: linear-gradient(135deg, var(--fill-a), var(--fill-b)); filter: brightness(1.05); }
.big[disabled] { background: var(--panel-3); color: var(--muted); cursor: not-allowed; }
.big[disabled]:hover { background: var(--panel-3); filter: none; }
.hint-line { text-align: center; color: var(--muted); font-size: 13px; margin: 10px 0 0; }

/* 创作页右上角：视频模型 + 出片方式，窄屏时换行 */
.head-ops { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 10px 14px; }
.model-pick { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--muted); white-space: nowrap; }
.model-pick select {
    min-width: 170px; font: inherit; font-family: var(--ui); font-size: 13px; color: var(--ink);
    padding: 7px 10px; border: 1px solid var(--rule-strong); border-radius: var(--r-pill); background: var(--panel);
}
.model-pick select:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* 出片方式切换。🔴 别叫 .seg：分段时间轴（参考拆解、方案里的镜头）早就用了 .seg，
   这条写在后面会把时间轴的三列网格顶成一排胶囊——09-22 线上时间一行一行错位、
   角色字竖着排，就是这么来的 */
.switch { display: inline-flex; background: var(--panel); border: 1px solid var(--rule); border-radius: var(--r-pill); padding: 4px; }
.switch button { padding: 7px 16px; border-radius: var(--r-pill); font-weight: 600; color: var(--muted); border: 0; background: none; cursor: pointer; }
.switch button.on { background: linear-gradient(135deg, var(--fill-a), var(--fill-b)); color: #fff; }

/* 选商品 */
.pick { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 12px; }
.pick .p {
    border: 1.5px solid var(--rule); border-radius: var(--r-ctl); padding: 10px; text-align: left;
    background: var(--panel); cursor: pointer; display: flex; flex-direction: column; gap: 8px;
    /* 🔴 必须显式给字色：这是个 <button>，不写就继承按钮那套的主色，
       商品名会变成一行浅粉字压在浅粉选中底上，几乎看不见（09-20 截图实证） */
    color: var(--ink);
}
.pick .p:hover { border-color: var(--rule-strong); }
.pick .p.on { border-color: var(--accent); background: var(--accent-soft); }
.pick .p .img { aspect-ratio: 1; border-radius: 8px; background: var(--panel-3); display: grid; place-items: center; color: var(--muted); font-size: 12px; overflow: hidden; }
.pick .p .img img { width: 100%; height: 100%; object-fit: cover; }
.pick .p b { font-size: 13px; }
.pick .p span { font-size: 12px; color: var(--muted); }
.pick .add {
    border: 1.5px dashed var(--rule-strong); border-radius: var(--r-ctl); min-height: 150px;
    display: grid; place-items: center; color: var(--muted); background: var(--panel-2); cursor: pointer;
}
.pick .add:hover { border-color: var(--accent); color: var(--accent); }

/* 参考页签与上传框 */
.tabs2 { display: flex; gap: 6px; margin-bottom: 12px; flex-wrap: wrap; }
.tabs2 button { padding: 6px 14px; border-radius: var(--r-pill); border: 1px solid var(--rule-strong); background: var(--panel); font-size: 13px; color: var(--muted); cursor: pointer; }
.tabs2 button.on { border-color: var(--ink); color: var(--ink); font-weight: 600; }
.drop {
    display: block; border: 1.5px dashed var(--rule-strong); border-radius: var(--r-ctl);
    padding: 22px; text-align: center; color: var(--muted); background: var(--panel-2); cursor: pointer;
}
.drop b { display: block; color: var(--ink); margin-bottom: 4px; }
.drop.done { border-style: solid; border-color: var(--ok); background: var(--ok-soft); cursor: default; }
.drop.done b { color: var(--ok); }
.drop input[type=file] { display: none; }
.sub-field { display: block; margin-top: 10px; font-size: 12px; color: var(--muted); }
.adv { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 12px 16px; margin-top: 14px; padding: 14px; border: 1px solid var(--rule); border-radius: var(--r-ctl); background: var(--panel); }
.adv label { font-size: 12px; color: var(--muted); display: flex; flex-direction: column; gap: 6px; }

/* 玩法 */
.play { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 12px; }
.play .g { display: flex; gap: 12px; align-items: flex-start; border: 1.5px solid var(--rule); border-radius: var(--r-ctl); padding: 14px; text-align: left; background: var(--panel); cursor: pointer; color: var(--ink); }
.play .g.on { border-color: var(--accent); background: var(--accent-soft); }
.play .g.off { opacity: 1; color: var(--muted); cursor: not-allowed; background: var(--panel-2); }
.play .g i { width: 36px; height: 36px; border-radius: 10px; background: var(--accent-soft); color: var(--accent); display: grid; place-items: center; font-style: normal; font-weight: 700; flex: none; }
.play .g b { display: block; font-size: 14px; margin-bottom: 2px; }
.play .g span { font-size: 12px; color: var(--muted); }
.tag { font-size: 11px; color: var(--muted); border: 1px solid var(--rule-strong); border-radius: 6px; padding: 0 5px; margin-left: 6px; font-weight: 400; }

/* 筛选 chip */
.chips { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }
.chip { padding: 6px 12px; border-radius: var(--r-pill); border: 1px solid var(--rule-strong); background: var(--panel); font-size: 13px; color: var(--muted); cursor: pointer; }
.chip.on { border-color: var(--ink); color: var(--ink); font-weight: 600; }
.chip em { font-style: normal; color: var(--muted); margin-left: 4px; }

/* 作品墙 */
.wall { display: grid; grid-template-columns: repeat(auto-fill, minmax(170px, 1fr)); gap: 16px; }
.w { background: var(--panel); border: 1px solid var(--rule); border-radius: 14px; overflow: hidden; text-align: left; padding: 0; cursor: pointer; display: block; color: inherit; text-decoration: none; }
/* 全局的 a:hover{text-decoration:underline} 特异性比裸 .w 高，
   得在 hover 态上再关一次，否则鼠标一放整张卡的文字都带下划线 */
.w:hover { border-color: var(--rule-strong); text-decoration: none; }
.w .cv { aspect-ratio: 9 / 16; position: relative; background: var(--panel-3); display: grid; place-items: center; overflow: hidden; }
.w .cv img, .w .cv video { width: 100%; height: 100%; object-fit: cover; }
/* 占位文字垫在最底层：封面是 <img> 盖在它上面的，
   不绝对定位的话 grid 会把两者摞成两行——有封面的卡片下面还挂着一行"暂无封面" */
.w .cv .ph { position: absolute; inset: 0; display: grid; place-items: center; color: var(--muted); font-size: 12px; }
/* 封面要压在占位文字上面。都是定位元素时后来者在上，而占位在 DOM 里排在封面之后——
   只给 position 不给 z-index 的话，每张有封面的卡中间都透着一行"暂无封面"（09-20 截图实证） */
.w .cv img, .w .cv video { position: relative; z-index: 1; }
/* 悬停预览：盖在封面之上。状态标签是 z-index 自然层叠里的绝对定位元素，
   给它更高的层级，免得预览一起播就把"待确认上传"盖住了 */
.w .cv video.pv { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 2; }
/* 状态标签和"正在改第几段"那行都要浮在预览之上，否则视频一播就把它们盖了 */
.w .cv .st, .w .cv .prog { z-index: 3; }
.w .cv .st { position: absolute; top: 8px; left: 8px; }
.w .cv .prog { position: absolute; left: 8px; right: 8px; bottom: 8px; background: var(--panel); border-radius: 8px; padding: 7px 9px; font-size: 12px; }
.w .meta { padding: 10px 12px 12px; }
.w .meta b { display: block; font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* 标题拆两行（09-22 线上：「金豆芽金银花柚子汁 · 0…」三张卡看着一样）：商品名一行、放不下省略；
   「月-日 · 第几条」单独一行永远看得见——只靠多行截断的话，窄屏或商品名再长一点，编号照样被吃掉 */
/* 🔴 两个 span 都会被下面通用的 .w .meta span（12px、灰色副标题）命中，这里的选择器更具体，
   得把字号颜色写明，不然商品名那一行就降成了副标题的样子 */
.w .meta b .nm { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
                 font-size: 13px; color: var(--ink); }
.w .meta b .sfx { display: block; font-weight: 400; color: var(--muted); font-size: 12px; margin-top: 1px; }
.w .meta span { display: block; font-size: 12px; color: var(--muted); }
.w .meta .tid { font-size: 11px; opacity: .8; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* 作品页：状态 chip 在左、搜索框在右，窄屏时搜索框换到下一行占满 */
.works-bar { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 8px 16px; margin-bottom: 14px; }
.works-bar .chips { margin-bottom: 0; flex: 1 1 auto; }
.works-bar input[type=search] { flex: 0 1 260px; min-width: 180px; }

/* 卡片网格（商品库 / 素材库） */
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 16px; }
.pc { background: var(--panel); border: 1px solid var(--rule); border-radius: 14px; overflow: hidden; }
.pc .img { aspect-ratio: 4 / 3; background: var(--panel-3); display: grid; place-items: center; color: var(--muted); font-size: 12px; overflow: hidden; }
.pc .img img { width: 100%; height: 100%; object-fit: cover; }
.pc .bd { padding: 12px; }
.pc .bd b { display: block; font-size: 14px; }
.pc .bd span { display: block; font-size: 12px; color: var(--muted); margin-top: 2px; }
.pc .bd .ops { display: flex; gap: 6px; margin-top: 10px; flex-wrap: wrap; }
/* 素材卡的缩略图是个链接，右下角压一个播放角标。
   不复用表格那套 .thumb .play：那个是固定 72×96 的小框，网格里要铺满整格 */
.pc a.img { position: relative; }
.pc .img .pv {
    position: absolute; right: 6px; bottom: 6px; color: #fff; font-size: 11px; line-height: 1;
    background: rgba(0, 0, 0, .65); border-radius: 4px; padding: 3px 5px;
}
/* .pc .bd span 把卡里的 span 都摊成整行，标签得单独拉回行内，否则一个标签占一行 */
.pc .bd .tags span { display: inline-block; margin-top: 0; }

/* 三期新版页面里的表单控件。
   旧版这套样式钉在 .form 上，而创作页与弹窗改成了卡片布局、不再套 .form——
   不补这一段的话，输入框会掉回浏览器默认样式：深色皮肤上白底黑字，还没有焦点环 */
.card input:not([type=file]), .card textarea, .card select,
.modal input:not([type=file]), .modal textarea, .modal select {
    display: block; width: 100%; margin-top: 4px;
    font: inherit; font-family: var(--ui); font-size: 14px; color: var(--ink);
    padding: 6px 9px; border: 1px solid var(--rule-strong);
    border-radius: var(--r-ctl); background: var(--field);
}
.card input:focus-visible, .card textarea:focus-visible, .card select:focus-visible,
.modal input:focus-visible, .modal textarea:focus-visible, .modal select:focus-visible {
    outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft);
}
.card .hint { display: block; margin-top: 3px; font-size: 11.5px; color: var(--muted); line-height: 1.5; }

/* 建商品弹窗 */
.mask { position: fixed; inset: 0; background: rgba(15, 15, 20, .45); display: grid; place-items: center; z-index: 60; padding: 16px; }
.modal { background: var(--panel); border-radius: 18px; width: min(600px, 100%); max-height: 88vh; overflow: auto; padding: 20px 22px; }
.modal h3 { margin: 0 0 14px; font-size: 17px; display: flex; justify-content: space-between; align-items: center; }
.modal .form2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.modal .form2 label { font-size: 12px; color: var(--muted); display: flex; flex-direction: column; gap: 6px; }
.modal .form2 label.full { grid-column: 1 / -1; }
.modal .foot { display: flex; justify-content: flex-end; gap: 8px; margin-top: 16px; }

@media (max-width: 720px) {
    .modal .form2 { grid-template-columns: 1fr; }
    .wall { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); }
}
.video-pick-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(210px,1fr));gap:16px;margin:16px 0}
.video-pick{border:1px solid var(--rule-strong);border-radius:12px;padding:12px;display:flex;flex-direction:column;gap:8px;min-width:0}
.video-pick.selected{outline:2px solid var(--accent)}
.video-pick b,.video-pick span{overflow-wrap:anywhere}
.video-pick-cover{width:100%;padding:0;background:transparent;border:0;overflow:hidden;border-radius:8px}
.video-pick-cover .thumb{height:150px;width:100%;object-fit:contain}
.video-pick-cover img{width:100%;height:150px;object-fit:contain}
#headUpload .row label,#ref-sample .row label{flex:1;min-width:160px}
#headRecommendations label.head-choice{display:flex;flex-direction:row;align-items:center;gap:8px;margin:12px 0 4px}
.rerun-panel input[data-head-select],.card input[data-head-select],.modal input[data-head-select]{width:auto;min-width:0;height:auto;flex:none;padding:0;margin:0}
#headBasePreview,#referencePreview{margin-top:16px}
/* 玩法选择：图标、用途、选中状态都放在卡片里。 */
.playbook-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
.playbook-grid .playbook-card { min-height: 112px; border-radius: 22px; align-items: center; padding: 20px; transition: border-color .15s, background .15s; }
.playbook-grid .playbook-card i { width: 64px; height: 64px; flex: 0 0 64px; border-radius: 18px; background: #ff5b7617; color: #ff5b76; display: grid; place-items: center; }
.playbook-card svg { width: 31px; height: 31px; }
.playbook-grid .playbook-card b { display: flex; align-items: center; gap: 10px; font-size: 17px; }
.playbook-grid .playbook-card.on { border-color: #ff5b76; background: #ff5b7612; box-shadow: 0 0 0 2px #ff5b76; }
.playbook-grid .playbook-card.on i { background: linear-gradient(130deg, #ff4c77, #ff8354); color: white; }
.play-check { display: none; color: #ff5b76; font-size: 16px; }
.playbook-card.on .play-check { display: inline; }
.play-reference { margin: 0 0 8px; border-radius: 30px; background: transparent; }
.play-reference[aria-pressed="true"], #playBatch[aria-pressed="true"] { border-color: var(--accent); background: var(--accent-soft); color: var(--ink); }
.playbook-settings { border: 1px solid var(--rule-strong); border-radius: 20px; margin-top: 20px; padding: 20px; background: var(--panel-2); }
.play-recommend { background: linear-gradient(100deg, #ff4c77, #ff8354); color: #fff; border: none; border-radius: 30px; }
.playbook-pace { padding: 0; border: none; margin: 16px 0; }
.playbook-pace legend { color: var(--muted); margin-bottom: 12px; }
.playbook-pace label { display: inline-flex; align-items: center; gap: 8px; margin-right: 12px; padding: 10px 14px; border: 1px solid var(--rule-strong); border-radius: 12px; cursor: pointer; white-space: nowrap; width: auto; }
.playbook-pace input[type="radio"] { width: auto; flex: 0 0 auto; margin: 0; }
.playbook-pace label:has(input:checked) { border-color: #ff5b76; background: #ff5b7615; }
.portrait-choice label { display: inline-flex; align-items: center; gap: 8px; margin-right: 12px; padding: 10px 14px; border: 1px solid var(--rule-strong); border-radius: 12px; cursor: pointer; }
.portrait-choice label:has(input:checked) { border-color: var(--accent); background: var(--panel-2); }
.portrait-choice input[type="checkbox"] { width: 16px; height: 16px; margin: 0; }
@media(max-width: 760px) { .playbook-grid { grid-template-columns: 1fr; } .playbook-card { min-height: 105px; } }
