/* 🔴 2026-09-13 第 2 批：这里原来还有一整个 :root，十个 token 全都被后面那个收口块盖死了（--muted #687281、--amber #b45309、--bg #f4f6f8 …）。B1 那一轮只清掉了 --line 这一个，剩下九个留在这儿——同一个 token 两份值同时写在文件里，改色时改中哪一份全看运气，`--line` 当初就是这么长出两个色的。逐个核过：十个后面**全部**有新定义，这一整段是死的，整段删除。 */
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}button,input,select,textarea{font:inherit}button{border:1px solid #cbd4df;background:#fff;border-radius:8px;padding:9px 12px;font-weight:700;cursor:pointer}.app{min-height:100vh;display:grid;grid-template-columns:248px minmax(0,1fr)}aside{background:#111827;color:#f9fafb;padding:18px 14px;display:flex;flex-direction:column;gap:18px;position:sticky;top:0;align-self:start;height:100vh;overflow-y:auto}.brand{display:flex;gap:10px;align-items:center;padding:6px 6px 16px;border-bottom:1px solid rgba(255,255,255,.12)}.brand-mark{width:38px;height:38px;flex:none;display:block;object-fit:contain}.brand span{display:block;color:#aeb6c4;font-size:12px;margin-top:3px}nav{display:grid;gap:6px}.nav-btn{border:0;background:transparent;color:#cbd5e1;text-align:left;border-radius:8px;padding:11px 12px}.nav-btn.active,.nav-btn:hover{background:rgba(255,255,255,.11);color:#fff}.tenant-switch{display:grid;gap:7px;color:#cbd5e1;font-size:13px}.tenant-switch select{width:100%;padding:9px;border-radius:8px;border:1px solid rgba(255,255,255,.18);background:#1f2937;color:#fff}.aside-note{margin-top:auto;border:1px solid rgba(255,255,255,.14);border-radius:8px;padding:12px;background:rgba(255,255,255,.05);font-size:12px;line-height:1.5;color:#d1d5db}main{min-width:0}header{height:72px;background:rgba(255,255,255,.92);border-bottom:1px solid var(--line);display:flex;justify-content:space-between;align-items:center;padding:0 28px;position:sticky;top:0;z-index:5}header h1{margin:0;font-size:20px}header p{margin:4px 0 0;color:var(--muted);font-size:13px}#content{padding:24px 28px 40px}.hero{background:linear-gradient(135deg,#0f766e,#164e63 52%,#be123c);color:#fff;border-radius:8px;padding:22px;display:flex;justify-content:space-between;gap:18px;align-items:center;box-shadow:var(--shadow)}.hero h2{margin:0;font-size:22px}.hero p{color:rgba(255,255,255,.78)}.grid{display:grid;gap:16px}.cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.panel{background:#fff;border:1px solid var(--line);border-radius:8px;box-shadow:var(--shadow)}.panel-head{padding:17px 18px;border-bottom:1px solid var(--line);display:flex;justify-content:space-between;align-items:center;gap:14px}.panel-head h3{margin:0;font-size:16px}.panel-head p{margin:4px 0 0;color:var(--muted);font-size:13px}.panel-body{padding:18px}.metric{padding:17px;min-height:118px;display:flex;flex-direction:column;justify-content:space-between}.metric-label{color:var(--muted);font-size:13px}.metric-value{font-size:30px;font-weight:850}.tag{display:inline-flex;border-radius:999px;padding:3px 8px;font-size:12px;font-weight:700}.green{color:var(--green);background:#dcfce7}.blue{color:var(--blue);background:#dbeafe}.amber{color:var(--amber);background:#ffedd5}.red{color:var(--red);background:#fee2e2}.gray{color:#475569;background:#e2e8f0}.card{border:1px solid var(--line);border-radius:8px;padding:14px;background:#fff;display:flex;flex-direction:column;gap:9px}.card.selected{border-color:var(--accent);background:#f4fbf9;box-shadow:0 0 0 3px rgba(15,118,110,.1)}.card h4{margin:0;display:flex;justify-content:space-between;align-items:center;gap:8px}.card p{margin:0;color:var(--muted);line-height:1.45;font-size:13px}.quick-list{display:grid;gap:10px}.quick-item{border:1px solid var(--line);border-radius:8px;padding:13px;display:flex;justify-content:space-between;gap:12px;align-items:center;background:#fff}.muted{color:var(--muted)}
/* Boss 2026-09-28：灰色说明字以前只定了颜色、没定字号，独立成段的就继承 16px，各处再单独补小，漏补的就成了大灰字。
   段落级（p / div）统一 13px；:where 让它优先级最低，各处已有的专门字号照旧生效。行内 span 仍跟着所在那行走。 */
:where(p,div).muted{font-size:13px}.form-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px}.field{display:grid;gap:6px}.field label{font-size:13px;color:#465162;font-weight:750}.field input,.field select,.field textarea{width:100%;border:1px solid #cbd4df;border-radius:8px;padding:11px 12px}.field textarea{min-height:92px}.full{grid-column:1/-1}.phone{width:260px;max-width:100%;aspect-ratio:9/16;border-radius:8px;margin:auto;background:linear-gradient(160deg,#0f172a,#155e75,#9f1239);position:relative;color:#fff;overflow:hidden}.phone-title{position:absolute;top:28px;left:20px;right:20px;text-align:center;font-size:20px;font-weight:850}.phone-product{position:absolute;top:30%;left:50%;transform:translateX(-50%);width:44%;aspect-ratio:1;border-radius:8px;background:linear-gradient(135deg,#fff,#e0f2fe)}.phone-caption{position:absolute;left:24px;right:60px;bottom:96px;text-align:center;font-weight:800}.safe-right{position:absolute;top:86px;right:0;bottom:76px;width:52px;border-left:1px dashed rgba(255,255,255,.55);background:rgba(255,255,255,.09);writing-mode:vertical-rl;display:grid;place-items:center;font-size:11px}.safe-bottom{position:absolute;left:0;right:0;bottom:0;height:74px;border-top:1px dashed rgba(255,255,255,.55);background:rgba(0,0,0,.24);display:grid;place-items:center;font-size:11px}.video-cover{aspect-ratio:9/16;background:linear-gradient(160deg,#111827,#0f766e,#e11d48);position:relative}.video-cover:after{content:"";position:absolute;left:25%;top:28%;width:50%;aspect-ratio:1;border-radius:8px;background:rgba(255,255,255,.9)}.video-player{aspect-ratio:9/16;width:100%;border-radius:8px;background:#000;display:block;object-fit:contain}table{width:100%;border-collapse:collapse}th,td{border-bottom:1px solid var(--line);padding:12px;text-align:left;font-size:13px}th{color:var(--muted);background:#fafbfc}.notice{border:1px solid #bfdbfe;background:#eef5ff;color:#1d4ed8;border-radius:8px;padding:12px;font-size:13px;line-height:1.5}.notice-error{border-color:#fecaca;background:#fef2f2;color:var(--red)}.row-clickable{cursor:pointer}.row-clickable:hover{background:#f8fafc}.button-link{display:inline-flex;align-items:center;border:1px solid #cbd4df;background:#fff;border-radius:8px;padding:9px 12px;font-weight:700;cursor:pointer;text-decoration:none;color:var(--ink)}.modal-overlay{position:fixed;inset:0;background:rgba(15,23,42,.55);display:flex;align-items:center;justify-content:center;padding:24px;z-index:50}.modal{background:#fff;border-radius:8px;box-shadow:var(--shadow);width:520px;max-width:100%;max-height:88vh;display:flex;flex-direction:column;overflow:hidden}.modal-wide{width:760px}.modal-header{padding:16px 20px;border-bottom:1px solid var(--line);display:flex;justify-content:space-between;align-items:center}.modal-header h3{margin:0;font-size:17px}.modal-body{padding:20px;overflow:auto}.modal-footer{padding:16px 20px;border-top:1px solid var(--line);display:flex;justify-content:flex-end;gap:10px}.icon-btn{border:0;background:transparent;font-size:20px;line-height:1;padding:2px 6px;color:var(--muted)}.table-scroll{overflow-x:auto}.detail-section{margin-top:16px}.detail-section h4{margin:0 0 8px;font-size:14px}.how-to-fix{color:var(--accent);font-size:12px;margin-top:4px}#import-csv-text{width:100%;min-height:150px;border:1px solid #cbd4df;border-radius:8px;padding:11px 12px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}
button:disabled{opacity:.5;cursor:not-allowed}
.checkbox-label{display:flex;align-items:center;gap:8px;font-size:13px;font-weight:700;color:var(--ink);cursor:pointer}
.checkbox-label input{width:16px;height:16px}
.link-muted{border:0;background:transparent;color:var(--muted);font-weight:700;text-decoration:underline;padding:2px 4px;cursor:pointer;font-size:12px}
.link-muted:hover{color:var(--ink)}
.banner{margin-bottom:16px;display:flex;justify-content:space-between;align-items:center;gap:14px}
.banner-actions{display:flex;align-items:center;gap:8px;flex:none}
/* TP-55 D1：备份告警里的「现在该做什么」。告警只说「坏了」没用，要说下一步怎么办。 */
.backup-freshness-advice{margin:6px 0 0;padding-left:20px;line-height:1.6}
.notice-warning{border-color:#fde68a;background:#fffbeb;color:var(--amber)}
.progress-track{width:100%;height:14px;border-radius:999px;background:#e6ebf1;overflow:hidden}
.progress-fill{height:100%;border-radius:999px}
.progress-fill.green{background:var(--green)}
.progress-fill.amber{background:var(--amber)}
.progress-fill.red{background:var(--red)}
.progress-meta{display:flex;justify-content:space-between;margin-top:8px;font-size:13px;color:var(--muted)}
.plan-price{font-size:22px;font-weight:850;color:var(--ink);white-space:nowrap}
.plan-table td{vertical-align:top}
.plan-table td:last-child{white-space:nowrap}
.table-row-current{background:#f4fbf9}
.table-row-current td:first-child{box-shadow:inset 3px 0 0 var(--accent)}
.overage-panel{border:1px solid var(--line);border-radius:8px;padding:14px;margin-top:14px;background:#fafbfc}
.overage-panel-head{display:flex;justify-content:space-between;align-items:flex-start;gap:14px}
.switch{position:relative;display:inline-block;width:42px;height:24px;flex:none}
.switch input{opacity:0;width:0;height:0}
.switch-slider{position:absolute;cursor:pointer;inset:0;background:#cbd4df;border-radius:999px;transition:background .15s}
.switch-slider:before{content:"";position:absolute;height:18px;width:18px;left:3px;top:3px;background:#fff;border-radius:999px;transition:transform .15s;box-shadow:0 1px 2px rgba(0,0,0,.25)}
.switch input:checked+.switch-slider{background:var(--accent)}
.switch input:checked+.switch-slider:before{transform:translateX(18px)}
.switch input:disabled+.switch-slider{opacity:.5;cursor:not-allowed}
.plan-unit-row{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap}
.plan-unit-was{color:var(--muted);text-decoration:line-through;font-size:12px;margin-top:3px}
.plan-unit-now{font-size:26px;font-weight:850;color:var(--accent);line-height:1.15}
.plan-unit-save{font-size:12px;font-weight:700;color:var(--green);background:#dcfce7;border-radius:999px;padding:2px 8px;white-space:nowrap}
.plan-monthly-fee{font-size:13px;font-weight:700;color:var(--muted);margin-top:3px}
.plan-table-footnote{margin-top:12px;font-size:12px}
.asset-card{gap:8px}
.asset-thumb{width:100%;aspect-ratio:1;border-radius:6px;background:#eef1f5;display:block}
/* TP-43 D6：视频/音频素材的缩略图位。原来这里是一句写死的通用文案，9 个实拍视频长得一模一样。
   现在视频位真的画首帧（和 TP-38 的成片封面同一招：<video preload=metadata #t=0.1>），
   首帧之下永远压一条**这个素材自己的**名字 + 体积/时长——首帧解不出来时它是唯一的区分依据。 */
.asset-thumb-placeholder,.asset-thumb-video{position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;color:var(--muted);font-size:13px;text-align:center;padding:0;overflow:hidden}
.asset-thumb-video{background:#0f172a}
/* 🔴 2026-09-13：视频首帧和商品图一样，`cover` 会把竖拍视频裁成中间一条带。
   和素材库 / 素材上传页统一成 `contain`——首帧是用来认"这是哪条视频"的，裁掉就认不出来了。 */
.asset-thumb-frame{position:absolute;inset:0;width:100%;height:100%;object-fit:contain;background:#0f172a}
.asset-thumb-icon{font-size:22px;line-height:1;margin-bottom:6px}
.asset-thumb-caption{position:relative;z-index:1;align-self:stretch;margin-top:auto;padding:6px 8px;display:grid;gap:2px;background:linear-gradient(rgba(15,23,42,0),rgba(15,23,42,.82));color:#f8fafc}
.asset-thumb-placeholder .asset-thumb-caption{background:none;color:var(--muted)}
.asset-thumb-name{font-weight:750;font-size:12px;line-height:1.3;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.asset-thumb-meta{font-size:11px;opacity:.85}
.metric-value-text{font-size:18px;font-weight:750;line-height:1.35}
.nowrap{white-space:nowrap}
/* TP-211：变淡落在各行内容上而不是整张卡——右上「更多 ▾」的浮层菜单不能跟着淡（opacity 会连子元素一起压）。 */
.voice-card-disabled{background:#f8fafc;cursor:not-allowed}
.voice-card-disabled>*:not(.voice-card-head),.voice-card-disabled>.voice-card-head>h4{opacity:.6}
.voice-disabled-reason{margin:0;color:var(--amber);font-size:12px;font-weight:700}
.subtitle-box{position:absolute;box-sizing:border-box;display:flex;align-items:center;justify-content:center;text-align:center;padding:2px 4px;overflow:hidden;background:rgba(15,23,42,.6);border:1px solid rgba(255,255,255,.9);border-radius:6px;color:#fff;font-size:10px;line-height:1.25}

/* ---------- TP-29：生成向导改版（用途卡 / 档位卡 / 素材分类与质检 / 促销角度折叠组） ---------- */
.purpose-card p,.tier-card p{margin:0;color:var(--muted);line-height:1.45;font-size:13px}
.purpose-card-disabled,.tier-card-disabled{opacity:.55;background:#f8fafc;cursor:not-allowed}
.purpose-card:not(.purpose-card-disabled),.tier-card:not(.tier-card-disabled),.subtitle-preset-card:not(.tier-card-disabled),.generate-asset-card:not(.asset-blocked){cursor:pointer}
/* ---------- TP-39：字幕样式预设卡 ---------- *
 * 预览底色刻意用深灰而不是白：字幕预设几乎都是白字 + 深描边，压在白底上等于什么都看不见。
 * 深底更接近真实成片（字幕压在商品图上），但**只还原字体和颜色**——字号不按比例缩，
 * 卡片下方另写一句「成片里更大」，绝不给客户一个和成片不一样的"精确预览"。 */
.subtitle-preset-card p{margin:0;color:var(--muted);line-height:1.45;font-size:13px}
/* TP-43 D1：预览要还原整套效果，所以字号/描边/色块底全部由内联样式（shared 的
   subtitlePreviewHtml 算出来的）决定，这里只留容器和「每行一块」需要的排版。
   ⚠️ 别在这里给 .subtitle-swatch-line 写死 font-size —— 那会把「几个预设的大小差别」
   这件事覆盖掉，正好回到 TP-39 那版「几个预设看着一样」的毛病。
   高度给一个下限，是为了让字号不同的卡片高度对齐、方便横向比较。 */
/* 🔴 2026-09-13：样张从两行压到一行（Boss：「每种只要一行字」），这个盒子原来的
   min-height:104px 是按两行留的，现在空着一大半。压到 56px。
   🔴 不能再往下压：泰文有上下叠的元音和声调符号，实测最高的一档（亲切手写）墨迹高 26.9px，
   56px 给它上下各留约 14px 余量。verify 里有一条判据按**字体实际墨迹**逐档量这个余量
   （canvas 的 actualBoundingBoxAscent/Descent + 半行距算出基线），不是拍的数。 */
.subtitle-swatch{margin-top:8px;padding:10px 12px;border-radius:8px;background:#2b3440;text-align:center;overflow:hidden;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;min-height:56px}
/* ⚠️ 这里也不许写死 line-height —— TP-45 D6 把泰文行高定成了样式规格的一部分
   （1.12~1.18），它和成片读的是同一个数，写死会让预览的行距和成片对不上。
   行高偏小时泰文上方的声调符号会溢出行盒，所以容器留了 padding、不能再夹紧。 */
.subtitle-swatch-line{white-space:nowrap;display:inline-block;max-width:100%}
.asset-blocked{opacity:.5;background:#f8fafc;cursor:not-allowed}
.asset-tag-row{display:flex;gap:6px;flex-wrap:wrap}
.asset-quality-warn{margin:0;color:var(--amber);font-size:12px;line-height:1.5}
.asset-quality-ok{margin:0;color:var(--green);font-size:12px;font-weight:700}
.asset-classify{display:grid;gap:4px}
.asset-classify-label{color:#465162;font-weight:750}
.asset-classify p{margin:0}
.promo-group{border:1px solid var(--line);border-radius:8px;background:#fafbfc}
.promo-group>summary{cursor:pointer;padding:12px 14px;font-size:14px;list-style:none}
.promo-group>summary::-webkit-details-marker{display:none}
.promo-group>summary::before{content:"▸ ";color:var(--muted)}
.promo-group[open]>summary::before{content:"▾ "}
.promo-group-body{padding:0 14px 14px}
.shot-structure{display:flex;gap:6px;flex-wrap:wrap;align-items:center}
.shot-arrow{color:var(--muted);font-size:12px}
.video-attrs{display:flex;gap:6px;flex-wrap:wrap;align-items:center}
.reference-risk{line-height:1.55}
.tier-grid{display:grid;gap:16px;grid-template-columns:repeat(auto-fit,minmax(200px,1fr))}
.tier-price{margin:0;font-size:12px;color:var(--ink);line-height:1.5}
.asset-not-recommended{border-color:#fde68a;background:#fffbeb}
.asset-not-recommended.selected{border-color:var(--amber);box-shadow:0 0 0 3px rgba(180,83,9,.12)}

/* ---------- TP-38 D1：视频资产卡瘦身（1011px → 194px，一屏从 1.5 条变成 8 条） ----------
 *
 * 🔴 卡片高度 / 封面高度 / 列数 / 间距**没有一个数字写在这里**，全部读
 * `--video-*` 自定义属性，而那几个属性由 app.js 从 `src/shared/video-card.js` 的
 * VIDEO_LIST_LAYOUT 写进 :root。这样「首屏能看几条」那条自动检查算的，
 * 就是浏览器真正用的那几个数——两处各写一个数字必然有一处会过期。 */
/* 🔴 2026-09-13 第 3 批（PM 裁决）：同排卡片等高，按钮贴底。
   收之前 1440 宽下同排高差 41 / 65px，按钮离卡底 86px vs 127px——
   同一个「下载」在一行里高低错开，眼睛要重新找一次。
   不用 masonry：这一页是按时间倒序读的，masonry 会把顺序打乱，而顺序在这一页有意义。
   做法是 align-items:stretch 让同排等高，再让 .video-card-summary 撑开、
   配合 .video-card-actions 上已有的 margin-top:auto——
   于是多出来的空白落在**正文和按钮之间**（卡内），而不是堆在按钮底下。 */
.video-list{display:grid;gap:var(--video-card-gap);grid-template-columns:repeat(var(--video-card-columns),minmax(0,1fr));align-items:stretch}
.card.video-card{padding:12px;gap:0;align-content:start}
.video-card-summary{display:flex;gap:12px;min-height:var(--video-cover-height);align-items:stretch;flex:1 1 auto}
.video-cover-slot{flex:none;width:calc(var(--video-cover-height) * 9 / 16);height:var(--video-cover-height);border-radius:6px;background:#0b1220;position:relative;overflow:hidden;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;cursor:pointer}
.video-cover-frame{width:100%;height:100%;object-fit:cover;display:block;background:#000}
.video-cover-play{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width:32px;height:32px;border-radius:999px;background:rgba(15,23,42,.62);color:#fff;display:grid;place-items:center;font-size:13px;pointer-events:none}
/* 抽不到帧的四种情况（文件丢了 / 已清理 / 还没生成 / 生成失败）各说各的话，
 * 绝不回落成一张看起来正常的假缩略图——那正是 TP-35 修掉的那个「看起来正常」。 */
.video-cover-fallback{cursor:default;padding:8px;text-align:center;font-size:12px;font-weight:700;line-height:1.4}
.video-cover-muted{background:#eef1f5;color:var(--muted)}
.video-cover-error{background:#fef2f2;color:var(--red)}
.video-cover-sub{font-weight:400;font-size:11px;opacity:.9}
.video-card-main{min-width:0;flex:1;display:flex;flex-direction:column;gap:6px}
.video-card h4.video-card-title{margin:0;font-size:15px}
.video-card .video-card-sub{margin:0;font-size:12px;line-height:1.6;display:flex;align-items:center;gap:6px;flex-wrap:wrap}
.video-card-actions{display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-top:auto}
.video-card-actions button,.video-card-actions .button-link{padding:6px 10px;font-size:13px}
.video-card .video-card-step{margin:10px 0 0;font-size:12px}
.video-card-notice{margin-top:10px;font-size:12px;line-height:1.55}
.video-card-detail{margin-top:12px;padding-top:12px;border-top:1px solid var(--line);display:flex;gap:14px;align-items:flex-start}
.video-card-detail-player{width:200px;flex:none}
.video-card-detail-text{min-width:0;flex:1}
.video-card-detail-text h5{margin:0 0 6px;font-size:13px}
.video-card .video-script-full{margin:0;font-size:13px;line-height:1.65;color:var(--ink);white-space:pre-wrap;overflow-wrap:anywhere}
/* 删除是不可逆操作，按钮要看得出来是红线，但不做成主按钮（免得比「下载」还抢眼）。 */
button.danger{border-color:#fecaca;color:var(--red)}
button.danger:hover:not(:disabled){background:#fef2f2}
.modal-footer button.danger{background:var(--red);border-color:var(--red);color:#fff}
@media(max-width:1100px){.video-list{grid-template-columns:1fr}}
@media(max-width:760px){.video-card-summary{flex-direction:column}.video-cover-slot{width:100%;height:auto;aspect-ratio:16/9}.video-card-detail{flex-direction:column}.video-card-detail-player{width:100%;max-width:240px}}
/* ---------- TP-38 D3：素材卡同样偏高（实测 548~722px / 张，首屏一张完整的都看不到） ----------
 * 主因和视频卡是同一个：一张按列宽等比撑开的方图（cols-3 下一张缩略图就有 ~370px 高）。
 * 修法也一样——**给缩略图一个固定高度**，信息一条不少（分类下拉、质检结论、授权状态标签、
 * 参考视频风险提示全部原样保留），只是不再让一张图独占大半屏。 */
/* ══════════════════════════════════════════════════════════════════════════
 * 🔴 Boss 2026-09-13 点名：素材上传页要和「电商图片工作台 · 素材库」是同一套视觉。
 * ══════════════════════════════════════════════════════════════════════════
 * 他看到的两件事：这一页的列表被横向拉长、风格和素材库对不上。
 * 量出来的根因有三个，都在这一段修：
 *
 * ① **`object-fit:cover`**：框是 2.568:1 的横条，而客户的商品图多是 0.75:1 的竖图，
 *    于是只看得见 **29.2%**——竖图被裁成中间一条横带，客户认不出哪张是哪张。
 *    素材库那边用的是 `contain`，整张图都在。
 * ② 卡片是**一列一张、全宽**，于是一张卡横着铺满整行；素材库是 170px 起的自适应网格。
 * ③ 同一行的卡按钮数不同（2 个 vs 3 个）导致**不等高**，底下留出 105px 空白——
 *    素材库用 `align-items:start` + 小号次按钮，没有这个问题。
 *
 * 🔴 只换承载形式，一个字都没删：授权状态、质检结论、分类把握度、「不花任何额度」
 * 全都还在（判据和扣费口径只准换形式，不准删）。
 * 🔴 「删除」保持红色描边：它删的是**已入库资产**，和「丢掉」（丢未确认草稿）不是一个档。
 * ══════════════════════════════════════════════════════════════════════════ */
.asset-thumb{height:var(--asset-thumb-size);aspect-ratio:auto;object-fit:contain;background:#fff;border:1px solid #e3eaf2}
/* 🔴 2026-09-13 Boss：「请以素材库的列表显示样式为准」。两页原来都已经是
   height:132px + object-fit:contain 了，但**列宽不一样**——素材库 minmax(170px)、
   这里 minmax(212px)：同样的高、更宽的框，画面比例就对不上。改成和素材库同一个数。
   （2026-09-13 第二轮之后工作台那份列表没有了，只剩这一处。） */
.materials-asset-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(170px,1fr));gap:12px;align-items:start}
.materials-asset-grid>.card{padding:10px;gap:9px;min-width:0;align-self:start}
.materials-asset-grid>.card h4{font-size:14px;line-height:1.35;overflow-wrap:anywhere}
.materials-asset-grid>.card .asset-thumb-slot{width:100%}
/* 次按钮收成一行小号按钮，不再是一摞全宽大按钮（这是卡高 585~631px 的主因之一）。 */
.materials-asset-actions{display:flex;gap:8px;flex-wrap:wrap}
/**
 * 🔴 卡里的**每一个**按钮都收成小号（不只是「处理图片 / 删除」那两个——
 * 分类块里的「重新判定」、语音样本那两个也在里面）。这是"一摞大按钮把卡撑高、
 * 同一行卡片不等高"的那一半。
 *
 * 🔴 这里**没有** `align-self:flex-start`：第一版写了，变异测试发现把它改成 `stretch`
 * 屏幕上什么都不变——那几个按钮的父级是 `.materials-asset-actions`（flex 行）和
 * `.asset-classify`（grid），`align-self` 在那儿管的是高度不是宽度。
 * 它是一条自己不生效的规则，留着只会让下一个人以为宽度是靠它控住的。
 */
.materials-asset-grid .card button{
  min-height:var(--btn-sm-h);padding:var(--btn-sm-pad);font-size:var(--btn-sm-fs)}
.materials-asset-grid .card .asset-thumb-open{min-height:0;padding:0}
/* 2026-09-15 Boss 报「ⓘ 外框被拉长」：上面那条给格子里所有 button 加的 min-height/padding 也套到了 ⓘ 上，ⓘ 必须保持 18×18 */
.materials-asset-grid .card .info-hint-toggle{min-height:auto;height:18px;width:18px;padding:0;font-size:12px}
/* 判定说明折叠：**结论平铺、理由收起**。收起的是"为什么"，不是"判成了什么"。 */
/* TP-209：「这是怎么判的？」/「是哪几条？」改成弹窗入口（原来是 <details> 就地展开）。 */
.asset-why{margin-top:2px;font-size:12px}
.asset-why-link{padding:0;font-size:12px}
/* ---------- TP-72 D1：确认关的「原图 / 加工后」比对位 ----------
 * 🔴 这一栏存在的唯一理由是让客户拿原图和加工后比对，所以两边必须是**同一套渲染**：
 * 同一个盒子、同一个 object-fit:contain、同一个高度，整张图一个像素都不裁。
 * 它刻意**不复用上面那个 .asset-thumb** —— 那个类在素材网格里是 132px + cover
 *（TP-38 D3 为压卡片高度定的，在那个场景里是对的）：Boss 那张 2048×2048 的方图
 * 在 ~300×132 的盒子里按 cover 缩放后高度是 300px，只有中间 44% 露在外面，
 * 看起来就成了一条 3:1 的横带。contain 之下同一张图缩成 132×132，整张都在。 */
.image-compare-img{width:100%;height:300px;object-fit:contain;background:#0f172a;border-radius:6px;display:block}
.image-compare-missing{display:flex;align-items:center;justify-content:center;color:#e2e8f0;font-size:13px;text-align:center;padding:12px}
/* ---------- 主图工作台：同一行里的图必须一样大 ----------
 * Boss 2026-09-10：「排版不整齐，有大有小，非常不整齐——参考"视频资产"页面调整」。
 * 根因和 TP-38 D1（视频卡）、TP-72 D1（比对位）是同一个：草稿卡上的 <img> 只写了
 * `width:100%`，高度就由图**自己的长宽比**决定——9:16 那张（1881×3344）在半栏宽里
 * 撑出 ~600px 高，1:1 那张（3342²）是方的，815² 的又矮一截，一行三张三个高度。
 * 修法沿用 `.image-compare-img` 那一条：**同一个盒子、同一个高度、object-fit:contain**。
 * 🔴 只能 contain，不能 cover：这一屏是客户在验收**他自己的主图**，
 *    裁掉一个像素都是在骗他（`.asset-thumb` 那个 cover 是素材网格的口径，不能挪过来）。
 * 🔴 底色白，不是 `.image-compare-img` 的 #0f172a：白底主图摆进深色盒子里，
 *    商品四周那圈白边会被衬得像"多出来一圈白"，等于引着他去挑一个不存在的毛病。 */
.studio-figure{width:100%;height:260px;object-fit:contain;background:#fff;border:1px solid var(--line);border-radius:8px;display:block}
/* 光把高度钉住还不够：竖版图（9:16）在 656px 宽的卡里按 contain 只占中间 146px，
 * 两边各留一大片空白，看起来仍然是"没排齐"。Boss 说的「参考视频资产页面」指的就是那一页的
 * 形状——**左边一个固定尺寸的图槽，右边是信息**（`.video-card-summary` + `.video-cover-slot`）。
 * 这里照那个形状做，但用 grid 而不是 flex：卡片里的说明有七八段、按钮有三四个，
 * 用 flex 就得再包一层 DOM 才能把它们收进右栏；grid 只要一句「除图之外都进第 2 列」。
 * 🔴 图槽跨满所有行（`grid-row:1/span 50`）：不跨的话它只占第一行，
 *    下面那些说明会绕到图的正下方去，又变回上下堆叠。 */
.studio-card{display:grid;grid-template-columns:220px minmax(0,1fr);gap:9px 14px;align-content:start}
.studio-card > *{grid-column:2;min-width:0}
.studio-card > .studio-figure{grid-column:1;grid-row:1/span 50;align-self:start}
/* 🔴 TP-151：棚拍增强一次交付**两张**成品（白底 + 透明底），所以图槽里要摆得下两张。
 * 做法是给图槽包一层，**图本身仍然是 `.studio-figure`**——不这样的话两张图都会去抢
 * `grid-row:1/span 50` 那一格，直接叠在一起。Boss 刚投诉过"排版有大有小"，
 * 所以新加的图一律走同一个盒子、同一个高度，不许再写内联的 `width:100%`。 */
/* 透明底那一张摆在纯白盒子里等于看不见（白瓶子 + 白底 = 一片白）。棋盘格是这件事的
 * 通用画法，而且它同时告诉客户「这张图的背景是空的，不是白的」——那正是它的卖点。 */
.studio-figure-alpha{background-color:#fff;background-image:linear-gradient(45deg,#e6e8ec 25%,transparent 25%),linear-gradient(-45deg,#e6e8ec 25%,transparent 25%),linear-gradient(45deg,transparent 75%,#e6e8ec 75%),linear-gradient(-45deg,transparent 75%,#e6e8ec 75%);background-size:16px 16px;background-position:0 0,0 8px,8px -8px,-8px 0}
/* 窄屏回落成上下堆叠：220px 图槽 + 说明在手机上会把右栏挤成一条。 */
@media(max-width:1100px){.studio-card{grid-template-columns:minmax(0,1fr)}.studio-card > *{grid-column:1}.studio-card > .studio-figure,.studio-card > .studio-figure-pair{grid-row:auto}}
/* ---------- TP-73 D1：图片加工面板装在**当前窗口内的弹窗**里 ----------
 * Boss 2026-09-05：「直接在每个素材下方加一个按钮，点击在当前窗口弹窗进入图片增强
 * 操作页面，而不是在整个素材下面展开」。TP-72 D3 那版「就地展开」是 PM 理解错了。
 *
 * 弹窗要满足的几件事，逐条对应下面一行：
 * - 遮罩 + position:fixed 由既有的 .modal-overlay 提供（和全站别的弹窗同一套）；
 * - 🔴 **高度要够用**：这一屏内容很长（6 个档位 + 确认关 + 1:1 原大对照），
 *   .modal 默认的 88vh 上限在这里不够体感，min-height 兜住「别在一个矮框里滚半天」；
 * - 🔴 **弹窗内部自己滚，页面背景不跟着滚**：overscroll-behavior:contain 挡住滚动链——
 *   没有它的话，弹窗里滚到底之后滚轮会继续往下滚背景页面，客户关掉弹窗发现自己
 *   已经不知道被滚到哪儿去了。
 *   🔴 TP-201（PM 收尾）：**不再有 min-height，高度随内容**——减字之后默认态只剩六张卡 + 一行风险，
 *   固定 760 会在卡片区和底栏之间留约 150px 空白；展开「了解详情」/ 确认关出来时它跟着内容长，
 *   上限仍是 92vh，超出在弹窗内部滚（tp73 那条「高度要够用」判的现在是：要滚的时候框才必须够高）。 */
.modal-image-op{width:min(1040px,100%);max-height:92vh}
.modal-image-op .image-op-panel{display:flex;flex-direction:column;flex:1 1 auto;min-height:0}
.modal-image-op .image-op-panel>.modal-body{flex:1 1 auto;min-height:0;overflow:auto;overscroll-behavior:contain}
.image-op-panel .detail-section:first-of-type{margin-top:10px}
.image-op-panel-actions{display:flex;gap:8px;flex-wrap:wrap;justify-content:flex-end}
/* 🔴 TP-74 D1：只属于某一档的输入框，长在那一档的卡里面。
 * 它是卡的一部分（上面一条分隔线把它和档位说明分开），客户一眼就看得出
 * 「我填的这一句是给这张卡的」——以前这一段排在整个档位网格外面，
 * 永远贴在最左边那张卡下面，选中的却可能是中间那一张。 */
.tier-card-input{border-top:1px dashed var(--line);margin-top:2px;padding-top:10px;display:grid;gap:8px;cursor:auto}
.tier-card-input h5{margin:0;font-size:13px;font-weight:750}
.tier-card-input label{display:grid;gap:4px;font-size:12px;color:#465162;font-weight:700}
.tier-card-input input,.tier-card-input textarea{width:100%;border:1px solid #cbd4df;border-radius:8px;padding:9px 10px;background:#fff}
.tier-card-input textarea{resize:vertical;line-height:1.5}
.tier-card-input .grid{gap:8px}
/* 🔴 TP-73 D2：生成第 2 步每张图片素材下面那个「处理图片」。
 * 它长在 .generate-asset-card **外面**——那张卡整个就是「选中/取消选中」的开关，
 * 按钮塞进卡里的话点它会先被卡接住（assetCardClickIntent 由内向外先命中的赢），
 * 客户点「处理图片」会顺手把这个素材选进/踢出这次生成。在外面就结构上不可能。 */
.generate-asset-slot{display:flex;flex-direction:column;gap:8px;min-width:0}
/* ---------- 🔴 TP-74 D2：生成页的分步向导 ----------
 * 步骤指示是一排**真按钮**（不是一排装饰性的小圆点）：已完成的那几步点得下去，
 * 够不着的那几步 disabled 并且各自印着「要先选完第 N 步」——
 * 一个不说原因的灰按钮，客户只会以为页面坏了。 */
.wizard-steps{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px}
.wizard-step{display:flex;min-width:0}
/* 主图工作台顶上那条四步进度条**不是按钮**（那一页没有"跳到第 3 步"这个动作，
   它只是在说现在选到哪儿了），但它要和生成页那条长一模一样——所以共用同一条规则，
   不另抄一份：抄一份的那天两页的进度条就会开始各长各的。 */
.wizard-step>button,.wizard-step>.wizard-step-face{flex:1 1 auto;display:grid;grid-template-columns:26px minmax(0,1fr);grid-template-areas:"idx title" "idx state";gap:2px 10px;align-items:center;text-align:left;padding:10px 12px;border:1px solid var(--line);border-radius:8px;background:#fff;font-weight:600}
.wizard-step-index{grid-area:idx;display:grid;place-items:center;width:26px;height:26px;border-radius:999px;background:#e2e8f0;color:#475569;font-weight:800;font-size:13px}
.wizard-step-title{grid-area:title;font-size:13px;font-weight:750;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.wizard-step-state{grid-area:state;font-size:11px;color:var(--muted);font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.wizard-step.is-done>button{border-color:#bbf7d0;background:#f0fdf4}
.wizard-step.is-done .wizard-step-index{background:#dcfce7;color:var(--green)}
.wizard-step.is-current>button{border-color:var(--accent);background:#f4fbf9;box-shadow:0 0 0 3px rgba(15,118,110,.1)}
.wizard-step.is-current .wizard-step-index{background:var(--accent);color:#fff}
.wizard-step.is-locked>button{opacity:.6;cursor:not-allowed;background:#f8fafc}
.wizard-progress{margin:12px 0 0;font-size:13px;line-height:1.5}
.wizard-reset-notice{margin-top:12px}
.wizard-nav{display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap}
.wizard-nav-reason{margin-bottom:12px}
.generate-asset-slot>.card{flex:1 1 auto}
.enhance-row{display:grid;gap:0}
/* 折叠态卡片的外框高度同样读预算，别让 padding/边框的改动把「一屏 8 条」悄悄变成谎话。 */
.card.video-card{min-height:var(--video-card-height)}
/* ---------- TP-50 D1：说明收进「?」 ----------
   浮层用绝对定位挂在问号旁边，**不占文档流**——收起来之后那一节的高度就真的少了那几行，
   这正是本轮要的效果（Boss：「生成视频页面太长了」）。
   ⚠️ 正文默认根本不在 HTML 里（见 /shared/generate-page-copy.js 的 infoHintHtml），
   这里的 [hidden] 只是展开态切回收起态时的兜底，不是「默认不可见」的实现方式。 */
.info-hint{position:relative;display:inline-block;vertical-align:middle;margin-left:6px;font-weight:400}
/* Boss 2026-09-28：全站「看说明」只有一个样子——Lucide 的圆圈 i（INFO_HINT_ICON），以前是灰圈「?」/ 斜体「i」两种。
   圈是图标自己画的，按钮本身不再带边框和底色；图标撑满按钮（18 / 16 两档尺寸由各处的 width/height 决定）。 */
.info-hint-toggle{width:18px;height:18px;padding:0;border-radius:999px;border:0;background:transparent;color:var(--muted);line-height:1;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;vertical-align:middle}
.info-hint-toggle .ui-icon{width:100%;height:100%}
.info-hint-toggle:hover,.info-hint-toggle[aria-expanded="true"]{background:#e8efff;color:var(--accent)}
.info-hint-toggle:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.info-hint-body{position:absolute;z-index:40;top:calc(100% + 8px);left:0;width:320px;max-width:min(320px,calc(100vw - 48px));display:block;background:#fff;border:1px solid var(--line);border-radius:8px;box-shadow:var(--shadow);padding:12px;font-size:13px;line-height:1.55;font-weight:400;color:var(--ink);white-space:normal;text-align:left}
.info-hint-body[hidden]{display:none}
.info-hint-line{display:block;margin-top:6px}
/* ---------- TP-50 D3/D4：看大图 / 预览视频 ----------
   角标按钮压在缩略图右上角。它是**卡里更内层的一个目标**，所以点它由 assetCardClickIntent
   判成 preview——「看大图」和「选中素材」因此不会互相误触发。 */
.asset-thumb-slot{position:relative}
.media-preview-btn{position:absolute;top:6px;right:6px;z-index:2;width:26px;height:26px;padding:0;border-radius:6px;border:1px solid rgba(255,255,255,.5);background:rgba(15,23,42,.62);color:#fff;font-size:13px;font-weight:800;line-height:1;display:inline-flex;align-items:center;justify-content:center;cursor:pointer}
.media-preview-btn:hover{background:rgba(15,23,42,.86)}
.media-preview-btn:focus-visible{outline:2px solid #fff;outline-offset:1px}
.media-preview-modal{width:min(560px,100%)}
.media-preview-body{display:flex;align-items:center;justify-content:center;background:#0f172a}
.media-preview-image{max-width:100%;max-height:70vh;object-fit:contain;display:block}
.media-preview-video{width:100%;max-height:70vh;background:#000;display:block}
.media-preview-audio{width:100%}
.media-preview-unavailable{width:100%;margin:0}

/* TP-54 D3：第 3 步「结构从哪来」的来源选择，和 D2 的逐镜头拍摄脚本表。 */
.structure-source .radio-row{display:flex;align-items:flex-start;gap:8px;font-size:13px;color:var(--ink);cursor:pointer;line-height:1.6}
.structure-source .radio-row input{margin-top:3px;width:16px;height:16px;flex:none}
.structure-source .radio-row input:disabled+span{opacity:.55;cursor:not-allowed}
.shooting-script-table{width:100%;border-collapse:collapse;font-size:12px}
.shooting-script-table th,.shooting-script-table td{border:1px solid var(--line);padding:6px 8px;text-align:left;vertical-align:top}
.shooting-script-table th{background:#fafbfc;font-weight:700;white-space:nowrap}
.shooting-script-table td:nth-child(1){width:44px;text-align:center}
.shooting-script-table td:nth-child(2){white-space:nowrap;width:96px}
/* 🔴 TP-61 D2：一个动作**做完之后**的可见反馈。Boss 实测「点了重新生成看不出在干活」——
   后端 14 毫秒就回来了，「正在…」一闪而过，完成之后又什么都不说。这两条是那句话的解药：
   结果条本身（绿），加上卡片描一圈边——一眼就能认出「刚才动的是这一张」。
   刻意不做自动消失的动画：用户抬眼看的那一刻它得还在。 */
.notice-done{border-color:#bbf7d0;background:#f0fdf4;color:var(--green)}
/* ---------- 🔴 TP-75 D1：等高卡片不许把多出来的高度塞给按钮 ----------
 *
 * Boss 2026-09-05：「页面按钮被拉长，变形。」实测第 3 步同一行的两张话术卡：
 * 内容矮的那张被网格拉到 943px（同行有一张带红字说明的高卡），卡里最后一行是按钮行，
 * 按钮被撑到 83px，旁边那张同样的按钮只有 43px。
 *
 * 链条是三段，缺一段都不会变形：
 *   ① 卡片网格 align-items:normal(=stretch) → 同一行的卡被拉成等高（**这一段是对的，不动**：
 *      一行卡参差不齐更难看，PM 明确禁止用「取消等高」来糊弄）；
 *   ② `.card` 原来是 `display:grid` → align-content 默认 normal(=stretch)，多出来的高度被
 *      **平摊给每一个 auto 行**（943-303=640，八行左右，每行各涨 80——正好是实测的 93→173）；
 *   ③ 按钮行 display:flex + align-items:normal(=stretch) → 按钮被拉到那一行的行高。
 *
 * 修法只动第 ② 段：`.card` 改成 **flex 列容器**。flex 的主轴（这里是纵向）上，
 * 弹性项目**不会被拉伸**（flex-grow 默认 0），所以每一行都保持自然高度，
 * 多出来的空间原样留在容器里；再给按钮行一句 `margin-top:auto` 把它推到卡片底边——
 * 于是「多出来的空间在按钮上面、按钮贴着卡底」，正是等高卡片该有的样子。
 * grid 做不到这一点：grid 的 auto margin 只吃**自己那格**里的空隙，
 * 而 `align-content:start` 之下那一格根本没有空隙，按钮只会停在内容正下方、卡底空一大片。
 *
 * 🔴 两条明确不许走的路（PM 在 TP-75 里点名）：
 * - 不许改成「卡片不等高」；
 * - 不许写全局 `button{align-self:start}`：在**竖向**排列的容器里 align-self 管的是横向，
 *   会把本来占满整行的按钮（比如素材卡里的「删除」、弹窗底部那些）缩成一小块。
 *   所以修法挂在**这一类按钮行**（.card-actions）和卡片容器本身上，不挂在 button 上。
 *
 * 🔴 这里刻意**不给 .card-actions 写 align-items:center**。写了确实也能让按钮回到 43px，
 * 但那是在「卡片仍然被拉伸」的前提下把按钮单独摁住——同一张卡里的 .notice 块照样被拉高，
 * 缺陷只是被挡住了一半，而守卫（量按钮高度那条）从此再也测不出卡片有没有在乱拉伸。
 * 遮住症状的冗余等于让守卫失灵，本项目已经吃过这个亏。 */
.card-actions{display:flex;flex-wrap:wrap;gap:8px;margin-top:auto}
/* ---------- 🔴 TP-75 D2：「已选中」和「刚重新生成」必须一眼分得开 ----------
 *
 * Boss 会去点第 3 步的话术卡，正是因为看到有一张是绿框的——那个绿框是「刚刚重新生成过」，
 * 不是「已选中」。两件完全不同的事长成同一个样子，客户当然读成「当前选的是这条」。
 *
 * 现在两者不同的**不只是颜色，是形状**，而且可以同时出现在一张卡上还互不干扰：
 *   已选中     = 整圈墨绿(accent)描边 + 淡底 + 外环 + 标题里一枚实心徽章「✅ 已选中·这条出片」
 *   刚重新生成 = 只在左边一条 5px 的绿竖条，不描整圈、不改底色，配卡里那条绿色结果通知
 * 左竖条写在选中规则**后面**，两条同时命中时左边那条压得住，卡片仍然读得出「又选中又刚生成」。 */
.card.script-card-selected{border-color:var(--accent);background:#f4fbf9;box-shadow:0 0 0 3px rgba(15,118,110,.13)}
/* ⚠️ 选择器要写成 `.card p.xxx`：上面那条 `.card p{color:var(--muted)}` 的特异性是 (0,1,1)，
   只写一个类名压不过它——徽章会变成一块灰底灰字，正好又混进「看不出选没选中」那个坑。 */
.card p.script-selected-badge{margin:0;align-self:flex-start;border-radius:999px;padding:4px 11px;font-size:12px;font-weight:750;background:var(--accent);color:#fff}
.card p.script-pick-hint{margin:0;font-size:12px;font-weight:700;color:var(--accent)}
.card.script-card-pickable{cursor:pointer}
/* TP-61 D1：几个镜头共用一句时，这几行在表格里连成一块，看得出是同一句。 */
.shooting-script-table tr[data-line-span]{background:#fbfdff}

/* ---------- 🔴 TP-80 D5：登录页 ----------
 *
 * 没登录时 `.app` 整个 hidden，页面上只剩这一张卡片。刻意不是"框架照常显示、
 * 里面空着"——那种状态正是这一单点名禁止的（"页面能打开但数据是空的"）：
 * 客户会以为自己已经进来了、只是数据没加载出来，然后去点刷新。
 *
 * 卡片本身故意做得很朴素：这一页没有任何可读的业务信息，多画一点都是噪声。 */
/* 🔴 `hidden` 属性对 `.app` **不管用**——`.app{display:grid}` 的特异性压过浏览器默认的
 * `[hidden]{display:none}`。第一版就栽在这里：DOM 上 `.app.hidden === true`、
 * `#content` 也确实是空的（断言全过），但用户看到的是**整套工作台框架还在、内容空着**，
 * 登录卡片被 `min-height:100vh` 顶到了一屏之外——正好就是这一单点名禁止的那种状态。
 * 判据因此改成看**渲染结果**（computed display + 登录卡片在不在视口里），不看属性。 */
.app[hidden]{display:none!important}
.login-root{display:none}
.login-root[data-open="1"]{display:flex;align-items:center;justify-content:center;min-height:100vh;padding:24px;background:var(--bg)}
.login-card{width:100%;max-width:380px;background:#fff;border:1px solid var(--line);border-radius:8px;padding:28px 26px;box-shadow:0 10px 30px rgba(15,23,42,.07)}
/* TP-228：登录卡顶上是一张横版彩标（TP-233 起是 /brand/spark-logo.svg，viewBox 1317×560），不再重复文字。 */
.login-card .login-logo{display:block;width:220px;max-width:100%;height:auto;margin:0 0 18px}
.login-card h1{margin:0 0 4px;font-size:20px}
.login-card .login-sub{margin:0 0 18px;color:var(--muted);font-size:13px;line-height:1.6}
.login-card label{display:block;font-size:13px;font-weight:650;margin:12px 0 5px}
.login-card input{width:100%;box-sizing:border-box;padding:10px 12px;border:1px solid var(--line);border-radius:8px;font-size:14px}
.login-card button{width:100%;margin-top:18px}
/* SaaS S3b 注册表单：邮箱 + 「发送验证码」一行；卡片里按钮默认占满一行，这一行要让输入框占剩下的宽度（2026-10-05 真机验收：输入框被挤成小方块） */
.login-card .signup-email-row{display:flex;gap:8px;align-items:stretch}
.login-card .signup-email-row input{flex:1 1 auto;min-width:0;width:auto}
.login-card .signup-email-row button{width:auto;flex:0 0 auto;margin-top:0;padding:0 12px;white-space:nowrap}
/* 登录 / 注册之间的切换链接：和站内 .link-btn 同一个颜色和字重，不用浏览器默认的蓝底线 */
.login-card .login-switch-link{color:var(--blue);font-weight:700;text-decoration:none;margin-left:4px}
.login-card .login-switch-link:hover{text-decoration:underline}
/* 登录失败的那段话可能有两行（比如"账号不属于这台机器的客户"），要能整段读完。 */
.login-error{margin-top:14px;padding:10px 12px;border-radius:8px;background:#fef2f2;border:1px solid #fecaca;color:#991b1b;font-size:13px;line-height:1.65;white-space:pre-wrap}
.login-notice{margin-top:14px;padding:10px 12px;border-radius:8px;background:#f8fafc;border:1px solid var(--line);color:var(--muted);font-size:13px;line-height:1.65;white-space:pre-wrap}
/* 侧边栏里"我是谁 + 退出登录"。它占的正是原来那个租户切换下拉框的位置。 */
#session-box .session-user{display:block;font-weight:700;font-size:13px;color:#fff}
#session-box .session-meta{display:block;font-size:12px;color:var(--muted);margin:2px 0 8px;line-height:1.5}

/* ---------- 🔴 TP-85 D1：左下角的账号菜单 ----------
 * Boss 2026-09-06：「退出登录、修改密码等安全相关的按照习惯放右上角或者左下角比较好。」
 * 位置没变（TP-80 起就在左下角），这一轮把它从散着的几行文字收成一个可展开的菜单，
 * 并补上了它一直缺的那一项：修改密码。 */
#session-box{position:relative}
#session-box .session-account-toggle{width:100%;display:flex;align-items:center;justify-content:space-between;gap:8px;border:1px solid rgba(255,255,255,.18);background:rgba(255,255,255,.06);color:#fff;padding:9px 10px;border-radius:8px;text-align:left}
#session-box .session-account-toggle:hover{background:rgba(255,255,255,.12)}
#session-box .session-user{display:block;font-weight:700;font-size:13px;color:#fff;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
#session-box .session-caret{color:#9aa4b2;font-size:11px;flex:none}
#session-box .session-menu{margin-top:8px;display:grid;gap:8px;border:1px solid rgba(255,255,255,.14);border-radius:8px;padding:10px;background:rgba(255,255,255,.05)}
#session-box .session-menu-row{display:grid;gap:2px;font-size:12px;color:#9aa4b2;line-height:1.45}
#session-box .session-menu-row b{color:#e5e7eb;font-weight:700;overflow-wrap:anywhere}
#session-box .session-menu button{width:100%;background:#1f2937;border-color:rgba(255,255,255,.18);color:#f9fafb;padding:8px 10px;font-size:13px}
#session-box .session-menu button:hover{background:#374151}
/* 改密码弹窗：内容不长，用默认宽度就好；提交前那句「要求 + 改完要重新登录」在最上面。 */
.modal-password{width:460px}
.modal-password .password-panel{display:flex;flex-direction:column;min-height:0}
.modal-password .password-panel>.modal-body{overflow:auto;overscroll-behavior:contain}

/* ---------- 🔴 TP-85 D3：话术全文弹窗 ----------
 * Boss 2026-09-06：「展开看话术全文，也用弹出比较好，并且不需要视频、只看话术就行，
 * 现在的在下方弹出，页面会移动，用户体验不好。」
 * 形态和 TP-73 的图片加工弹窗同一套：遮罩由 .modal-overlay 提供，弹窗内部自己滚
 *（overscroll-behavior:contain 挡住滚动链，不然滚到底会继续滚背景页面）。
 * 🔴 这一屏里没有播放器——看片子有它自己的入口（点封面弹窗播放）。 */
.modal-video-script{width:min(720px,100%);max-height:88vh}
.modal-video-script .video-script-panel{display:flex;flex-direction:column;flex:1 1 auto;min-height:0}
.modal-video-script .video-script-panel>.modal-body{flex:1 1 auto;min-height:0;overflow:auto;overscroll-behavior:contain}
.modal-video-script .detail-section:first-of-type{margin-top:0}
.modal-video-script .video-script-full{margin:0;font-size:14px;line-height:1.8;color:var(--ink);white-space:pre-wrap;overflow-wrap:anywhere}
.video-script-copy-note{margin-right:auto;font-size:12px;color:var(--muted);align-self:center}
.video-script-copy-note.notice-error-text{color:var(--red)}

/* ---------- 🔴 TP-85 D2：视频卡上的生成时间 ---------- */
.video-card .video-card-time{margin:2px 0 0;font-size:12px}
/* TP-251：「画面标题「…」· 背景音乐：…」一行（两截是 span，行本身是 .video-card-meta-line）。 */
.video-card-meta-line{margin:4px 0 0;font-size:12px;line-height:1.6;display:flex;flex-wrap:nowrap;gap:0 2px;align-items:baseline;min-width:0;max-width:100%}
.video-card-meta-line .video-card-title-overlay{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.video-card-meta-line .video-card-bgm{flex:none;white-space:nowrap}
.video-card-meta-sep{flex:none}
/* TP-251：第 1 步「促销角度」那块蓝底说明压成一行摘要 + ⓘ（承诺半句留在行上）。 */
.hook-promo-line{display:flex;align-items:center;flex-wrap:wrap;gap:0 2px;font-size:13px;line-height:1.5;margin:0 0 2px}
.hook-promo-line .hook-promo-note{color:var(--muted)}
.hook-promo-line.is-warn .hook-promo-note{color:var(--amber)}
.opening-variant-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}
.opening-variant-option{min-height:82px;padding:12px;text-align:left;display:flex;flex-direction:column;align-items:flex-start;justify-content:flex-start;gap:5px;background:var(--surface,#fff)}
.opening-variant-option span{font-size:12px;line-height:1.5;color:var(--muted)}
.opening-variant-option .opening-variant-title{width:100%;display:flex;align-items:center;justify-content:space-between;gap:8px;color:var(--text)}
.opening-variant-title small{flex:0 0 auto;padding:2px 6px;border-radius:999px;background:#fff3df;color:#a05208;font-size:11px;font-weight:600}
.opening-variant-title small.is-unused{background:#e8f8ee;color:#167447}
.opening-batch-list{display:grid;gap:6px}
.opening-batch-row{display:grid;grid-template-columns:28px minmax(0,1fr) auto auto;gap:10px;align-items:center;padding:8px 10px;border:1px solid var(--line);border-radius:6px;background:var(--surface,#fff)}
.opening-batch-index{display:grid;place-items:center;width:24px;height:24px;border-radius:50%;background:#eef4ff;color:var(--blue);font-size:12px;font-weight:700}
.opening-batch-copy{display:grid;gap:2px;min-width:0}
.opening-batch-copy small{color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.opening-batch-state{font-size:11px;color:#a05208;background:#fff3df;border-radius:999px;padding:3px 7px;white-space:nowrap}
.opening-batch-state.is-unused{color:#167447;background:#e8f8ee}
.creative-batch-list{display:grid;gap:10px}
.creative-batch-list>button{display:flex;justify-content:space-between;gap:12px;align-items:center;width:100%;padding:12px;text-align:left;border:1px solid var(--line);border-radius:6px;background:var(--surface,#fff)}
.creative-batch-item{padding:12px;border:1px solid var(--line);border-radius:6px;background:var(--surface,#fff)}
.creative-batch-item-head{display:flex;justify-content:space-between;gap:12px;align-items:center}
.creative-batch-item-head span{font-size:12px;color:var(--muted)}
.creative-batch-item p{margin:8px 0;font-size:13px;line-height:1.5}
.creative-batch-fields{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}
.creative-batch-fields label{display:grid;gap:4px;min-width:0;font-size:12px;font-weight:600}
.creative-batch-fields input,.creative-batch-fields select{width:100%;min-width:0;height:38px;padding:6px 8px;border:1px solid var(--line);border-radius:6px;background:#fff;font-size:13px}
.creative-batch-blocker{color:#a3292a}
.creative-batch-script{white-space:pre-wrap}
@media(max-width:620px){.creative-batch-fields{grid-template-columns:1fr}.creative-batch-list>button{align-items:flex-start;flex-direction:column}}
@media(max-width:820px){.opening-variant-grid{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media(max-width:620px){.opening-variant-grid{grid-template-columns:1fr}.opening-batch-row{grid-template-columns:28px minmax(0,1fr) auto}.opening-batch-state{grid-column:2}.opening-batch-row button{grid-column:3;grid-row:1 / span 2}.opening-batch-copy small{white-space:normal}}

/* ---------- 🔴 TP-85 D4：提示里「哪条素材」和「怎么截的」分两层 ----------
 * Boss：「蓝色字体说明不清晰，『这段视频』重复几次了也不知道是哪段」。
 * 三条素材并排时，名字要能被一眼扫到，截取说明退到第二层。 */
.notice-items{margin:0;padding:0;list-style:none;display:grid;gap:8px}
.notice-items li{display:grid;gap:2px}
.notice-item-label{font-weight:750}
.notice-item-text{opacity:.9;line-height:1.55}

/* 🔴 TP-93 D3：到期那一屏上的续费二维码。
 * 图片是 data: URI（服务端把本机那张 PNG 内嵌进来），页面不发任何外部请求——
 * 零外链是硬约束，而且到期时我们可能正好联系不上云端。
 * 🔴 一张都没配时这一块整个不出现：不许留一个空的图片框（TP-90 立的口径）。 */
.renewal-codes{display:flex;gap:18px;flex-wrap:wrap;margin:12px 0 10px}
.renewal-code{margin:0;text-align:center}
.renewal-code img{display:block;width:180px;height:180px;border:1px solid var(--line);border-radius:8px;background:#fff}
.renewal-code figcaption{margin-top:6px;font-size:13px}

/* ---------- 🔴 主图工作台：Boss 2026-09-10 参考稿的三栏向导 ----------
 * 稿子上是「上传图片 / 选择类型 / 参数设置」三张卡并排。这里用 grid 三栏，
 * 窄屏按既有断点回落成一栏——和 .cols-3 同一套断点，不另立规矩。
 * 🔴 三栏 `align-items:start`：三栏内容长度天差地别（类型那栏有五张卡），
 * 不写的话短的那两栏会被拉到和最长的一样高，卡里的空白比内容还多。 */
/* ==========================================================================
 * 🔴 TP-154：电商图片工作台的分层布局（L0 顶栏 / L1 选图 / L2 类型 / L3 参数 /
 * 常驻 CTA 条 / L4 结果 / L5 历史 / 右侧抽屉）。
 *
 * 旧的 `.studio-columns` 三栏**退休**了：那一版第 2 栏单独就近 3000px、右栏空着，
 * 整页 7709px。这一轮换的是轴——每一层横着铺开、长说明进抽屉，
 * 核心操作区（顶栏 + 选图 + 类型 + 参数 + CTA）目标控制在 1440p 下一屏内。
 * ========================================================================== */
/* 🔴 TP-156：CTA 不再是 fixed 底栏（它搬进了第 3 栏），所以底部那 190px 的留白也去掉了。 */
.studio-page{display:grid;gap:14px}
/* 🔴 TP-156 第一段：三栏并排 = 标杆稿那一屏。
   第 3 栏（参数 + 生成）比前两栏宽一点：它装着参数、两句合规话和主按钮。 */
.studio-flow{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr) minmax(0,1.15fr);gap:14px;align-items:stretch}
.studio-flow>.studio-layer{margin:0}
.studio-col-params{display:flex;flex-direction:column;gap:12px}
.studio-layer{padding:14px 16px}
.studio-layer-head{display:flex;justify-content:space-between;align-items:center;gap:12px;margin-bottom:10px;flex-wrap:wrap}
.studio-layer-head h4{margin:0;font-size:14px;font-weight:800;letter-spacing:.02em}
.link-btn{border:0;background:transparent;color:var(--blue);padding:2px 4px;font-weight:700;font-size:13px;cursor:pointer}
/* 缩略图：2026-09-12 定版之后只剩「换一张素材」抽屉在用（上半区只放当前这一张，
   完整素材列表在下半区素材库）。竖向 auto-fill 网格，不是横向滚动条 ——
   `renderStudioThumb` 上那两个 loading=lazy / decoding=async 才真的只解码可视区。 */
.studio-thumbs{display:grid;grid-template-columns:repeat(auto-fill,minmax(104px,1fr));gap:10px}
.studio-thumb{display:flex;flex-direction:column;gap:4px;padding:6px;text-align:left;font-weight:600;overflow:hidden}
.studio-thumb img{width:100%;height:72px;object-fit:contain;background:#fff;border:1px solid var(--line);border-radius:6px;display:block}
.studio-thumb-name{font-size:12px;font-weight:750;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.studio-thumb-badges{font-size:11px;color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.studio-thumb.is-picked{border-color:var(--accent);background:#f4fbf9;box-shadow:0 0 0 3px rgba(15,118,110,.1)}
.studio-thumb.is-unusable{opacity:.55}
/* L2 类型卡：横排，一卡三行 */
.studio-goals{display:flex;gap:6px}
.studio-goal{padding:5px 12px;font-size:13px;border-radius:999px}
.studio-goal.is-active{background:var(--blue);border-color:var(--blue);color:#fff}
/* 「本商品素材」的筛选 chip：和 .studio-goal 同一种分段控件形态（当前段实心蓝），不是"选中了一项内容"。 */
.studio-library-filters{display:flex;gap:6px;flex-wrap:wrap;padding:12px 14px 0}
.studio-library-filter{padding:5px 12px;font-size:13px;border-radius:999px}
.studio-library-filter small{font-weight:600;opacity:.75}
.studio-library-filter.is-active{background:var(--blue);border-color:var(--blue);color:#fff}
/* 素材库页（2026-09-13 第二轮）：商品 → 图片 / 视频 / 其它 → 类别格 */
/* TP-212：商品下拉不再套 .field（要和成品库一样直接做「批量管理」开关的兄弟），原来 .field select 给的边框 / 圆角 / 宽度在这里接住；
   封顶 360px（最长的「商品名 · SKU」选项会把它撑到 470px，那样整排放不进一行）——闭合态最长那条名字截尾，展开的列表仍是全名。 */
select.materials-product-filter{min-width:260px;max-width:360px;border:1px solid #cbd4df;border-radius:8px;padding:8px 10px;background:#fff}
.materials-category-filters{padding:0 0 12px}
.materials-group{margin-top:14px}
.materials-group h4{margin:0 0 6px;font-size:14px;display:flex;gap:8px;align-items:baseline}
.materials-group h4 .muted{font-weight:600;font-size:12px}
.materials-group>summary{cursor:pointer;list-style:none}
.materials-group>summary h4{display:inline-flex}
.materials-group>summary::before{content:"▸";display:inline-block;margin-right:6px;color:var(--muted)}
.materials-group[open]>summary::before{content:"▾"}
.materials-category{border:1px solid var(--line);border-radius:8px;padding:10px 12px;margin-top:8px;background:#fff}
.materials-category.is-empty{background:#fafbfc}
.materials-category-head{display:flex;gap:10px;align-items:center;flex-wrap:wrap}
.materials-category-head h5{margin:0;font-size:13px;display:flex;gap:6px;align-items:baseline}
.materials-category-count{font-weight:600;color:var(--muted);font-size:12px}
.materials-category-head .muted{font-size:12px;flex:1 1 auto}
.materials-category-select-all{white-space:nowrap}
.materials-category .materials-asset-grid{margin-top:10px}
.asset-thumb-alpha{background:repeating-conic-gradient(#e5e7eb 0 25%,#fff 0 50%) 0 0/16px 16px;border:1px solid #e3eaf2;border-radius:6px;overflow:hidden;height:var(--asset-thumb-size)}
.asset-thumb-alpha img{width:100%;height:100%;object-fit:contain;display:block}
.upload-asset-target{margin:0 0 6px;font-size:14px}
.modal-upload-asset .upload-asset-fields .form-grid{margin-top:0}
.studio-type-row{display:grid;grid-template-columns:1fr;gap:10px}
.studio-type-card{cursor:pointer;gap:6px;border-radius:8px}
.studio-type-head{display:flex;align-items:center;gap:7px;flex-wrap:wrap;font-size:14px}
.studio-type-head input{width:16px;height:16px;flex:none;margin:0}
.studio-type-icon{font-size:16px}
.studio-type-summary{font-size:13px;line-height:1.45;margin:0}
.studio-type-deliverables{font-size:13px;line-height:1.45;margin:0;font-weight:600}
.studio-type-more{margin-top:auto}
.studio-type-card-blocked{background:#fffbeb;border-color:#fde68a}
.studio-type-blocked{font-size:12px;line-height:1.5;color:var(--amber);font-weight:700}
.studio-inline-note{margin:10px 0 0;font-size:12px}
.studio-transparent{display:grid;gap:12px}
.studio-transparent-choices{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:10px}
.studio-transparent-choices button{text-align:left;display:grid;gap:4px;padding:12px 14px}
.studio-transparent-choices small{font-weight:600;opacity:.85}
/* L3 参数 */
.studio-params{display:grid;grid-template-columns:1fr;gap:12px;align-items:start}
.studio-params .detail-section{margin-top:0}
.studio-fact-list{display:grid;grid-template-columns:64px minmax(0,1fr);gap:4px 10px;margin:0;font-size:13px}
.studio-fact-list dt{color:var(--muted);font-weight:750}
.studio-fact-list dd{margin:0}
.studio-scene-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:8px}
.studio-scene-card{display:grid;gap:2px;border:1px solid var(--line);border-radius:8px;padding:8px 10px;cursor:pointer;font-size:13px}
.studio-scene-card input{margin:0 6px 0 0}
.studio-scene-card small{font-size:11px;line-height:1.4}
.studio-scene-card.is-picked{border-color:var(--accent);background:#f4fbf9}
.studio-prompt-cards{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:8px}
.studio-prompt-card{padding:5px 10px;font-size:12px;border-radius:999px}
.studio-mode-stack{display:grid;gap:8px;margin-bottom:10px}.studio-mode-stack .notice{margin:0}
.studio-fusion-card{align-content:start}
.studio-fusion-thumb{width:100%;aspect-ratio:1/1;object-fit:contain;border-radius:6px;border:1px solid var(--line);background:#f8fafc}
[data-studio-scene-prompt]{width:100%;border:1px solid #cbd4df;border-radius:8px;padding:9px 11px;font:inherit;resize:vertical}
.studio-prompt-foot{display:flex;justify-content:space-between;gap:12px;font-size:12px;margin-top:4px;flex-wrap:wrap}
.studio-over{color:var(--red);font-weight:800}
.studio-advanced{margin-top:4px}
/* 🔴 TP-156：确认关。从「横贯整个屏幕的 fixed 红色底栏」改成第 3 栏里的一块中性信息。
   任务书 §2.3：两句合规话**一个字都不许删**，改的只有它长什么样——
   红色告警框会把整条主流程读成故障页（Boss 的截图里首屏最醒目的就是那两条红条），
   而它们说的其实是"这里会发生什么"，不是"出事了"。 */
.studio-cta{margin-top:auto;display:grid;gap:8px;border-top:1px solid var(--line);padding-top:12px}
.studio-cta-compliance{border:1px solid var(--line);border-left:3px solid var(--blue);background:#f8fafc;color:var(--ink);border-radius:8px;padding:8px 10px;font-size:12px;line-height:1.55}
.studio-cta-main{display:grid;gap:10px}
.studio-cta-say{display:grid;gap:6px;min-width:0}
.studio-cta-say .notice{padding:8px 10px;font-size:12px}
.studio-cta-cost{font-size:12px;color:var(--muted)}
.studio-cta-button{padding:11px 22px;font-size:14px;background:var(--blue);border-color:var(--blue);width:100%}
.studio-cta-button:disabled{opacity:.5;cursor:not-allowed}
/* 🔴 TP-162 / TP-163：电商营销图的 brief 输入壳。这几栏**已经接进出图链路**
   （随 AI 营销海报请求发给服务端）；那句说明仍然用 notice 样式排在表单**上面**——
   接没接上由服务端的 applied_to_output 说了算，客户在填字之前就得读到它。 */
.studio-brief-pending{margin:0 0 8px;font-size:12px;line-height:1.55}
.studio-brief-source{margin:0 0 10px;font-size:12px;line-height:1.5}
.studio-brief-grid{display:grid;grid-template-columns:1fr;gap:8px}
.studio-brief-field{display:grid;gap:4px;font-size:12px}
.studio-brief-field span{font-weight:750;color:var(--muted)}
.studio-brief-field input{border:1px solid #cbd4df;border-radius:8px;padding:8px 10px;font:inherit;width:100%}
.studio-brief-style-head{margin:12px 0 6px}
/* 🔴 TP-176 中泰对照：左边是他填的中文（只读回显），右边是可以改的泰语。
   `min-width:0` 是 TP-174 那条同一形状的防线——长句子在 grid 子项里会把整栏撑破。 */
.studio-brief-th-grid{display:grid;gap:10px;margin-top:10px}
.studio-brief-th-row{display:grid;gap:6px;padding:8px 10px;border:1px solid var(--line);border-radius:8px;min-width:0}
.studio-brief-th-row>*{min-width:0}
.studio-brief-th-zh{display:grid;gap:2px;font-size:12px;overflow-wrap:anywhere}
.studio-brief-th-zh span{font-weight:750}
.studio-brief-th-th{display:grid;gap:4px;font-size:12px}
.studio-brief-th-th input{border:1px solid #cbd4df;border-radius:8px;padding:8px 10px;font:inherit;width:100%}
/* 中文改过、译文还是上一版的：这一栏当场标出来，并且 CTA 按钮同时灰掉。 */
.studio-brief-th-row.is-stale{border-color:var(--amber,#c07a12);background:rgba(192,122,18,.06)}
/* 🔴 P0-4：文件在我们这儿就没有的素材卡——置灰，并且不许再显示成"已选"。 */
.generate-asset-card.asset-missing-file{opacity:.55;filter:grayscale(1)}
/* 🔴 P1-2 素材抽屉：按"这一档能不能做"分组置顶，名字给两行。
   名字原来是 nowrap 单行塞进 ~99px 的槽，422px 的长名字有 76% 看不见。 */
.studio-thumb-group{margin-bottom:14px;min-width:0}
.studio-thumb-group>h4{margin:0 0 8px;font-size:13px}
.studio-thumb-group[data-studio-thumb-group="unusable"]{opacity:.82}
.studio-drawer .studio-thumb-name{white-space:normal;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;line-height:1.35;word-break:break-word;overflow-wrap:anywhere}
.studio-thumb-blocked{font-size:11px;font-weight:750;color:var(--amber)}
/* 🔴 UED 第二轮：素材卡上的视频缩略图补播放角标 + 深色底，和「视频资产」那一页同一个读法。
   角标不抢点击（整张卡是选中开关，看大图走右上角那个入口）。 */
.asset-thumb-video{background:#0b1220;position:relative}
.asset-thumb-play{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width:32px;height:32px;border-radius:999px;background:rgba(15,23,42,.62);color:#fff;display:grid;place-items:center;font-size:13px;pointer-events:none}
/* 🔴 素材上传页：点缩略图**本体**就能看大图（原来只有右上角那个 26×38 的小角标可点，
   而生成视频 / 视频资产 / 素材库三处都是整张图可点——同一个动作三处一个样、一处不一样）。 */
.asset-thumb-slot{position:relative}
.asset-thumb-open{position:absolute;inset:0;border:0;background:transparent;padding:0;cursor:zoom-in}
/* 🔴 2026-09-13：素材上传页去掉 ⤢ 角标之后，这一层成了「看大图」**唯一**的入口，
   所以它也成了唯一那个能被 Tab 停住的元素——必须看得见焦点。
   原来它是 tabindex="-1" aria-hidden，键盘根本走不到，也就不需要焦点样式。
   用 focus-visible：鼠标点的时候不画框，键盘走到才画。 */
.asset-thumb-open:focus-visible{outline:2px solid var(--blue);outline-offset:2px;border-radius:6px}
.studio-brief-th-cost{font-size:11px;line-height:1.45;margin-top:6px}
.studio-brief-th-stale,.studio-brief-th-edited{font-size:11px;line-height:1.45;overflow-wrap:anywhere}
.studio-brief-style small{font-size:11px;line-height:1.4}
/* 素材库卡片上的「继续制作」下拉菜单 */
.studio-continue{display:grid;gap:6px;border-top:1px solid var(--line);padding-top:8px}
.studio-continue button{text-align:left;display:grid;gap:2px;font-size:13px}
/* 右侧抽屉 */
.studio-drawer-overlay{position:fixed;inset:0;background:rgba(15,23,42,.4);display:flex;justify-content:flex-end;z-index:60}
/* 🔴 TP-168：抽屉是一个 <aside>，于是被全局那条 `aside{background:#111827;color:#f9fafb;
   padding:18px 14px;gap:18px}`（写给左侧深色导航栏的）整条命中。上一版只覆盖了 background，
   **没覆盖 color** —— 结果是白底 + #f9fafb 近白文字，抽屉里的标题、时间列、类型列、参数列
   全部看不见（只有 .tag 和缩略图自带颜色才露出来）。生成记录本来就是定版新搬进抽屉的入口，
   等于客户每次查流水都撞上一屏隐形字。
   🔴 修法选的是**给 .studio-drawer 显式设值**，不是去收窄全局 aside{}：
   后者更治本，但全站侧栏、租户切换器、aside-note 全在那条规则底下，改它是一次整站视觉变更，
   而本轮的目标是收口。影响面因此只有这一个 class。
   padding/gap 一并归零：抽屉自己的 head/body 已经有内边距，继承那一份只是多一圈外缩进。 */
.studio-drawer{background:#fff;color:var(--ink);padding:0;gap:0;width:520px;max-width:92vw;height:100vh;display:flex;flex-direction:column;box-shadow:-18px 0 40px rgba(15,23,42,.18)}
/* 🔴 生成记录那张表有六列（定版文档写死，一列都不能删），520px 装不下：实测参数列被压到
   50px、行高撑到 147px、文字竖排、动作列被挤出视口。所以**只给这一类抽屉**加宽，
   别的抽屉（档位说明/结果详情/全部图片）装的是段落和缩略图网格，520px 正合适。
   再配一个 min-width + .table-scroll 横滚兜底：窗口再窄时表格自己能滚，不靠挤列。 */
.studio-drawer-wide{width:1000px}
.studio-drawer-wide .studio-drawer-body{padding:14px 18px}
.studio-drawer-wide table{min-width:840px}
.studio-drawer-wide td,.studio-drawer-wide th{padding:10px 12px;vertical-align:top}
.studio-drawer-wide td:first-child,.studio-drawer-wide th:first-child{white-space:nowrap}
.studio-drawer-wide td:last-child{white-space:nowrap}
.studio-img-broken{min-height:60px;display:grid;place-items:center;font-size:12px;text-align:center;padding:8px}
.studio-v4-review-draft.is-broken{border-color:#fde68a;background:#fffbeb}
.studio-drawer-head{padding:16px 20px;border-bottom:1px solid var(--line);display:flex;justify-content:space-between;align-items:center;gap:12px}
.studio-drawer-head h3{margin:0;font-size:16px}
.studio-drawer-body{padding:18px 20px;overflow:auto;display:grid;gap:8px;align-content:start}
.studio-drawer-body p{margin:0;line-height:1.6;font-size:13px}
@media(max-width:1240px){.studio-flow{grid-template-columns:1fr}.studio-type-row{grid-template-columns:repeat(auto-fit,minmax(190px,1fr))}.studio-params{grid-template-columns:repeat(auto-fit,minmax(240px,1fr))}}
.studio-log-thumb{width:52px;height:52px;object-fit:contain;background:#fff;border:1px solid var(--line);border-radius:6px;display:block}

/* TP-165 透明优先：棚拍增强卡片上透明 PNG（棋盘格底，只在界面上铺，不烘进文件）和白底图并排 */
.studio-human-gate{margin:4px 0}

/* ---------- 2026-09-12 UED 全局风格收口 ----------
 * Boss 已确认图片工作台新版方向：后台工具要安静、清晰、任务导向，不能像营销页。
 * 这一段先统一全站视觉语言，不改业务流：渐变 hero 降级为任务条，卡片/按钮/表格密度统一。
 * 详细审计见 docs/web-ued-style-audit-2026-09-12.md。 */
:root{
  --bg:#eef3f7;
  --panel:#fff;
  --ink:#172033;
  /**
   * 🔴 2026-09-13 第 2 批：`--muted` / `--amber` 调到**刚好过 WCAG AA（4.5:1）**。
   *
   * 收之前全站扫出 109 处落在 3.0~4.5 之间——看得见，但够不着正文档的下限：
   *   `--muted #657184` 在 #eef1f5 上 4.36；`--amber #b86618` 在浅黄 notice 上 4.01。
   *
   * 🔴 只压暗到"刚好够"，不多压一分：`--muted` 是全站次要文字的基色，
   * 调狠了会把视觉层次压平——所有说明文字都变得和主文案一样重，等于没有层次。
   * 实测压暗 3%（#657184 → #626e80）就让它在四种底色上分别达到 5.17 / 4.56 / 4.89 / 4.89，
   * 余量很小正是**有意的**。`--amber` 同理。
   *
   * 🔴 `--amber` 第一版定在 #ab5f16（白底 4.54，"刚好过"），但守卫新加的那条腿
   * ——token 对**页面底** `--bg #eef3f7` 和**面板底** `--panel #fff` 这两块最基本的面——
   * 量出它在页面底上只有 4.29。今天屏幕上恰好没有琥珀字落在页面底，所以按元素扫是扫不出来的；
   * 但这是个**全站通用**的警告色，落上去只是早晚的事。再压暗到 #a65b15（页面底 4.55、白底 5.09），
   * 仍然是"刚好过"，只是尺子多量了一块面。
   *
   * 🔴 真机全站普查（8 页 1976 个有字的元素，同一次运行里量两遍：先把两个 token
   * 用内联样式改回旧值量 before，再还原量 after，底色/布局/字号完全同一份）——
   * 改 token 只把未过 AA 的从 102 压到 25，剩下的 25 处**根本没跟着 token 走**：
   *   ① 五处把这两个色**写成了 hex 字面量**（已删的流程条 em / 已删的工作台素材列表 meta span
   *      / `.studio-v4-checklist li.is-warn::before` / `.studio-v4-more span`
   *      / `.studio-v4-review-draft .studio-v4-checklist li.is-warn`）。
   *      同一个颜色写两遍，改 token 时必然漏掉一遍——已全部改成 `var(--muted)` / `var(--amber)`。
   *   ② 工作台素材列表（已删）那条 meta span 选择器把**标签**也一起盖了：`.tag.gray`
   *      自己的 #526071 在 #e8eef5 上本来有 5.50，被盖成 `--muted` 之后掉到 4.43。
   *      这条不止是对比度——标签的颜色是**语义**（第 181 轮刚定的 5 色规则），
   *      被一条排版规则盖掉等于语义失效。改成 `span:not(.tag)`，让标签留着自己的色。
   * 收完：未过 AA 85 → 4，`--muted` / `--amber` 名下 0 处。
   *
   * 🔴 剩的 4 处不是这两个 token，是 `--green #16865f` 落在 #dff7e8 上（4.04），
   * 也就是工作台四个步骤序号那个绿圆点。第 2 批授权的范围是 `--muted` / `--amber`，
   * 动第三个 token 属于超纲，**留给 PM 裁决**，没有自作主张改。
   */
  --muted:#626e80;
  /* 🔴 B1（2026-09-13）：`--line` 原来被定义了两次（#dfe5ec 和 #d9e2ec），
     两个值同时在跑——`.panel` 的边框和 `.studio-v4-card` 的边框不是一个色。
     保留**后定义**的这一个（它是收口块里的那版，全站大多数面已经跟着它了），
     第一处定义在文件开头，已经删掉。 */
  --line:#d9e2ec;
  --accent:#2767e7;
  --blue:#2767e7;
  --amber:#a65b15;
  /* 🔴 2026-09-13 第 2 批（PM 批准把 --green 一起收）：#16865f 落在步骤序号那个
     浅绿圆点底 #dff7e8 上只有 4.04。同样只调到"刚好过"：#157c58 在那块底上 4.59、
     白底 5.18、页面底 4.63。和 muted/amber 一样，它也有两处 hex 字面量副本
     （已删的流程条 b 和 .studio-v4-checklist li::before），一并改成 var(--green)——
     不然下次调这个 token 又会漏掉这两处，这一批已经栽过一次了。 */
  --green:#157c58;
  --red:#b42318;
  --shadow:0 1px 2px rgba(15,23,42,.05);
  --shadow-soft:0 10px 24px rgba(15,23,42,.06);
}
body{background:var(--bg);color:var(--ink)}
/* ══════════════════════════════════════════════════════════════════════════
 * 🔴 B3（2026-09-13）：按钮只有 **sm / md 两档**。
 * 收之前量出来是 14 个视觉变体：次按钮 4 种高度（32/34/38/44）、2 种圆角、
 * 3 种字号（12/13/15）、4 种内边距；主按钮 4 种高度、2 种圆角。
 * 同一个动作在不同页面长得不一样，客户学不会"这类东西长这样"。
 *
 * md = 主流程按钮（生成、上传、确认…）；sm = 卡片里的次动作（处理图片、删除、下载…）。
 * 🔴 收的是**尺寸**，不是语义：`.primary` / `.danger` 的颜色一个都没动。
 * ══════════════════════════════════════════════════════════════════════════ */
:root{
  --btn-md-h:38px; --btn-md-pad:9px 14px; --btn-md-fs:14px;
  --btn-sm-h:32px; --btn-sm-pad:6px 10px; --btn-sm-fs:13px;
  /* TP-215：素材卡缩略图盒子的边长（132×132 方盒，横卡左边那格）。`.asset-thumb` / `.asset-thumb-alpha` / 横卡的 `.asset-thumb-slot` 都读它，不再各写一个 132。 */
  --asset-thumb-size:132px;
}
button,.button-link{min-height:var(--btn-md-h);padding:var(--btn-md-pad);font-size:var(--btn-md-fs);border-radius:8px}
.btn-sm,.materials-asset-actions button,.materials-asset-actions .button-link{
  min-height:var(--btn-sm-h);padding:var(--btn-sm-pad);font-size:var(--btn-sm-fs)}
/**
 * 图标按钮（×、⤢、?）不受两档尺寸约束：它们是纯图标、没有文字宽度可言，
 * 尺寸由它们自己的 `width`/`height` 说了算。
 *
 * 🔴 2026-09-13 Boss 报「全站问号被拉变形」：`.info-hint-toggle` 自己写着 18×18，
 * 却没在这张名单里，于是吃到了 `button{min-height:38px}` ——**min-height 压过 height**，
 * 渲染出来是 18 宽 × 38 高的竖长条。`.media-preview-btn` 当时显式写了自己的
 * `min-height:32px` 才躲过一劫，靠的是"那个人恰好记得"。
 *
 * 🔴 为什么不改成"全局不设 min-height、由 sm/md 两档显式设"：那要给全站每一个普通按钮
 * 都加一个类，漏掉的那些会**悄悄失去触摸目标高度**——错的方向比现在这个更危险
 *（现在错了是"变形"，看得见；那样错了是"点不准"，看不见）。
 * 🔴 也不能用 `:where()` 降优先级：这里根本不是优先级问题——`.info-hint-toggle`
 * 压根没声明 `min-height`，降谁的优先级都不影响一条"它自己没写"的属性。
 *
 * 所以结构上仍然是**一张显式名单**，代价是"下一个图标按钮要记得加进来"。
 * 这个代价由**判据**兜底，不由记性兜底：tp188 检查 1 逐个量全站按钮的
 * 「渲染高度 == 它自己声明的高度」，下一个漏网的会当场变红。
 */
.icon-btn,.info-hint-toggle{min-height:auto}
.icon-btn{padding:2px 6px}
/**
 * 🔴 2026-09-13：这里原来还有一个写死的 `min-height:38px`。
 *
 * 它是上面那条 `button,.button-link{min-height:var(--btn-md-h)}` 的**第二份说法**
 *（--btn-md-h 正好也是 38px，所以一直没人发现），和 `--line` 两个值、`.primary` 三处定义、
 * `.video-card-title` 那条截断是同一类事故：同一个属性两处写，改 token 时必然漏掉一处。
 * 删掉这一份，min-height 只由两档尺寸 token 说了算。
 */
button,.button-link{border-color:#cbd6e2;border-radius:8px;background:#fff;color:var(--ink)}
button:hover:not(:disabled),.button-link:hover{background:#f7fafc;border-color:#b8c6d6}
.primary:hover:not(:disabled){background:#1f56c8;border-color:#1f56c8}
.app{grid-template-columns:224px minmax(0,1fr)}
aside{background:#0f1724;padding:16px 12px;gap:16px}
.brand{padding:4px 6px 15px}
/* TP-228：图形标是透明底的标（TP-233 起是 /brand/spark-mark.svg），不再有渐变底 / TK 字；尺寸在上面主样式里。 */
.brand span{color:#9aa8bd}
nav{gap:4px}
.nav-btn{border-radius:8px;padding:10px 12px;font-weight:760}
.nav-btn.active,.nav-btn:hover{background:rgba(255,255,255,.13)}
.tenant-switch select{border-color:rgba(255,255,255,.16);background:#182235}
.aside-note{border-color:rgba(255,255,255,.12);background:rgba(255,255,255,.04);color:#aeb8c7}
header{height:68px;background:rgba(255,255,255,.96);padding:0 24px;border-bottom-color:var(--line);box-shadow:0 1px 0 rgba(15,23,42,.02)}
header h1{font-size:20px;font-weight:820}
header p{color:var(--muted);font-size:13px}
#content{padding:18px 22px 32px}
.hero{background:var(--panel);color:var(--ink);border:1px solid var(--line);border-radius:8px;padding:16px 18px;box-shadow:var(--shadow)}
.hero h2{font-size:18px;line-height:1.28}
.hero p{color:var(--muted);font-size:13px;line-height:1.55;margin:6px 0 0}
.grid{gap:14px}
.panel{border-color:var(--line);border-radius:8px;box-shadow:var(--shadow)}
.panel-head{padding:14px 16px}
.panel-head h3{font-size:15px}
.panel-head p{font-size:13px;color:var(--muted)}
.panel-body{padding:16px}
/* Boss 2026-09-28：全站数字卡只有一种排法——小标题 / 值 / 说明从上往下排，行距一致（以前 space-between 把只有两行的卡的数字压到底部，和三行的卡对不齐）。
   值：数字 28px，文字 18px（metric-value-text）；说明 13px 灰字。 */
.metric{min-height:104px;padding:15px 16px;justify-content:flex-start;gap:6px;text-align:left}
.metric-label{font-size:12px;font-weight:760;letter-spacing:0;color:var(--muted)}
.metric-value{font-size:28px;line-height:1.1}
.metric-value-text{font-size:18px}
.card{border-color:var(--line);border-radius:8px;padding:12px;gap:8px}
.card.selected{border-color:var(--accent);background:#f5f9ff;box-shadow:inset 0 0 0 1px rgba(39,103,231,.14)}
.quick-item{border-color:var(--line);border-radius:8px;padding:11px 12px}
table{border-collapse:separate;border-spacing:0}
th{background:#f7fafc;color:var(--muted);font-size:12px;font-weight:780}
th,td{padding:10px 12px;border-bottom-color:var(--line)}
tr:last-child td{border-bottom:0}
.notice{border-radius:8px;background:#eff6ff;border-color:#bfd7ff;color:#1e4fb8}
.notice-warning{background:#fff8eb;border-color:#f4d6a3;color:var(--amber)}
.notice-error{background:#fff1f0;border-color:#ffc8c2;color:var(--red)}
/* 🔴 2026-09-13（第一轮 UED review P2 第 6 条）：标签**不许折行**。
   实测：用途卡标题里的「最常用」「需填直播信息」「暂未开放」被挤成两行，
   高度 40px，而同屏兄弟标签是 23px——一个圆角 pill 折成两行会读成两个标签。
   `flex:none` 是配套的那一半：标签在标题的 flex 行里不许被压缩，
   该让位的是标题文字（它能自然折行），不是标签。
   🔴 只写在这一处：`.tag` 在这个文件里还有一条更早的定义（那条管 display/inline-flex），
   同一个属性两处写迟早有一处过期——white-space 只由这一条说了算。 */
.tag{border-radius:999px;padding:3px 8px;font-size:12px;font-weight:780;white-space:nowrap;flex:none}
.blue{color:#1e4fb8;background:#e7f0ff}
.green{color:#137653;background:#e4f7ee}
.amber{color:#a45510;background:#fff0d9}
.red{color:#a51f15;background:#ffe1df}
.gray{color:#526071;background:#e8eef5}
.field label{color:#536174}
.field input,.field select,.field textarea{border-color:#cbd6e2;border-radius:8px;background:#fff}
.modal{border-radius:8px;box-shadow:var(--shadow-soft)}
.modal-header,.modal-footer{border-color:var(--line)}
.row-clickable:hover{background:#f7fafc}
.progress-track{background:#e2eaf2}
.table-row-current{background:#f5f9ff}
.table-row-current td:first-child{box-shadow:inset 3px 0 0 var(--accent)}
.overage-panel,.promo-group{background:#f7fafc;border-color:var(--line)}
.video-card h4.video-card-title{font-size:14px}
.studio-page .panel,.studio-layer,.studio-card{box-shadow:var(--shadow)}

@media(max-width:1000px){
  .app{grid-template-columns:76px 1fr}
}
@media(max-width:760px){
  #content{padding:14px}
  header{padding:13px 14px}
  .hero{padding:14px}
}

/* ---------- 2026-09-12 图片工作台 V2：结构级改版 ----------
 * 这一段不是换皮：把图片工作台改成「任务简报 → 生产流程 → 产出验收」三段。
 * 目标是让用户第一眼知道：现在做哪个商品、走哪条线、哪里生成、哪里验收。 */
.studio-workbench-head{display:grid;grid-template-columns:minmax(0,1.25fr) minmax(340px,.75fr);gap:14px;align-items:start}
.studio-production-board,.studio-review-board{border:1px solid #d8e2ee;border-radius:8px;background:#f8fbff;padding:12px;box-shadow:var(--shadow)}
.studio-production-board{background:#f7fafc}
.studio-review-board{display:grid;gap:14px;background:#fff}
.studio-review-board>[data-studio-layer="results"]{order:1}
.studio-review-board>[data-studio-layer="examples"]{order:2}
@media(max-width:1320px){
  .studio-workbench-head{grid-template-columns:1fr}
}

/* ---------- 图片工作台 V3：真正按工作台用法重排 ----------
 * 首屏只做三件事：选素材、配置生成、验收结果。历史和能力清单降级到下方。 */
.studio-workbench-v3{display:grid;grid-template-columns:minmax(0,1fr) minmax(360px,430px);gap:12px;align-items:start}
.studio-production-v3{display:grid;grid-template-columns:minmax(250px,300px) minmax(280px,.88fr) minmax(330px,1fr);gap:12px;align-items:start}
.studio-inspector-v3{position:sticky;top:84px;min-width:0;background:transparent;padding:0;gap:0}
.studio-inspector-v3>[data-studio-layer="results"]{max-height:calc(100vh - 96px);overflow:auto}

@media(max-width:1500px){
  .studio-workbench-v3{grid-template-columns:1fr}
  .studio-inspector-v3{position:static}
  .studio-inspector-v3>[data-studio-layer="results"]{max-height:none}
}
@media(max-width:1180px){
  .studio-production-v3{grid-template-columns:1fr}
}

/* ---------- 图片工作台 V4：从产品使用重新设计 ----------
 * 不复刻旧页，也不照参考图。把页面当成一台图片生产机器：
 * 顶部选业务线和商品，主体是 素材 / 设置 / 验收，底部只留轻量记录。 */
.studio-page-v4{gap:12px}
.studio-v4-nav,.studio-v4-card{border:1px solid #d8e2ee;border-radius:8px;background:#fff;box-shadow:var(--shadow);overflow:hidden}
.studio-v4-nav{display:grid;grid-template-columns:minmax(0,1fr) 330px auto;gap:14px;padding:14px;align-items:end}
.studio-v4-tabs{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.studio-v4-tab{display:inline-flex;align-items:center;gap:8px;min-height:40px;padding:8px 14px;border-color:#d8e2ee;background:#f8fafc;border-radius:8px;font-weight:820;color:#526071}
.studio-v4-tab span{display:inline-grid;place-items:center;width:22px;height:22px;border-radius:999px;background:#e8eef5;color:#405066;font-size:12px}
.studio-v4-tab b{display:inline-grid;place-items:center;min-width:20px;height:20px;padding:0 6px;border-radius:999px;background:#ffedd5;color:#a45510;font-size:12px}
.studio-v4-tab.is-active{background:#eaf1ff;border-color:#9fbeff;color:#174fca;box-shadow:inset 0 0 0 1px rgba(39,103,231,.12)}
.studio-v4-tab.is-active span{background:#2767e7;color:#fff}
.studio-v4-product{min-width:0;max-width:none}
.studio-v4-product label{font-size:12px}
.studio-v4-product select{min-height:40px;background:#fff;border-color:#cbd6e2;border-radius:8px}
.studio-v4-record{min-height:40px;padding:8px 16px;border-radius:8px;border:1px solid #cbd6e2;background:#fff;font-weight:820;color:#1f2d3d;white-space:nowrap}
.studio-v4-record:hover{border-color:#9fbeff;background:#f8fbff}
/* TP-232 #9：四步流程条（原 studio-v4 的 steps 那四条规则）删了——Tab 行 + 三栏标题已是同一条流程；「N 张待确认」在右栏标题上。 */
.studio-v4-stage{display:grid;grid-template-columns:minmax(0,1fr) minmax(380px,460px);gap:12px;align-items:start}
/* 🔴 2026-09-13 第 2 批：上半区两栏各自按内容高，不再被拉平。
   量出来的样子：容器 1270px，而左边那张卡的内容只有 535px——**735px 是空的**，
   却仍然画着卡片的边框和底色，看起来像"这里本该有东西但没加载出来"。
   根因是这个 grid 没写 `align-items`，默认 `stretch` 把两栏拉成一样高。
   `.studio-v4-stage`（外面那层）本来就是 `align-items:start`，这里跟它一致。 */
.studio-v4-current{display:grid;grid-template-columns:minmax(300px,380px) minmax(420px,1fr);gap:12px;min-width:0;align-items:start}
.studio-v4-card header{display:flex;justify-content:space-between;gap:12px;align-items:flex-start;padding:14px 16px;border-bottom:1px solid var(--line)}
.studio-v4-card h3{margin:0;font-size:16px;line-height:1.25}
.studio-v4-card header p{margin:4px 0 0;color:var(--muted);font-size:12px;line-height:1.45}
.studio-v4-card header>span,.studio-v4-card header .tag{flex:none;align-self:center}
/* 标题右侧计数（N 张可选 / N 张待确认）：以前没定字号，继承 16px 比 h3 还显眼；和其它面板的副信息同级 */
.studio-v4-card header>span:not(.tag){font-size:13px;color:var(--muted)}
.studio-v4-actions{display:flex;gap:8px;flex-wrap:wrap;padding:0 14px 14px}
.studio-v4-actions button,.studio-v4-actions .button-link{flex:1 1 auto}
.studio-v4-current-asset{min-width:0}
/* 🔴 2026-09-12 Boss 报的 bug：素材名一长，整张「当前输入素材」卡炸开——图片溢出卡片、
   商品名冲出右边界被中间栏盖住、第三个按钮被切掉。
   根因不是 h4 的省略号失效，是**它上游的 grid 项没有 min-width:0**：
   grid/flex item 的默认 `min-width:auto` 意思是"不许小于内容的最小宽度"，
   于是一个 `white-space:nowrap` 的长标题会把整条轨道顶宽，容器先被撑破，
   h4 上那条 `overflow:hidden;text-overflow:ellipsis` 自然就没有可省略的必要了。
   触发它的是 `洗车液 3243（TP-164 硬瓶样本）（棚拍增强）（AI 营销海报）`——每加工一次叠一层括号。
   🔴 修在 min-width 上，不给 h4 打 max-width 之类的补丁：补丁只挡住这一个元素，
   同一张卡里的图片和按钮行照样会把轨道顶宽。 */
.studio-v4-current-body{display:grid;gap:12px;padding:14px;min-width:0}
.studio-v4-current-body>*{min-width:0}
.studio-v4-current-body>img{display:block;width:100%;height:240px;object-fit:contain;border:1px solid #d8e2ee;border-radius:8px;background:#fff}
.studio-v4-current-body h4{margin:0 0 6px;font-size:15px;line-height:1.35;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.studio-v4-current-body .studio-v4-actions{padding:8px 0 0}
.studio-v4-current-empty{min-height:260px}
.studio-v4-settings{min-width:0}
.studio-v4-settings-body{display:grid;gap:12px;padding:14px}
.studio-v4-settings .studio-layer{border:1px solid #d8e2ee;box-shadow:none;border-radius:8px}
.studio-v4-settings .studio-layer-head{padding:12px 12px 8px;margin:0;border-bottom:1px solid #edf2f7}
.studio-v4-settings .studio-layer-head h4{font-size:14px}
.studio-v4-settings .studio-type-row,.studio-v4-settings .studio-params{padding:12px}
.studio-v4-settings .studio-type-card{border-radius:8px;border-color:#d8e2ee}
.studio-v4-settings .studio-type-card.selected,.studio-v4-settings .studio-type-card:has(input:checked){border-color:#2767e7;background:#f5f9ff}
.studio-v4-settings .studio-scene-grid{grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}
.studio-v4-settings .studio-scene-card{border-radius:8px}
.studio-v4-settings .studio-cta{padding:12px;margin:0;border-top:1px solid #edf2f7}
.studio-v4-settings .studio-cta-button{min-height:44px;border-radius:8px;font-size:15px}
.studio-v4-review{position:sticky;top:84px}
.studio-v4-preview{padding:14px 16px 0}
.studio-v4-preview img,.studio-v4-empty{display:block;width:100%;height:300px;object-fit:contain;border:1px solid #d8e2ee;border-radius:8px;background:#fff}
.studio-v4-empty{display:grid;place-items:center}
.studio-v4-review h4{margin:12px 16px 4px;font-size:15px;line-height:1.35}
.studio-v4-review>p{margin:0 16px 10px;font-size:12px}
.studio-v4-checklist{list-style:none;display:grid;gap:7px;margin:12px 16px;padding:0}
.studio-v4-checklist li{position:relative;padding-left:20px;color:#526071;font-size:13px;line-height:1.45}
.studio-v4-checklist li::before{content:"✓";position:absolute;left:0;color:var(--green);font-weight:900}
.studio-v4-checklist li.is-warn::before{content:"!";color:var(--amber)}
.studio-v4-review .studio-v4-actions{padding:0 16px 16px}
/* TP-216 返修：棚拍增强草稿卡上那排候选（每张一格：水印缩图 / 第 N 张 / 视角 chip / 当前选中 or 选这张）。 */
.studio-candidate-row{display:flex;gap:10px;flex-wrap:wrap;padding:0 16px 10px}
.studio-candidate{margin:0;display:flex;gap:8px;align-items:center;border:1px solid var(--line,#d8e2ee);border-radius:8px;padding:6px 8px;background:#fff;min-width:0}
.studio-candidate.is-selected{border-color:#2563eb;box-shadow:0 0 0 2px rgba(37,99,235,.15)}
.studio-candidate-thumb{width:56px;height:84px;flex:0 0 56px;position:relative;display:block;border-radius:6px;overflow:hidden;background:#fff}
.studio-candidate-thumb img{width:56px;height:84px;object-fit:contain;display:block}
.studio-candidate figcaption{font-size:12px;display:grid;gap:4px}
.studio-candidate figcaption b{font-weight:600;margin-right:4px}
.studio-candidate-selected{color:#2563eb;font-weight:600}
.studio-v4-review .studio-v4-actions .primary{flex:2 1 180px}
.studio-v4-review-list{display:grid;gap:12px;padding:14px 16px 16px}
.studio-v4-review-draft{display:grid;grid-template-columns:180px minmax(0,1fr);gap:14px;padding:12px;border:1px solid #d8e2ee;border-radius:8px;background:#fff}
.studio-v4-review-draft.is-active{border-color:#8fb4ff;background:#f7fbff}
.studio-v4-review-draft.is-new{box-shadow:inset 3px 0 0 #2767e7}
.studio-v4-review-draft-image{position:relative;min-width:0}
.studio-v4-review-draft-image img,.studio-v4-review-draft-image .studio-v4-empty{display:block;width:100%;height:180px;object-fit:contain;border:1px solid #d8e2ee;border-radius:8px;background:#fff}
.studio-v4-review-draft-image span{position:absolute;left:8px;top:8px;padding:4px 8px;border-radius:999px;background:#2767e7;color:#fff;font-size:12px;font-weight:820}
/* 「最新」那个徽标是绝对定位在左上角的，所以空态那几个字要往下让一让——
   不让的话（产物文件丢失那张卡上）文字会被徽标压住，正好压在最该读清楚的那一句上。 */
.studio-v4-review-draft-image .studio-v4-empty{display:grid;place-items:center;padding:34px 12px 12px;line-height:1.6;text-align:center}
.studio-v4-review-draft-body{min-width:0}
.studio-v4-review-draft-body h4{margin:2px 0 4px;font-size:16px;line-height:1.35}
.studio-v4-review-draft-body>p{margin:0 0 8px;font-size:12px}
.studio-v4-review-draft .studio-v4-checklist{margin:10px 0 12px}
.studio-v4-review-draft .studio-v4-actions{padding:0}
.studio-v4-review-tail{margin:0 16px 16px;padding:10px 12px;border:1px dashed #c9d6e6;border-radius:8px;background:#fbfdff;font-size:12px}
.studio-v4-more{display:grid;gap:8px;padding:0 16px 16px}
.studio-v4-more div{display:grid;grid-template-columns:70px minmax(0,1fr);gap:4px 8px;padding:8px 10px;border:1px solid #e1e8f1;border-radius:8px;background:#fbfdff}
.studio-v4-more span{font-size:12px;color:var(--amber);font-weight:820}
.studio-v4-more b{font-size:13px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.studio-v4-more small{grid-column:2;color:var(--muted);font-size:11px}
.studio-v4-review.is-focused{border-color:#9fbeff;box-shadow:0 0 0 3px rgba(39,103,231,.12),var(--shadow)}
/* 🔴 2026-09-13 第二轮：工作台下半区「本商品素材」整块去掉（Boss），它那 20 条 .studio-v4-library-* 样式一并删了——
   tp183 检查 2 盯着"没人用的 studio-v4-* 类名"。素材现在只在左侧「素材库」页（.materials-*）。 */

@media(max-width:1500px){
  .studio-v4-stage{grid-template-columns:1fr}
  .studio-v4-current{grid-template-columns:minmax(260px,340px) minmax(0,1fr)}
  .studio-v4-review{grid-column:1 / -1;position:static}
  .studio-v4-preview img,.studio-v4-empty{height:260px}
}
@media(max-width:980px){
  .studio-v4-nav{grid-template-columns:1fr}
  .studio-v4-stage,.studio-v4-current{grid-template-columns:1fr}
  .studio-v4-review{position:static}
}

/* ---------- 2026-09-12 UED 第一轮收尾：把「主动作蓝」和「已选/通过青绿」拆开 ----------
 * UED 文档 §目标风格 7：「蓝色作为主动作，青绿色作为已选/通过，琥珀色作为待确认，
 * 红色只用于阻断/危险」。
 *
 * 🔴 上一版把 `--accent` 整个改成了蓝（#2767e7）。它看起来像"统一主色"，实际后果是
 * **把"已选/通过"和"主动作"合并成了同一个颜色**——`--accent` 有 16 处用法，其中
 * 只有 `.primary` 一处是主动作，剩下 15 处全是已选/当前/通过（选中的卡、当前向导步、
 * 选中的话术、当前表格行、打开的开关、当前套餐单价、"怎么补"引导）。
 *
 * 实测后果（生成视频那一页）：当前步骤、选中的用途卡、选中的加工档、选中的字幕样式
 * 一共 6 个地方是蓝框，和真正的主动作「开始生成」「下一步」用同一个蓝——
 * 正是 UED 文档「当前问题 §5 主按钮不够唯一」说的那件事，只是被一次改色放大了。
 *
 * 所以这里把两者拆开：`--accent` 回到青绿（已选/通过），主动作显式走 `--blue`。
 * 🔴 一个字的文案都没改，也没有动任何业务逻辑——纯颜色语义归位。
 */
:root{
  /* 已选 / 当前 / 通过。回到 TP 之前那个青绿，和 `.green` 是同一家族但更深、可作边框。 */
  --accent:#0f766e;
  /* 主动作。和电商图片工作台 `studio-v4` 已经在用的那个蓝是同一个值——这一轮的目的
     就是让其它页面和工作台长成一家人，另起一个蓝等于又造了第二套。 */
  --blue:#2767e7;
}
/* 🔴 主动作是**唯一**用蓝的地方（危险动作仍是白底红字的 `button.danger`，不抢它的权重）。 */
/* 🔴 2026-09-13：`.primary` 原来有**三处**定义（基础行一处、收口块一处、这里一处），
   前两处都读 `var(--accent)`（青绿）、都被这一处盖死——也就是说主按钮的颜色有两份
   写着但不生效的说法。收口时最容易发生的事就是有人去改那两处里的一处，改完一看
   没反应、再去别处加一条，于是变体越滚越多。这里只留这一处。 */
.primary{background:var(--blue);border-color:var(--blue);color:#fff}
.primary:hover:not(:disabled){background:#1f56c8;border-color:#1f56c8}
/* 已选态的底色/描边跟着回青绿——上一版只把边框改蓝、底色还留着蓝调，两边对不上。 */
.card.selected{border-color:var(--accent);background:#f2faf8;box-shadow:inset 0 0 0 1px rgba(15,118,110,.18)}
.table-row-current{background:#f2faf8}
/**
 * 🔴 电商图片工作台这两条**刻意钉回蓝**，不跟着回青绿。
 *
 * 理由：这一页是 Boss 已验收的那一版，它自己的选中态（`.studio-v4-tab.is-active`、
 * 已删的工作台素材列表缩略图 is-picked、验收卡的 `is-new`）全是**写死的 #2767e7**，
 * 不走 `--accent`。只让这两条回青绿，等于在同一屏上摆出两种"选中"——
 * 抽屉里的缩略图青绿、旁边素材库的卡片蓝。
 * 本轮的约束是「图片工作台保持已验收结构、不大拆」，所以这里跟**它自己**保持一致。
 * （顺带修掉一个上一版留下的错配：这两条原来是青绿底 + 蓝边框。）
 * 🔴 如果 Boss 要整站"已选一律青绿"，把这两条删掉即可，其余不用动。
 */
.studio-thumb.is-picked{border-color:var(--blue);background:#f5f9ff;box-shadow:inset 0 0 0 1px rgba(39,103,231,.16)}
.studio-scene-card.is-picked{border-color:var(--blue);background:#f5f9ff}

/* ---------- 2026-09-12 修：鼠标移到左侧导航上，那一项的字就没了 ----------
 * 像素取证（PM 在 ui-after/1-工作台.png 上量的）：选中项「工作台」背景 #f7fafc、
 * 文字 #f7fafc —— 完全同色。对照未选中的「商品中心」：背景 #0f1724、文字 #b4beca，正常。
 *
 * 根因是特异性打架，两条规则都在上面那段「UED 全局风格收口」里：
 *   button:hover:not(:disabled),.button-link:hover{background:#f7fafc}   → (0,2,1)
 *   .nav-btn.active,.nav-btn:hover{background:rgba(255,255,255,.13)}      → (0,2,0)
 * (0,2,1) > (0,2,0)，于是**深色侧栏的按钮 hover 时被通用的浅色按钮规则改成了近白底**，
 * 而文字色仍由 `.nav-btn`/`.nav-btn.active` 决定（#cbd5e1 / #fff）⇒ 白底白字。
 * 这不是截图伪影：真实用户每一页、每一次把鼠标移到侧边栏都会撞上。
 *
 * 🔴 修法**针对的是这一类问题，不是这一个按钮**：通用 hover 规则里那套浅色值
 *（#f7fafc 底）对**任何深色面**都是错的，下一个加进侧栏的按钮会原样再中一次。
 * 所以这里一次性圈住"侧栏里的所有按钮"，而不是只把 `.nav-btn` 的特异性抬上去。
 *
 * 🔴 为什么是 `.app > aside` 而不是 `aside`：`<aside class="studio-drawer">`（图片工作台
 * 那个右侧抽屉）也是 aside，但它是**白底**的，套上深色 hover 就是同一个错误的镜像。
 * 侧栏是 `.app` 的直接子元素，抽屉挂在 `#content` 里面 —— 这个结构差别把两者分得很干净。
 *
 * 特异性 (0,3,2) > (0,2,1)，**不用 `!important`**。
 * `#session-box` 那两条自带 hover 的（账号展开条 (1,2,0)、菜单里的按钮 (1,2,1)）
 * 带 id，照样赢过这一条，保持原样不受影响。
 */
.app > aside button:hover:not(:disabled){background:rgba(255,255,255,.13);border-color:rgba(255,255,255,.22);color:#fff}

/* ---------- 2026-09-12 UED 逐页改版第 1 页：生成视频（TP-173） ----------
 * 依据 docs/web-ued-style-audit-2026-09-12.md「后续逐页改版顺序」第 2 条：
 * 保留四步模型，把右侧总账和每步内容做成更像「生产台」的布局。
 *
 * 🔴 这一段**只动版面，不动任何业务判据出口**：13 类风控/额度/拦截出口
 *（本条消耗、增强总价、增强未确认拦截、素材用量、用途不符的话术、走不下去缺什么、
 *  预览版面说明、确认关……）全部保持平铺可见，一个都没折叠、没隐藏、没改文案。
 */

/* A｜四步导航横跨整个生产台，当前任务拿主要宽度，总账留在右侧。
   之前外层直接复用通用 `.cols-2`，步骤条也被关在左边半栏：1440px 屏幕上
   四个步骤标题几乎逐字换行，第 2 / 3 步的表格只有约 500px，核心内容读不动。
   这里给视频工作台自己的网格，不影响站内其他两栏页面。 */
.generate-workspace{display:grid;grid-template-columns:minmax(0,1.65fr) minmax(360px,.85fr);gap:16px;align-items:start}
.generate-workspace>.wizard-panel{grid-column:1/-1}
.generate-workspace-main{min-width:0}

/* B｜总账跟着滚。
   实测第 2 步左栏 3989px 高（21 张素材卡），而右栏内容只有约 900px——
   客户滚下去挑素材时，"这一条要花多少额度 / 还缺什么 / 能不能生成"早就滚出视野了，
   而他正在做的恰恰是**改变这份账**的事。sticky 之后它一直跟着。
   `align-self:start` 是关键：不加的话 grid 会把这一栏拉伸到和左栏一样高（实测 3989px），
   拉伸过的元素 sticky 不起作用（它本来就"够高"，永远不需要粘）。 */
.generate-ledger{position:sticky;top:84px;align-self:start;max-height:calc(100vh - 104px);overflow:auto}
.generate-ledger .panel-body{display:flex;flex-direction:column;gap:10px}
.generate-ledger .panel-body>*{margin-top:0}
/* C｜预览挪到账下面之后，和上面那堆 notice 之间给一条分隔，读起来是两件事。 */
.generate-ledger-preview{margin-top:4px;padding-top:12px;border-top:1px solid var(--line)}
.generate-ledger-preview .phone{margin-left:auto;margin-right:auto}

/* E｜步骤条不许截断。
   改之前是四个等宽 flex 项 + `text-overflow:ellipsis`，于是「1. 选择商品和用途」被截成
  「1. 选择商品...」、「已选完，点这里回去改」被截成「已选完，点这...」——
   而那句话正是在告诉客户"这一步可以回去改"，截掉之后它什么都没说。 */
.wizard-steps{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px}
.wizard-step{min-width:0}
.wizard-step>button{width:100%;height:100%;align-items:flex-start;text-align:left}
.wizard-step-title,.wizard-step-state{white-space:normal;overflow:visible;text-overflow:clip;line-height:1.35}
.wizard-step-state{font-size:12px}

/* F｜左栏密度对齐电商图片工作台（同一套圆角/边框/间距），让两页看起来是一个产品。 */
.generate-ledger,.wizard-steps-wrap{border-radius:8px}
@media(max-width:1240px){
  /* 窄屏回落成普通单栏：先完成当前步骤，再看总账；核心表格不再被右栏挤窄。 */
  .generate-workspace{grid-template-columns:1fr}
  .generate-workspace>.wizard-panel{grid-column:auto}
  .generate-ledger{position:static;max-height:none;overflow:visible}
}

/* ---------- 2026-09-12 Boss 验收：待确认卡片压缩（TP-175） ----------
 * Boss 原话：「待确认列表这里的排版，按钮很大，而且有很多解释的文字。」
 * 实测一张健康卡 279px：预览图 227px、checklist 三行 71px、三个按钮各 42px 共 92px。
 * 一屏只装得下 4 张。
 *
 * 🔴 **验收 gate 的实质内容一个字都没少**：checklist 三条原文逐字保留，
 * 只是从三行竖排变成一行小字；`is-warn` 那条（人工确认 gate）用 ⚠ 顶替原来的 "!"，
 * 视觉权重反而更高——它是这张卡上最要紧的一句。
 * 🔴 文案一个字没改，下面全是排版。
 */
/* ③-3 预览图 180 → 150（卡片本身 227px 高的主要来源），一屏能多看两张 */
.studio-v4-review-draft{grid-template-columns:150px minmax(0,1fr);gap:12px;padding:10px}
.studio-v4-review-draft .studio-v4-review-draft-image img,
.studio-v4-review-draft .studio-v4-review-draft-image .studio-v4-empty{height:150px}
/* ③-4 时间戳并进标题行：标题和时间本来上下两行，占 37px；一行 22px 就够 */
.studio-v4-review-draft-head{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap;min-width:0}
.studio-v4-review-draft-head h4{margin:0;font-size:15px;line-height:1.3;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.studio-v4-review-draft-head time{color:var(--muted);font-size:12px;white-space:nowrap;flex:none}
/* ③-2 checklist 三行 → 一行小字。内容一个字没删，只是横着排。 */
.studio-v4-review-draft .studio-v4-checklist{display:flex;flex-wrap:wrap;gap:4px 12px;margin:8px 0 10px}
.studio-v4-review-draft .studio-v4-checklist li{font-size:12px;padding-left:16px;line-height:1.5}
.studio-v4-review-draft .studio-v4-checklist li.is-warn{color:var(--amber);font-weight:700}
.studio-v4-review-draft .studio-v4-checklist li.is-warn::before{content:"⚠";font-weight:700}
/* ③-1 按钮降一号：42 → 32。主按钮仍然是唯一的蓝，只是不再占掉三分之一张卡。 */
.studio-v4-review-draft .studio-v4-actions{gap:6px}
.studio-v4-review-draft .studio-v4-actions button{min-height:32px;padding:5px 12px;font-size:13px}
.studio-v4-review-draft .studio-v4-actions .link-btn{padding:5px 6px}

/* ══════════════════════════════════════════════════════════════════════════
 * 🔴 2026-09-12 UED 第二轮 P0-2/P0-3：**响应式规则必须排在所有无条件声明之后。**
 * ══════════════════════════════════════════════════════════════════════════
 * 缺陷的样子：1024 宽下「生成视频」四个步骤卡塌成 76×328px 的竖排单字，读不出来；
 * 1280 / 1440 正常。
 *
 * 根因是**层叠顺序**，不是哪条规则写错了：断点规则原来写在文件靠前的位置，
 * 而后面两段无条件声明（2026-09-12「UED 全局风格收口」和「生成视频改版」）
 * 又把同一批属性重新声明了一遍。同优先级下**后面的赢**，于是断点在窄屏上
 * 一条都没生效——`.app{grid-template-columns}`、`header{height,padding}`、
 * `#content{padding}`、`.wizard-steps{display,gap,grid-template-columns}` 全被盖死。
 *
 * 🔴 修法是**把这几段原样搬到文件最后**，不是给收口块里逐条补断点：
 * 逐条补等于把同一条规则写在两个地方，下一次再有人加一段收口块，同样的事会再来一次。
 * 搬到最后之后，"窄屏怎么排"这件事永远赢——这是它本来就该有的位置。
 * 🔴 搬的是**同一段字符**，一个字符都没改（改了的话就不只是修层叠了）。
 * ══════════════════════════════════════════════════════════════════════════ */
@media(max-width:1000px){.app{grid-template-columns:84px 1fr}.brand strong,.brand span,.nav-btn span,.tenant-switch,.aside-note{display:none}.cols-4,.cols-3,.cols-2{grid-template-columns:1fr 1fr}}@media(max-width:760px){.app{display:block}aside{position:sticky;top:0;z-index:10;flex-direction:row;overflow:auto;height:auto;align-self:auto}.brand{display:none}nav{display:flex}.nav-btn{white-space:nowrap}.nav-btn span{display:inline}header{height:auto;align-items:flex-start;flex-direction:column;padding:14px 16px}#content{padding:16px}.cols-4,.cols-3,.cols-2,.form-grid{grid-template-columns:1fr}.hero{align-items:flex-start;flex-direction:column}}
/* 🔴 断点从 1000 提到 1200：层叠修好之后 1024 仍然是 78×328px 的竖排单字——
   因为**那个宽度上本来就没有断点**（1024 > 1000）。1024 宽下正文区只有 800px，
   四等分每格 78px，「1. 选择商品和用途」在里面只能一个字一行。
   实测：1440 每格 130px / 1280 每格 110px 都读得下去，1024 必须换两行排。 */
@media(max-width:1200px){.wizard-steps{grid-template-columns:1fr 1fr}}
@media(max-width:1000px){.wizard-steps{grid-template-columns:1fr 1fr}}
@media(max-width:760px){.wizard-steps{grid-template-columns:1fr}}

/* ══════════════════════════════════════════════════════════════════════════
 * 🔴 B4（2026-09-13）：**"选中"只有一种画法。**
 * ══════════════════════════════════════════════════════════════════════════
 * 收之前量出来是 5 种画法、2 种色相：
 *   ① teal 3px 外环（`.card.selected` 旧版 / `.studio-thumb.is-picked` / `.card.script-card-selected`）
 *   ② teal 无外环（`.studio-scene-card.is-picked`）
 *   ③ blue 1px 内阴影（收口块里的 `.card.selected`）
 *   ④ amber 3px 外环（`.asset-not-recommended.selected`）
 *   ⑤ 实心蓝填充（`.studio-goal.is-active` —— 它是分段控件，**不在这一档里**，见下）
 * 同一个"我选了这个"在四个页面四种样子，客户学不会。
 *
 * 统一配方（**用蓝**，和主按钮同色）：`1px 蓝边 + 极浅蓝底 + 1px 内环`。
 * 🔴 `--accent`（#0f766e 青绿）是 `.primary` 的**原始**定义、现在被后面的蓝盖住——
 * 这一轮**没有把主按钮变回青绿**：`.primary` 仍然读收口块里的蓝。
 * 青绿保留给 `.how-to-fix` 这类"引导/通过"语义，那几处一个字没动。
 *
 * 🔴 例外一个：`.asset-not-recommended.selected`（"不建议但你还是选了"）**保留琥珀**——
 * 它要说的不是"选中了"，是"选中了而且我提醒过你"，那是 amber 的语义（待你处理）。
 * 把它并进蓝，会把一条警告降级成一个普通选中态。
 * 🔴 `.studio-goal.is-active` 是**分段控件的当前段**，不是"选中了一项内容"，
 * 保留实心蓝（和 Tab 同类），否则分段控件会看不出当前在哪一段。
 * ══════════════════════════════════════════════════════════════════════════ */
.card.selected,
.card.script-card-selected,
.studio-thumb.is-picked,
.studio-scene-card.is-picked{
  border-color:var(--blue);
  background:#f5f9ff;
  box-shadow:inset 0 0 0 1px rgba(39,103,231,.22);
}
/**
 * 🔴「刚动过的那张」——它**不是第二种选中态**，所以没有并进上面那一组。
 *
 * 2026-09-13 PM 定性的回归：视觉 token 收口那一轮把 5 种选中态并成 1 种时，
 * 把这个**第三种状态**一起并掉了。机制和 `.video-card-title` 那条截断、`--line` 那两个值
 * 是同一类事故：这条规则本来写在文件靠前的位置（第 375 行附近），而上面那组统一选中态
 * 写在后面、又设了 `border-color`——**后面的赢**，于是左边那道绿条被刷成了蓝，
 * 和普通已选卡一模一样。客户点完「重新生成」回来，找不到哪张是新的，
 * 而那正是他此刻唯一想看的那张。
 *
 * 🔴 修法是把它**移到统一选中态之后**（就是现在这个位置），不是再加一种选中色：
 * 它说的是"这是你刚才那一下的结果"，语义属于**已完成**，所以用 `--green`
 *（和 5 色规则一致：green = 已完成/通过）。整份文件里只有这一处定义它。
 *
 * 只改左边那道竖条的**颜色和粗细**，边框其余三边、底色、内阴影全部沿用统一选中态——
 * 所以它可以和"已选"叠加显示，而不是取代它。
 */
.card.script-card-just-changed{
  border-left-color:var(--green);
  border-left-width:5px;
  border-left-style:solid;
  padding-left:10px;
}

/* 🔴 这一条排在上面那组之后，所以"不建议但选了"仍然是琥珀（它是警告，不是普通选中）。 */
.asset-not-recommended.selected{
  border-color:var(--amber);
  background:#fff8eb;
  box-shadow:inset 0 0 0 1px rgba(184,102,24,.26);
}

/* ══════════════════════════════════════════════════════════════════════════
 * 🔴 B5（2026-09-13，PM 拍板）：`.tag` 的五种颜色各自背什么语义。
 * ══════════════════════════════════════════════════════════════════════════
 *   green  = 已完成 / 通过 **或不花钱**
 *   blue   = 分类 / 属性
 *   amber  = 待你处理 **或涉及额度**
 *   red    = 失败与阻断（含"这里坏了"）
 *   gray   = 禁用 / 不可用
 *
 * 🔴 amber 背"钱"这一条是**跟着产品既有语义走**，不是跟规则走：
 * 图片工作台那个「扣 3 次 L2」一直是琥珀 pill，客户已经学会了"琥珀 = 要花钱"。
 * 额度流水里的 `-1 次 L2` 原来是**红色**——那会把一笔正常扣费说成一次故障；
 * 换成蓝色则会让它淹没在一堆分类标签里，而"扣费不许被淹没"正是这两天一直在防的事。
 *
 * 🔴 green 的「免费」和 amber 的「扣 N 次」是**一对**（不花钱 / 花钱），不拆散。
 * 🔴 「文件缺失，不计入」从灰改红：它不是"正常的禁用"，是**这条素材坏了**，必须显眼。
 * ══════════════════════════════════════════════════════════════════════════ */

/* ══════════════════════════════════════════════════════════════════════════
 * 🔴 2026-09-13 第 1 批：表格的「操作」列一律靠右，单元格顶对齐。
 * ══════════════════════════════════════════════════════════════════════════
 * 收之前四张表的操作列都是左对齐：按钮紧贴着它左边那一列的文字，
 * 而那一列长短不一，于是同一张表里"可以点的东西"每一行都在不同的横向位置上。
 * 靠右之后它们排成一条竖线——客户的眼睛只要走一条线。
 * `vertical-align:top` 一并统一：有的行有两行参数、有的只有一行，
 * 居中会让同一行里的时间、状态、按钮各自浮在不同高度上。
 * ══════════════════════════════════════════════════════════════════════════ */
td.table-actions,th.table-actions{text-align:right;white-space:nowrap}
td.table-actions>*{margin-left:6px}
th,td{vertical-align:top}

/* 🔴 2026-09-13：视频卡标题不再单行省略。
   1280 宽下实测截掉 18%，而被砍掉的正好是「· 15秒 · #026」——
   区分度最高的那一截（同一个商品同一个钩子的几条片子，靠的就是时长和编号）。
   改成最多两行、按词断行；`title` 上仍然挂着全文。

   🔴 第 2 批复查时发现这条**当时根本没生效**：前面还有一条
   `.video-card h4.video-card-title{…white-space:nowrap;text-overflow:ellipsis}`，
   选择器权重 (0,2,1) 压过这里的 (0,1,0)——写在后面也没用，权重不是顺序。
   跟 `.primary` 那三处定义、`--line` 那两个值是同一类事故：同一个属性两处说法。
   已经把前面那条里冲突的三个属性删掉，现在整份文件里只有这里声明这个截断方式。 */
.video-card-title{white-space:normal;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;line-height:1.35;overflow-wrap:anywhere}
/* 🔴 订阅页 1024 宽下 nowrap 溢出 12px：那几处是"剩余 N 次""已用 X%"这类短句，
   允许它们在窄屏折行，比溢出好——溢出会把整页推出一个横向滚动条。 */
@media(max-width:1100px){
  .progress-meta{flex-wrap:wrap;row-gap:4px}
  .progress-meta>span{white-space:normal}
}

/* 🔴 2026-09-13：看大图那个角标从 26×26 放到 32×32。
   它原来是全站最小的可点目标（实测 26×38 的命中区），而同一格现在还盖着一层
   "点图本体也能看大图"的透明按钮——两个目标叠在一起时，小的那个更需要够得着。
   🔴 `min-height` 要盖掉上一轮 B3 那两档尺寸 token（角标是纯图标，不走文字按钮那两档）。 */
.media-preview-btn{width:32px;height:32px;min-height:32px;font-size:14px}

/* 🔴 2026-09-13（第一轮 UED review P2 第 4 条）：数据回填两栏不等高。
   实测右栏「成交归因」1022px 高、内容 249px，底下 773px 是被 grid 的默认 stretch 撑出来的空白。
   右栏按自己的内容高，并且粘住——往下滚着填表时那份账一直在视野里。
   🔴 窄屏（≤1240，和生成页右栏总账同一个断点）回落成普通流式：单栏里粘住只会挡住内容。 */
.perf-grid{align-items:start}
.perf-side{position:sticky;top:84px;align-self:start;max-height:calc(100vh - 104px);overflow:auto}
@media(max-width:1240px){
  .perf-side{position:static;max-height:none;overflow:visible}
}

/* 🔴 2026-09-13：卡片标题行允许换行，让**标签整枚掉到下一行**，而不是把标题挤成一列单字。
   背景：上一条给 `.tag` 加了 `white-space:nowrap;flex:none`（标签不许折行，这是对的），
   但用途卡这一轮又从 2 列改成了 4 列——卡只有 ~118px 宽，标签占住位置之后
   「商品页视频」被压成一字一行。实测截图上就是这个样子。
   `flex-wrap:wrap` 让这一行放不下时整枚标签换行，标题保持正常断行。
   🔴 标签自己仍然 nowrap：换的是**行**，不是把 pill 劈成两半。 */
.card h4{flex-wrap:wrap}

/* 2026-09-13（UED P2 第 11 条）那枚「N 张待确认」琥珀 pill（.studio-steps-pending）随流程条一起删了（TP-232 #9）：
   待确认数现在印在右栏标题上，那一栏本身就是列表，不再需要"滚过去"的入口。 */

/* 🔴 brief 六栏右上角的字数计数器（第一轮 UED review P2 第 12 条）。
   打满时标红——那一刻键盘会"没反应"，得让客户知道是到上限了，不是坏了。 */
.studio-brief-field>span{display:flex;justify-content:space-between;align-items:baseline;gap:8px}
.studio-brief-count{font-weight:700;font-size:12px;color:var(--muted);font-variant-numeric:tabular-nums}
.studio-brief-count.is-full{color:var(--amber)}

/* 🔴 素材库卡片上的三个下载口统一成一档（UED 第二轮漏下的那条）。
   原来「下载成品」是带框按钮、另外两个是裸链接，点击区只有 48×17 / 54×17px。
   .btn-sm 已经给了 32px 的最小高度，这里只补"三个排在一起时的间距和换行"。 */
.studio-file-link{text-decoration:none}

/* 🔴 商品中心状态看板：那三张计数卡现在是筛选开关（UED 文档「状态看板 + 商品表格」）。
   它们是 <button>，要压掉按钮的默认外观，长得和旁边那张「商品总数」一模一样——
   唯一的差别是能点（指针 + hover），选中时套一圈蓝，和全站统一的选中态同一个配方。 */
.metric-filter{
  min-height:104px;
  padding:15px 16px;  /* 和 .metric 一样（09-28 统一数字卡），以前 17px 比旁边那张「商品总数」胖一圈 */
  text-align:left;
  font:inherit;
  cursor:pointer;
  border:1px solid var(--line);
}
.metric-filter:hover{border-color:var(--blue)}
.metric-filter.is-on{border-color:var(--blue);background:#f5f9ff;box-shadow:inset 0 0 0 1px rgba(39,103,231,.22)}

/**
 * 2026-09-14 Boss 第三轮（素材库）。
 * ① 页顶「上传素材」主按钮：和商品筛选并排在 panel-head 右侧，不挤 h3 那一列。
 * ② 质检通则拆成三句：列表不带默认缩进，行距紧一点。
 * ③ 素材卡「更多」菜单：切换钮和旁边的小号次按钮同一档；展开后其余动作在卡内**另起一行铺开**
 *    （不做悬浮层——卡只有 170px 宽，悬浮层放不下，也容易被邻卡盖住）。
 */
/* TP-212：这一排不许被左边的标题 / 副标题挤到换行——「批量管理」开关必须和商品下拉待在同一行（Boss：成品库那样「在列表右上、紧挨商品下拉」）；副标题那边多折一行没关系。 */
.materials-toolbar-head{display:flex;gap:10px;align-items:center;flex-wrap:wrap;justify-content:flex-end;flex:none}
.materials-quality-note ul{margin:0;padding-left:18px;display:grid;gap:2px}
.asset-more-toggle::after{content:" ▾";font-size:11px}
.asset-more-toggle[aria-expanded="true"]{background:#f1f5f9}
.asset-more-toggle[aria-expanded="true"]::after{content:" ▴"}
.asset-more-menu{margin-top:-2px}
/* 🔴 `.materials-asset-actions{display:flex}` 会压过 UA 的 [hidden]，合上时得显式 none */
.asset-more-menu[hidden]{display:none}

/* 2026-09-14：「新增单个商品」下面的两条路（手动 / 链接导入）。锚在按钮上，向右对齐。 */
.menu-anchor{position:relative;display:inline-block}
.menu-pop{color:var(--ink);position:absolute;right:0;top:calc(100% + 6px);z-index:30;min-width:260px;background:#fff;border:1px solid var(--line);border-radius:8px;box-shadow:var(--shadow);padding:6px;display:grid;gap:4px;text-align:left}
.menu-pop button{display:grid;gap:2px;text-align:left;border:0;background:transparent;border-radius:6px;padding:9px 10px;font-weight:600;min-height:0}
.menu-pop button:hover{background:#f1f5f9}
.menu-pop button .muted{font-size:12px;font-weight:500}

/* 2026-09-14：审核卡上两张大图外面套一层，好挂「看大图」那个盖住整格的按钮（和素材库同一套 .asset-thumb-open）。 */
.studio-figure-slot{position:relative;display:block}
.studio-figure-slot .asset-thumb-open{min-height:0;padding:0}
.studio-v4-review-draft-image .asset-thumb-open{min-height:0;padding:0}

/* 2026-09-14 Boss：右栏「确认生成面板」——四行关键信息 + 风险短句 + 了解详情（默认收起）+ 按钮。 */
.studio-confirm-list{display:grid;grid-template-columns:64px minmax(0,1fr);gap:6px 10px;margin:0;font-size:13px}
.studio-confirm-list dt{color:var(--muted);font-weight:750}
.studio-confirm-list dd{margin:0;font-weight:600}
.studio-confirm-list dd[data-studio-confirm-op]{font-size:15px;font-weight:800}
.studio-confirm-risk{margin:8px 0 0;font-size:12px}
.studio-confirm-details{border:1px solid var(--line);border-radius:8px;padding:8px 10px;font-size:12px;color:var(--muted)}
.studio-confirm-details>summary{cursor:pointer;color:var(--accent);font-weight:750;list-style:none}
.studio-confirm-details>summary::-webkit-details-marker{display:none}
.studio-confirm-details>summary::after{content:" ▾"}
.studio-confirm-details[open]>summary::after{content:" ▴"}
.studio-confirm-details .studio-facts{margin-top:8px}
.studio-confirm-details .studio-facts h4{font-size:12px;margin:0 0 4px}
.studio-confirm-notes{display:grid;gap:6px;margin-top:8px;line-height:1.55}
.studio-confirm-notes p{margin:0}
.studio-confirm-cta .notice-warning{padding:8px 10px;font-size:12px}

/* ---------- TP-195：视频工作台「爆款视频复刻」（制作方式卡 / 分镜匹配表 / 逐镜文案卡 / 复刻方案面板） ---------- */
.generate-mode-card{min-height:96px}
/* 🔴 表要装进左栏（约 590px），不许把 grid 撑开压到右栏：固定布局 + 百分比列宽，单元格内换行。 */
.replicate-match-table{width:100%;table-layout:fixed}
.replicate-match-table td{vertical-align:top;font-size:13px;word-break:break-word}
.replicate-match-table th{white-space:nowrap}
.replicate-match-table th:nth-child(1){width:22%}
.replicate-match-table th:nth-child(2){width:28%}
.replicate-match-table th:nth-child(3){width:30%}
.replicate-match-table th:nth-child(4){width:20%}
.replicate-match-table td:last-child button{white-space:nowrap}
.material-health{border:1px solid var(--line);border-left:3px solid var(--green);border-radius:8px;padding:12px;margin:0 0 10px;background:#fff}
.material-health-improve{border-left-color:var(--accent)}
.material-health-missing{border-left-color:var(--amber)}
.material-health-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}
.material-health-head h4,.material-health-head p{margin:0}
.material-health-head p{margin-top:3px;font-size:13px}
.material-health-metrics{display:flex;gap:8px;flex-wrap:wrap;margin-top:10px}
.material-health-metrics span{font-size:12px;color:var(--muted);border-right:1px solid var(--line);padding-right:8px}
.material-health-metrics span:last-child{border-right:0;padding-right:0}
.material-health-note{margin:7px 0 0;font-size:12px}
.material-health-guide{margin-top:10px;border-top:1px solid var(--line);padding-top:10px}
.material-health-guide>summary{cursor:pointer;list-style:none;display:flex;justify-content:space-between;gap:12px;align-items:center}
.material-health-guide>summary::-webkit-details-marker{display:none}
.material-health-guide>summary::after{content:" ▾";color:var(--accent);font-weight:800}
.material-health-guide[open]>summary::after{content:" ▴"}
.material-health-guide>ol{margin:10px 0 0;padding:0;list-style:none;display:grid;gap:8px}
.material-health-task{border:1px solid var(--line);border-radius:8px;padding:10px;background:#f8fafc}
.material-health-task-head{display:flex;justify-content:space-between;gap:8px;align-items:center}
.material-health-task p{margin:5px 0;font-size:13px;line-height:1.5}
.material-health-task small{color:var(--muted)}
.material-health-actions{margin-top:10px;display:flex;justify-content:flex-end}
.replicate-match-asset{display:flex;gap:10px;align-items:flex-start}
.replicate-match-thumb{width:72px;height:72px;flex:0 0 72px;overflow:hidden;border-radius:6px}
.replicate-match-thumb .asset-thumb,.replicate-match-thumb .asset-thumb-video{width:72px;height:72px;min-height:0;margin:0}
.replicate-match-thumb img,.replicate-match-thumb video{width:72px;height:72px;object-fit:contain;border-radius:6px;border:1px solid var(--line);display:block}
.replicate-match-thumb .asset-thumb-caption{display:none}
.replicate-shot-card textarea{width:100%;box-sizing:border-box;font-size:13px}
.replicate-confirm-list{grid-template-columns:78px minmax(0,1fr)}

/* ---------- TP-198：复刻第二轮打磨（第 1 步字幕下拉 / 更多选项；第 2 步参考视频小播放器 + 质量档；第 3 步紧凑逐镜表；成品库折叠；首页能力行） ---------- */
.replicate-subtitle-row{display:flex;gap:12px;align-items:center;flex-wrap:wrap}
.replicate-subtitle-row .field{margin:0}
.replicate-subtitle-preview{flex:1 1 220px;min-width:0}
.replicate-subtitle-preview .subtitle-swatch{margin-top:0;min-height:48px;padding:8px 12px}
.replicate-more-options{border:1px solid var(--line);border-radius:8px;padding:10px 12px}
.replicate-more-options>summary{cursor:pointer;list-style:none;font-size:13px}
.replicate-more-options>summary::-webkit-details-marker{display:none}
.replicate-more-options>summary::after{content:" ▾";color:var(--accent)}
.replicate-more-options[open]>summary::after{content:" ▴"}
.replicate-reference-player{display:flex;gap:12px;align-items:flex-start;margin:0 0 12px;padding:10px;border:1px solid var(--line);border-radius:8px;background:#fafbfc}
.replicate-reference-video{width:96px;height:170px;flex:0 0 96px;border-radius:6px;background:#000;object-fit:contain;display:block}
.replicate-reference-meta{display:grid;gap:4px;font-size:13px;min-width:0}
.replicate-shot-table-head{display:flex;justify-content:space-between;align-items:center;gap:10px;flex-wrap:wrap;margin:0 0 8px}
.replicate-budget-line{font-weight:600;font-size:13px;color:var(--muted);margin-left:8px}
.replicate-budget-line.is-over{color:var(--red)}
/* 🔴 同匹配表：固定布局 + 百分比列宽，装进左栏不撑 grid。 */
.replicate-shot-table{width:100%;table-layout:fixed}
.replicate-shot-table th{white-space:nowrap}
.replicate-shot-table td{vertical-align:top;font-size:13px;word-break:break-word;padding:8px 10px}
/* TP-260：多了「中文（可选）」一列（第 5 列），六列重新分宽——泰语文案仍然最宽，预算那列够印两行字。 */
.replicate-shot-table th:nth-child(1){width:10%}
.replicate-shot-table th:nth-child(2){width:11%}
.replicate-shot-table th:nth-child(3){width:19%}
.replicate-shot-table th:nth-child(4){width:26%}
.replicate-shot-table th:nth-child(5){width:20%}
.replicate-shot-table th:nth-child(6){width:14%}
/* TP-260：刚按中文重写过的那一镜——泰语那格高亮 1.5 秒（和「刚改过」那道绿条同一套颜色，不另起一种绿）。 */
.replicate-shot-table td.is-zh-to-th-done{background:#f0fdf4;box-shadow:inset 0 0 0 2px var(--green)}
.replicate-zh-to-th-done{margin-top:4px;font-size:12px;color:var(--green);font-weight:750}
.replicate-shot-zh-cell textarea{min-height:0}
/* TP-262 追加 5 / 6 / 8：中文框下面那一行秒数估算（本机算的，零调用）。默认灰；差得多标黄；会撑不满绑定片段标红。 */
.replicate-zh-estimate{margin-top:4px;font-size:11px;line-height:1.45;color:var(--muted)}
.replicate-zh-estimate.is-warn{color:var(--amber);font-weight:700}
.replicate-zh-estimate.is-block{color:var(--red);font-weight:750}
.replicate-zh-estimate-note{margin-top:2px;font-weight:600}
/* review 2026-09-22：中文草稿没存上那一行——红且不跟着估算行的灰/黄走（这一格可能整格是灰的，
   但「没存上」永远是红的：这一单的承诺就是刷新不丢，存不上必须最显眼）。 */
.replicate-zh-estimate .replicate-zh-save-failed{color:var(--red);font-weight:750}
.replicate-shot-zh-cell .link-btn{margin-top:4px;font-size:12px;white-space:nowrap}
.replicate-shot-table .replicate-shot-detail textarea{min-height:0}
.replicate-shot-table .replicate-shot-toggle{padding:0;white-space:nowrap}
.replicate-shot-table input[type="text"],.replicate-shot-table textarea{width:100%;box-sizing:border-box;font-size:13px;border:1px solid #cbd4df;border-radius:6px;padding:7px 9px}
.replicate-shot-table tr.is-over>td{background:#fff5f4}
.replicate-shot-table tr.is-over input[type="text"],.replicate-shot-table tr.is-over textarea{border-color:#ffb3ab}
.replicate-shot-table tr.is-long>td{background:#fffbeb}
.replicate-shot-table tr.has-issue:not(.is-over):not(.is-long)>td{background:#fffbeb}
.replicate-shot-table .replicate-shot-detail>td{background:#f8fafc;padding-left:20px}
.replicate-shot-table .replicate-shot-detail .field{margin-top:6px}
.replicate-shot-table .replicate-shot-transcript{color:var(--muted)}
/* TP-209：成品库卡上的选段说明只剩一行「查看选段说明 · N 个镜头」，点它弹窗（原来是 <details> 折叠行就地展开）。 */
.video-shot-notes-collapsed{display:flex;align-items:center;flex-wrap:wrap;gap:0 2px;padding:8px 12px}
.video-shot-notes-link{padding:0;font-size:13px}
.capability-line{margin:10px 0 0;font-size:12px;display:flex;gap:6px;flex-wrap:wrap;align-items:center}
.capability-item{white-space:nowrap}
.capability-item.is-mock{color:var(--amber);background:#fff8eb;border:1px solid #f4d6a3;border-radius:999px;padding:2px 8px;white-space:normal}
.capability-sep{color:var(--muted)}
/* 复刻标签和标题同一行（标题是 -webkit-box 两行截断，inline-flex 的标签会被挤成单独一行）。 */
.video-card-title [data-video-mode-label]{display:inline;vertical-align:1px;margin-right:6px}

/* ---------- 🔴 TP-199：普通带货视频走「视频方案」流程（复用复刻的表；模板结构下第 3 列是「这一镜要说什么」） ---------- */
.replicate-shot-table .replicate-shot-goal{color:var(--muted)}
[data-plan-structure-section] .shot-structure{display:flex;flex-wrap:wrap;gap:4px;align-items:center}
.replicate-blocker-actions .next-step-action{margin:0;display:inline-block}
/* TP-61 D2 / TP-199：刚被这次动作改过的镜头——左边一道绿条 + 淡绿底，和「超预算」红、「有问题」黄分得开。 */
.replicate-shot-table tr.is-just-changed>td{background:#f0fdf4;box-shadow:inset 3px 0 0 var(--green)}

/* ---------- 🔴 TP-200：实拍视频按片段匹配（匹配表「用了『…』那段」/ 更换素材抽屉里的片段组 / 素材库卡上的片段列表） ---------- */
.replicate-segment-note{font-size:12px;margin:2px 0 4px;display:flex;gap:4px;align-items:center;flex-wrap:wrap}
.replicate-segment-note .tag{font-size:11px;padding:1px 6px}
.replicate-drawer-video{margin:0 0 12px;padding:8px;border:1px solid var(--line);border-radius:8px;background:#fafbfc;min-width:0}
.replicate-drawer-video-head{font-size:13px;margin:0 0 8px;display:flex;gap:6px;flex-wrap:wrap;align-items:center;min-width:0}
.replicate-drawer-video+.studio-thumbs,.studio-thumbs+.replicate-drawer-video{margin-top:10px}
.replicate-segment-thumb .studio-img{background:#0f172a}
.replicate-segment-chips{display:flex;gap:3px;flex-wrap:wrap}
.replicate-segment-chips .tag{font-size:10px;padding:1px 5px}
.replicate-segment-thumb[data-segment-role-hit="1"] .studio-thumb-name{color:var(--accent)}
.video-segments-block{margin-top:8px}
.video-segments-list{list-style:none;margin:8px 0 0;padding:0;display:grid;gap:8px}
/* 卡很窄（素材库网格一格 ~180px）：缩略图 + 一句话并排一行，角色勾选框整行流式排在下面，标签不折字。 */
.video-segment-row{display:grid;grid-template-columns:48px minmax(0,1fr);gap:6px 8px;align-items:start;padding:8px;border:1px solid var(--line);border-radius:8px;background:#fafbfc}
.video-segment-thumb{width:48px;height:48px;border-radius:6px;overflow:hidden;background:#0f172a}
.video-segment-thumb img{width:48px;height:48px;object-fit:contain;display:block}
.video-segment-body{min-width:0;font-size:12px;line-height:1.4;display:contents}
.video-segment-body>div:first-child{min-width:0;word-break:break-word}
.video-segment-body .tag{font-size:10px;padding:1px 5px;margin-left:2px;vertical-align:1px}
.video-segment-roles{grid-column:1/-1;display:flex;gap:4px;flex-wrap:wrap}
.video-segment-role{display:inline-flex;align-items:center;gap:3px;font-size:11px;white-space:nowrap;border:1px solid var(--line);border-radius:999px;padding:1px 7px;cursor:pointer;background:#fff}
.video-segment-role.is-on{border-color:var(--blue);background:#dbeafe;color:var(--blue)}
.video-segment-role input{margin:0;width:12px;height:12px}

/* ---------- 🔴 TP-201：「处理图片」弹窗减字重排（Boss：「文字太多，用户要被文字淹死。排版也不清晰」） ----------
 * 默认态每张卡只有：标题 + 价格 chip + 一句话 + 「了解详情」；长文案在 details 里，默认收起。
 * 六张卡等高：卡是 flex 列，「了解详情」用 margin-top:auto 压到底边——同一排里不管谁的字长一点，
 * 六个「了解详情」都在同一条线上。 */
.image-op-card{min-height:118px}
.image-op-card-lead{margin:0;color:var(--ink);font-size:13px;line-height:1.5}
.image-op-details{margin-top:auto;font-size:12px;color:var(--muted);cursor:auto}
.image-op-details>summary{cursor:pointer;color:var(--accent);font-weight:750;list-style:none;display:inline}
.image-op-details>summary::-webkit-details-marker{display:none}
.image-op-details>summary::after{content:" ▾"}
.image-op-details[open]>summary::after{content:" ▴"}
.image-op-details>div{margin-top:6px;display:grid;gap:6px;line-height:1.5}
.image-op-details>div p{margin:0}
/* 扣费组上方那一行：一句风险 + 同一行末尾的「了解详情」；展开后原来那三段按段落列在下面。 */
.image-op-risk{padding:10px 12px}
.image-op-risk-details{display:inline;margin:0 0 0 6px;font-size:12px;color:inherit}
.image-op-risk-details[open]{display:block;margin:6px 0 0}
.image-op-risk-details>div{color:var(--ink)}
/* 「替换图片内容」的描述框默认折成一行占位，点进去 / 有内容时展开到能看完一句提示词的高度（TP-74）。 */
.tier-card-input-fold textarea{height:38px;min-height:38px;font-size:13px;resize:none;overflow:hidden;transition:height .15s ease}
.tier-card-input-fold textarea:focus,.tier-card-input-fold textarea:not(:placeholder-shown){height:120px;resize:vertical;overflow:auto}
/* 底部那行小字（原标题下的蓝框原文）：靠左，按钮靠右。 */
.image-op-footnote{margin:0 auto 0 0;font-size:12px;line-height:1.5;align-self:center;max-width:60%}
/* 第 2 步匹配表：这一镜的图上已有文字（质检 burned_text），字幕会压字。 */
.replicate-burned-text-note{font-size:12px;color:var(--amber);margin:2px 0 4px}

/* ---------- 🔴 TP-259：第 2 步匹配表行内「这段比这一镜短，画面会重复」那一行（红 = 拦生成 / 黄 = 只提示） ---------- */
.replicate-footage-note{font-size:12px;margin:2px 0 4px;line-height:1.35}
.replicate-footage-note-block{color:var(--red)}
.replicate-footage-note-notice{color:var(--amber)}
.replicate-footage-actions{display:flex;gap:6px;flex-wrap:wrap;margin-top:4px}
.replicate-footage-actions button{font-size:12px;padding:3px 9px}
/* 「换更长的片段」抽屉里比这一镜短的那几段：灰着提醒，但**仍然能点**（这是提醒不是禁止）。 */
.studio-thumb.is-too-short{opacity:.5}
.studio-thumb.is-too-short .studio-thumb-badges{color:var(--red)}

/* ---------- 🔴 TP-202：图片素材的电商角色标签（匹配表「适合：…」/ 抽屉图片格上的 chip / 素材库图片卡的角色块） ---------- */
.replicate-roles-note{font-size:12px;margin:2px 0 4px;display:flex;gap:4px;align-items:center;flex-wrap:wrap}
.replicate-roles-note .tag{font-size:11px;padding:1px 6px}
.commerce-roles-block{margin-top:8px;font-size:12px}
.commerce-roles-line{display:flex;gap:4px;align-items:center;flex-wrap:wrap}
.commerce-roles-line .tag{font-size:10px;padding:1px 5px}
.commerce-roles-editor{margin-top:6px}

/* ---------- 🔴 TP-204：第 4 步「背景音乐」（情绪 chip 单选 + 曲子下拉 + 试听）/ 成品库卡「背景音乐」一行 ---------- */
.bgm-moods{display:flex;gap:6px;flex-wrap:wrap}
.bgm-mood-chip{display:inline-flex;align-items:center;gap:5px;font-size:13px;font-weight:700;white-space:nowrap;border:1px solid var(--line);border-radius:999px;padding:5px 12px;background:#fff;color:var(--ink);cursor:pointer}
.bgm-mood-chip small{font-size:11px;font-weight:600;color:var(--muted)}
.bgm-mood-chip.is-on{border-color:var(--accent);background:#e6f4f1;color:var(--accent)}
.bgm-mood-chip.is-on small{color:var(--accent)}
.bgm-mood-chip[disabled]{opacity:.55;cursor:not-allowed;background:#f8fafc}
.bgm-track-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-top:10px}
.bgm-track-row select{border:1px solid #cbd4df;border-radius:8px;padding:9px 12px;min-width:220px;max-width:100%}
/* TP-205：第 4 步「背景音乐」音量三档（轻 / 标准 / 响）那一行；chip 复用 .bgm-mood-chip */
.bgm-volume-row{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-top:10px}
.bgm-volume-row > .muted{font-size:12px}

/* ---------- 🔴 TP-206：PM 真机走查修补 + 成品库「最新生成」 ----------
 * ① 第 2 步匹配表：一格 = 缩略图 + 两行字（素材名 / 「用了『…』那段 · 起–止 秒」）+ 一行 chip（清晰度 / 类型 / 质量 / 自动·手选）。
 *    改之前四个 chip 各占一行（第 3 列 30% ≈ 160px，减去 72px 缩略图只剩 80px，什么都得折行），一镜 240px；
 *    现在第 3 列放宽到 44%、缩略图 56px、chip 行独占一整行（grid 第二行横跨两列），一镜 ≈ 100px，18 镜两屏看完。 */
.replicate-match-table th:nth-child(1){width:17%}
.replicate-match-table th:nth-child(2){width:22%}
.replicate-match-table th:nth-child(3){width:44%}
.replicate-match-table th:nth-child(4){width:17%}
.replicate-match-table td{padding:8px 8px}
.replicate-match-table td:first-child b{white-space:nowrap}
.replicate-match-asset{display:grid;grid-template-columns:56px minmax(0,1fr);gap:4px 8px;align-items:start}
.replicate-match-thumb{width:56px;height:56px;flex:0 0 56px}
.replicate-match-thumb .asset-thumb,.replicate-match-thumb .asset-thumb-video{width:56px;height:56px}
.replicate-match-thumb img,.replicate-match-thumb video{width:56px;height:56px}
.replicate-match-text{min-width:0;line-height:1.35}
.replicate-match-name{font-weight:700;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;word-break:break-word}
.replicate-match-text .replicate-segment-note,.replicate-match-text .replicate-roles-note,.replicate-match-text .replicate-burned-text-note{margin:2px 0 0;display:block;font-size:12px;line-height:1.35}
.replicate-match-text .replicate-roles-note .tag{font-size:10px;padding:0 5px;vertical-align:1px}
.replicate-match-chips{grid-column:1/-1;display:flex;gap:3px;flex-wrap:wrap;align-items:center;min-height:0}
.replicate-match-chips .tag{font-size:11px;padding:1px 5px}
/* TP-224：chip 行末尾那句服务端的匹配理由（「特写 + 推近，适合钩子」）：和 chip 同一字号、跟在 chip 后面；这一行剩的宽度装不下整句就按 flex-wrap 折到下一行（flex-basis auto），再装不下才省略号——不另起块、不改行距。 */
.replicate-match-chips .replicate-match-reason{font-size:11px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0;flex:1 1 auto;margin-left:2px}
/* 手机端按逐镜卡片复核。四列表在窄屏会把用途、素材名和操作压成竖排，无法完成实际选择。 */
@media(max-width:760px){
  .table-scroll:has(.replicate-match-table){overflow:visible}
  .replicate-match-table,.replicate-match-table tbody,.replicate-match-table tr,.replicate-match-table td{display:block;width:100%;box-sizing:border-box}
  .replicate-match-table thead{display:none}
  .replicate-match-table tr{border:1px solid var(--line);border-radius:8px;margin:0 0 10px;overflow:hidden;background:#fff}
  .replicate-match-table td{border:0!important;padding:10px 12px!important}
  .replicate-match-table td+td{border-top:1px solid var(--line)!important}
  .replicate-match-table td:first-child{background:#f8fafc;font-weight:700}
  .replicate-match-table td:nth-child(2)::before,.replicate-match-table td:nth-child(3)::before,.replicate-match-table td:nth-child(4)::before{display:block;margin:0 0 6px;color:var(--muted);font-size:11px;font-weight:750}
  .replicate-match-table td:nth-child(2)::before{content:"这一镜要做什么"}
  .replicate-match-table td:nth-child(3)::before{content:"匹配素材"}
  .replicate-match-table td:nth-child(4)::before{content:"操作"}
  .replicate-match-table td:last-child{display:flex;gap:8px;align-items:center;flex-wrap:wrap}
  .replicate-match-table td:last-child::before{flex:0 0 100%;margin-bottom:0}
  .replicate-match-table td:last-child button{margin-top:0!important}
  .replicate-match-asset{grid-template-columns:64px minmax(0,1fr)}
  .replicate-match-thumb,.replicate-match-thumb .asset-thumb,.replicate-match-thumb .asset-thumb-video,.replicate-match-thumb img,.replicate-match-thumb video{width:64px;height:64px}
  .table-scroll:has(.replicate-shot-table){overflow:visible}
  .replicate-shot-table,.replicate-shot-table tbody,.replicate-shot-table .replicate-shot-row,.replicate-shot-table .replicate-shot-row>td{display:block;width:100%;box-sizing:border-box}
  .replicate-shot-table thead{display:none}
  .replicate-shot-table .replicate-shot-row{border:1px solid var(--line);border-radius:8px;margin:0 0 10px;overflow:hidden;background:#fff}
  .replicate-shot-table .replicate-shot-row>td{border:0!important;padding:10px 12px!important}
  .replicate-shot-table .replicate-shot-row>td+td{border-top:1px solid var(--line)!important}
  .replicate-shot-table .replicate-shot-row>td:first-child{background:#f8fafc}
  .replicate-shot-table .replicate-shot-row>td[data-cell-label]::before{content:attr(data-cell-label);display:block;margin:0 0 5px;color:var(--muted);font-size:11px;font-weight:750}
  .replicate-shot-table .replicate-shot-detail{display:block;margin:-10px 0 10px}
  .replicate-shot-table .replicate-shot-detail>td{display:block;width:100%;box-sizing:border-box;border:1px solid var(--line);border-top:0;border-radius:0 0 8px 8px;padding:10px 12px}
}
/* ② 第 3 步展开行只剩画面提示 / 原字幕，两行之间留一点缝。 */
.replicate-shot-table .replicate-shot-detail>td p+p{margin-top:4px!important}

/* ③ 成品库顶部「最新生成」块（Boss 方案 §10）：左边播放器（9:16，高度按视口收）+ 右边标题 / 状态 / 五个动作 / chip / 提示。 */
.latest-video{margin-bottom:16px;border-color:#99f6e4;box-shadow:0 0 0 3px rgba(15,118,110,.08),var(--shadow)}
.latest-video .panel-head{background:linear-gradient(90deg,#f0fdfa,#fff)}
.latest-video-body{display:flex;gap:18px;align-items:flex-start}
.latest-video-stage{flex:none;width:224px}
.latest-video-player{display:block;width:224px;aspect-ratio:9/16;max-height:min(400px,60vh);border-radius:8px;background:#000;object-fit:contain}
.latest-video-cover-wrap{position:relative;width:224px}
.latest-video-cover-wrap .video-cover-slot{width:224px;height:auto;aspect-ratio:9/16;max-height:min(400px,60vh)}
.latest-video-cover-wrap .video-cover-fallback{font-size:14px}
.latest-video-cover-wrap .video-cover-fallback span:first-child{font-size:15px}
/* 生成中：封面位底部一条来回跑的细线——只表示「还在跑」，不是进度百分比（没有百分比可给）。 */
.latest-video-progress{position:absolute;left:0;right:0;bottom:0;height:4px;background:#e2e8f0;overflow:hidden}
.latest-video-progress span{position:absolute;top:0;bottom:0;width:38%;background:var(--accent);border-radius:999px;animation:latest-video-sweep 1.4s ease-in-out infinite}
@keyframes latest-video-sweep{0%{left:-38%}100%{left:100%}}
.latest-video-main{min-width:0;flex:1;display:flex;flex-direction:column;gap:8px}
.latest-video-main p{margin:0}
.latest-video h4.latest-video-title{font-size:17px;line-height:1.35;display:block}
.latest-video-title [data-video-mode-label]{display:inline;vertical-align:2px;margin-right:6px}
.latest-video-generating{border:1px solid #fde68a;background:#fffbeb;border-radius:8px;padding:10px 12px;font-size:13px;line-height:1.5}
.latest-video-steps{list-style:none;margin:6px 0 0;padding:0;display:flex;gap:14px;flex-wrap:wrap;font-size:13px}
.latest-video-steps li{display:inline-flex;align-items:center;gap:6px;color:var(--muted)}
.latest-video-step-index{width:20px;height:20px;border-radius:999px;border:1px solid #cbd4df;display:inline-grid;place-items:center;font-size:11px;font-weight:800;background:#fff}
.latest-video-steps li.is-running{color:var(--ink);font-weight:750}
.latest-video-steps li.is-running .latest-video-step-index{border-color:var(--accent);background:var(--accent);color:#fff}
.latest-video-steps li.is-done{color:var(--green)}
.latest-video-steps li.is-done .latest-video-step-index{border-color:var(--green);background:#dcfce7;color:var(--green)}
.latest-video-steps li.is-failed{color:var(--red)}
.latest-video-actions{margin-top:2px;gap:8px}
.latest-video-actions button,.latest-video-actions .button-link{padding:8px 14px;font-size:13px}
/* 「下载成片」主按钮用全局 .primary（蓝，TP-181 一处定义），这里不另写颜色。 */
.latest-video-actions .button-link.is-disabled{opacity:.5;cursor:not-allowed;pointer-events:none}
/* TP-208 §1.2：「删除」和列表卡一样是带边框的 button.danger（原来是裸链接 link-muted，Boss 问「没外线框是不是 bug」）。 */
.latest-video .video-card-notice{margin-top:2px}
@media(max-width:760px){.latest-video-body{flex-direction:column}.latest-video-stage,.latest-video-player,.latest-video-cover-wrap,.latest-video-cover-wrap .video-cover-slot{width:100%;max-width:260px}}

/* ④ 「查看使用素材」弹窗：每镜一行（缩略图 + 镜头 N · 秒 · 素材名 · 类型 chip / 选段说明）。 */
.video-assets-list{list-style:none;margin:0;padding:0;display:grid;gap:8px}
.video-assets-row{display:grid;grid-template-columns:56px minmax(0,1fr);gap:10px;align-items:start;padding:8px;border:1px solid var(--line);border-radius:8px;background:#fafbfc;font-size:13px;line-height:1.45}
.video-assets-thumb{width:56px;height:56px;border-radius:6px;overflow:hidden}
.video-assets-thumb .asset-thumb,.video-assets-thumb .asset-thumb-video{width:56px;height:56px;min-height:0;margin:0}
.video-assets-thumb img,.video-assets-thumb video{width:56px;height:56px;object-fit:contain;display:block;border-radius:6px;border:1px solid var(--line)}
.video-assets-thumb .asset-thumb-caption{display:none}
.video-assets-body{min-width:0}
.video-assets-body .tag{font-size:11px;padding:1px 6px;vertical-align:1px}
/* ---------- TP-207：素材删不掉时的出口 + 成品库定位高亮 ----------
   红框里是一句人话 + 编号列表（可点）+「去成品库看这 N 条」/「知道了」；不再是整段口播。
   成品库那几张卡高亮 3 秒（`.is-highlighted`，由 app.js 的 settleVideoHighlight 起表 / 收掉）。 */
/* 追加后红框本体走 .notice-compact（下面那段）；这里只剩 ⓘ 浮层里的编号列表。 */
.asset-delete-blocked-text{margin:0}
.asset-delete-refs{margin:0;padding:0;list-style:none;display:grid;gap:4px}
.asset-delete-refs li{display:flex;align-items:center;flex-wrap:wrap;gap:2px 6px;min-width:0;font-size:12px;color:var(--ink)}
.asset-delete-ref-serial{padding:0 2px;font-size:12px;flex:none}
/* 素材卡很窄（一行 5～6 张）：短标题另起一行、可换行，不用省略号把它截成「痛…」。 */
.asset-delete-ref-title{flex:1 0 100%;min-width:0;color:var(--muted);line-height:1.35;word-break:break-word}
.asset-delete-refs .tag{flex:none;font-size:11px;padding:1px 6px}
.card.video-card.is-highlighted,.latest-video.video-card.is-highlighted{outline:3px solid var(--accent);outline-offset:2px;box-shadow:0 0 0 6px rgba(15,118,110,.18);animation:video-card-highlight 1s ease-in-out 3}
@keyframes video-card-highlight{0%,100%{box-shadow:0 0 0 6px rgba(15,118,110,.18)}50%{box-shadow:0 0 0 10px rgba(15,118,110,.06)}}
/* ---------- TP-207 追加（Boss：错误提示不要那么长）：一行摘要 + ⓘ 详情 + 同行按钮 ----------
   默认态只有摘要那一行（窄卡上自动折行，最多两行）；全文 / 编号列表在 ⓘ 的浮层里（复用 .info-hint 的 hover / 点击）。 */
.notice-compact{display:flex;align-items:center;flex-wrap:wrap;gap:6px 8px;padding:8px 10px}
.notice-compact-summary{flex:1 1 160px;min-width:0;line-height:1.4}
.notice-compact-actions{display:inline-flex;align-items:center;gap:6px;flex-wrap:wrap}
.notice-compact-actions .primary{padding:6px 10px;min-height:auto}
.notice-compact .notice-detail{margin-left:4px;vertical-align:-3px}
/* 提示条里的 ⓘ 形状和全站一样，颜色跟提示条的文字走（TP-232：出错红、注意琥珀、中性说明灰）。 */
.notice-detail-toggle{color:var(--red)}
.notice-warning .notice-detail-toggle{color:var(--amber)}
/* TP-232：中性说明处的 ⓘ（确认面板 / 创意融合方向 / 草稿卡事实行）不是错误，用灰的。 */
.studio-confirm-line .notice-detail-toggle,.studio-param-head .notice-detail-toggle,.studio-v4-review-draft-facts .notice-detail-toggle{color:var(--muted)}
.notice-detail-body{color:var(--ink);font-weight:400;width:360px;max-width:min(360px,calc(100vw - 48px))}
/* TP-209：ⓘ 点击不再就地展开（is-inline 那套删了），改弹窗（.error-detail-modal-body，见文件末尾）；悬停浮层照旧。 */
.notice-detail-body .asset-delete-refs{margin-top:8px}
.notice-detail-body .asset-delete-blocked-text{color:var(--red)}
/* ---------- TP-208：成品库三件 ----------
   ① 选段说明对所有成片折叠（结构在 app.js renderVideoCardNotices，样式沿用 .video-shot-notes-collapsed）；
   ② 「最新生成」块的删除按钮走全局 button.danger；
   ③ 批量管理：工具条开关 / 卡片左上角勾选框 / 底部浮条 / 确认弹窗行 / 汇总。
   🔴 TP-210：素材库批量删除照同一套——勾选框 / 浮条 / 弹窗行 / 汇总的类名去掉 videos- 前缀成了两边共用的
   `.bulk-pick` / `.bulk-bar` / `.bulk-rows` / `.bulk-result`（成片那边的 DOM 也改用这些名字，data-* 钩子一个没动）。 */
.videos-toolbar{display:flex;gap:8px;align-items:center;flex-wrap:wrap;justify-content:flex-end}
/* 2026-09-16 Boss：素材库每个商品分节头右侧 = [批量管理][可先生成 标签]，和成品库「批量管理紧挨列表右上」同位。 */
.materials-section-tools{display:flex;gap:10px;align-items:center;flex:none}
/* TP-214 译文（Boss 2026-09-16：「翻译出来的内容换一个文字颜色，与原文区别开来」）：泰语原文是正文黑，中文译文用深青，过期 ⇒ 灰 + 半透明。 */
.replicate-zh{color:#0f766e;font-size:12px;white-space:pre-line}
.replicate-zh.is-stale{color:var(--muted)}
.bulk-toggle.is-active{border-color:var(--accent);color:var(--accent);background:#f4fbf9}
.latest-video-head{display:flex;gap:12px;align-items:flex-start;min-width:0}
.bulk-pick{flex:none;align-self:flex-start;display:flex;flex-direction:column;align-items:center;gap:4px;width:48px;padding-top:2px;cursor:pointer}
.bulk-pick input{width:20px;height:20px;margin:0;cursor:pointer;accent-color:var(--accent)}
.bulk-pick.is-locked{opacity:.55;cursor:not-allowed;width:auto;min-width:48px}
.bulk-pick.is-locked input{cursor:not-allowed}
.bulk-pick-lock-note{font-size:11px;line-height:1.25;color:var(--muted);text-align:center;white-space:nowrap}
/* 素材卡（TP-212，Boss：「和成品库对齐，勾在图片左边」）：批量态下缩略图那行是 [勾选列][缩略图] 横排——勾选框就是上面那副共用的 .bulk-pick
   （48px 一列、不压图），原来 TP-210 那条「绝对定位压在缩略图左上角」的 .asset-bulk-pick 特化删了。
   缩略图 + 「看大图」那层收进 .asset-thumb-media（position:relative，.asset-thumb-open 的 inset:0 只盖住图）；非批量态没有这两个 class，一像素不变。 */
.asset-thumb-slot.is-bulk{display:flex;gap:8px;align-items:flex-start}
.asset-thumb-media{position:relative;flex:1 1 auto;min-width:0}
.materials-asset-grid .card .asset-bulk-pick input{min-height:0}
.card.video-card.is-bulk-selected,.latest-video.video-card.is-bulk-selected,.card.asset-card.is-bulk-selected{border-color:var(--accent);background:#f4fbf9;box-shadow:0 0 0 3px rgba(15,118,110,.1)}
.card.video-card.is-bulk-locked,.latest-video.video-card.is-bulk-locked,.card.asset-card.is-bulk-locked{opacity:.7}
.bulk-bar{position:sticky;bottom:12px;z-index:6;display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-top:16px;padding:10px 14px;background:#111827;color:#f9fafb;border-radius:8px;box-shadow:var(--shadow)}
.bulk-bar b{font-size:15px}
.bulk-bar-hint{font-size:12px;color:#cbd5e1}
.bulk-bar-actions{margin-left:auto;display:inline-flex;gap:8px;align-items:center}
.bulk-bar button{padding:7px 12px;font-size:13px}
.bulk-bar button.danger:disabled{opacity:.5;cursor:not-allowed}
.bulk-result{margin-bottom:16px}
.bulk-failures{margin:0;padding-left:18px;display:grid;gap:6px}
.bulk-failures .asset-delete-refs{margin-top:4px}
.bulk-rows{list-style:none;margin:8px 0 0;padding:0;display:grid;gap:6px}
.bulk-row{padding:8px 10px;border:1px solid #fecaca;border-radius:6px;background:rgba(255,255,255,.7);font-size:13px;line-height:1.45;color:var(--ink)}
.modal [data-bulk-delete-assets-rows]{margin-top:0;max-height:220px;overflow:auto}
.modal [data-bulk-delete-assets-rows] .bulk-row{border-color:var(--line)}
.videos-bulk-row-detail{font-size:12px;margin-top:2px}
.videos-bulk-row-error{font-size:12px;margin-top:2px;color:var(--red)}
/* TP-210：「素材类型」弹窗（勾类型 + 保存）——原来是卡里就地展开的下拉。 */
.asset-classify-options{display:flex;flex-wrap:wrap;gap:8px}
.asset-classify-option{display:inline-flex;align-items:center;gap:6px;padding:6px 10px;border:1px solid var(--line);border-radius:999px;font-size:13px;cursor:pointer;background:#fff}
.asset-classify-option.is-on{border-color:var(--accent);background:#f4fbf9;color:var(--accent)}
.asset-classify-option input{margin:0;accent-color:var(--accent)}
.asset-classify-note{margin-top:12px;padding:10px 12px;border:1px solid var(--line);border-radius:8px;font-size:13px;line-height:1.5;background:#fafbfc}
/* ---------- TP-209（Boss：点击展开的内容都改成像「看话术全文」那种弹窗）----------
   四种信息弹窗（选段说明 / 素材判定说明 / 片段列表 / 改角色）+ ⓘ 详情弹窗共用一副壳：
   形态同 .modal-video-script（遮罩由 .modal-overlay 提供，弹窗内部自己滚，背景不跟着滚）。 */
.modal-info{width:min(720px,100%);max-height:88vh}
.modal-info.modal-wide{width:min(860px,100%)}
.modal-info .info-modal-panel{display:flex;flex-direction:column;flex:1 1 auto;min-height:0}
.modal-info .info-modal-panel>.modal-body{flex:1 1 auto;min-height:0;overflow:auto;overscroll-behavior:contain}
.info-modal-lead{margin:0 0 12px;font-size:13px;line-height:1.5}
.info-modal-notice{margin:0}
.info-modal-notice .notice-items{margin-top:0}
.info-modal-actions{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-top:12px}
.info-modal-issues{margin:0;padding-left:18px;display:grid;gap:6px;font-size:13px;line-height:1.5}
.modal-info .video-segments-list{margin-top:0}
.modal-info .video-segment-row{grid-template-columns:64px minmax(0,1fr)}
.modal-info .video-segment-thumb,.modal-info .video-segment-thumb img{width:64px;height:64px}
.modal-info .video-segment-body{font-size:13px}
.modal-info .video-segment-role{font-size:12px;padding:2px 9px}
.modal-info .commerce-roles-editor{margin-top:0}
.error-detail-modal-body{font-size:13px;line-height:1.6;color:var(--ink)}
.error-detail-modal-body .asset-delete-blocked-text{color:var(--red)}
.error-detail-modal-body .asset-delete-refs{margin-top:10px}
/* ---------- TP-211：删专属音色 ----------
   ① 语音样本卡 409 红框里那颗「删除这个专属音色」（notice-compact-actions 里，和「知道了」同一行）+ 失败时的一句红字；
   ② 第 4 步音色卡右上「更多 ▾」→ 浮层菜单（同商品页「新增单个商品 ▾」那套 .menu-anchor/.menu-pop，不改布局）；
   ③ 说明态（内置 / 正被生成中的成片用着）是一句灰字，不是按钮。圆角只用 8 / 6 / 999。 */
.voice-delete-action{display:inline-flex;align-items:center;gap:6px;flex-wrap:wrap}
.voice-delete-error{font-size:12px;color:var(--red)}
.voice-delete-blocked{font-size:12px}
.voice-card-head{display:flex;align-items:flex-start;justify-content:space-between;gap:8px}
.voice-card-head h4{flex:1 1 auto;min-width:0}
.voice-card-more{flex:0 0 auto}
.voice-more-pop{min-width:240px}
.menu-pop button.danger-text{color:var(--red)}
.menu-pop button.danger-text .muted{color:var(--muted)}
.menu-pop button[disabled]{opacity:.6;cursor:default}
/* ---------- TP-215：素材库卡片改成成品库那种「一行两张、图左文右」横卡 ----------
   Boss 2026-09-16：「素材库也改成『成品库』的两个卡片一行的显示，看起来更整齐，现在的素材库很多行高不一样的……图片在左边，文字和操作在图片右边」。
   标杆 = `.card.video-card / .video-card-summary / .video-cover-slot / .video-card-main / .video-card-actions`（≈122～144）。
   ① 网格：一行两张（`repeat(2,minmax(0,1fr))`）+ `align-items:stretch` ⇒ 同排两张一样高；窄于 900px 一列。
      🔴 只作用在素材库页的分类格子（`.materials-category` 之下）——`.materials-asset-grid` 这个 class 「新增单个商品 · 链接导入」弹窗里的主图勾选格也在用，那边不改。
   ② 卡：`.asset-card-summary = [缩略图盒子 132×132][右栏 flex:column]`，右栏底部动作行 `margin-top:auto` 贴底，多出来的空白落在正文和按钮之间（tp184 检查 3 那条规矩）。
   ③ 缩略图盒子尺寸**一致**是"整齐"的关键：图片 contain 白底、视频帧 contain 黑底 + ▶、透明图棋盘格、语音 / PDF 占位块，全都装进同一个 132×132 的盒子；「看大图」那层仍只盖住盒子。
   ④ 标题两行截断（`-webkit-line-clamp:2`，和 `.video-card-title` 同法），80 字的文件名也不把卡撑高。
   ⑤ 「更多 ▾」改悬浮下拉（`.menu-anchor` + `.menu-pop`，同商品页「新增单个商品 ▾」/ 第 4 步音色卡那套），不再在卡内另起一行铺开——
      ≈1339 那段「不做悬浮层：卡只有 170px 宽放不下」的理由横卡之后不成立了。开着菜单的那张卡整体抬到 z-index 31（`:has`），盖住下一行卡里
      position:relative 的缩略图盒子和 opacity 造出来的层叠上下文（批量态锁定卡 `.is-bulk-locked{opacity:.7}`）。
   ⑥ 🔴 批量态（TP-212 `[勾选列][.asset-thumb-media]`）：勾选列 **32px + 8px 间距从缩略图盒子里分**（图收窄到 92×132），盒子外沿仍是 132 ⇒
      右栏宽度在开关前后一个像素不变。成品库那边是右栏收窄——素材卡不能照抄：右栏一变窄，任何一段折行的文字（风险块、409 红框、适合…）都可能多出一行，
      卡高就跳了，tp212 检查 ③ / 本单 §2.2 钉的正是「开关前后卡高和网格 top 集合不变」。锁定卡的「分析中不能删」在 32px 列里折成两行 10px 小字。 */
.materials-category .materials-asset-grid{grid-template-columns:repeat(2,minmax(0,1fr));align-items:stretch}
.materials-category .materials-asset-grid>.card{align-self:stretch;gap:0}
@media(max-width:900px){.materials-category .materials-asset-grid{grid-template-columns:1fr}}
.asset-card-summary{display:flex;gap:12px;align-items:stretch;flex:1 1 auto;min-height:var(--asset-thumb-size);min-width:0}
.materials-category .materials-asset-grid>.card .asset-thumb-slot{flex:none;width:var(--asset-thumb-size);height:var(--asset-thumb-size)}
.asset-card-main{min-width:0;flex:1 1 auto;display:flex;flex-direction:column;gap:6px}
.asset-card-actions{margin-top:auto;align-items:center}
.materials-category .materials-asset-grid>.card h4.asset-card-title{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:20px;height:20px;font-size:14px}
/* ---------- TP-215 二次（Boss 12:12 截图「节日主图」：「文字和图片应该对齐，现在文字凸出来了。不够位置就收起部分信息……简化，对齐」）----------
   右栏定型：高度 = 缩略图盒子高度（132）、最多 5 行、每行单行（溢出省略号）——4 行 × 20px + 动作行 32px + 4 × 4px 间距 = 128 ≤ 132，动作行 margin-top:auto 贴底。
   🔴 右栏**不**加 overflow:hidden：「更多」悬浮下拉和 ⓘ 的悬停浮层都是它的后代，一裁就没了；单行的截断落在每一行自己身上
   （`.asset-line-text` / `.asset-line-chips` overflow hidden），行尾那几个固定项（改 / 质检 ⓘ / 仅学习结构 ⓘ）flex:none 永远看得见。
   标题从一轮的两行截断改成**一行**——两行标题 + 4 行内容装不进 132。 */
.asset-card-main{height:var(--asset-thumb-size);gap:4px;justify-content:flex-start}
.asset-card-line{flex:none;display:flex;align-items:center;gap:6px;height:20px;min-width:0;white-space:nowrap;font-size:13px;margin:0}
.asset-card-line.asset-card-meta{height:18px;line-height:18px;font-size:12px;display:block;overflow:hidden;text-overflow:ellipsis}
.asset-card-line .asset-line-text,.asset-line-text{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px}
.asset-line-label{flex:none;font-size:12px}
.asset-line-chips{flex:0 1 auto;min-width:0;display:flex;flex-wrap:nowrap;align-items:center;gap:6px;overflow:hidden;white-space:nowrap}
.asset-line-fixed{flex:none;display:inline-flex;align-items:center;gap:4px}
.asset-card-line .tag{font-size:11px;padding:2px 7px;line-height:1.3;white-space:nowrap;flex:none}
.asset-card-line button.link-btn,.asset-card-line button.link-muted{min-height:0;padding:0 2px;font-size:12px;line-height:20px;flex:none;white-space:nowrap}
.asset-card-line.commerce-roles-block{margin-top:0}
.reference-risk-chip{display:inline-flex;align-items:center;gap:2px}
.reference-risk-chip .info-hint{margin-left:2px}
/* 09-28：素材卡里那条 `.materials-asset-grid .card .info-hint-toggle`（18px）优先级更高，以前把这里的 16px 盖掉了（TP-188 检查 1 一直因此红）。 */
.reference-risk-chip .info-hint-toggle,.materials-asset-grid .card .reference-risk-chip .info-hint-toggle{width:16px;height:16px;font-size:11px}
/* 例外：409 / 分析失败那种红框是临时态，作为整卡下方一行（横跨全宽、不挤右栏）。 */
.asset-card>.asset-card-notice{margin-top:8px}
.info-modal-section{margin:12px 0 6px;font-size:13px}
.info-modal-summary{margin:0;font-size:13px;line-height:1.6}
/* 批量态：勾选列 + 间距从 132 的盒子里分，右栏不动（见上面 ⑥）。`.bulk-pick{width:48px}` / `.bulk-pick.is-locked{width:auto;min-width:48px}` 是两边共用的，只在素材卡的盒子里收窄。 */
.materials-category .materials-asset-grid>.card .asset-thumb-slot.is-bulk .asset-bulk-pick,.materials-category .materials-asset-grid>.card .asset-thumb-slot.is-bulk .asset-bulk-pick.is-locked{width:32px;min-width:0}
.asset-thumb-slot.is-bulk .asset-bulk-pick .bulk-pick-lock-note{white-space:normal;font-size:10px;line-height:1.2}
.asset-thumb-slot.is-bulk .asset-thumb-media{height:var(--asset-thumb-size)}
/* 「更多 ▾」悬浮下拉：挂在切换钮正下方、左对齐（切换钮不在卡的右缘，右对齐会把菜单甩出卡外）；盒子的样子由 .menu-pop 统一。 */
.asset-more-anchor{flex:none}
.asset-more-menu.menu-pop{left:0;right:auto;margin-top:0;min-width:220px}
.asset-more-menu.menu-pop .button-link{display:flex;align-items:center;justify-content:flex-start;text-align:left;border:0;background:transparent;border-radius:6px;font-weight:600}
.asset-more-menu.menu-pop .button-link:hover{background:#f1f5f9}
.asset-more-menu.menu-pop button{display:flex;align-items:center;text-align:left}
.asset-more-menu.menu-pop button.danger{color:var(--red)}
.asset-more-menu.menu-pop button.danger:hover:not(:disabled){background:#fef2f2}
.asset-card:has(.asset-more-menu:not([hidden])){position:relative;z-index:31}
/* 2026-09-16：图片工作台等待块压成一行（预计 · 已等 · 扣费口径 ⓘ + 停止等待） */
.wait-compact-line{display:flex;align-items:center;gap:6px;flex-wrap:wrap;font-size:13px}
.wait-compact-line .info-hint{display:inline-flex}
/* TP-232（PM review TP-231 顺延）：进行中块只剩 ⓘ + 「后台继续跑」，整块压到一行高——内边距和按钮跟着收。 */
.notice.wait-compact{padding:4px 10px}
.wait-compact-line>button{padding:4px 10px;min-height:auto;font-size:12px;line-height:1.4}
/* TP-232：图片工作台减字 */
.studio-confirm-line{display:flex;align-items:center;gap:6px;flex-wrap:wrap;font-size:13px;font-weight:600;min-height:20px}
.studio-confirm-line .info-hint{display:inline-flex}
.studio-param-head{display:flex;align-items:center;gap:6px}
.studio-param-head .info-hint{display:inline-flex}
.studio-gate-line{font-size:12px}
.studio-gate-line .notice-compact-summary{font-weight:700}
.studio-type-blocked-chip{font-weight:700}
.studio-v4-review-draft-body>p.studio-v4-review-draft-facts{display:flex;align-items:center;gap:4px 8px;flex-wrap:wrap;margin:0 0 8px;font-size:12px}
.studio-v4-review-draft-facts .info-hint{display:inline-flex}
.studio-v4-review>p.studio-v4-review-empty{margin:14px 16px 16px;font-size:13px}
/* 2026-09-17：片段列表「预览这一段」——同一个原文件 + #t=start,end 媒体片段，不另存切片 */
/* .video-segment-body 是 display:contents，播放器直接落进 li 的 grid：占满两列 */
.video-segment-player{grid-column:1 / -1;margin-top:4px}
.video-segment-player video{width:100%;max-height:360px;background:#000;border-radius:6px}
.video-segment-row.is-previewing{border-color:var(--accent)}
/* 视频开场标题：编辑控件与真实成片保持同一条「上半部、避开右侧按钮区」规则。 */
.video-title-control{border-top:1px solid var(--line);padding-top:14px}
.video-title-control-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;margin-bottom:10px}
.video-title-control-head h4,.video-title-control-head p{margin:0}
.segmented-control{display:inline-flex;border:1px solid #cbd4df;border-radius:8px;overflow:hidden;flex:none}
.segmented-control button{border:0;border-radius:0;padding:7px 12px;color:var(--muted)}
.segmented-control button+button{border-left:1px solid #cbd4df}
.segmented-control button.active{background:var(--accent);color:#fff}
.phone-video-title{position:absolute;z-index:2;top:10%;left:5%;right:24%;text-align:center;font-size:30px;line-height:1.08;font-weight:850;color:#fff;text-shadow:0 3px 2px rgba(0,0,0,.72),3px 0 0 rgba(0,0,0,.88),-3px 0 0 rgba(0,0,0,.88),0 3px 0 rgba(0,0,0,.88),0 -3px 0 rgba(0,0,0,.88);overflow-wrap:anywhere}
@media(max-width:520px){.video-title-control-head{align-items:stretch;flex-direction:column}.segmented-control{align-self:flex-start}.phone-video-title{font-size:26px}}

/* 🔴 TP-251：第 1 步「画面文字风格」默认态只有一条 strip（≤ 120px）：当前名 + 10 个 56px 高小缩略（横向一行、窄屏横滚）+「看大图 ›」+ ⓘ + 快速选择下拉；
   4 列大卡 .text-style-picker 只在「看大图」弹窗里画。 */
.text-style-strip{display:flex;align-items:center;gap:8px;flex-wrap:wrap;min-height:56px}
/* TP-251：第 1 步「视频时长」下拉 + 预算说明同一行；「这条视频用来做什么？」标题 + 那句 CTA 说明同一行 */
.duration-row{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.purpose-head{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap}
.purpose-head-note{font-weight:400;font-size:13px}
.text-style-strip-current{flex:none;font-size:13px;white-space:nowrap}
.text-style-strip-current b{font-size:14px}
.text-style-thumbs{display:flex;gap:3px;align-items:center;flex:0 1 auto;min-width:0;max-width:100%;overflow-x:auto;overscroll-behavior-x:contain;padding:2px;margin:-2px;scrollbar-width:thin}
.text-style-thumb{flex:none;width:31px;height:56px;padding:0;min-height:0;border:1px solid var(--line);border-radius:5px;background:#111820;overflow:hidden;display:block;cursor:pointer}
.text-style-thumb img{width:100%;height:100%;object-fit:cover;display:block}
.text-style-thumb:hover{border-color:#91afff}
.text-style-thumb.is-on{border-color:var(--primary);box-shadow:0 0 0 2px rgba(47,107,255,.28)}
.text-style-strip-more{flex:none;white-space:nowrap;min-height:0;font-size:13px}
.text-style-strip>.info-hint{margin-left:0}
.text-style-hover{position:fixed;z-index:60;width:216px;background:#fff;border:1px solid var(--line);border-radius:10px;box-shadow:0 12px 32px rgba(15,23,42,.22);padding:6px;pointer-events:none}
.text-style-hover img{display:block;width:204px;height:363px;object-fit:cover;border-radius:7px;background:#111820}
.text-style-hover-cap{display:flex;justify-content:space-between;align-items:baseline;gap:8px;padding:6px 4px 2px;font-size:13px}
.text-style-hover-cap span{color:var(--muted);font-size:12px}
.text-style-strip .text-style-quick-select{flex:0 1 176px;min-width:130px;margin:0;display:flex;align-items:center;gap:6px}
.text-style-strip .text-style-quick-select label{flex:none;font-size:12px;white-space:nowrap}
.text-style-strip .text-style-quick-select select{padding:6px 6px;font-size:12px;min-height:0}
.modal-text-style-gallery .text-style-picker{grid-template-columns:repeat(4,minmax(0,1fr))}
@media(max-width:1180px){.text-style-strip .text-style-quick-select{flex-basis:100%}}
.text-style-picker{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:12px}
.text-style-quick-select{max-width:300px;margin-bottom:10px}
.text-style-card{min-width:0;padding:0;border:1px solid var(--line);background:#fff;text-align:left;overflow:hidden;display:flex;flex-direction:column}
.text-style-card:hover{border-color:#91afff}
.text-style-card.is-on{border-color:var(--primary);box-shadow:0 0 0 2px rgba(47,107,255,.14)}
.text-style-sample-real{width:100%;aspect-ratio:9/16;box-sizing:border-box;display:block;background:#111820;overflow:hidden;border:3px solid #273241;border-radius:8px 8px 0 0}
.text-style-sample-real img{width:100%;height:100%;object-fit:cover;display:block}
.text-style-copy{padding:10px 11px;display:flex;flex-direction:column;gap:2px}
.text-style-copy b{font-size:14px;line-height:1.25}
.text-style-copy small{color:var(--muted);font-size:12px;line-height:1.25}
@media(max-width:1180px){.text-style-picker{grid-template-columns:repeat(3,minmax(0,1fr))}}
@media(max-width:720px){.text-style-picker{grid-template-columns:repeat(2,minmax(0,1fr))}}

/* 2026-09-19：桌面批量上传 + 手机扫码直传。 */
.desktop-upload-queue{margin-top:10px;display:grid;gap:6px;max-height:220px;overflow:auto}
.desktop-upload-row{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:12px;align-items:center;padding:8px 10px;border:1px solid var(--line);border-radius:6px;background:#f8fafc;font-size:12px}
.desktop-upload-row.success{border-color:#b7e3ca;background:#f0faf4;color:#176b45}
.desktop-upload-row.error{border-color:#f2c3c0;background:#fff5f4;color:#a82720}
.desktop-upload-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:inherit}
.mobile-pair-layout{display:grid;grid-template-columns:250px minmax(0,1fr);gap:24px;align-items:center}
.mobile-pair-qr{display:grid;place-items:center;padding:10px;border:1px solid var(--line);border-radius:8px;background:#fff}
.mobile-pair-qr svg{width:100%;height:auto;display:block}
.mobile-pair-copy h4{margin:0 0 8px;font-size:20px}
.mobile-pair-copy p{line-height:1.55}
.mobile-pair-url{padding:10px 12px;border-radius:6px;background:#f1f5f9;font:12px/1.5 ui-monospace,SFMono-Regular,Menlo,monospace;overflow-wrap:anywhere;user-select:all}
.mobile-pair-stats{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-top:14px}
.mobile-pair-stats>*{padding:5px 9px;border-radius:999px;background:#eef2f7;font-size:12px}
.mobile-pair-stats b{background:#e9f8ef;color:#176b45}
.mobile-pair-loading{padding:40px;text-align:center;color:var(--muted)}
@media(max-width:720px){.mobile-pair-layout{grid-template-columns:1fr}.mobile-pair-qr{max-width:260px;margin:auto}.materials-toolbar-head{flex-wrap:wrap}}

/* 🔴 TP-263：第 1 步「镜头转场」——一行四个小按钮 + ⓘ；选中非 none 时下面常驻一行黄字（代价 + 时长补回的承诺，
   按 TP-50 D1 的口径不许收进 ⓘ）。窄屏（400px）四个按钮换行，黄字整行占满，不横向溢出。 */
.transition-strip{display:flex;align-items:center;gap:8px;flex-wrap:wrap;position:relative}
.transition-options{display:flex;gap:6px;flex-wrap:wrap;min-width:0}
.transition-option{flex:none;min-height:0;padding:6px 12px;font-size:13px;border-radius:999px;background:#fff;cursor:pointer}
.transition-option:hover{border-color:#91afff}
.transition-option.is-on{border-color:var(--primary);color:var(--primary);box-shadow:0 0 0 2px rgba(47,107,255,.24)}
/* 🔴 ⓘ 的浮层默认锚在那个 18px 的问号上（.info-hint 自己 position:relative），问号排在四个按钮右边时，
   400px 窄屏上浮层会整个探出屏幕（既有缺陷：第 1 步「画面文字风格」那个 ⓘ 在 400px 下同样溢出 83px）。
   这里**只改本条 strip**：让 .info-hint 退成 static，浮层改锚在整条 strip 上、左对齐 ⇒ 窄屏 0 溢出。
   不去动全站那条 .info-hint-body（那会挪动几十处浮层的位置，不是本单的事）。 */
.transition-strip>.info-hint{margin-left:0;position:static}
.transition-strip>.info-hint>.info-hint-body{left:0}
.transition-cost-note{flex:1 0 100%;margin:2px 0 0;color:var(--amber);background:#fffbeb;border:1px solid #fde68a;border-radius:8px;padding:8px 10px;font-size:12px;line-height:1.5;font-weight:700}

/* 客户 2026-09-25：成品备注弹窗 */
.video-note-tags{display:flex;gap:16px;flex-wrap:wrap;margin:0 0 12px}
.video-note-tag{display:inline-flex;align-items:center;gap:6px;font-weight:700;cursor:pointer}
#video-note-text{width:100%;border:1px solid #cbd4df;border-radius:8px;padding:10px 12px;resize:vertical}
.video-note-badge{display:inline-flex;align-items:center;border-radius:6px;padding:2px 9px;margin-right:8px;font-size:13px;font-weight:800;line-height:1.5;vertical-align:middle;white-space:nowrap}
.video-note-badge.is-used{background:#7c3aed;color:#fff}
.video-note-badge.is-unusable{background:#be185d;color:#fff}
.video-note-badge.is-text{background:#f3e8ff;color:#6b21a8;border:1px solid #c084fc}

/* TP-273：视频翻译页。侧栏子菜单（挂在「成品库」下面）缩进一格、字小一号；任务一行摘要 + ⓘ，结果一行三组「预览 / 下载」。 */
.vt-task{padding:10px 0;border-bottom:1px solid var(--line);display:flex;gap:12px;align-items:flex-start}
.vt-task-body{flex:1;min-width:0}
/* Boss 10-01：缩略图 54×72，点开看原片。全站缩略图统一 contain（TP-182：cover 会把竖图裁成中间一条带），横屏片上下留深色底 */
.vt-thumb{flex:0 0 auto;width:54px;height:72px;padding:0;border:1px solid var(--line);border-radius:6px;overflow:hidden;background:#0f172a;cursor:pointer}
.vt-thumb img{width:100%;height:100%;object-fit:contain;display:block}
.vt-task:last-child{border-bottom:0}
.vt-task-line{display:flex;align-items:center;gap:8px;flex-wrap:wrap;min-width:0}
.vt-task-name{font-weight:700;max-width:280px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.vt-task-no{flex:none;font-variant-numeric:tabular-nums;font-weight:600}
.vt-task-time{margin-left:auto}
.vt-task-actions{display:inline-flex;gap:6px;align-items:center}
.vt-outputs{display:flex;flex-wrap:wrap;gap:6px 16px;margin-top:6px;font-size:13px}
.vt-output{display:inline-flex;align-items:center;gap:6px}
.vt-output .button-link{text-decoration:none}
.vt-file-input{display:none}
/* 同 TP-232：任务行的 ⓘ 是步骤明细、不是报错 ⇒ 灰的；只有失败那一行保持红。 */
.vt-task:not([data-vt-status="failed"]) .notice-detail-toggle{color:var(--muted);border-color:#cbd4df;background:#f1f5f9}
.vt-attest{display:flex;gap:8px;align-items:flex-start;font-weight:600;font-size:14px}
.vt-upload .muted,.vt-draft .muted{font-size:13px}
.vt-draft .field select{max-width:360px}
.vt-lines td{vertical-align:top}
.vt-line-flagged td{background:#fff4f2}

/* ---------- Boss 2026-09-28：全站小图标（src/shared/ui-icons.js，Lucide 线性图标） ---------- */
.ui-icon{flex:none;vertical-align:-3px}
.ui-icon-ok{color:var(--green)}
.ui-icon-warn{color:var(--amber)}
/* 左侧菜单：18px 图标 + 按工作流分组的细分隔线 */
.nav-btn{display:flex;align-items:center;gap:10px;text-align:left}
.nav-btn .ui-icon{opacity:.78;margin-block:-2px}  /* 图标比一行字高一点，不许把 9 个按钮各撑高 1px（同 TP-71 检查 17） */
.nav-btn.active .ui-icon,.nav-btn:hover .ui-icon{opacity:1}
/* 分隔线画在每组第一项按钮上方那 4px 间距的正中、不占布局高度（TP-71 检查 17：视口只有 450px 高时侧栏最后一项也要看得见，原来只剩约 8px 余量）。 */
.nav-btn.nav-group-start{position:relative}
.nav-btn.nav-group-start::before{content:"";position:absolute;left:10px;right:10px;top:-3px;height:1px;background:rgba(255,255,255,.16);pointer-events:none}
/* 页头标题前放同一个图标 */
header h1{display:flex;align-items:center;gap:8px}
.page-title-icon{color:var(--accent)}
/* 15 种常用动作按钮前面的图标（decorateActionIcons） */
.action-icon{margin-inline-end:6px}
button.primary .action-icon,.button-link.primary .action-icon{opacity:.95}
/* 成品备注徽章里的 ✓ ✕ 📌 换成线条图标 */
.video-note-badge{gap:4px}
.video-note-badge .ui-icon{vertical-align:0}
/* 素材缩略图占位（视频 / 片段） */
.asset-thumb-icon .ui-icon{vertical-align:0;color:var(--muted)}
.studio-type-icon{display:inline-flex;color:var(--accent)}
/* 窄屏（< 1000px）侧栏只留 84px、菜单文字藏起来——以前那时菜单是一排空按钮，现在剩图标，居中；鼠标停留看名字（title）。
   更窄（< 760px）菜单横排，分隔线不画。 */
@media(max-width:1000px){.nav-btn{justify-content:center}}
@media(max-width:760px){.nav-btn{justify-content:flex-start}.nav-btn.nav-group-start::before{display:none}}
/* Boss 2026-09-28：商品表「素材 / 视频」两列以前被按钮挤成竖排（表头「视 频」上下排、「12 个 素材」断两行）。
   现在只写数字、单位进表头，不折行。 */
th.num-col,td.num-col{white-space:nowrap;text-align:right;font-variant-numeric:tabular-nums}
/* Boss 2026-09-28：空状态统一（emptyStateHtml）：图标 + 一句话 + 可选按钮，一行排开，窄了自动换行。 */
.empty-state{display:flex;align-items:center;flex-wrap:wrap;gap:8px 10px;color:var(--muted);font-size:13px}
.empty-state .ui-icon{color:#9aa8bd;vertical-align:0}
.empty-state button{margin-left:auto}
/* ---------- Boss 2026-09-28：提示框收成四种，每种前面一个小图标（和 ui-icons.js 同一套 Lucide） ----------
   说明 = 蓝 + info；注意 / 花钱 = 琥珀 + triangle-alert；出错 = 红 + circle-alert；做完了 = 绿 + circle-check。
   图标用伪元素钉在左上角、不进文档流：提示框里面 150 多处各式排版（列表 / 按钮 / 两栏）一个都不用动。 */
.notice{position:relative;padding-left:32px}
.notice::before{content:"";position:absolute;left:10px;top:9px;width:15px;height:15px;background-color:currentColor;-webkit-mask:var(--notice-icon) center/contain no-repeat;mask:var(--notice-icon) center/contain no-repeat;--notice-icon:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpath d='M12 16v-4'/%3E%3Cpath d='M12 8h.01'/%3E%3C/svg%3E")}
.notice-warning::before{--notice-icon:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3'/%3E%3Cpath d='M12 9v4'/%3E%3Cpath d='M12 17h.01'/%3E%3C/svg%3E")}
.notice-error::before{--notice-icon:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cline x1='12' x2='12' y1='8' y2='12'/%3E%3Cline x1='12' x2='12.01' y1='16' y2='16'/%3E%3C/svg%3E")}
.notice-done::before{--notice-icon:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpath d='m16 9-5.5 5.5L8 12'/%3E%3C/svg%3E")}
.notice.wait-compact::before,.notice-compact::before{top:50%;transform:translateY(-50%)}
/* Boss 2026-09-29：视频翻译「水印」三选一，和全站分段选择同一个选中态（蓝边 + 浅蓝底） */
.vt-watermark{display:flex;flex-wrap:wrap;gap:8px}
.vt-watermark-option{display:inline-flex;align-items:center;gap:6px;border:1px solid var(--line);border-radius:8px;padding:7px 12px;font-size:14px;font-weight:700;cursor:pointer;background:#fff}
.vt-watermark-option.is-on{border-color:var(--blue);background:#f5f9ff;box-shadow:inset 0 0 0 1px rgba(39,103,231,.22)}
.vt-watermark-option input{margin:0}
/* 全站 `.field label` / `.field input` 是给「标签在上、输入框整行」写的，会把这里的选项挤成竖排 ⇒ 这里压回一行 */
.field .vt-watermark-option{display:inline-flex;width:auto;white-space:nowrap;margin:0}
.field .vt-watermark-option input{width:auto;min-height:0;padding:0}
/* Boss 2026-09-29：界面语言切换（侧栏账号下面一排 中 / EN / ไทย） */
.lang-switch{display:flex;gap:4px;padding:0 2px}
.lang-switch .lang-btn{flex:1;min-height:0;padding:5px 0;border:1px solid rgba(255,255,255,.16);background:transparent;color:#aeb8c7;font-size:12px;font-weight:700;border-radius:6px}
.lang-switch .lang-btn:hover{background:rgba(255,255,255,.08);color:#fff}
.lang-switch .lang-btn.is-on{background:rgba(255,255,255,.16);color:#fff;border-color:rgba(255,255,255,.3)}
html.i18n-pending body{visibility:hidden}
@media(max-width:1000px){.lang-switch{flex-direction:column}}
/* 登录卡右上角那一排（浅色底上） */
.login-card{position:relative}
.lang-switch-login{position:absolute;top:14px;right:14px;justify-content:flex-end}
/* 登录卡里的按钮全站写的是整行宽（登录按钮），这三个小按钮要压回自身宽度 */
.login-card .lang-switch-login .lang-btn{width:auto;flex:0 0 auto;min-height:0;padding:3px 9px;font-size:12px}
.lang-switch-login .lang-btn{flex:0 0 auto;padding:4px 10px;border-color:var(--line);color:var(--muted);background:#fff}
.lang-switch-login .lang-btn:hover{color:var(--ink);background:#f7fafc}
.lang-switch-login .lang-btn.is-on{color:var(--accent);border-color:var(--accent);background:#eef4ff}
