/* ═══════════════════════════════════════════════════════════
   Component styles — Video Factory Console (Fable5 redesign, #385)

   Extracted VERBATIM from the prototype's <style> block, everything AFTER
   the :root token block (now tokens.css):
     m0_redesign_prototype.html  <style> lines 22-185

   Includes rules for views not yet wired to real data this phase
   (topology/decisions) — they're part of the single design system handed
   down by the prototype and cost nothing to keep even though views/
   topology.js etc. are stubs until their own phase. Don't re-author this
   file per-phase; re-extract from the prototype if the design changes.
   ═══════════════════════════════════════════════════════════ */
*{box-sizing:border-box;margin:0;padding:0}
html,body{height:100%}
body{background:var(--ink-0);color:var(--tx-1);font:13px/1.5 var(--font-ui);
  -webkit-font-smoothing:antialiased}
::-webkit-scrollbar{width:10px;height:10px}
::-webkit-scrollbar-thumb{background:var(--ink-3);border:2px solid var(--ink-0);border-radius:5px}
:focus-visible{outline:1.5px solid var(--tx-0);outline-offset:2px}
a{color:var(--tx-1);text-decoration:underline;text-underline-offset:3px;text-decoration-color:var(--tx-3)}
a:hover{text-decoration-color:var(--tx-1)}

/* ═══ shell ═══ */
#shell{display:grid;grid-template:48px 1fr / 200px 1fr;height:100vh}
#topbar{grid-column:1/3;display:flex;align-items:center;gap:16px;padding:0 16px;
  background:var(--ink-1);border-bottom:1px solid var(--line-1)}
.mark{font:700 13px/1 var(--font-display);letter-spacing:.14em;color:var(--tx-0);white-space:nowrap}
.mark small{color:var(--tx-3);font-weight:500;letter-spacing:.1em;margin-left:8px}
#top-status{display:flex;gap:14px;margin-left:auto;font:11px var(--font-mono);color:var(--tx-2)}
#top-status .warn{color:var(--st-wait)}
#clock{font:12px var(--font-mono);color:var(--tx-2);min-width:88px;text-align:right}
.seg{display:inline-flex;border:1px solid var(--line-1);border-radius:var(--r);overflow:hidden}
.seg button{background:none;border:0;color:var(--tx-2);font:600 11px var(--font-ui);
  padding:5px 10px;cursor:pointer;letter-spacing:.05em}
.seg button.on{background:var(--paper);color:var(--ink-0)}

#rail{background:var(--ink-1);border-right:1px solid var(--line-1);padding:14px 0;
  display:flex;flex-direction:column;gap:2px}
.rail-item{display:flex;align-items:center;gap:10px;padding:9px 16px;cursor:pointer;
  color:var(--tx-2);border-left:2px solid transparent;user-select:none}
.rail-item:hover{color:var(--tx-0);background:var(--ink-2)}
.rail-item.on{color:var(--tx-0);background:var(--ink-2);border-left-color:var(--tx-0)}
.rail-num{font:600 10px var(--font-mono);color:var(--tx-3);letter-spacing:.1em}
.rail-item.on .rail-num{color:var(--tx-1)}
.rail-badge{margin-left:auto;font:600 10px var(--font-mono);color:var(--ink-0);
  background:var(--st-wait);border-radius:8px;padding:1px 6px}
.rail-sep{margin:12px 16px;border-top:1px solid var(--line-0)}
.rail-ext{padding:7px 16px;font-size:12px;color:var(--tx-3)}
.rail-ext a{color:var(--tx-2)}

#view{overflow:auto;padding:20px 24px}
.view-hd{display:flex;align-items:baseline;gap:12px;margin-bottom:16px}
.view-hd h1{font:600 20px/1.2 var(--font-display);color:var(--tx-0);letter-spacing:.01em}
.view-hd .sub{color:var(--tx-3);font-size:12px}

/* ═══ primitives ═══ */
.lbl{font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:var(--tx-2)}
.panel{background:var(--ink-1);border:1px solid var(--line-0);border-radius:3px}
.panel-hd{display:flex;align-items:center;gap:10px;padding:10px 14px;border-bottom:1px solid var(--line-0)}
.panel-bd{padding:12px 14px}
.mono{font-family:var(--font-mono);font-variant-numeric:tabular-nums}
.dot{display:inline-block;width:7px;height:7px;border-radius:50%;background:var(--st-idle);flex:none}
.dot.run{background:var(--st-run)} .dot.wait{background:var(--st-wait)}
.dot.fail{background:var(--st-fail)}
.dot.pulse{animation:pulse 1.6s ease-in-out infinite}
@keyframes pulse{50%{opacity:.35}}
.pill{display:inline-flex;align-items:center;gap:6px;height:20px;padding:0 8px;
  border:1px solid var(--line-1);border-radius:var(--r);font:11px var(--font-mono);color:var(--tx-2)}
.btn{height:28px;padding:0 12px;font:500 12px var(--font-ui);color:var(--tx-1);
  background:var(--ink-2);border:1px solid var(--line-1);border-radius:var(--r);cursor:pointer}
.btn:hover{background:var(--ink-3)}
.btn-pri{background:var(--paper);color:var(--ink-0);border-color:var(--paper);font-weight:600}
.btn-pri:hover{opacity:.88}   /* 0720: was background:#fff — wrong in light theme (white bg + light text) */
/* 0720 polish: border was a hardcoded dark-theme brown (#6E4038) that read as
   dirt on a light page; derive it from the theme's own fail hue instead. */
.btn-danger{background:none;color:var(--st-fail);border-color:color-mix(in srgb,var(--st-fail) 45%,transparent)}
.btn-danger:hover{background:rgba(240,97,74,.08)}
.btn[disabled]{opacity:.45;cursor:not-allowed}
.chip{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;cursor:pointer;
  border:1px solid var(--line-1);border-radius:var(--r);font:12px var(--font-ui);color:var(--tx-2);background:none}
.chip:hover{color:var(--tx-0)}
.chip.on{background:var(--paper);color:var(--ink-0);border-color:var(--paper);font-weight:600}
.chip .n{font:10px var(--font-mono);opacity:.7}
input[type=text],select,textarea{background:var(--ink-0);border:1px solid var(--line-1);
  border-radius:var(--r);color:var(--tx-1);font:12px var(--font-ui);padding:5px 8px}
textarea{font-family:var(--font-mono);width:100%;resize:vertical}
table.data{width:100%;border-collapse:collapse;font-size:12px}
table.data th{text-align:left;padding:6px 10px;color:var(--tx-3);font-weight:500;
  font-size:11px;letter-spacing:.06em;border-bottom:1px solid var(--line-1)}
table.data td{padding:7px 10px;border-bottom:1px solid var(--line-0);vertical-align:top}
table.data tr:hover td{background:var(--ink-2)}
.code{background:var(--ink-0);border:1px solid var(--line-0);border-radius:var(--r);
  padding:10px;font:11px/1.6 var(--font-mono);color:var(--tx-2);overflow:auto;white-space:pre}
.empty{color:var(--tx-3);font-size:12px;padding:18px;text-align:center}
.kpis{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin-bottom:16px}
.kpi{background:var(--ink-1);border:1px solid var(--line-0);border-radius:3px;padding:12px 14px}
.kpi b{display:block;font:500 30px/1.1 var(--font-display);color:var(--tx-0);
  font-variant-numeric:tabular-nums;margin-top:6px}
.kpi small{color:var(--tx-3);font-size:11px}
.kpi-link{cursor:pointer}
.kpi-link:hover{border-color:var(--line-1);background:var(--ink-2)}
.grid2{display:grid;grid-template-columns:1fr 1fr;gap:14px}
@media(max-width:1100px){.grid2{grid-template-columns:1fr}}

/* ═══ quality overview (decisions route 05, mode "overview", 0720) ═══ */
.qo-strip{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin-bottom:14px}
.qo-stat{background:var(--ink-1);border:1px solid var(--line-0);border-radius:3px;padding:10px 14px}
.qo-stat b{display:block;font:500 24px/1.1 var(--font-display);color:var(--tx-0);
  font-variant-numeric:tabular-nums;margin-top:4px}
.qo-stat small{color:var(--tx-3);font-size:11px}
.qo-stat.crit b{color:var(--st-fail)} .qo-stat.warn b{color:var(--st-wait)}
.qo-stat.dim b{color:var(--tx-3)}
/* trend chips: more signals than the previous window = worse (fail hue) */
.qo-tr{font:600 10px var(--font-mono);letter-spacing:.03em;white-space:nowrap}
.qo-tr.up{color:var(--st-fail)} .qo-tr.down{color:var(--st-run)} .qo-tr.flat{color:var(--tx-3)}
.qo-tr.new{color:var(--st-wait);border:1px solid var(--st-wait);border-radius:8px;padding:0 5px}
.qo-mix{font:11px var(--font-mono);color:var(--tx-2);white-space:nowrap;margin-right:6px}
.qo-key{display:inline-flex;align-items:center;gap:4px;font-size:10px;padding:1px 6px;
  border-radius:8px;background:var(--ink-3);color:var(--tx-2);margin:1px 0;max-width:150px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.qo-key i{font-style:normal;color:var(--tx-3)}
.qo-sig-link{display:inline-block;margin-left:6px;color:var(--ln-prod);text-decoration:none;
  font-size:10px;cursor:pointer}
.qo-sig-link:hover{text-decoration:underline}

/* ═══ topbar theme toggle (0720) ═══ */
#theme-btn{background:none;border:1px solid var(--line-1);border-radius:var(--r);
  color:var(--tx-2);width:26px;height:24px;font-size:13px;line-height:1;cursor:pointer;padding:0}
#theme-btn:hover{color:var(--tx-0);background:var(--ink-2)}

/* ═══ production pipeline cards (Phase 4, #388) — 0719 fix: pipeCardHtml()'s
   markup (.pp-card/.pp-steps/.pp-step/...) shipped with no matching rules
   anywhere in this file, so every stage step fell back to browser-default
   `display:block` and stacked into one long vertical strip of unstyled divs
   (issue #395). Compact horizontal dot-stepper instead: connector bars +
   small state-colored dots, no inline per-step label (13 stages don't fit
   readably in a ~500px half-width column) — the label lives in each dot's
   title= tooltip and, for the one stage actually in progress, in the
   larger .pp-cur-box below (name + live sub-progress + meta), which is
   the text a user scanning the card actually needs at a glance. ═══ */
.pp-card{padding:12px 0;border-bottom:1px solid var(--line-0)}
.pp-card:first-child{padding-top:0}
.pp-card:last-child{border-bottom:none;padding-bottom:0}
.pp-head{display:flex;align-items:baseline;justify-content:space-between;gap:10px;margin-bottom:8px;flex-wrap:wrap}
.pp-vid{font-size:11px;color:var(--tx-3)}
.pp-title{font-size:12px;color:var(--tx-1);margin-left:6px}
.pp-topic{font-size:11px;color:var(--tx-3)}
.pp-pct{font-size:11px;color:var(--tx-2);white-space:nowrap;font-family:var(--font-mono);font-variant-numeric:tabular-nums}
.pp-pct.fail{color:var(--st-fail)}

.pp-steps{display:flex;align-items:center;overflow-x:auto;padding:2px 0 8px}
.pp-step{display:flex;align-items:center;flex:1 1 0;min-width:0;cursor:pointer}
.pp-bar{flex:1 1 auto;height:2px;min-width:4px;background:var(--line-1)}
.pp-step.done .pp-bar{background:var(--st-run)}
.pp-dot{flex:none;width:18px;height:18px;border-radius:50%;display:flex;align-items:center;justify-content:center;
  font:600 9px var(--font-mono);color:var(--tx-3);background:var(--ink-2);border:1px solid var(--line-1)}
.pp-step.done .pp-dot{background:var(--st-run);border-color:var(--st-run);color:var(--ink-0)}
.pp-step.cur .pp-dot{background:var(--ink-2);border-color:var(--st-wait);color:var(--st-wait);animation:pulse 1.6s ease-in-out infinite}
.pp-step.fail .pp-dot{background:var(--st-fail);border-color:var(--st-fail);color:var(--ink-0)}
.pp-lbl{display:none}

.pp-ovbar{height:3px;background:var(--ink-3);border-radius:2px;overflow:hidden;margin-bottom:8px}
.pp-ovfill{height:100%;background:var(--ln-prod);border-radius:2px}
.pp-ovfill.fail{background:var(--st-fail)}

.pp-cur-box{display:flex;align-items:center;gap:8px;font-size:11px;padding:6px 8px;
  background:var(--ink-0);border-radius:var(--r);flex-wrap:wrap}
.pp-cur-box.fail{background:rgba(240,97,74,.08)}
.pp-play{flex:none;color:var(--st-run);font-size:10px}
.pp-cur-box.fail .pp-play{color:var(--st-fail)}
.pp-mod{flex:none;font-weight:600;color:var(--tx-1)}
.pp-gpu{flex:none;font-size:10px;padding:1px 6px;border-radius:8px;background:var(--ink-3);color:var(--tx-2)}
.pp-mini{flex:1 1 60px;min-width:40px;height:3px;background:var(--ink-3);border-radius:2px;overflow:hidden}
.pp-minifill{height:100%;background:var(--ln-prod)}
.pp-meta{flex:1 1 auto;min-width:0;color:var(--tx-3);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pp-acts{flex:none;display:flex;gap:8px}
.pp-acts a{color:var(--ln-prod);text-decoration:none;font-size:10px}
.pp-acts a:hover{text-decoration:underline}

.pp-step-detail{margin-top:8px;padding:8px;background:var(--ink-0);border-radius:var(--r);
  font-size:11px;color:var(--tx-2);line-height:1.6}

/* ═══ topics (Phase 4, #388) — 0719 fix: same gap as production above,
   .seed-group/.seed-item/.mm-frame/etc. shipped with zero matching rules
   (issue #395). Mindmap detail's actual black-on-black text is a separate,
   pre-existing bug in mindmap_api.py's own _VIEW_HTML (fixed there); the
   iframe itself still needed real dimensions -- default <iframe> size with
   no CSS is 300×150px, which read as "视窗大小的问题". ═══ */
.mm-detail-bar{display:flex;align-items:center;gap:12px;margin-bottom:10px}
.mm-frame{width:100%;height:min(78vh,900px);border:1px solid var(--line-0);border-radius:3px;display:block;background:var(--ink-0)} /* 0720: was hardcoded #0d1117 — a black slab while loading in light theme */

.seed-group{margin-bottom:10px;border:1px solid var(--line-0);border-radius:3px;overflow:hidden}
.seed-group-hdr{display:flex;align-items:center;gap:8px;padding:8px 12px;cursor:pointer;background:var(--ink-2);font-size:12px}
.seed-group-hdr:hover{background:var(--ink-3)}
.caret{display:inline-block;color:var(--tx-3);transition:transform .15s}
.seed-group.collapsed .caret{transform:rotate(-90deg)}
.gc{margin-left:auto;color:var(--tx-3);font-size:11px}
.seed-item{display:flex;align-items:center;gap:10px;padding:7px 12px;border-top:1px solid var(--line-0);font-size:12px}
.seed-item.deleted{opacity:.5}
.stopic{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--tx-1)}
.sbadges{flex:none;display:flex;align-items:center;gap:6px}
.sbadge{display:inline-flex;align-items:center;justify-content:center;font-size:10px;padding:1px 6px;
  border-radius:8px;background:var(--ink-3);color:var(--tx-2)}
.sbadge.ts{background:rgba(227,179,65,.15);color:var(--st-wait)}
.sbadge.pri{font-family:var(--font-mono)}
.sactions{flex:none;display:flex;gap:4px;flex-wrap:wrap;justify-content:flex-end}

/* ═══ topology (Phase 2) ═══ */
#topo-tools{display:flex;align-items:center;gap:10px;margin-bottom:12px;flex-wrap:wrap}
.legend{display:flex;gap:14px;align-items:center;font-size:11px;color:var(--tx-2);flex-wrap:wrap}
.legend i{display:inline-block;width:14px;height:3px;border-radius:2px;vertical-align:middle}
#topo-viewport{overflow:auto;border:1px solid var(--line-0);border-radius:3px;background:var(--ink-0)}
#topo-scale{transform-origin:0 0}
svg text{font-family:var(--font-ui);user-select:none}
.tn{cursor:pointer}
.tn-box{fill:var(--ink-1);stroke:var(--line-1);stroke-width:1;rx:2}
.tn:hover .tn-box,.tn.sel .tn-box{stroke:var(--tx-2)}
.tn.dim{opacity:.22}
.tn.lit .tn-box{stroke:var(--tx-1)}
.tn-code{font:600 10px var(--font-mono);letter-spacing:.06em}
.tn-name{font-size:11px;fill:var(--tx-1)}
.tn-tech{font:9px var(--font-mono);fill:var(--tx-3)}
.tn-cnt{font:9px var(--font-mono);fill:var(--tx-2)}
.edge{fill:none;stroke-width:1.4;stroke-opacity:.32}
.edge.active{stroke-opacity:.95;stroke-dasharray:5 5;animation:flow 1s linear infinite}
@keyframes flow{to{stroke-dashoffset:-10}}
@media(prefers-reduced-motion:reduce){.edge.active{animation:none}.dot.pulse{animation:none}}
.lane-lbl{font-size:10.5px;letter-spacing:.1em;fill:var(--tx-3);text-transform:uppercase}
.bus-box{fill:var(--ink-1);stroke:var(--line-1)}
.tick{stroke:var(--line-1);stroke-width:1}

/* ═══ drawer (generic host, see app.js openDrawer/closeDrawer) ═══ */
#scrim{position:fixed;inset:0;background:rgba(6,8,10,.5);opacity:0;pointer-events:none;transition:opacity .18s}
#drawer{position:fixed;top:0;right:-420px;width:400px;height:100vh;background:var(--ink-1);
  border-left:1px solid var(--line-1);box-shadow:var(--shadow-pop);transition:right .2s ease;
  display:flex;flex-direction:column;z-index:50}
body.drawer-open #scrim{opacity:1;pointer-events:auto}
body.drawer-open #drawer{right:0}
.dw-hd{display:flex;align-items:center;gap:10px;padding:14px 16px;border-bottom:1px solid var(--line-0)}
.dw-code{font:700 11px var(--font-mono);letter-spacing:.08em;padding:3px 7px;
  border:1px solid var(--line-1);border-radius:var(--r)}
.dw-hd h2{font:600 15px var(--font-ui);color:var(--tx-0);flex:1}
.dw-x{background:none;border:0;color:var(--tx-3);font-size:18px;cursor:pointer;line-height:1}
.dw-x:hover{color:var(--tx-0)}
.dw-bd{flex:1;overflow:auto;padding:14px 16px;display:flex;flex-direction:column;gap:16px}
.dw-sec .lbl{display:block;margin-bottom:8px}
.kv{display:flex;justify-content:space-between;gap:10px;padding:5px 0;font-size:12px;
  border-bottom:1px solid var(--line-0)}
.kv span:first-child{color:var(--tx-2)} .kv span:last-child{text-align:right}
.dw-row{display:flex;gap:8px;flex-wrap:wrap;align-items:center}
.field{display:flex;flex-direction:column;gap:4px;margin-bottom:10px}
.field label{font-size:11px;color:var(--tx-2)}

/* ═══ knowledge graph (Phase 5, #389) — extracted verbatim from
   m0_redesign_addendum_prototype.html's <style> block (section C's
   kg- / comm- / conn- prefixed rules only; embed-mock, sess-badge, fam-item
   and note belong to Topics/Family, out of this phase's scope) ═══ */
/* ★ 上面这段注释原本用通配写法列举选择器（kg 星号 斜杠 comm 星号 …）。
   星号紧跟斜杠正好构成 CSS 注释的结束符，注释就在那里提前闭合了：后半句变成
   非法 CSS，解析器为了恢复把紧随其后的 .kg-wrap{display:grid;...} 一并吞掉，
   直到下一行 @media 才重新对齐。结果是知识图谱的三栏栅格**从来没生效过**
   （父容器算不出宽度 → canvas 宽 0，图根本画不出来）。
   教训：CSS 注释里绝不要写通配选择器，星号后面跟斜杠就是结束符。这里改成
   纯文字描述，而不是用零宽空格之类的隐形字符去"绕过"——那种修法下一个人
   看不见，复制粘贴就会把 bug 带回来。 */
.kg-wrap{display:grid;grid-template-columns:216px minmax(0,1fr) 300px;gap:14px}
@media(max-width:1250px){.kg-wrap{grid-template-columns:1fr}.kg-side{display:none}}
#kg-tools{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-bottom:12px}
#kg-types{display:flex;gap:6px;flex-wrap:wrap}
.kg-chip-dot{width:8px;height:8px;border-radius:50%;display:inline-block}
#kg-cvp{position:relative;background:var(--ink-0);border:1px solid var(--line-0);border-radius:3px;overflow:hidden}
#kg-cv{display:block;cursor:default}
.kg-gate{position:absolute;left:12px;bottom:10px;font:10px var(--font-mono);color:var(--tx-3);pointer-events:none}
.comm-item{padding:9px 12px;border-bottom:1px solid var(--line-0);cursor:pointer;font-size:12px}
.comm-item:hover{background:var(--ink-2)}
.comm-item.on{background:var(--ink-3)}
.comm-item .cn{color:var(--tx-1)} .comm-item .cs{color:var(--tx-3);font-size:11px;margin-top:2px}
.conn-row{display:flex;gap:8px;padding:5px 0;border-bottom:1px solid var(--line-0);cursor:pointer;font-size:12px}
.conn-row:hover{background:var(--ink-2)}
.conn-rel{font:10.5px var(--font-mono);color:var(--tx-3);min-width:72px}

/* ── #410 事实区：三元组 + 原句依据 + 出处 ────────────────────────────
   连线只说明"谁连着谁"，这里回答"凭什么这么说"。依据用引号+斜体和三元组
   本身区分开，出处用等宽字体压暗——它是可追溯的凭据，不是正文。 */
.fact-row{padding:7px 0;border-bottom:1px solid var(--line-0)}
.fact-row:last-child{border-bottom:none}
.fact-triple{display:flex;flex-wrap:wrap;gap:6px;align-items:baseline;font-size:12px;color:var(--tx-1)}
.fact-rel{font:10.5px var(--font-mono);color:var(--kg-3);white-space:nowrap}
.fact-ev{margin-top:4px;font-size:11.5px;line-height:1.5;color:var(--tx-2);font-style:italic}
.fact-src{margin-top:3px;font-size:10px;color:var(--tx-3)}

/* 度数标注：枢纽是正向信号(骨架节点)，孤岛是负向信号(抽出来却没连上,
   通常意味着抽取质量有问题)，所以两者用不同色温，不要一视同仁。 */
.kg-badge{font-size:9.5px;padding:1px 6px;border-radius:9px;letter-spacing:.3px;cursor:help}
.kg-badge-hub{background:color-mix(in srgb,var(--kg-1) 22%,transparent);color:var(--kg-1)}
/* 0720: previously referenced a "--warn" token that never existed in
   tokens.css, so the isolate badge silently rendered colorless. --st-wait
   is the real amber. */
.kg-badge-iso{background:color-mix(in srgb,var(--st-wait) 20%,transparent);color:var(--st-wait)}

/* #414 边悬停 tooltip：一条边不该是没有出处的断言。定位由 JS 计算(相对 #kg-cvp)。 */
#kg-cvp{position:relative}
.kg-edge-tip{position:absolute;z-index:20;max-width:300px;pointer-events:none;
  background:var(--ink-1);border:1px solid var(--line-1);border-radius:6px;
  padding:8px 10px;box-shadow:var(--shadow-pop)} /* 0720: use the theme's shadow token (old literal was dark-only) */
.kg-edge-tip .tip-triple{display:flex;flex-wrap:wrap;gap:6px;align-items:baseline;
  font-size:12px;color:var(--tx-0)}
.kg-edge-tip .tip-triple .mono{font-size:10.5px;color:var(--kg-3)}
.kg-edge-tip .tip-ev{margin-top:5px;font-size:11.5px;line-height:1.5;color:var(--tx-2);font-style:italic}
.kg-edge-tip .tip-src{margin-top:4px;font-size:10px;color:var(--tx-3)}

/* ═══ decisions (Phase 3) ═══ */
.dx{display:grid;grid-template-columns:minmax(0,1fr) 440px;gap:14px}
@media(max-width:1200px){.dx{grid-template-columns:1fr}}
#dx-filters{display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-bottom:12px}
.dx-row{display:grid;grid-template-columns:66px 112px 46px 1fr 34px;gap:10px;align-items:baseline;
  padding:8px 12px;border-bottom:1px solid var(--line-0);cursor:pointer;font-size:12px}
.dx-row:hover{background:var(--ink-2)} .dx-row.sel{background:var(--ink-3)}
.dx-t{font:11px var(--font-mono);color:var(--tx-3)}
.dx-type{font:600 10.5px var(--font-mono);letter-spacing:.03em}
.dx-mod{font:11px var(--font-mono);color:var(--tx-2)}
.dx-sum{color:var(--tx-1);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.dx-alt{font:10px var(--font-mono);color:var(--tx-3);text-align:right}
.alt-item{display:grid;grid-template-columns:1fr auto;gap:4px 10px;padding:8px 10px;
  border:1px solid var(--line-0);border-radius:var(--r);margin-bottom:6px}
/* 0720 polish: same hardcoded-dark-value fix as .btn-danger — both the
   border (#2C5C46) and the tint were dark-theme literals. */
.alt-item.chosen{border-color:color-mix(in srgb,var(--st-run) 40%,transparent);background:color-mix(in srgb,var(--st-run) 6%,transparent)}
.alt-bar{grid-column:1/3;height:3px;background:var(--ink-3);border-radius:2px;overflow:hidden}
.alt-bar i{display:block;height:100%;background:var(--ln-prod)}
.alt-item.chosen .alt-bar i{background:var(--st-run)}
.alt-name{font-size:12px;color:var(--tx-1)} .alt-note{font-size:11px;color:var(--tx-3)}
.alt-score{font:11px var(--font-mono);color:var(--tx-2)}
.tag-chosen{font:600 9.5px var(--font-mono);letter-spacing:.08em;color:var(--st-run)}
.reason{background:var(--ink-0);border:1px solid var(--line-0);border-radius:var(--r);
  padding:10px 12px;font-size:12.5px;color:var(--tx-1);line-height:1.65}
.prov{display:flex;gap:12px;flex-wrap:wrap;font:11px var(--font-mono);color:var(--tx-2)}
.prov a{cursor:pointer}
.sev{width:7px;height:7px;border-radius:50%;display:inline-block;margin-right:6px}
/* 057's seed taxonomy says minor/major/critical, but severity is an open
   column — the first real emitter (m10 footage_all_rejected) writes 'warn',
   which previously fell through to a colorless dot. */
.sev.minor{background:var(--tx-3)} .sev.warn{background:var(--st-wait)}
.sev.major{background:var(--st-wait)} .sev.critical{background:var(--st-fail)}

/* 0720 polish round 3: explicit "not recorded" state (never a blank cell,
   never a fabricated 0/1 — see decisions.js itemScore()/renderAltsBlock()
   header comment for the live bug this replaces) + clip_pick's per-item
   4-axis score breakdown (chosen.breakdown = {visual,text,intent,tag}),
   visualized instead of listed as raw numbers. */
.dx-na{color:var(--tx-3)}
.score-breakdown{grid-column:1/3;display:flex;flex-direction:column;gap:4px;
  margin-top:8px;padding-top:8px;border-top:1px solid var(--line-0)}
.sb-row{display:grid;grid-template-columns:52px 1fr 40px;align-items:center;gap:8px}
.sb-k{font-size:10px;color:var(--tx-3);text-transform:uppercase;letter-spacing:.04em}
.sb-track{height:4px;background:var(--ink-3);border-radius:2px;overflow:hidden}
.sb-track i{display:block;height:100%;background:var(--st-run)}
.sb-v{font-size:10.5px;color:var(--tx-2);text-align:right}
#toasts{position:fixed;bottom:18px;right:18px;display:flex;flex-direction:column;gap:8px;z-index:99}
.toast{background:var(--paper);color:var(--ink-0);font:500 12px var(--font-ui);
  padding:10px 14px;border-radius:3px;box-shadow:var(--shadow-pop);
  max-width:420px;word-break:break-word}
/* 0719 (#397): error toasts now carry real backend failure reasons, which are
   both longer and more important than a success confirmation -- distinct
   color so a failure never reads as a success at a glance.
   0720 polish: color was var(--paper), which light theme remaps to a DARK
   chip color (#21262E) — dark text on the red fill measured ~1.8:1, i.e. the
   one message that must be readable (a real failure reason) was near-
   invisible in light mode. Fixed white works on both themes' reds. */
.toast.err{background:var(--st-fail);color:#fff}

/* ═══ family (Phase 6, #390) — ported verbatim from the addendum
   prototype's own supplementary style block (m0_redesign_addendum_
   prototype.html lines ~97-104); .embed-mock/.note were mock-only
   (placeholder box / dev-note callout) and aren't needed now that the
   iframe embed and its caption are real. ═══ */
.fam-item{padding:9px 12px;border-bottom:1px solid var(--line-0);cursor:pointer;font-size:12.5px;
  display:flex;justify-content:space-between;gap:8px}
.fam-item:hover{background:var(--ink-2)} .fam-item.on{background:var(--ink-3)}
.sess-badge{font:10px var(--font-mono);color:var(--tx-2);border:1px solid var(--line-1);
  border-radius:var(--r);padding:1px 6px}
.fam-embed{width:100%;height:min(72vh,760px);border:0;display:block;background:var(--ink-0)} /* 0720: was hardcoded #0b0e13, same light-theme slab as .mm-frame */

/* ═══ LLM gateway (route 09, new) — local/cloud/mcp/unresolved route colors.
   Reuses existing theme tokens (no new hex literals): local=run green,
   cloud=the production-line blue already used for progress fills
   (pp-ovfill/alt-bar), mcp=the KG purple (a 3rd-party-surface hue, distinct
   from both), unresolved=fail red (it's overwhelmingly failed calls, see
   db.py get_llm_gateway_overview's classification comment). Same classnames
   drive both the proportion bar segments and the legend swatches so the two
   never drift out of sync with each other. ═══ */
.lg-local{background:var(--st-run)}
.lg-cloud{background:var(--ln-prod)}
.lg-mcp{background:var(--kg-3)}
.lg-unresolved{background:var(--st-fail)}
.lg-bar{display:flex;height:10px;border-radius:5px;overflow:hidden;background:var(--ink-3)}
.lg-bar i{display:block;height:100%}
.lg-legend{display:flex;gap:14px;flex-wrap:wrap;font-size:11px;color:var(--tx-2);margin:8px 0 0}
.lg-legend i{display:inline-block;width:9px;height:9px;border-radius:2px;margin-right:5px;vertical-align:middle}
.lg-trend-bar{display:flex;height:8px;border-radius:4px;overflow:hidden;background:var(--ink-3);min-width:60px}
.lg-trend-bar i{display:block;height:100%}

/* ═══ material health (route 10, #419) — ingest/retrieval/orchestration
   observation window. Section-level KPI tiles reuse .qo-strip/.qo-stat and
   the real/generated proportion bar reuses .lg-bar/.lg-legend (both already
   generic 2-N segment shapes, not LLM-specific despite the class prefix) --
   these .mh-* rules only cover the per-metric detail row shape (label + big
   mono value + target/alert hint + optional mini progress bar) that neither
   existing component has. Token-only, no new hex literals. ═══ */
.mh-row{padding:10px 12px;border-bottom:1px solid var(--line-0)}
.mh-row:last-child{border-bottom:none}
.mh-row-top{display:flex;align-items:baseline;justify-content:space-between;gap:10px;flex-wrap:wrap}
.mh-metric{color:var(--tx-1);font-size:12.5px}
.mh-val{font:600 16px var(--font-mono);color:var(--tx-0);white-space:nowrap;font-variant-numeric:tabular-nums}
.mh-val.ok{color:var(--st-run)} .mh-val.warn{color:var(--st-wait)} .mh-val.bad{color:var(--st-fail)}
.mh-val.dim{color:var(--tx-3);font-weight:500}
.mh-sub{font-size:10.5px;color:var(--tx-3);margin-top:2px;line-height:1.5}
.mh-bar{height:5px;background:var(--ink-3);border-radius:3px;overflow:hidden;margin-top:6px}
.mh-bar i{display:block;height:100%;background:var(--st-run)}
.mh-bar i.warn{background:var(--st-wait)} .mh-bar i.bad{background:var(--st-fail)}
.mh-na{padding:8px 0;color:var(--tx-3);font-size:11.5px;font-style:italic;line-height:1.6}
.mh-real{background:var(--st-run)}
.mh-generated{background:var(--ln-prod)}
