/* ============================================================================
   录像数据分析台 · 样式
   ⚠️ 本文件由 scripts/extract-lab-css.mjs 从 prototype/data-lab.template.html
      的 <style> 块**逐字节提取**，再追加「运行态外壳」一节。
      改样式请改这里（提取只在原型与生产需要重新对齐时才跑一次）。

   设计系统速览：
     浅色主题；--a 红 = 玩家 A、--b 蓝 = 玩家 B（中国习惯：涨红跌绿）；
     图表与时间轴共用一个全局游标 S.t；所有卡片都由 syncCursor() 驱动刷新。
   ============================================================================ */

/* ==========================================================================
   录像数据分析台 —— 单文件原型
   数据：官方回放 SPlayerStatsEvent 的 39 个计分字段（内联 JSON，真实录像导出）
   口径：官方客户端回放 overlay 的 Resources / Income / Spending / Army / Losses
   ========================================================================== */
:root{
  --bg:#f3f5f9; --card:#fff; --card2:#fafbfd;
  --line:#e2e7ef; --line2:#edf0f6; --line3:#dfe4ec;
  --ink:#141922; --ink2:#525d70; --ink3:#8792a5; --ink4:#aab3c2;
  --a:#d94f45; --b:#2f80d6;
  --accent:#2f6feb; --accent-soft:#eef3fe;
  --warn:#c9821a; --ok:#2f9e6a;
  --r:10px; --r2:8px;
  --mono:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,monospace;
  --sans:-apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Hiragino Sans GB","Microsoft YaHei",sans-serif;
}
*{box-sizing:border-box}
html,body{margin:0;height:100%}
body{background:var(--bg);color:var(--ink);font:13px/1.5 var(--sans);-webkit-font-smoothing:antialiased}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer;padding:0}
::-webkit-scrollbar{width:10px;height:10px}
::-webkit-scrollbar-thumb{background:#cfd6e2;border-radius:6px;border:2px solid transparent;background-clip:content-box}
::-webkit-scrollbar-thumb:hover{background:#b6c0d0;background-clip:content-box}
::-webkit-scrollbar-track{background:transparent}

.top{position:sticky;top:0;z-index:60;background:rgba(255,255,255,.94);backdrop-filter:blur(10px);
     border-bottom:1px solid var(--line);height:52px;display:flex;align-items:center;gap:14px;padding:0 18px}
.brand{display:flex;align-items:center;gap:9px;font-weight:650;letter-spacing:.2px;white-space:nowrap}
.brand i{width:20px;height:20px;border-radius:5px;background:linear-gradient(135deg,var(--accent),#7aa8f5);
        display:grid;place-items:center;color:#fff;font-size:11px;font-style:normal;font-weight:800}
.brand small{font-weight:400;color:var(--ink3);font-size:11px;margin-left:2px}
.seg{display:flex;background:var(--bg);border:1px solid var(--line);border-radius:8px;padding:2px;gap:2px}
.seg button{padding:4px 11px;border-radius:6px;color:var(--ink2);font-size:12px;white-space:nowrap}
.seg button.on{background:#fff;color:var(--ink);box-shadow:0 1px 2px rgba(20,25,34,.10);font-weight:600}
.spacer{flex:1}
.tbtn{display:flex;align-items:center;gap:6px;border:1px solid var(--line);background:#fff;border-radius:8px;
      padding:5px 11px;font-size:12px;color:var(--ink2);white-space:nowrap}
.tbtn:hover{border-color:var(--line3);color:var(--ink)}

.samples{display:flex;gap:6px;overflow-x:auto;padding:12px 0 0}
/* 卡片高度直接决定「样本列表能显示几条」。三行内容一行不减，只收 padding 与行距：
   实测 78px → 56px，同样半栏能多看 1~2 条。 */
.smp{flex:0 0 auto;text-align:left;background:#fff;border:1px solid var(--line);border-radius:var(--r2);
     padding:5px 10px;min-width:190px;transition:.12s}
.smp:hover{border-color:#c9d2e0}
.smp.on{border-color:var(--accent);background:var(--accent-soft);box-shadow:0 0 0 1px var(--accent) inset}
.smp b{display:block;font-size:11.5px;font-weight:600;line-height:1.35;
       white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.smp span{display:block;font-size:10px;line-height:1.35;color:var(--ink3);font-family:var(--mono);margin-top:1px;
          white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.smp.on span{color:var(--accent)}
/* 列表首卡：添加录像（虚线框，与真实样本卡区分；宽度撑满一栏） */
.smp-add{width:100%;display:flex;align-items:center;justify-content:center;gap:6px;flex:0 0 auto;
         border-style:dashed;color:var(--ink3);font-size:11.5px;font-weight:600;cursor:pointer}
.smp-add:hover{border-color:var(--accent);color:var(--accent);background:var(--accent-soft)}

.wrap{padding:12px 18px 40px;max-width:1600px;margin:0 auto}
.head{background:var(--card);border:1px solid var(--line);border-radius:var(--r);padding:14px 16px;
      display:flex;align-items:center;gap:20px;flex-wrap:wrap}
.hmeta{display:flex;flex-direction:column;gap:3px;min-width:0}
.hmeta h1{margin:0;font-size:15px;font-weight:650;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.hmeta .sub{display:flex;gap:7px;flex-wrap:wrap;font-size:11px;color:var(--ink3);font-family:var(--mono)}
.hmeta .sub b{font-weight:600;color:var(--ink2)}
.vs{flex:1;display:flex;align-items:center;justify-content:center;gap:18px;min-width:360px}
.pl{display:flex;align-items:center;gap:9px}
.pl .rc{width:30px;height:30px;border-radius:7px;display:grid;place-items:center;font-weight:800;font-size:13px;color:#fff}
.pl.pa .rc{background:var(--a)} .pl.pb .rc{background:var(--b)}
.pl .nm{display:flex;flex-direction:column;line-height:1.25}
.pl .nm b{font-size:13px;font-weight:650}
.pl .nm span{font-size:11px;color:var(--ink3)}
.pl .win{font-size:10px;font-weight:700;color:var(--ok);border:1px solid #bfe3d1;background:#f0faf5;
         border-radius:4px;padding:1px 5px;margin-left:3px}
.vslab{font-size:11px;color:var(--ink4);font-weight:600;font-family:var(--mono)}

.tlbox{background:var(--card);border:1px solid var(--line);border-radius:var(--r);margin-top:12px;overflow:hidden}
.tlhead{display:flex;align-items:center;gap:12px;padding:9px 14px;border-bottom:1px solid var(--line2)}
.tlhead .lab{font-size:11px;font-weight:650;color:var(--ink2);letter-spacing:.3px;white-space:nowrap}
.clock{font-family:var(--mono);font-size:19px;font-weight:650;letter-spacing:-.4px;font-variant-numeric:tabular-nums}
.clock em{font-style:normal;color:var(--ink4);font-size:12px;font-weight:400}
.tl{position:relative;padding:0 14px 8px;user-select:none;cursor:ew-resize}
.tl svg{display:block;width:100%}
.tl .evlay{position:absolute;left:14px;right:14px;top:0;height:14px;pointer-events:none}
.tl .ev{position:absolute;top:3px;height:10px;width:2px;border-radius:1px;background:var(--warn);opacity:.7}
.tl .ev.battle{background:var(--a);opacity:.55}
.tl .cl{position:absolute;top:0;bottom:8px;width:1px;background:var(--ink);pointer-events:none;z-index:5}
.tl .cl::after{content:"";position:absolute;top:-3px;left:-3.5px;width:8px;height:8px;border-radius:50%;
               background:#fff;border:2px solid var(--ink)}
.tl .tl-peek{position:absolute;top:2px;bottom:2px;width:0;border-left:1px dashed var(--line3);pointer-events:none;opacity:0;transition:opacity .12s}
.tl .tl-peek.on{opacity:1}
.tl .tl-peek::after{content:attr(data-t);position:absolute;top:1px;left:6px;font-family:var(--mono);font-size:9.5px;
                    color:var(--ink2);background:var(--card);border:1px solid var(--line);border-radius:4px;padding:0 4px;white-space:nowrap}
.tl.scrubbing .cl{width:2px;background:var(--accent)}
.tllegend{display:flex;gap:14px;padding:0 14px 10px;font-size:11px;color:var(--ink3);flex-wrap:wrap}
.tllegend i{display:inline-block;width:9px;height:9px;border-radius:2px;vertical-align:-1px;margin-right:5px}

.readouts{display:flex;gap:10px;overflow-x:auto;padding:12px 0 0}
.ro{flex:0 0 auto;background:var(--card);border:1px solid var(--line);border-radius:var(--r2);padding:9px 12px;min-width:164px}
.ro .t{font-size:11px;color:var(--ink3);white-space:nowrap;margin-bottom:6px}
.ro .row{display:flex;align-items:baseline;justify-content:space-between;gap:12px;font-family:var(--mono);font-size:12.5px}
.ro .row+.row{margin-top:3px}
.ro .row .k{display:flex;align-items:center;gap:5px;color:var(--ink3);font-family:var(--sans);font-size:11px}
.ro .row .k i{width:7px;height:7px;border-radius:2px}
.ro .row .v{font-variant-numeric:tabular-nums;font-weight:600}
.ro .d{font-family:var(--mono);font-size:11px;margin-top:6px;padding-top:6px;border-top:1px dashed var(--line2);color:var(--ink2)}
.up{color:var(--a)} .down{color:var(--b)}

.body{display:grid;grid-template-columns:236px 1fr;gap:12px;margin-top:12px;align-items:start}
.side{background:var(--card);border:1px solid var(--line);border-radius:var(--r);position:sticky;top:64px;
      max-height:calc(100vh - 80px);overflow:auto}
.side h3{margin:0;font-size:11px;font-weight:650;color:var(--ink3);letter-spacing:.4px;padding:11px 14px 6px}
.grp{border-top:1px solid var(--line2);padding:2px 0 8px}
.grp>.gh{display:flex;align-items:center;justify-content:space-between;padding:6px 14px;cursor:pointer}
.grp>.gh b{font-size:11.5px;font-weight:650;color:var(--ink2)}
.grp>.gh em{font-style:normal;font-size:10.5px;color:var(--ink4);font-family:var(--mono)}
.ck{display:flex;align-items:center;gap:8px;padding:4px 14px;cursor:pointer;font-size:12px;color:var(--ink2)}
.ck:hover{background:var(--card2)}
.ck input{margin:0;accent-color:var(--accent);width:13px;height:13px;flex:0 0 auto}
.ck span{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ck em{font-style:normal;font-size:9.5px;color:var(--ink4);font-family:var(--mono)}
.ck.on span{color:var(--ink);font-weight:600}
.sidefoot{border-top:1px solid var(--line2);padding:9px 14px;display:flex;gap:7px;flex-wrap:wrap}
.mini{font-size:11px;border:1px solid var(--line);border-radius:6px;padding:3px 8px;color:var(--ink2)}
.mini:hover{border-color:var(--line3);color:var(--ink)}

.charts{display:flex;flex-direction:column;gap:12px;min-width:0}
.card{background:var(--card);border:1px solid var(--line);border-radius:var(--r);overflow:hidden}
.card>header{display:flex;align-items:center;gap:10px;padding:10px 14px;border-bottom:1px solid var(--line2);flex-wrap:wrap}
.card>header .tt{display:flex;flex-direction:column;line-height:1.3;min-width:0}
.card>header .tt b{font-size:12.5px;font-weight:650}
.card>header .tt span{font-size:10.5px;color:var(--ink4);font-family:var(--mono)}
.card>header .sp{flex:1}
.curread{display:flex;gap:14px;font-family:var(--mono);font-size:12px;font-variant-numeric:tabular-nums}
.curread .v{display:flex;align-items:center;gap:5px}
.curread .v i{width:7px;height:7px;border-radius:2px}
/* user-select:none —— 图表是「按住拖动」扫游标的，拖到刻度文字上会把文字选中（同 .tl） */
.plot{position:relative;padding:6px 12px 8px;cursor:crosshair;user-select:none;-webkit-user-select:none}
.plot svg{display:block;width:100%}
.plot .cl{position:absolute;top:6px;bottom:8px;width:1px;background:var(--ink);opacity:.32;pointer-events:none}
.plot .dot{position:absolute;width:7px;height:7px;border-radius:50%;border:1.5px solid #fff;pointer-events:none;
           transform:translate(-50%,-50%);box-shadow:0 0 0 1px rgba(0,0,0,.12)}
.empty{padding:30px 14px;text-align:center;color:var(--ink4);font-size:12px}

.tblbox{background:var(--card);border:1px solid var(--line);border-radius:var(--r);margin-top:12px;overflow:hidden}
.tblbox>header{display:flex;align-items:center;gap:10px;padding:10px 14px;border-bottom:1px solid var(--line2);flex-wrap:wrap}
.tblbox>header b{font-size:12.5px;font-weight:650}
.tblbox>header .sp{flex:1}
.tblwrap{max-height:392px;overflow:auto}
table{border-collapse:collapse;width:100%;font-family:var(--mono);font-size:11.5px;font-variant-numeric:tabular-nums}
th,td{padding:5px 12px;text-align:right;white-space:nowrap;border-bottom:1px solid var(--line2)}
th{position:sticky;top:0;background:var(--card2);color:var(--ink3);font-weight:600;font-size:10.5px;z-index:2;
   border-bottom:1px solid var(--line)}
td:first-child,th:first-child{text-align:left;position:sticky;left:0;background:var(--card);z-index:3}
th:first-child{background:var(--card2);z-index:4}
tbody tr:hover td{background:var(--accent-soft)}
tbody tr.on td{background:#e7eefe;font-weight:600}
.tblwrap .sep{border-left:1px solid var(--line)}
.note{font-size:11px;color:var(--ink4);padding:10px 14px;border-top:1px solid var(--line2);line-height:1.75}
.note code{font-family:var(--mono);background:var(--bg);padding:1px 5px;border-radius:4px;font-size:10.5px}
.note b{color:var(--ink2)}

/* ==========================================================================
   视图切换（数据分析 / 建造顺序 / 沙盘模拟 / 对局聊天）
   ========================================================================== */
body.boview .v-data, body.chatview .v-data, body.sandboxview .v-data{display:none}
body:not(.boview) .v-bo, body.chatview .v-bo, body.sandboxview .v-bo{display:none}
body:not(.chatview) .v-chat{display:none}
body:not(.sandboxview) .v-sandbox{display:none}

/* ==========================================================================
   沙盘模拟
   沙盘视图要「足够大」：切进来后整页让位 —— 左侧录像列表与对局头隐藏、
   1600px 宽度上限解除、播报条隐藏，画布吃掉时间轴以下的全部空间；
   「⛶ 全屏」再对 .wrap 申请 Fullscreen API，:fullscreen 下画布继续放大。
   ========================================================================== */
body.sandboxview .wrap{max-width:none;padding:10px 16px 16px}
body.sandboxview .layout{display:block;margin-top:10px}
body.sandboxview .rail,body.sandboxview .head,body.sandboxview .vb,body.sandboxview .infobar{display:none}
body.sandboxview .v-sandbox{margin-top:0}

.sb-tools{display:flex;align-items:center;gap:10px;padding:8px 12px;flex-wrap:wrap}
.sb-select{font:12px var(--sans);color:var(--ink);background:#fff;border:1px solid var(--line);
           border-radius:8px;padding:4px 8px;max-width:300px}
.sb-mapinfo{font-family:var(--mono);font-size:11px;color:var(--ink3);white-space:nowrap;
            overflow:hidden;text-overflow:ellipsis;max-width:420px}
.sb-play{font-weight:600;color:var(--accent);border-color:var(--accent)}
.sb-play:hover{background:var(--accent-soft)}
.sbopt{display:flex;align-items:center;gap:5px;font-size:12px;color:var(--ink2);cursor:pointer;user-select:none}
.sbopt input{accent-color:var(--accent);margin:0}
/* 高度构成：100vh − 顶栏52 − 工具条~44 − 时间轴盒~150 − 页边距~34 */
.sbstage{position:relative;background:#0b0f15;height:calc(100vh - 282px);min-height:420px;
         overflow:hidden;border-radius:var(--r)}
.sbstage canvas{display:block;width:100%;height:100%;cursor:grab;touch-action:none}
.sbstage.panning canvas{cursor:grabbing}
.sbhint{position:absolute;bottom:8px;left:0;right:0;text-align:center;pointer-events:none;
        font-size:11px;color:rgba(180,195,215,.55);font-family:var(--mono)}

/* 斜视角切换按钮的选中态 */
.sb-toggle{color:var(--ink3)}
.sb-toggle.on{color:var(--accent);border-color:var(--accent);background:var(--accent-soft);font-weight:600}

/* 左上/右上面板：玩家名 + 实时计数 + 编成 chips + 生产条（对标原站 overview 播放器） */
/* 纯展示层：点击穿透，不挡画布拖拽/缩放 */
.sbpanel{position:absolute;top:12px;left:14px;width:310px;max-width:44%;pointer-events:none;
         background:rgba(13,18,26,.82);border:1px solid rgba(120,140,170,.25);
         border-left:3px solid var(--a);border-radius:8px;padding:8px 10px;
         font-size:11.5px;color:#d7deea;backdrop-filter:blur(4px)}
.sbpanel-b{left:auto;right:14px;border-left:1px solid rgba(120,140,170,.25);
           border-right:3px solid var(--b);text-align:right}
.sbpanel .sp-name{display:flex;align-items:baseline;gap:7px;margin-bottom:2px}
.sbpanel-b .sp-name{justify-content:flex-end}
.sbpanel .sp-name b{font-weight:700;font-size:12.5px}
.sbpanel .sp-name span{color:#9aa6ba;font-size:10.5px}
.sbpanel .sp-counts{font-family:var(--mono);font-size:10.5px;color:#c3cddb;margin-bottom:5px}
.sbpanel .sp-row{display:flex;flex-wrap:wrap;gap:3px;justify-content:flex-start;margin-bottom:5px}
.sbpanel-b .sp-row{justify-content:flex-end}
.sbpanel .sp-stats{font-family:var(--mono);font-size:10.5px;color:#9aa6ba;border-top:1px solid rgba(120,140,170,.2);
                   padding-top:4px;margin-top:1px}
.sbpanel .sp-stats b{color:#e8eef7}
.chip{position:relative;display:inline-flex;align-items:center;gap:2px;background:rgba(40,50,66,.85);
      border:1px solid rgba(120,140,170,.28);border-radius:4px;padding:1px 4px 1px 2px;
      font-family:var(--mono);font-size:10px;color:#dfe6f0;line-height:1.3}
/* 面板整体 pointer-events:none(不挡画布拖拽),但 chip 要能吃鼠标事件出悬浮提示 */
.sbpanel .chip{pointer-events:auto;cursor:default}
/* 图标素材是黑底图：screen 混合让黑底融进 chip 深色底（同沙盘画布的抠底思路） */
.chip img{width:15px;height:15px;display:block;image-rendering:auto;mix-blend-mode:screen}
.chip.prod{padding-bottom:3px;overflow:hidden}
/* 只作用于进度条本体（.bar）：泛选 `i` 会把无图标科技的中文名 chip-txt 也打成 2px 小条。
   内联 width 是 % + left:2px 时 right 会被忽略（过约束），100% 会戳出右边框 2~3px ——
   max-width 把条钳回 [left+2, right−2] 的轨道内，overflow:hidden 再兜底圆角 */
.chip.prod .bar{position:absolute;left:2px;right:2px;bottom:1px;height:2px;max-width:calc(100% - 4px);background:var(--accent);border-radius:1px}

/* HUD chip 悬浮提示：中文名主行 + 英文/进度/时刻副行 */
.sbtip{position:fixed;z-index:80;display:none;pointer-events:none;max-width:280px;
       background:rgba(16,22,33,.97);border:1px solid rgba(120,140,170,.42);border-radius:6px;
       padding:5px 10px;box-shadow:0 6px 18px rgba(0,0,0,.45);color:#e8eef7;font-size:12px;line-height:1.5}
.sbtip b{display:block;font-weight:600;color:#fff}
.sbtip span{display:block;color:#8792a5;font-family:var(--mono);font-size:10px}

/* 底部资源条：双方 矿/气（含采集率）+ 人口（纯展示，点击穿透不挡拖拽） */
.sbres{position:absolute;bottom:26px;left:50%;transform:translateX(-50%);max-width:92%;pointer-events:none;
       display:flex;align-items:center;gap:8px;background:rgba(13,18,26,.82);
       border:1px solid rgba(120,140,170,.25);border-radius:7px;padding:4px 12px;
       font-family:var(--mono);font-size:11px;color:#c3cddb;white-space:nowrap;backdrop-filter:blur(4px)}
.sbres b{font-family:var(--sans);font-weight:650;font-size:11px;max-width:120px;
         overflow:hidden;text-overflow:ellipsis}
.sbres .r-min{color:#d8a531}.sbres .r-gas{color:#5cb87f}.sbres .r-sup{color:#9aa6ba}
.sbres em{font-style:normal;color:#7f8ba0;font-size:10px;margin:-4px 6px 0 -4px}
.sbres .r-vs{color:#5d6878;font-weight:700;font-size:10px}
.sblegend{display:flex;gap:13px;flex-wrap:wrap;padding:8px 2px 0;font-size:11px;color:var(--ink3)}
.sblegend span{display:flex;align-items:center;gap:5px}
.sblegend i{display:inline-block;vertical-align:-1px}
.lg-dot{width:9px;height:9px;border-radius:50%}
.lg-dot.sm{width:7px;height:7px}
.lg-sq{width:9px;height:9px;border-radius:2px;opacity:.85}
.lg-di{width:8px;height:8px;transform:rotate(45deg)}
.lg-ring{width:9px;height:9px;border-radius:50%;border:1.6px solid;background:transparent}
.lg-x{width:10px;height:10px;position:relative;font-style:normal}
.lg-x::before{content:"";position:absolute;inset:3px;border:1.4px solid currentColor;border-radius:50%;
              opacity:.8;box-shadow:0 0 0 2.5px transparent}

/* 全屏（⛶ 按钮对 .wrap 申请）：画布吃掉工具条 + 时间轴以下的全部高度 */
.wrap:fullscreen{background:var(--bg);padding:12px 16px;max-width:none}
.wrap:fullscreen .sbstage{height:calc(100vh - 236px);min-height:400px}
.wrap:fullscreen .sblegend{padding:8px 2px 0}

/* ==========================================================================
   对局聊天
   ========================================================================== */
.chatlist{max-height:calc(100vh - 156px);overflow:auto}
.chatrow{display:flex;align-items:baseline;gap:10px;padding:7px 14px;border-bottom:1px solid var(--line2);cursor:pointer}
.chatrow:hover{background:var(--accent-soft)}
.chatrow.past{opacity:.5}
.chatrow.now{background:var(--accent-soft);box-shadow:inset 3px 0 0 var(--accent)}
.chatrow .tm{font-family:var(--mono);font-size:10.5px;color:var(--ink3);flex:0 0 auto}
.chatrow .who{font-weight:650;font-size:12px;color:var(--pc,var(--ink2));flex:0 0 auto;max-width:220px;
             overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.chatrow .who em{font-style:normal;font-size:10px;color:var(--ink3);border:1px solid var(--line3);border-radius:3px;padding:0 3px}
.chatrow .tx{font-size:12.5px;color:var(--ink);overflow-wrap:anywhere}
.chatempty{padding:40px;text-align:center;color:var(--ink4)}
.wrap{padding-bottom:84px}

/* ==========================================================================
   建造顺序
   ========================================================================== */
.bo{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-top:12px}
@media(max-width:960px){.bo{grid-template-columns:1fr}}
.bocol{background:var(--card);border:1px solid var(--line);border-radius:var(--r);overflow:hidden;display:flex;flex-direction:column;min-width:0}
.bocol>header{display:flex;align-items:center;gap:8px;padding:9px 12px;border-bottom:1px solid var(--line2);flex-wrap:wrap}
.bocol .rc{width:26px;height:26px;border-radius:6px;display:grid;place-items:center;font-weight:800;font-size:12px;color:#fff;flex:0 0 auto}
.bocol.pa .rc{background:var(--a)} .bocol.pb .rc{background:var(--b)}
.bocol .who{display:flex;flex-direction:column;line-height:1.25;min-width:0}
.bocol .who b{font-size:12.5px;font-weight:650;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:190px}
.bocol .who span{font-size:10.5px;color:var(--ink3);font-family:var(--mono)}
.bocol .sp{flex:1}
.bofilter{display:flex;gap:4px;flex-wrap:wrap}
.bofilter button{border:1px solid var(--line);border-radius:6px;padding:2px 8px;font-size:11px;color:var(--ink3);background:#fff}
.bofilter button.on{background:var(--accent-soft);border-color:var(--accent);color:var(--accent);font-weight:600}

.bolist{overflow:auto;max-height:clamp(300px,calc(100vh - 640px),660px);padding:0 0 6px}
.bog{position:sticky;top:0;z-index:2;display:flex;align-items:center;gap:8px;padding:3px 12px;
     background:var(--card2);border-bottom:1px solid var(--line2);font-family:var(--mono);font-size:10px;color:var(--ink3)}
.bog b{color:var(--ink2);font-weight:600}
.bog .ln{flex:1;height:1px;background:var(--line2)}
.borow{display:grid;grid-template-columns:50px 30px 1fr;align-items:center;gap:7px;
       padding:3px 12px;border-bottom:1px solid var(--line2);cursor:default;
       border-left:2px solid transparent}
.borow:hover{background:var(--accent-soft)}
.borow .tm{font-family:var(--mono);font-size:11px;color:var(--ink3);font-variant-numeric:tabular-nums}
.borow .sp2{font-family:var(--mono);font-size:10.5px;color:var(--ink4);text-align:right}
/* 同一秒的操作合并成一行：行内多个操作块（图标+名+×N），同名项聚合计数 */
.borow .ops{display:flex;flex-wrap:wrap;align-items:center;gap:3px 14px;min-width:0}
.boop{display:inline-flex;align-items:center;gap:5px;min-width:0}
.boop .gly{width:16px;height:16px;border-radius:4px;display:grid;place-items:center;font-size:9px;font-weight:700;color:#fff;font-style:normal;flex:none}
.boop .nm{font-size:12px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0}
.boop .en{font-size:10px;color:var(--ink4);font-family:var(--mono);margin-left:6px}
.boop .cnt{font-family:var(--mono);font-size:10px;color:var(--accent);font-weight:700}
.boop.k-building .gly{background:#6b7c93}
.boop.k-unit     .gly{background:var(--b)}
.boop.k-worker   .gly{background:#9aa6b6}
.boop.k-upgrade  .gly{background:#7a6cf0}
.boop.k-chrono   .gly{background:#3f9e6f}
.boop.k-recall   .gly{background:#30a0a8}
.boop.k-unknown  .gly{background:#c2c9d4}
.boop.k-worker .nm{color:var(--ink3)}
.boop.k-upgrade .nm{color:#5f52d6}
.boop.k-chrono .nm{color:#2f7f57}
.boop.k-recall .nm{color:#1f7f86;font-weight:600}
.borow.now{background:#e7eefe;border-left-color:var(--accent)}
.borow.now .tm{color:var(--accent);font-weight:700}
.boempty{padding:26px 12px;text-align:center;color:var(--ink4);font-size:12px}
.bostat{padding:8px 12px;border-top:1px solid var(--line2);font-size:10.5px;color:var(--ink3);font-family:var(--mono)}

/* ==========================================================================
   左侧板栏里的播报面板
   —— 2026-09-23 由「底部固定播放器条」重做：全宽横条 → 左栏纵向卡片。
      观看与定位交给顶部时间轴（#tl），这里只管「播报」与「悬浮输出」。
      两个倍率：游戏倍率（ovSpeed，对齐游戏内时钟）+ 语速倍率（vbRate，TTS）。
      ⚠️ 原「倍速 1×/2×/4×/8×」（V.speed）已删除 —— 它推的是**全局游标 S.t**，
      是整页播放速度而非语音设置，摆在语音区属于位置与作用域不符。
   ========================================================================== */
/* 播报面板（原底部条 → 栏内纵向卡片）。与 .samples 五五分：`flex:1 1 0` + 内部滚动，
   内容照旧全部展开（不折叠），只是超出半栏时自己滚。 */
.vb{display:flex;flex-direction:column;gap:9px;flex:1 1 0;min-height:0;overflow-y:auto;
    padding:11px 12px;background:var(--card);border:1px solid var(--line);border-radius:var(--r)}
.vb .clock2{font-family:var(--mono);font-size:17px;font-weight:700;letter-spacing:-.4px;
            font-variant-numeric:tabular-nums;color:var(--ink2)}
.vb .clock2.on{color:var(--ok)}
.vb .queue{display:flex;flex-direction:column;gap:2px;min-width:0}
.vb .qs{display:flex;align-items:baseline;gap:8px;font-size:12px;color:var(--ink4);line-height:1.35}
.vb .qs .t{font-family:var(--mono);font-size:10.5px;flex:0 0 auto;min-width:42px;text-align:right}
.vb .qs.cur{color:var(--ink);font-size:13.5px;font-weight:650}
.vb .qs.cur .t{color:var(--warn);font-size:11.5px;font-weight:600}
.vb .qs.dim{opacity:.62}
.vb .prog{height:3px;background:var(--line2);border-radius:2px;overflow:hidden;margin-top:5px}
.vb .prog i{display:block;height:100%;width:0;background:var(--accent);transition:width .1s linear}
.vb .ctl{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.vbbtn{border:1px solid var(--line);background:#fff;border-radius:8px;padding:6px 13px;font-size:12px;font-weight:600;color:var(--ink2)}
.vbbtn:hover{border-color:var(--line3);color:var(--ink)}
.vbbtn.primary{background:var(--accent);border-color:var(--accent);color:#fff}
.vbbtn.primary:hover{background:#2560d8}
.vbset{display:flex;align-items:center;gap:6px;font-size:11px;color:var(--ink3)}
.vbset>span{flex:0 0 auto;min-width:46px}
.vbset select,.vbset input[type=range]{font:inherit;font-size:11px;color:var(--ink2)}
.vbset select{flex:1;min-width:0;border:1px solid var(--line);border-radius:6px;padding:3px 6px;background:#fff}
.vbset input[type=range]{flex:1;min-width:0}
.vbset input[type=text]{flex:1;min-width:0;font:inherit;font-size:11px;color:var(--ink2);
                        border:1px solid var(--line);border-radius:6px;padding:3px 6px;background:#fff}
.vbsep{height:1px;background:var(--line2);margin:1px 0}
.vb .ov{display:flex;align-items:flex-start;gap:7px}
.ovdot{width:8px;height:8px;border-radius:50%;background:var(--ink4);flex:0 0 auto;margin-top:4px}
.ovdot.ok{background:var(--ok)} .ovdot.bad{background:#c0392b} .ovdot.wait{background:var(--warn)}
.ovtxt{font-size:11px;color:var(--ink3);line-height:1.35}
.ovtxt a{color:var(--accent);text-decoration:none}
.ovtxt a:hover{text-decoration:underline}
/* 左栏入口：「筛选」按钮（样本多时左栏塞不下，大范围挑选走弹窗） */
.pickbtn{font:inherit;font-size:10.5px;color:var(--ink2);background:#fff;flex:0 0 auto;
         border:1px solid var(--line);border-radius:6px;padding:2px 8px;cursor:pointer}
.pickbtn:hover{border-color:var(--line3);color:var(--ink)}

/* ==========================================================================
   样本筛选弹窗
   —— 左栏只有 236px，样本一多就没法比较「哪一局更值得看」。
      这里给一个宽面板：搜索（玩家/地图/文件名）+ 排序（比赛时间/时长/采样数/地图）
      + 种族筛选；点条目即切换样本并关闭。逻辑见 views.js 的 pickHits/renderPick/openPick。
   ========================================================================== */
.modal{position:fixed;inset:0;z-index:200;display:flex;align-items:center;justify-content:center;padding:24px}
.modal[hidden]{display:none}
.modal-bg{position:absolute;inset:0;background:rgba(20,25,34,.42)}
.modal-panel{position:relative;display:flex;flex-direction:column;width:min(720px,100%);
             max-height:min(78vh,760px);background:var(--card);border:1px solid var(--line);
             border-radius:var(--r);overflow:hidden}
.modal-panel>header{display:flex;align-items:center;gap:10px;padding:12px 16px;
                    border-bottom:1px solid var(--line2);flex:0 0 auto}
.modal-panel>header b{font-size:13px;font-weight:650}
.modal-panel>header .cnt{font-size:11.5px;color:var(--ink3);font-family:var(--mono)}
.modal-x{margin-left:auto;border:0;background:none;font-size:17px;line-height:1;
         color:var(--ink3);cursor:pointer;padding:2px 4px}
.modal-x:hover{color:var(--ink)}
.modal-filter{display:flex;flex-direction:column;gap:9px;padding:12px 16px;
              border-bottom:1px solid var(--line2);flex:0 0 auto}
.modal-filter input{font:inherit;font-size:12.5px;color:var(--ink);background:#fff;
                    border:1px solid var(--line);border-radius:7px;padding:7px 10px;width:100%}
.modal-row{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:11.5px;color:var(--ink3)}
.modal-row select{font:inherit;font-size:11.5px;color:var(--ink2);background:#fff;
                  border:1px solid var(--line);border-radius:6px;padding:4px 7px}
.modal-list{flex:1 1 auto;min-height:0;overflow-y:auto}
.pitem{display:flex;align-items:flex-start;gap:10px;padding:10px 16px;
       border-bottom:1px solid var(--line2);cursor:pointer}
.pitem:hover,.pitem.on{background:var(--accent-soft)}
.pitem .dot{width:8px;height:8px;border-radius:50%;border:1px solid var(--line3);flex:0 0 auto;margin-top:5px}
.pitem.on .dot{background:var(--accent);border-color:var(--accent)}
.pitem .bd{min-width:0;flex:1}
.pitem .t1{display:flex;align-items:baseline;gap:8px}
.pitem .t1 b{font-size:12.5px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pitem .t1 i{font-style:normal;font-size:10.5px;color:var(--ink4);font-family:var(--mono);
             white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pitem .t2{font-size:11.5px;color:var(--ink2);margin-top:2px}
.pitem .t3{font-size:10.5px;color:var(--ink4);font-family:var(--mono);margin-top:2px}
.pempty{padding:26px 16px;text-align:center;color:var(--ink4);font-size:12px}
.modal-panel>footer{display:flex;align-items:center;gap:10px;padding:11px 16px;
                    border-top:1px solid var(--line2);flex:0 0 auto}
.modal-panel>footer .st{font-size:11.5px;color:var(--ink3);font-family:var(--mono)}
/* 样本筛选框：只在样本数 > 6 时露出（views.js::renderSamples 控制） */
.smpsearch{font:inherit;font-size:11px;color:var(--ink2);background:#fff;flex:0 0 auto;
           border:1px solid var(--line);border-radius:6px;padding:4px 7px}
.smpempty{padding:14px 4px;text-align:center;color:var(--ink4);font-size:11.5px}
/* 左栏分区标题里的细横线（与「录像列表」同一行风格） */
.rail .railhead .ln{flex:1;height:1px;background:var(--line2);align-self:center}
/* 左栏顶部提示（只有解析失败/装配失败时才有内容）。
   ⚠️ 它原来是 `.railhead` 的 flex 子项，**空着也会带 gap 撑出一行空白**（实测 ~26px）——
   所以挪出 railhead，并让空内容整条消失。 */
#railNote{font-size:10px;line-height:1.5;color:var(--ink3);white-space:normal}
#railNote:empty{display:none}

/* ==========================================================================
   运行态外壳（原型里没有，真实页面才有）
   ========================================================================== */

/* 主结果区：没数据时整块不占位（语音条也在里面，靠这条一起隐藏） */
#result{display:none}
#result.on{display:block}

/* --------------------------------------------------------------------------
   底栏已在 2026-09-23 移除：播报 + 悬浮面板搬进了左侧栏 .rail。
   这里原本有一层 .vb / .vbin 外壳（作用是把固定条的内层与 .wrap 严格对齐 ——
   1920 屏上条内容曾比主体左边缘偏 142px），以及一套 --vbh 底部留白机制。
   条没了，留白回到定义系统里的 26px（原型值），--vbh 也不再需要。
   ⚠️ 若将来又把某个面板做回固定底栏：本文件里有 html,body{height:100%}，
   此时 body 的 padding-bottom 不产生任何滚动空间（内容会**穿过** padding 区域），
   必须走 --vbh + 末尾元素外边距那条路。
   -------------------------------------------------------------------------- */
.wrap{padding-bottom:26px}
.infobar{text-align:center;color:var(--ink4);font-size:11.5px;line-height:1.9;padding:0 16px}
.infobar a{color:var(--accent)}
.infobar code{font-family:var(--mono);background:var(--bg);padding:1px 5px;border-radius:4px}
/* 内部指标侧栏同理：原规则只扣了顶部，没扣底部条，会钻到条下面去 */
.side{max-height:calc(100vh - 96px)}

/* --------------------------------------------------------------------------
   录像列表侧边栏
   --------------------------------------------------------------------------
   宽度取 236px，与内部「指标分组」侧栏（.side）同宽，两条竖栏对齐。
   窄屏（≤1180px）退回顶部横排，避免挤压图表。 */
.layout{display:grid;grid-template-columns:236px minmax(0,1fr);gap:16px;align-items:start;margin-top:14px}
.rail{display:flex;flex-direction:column;gap:8px;min-height:0;
      position:sticky;top:64px;height:calc(100vh - 92px);overflow:hidden}
.rail .railhead{margin:0;flex-wrap:wrap;flex:0 0 auto}   /* 左栏分区标题不参与压缩 */
.rail .railhead .grow{flex:0 0 100%;height:0}         /* 强制提示文字独占一行 */
.rail .railhead .lab{font-size:10px;white-space:normal;line-height:1.5}
/* 样本列表与播报面板五五分（各自内部滚动）—— 播报按内容自然高时，矮屏上样本列表只剩两三条。 */
.rail .samples{display:flex;flex-direction:column;gap:6px;flex:1 1 0;min-height:0;
               overflow-y:auto;overflow-x:hidden;padding:0 3px 2px 0}
.rail .smp{flex:0 0 auto;width:100%;min-width:0}
.rail .smp b,.rail .smp span{max-width:none}
.rail .smp b{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;
             white-space:normal;overflow-wrap:anywhere;overflow:hidden}

@media (max-width:1180px){
  .layout{grid-template-columns:minmax(0,1fr)}
  /* 窄屏退回单列：左栏不再固定高，样本横排、播报按内容自然排
     （height:auto 下基类的 flex:1 1 0 会把两段压成 0 高，必须显式覆盖回 0 0 auto） */
  .rail{position:static;height:auto}
  .rail .railhead{margin:12px 0 0}
  .rail .railhead .grow{flex:1;height:auto}
  .rail .samples{flex:0 0 auto;flex-direction:row;overflow-x:auto;overflow-y:visible;padding:12px 0 0}
  .rail .vb{flex:0 0 auto;overflow:visible}
  .rail .smp{width:auto;min-width:190px}
  .rail .smp b{display:block;white-space:nowrap;max-width:230px}
  /* 底部条也收紧：原型里 .queue 的 min-width:280px 会把窄屏的条挤成两行（实测 126px 高） */
  .vbin{gap:10px;padding:7px 18px}
  .vb .queue{min-width:150px}
}

.shellbox{background:var(--card);border:1px solid var(--line);border-radius:var(--r);padding:18px 20px}
.init-status{text-align:center;padding:26px 18px}
.init-status p{margin:0 0 6px;color:var(--ink2)}
.init-status .progress{font-family:var(--mono);font-size:12px;color:var(--ink3)}
.init-status .spinner{margin:0 auto 14px}
.spinner{width:26px;height:26px;border:2.5px solid var(--line);border-top-color:var(--accent);
         border-radius:50%;animation:spin .8s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion:reduce){.spinner{animation-duration:2.4s}}

.drop-zone{border:1.5px dashed var(--line3);border-radius:var(--r);padding:34px 20px;text-align:center;
           background:var(--card);cursor:pointer;transition:.15s;margin-top:12px}
.drop-zone:hover,.drop-zone.dragover,.drop-zone:focus-visible{border-color:var(--accent);background:var(--accent-soft)}
.drop-zone:focus{outline:none}
.drop-zone .icon{font-size:30px;line-height:1;margin-bottom:10px}
.drop-zone p{margin:0 0 5px;color:var(--ink2)}
.drop-zone .hint{font-size:11.5px;color:var(--ink4)}
.drop-zone input[type=file]{display:none}

.loading{display:none;align-items:center;gap:11px;justify-content:center;padding:16px;margin-top:12px;
         background:var(--card);border:1px solid var(--line);border-radius:var(--r);color:var(--ink2);font-size:12.5px}
.loading.on{display:flex}
.loading .spinner{width:16px;height:16px;border-width:2px;margin:0}

.errorbar{display:none;padding:12px 15px;margin-top:12px;border-radius:var(--r);font-size:12.5px;
          background:#fdecea;border:1px solid #f3c2bd;color:#8a2b21}
.errorbar.on{display:block}
.errorbar strong{display:block;margin-bottom:3px}

.empty{padding:34px 18px;text-align:center;color:var(--ink4);font-size:12.5px;line-height:1.9}

/* 录像列表（本地批量；砍掉共享库之后的唯一入口） */
.railhead{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin:12px 0 0}
.railhead .lab{font-size:11px;color:var(--ink3);font-family:var(--mono)}
.railhead .lab.err{color:#c0392b}
.railhead .grow{flex:1}
.smp{position:relative;padding-left:14px}
.smp .rule{position:absolute;left:0;top:0;bottom:0;width:3px;background:var(--line3)}
.smp.on .rule{background:var(--accent)}
.smp .err{color:#c0392b}
.smp .busy{color:var(--warn)}

/* 沙盘面板：科技行标签 + 无图标科技的中文名 chip */
.sbpanel .sp-label{font-family:var(--mono);font-size:9.5px;color:#7f8ba0;margin-bottom:3px;letter-spacing:.5px}
.sbpanel-b .sp-label{text-align:right}
.chip .chip-txt{font-style:normal;font-size:9.5px;color:#dfe6f0;max-width:56px;overflow:hidden;
                text-overflow:ellipsis;white-space:nowrap}

/* HUD 显示开关行（画布图例下方，用户决定两角面板/资源条/镜头标记显示什么） */
.sb-hudopts{display:flex;gap:10px;flex-wrap:wrap;align-items:center;padding:6px 2px 0;
            border-top:1px dashed var(--line2);margin-top:6px}
.sb-hudopts .lab{color:var(--ink3)}

/* 建造顺序行：类别方格内放单位/科技图标（无图标的回退类别字） */
.borow .gly img{width:14px;height:14px;display:block;border-radius:3px}
