/* 星翊顶墙快绘算工作台 · 「把房间画出来」
 * 风格 A 暖陶土 —— 与 outputs/ui/ 的双端稿同源
 * 断点：桌面 1280（左栏 236 + 主区 1fr/330）· 手机 390（单列 + 底部 tab）
 */
:root{
  --clay:#8C3D14; --clay2:#B04A19; --amber:#D97706; --sand:#F7F2EA;
  --paper:#FFFFFF; --ink:#2A211B; --muted:#7C6A5B; --faint:#A2937F;
  --line:#EBE0D2; --soft:#FBF2E9; --warn:#B04A19; --red:#C0392B;
  --green:#2F6B45; --yellow:#B07D0A;
}
*{box-sizing:border-box}
html,body{margin:0;padding:0}
body{
  background:var(--sand);color:var(--ink);
  font-family:"Microsoft YaHei","PingFang SC","Segoe UI",sans-serif;
  font-size:13.5px;line-height:1.6;-webkit-font-smoothing:antialiased;
}
svg{display:block}
a{color:inherit;text-decoration:none}
button,input,select{font:inherit;color:inherit}
.app{display:flex;align-items:stretch;min-height:100vh}

/* ==================== 左栏 ==================== */
.side{width:236px;flex-shrink:0;background:var(--paper);border-right:1px solid var(--line);
  display:flex;flex-direction:column;position:sticky;top:0;height:100vh}
.brand{height:56px;display:flex;align-items:center;gap:9px;padding:0 14px;border-bottom:1px solid var(--line);flex-shrink:0}
.brand .mark{width:28px;height:28px;border-radius:8px;display:grid;place-items:center;flex-shrink:0;
  background:linear-gradient(135deg,var(--clay),var(--clay2));color:#fff;font-size:13.5px;font-weight:700}
.brand b{font-size:13.5px;line-height:1.2}
.brand b i{display:block;font-style:normal;font-size:10px;color:var(--faint);font-weight:400}
.sidepad{padding:12px 12px 10px;flex-shrink:0}
.newbtn{width:100%;border:0;font-size:13px;font-weight:700;color:#fff;cursor:pointer;
  background:linear-gradient(150deg,#8C3D14,#A8491A);border-radius:11px;padding:10px 0;
  box-shadow:0 4px 12px rgba(140,61,20,.22)}
.search{margin-top:9px;display:flex;align-items:center;gap:7px;background:var(--sand);
  border:1px solid var(--line);border-radius:9px;padding:7px 10px;font-size:11.5px;color:var(--faint)}
.tree{flex:1;overflow:auto;padding:6px 8px 10px}
.tree .grp{font-size:10px;color:var(--faint);letter-spacing:.12em;padding:8px 8px 5px}
.tree .grp.sep{margin-top:7px;padding-top:11px;border-top:1px solid var(--line)}

/* ============================================================================
 * ★★★ 2026-09-26 第二十五轮 · 13 号「清开发痕迹」—— **调试模式（`?debug=1`）总闸**
 * ----------------------------------------------------------------------------
 * 老唐在真站上发现三处、总设计扫出一处，**同一个根因**：
 *   开发期的「板块进度表 / 接口取证面板」被当成客户界面用了。
 * ⇒ 根因同一个，处置也应当同一个：**默认不显示，只在 `?debug=1` 时出现**。
 *
 * 开关本身在 `index.html` 的 `<head>` 里（同步内联脚本，第一帧之前就把 class 挂上）：
 *   `?debug=1` ⇒ `<html class="dbg">`；不加 ⇒ 客户态。
 *   —— 为什么用 URL 参数、不用 localStorage、也不做界面开关：见 `index.html` 那段注释（三条理由）。
 *
 * `class="dbgonly"` 挂在这些地方（**全部是开发期信息，客户看了既不懂也用不上**）：
 *   · 右栏 `#cardEngine`（引擎与接口能力：服务版本 / 默认板格 / 实测用板 / 三个上限 / 四条能力探测）
 *   · 左栏底部 `.mepill`（「引擎已连接 · v0.1.0」状态行）
 *   · 左栏 04「计算引擎」那一行 ＋ 手机底导「引擎」那一项（**连带**：它们的唯一落点就是上面那张卡，
 *     不收就成了死行 —— 第二十四轮刚清零过，老唐也刚抱怨过）
 *   · 左栏每行右侧的 `.cnt` 项数（4/7/17/9/12/6/7/8/9，出处《功能板块清单》）
 *   · `.tagv2`（V2 徽标）· `.lv2 em`（二级项的 V1/V3）· 顶部面包屑 `.tagv`（「板块 01/02/12 · V1 13 项」）
 *   · 排板起点卡标题里的 `<em>板块 12 V1</em>`
 *
 * ⚠️ **保留不动的**（客户有用，不是开发痕迹）：
 *   · 板块编号 `01`~`13`（当"功能序号"用，与验收判据绑定）
 *   · `.role` 权限标签（「仅老板」/「不算料」—— 说明权限与范围）
 *   · 账号区（店铺名 / 角色 / 试用天数）
 *
 * ⚠️ 用一条 `!important` 的通用规则、而不是给每处各写一条 —— 少一处漏网。
 *    新增开发期信息时，只要挂 `dbgonly` 就自动归位，不必再改 CSS。
 * ========================================================================== */
html:not(.dbg) .dbgonly{display:none!important}

.lv1{display:flex;align-items:center;gap:8px;height:37px;padding:0 9px;border-radius:9px;color:#54463A;position:relative;cursor:pointer}
.lv1 .ico{width:18px;height:18px;flex-shrink:0;color:#8A7867;display:grid;place-items:center}
.lv1 .nm{font-size:12.5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.lv1 .no{font-size:9.5px;color:var(--faint);font-family:Consolas,monospace}
.lv1 .cnt{margin-left:auto;font-size:10px;color:var(--faint);background:var(--sand);border-radius:5px;padding:1px 5px}
.lv1.active{background:var(--soft);color:var(--clay);font-weight:700}
.lv1.active .ico{color:var(--clay)}
.lv1.active .cnt{background:#F3E2CF;color:var(--clay)}
.lv1.active::before{content:"";position:absolute;left:0;top:9px;bottom:9px;width:3px;border-radius:2px;background:var(--clay)}
/* ★★ 2026-09-26 第二十四轮 · 13 号「左栏接线（A 方案）」—— **补 hover 反馈**。
   病根：全局原来**只有 `.lv1.newlink:hover` 一条** ⇒ 页内锚点那几行（01 / 02 / 03 / 04 / 05 / 06 /
   07 / 12 / 参数配置）**点得动，鼠标移上去却毫无反馈**，看上去跟死行一模一样 ——
   这正是老唐 03:18 说「点了没反应」的一部分来源（不只是"真没接线"）。
   ⚠️ 用 `[href]` 限定 ⇒ **只有真的能点走的行**才有反馈：置灰的 11（`.soon`、没有 href）天然不受影响；
      再叠一道 `:not(.soon)` 是为了防后人给 11 补上 href 时把它一起点亮。
   ⚠️ 底色与 `.newlink:hover` 一致（`--soft`）—— 页内跳和跨页跳看起来应当是同一件事。
   ⚠️ `.lv1.active`（当前屏）本来就是 `--soft` ⇒ 它 hover 前后无变化，这是对的（它已经在"这儿"了）。 */
.lv1[href]:not(.soon):hover{background:var(--soft)}
.lv1 .role{margin-left:auto;font-size:9px;color:#9A5A11;background:#FDF1E3;border:1px solid #EFDCB8;border-radius:4px;padding:0 3px;white-space:nowrap}
/* ★★ 2026-09-26 第二十五轮 · 13 号 E 项 —— 二级项的**展开箭头**。
   为什么非要有它：老唐原话「这个 2 级也收不回，**点了也没反应**」。
   病根就是**没有任何可点的迹象**（`a.lv2` 没有 href、没有 cursor:pointer、也没有箭头）。
   ⇒ 折叠之后必须给一个"这里有东西可以展开"的明示，否则客户永远发现不了（＝白折叠）。
   位置：紧跟在行号后面（`.cnt` 自带 `margin-left:auto` 会把它推到最右，所以箭头**不会**跑到右边去）。
   形状：一颗小 `›`，展开时旋 90° 变 `⌄`（`aria-expanded` 由 `main.js` 维护，样式跟着它走）。 */
.lv1 .caret{display:grid;place-items:center;width:13px;height:13px;flex-shrink:0;color:#B4A491;
  transition:transform .16s ease}
.lv1[aria-expanded="true"] .caret{transform:rotate(90deg);color:var(--clay)}
.lv2wrap{padding:1px 0 5px 21px;border-left:1px dashed #E7D8C6;margin:0 0 3px 18px}
/* ★ E 项：收起时**整块一并隐藏、不留空白**（老唐特意点过这一条）。用 `hidden` 属性 + 显式 CSS
   （`.lv2wrap` 自己没设 display，UA 的 `[hidden]{display:none}` 本来也够，这里写死一道免得被后人改坏）。 */
.lv2wrap[hidden]{display:none}
.lv2{display:flex;align-items:center;height:28px;padding:0 9px;border-radius:8px;font-size:11.5px;color:var(--muted)}
.lv2 .dot{width:5px;height:5px;border-radius:50%;background:#D9C9B6;margin-right:9px;flex-shrink:0}
.lv2 em{margin-left:auto;font-style:normal;font-size:9px;color:var(--faint)}
.lv2.on{color:var(--clay)}
.lv2.on .dot{background:var(--clay);width:6px;height:6px}
.sidefoot{border-top:1px solid var(--line);padding:11px 12px 13px;flex-shrink:0}
.mepill{display:flex;align-items:center;gap:7px;font-size:11px;color:var(--muted);background:var(--sand);
  border:1px solid var(--line);border-radius:9px;padding:7px 10px}
.mepill .dot{width:7px;height:7px;border-radius:50%;background:var(--faint);flex-shrink:0}
.mepill.ok .dot{background:var(--green)} .mepill.ok{color:var(--green)}
.mepill.bad .dot{background:var(--red)} .mepill.bad{color:var(--red)}
.mepill span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.me{display:flex;align-items:center;gap:8px;margin-top:11px;padding-top:10px;border-top:1px solid var(--line)}
.me .pic{width:26px;height:26px;border-radius:50%;background:#E7DAC9;display:grid;place-items:center;font-size:11px;color:#7A5A3C;font-weight:700}
.me b{font-size:11.5px;font-weight:600}
.me b span{display:block;font-size:10px;color:var(--faint);font-weight:400}

/* ==================== 主区 ==================== */
.main{flex:1;min-width:0;display:flex;flex-direction:column}
.topbar{height:56px;background:var(--paper);border-bottom:1px solid var(--line);
  display:flex;align-items:center;gap:12px;padding:0 18px;flex-shrink:0;position:sticky;top:0;z-index:20}
.crumb{display:flex;align-items:center;gap:7px;font-size:12.5px;color:var(--faint);min-width:0}
.crumb b{color:var(--ink);font-size:14px;white-space:nowrap}
.crumb .tagv{font-size:10.5px;background:var(--soft);color:#9A5A11;border:1px solid #EFDCB8;border-radius:6px;padding:2px 7px;white-space:nowrap}
.seg{margin-left:auto;display:flex;background:var(--sand);border:1px solid var(--line);border-radius:10px;padding:3px}
.seg button{border:0;background:transparent;font-size:12px;color:var(--muted);padding:6px 12px;border-radius:8px;cursor:pointer}
.seg button.on{background:var(--paper);color:var(--clay);font-weight:700;box-shadow:0 1px 4px rgba(140,61,20,.12)}
.tb-btn{border:1px solid var(--line);background:var(--paper);color:var(--muted);font-size:12px;
  border-radius:9px;padding:7px 11px;cursor:pointer}
.tb-btn:hover{color:var(--clay);border-color:#E3CDB4}
/* ★ 可用 / 禁用要一眼分得出来 —— 老唐 09-23 截图里把「撤销」看成灰的不能用（实测能点），
 *   原来的 `.42` 透明度差别太弱。禁用改成虚线 + 更淡 + not-allowed。 */
.tb-btn:not(:disabled){border-color:#E3CDB4;color:var(--clay)}
.tb-btn:disabled{opacity:.34;cursor:not-allowed;border-style:dashed;background:transparent;color:var(--faint)}
.savepill{font-size:11px;color:var(--green);background:#EDF6F0;border-radius:20px;padding:4px 10px;white-space:nowrap}
.savepill.dirty{color:#9A5A11;background:#FDF1E3}

.content{padding:16px 18px 26px;display:grid;grid-template-columns:minmax(0,1fr) 336px;gap:16px;align-items:start}
.stagecol{min-width:0;display:flex;flex-direction:column;gap:14px}
.panelcol{display:flex;flex-direction:column;gap:14px;position:sticky;top:72px}
.card{background:var(--paper);border:1px solid var(--line);border-radius:15px;padding:14px 16px}
.card > h3{margin:0 0 10px;font-size:13px;color:var(--clay);display:flex;align-items:baseline;gap:8px;flex-wrap:wrap}
.card > h3 em{font-style:normal;font-size:10.5px;color:var(--faint);font-weight:400}
.canvas-card{padding:10px 10px 0}

/* 工具条 */
.toolbar{display:flex;align-items:center;gap:10px;flex-wrap:wrap;padding:2px 4px 10px}
.toolgroup{display:flex;align-items:center;gap:6px;flex-wrap:wrap}
.tg-label{font-size:11.5px;color:var(--muted)}
.tsep{width:1px;height:22px;background:var(--line)}
.tool{border:1px solid var(--line);background:#FFFDFA;color:var(--muted);border-radius:9px;
  padding:6px 10px;font-size:12px;cursor:pointer;display:inline-flex;align-items:center;gap:5px}
.tool:hover{color:var(--clay);border-color:#E3CDB4}
.tool.on{background:var(--clay);border-color:var(--clay);color:#fff;font-weight:700}
.tool.on svg{stroke:#fff}
.zoomgroup{color:var(--muted);font-size:11.5px;gap:5px}
.zoomgroup .tool{padding:5px 9px}
.tpl{border:1px solid var(--line);background:#FFFDFA;border-radius:10px;padding:6px 10px;cursor:pointer;text-align:left;line-height:1.25}
.tpl:hover{border-color:#E3CDB4}
.tpl b{display:block;font-size:12px;color:var(--ink)}
.tpl span{font-size:10px;color:var(--faint)}
.tpl.on{background:var(--soft);border-color:var(--clay)}
.tpl.on b{color:var(--clay)}

/* 画布 */
.stage{position:relative;height:min(58vh,520px);min-height:400px;border-radius:11px;overflow:hidden;
  background:#FBF7F1;border:1px solid var(--line)}
.stage svg{width:100%;height:100%;display:block;touch-action:none;cursor:crosshair}
/* ★ `#overlay` 是纯装饰层（产品标签 / 房外距离 / 房间宽高），**不该参与命中测试**。
 *   2026-09-24 实测它一直是 `pointer-events:auto` ⇒ `elementFromPoint` 命中的是标签文字而不是画布，
 *   做命中/避让类判据时会被它带偏。画布自己的命中走 `hitAppliance()`（数学判定、不读 DOM target），
 *   所以关掉它不影响任何点击 —— 只是让"谁在上面"这个问题回到真话。 */
#overlay{pointer-events:none}
.stage.tool-pan svg{cursor:grab}
.stage.tool-moveLayout svg{cursor:move}
.stagehint{position:absolute;left:10px;top:10px;max-width:min(78%,430px);pointer-events:none;
  font-size:11.5px;line-height:1.55;color:var(--warn);background:rgba(253,241,236,.95);
  border:1px solid #F0CDBC;border-radius:9px;padding:7px 10px}
.stagehint:empty{display:none}
.stagehint.info{color:#7A5410;background:rgba(253,247,234,.95);border-color:#EFDDB4}
.stagelegend{position:absolute;left:10px;bottom:10px;display:flex;gap:12px;flex-wrap:wrap;pointer-events:none;
  background:rgba(255,255,255,.9);border:1px solid var(--line);border-radius:9px;padding:6px 10px;
  font-size:10.5px;color:var(--muted)}
.stagelegend i{display:inline-block;width:16px;height:9px;border-radius:2px;margin-right:5px;vertical-align:middle}

/* ==================== 基元尺寸卡（第五轮 · 2026-09-24） ====================
 * 「先输长宽出矩形 → 再放基元（缺口 / 洞口）→ 拖动定位」里的那张卡。
 * ★ 位置＝画布**右上角**：左下角是图例、右下角是放置条、左上角是提示条，这里是唯一空着的一角。
 *   （稿子把卡摆在画布左侧，会压住房间正中；按「不遮房间」挪到右上，其余照稿。）
 * ★ 手机＝**底部抽屉**（画布只有 390 宽，浮卡必然盖住房间）＋ 图例让位。
 * ⚠️ 它长在 `.stage` 里、里面有按钮 ⇒ `main.js` 的 pointerdown 守卫必须带上 `#primCard`
 *   （`setPointerCapture` 会抢走 pointerup ⇒ 浏览器不生成 click，本项目踩过三次的同一条雷）。 */
.dimcard{position:absolute;right:10px;top:10px;width:254px;z-index:8;
  background:rgba(255,255,255,.985);border:1px solid #E3D2BC;border-radius:13px;
  padding:11px 13px 13px;box-shadow:0 12px 30px rgba(42,33,27,.16)}
.dimcard[hidden]{display:none}
.dimcard .hd{font-size:12.5px;font-weight:700;color:var(--ink);display:flex;align-items:baseline;gap:7px}
.dimcard .hd span{font-size:10px;font-weight:400;color:var(--faint)}
.dimcard .rw{display:flex;align-items:center;gap:7px;margin-top:9px;font-size:11.5px;color:var(--muted)}
.dimcard .rw .lb{width:52px;flex-shrink:0;color:var(--faint);font-size:11px}
.dimcard input{width:62px;min-width:0;border:1px solid #E3D2BC;border-radius:8px;padding:5px 7px;
  font-size:12px;text-align:right;background:#FFFDFA;font-variant-numeric:tabular-nums}
.dimcard input:disabled{background:#F4F0EA;color:var(--faint);border-style:dashed}
.dimcard .u{font-size:10px;color:var(--faint)}
.dimcard .x{color:var(--faint);font-size:11px}
.dseg{display:flex;background:var(--sand);border:1px solid var(--line);border-radius:9px;padding:2px}
.dseg button{border:0;background:none;cursor:pointer;white-space:nowrap;font-size:11px;font-weight:400;
  color:var(--muted);padding:3px 10px;border-radius:7px}
.dseg button.on{background:var(--paper);color:var(--clay);font-weight:700;box-shadow:0 1px 3px rgba(140,61,20,.14)}
.dseg.tiny button{font-size:10px;padding:3px 7px}
.dcnote{font-size:10.5px;color:#9A5A11;background:#FDF7EA;border:1px solid #EFDDB4;border-radius:8px;
  padding:5px 8px;margin-top:10px;line-height:1.5}
.dcnote.gray{color:var(--muted);background:#F6F4F0;border-color:#E6E2DA}
.dcnote b{color:var(--clay)}
.dcact{display:flex;gap:7px;margin-top:10px}
.dcbtn{flex:1;border:0;background:var(--clay);color:#fff;border-radius:9px;padding:8px 0;
  font-size:12px;font-weight:700;cursor:pointer}
.dcbtn.g2{background:#FFFDFA;color:var(--clay);border:1px solid #E3D2BC;flex:0 0 auto;padding:8px 11px}

/* 尺寸标注「点一下改数」的就地输入浮层（第 4 件一期：电器 ＋ 洞口） */
.dimedit{position:absolute;z-index:9;background:rgba(255,255,255,.99);border:1px solid #E3D2BC;
  border-radius:12px;padding:10px 12px 12px;box-shadow:0 12px 30px rgba(42,33,27,.18);width:214px}
.dimedit > b{display:block;font-size:12px;color:var(--ink);margin-bottom:7px}
.dimedit label.mini{display:flex;align-items:center;gap:7px;font-size:11px;color:var(--faint);margin-top:7px}
.dimedit input{width:76px;border:1px solid #E3D2BC;border-radius:8px;padding:5px 7px;font-size:12px;
  text-align:right;background:#FFFDFA;font-variant-numeric:tabular-nums}
.dimedit .deact{display:flex;gap:7px;margin-top:10px}
.dimedit button{flex:1;border-radius:9px;padding:7px 0;font-size:12px;font-weight:700;cursor:pointer;border:0;
  background:var(--clay);color:#fff}
.dimedit button.dec{background:#FFFDFA;color:var(--clay);border:1px solid #E3D2BC;font-weight:400}
.dimedit .dehint{font-size:10.5px;color:var(--faint);margin-top:8px;line-height:1.5}

/* ★★★ 放置条（老唐 A「加一个确认键」）—— 落在**画布内底部右端**（他圈的那个角），
 * 与左下角的 `.stagelegend` **同一行、右对齐**。只在「放电器」工具下出现。
 * ⚠️ 两条雷都按本项目已有的先例处理（都真踩过）：
 *   ① 它和 `.stagelegend` 一样是浮层 ⇒ 整条 `pointer-events:none`（不许挡画布），
 *      **按钮自己再 `pointer-events:auto`**（先例：`.stagehint .undo-in-hint` `:290-292`）。
 *   ② 光有 ① 不够：画布的 `pointerdown` 会 `stage.setPointerCapture()` 把 `pointerup` 抢走
 *      ⇒ 按下与抬起不是同一个元素 ⇒ **浏览器根本不生成 click**（按钮在那儿、按下去毫无反应）。
 *      ⇒ `main.js` 的 `pointerdown` 开头**必须加第三条守卫**（`barEl.contains(e.target)` 就 return），
 *        与已有的 `menuEl` / `popEl` / `hintEl` 三处同款。 */
.stagebar{position:absolute;right:10px;bottom:10px;z-index:12;display:none;align-items:center;gap:8px;
  flex-wrap:wrap;max-width:calc(100% - 20px);
  background:rgba(255,255,255,.96);border:1px solid var(--line);border-radius:10px;padding:5px 7px 5px 11px;
  font-size:11.5px;color:var(--muted);box-shadow:0 4px 14px rgba(42,33,27,.13);pointer-events:none}
.stagebar.on{display:flex}
.stagebar .sbnow b{color:var(--clay);font-variant-numeric:tabular-nums}
.stagebar .sbdir{display:flex;gap:3px}
.stagebar .sbdir button{border:1px solid var(--line);background:#FFFDFA;border-radius:7px;padding:3px 9px;
  font-size:11px;color:var(--muted);cursor:pointer;pointer-events:auto}
.stagebar .sbdir button:hover{background:var(--soft);color:var(--clay)}
.stagebar .sbdir button.on{background:var(--soft);border-color:var(--clay);color:var(--clay);font-weight:700}
.stagebar .sbok{border:0;background:var(--clay);color:#fff;border-radius:8px;padding:5px 12px;
  font-size:11.5px;font-weight:700;cursor:pointer;pointer-events:auto}
.stagebar .sbok:hover{background:#7A3410}
/* 放置态：图例给放置条**让位**。
 * ★ 桌面走「收窄」——右端实测剩 180px、与图例同处一行 ⇒ 把图例的最大宽度按放置条实测宽度收掉即可
 *   （`--legendroom` 由 `main.js placeStageBar()` 量完真实宽度后写到 `.stage` 上）；
 *   桌面万一被挤到放不下（窄窗口），`main.js` 会给图例加 `.away` 直接隐藏，
 *   与手机同一套结果、只是触发条件不同。 */
.stage.placement .stagelegend{max-width:calc(100% - var(--legendroom,300px) - 32px)}
.stage.placement .stagelegend.away{display:none}
.statusbar{display:flex;gap:14px;flex-wrap:wrap;font-size:11.5px;color:var(--muted);padding:9px 6px 11px}
.statusbar b{color:var(--ink);font-variant-numeric:tabular-nums}
.statusbar .sep{width:1px;height:12px;background:var(--line)}

/* 校验 */
.vlist{display:flex;flex-direction:column;gap:8px}
.vitem{border-radius:10px;padding:9px 12px;font-size:12.5px;line-height:1.6;border:1px solid transparent}
.vitem b{display:block;font-size:12.5px;margin-bottom:2px}
.vitem .fix{font-size:11.5px;color:var(--muted);display:block;margin-top:3px}
.vitem .stale{font-size:10.5px;color:var(--faint);margin-left:6px;font-weight:400}
.vitem.red{background:#FDF3F2;border-color:#F1D3CE;color:#8A2D1C}
.vitem.yellow{background:#FDF9EE;border-color:#EFE0BE;color:#7A5410}
.vitem.green{background:#F0F7F2;border-color:#D6E8DC;color:#285C3B}
.vitem.info{background:var(--soft);border-color:var(--line);color:var(--muted)}
.vcount{display:flex;gap:7px;margin-bottom:9px;flex-wrap:wrap}
.vcount span{font-size:11px;border-radius:20px;padding:3px 10px;background:var(--sand);color:var(--muted)}
.vcount span.red{background:#FDF3F2;color:#8A2D1C}
.vcount span.yellow{background:#FDF9EE;color:#7A5410}
.vcount span.green{background:#F0F7F2;color:#285C3B}

/* 料单 */
.kpis{display:flex;gap:9px;flex-wrap:wrap;margin-bottom:11px}
.kpi{flex:1 1 88px;background:#FFFDFA;border:1px solid var(--line);border-radius:10px;padding:9px 11px}
.kpi b{display:block;font-size:19px;color:var(--clay);line-height:1.22;font-variant-numeric:tabular-nums}
.kpi span{font-size:11px;color:var(--faint)}
table.bomtable{width:100%;border-collapse:collapse;font-size:12.5px}
table.bomtable td{padding:8px 6px;border-bottom:1px solid #F6F1E9;vertical-align:middle}
table.bomtable tr:last-child td{border-bottom:0}
table.bomtable td.num{text-align:right;font-weight:700;color:var(--clay);font-variant-numeric:tabular-nums}
table.bomtable td.unit{color:var(--faint);font-size:11px;width:30px}
.tagline{font-size:10.5px;background:var(--soft);color:#9A5A11;border-radius:5px;padding:1px 6px}
.structbox{margin-top:11px;border-top:1px dashed var(--line);padding-top:10px;font-size:11.5px;color:var(--muted);line-height:1.9}
.structbox b{color:var(--ink);font-variant-numeric:tabular-nums}
.warnbox{margin-top:10px;display:flex;flex-direction:column;gap:6px}

/* 右栏控件 */
.kvlist{display:flex;flex-direction:column;gap:6px;font-size:12px}
.kvrow{display:flex;gap:8px}
.kvrow span{color:var(--faint);min-width:74px;font-size:11.5px;padding-top:1px}
.kvrow b{font-weight:600;word-break:break-all;font-variant-numeric:tabular-nums}
.capsbox{margin-top:10px;display:flex;flex-direction:column;gap:6px}
.cap{border-radius:9px;padding:7px 10px;font-size:11.5px;line-height:1.55;border:1px solid transparent}
.cap.adopted{background:#F0F7F2;border-color:#D6E8DC;color:#285C3B}
.cap.ignored{background:#FDF9EE;border-color:#EFE0BE;color:#7A5410}
.cap.nochange{background:#FDF9EE;border-color:#EFE0BE;color:#7A5410}
.cap.error,.cap.unknown{background:#F6F2EC;border-color:var(--line);color:var(--muted)}
.cap.estimate{background:#FFF8F1;border-color:#EFDCB8;color:#9A5A11}
.cap b{display:block;font-size:11.5px}
label.fl{display:block;font-size:11.5px;color:var(--muted);margin:0 0 4px}
label.fl.lag{margin-top:11px}
.row2{display:flex;gap:8px}
.row2 input,.row2 select{flex:1;min-width:0}
input[type=number],select{width:100%;padding:7px 9px;border:1px solid var(--line);border-radius:8px;
  background:#FFFDFA;font-size:13px;color:var(--ink)}
input[type=number]:focus,select:focus{outline:2px solid #F0DCC4;outline-offset:0;border-color:var(--clay)}
.btn{width:100%;margin-top:10px;padding:9px;border:0;border-radius:9px;background:var(--clay);
  color:#fff;font-size:13px;font-weight:700;cursor:pointer}
.btn:hover{background:#7A3410}
.btn.ghost{background:#fff;color:var(--clay);border:1px solid var(--line)}
.btn.ghost:hover{background:var(--soft)}
.sw{display:flex;align-items:flex-start;gap:8px;margin:10px 0 0;font-size:12.5px}
.sw input{margin-top:3px;flex-shrink:0}
.sw label{cursor:pointer}
.sw label span{display:block;color:var(--faint);font-size:11px;line-height:1.45;font-weight:400}
.segfull{display:flex;gap:5px;background:var(--sand);border:1px solid var(--line);border-radius:10px;padding:3px}
.segfull button{flex:1;border:0;background:transparent;font-size:12px;color:var(--muted);padding:7px 4px;border-radius:8px;cursor:pointer}
.segfull button.on{background:var(--paper);color:var(--clay);font-weight:700;box-shadow:0 1px 4px rgba(140,61,20,.12)}
.nudge{margin-top:10px}
.fl2{display:block;font-size:11.5px;color:var(--muted);margin-bottom:5px}
label.mini{flex:1;display:flex;align-items:center;gap:6px;font-size:11.5px;color:var(--faint)}
label.mini input{flex:1;min-width:0}
.capnote{margin-top:10px;font-size:11.5px;line-height:1.6;color:var(--muted);
  background:var(--soft);border-left:3px solid var(--amber);border-radius:0 8px 8px 0;padding:8px 11px}
.capnote b{color:var(--clay)}
.capnote .sub{margin-top:4px;color:var(--faint)}
.capnote .sub.warn{color:#B07D0A}
.appllist{margin-top:11px;display:flex;flex-direction:column;gap:7px}
.aprow{display:flex;align-items:center;gap:8px;font-size:12px;border:1px solid var(--line);border-radius:10px;padding:7px 10px;background:#FFFDFA}
.aprow .k{font-size:10px;font-weight:700;border-radius:5px;padding:1px 6px;background:#EDE7DE;color:var(--muted);flex-shrink:0}
.aprow .nm{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.aprow .sz{font-size:10.5px;color:var(--faint);flex-shrink:0}
.aprow .del{border:0;background:transparent;color:var(--faint);cursor:pointer;font-size:14px;padding:0 2px;flex-shrink:0}
.aprow .del:hover{color:var(--red)}
.aprow.sel{border-color:var(--clay);background:var(--soft)}

/* 管线 */
.pipeline{display:flex;flex-direction:column;gap:5px}
.pstep{display:flex;align-items:flex-start;gap:8px;font-size:12px;color:var(--faint)}
.pstep .pd{width:15px;height:15px;border-radius:50%;border:1.5px solid var(--line);flex-shrink:0;margin-top:2px;
  display:grid;place-items:center;font-size:9px;color:#fff}
.pstep .pt{flex:1;min-width:0}
.pstep .pt b{font-weight:600;display:block}
.pstep .pt span{font-size:10.5px;color:var(--faint);display:block;line-height:1.4}
.pstep.run{color:var(--amber)}
.pstep.run .pd{border-color:var(--amber);background:var(--amber);animation:pulse 1s ease-in-out infinite}
.pstep.ok{color:var(--ink)}
.pstep.ok .pd{border-color:var(--green);background:var(--green)}
.pstep.err{color:var(--red)}
.pstep.err .pd{border-color:var(--red);background:var(--red)}
.pstep.skip .pd{opacity:.4}
@keyframes pulse{0%,100%{transform:scale(1);opacity:1}50%{transform:scale(1.18);opacity:.7}}

.mobnav{display:none}

/* 长按格子的电器菜单 */
.ctxmenu{position:absolute;z-index:30;width:196px;background:var(--paper);border:1px solid var(--line);
  border-radius:12px;padding:9px 10px;box-shadow:0 10px 26px rgba(42,33,27,.16)}
.ctxmenu b{display:block;font-size:11.5px;color:var(--clay);margin-bottom:6px}
.ctxgrid{display:flex;flex-direction:column;gap:4px;max-height:210px;overflow:auto}
.ctxgrid button{border:1px solid var(--line);background:#FFFDFA;border-radius:8px;padding:6px 9px;
  font-size:11.5px;color:var(--ink);text-align:left;cursor:pointer}
.ctxgrid button:hover{background:var(--soft);border-color:#E3CDB4;color:var(--clay)}

/* ==================== 电器交互（8 号 · 2026-09-23 23:3x 增） ==================== *
 * 三条诉求的落点：①「放电器」按钮弹型号面板 ② 画布选中电器 → 就地浮窗（改尺寸/旋转/删除）
 * ③ 撤销相关：报错红框里给「撤销上一步」+ 数字键切工具给可见反馈。 */

/* ① 「放电器」按钮外面包一层，把型号面板锚在按钮正下方 */
.toolwrap{position:relative;display:inline-flex}
.applmenu{position:absolute;left:0;top:36px;z-index:30;width:302px;background:var(--paper);
  border:1px solid var(--line);border-radius:12px;padding:10px 11px 9px;
  box-shadow:0 12px 28px rgba(42,33,27,.18)}
.applmenu b{display:block;font-size:11.5px;color:var(--clay);margin-bottom:7px}
.amgrid{display:grid;grid-template-columns:1fr 1fr;gap:5px}
.amgrid button{border:1px solid var(--line);background:#FFFDFA;border-radius:8px;padding:7px 8px;
  font-size:11px;color:var(--ink);text-align:left;cursor:pointer;line-height:1.32}
.amgrid button:hover{background:var(--soft);border-color:#E3CDB4;color:var(--clay)}
.amgrid button.on{background:var(--soft);border-color:var(--clay);color:var(--clay);font-weight:700}
/* 型号面板顶部的「横放 / 竖放」—— 「产品自身方向」的入口。
 * ★ 手机没有 R 键 ⇒ 面板里这两个按钮就是**手机的唯一入口**，所以它必须常驻在面板顶部
 *   （不是藏在某个二级菜单里）。桌面端它和 R 键等价，两条路都成立。
 * ⚠️ 尺寸文案是「现拼」的（`specText` 按方向算）⇒ 两个按钮上直接写清「横放 600×300 / 竖放 300×600」，
 *   用户点之前就知道转过去是什么尺寸，不用试。 */
.amorient{display:flex;align-items:center;gap:5px;margin:-1px 0 8px}
.amorient>span{font-size:11px;color:var(--faint);flex-shrink:0}
.amorient button{flex:1;border:1px solid var(--line);background:#FFFDFA;border-radius:8px;
  padding:6px 4px;font-size:11px;color:var(--muted);cursor:pointer;line-height:1.3}
.amorient button:hover{background:var(--soft);border-color:#E3CDB4;color:var(--clay)}
.amorient button.on{background:var(--soft);border-color:var(--clay);color:var(--clay);font-weight:700}
.amfoot{margin-top:8px;font-size:10.5px;color:var(--faint);line-height:1.5;
  border-top:1px dashed var(--line);padding-top:7px}
.amfoot b{color:var(--clay)}

/* ② 画布上选中电器 → 就地小浮窗 */
.applpop{position:absolute;z-index:30;width:238px;background:var(--paper);border:1px solid var(--line);
  border-radius:12px;padding:11px 12px 10px;box-shadow:0 12px 28px rgba(42,33,27,.2)}
.applpop .ph{display:flex;align-items:center;gap:7px;margin-bottom:9px}
.applpop .pk{font-size:10px;font-weight:700;border-radius:5px;padding:1px 6px;flex-shrink:0}
.applpop .pn{flex:1;min-width:0;font-size:12.5px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* 四类归属那行 —— 只给字母看不出「改尺寸会换类」，这里写出全称（A 占格 / B 嵌入 / C 开孔 / D 替龙） */
.applpop .pkd{font-size:11.5px;color:var(--faint);margin:-3px 0 9px}
.applpop .pkd b{color:var(--ink);font-weight:600}
.applpop .px{border:0;background:transparent;color:var(--faint);font-size:17px;cursor:pointer;line-height:1;padding:0 2px;flex-shrink:0}
.applpop .px:hover{color:var(--red)}
.applpop .prow{display:flex;gap:8px;align-items:center}
.applpop label.mini{flex:1;display:flex;align-items:center;gap:5px;font-size:11px;color:var(--faint)}
.applpop input[type=number]{width:100%;min-width:0;padding:6px 8px;font-size:12px}
.applpop .pacts{display:flex;gap:7px;margin-top:9px}
.applpop .pbtn{flex:1;border:1px solid var(--line);background:#FFFDFA;border-radius:8px;padding:7px 6px;
  font-size:11.5px;color:var(--clay);cursor:pointer}
.applpop .pbtn:hover{background:var(--soft)}
.applpop .pdanger{color:#B04A19;border-color:#F0CDBC;background:#FDF1EC}
.applpop .pdanger:hover{background:#FBE7DE}
.applpop .phint{margin-top:8px;font-size:10.5px;color:var(--faint);line-height:1.5}
/* ★ 到墙距离（老唐：「房间外边显示产品边到房间边的距离，这个距离可以支持手动输入」）
 * 4 个「标签 + 输入框」在 238px 宽里一行放不下（实测算下来约 288px）⇒ 2×2 栅格。 */
.applpop .pdist-lab{font-size:10.5px;color:var(--faint);margin:10px 0 -3px}
.applpop .pdist{display:grid;grid-template-columns:1fr 1fr;gap:6px 8px;margin-top:9px}
/* 手输不拦、只提示：出房间（负距离）用红、其余软告警用黄 */
.applpop .pdwarn{margin-top:8px;font-size:10.5px;line-height:1.5;border-radius:7px;padding:5px 8px;
  background:#FDF7EA;border:1px solid #EFDDB4;color:#7A5410}
.applpop .pdwarn.bad{background:#FDF1EC;border:1px solid #F0CDBC;color:#B04A19}
.applpop .pdwarn b{font-weight:700}

/* ③ 数字键切工具的可见反馈（老唐截图里当前工具正是「擦除」⇒「画错」多半是误按 2 键） */
.toolflash{position:absolute;left:12px;bottom:48px;z-index:29;font-size:11.5px;color:#7A5410;
  background:rgba(253,247,234,.97);border:1px solid #EFDDB4;border-radius:9px;padding:6px 11px;
  pointer-events:none}

/* ④ 报错红框里的「撤销上一步」（红框本身 pointer-events:none，按钮要单独放开） */
.stagehint .undo-in-hint{margin-top:6px;border:1px solid #E7B9A4;background:#fff;color:#8A2D1C;
  border-radius:7px;padding:4px 10px;font-size:11.5px;cursor:pointer;pointer-events:auto}
.stagehint .undo-in-hint:hover{background:#FDF3F2}

@media (max-width:760px){
  /* 手机上「放电器」按钮换行后位置靠左，面板 302px 会溢出右边（实测 110px）⇒ 收窄成一列 */
  .applmenu{width:240px;top:34px}
  .amgrid{grid-template-columns:1fr}
  .amgrid button{padding:6px 8px}
  /* 浮窗 210px 时输入框里的「1200」会被截成「12」 */
  .applpop{width:240px;padding:10px}
  /* 手机本来就没有数字键 */
  .toolflash{display:none}
}

/* ==================== 手机 390 ==================== */
@media (max-width:760px){
  .side{display:none}
  .topbar{height:auto;padding:8px 12px;gap:8px;flex-wrap:wrap}
  .crumb{font-size:11.5px}
  .crumb b{font-size:13.5px}
  .crumb .tagv{font-size:9.5px}
  .seg{margin-left:0;order:4}
  .tb-btn{padding:5px 9px;font-size:11px}
  .content{grid-template-columns:1fr;padding:12px 12px 78px;gap:12px}
  .panelcol{position:static}
  .stage{height:52vh;min-height:300px}
  .stagelegend{font-size:9.5px;gap:8px;padding:5px 8px;max-width:calc(100% - 20px)}
  /* 放置态：390px 上图例只剩 11px、且已经是两行 ⇒ **图例整条让位**（实测不让位必然再挤一行）。
   * 位置没变，还是老唐指定的那个角；放产品时图例的价值最低（你要看的是落点和方向）。 */
  .stage.placement .stagelegend{display:none}
  /* 基元尺寸卡：手机改**底部抽屉** —— 390 宽的画布上放浮卡必然盖住房间（照稿 §手机 390）。
   * 抽屉压在画布底部 ⇒ 同角的图例一并让位（与放置态同一条做法）。
   *
   * ★★ 2026-09-24 实测后**压紧一版**：抽屉原本 265px / 画布 437px = **占 60.6%**，
   *    而房间中心（屏幕 y=478）**正好落在抽屉上**（`elementFromPoint` 判出来是 `#primCard`）
   *    ⇒ 手机上「放缺口 / 放洞口」点了没反应 —— 不是逻辑错，是**画布被自己的卡盖住了**。
   *    收敛手段：行距/内边距/字号整体收一档 ＋ 长说明（口径那句）手机上**不显示**（桌面照旧）＋
   *    兜底给 `max-height + 内部滚动`（极窄屏也不会顶到顶部）。
   *    ⚠️ 改完**必须重新量**：`web/tools/_probe_mobile_card.mjs` 会报「卡高/画布高 占比」和
   *      「房间中心落在谁身上」——后者必须是 `rect`（画布），不能再是 `尺寸卡（被挡）`。 */
  .stage.primOpen .stagelegend{display:none}
  .dimcard{left:9px;right:9px;top:auto;bottom:9px;width:auto;padding:8px 10px 9px;
    max-height:46vh;overflow-y:auto;border-radius:12px}
  .dimcard .hd{font-size:12px}
  .dimcard .hd span{font-size:9.5px}
  .dimcard .rw{margin-top:5px;font-size:11px;gap:6px}
  .dimcard .rw .lb{width:42px;font-size:10.5px}
  .dimcard input{width:50px;padding:4px 6px;font-size:12px}
  .dimcard .dseg button{font-size:10.5px;padding:3px 8px}
  .dimcard .dcnote{display:none}
  /* ★★ 再收一档：**「半径 R」在「缺角」下本来就是灰的**（要选「圆角」才可填）⇒ 手机上把整行收掉。
   *   181px → ~147px（画布 437px ⇒ 占比 41.3% → ~33.6%）。
   *   ⚠️ 用 `:has()` 而不是在 JS 里少画一行 —— **不支持 `:has()` 的浏览器只是"这一行还留着"（＝现状），不会坏**；
   *     而且窗口拉宽/收窄到断点时它自己会跟着变，不必等下一次重渲。
   *   ⚠️ `.dimcard` 里**只有 `#pdR` 会带 disabled**（洞口那支没有 disabled 输入）⇒ 这条不会误伤。 */
  .dimcard .rw:has(input:disabled){display:none}
  .dimcard .dcact{margin-top:7px}
  .dimcard .dcbtn{padding:7px 0;font-size:12px}
  .dimedit{width:min(214px,calc(100% - 18px))}
  .stagebar{gap:6px;font-size:11px;padding:5px 6px 5px 9px}
  .stagebar .sbdir button{padding:3px 8px}
  .stagebar .sbok{padding:5px 10px}
  .toolbar{gap:7px}
  .tg-label{width:100%}
  .canvas-card{padding:9px 9px 0}
  .kpi{flex:1 1 72px;padding:7px 9px}
  .kpi b{font-size:16px}
  .tool{padding:6px 8px;font-size:11.5px}
  .tool svg{width:13px;height:13px}
  .mobnav{display:flex;position:fixed;left:0;right:0;bottom:0;z-index:40;background:rgba(255,255,255,.97);
    border-top:1px solid var(--line);padding:6px 4px calc(6px + env(safe-area-inset-bottom))}
  .mobnav a{flex:1;text-align:center;font-size:11.5px;color:var(--muted);padding:7px 0;border-radius:9px}
  .mobnav a.on{color:var(--clay);font-weight:700;background:var(--soft)}
  /* ★ 2026-09-26 第二十三轮·追加②：底导右端两条**跨页**入口（13 谈单图 / 08 项目）。
   *   ① `.mobsep` —— 一条细分隔线，把「页内滚动」与「跳去别页」切开（不然七项连成一片，
   *      客户分不清哪个是跳走的）；
   *   ② `.jump` —— 陶土色加粗 ＋ 右上角 ↗（＝离开本页），且不参与 `.on` 高亮。 */
  .mobnav .mobsep{width:1px;flex:0 0 1px;background:var(--line);margin:5px 2px}
  .mobnav a.jump{color:var(--clay);font-weight:700;display:flex;align-items:center;justify-content:center;gap:1px}
  .mobnav a.jump i{font-style:normal;font-size:9px;opacity:.7}
  .mobnav a.jump:active{background:var(--soft)}
}
@media (max-width:1120px) and (min-width:761px){
  .content{grid-template-columns:minmax(0,1fr) 300px}
  .side{width:200px}
}

/* ==================== 报价面板 + 价格库（板块 06 · 7 号 2026-09-23 加） ==================== */
#cardQuote .qbar{display:flex;flex-wrap:wrap;gap:8px 12px;align-items:center;margin-bottom:10px}
#cardQuote .qbar .seg{margin-left:0}
#cardQuote .qopts{display:flex;flex-wrap:wrap;gap:8px 14px;align-items:center}
#cardQuote .qopts label.mini{flex:0 0 auto;gap:5px}
#cardQuote .qopts input{width:72px;min-width:0}
#cardQuote .qopts select{width:auto;padding:4px 6px;font-size:11.5px}
.qhead{display:flex;flex-wrap:wrap;gap:12px;align-items:flex-end;justify-content:space-between;
  background:linear-gradient(180deg,#FFFDFA,#FBF2E9);border:1px solid var(--line);border-radius:12px;padding:12px 14px}
.qtotal span{display:block;font-size:11.5px;color:var(--muted);margin-bottom:2px}
.qtotal b{font-size:26px;color:var(--clay);letter-spacing:-.5px}
.qtotal.merch b{font-size:21px}
.qmeta{font-size:11.5px;color:var(--muted);line-height:1.85;text-align:right}
.qmeta b{color:var(--ink)}
.qbars{margin-top:9px;display:flex;flex-direction:column;gap:5px}
.qb{font-size:11.5px;border-radius:8px;padding:6px 9px;border-left:3px solid var(--line);background:var(--sand);color:var(--muted)}
.qb.ok{border-left-color:var(--green);color:var(--green);background:#F2F8F4}
.qb.warn{border-left-color:var(--amber);color:#8A6108;background:#FEF7E8}
.qb.bad{border-left-color:var(--red);color:var(--red);background:#FDF1EF}
.qgroups{display:flex;flex-wrap:wrap;gap:8px;margin-top:11px}
.qgroup{flex:1 1 118px;min-width:0;border:1px solid var(--line);border-radius:10px;padding:8px 10px;background:#FFFDFA}
.qgroup span{display:block;font-size:11px;color:var(--muted)}
.qgroup b{font-size:15px;color:var(--ink)}
.qgroup em{display:block;font-style:normal;font-size:10.5px;color:var(--faint)}
#cardQuote h4{margin:14px 0 6px;font-size:12.5px;color:var(--ink)}
#cardQuote h4 .sub{margin-left:8px;font-weight:400;font-size:11px;color:var(--faint)}
.qtable{width:100%;border-collapse:collapse;font-size:12px;table-layout:fixed}
.qtable th{text-align:left;font-size:10.5px;color:var(--muted);font-weight:600;padding:5px 6px;background:var(--sand);border-bottom:1px solid var(--line)}
.qtable td{padding:6px;border-bottom:1px solid var(--line);vertical-align:top;overflow-wrap:anywhere}
.qtable .r{text-align:right;white-space:nowrap}
.qtable.tight td,.qtable.tight th{padding:4px 5px;font-size:11.5px}
.qtable tr.unbound td{background:#FEF7E8}
.qtable .bk{display:inline-block;font-size:9.5px;color:var(--faint);background:var(--sand);border-radius:4px;padding:0 4px;margin-left:5px}
.qtable .sp{display:inline-block;font-size:10px;color:var(--faint);margin-left:5px}
.qtable .fm{display:block;font-size:10.5px;color:var(--faint);margin-top:2px}
.qnote{margin-top:11px;font-size:11.5px;line-height:1.65;color:var(--muted);background:var(--soft);
  border-left:3px solid var(--amber);border-radius:0 8px 8px 0;padding:8px 11px}
.qnote b{color:var(--clay);margin-right:6px}
.qnote .sub{color:var(--faint);margin-top:3px}
.qfoot{margin-top:10px;font-size:11px;color:var(--faint);border-top:1px dashed var(--line);padding-top:8px}
.qempty{font-size:12px;color:var(--muted);background:var(--sand);border:1px dashed var(--line);border-radius:10px;padding:12px;line-height:1.65}
.qempty .sub{color:var(--faint);font-size:11px;margin-top:5px}
.qactions{margin-top:12px;display:flex;flex-wrap:wrap;gap:8px;align-items:center}
/* ★ 2026-09-26 第二十七轮 · 13 号 07 接线：料单卡底部的导出条 —— 与报价卡的 `.qactions` 同一排法，
 *   只是那三颗按钮长在料单卡里；`.seg`（Excel / CSV 二选一）在这里**不许**再 `margin-left:auto`
 *   （那是给 `.qbar` 用的，会把两个按钮拱到两端、中间空一大截）。 */
.bomactions{margin-top:12px;display:flex;flex-wrap:wrap;gap:8px;align-items:center}
.bomactions .seg{margin-left:0}
.pnote{font-size:11px;color:var(--faint)}
.pnote.ok{color:var(--green)}
.pnote.bad{color:var(--red)}
.pcat{margin-bottom:12px}
.pcat h4{margin:0 0 5px;font-size:12.5px;color:var(--ink)}
.pcat h4 .sub{margin-left:7px;font-weight:400;font-size:10.5px;color:var(--faint)}
.ptable{table-layout:auto}
.ptable .pin{width:64px;padding:4px 6px;font-size:11.5px;text-align:right}
.pfoot{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin-top:4px}
@media (max-width:760px){
  .qtotal b{font-size:22px}
  .qmeta{text-align:left}
  .qtable{font-size:11.5px}
  #cardQuote .qopts input{width:62px}
  .ptable .pin{width:56px}
}

/* ============================================================================
 * 第二十二轮 · 13 号（2026-09-25）—— 左栏补两项 / 电器商品库 / 宽度档位 / 45°
 * ========================================================================== */

/* ---------------- ① 左栏：11 置灰占位 ＋ 13（08）真入口 ---------------- */
/* 13 / 08 这类「点了会离开工作台去独立页」的项 —— 用品牌色 + 右上角 ↗ 说清去向 */
.lv1.newlink{color:var(--clay)}
.lv1.newlink .ico{color:var(--clay)}
.lv1.newlink:hover{background:var(--soft)}
.lv1.newlink .cnt{background:#F3E2CF;color:var(--clay)}
.lv1 .goicon{margin-left:6px;flex-shrink:0;color:var(--clay);display:grid;place-items:center}
/* ⚠️ 顺序：`.cnt` 自带 `margin-left:auto` 把尾巴推右；`.goicon` 紧跟在它后面就行。
   但 13 那行 .cnt 前面还有 badge ⇒ 只能让第一个带 auto 的占位，别重复 auto。 */
.lv1 .nm + .tagv2,.lv1 .nm + .tagnew{margin-left:2px}
/* ★★ 2026-09-25 · 13 号（实测修的）：**型号名要能省略号、别换行**。
   `.nm` 是 flex 子项，默认 `min-width:auto` ⇒ 挤不下时它**不省略、改成折行**，
   而 `.lv1` 是定高 37px ⇒ 名字会贴着上下边、行高被撑破。
   实测症状：13 那行挂了「不算料 ＋ 新页 ＋ 15 ＋ ↗」之后，名字被截成「蜂窝…」（看不见是哪个板块）。
   两处一起改：① `.nm` 允许收缩并省略；② 行尾那几颗徽标一律 `flex-shrink:0`（不许被压扁）。 */
.lv1 .nm{min-width:0;flex:0 1 auto}
.lv1 .no,.lv1 .cnt,.lv1 .role,.lv1 .tagv2,.lv1 .tagnew,.lv1 .goicon,.lv1 .lockicon{flex-shrink:0}
.lv1 .tagnew{font-size:8.5px;font-weight:700;color:#fff;background:var(--clay);border-radius:4px;padding:0 4px;white-space:nowrap}
.lv1 .tagv2{font-size:8.5px;font-weight:700;color:#8A7A66;background:#F3EEE6;border:1px solid #E4DCD0;border-radius:4px;padding:0 4px;white-space:nowrap}
/* 11 吊顶效果图预览：整块 V2（V1 项 0）⇒ 置灰、点不动。**不做假入口**（老唐明确）。 */
.lv1.soon{color:#B4A897;cursor:not-allowed}
.lv1.soon .ico{color:#C9BEB0}
.lv1.soon .nm{color:#B4A897}
.lv1.soon .cnt{background:#F5F1EA;color:#C5BAA9}
.lv1.soon:hover{background:transparent}
.lv1 .lockicon{margin-left:6px;flex-shrink:0;color:#CFC4B4;display:grid;place-items:center}
/* 13「不算料」灰标 —— 复用 .role 的形状，但**必须把自带的 `margin-left:auto` 收掉**
   （基础样式里 .role 是靠 auto 顶到最右的「仅老板」用法；13 这一行要的是紧跟型号名 ⇒ 改成 5px）。
   配色也改成中性灰（区别于「仅老板」那类橙标）。 */
.lv1 .role.nocalc{margin-left:5px;color:#7C6A5B;background:#F3EEE6;border-color:#E2DED4}

/* ---------------- ② 右栏「电器」卡：管理商品库入口 ＋ 宽度档位行 ---------------- */
.libbtn{margin-left:auto;border:1px solid var(--clay);background:var(--clay);color:#fff;
  border-radius:8px;padding:3px 10px;font-size:11px;font-weight:700;cursor:pointer;white-space:nowrap}
.libbtn:hover{background:#7A3410}
.apwrow{margin-top:10px;border-top:1px dashed var(--line);padding-top:9px}
.apwrow:empty{display:none;margin:0;padding:0;border:0}
.wlab{font-size:10.5px;color:var(--faint);margin-bottom:5px;line-height:1.5}
.wlab b{color:var(--clay)}
.wchips{display:flex;flex-wrap:wrap;gap:4px}
.wchips button{border:1px solid var(--line);background:#FFFDFA;border-radius:999px;padding:3px 10px;
  font-size:11px;color:var(--muted);cursor:pointer;font-variant-numeric:tabular-nums;line-height:1.5}
.wchips button:hover{background:var(--soft);color:var(--clay)}
.wchips button.on{background:var(--clay);border-color:var(--clay);color:#fff;font-weight:700}
/* 「当前值不在档位表里」的那一颗 —— 虚框提示「这一档是你自己填的 / 品库遗留值」 */
.wchips button.cur{background:#FFFDFA;border-style:dashed;border-color:#C9A66B;color:#8A6108;font-weight:700}
.wchips button.cur.on{background:var(--clay);border-color:var(--clay);color:#fff}

/* ---------------- ③ 型号面板 / 浮窗里的「斜 45°」与「宽度档位」 ---------------- */
.amorient button[disabled]{background:#F6F2EB;border-color:#E6DED2;color:#BEB2A2;cursor:not-allowed}
.amorient button[disabled]:hover{background:#F6F2EB;border-color:#E6DED2;color:#BEB2A2}
.applmenu .wrow,.applpop .wrow{margin-top:9px;border-top:1px dashed var(--line);padding-top:8px}
.applpop .pbtn[disabled]{background:#F6F2EB;color:#BEB2A2;cursor:not-allowed}
.applpop .pbtn[disabled]:hover{background:#F6F2EB}
.stagebar .sbdir button[disabled]{background:#F6F2EB;border-color:#E6DED2;color:#BEB2A2;cursor:not-allowed}
.stagebar .sbdir button[disabled]:hover{background:#F6F2EB;color:#BEB2A2}
/* 45° 那颗键与横/竖不同色 —— 它是「呈现层」的东西（不改算料），用虚线边提示「这个不是方向」 */
.stagebar .sbdir button[data-o="45"]{border-style:dashed}
.stagebar .sbdir button[data-o="45"].on{background:#FDF1E3;border-color:#D97706;color:#8A6108}
/* 电器列表行里的 45° 角标 */
.aprow .t45{font-size:9px;font-weight:700;color:#8A6108;background:#FDF1E3;border:1px solid #EFDCB8;
  border-radius:4px;padding:0 4px;flex-shrink:0}

/* ---------------- ④ 电器商品库（弹层，型号维表增删改启停） ---------------- */
.libmask{position:fixed;inset:0;z-index:70;background:rgba(42,33,27,.44);
  display:flex;align-items:center;justify-content:center;padding:18px}
.libmodal{background:var(--paper);border:1px solid var(--line);border-radius:14px;
  width:min(960px,100%);max-height:min(88vh,780px);display:flex;flex-direction:column;
  box-shadow:0 20px 52px rgba(42,33,27,.32)}
.libmodal .lh{display:flex;align-items:center;gap:9px;padding:12px 15px;border-bottom:1px solid var(--line);flex-shrink:0}
.libmodal .lh h3{margin:0;font-size:13.5px;color:var(--clay)}
.libmodal .lh .no{font-size:10.5px;color:var(--faint);font-weight:400}
.libmodal .lx{margin-left:auto;border:0;background:transparent;font-size:20px;color:var(--faint);
  cursor:pointer;line-height:1;padding:0 3px}
.libmodal .lx:hover{color:var(--red)}
.libmodal .lbody{padding:12px 15px 15px;overflow:auto}
.libtools{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin-bottom:10px}
.libtools .sp{margin-left:auto;font-size:11px;color:var(--faint)}
.libtools .sp b{color:var(--ink);font-variant-numeric:tabular-nums}
/* ⚠️⚠️ 必须显式把 `width` / `margin-top` 收回来。
   基础样式里有一条**全局** `.btn{width:100%;margin-top:10px;…}`（第 268 行，本来是给
   报价卡那类「整行大按钮」用的）—— `.libtools .btn` 的优先级虽然更高，但**我没声明的属性
   仍然继承**它 ⇒ 实测三颗工具键被拉成三条整行色带、还各带 10px 上边距（截图抓出来的）。
   凡是把 `.btn` 放进横向 flex 行里，都得这样复位一次。 */
.libtools .btn{width:auto;margin-top:0;border:1px solid var(--line);background:#fff;border-radius:8px;padding:6px 11px;
  font-size:11.5px;color:#54463A;cursor:pointer;white-space:nowrap}
.libtools .btn:hover{background:var(--soft)}
.libtools .btn.primary{background:var(--clay);border-color:var(--clay);color:#fff;font-weight:700}
.libtools .btn.primary:hover{background:#7A3410}
.libtools .btn[disabled]{background:#F6F2EB;color:#BEB2A2;cursor:not-allowed;border-color:#E6DED2}
.libtools .btn[disabled]:hover{background:#F6F2EB}
/* ★ 第二十三轮：库搬到服务端 ⇒ 工具条上要有一颗「我现在连的是哪份库」的牌子
 *   （已接服务端 / 只读·内置型号表）—— 不画的话，客户改不动型号时不知道是"没登录"还是"坏了"。 */
.libtools .libsign{flex-shrink:0}
.libtools .pill{font-size:9.5px;font-weight:700;border-radius:5px;padding:2px 7px;white-space:nowrap}
.libtools .pill.ok{background:#EDF6F0;color:#2f6b48;border:1px solid #CFE5D8}
.libtools .pill.warn{background:#FDF1E3;color:#9A5A11;border:1px solid #EFDCB8}
/* ★ 横幅（旧本机库迁移提示 / 未登录只读说明）—— `:empty` 时整块收掉，不留一条空带 */
#libBanner:empty{display:none}
.banner{display:flex;align-items:flex-start;gap:10px;margin-bottom:10px;font-size:11px;line-height:1.6;
  border-radius:9px;padding:9px 11px}
.banner b{color:var(--clay)}
.banner .mut{color:var(--faint)}
.banner.info{background:#FDF6EC;border:1px solid #EFDCB8;color:#6B5A49}
.banner.warn{background:#FFF8EF;border:1px solid #E9CFA8;color:#7A5B33}
.banner>span{flex:1;min-width:0}
.banner .acts{flex-shrink:0;display:flex;flex-direction:column;gap:6px;align-items:stretch}
/* ⚠️ `.btn` 的全局 `width:100%;margin-top:10px` 会渗透进弹层（第 268 行）——
 *   横幅里的按钮同样要显式复位（见上面 `.libtools .btn` 那条注释，踩过一次）。 */
.banner .go{width:auto;margin-top:0;border:1px solid var(--line);background:#fff;border-radius:8px;
  padding:5px 10px;font-size:11px;color:#54463A;cursor:pointer;white-space:nowrap}
.banner .go:hover{background:#FFFDFA}
.banner .go.solid{background:var(--clay);border-color:var(--clay);color:#fff;font-weight:700}
.banner .go.solid:hover{background:#7A3410}
.libtable{width:100%;border-collapse:collapse;font-size:11.5px}
.libtable th{text-align:left;font-size:10.5px;font-weight:600;color:var(--muted);
  padding:6px 6px;border-bottom:1px solid var(--line);white-space:nowrap}
.libtable td{padding:5px 6px;border-bottom:1px solid #F6F1E9;white-space:nowrap;vertical-align:middle}
.libtable tr.newrow td{background:#FFF8EF}
.libtable tr.offrow td{background:#FAF7F2;color:#A2937F}
.libtable td.nm{font-weight:600}
.libtable td.r{text-align:right;font-variant-numeric:tabular-nums}
.libtable td.mut{color:var(--faint)}
.libtable input[type=text],.libtable input[type=number]{width:100%;min-width:44px;border:1px solid #E3D2BC;
  border-radius:7px;padding:4px 6px;font-size:11.5px;font-variant-numeric:tabular-nums}
.libtable input.nm-in{min-width:96px}
.libtable .tag{font-size:9.5px;border-radius:4px;padding:1px 6px;background:#F3EEE6;color:#7C6A5B;white-space:nowrap}
.libtable .tag.g{background:#EDF6F0;color:#2f6b48}
.libtable .tag.y{background:#FDF1E3;color:#9A5A11;border:1px solid #EFDCB8}
.libtable .tag.gr{background:#F3EEE6;color:#8A7A66}
/* ★ 第二十三轮「来源」三态 —— 色带要各不相同，光靠文字扫不出差别：
 *   公共库＝灰蓝（平台维护、只读）· 我的＝陶土色（我自己加的）· 已自定义＝琥珀（盖住了公共库那条） */
.libtable .tag.pub{background:#EEF2F6;color:#4A5D70;border:1px solid #D8E1EA}
.libtable .tag.mine{background:#F7EDE6;color:#8C3D14;border:1px solid #EBD5C6}
.libtable .tag.copied{background:#FDF1E3;color:#8A6108;border:1px solid #EFDCB8}
/* ★ 第二十七轮 · 13 号跟 B1：`origin` 从三态变**四态** ⇒ 多一颗「来自上级」的徽标。
 *   它必须和「公共库」**看得出区别**（都是"不是我的"，但出路完全不同：
 *   公共库是平台的、上级是同一个店里我上家的），所以另给一套青绿调，
 *   并允许换行（带上级店名，可能比其它徽标长 —— `white-space:nowrap` 会把它挤爆列宽）。 */
.libtable .tag.inh{background:#EAF4F1;color:#2F6B5E;border:1px solid #CFE6DF;
  white-space:normal;max-width:170px;line-height:1.35;display:inline-block}
.libtable .ops a{color:var(--clay);font-size:11px;margin-right:9px;cursor:pointer;white-space:nowrap}
.libtable .ops a.danger{color:#B04A19}
.libtable .ops a.mut{color:var(--faint);cursor:default;margin-right:9px}
/* ★ 置灰的操作（公共库只读 / 未登录）—— **必须画出来**并带 title 说明为什么，
 *   不能"不画"（不画＝客户以为漏做了；画了不说明＝点了没反应）。 */
.libtable .ops a.gray{color:#C3B8AA;cursor:not-allowed}
/* 「恢复公共库版本」—— 只在覆盖行上出现，用"回退"色（绿）区别于"删除"色（红） */
.libtable .ops a.rest{color:#2f6b48;font-weight:600}
.libnote{margin-top:11px;font-size:11px;color:var(--muted);line-height:1.65;background:var(--soft);
  border:1px solid #EFE0CE;border-radius:9px;padding:9px 11px}
.libnote b{color:var(--clay)}
/* ★ 第二十三轮：脚注 —— 「这一份存在服务端、归门店共用」这句话必须常驻，
 *   否则客户会按"我自己的电脑"理解（一期那份 localStorage 库正是这么被误解的）。 */
.libfoot{margin-top:9px;font-size:10.5px;color:var(--faint);line-height:1.6;text-align:right}
.libfoot b{color:var(--muted)}
.libempty{font-size:12px;color:var(--muted);background:var(--sand);border:1px dashed var(--line);
  border-radius:10px;padding:14px;text-align:center}
/* 手机：弹层铺满、表格横向滚动（列多，390 放不下 —— 横滚比截断好） */
@media (max-width:760px){
  .libmask{padding:0;align-items:flex-end}
  .libmodal{width:100%;max-height:92vh;border-radius:14px 14px 0 0}
  /* ⚠️ 2026-09-25 修：原来这儿写的是 `.libbody`（拼错了，少个 moda-）—— **一个字都没生效**。
     正确的容器类是 `.lbody`。两个作用都要：① `flex:1;min-height:0` 让表体在抽屉里自己滚
     （不加的话长表会把 92vh 的抽屉顶破）；② `overflow:auto` 让 640px 的宽表在抽屉内**横滚**
     （不加的话「宽度档位 / 启用 / 操作」三列在手机上够不到 ⇒ 改不了、停用不了）。 */
  .libmodal .lbody{flex:1;min-height:0;overflow:auto;padding:10px 11px 14px}
  .libtable{min-width:640px}
  /* 横幅在手机上改成竖排（横排会把两颗按钮挤出屏外，390 实测） */
  .banner{flex-direction:column;align-items:stretch}
  .banner .acts{flex-direction:row;justify-content:flex-end}
  .libfoot{text-align:left}
  .apwrow .wlab{font-size:10px}
  .wchips button{padding:3px 9px;font-size:10.5px}
}
