/* ══════════════════════════════════════════════════════════════════
   漫剧工作台 · 全局设计系统
   来源：open-design-prompts.md P0。色值只在本文件 :root 出现，
   其余任何位置一律走 var() 或 color-mix()。
   ══════════════════════════════════════════════════════════════════ */

:root{
  /* ── 中性底色与文字 ── */
  --bg:      #0B0D10;   /* ink 应用最底层画布 */
  --surface: #12151B;   /* panel 侧栏/卡片/表格行 */
  --raised:  #171B22;   /* panel-raised 弹窗/下拉/悬浮层 */
  --border:  #2A3140;   /* line 全部分割线与边框 1px */
  --stage:   #14171C;   /* neutral-stage 媒体承载底，去暖调 */
  --fg:      #FFFFFF;   /* 标题与强调 */
  --muted:   #C7CDD8;   /* mist 正文 */
  --read:    #E8ECF2;   /* read-text 长文阅读专用 */

  /* ── 品牌色：只表达「当前焦点位置」与「当前页主行动」 ── */
  --accent:  #D4B483;

  /* ── 状态色阶：独立于品牌色，偏冷，仅失败偏暖 ── */
  --st-idle:    #6B7280;   /* 静止/草稿/排队 */
  --st-active:  #4A6FA5;   /* 进行中/执行中 */
  --st-waiting: #3E8A94;   /* 待审/等产物 */
  --st-warn:    #C8922F;   /* 警示/缺料，仅整行提示条与 Toast */
  --st-done:    #4E8A6B;   /* 已批准/成功 */
  --st-error:   #C45C26;   /* ember 失败，兼破坏性 */
  --st-void:    #4B5563;   /* 已取消 */

  /* ── 派生 ── */
  --muted-80: color-mix(in oklch, var(--muted) 80%, transparent);
  --muted-70: color-mix(in oklch, var(--muted) 70%, transparent);
  /* 60% 是在 raised 悬停底上仍达 4.68:1 的下限。承载信息的次要文字用它 */
  --muted-60: color-mix(in oklch, var(--muted) 60%, transparent);
  /* 40% 只有 2.8:1，仅限纯装饰：分隔符、占位插图、空封面图标 */
  --muted-40: color-mix(in oklch, var(--muted) 40%, transparent);
  --gold-40:  color-mix(in oklch, var(--accent) 40%, transparent);
  --gold-30:  color-mix(in oklch, var(--accent) 30%, transparent);
  --gold-24:  color-mix(in oklch, var(--accent) 24%, transparent);
  --gold-20:  color-mix(in oklch, var(--accent) 20%, transparent);
  --scrim:    color-mix(in oklch, var(--bg) 72%, transparent);
  --scrim-92: color-mix(in oklch, var(--bg) 92%, transparent);

  /* 状态色文字：暗底上原始状态色只有 2.2–3.8:1，达不到正文 4.5:1。
     底色仍用规范的 16% / 12%，文字沿同色相提亮，色相与语义不变，
     且每个状态都另配图标做第二识别维度。行悬停会垫高底色，
     故按 raised 最坏底取值。 */
  --tx-idle:    color-mix(in oklch, var(--st-idle) 78%, white);
  --tx-active:  color-mix(in oklch, var(--st-active) 76%, white);
  --tx-waiting: color-mix(in oklch, var(--st-waiting) 84%, white);
  --tx-warn:    var(--st-warn);
  --tx-done:    color-mix(in oklch, var(--st-done) 82%, white);
  --tx-error:   color-mix(in oklch, var(--st-error) 80%, white);
  --tx-void:    color-mix(in oklch, var(--st-void) 66%, white);

  /* ── 字体：全局收敛为一套，避免中西混排字重与垂直度量打架 ── */
  --font-body: 'IBM Plex Sans','Noto Sans SC',-apple-system,BlinkMacSystemFont,'Segoe UI',system-ui,sans-serif;
  --font-mono: 'IBM Plex Mono',ui-monospace,'SF Mono',Menlo,Consolas,monospace;

  /* ── 密度：宽松档（默认）；[data-density=compact] 覆盖 ── */
  --row-h:   56px;   /* 表格行 */
  --list-h:  48px;   /* 列表项 */
  --nav-h:   40px;   /* 侧栏导航项 */
  --head-h:  44px;   /* 表头 */
  --cell-py: 12px;   /* 单元格纵向内边距 */
  --badge-h: 22px;
  --bar-h:   44px;   /* 工具条 */

  /* ── 圆角上限 8px，不用胶囊 ── */
  --r-ctl:   6px;
  --r-card:  8px;
  --r-badge: 4px;
  --shadow-pop: 0 8px 24px rgba(0,0,0,0.45);

  /* ── 栅格 ── */
  --rail-l:    280px;
  --rail-r:    320px;
  --rail-set:  240px;   /* 设置类页面左栏 */
  --topbar-h:  56px;
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-right: env(safe-area-inset-right, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  --safe-left: env(safe-area-inset-left, 0px);
  --content-max: 1440px;
}

[data-density="compact"]{
  --row-h: 40px; --list-h: 36px; --nav-h: 32px; --head-h: 36px;
  --cell-py: 6px; --badge-h: 18px; --bar-h: 36px;
}

/* ══ reset ══ */
*,*::before,*::after{ box-sizing:border-box; }
html{ -webkit-text-size-adjust:100%; }
/* [hidden] 必须真的藏住。浏览器默认的 [hidden]{display:none} 属于 UA 样式，
   任何作者侧的 display（.btn 的 inline-flex、.row 的 flex 等）都会压过它，
   于是 el.hidden = true 看着生效、元素其实还在渲染。这条一次性兜住全部构件 */
[hidden]{ display:none !important; }
body{
  margin:0; background:var(--bg); color:var(--muted);
  font-family:var(--font-body); font-size:14px; line-height:1.7;
  letter-spacing:0; -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
}
img,svg,video{ display:block; max-width:100%; }
a{ color:inherit; text-decoration:none; }
button{ font:inherit; cursor:pointer; color:inherit; letter-spacing:0; }
input,select,textarea{ font:inherit; letter-spacing:0; }
h1,h2,h3,h4{ margin:0; color:var(--fg); font-weight:500; }
p{ margin:0; }
table{ border-collapse:separate; border-spacing:0; }
fieldset{ margin:0; padding:0; border:0; }

/* Windows 端字重补偿：同字重在 Win 上偏细 */
body[data-platform="win"]{ font-weight:450; }

/* ══ 字号层级（行高为中英混排值） ══ */
.t-page  { font-size:24px; line-height:1.6;  font-weight:600; color:var(--fg); }
.t-sect  { font-size:18px; line-height:1.68; font-weight:500; color:var(--fg); }
.t-card  { font-size:15px; line-height:1.6;  font-weight:500; color:var(--fg); }
.t-body  { font-size:14px; line-height:1.72; }
.t-sub   { font-size:13px; line-height:1.7;  color:var(--muted-70); }
.t-hint  { font-size:13px; line-height:1.7;  color:var(--muted-80); }
.t-label { font-size:12px; line-height:1.5;  font-weight:500; }
/* 附注：控件与区块下方的一两句说明。比 .t-sub 再小一档，
   页面里成段出现（弹窗补充说明、区块脚注、标题下的一行解释）。
   12px 属正文级，mist 70% 在 panel 与 panel-raised 上都过 4.5:1。 */
.t-note  { font-size:12px; line-height:1.7;  color:var(--muted-70); }
.t-note.is-error{ color:var(--tx-error); }
.t-note.is-warn{ color:var(--tx-warn); }
.mono{ font-family:var(--font-mono); font-variant-numeric:tabular-nums; line-height:1.5; }
.num { font-family:var(--font-mono); font-variant-numeric:tabular-nums; }

/* 长文阅读：40–48 汉字/行，用 max-width 限制而不是拉满 */
.longform{
  color:var(--read); font-size:15px; line-height:1.75;
  max-width:46em;
}

/* 只给读屏的隐藏文字。必须写 top/left：只写 position:absolute 而不给偏移，
   元素会停在自己的静态流位置上——虽然被裁成 1px 看不见，却仍在往下推文档高度。
   P14 表格视图里 28 个 .vh 逐行下沉，把 scrollHeight 从 1000 顶到 2041，
   多出 1041px 纯空白，用户随手一滚就能把整个界面滑出屏幕。 */
.vh{ position:absolute; top:0; left:0; width:1px; height:1px; overflow:hidden; clip-path:inset(50%); white-space:nowrap; }
.truncate{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; min-width:0; }
.clamp2{ display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; }

/* ══ 焦点环：键盘导航可见，鼠标点击不显示 ══ */
:focus{ outline:none; }
:focus-visible{ outline:2px solid var(--gold-40); outline-offset:2px; border-radius:var(--r-ctl); }
/* Windows 11 双层焦点指示：内层深色环 + 外层 gold */
body[data-platform="win"] :focus-visible{
  outline:2px solid var(--accent); outline-offset:1px;
  box-shadow:0 0 0 1px var(--bg);
}

/* ══════════════════════════════════════════════════════════════════
   平台适配层：顶栏 / 滚动条
   ══════════════════════════════════════════════════════════════════ */
.topbar{
  height:calc(var(--topbar-h) + var(--safe-top)); display:flex; align-items:center; gap:16px;
  padding:var(--safe-top) calc(16px + var(--safe-right)) 0 calc(16px + var(--safe-left));
  background:var(--surface);
  border-bottom:1px solid var(--border);
  position:sticky; top:0; z-index:40;
  -webkit-app-region:drag;
}
body[data-platform="mac"] .topbar{ padding-left:calc(78px + var(--safe-left)); }   /* 避让红绿灯 */
body[data-platform="win"] .topbar{ padding-right:calc(138px + var(--safe-right)); } /* 避让三控件 */
.topbar > *{ -webkit-app-region:no-drag; }
.winctl{ display:none; position:fixed; top:0; right:0; z-index:41; height:32px; }
body[data-platform="win"] .winctl{ display:flex; }
.winctl button{
  width:46px; height:32px; display:grid; place-items:center;
  background:transparent; border:0; color:var(--muted-70);
}
.winctl button svg{ width:10px; height:10px; stroke:currentColor; stroke-width:1.2; fill:none; }
.winctl button:hover{ background:var(--raised); color:var(--fg); }
.winctl button.cl:hover{ background:var(--st-error); color:var(--fg); }

.brand{ display:flex; align-items:center; gap:8px; color:var(--fg); font-weight:600; font-size:15px; flex:none; }
.brand svg{ width:20px; height:20px; stroke:var(--accent); stroke-width:1.5; fill:none; }
.crumb{ display:flex; align-items:center; gap:6px; font-size:13px; color:var(--muted-70); min-width:0; }
.crumb a{ color:var(--muted-70); border-radius:3px; }
.crumb a:hover{ color:var(--fg); text-decoration:underline; }
.crumb .sep{ color:var(--muted-40); }
.crumb .cur{ color:var(--fg); }
.topbar-right{ margin-left:auto; display:flex; align-items:center; gap:8px; flex:none; }

/* 滚动条：mac 覆盖式默认隐藏；Win 常驻 10px 占位 */
.scroll{ overflow-y:auto; overscroll-behavior:contain; }
.scroll-x{ overflow-x:auto; }
/* .scroll 的别名，外加 min-height:0。
   用在 flex/grid 子项上时光给 overflow-y 不够：flex 子项的默认
   min-height:auto 会让它被内容撑到自然高度，overflow 永远不触发，
   内容顶穿固定高度的 .shell 把文档推长，sticky 顶栏跟着滚出屏幕。
   （P4/P7/P8/P10 曾因为这个类压根没定义而顶栏被推到 y=-2877。） */
.scroll-y{ overflow-y:auto; overscroll-behavior:contain; min-height:0; }
body[data-platform="win"] .scroll,
body[data-platform="win"] .scroll-y,
body[data-platform="win"] .scroll-x{ scrollbar-width:thin; scrollbar-color:var(--border) transparent; }
body[data-platform="win"] .scroll::-webkit-scrollbar,
body[data-platform="win"] .scroll-y::-webkit-scrollbar,
body[data-platform="win"] .scroll-x::-webkit-scrollbar{ width:10px; height:10px; }
body[data-platform="win"] .scroll::-webkit-scrollbar-thumb,
body[data-platform="win"] .scroll-y::-webkit-scrollbar-thumb,
body[data-platform="win"] .scroll-x::-webkit-scrollbar-thumb{ background:var(--border); border-radius:5px; }
body[data-platform="win"] .scroll::-webkit-scrollbar-thumb:hover,
body[data-platform="win"] .scroll-y::-webkit-scrollbar-thumb:hover,
body[data-platform="win"] .scroll-x::-webkit-scrollbar-thumb:hover{ background:var(--st-idle); }
body[data-platform="mac"] .scroll,
body[data-platform="mac"] .scroll-y,
body[data-platform="mac"] .scroll-x{ scrollbar-width:none; }
body[data-platform="mac"] .scroll::-webkit-scrollbar,
body[data-platform="mac"] .scroll-y::-webkit-scrollbar,
body[data-platform="mac"] .scroll-x::-webkit-scrollbar{ width:0; height:0; }

/* 启动数据失败：覆盖业务空态，避免把连接故障误认为数据被清空 */
.boot-error{
  position:fixed; inset:var(--topbar-h) 0 0; z-index:39;
  display:grid; place-items:center; padding:24px 16px;
  overflow-y:auto; background:var(--bg);
}
.boot-error-card{
  width:min(100%,560px); display:grid; justify-items:center; gap:12px;
  padding:28px; background:var(--surface); border:1px solid var(--border);
  border-radius:var(--r-card); text-align:center;
}
.boot-error-card > svg{ width:32px; height:32px; stroke:var(--tx-error); fill:none; }
.boot-error-card h1{ margin:0; font-size:18px; line-height:1.5; color:var(--fg); }
.boot-error-card p{ margin:0; font-size:13px; line-height:1.7; color:var(--muted); }
.boot-error-card .sub{ color:var(--muted-70); }
.boot-error-card .acts{ display:flex; justify-content:center; gap:10px; margin-top:4px; }
@media (max-width:479px){
  .boot-error-card{ padding:22px 16px; }
  .boot-error-card .acts{ width:100%; flex-direction:column; }
  .boot-error-card .acts .btn{ width:100%; justify-content:center; }
}

/* ══ tooltip：所有纯图标按钮必备 ══ */
/* contain:layout 让隐藏态的 ::after 不参与祖先的 scrollWidth，
   否则贴右边缘的图标按钮会把整个文档撑宽出现横向滚动条 */
[data-tip]{ position:relative; contain:layout; }
/* 顶栏最右侧的按钮 tooltip 一律靠右对齐，避免捅出视口 */
.topbar > [data-tip]:last-of-type::after,
.topbar-right > [data-tip]:last-child::after{ left:auto; right:0; transform:none; }
[data-tip]::after{
  content:attr(data-tip); position:absolute; top:calc(100% + 8px); left:50%;
  transform:translateX(-50%); z-index:70;
  background:var(--raised); color:var(--read); border:1px solid var(--border);
  border-radius:var(--r-ctl); padding:6px 10px;
  font-size:12px; line-height:1.5; font-weight:400; white-space:nowrap;
  box-shadow:var(--shadow-pop); pointer-events:none;
  opacity:0; visibility:hidden; transition:opacity .12s ease;
}
[data-tip]:hover::after,[data-tip]:focus-visible::after{ opacity:1; visibility:visible; }
[data-tip].tip-l::after{ left:auto; right:0; transform:none; }
[data-tip].tip-r::after{ left:0; transform:none; }
[data-tip].tip-up::after{ top:auto; bottom:calc(100% + 8px); }
[data-tip].tip-wrap::after{ white-space:normal; width:220px; }

/* ══════════════════════════════════════════════════════════════════
   三栏骨架
   ══════════════════════════════════════════════════════════════════ */
.shell{
  display:grid;
  grid-template-columns:var(--rail-l) minmax(0,1fr) var(--rail-r);
  height:calc(100vh - var(--topbar-h) - var(--safe-top));
  height:calc(100dvh - var(--topbar-h) - var(--safe-top));
}
/* 阶段页在顶栏与 shell 之间还夹一条 56px 流水线带（.pipeline，body 的兄弟节点），
   只减一个 --topbar-h 会让总高变成 100vh + 56，多出一条 56px 的整页滚动。
   .pipeline 是 shell 的前一个兄弟，用 :has 判断，非阶段页不受影响。 */
body:has(> .pipeline) .shell{
  height:calc(100vh - var(--topbar-h) * 2 - var(--safe-top));
  height:calc(100dvh - var(--topbar-h) * 2 - var(--safe-top));
}
.shell.no-right{ grid-template-columns:var(--rail-l) minmax(0,1fr); }
/* 阶段页：没有左栏，只有正文 + 右侧信息栏。
   不加这个类的话正文会掉进中间轨，被压成 --rail-r 的宽度 */
.shell.no-left{ grid-template-columns:minmax(0,1fr) var(--rail-r); }
/* 左右栏同时收起时正文吃满整行。必须显式写出这条组合：.no-left 在 .no-right
   之后声明，同权重下会赢，只加 .no-right 的话右侧仍留一条 --rail-r 空轨。 */
.shell.no-left.no-right{ grid-template-columns:minmax(0,1fr); }
.shell.single{ grid-template-columns:minmax(0,1fr); }
.shell.settings{ grid-template-columns:var(--rail-set) minmax(0,1fr); }
.rail-l{ background:var(--surface); border-right:1px solid var(--border); display:flex; flex-direction:column; min-height:0; min-width:0; }
.rail-r{ background:var(--surface); border-left:1px solid var(--border); display:flex; flex-direction:column; min-height:0; min-width:0; }
.main{ min-width:0; display:flex; flex-direction:column; min-height:0; background:var(--bg); }
.main-head{ padding:20px 24px 0; flex:none; }
.main-body{ flex:1; min-height:0; padding:16px 24px 24px; }
.main-body.cap{ max-width:var(--content-max); width:100%; }
.main-body.narrow{ max-width:960px; }

.title-row{ display:flex; align-items:center; gap:16px; flex-wrap:wrap; }
.title-row .tools{ margin-left:auto; display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.sect-gap{ display:flex; flex-direction:column; gap:24px; }
.block{
  background:var(--surface); border:1px solid var(--border);
  border-radius:var(--r-card); padding:20px;
}
.block-flat{ padding:20px 0; }
.block-flat + .block-flat{ border-top:1px solid var(--border); }
.info-block{
  background:var(--bg); border:1px solid var(--border);
  border-radius:var(--r-card); padding:16px;
}
.rule{ border:0; border-top:1px solid var(--border); margin:0; }

/* ══════════════════════════════════════════════════════════════════
   按钮：gold 实底同一视口最多一次，且只属于主按钮
   ══════════════════════════════════════════════════════════════════ */
.btn{
  display:inline-flex; align-items:center; justify-content:center; gap:6px;
  height:32px; padding-inline:14px; border-radius:var(--r-ctl);
  border:1px solid transparent; font-size:13px; font-weight:500; line-height:1.5;
  white-space:nowrap; flex:none;
  transition:background .14s ease, border-color .14s ease, color .14s ease, transform .05s ease;
}
.btn svg{ width:14px; height:14px; stroke:currentColor; stroke-width:1.5; fill:none; flex:none; }
.btn-lg{ height:40px; padding-inline:20px; font-size:14px; }
.btn-sm{ height:26px; padding-inline:10px; font-size:12px; }

.btn-primary{ background:var(--accent); color:var(--bg); border-color:var(--accent); }
.btn-primary:hover{ background:color-mix(in oklch,var(--accent) 94%,white); color:var(--bg); }
.btn-primary:active{ background:color-mix(in oklch,var(--accent) 92%,black); transform:translateY(1px); }

.btn-secondary{ background:transparent; color:var(--muted); border-color:var(--border); }
.btn-secondary:hover{ border-color:var(--gold-40); color:var(--fg); }
.btn-secondary:active{ transform:translateY(1px); }

.btn-text{ background:transparent; border-color:transparent; color:var(--muted); height:auto; padding:0 2px; }
.btn-text:hover{ color:var(--fg); text-decoration:underline; }

/* 破坏性：描边保持原始 ember，文字提亮以过 4.5:1 */
.btn-danger{ background:transparent; color:var(--tx-error); border-color:var(--st-error); }
.btn-danger:hover{ background:color-mix(in oklch,var(--st-error) 12%,transparent); color:color-mix(in oklch,var(--st-error) 74%,white); }
.btn-danger-text{ background:transparent; border-color:transparent; color:var(--tx-error); height:auto; padding:0 2px; }
.btn-danger-text:hover{ color:color-mix(in oklch,var(--st-error) 74%,white); text-decoration:underline; }

.btn[disabled],.btn[aria-disabled="true"]{ cursor:not-allowed; }
.btn-primary[disabled]{ background:color-mix(in oklch,var(--accent) 30%,transparent); border-color:transparent; color:var(--muted-40); }
.btn-secondary[disabled],.btn-text[disabled],.btn-danger[disabled],.btn-danger-text[disabled]{
  color:var(--muted-40); border-color:color-mix(in oklch,var(--border) 60%,transparent);
}
.btn-secondary[disabled]:hover,.btn-danger[disabled]:hover{
  border-color:color-mix(in oklch,var(--border) 60%,transparent); color:var(--muted-40); background:transparent;
}
.btn-text[disabled]:hover,.btn-danger-text[disabled]:hover{ color:var(--muted-40); text-decoration:none; }
.btn-full{ width:100%; }

/* 图标按钮 */
.iconbtn{
  position:relative; width:32px; height:32px; display:grid; place-items:center; flex:none;
  background:transparent; border:1px solid transparent; border-radius:var(--r-ctl); color:var(--muted);
}
.iconbtn svg{ width:18px; height:18px; stroke:currentColor; stroke-width:1.5; fill:none; }
.iconbtn:hover{ background:var(--raised); color:var(--fg); }
.iconbtn[aria-pressed="true"],.iconbtn.on{ background:var(--raised); color:var(--fg); }
.iconbtn[disabled]{ color:var(--muted-40); cursor:not-allowed; }
.iconbtn[disabled]:hover{ background:transparent; color:var(--muted-40); }
.iconbtn-sm{ width:28px; height:28px; }
.iconbtn-sm svg{ width:15px; height:15px; }
.iconbtn-xs{ width:24px; height:24px; }
.iconbtn-xs svg{ width:14px; height:14px; }
.iconbtn-danger{ color:var(--tx-error); }
.iconbtn-danger:hover{ background:color-mix(in oklch,var(--st-error) 12%,transparent); color:color-mix(in oklch,var(--st-error) 74%,white); }
.iconbtn .dot{
  position:absolute; top:1px; right:1px; min-width:16px; height:16px; padding-inline:4px;
  border-radius:8px; background:var(--st-active); color:var(--fg);
  font-family:var(--font-mono); font-size:10px; line-height:16px; text-align:center;
  border:2px solid var(--surface);
}
.iconbtn .dot.err{ background:var(--st-error); }

/* 旋转指示器 / 脉冲圆点 */
.spin{
  width:14px; height:14px; flex:none; border-radius:50%;
  border:1.5px solid color-mix(in oklch,currentColor 28%,transparent);
  border-top-color:currentColor; animation:spin .7s linear infinite;
}
.spin-sm{ width:11px; height:11px; border-width:1.5px; }
@keyframes spin{ to{ transform:rotate(360deg); } }
.pulse{ width:7px; height:7px; border-radius:50%; background:currentColor; flex:none; animation:pulse 1.6s ease-in-out infinite; }
@keyframes pulse{ 0%,100%{ opacity:.35; transform:scale(.8); } 50%{ opacity:1; transform:scale(1); } }

/* ══════════════════════════════════════════════════════════════════
   徽章：4px 圆角，一律不用 gold
   ══════════════════════════════════════════════════════════════════ */
.badge{
  display:inline-flex; align-items:center; gap:5px; height:var(--badge-h);
  padding-inline:8px; border-radius:var(--r-badge);
  font-size:12px; font-weight:500; line-height:1.5; white-space:nowrap; flex:none;
}
.badge svg{ width:12px; height:12px; stroke:currentColor; stroke-width:1.8; fill:none; flex:none; }
/* 分类标签：分类不是状态，中性底 + 图标区分 */
.badge.neutral{ background:var(--bg); border:1px solid var(--border); color:var(--muted); }
.badge.neutral.attn{ border-color:var(--st-warn); }
/* 当前位置：gold 仅作 2px 下划线 */
.badge.cur{ background:var(--bg); color:var(--fg); box-shadow:inset 0 -2px 0 var(--accent); }

.b-idle{    background:color-mix(in oklch,var(--st-idle) 16%,transparent);    color:var(--tx-idle); }
.b-active{  background:color-mix(in oklch,var(--st-active) 16%,transparent);  color:var(--tx-active); }
.b-waiting{ background:color-mix(in oklch,var(--st-waiting) 16%,transparent); color:var(--tx-waiting); }
.b-warn{    background:color-mix(in oklch,var(--st-warn) 16%,transparent);    color:var(--tx-warn); }
.b-done{    background:color-mix(in oklch,var(--st-done) 16%,transparent);    color:var(--tx-done); }
.b-error{   background:color-mix(in oklch,var(--st-error) 16%,transparent);   color:var(--tx-error); }
/* 已取消：P0 要求文字加删除线。只作用于文字，不划穿图标 */
.b-void{
  background:color-mix(in oklch,var(--st-void) 16%,transparent);
  color:var(--tx-void);
  text-decoration:line-through; text-decoration-thickness:1.5px; text-decoration-color:currentColor;
}
.b-void svg{ text-decoration:none; }

/* 状态点：颜色 + 形状双维度 */
.sdot{ width:8px; height:8px; border-radius:50%; flex:none; border:1px solid var(--border); }
.sdot.idle{ background:var(--st-idle); border-color:transparent; }
.sdot.active{ background:var(--st-active); border-color:transparent; }
.sdot.waiting{ background:var(--st-waiting); border-color:transparent; }
.sdot.warn{ background:var(--st-warn); border-color:transparent; }
.sdot.done{ background:var(--st-done); border-color:transparent; }
.sdot.error{ background:var(--st-error); border-color:transparent; }
.sdot.void{ background:var(--st-void); border-color:transparent; }
.sdot.cur{ background:transparent; border:2px solid var(--accent); width:10px; height:10px; }

/* ══════════════════════════════════════════════════════════════════
   表单控件
   ══════════════════════════════════════════════════════════════════ */
.field{ display:flex; flex-direction:column; gap:6px; min-width:0; }
.field > label,.flabel{ font-size:12px; color:var(--muted-70); line-height:1.5; }
.input,.select,.textarea{
  background:var(--surface); border:1px solid var(--border); border-radius:var(--r-ctl);
  color:var(--muted); font-size:13px; line-height:1.5; width:100%;
  transition:border-color .14s ease, box-shadow .14s ease;
}
.input,.select{ height:32px; padding:0 12px; }
.textarea{ padding:8px 12px; line-height:1.72; resize:vertical; min-height:80px; }
.input::placeholder,.textarea::placeholder{ color:var(--muted-40); }
.input:hover,.select:hover,.textarea:hover{ border-color:color-mix(in oklch,var(--border) 70%,white); }
.input:focus-visible,.select:focus-visible,.textarea:focus-visible{
  outline:none; border-color:var(--accent);
  box-shadow:0 0 0 2px var(--gold-20); color:var(--fg);
}
.input[aria-invalid="true"],.textarea[aria-invalid="true"],.select[aria-invalid="true"]{ border-color:var(--st-error); }
.input[disabled],.select[disabled],.textarea[disabled]{
  background:var(--bg); color:var(--muted-40); cursor:not-allowed;
}
.input[readonly]{ color:var(--muted-70); }
.err-msg{ font-size:13px; line-height:1.6; color:color-mix(in oklch,var(--st-error) 90%,white); }

.select{
  appearance:none; padding-right:30px; cursor:pointer;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23C7CDD8' stroke-width='1.5'><path d='M6 9l6 6 6-6'/></svg>");
  background-repeat:no-repeat; background-position:right 8px center; background-size:16px 16px;
}
.select[disabled]{ cursor:not-allowed; }
/* 小号：与 .btn-sm 同高 28px，用于顶栏与工具条里的筛选器 */
.select-sm,.input-sm{ height:28px; font-size:12px; padding-block:0; }
.select-sm{ padding-right:26px; background-position:right 6px center; background-size:14px 14px; }

.search{ position:relative; min-width:0; }
.search .input{ padding-left:32px; }
.search > svg{
  position:absolute; left:10px; top:50%; transform:translateY(-50%);
  width:14px; height:14px; stroke:var(--muted-40); stroke-width:1.5; fill:none; pointer-events:none;
}

/* 密码类：永不回显已保存值 */
.pwd{ position:relative; }
.pwd .input{ padding-right:38px; font-family:var(--font-mono); }
.pwd .iconbtn{ position:absolute; right:2px; top:2px; width:28px; height:28px; }
.pwd .iconbtn svg{ width:15px; height:15px; }

/* 分段控件 */
.seg{ display:inline-flex; padding:2px; background:var(--bg); border:1px solid var(--border); border-radius:var(--r-ctl); flex:none; }
.seg button{
  height:26px; padding-inline:12px; background:transparent; border:0;
  display:inline-flex; align-items:center; justify-content:center; gap:6px;
  border-radius:var(--r-badge); color:var(--muted-70); font-size:12px; font-weight:500; line-height:1.5;
  white-space:nowrap;
}
.seg button > svg{
  width:14px; height:14px; flex:none; stroke:currentColor;
  stroke-width:1.6; fill:none;
}
.seg button:hover{ color:var(--fg); }
.seg button[aria-selected="true"],.seg button[aria-pressed="true"]{
  background:var(--raised); color:var(--fg); box-shadow:inset 0 -2px 0 var(--accent);
}
.seg button[disabled]{ color:var(--muted-40); cursor:not-allowed; }
.seg button[disabled]:hover{ color:var(--muted-40); }

/* 复选框 / 单选 */
/* 方框只有 16px，但整个 label 才是可点区。
   min-height 24 + 横向内边距，让无文字的纯勾选列也够点。 */
.check{
  display:inline-flex; align-items:center; gap:8px; font-size:13px; cursor:pointer;
  min-height:24px; padding-inline:4px; margin-inline:-4px;
}
.check input{
  appearance:none; width:16px; height:16px; flex:none; margin:0;
  background:var(--surface); border:1px solid var(--border); border-radius:3px;
  display:grid; place-items:center;
}
.check input[type="radio"]{ border-radius:50%; }
/* 选中态用 gold 表达「你选了这个」，但不做实底：
   P0 规定 gold 实底同一视口最多一次且只属于主按钮，
   而复选框在批量选择时会同时出现几十个。故降为描边 + 对勾。 */
.check input:checked{ background:var(--gold-24); border-color:var(--accent); }
.check input:checked::after{
  content:''; width:9px; height:5px;
  border-left:1.8px solid var(--accent); border-bottom:1.8px solid var(--accent);
  transform:rotate(-45deg) translateY(-1px);
}
.check input[type="radio"]:checked::after{
  content:''; width:6px; height:6px; border:0; border-radius:50%; background:var(--accent); transform:none;
}
.check input[disabled]{ background:var(--bg); cursor:not-allowed; }

/* 滑块 */
.range{ appearance:none; width:100%; height:20px; background:transparent; cursor:pointer; }
.range::-webkit-slider-runnable-track{ height:4px; border-radius:2px; background:var(--border); }
.range::-webkit-slider-thumb{
  appearance:none; width:14px; height:14px; margin-top:-5px; border-radius:50%;
  background:var(--muted); border:1px solid var(--bg);
}
.range:hover::-webkit-slider-thumb{ background:var(--fg); }
.range:focus-visible::-webkit-slider-thumb{ background:var(--accent); }
.range::-moz-range-track{ height:4px; border-radius:2px; background:var(--border); }
.range::-moz-range-thumb{ width:14px; height:14px; border-radius:50%; background:var(--muted); border:1px solid var(--bg); }

/* 只读锁定字段 */
.locked{
  display:flex; align-items:center; gap:8px; height:32px; padding:0 12px;
  background:var(--bg); border:1px solid var(--border); border-radius:var(--r-ctl);
  font-family:var(--font-mono); font-size:12px; color:var(--muted-70);
}
.locked svg{ width:14px; height:14px; stroke:var(--muted-60); stroke-width:1.5; fill:none; flex:none; }

/* 路径胶囊 */
.pill{
  display:inline-flex; align-items:center; gap:6px; height:28px; padding-inline:10px; min-width:0;
  background:var(--bg); border:1px solid var(--border); border-radius:var(--r-ctl);
  font-family:var(--font-mono); font-size:12px; color:var(--muted-70); line-height:1.5;
  max-width:280px;
  /* 不参与 flex 压缩：短标签（画幅、比例）被压 8px 就会裁字。
     真正需要收缩的是带 .path 的长路径胶囊，那种自己开 flex-shrink。
     win 平台字重 450 让文字更宽，顶栏刚好撑满时首先牺牲的就是这里。 */
  flex:none;
}
/* 长路径胶囊要能收缩，靠内部 .path 的 ellipsis 收边 */
.pill:has(.path){ flex:0 1 auto; }
.pill:hover{ border-color:var(--gold-40); color:var(--fg); }
.pill svg{ width:14px; height:14px; flex:none; stroke:currentColor; stroke-width:1.5; fill:none; }
.pill .path{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; direction:rtl; text-align:left; }

.health{ display:inline-flex; align-items:center; gap:6px; font-size:12px; color:var(--muted-70); flex:none; }
.health .led{ width:8px; height:8px; border-radius:50%; background:var(--st-done); flex:none; }
.health.breathe .led{ animation:pulse 2.4s ease-in-out infinite; }
.health.down{ color:var(--tx-error); }
.health.down .led{ background:var(--st-error); animation:none; }
/* 可点的健康指示器（P18 六：断开时点开小面板看重试与最近错误）。
   .health 本体是纯状态文字，12px × 1.7 得 20.4px 高，读足够；
   但一旦挂上 role="button" 它就是点击目标，P0 的下限是 24px。
   这里只补 min-height 与横向内距，不写 color——
   写了会盖掉 .health.down 的 ember（两者同为 0,2,0 特异度，后来者赢）。
   按 .stat.clickable 的先例清掉 <button> 的 UA 底色：
   这个位置既可能是 <span role=button> 也可能是真 <button>，两种都要成立。 */
.health.clickable{
  cursor:pointer; appearance:none; background:none; border:0;
  font:inherit; min-height:24px; padding:0 6px;
  border-radius:var(--r-badge);
}
.health.clickable:hover{ background:var(--raised); }
.health.clickable:focus-visible{ outline:2px solid var(--gold-40); outline-offset:2px; }

/* ══════════════════════════════════════════════════════════════════
   导航项 / 列表项
   ══════════════════════════════════════════════════════════════════ */
.navitem{
  width:100%; display:flex; align-items:center; gap:10px; text-align:left;
  min-height:var(--nav-h); padding:4px 10px; margin-bottom:2px;
  background:transparent; border:0; border-left:2px solid transparent;
  border-radius:var(--r-ctl); color:var(--muted); font-size:13px;
}
.navitem:hover{ background:var(--raised); }
.navitem[aria-current="true"],.navitem.on{
  background:var(--raised); border-left-color:var(--accent); color:var(--fg);
}
.navitem svg{ width:16px; height:16px; stroke:currentColor; stroke-width:1.5; fill:none; flex:none; }
.navitem .cnt{ margin-left:auto; font-family:var(--font-mono); font-size:12px; color:var(--muted-60); flex:none; }
.navitem[aria-current="true"] .cnt{ color:var(--muted-70); }
.navitem .cnt.warn{ color:var(--tx-warn); }

.listitem{
  width:100%; display:flex; align-items:center; gap:10px; text-align:left;
  min-height:var(--list-h); padding:6px 10px 6px 8px; margin-bottom:2px;
  background:transparent; border:0; border-left:2px solid transparent;
  border-radius:var(--r-ctl); color:var(--muted);
}
.listitem:hover{ background:var(--raised); }
.listitem[aria-current="true"]{ background:var(--raised); border-left-color:var(--accent); color:var(--fg); }
.listitem .no{ font-family:var(--font-mono); font-size:12px; color:var(--muted-60); flex:none; }
.listitem[aria-current="true"] .no{ color:var(--accent); }

.rail-head{
  display:flex; align-items:center; justify-content:space-between; gap:8px;
  padding:12px 16px 8px; font-size:12px; color:var(--muted-70); line-height:1.5; flex:none;
}
.rail-scroll{ flex:1; min-height:0; padding:0 8px 8px; }
.rail-foot{ padding:12px 16px; border-top:1px solid var(--border); flex:none; }
.rail-top{ padding:16px; border-bottom:1px solid var(--border); flex:none; }

/* 右栏可折叠区块 */
.sect + .sect{ border-top:1px solid var(--border); }
.sect-head{
  width:100%; display:flex; align-items:center; gap:8px; padding:14px 16px;
  background:transparent; border:0; color:var(--fg); font-size:13px; font-weight:500; text-align:left;
}
.sect-head > svg:not(.chev){
  width:16px; height:16px; flex:none; stroke:currentColor; stroke-width:1.5; fill:none;
}
.sect-head:hover{ background:var(--raised); }
.sect-head .chev{ width:14px; height:14px; stroke:var(--muted-70); stroke-width:1.6; fill:none; flex:none; transition:transform .16s ease; }
.sect-head[aria-expanded="false"] .chev{ transform:rotate(-90deg); }
.sect-head .cnt{ margin-left:auto; font-family:var(--font-mono); font-size:12px; color:var(--muted-60); }
.sect-body{ padding:0 16px 14px; }
.sect-head[aria-expanded="false"] + .sect-body{ display:none; }

/* ══════════════════════════════════════════════════════════════════
   表格
   ══════════════════════════════════════════════════════════════════ */
.tbl-wrap{
  border:1px solid var(--border); border-radius:var(--r-card);
  background:var(--surface); overflow:auto; max-height:100%;
}
table.tbl{ width:100%; min-width:0; }
/* 纵向内边距走密度 token：宽松 12px / 紧凑 6px。
   行高用 min-height 而非 height，多行单元格才能撑开而不裁切 */
.tbl th,.tbl td{ padding:var(--cell-py) 12px; text-align:left; vertical-align:middle; }
.tbl thead th{
  position:sticky; top:0; z-index:3; height:var(--head-h);
  background:var(--surface); border-bottom:1px solid var(--border);
  font-size:12px; font-weight:500; color:var(--muted-70); line-height:1.5; white-space:nowrap;
}
.tbl tbody tr{ height:var(--row-h); }   /* table-row 上 height 即 min-height 语义 */
.tbl tbody td{
  border-bottom:1px solid var(--border); background:var(--surface);
  font-size:13px; transition:background .12s ease;
}
.tbl tbody tr:last-child td{ border-bottom:0; }
.tbl tbody tr:hover td{ background:var(--raised); }
.tbl tbody tr[aria-selected="true"] td{ background:var(--raised); }
.tbl tbody tr[aria-selected="true"] td:first-child{ box-shadow:inset 2px 0 0 var(--accent); }
/* 数字与 ID 列右对齐 + 等宽（P0）。.n 与 .num 两个类名都支持 */
.tbl .n,.tbl th.n,.tbl .num,.tbl th.num{
  text-align:right; font-family:var(--font-mono); font-variant-numeric:tabular-nums;
}
.tbl .act{ text-align:right; white-space:nowrap; }
.tbl .two-line{ display:flex; flex-direction:column; gap:2px; line-height:1.4; padding-block:6px; }
.tbl .two-line .sub{ font-size:12px; color:var(--muted-60); font-family:var(--font-mono); }
.sortable{ cursor:pointer; }
.sortable:hover{ color:var(--fg); }
.sortable .arw{ display:inline-block; margin-left:4px; color:var(--muted-60); }
.sortable[aria-sort] .arw{ color:var(--accent); }
/* 分组头 */
.grp-head{
  display:flex; align-items:center; gap:10px; height:var(--head-h); padding-inline:12px;
  background:var(--surface); border-bottom:1px solid var(--border); font-size:13px; color:var(--fg);
}
.grp-head.tight{ height:36px; }
.grp-head .chev{ width:14px; height:14px; stroke:var(--muted-70); stroke-width:1.6; fill:none; flex:none; transition:transform .16s ease; }
.grp-head[aria-expanded="false"] .chev{ transform:rotate(-90deg); }
.grp-head .sp{ margin-left:auto; display:flex; align-items:center; gap:6px; }

/* ══════════════════════════════════════════════════════════════════
   P18 全局状态与反馈
   ══════════════════════════════════════════════════════════════════ */

/* 一、空态三规格 */
.empty-page{
  display:flex; flex-direction:column; align-items:center; gap:12px;
  padding:48px 24px; text-align:center;
}
.empty-page > svg{ width:40px; height:40px; stroke:var(--muted-40); stroke-width:1.4; fill:none; }
.empty-page h2{ font-size:18px; font-weight:500; }
.empty-page p{ font-size:13px; color:var(--muted-70); line-height:1.7; max-width:44ch; }
.empty-page .acts{ display:flex; align-items:center; gap:12px; margin-top:4px; flex-wrap:wrap; justify-content:center; }

.empty-row{
  display:flex; align-items:center; justify-content:center; gap:10px;
  height:36px; padding-inline:12px; font-size:13px; color:var(--muted-60);
}

.empty-cell{
  display:grid; place-items:center; gap:6px; width:100%; height:100%;
  background:var(--bg); border:1px dashed var(--border); border-radius:var(--r-badge);
  color:var(--muted-60); font-size:12px; text-align:center; padding:8px;
}
.empty-cell > svg{ width:20px; height:20px; stroke:currentColor; stroke-width:1.5; fill:none; }

/* 二、错误态四规格 */
.notice{
  display:flex; align-items:center; gap:10px; padding:12px 16px;
  border-radius:var(--r-card); border-left:2px solid transparent;
  font-size:13px; line-height:1.7;
}
.notice > svg{ width:16px; height:16px; stroke:currentColor; stroke-width:1.6; fill:none; flex:none; }
.notice .grow{ flex:1; min-width:0; }
.notice .btn-text,.notice .btn-danger-text{ color:inherit; text-decoration:underline; flex:none; }
.notice .btn-text:hover{ color:var(--fg); }
/* 三条提示条的文字都按「panel 底 + 12% 同色晕染」的最坏底实测过 4.5:1 */
.notice.err{  background:color-mix(in oklch,var(--st-error) 12%,transparent);   border-left-color:var(--st-error);   color:color-mix(in oklch,var(--st-error) 82%,white); }
/* 百分比是「状态色占比」，越小越白。84% 只有 4.36:1，取 78% 留出余量 */
.notice.info{ background:color-mix(in oklch,var(--st-active) 12%,transparent);  border-left-color:var(--st-active);  color:color-mix(in oklch,var(--st-active) 78%,white); }
.notice.warn{ background:color-mix(in oklch,var(--st-warn) 12%,transparent);    border-left-color:var(--st-warn);    color:var(--st-warn); }
.notice.done{ background:color-mix(in oklch,var(--st-done) 12%,transparent);    border-left-color:var(--st-done);    color:color-mix(in oklch,var(--st-done) 84%,white); }
.notice.thin{ padding:8px 12px; font-size:12px; }

.err-block{
  position:relative; background:var(--bg);
  border:1px solid color-mix(in oklch,var(--st-error) 40%,transparent);
  border-radius:var(--r-ctl); padding:10px 36px 10px 12px;
  font-family:var(--font-mono); font-size:12px; line-height:1.5; color:var(--read);
  max-height:calc(1.5em * 8 + 20px); overflow:auto; white-space:pre-wrap; text-align:left;
}
.err-block > .iconbtn{ position:absolute; top:4px; right:4px; }

.cell-err{
  display:grid; place-items:center; gap:4px; width:100%; height:100%;
  background:var(--bg); border:1px solid var(--st-error); border-radius:var(--r-badge);
  color:var(--tx-error); font-size:12px; text-align:center; padding:8px;
}
.cell-err > svg{ width:20px; height:20px; stroke:currentColor; stroke-width:1.5; fill:none; }

/* 三、加载态四规格 */
.sk{
  background:var(--border); border-radius:var(--r-badge);
  animation:breathe 1.5s ease-in-out infinite;
}
.sk-text{ height:14px; }
.sk-line{ height:12px; }
@keyframes breathe{ 0%,100%{ opacity:.35; } 50%{ opacity:.78; } }

.bar{ height:3px; border-radius:2px; background:var(--border); overflow:hidden; }
.bar > i{ display:block; height:100%; background:var(--st-active); border-radius:2px; }
.bar.flow > i{ width:35%; animation:flow 1.4s ease-in-out infinite; }
@keyframes flow{ 0%{ margin-left:-35%; } 100%{ margin-left:100%; } }
.bar.determinate{ height:6px; border-radius:3px; }
.bar.determinate > i{ border-radius:3px; transition:width .3s ease; }
.progress-row{ display:flex; align-items:center; gap:10px; }
.progress-row .bar{ flex:1; }
.progress-row .pct{ font-family:var(--font-mono); font-size:12px; color:var(--muted-70); flex:none; min-width:38px; text-align:right; }

/* 分段进度条（作品卡七阶段 / 画面阶段镜头分段 / 磁盘占用） */
.segbar{ display:flex; gap:2px; height:4px; }
.segbar.tall{ height:6px; }
.segbar > i{ flex:1; background:var(--border); border-radius:1px; }
.segbar > i.done{ background:var(--st-done); }
.segbar > i.waiting{ background:var(--st-waiting); }
.segbar > i.active{ background:var(--st-active); }
.segbar > i.error{ background:var(--st-error); }
.segbar > i.cur{ background:var(--accent); }
/* 磁盘占用按比例分段 */
.usebar{ display:flex; height:8px; border-radius:4px; overflow:hidden; background:var(--border); }
.usebar > i{ display:block; height:100%; }
.usebar > i.img{ background:var(--st-active); }
.usebar > i.vid{ background:var(--st-waiting); }
.usebar > i.aud{ background:var(--st-done); }
.usebar > i.oth{ background:var(--st-idle); }
.legend{ display:flex; flex-wrap:wrap; gap:8px 20px; margin-top:10px; }
.legend .it{ display:flex; align-items:center; gap:6px; font-size:12px; color:var(--muted-70); }
.legend .sw{ width:10px; height:10px; border-radius:2px; flex:none; }

/* 四、Toast：右上角，最多 3 条 */
.toasts{
  position:fixed; top:calc(var(--topbar-h) + var(--safe-top) + 16px); right:calc(24px + var(--safe-right)); z-index:95;
  display:flex; flex-direction:column; gap:8px; pointer-events:none;
}
.toast{
  width:360px; max-width:calc(100vw - 48px); display:flex; align-items:flex-start; gap:10px;
  padding:14px 16px; background:var(--raised); border:1px solid var(--border);
  border-radius:var(--r-card); border-left:3px solid var(--st-done);
  box-shadow:var(--shadow-pop); font-size:13px; line-height:1.7; color:var(--read);
  pointer-events:auto; animation:slidein .18s ease;
}
@keyframes slidein{ from{ opacity:0; transform:translateX(12px); } to{ opacity:1; transform:none; } }
.toast > svg{ width:16px; height:16px; stroke:var(--st-done); stroke-width:1.7; fill:none; flex:none; margin-top:3px; }
/* 正文列。类名必须同时写 .bd 与 .body：app.js 的 toast() 生成的是
   `<div class="bd">`，而这里原来只写了 .body——选择器一直没命中，
   正文拿的是 flex:0 1 auto + min-width:auto，遇到不可断的长串
   （长文件名、任务 ID、路径）会撑破固定的 360px 宽：实测 .bd 到 405px，
   文字溢出 toast 右边界 90px 铺到卡片外面。
   min-width:0 是关键那一条，flex:1 只解决分配、不解决"最小内容宽"。 */
.toast .bd,.toast .body{ flex:1; min-width:0; }
/* 长串要能断。overflow-wrap 只在词内无处可断时才切，正常中英文不受影响。 */
.toast .bd p,.toast .body p{ overflow-wrap:anywhere; }
/* 详情行（toast 的第二行，通常是路径或原因）。此前没有任何样式——
   设计系统里的 .sub 只在 .tbl .two-line 与 .menu button 下有定义，
   toast 里这个是裸的，实测与主文案同为 13px/read 色，读起来是并列的两句话，
   分不出哪句是结果、哪句是补充。 */
.toast .bd > .sub,.toast .body > .sub{ margin-top:3px; font-size:12px;
  line-height:1.6; color:var(--muted-70); overflow-wrap:anywhere; }
/* 动作按钮（「查看队列」「在文件夹中显示」）。.btn-text 的 padding 是
   0 2px，不给上外边距它会紧贴详情行（实测间距 0），读起来像同一行的续写。 */
.toast .bd > .btn-text,.toast .body > .btn-text{ margin-top:8px; display:block; }
.toast .bd .btn-text,.toast .body .btn-text{ color:var(--muted); }
.toast .bd .btn-text:hover,.toast .body .btn-text:hover{ color:var(--fg); }
.toast.info{ border-left-color:var(--st-active); } .toast.info > svg{ stroke:var(--st-active); }
.toast.warn{ border-left-color:var(--st-warn); }   .toast.warn > svg{ stroke:var(--st-warn); }
.toast.error{ border-left-color:var(--st-error); } .toast.error > svg{ stroke:var(--st-error); }

/* 五、弹窗三等级 */
.scrim{
  position:fixed; inset:0; z-index:85; background:var(--scrim);
  display:grid; place-items:center;
  padding:calc(16px + var(--safe-top)) calc(16px + var(--safe-right)) calc(16px + var(--safe-bottom)) calc(16px + var(--safe-left));
}
.scrim[hidden]{ display:none; }
.scrim.lightbox{ background:color-mix(in oklch,var(--bg) 92%, transparent); }
/* P0：弹窗宽度 480–640px。flex 列布局让头尾固定、body 自己滚 */
.modal{
  width:480px; max-width:100%; max-height:calc(100vh - 32px);
  max-height:calc(100dvh - 32px - var(--safe-top) - var(--safe-bottom));
  display:flex; flex-direction:column; min-height:0;
  background:var(--raised); border:1px solid var(--border);
  border-radius:var(--r-card); box-shadow:var(--shadow-pop);
}
.modal.w560,.modal.md{ width:560px; }
.modal.w640,.modal.lg{ width:640px; }
.modal-head{ display:flex; align-items:center; gap:10px; padding:20px; border-bottom:1px solid var(--border); flex:none; }
.modal-head h2{ font-size:18px; font-weight:600; }
.modal-head > svg{ width:20px; height:20px; stroke:var(--accent); stroke-width:1.6; fill:none; flex:none; }
.modal-head.danger > svg{ stroke:var(--st-error); }
.modal-head .iconbtn{ margin-left:auto; }
.modal-seg{ padding:16px 20px; }
.modal-seg + .modal-seg{ border-top:1px solid var(--border); }
.modal-seg .lab{ font-size:12px; color:var(--muted-70); margin-bottom:10px; line-height:1.5; }
.modal-foot{
  display:flex; align-items:center; gap:12px; padding:20px;
  border-top:1px solid var(--border); justify-content:flex-end;
}
.modal-foot .lead{ margin-right:auto; font-family:var(--font-mono); font-size:12px; color:var(--muted-70); }
.modal-foot .lead.warn{ color:var(--tx-warn); }
.modal-foot .lead.err{ color:color-mix(in oklch,var(--st-error) 90%,white); }

/* 标签/值结构：标签列固定 96px，值列右对齐等宽 */
.kv{ display:grid; grid-template-columns:96px 1fr; gap:10px 16px; align-items:baseline; }
.kv.w120{ grid-template-columns:120px 1fr; }
.kv dt{ font-size:13px; color:var(--muted-70); line-height:1.7; }
.kv dd{ margin:0; font-size:13px; color:var(--fg); line-height:1.7; text-align:right; }
.kv dd.mono{ font-size:12px; }
.kv dd .badge{ vertical-align:middle; margin-left:6px; }
.kv dd .big{ font-family:var(--font-mono); font-size:18px; color:var(--fg); }
/* 设置页的标签/控件行：值列左对齐 */
.kv.form dd{ text-align:left; }

/* 预估费用突出区：唯一允许 gold 描边的信息块 */
.cost{
  background:var(--bg); border:1px solid var(--gold-40);
  border-radius:var(--r-card); padding:16px; text-align:center;
}
.cost .v{ font-family:var(--font-mono); font-size:24px; color:var(--accent); line-height:1.4; }
.cost .v.unknown{ font-family:var(--font-body); font-size:15px; }
.cost .n{ font-size:12px; color:var(--muted-70); margin-top:4px; line-height:1.6; }

/* 对比区（覆盖确认） */
.diff{ display:grid; grid-template-columns:1fr 1fr; gap:16px; }
.diff .col{ background:var(--bg); border:1px solid var(--border); border-radius:var(--r-ctl); padding:12px; }
.diff .col h3{ font-size:12px; color:var(--muted-70); font-weight:500; margin-bottom:8px; }
.diff .col .txt{ font-size:13px; line-height:1.7; color:var(--read); max-height:160px; overflow:auto; }
.diff .col.new{ border-color:var(--gold-40); }
/* 新旧数量对比 */
.count-diff{ display:flex; align-items:center; justify-content:center; gap:16px; padding:12px 0; }
.count-diff .grp{ text-align:center; }
.count-diff .grp .v{ font-family:var(--font-mono); font-size:24px; color:var(--fg); }
.count-diff .grp .l{ font-size:12px; color:var(--muted-70); }
.count-diff > svg{ width:20px; height:20px; stroke:var(--muted-60); stroke-width:1.5; fill:none; }
/* 影响清单：最多 8 项后滚动 */
.impact{
  background:var(--bg); border:1px solid var(--border); border-radius:var(--r-ctl);
  padding:8px 12px; max-height:calc(1.7em * 8 + 16px); overflow:auto;
}
.impact li{ font-size:13px; line-height:1.7; color:var(--muted); list-style:none; }
.impact li .mono{ font-family:var(--font-mono); font-size:12px; color:var(--muted-70); }

/* 六、抽屉 */
.drawer{
  position:fixed; top:var(--topbar-h); right:0; bottom:0; width:480px; max-width:100%; z-index:80;
  background:var(--raised); border-left:1px solid var(--border);
  box-shadow:var(--shadow-pop); display:flex; flex-direction:column;
  transform:translateX(100%); transition:transform .2s ease; visibility:hidden;
}
.drawer[data-open="true"]{ transform:none; visibility:visible; }
.drawer.w520{ width:520px; }
.drawer.w320{ width:320px; }
.drawer-head{ display:flex; align-items:center; gap:10px; padding:16px 20px; border-bottom:1px solid var(--border); flex:none; }
.drawer-head > svg{ width:18px; height:18px; flex:none; stroke:currentColor; stroke-width:1.5; fill:none; color:var(--muted-70); }
.drawer-head h2{ font-size:18px; font-weight:500; }
.drawer-head .iconbtn{ margin-left:auto; }
.drawer-body{ flex:1; min-height:0; padding:16px 20px; }
.drawer-foot{ display:flex; align-items:center; justify-content:flex-end; gap:12px; padding:16px 20px; border-top:1px solid var(--border); flex:none; }

/* ══ Codex 任务面板（P5–P11 共用） ═══════════════════════════════ */
.codex-drawer{ width:520px; isolation:isolate; background:var(--raised); }
.codex-drawer .drawer-head{ background:var(--raised); padding:12px 16px; }
.codex-drawer .drawer-head h2{ font-size:16px; }
.codex-drawer .drawer-head > svg{ color:var(--accent); }
.codex-stage{
  color:var(--muted-70); font-size:12px; line-height:1.4;
  padding:2px 8px; border:1px solid var(--border); border-radius:var(--r-badge);
  background:var(--surface); flex:none;
}
.codex-body{ display:flex; flex-direction:column; gap:12px; min-width:0; overflow-x:hidden; overflow-wrap:anywhere; background:var(--raised); }
.codex-body > *{ min-width:0; }
.codex-compose{ display:grid; gap:8px; padding-bottom:12px; border-bottom:1px solid var(--border); }
.codex-compose .field{ display:grid; gap:6px; }
.codex-compose textarea{ min-height:80px; resize:vertical; }
.codex-compose .btn{ justify-self:start; }
.codex-hint{ color:var(--muted-70); font-size:12px; line-height:1.7; }
.codex-run{ display:grid; grid-template-columns:minmax(0,1fr); gap:12px; min-width:0; }
.codex-run-head{ display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.codex-run-head .codex-run-id{ margin-left:auto; max-width:48%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; color:var(--muted-60); font:12px var(--font-mono); }
.codex-status{ display:flex; align-items:center; gap:8px; min-width:0; }
.codex-status > svg{ width:16px; height:16px; stroke:currentColor; fill:none; flex:none; }
.codex-status-text{ min-width:0; overflow-wrap:anywhere; }
.codex-progress{ display:grid; gap:6px; }
.codex-progress .bar{ width:100%; }
.codex-events{ display:grid; gap:8px; max-height:260px; overflow-y:auto; overflow-x:hidden; padding-right:2px; }
.codex-event{ display:grid; grid-template-columns:16px minmax(0,1fr); gap:8px; align-items:start; padding:8px 10px; background:var(--surface); border:1px solid var(--border); border-radius:var(--r-ctl); }
.codex-event > svg,
.codex-event > span > svg{ width:15px; height:15px; margin-top:2px; stroke:var(--muted-70); stroke-width:1.5; fill:none; }
.codex-event .t{ color:var(--read); font-size:13px; line-height:1.6; overflow-wrap:anywhere; }
.codex-event .at{ color:var(--muted-60); font:11px var(--font-mono); }
.codex-pending{ display:grid; gap:10px; padding:12px; background:var(--stage); border:1px solid var(--st-waiting); border-radius:var(--r-card); }
.codex-pending h3{ font-size:14px; font-weight:500; color:var(--fg); }
.codex-pending p{ color:var(--muted-80); font-size:13px; line-height:1.7; white-space:pre-wrap; overflow-wrap:anywhere; }
.codex-pending .field{ display:grid; gap:6px; }
.codex-pending textarea{ min-height:72px; resize:vertical; }
.codex-pending .acts{ display:flex; gap:8px; flex-wrap:wrap; }
.codex-approval{ border-color:var(--st-warn); }
.codex-approval .acts .btn-primary{ background:var(--st-done); border-color:var(--st-done); color:var(--bg); }
.codex-result{ display:grid; gap:8px; padding:12px; background:var(--surface); border:1px solid var(--st-done); border-radius:var(--r-card); }
.codex-result .title{ display:flex; align-items:center; gap:8px; color:var(--fg); font-size:14px; }
.codex-result .title > svg{ width:16px; height:16px; flex:none; stroke:var(--st-done); stroke-width:1.5; fill:none; }
.codex-result .meta{ color:var(--muted-70); font-size:12px; line-height:1.6; overflow-wrap:anywhere; }
.codex-candidate{ display:grid; gap:6px; padding:10px; background:var(--bg); border:1px solid var(--border); border-radius:var(--r-ctl); }
.codex-candidate summary{ color:var(--muted-80); font-size:12px; cursor:pointer; }
.codex-candidate pre{ min-width:0; max-height:280px; overflow-y:auto; overflow-x:hidden; margin:0; color:var(--read); font:12px/1.65 var(--font-mono); white-space:pre-wrap; overflow-wrap:anywhere; }
.codex-candidate .line{ color:var(--muted-80); font-size:12px; line-height:1.6; overflow-wrap:anywhere; }
#codex-candidate-modal .codex-candidate pre{ max-height:none; overflow:visible; }
.codex-image-grid{ display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:8px; }
.codex-image-item{ display:grid; gap:5px; min-width:0; }
.codex-image-item .media{ min-height:96px; }
.codex-image-item img{ width:100%; height:100%; max-height:180px; object-fit:contain; background:var(--stage); }
.codex-image-item .meta{ color:var(--muted-70); font:11px/1.5 var(--font-mono); overflow-wrap:anywhere; }
@media (max-width:479px){ .codex-image-grid{ grid-template-columns:minmax(0,1fr); } }
.codex-history{ display:grid; grid-template-columns:minmax(0,1fr); gap:8px; min-width:0; }
.codex-history h3{ font-size:13px; color:var(--fg); font-weight:500; }
.codex-history-list{ display:grid; grid-template-columns:minmax(0,1fr); gap:6px; min-width:0; }
.codex-history-item{ display:grid; grid-template-columns:minmax(0,1fr) auto; align-items:center; gap:6px 8px; width:100%; min-width:0; padding:8px 10px; background:transparent; border:1px solid var(--border); border-radius:var(--r-ctl); text-align:left; }
.codex-history-item:hover{ background:var(--surface); border-color:var(--gold-40); }
.codex-history-item .badge{ justify-self:start; }
.codex-history-item .name{
  grid-column:1 / -1; grid-row:2; min-width:0; color:var(--muted); line-height:1.55;
  display:-webkit-box; -webkit-line-clamp:3; -webkit-box-orient:vertical; overflow:hidden;
}
.codex-history-item .time{ grid-column:2; grid-row:1; color:var(--muted-60); font:11px var(--font-mono); }
.codex-empty{ display:grid; place-items:center; gap:6px; min-height:88px; padding:16px; border:1px dashed var(--border); border-radius:var(--r-card); color:var(--muted-70); text-align:center; }
.codex-empty > svg{ width:22px; height:22px; stroke:currentColor; fill:none; }
.codex-budget-count{ font:12px var(--font-mono); color:var(--muted-70); }
.codex-budget-count.warn{ color:var(--tx-warn); }
.codex-budget-count.err{ color:var(--tx-error); }
.codex-budget-status{ margin-top:10px; }
.codex-budget-result{ color:var(--muted-80); font-size:13px; line-height:1.7; }
@media (max-width:1023px){
  .codex-drawer{ width:100%; }
  .codex-compose .btn{ width:100%; }
  .codex-run-head .codex-run-id{ max-width:100%; width:100%; margin-left:0; }
}

/* 下拉菜单 */
.rel{ position:relative; }
.menu{
  position:absolute; right:0; top:calc(100% + 4px); z-index:60; min-width:200px; padding:4px;
  max-height:calc(100vh - 32px); overflow-y:auto; overscroll-behavior:contain;
  background:var(--raised); border:1px solid var(--border); border-radius:var(--r-card);
  box-shadow:var(--shadow-pop);
}
.menu[hidden]{ display:none; }
.menu.up{ top:auto; bottom:calc(100% + 4px); }
.menu.left{ right:auto; left:0; }
.menu button{
  width:100%; display:flex; align-items:flex-start; gap:8px; padding:8px 10px;
  background:transparent; border:0; border-radius:var(--r-ctl);
  color:var(--muted); font-size:13px; text-align:left; line-height:1.6;
}
.menu button svg{ width:14px; height:14px; stroke:currentColor; stroke-width:1.5; fill:none; flex:none; margin-top:3px; }
.menu button:hover{ background:var(--surface); color:var(--fg); }
.menu button .sub{ display:block; font-size:12px; color:var(--muted-60); }
.menu button:hover .sub{ color:var(--muted-70); }
.menu button.danger{ color:var(--tx-error); }
.menu button.danger:hover{ background:color-mix(in oklch,var(--st-error) 12%,transparent); color:color-mix(in oklch,var(--st-error) 74%,white); }
.menu hr{ border:0; border-top:1px solid var(--border); margin:4px 0; }
.menu .mlabel{ padding:6px 10px 4px; font-size:12px; color:var(--muted-60); }

/* 批量操作条：浮在内容区底部 */
.bulkbar{
  position:sticky; bottom:0; z-index:20; display:flex; align-items:center; gap:12px;
  margin-top:12px; padding:12px 16px;
  background:var(--raised); border:1px solid var(--border); border-radius:var(--r-card);
  box-shadow:var(--shadow-pop); font-size:13px;
}
.bulkbar .cnt{ font-family:var(--font-mono); color:var(--fg); }
.bulkbar .sp{ margin-left:auto; display:flex; align-items:center; gap:8px; }

/* ══════════════════════════════════════════════════════════════════
   媒体承载：缩略图 / 预览 / 灯箱
   neutral-stage 去暖调，周围 24px 内不允许 gold 色块
   ══════════════════════════════════════════════════════════════════ */
.media{
  background:var(--stage); border:1px solid var(--border); border-radius:var(--r-badge);
  display:grid; place-items:center; overflow:hidden; position:relative;
  color:var(--muted-60); font-size:12px;
}
.media > svg{ width:20px; height:20px; stroke:currentColor; stroke-width:1.5; fill:none; }
.media.v916{ aspect-ratio:9/16; }
.media.v169{ aspect-ratio:16/9; }
.media.v11{  aspect-ratio:1/1; }
.media.v43{  aspect-ratio:4/3; }
.thumb{ width:32px; height:32px; flex:none; }
.thumb-40{ width:40px; height:40px; flex:none; }
.thumb-48{ width:48px; height:48px; flex:none; }
.thumb-64{ width:64px; height:64px; flex:none; }
.media.broken{ border-color:var(--st-error); color:var(--tx-error); }
/* 单元错误的内容层（P18 规范化）。.media 是 place-items:center，
   图标与文字两个子元素会叠在同一格；套一层 .empty-cell 拿到纵向排列，
   再用这个类把虚线描边去掉、配色交给外层 .media.broken 决定
   （虚线是「还没有」，实线 ember 是「坏了」，两件事不能长一样）。 */
.empty-cell.err-cellv{ border-style:solid; border-color:transparent;
  background:transparent; color:inherit; }
/* 叠加层：镜头号 / 版本 / 时长 */
.ovl{
  position:absolute; z-index:2; display:inline-flex; align-items:center; gap:4px;
  height:20px; padding-inline:6px; border-radius:var(--r-badge);
  background:color-mix(in oklch, var(--bg) 78%, transparent);
  color:var(--fg); font-family:var(--font-mono); font-size:11px; line-height:1;
  backdrop-filter:blur(4px);
}
.ovl.tl{ top:6px; left:6px; } .ovl.tr{ top:6px; right:6px; }
.ovl.bl{ bottom:6px; left:6px; } .ovl.br{ bottom:6px; right:6px; }
.ovl svg{ width:11px; height:11px; stroke:currentColor; stroke-width:1.8; fill:none; }
/* 未引用标记是唯一允许的 gold 叠加，且只在素材库缩略图上 */
.ovl.unref{ background:var(--bg); border:1px solid var(--gold-40); color:var(--accent); }
/* 生成中遮罩 */
.gen-mask{
  position:absolute; inset:0; z-index:3; display:grid; place-items:center; gap:6px;
  background:color-mix(in oklch, var(--bg) 62%, transparent);
  color:var(--muted); font-size:12px; align-content:center;
}
/* 波形 */
.wave{ display:flex; align-items:center; gap:2px; height:24px; width:120px; flex:none; }
.wave > i{ flex:1; background:var(--border); border-radius:1px; min-height:3px; }
.wave.ready > i{ background:var(--muted-80); }
.wave-wrap{ position:relative; }
.wave-cursor{ position:absolute; top:0; bottom:0; width:1px; background:var(--accent); }

/* 版本切换器 */
.vers{ display:flex; align-items:center; gap:6px; flex-wrap:wrap; }
.vers button{
  width:32px; height:32px; flex:none; padding:0; position:relative;
  background:var(--stage); border:1px solid var(--border); border-radius:var(--r-badge);
  color:var(--muted-60); font-family:var(--font-mono); font-size:11px;
}
.vers button:hover{ border-color:color-mix(in oklch,var(--border) 60%,white); color:var(--muted); }
.vers button[aria-pressed="true"]{ border-color:var(--accent); color:var(--fg); }
.vers button[aria-pressed="true"]::after{
  content:''; position:absolute; top:-3px; right:-3px; width:12px; height:12px;
  border-radius:50%; background:var(--st-done);
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3.5'><path d='M20 6 9 17l-5-5'/></svg>");
  background-size:9px 9px; background-position:center; background-repeat:no-repeat;
}

/* 灯箱 */
.lightbox-body{ display:flex; flex-direction:column; align-items:center; gap:16px; max-height:calc(100vh - 32px); }
.lightbox-body .stagearea{
  background:var(--stage); border:1px solid var(--border); border-radius:var(--r-card);
  max-height:78vh; display:grid; place-items:center; overflow:hidden;
}
.lightbox-bar{
  display:flex; align-items:center; gap:12px; padding:10px 16px;
  background:var(--raised); border:1px solid var(--border); border-radius:var(--r-card);
  box-shadow:var(--shadow-pop); font-size:12px; max-width:100%;
}

/* 播放控制条 */
.playbar{
  display:flex; align-items:center; gap:12px; padding:10px 14px;
  background:var(--surface); border:1px solid var(--border); border-radius:var(--r-ctl);
}
.playbar .tc{ font-family:var(--font-mono); font-size:12px; color:var(--muted-70); flex:none; }
.playbar .bar{ flex:1; cursor:pointer; }

/* 卡片 */
.card{
  background:var(--surface); border:1px solid var(--border); border-radius:var(--r-card);
  display:flex; flex-direction:column; min-width:0; position:relative;
  transition:border-color .14s ease;
}
.card:hover{ border-color:var(--gold-40); }
.card[aria-selected="true"]{ border-color:var(--accent); }
.card.dimmed{ opacity:.4; }
.card-pad{ padding:12px; display:flex; flex-direction:column; gap:6px; min-width:0; }
.card .hover-acts{ position:absolute; top:8px; right:8px; z-index:4; display:flex; gap:4px; opacity:0; transition:opacity .14s ease; }
.card:hover .hover-acts,.card:focus-within .hover-acts{ opacity:1; }
.card .hover-acts .iconbtn{ background:var(--raised); border-color:var(--border); }

/* 网格 */
.grid{ display:grid; gap:16px; }
.g-auto-160{ grid-template-columns:repeat(auto-fill,minmax(160px,1fr)); }
.g-auto-200{ grid-template-columns:repeat(auto-fill,minmax(200px,1fr)); }
.g-auto-240{ grid-template-columns:repeat(auto-fill,minmax(240px,1fr)); }
.g2{ grid-template-columns:repeat(2,minmax(0,1fr)); }
.g3{ grid-template-columns:repeat(3,minmax(0,1fr)); }
.g4{ grid-template-columns:repeat(4,minmax(0,1fr)); }
/* P6 角色/场景卡片网格：≥1600 五列，逐档降到 <1024 两列 */
.g5{ grid-template-columns:repeat(5,minmax(0,1fr)); }
.row{ display:flex; align-items:center; gap:12px; flex-wrap:wrap; }
.row-t{ display:flex; align-items:flex-start; gap:12px; }
.row-between{ display:flex; align-items:center; justify-content:space-between; gap:12px; }
.col{ display:flex; flex-direction:column; gap:12px; min-width:0; }
.col-sm{ display:flex; flex-direction:column; gap:8px; min-width:0; }
.grow{ flex:1; min-width:0; }
.push{ margin-left:auto; }
.stats{ display:flex; align-items:flex-start; gap:32px; flex-wrap:wrap; }
/* 统计项：标签在上、数值在下。这条以前缺失——.stat 只有 block 默认值，
   里面两个 <span> 是行内元素，会并排挤在同一行盒里（实测 P7「场次 3」
   收成 36px 宽的一行，12px 标签与 20px 数值基线对齐，差 8px 参差不齐）。
   受影响的是 P7 五处、P9 两处与索引页；P5 那个单 <span class="stat">
   没有子结构，不受这条影响。 */
.stat{ display:flex; flex-direction:column; min-width:0; }
.stat .l{ font-size:12px; color:var(--muted-70); line-height:1.5; }
.stat .v{ font-family:var(--font-mono); font-size:20px; color:var(--fg); line-height:1.4; }
.stat .v.sm{ font-size:16px; }
.stat .v.lg{ font-size:28px; }
.stat .v.warn{ color:var(--tx-warn); }
.stat .v.err{ color:var(--tx-error); }
/* 可点统计项常常是 <button>（如「只看未绑定」），必须清掉 UA 的
   浅灰底和居中对齐，否则暗底页面上会出现一块 #EFEFEF 的白斑，
   里面的 mist 文字直接掉到 1.3:1 */
.stat.clickable{
  cursor:pointer; appearance:none; background:none; border:0; padding:0;
  font:inherit; text-align:left; color:inherit; border-radius:var(--r-badge);
}
.stat.clickable:hover .v{ text-decoration:underline; }
.stat.clickable:focus-visible{ outline:2px solid var(--accent); outline-offset:3px; }
/* 已按下（正在筛选）时给一条 1px gold 指示线——gold 表达焦点位置，不是状态 */
.stat.clickable[aria-pressed="true"]{ box-shadow:inset 0 -1px 0 var(--accent); }
/* 统计卡块：顶部 2px 状态色条 */
.statcard{
  background:var(--surface); border:1px solid var(--border); border-radius:var(--r-card);
  padding:12px 14px; position:relative; overflow:hidden; text-align:left; min-width:0;
}
.statcard::before{ content:''; position:absolute; top:0; left:0; right:0; height:2px; background:var(--st-idle); }
.statcard.idle::before{ background:var(--st-idle); }
.statcard.active::before{ background:var(--st-active); }
.statcard.waiting::before{ background:var(--st-waiting); }
.statcard.done::before{ background:var(--st-done); }
.statcard.error::before{ background:var(--st-error); }
.statcard.void::before{ background:var(--st-void); }
.statcard:hover{ border-color:var(--gold-40); }
.statcard[aria-pressed="true"]{ border-color:var(--accent); }

/* ══════════════════════════════════════════════════════════════════
   P4 阶段流水线条
   ══════════════════════════════════════════════════════════════════ */
.pipeline{
  height:var(--topbar-h); display:flex; align-items:center; gap:16px;
  padding-inline:calc(24px + var(--safe-left)) calc(24px + var(--safe-right));
  background:var(--surface); border-bottom:1px solid var(--border);
  flex:none;
}
.pipe-nodes{ display:flex; align-items:center; flex:1; min-width:0; }
.pipe-node{
  display:flex; align-items:center; gap:8px; padding:6px 8px; flex:none;
  background:transparent; border:0; border-radius:var(--r-ctl); color:var(--muted-70);
  font-size:13px; position:relative;
}
.pipe-node .mk{
  width:20px; height:20px; flex:none; border-radius:50%; display:grid; place-items:center;
  border:1px solid var(--border); background:var(--bg);
  font-family:var(--font-mono); font-size:11px; color:var(--muted-60);
}
.pipe-node .mk svg{ width:12px; height:12px; stroke:currentColor; stroke-width:2; fill:none; }
.pipe-node:hover{ background:var(--raised); }
.pipe-node[disabled]{ cursor:not-allowed; color:var(--muted-40); }
.pipe-node[disabled]:hover{ background:transparent; }
.pipe-link{ flex:1; min-width:12px; height:1px; background:var(--border); }
/* 状态：已批准 / 待审 / 失败 用实底；当前用 2px gold 描边 + 下划线 */
.pipe-node.done .mk{ background:var(--st-done); border-color:transparent; color:var(--fg); }
.pipe-node.waiting .mk{ background:var(--st-waiting); border-color:transparent; color:var(--fg); }
.pipe-node.error .mk{ background:var(--st-error); border-color:transparent; color:var(--fg); }
.pipe-node.active .mk{ background:var(--st-active); border-color:transparent; color:var(--fg); }
.pipe-node.void .mk{ background:var(--st-void); border-color:transparent; color:var(--fg); }
.pipe-node.cur{ color:var(--fg); font-weight:500; box-shadow:inset 0 -2px 0 var(--accent); }
.pipe-node.cur .mk{ background:var(--bg); border:2px solid var(--accent); color:var(--fg); }
.pipe-right{ display:flex; align-items:center; gap:12px; flex:none; }

/* 阶段内容区标题行 */
/* 工具条高度走密度 token：宽松 44px / 紧凑 36px。
   min-height 而非 height，换行时能长高不裁切 */
.stage-head{ display:flex; align-items:center; gap:16px; flex-wrap:wrap; min-height:var(--bar-h); }
.toolbar{
  display:flex; align-items:center; gap:8px; flex-wrap:wrap;
  min-height:var(--bar-h); padding-inline:12px;
  background:var(--surface); border:1px solid var(--border); border-radius:var(--r-card);
}
.toolbar .sp{ margin-left:auto; display:flex; align-items:center; gap:8px; }
.stage-head h2{ font-size:18px; font-weight:500; }
.stage-head .tools{ margin-left:auto; display:flex; align-items:center; gap:8px; flex-wrap:wrap; }

/* 阶段标签（P0 六节） */
.stage-tag{
  display:inline-flex; align-items:center; gap:5px; height:var(--badge-h); padding-inline:8px;
  border-radius:var(--r-badge); font-size:12px; font-weight:500; line-height:1.5; white-space:nowrap;
}
.stage-tag svg{ width:12px; height:12px; stroke:currentColor; stroke-width:1.8; fill:none; }
.stage-tag.cur{ background:var(--surface); color:var(--fg); box-shadow:inset 0 -2px 0 var(--accent); }
.stage-tag.done{ background:color-mix(in oklch,var(--st-done) 16%,transparent); color:var(--tx-done); }
.stage-tag.waiting{ background:color-mix(in oklch,var(--st-waiting) 16%,transparent); color:var(--tx-waiting); }
.stage-tag.error{ background:color-mix(in oklch,var(--st-error) 16%,transparent); color:var(--tx-error); }
.stage-tag.locked{ background:var(--bg); border:1px solid var(--border); color:var(--muted-60); }

/* 矩阵状态格 */
.cell{
  display:inline-flex; align-items:center; justify-content:center; gap:4px;
  width:100%; max-width:76px; height:32px; border-radius:var(--r-badge);
  border:1px solid transparent; font-size:12px; line-height:1;
}
.cell svg{ width:13px; height:13px; stroke:currentColor; stroke-width:1.9; fill:none; }
.cell.done{    background:color-mix(in oklch,var(--st-done) 16%,transparent);    color:var(--tx-done); }
.cell.waiting{ background:color-mix(in oklch,var(--st-waiting) 16%,transparent); color:var(--tx-waiting); }
.cell.error{   background:color-mix(in oklch,var(--st-error) 16%,transparent);   color:var(--tx-error); }
.cell.active{  background:color-mix(in oklch,var(--st-active) 16%,transparent);  color:var(--tx-active); }
.cell.void{    background:color-mix(in oklch,var(--st-void) 16%,transparent);    color:var(--tx-void); }
.cell.todo{    border-color:var(--border); color:var(--muted-60); }
.cell.cur{ background:var(--surface); border-color:var(--border); color:var(--fg); font-weight:500; box-shadow:inset 0 -2px 0 var(--accent); }
.tbl tbody tr:hover .cell.cur{ background:var(--bg); }

/* 步骤徽章（P1 引导） */
.step-badge{
  width:24px; height:24px; flex:none; border-radius:50%; display:grid; place-items:center;
  border:1px solid var(--border); background:var(--bg);
  font-family:var(--font-mono); font-size:12px; color:var(--muted-60);
}
.step-badge.cur{ border:2px solid var(--accent); color:var(--fg); }
.step-badge.done{ background:var(--st-done); border-color:transparent; color:var(--fg); }
.step-badge.done svg{ width:13px; height:13px; stroke:currentColor; stroke-width:2.4; fill:none; }
.step{ display:flex; gap:16px; padding:20px 0; }
.step + .step{ border-top:1px solid var(--border); }
.step.locked{ opacity:.4; pointer-events:none; }
.step .sbody{ flex:1; min-width:0; display:flex; flex-direction:column; gap:10px; }

/* 时间线（P11） */
.tl-wrap{ display:grid; grid-template-columns:120px minmax(0,1fr); border:1px solid var(--border); border-radius:var(--r-card); background:var(--surface); overflow:hidden; }
.tl-names{ border-right:1px solid var(--border); }
.tl-names .nm{ display:flex; align-items:center; height:56px; padding-inline:12px; font-size:12px; color:var(--muted-70); border-bottom:1px solid var(--border); }
.tl-names .nm:last-child{ border-bottom:0; }
.tl-names .hd{ height:24px; border-bottom:1px solid var(--border); }
.tl-body{ position:relative; overflow-x:auto; }
.tl-ruler{ height:24px; border-bottom:1px solid var(--border); position:relative; min-width:900px; }
.tl-ruler .tick{ position:absolute; top:0; bottom:0; border-left:1px solid var(--border); padding-left:4px; font-family:var(--font-mono); font-size:10px; color:var(--muted-80); line-height:24px; }
.tl-track{ height:56px; border-bottom:1px solid var(--border); position:relative; min-width:900px; }
.tl-track:last-child{ border-bottom:0; }
.tl-clip{
  position:absolute; top:4px; height:48px; border-radius:var(--r-badge);
  background:var(--stage); border:1px solid var(--border);
  display:flex; align-items:center; gap:6px; padding:0 6px; overflow:hidden;
  font-family:var(--font-mono); font-size:11px; color:var(--muted);
}
.tl-clip:hover{ border-color:color-mix(in oklch,var(--border) 55%,white); }
.tl-clip[aria-selected="true"]{ border:2px solid var(--accent); }
.tl-clip.narr{ top:12px; height:32px; background:color-mix(in oklch,var(--accent) 24%,var(--surface)); border-color:var(--gold-40); }
.tl-clip.music{ top:12px; height:32px; background:color-mix(in oklch,var(--st-active) 20%,var(--surface)); border-color:var(--st-active); }
.tl-clip.missing{ background:transparent; border:1px dashed var(--st-error); color:var(--tx-error); }
.tl-cursor{ position:absolute; top:0; bottom:0; width:1px; background:var(--accent); z-index:5; pointer-events:none; }
.tl-cursor::before{
  content:''; position:absolute; top:0; left:-5px; width:11px; height:8px; background:var(--accent);
  clip-path:polygon(0 0,100% 0,50% 100%);
}
.tl-trans{
  position:absolute; top:22px; width:12px; height:12px; z-index:4;
  background:var(--surface); border:1px solid var(--border); transform:rotate(45deg);
}
.tl-trans[aria-selected="true"]{ border-color:var(--accent); }

/* ══════════════════════════════════════════════════════════════════
   响应式：断点为窗口宽度，非设备宽度
   ══════════════════════════════════════════════════════════════════ */
.drawer-toggle{ display:none; }
.rail-picker{ display:none; }

/* 1280–1599px：右栏收窄至 280px */
@media (max-width:1599px){
  :root{ --rail-r:280px; }
  .g5{ grid-template-columns:repeat(4,minmax(0,1fr)); }
}

/* 1024–1279px：右栏折叠为抽屉 */
@media (max-width:1279px){
  .shell{ grid-template-columns:var(--rail-l) minmax(0,1fr); }
  /* 右栏变抽屉后不再占轨，正文吃满整行 */
  .shell.no-left{ grid-template-columns:minmax(0,1fr); }
  .rail-r{
    position:fixed; top:var(--topbar-h); right:0; bottom:0; width:320px; z-index:80;
    transform:translateX(100%); transition:transform .2s ease; visibility:hidden;
    box-shadow:var(--shadow-pop);
  }
  .rail-r[data-open="true"]{ transform:none; visibility:visible; }
  .drawer-toggle{ display:grid; }
  .g4,.g5{ grid-template-columns:repeat(3,minmax(0,1fr)); }
  .hide-1279{ display:none !important; }
}

@media (min-width:768px) and (max-width:1023px){
  .topbar .crumb{ display:none !important; }
}

/* <1024px：单栏，左栏折为顶部下拉，表格转堆叠卡片，触控目标 44px */
@media (max-width:1023px){
  .shell,.shell.no-right,.shell.no-left,.shell.settings{ grid-template-columns:minmax(0,1fr); height:auto; }
  .rail-l{ display:none; }
  .rail-picker{ display:block; padding:16px 16px 0; }
  .main-head{ padding:16px 16px 0; }
  .main-body{ padding:16px; }
  .t-page{ font-size:20px; }
  .title-row .tools{ width:100%; margin-left:0; }
  .btn{ height:44px; padding-inline:16px; }
  .btn-sm{ height:44px; }
  .iconbtn{ width:44px; height:44px; }
  .iconbtn-sm,.iconbtn-xs{ width:44px; height:44px; }
  .seg button{ min-width:44px; height:44px; }
  .input,.select,.select-sm{ height:44px; }
  .pwd .input{ padding-right:48px; }
  .pwd .iconbtn{ right:0; top:0; width:44px; height:44px; }
  .range{ height:44px; }
  .btn-text,.btn-danger-text{ min-width:44px; min-height:44px; display:inline-flex; align-items:center; }
  .brand{ min-height:44px; }
  .pill{ min-height:44px; }
  .health.clickable{ min-height:44px; }
  .check{ min-width:44px; min-height:44px; }
  .check input{ width:22px; height:22px; }
  .navitem{ min-height:44px; }
  .card .hover-acts{ opacity:1; }
  .g2,.g3,.g4,.g5{ grid-template-columns:repeat(2,minmax(0,1fr)); }
  .g-auto-200,.g-auto-240{ grid-template-columns:repeat(2,minmax(0,1fr)); }
  .modal,.modal.w560,.modal.w640,.modal.md,.modal.lg{ width:calc(100vw - 32px); }
  .kv,.kv.w120{ grid-template-columns:1fr; gap:4px; }
  .kv dd{ text-align:left; padding-bottom:8px; }
  .modal-foot{ flex-direction:column-reverse; align-items:stretch; }
  .modal-foot .btn{ width:100%; }
  .modal-foot .lead{ margin:0 0 4px; text-align:center; }
  .drawer,.drawer.w520,.drawer.w320{ top:0; width:100%; }
  .rail-r{ top:0; width:100%; }
  .toasts{ top:calc(64px + var(--safe-top)); right:calc(16px + var(--safe-right)); left:calc(16px + var(--safe-left)); }
  .toast{ width:auto; }
  .drawer,.rail-r{ padding-bottom:var(--safe-bottom); }
  .diff{ grid-template-columns:1fr; }
  .stats{ gap:16px 24px; }
  .pipeline{ padding-inline:16px; overflow-x:auto; gap:8px; }
  .pipe-nodes{ flex:none; }
  .pipe-node{ min-width:44px; min-height:44px; }
  .pipe-right{ display:none; }
  .stage-head .tools{ width:100%; margin-left:0; }
  .hide-1023{ display:none !important; }
  /* 表格转纵向堆叠卡片由各页的 .stack-cards 承担 */
  .stack-cards{ display:flex; flex-direction:column; gap:12px; }
  .stack-card{ background:var(--surface); border:1px solid var(--border); border-radius:var(--r-card); padding:16px; }
  .stack-card .hd{ display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
  .stack-card .kvs{ display:grid; grid-template-columns:1fr 1fr; gap:8px 16px; margin-top:14px; font-size:13px; }
  .stack-card .kvs .l{ color:var(--muted-70); }
  .stack-card .kvs .v{ color:var(--fg); font-family:var(--font-mono); font-size:12px; }
  .stack-card .acts{ display:flex; gap:8px; margin-top:16px; }
  .stack-card .acts .btn{ flex:1; }
  .bulkbar{ flex-wrap:wrap; }
  .bulkbar .sp{ width:100%; margin-left:0; }
  .bulkbar .sp .btn{ flex:1; }
}

/* 手机顶栏只保留品牌与核心页面动作；桌面预览控件在设置页仍可访问。 */
@media (max-width:767px){
  .topbar{ gap:8px; }
  body[data-platform="mac"] .topbar{ padding-left:calc(16px + var(--safe-left)); }
  body[data-platform="win"] .topbar{ padding-right:calc(16px + var(--safe-right)); }
  .topbar .crumb{ display:none; }
  .topbar .pill{ display:none; }
  body[data-platform="win"] .winctl{ display:none; }
}

/* 320–479px 不再让七阶段条横向滚动，只保留七个等宽阶段标记。 */
@media (max-width:479px){
  .pipeline{ overflow:visible; padding-inline:6px; gap:0; }
  .pipe-nodes{
    display:grid; grid-template-columns:repeat(7,minmax(0,1fr));
    width:100%; flex:1;
  }
  .pipe-link{ display:none; }
  .pipe-node{ min-width:0; width:100%; height:44px; justify-content:center; padding:6px 2px; }
  .pipe-node .nm{ white-space:nowrap; font-size:11px; }
  .pipe-node.cur .nm{ display:inline; max-width:100%; overflow:hidden; text-overflow:ellipsis; }
}

@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{ animation-duration:.001ms !important; transition-duration:.001ms !important; }
}

/* ══════════════════════════════════════════════════════════════════
   补充：工具类与前面章节遗漏的构件
   ══════════════════════════════════════════════════════════════════ */
.spacer{ margin-left:auto; }
.wrap{ flex-wrap:wrap; }
.kbd{
  font-family:var(--font-mono); font-size:11px; color:var(--muted-60);
  border:1px solid var(--border); border-radius:3px; padding:1px 5px; white-space:nowrap;
}

/* 统计卡条：statcard 的栅格容器与内文 */
.statcards{ display:grid; gap:12px; grid-template-columns:repeat(auto-fit,minmax(124px,1fr)); }
.statcard .l{ font-size:12px; color:var(--muted-70); line-height:1.6; }
.statcard .v{ font-family:var(--font-mono); font-size:26px; color:var(--fg); line-height:1.35; }
.statcard .v.err{ color:var(--tx-error); }
.statcard .v.warn{ color:var(--tx-warn); }

/* 弹窗滚动体 */
.modal-body{ padding:20px; overflow-y:auto; flex:1 1 auto; min-height:0; }
.modal-body > * + *{ margin-top:16px; }
.modal-foot .lead-note{ font-size:12px; color:var(--muted-70); line-height:1.6; margin-right:auto; max-width:34em; }
.modal-foot .lead-note.warn{ color:var(--tx-warn); }
.modal-foot .lead-note.err{ color:var(--tx-error); }

/* 键值行（比 .kv 更紧，用于弹窗内清单） */
.kv-rows{ display:flex; flex-direction:column; gap:8px; font-size:13px; }
.kv-rows > .lb{ display:flex; gap:12px; align-items:baseline; }
.kv-rows > .lb > .l{ color:var(--muted-70); min-width:8em; flex:none; }
.kv-rows > .lb > .v{ color:var(--fg); }

/* 未解锁徽章：ink 底 + line 描边 + mist 40%，不占任何状态色 */
.b-lock{ background:var(--bg); border:1px solid var(--border); color:var(--muted-60); }

/* 宽屏下堆叠卡片容器不渲染。用 min-width 与窄屏 media 互斥，
   避免与 max-width:1023px 里的 display:flex 同权重打架 */
@media (min-width:1024px){
  .stack-cards{ display:none; }
}

/* ══════════════════════════════════════════════════════════════════
   P4 外壳：窄屏批准条、右栏任务行/模型行、空态
   ══════════════════════════════════════════════════════════════════ */
/* 宽屏不渲染。批准区在流水线条右端，见 .pipe-right */
.stage-foot{ display:none; }

/* 右栏任务行。四段网格：状态徽章 / 任务号 / 耗时 / 操作。
   任务号 minmax(0,1fr) 才能在 320px 右栏里让 ellipsis 生效 */
.task-row{
  display:grid; grid-template-columns:auto minmax(0,1fr) auto auto;
  align-items:center; gap:8px; min-height:32px; font-size:12px;
}
.task-row + .task-row{ border-top:1px solid var(--border); }
.task-row .tid{
  font-size:11px; color:var(--muted-60);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.task-row .dur{ font-size:11px; color:var(--muted-60); flex:none; }

/* 模型行。同样让模型 id 挤压，「更换」永不被推出 */
.model-row{
  display:grid; grid-template-columns:minmax(0,1fr) auto; align-items:center;
  gap:4px 8px; padding-block:8px; font-size:12px;
}
.model-row + .model-row{ border-top:1px solid var(--border); }
.model-row .mid{
  font-size:12px; color:var(--fg);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
/* 能力标签换到第二行通栏，窄栏里不和 id 抢宽度 */
.model-row .caps{ grid-column:1/-1; display:flex; flex-wrap:wrap; gap:4px; }

/* 空态短句。比 .t-hint 更轻，但仍达 4.5:1，因为它承载信息 */
.empty-note{ font-size:12px; line-height:1.7; color:var(--muted-60); padding-block:4px; }

/* 流水线节点里的序号。跟 .mk 同色，单独给一个类是为了让 JS 替换时不带样式 */
.pipe-node .mk .no{ font-family:var(--font-mono); font-size:11px; line-height:1; }

/* 8px 转圈：塞进 20px 的节点标记里。沿用 .spin 的 currentColor 写法 */
.spin-xs{
  display:inline-block; width:8px; height:8px; flex:none; border-radius:50%;
  border:1.5px solid color-mix(in oklch,currentColor 30%,transparent);
  border-top-color:currentColor; animation:spin .7s linear infinite;
}

/* 顶栏队列角标。绝对定位在图标按钮右上角 */
.qcnt{
  position:absolute; top:-2px; right:-2px; min-width:16px; height:16px;
  padding-inline:4px; gap:0; font-size:10px; line-height:16px; justify-content:center;
}

@media (max-width:1023px){
  /* 批准区从流水线右端移到屏底固定条。
     用 fixed 而非 sticky：这一条是 body 的直接子节点，
     而滚动发生在 .main 里，sticky 会贴到文档流位置而不是屏底。
     配套给滚动列留出 --foot-h 的底部内边距，末尾内容不会被压住 */
  .stage-foot{
    display:flex; align-items:center; gap:12px;
    position:fixed; inset:auto 0 0; z-index:20;
    min-height:56px;
    padding:10px calc(16px + var(--safe-right)) calc(10px + var(--safe-bottom)) calc(16px + var(--safe-left));
    background:var(--surface); border-top:1px solid var(--border);
    box-shadow:0 -6px 16px color-mix(in oklch, var(--bg) 60%, transparent);
  }
  .stage-foot .btn{ flex:1; }
  /* 66px = 56 条高 + 10 呼吸。带 .has-foot 的页面才让位 */
  body.has-foot .main{ padding-bottom:calc(66px + var(--safe-bottom)); }
  body.has-foot .rail-r{ padding-bottom:calc(66px + var(--safe-bottom)); }
  /* 窄屏节点压成横滚标签条：只留标记，名字交给 aria-label */
  .pipe-node{ padding:6px; }
  .pipe-node .nm{ display:none; }
  .pipe-node.cur .nm{ display:inline; }
  .pipe-link{ min-width:8px; }
}

/* ── 行内编辑格（P7 分镜表）─────────────────────────────────────
   平时无边框、看着就是表格文字；聚焦才现 gold 描边。
   用 textarea 而不是 contenteditable：能拿到原生 IME、撤销栈与 readOnly */
.ed-cell{
  display:block; width:100%; min-width:0; resize:none; overflow:hidden;
  padding:4px 6px; margin:-4px -6px;
  background:transparent; border:1px solid transparent; border-radius:var(--r-badge);
  font-family:inherit; font-size:12px; line-height:1.5; color:var(--muted);
}
.ed-cell:hover:not(:read-only){ border-color:var(--border); }
.ed-cell:focus{
  outline:none; border-color:var(--accent); background:var(--bg); color:var(--read);
}
.ed-cell[aria-invalid="true"]{ border-color:var(--st-error); }
.ed-cell:read-only{ cursor:default; }
/* 单元格下方的校验提示，12px ember */
.cell-err{ font-size:12px; line-height:1.5; color:var(--tx-error); margin-top:4px; }

/* ── 标签多选（出场角色）───────────────────────────────────────
   已选项是可删的小徽章，末尾一个 + 触发下拉 */
.chips{ display:flex; align-items:center; gap:4px; flex-wrap:wrap; min-width:0; }
.chip{
  display:inline-flex; align-items:center; gap:3px; height:20px; padding-inline:6px;
  background:var(--raised); border:1px solid var(--border); border-radius:var(--r-badge);
  font-size:11px; color:var(--muted); flex:none; max-width:100%;
}
.chip > span{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.chip button{
  display:grid; place-items:center; width:14px; height:14px; flex:none;
  background:none; border:0; padding:0; cursor:pointer; color:var(--muted-60);
  border-radius:2px; position:relative;
}
/* 视觉 14px，可点区扩到 24px。密集表格里放不下 24px 的方块，
   但 14px 的删除叉在触屏上根本点不中，所以用伪元素向外撑开热区 */
.chip button::after{
  content:''; position:absolute; inset:-5px; border-radius:4px;
}
.chip button:hover{ color:var(--tx-error); background:color-mix(in oklch,var(--st-error) 16%,transparent); }
.chip button svg{ width:10px; height:10px; stroke:currentColor; stroke-width:2.2; fill:none; }
.chip button:focus-visible{ outline:2px solid var(--accent); outline-offset:1px; }
/* 未绑定时的占位：ember 虚线框，一眼看出这里缺东西 */
.chips.empty .chip-add{ border-style:dashed; border-color:var(--st-error); color:var(--tx-error); }
.chip-add{
  display:inline-flex; align-items:center; gap:2px; height:20px; padding-inline:5px;
  background:none; border:1px dashed var(--border); border-radius:var(--r-badge);
  font-size:11px; color:var(--muted-60); cursor:pointer; flex:none;
  position:relative;
}
/* 同上：视觉高度跟随 .chip 的 20px，热区上下各撑 2px 到 24px */
.chip-add::after{ content:''; position:absolute; inset:-2px -1px; border-radius:5px; }
.chip-add:hover{ border-color:var(--accent); color:var(--fg); }
.chip-add:disabled{ cursor:not-allowed; opacity:.5; }
.chip-add svg{ width:11px; height:11px; stroke:currentColor; stroke-width:2; fill:none; }

/* ── 拖拽手柄 ─────────────────────────────────────────────────
   常态透明、行悬停才现，避免 38 行手柄在静态页面上抢注意力 */
.dragh{
  display:grid; place-items:center; width:20px; height:24px; flex:none;
  background:none; border:0; padding:0; cursor:grab; color:var(--muted-40);
  opacity:0; transition:opacity .14s ease;
}
.tbl tbody tr:hover .dragh,.dragh:focus-visible{ opacity:1; }
.dragh:focus-visible{ outline:2px solid var(--accent); outline-offset:1px; }
.dragh svg{ width:14px; height:14px; stroke:currentColor; stroke-width:1.6; fill:none; }
.dragh:active{ cursor:grabbing; }

/* 保存失败的行：左侧 ember 竖条。与选中态的 gold 竖条同一位置，
   失败优先——正在报错的行比「我选了它」更需要被看见 */
.tbl tbody tr.row-err td:first-child{ box-shadow:inset 2px 0 0 var(--st-error) !important; }
.tbl tbody tr.row-err td{ background:color-mix(in oklch,var(--st-error) 8%,transparent); }

/* ── 状态三点（画面 / 视频 / 旁白 各一枚 12px 图标）───────────── */
.trio{ display:inline-flex; align-items:center; gap:5px; }
.trio i{ display:grid; place-items:center; width:12px; height:12px; color:var(--border); }
.trio i.on{ color:var(--st-done); }
.trio i.gen{ color:var(--st-active); }
.trio i.err{ color:var(--st-error); }
.trio svg{ width:12px; height:12px; stroke:currentColor; stroke-width:2; fill:none; }

/* ── 骨架屏 ───────────────────────────────────────────────────────
   P0 规定加载态用 line 色骨架，不用转圈。沿用已有的 breathe 动画，
   全局 prefers-reduced-motion 会把它压掉，这里不必再判 */
.skel{ background:var(--border); border-radius:var(--r-badge); animation:breathe 1.6s ease-in-out infinite; }
/* 文本行骨架。宽度用行内 style 给，避免为每种长度定一个类 */
.skel-line{ height:10px; }
.skel-line.sm{ height:8px; }
/* 媒体位骨架：铺满 .media 容器，比例由容器的 .v11/.v169 决定。
   .media 是 grid + place-items:center，子元素按内容尺寸居中、不拉伸，
   所以这里用绝对定位贴四边，而不是 width/height:100% */
.skel-media{ position:absolute; inset:0; border-radius:0; }
/* 骨架卡片整体不响应指针，也不进 tab 序 */
.card.skel-card{ pointer-events:none; }
.card.skel-card:hover{ border-color:var(--border); }

/* ── 全屏播放器 ───────────────────────────────────────────────────
   P9 播放覆盖层。复用 .scrim 的全屏遮罩与 .lightbox 的 ink 92% 底，
   区别是底部多一条常显控制条，所以视频高度要再减掉控制条那 56px。 */
.scrim.player{ padding:24px; }
.pl-stage{ display:grid; justify-items:center; align-content:center; gap:12px; max-width:100%; }
/* 和灯箱同一个坑：高度只能按视口算。父级高度由内容决定，
   百分比会取到塌陷值（见 .lb-stage .media .media 的注释） */
.pl-stage .media{
  height:calc(100vh - 208px); max-height:82vh; width:auto; max-width:100%;
  background:var(--stage); border-color:transparent; border-radius:var(--r-card);
}
.pl-x{ position:absolute; top:16px; right:16px; z-index:2; }
/* 控制条：播放/暂停 + 时间码 + 进度 + 音量 + 全屏 */
.pl-ctl{
  display:flex; align-items:center; gap:12px;
  width:min(100%,720px); height:56px; padding:0 14px; box-sizing:border-box;
  background:var(--surface); border:1px solid var(--border); border-radius:var(--r-card);
}
.pl-ctl .tc{ font-family:var(--font-mono); font-size:12px; color:var(--muted-80);
  flex:none; font-variant-numeric:tabular-nums; }
.pl-ctl .bar{ flex:1; cursor:pointer; }
.pl-ctl .vol{ display:flex; align-items:center; gap:6px; flex:none; }
/* 音量滑杆只有 72px 宽，够不上「整行宽目标可以矮到 20px」那条豁免，
   所以高度补到 24px 满足 P0 的最小点击区。轨道由浏览器在框内垂直居中，
   滑块的 margin-top:-5px 是相对轨道算的，改高度不会让它偏。 */
.pl-ctl .vol .range{ width:72px; height:24px; }
@media (max-width:1023px){
  /* 窄屏藏音量滑杆，只留静音开关；视口也再让出一点高度 */
  .pl-ctl .vol .range{ display:none; }
  .pl-stage .media{ height:calc(100vh - 188px); }
}

/* ── 波形预览 ─────────────────────────────────────────────────────
   P10 旁白。宽高固定，未生成时也占满位置——否则生成前后行内布局会跳。
   竖条数由 JS 给（28 根），高度用行内 style 的百分比。 */
.wave{
  position:relative; width:120px; height:24px; flex:none;
  display:flex; align-items:center; gap:1px; overflow:hidden;
}
.wave i{ flex:1; min-width:1px; background:var(--border); border-radius:1px; }
/* 已生成：竖条提到 mist 80%，能看出这是真波形而不是占位 */
.wave.on i{ background:var(--muted-80); }
.wave.err i{ background:color-mix(in oklch,var(--st-error) 40%,transparent); }
/* 未生成的占位：统一低幅，别用随机高度假装有内容 */
.wave.off i{ height:5px !important; }
/* 播放游标：1px gold 竖线，位置由行内 style 的 left 给 */
.wave .cur{
  position:absolute; top:0; bottom:0; width:1px; background:var(--accent);
  transition:left .1s linear; pointer-events:none;
}
/* 生成中/失败的覆盖层：压住波形，居中放指示器 */
.wave .ovl-c{
  position:absolute; inset:0; display:grid; place-items:center;
  background:color-mix(in oklch,var(--surface) 72%,transparent);
}
.wave .ovl-c svg{ width:15px; height:15px; stroke:var(--st-error);
  stroke-width:1.6; fill:none; }
/* 整块可点（点哪跳哪）时给指针与聚焦环 */
.wave.seek{ cursor:pointer; }
.wave.seek:focus-visible{ outline:2px solid var(--accent); outline-offset:2px; }

/* ── 可复制等宽字段 ───────────────────────────────────────────────
   P9 远端任务号：整块是按钮，点了复制。默认只露等宽文本，
   复制图标在悬停/聚焦时才出现，免得每行右边挂 21 个图标。 */
.copyf{
  display:inline-flex; align-items:center; gap:5px;
  appearance:none; background:none; border:0; padding:2px 4px; margin:-2px -4px;
  border-radius:var(--r-badge); cursor:pointer;
  font-family:var(--font-mono); font-size:12px; color:var(--muted-80);
  max-width:100%;
}
.copyf .t{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.copyf svg{ width:13px; height:13px; flex:none; stroke:var(--muted-60);
  stroke-width:1.5; fill:none; opacity:0; transition:opacity .12s ease; }
.copyf:hover{ background:var(--raised); color:var(--fg); }
.copyf:hover svg, .copyf:focus-visible svg{ opacity:1; }
.copyf:focus-visible{ outline:2px solid var(--accent); outline-offset:1px; }
/* 复制成功的瞬时反馈：图标换成对勾并染成 sage，1.2s 后由 JS 撤掉 */
.copyf.ok svg{ opacity:1; stroke:var(--st-done); }
.copyf.ok{ color:var(--tx-done); }
/* 表格里够不着 24px 的点击区，靠透明 ::after 补齐（见 P0 冲突记录 9） */
.copyf::before{ content:''; position:absolute; inset:-4px -2px; }
.copyf{ position:relative; }

/* ── 行内错误详情 ─────────────────────────────────────────────────
   P9 失败行下方展开一整行：ember 12% 底 + 等宽错误原文 + 两个文字按钮。
   作为 <tr> 用时给 td 加 .err-cell，避免 padding 落在 tr 上失效。 */
.err-row{ background:color-mix(in oklch,var(--st-error) 12%,transparent); }
.err-cell{ padding:10px 14px !important; }
.err-detail{ display:flex; align-items:flex-start; gap:10px; }
.err-detail > svg{ width:15px; height:15px; flex:none; margin-top:1px;
  stroke:var(--st-error); stroke-width:1.5; fill:none; }
.err-detail .bd{ flex:1; min-width:0; }
.err-detail p{
  font-family:var(--font-mono); font-size:12px; line-height:1.6;
  color:var(--tx-error); margin:0; word-break:break-word;
  -webkit-user-select:text; user-select:text;
}
.err-detail .acts{ display:flex; align-items:center; gap:14px; margin-top:6px; }

/* ── 错误详情块（P18 规范化） ─────────────────────────────────────
   完整技术错误原文：ink 底 + 1px ember 40% 描边 + 12px 等宽，8 行后滚动，
   右上角一个复制按钮。此前 P13（.q-errbox pre）与 P16（.conn-det）
   各写了一遍同一个东西，这里收成正式构件。
   与上面 .err-detail 的分工：.err-detail 是「一句话原因 + 操作」的行内条，
   这个是「整段原文」的代码块，两者常上下相邻出现。 */
.err-wrap{ position:relative; }
.err-wrap > .err-copy{ position:absolute; top:6px; right:6px; z-index:2;
  background:color-mix(in oklch,var(--bg) 80%,transparent); }
.err-code{
  margin:0; padding:10px 12px; padding-right:38px;
  background:var(--bg);
  border:1px solid color-mix(in oklch,var(--st-error) 40%,transparent);
  border-radius:var(--r-ctl);
  font-family:var(--font-mono); font-size:12px; line-height:1.7;
  color:var(--tx-error);
  /* 8 行后滚动。用 em 而不是 px：字号改了高度自动跟着变。 */
  max-height:calc(1.7em * 8); overflow:auto; overscroll-behavior:contain;
  white-space:pre-wrap; word-break:break-word;
  -webkit-user-select:text; user-select:text;
}

/* ── 媒体位角标 ───────────────────────────────────────────────────
   P8/P9：镜头号与版本号叠在预览图上。ink 72% 底保证压住任何画面内容，
   不用 gold——它们是标识不是焦点 */
.ovl{
  position:absolute; z-index:2; display:inline-flex; align-items:center; gap:4px;
  height:18px; padding-inline:5px; border-radius:var(--r-badge);
  background:color-mix(in oklch,var(--bg) 72%,transparent);
  font-family:var(--font-mono); font-size:11px; line-height:1; color:#fff;
}
.ovl.tl{ top:6px; left:6px; }
.ovl.tr{ top:6px; right:6px; }
.ovl svg{ width:11px; height:11px; stroke:currentColor; stroke-width:2; fill:none; }
/* 排队角标带位次，用状态色描边而不是实底——实底会盖掉画面 */
.ovl.q{ border:1px solid var(--st-active); color:color-mix(in oklch,var(--st-active) 70%,white); }

/* 生成中蒙层：压住预览区，转圈 + 12px 说明 */
.mask{
  position:absolute; inset:0; z-index:3; display:grid; place-items:center; gap:8px;
  grid-auto-flow:row; align-content:center;
  background:color-mix(in oklch,var(--bg) 60%,transparent);
  font-size:12px; line-height:1.5; color:var(--muted);
}

/* ── 版本切换带 ───────────────────────────────────────────────────
   P8：32px 方块缩略图，当前选定 gold 描边 + 右上对勾。
   gold 在这里表达「当前焦点位置」，合规；实底仍留给主按钮 */
.vers{ display:flex; align-items:center; gap:4px; flex-wrap:wrap; }
.ver{
  position:relative; width:32px; height:32px; flex:none; padding:0;
  display:grid; place-items:center;
  background:var(--stage); border:1px solid var(--border); border-radius:4px;
  color:var(--muted-60); cursor:pointer;
}
.ver svg{ width:14px; height:14px; stroke:currentColor; stroke-width:1.5; fill:none; }
.ver:hover{ border-color:color-mix(in oklch,var(--accent) 40%,transparent); }
.ver[aria-pressed="true"]{ border-color:var(--accent); color:var(--muted); }
/* 选定角标：gold 描边圆点里放真对勾（不是 CSS 画的短横）。
   raised 底把它从缩略图上抬起来，小尺寸下才认得出 */
.ver .ck{
  position:absolute; top:-4px; right:-4px; width:13px; height:13px;
  display:grid; place-items:center; border-radius:50%;
  background:var(--raised); border:1px solid var(--accent); color:var(--accent);
}
.ver .ck svg{ width:9px; height:9px; stroke-width:2.6; }
.ver:disabled{ cursor:not-allowed; opacity:.5; }
.ver.err{ border-color:var(--st-error); color:var(--tx-error); }
/* 「+N」展开钮：与缩略图同高，文字而非图标 */
.ver.more{ width:auto; min-width:32px; padding-inline:6px;
  font-family:var(--font-mono); font-size:11px; }

/* ── 分段进度条 ───────────────────────────────────────────────────
   P8：按镜头总数分段，每段一个状态色。比一根连续条更能表达
   「哪些定了、哪些还差」，且不靠颜色单一维度——右侧有等宽计数 */
.segbar{ display:flex; align-items:center; gap:10px; }
.segbar .track{ display:flex; gap:2px; flex:1; min-width:0; height:6px; }
.segbar .track i{ flex:1; min-width:2px; background:var(--border); border-radius:1px; }
.segbar .track i.done{ background:var(--st-done); }
.segbar .track i.wait{ background:var(--st-waiting); }
.segbar .track i.err{ background:var(--st-error); }
.segbar .track i.run{ background:var(--st-active); animation:breathe 1.4s ease-in-out infinite; }
.segbar .cnt{ flex:none; font-family:var(--font-mono); font-size:12px; color:var(--muted-70); }

/* ── 灯箱 ─────────────────────────────────────────────────────────
   app.js 的 lightbox() 拼的结构。外层复用 .scrim.lightbox（ink 92%），
   这里只管内部三块：左右翻页钮、中间舞台、右上关闭。
   注意 .kv-rows > .lb 是另一个同名类（键值行），子选择器已把它圈住，不冲突 */
.scrim.lightbox{ grid-template-columns:auto minmax(0,1fr) auto; gap:8px; padding:24px; }
.lb-x{ position:absolute; top:16px; right:16px; z-index:2; }
.lb-p, .lb-n{ flex:none; align-self:center; }
.lb-stage{
  display:flex; flex-direction:column; align-items:center; gap:12px;
  min-width:0; max-height:calc(100vh - 48px);
}
/* P8：大图按原始比例，最高 90vh。
   #lb-media 只当外框（不再限制自身尺寸），真正定尺寸的是里面那层带比例类的
   .media —— 嵌套两层 .media 时若都靠内容定宽，宽度会塌成图标那么大 */
#lb-media{
  max-height:90vh; max-width:100%; background:transparent;
  border-color:transparent; border-radius:var(--r-card); overflow:visible;
}
/* 舞台里的比例块：高度只按视口算，宽度由 aspect-ratio 反算。
   这里不能用百分比——父级 #lb-media 的高度本身由内容决定，
   `min(90vh,100%)` 会取到那个塌陷值（实测只剩 17vh）。
   留 152px 给标题栏、字幕行与底栏（翻页钮在两侧不占高） */
.lb-stage .media .media{
  height:calc(100vh - 152px); max-height:90vh; width:auto; max-width:100%;
  background:var(--stage); border-color:transparent; border-radius:var(--r-card);
}
.lb-cap{ font-size:13px; line-height:1.6; color:var(--muted-70); text-align:center; margin:0; }
.lb-cap .mono{ font-family:var(--font-mono); }
.lb-foot{ display:flex; align-items:center; gap:12px; flex-wrap:wrap; justify-content:center; }
.lb-foot[hidden]{ display:none; }
@media (max-width:1023px){
  /* 窄屏灯箱全屏，翻页钮压到底部两侧，腾出画面高度 */
  .scrim.lightbox{ padding:0; grid-template-columns:minmax(0,1fr); }
  .lb-stage{ max-height:100vh; justify-content:center; gap:8px; }
  .lb-stage .media{ border-radius:0; }
  .lb-p, .lb-n{ position:absolute; bottom:16px; z-index:2; }
  .lb-p{ left:16px; }
  .lb-n{ right:16px; }
}
/* ══════════════════════════════════════════════════════════════════
   模型价格区块 开始（P15 双价格 UI · Task 8 Slice D）
   只使用 :root 既有 token（var() 与基于 token 的 color-mix），
   不引入 raw hex / rgb() / rgba()，不新增任何动画或 transition——
   全局 prefers-reduced-motion 规则无需为此区块另判。
   桌面端 10 列的可读性由既有 .tbl-wrap{overflow:auto} 承担：
   本区块所有长内容（长单位、模型 ID、错误原文、活动期限、详情数据）
   一律 min-width:0 + overflow-wrap:anywhere，绝不把 body 撑出横溢。
   ══════════════════════════════════════════════════════════════════ */

/* ── 目录单元格：官方/实结两列共用 .price 容器 ─────────────────────
   值用等宽数字便于扫读；活动价 = 语义 done token + 「活动价」文字标签
   双维度强调，stale 与 missing 各有文字与形态提示，不只靠颜色。 */
.price{
  display:inline-flex; flex-wrap:wrap; align-items:baseline;
  column-gap:6px; row-gap:2px;
  min-width:0; max-width:100%; font-size:13px; line-height:1.5;
}
.price-value{
  font-family:var(--font-mono); font-variant-numeric:tabular-nums;
  color:var(--fg); min-width:0; overflow-wrap:anywhere;
}
/* 活动价：语义 done token + .price-tag 文字标签 */
.price-activity .price-value{ color:var(--tx-done); font-weight:600; }
.price-tag{
  display:inline-flex; align-items:center; flex:none;
  height:var(--badge-h); padding-inline:6px; border-radius:var(--r-badge);
  background:color-mix(in oklch,var(--st-done) 16%,transparent);
  color:var(--tx-done); font-size:11px; font-weight:500; line-height:1.5;
  white-space:nowrap;
}
/* stale：数值降到 mist，另有虚线描边的提示行，形态上即可区分 */
.price-stale .price-value{ color:var(--muted-70); }
.price-hint{
  display:inline-flex; align-items:center; padding-inline:5px;
  border:1px dashed color-mix(in oklch,var(--st-warn) 45%,transparent);
  border-radius:var(--r-badge); font-size:11px; line-height:1.6;
  color:var(--tx-warn); white-space:nowrap;
}
/* 缺失：虚线占位（沿用 .empty-cell 的「还没有」虚线语义），文案本身是说明 */
.price-missing{
  display:inline-flex; align-items:center; padding:2px 8px;
  border:1px dashed var(--border); border-radius:var(--r-badge);
  font-size:12px; color:var(--muted-60);
}

/* 来源同步失败：缓存价下方独立一行，error 语义 token + 虚线描边，
   形态与颜色双维度可辨；文案由 pricing-view.js 白名单提供（中文失败原因
   + 有缓存时「显示上次缓存」），绝不回显原始错误码。
   字号/行高与 .price-hint 同档（11px/1.6），长文案任意断行不撑破单元格。 */
.price-source-failure{
  display:block; margin-top:4px; padding-inline:5px;
  border:1px dashed color-mix(in oklch,var(--st-error) 45%,transparent);
  border-radius:var(--r-badge); font-size:11px; line-height:1.6;
  color:var(--tx-error); min-width:0; max-width:100%; overflow-wrap:anywhere;
}

/* 优惠情况列 */
.price-promo{
  display:inline-flex; flex-wrap:wrap; align-items:center; gap:4px;
  min-width:0; font-size:12px; line-height:1.5;
}
.price-promo-none{ color:var(--muted-60); }
.price-promo-active{ color:var(--tx-done); }
.price-discount{
  font-family:var(--font-mono); font-variant-numeric:tabular-nums;
  color:var(--tx-done);
}

/* ── 价格数据状态区：每源一行，窄屏自然换行堆叠 ──────────────────── */
.price-srcs{
  display:flex; flex-direction:column; gap:8px; padding:12px 14px;
  background:var(--surface); border:1px solid var(--border);
  border-radius:var(--r-card); min-width:0;
}
.price-src{
  display:flex; align-items:center; gap:8px; flex-wrap:wrap;
  min-width:0; font-size:13px; line-height:1.5;
}
.price-src-name{ color:var(--fg); font-weight:500; flex:none; }
.price-src-n{
  font-family:var(--font-mono); font-variant-numeric:tabular-nums;
  font-size:12px; color:var(--muted-60); flex:none;
}
/* 同步错误原文可能很长：独占一行并任意断行，语义 error token */
.price-src-err{
  flex:1 1 100%; min-width:0; overflow-wrap:anywhere;
  font-family:var(--font-mono); font-size:12px; line-height:1.6;
  color:var(--tx-error);
}
.price-meta{
  font-size:12px; line-height:1.6; color:var(--muted-60);
  min-width:0; overflow-wrap:anywhere;
}

/* ── 操作组与抽屉标题 ──────────────────────────────────────────────
   桌面端操作列右对齐；窄屏卡片态下由 <1024 媒体查询改回左对齐。 */
.act-grp{
  display:flex; align-items:center; justify-content:flex-end;
  gap:8px; flex-wrap:wrap; min-width:0;
}
/* 抽屉标题里的模型 ID：等宽，可任意断行 */
.price-dt-id{
  margin-left:8px; font-family:var(--font-mono); font-size:12px;
  font-weight:400; color:var(--muted-60);
  min-width:0; overflow-wrap:anywhere;
}
/* 抽屉加载/失败占位 */
.price-dt-state{
  display:flex; align-items:center; justify-content:center; gap:10px;
  padding:32px 12px; font-size:13px; line-height:1.7;
  color:var(--muted-70); min-width:0; overflow-wrap:anywhere;
}

/* ── 价格详情列表（抽屉正文） ────────────────────────────────────── */
.price-detail-list{
  list-style:none; margin:0; padding:0;
  display:flex; flex-direction:column; min-width:0;
}
.price-detail-row{ padding:12px 0; min-width:0; }
.price-detail-row + .price-detail-row{ border-top:1px solid var(--border); }
.price-detail-head{
  display:flex; align-items:center; gap:8px; flex-wrap:wrap;
  margin-bottom:4px; min-width:0;
}
.price-source{ font-size:12px; font-weight:500; color:var(--fg); flex:none; }
/* 计量标签沿用 .prm > i 的「只有底没有描边」轻一档写法 */
.price-meter{
  display:inline-block; padding:1px 6px; border-radius:var(--r-badge);
  background:var(--bg); font-family:var(--font-mono); font-size:11px;
  color:var(--muted-70); min-width:0; overflow-wrap:anywhere;
}
.price-detail-prices{
  display:flex; flex-wrap:wrap; align-items:baseline;
  gap:2px 14px; min-width:0;
}
.price-list{
  font-family:var(--font-mono); font-variant-numeric:tabular-nums;
  font-size:12px; color:var(--muted-70);
  min-width:0; overflow-wrap:anywhere;
}
.price-effective{
  font-family:var(--font-mono); font-variant-numeric:tabular-nums;
  font-size:13px; font-weight:500; color:var(--fg);
  min-width:0; overflow-wrap:anywhere;
}
.price-effective.price-activity{ color:var(--tx-done); font-weight:600; }
.price-detail-promo,.price-detail-sample{
  margin-top:6px; font-size:12px; line-height:1.6;
  min-width:0; overflow-wrap:anywhere;
}
.price-detail-sample{ color:var(--muted-70); }
.price-promo-label{ margin-left:6px; color:var(--muted-80); overflow-wrap:anywhere; }
.price-deadline{
  display:block; margin-top:2px; color:var(--muted-70);
  min-width:0; overflow-wrap:anywhere;
}
/* 活动期限三态：临期 warn / 已结束 void / 未公布 muted，
   文案本身已说明状态，颜色是第二维度 */
.price-deadline-soon{ color:var(--tx-warn); font-weight:500; }
.price-deadline-ended{ color:var(--tx-void); }
.price-deadline-unknown{ color:var(--muted-60); }
.price-detail-empty{ padding:24px 12px; justify-content:center; }

/* <1024px：操作组左对齐可换行，状态区堆叠，详情间距略收紧。
   表格卡片化与抽屉全宽沿用 P15 与全局既有断点，这里不重复。 */
@media (max-width:1023px){
  .act-grp{ justify-content:flex-start; }
  .price-src{ align-items:flex-start; }
  .price-src-name{ flex:1 1 auto; }
  .price-detail-prices{ gap:4px 10px; }
}

.budget-price{
  display:inline-flex; flex-wrap:wrap; align-items:baseline;
  column-gap:6px; row-gap:2px;
  min-width:0; max-width:100%; font-size:13px; line-height:1.6;
}
.budget-price-cost{ color:var(--fg); }
.budget-price-value{
  font-family:var(--font-mono); font-variant-numeric:tabular-nums;
  font-weight:500; color:var(--fg); min-width:0; overflow-wrap:anywhere;
}
.budget-price-tag{
  display:inline-flex; align-items:center; flex:none;
  height:var(--badge-h); padding-inline:6px; border-radius:var(--r-badge);
  background:color-mix(in oklch,var(--st-done) 16%,transparent);
  color:var(--tx-done); font-size:11px; font-weight:500; line-height:1.5;
  white-space:nowrap;
}
.budget-price-unknown{
  padding-inline:5px;
  border:1px dashed color-mix(in oklch,var(--st-warn) 45%,transparent);
  border-radius:var(--r-badge); color:var(--tx-warn); font-weight:500;
  min-width:0; overflow-wrap:anywhere;
}
.budget-price-unit{
  font-family:var(--font-mono); font-variant-numeric:tabular-nums;
  font-size:12px; color:var(--muted-70); min-width:0; overflow-wrap:anywhere;
}
.budget-price-actual{
  font-size:12px; color:var(--muted-60); min-width:0; overflow-wrap:anywhere;
}
.budget-price-note{
  font-size:12px; color:var(--muted-70); min-width:0; overflow-wrap:anywhere;
}
.budget-price-deadline{ min-width:0; overflow-wrap:anywhere; }
.budget-price-deadline-soon{ color:var(--tx-warn); font-weight:500; }
.budget-price-deadline-ended{ color:var(--tx-void); }
.budget-price-deadline-unknown{ color:var(--muted-60); }
.budget-price-extra{
  display:block; flex:1 1 100%; margin-top:2px;
  font-size:12px; line-height:1.6; color:var(--muted-70);
  min-width:0; overflow-wrap:anywhere;
}
@media (max-width:1023px){
  .budget-price{ row-gap:3px; }
  .budget-price-extra{ margin-top:4px; }
}

/* 隔离云端网页登录与任务入口：沿用全局设计令牌。 */
.cloud-login{width:min(100% - 40px,420px);margin:clamp(40px,10vh,120px) auto;padding-bottom:32px}
.cloud-eyebrow{color:var(--accent);font-size:13px;letter-spacing:0}
.cloud-login h1{font-size:30px;line-height:1.4;color:var(--fg);font-weight:600}
.cloud-note{color:var(--muted);font-size:14px;line-height:1.75}
.cloud-login form{display:grid;gap:20px;margin:32px 0}
.cloud-login label{display:grid;gap:8px;color:var(--fg);font-size:14px}
.cloud-login input{width:100%;box-sizing:border-box;min-height:46px;padding:10px 12px;border:1px solid var(--border);border-radius:6px;background:var(--surface);color:var(--fg);font:inherit;font-size:16px}
.cloud-login input:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.cloud-login button{min-height:46px;width:100%}
#login-status{color:var(--muted);min-height:24px;font-size:14px;line-height:1.6;margin:0}
.cloud-wrap{max-width:1100px;margin:0 auto;padding:clamp(16px,4vw,40px)}
.cloud-header{display:flex;gap:12px;align-items:center;justify-content:space-between;flex-wrap:wrap}
.cloud-links{display:flex;gap:12px;align-items:center;flex-wrap:wrap}
.cloud-panel{border:1px solid var(--border);border-radius:8px;background:var(--surface);padding:20px;margin:24px 0}
.cloud-task{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:16px 0;border-bottom:1px solid var(--border);flex-wrap:wrap}
.cloud-task:last-child{border-bottom:0}.cloud-task code{overflow-wrap:anywhere}
.cloud-task p{color:var(--muted);font-size:14px;line-height:1.6;margin:8px 0}
.cloud-status{font-size:14px;line-height:1.75;color:var(--muted)}
.cloud-video{display:block;width:min(100%,420px);max-height:360px;background:var(--stage);border-radius:4px}
@media(max-width:639px){.cloud-task{align-items:stretch;flex-direction:column}.cloud-task button{min-height:44px;width:100%}.cloud-panel{padding:16px}}
