:root{
  --brand:#1f3b57; --brand-2:#2d5876; --accent:#e8823a;
  --bg:#f4f6f9; --panel:#fff; --line:#e3e8ef; --line-2:#eef2f6;
  --text:#1b2b3a; --muted:#6b7f92; --muted-2:#94a7b8;
  --ok:#0f9960; --warn:#e8823a; --danger:#d92b2b;
  --radius:12px; --radius-sm:8px;
  --shadow:0 1px 3px rgba(16,32,48,.06),0 4px 16px rgba(16,32,48,.05);
  --mono:'Lato','SF Mono',Consolas,'Roboto Mono',Menlo,monospace;
  /* sticky 表头高度：品类行冻结要严丝合缝地贴在它下面，两边共用这一个值 */
  --thead-h:34px;
  /* 单个品类行的高度。品类行是**逐行累加冻结**的：
     第 i 个品类行冻结在 top = --thead-h + i × --grp-h，
     所以 OA 钉住不动、TLF 顶上来时停在 OA 下面，两行同时在屏幕上（以此类推）。
     渲染后 JS 会用实测行高覆盖这个值，避免行高微差累积错位。 */
  --grp-h:30px;
}
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--text);
  font-family:-apple-system,'Segoe UI','Microsoft YaHei',sans-serif;font-size:14px}
a{color:var(--brand-2);text-decoration:none}
.hidden{display:none!important}

/* ---------------- 登录门 ---------------- */
.gate{position:fixed;inset:0;background:linear-gradient(160deg,#1f3b57,#2d5876);
  display:flex;align-items:center;justify-content:center;z-index:50}
.gate-card{width:380px;background:#fff;border-radius:16px;padding:30px 28px;box-shadow:0 20px 60px rgba(0,0,0,.25)}
.gate-card h1{margin:0 0 4px;font-size:20px}
.gate-card .sub{margin:0 0 22px;color:var(--muted);font-size:12.5px}
.field{margin-bottom:14px}
.field label{display:block;font-size:12.5px;color:var(--muted);margin-bottom:5px}
.field input,.field select{width:100%;padding:9px 11px;border:1px solid var(--line);
  border-radius:var(--radius-sm);font-size:13.5px;font-family:inherit;background:#fff}
.field input:focus,.field select:focus{outline:none;border-color:var(--brand-2)}
.btn{border:1px solid var(--line);background:#fff;color:var(--text);padding:8px 14px;
  border-radius:var(--radius-sm);cursor:pointer;font-size:13px;font-family:inherit}
.btn:hover{border-color:var(--brand-2);color:var(--brand-2)}
.btn.primary{background:var(--brand-2);border-color:var(--brand-2);color:#fff;width:100%;padding:10px;font-size:14px}
.btn.primary:hover{background:var(--brand);color:#fff}
.btn.primary:disabled{opacity:.6;cursor:default}
.btn.danger{color:var(--danger);border-color:#f3c9c9}
.btn.danger:hover{background:#fdf1f1;border-color:var(--danger)}
.btn.sm{padding:4px 9px;font-size:12px}
.err{color:var(--danger);font-size:12.5px;min-height:18px;margin:6px 0}
.note{color:var(--muted);font-size:12px;line-height:1.7;margin-top:14px}

/* ---------------- 外壳 ---------------- */
header{background:var(--brand);color:#fff;padding:0 22px;height:56px;display:flex;align-items:center;gap:16px}
header .title{font-size:16px;font-weight:600}
header .badge{background:rgba(255,255,255,.15);border-radius:20px;padding:3px 10px;font-size:11.5px}
header .spacer{flex:1}
header a.btn{background:rgba(255,255,255,.12);border-color:rgba(255,255,255,.25);color:#fff}
header a.btn:hover{background:rgba(255,255,255,.22);color:#fff}
/* 标题栏的同步按钮（Jason 2026-09-19；第五轮改）：
   dirty = 有改动未同步（橙色）；ok = 已同步到 ERP（绿色）。
   🔴 两种状态**都必须可点**（点了就跑「立即应用到 ERP」）——
      第三轮把 ok 态写成 disabled + cursor:default，浏览器吞掉点击，
      Jason 看到的就是「能按，但点了没反应」。所以这里 cursor 一律 pointer、
      也不再写 :disabled 的样式。 */
header .btn.sync-btn{border-radius:20px;padding:5px 13px;font-size:12px;font-weight:600;
  border:1px solid transparent;cursor:pointer;transition:background .18s,border-color .18s,color .18s}
header .btn.sync-btn.dirty{background:var(--accent);border-color:var(--accent);color:#fff;
  box-shadow:0 2px 8px rgba(232,130,58,.35)}
header .btn.sync-btn.dirty:hover{background:#d9762e;border-color:#d9762e;color:#fff}
header .btn.sync-btn.ok{background:var(--ok);border-color:var(--ok);color:#fff}
header .btn.sync-btn.ok:hover{background:#0d8a56;border-color:#0d8a56;color:#fff}
.user{font-size:12.5px;opacity:.9}
nav{background:#fff;border-bottom:1px solid var(--line);padding:0 22px;display:flex;gap:4px}
nav button{background:none;border:none;padding:12px 16px;font-size:13.5px;color:var(--muted);
  cursor:pointer;border-bottom:2px solid transparent;font-family:inherit}
nav button.active{color:var(--brand-2);border-bottom-color:var(--accent);font-weight:600}
main{padding:20px 22px 60px;max-width:1400px}
section{display:none}
section.active{display:block}

.panel{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);
  box-shadow:var(--shadow);margin-bottom:18px;overflow:hidden}
.panel-head{padding:13px 16px;border-bottom:1px solid var(--line-2);display:flex;align-items:center;
  gap:10px;flex-wrap:wrap}
.panel-head h3{margin:0;font-size:14.5px}
.panel-head .spacer{flex:1}
.panel-body{padding:16px}

/* 2026-09-14：原「分段按钮（集团客户 / 账号明细）」的 .pill-tabs 规则已删 ——
   Jason 取消集团口径后后台再没有口径切换控件（前台 ERP 的 .pill-tabs 在 index.html
   自己的样式里，与本文件无关）。要再加同类切换时照抄前台那套即可。 */

/* ---------------- 价格体系两页的多行表头（V3.5 · Jason 2026-09-19 第九轮定稿） ----------------
     第一行（控件行）：客户名称▾ | 进货价格体系▾ | ＋新建进货价格体系 | 删除 |  ←按内容宽度
                       …spacer…（把下面两个顶到最右）→ ＋ 新增产品 | 保存价格体系   ←右对齐
     第二行（工具行，视觉降级）：搜索 / 重新载入 / 批量删除 / 管理所有体系 / 重命名 / 导出 / 导入
   ⚠️ V3.4 时这里是**三行**（第一行四个控件、第二行「新增产品 + 保存价格体系」、
      第三行工具）。第九轮 Jason 原话：「把『新增产品』『保存价格体系』挪到第一行
      最右边右对齐」→ 第二行整行删掉，两个按钮并进第一行，靠 .spacer{flex:1} 顶到右端。
      所以：`shoot_demo.js` 里数 `.ph-row` 的地方从 3 改成 **2**（它按这个判版式）。
   ⚠️ Jason 同轮还说「客户名称、销售价格体系、新建价格体系、删除 这一行均分一点
      这样不好看」—— 处理方式是**不给这一行加 justify-content:space-between**
      （四个控件保持紧凑左对齐、按内容宽度），难看的那半边交给右对齐的两个按钮去占。
      即：左端一组、右端一组，中间留白由 spacer 吃掉。
   ⚠️ .panel-head 默认是**行** flex（h3 与按钮挤在同一行）——
      这里必须显式改成**列**，每个 .ph-row 才各占一行（.ph-stack）。
   ⚠️ 工具行用 spacer 顶到右边 + 字色降级，是为了让 Jason 画的那行保持「主操作只有六个 /
      两个」的干净观感，同时不把「导出模板 / 导入价格 / 批量删除 / 搜索」这些**真在用的**
      功能删掉（他这轮的版式图里没画，但删了就是功能倒退）。 */
.panel-head.ph-stack{display:flex;flex-direction:column;align-items:stretch;gap:9px}
.ph-row{display:flex;align-items:center;gap:9px;flex-wrap:wrap;min-height:30px}
/* ⚠️ .ph-row 里的按钮一律**按内容宽度**。
   `.btn.primary` 出厂带 `width:100%`（给表单里的主按钮独占一行用的），
   在 .ph-row 这种「一行控件」里会把自己撑成整行、把后面的按钮（比如「删除」）挤到下一行 ——
   Jason 的版式要求「＋ 新建进货价格体系」与「删除」**同一行并排**。
   ⚠️ 用三个类（.panel-head .ph-row .btn）是刻意的：`.btn.primary` 是两个类，
   同分靠书写顺序定胜负太脆弱，多一个类把优先级明确压过它。
   （2026-09-19 第八轮截图上肉眼抓到的：只给「新增产品 / 保存价格体系」贴了内联
    style="width:auto" 当补丁，漏了新建/删除两个，第一行就断成了三段。
    第九轮这两个按钮也搬进 .ph-row 了，同一条规则继续兜住它们。） */
.panel-head .ph-row .btn{width:auto}
/* 控件行里的占位弹簧：把「＋ 新增产品 / 保存价格体系」顶到整行最右（第九轮）。
   注：`.panel-head .spacer{flex:1}` 已在上方定义，这里不重复 —— 只留痕说明
   它在 .ph-row 里同样生效（工具行早就在用同一个类）。 */
.ph-lab{font-size:12.5px;color:var(--muted);white-space:nowrap;font-weight:600}
/* 体系下拉：放在客户下拉右边，窄一档（体系名一般比「编号 - 客户名」短） */
.ps-sys{min-width:200px;max-width:330px}
/* 工具行：与上面两行隔一条虚线，按钮与搜索框整体降一档不抢眼 */
.ph-row.tools{margin-top:1px;padding-top:9px;border-top:1px dashed var(--line)}
.ph-row.tools .btn{color:var(--muted)}
.ph-row.tools .btn:hover{color:var(--brand-2);border-color:var(--brand-2)}
.ph-row.tools .btn.primary,.ph-row.tools .btn.danger{color:inherit}
.ph-row.tools .btn.danger:hover{color:var(--danger)}

.cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:12px;margin-bottom:18px}

.card{background:#fff;border:1px solid var(--line);border-radius:var(--radius);padding:14px 16px}
.card .k{color:var(--muted);font-size:12px}
.card .v{font-size:22px;font-weight:700;font-family:var(--mono);margin-top:4px}

table{width:100%;border-collapse:collapse;font-size:13px}
/* ⚠️ sticky 表头：必须带不透明背景 + z-index，否则滚轮下滚时表体内容会
   「透过」表头叠在一起（Jason 2026-09-13 反馈的问题）。
   同时把 header 的 border 换成 inset box-shadow —— border 在 sticky 元素上
   滚动时会跟着走，露出下面一行。 */
thead th{background:#f4f7fa;text-align:left;padding:9px 10px;font-weight:600;color:var(--muted);
  font-size:12px;white-space:nowrap;position:sticky;top:0;z-index:3;
  height:var(--thead-h);
  box-shadow:inset 0 -1px 0 var(--line),0 2px 4px -2px rgba(16,32,48,.10)}
tbody td{padding:8px 10px;border-bottom:1px solid var(--line-2);vertical-align:middle}
tbody tr:hover{background:#fafbfc}
td.num,th.num{text-align:right;font-family:var(--mono)}
.mono{font-family:var(--mono)}

/* 可排序表头：点击三态循环（默认 → 升 → 降 → 默认） */
thead th.sortable{cursor:pointer;user-select:none}
thead th.sortable:hover{color:var(--brand-2)}
thead th.sortable.sorted{color:var(--brand-2)}
thead th .sort-i{font-style:normal;font-size:9px;margin-left:4px;opacity:.35}
thead th.sortable.sorted .sort-i{opacity:1;color:var(--accent)}
/* 编号：与 ERP 前台 td.code-txt 完全一致（Lato + 品牌蓝加粗 + .02em 字距）。
   Lato 本身不含中文字形，中文会回落到后面的等宽字体，表现与前台一致。 */
.code,.mono.code{
  font-family:'Lato',var(--mono);font-weight:700;color:var(--brand-2);
  letter-spacing:.02em;white-space:nowrap;
}
/* 产品编号：宋体（Jason 2026-09-12 指定，与前台产品明细的字形保持一致）
   —— 客户编号仍是上面的 Lato（.code），两者故意区分开。 */
td.code.sim,th.code.sim{font-family:'SimSun','宋体',serif;font-weight:700;
  color:#1b2b3a;letter-spacing:0}
/* 供应商系列分组行：可点击折叠 + **逐行累加冻结在表头下方**
   （Jason 2026-09-13：滚轮下滚时 OA 品类行钉住不隐藏，滚到 TLF 时 TLF 排在
     OA 下面、OA 继续留在屏幕上 —— 就是「两行冻结」，再往下以此类推。
     实现方式：每个品类行带 style="--_gi:i"（它在表格里的序号），
     top 用 calc 算成 --thead-h + i × --grp-h，于是序号越大钉得越靠下，
     后面的品类行永远停在前面那行的正下方，互不覆盖。）
   ⚠️ z-index 必须 **小于** 表头的 3，否则会盖住表头；
   ⚠️ sticky 元素必须有**不透明背景**，否则下面的产品行会透过它显示。 */
tr.grp td{background:#f2f7fb;border-bottom:1px solid var(--line);
  padding:7px 10px;font-size:12px;font-weight:650;color:var(--brand);letter-spacing:.02em;
  cursor:pointer;user-select:none;white-space:nowrap;
  height:var(--grp-h);
  position:sticky;top:calc(var(--thead-h) + var(--_gi,0) * var(--grp-h));z-index:2;
  box-shadow:0 2px 5px -2px rgba(16,32,48,.12)}
tr.grp td:hover{background:#e8f1f7}
tr.grp td .n{font-weight:400;color:var(--muted);margin-left:8px}
tr.grp td .fold{display:inline-block;width:16px;color:var(--brand-2);font-size:10px;vertical-align:1px}
tr.grp td .gt{margin-left:2px}
/* 折叠后隐藏该组产品行 */
tr.grp.folded + tr.prod-row{/* 由 JS 直接控制 style.display */}

/* 客户管理页的「集团标题行」（V3.2 · 2026-09-19 Jason：ENN / SF 各收拢成一组，带展开箭头）。
   非 sticky —— 客户表本身不冻结表头，这里跟着普通行滚即可。 */
tr.cgrp td{background:#f2f7fb;border-bottom:1px solid var(--line);border-top:1px solid var(--line);
  padding:8px 10px;font-size:12.5px;font-weight:700;color:var(--brand);letter-spacing:.02em;
  cursor:pointer;user-select:none;white-space:nowrap}
tr.cgrp td:hover{background:#e8f1f7}
tr.cgrp td .fold{display:inline-block;width:16px;color:var(--brand-2);font-size:10px;vertical-align:1px}
tr.cgrp td .gt{margin-left:2px}
tr.cgrp td .n{font-weight:400;color:var(--muted);margin-left:8px}

/* 价格体系页「当前价格体系」提示条：加大字体，明显一点 */
#pTableToolbar{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:12px}
.big-sys{font-size:15px;font-weight:700;color:var(--brand)}
/* ⚠️ 两个选择器都要写：HTML 里「当前客户…」那行是 `<div class="big-sys who">`
   （同一个元素两个类），后代写法 .big-sys .who 是匹配不上的。 */
.big-sys.who,.big-sys .who{font-weight:400;font-size:13px;color:var(--muted)}

/* ---------------- 价格体系两页（V3.2 · 2026-09-19） ----------------
   Jason 把原来「一个客户一个页面、进货价销售价混在一起」拆成
   【进货价格体系】+【销售价格体系】两个板块，两页各自有自己的客户下拉。 */
/* ① 客户下拉：比默认 select 大一号、圆角，方便一眼看到选的是哪家 */
.ps-cust{min-width:230px;max-width:340px;padding:7px 9px;font-size:12.5px;
  border:1px solid var(--line);border-radius:8px;background:#fff;font-family:inherit;
  color:var(--fg,#1b2b3a)}
.ps-cust:focus{outline:none;border-color:var(--brand-2);box-shadow:0 0 0 3px rgba(31,86,132,.10)}
/* ② 体系切换条 .pill-tabs —— **2026-09-19 第八轮（V3.4）已整套删除**。
   Jason 把这页的版式画成「客户名称[下拉]  进货价格体系[下拉]  新建进货价格体系  删除」，
   于是「一个体系一个按钮」的切换条换成了真正的 <select>（#qSys / #pSys，样式沿用 .ps-cust）。
   后端 / 前端相关代码（renderSysTabs、PPAGE[..].slots、index.html 的 #costSlots/#pSlots）
   一并删干净；这里留痕，避免下次有人以为「切换条还在，只是没渲染」。 */
/* 长文本（收货地址）单行省略，悬停看全 */
td.ell{max-width:220px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* 单行不折行（Jason 2026-09-13：客户编号 / 客户名称「每个信息一行，不要两行」；
   2026-09-19 第八轮追加电话「135 6603 3116」—— 带空格的号码会在空格处折行）：
   客户名称「宁波新奥新瑞燃气集团有限公司」以前被挤成两行，把整行撑高。
   加 .nw 后永不折行，列不够宽时由 .tbl-wrap 横向滚动兜底。
   ⚠️ 长文本列（收货地址）仍用 .ell 省略号，两者不要混用。 */
td.nw,th.nw{white-space:nowrap}
.tbl-wrap{max-height:62vh;overflow:auto}
/* 价格体系表列多（12 列），窄屏时给个下限，避免数字列被压到换行 */
#pTable{min-width:1080px}
/* 客户表列宽下限：10 列（V3.4 删掉「执行价格」列）单行不折行（.nw）后按实测可用宽倒推
   —— 见 admin.js renderCustomers 上的注释。列宽合计 1094，定成 1090（略小于 main 内容区的
   1322），保证不折行的同时**不出横向滚动条**。 */
#cTable{min-width:1090px}
#sTable{min-width:1080px}
.tag{display:inline-block;padding:1px 7px;border-radius:10px;font-size:11px;background:#eef2f6;color:var(--muted)}
.tag.src{background:#e8f1f7;color:var(--brand-2)}
.tag.new{background:#eaf7f0;color:var(--ok)}
.tag.del{background:#fdecec;color:var(--danger)}
tr.deleted{opacity:.5;text-decoration:line-through}

/* 2026-09-13 Jason：价格输入框缩短（110 → 74px），让价格体系整张表一行放得下 */
input.inp{padding:5px 6px;border:1px solid var(--line);border-radius:6px;font-size:12.5px;
  font-family:var(--mono);width:74px;text-align:right}
input.inp:focus{outline:none;border-color:var(--brand-2)}
input.inp.dirty{border-color:var(--accent);background:#fff8f2}
.search{padding:7px 11px;border:1px solid var(--line);border-radius:var(--radius-sm);
  font-size:13px;width:260px;font-family:inherit}
select.sel{padding:7px 10px;border:1px solid var(--line);border-radius:var(--radius-sm);
  font-size:13px;font-family:inherit;max-width:360px}

/* 2026-09-16 第 27 轮：客户列表右上角「显示已删除（N）」开关 */
.chk-line{display:inline-flex;align-items:center;gap:6px;font-size:13px;color:var(--muted);
  cursor:pointer;user-select:none;white-space:nowrap}
.chk-line input{width:15px;height:15px;margin:0;cursor:pointer;accent-color:var(--primary)}
.chk-line:hover{color:var(--text)}

/* ---------------- 列对齐下拉（combo） ---------------- */
/* <select> 的 option 没法分栏，编号/名称/体系挤成一团看不清，所以自己画一个面板。
   ⚠️ 2026-09-13 Jason：价格体系的下拉框字体要「明显一点」，所以整体放大到 15px，
   行高、内边距和面板列宽也跟着放大，选中后一眼就能看清体系名与客户名。 */
.combo{display:inline-flex;align-items:center;justify-content:space-between;gap:10px;
  min-width:280px;padding:9px 12px;border:1px solid var(--line);border-radius:var(--radius-sm);
  background:#fff;cursor:pointer;font-size:15px;font-weight:600;color:var(--brand);
  user-select:none;font-family:inherit}
.combo:hover{border-color:var(--brand-2)}
.combo:focus{outline:none;border-color:var(--brand-2)}
.combo .cv{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.combo .cv.ph{color:var(--muted-2);font-weight:400}
.combo .cx{color:var(--muted);font-size:11px}
.cbo-panel{position:fixed;z-index:90;background:#fff;border:1px solid var(--line);
  border-radius:var(--radius-sm);box-shadow:0 14px 36px rgba(16,32,48,.18);overflow:hidden}
.cbo-panel .chead,.cbo-panel .crow{display:flex;align-items:center;gap:8px;padding:8px 12px}
.cbo-panel .chead{background:#f4f7fa;color:var(--muted);font-size:12px;font-weight:600;
  border-bottom:1px solid var(--line-2)}
.cbo-panel .cbody{max-height:340px;overflow:auto}
.cbo-panel .crow{cursor:pointer;border-bottom:1px solid var(--line-2);font-size:13.5px}
.cbo-panel .crow:hover{background:#f2f7fb}
.cbo-panel .crow.on{background:#e8f1f7;color:var(--brand-2);font-weight:600}
.cbo-panel .crow span,.cbo-panel .chead span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cbo-panel .ccode{font-family:'Lato',var(--mono);font-weight:700;color:var(--brand-2)}
.cbo-panel .cempty{padding:14px;color:var(--muted);font-size:13px}
.cbo-panel .cfoot{padding:8px 12px;border-top:1px solid var(--line-2);background:#fafbfc}

/* ---------------- 弹窗 / 提示 ---------------- */
.mask{position:fixed;inset:0;background:rgba(16,32,48,.45);display:flex;align-items:center;
  justify-content:center;z-index:60}
/* Jason 2026-09-16：弹窗改成 标题/内容/页脚 三段式——内容区自己滚动，
   页脚（取消 / 保存）**固定钉在底部**，字段多的时候也不会把保存按钮顶出屏幕。 */
.modal{background:#fff;border-radius:var(--radius);width:520px;max-width:92vw;max-height:88vh;
  display:flex;flex-direction:column;overflow:hidden;box-shadow:0 20px 60px rgba(0,0,0,.3)}
.modal h3{margin:0;padding:15px 18px;border-bottom:1px solid var(--line-2);font-size:15px;flex:0 0 auto}
.modal .body{padding:16px 18px;overflow:auto;flex:1 1 auto;min-height:0}
.modal .foot{padding:12px 18px;border-top:1px solid var(--line-2);display:flex;gap:8px;justify-content:flex-end;flex:0 0 auto}
.modal .grid{display:grid;grid-template-columns:1fr 1fr;gap:12px}
#toast{position:fixed;right:20px;bottom:20px;display:flex;flex-direction:column;gap:8px;z-index:80}
.toast{background:var(--brand);color:#fff;padding:10px 16px;border-radius:var(--radius-sm);
  box-shadow:var(--shadow);font-size:13px;max-width:440px}
.toast.ok{background:var(--ok)}
.toast.bad{background:var(--danger)}
pre.log{background:#0f1b26;color:#cfe3f0;padding:14px;border-radius:var(--radius-sm);
  font-size:12px;line-height:1.6;max-height:340px;overflow:auto;font-family:var(--mono);white-space:pre-wrap}
.warn-box{background:#fff8f2;border:1px solid #f6d9c4;color:#8a4b17;padding:10px 14px;
  border-radius:var(--radius-sm);font-size:12.5px;line-height:1.7;margin-bottom:14px}
.info-box{background:#f2f7fb;border:1px solid #d6e6f2;color:#254e6b;padding:10px 14px;
  border-radius:var(--radius-sm);font-size:12.5px;line-height:1.7;margin-bottom:14px}

/* ==== 2026-09-20 第十三轮（Jason）：价格体系按钮同尺寸 + 下拉定宽 + 同步按钮配色 ==== */
/* ① 两页价格体系的**行内按钮一律同一个尺寸**（颜色仍按各自语义走：primary 深蓝 / danger 红 / 工具行灰）。
      Jason：「四个按钮大小一致，颜色样式保持现状，大小与『重新载入』一致」。
      ⚠️ 上一版只写 width:auto —— 「不位移」做到了，但四个按钮宽度各不相同
         （实测 151 / 56 / 99 / 108）**不符合「大小一致」**。这一版改成**固定宽度 152px**：
         最长的「＋ 新建进货价格体系」量出来 151px，留 1px 余量 ⇒ 既不截断、也不再随内容变宽。
      ⚠️ 必须压过两条同权重出厂规则，所以选择器带上页 id（id 恒压类，不靠书写顺序）：
         `.btn.sm{padding:4px 9px;font-size:12px}`
         `.btn.primary{padding:10px;font-size:14px;width:100%}` */
#tab-costsys .ph-row .btn,
#tab-products .ph-row .btn{
  width:152px;min-width:152px;max-width:152px;
  padding:8px 10px;font-size:13px;line-height:1.5;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
/* ② 客户名称 / 价格体系两个下拉框**定宽**：它们的宽度本来跟着选中项文字长短变，
      一改选就把右边那排按钮整体推走 —— Jason：「不要因为客户名称、进货价格体系的选择
      导致长度变化而发生位移」。定宽后整行版式对任何选中项都不动。 */
#tab-costsys .ph-row .ps-cust,
#tab-products .ph-row .ps-cust{width:238px;max-width:238px}

/* 同步与备份（Jason 2026-09-20 第十二轮）：立即应用到 ERP = 绿色；仅备份当前数据 = 橙色 */
.btn.ok{background:var(--ok);border-color:var(--ok);color:#fff}
.btn.ok:hover{background:#0d8a56;border-color:#0d8a56;color:#fff}
.btn.warn{background:var(--warn);border-color:var(--warn);color:#fff}
.btn.warn:hover{background:#d9762e;border-color:#d9762e;color:#fff}
