/* ============ SHGANI 项目管理系统 样式 ============ */
:root{
  --green:#01276c; --green-d:#001a4a; --green-l:#e6ecf5;
  --bg:#f4f6f8; --card:#fff; --line:#e4e8ec; --tx:#22303c; --tx2:#68788a;
  --red:#d94f43; --orange:#e8933c; --blue:#2f7de0; --purple:#8260c8;
  --acc:#2563eb; /* v4.16 调：消息中心等需要强调色/自身气泡背景 */
  --radius:12px; --shadow:0 1px 3px rgba(20,40,60,.08);
  /* ★ v6.83⑬（老板 10-11 01:2x）补一批「历史遗留、从未定义」的变量 ——
     根因：列头筛选按钮「已筛选」态 = `background:var(--ac);color:#fff`，
     而 `--ac` 全站从未定义（正主是 `--acc`）⇒ 背景回退 transparent、
     白字压在浅灰表头(#f7f9fa)上 ⇒ **看不见**（＝老板说的"应用筛选后按钮消失了"）。
     以下均为「无兜底 + 未定义」，共 80 处调用点；此处一处补齐，不改调用点：
       --ac  43 处（强调色：文字/左边条/筛选按钮底）  → --acc
       --tx1 17 处（主文字，配 font-weight:600）      → --tx
       --tx3  9 处（三级灰小字）                      → #98a2ae
       --bg2  4 处（次级背景：汇总行/表头）           → #f7f9fa
       --bd   3 处（边框/虚线分隔）                   → --line
       --pri  2 处（主色边框）                        → --green
       --err  2 处（错误提示文字）                    → --red */
  --ac:var(--acc); --tx1:var(--tx); --tx3:#98a2ae; --bg2:#f7f9fa;
  --bd:var(--line); --pri:var(--green); --err:var(--red);
}
/* ★ 2026-10-24（老板 20:1x：「运营中心换一个色系，和业务中心分开」）：
   两个中心共用一份 styles.css，光靠图标分不出来 —— 这里按入口换主题色。
   业务中心 = 默认的深蓝（不动）；运营中心 = **墨绿**（后勤气质，也和蓝的明显区分）。
   · 只覆盖几个主题变量，其余样式全部复用；真正生效靠 body.dd-ops（下面 app.js 末尾按 DD_APP 加类）。
   · 侧栏/底栏那些写死 #001a4a 的地方已统一改成 var(--green-d)，所以改一个变量整条侧栏就变色。 */
/* ★ 2026-10-24 改（老板 20:17：「会客中心就是绿色」）——
   运营中心原先给的墨绿跟会客中心撞了，换成**咖啡棕**：
   业务中心深蓝、会客中心墨绿、运营中心咖啡，三色互不相近。 */
/* ★ 2026-10-24 再改（老板 20:31：「这个颜色换一个淡一点的吧，有没有马卡龙色系」）：
   运营中心 = **马卡龙藕荷紫**（侧栏淡紫底 #e8e0f5 + 深色字），比咖啡亮得多。
   浅底侧栏必须把原来为深底设计的白系文字一并换成深色，否则看不清 —— 下面整组覆盖。 */
/* ★ 2026-10-24 再调（老板 20:40：「运营中心的紫色不行，看不清」）：
   上一版侧栏淡紫(#e8e0f5)配中紫字(#4a3f68)对比不够，菜单看着发灰。
   这一版三处一起加深：侧栏底 #dcd0ef、菜单字 #5a4520、主色 #5f4b8c（按钮上白字更清楚），
   分组标题也从浅紫灰提到 #7a6134。马卡龙的感觉保留，但字看得清了。 */
body.dd-ops{
  --green:#a8813f;      /* 主色（燕麦卡其） */
  --green-d:#f0e7d6;    /* 侧栏底色（燕麦卡其淡底） */
  --green-l:#f8f2e6;    /* 浅底/筛选条 */
  --blue:#b08a4a;
  --acc:#a8813f;
}
body.dd-ops .sidebar{color:#5a4520}
body.dd-ops .sidebar .brand{color:#4a3818;border-bottom-color:rgba(120,95,40,.18)}
body.dd-ops .sidebar .brand b{color:#4a3818}
body.dd-ops .sidebar .brand small{color:#7a6134}
body.dd-ops .sidebar .brand .dd-brand-name{color:#4a3818}
body.dd-ops .sidebar .brand .dd-brand-sub{color:#7a6134}
body.dd-ops .nav .grp{color:#7a6134;font-weight:600}
body.dd-ops .nav a{color:#5a4520}
body.dd-ops .nav a:hover{background:rgba(150,115,55,.18);color:#4a3818}
body.dd-ops .nav a.on{background:var(--green);color:#fff;font-weight:600}
body.dd-ops .sidebar .foot{color:#7a6134;border-top-color:rgba(120,95,40,.18)}
body.dd-ops .sidebar .foot b{color:#4a3818}
body.dd-ops .sidebar .foot .ver{color:#7a6134}
body.dd-ops .sidebar .foot .out{color:#9c3b33}

/* ★ 2026-10-25（老板 07:58：「左下角的个人中心看不清」）：
   侧栏整体换成燕麦卡其浅底后，**个人中心那一块还是原来深底的配色**
   （名字 #eaf3ed 近白、副标 #8fa3bb 灰蓝、退出按钮半透明白边）⇒ 落在浅底上几乎看不见。
   这里逐个换成深字 + 卡其描边。 */
body.dd-ops .sidebar .foot .who-txt b.who-line{color:#4a3818}
body.dd-ops .sidebar .foot .who-txt s{color:#7a6134}
body.dd-ops .sidebar .foot .who-meta .verlink{color:#8a7248}
body.dd-ops .sidebar .foot .who-meta .verlink:hover{color:#4a3818}
body.dd-ops .sidebar .foot .who-av{background:rgba(168,129,63,.18);color:#4a3818;border-color:rgba(168,129,63,.55)}
body.dd-ops .sidebar .foot .logout-btn{background:rgba(120,95,40,.10);border-color:rgba(120,95,40,.26);color:#5a4520}
body.dd-ops .sidebar .foot .logout-btn:hover{background:rgba(190,60,45,.16);border-color:rgba(190,60,45,.42);color:#8c2f22}
body.dd-ops .sidebar .foot .st-on{color:#1d7350;background:rgba(29,115,80,.14)}
body.dd-ops .sidebar .foot .st-off{color:#a33a2c;background:rgba(163,58,44,.14)}
body.dd-ops .sidebar .foot .st-run{color:#1e5f8a;background:rgba(30,95,138,.14)}
body.dd-ops .sidebar .foot .st-wait{color:#8a6410;background:rgba(138,100,16,.14)}

/* 2026-10-24 撤销：老板「业务中心还是用之前的蓝色」⇒ 上面那套 body.dd-biz 雾霾蓝已移除，业务中心回到默认深蓝。 */
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:"PingFang SC","Microsoft YaHei",system-ui,sans-serif;background:var(--bg);color:var(--tx);font-size:14px}
a{color:var(--green);text-decoration:none}
h1,h2,h3{font-weight:600}

/* ===== v5.05b 需求⑤：手机端界面「定型」—— 不许手势放大缩小 =====
   老板原话：「手机端界面要定型，不能随意放大缩小」。
   这一条必须**三处一起做**，缺哪一条都会在某个浏览器内核上漏掉：
     ① index.html 的 viewport 补 `maximum-scale=1, user-scalable=no`
        —— 安卓 Chrome / 微信内置浏览器直接生效；
     ② 这里禁双击缩放与捏合（`touch-action`）—— **iOS 10+ 的 Safari 会刻意忽略
        `user-scalable=no`**（无障碍考虑，允许用户放大），只有 touch-action 拦得住双击，
        `pan-x pan-y` 还能一并拦住捏合；
     ③ 窄屏下把可编辑表单元素字号提到 16px（见文件内的 @media 段）—— iOS 在字号 <16px
        的输入框获得焦点时会**强行放大整页**，这是「没人碰它、界面自己会放大」的最常见原因。
   注：`touch-action:pan-x pan-y` 只禁缩放，正常上下/左右滚动、内部 overflow 滚动都不受影响。 */
html{
  touch-action:pan-x pan-y;
  -webkit-text-size-adjust:100%;  /* 横竖屏切换时禁止 Safari 自作主张改字号 */
  text-size-adjust:100%;
}

/* ---- 登录页 ---- */
.login-wrap{min-height:100vh;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,#01276c 0%,#1a4d99 60%,#2563eb 100%);padding:20px}
.login-card{background:#fff;border-radius:16px;padding:36px 32px;width:100%;max-width:400px;box-shadow:0 12px 40px rgba(1,39,108,.3)}
.login-logo{display:flex;align-items:center;gap:10px;margin-bottom:6px}
.login-logo .mark{width:44px;height:44px;border-radius:11px;background:linear-gradient(135deg,#fdeec0,#f3d493 42%,#dfae52 78%,#c8912f);color:#01276c;display:flex;align-items:center;justify-content:center;font-weight:700;font-size:14px;letter-spacing:1px;box-shadow:0 2px 6px rgba(200,145,47,.28)}
.login-card h1{font-size:20px;margin-bottom:2px}
.login-card .sub{color:var(--tx2);font-size:12px;margin-bottom:24px}
.field{margin-bottom:14px}
.field label{display:block;font-size:12px;color:var(--tx2);margin-bottom:5px}
.field input,.field select,.field textarea{width:100%;padding:10px 12px;border:1px solid var(--line);border-radius:8px;font-size:14px;background:#fff;color:var(--tx)}
.field input:focus,.field select:focus,.field textarea:focus{outline:none;border-color:var(--green)}
.btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:9px 16px;border:none;border-radius:8px;font-size:14px;cursor:pointer;background:var(--green);color:#fff;transition:.15s}
.btn:hover{background:var(--green-d)}
.btn.block{width:100%;padding:12px}
.btn.ghost{background:#fff;color:var(--tx);border:1px solid var(--line)}
.btn.ghost:hover{border-color:var(--green);color:var(--green);background:var(--green-l)}
.btn.danger{background:var(--red)}
.btn.sm{padding:5px 10px;font-size:12px;border-radius:6px}
.btn:disabled{opacity:.5;cursor:not-allowed}

/* ---- v4.3 导入区三合一（选择/拖拽/粘贴）----（app.js 也会动态注入同名样式，部署不更新本文件也能用） */
.imp-pick{display:flex;align-items:center;justify-content:center;gap:12px;padding:18px 14px;border:1.5px dashed #c6d2cc;border-radius:10px;background:#fbfdfc;color:var(--tx2);cursor:pointer;text-align:center;transition:.15s;user-select:none;min-height:64px}
.imp-pick:hover,.imp-pick:focus{border-color:var(--green);background:var(--green-l);outline:none}
.imp-pick.imp-over{border-color:var(--green);background:var(--green-l);box-shadow:inset 0 0 0 1px var(--green)}
.imp-pick .imp-pick-ic{font-size:26px;line-height:1;flex:none}
.imp-pick .imp-pick-tx{font-size:13px;line-height:1.7}
.imp-pick .imp-pick-tx b{color:var(--tx)}
.imp-pick .imp-pick-tx span{font-size:12px;color:var(--tx2)}

/* ---- 布局 ---- */
.layout{display:flex;min-height:100vh}
.sidebar{width:216px;background:var(--green-d);color:#c8d4e0;display:flex;flex-direction:column;position:fixed;top:0;bottom:0;left:0;z-index:50;transition:transform .25s}
.sidebar .brand{display:flex;align-items:center;gap:9px;padding:18px 16px 15px;border-bottom:1px solid rgba(255,255,255,.09);color:#fff}
.sidebar .brand .mark{width:38px;height:38px;border-radius:9px;background:linear-gradient(135deg,#fdeec0,#f3d493 42%,#dfae52 78%,#c8912f);color:#01276c;display:flex;align-items:center;justify-content:center;font-weight:700;font-size:13px;letter-spacing:1.5px;flex:none;box-shadow:0 1px 4px rgba(200,145,47,.26)}
.sidebar .brand b{font-size:15px}
.sidebar .brand small{display:block;font-size:10px;color:#8fb0d8;font-weight:400}
/* ★★ 2026-10-24 老板：「左上角 logo 用大鼎 2 个字的金标，按 PC 端顶栏的信息设计，
   右边改成运营中心、英文放下面」⇒ 与桌面壳顶栏同一套：金标「大鼎」＋ 软件名 ＋ 大写英文副标。
   ⚠ 英文用 9px/字距 1.1 是为了在 216px 侧栏里不换行（"OPERATIONS CENTER" 17 字符约 105px）。 */
.sidebar .brand .dd-brand{display:inline-block;flex:none;color:#01276c;font-weight:700;font-size:12px;letter-spacing:2px;line-height:1;padding:2px 7px;border-radius:5px;background:linear-gradient(135deg,#fdeec0 0%,#f3d493 42%,#dfae52 78%,#c8912f 100%);box-shadow:0 2px 6px rgba(0,0,0,.22);user-select:none}
.sidebar .brand .dd-brand-wrap{display:flex;flex-direction:column;min-width:0}
.sidebar .brand .dd-brand-name{font-size:13px;font-weight:600;letter-spacing:1.6px;line-height:1.15;color:#eef4f0;white-space:nowrap}
.sidebar .brand .dd-brand-sub{display:block;font-size:8.5px;letter-spacing:1.1px;text-transform:uppercase;color:#7f95b3;margin-top:3px;white-space:nowrap}
/* v5.58 ⑫：导航菜单长了自己能滚，但**不显示滚动条**（老板原话「导航栏的滚动条隐藏」）
   —— 只藏外观不关滚动：scrollbar-width:none（Firefox）+ -ms-overflow-style:none（旧 Edge）
   + ::-webkit-scrollbar 归零（Chrome/Safari/新 Edge）；overflow-y:auto 原样保留。 */
/* ★ 2026-10-24：加 min-height:0 —— 侧栏底部个人中心变高后，没有它 flex 子项不肯收缩，
   导航会从底部「漏」出来压住个人中心（本地实测：销售预算 被压在退出登录按钮上）。 */
/* ★ 2026-10-24（老板 20:0x：「我在运营中心的导航栏滑动怎么窗口在动」）：
   侧栏菜单滚到底后继续滚，滚动链会传给整页 —— 以前 overscroll-behavior:contain 只在
   ≤820px 的抽屉模式里加过，桌面端常驻侧栏**没加**（v5.45 注释里写着"另说，不在本次口径内"）。
   现在全局加：菜单滚到头也不再带动后面的页面。侧栏本身同样兜一层，防品牌区那截漏出去。 */
.nav{flex:1;min-height:0;overflow-y:auto;padding:6px 10px;scrollbar-width:none;-ms-overflow-style:none;overscroll-behavior:contain}
.sidebar{overscroll-behavior:contain}
.nav::-webkit-scrollbar{width:0;height:0}
.nav .grp{font-size:11px;color:#7e93c8;padding:12px 8px 4px;letter-spacing:1px}
/* ★ v8.76：业务中心 ↔ 运营中心 的切换入口（虚线框，和普通菜单项区分开，别误点） */
.nav .navx{display:flex;align-items:center;gap:8px;margin:2px 2px 6px;padding:9px 10px;border-radius:8px;
  border:1px dashed rgba(255,255,255,.22);color:#a9bcd8 !important;font-size:12.5px;letter-spacing:.3px}
.nav .navx span{color:#cfe0f5}
.nav .navx:hover{background:rgba(255,255,255,.08);border-color:rgba(255,255,255,.4);color:#fff !important}
.nav a{display:flex;align-items:center;gap:10px;padding:9px 12px;border-radius:8px;color:#c8d4e0;font-size:13.5px;margin-bottom:2px}
.nav a .ic{width:18px;text-align:center;opacity:.85}
.nav a:hover{background:rgba(255,255,255,.07);color:#fff}
.nav a.on{background:var(--green);color:#fff}
.sidebar .foot{flex:none;background:var(--green-d);padding:12px 16px 14px;border-top:1px solid rgba(255,255,255,.08);font-size:12px;color:#9da8c0}
.sidebar .foot b{color:#fff;font-weight:600}
.sidebar .foot .out{cursor:pointer;color:#f0a89d;margin-right:10px} /* v4.22：改密/登录管理/退出三个入口之间加间距 */
.sidebar .foot .ver{margin-top:7px;font-size:10px;color:#7f93c8;letter-spacing:.4px;user-select:all}
/* ★★ 2026-10-24 老板：「网页版左下角的个人中心都按照会客中心的排版设计」
   ⇒ 头像 + 姓名 + 角色 → 整宽「退出登录」按钮 → 最底一行元信息。数值与会客中心逐项对齐：
      头像 30px 圆 / 姓名 13px · 角色 10.5px / 按钮 padding 9px10px · 圆角 8 · 12.5px。
   头像「鼎」全端同一个（金圈深蓝字），老板 2026-10-24：「所有的软件的用户头像都要同步用同一个」。 */
/* ★ 2026-10-24 老板：「我的页改成点头像直接进」⇒ 头像＋姓名整块就是入口，给 hover/active 反馈 */
.sidebar .foot .who{display:flex;align-items:center;gap:9px;padding:5px 4px;border-radius:8px;cursor:pointer;transition:background .15s}
.sidebar .foot .who:hover{background:rgba(255,255,255,.08)}
.sidebar .foot .who:active{background:rgba(255,255,255,.13)}
.sidebar .foot .who-av{position:relative;flex:none;width:30px;height:30px;border-radius:50%;display:flex;align-items:center;justify-content:center;
  background:linear-gradient(135deg,#fdeec0,#f3d493 42%,#dfae52 78%,#c8912f);color:#01276c;font-size:13px;font-weight:700;
  border:1px solid rgba(200,145,47,.5);box-shadow:0 1px 4px rgba(0,0,0,.25);user-select:none}
/* ★ 2026-10-24：本人在线设备数改成**头像右上角的红色小圆标**。
   原来它跟在姓名行里，显示成「朱海兵 v8.75 [在线] 1」—— 那个 1 会被读成"在线 1"，像是状态值；
   角标化之后姓名行只剩「姓名 · 版本号 · 在线」，一眼就清楚。 */
.sidebar .foot .who-av #sess-badge{position:absolute;top:-5px;right:-5px;min-width:14px;height:14px;padding:0 3px;
  border-radius:7px;background:#e53935;color:#fff;font-size:9px;line-height:14px;text-align:center;font-weight:700;
  box-sizing:border-box;box-shadow:0 1px 3px rgba(0,0,0,.4);letter-spacing:0}
/* ★ 2026-10-24：用户自上传的真实头像（老板：「每人一张真实头像（可自己上传）」）。
   照片铺满 30px 金圈；没设头像时还是那个「鼎」字，两者外观尺寸一致，不会跳。 */
.sidebar .foot .who-av img{width:100%;height:100%;border-radius:50%;object-fit:cover;display:block}

/* 「我的账号」弹窗顶部头像区（点头像或「更换头像」都能换） */
.acc-hd{display:flex;align-items:center;gap:14px;padding:2px 0 14px;margin-bottom:12px;border-bottom:1px solid var(--line,#e3e8ee)}
.acc-hd-av{flex:none;width:60px;height:60px;border-radius:50%;display:flex;align-items:center;justify-content:center;
  background:linear-gradient(135deg,#fdeec0,#f3d493 42%,#dfae52 78%,#c8912f);color:#01276c;
  font-size:22px;font-weight:700;border:2px solid rgba(200,145,47,.55);box-shadow:0 2px 8px rgba(200,145,47,.22);
  cursor:pointer;overflow:hidden;user-select:none;transition:box-shadow .15s,transform .12s}
.acc-hd-av:hover{box-shadow:0 3px 12px rgba(200,145,47,.42);transform:translateY(-1px)}
.acc-hd-av img{width:100%;height:100%;object-fit:cover;display:block}
.acc-hd-tx{flex:1;min-width:0;display:flex;flex-direction:column;align-items:flex-start;gap:3px}
.acc-hd-tx b{font-size:16px;font-weight:600;color:var(--tx,#2b3a4a)}
.acc-hd-tx s{text-decoration:none;font-size:12px;color:var(--tx2,#6b7a8c)}
.acc-hd-tx .btn{margin-top:5px}
.sidebar .foot .who-txt{flex:1;min-width:0;line-height:1.25}
/* ★ 2026-10-24 老板：「版本号和在线状态放姓名后面」⇒ 姓名行改 flex，版本号/在线徽标贴右侧；
   姓名过长只截姓名本身，徽标不被挤掉。 */
.sidebar .foot .who-txt b.who-line{display:flex;align-items:center;gap:6px;min-width:0;font-size:13px;font-weight:600;color:#eaf3ed}
.sidebar .foot .who-name{min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.sidebar .foot .who-meta{display:inline-flex;align-items:center;gap:5px;flex:none;font-weight:400}
.sidebar .foot .who-meta .verlink{font-size:10px;color:#7f93c8;letter-spacing:.2px;cursor:pointer;text-decoration:underline dotted;text-underline-offset:2px}
.sidebar .foot .who-meta .verlink:hover{color:#fff}
.sidebar .foot .who-txt s{display:block;text-decoration:none;font-size:10.5px;color:#8fa3bb;margin-top:2px}
.sidebar .foot .logout-btn{display:flex;align-items:center;justify-content:center;gap:7px;margin-top:9px;padding:9px 10px;border-radius:8px;
  background:rgba(255,255,255,.07);border:1px solid rgba(255,255,255,.16);color:#c2d4c9;font-size:12.5px;letter-spacing:.8px;
  cursor:pointer;transition:background .15s,border-color .15s,color .15s}
.sidebar .foot .logout-btn:hover{background:rgba(230,80,80,.20);border-color:rgba(255,130,130,.42);color:#fff}
.sidebar .foot .logout-btn svg{flex:none}
/* v4.7-59：同步状态指示器（在线/离线/同步中/待同步） */
/* ★ 2026-10-24：在线徽标从底部 .ver 挪进姓名行 ⇒ 选择器不再限定 .ver（否则配色全丢） */
.sidebar .foot .st{display:inline-block;margin-left:0;padding:0 6px;border-radius:3px;font-size:10px;line-height:16px;font-style:normal;letter-spacing:0;vertical-align:1px;user-select:none}
.st-on{color:#6ddba9;background:rgba(109,219,169,.16)}
.st-off{color:#ff9f92;background:rgba(255,159,146,.16)}
.st-run{color:#7cc4f0;background:rgba(124,196,240,.16)}
.st-wait{color:#ffce7a;background:rgba(255,206,122,.16)}
.sidebar .out:hover{color:#ff8f80}
.main{flex:1;margin-left:216px;padding:22px 26px;max-width:100%}
.topbar{display:flex;align-items:center;gap:12px;margin-bottom:18px}
.topbar h2{font-size:19px}
.topbar .sp{flex:1}
.hamburger{display:none;background:none;border:none;font-size:22px;cursor:pointer}
/* v3.0：返回按钮（每个功能页顶部退回上一界面） */
.backbtn{background:var(--card,#fff);border:1px solid var(--line,#e3e8ee);color:var(--tx,#2b3a4a);border-radius:8px;padding:5px 12px;font-size:13px;cursor:pointer;white-space:nowrap;box-shadow:var(--shadow,0 1px 3px rgba(0,0,0,.06))}
.backbtn:hover{background:var(--bg2,#f4f6f8);border-color:#c9d3dd}

/* ---- 组件 ---- */
.cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(170px,1fr));gap:14px;margin-bottom:18px}
.stat{background:var(--card);border-radius:var(--radius);padding:16px 18px;box-shadow:var(--shadow)}
.stat .lbl{font-size:12px;color:var(--tx2)}
.stat .val{font-size:26px;font-weight:700;margin-top:4px}
.stat .val small{font-size:12px;font-weight:400;color:var(--tx2)}
.stat.g .val{color:var(--green)} .stat.o .val{color:var(--orange)} .stat.b .val{color:var(--blue)} .stat.r .val{color:var(--red)}
.panel{background:var(--card);border-radius:var(--radius);box-shadow:var(--shadow);margin-bottom:18px;overflow:hidden}
.panel .ph{display:flex;align-items:center;gap:10px;padding:14px 18px;border-bottom:1px solid var(--line)}
.panel .ph h3{font-size:15px}
.panel .ph .sp{flex:1}
/* v5.61：订单详情页头**手机端专属**的 🔗 分享按钮。桌面端由那条按钮排承担，
   这里一律不显示（避免桌面多出一个重复入口）。选择器带 .panel .ph 两层 ⇒
   特异性 (0,2,1) 压过 .btn 的 (0,1,0)，不必用 !important。 */
.panel .ph .od-share-mb{display:none}
.panel .pb{padding:16px 18px}
.toolbar{display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin-bottom:14px}
.toolbar input,.toolbar select{padding:8px 12px;border:1px solid var(--line);border-radius:8px;font-size:13px;background:#fff}
/* ★ v7.93（老板 10-17 10:51）：「电脑端的第一个产品搜索框调成和色号筛选框一样宽」
   ⇒ 产品仓库工具栏的搜索框与色号下拉统一成 220px（输入框默认按 20 字符算、下拉默认按最长选项算，
     两者天生不一样宽，只能钉成同一个值）。
   ⚠ 必须包在 min-width:821px 里 —— `#ipt_proQ` 是 **ID 选择器 (1,0,0)**，会压过手机段
     `.toolbar input,.toolbar select{flex:1}` 的 (0,1,1)；不包的手机端搜索框会被钉死成 220px。
   ⚠ 色号最长有 30+ 字符，下拉收起时会被截断，点开后的浮层仍显示完整文本（浏览器行为，无法也不该改）。 */
@media(min-width:821px){
  #ipt_proQ,.toolbar>.pro-col-sel{width:220px;min-width:220px;max-width:220px;flex:none}
}
.table-wrap{overflow-x:auto}
/* v6.16②：住户电费结清区「可拖动改高度」——容器限高内滚，右下角手柄 sticky 常驻（滚动时也看得见）。
   光标 ns-resize（上下拉），与 v6.15① 搜索下拉的 nwse-resize 手柄区分开。 */
.ems-box{overflow:auto;overscroll-behavior:contain}
.ems-grip{position:sticky;bottom:0;height:16px;cursor:ns-resize;touch-action:none;background:#fff;border-top:1px solid #f0f3f5}
/* ★ v7.22：只调高低 ⇒ 正中间一条短横（原来是贴右边的 ⇅ 字符，与其它手柄形态不一致） */
.ems-grip:after{content:"";position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width:42px;height:3px;border-radius:2px;background:#a9bac8}
.ems-grip:hover{background:#f7fafc}
.ems-grip:hover:after{background:var(--green,#22a06b)}
table.tbl{width:100%;border-collapse:collapse;font-size:13px}
/* v1.7：列宽拖拽调整——表头右边缘为拖拽热区，宽度持久化到 localStorage */
.tbl th{position:relative;user-select:none}
.tbl th.col-drag{border-right:2px solid var(--green)}
/* v5.34 ①：表头列宽拖拽把手可视化（仅挂 data-colkey 的表＝销售预算单三明细/图纸明细/加工费档案；
   其他表格拖拽热区行为不变，只是不显示把手）。热区几何与 colRes/colCursor 完全一致：表头右缘 8px。
   常显浅灰细条 → 悬停变绿 → 拖动中实心绿，老板此前反馈「怎么弄都没反应」的可见性问题在源头解决。 */
table.tbl[data-colkey] th::after{content:'';position:absolute;right:0;top:22%;bottom:22%;width:8px;cursor:col-resize;border-radius:3px;background:rgba(100,116,139,.25);z-index:2}
table.tbl[data-colkey] th:hover::after{background:rgba(34,160,107,.4)}
table.tbl[data-colkey] th.col-drag::after{background:var(--green);box-shadow:0 0 0 1px rgba(34,160,107,.25)}
.tbl th{background:#f7f9fa;text-align:left;padding:10px 12px;color:var(--tx2);font-weight:600;white-space:nowrap;border-bottom:1px solid var(--line)}
.tbl td{padding:10px 12px;border-bottom:1px solid #f0f3f5;vertical-align:middle}
.tbl tr.click{cursor:pointer}
.tbl tr.click:hover td{background:#f8fbfa}
/* ★ v6.29（老板 10-09）：「项目管理和订单管理列表出现横向滚动条 —— 要跟着窗口自适应」
   根因：**单元格默认不折行**（CSS 里 th 写了 nowrap，另有三四十处 td 带行内 `white-space:nowrap`）
   ⇒ 每一列的最小宽度被内容钉死（一条长地址就能占几百 px），列一多整表就超出容器
   ⇒ 出现横向滚动条，窗口拉宽也救不回来（列宽不跟着涨）。
   修法（**只作用于桌面端 ≥821px**，手机端是卡片视图、另有 min-width:640px 的横向滚动设计，不动）：
     · th / td 一律允许折行 + 长串可断（`word-break` + `overflow-wrap:anywhere`）
     · ⚠ 必须带 `!important` —— 否则被那三四十处**行内** nowrap 盖掉（行内优先级高于类选择器）
     · td 顶对齐，多行内容看着整齐
   ⇒ 长内容在列内折行、列可被压到容器宽，整表收敛进窗口 ⇒ 不再横向滚动。 */
@media (min-width:821px){
  table.tbl th,table.tbl td{white-space:normal !important;word-break:break-word;overflow-wrap:anywhere}
  table.tbl td{vertical-align:top}
}
/* v4.63：订单加急——列表行浅红底（申请中/已加急醒目），hover 保持红底不跳灰 */
.tbl tr.row-urge td{background:#fdecea}
.tbl tr.row-urge.click:hover td{background:#fbe4e2}
/* v4.69：加急行左侧红条强化——待办提醒/项目档案/发货管理复用同一行级标红 */
.tbl tr.row-urge td:first-child{box-shadow:inset 3px 0 0 #e5484d}
.badge.b-urge{background:#fdecea;color:#c0392b;font-weight:600}
/* ㊲：列表表格——容器视口内滚动（JS 设 max-height）+ 列头滚动固定 + 默认 50 行底部条 */
.tbl-pg-wrap{overflow:auto}
.tbl-sticky thead tr:first-child th,.tbl-sticky tbody:first-child tr:first-child th{position:sticky;top:0;z-index:6;box-shadow:inset 0 -1px 0 var(--line)}
.tbl-sticky.data-table thead tr:first-child th{background:var(--card)} /* ㊲补：广告屏 data-table 原无 th 背景，sticky 需不透明底色 */
/* ★ v6.83①（老板 10-11 00:1x）：「电脑端项目列表和订单列表和发货列表，表单表头及以上的部分滚动的时候不能动」。
   v6.54⑯ 曾把列表容器**放开限高**改整页滚动（当时是为了消掉吸底那条"空白框"）；
   现在要让「工具栏＋表头」都不动 ⇒ 恢复**容器限高内滚**：页面不滚 ⇒ 工具栏自然不动，
   表头由上面的 .tbl-sticky 吸在容器顶。**但不恢复吸底条**（那才是当年被嫌的空白框）。
   ⚠ 只作用于电脑端（≥821px）；手机端是卡片视图，不受影响。 */
@media(min-width:821px){
  .tbl-pg-wrap{max-height:calc(100vh - 190px);overflow:auto}
}
/* ★★★ v7.77（老板 10-16 12:38）：「有些页面只有一个窗口的就不要下拉横拉条了，把底下的留空和左右两边留一样」
   —— 判据：页面里**只有一个列表窗口**（≤1 张列表 / ≤2 个面板）⇒ `pageSoloApply()` 给 `.main` 挂 `solo` 类。
   老板要的三件事一起拿到，**不牺牲 v6.83① 的表头固定**：
     ① 那个窗口**撑满**页面剩余高度 ⇒ 内容少时卡片也到底，底部留白＝左右留白（各 26px）；
     ② 内容多时照旧**内部滚动**（上面那条 `max-height:calc(100vh - 190px)` 一字未改，
        它是 v750~v781 共 31 套测试的锚点，**绝不能动**）；
     ③ 表头吸顶（`.tbl-sticky`）照旧。
   ⚠ 父级 `.layout{display:flex;min-height:100vh}` ⇒ `.main` 本来就铺满一屏，这里只需把它改成 flex 列，
     末尾那个块 `flex:1` 就自然拉伸到 `.main` 底 ⇒ 不必写 height/min-height（少一层副作用）。
   ⚠ 特异性：`.main.solo`(0,2,0) 压 `.main`(0,1,0)；`.main.solo .panel>.table-wrap`(0,3,0) 压 `.table-wrap`(0,1,0)
     ⇒ 全程不用 !important。
   ⚠ 只作用于电脑端（≥821px）：手机端是卡片视图，底部还要给悬浮按钮/安全区让位。
   ⚠ `.main` 的 `padding:22px 26px` 原文**不动**（15 套测试锚点），只在这里把底部 22 改成 26 与左右齐平。 */
@media(min-width:821px){
  .main.solo{display:flex;flex-direction:column;padding-bottom:26px}
  .main.solo>*:last-child{flex:1 1 auto;min-height:0;display:flex;flex-direction:column}
  /* ⚠ 两种页面结构都要覆盖（实测得来的，别删任一条）：
       ① 面板直接是 .main 的子级 —— `.main > .panel`（如 #/electric 那类渲染出来的卡片）
       ② 面板包在最后一层容器里 —— `.main > #zone_xxx > .panel`（如 #/users、#/projects）
     漏掉① ⇒ 面板的 `margin-bottom:18px` 留下来 ⇒ 底部留白 44px 而不是 26px（首轮实测踩到）。 */
  .main.solo>*:last-child>.panel,
  .main.solo>.panel:last-child{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;margin-bottom:0}
  .main.solo .panel>.table-wrap{flex:1 1 auto;min-height:0}
  /* ★ v7.80（老板 10-16 15:51：「界面上只有一个窗口的且已经铺到底了的就把那个横拉手隐藏掉吧」）
     —— 铺到底之后那条「调高低」的横拉手（`.pz-grip-h`，就是底部正中一条 42×3 短横那个）已经没有意义了：
        高度已经＝一整屏，再往下拖没地方可去；而且它 sticky 占位，正好压在列表最后一行上。
     ⚠ 必须用 `!important`：柄的 `display:block` 是 `paneGrip` 用**行内样式**写进去的
        （见 app.js 里 'position:sticky;bottom:0;display:block;width:100%;height:14px;…' 那句），
        行内优先级压过任何普通外链样式。
     ⚠ 只 `display:none`、**绝不从 DOM 删**：v578 / v706~v710 / v726~v740 那批专测都按 `.pz-grip` 计数，
        删掉元素会让它们集体红（铁律 13：能兼容就兼容）。
     ⚠ 右下角那个斜三角（`.pz-grip-w`）**不动** —— 老板只点名了横拉手。 */
  .main.solo .pz-grip-h{display:none !important}
  /* ★★★ v7.81（老板 10-16 16:04 / 16:06，两条一起）
     ① 「订单管理里不想看到右边窗口的滚动条 —— 不是列表里面的，是列表外面的」。
        【根因】不是容器限高不够：是 v7.77 的 `.main.solo{display:flex}` 让订单表的
          **min-content 宽度**（≈1331px）顺着 flex 链一路上推（`.table-wrap` → `.panel` → `#zone_ord`
          → `.main` 的 `min-width:auto`）⇒ 1440 这档窗口整页横向溢出 ⇒ html 出现横向滚动条
          ⇒ `clientHeight` 掉 10px ⇒ **连带挤出右边那条竖向滚动条**（老板看到的就是它）。
        【修】给这条链上每个 flex 项补 `min-width:0` —— 表格改为在容器内横向滚动。
        ⚠ 老规则一个字没动（另起一条），`.main.solo{...}` 那几条原样保留（v781 逐字锚定）。
        ⚠ 真机实测（无头 Edge 1440×900，336 张订单）：修前 html 1609×808 / client 1406×798
          （横竖两条都在）→ 修后 html 1416×808 / client 1416×808（两条全无，容器 169~780 正好到窗口底）。
     ② `.table-wrap.tbl-fith`：容器高度＝「到窗口底的真实剩余」（JS 算好写进 `--tblh`，见 tblFitApply）。
        `calc(100vh-190px)` 是死数字，上方实际占位一变就不准 ⇒ 现在只在**单窗口铺到底**的页面覆盖它。
        ⚠ 只在本 `.main.solo` 作用域里覆盖 ⇒ 一页多块 / 内容超屏的页面照旧用上面那条
          `calc(100vh - 190px)`（v750~v784 那批断言逐字锚定它，**绝不能动**）。 */
  .main.solo,.main.solo>*:last-child,.main.solo>*:last-child>.panel,.main.solo>.panel:last-child,.main.solo .panel>.table-wrap{min-width:0}
  .main.solo .table-wrap.tbl-fith{max-height:var(--tblh,calc(100vh - 190px))}
  /* ★★★ v7.81（老板 10-16 16:06）：「列表底下那行『共 N 行 · 已显示 M 行』固定到窗口底部，不要随明细一起滚」。
     分页条就是表格里的最后一行 `<tr class="tbl-pg-foot">`，默认随内容一起滚。改成 sticky 吸在**容器底**：
     容器底已经＝窗口底（上面那条 `.tbl-fith`）⇒ 看着就固定在窗口底部。
     ⚠ `tr` 与 `td` **都写**：只写 td 时行还留在原处（真机量到 tr top=3153 / td top=735，行不跟着上来
       观感不一致）；两个都写 ⇒ 滚动前后 tr/td top 都恒 735（容器底 780 − 36 行高 − 9 横向滚动条）。
     ⚠ `td` 的底色原规则就是 `#f2f5f7`（不透明），吸底时不会透出下面的行。
     ⚠ 只作用于电脑端（≥821px）：手机端是卡片视图，不掺和。
     ⚠ v252 / v656 里那两条「底部条**不吸底**」的老断言是 v6.54⑮ 当时的口径（老板当年嫌它像空白框），
       本版按老板新要求改回吸底 —— 两条断言已同步改写。 */
  .tbl-pg-foot,.tbl-pg-foot td{position:sticky;bottom:0;z-index:5}
  .tbl-pg-foot td{box-shadow:inset 0 1px 0 var(--line)}
  /* ★★★ v7.82（老板 10-16 17:13）：「发货管理的列表窗口底部我点每页 15 行后底部的框跟着明细跑了，
     没有固定在底下」。
     【根因】`position:sticky; bottom:0` **不是**「钉在容器底」——它只在「元素本该被滚出视口」时才顶住；
       明细比容器矮时（点「每页 15 行」后很常见），底部条就落在内容该在的位置（紧跟最后一行），
       容器底那片空白露在它下面 ⇒ 看着就是「跟着明细跑了，没固定在底下」。
       真机实测（无头 Edge 1440×900、60 张发货单、可见 3 行）：底部条**离容器底 209px** —— 悬空。
     【修法】底部条前面挂一行空白吸高行（`.tbl-pg-fill`：JS 按几何算好高度写行内 `height`，
       见 app.js 的 tblPgFillSet / tblFitApply 末尾那段），把底部条顶到容器底。
       实测：209px → **10px**（＝容器底 780 − 横向滚动条 10 − 底部条 35，正好贴底）；
         明细长于容器时这一行根本不生成、表格高度一字不变（4574）⇒ 长短两个方向都安全。
     ⚠ 这里只给这一行定「没有内容、没有边框、不显示」的形态；**高度绝不写百分比**
       （`height:100%` 会把剩余高度交给浏览器的行分配算法平摊到每一行上，行全变胖 —— 实测踩到过）。
     ⚠ 只作用在「单窗口铺到底」的页面（`.tbl-fith`）；一页多块 / 手机端不掺和。 */
  .main.solo .table-wrap.tbl-fith tr.tbl-pg-fill>td{padding:0;border-bottom:none;background:transparent}
}

/* ★★★ v8.01（老板 10-17 17:2x）加工外单详情五个明细页签（产品 / 加工 / 包装 / 发货 / 附件）两件事：
   ① 「明细窗口底部的留空改成和左右一样」；
   ② 「取消下横拉条」。
   【为什么底部会多一截】这几个页签都是「整页只有一个窗口」的形态，`.main.solo>*:last-child` 与
     `.main.solo .panel>.table-wrap` 会被 `flex:1 1 auto` 拉到**窗口底**：明细行少的时候，
     表格下面挂一大片容器空白，比左右 26px 厚得多。⇒ 给这几个面板挂 `.eo-dtp`，改成按**内容高度**收口
     （底部留白回到 `.main` 的 26px，与左右一致）。
   【下横拉条】`.table-wrap{overflow-x:auto}` 是全局默认，列不多时也会挂一条（~10px，正好又把底部撑厚一层）
     ⇒ `.eo-dtw` 关掉横向滚动，列宽按容器百分比分配。
   ⚠ 特异性：`.main.solo .panel.eo-dtp`(0,4,0) 与 `.main.solo>*:last-child>.panel`(0,4,0) 同分，
     **必须写在它之后**才生效（本段就在 271 行那块之后）；`.main.solo .panel.eo-dtp>.table-wrap`(0,5,0)
     压 `.main.solo .panel>.table-wrap`(0,3,0)。
   ⚠ 只动外单详情这五个页签（`.eo-dtp` / `.eo-dtw` 只在那里用），订单详情、发货管理、各列表**一字未改**。 */
@media(min-width:821px){
  .main.solo .panel.eo-dtp{flex:0 0 auto}
  .main.solo .panel.eo-dtp>.table-wrap{flex:0 0 auto}
}
.eo-dtw{overflow-x:hidden;padding-bottom:0}
.eo-dtw .tbl{width:100%}
/* 「取消下横拉条」：这条就是表格容器底部那条「拖动调高低」的横柄（`.pz-grip-h`，14px 高、行内写了 display:block）
   —— 这几个页签不铺满窗口，拖高低没意义，反而把卡片底部撑出一条杠。
   ⚠ 必须 !important（display 是行内样式写的）；⚠ 只 display:none、**不从 DOM 删**（v578/v706~v710/v726~v740 那批按 `.pz-grip` 计数）。 */
.eo-dtp .pz-grip-h{display:none !important}

/* ★ v7.82：吸高空白行的通用形态（任何宽度下都别显形）＋ 手机端卡片视图里整行藏掉 */
tr.tbl-pg-fill>td{padding:0;border-bottom:none;background:transparent}
@media(max-width:820px){ tr.tbl-pg-fill{display:none !important} }
/* ★ v7.46（老板 10-14 12:5x：「别的页面如果也想要这个效果，希望表头也是固定的」）——
   第三方采购单 / 商品目录：容器限高内滚（横向滚动条常驻框底）**且**表头吸顶。
   ⚠ 关键：**不能用行内 max-height** —— `tblPageApply()` 把"已有行内固定高度"的容器当成
     「页面内嵌编辑/明细表」（v5.32 口径）直接 continue ⇒ 连带丢掉 `.tbl-sticky`（表头不再吸顶）
     与分页条（v7.45 就是这么踩的）。改用**类**限高即可绕开那道闸门，两样一起拿回来。
   ⚠ 双类名（0,2,0）压过上面 `.tbl-pg-wrap` 的桌面端规则（0,1,0），桌面端数值以本类为准；
     且不限媒体宽度 ⇒ 窄窗口 / 手机端同样限高（保住 v7.45 已上线的观感，不回退）。 */
.table-wrap.tbl-auxscroll{max-height:62vh;overflow:auto}
/* ★ v6.83④：手机端专用隐藏（守 v6.52「手机端项目列表只留搜索框＋新建」口径） */
@media(max-width:820px){ .m-hide{display:none !important} }
/* ★★ v7.92（老板 10-17 10:14）：「全部模块在手机端都不要显示导入 / 导出按钮」。
   全站的导入都叫 xxxImport / xlsListImport，导出都叫 xxxExport / xlsListExport
   ⇒ 用属性选择器**一条覆盖全部模块**，不必逐个加类、也就不会漏掉哪个模块。
   ⚠ 只在 max-width:820px 下生效，桌面端与平板横屏完全不受影响；
   ⚠ 用 !important 压过各模块按钮自身的 btn 类样式。 */
@media(max-width:820px){
  button[onclick*="Import"],button[onclick*="Export"]{display:none !important}
}
/* ★ v6.54⑮（老板 10-09 夜）：「列表底下的空白条缩小一点」
   · 去掉 sticky 吸底：容器不再限高后，「吸底」会让它相对视口一直浮在屏幕下方（正是老板说的"空白框"）
   · 留白 8px→3px、字号 12.5→11.5px：这条只放「共 N 行 · 已显示 M 行 · 显示全部 · 每页」，越紧凑越好 */
.tbl-pg-foot td{background:#f2f5f7;text-align:center;padding:3px 8px;color:var(--tx2);font-size:11.5px;white-space:nowrap;border-bottom:none}
.tbl-pg-foot .btn{margin:0 3px;vertical-align:middle}
.badge{display:inline-block;padding:3px 10px;border-radius:20px;font-size:12px;white-space:nowrap}
.b-gray{background:#eef1f4;color:#68788a}.b-green{background:var(--green-l);color:var(--green)}
.b-orange{background:#fdf1e3;color:#c4701a}.b-blue{background:#e8f0fc;color:#2b62b0}
.b-red{background:#fdecea;color:#c0392b}.b-purple{background:#f1ecfa;color:#6c4bb8}
.empty{padding:40px;text-align:center;color:var(--tx2)}
.kv{display:grid;grid-template-columns:110px 1fr;gap:8px 14px;font-size:13.5px}
.kv dt{color:var(--tx2)}
.kv dd{word-break:break-all}
.pager{display:flex;gap:6px;align-items:center;justify-content:flex-end;padding:10px 4px;font-size:12px;color:var(--tx2)}
.pager button{padding:5px 10px;border:1px solid var(--line);background:#fff;border-radius:6px;cursor:pointer}
.pager button:disabled{opacity:.4}

/* 状态条 */
.flow{display:flex;flex-wrap:wrap;gap:6px;align-items:center;margin:8px 0}
.flow .step{display:flex;align-items:center;gap:6px}
.flow .dot{padding:4px 12px;border-radius:16px;background:#eef1f4;color:#68788a;font-size:12px}
.flow .dot.on{background:var(--green);color:#fff}
.flow .arrow{color:#b8c4ce;font-size:11px}

/* 工序 */
.proc{display:flex;align-items:center;gap:10px;padding:10px 12px;border:1px solid var(--line);border-radius:8px;margin-bottom:8px}
.proc .nm{flex:1;font-size:13.5px}
.proc .meta{font-size:11px;color:var(--tx2)}
.proc.done{background:var(--green-l);border-color:#bfe3d6}
.check{width:20px;height:20px;border-radius:50%;border:2px solid #c6d0d8;display:flex;align-items:center;justify-content:center;color:#fff;font-size:12px;flex-shrink:0}
.proc.done .check{background:var(--green);border-color:var(--green)}
/* v1.6：工序多选批量完成 */
.proc input[type=checkbox]{width:17px;height:17px;accent-color:var(--green);cursor:pointer;flex-shrink:0;margin:0}
.proc input[type=checkbox]+.nm{cursor:default}

/* 二维码 */
.qr-box{background:#fff;padding:14px;border-radius:10px;border:1px solid var(--line);display:inline-block;text-align:center}
.qr-box svg,.qr-box img{display:block}
.qr-box .cap{font-size:11px;color:var(--tx2);margin-top:8px}

/* 图片 */
.photos{display:flex;flex-wrap:wrap;gap:8px;margin-top:8px}
.photos .ph-item{position:relative;width:86px;height:86px;border-radius:8px;overflow:hidden;border:1px solid var(--line)}
.photos img{width:100%;height:100%;object-fit:cover}
.photos .del{position:absolute;top:2px;right:2px;background:rgba(0,0,0,.55);color:#fff;border:none;border-radius:50%;width:20px;height:20px;cursor:pointer;font-size:11px}
.photo-add{width:86px;height:86px;border:1.5px dashed #c6d0d8;border-radius:8px;display:flex;flex-direction:column;align-items:center;justify-content:center;color:var(--tx2);font-size:11px;cursor:pointer;gap:2px}
.photo-add:hover{border-color:var(--green);color:var(--green)}

/* 模态框 */
#modal-root:empty{display:none}
/* ㊱补：配送方式孤儿补充字段（未分配给任何方式）强制隐藏，防止在所有配送方式下串显 */
#modal-root [data-dm=""]{display:none !important}
/* v4.7-37：弹窗避开刘海屏——overlay 四周留出安全区，弹窗高度同步扣减 */
.overlay{position:fixed;inset:0;background:rgba(15,30,40,.45);z-index:1000;/* ★ 2026-10-27 19:05（老板：「所有有图片的地方都改一下」）——
     手机底栏 #ddMobTabs 是 900、手机菜单 901，而弹窗原来只有 100 ⇒ **所有弹窗都被底栏压住一条**
     （弹窗里的图片、底部按钮被底栏盖住）。统一提到 1000：弹窗永远在底栏之上。 */display:flex;align-items:center;justify-content:center;padding:calc(16px + env(safe-area-inset-top,0px)) calc(16px + env(safe-area-inset-right,0px)) calc(16px + env(safe-area-inset-bottom,0px)) calc(16px + env(safe-area-inset-left,0px));animation:fade .15s;box-sizing:border-box}
@keyframes fade{from{opacity:0}}
.modal{background:#fff;border-radius:14px;width:100%;max-width:min(560px,92vw);max-height:calc(92vh - env(safe-area-inset-top,0px) - env(safe-area-inset-bottom,0px));display:flex;flex-direction:column;animation:up .2s;box-sizing:border-box}
@keyframes up{from{transform:translateY(16px);opacity:0}}
.modal.wide{max-width:min(760px,92vw)}   /* v9.21：窗口变窄时弹窗跟着缩（原来固定 760，缩窗口照片不动） */
.modal .mh{display:flex;align-items:center;padding:16px 20px;border-bottom:1px solid var(--line)}
.modal .mh h3{font-size:16px;flex:1}
.modal .mh .x{background:none;border:none;font-size:20px;cursor:pointer;color:var(--tx2)}
.modal .mb{padding:18px 20px;overflow-y:auto;overscroll-behavior:contain}
.modal .mf{padding:14px 20px;border-top:1px solid var(--line);display:flex;gap:10px;justify-content:flex-end}
/* ★★ v7.12：默认密码强制改密弹窗 —— 内容区可收缩、底栏不被挤出去。
   【真因（老板 10-12 18:4x「苹果手机端改密还是没有保存键，苹果电脑上也没有」）】
     `.modal` 是 flex 列，`.mb` 是 `overflow-y:auto` 的弹性项。**WebKit/Safari 对弹性项
     `min-height:auto` 的解析与 Chromium 不同** —— Safari 上 `.mb` 不收缩 ⇒ 内容总高超出
     `max-height` ⇒ 底栏 `.mf`（保存按钮）被**顶到弹窗外**，而 `.modal` 没有 overflow:hidden，
     溢出的部分正好落到屏幕外 ⇒ 用户「填完密码找不到保存键」。
     我在 Chromium（Edge）里怎么模拟都复现不出来（Chromium 会收缩）—— 一换成 Safari 就中招，
     这解释了为什么「苹果手机 + 苹果电脑都没有，而安卓/Chrome 正常」。
   ⚠⚠ 必须写在**全局**：上一版我把它写进了「≤820px 的媒体查询」里 ——
     **Mac 上宽度 >820，媒体查询根本不生效** ⇒ 电脑端等于没修（老板反馈正是如此）。
   ⚠ 只作用于 `.pwd-must-modal`（强制改密弹窗），其余弹窗一个字不动。 */
.modal.pwd-must-modal .mb{flex:1 1 auto;min-height:0}
.modal.pwd-must-modal .mf{flex-shrink:0}
/* ★ v7.12：窄屏时保存按钮改写进**内容区末尾**（见 pwdMustDlg）——
   它是内容的一部分、跟着输入框一起被聚焦滚动带着走，任何「底栏被挤出去 / 被键盘盖住」都不再能让它消失。 */
.pwm-save-row{margin-top:16px;display:flex;justify-content:flex-end}
/* v7.14：强制改密弹窗**标题栏**里的「保存」——与标题同一行、永远在弹窗最顶端。
   老板 10-12 连报三次「看不到保存/确认修改按钮」⇒ 内容区的按钮会因软键盘把可视区压矮、
   内容区变可滚、弹窗高度被记忆尺寸钳小而落到折叠线以下；标题栏永远先被画出来 ⇒ 保底入口。 */
.modal .mh .pwm-hbtn{margin-left:10px;flex:none;padding:7px 16px;font-size:13px;line-height:1.4;min-height:0;white-space:nowrap}
/* v4.7-24：电脑版弹窗可调节大小——右下角拖拽缩放（宽≥900px 生效；手机端不变）
   v4.7-91 需求3：右下角加明显的斜纹拖拽手柄（原生 resize 生效，手柄仅视觉提示，不拦截鼠标）
   v4.19 修复：原生 resize 在 flex 子项主轴（宽度）方向不生效——加 flex:0 0 auto 让宽度可调 */
/* ============================================================================
   v7.22：**拖拽手柄视觉统一**（老板 10-13 的两条规则）
     · 只能调高低 ⇒ **底部一条短横**（`.pz-grip-h` / `.ems-grip` / `.chat-ipt-bar`）
     · 能调高低 + 宽窄 ⇒ **右下角斜杠三角**（`.pz-grip-w` / `#smpd_drop .sd-grip`）
   ⚠ 位置与光标仍由 JS/行内给（专测 v629/v630 直接读 `style.cursor`）—— 本处只管"长什么样"。
   ⚠ 两类的观感与下面 @media 里弹窗自带的 `.modal-rz` / `.modal::after` 保持一致（同一种斜纹/短横语言）。
   ============================================================================ */
/* 只调高低：贴底一条 14px 的条 + 正中间一条 42×3 的短横（悬停变绿） */
.pz-grip-h{background:linear-gradient(to top,rgba(148,163,184,.16),rgba(148,163,184,0));border-radius:0 0 8px 8px}
.pz-grip-h::before{content:"";position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width:42px;height:3px;border-radius:2px;background:#a9bac8}
.pz-grip-h:hover::before{background:var(--green,#22a06b)}
/* 可调高低 + 宽窄：右下角两条斜杠组成的角（与弹窗右下角同一种语言） */
.pz-grip-w,#smpd_drop .sd-grip{background:linear-gradient(135deg,transparent 45%,#a9bac8 45%,#a9bac8 55%,transparent 55%,transparent 70%,#a9bac8 70%,#a9bac8 80%,transparent 80%);opacity:.8}
.pz-grip-w:hover,#smpd_drop .sd-grip:hover{opacity:1}
/* 消息中心输入框：原来是原生 `resize:vertical` ⇒ 右下角三角，可它**只能调高低**（不合规则）。
   v7.22 改成「输入框 + 贴底一条短横」：外包一层 .chat-ipt-wrap 管边框与圆角，
   里面 textarea 用 flex:0 0 auto + 行内 height（拖动只改 textarea 高度，壳子跟着长/缩）。 */
.chat-ipt-wrap{display:flex;flex-direction:column;flex:1;min-width:150px;border:1px solid var(--line,#e4e8ec);border-radius:8px;background:#fff;overflow:hidden}
.chat-ipt-wrap:focus-within{border-color:var(--green,#22a06b)}
.chat-ipt-wrap>textarea{width:100%;flex:0 0 auto;height:46px;min-height:34px;resize:none;border:0;outline:0;padding:8px 10px;font-size:14px;line-height:1.5;background:transparent;color:var(--tx);box-sizing:border-box}
.chat-ipt-bar{height:15px;flex:none;cursor:ns-resize;touch-action:none;display:flex;align-items:center;justify-content:center;background:var(--bg2,#f4f6f8);border-top:1px solid var(--line,#e4e8ec);-webkit-user-select:none;user-select:none}
.chat-ipt-bar:before{content:"";display:block;width:42px;height:3px;border-radius:2px;background:#a9bac8}
.chat-ipt-bar:hover:before,.chat-ipt-bar.on:before{background:var(--green,#22a06b)}
@media (min-width:900px){
  .modal{flex:0 0 auto;resize:both;overflow:hidden;min-width:340px;min-height:220px;max-width:96vw;max-height:94vh;width:560px;position:relative}
  .modal.wide{width:760px;max-width:96vw} /* v4.37 需求3/4：wide 弹窗桌面端宽度上限放开到 96vw（base 层 760px 会钳制 inline width），默认仍 760px、initW 调用方（小样明细/发货单详情=940px）与拖拽可调更宽 */
  .modal .mb{overflow-x:auto} /* v4.27 增补㉑：宽表格在弹窗内横向滚动，不再顶死弹窗宽度（宽度随拖拽任意调） */
  .modal .mh{cursor:move;user-select:none;-webkit-user-select:none} /* v4.18 调：标题栏可拖动弹窗位置；v4.27 增补㉑：双击标题栏恢复默认尺寸 */
  .modal::after{content:"";position:absolute;right:2px;bottom:2px;width:24px;height:24px;cursor:nwse-resize;pointer-events:none;border-radius:0 0 10px 0;
    background:repeating-linear-gradient(135deg,transparent 0 3px,#a9bac8 3px 5px)}
  .modal:hover::after{background:repeating-linear-gradient(135deg,transparent 0 3px,#7f95a8 3px 5px)}
  .modal-rz{position:absolute;right:0;bottom:0;width:26px;height:26px;z-index:5;cursor:nwse-resize;border-radius:0 0 12px 0;
    background:repeating-linear-gradient(135deg,transparent 0 3px,rgba(127,149,168,.65) 3px 5px);opacity:.9;transition:opacity .15s}  /* v4.27 增补㉑：手柄加大（18→26px）更易发现，宽高都可拖 */
  .modal:hover .modal-rz{opacity:1}
}
/* v5.37 ①（老板 09-19：「添加辅料明细里明细列表改成不能左右滑动」）：auxdNoSlide() 在
   auxDetailDlg/retAuxDlg 打开弹窗后给本弹窗 .mb 挂 mb-nox 类——双类选择器 specificity 高于上面
   @media 桌面端 .modal .mb{overflow-x:auto}，从机制上关死横向滚动（v5.36 ② 只限宽了标签，真机
   仍可能被诊断未覆盖的超宽源撑动）；宽表格弹窗不挂此类，仍可正常横滚不受影响 */
.modal .mb.mb-nox{overflow-x:hidden}
.modal .mb.mb-nox .grid2>*{min-width:0} /* 防 iOS date 输入固有宽度把 .grid2 的 1fr 1fr 半宽列撑破（grid item 默认 min-width:auto） */
/* v5.58（老板 09-22：「巡场时间的框框没有显示完整，右边没有收口」）：把上面那条 **mb-nox 专用**的
   兜底提升为**弹窗通用**，并给日期/时间类输入单列一条 min-width:0。
   机制：grid/flex item 默认 min-width:auto ⇒ <input type=date> 按**固有宽度**（Chromium/iOS 实测
   ~160px）撑破半宽列；桌面上 .mb 是 overflow-x:auto（出现横向滚动、右缘看着像被切），
   手机上 .mb 是 overflow-x:hidden（直接裁掉右半 ⇒ 就是「右边没有收口」）。 */
.modal .mb .grid2>*{min-width:0}
#modal-root .mb input[type="date"],#modal-root .mb input[type="time"],
#modal-root .mb input[type="datetime-local"],#modal-root .mb input[type="month"]{width:100%;min-width:0;max-width:100%;box-sizing:border-box} /* v6.27⑩ 追加 width:100%（收口）*/
.grid2{display:grid;grid-template-columns:1fr 1fr;gap:0 16px}

/* toast */
#toast{position:fixed;top:18px;left:50%;transform:translateX(-50%);z-index:200;display:flex;flex-direction:column;gap:8px;align-items:center}

/* v4.7-24：搜索框清除按钮 */
.srchw{position:relative;display:inline-flex;align-items:center}
.srchw input{padding-right:26px}
.srch-x{display:none;position:absolute;right:7px;width:17px;height:17px;line-height:17px;text-align:center;border-radius:50%;background:#dde5ec;color:#5a6b7a;cursor:pointer;font-size:11px;user-select:none;font-family:Arial,sans-serif}
.srch-x:hover{background:#c9d5e0;color:#33475b}
.toast-item{background:#22303c;color:#fff;padding:10px 18px 10px 14px;border-radius:8px;font-size:13px;box-shadow:0 4px 14px rgba(0,0,0,.25);animation:up .2s;display:flex;align-items:center;gap:10px;max-width:90vw}
.toast-item .tx-msg{flex:1 1 auto;line-height:1.4}
.toast-item .tx-x{flex:0 0 auto;width:20px;height:20px;line-height:20px;text-align:center;border-radius:50%;background:rgba(255,255,255,.18);cursor:pointer;font-size:14px;user-select:none;font-family:Arial,sans-serif}
.toast-item .tx-x:hover{background:rgba(255,255,255,.32)}
.toast-item.ok{background:var(--green)} .toast-item.err{background:var(--red)}

/* 权限矩阵 */
.perm-grid{width:100%;border-collapse:collapse;font-size:13px}
.perm-grid th,.perm-grid td{border:1px solid var(--line);padding:8px 10px;text-align:center}
.perm-grid th{background:#f7f9fa}
.perm-grid td:first-child{text-align:left;font-weight:600;white-space:nowrap}

/* 项目/订单 搜索选择器 */
.op-list{max-height:200px;overflow-y:auto;overscroll-behavior:contain;border:1px solid var(--line);border-radius:8px;margin-top:6px;background:#fff}
.op-item{display:block;padding:8px 12px;cursor:pointer;border-bottom:1px solid #f0f3f5}
.op-item:last-child{border-bottom:none}
.op-item b{display:block;font-size:13px}
.op-item .op-sub{display:block;font-size:11.5px;color:var(--tx2);margin-top:1px}
.op-item:hover{background:var(--green-l)}
.op-item.on{background:var(--green-l);border-left:3px solid var(--green)}
.op-empty{padding:14px;text-align:center;font-size:12px;color:var(--tx2)}
.op-check{display:flex;align-items:flex-start;gap:8px}
.op-check input{margin-top:3px}
.op-item.sp-off{opacity:.5;cursor:not-allowed;background:#f7f8fa}
.op-item.sp-off:hover{background:#f7f8fa}
.op-item.sp-it{display:flex;align-items:center;gap:8px;padding:7px 12px}
.op-item.sp-it .op-sub{display:inline}
.sp-otitle{font-size:12px;font-weight:700;color:var(--tx2);padding:8px 4px 2px}
.sp-qtylab{font-size:12px;color:var(--tx2);white-space:nowrap}
.sp-qtylab input{padding:4px 6px;border:1px solid var(--line);border-radius:6px;width:64px}
/* ★ v7.03（老板 10-12 复盘：「③ 明细排版改得比以前还不好看，重新设计一下」）：
   v7.02 那版给每个字段加了**底色＋边框＋圆角** ⇒ 一屏几十个色块，反而比原来更花。
   本版**去掉所有装饰**，回到"文本行"的观感，只保留两件事：
     ① **字段名弱化**（浅灰）＋ 值正常色 —— 一眼分得清"哪个值是哪个字段"；
     ② **均匀间距 ＋ 自动换行**（gap 16px，取代原来的全角空格）—— 不再挤成一团。
   ⚠ 类名不变（.sp-f / .sp-fk / .sp-fs），所以 v7.02 的验证与 select 断言都不用改。 */
/* ★ v7.04（老板 10-12：「③ 里面的信息改成**上下布局**，列名称在上面、明细信息显示在下面」）——
   每个字段从「列名 值」左右并排，改成**上下两行**：上行淡色小字列名、下行值。
   一个字段一块，块与块之间自动换行排布（窄屏也好看）。 */
.op-item.sp-it .op-sub.sp-fs{display:flex;flex-wrap:wrap;gap:5px 18px;margin-top:4px}
.sp-f{display:inline-flex;flex-direction:column;font-size:12px;color:var(--tx1,#333);line-height:1.45;min-width:0;max-width:100%;word-break:break-word}
.sp-fk{color:var(--tx3,#98a2ae);font-size:10.5px;line-height:1.3;margin:0 0 1px}
.sp-f.sp-f-al{color:#b7791f;font-weight:600}
.order-link{color:var(--green);text-decoration:none;font-weight:600;font-size:12.5px}
.order-link:hover{text-decoration:underline}

/* 订单/明细 tab */
.tabs{display:flex;gap:4px;margin-bottom:14px;background:#eef1f4;border-radius:10px;padding:4px;width:fit-content}
.tabs a{padding:7px 18px;border-radius:8px;font-size:13px;color:var(--tx2)}
.tabs a.on{background:#fff;color:var(--green);font-weight:600;box-shadow:var(--shadow)}
/* v5.31（老板 2026-09-17）：回顶部按钮——固定在视口右上不随内容滚动；平时隐藏，
   下滑超过约一屏的 1/3（300px）才淡入，点击平滑回到顶部。手机端避开刘海安全区 */
.to-top{position:fixed;top:14px;right:20px;z-index:60;width:40px;height:40px;border:none;border-radius:50%;cursor:pointer;
  background:var(--green);color:#fff;font-size:19px;line-height:1;box-shadow:0 4px 14px rgba(0,0,0,.25);
  opacity:0;pointer-events:none;transform:translateY(-8px);transition:opacity .18s,transform .18s}
.to-top.show{opacity:1;pointer-events:auto;transform:none}
.to-top:hover{background:var(--green-d)}
@media(max-width:820px){.to-top{top:calc(12px + env(safe-area-inset-top,0px));right:14px;width:42px;height:42px;font-size:21px}}

/* 提示框 */
.tip-box{background:#f4f7f5;border:1px dashed #c9d8cf;border-radius:8px;padding:8px 10px;font-size:12.5px;line-height:1.6}

/* ===== v5.44：手机端友好的表单组件（老板 09-21：「要好操作、好看、美观」）=====
   设计口径（都是真机实测逼出来的）：
     · 勾选项/单选项一律做成**整块可点**的 ≥44px 目标 —— 原来勾选框 13px、label 整块 50×20px，
       手指点不准；长名字（「仓库管理」）还会被挤成 43×51px 的**竖条**。
     · 网格用 `auto-fill + minmax`：窄屏 2~3 列、宽屏自动铺满（工作日 7 项在 720px 弹窗里正好 1 行），
       零断点、不用写媒体查询。
     · 说明文字从 label 里挪到控件**下方**（`.fld-note`）—— label 里塞长说明是 sheet 折 3~4 行、
       把整块表单撑高的主因。
   ⚠ 与 v5.43 的关系：那一版在「添加法定假日」里用的是**内联** grid；这版把同一套形态固化成
     组件类，后续弹窗逐步从内联样式迁移过来（内联样式优先级高于媒体查询，迁到类里才可控）。 */
.chk-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(88px,1fr));gap:7px;margin-top:5px}
.chk-grid.wide{grid-template-columns:repeat(auto-fill,minmax(116px,1fr))}
.chk-grid>label{display:flex;align-items:center;gap:8px;padding:10px 11px;min-height:46px;border:1px solid var(--line);border-radius:9px;font-size:13px;cursor:pointer;min-width:0;box-sizing:border-box;margin:0;line-height:1.35;background:#fff}
.chk-grid>label>span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.chk-grid>label:active{background:var(--green-l)}
.chk-grid>label:has(input:checked){border-color:var(--green);background:var(--green-l)}
.opt-card{display:flex;align-items:center;gap:9px;padding:10px 12px;border:1px solid var(--line);border-radius:9px;font-size:13px;cursor:pointer;margin:0 0 7px 0;line-height:1.4;box-sizing:border-box;background:#fff}
.opt-card:last-child{margin-bottom:0}
.opt-card input{flex:0 0 auto}
.opt-card .oc-t{font-weight:600}
.opt-card .oc-s{display:block;color:var(--tx2);font-size:12px;margin-top:1px}
.opt-card:has(input:checked){border-color:var(--green);background:var(--green-l)}
.fld-note{color:var(--tx2);font-size:12px;line-height:1.45;margin-top:5px}

/* 汇总行 */
.sum-row td{background:var(--green-l);font-weight:600;border-top:2px solid var(--green)}

/* 配送移动卡片 */
.ship-card{background:#fff;border-radius:var(--radius);box-shadow:var(--shadow);padding:16px;margin-bottom:12px;cursor:pointer;border-left:4px solid var(--green)}
.ship-card.hold{border-left-color:var(--orange)}
.ship-card.done{border-left-color:#9aa8b2}
.ship-card h4{font-size:15px;margin-bottom:6px}
.ship-card .row{font-size:12.5px;color:var(--tx2);margin-bottom:3px}
.ship-card .row b{color:var(--tx);font-weight:500}

/* v3.37：我的施工卡片网格（桌面自适应多列，手机单列） */
.mc-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:12px;margin-top:12px}
.mc-areas{display:grid;grid-template-columns:repeat(auto-fill,minmax(210px,1fr));gap:10px}

/* 扫码 */
.scan-box{max-width:480px;margin:0 auto;text-align:center}
.scan-video{width:100%;border-radius:12px;background:#000;min-height:240px}

/* 响应式 */
@media(max-width:820px){
  /* ★ v6.53⑨（老板 10-09 22:xx）：「手机端点开导航栏后底下有个大白条」
     ⇒ 侧栏是 `position:fixed;top:0;bottom:0`；移动端浏览器「地址栏伸缩 / 视口高度变化」时
        `bottom:0` 会短一截 ⇒ 深蓝侧栏没铺到底，露出一条页面底色（浅灰）＝那条"大白条"。
     修法：显式给高度 —— `100vh` 兜底 + `100dvh`（动态视口，最准；不支持的浏览器自动忽略）。
     ⚠ 同时给了 top/bottom/height 时，height 优先、bottom 被忽略 ⇒ 老浏览器回落原行为，不会更糟。 */
  /* ★ v6.55⑪（老板 10-09 23:0x）：「导航栏底下的白条还在」＋「手机端点开导航栏有时候会闪退」
     ⇒ v6.53⑨ 给的那句 `height:100vh;height:100dvh` **就是新病根**，两处一起撤：
       · `dvh` 在安卓 WebView 上会随地址栏 / 键盘伸缩**反复重算**：算小的那一瞬侧栏没铺到底
         ⇒ 露出页面底色（那条白条）；每次重算又trigger一轮 layout/paint，
         在大页面（如元素图库一堆大图）上会把 WebView 拖崩 ⇒ 老板报的"闪退"。**同一个根**。
       · 侧栏本就是 `position:fixed;top:0;bottom:0`（包含块 = 视口）⇒ **不写 height 才最准**；
         要保险用 `min-height`（只会更高、不会更矮），别用固定 height。
     ★ 另加兜底：抽屉打开期间把**根元素**背景换成侧栏同色（`html.side-bg`，见 sideLockOn/Off）——
       万一还露底，露出来的是深蓝而不是浅色，白条从视觉上彻底消失。 */
  .sidebar{transform:translateX(-100%);
    /* ★ v6.55⑬（老板 10-09 23:x）：「手机界面顶部不要顶格」⇒ 顶上给 10px 底数再叠安全区。
       ⚠ 安卓的 env(safe-area-inset-top) 恒为 0（那套变量只认 iOS 刘海），所以原先等于**没留白**，
         抽屉里的品牌区几乎贴着状态栏。加底数后安卓有 10px、iOS 是 10+47px，都够。 */
    padding-top:calc(10px + env(safe-area-inset-top,0px));
    padding-bottom:calc(8px + env(safe-area-inset-bottom,0px));
    min-height:100%;min-height:100lvh}
  .sidebar.open{transform:none;box-shadow:0 0 40px rgba(0,0,0,.35)}
  /* ★ v6.71②**已撤销**（老板 10-10 17:3x：「微信打开导航栏现在也有一条白色半透明渐变的恒条了」）：
   原方案是给 `.sidebar` 的 `::before` 铺一条 72px「深蓝 → 画布色」竖向渐变，想让侧栏底边与画布连成一片。
   实测两个后果都不对：① PWA 里那条灰条没消失；② **微信里反而多出一条常驻的白色半透明渐变条**
   （微信是全屏 WebView、本来没有视口外区域，那条是这条渐变凭空造出来的）。
   ⇒ 整条撤除（`::before` 规则已删），侧栏恢复实心深蓝；微信里那条随之消失。
   ⚠ 教训：**划在视口内的装饰性渐变会同时出现在"没有画布问题"的容器里**，不能用来治那条。 */

  /* v4.7-25 R8：刘海屏适配——顶部留出安全区，左右留出圆角安全区 */
  .main{margin-left:0;padding:calc(14px + env(safe-area-inset-top,0px)) calc(14px + env(safe-area-inset-right,0px)) calc(90px + env(safe-area-inset-bottom,0px)) calc(14px + env(safe-area-inset-left,0px))}
  .hamburger{display:block;padding:6px 10px;margin:-6px -4px -6px -10px;font-size:24px;min-width:44px;min-height:44px;align-items:center;justify-content:center}
  .topbar{gap:8px}
  .grid2{grid-template-columns:1fr}
  .cards{grid-template-columns:repeat(2,1fr);gap:10px}
  .stat .val{font-size:20px}
  .kv{grid-template-columns:90px 1fr}
  table.tbl{min-width:640px}
  /* v3.37：我的施工手机端适配——卡片/区域网格单列，弹窗占满屏宽（tab 条当时为横向滚动）
     v5.31（老板 2026-09-17「手机端一排 tab 显示不全」）：tab 条由横向滚动改为自动换行——
     一行放不下就折到下一行，不用再滑动找 tab */
  .mc-grid{grid-template-columns:1fr}
  .mc-areas{grid-template-columns:1fr}
  .tabs{max-width:100%;flex-wrap:wrap;width:100%}
  .tabs a{white-space:nowrap;flex:none}
  .modal{max-height:calc(96vh - env(safe-area-inset-top,0px) - env(safe-area-inset-bottom,0px))}
  /* v7.11/v7.12：默认密码强制改密弹窗——遮罩高度由 visualViewport 钉住（见 _pwdMustVvSync），
     这里让它跟着遮罩走；`.mb` 收缩 / `.mf` 不收缩两条已提升到**全局**（Mac 也要生效） */
  .modal.pwd-must-modal{height:auto;max-height:100%}
  .pwm-save-row{justify-content:stretch}
  .pwm-save-row .btn{flex:1}
  .toolbar input,.toolbar select{flex:1;min-width:120px}
}
.scrim{position:fixed;inset:0;background:rgba(0,0,0,.3);z-index:40;display:none}
@media(max-width:820px){.scrim.show{display:block}}
/* ★ v6.57①：遮罩也按"大视口"兜底（同 .sidebar 的理由）；不支持 lvh 的浏览器自动忽略这一行。 */
@media(max-width:820px){.scrim{min-height:100lvh}}
/* ★ v6.56①（老板 10-09 深夜 → 10-10 凌晨，iPhone 截图）：「苹果手机底下白条还是在」
   【定性】把老板那张截图（1290×2796 = iPhone 14/15 Pro Max）**逐像素量**了一遍：
     · 底部自 y=2619 起（距底 177px = 59pt）**整条全宽**都是 (244,246,248) = 本页 --bg #f4f6f8；
     · 条内逐行极差恒为 0（**零内容**）⇒ 排除"浏览器工具栏"；
     · 若条内是"遮罩下的页面"，像素应是 --bg × 0.7 = (170,172,173)（该数值在同图左上角实测到过），
       但底条是**原色** ⇒ **遮罩也没盖到它**。
     ⇒ 结论：`.scrim`（inset:0）与 `.sidebar`（bottom:0）这两个 fixed 层，在这台机器的 iOS 上
       **都止步于距屏幕底 59pt** —— 该内核给 fixed 定位用的那套"视口高度"比物理屏幕矮一截。
     · 历史对照：v6.53 用 `height:100vh;height:100dvh`，而 **dvh = 当前可见视口 = 矮的那个**，
       把 vh 覆盖掉了 ⇒ 没治好（还引出滚动重排）；v6.55 撤掉两者改 `bottom:0 + min-height:100%`
       ⇒ 仍然落在同一个矮视口上。
   【修法】不和视口高度较劲 —— **只加"往下多铺一段"的涂层，布局一个字节不动**：
     两个 `.scrim`/`.sidebar` 各挂一个 `::after`，用 `top:100%` 贴在自己下边缘之下，再往下铺 160px
     （远大于任何机型的底部保留区）⇒ 底下露出来的是我们自己画的深蓝 / 遮罩色，白条从视觉上消失。
     ⚠ 千万别改成给容器 `height:calc(100% + 160px)`：那会把 flex 列里的 `.foot`
       （我的 / 退出 / 版本号）一起顶进底部留白里，反倒看不见了。::after 是绝对定位、
       不参与 flex 布局 ⇒ 对内容零副作用。
     ⚠ 只在 ≤820px 生效（桌面端侧栏常驻，不需要）。::after 不参与命中测试 ⇒ 不影响任何点击。 */
@media(max-width:820px){
  .sidebar::after{content:'';position:absolute;left:0;right:0;top:100%;height:160px;background:var(--green-d);pointer-events:none}
  /* ★ v6.61①：这一层的颜色与 body 那条"左抽屉深蓝 / 右遮罩页"渐变保持完全一致 ——
     谁生效都对、叠在一起也不变色（原来是半透明黑，压在渐变上会把颜色压暗）。
     详见文件后面 v6.61① 那段说明。 */
  .scrim::after{content:'';position:absolute;left:0;right:0;top:100%;height:160px;background:linear-gradient(to right,var(--green-d) 0,var(--green-d) 216px,#abacae 216px,#abacae 100%);pointer-events:none}
}
/* ★ v6.55⑪：抽屉打开期间的"露底兜底" —— 根元素背景换成侧栏同色（类由 sideLockOn/Off 挂摘）。
   ⚠ 只有 `html` 有背景时 body 背景才不再向 canvas 传播，所以这一条必须落在 html 上。
   只在抽屉打开时挂 ⇒ 平时页面背景一个字节不变。 */
html.side-bg{background:#abacae}
/* ★ v6.66①（老板 10-10 16:0x 发来测量页截图；此前已第 8 次抱怨这条）：
   实测数据（iPhone，PWA「添加到主屏幕」）：innerHeight 873 / screen.height 932 / safe-top 59 /
   `screen.height - innerHeight = 59`、`docEl.scrollHeight = 873`、safe-bottom 34。
   ⇒ **屏幕最底 873~932 那 59px 在网页视口之外**：`100vh / dvh / lvh` 都超不过视口，
     **任何内容都画不进去**（v6.53~v6.58 已证）——它不是"页面的元素"，而是**画布(canvas)露出的底色**。
   ⇒ 所以「把它取消掉」在技术上做不到；能做到的只有**让它和上面的内容看起来连成一体**。
   为什么 v6.61 那条渐变（写在 body 上）没生效：按本文件 v6.55 那段自己写的规则 ——
   **只有 html 有背景时 body 背景才不再向 canvas 传播**；而 `html.side-bg` 有背景（深蓝），
   于是 canvas 取的是 **html 的背景**（纯色）。v6.61 只给 body 加了渐变 ⇒ 白加；
   v6.62 把 html 的取色改成 #abacae ⇒ 那条才变灰（**这反过来证明 canvas 取的就是 html**）。
   修法：把**同一条横向渐变补到 `html.side-bg` 上**（左 216px = 抽屉深蓝、右边 = 遮罩后的页面色），
   canvas 就会拿到渐变 ⇒ 那条与上方内容**无缝**，看起来"没有这条"。
   ⚠ 零回退：老内核若只认 background-color（不认 canvas 的 image），
     html 的 color 仍是 #abacae ⇒ **与 v6.62 现状一模一样**，不会更差。
   ⚠ 上面那条纯色**原样保留**（既有断言钉着它，且它是"只认纯色"内核的兜底）。 */
html.side-bg{background-image:linear-gradient(to right,var(--green-d) 0,var(--green-d) 216px,#abacae 216px,#abacae 100%)}
/* ★ v6.58②（老板 10-10 08:x「这些打开方式都有」＝换哪种打开方式那条都在）
   对老板三张截图逐像素量的结果：那条 59pt 的颜色**精确等于 body 的底色 --bg #f4f6f8**
   （既不是 html 的、也不是 theme-color=#01276c、更不是浏览器工具栏那种带内容的条）——
   说明容器是拿「**页面（body）底色**」去填它自己预留的那一条。而我 v6.55~v6.57 只改了 html。
   修法：抽屉打开期间把 **body 的底色**也换成侧栏深蓝（这样容器不管采 html 还是 body 都采到深蓝）；
   同时用 `#app` 把**页面本体**重新刷回浅灰（#app 的盒子＝整页内容高度，盖住整个可视区），
   所以"打开导航栏时右边的页面变深蓝"这种副作用不会发生 —— 变的只是网页画不到的那一条。 */
html.side-bg body{background:var(--green-d)}
/* ★ v6.61①（老板 10-10 12:1x：「苹果保存到桌面打开的底下还是有蓝条，其他的没有了」）：
   「添加到主屏幕」的 PWA（standalone）里那条仍然醒目 —— 因为 v6.58 让"网页画不到的那一条"
   整屏都成了侧栏深蓝，而抽屉只占左边 216px，右边一大片本该是"遮罩下的页面"
   ⇒ 于是露出来的是一条**整宽蓝边**（Safari / 微信里它紧贴屏幕最底、看着像抽屉的延伸，
   所以老板当时说"没有了"；PWA 里没有地址栏、视口更高，这条就显出来了）。
   修法：那条区域只能靠 body 底层色控制（v6.58 已证），那就把它做成**横向渐变** ——
   左边 216px 延续抽屉深蓝（= 侧栏宽度），右边接遮罩后的页面色
   （--bg #f4f6f8 叠 rgba(0,0,0,.3) ⇒ RGB(171,172,174) #abacae）⇒ 与上面的内容无缝衔接。
   ⚠ 横向渐变在垂直方向恒定 ⇒ 不依赖 body 实际高度、也不怕屏幕最底落在 body 的哪一段。
   ⚠ 上面那条纯色**原样保留**：只认 background-color 的内核（老 iOS）继续走深蓝兜底，
     绝不会因此回退成"白条"。（分两条写还有一个好处：既有断言钉的是那条纯色，一动不该动它。） */
/* ★ v6.62①（老板 10-10 14:3x：「苹果添加到桌面的底下还是有一个蓝色条」）：
   v6.61 那条渐变**没起作用** —— 因为按本文件 v6.55 那段自己写的规则：
   **只有 html 有背景时 body 背景才不再向 canvas 传播** ⇒ 而 `html.side-bg` 是深蓝，
   于是"网页画不到的那一条"（overscroll / canvas 区）取的是 **html 的背景**（深蓝）⇒ 整宽蓝条。
   （v6.61 只改了 body 的 image，color 仍是深蓝，两条都是蓝 ⇒ 看着当然没变。）
   修法：把那一条的**取色源本身**换成「遮罩后的页面色」#abacae（--bg #f4f6f8 叠 rgba(0,0,0,.3)）——
   · `html.side-bg`  → #abacae（canvas 走这条）
   · `html.side-bg body` 同时给 background-color:#abacae（容器若采 body，也采到灰）
   为什么选灰不选深蓝：那条**整宽只能一个颜色**（不可绘制，v6.53~v6.58 已证），
   而抽屉打开时右侧一大片是遮罩灰、只有左边 216px 是抽屉深蓝 ⇒ **按面积选灰**，
   大面积与上方内容无缝；抽屉正下方那 216px 会露一小块灰（比整宽蓝条轻得多）。
   ⚠ 三条老规则（html / body 纯色 / #app 回刷浅灰）**都保留**，只换了取色值。 */
html.side-bg body{background:#abacae;background-image:linear-gradient(to right,var(--green-d) 0,var(--green-d) 216px,#abacae 216px,#abacae 100%)}
html.side-bg #app{background:var(--bg)}

/* ★ v6.69①（老板 10-10 16:4x：「底下那条灰色的删不了吗，问题是之前没有，这几天才出现，
   苹果微信里进网站没有这一条」）—— 把「那条」从根上治掉：让画布(canvas)改取 body 的背景。

   ── 那条是什么：PWA（添加到主屏幕）里 **网页视口之外、窗口画布之上** 的那一圈
      （实测 innerHeight 873 / screen.height 932 ⇒ 59pt）。它**不是页面元素**，
      往里塞任何东西都不渲染（v6.53~v6.58 逐条证过）；颜色**只由「根元素(html) 的背景」决定**
      （html 没有背景时，按 CSS 背景传播规则取 body 的背景）。

   ── 为什么"之前没有"：v6.55 之前 `html` **没有背景** ⇒ canvas 一直取 body 的 `--bg #f4f6f8`
      ＝ 页面主体色 ⇒ 那条与页面同色、看不出来。v6.55 为修抽屉露底给 `html.side-bg` 染了色
      ⇒ **html 一有背景，body 就不再向 canvas 传播** ⇒ canvas 改取 html 的纯色
      （先是深蓝、v6.62 起是 #abacae 灰）⇒ 那条从此显形。**"这几天才出现"就是从 v6.55 开始的。**

   ── 为什么"微信里没有"：微信内置浏览器是全屏 WebView，视口＝整屏，
      **压根没有视口外区域** ⇒ 无此条。

   ── 修法：把 html 的背景**整个置空**（image:none ＋ color:transparent）
      ⇒ 传播规则重新生效 ⇒ canvas 改取 **body 的背景**：
        · 平时       → body = var(--bg) #f4f6f8 ＝ 页面主体色 ⇒ 那条**彻底看不出来**（＝回到"之前没有"）；
        · 抽屉打开时 → body = #abacae ＋ 横向渐变（左 216px 抽屉深蓝 / 右遮罩灰）
                       ⇒ 整条渐变都会被画到 canvas ⇒ 与上方**逐像素同色**；
                       ⇒ 万一只认纯色，则等于现状 #abacae（**零回退**，不比现在差）。

   ⚠ 上面两条「html.side-bg 的纯色 / 渐变」**原样保留**
     （v6.55/v6.62 的既有断言钉着它们，且是"只认纯色的内核"的兜底）；
     本规则写在**它们之后** ⇒ 同特异性、后出现者胜 ⇒ 实际生效的是本条。
   ⚠ 纯 CSS，不动任何 DOM/JS 结构；桌面端（>820px）本就不挂 side-bg，零影响。 */
html.side-bg{background:transparent}

/* ★ v6.70①（老板 10-10 17:0x：「v6.69 导航栏打开后底下那个灰条还在」）
   —— 把抽屉打开时 body 的**背景色**也改回页面主体色，画布(canvas)从此不再是"那条灰"。

   ── 这条反馈把机制钉死了：v6.69 已把 html 的背景置空（传播规则复活），画布改取 **body 的背景**；
      而 body 在抽屉打开时是 #abacae（v6.62 定的"按面积选灰"）⇒ 画布 = 灰 ⇒ 看到的就是这条灰。
   ── 由此**反证**：iOS 给画布上色时**只认 background-color、不认 background-image**
      （v6.66 把同款横向渐变补到 html、v6.69 补到 body，两次都"没变化" ⇒ 渐变从未被采用）。
   ── 又因为老板说"之前没有"：v6.55 之前 html 没有背景 ⇒ 画布一直取 body 的 `--bg` #f4f6f8
      ＝ 页面主体色 ⇒ 那条与页面同色、根本看不出来。**这才是他记忆里的"没有"。**

   ── 修法：只补一行（就在本注释下方）—— 把 body 的**背景色**设回 `var(--bg)`
      · 只覆盖 body 的 **color** —— 上一条（v6.66）里那份横向渐变**原样留着**，
        万一遇到认渐变的内核，画布依旧是"左抽屉深蓝 / 右遮罩灰"的逐像素无缝版；
      · 只认纯色的内核（实测就是 iOS）拿到的是 `--bg` ⇒ 与页面主体同色 ⇒ 看不出有这条。
   ⚠ 既有规则一条都没删没改（v6.55/v6.62/v6.66 的断言钉着它们）；本行写在最后 ⇒ 同特异性、后出现者胜。
   ⚠ 纯 CSS，不动 DOM/JS；桌面端（>820px）本就不挂 side-bg，零影响。 */
html.side-bg body{background-color:var(--bg)}

/* ★ v6.71①（老板 10-10 17:1x：「打开导航栏之后底下上来的横条又变成白色了」）
   —— 画布(canvas)回"遮罩后的页面色"。
   v6.70 让画布取到了 `--bg`（页面本色 #f4f6f8），可导航栏一开，页面上方被遮罩压暗成 #abacae
   ⇒ 白的那条对比反而更强。改回"遮罩后的页面色" ⇒ **右侧一大片与画布逐像素同色**。
   ⚠ v6.70 那条**原样保留**（它钉在断言里）；本行写在最后 ⇒ 同特异性、后出现者胜。 */
html.side-bg body{background-color:#abacae}

/* ★ v6.71③（老板 10-10 17:2x：「导航栏打开后左下角的视口和扫码就绪取消显示」）
   —— 侧栏左下角那个「扫码就绪」徽标（`#gun-st`）取消显示。
   ⚠ 只隐藏**显示**：v5.92 的启动就绪自检（scanGunReady）照常跑、照常写 title，
     车间那套逻辑一个字没动；需要时把这条删掉即恢复。 */
#gun-st{display:none}

/* ★ v6.77①（老板 10-10 18:0x 发来 4 张截图：「**是整条宽都没内容**」＋
   「其他界面底下这个位置**是可以显示内容的**」）—— 逐像素量完这 4 张图，机制彻底闭环，本条收口。

   【量出来的事实】（截图 1290×2796 = 430×932pt；设备 3x）
     · 问题图（抽屉打开）：底部自 y=2619（距底 177px = **59pt**）起，**整条全宽**恒为画布色、零内容；
       同图里侧栏那条深蓝在 y=2618 还在、y=2620 就没了 ⇒ 侧栏/遮罩/页面内容**全部止步于 59pt 之前**。
     · 对照图（工作台 / 订单列表，抽屉关着）：内容**一直画到 y=2795（屏幕最底）**，没有任何纯色带。
   ⇒ 结论（与 v6.56 那次量到的完全一致）：这台机器上网页的**可视视口比物理屏幕矮 59pt**；
     最底那 59pt **只有"正常流内容"和"画布(canvas)"画得进去**，
     **position:fixed 的层（侧栏、遮罩，以及它们的全部后代，含 ::after）一律被裁掉**
     —— 这也正是 v6.56 那条「贴在自己下边缘往下铺」当年"实测无效"的原因。

   【为什么只有"打开导航栏"才看得见】
     · 平时：画布 = 页面主体色 ⇒ 与页面同色 ⇒ 看不出来（老板说"其他界面没有"就是这个）；
     · 打开导航栏：遮罩把页面上方压暗，**却压不到视口外那块画布** ⇒ 出现色差 ⇒ 条显形。
     · 微信是全屏 WebView、没有这块"视口外区域" ⇒ 那边从来就没有（老板已确认）。

   【收口办法】只动颜色，一个像素的布局都不动：
     · 把画布色从"遮罩灰"改成**侧栏深蓝**（本行）⇒ 左侧 216px（侧栏正下方）与侧栏**同色、无缝**；
     · 给**遮罩**在视口内最底部铺一条 `--vp-gap` 高的**竖向渐变（透明 → 侧栏深蓝）**（见下面那段）
       ⇒ 右侧那片页面的底边**逐渐**接到深蓝 ⇒ 到视口最底那一行，左右两侧**同时等于画布色**
       ⇒ **整条宽的硬边消失**（数学上必然无缝：渐变末色 = 画布色 = 侧栏色，三者在同一行重合）。
     · `--vp-gap` 由 app.js 算出并写到根元素（＝ 物理屏幕高 − 可视视口高；非"添加到主屏幕"环境为 0）
       ⇒ **其它平台这段渐变的等效高度为 0，一个字节都不生效**（微信 / 安卓 / 桌面端零影响）。
   ⚠ 渐变必须铺在**视口内**（贴遮罩自己的下边、往上 59px）—— 这是唯一"画得进去"的位置；
     贴在下边往下铺（v6.56 的做法）会落在视口外，浏览器根本不渲染，所以当年无效。
   ⚠ 只改颜色，不动 DOM/JS 结构；既有规则一条没删没改（v6.55~v6.71 的断言仍钉着它们）。 */
html.side-bg body{background-color:var(--green-d)}


/* ★ v6.78①（老板 10-10 18:4x 发来屏幕录像：「我在右边往下滑的时候底下那个宽横条会往下移」）
   —— **v6.77 那条接色渐变挂错了层，根本没渲染**，改挂到 `body.side-lock::after`。

   【为什么 v6.77 没生效】它挂在 `.scrim::before`（`bottom:0`），而 `.scrim` 带
     `min-height:100lvh` —— 这台机器上 `lvh` 比可视视口高（≥屏幕高）⇒ 遮罩的盒子
     比视口**更高** ⇒ `bottom:0` 落在**视口之外**（就是那条"画不进去"的死带）
     ⇒ **一个像素都没画出来**（录像里底部是"整条纯深蓝、带硬边"，
     正是只剩画布、没有渐变的样子）。
   ⇒ **铁律：凡是要在视口内渲染的装饰，必须挂在"盒子就等于可视视口"的元素上。**
     抽屉打开时 **`body.side-lock`** 正是：它是 `position:fixed`、高度 = `100vh + 已滚距离`
     ⇒ 它的**底边正好压在视口底**（就是屏幕上看得到的最下一行）。

   ⚠ 只铺 **x ≥ 216px（页面区）**：左侧 216px 是侧栏，它本身与画布同色
     （都是侧栏深蓝）⇒ 无需渐变；而**铺满整宽会把侧栏底部的
     「我的 / 退出 / 版本号」一起蒙掉**（v6.75 就是这么翻车的）。
   ⚠ 渐变末色 = 侧栏深蓝 = 画布色 = 侧栏色 ⇒ 视口最底那一行**左右两侧重合**
     ⇒ 硬边消失。
   ⚠ `--vp-gap` 在非「添加到主屏幕」环境为 0 ⇒ 本条的等效高度为 0，
     **其它平台一个字节不生效**。 */
body.side-lock::after{content:'';position:absolute;left:216px;right:0;bottom:0;height:var(--vp-gap,0px);background:linear-gradient(to bottom,rgba(0,26,74,0),var(--green-d) 100%);pointer-events:none}

/* ★ v6.78②（同一封反馈：「往下滑的时候底下那个宽横条会往下移」）
   —— 抽屉打开期间**禁掉 iOS 的「橡皮筋回弹」**。
   回弹会把**整页（连同画布）一起位移**，而那条死带的边界是**钉在屏幕上不动**的
   ⇒ 看上去就是"那条横条跟着手指上下跑"。
   ⚠ 只在抽屉打开时（`html.side-bg`）挂上；JS 侧另有一道 `touchmove` 兑底。 */
html.side-bg{overscroll-behavior:none}

/* ★ v6.79①（代登录）—— 顶部常驻红条 + 让页面/侧栏给它让出高度。
   ⚠ 红条放**顶部**：底部那条在 iOS「添加到主屏幕」里会被"死带"裁掉（见 v6.76/v6.78 的教训），
     所以凡是"必须看得见"的常驻提醒，一律贴顶。
   ⚠ 高度用 `--imp-h`（app.js 量出来写到根元素），并叠加 `env(safe-area-inset-top)` ⇒ 刘海屏也不压内容。 */
#imp-bar{position:fixed;left:0;right:0;top:0;z-index:96;display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:10px;
  background:#b71c1c;color:#fff;font-size:12.5px;line-height:1.4;padding:calc(6px + env(safe-area-inset-top,0px)) 12px 6px;
  box-shadow:0 2px 10px rgba(0,0,0,.28)}
html.imp-on .main{padding-top:calc(var(--imp-h,38px) + 14px + env(safe-area-inset-top,0px))}
html.imp-on .sidebar{padding-top:calc(var(--imp-h,38px) + 10px + env(safe-area-inset-top,0px))}







/* v5.45：手机端「抽屉导航」打开时锁住页面滚动
   （老板 09-21 实测：「手机端把导航栏打开了，在导航栏上滑动时后面的页面还在跟着滑动」）
   · .side-lock 由 app.js 的 sideLockOn/Off 加在 body 上，并用内联 top:-滚动Y 保住视觉位置
     （position:fixed 是 iOS Safari 上**唯一可靠**的锁法；body{overflow:hidden} 在 iOS 上不拦页面滚动）。
   · overscroll-behavior:contain 断掉「滚动链」：导航列表滚到顶 / 底后继续滑，
     滚动不再交给后面的页面 —— 这才是「在导航栏上滑动、后面页面跟着动」的直接原因。
   · 只在 ≤820px 生效：桌面端侧边栏是**常驻**的，既不需要锁、也不该锁
     （桌面端在侧边栏上滚动到底后继续滚，把页面带走同样是毛病，但常驻侧边栏的
     滚动链问题另说，不在本次口径内 —— 免得改了桌面端行为又出一版回归）。 */
@media(max-width:820px){
  body.side-lock{position:fixed;left:0;right:0;width:100%;overflow:hidden;min-height:100vh} /* ★ v6.76①：兜底 —— 锁定期间至少一屏高（JS 还会按已滚距离再撑高），否则内容矮的页面下方会空出一块、露出底色（＝老板报的那条横条） */
  .sidebar .nav{overscroll-behavior:contain}
}

/* v4.7-28 R1：iOS 主屏图标（standalone）刘海屏安全区适配
   toast / 登录页 / 侧边抽屉让位状态栏与刘海（需 viewport-fit=cover，app.js 启动时自动补）；
   sa-fallback = env() 未生效时（老 index.html 无 viewport-fit 或系统异常）的固定兜底留白 24px */
#toast{top:calc(18px + env(safe-area-inset-top,0px))}
.login-wrap{padding:calc(20px + env(safe-area-inset-top,0px)) calc(20px + env(safe-area-inset-right,0px)) calc(20px + env(safe-area-inset-bottom,0px)) calc(20px + env(safe-area-inset-left,0px))}
@media(max-width:820px){
  html.sa-fallback .main{padding-top:calc(14px + 24px)}
  html.sa-fallback .sidebar{padding-top:24px;padding-bottom:16px} /* ★ v6.55⑪：高度交回 top:0;bottom:0（不再写 height，理由同上面那段） */
  html.sa-fallback #toast{top:calc(18px + 24px)}
  html.sa-fallback .login-wrap{padding-top:calc(20px + 24px)}
  /* v4.7-37：env() 未生效兜底时，弹窗也固定留白避开刘海 */
  html.sa-fallback .overlay{padding-top:calc(16px + 24px);padding-bottom:calc(16px + 24px)}
  html.sa-fallback .modal{max-height:calc(92vh - 48px)}
}

/* ============ v4.7-84 视频会议 ============ */
.meet-lobby{max-width:480px;margin:0 auto;padding:20px;display:flex;flex-direction:column;gap:16px}
.meet-hero{text-align:center;padding:30px 0}
.meet-hero-ic{font-size:48px;margin-bottom:8px}
.meet-hero h2{font-size:20px;margin-bottom:4px}
.meet-hero p{color:var(--tx2);font-size:13px}
.meet-card{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);padding:20px;box-shadow:var(--shadow)}
.meet-card h3{font-size:15px;margin-bottom:8px}
.meet-hint{color:var(--tx2);font-size:12px;margin-bottom:14px}
.meet-input{width:100%;padding:12px 14px;border:1px solid var(--line);border-radius:8px;font-size:16px;letter-spacing:2px;text-transform:uppercase;margin-bottom:10px}
.meet-input:focus{outline:none;border-color:var(--blue)}
.meet-active{display:flex;flex-direction:column;height:calc(100vh - 100px)}
.meet-topbar{display:flex;align-items:center;gap:12px;padding:10px 0;flex-shrink:0}
.meet-room-info{font-size:13px;color:var(--tx2)}
.meet-room-info b{color:var(--tx);font-size:16px;letter-spacing:1px}
.meet-online{font-size:12px;color:var(--tx2);background:var(--green-l);padding:3px 8px;border-radius:6px}
.meet-videos{flex:1;display:grid;gap:8px;overflow:hidden;align-content:center;min-height:200px}
.meet-video{position:relative;background:#000;border-radius:8px;overflow:hidden;aspect-ratio:16/9;display:flex;align-items:center;justify-content:center}
.meet-video video{width:100%;height:100%;object-fit:cover}
.meet-video-label{position:absolute;bottom:6px;left:6px;background:rgba(0,0,0,.6);color:#fff;font-size:11px;padding:2px 8px;border-radius:4px}
.meet-video-local video{transform:scaleX(-1)}
/* v4.7-85：无摄像头观看模式——本地画面占位提示（黑屏不如一句人话） */
.meet-video-nocam{background:var(--bg2,#f2f4f8)}
.meet-video-nocam video{display:none}
.meet-nocam-note{color:var(--tx2);font-size:13px;text-align:center;line-height:1.7;white-space:pre-line;padding:0 14px}
.meet-controls{display:flex;justify-content:center;gap:18px;padding:14px 0 10px;flex-shrink:0;align-items:flex-start;flex-wrap:wrap}
.meet-ctrl{display:flex;flex-direction:column;align-items:center;gap:5px;background:transparent;border:none;padding:0;margin:0;cursor:pointer;color:var(--tx1);transition:transform .15s}
.meet-ctrl:hover{transform:translateY(-2px)}
.meet-ctrl .meet-ic{width:54px;height:54px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:24px;line-height:1;background:var(--card);border:1px solid var(--line);box-shadow:0 4px 12px rgba(15,40,70,.10);color:var(--tx1);transition:all .2s}
.meet-ctrl:hover .meet-ic{background:var(--bg);box-shadow:0 6px 16px rgba(15,40,70,.15)}
.meet-ctrl.on .meet-ic{background:var(--green);color:#fff;border-color:var(--green)}
.meet-ctrl.on .meet-lb{color:var(--green)}
.meet-ctrl.off .meet-ic{background:var(--line);color:var(--tx2)}
.meet-ctrl.off .meet-lb{color:var(--tx2)}
.meet-ctrl .meet-lb{font-size:12px;line-height:1;color:var(--tx2);white-space:nowrap}
.meet-ctrl .meet-txt{display:none}
.meet-ctrl-hangup .meet-ic{background:var(--red);color:#fff;border-color:var(--red)}
.meet-ctrl-hangup .meet-lb{color:var(--red)}
.meet-ctrl-hangup:hover .meet-ic{background:#c6392f}
/* 录制按钮（录制中红底闪烁） */
.meet-ctrl.rec-on .meet-ic{background:var(--red);color:#fff;border-color:var(--red);animation:meetRecPulse 1.6s infinite}
.meet-ctrl.rec-on .meet-lb{color:var(--red)}
@media(max-width:820px){
  .meet-lobby{padding:14px}
  .meet-hero{padding:16px 0}
  .meet-hero-ic{font-size:36px}
  .meet-active{height:calc(100vh - 80px)}
}

/* ===== v4.7-89 需求4：视频会议对标腾讯会议——成员/聊天侧栏 + 演讲者视图 ===== */
/* 主区行容器：左视频 + 右侧栏，侧栏开合自动挤压视频区 */
.meet-mid{flex:1;display:flex;min-height:0;gap:8px}
.meet-mid .meet-videos{flex:1;min-width:0}
/* 侧栏抽屉（width 过渡收展） */
.meet-side{width:0;flex:none;overflow:hidden;transition:width .25s ease;display:flex;flex-direction:column;background:var(--card);border:1px solid var(--line);border-radius:8px}
.meet-side.open{width:272px}
.meet-side-hd{display:flex;align-items:center;justify-content:space-between;padding:10px 12px;font-weight:600;font-size:13px;border-bottom:1px solid var(--line);flex-shrink:0;white-space:nowrap}
.meet-side-x{border:none;background:none;cursor:pointer;font-size:14px;color:var(--tx2);padding:2px 4px;line-height:1}
.meet-side-x:hover{color:var(--tx)}
.meet-side-bd{flex:1;overflow-y:auto;min-height:0}
.meet-side-ft{display:flex;gap:6px;padding:10px;border-top:1px solid var(--line);flex-shrink:0}
/* 成员列表 */
.meet-member{display:flex;align-items:center;gap:8px;padding:9px 12px;border-bottom:1px solid var(--line);font-size:13px}
.meet-member:last-child{border-bottom:none}
.meet-member-name{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.meet-member-tag{font-size:10px;background:#e8f0fb;color:var(--blue);padding:1px 6px;border-radius:3px;flex:none}
/* 公屏聊天 */
.meet-chat-list{padding:10px;display:flex;flex-direction:column;gap:10px}
.meet-chat-empty{color:var(--tx2);font-size:12px;text-align:center;padding:20px 0}
.meet-chat-msg{display:flex;flex-direction:column;gap:2px;max-width:100%}
.meet-chat-msg.me{align-items:flex-end}
.meet-chat-from{font-size:11px;color:var(--tx2)}
.meet-chat-msg.me .meet-chat-from{color:var(--green)}
.meet-chat-text{font-size:13px;line-height:1.5;background:var(--bg);padding:6px 10px;border-radius:8px;word-break:break-all}
.meet-chat-msg.me .meet-chat-text{background:var(--green-l)}
.meet-chat-input{flex:1;min-width:0;padding:8px 10px;border:1px solid var(--line);border-radius:8px;font-size:13px}
.meet-chat-input:focus{outline:none;border-color:var(--green)}
/* 演讲者视图：焦点大画面占满第一行，其余小窗底部横排；点击视频窗切换焦点（meetSpotSet） */
.meet-videos.speaker{display:grid;grid-template-rows:1fr auto;grid-template-columns:repeat(auto-fill,minmax(140px,1fr));align-content:stretch;align-items:stretch;gap:8px}
.meet-videos.speaker .meet-spot{grid-column:1/-1;grid-row:1;aspect-ratio:auto;min-height:0;cursor:default;outline:2px solid var(--green)}
.meet-videos.speaker .meet-video:not(.meet-spot){grid-row:2;height:84px;aspect-ratio:auto;cursor:pointer}
/* 手机端：侧栏改为覆盖式（不挤压本就狭小的视频区） */
@media(max-width:820px){
  .meet-mid{position:relative}
  .meet-side{position:absolute;right:0;top:0;bottom:0;z-index:5;box-shadow:-4px 0 16px rgba(20,40,60,.12)}
  .meet-videos.speaker .meet-video:not(.meet-spot){height:64px}
}

/* ===== v4.7-87：发货单详情「物流轨迹」时间线（快递100 查询结果展示） ===== */
.exp-tl{position:relative;padding:2px 0 2px 4px;margin:2px 0 4px}
.exp-tl:before{content:'';position:absolute;left:13px;top:10px;bottom:10px;width:2px;background:var(--line);border-radius:1px}
.exp-row{position:relative;padding:0 0 14px 34px}
.exp-row:last-child{padding-bottom:2px}
.exp-dot{position:absolute;left:7px;top:5px;width:10px;height:10px;border-radius:50%;background:var(--line);border:2px solid #fff;box-shadow:0 0 0 1px var(--line)}
.exp-row:first-child .exp-dot{background:var(--blue);box-shadow:0 0 0 1px var(--blue)}
.exp-time{font-size:11.5px;color:var(--tx2);line-height:1.5}
.exp-ctx{font-size:13px;line-height:1.55;margin-top:1px;word-break:break-all}
@media(max-width:820px){ .exp-row{padding-left:30px} .exp-tl:before{left:11px} .exp-dot{left:5px} }

/* ===== v4.7-93：小样明细搜索选择 + 行内输入样式 ===== */
#smpd_rows input,#smpd_rows select{padding:6px 8px;border:1px solid var(--line);border-radius:6px;font-size:13px;background:#fff;color:var(--tx);min-width:0}
#smpd_rows input:focus,#smpd_rows select:focus{outline:none;border-color:var(--green)}
#smpd_rows input[readonly]{background:var(--bg2,#f4f6f8)}
#smpd_rows td .srchw{display:flex;width:100%}
.smpd-srch{width:100%}
#smpd_drop{position:fixed;z-index:120;background:#fff;border:1px solid var(--line);border-radius:8px;box-shadow:0 8px 24px rgba(15,40,70,.18);max-height:760px;overflow-y:auto;overscroll-behavior:contain;min-width:300px} /* v6.14：放大 264→420 / 220→300；v6.15①：高度改由 JS 按「老板拉过的尺寸」设（存 DB.settings.smpdDropSize），max-height 放宽到上限 760 */
/* ★ v7.22：这个浮窗**宽窄和高低都能拖**（smpdGripDown 同时改 width/height）⇒ 按老板规则
   应该是**右下角斜杠三角**（原来是贴底一条 + 右边 ◢，形态与能力不匹配）。
   斜纹/光标沿用上面 `.pz-grip-w` 的共用规则；这里只给几何与定位。
   ⚠ #smpd_drop 是 position:fixed ⇒ 绝对定位子元素以它为参照；容器自身 overflow-y:auto 不受影响。 */
#smpd_drop .sd-grip{position:absolute;right:0;bottom:0;width:22px;height:22px;z-index:5;cursor:nwse-resize;touch-action:none;border-radius:0 0 8px 0}
#smpd_drop .sd-it{padding:10px 14px;cursor:pointer;border-bottom:1px solid #f0f3f5} /* v6.14：内边距放大 */
#smpd_drop .sd-it:last-child{border-bottom:none}
#smpd_drop .sd-it b{display:block;font-size:14.5px} /* v6.14：主行字号 13→14.5 */
#smpd_drop .sd-it .sd-sub{display:block;font-size:12.5px;color:#5a6672;margin-top:2px} /* v6.14：副行（含🏭仓库名）字号 11.5→12.5，颜色加深便于分辨同名不同仓 */
#smpd_drop .sd-it:hover,#smpd_drop .sd-it.on{background:var(--green-l)}
#smpd_drop .sd-empty{padding:14px;text-align:center;font-size:12px;color:var(--tx2)}
/* ===== v7.78（老板 10-16 14:08）：「加工外单 → 产品明细」的产品格改成**行内搜索选择**
   （原话「选择产品明细参考小样订单里的选择小样」）。同一套"搜索选择"观感，
   ⚠ **独立成一条规则，不改 #smpd_drop 任何一行**（那族被专测按原文断言着 —— v7.25 立下的规矩）。 */
#eo_drop{position:fixed;z-index:120;background:#fff;border:1px solid var(--line);border-radius:8px;box-shadow:0 8px 24px rgba(15,40,70,.18);max-height:420px;overflow-y:auto;overscroll-behavior:contain;min-width:320px}
#eo_drop .sd-it{padding:10px 14px;cursor:pointer;border-bottom:1px solid #f0f3f5}
#eo_drop .sd-it:last-child{border-bottom:none}
#eo_drop .sd-it b{display:block;font-size:14.5px}
#eo_drop .sd-it .sd-sub{display:block;font-size:12.5px;color:#5a6672;margin-top:2px}
#eo_drop .sd-it:hover,#eo_drop .sd-it.on{background:var(--green-l)}
#eo_drop .sd-empty{padding:14px;text-align:center;font-size:12px;color:var(--tx2)}
.eo-srch{width:100%;box-sizing:border-box}
/* ===== v7.25（老板 10-13）：「编辑小样明细」的「＋ 添加产品」面板 =====
   搜产品 → 点选 → 填数量/备注 → 「＋ 添加到明细」。
   ⚠ 复用 #smpd_drop 那一族的 .sd-it / .sd-sub / .sd-empty 观感（同一套"搜索小样"语言），
     但**单独成一条规则**（不改 #smpd_drop 任何一行 —— 那族被专测按原文断言着）。 */
#smpd_add{position:fixed;z-index:130;left:50%;top:50%;transform:translate(-50%,-50%);width:560px;max-width:94vw;height:520px;max-height:82vh;background:#fff;border:1px solid var(--line);border-radius:12px;box-shadow:0 16px 48px rgba(15,40,70,.28);display:flex;flex-direction:column;overflow:hidden}
#smpd_add .sda-hd{flex:0 0 auto;padding:11px 14px;font-size:14px;font-weight:700;border-bottom:1px solid var(--line);display:flex;align-items:center}
#smpd_add .sda-x{margin-left:auto;cursor:pointer;font-size:20px;line-height:1;color:var(--tx2);padding:0 4px}
#smpd_add .sda-x:hover{color:var(--red)}
#smpd_add .sda-srch{flex:0 0 auto;padding:10px 12px;border-bottom:1px solid #f0f3f5}
#smpd_add .sda-srch input{width:100%;box-sizing:border-box;font-size:14px}
#smpd_add .sda-list{flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain}
#smpd_add .sd-it{padding:10px 14px;cursor:pointer;border-bottom:1px solid #f0f3f5}
#smpd_add .sd-it:last-child{border-bottom:none}
#smpd_add .sd-it b{display:block;font-size:14.5px}
#smpd_add .sd-it .sd-sub{display:block;font-size:12.5px;color:#5a6672;margin-top:2px}
#smpd_add .sd-it:hover,#smpd_add .sd-it.on{background:var(--green-l)}
#smpd_add .sd-empty{padding:14px;text-align:center;font-size:12px;color:var(--tx2)}
#smpd_add .sda-ft{flex:0 0 auto;padding:10px 12px;border-top:1px solid var(--line);display:flex;gap:10px;align-items:center;flex-wrap:wrap;background:#fafbfc}
#smpd_add .sda-picked{flex:1 1 130px;min-width:0;font-size:12.5px;color:var(--tx2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
#smpd_add .sda-ft label{font-size:12.5px;color:var(--tx2);display:flex;align-items:center;gap:5px}
#smpd_add .sda-ft input{width:80px;font-size:13px}
#smpd_add .sda-ft #smpd_add_note{width:110px}
@media screen and (max-width:820px){
  #smpd_add{width:94vw;height:auto;max-height:82vh}
  #smpd_add .sda-picked{flex-basis:100%}
  #smpd_add .sda-ft input{width:70px}
  #smpd_add .sda-ft #smpd_add_note{width:96px}
}
/* v6.15②：采购单详情里的两个照片上传区（复用 imp-pick 组件，但收成紧凑一行） */
.tpo-pick .imp-pick{padding:8px 12px;min-height:auto;justify-content:flex-start;text-align:left;margin:4px 0 6px}
.tpo-pick .imp-pick .imp-pick-ic{font-size:18px}
.tpo-pick .imp-pick .imp-pick-tx{font-size:12.5px}
.tpo-pick .imp-pick .imp-pick-tx span{display:none}
/* v5.05：列宽由 class 承担（原先写在各 td 的行内样式上）。行内样式优先级高于媒体查询，
   留在 HTML 里会把手机端卡片宽度钉死成桌面尺寸，所以必须挪到样式表。 */
#smpd_rows .smpd-td-name{min-width:170px}
#smpd_rows .smpd-td-wt input{width:64px}
#smpd_rows .smpd-td-qty input{width:76px}
#smpd_rows .smpd-td-rem input{min-width:70px}

/* ===== v5.05 需求2：手机端「小样明细录入」改卡片式（一个屏幕能填完一条） =====
   做法刻意选**纯 CSS 变形**：DOM 仍是 `<tr class="smpd-row"><td data-lbl>`，只在窄屏把
   table/tr/td 改成块级并用 `td:before{content:attr(data-lbl)}` 印字段名。
   为什么不动 DOM：6 套历史回归（v130/v158/v163/v167/v219/v257）都依赖
   `#smpd_rows tr.smpd-row` 选择器与 `[data-k]` 取值契约，换结构等于把既有回归全部推倒。 */
@media(max-width:820px){
  /* 说明文字最多两行（手机上一屏要留给卡片，这两段第一次看过就不用再占地方） */
  .smpd-tip{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;line-height:1.5}
  .smpd-wrap{overflow:visible}
  /* 表格整体降为块级 —— 不再横向滚动
     ⚠ 必须连 **tbody** 一起降级：只改 table/tr/td 不够，`tbody` 仍是 `display:table-row-group`，
     会按表格算法用「内容最小宽度」撑开父级 —— 实测表现为卡片右半边溢出屏幕被截断
     （真浏览器截图 `_shot_1_smpd.png` 抓到的就是这个）。 */
  #smpd_rows{display:block;width:100%;min-width:0;border-collapse:separate}
  #smpd_rows tbody{display:block;width:100%;min-width:0}
  #smpd_rows .smpd-hd{display:none}
  #smpd_rows tr.smpd-row{
    display:flex;flex-wrap:wrap;align-items:center;gap:0 8px;width:100%;
    background:#fff;border:1px solid var(--line);border-radius:12px;
    padding:9px 11px;margin:0 0 9px;box-shadow:0 1px 3px rgba(20,40,60,.06);
  }
  #smpd_rows tr.smpd-row[data-locked="1"]{border-color:var(--green);background:var(--green-l)}
  #smpd_rows tr.smpd-row td{display:block;border:none;padding:0;margin:0;min-width:0}
  /* 换行靠这两个「零高 · 全宽」占位伪元素。
     ⚠ 为什么非它不可：flex-wrap 只会把子项**按 order 顺序往右填**，填不下才换行 ——
     6 个只读小字格（编码/整砖/小样/库位/单位/单片kg）在窄屏下的自然宽度之和
     刚好能塞进第一行，于是它们骑在搜索框那一行上、把搜索框压成 **31px**
     （真机探针实测：`.smpd-td-name client=31`）。
     `flex-basis:100%` + `height:0` = 在此处强制断行，且不占任何高度（跨行只靠 row-gap:0）。 */
  #smpd_rows tr.smpd-row::before{content:'';flex-basis:100%;height:0;order:3}
  #smpd_rows tr.smpd-row::after{content:'';flex-basis:100%;height:0;order:10}
  /* 行①：产品搜索（占满剩余宽度）+ 删除按钮 */
  #smpd_rows .smpd-td-name{flex:1 1 0;min-width:0;order:1}
  #smpd_rows .smpd-td-name input{width:100%;height:42px;font-size:15px;padding:0 10px}
  /* 行①右：删除按钮 42×42 触控区 */
  #smpd_rows .smpd-td-del{flex:0 0 auto;order:2}
  #smpd_rows .smpd-td-del .btn{width:42px;height:42px;font-size:20px;line-height:1;padding:0;border:1px solid var(--line);border-radius:12px;background:#fff}
  /* 行②③：只读带出字段，小字两行，形如「编码 KL-001　整砖 800×800　小样 100×100」
     各格宽度按「每行放得下 3 格」定：行② = 编码+整砖+小样（约 253px+间隔），
     行③ = 库位+单位+单片kg（约 192px+间隔）。放不下的情况交给 flex 自动换行兜底，
     因为每格自然宽度都远小于整行宽，永远不会出现单格撑破卡片。 */
  /* ⚠ 下面三条「格级」规则一律带 `tr.smpd-row` 前缀 —— 上面那条通用规则
     `#smpd_rows tr.smpd-row td{display:block;...;margin:0}` 的特异性是 (1,1,2)，
     而 `#smpd_rows .smpd-td-qty` 只有 (1,1,0) ⇒ 会被它压掉（表现：数量/备注格没变成
     flex 行、「备注」标签掉到输入框上面单独占一行、margin-top 也不生效）。
     加层级后变成 (1,2,2)，稳定胜出。 */
  #smpd_rows tr.smpd-row .smpd-td-ro{order:4;margin-top:7px;font-size:11.5px;color:var(--tx2);line-height:1.6;white-space:nowrap}
  #smpd_rows tr.smpd-row .smpd-td-qty{order:11;margin-top:9px;flex:0 0 auto;display:flex;align-items:center;gap:6px}
  #smpd_rows tr.smpd-row .smpd-td-rem{order:12;margin-top:9px;flex:1 1 110px;display:flex;align-items:center;gap:6px;min-width:0}
  #smpd_rows .smpd-td-ro:before{content:attr(data-lbl) ' ';color:#8a97a6;font-size:11px}
  #smpd_rows .smpd-td-ro input{border:none !important;background:transparent !important;padding:0 !important;color:var(--tx2);font-size:11.5px;min-width:0}
  #smpd_rows .smpd-td-ro[data-lbl="编码"] input{width:66px}
  #smpd_rows .smpd-td-ro[data-lbl="整砖"] input{width:60px}
  #smpd_rows .smpd-td-ro[data-lbl="小样"] input{width:52px}
  #smpd_rows .smpd-td-ro[data-lbl="库位"] input{width:46px}
  #smpd_rows .smpd-td-ro[data-lbl="单位"] input{width:22px}
  #smpd_rows .smpd-td-ro[data-lbl="单片kg"] input{width:36px}
  #smpd_rows .smpd-td-qty:before{content:attr(data-lbl);font-size:12px;color:var(--tx2);white-space:nowrap}
  /* 数量框 17px 要**保住**（比需求⑤ 的通用 16px 更大、更醒目），
     两条都带 !important 时比特异性：(1,1,1) > (0,2,1)，本条胜出。 */
  #smpd_rows .smpd-td-qty input{width:92px;height:42px;font-size:17px !important;font-weight:700;text-align:center;padding:0 4px}
  #smpd_rows .smpd-td-rem:before{content:attr(data-lbl);font-size:12px;color:var(--tx2);white-space:nowrap}
  #smpd_rows .smpd-td-rem input{flex:1 1 auto;height:42px;font-size:14px;min-width:0}
  /* 下拉在手机上更高更宽，滚动选更顺手 */
  #smpd_drop{max-height:62vh} /* v6.14：窄屏同步放大 */
}

/* ===== v5.05 需求3：手机端「项目列表 / 订单列表」改卡片显示 =====
   老板原话：「项目列表，订单列表在手机端也是改成卡片显示」。
   与需求2 同一套思路：表格加 `cardable` 标记（只标记这两个列表，其他 20+ 列表行为不变），
   窄屏下把 table/tr/td 降为块级 flex，字段名由 `td:before{content:attr(data-lbl)}` 印出——
   列是配置驱动的（可自定义/隐藏/换序），标签只能来自列定义，所以渲染时用 lblTd() 写进 data-lbl。
   勾选格（pkTd 的 .pk-td）与 colspan 行（空态/「显示更多」/分页脚）单独处理成非卡片。

   ===== v5.05c：卡片排版定稿（「歪歪扭扭」返工）=====
   老板原话（附订单列表手机端真机截图）：「手机端卡片信息显示的歪歪扭扭，在优化一下」。
   病根是 v5.05 的字段排法 `flex:0 1 auto` + `gap` —— 字段**按内容宽度横向自由流动**，
   于是每张卡、每一行的字段起点都落在不同的 X 上（越看越歪）；值里的空格还会被当成断行点，
   空值那个孤零零的 `-` 就被挤到下一行单独待着；主列没值时标题更是只剩一个 `-`。
   定稿改法：**一格一行 + 标签定宽左列**（标签靠悬挂缩进锁死，值全部从同一竖线起排），
   并把「只有破折号」的格在窄屏整格隐藏（`.card-hide`，由 app.js cardTitleApply() 打标）。
   ===== v5.05d：卡片「状态」徽标叠印（悬挂缩进的副作用，老板装完 v5.05c 当场反馈）=====
   老板原话（附手机截图）：「订单列表的卡片里面状态显示的还是不对，重叠了」——
   截图里 `已发完` 与 `待提货` 叠成一团（读起来像「已发完提货」），还压到了左边的 `状态` 标签。
   病根：**`text-indent` 是继承属性** —— 上面悬挂缩进那条的负值会被格内**每个子元素**继承，
   徽标 `<span class="badge">`（inline-block）继承后**内容宽度被算成 0**，盒子只剩 padding 的 20px
   （「备货完成」本该 68px）⇒ 文字溢出到框外、两个徽标互相叠印。
   修法：下面补一条「格内子元素 `text-indent:0`」（`:before` 那条只挡住标签自己，挡不住子元素）。
   取证（真 Edge）：徽标盒宽 **20px → 68px**；值起点 X 仍是**唯一值**（对齐未受影响）。
   ⚠ 全程纯 CSS 布局 + 渲染期加 class，**DOM 一行没改**（6 套历史回归都靠元素结构）。 */
/* ===== v5.06：全站列表自动卡片化（老板「所有的界面都把手机端重新优化一下，该用卡片的就用卡片」）=====
   本节规则**一条都没改** —— v5.05c/v5.05d 定稿的排版机制（一格一行 + 标签定宽左列 + 格内
   `text-indent:0`）直接复用；改的只是「谁来打标」：以前只有项目/订单/工作台待办三个列表
   手工写 `cardable` + `data-lbl`，现在由 app.js 的 `cardifyTables()` 在渲染出口统一补，
   于是**所有列表页在手机上自动变卡片**，各页面的渲染代码一行没动。
   弹窗（#modal-root）不经过渲染出口 ⇒ 备货单 A4 预览、订单明细编辑表仍保持普通表格。 */
/* ⚠ v5.06：`screen and` 不能省！A4 纸宽 ≈ 794px < 820px ⇒ `@media(max-width:820px)` 在**打印**时
   同样成立，会把要打印的单据/表格也改成卡片样式（施工费单、施工结算单这类单据是要打印的）。
   加上 `screen` 后：屏幕上窄屏走卡片，打印一律走桌面表格样式 —— 两边都符合预期。 */
@media screen and (max-width:820px){
  table.tbl.cardable{display:block;width:100%;min-width:0;border-collapse:separate}
  /* 同小样明细：tbody 必须一起降级，否则会按表格算法撑宽（卡片溢出屏幕右侧） */
  table.tbl.cardable > tbody{display:block;width:100%;min-width:0}
  /* 表头整行隐藏（卡片自身印字段名） */
  table.tbl.cardable > tbody > tr:first-child{display:none}
  table.tbl.cardable > tbody > tr{
    display:flex;flex-wrap:wrap;align-items:flex-start;gap:2px 0;width:100%;
    background:#fff;border:1px solid var(--line);border-radius:12px;
    padding:10px 12px;margin:0 0 10px;box-shadow:0 1px 3px rgba(20,40,60,.06);
  }
  table.tbl.cardable > tbody > tr.click:active{background:#f7fafc}
  table.tbl.cardable > tbody > tr > td{display:block;border:none;padding:0;margin:0;min-width:0;line-height:1.7}
  /* ---- 通用字段格：**一格一行 + 标签定宽左列**（v5.05c 定稿，见文件头 v5.05c 说明）----
     标签靠「悬挂缩进」锁在左边（padding-left + 负 text-indent，:before 自身宽 = 标签列宽）：
     首行整体左移给标签，值从 `--card-lbw` 处起排，**折行的续行也从同一条竖线开始**。
     ⇒ 每张卡、每一行的值都从同一个 X 开始，这就是老板要的「不歪」。
     ⚠ 唯一的宽度旋钮：`--card-lbw`（默认 74px ≈ 6 个中文字）。标签比它长时会在列内折行，
       但**值的起点不动**（宁可标签折行，也不要一行一个位置）。 */
  table.tbl.cardable > tbody > tr > td[data-lbl]{
    flex:1 1 100%;box-sizing:border-box;
    padding:1px 0 1px var(--card-lbw,74px);text-indent:calc(-1 * var(--card-lbw,74px));
    font-size:13px;color:var(--tx);word-break:break-all;
    /* ⚠ `text-align:left !important` 不是随手加的：订单列表「重量」格等带**行内**
       `style="text-align:right"`，行内优先级高于媒体查询（铁律 41①）⇒ 不清掉的话这一行的
       「标签+值」整段被推到右边，标签跑到别的 X 上 —— 又回到「歪」。卡片里一律左对齐。 */
    text-align:left !important;
  }
  table.tbl.cardable > tbody > tr > td[data-lbl]:before{
    content:attr(data-lbl);
    display:inline-block;width:var(--card-lbw,74px);text-indent:0;vertical-align:top;
    color:#8a97a6;font-size:11.5px;line-height:1.8;
  }
  /* ⚠⚠ v5.05d 修（老板 09-13 截图：「订单列表的卡片里面状态显示的还是不对，重叠了」）：
     **`text-indent` 是继承属性** —— 上面 td 的负值会被格内**每一个子元素**继承。
     实测（真 Edge 探针）：状态格里的徽标 `<span class="badge">` 是 inline-block，
     继承 `text-indent:-74px` 后**内容宽度被算成 0**（盒子只剩 padding 的 20px 宽，
     本该 68px），文字直接溢出到徽标框外面 ⇒ 两个徽标「叠印」成「已发完提货」，
     并与左侧标签粘在一起 —— 老板看到的「重叠」就是这个。
     ⇒ **格内所有子元素一律 `text-indent:0`**：悬挂缩进只保留在 td 自身的首行
     （`:before` 标签 + 首行纯文本），折行续行由 `padding-left` 对齐，不受影响。 */
  table.tbl.cardable > tbody > tr > td[data-lbl] *{text-indent:0}
  table.tbl.cardable > tbody > tr > td[data-lbl] .btn.sm{font-size:12px;padding:3px 8px}
  /* 空字段不印。否则卡片上会出现一排只有字段名、没有值的「要求到货日期 出库单号 加工单号」，
     纯噪声（真机截图 `_shot_2_orders.png` 一眼就看出来了）。
     `:empty` 只认「一个子节点都没有」的元素，所以渲染时不能往空格里塞空格/换行。 */
  table.tbl.cardable > tbody > tr > td[data-lbl]:empty{display:none}
  /* v5.05c：**只有破折号**的字段格整格隐藏。`:empty` 抓不到「内容是 `-`」的格（订单列表
     空值就渲染成 `-`），于是老板截图里 `单号日期 -`、`客户编码 -` 各占一整行、全是噪声。
     这些格由 app.js 的 cardTitleApply() 标上 `.card-hide`（判定在渲染期，CSS 抓不到文本内容）；
     ★ 只在窄屏隐藏 —— 桌面表格照旧显示 `-`，信息不丢。
     ⚠ 第二条选择器是把特异性顶到 (0,5,5) 才压得住：**主列（勾选格后的第一格）被标 card-hide 时**，
       它同时命中下面那条 `td.pk-td + td[data-lbl]`（(0,4,5)，比 `.card-hide` 单写法的 (0,3,4) 高；
       写成 `td.pk-td + td.card-hide` 只是 (0,4,5) 并列、位置又更靠前 ⇒ 照样输），
       结果空格以「标题样式」留在卡片顶部 —— 那正是老板截图里孤零零的那个 `－`
       （**真浏览器探针抓到的**：光看 DOM 有 class 是查不出来的，必须量 computed display）。 */
  table.tbl.cardable > tbody > tr > td.card-hide,
  table.tbl.cardable > tbody > tr > td.pk-td + td[data-lbl].card-hide{display:none}
  /* ---- 卡片标题：不印字段名、不缩进、放大加粗 ----
     两个来源，声明刻意保持一致：
       · `td.pk-td + td[data-lbl]` —— 主列（勾选格后的第一格），特异性 (0,4,5) 稳压下面的通用规则；
       · `td.card-title` —— 没有勾选列的表（工作台待办提醒），以及 v5.05c 主列为空时
         「提升」上来的第一个有值格（由 cardTitleApply() 打类）。
     ★ 这两条**必须排在通用 `td[data-lbl]` 规则之后**：与它同为 (0,3,4) 特异性，靠后来者胜出；
       排前面的话标题会被通用规则重新印上字段名 + 缩进 74px（v5.05b 就踩过这个坑）。
     ★ 别给「没有勾选列」的表复用 `.pk-td` —— 下面那条 `.pk-td{order:99;margin-left:auto}`
       会把标题挪到卡片右下角、贴着按钮。 */
  table.tbl.cardable > tbody > tr > td.card-title{
    flex:1 1 100%;order:0;display:block;
    font-size:15.5px;font-weight:600;color:var(--tx);
    padding:0 0 3px;text-indent:0;word-break:break-all;line-height:1.45;
  }
  table.tbl.cardable > tbody > tr > td.card-title:before{content:none}
  table.tbl.cardable > tbody > tr > td.pk-td + td[data-lbl]{
    flex:1 1 100%;order:0;display:block;
    font-size:15.5px;font-weight:600;color:var(--tx);
    padding:0 0 3px;text-indent:0;word-break:break-all;line-height:1.45;
  }
  table.tbl.cardable > tbody > tr > td.pk-td + td[data-lbl]:before{content:none}
  /* v6.04：日常巡场表卡片上「项目地址」加大加黑（老板 10-07：「卡片上的项目地址加大加黑」）。
     这一格是本卡片标题格（勾选格后第一格），客户名本身已 15.5px/600；地址原是小灰字 <small>，
     这里把它提到同级并再加重 —— 扫一眼就能看见地址。桌面表格不在本媒体查询内，保持原样。 */
  table.tbl.cardable > tbody > tr > td[data-lbl] small.dinsp-addr{
    font-size:15px;font-weight:700;color:var(--tx);line-height:1.5;
  }
  /* 订单二维码：必须显式写 width/height —— SVG 上带的是 width/height **表现属性**，
     CSS 优先级高于它，但只给 `max-width` 是不会把它从小缩到大的（原写法就是 max-width:72px，
     60px 的码原样输出）。
     v5.05c：这一格的行内样式带 `padding:6px 4px`，要用 !important 才压得住，
       并且同样吃标签列的悬挂缩进（码与其它值左对齐，不再靠 margin-left:auto 甩到右边）。 */
  table.tbl.cardable > tbody > tr > td[data-lbl="二维码"]{
    margin-left:0;padding:4px 0 2px var(--card-lbw,74px) !important;text-indent:calc(-1 * var(--card-lbw,74px));
  }
  table.tbl.cardable svg{width:52px;height:52px;max-width:52px}
  /* 操作列：独占一行、按钮撑开（拇指友好）；不印标签、不吃缩进 */
  table.tbl.cardable > tbody > tr > td[data-lbl="操作"]{
    flex:1 1 100%;order:98;display:flex;gap:8px;margin-top:4px;
    padding:0;text-indent:0;
  }
  table.tbl.cardable > tbody > tr > td[data-lbl="操作"] .btn{flex:1 1 0;height:40px;font-size:13.5px}
  table.tbl.cardable > tbody > tr > td[data-lbl="操作"]:before{content:none}
  /* 勾选格：挪到卡片右下角，44px 触控区 */
  table.tbl.cardable > tbody > tr > td.pk-td{order:99;margin-left:auto;padding-left:8px;text-indent:0}
  table.tbl.cardable > tbody > tr > td.pk-td input{width:22px;height:22px}
  /* 空态 / 「显示更多」/ 分页脚：不卡片化 */
  table.tbl.cardable > tbody > tr.card-skip,
  table.tbl.cardable > tbody > tr.tbl-pg-foot{
    display:block;background:transparent;border:none;box-shadow:none;padding:0;margin:0;
  }
  table.tbl.cardable > tbody > tr.card-skip > td,
  table.tbl.cardable > tbody > tr.tbl-pg-foot > td{display:block;text-align:center}
  table.tbl.cardable > tbody > tr.card-skip > td:before,
  table.tbl.cardable > tbody > tr.tbl-pg-foot > td:before{content:none}

  /* ===== v5.49：手机端显示内容开关（后台可单独配置，见 app.js 的 mobileCfg / mbApply / mbTag）=====
     老板原话：「订单列表手机端不要显示批量导入、导出、编辑、发货、批量删除、勾选框这些按钮」
     + 「最好是在后台可以单独配置手机端显示的内容」。配置存 DB.settings.mobile（`===1` 才显示
     ⇒ 缺省全部不显示）；app.js 的 mbApply() 按开关往 `<body>` 挂 `mb-hide-<k>` 状态类，
     mbTag() 给批量按钮 / 勾选元素打 `data-mbk`。本节只读 body 类与 data-mbk
     ⇒ **桌面端不受任何影响**（body 类只在窄屏挂上，见 mbApply 自己判窄屏并在宽屏清空）。
     ⚠ `!important` 是**刻意**的，不是偷懒：
       · 被隐藏的元素同时命中本节上面的卡片布局规则（`td[data-lbl="操作"]{display:flex}`、
         `td.pk-td + td[data-lbl]{display:block}`），hide 与 show 两组特异性相同 —— 靠
         「谁写在后面」决胜太脆（后人往后追加一条规则就会把已隐藏的元素复活）；
       · 开关语义是「用户显式要求不显示」，本就该压过一切布局规则（与上面 `text-align:left
         !important` 同一精神）。
     ⚠ 表头勾选列（pkTh / pkThInv 输出的 <th>）不用单独处理：卡片化已把整个表头行
       （`tbody > tr:first-child`）隐藏掉了，见上面那条。
     ⚠ `data-mbk="pk"` 一个值同时覆盖三种元素：每行的勾选格 `.pk-td`（由类判定）、
       「已勾选 N 条」计数提示、「清空勾选」按钮 —— 老板要的是「整套勾选功能不显示」。 */
  body.mb-hide-pk  td.pk-td,
  body.mb-hide-pk  [data-mbk="pk"]{display:none !important}
  /* v5.51 修 bug（老板 09-22 原话：「配置中心手机端显示内容每行操作按钮（编辑 / 发货 等）关了显示之后
     附件的下载、预览、删除不要关」）：原规则按**整列**隐藏，而这两张表的「操作」列里装的就是附件动作，
     一关「操作列按钮」附件动作跟着没了 ——
       · 订单详情 → 附件表（附件动作=预览 / ⬇下载 / 删除）
       · 销售预算单 → 附件表（附件动作=下载 / 删除）
     改法（外科手术，不是重写成"逐按钮打标"）：这两张表的操作格由渲染处挂 data-mbk="actkeep"
     （附件动作属"附件区"，不是"列表行操作"），本规则用 :not() 排除掉。
     ⇒ 其余 40+ 张表的「操作」列（编辑 / 发货 / 去处理）行为**逐字节不变**。
     ⚠ 刻意不改成"给每个行操作按钮打 data-mbk=act"：那样漏掉任意一个动作按钮，方向相反的
       回归（该隐藏的没隐藏）更隐蔽；按整列隐藏 + 白名单例外，错也只会错在明面上。 */
  body.mb-hide-act td[data-lbl="操作"]:not([data-mbk="actkeep"]){display:none !important}
  body.mb-hide-imp [data-mbk="imp"]{display:none !important}
  body.mb-hide-exp [data-mbk="exp"]{display:none !important}
  body.mb-hide-del [data-mbk="del"]{display:none !important}
  /* v5.59 F①：行内操作按钮（mbTag 只给「点得进详情的表」里的按钮打 data-mbk="row"） */
  body.mb-hide-row [data-mbk="row"]{display:none !important}
  /* ★ v6.54⑱（老板 10-09 夜）：「项目管理，订单管理列表手机端卡片上不要显示操作按钮」
     ⇒ 挂了 m-nodact 的两张表（项目 / 订单），窄屏卡片里整块「操作」不显示。
     ⚠ 只影响这两张表；其余列表仍按后台「手机端显示内容」的 act / row 开关走（线上这两项是开着的）。 */
  table.tbl.cardable.m-nodact td[data-lbl="操作"],
  table.tbl.cardable.m-nodact [data-mbk="row"]{display:none !important}
  /* v5.59 F②：订单列表状态徽章一排（撑不下才自动折行）—— 配合 app.js ordCell 去掉的 <br> */
  .ord-badges{display:inline-flex;flex-wrap:wrap;align-items:center;gap:4px 6px}
  .ord-badges .badge{margin:0}

  /* ===== v5.05b 需求④：工作台「待办提醒」手机端也改卡片 =====
     老板原话：「工作台待办提醒手机端也做成卡片」。
     主体沿用 v5.05 需求3 那套机制（表格打 `cardable` + 各格 `data-lbl` + 第一格 `.card-title`），
     这里只补一条：面板头上那段说明列了 12 个待办来源，手机上要占 4~5 行、
     会把卡片挤出屏幕 ⇒ 截成 2 行。与 `.smpd-tip` 同一手法。 */
  .dash-todo-tip{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
  /* 面板头换行：`.ph` 是 `display:flex` 且不换行，而待办提醒的说明文字很长（列了 12 个待办来源），
     它会抢走宽度、把 `h3` 压成**逐字竖排**（真截图 `_shot_5_todo.png` 里「📋 待办提醒（4）」
     被挤成三行）。解法沿用 v5.05 那招「零高 · 全宽换行位」，只是这次占位的是真实元素 `.sp`：
     让它变成 `flex-basis:100%; height:0` ⇒ 标题留在第一行、说明文字自然掉到第二行。
     ★ 这是**通用改善**：其它面板头的长说明在手机上同样会压标题，不止待办提醒一处。 */
  .panel .ph{flex-wrap:wrap}
  .panel .ph h3{flex:0 0 auto;white-space:nowrap}
  .panel .ph .sp{flex:1 0 100%;height:0}

  /* ===== v5.61：订单详情页头手机端改两行 =====
     老板原话：「订单详情手机端单号后面的状态没有自动换行，单号很长后面的状态都超出屏幕没有显示了，
     不行手机端就改成 2 行显示，第一行显示单号，第二行显示状态和分享图标」。
     ★ 只钉 **od-ph** 这个标记 —— 故意不动上面那条通用 `.panel .ph h3`（nowrap）：其它面板头的长说明
       正靠它压住标题（v5.05 待办提醒那一修），改通用规则会连坐其他面板。
     ★ h3 占满整行（flex-basis:100%）后内部自由折行；单号 .od-no 转 block ⇒ 独占第一行，
       后面的徽章组自然掉到第二行。
     ★ 长单号配 word-break:break-all：单号本身超宽时自己也能折，不再溢出屏幕。 */
  .panel .ph h3.od-ph{flex:1 1 100%;min-width:0;white-space:normal}
  .panel .ph h3.od-ph .od-no{display:block;word-break:break-all}
  .panel .ph .od-share-mb{display:inline-flex}
  /* ★ 按钮排那条「🔗 分享」在手机端让位：第二行已有图标（上一行），两条都留就是重复入口，
     也不是老板要的「2 行」。只在本媒体查询内隐藏 ⇒ 桌面端丝毫不受影响。 */
  .panel .ph .od-share-pc{display:none}

  /* ★ v7.48（老板 10-14 13:4x：「预约信息第一行手机端没有自动换行」）——
     场地预约的**记录卡片头**：v7.47 起"同一次预约的多个场地"合并成一张卡，标题串变成
     「场地1、场地2 · 日期（周x）＋ 状态＋类型＋共 N 个场地」很长；上面那条通用
     `.panel .ph h3{flex:0 0 auto;white-space:nowrap}`（v5.05 为「待办提醒」的长说明加的）
     会把它**一路撑出屏幕**（不折行）。
     ⇒ 与 v5.61 那次「订单详情单号不换行」**完全同款手法**：只钉 `vrec-ph` 这个标记 —— 标题占满整行后内部自由折行。
     （⚠ 这里刻意不写那次用的标记名：test-v563 有一条「该标记恰 3 处」的反回归计数断言，
       在注释里引用它会被算进去 —— "注释也算源码"。）
     ⚠ 上面那条通用 nowrap **原样不动**（其它面板头仍靠它压标题；test-v563 有反回归断言钉着）。 */
  .panel .ph h3.vrec-ph{flex:1 1 100%;min-width:0;white-space:normal}
  /* 场地名串自身可在任意字间断开（长场地名不溢出）；日期与徽标各是一块，整体换行不拆散 */
  .panel .ph h3.vrec-ph .vrec-vn{white-space:normal;word-break:break-word}

  /* ---- 需求⑤ ③：可编辑表单元素字号 ≥16px（防 iOS 聚焦自动放大）----
     iOS Safari 在 `font-size < 16px` 的输入框获得焦点时会强制把整页放大 —— 用户什么都没碰，
     界面就自己变大了，正是老板说的「不能随意放大缩小」里最烦的那一种。
     排除 `[readonly]`：只读带出字段（小样明细的 编码/整砖/小样/库位/单位/单片kg）是刻意的小字，
     撑到 16px 会把卡片顶高一截，而用户根本不会去聚焦它们。
     ★ 必须用 `!important`：站内大量输入框带着行内 `style="font-size:12px"`，
       而行内样式优先级高于媒体查询（v5.05 铁律 41①），不加就压不过去。 */
  input:not([readonly]), select, textarea{font-size:16px !important}
}

/* ===== v4.7-114：自由画布布局编辑器（场地屏排版） ===== */
.vle-mod{position:absolute;border:1px dashed rgba(255,255,255,.28);background:rgba(90,120,180,.14);border-radius:6px;cursor:move;box-sizing:border-box}
.vle-mod:hover{border-color:rgba(120,160,230,.75);background:rgba(90,120,180,.22)}
.vle-mod.sel{border:2px solid #4a90d9;background:rgba(74,144,217,.22);box-shadow:0 0 0 1px #4a90d9}
.vle-mod .vle-h{user-select:none}
.vle-mod.sel .vle-h{border-color:#e8f1ff}

/* ============ v4.27 增补⑳：视频会议中心（大厅/切页保活胶囊/录制/邀请/预约） ============ */
/* 会议层：body 级固定层（不在 #app 内，切页不销毁 → 声音与连接保持） */
.meet-wrap{position:fixed;left:0;top:0;right:0;bottom:0;z-index:180;background:var(--bg);display:flex;flex-direction:column;padding:12px 16px}
.meet-wrap .meet-active{height:100%}
/* 底部胶囊条：切到其他页面时会议收起为胶囊（点返回会议复原） */
.meet-capsule{position:fixed;left:50%;bottom:calc(16px + env(safe-area-inset-bottom,0px));transform:translateX(-50%);z-index:190;display:flex;align-items:center;gap:10px;background:var(--card);border:1px solid var(--line);border-radius:999px;padding:8px 14px;box-shadow:0 10px 28px rgba(15,40,70,.28);max-width:calc(100vw - 24px)}
.meet-capsule .mc-ic{font-size:20px;line-height:1}
.meet-capsule .mc-t{font-weight:600;font-size:13px;max-width:170px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.meet-capsule .mc-n{color:var(--tx2);font-size:12px;white-space:nowrap}
/* 右下角邀请/预约提醒弹窗（30 秒自动消失） */
.meet-pop{position:fixed;right:16px;bottom:calc(16px + env(safe-area-inset-bottom,0px));z-index:195;width:288px;background:var(--card);border:1px solid var(--line);border-radius:12px;box-shadow:0 12px 32px rgba(15,40,70,.3);padding:12px 14px;animation:fade .2s}
.meet-pop .mp-t{font-weight:600;font-size:13px;margin-bottom:4px;color:var(--green)}
.meet-pop .mp-b{color:var(--tx2);font-size:12.5px;line-height:1.65;margin-bottom:10px}
.meet-pop .mp-f{display:flex;gap:8px}
/* 会议大厅 */
.meet-hub{padding:2px 0 28px;max-width:980px;margin:0 auto;width:100%}
.meet-tabs{display:flex;gap:8px;margin-bottom:14px;flex-wrap:wrap}
.mtab{border:1px solid var(--line);background:var(--card);border-radius:8px;padding:8px 16px;font-size:13px;cursor:pointer;color:var(--tx)}
.mtab:hover{border-color:var(--green)}
.mtab.on{background:var(--green);border-color:var(--green);color:#fff}
/* 会中打开大厅的返回条 */
.meet-inmeet{display:flex;align-items:center;gap:10px;flex-wrap:wrap;background:var(--green-l);border:1px solid var(--green);border-radius:10px;padding:10px 14px;margin-bottom:12px;font-size:13px}
/* 快速会议 + 房号加入 双卡 */
.meet-quick{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:12px;margin-bottom:18px}
.meet-quick .mq{display:flex;flex-direction:column}
.meet-quick .mq .btn{margin-top:auto;align-self:flex-start}
/* 分区标题 */
.meet-sec-hd{display:flex;align-items:center;justify-content:space-between;gap:10px;margin:0 0 10px;font-size:14px}
/* 固定会议室卡片 */
.meet-rooms{display:grid;grid-template-columns:repeat(auto-fill,minmax(224px,1fr));gap:12px}
.meet-room{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);padding:14px;display:flex;flex-direction:column;gap:6px;box-shadow:var(--shadow)}
.meet-room.live{border-color:var(--green);box-shadow:0 0 0 1px var(--green)}
.meet-room .mr-name{font-weight:600;font-size:14px}
.meet-room .mr-code{color:var(--tx2);font-size:12px;letter-spacing:1px}
.meet-room .mr-live{color:var(--green);font-size:12.5px;line-height:1.6}
.meet-room .mr-live span{color:var(--tx2)}
.meet-room .mr-idle{color:var(--tx2);font-size:12.5px}
.meet-room .mr-act{display:flex;gap:6px;margin-top:6px;flex-wrap:wrap}
/* 历史会议 */
.meet-hist{display:flex;flex-direction:column;gap:10px}
.meet-hist-row{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);padding:12px 14px;box-shadow:var(--shadow)}
.meet-hist-row .mh-line1{display:flex;justify-content:space-between;align-items:baseline;gap:10px;flex-wrap:wrap}
.meet-hist-row .mh-time{color:var(--tx2);font-size:12px}
.meet-hist-row .mh-meta{color:var(--tx2);font-size:12.5px;margin:4px 0 8px}
.meet-hist-row .mh-recs{display:flex;gap:8px;align-items:center;flex-wrap:wrap}
.meet-hist-row .mh-norec{color:var(--tx2);font-size:12px}
.meet-hist-row .mh-video{width:100%;margin-top:8px}
.meet-hist-row .mh-video video{width:100%;max-width:640px;border-radius:8px;background:#000;display:block}
/* 表单字段（预约/新建会议室弹窗） */
.meet-fld{margin-bottom:10px}
.meet-fld label{display:block;font-size:12.5px;color:var(--tx2);margin-bottom:5px}
.meet-fld .meet-input{margin-bottom:0;letter-spacing:0}
/* 邀请/预约弹窗里的选人区 */
.meet-users{display:flex;flex-wrap:wrap;gap:6px;max-height:210px;overflow-y:auto;border:1px solid var(--line);border-radius:8px;padding:8px}
.meet-ucheck{display:flex;align-items:center;gap:5px;background:var(--bg);border:1px solid var(--line);border-radius:6px;padding:5px 10px;font-size:12.5px;cursor:pointer}
.meet-ucheck:hover{border-color:var(--green)}
.meet-ucheck input{margin:0}
/* 录制按钮（录制中红底闪烁） */
.meet-ctrl.rec-on{background:var(--red);color:#fff;animation:meetRecPulse 1.6s infinite}
@keyframes meetRecPulse{0%,100%{box-shadow:0 0 0 0 rgba(217,79,67,.45)}50%{box-shadow:0 0 0 8px rgba(217,79,67,0)}}
/* 移动端适配 */
@media(max-width:820px){
  .meet-wrap{padding:8px 10px}
  .meet-hub{padding:0 0 20px}
  .meet-rooms{grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:8px}
  .meet-quick{grid-template-columns:1fr}
  .meet-pop{width:calc(100vw - 32px)}
  .meet-capsule .mc-t{max-width:110px}
}

/* ============ v4.30 辅料明细「清单点选」模式（自有/第三方共用） ============ */
.auxd-bar{display:flex;gap:8px;align-items:center;margin:2px 0 10px}
.auxd-search{flex:1;min-width:0;padding:11px 13px;border:1px solid var(--line);border-radius:10px;font-size:15px;background:#fff;color:var(--tx)}
.auxd-search:focus{outline:none;border-color:var(--green)}
.auxd-count{font-size:12px;color:var(--tx2);background:var(--green-l);padding:3px 10px;border-radius:20px;white-space:nowrap;flex:none}
.auxd-list{display:flex;flex-direction:column;gap:8px}
.auxd-it{display:flex;align-items:center;gap:10px;padding:9px 10px 9px 13px;border:1px solid var(--line);border-radius:12px;background:#fff;transition:.12s;user-select:none}
.auxd-it.sel{border-color:var(--green);background:var(--green-l);box-shadow:inset 3px 0 0 var(--green)}
.auxd-it.orph{border-style:dashed;background:#fbfcfd}
/* v5.59 D②：无库存行灰掉（数量仍可显示"已选"，但点「＋」会被 auxdClamp 拦下并提示去目录补库存）。
   ⚠ 刻意**不用** pointer-events:none —— 那样点了完全没反应，老板会当成界面卡死；
   让它照常触发 auxdStep → 走 auxdClamp 弹出「库存为 0，先去补库存」的提示，才知道下一步该干什么。 */
.auxd-it.nostock{opacity:.55;filter:grayscale(.6)}
.auxd-it.nostock .auxd-t1 b{color:var(--tx2)}
.auxd-it .auxd-lf{flex:1;min-width:0}
.auxd-t1{display:flex;align-items:center;gap:6px;flex-wrap:wrap;line-height:1.45}
.auxd-t1 b{font-size:15.5px;color:var(--tx);word-break:break-word} /* v5.36：连续无空格长串（英文型号等）也能断行，不再撑宽弹窗 */
.auxd-t1 i{font-style:normal;font-size:12px;color:var(--tx2);background:#eef1f4;border-radius:5px;padding:1px 7px;white-space:nowrap;max-width:46%;overflow:hidden;text-overflow:ellipsis} /* v5.36：规格标签限宽截断——nowrap 无上限时长规格（1200×2600mm 抛光面/磨边…）会把弹窗内容区撑宽，手机端 .mb 可横向拖动（老板 09-19：「添加辅料明细弹窗在手机上还是能左右晃，改成固定不动的」）；max-width 用百分比自适应任意屏宽 */
.auxd-t1 .auxd-sup{font-style:normal;font-size:11px;color:var(--blue);background:#eaf1fb;border-radius:5px;padding:1px 7px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:120px}
.auxd-t1 .auxd-tag{font-style:normal;font-size:11px;color:var(--orange);background:#fdf0e0;border-radius:5px;padding:1px 7px;white-space:nowrap;max-width:38%;overflow:hidden;text-overflow:ellipsis} /* v5.36：保留行标签同样限宽（同款防撑宽） */
.auxd-t2{font-size:12px;color:var(--tx2);margin-top:1px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.auxd-t3{margin-top:6px;display:flex;align-items:center;gap:6px;flex-wrap:wrap}
.auxd-t3 label{font-size:12px;color:var(--tx2)}
.auxd-fl{font-size:13.5px;padding:6px 10px;border:1px solid var(--line);border-radius:9px;background:#fff;color:var(--tx);max-width:180px}
.auxd-qw{display:flex;align-items:center;gap:7px;flex:none}
.auxd-b{width:42px;height:42px;border-radius:50%;border:1px solid var(--line);background:#fff;color:var(--green);font-size:24px;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center;flex:none;transition:.12s;padding:0}
.auxd-b:hover{border-color:var(--green);background:var(--green-l)}
.auxd-b.add{background:var(--green);color:#fff;border-color:var(--green);font-weight:700}
.auxd-b.add:hover{background:var(--green-d);border-color:var(--green-d)}
.auxd-b:active{transform:scale(.9)}
.auxd-q{width:58px;height:42px;text-align:center;border:1px solid var(--line);border-radius:10px;font-size:16.5px;font-weight:600;background:#fff;color:var(--tx);flex:none;-moz-appearance:textfield}
.auxd-q::-webkit-outer-spin-button,.auxd-q::-webkit-inner-spin-button{-webkit-appearance:none}
.auxd-ft{font-size:13px;color:var(--tx);align-self:center;white-space:nowrap}
.auxd-ft b{color:var(--green)}
.auxd-empty{font-size:13px;color:var(--tx2);text-align:center;padding:26px 12px;border:1px dashed var(--line);border-radius:12px;background:#fbfcfd}
.auxd-def{margin-bottom:12px;padding:10px 12px;background:var(--green-l);border:1px solid #d3def0;border-radius:10px}
.auxd-def label{display:block;font-size:12px;color:var(--tx2);margin-bottom:6px}
.auxd-def select{width:100%;padding:11px 12px;border:1px solid var(--line);border-radius:10px;font-size:15px;background:#fff;color:var(--tx)}
.auxd-def select:focus{outline:none;border-color:var(--green)}
/* ============ v4.31 第三方辅料「楼层单子」卡 ============ */
.auxd-st-h{font-size:12px;color:var(--tx2);margin:2px 0 8px}
.auxd-st{border:1px solid var(--line);border-radius:10px;padding:8px 10px;margin-bottom:8px;background:var(--green-l)}
.auxd-st1{display:flex;align-items:center;gap:8px;font-size:13.5px;flex-wrap:wrap}
.auxd-st1 b{color:var(--green-d,var(--green))}
.auxd-st1 span{font-size:12px;color:var(--tx2)}
.auxd-st1 em{font-style:normal;font-size:11.5px;color:var(--tx2);margin-left:auto;max-width:45%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.auxd-st2{font-size:12px;color:var(--tx2);margin-top:4px;line-height:1.5;word-break:break-all}
.auxd-st3{display:flex;gap:6px;margin-top:6px}
@media(max-width:420px){
  .auxd-b{width:38px;height:38px;font-size:22px}
  .auxd-q{width:50px;height:38px;font-size:15.5px}
  .auxd-t1 b{font-size:14.5px}
  .auxd-st1 em{display:none}
}

/* ============ v5.32 自有辅料图片 + 按分类显示（老板 09-18） ============ */
/* 产品目录：分类筛选 chips 行（渲染在 #zone_auxprods 内，随表格一起 zone 刷新） */
.auxp-cats{display:flex;flex-wrap:wrap;gap:6px;margin:0 14px 8px}
/* 产品目录 / 清单行：无图占位（灰底 📦；尺寸由调用方 inline 给定，与有图缩略图同格不塌陷） */
.auxp-noimg{display:flex;align-items:center;justify-content:center;background:#f2f5f8;border:1px dashed var(--line);border-radius:8px;font-size:18px;color:#b9c4cf;flex:none}
/* 产品编辑弹窗：未上传时的上传占位 */
.auxp-addimg{width:86px;height:86px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;background:#fbfcfd;border:1px dashed var(--line);border-radius:8px;cursor:pointer;color:var(--tx2);font-size:12px;box-sizing:border-box}
.auxp-addimg:hover{border-color:var(--green);color:var(--green);background:var(--green-l)}
/* 添加辅料明细清单：行内分类标签 + 分类筛选下拉 */
.auxd-t1 .auxd-cat{font-style:normal;font-size:11px;color:#2f7a5a;background:#e8f5ee;border-radius:5px;padding:1px 7px;white-space:nowrap;max-width:38%;overflow:hidden;text-overflow:ellipsis} /* v5.36：分类标签限宽（同款防撑宽；test-v531 只断言 .auxd-cat{ 类存在，追加属性不冲突） */
.auxd-catsel{padding:11px 10px;border:1px solid var(--line);border-radius:10px;font-size:14px;background:#fff;color:var(--tx);flex:none;max-width:150px}
.auxd-catsel:focus{outline:none;border-color:var(--green)}
@media(max-width:420px){
  .auxd-catsel{max-width:112px;font-size:13px;padding:10px 8px}
  .auxd-t1 .auxd-cat{font-size:10.5px;padding:1px 6px}
}

/* ============ v5.33 批量设分类 + 分类改名（老板 09-18 晚） ============ */
/* 产品目录：批量模式操作条（已选计数 + 全选 + 分类输入 + 应用 + 退出），渲染在 #zone_auxprods 内 chips 行之前 */
.auxp-bbar{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin:0 14px 8px;padding:10px 12px;background:#f8fafc;border:1px solid var(--line);border-radius:10px}
.auxp-bbar input{padding:9px 10px;border:1px solid var(--line);border-radius:10px;font-size:14px}
.auxp-bbar input:focus{outline:none;border-color:var(--green)}
/* 产品目录：批量模式行内勾选框（塞在产品名称单元格内，勾选框不加列；库存列为 v5.33 新增第 6 列） */
.auxp-ck{width:16px;height:16px;accent-color:var(--green);cursor:pointer;vertical-align:-2px;margin-right:6px}
/* v5.33：产品目录「库存」列行内输入框（直接改库存，失焦/回车即存） */
.auxp-stock{width:86px;padding:7px 8px;border:1px solid var(--line);border-radius:8px;font-size:13.5px;color:var(--tx);background:#fff}
.auxp-stock:focus{outline:none;border-color:var(--green)}
.auxp-stock::placeholder{color:#b9c4cf}
/* v5.33：订单列表「导航红点溯源」——触发左侧导航「订单管理」角标的单，行左缘红条标记（与 row-urge 加急整行标红区分开） */
.row-ordhot>td:first-child{box-shadow:inset 3px 0 0 #e5484d}

/* ============ v4.72 纯联网版：同步状态横幅 + 写锁置灰 ============ */
#sync-banner{position:fixed;top:0;left:0;right:0;z-index:99990;display:none;align-items:center;justify-content:center;font-size:13px;padding:6px 12px;text-align:center;pointer-events:none;line-height:1.5}
#sync-banner.sb-red{display:flex;background:#dc2626;color:#fff}
#sync-banner.sb-yellow{display:flex;background:#d97706;color:#fff}
#sync-banner.sb-green{display:flex;background:#16a34a;color:#fff}
body.wlock .btn-write{opacity:.4;pointer-events:none}
/* v4.91：ICP 备案号页脚——《非经营性互联网信息服务备案管理办法》第13条：主页底部中央标明备案编号 + 其下方链接备案系统网址 */
.beian-foot{margin-top:14px;text-align:center;font-size:12px;line-height:1.6;color:var(--tx2)}
.beian-foot a{color:var(--tx2);text-decoration:none;border-bottom:1px dotted currentColor}
.beian-foot a:hover,.beian-foot a:active{color:var(--ac);border-bottom-color:var(--ac)}
/* 静态兜底（index.html 内 #beian-fallback）：JS 未加载时也满足「主页底部中央」；
   #app 有内容（JS 已渲染）时隐藏，改由 app.js 登录页内的 .beian-foot 显示，避免两条重复 */
#beian-fallback{position:fixed;left:0;right:0;bottom:calc(6px + env(safe-area-inset-bottom,0px));z-index:2;margin-top:0;pointer-events:none}
#beian-fallback a{pointer-events:auto}
#app:not(:empty)~#beian-fallback{display:none}

/* ===== v5.38 ②（老板 09-19：「所有的弹窗在手机端都改成不能左右滑动，一个界面显示不下的就自动换行」）=====
   本节只作用于**手机端**（`@media screen and`：A4 纸宽 ≈794px < 820px，不加 screen 打印单据会被打歪，v5.06 铁律）。
   ① 弹窗内容区一律不许横滑：`.modal .mb{overflow-x:hidden}` —— 手机端任何弹窗都拖不动。
      桌面端（≥900px）那条 `.modal .mb{overflow-x:auto}` **不动**：键鼠宽屏上横滚是合理操作，
      老板说的是手机端。同时关掉**内层**滚动容器（`.table-wrap` 默认 `overflow-x:auto`，
      弹窗里大量明细表还带行内 `max-height/overflow:auto`）⇒ 必须 `!important` 才压得住行内样式。
      ⚠ 与 v5.37 ① 的关系：那次是「对症」——诊断不出是哪个子元素撑宽，只给辅料明细/退货明细
      两个弹窗挂 .mb-nox；这次是「机制级」——**所有**弹窗一律关死。.mb-nox 保留（它还作用于桌面端，
      且被 test-v536 断言钉住），两者并存不冲突。
   ② 「显示不下的自动换行」不是靠裁切：**弹窗内的表格交给卡片化**（app.js cardifyTables 新增
      弹窗模式，由 openModal/scheduleCardify 打标）。只写 overflow-x:hidden 会把宽表格右半边
      直接裁掉、内容变得不可达 —— 这是 v5.05 那轮踩过的坑，卡片化才是「换行地显示完整」。
      配套压掉撑宽源头：表格 min-width / table-layout:fixed / 行内列宽 / 单元格 nowrap / 输入框固有宽。
   ③ thead 表（表头在 <thead> 里、不在 tbody 首行）：卡片模式默认规则「隐藏 tbody 第一行」会
      **误隐藏第一行真数据**（那是给「表头就写在 tbody 首行」的表格用的）⇒ 渲染期给这类表加
      `.card-thead` 类，这里把两条规则反过来：隐藏 thead、保留 tbody 第一行。 */
@media screen and (max-width:820px){
  .modal .mb{overflow-x:hidden}
  .modal .mf{flex-wrap:wrap}
  #modal-root .mb .table-wrap{overflow-x:hidden !important}
  /* 撑宽源头逐条压掉（!important：站内大量行内 style，其优先级高于媒体查询 —— v5.05 铁律 41①） */
  #modal-root table.tbl{min-width:0 !important;width:100% !important;table-layout:auto !important}
  #modal-root table.tbl th,#modal-root table.tbl td{white-space:normal !important;word-break:break-word;overflow-wrap:anywhere;max-width:100%}
  /* 列宽拖拽存下来的行内 th 宽度（v5.31 ⑧ / v5.34 ①）窄屏一律不生效：几十列固定宽之和超过屏宽
     又会把内容推出可视区，而手机上本来也拖不了列宽 */
  #modal-root table.tbl[data-colkey] th,#modal-root table.tbl[data-colkey] td{width:auto !important;min-width:0 !important}
  #modal-root .mb img,#modal-root .mb video,#modal-root .mb canvas{max-width:100%;height:auto}
  #modal-root .mb svg{max-width:100%}
  #modal-root .mb input,#modal-root .mb select,#modal-root .mb textarea{max-width:100%;box-sizing:border-box}
  #modal-root .mb .row,#modal-root .mb .toolbar,#modal-root .mb .tabs{flex-wrap:wrap;max-width:100%}
  #modal-root .mb .row>*,#modal-root .mb .grid2>*,#modal-root .mb .toolbar>*{min-width:0}
  #modal-root .mb pre,#modal-root .mb code{white-space:pre-wrap;word-break:break-word}
  /* thead 表卡片化配套（与 app.js 的 .card-thead 打标成对出现） */
  table.tbl.cardable.card-thead > thead{display:none}
  table.tbl.cardable.card-thead > tbody > tr:first-child{display:flex}

  /* ===== v5.44（老板 09-21）：「添加考勤组手机端的界面也修改一下，把所有手机端的界面都优化一下，
     要好操作、好看、美观，不要搞得乱七八糟的界面」=====
     ★★ ① 「0 宽列」根因修复（整张表单被撕成「一列 0 宽 + 一列满宽」）
     现象（真机 390×844 实测）：「添加考勤组」弹窗里「组名 / 上班时间 / 休息时长」三个输入框
     只剩 **26px 宽**（.field 宽 0 ⇒ input width:100% = 0 + padding 24 + border 2 = 26），
     而「考勤方式 / 下班时间 / 加班折算」却是满宽；`.grid2` 的 used value 打印出来是 `0px 302px`。
     根因：`.grid2` 在手机端是**单列**（grid-template-columns:1fr，CSSOM 实测生效），
     但 app.js 里 4 处 `.field` 写了**行内** `style="grid-column:span 2"`（考勤组「加班方式」+
     企微通知 3 处）。Grid 自动布局遇到 `span 2` 而显式列只有 1 条时会**创建隐式列**，
     隐式列宽按内容取 max-content（那个 select 的最长选项 ≈302px）⇒ 第一列（1fr）被压到 0。
     ⇒ 单列时必须把 span 归位到 `auto`。**内联样式优先级高于媒体查询 ⇒ 必须 !important**
     （同 v5.05 铁律 41① / v5.38 ② 的写法）。
     为什么以前没发现：桌面端是两列，`span 2` 本来就该跨两列 ⇒ 桌面完全正常；jsdom 不排版 ⇒ 测试全绿。
     这是典型的**静默失败**：只有真机窄屏才看得见。 */
  #modal-root .mb .grid2 > *{grid-column:auto !important}

  /* ② 触控尺寸达标（手机端弹窗内）---------------------------------------------
     真机实测（390×844，「添加考勤组」）：勾选框只有 **13×13px**（工作日 7 个 + 角色 4 个
     共 11 个）、单选 13px、输入框高 40~41px、页脚按钮 40px —— 全都低于 44px 的触控基线，
     手指点不准（老板说的「好操作」直接落在这上面）。放大后逐条真机复测。
     ⚠ 表格内的勾选框单独给 18px：手机端表格已卡片化，20px 会把勾选列的标题挤变形。 */
  #modal-root .mb input[type="checkbox"],#modal-root .mb input[type="radio"]{width:20px;height:20px;min-width:20px;min-height:20px;margin:0}
  #modal-root .mb table.tbl input[type="checkbox"],#modal-root .mb table.tbl input[type="radio"]{width:18px;height:18px;min-width:18px;min-height:18px}
  #modal-root .mb input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]),
  #modal-root .mb select,#modal-root .mb textarea{min-height:46px}
  #modal-root .mb textarea{min-height:76px}
  #modal-root .mf .btn{min-height:46px;padding:12px 20px;font-size:15px}
  /* ③ 括号里的「补充说明」不再贴着一行挤（勾选项文字可读性）：行内勾选/单选标签统一给足
     行高与最小高度，长名字不再被压成竖条（实测「店长」「销售」曾变成 43×51px 的竖条）。 */
  #modal-root .mb label{line-height:1.5}
  /* ④ 行内勾选/单选选项组：从「小方框 + 贴着字」升级为**整块可点的标签**
     真机实测：勾选框放大到 20px 之后，label 整块仍只有 **50×20px**（手指点的是标签不是方框）
     ⇒ 给足内边距与最小高度，让真实可点区域 ≥44px；短线选项（周日…周六）在容器的
     `flex-wrap:wrap` 里自动排成**标签云**，长选项（如「大小周（大周周六上班 / 小周双休，交替轮换）」）
     自动独占一行 —— 两种情况都不用改 app.js。
     ⚠ 必须 `!important`：这些 label 的 `display:inline-flex;gap:4px;margin:…` 全是**行内样式**
     （站内 4 处 grid-column:span 同理，见 ①）。 */
  #modal-root .mb label[style*="inline-flex"]{
    display:inline-flex !important;align-items:center !important;gap:8px !important;
    min-height:44px;padding:7px 12px;border-radius:9px;background:var(--bg);
    box-sizing:border-box;margin:0 8px 7px 0 !important;font-size:13.5px !important;
  }
  #modal-root .mb label[style*="inline-flex"]:active{background:var(--green-l)}
  /* ⑤ 字段间距收紧：手机端屏幕金贵，`14px` 的字段间距在 10 个字段的弹窗里白吃掉 140px
     （真机实测「添加考勤组」内容高 1498px、可视只有 678px）。 */
  #modal-root .mb .field{margin-bottom:11px}
  #modal-root .mb .fld-note{font-size:11.5px}
}

/* v5.59 需求I（老板 09-23「各种界面右边和底下的双滚动条优化一下」）——全站滚动条细化。
   现象：内容区 / 弹窗 / 表格横向滚动各自长出一条**又宽又实**的滚动条，与窗口滚动条并排
   ⇒ 看着像"两个滚动条"。v5.58 ⑫ 已把**导航栏**那条藏掉（它最碍眼，菜单一多就有）。
   本版做**全站统一**：宽/高收窄到 6px、轨道透明、滑块半透明，鼠标移上去才加深。
   ⚠ 铁律：只改**外观**，一条滚动能力都不关 —— 不许用 overflow:hidden（会把内容裁掉，
     导航栏菜单项会点不到），也不动任何容器的 overflow 值。
   ⚠ `*` 的特异性低于 `.nav{scrollbar-width:none}` ⇒ 导航栏仍然不显示滚动条，不被本条覆盖。 */
::-webkit-scrollbar{width:6px;height:6px}
::-webkit-scrollbar-track{background:transparent}
::-webkit-scrollbar-thumb{background:rgba(0,0,0,.18);border-radius:3px}
::-webkit-scrollbar-thumb:hover{background:rgba(0,0,0,.34)}
::-webkit-scrollbar-corner{background:transparent}
*{scrollbar-width:thin;scrollbar-color:rgba(0,0,0,.18) transparent}

/* v5.59 需求L（老板 09-23「销售预算单里的输入框改成圆润一点好看一点」「明细输入框加高一点」
   ＋「项目信息里所有输入框改成一样宽」）：
   · 圆润：边框倒角 10px、内边距加大、聚焦时外发光（只改外观，不改任何值 / 行为）。
   · 加高：明细行内输入框原先只有浏览器默认高度（老板看着"很扁"）⇒ padding 8px 11px +
     min-height 36px、字号跟正文 14px（不改行高逻辑，表格自己会撑开）。
   · 等宽：项目信息是 kv 两列表，输入框 width:100% ⇒ 一列多宽就多宽；min/max 只是防极端屏。
   ⚠ 选择器钉在 #bq_body（bqDetail 弹窗正文容器）⇒ 只影响销售预算单，其它弹窗零波及。
   ⚠ 不碰 button / a —— 只 input / select / textarea。 */
#bq_body input,#bq_body select,#bq_body textarea{
  border:1px solid var(--line);border-radius:10px;padding:8px 11px;background:var(--bg);
  min-height:36px;font-size:14px;line-height:1.4;box-sizing:border-box;
  transition:border-color .15s,box-shadow .15s;
}
#bq_body input:focus,#bq_body select:focus,#bq_body textarea:focus{
  outline:none;border-color:var(--green);box-shadow:0 0 0 3px rgba(1,39,108,.12);
}
#bq_body table.kv td input{width:100%;min-width:210px;max-width:340px}

/* v5.70 ①：第三方采购单「采购明细」—— 输入框跟随列宽（改列宽，输入框自动跟着变）
   改之前：列宽写在 <th>（重量70/数量60/楼层60/单价80），输入框又各写一套死值
   （单位56/重量66/数量58/楼层58/备注 min-width:70）⇒ 两组数字互不相关，改列宽输入框不动。
   现在：① 输入框 width:100% + box-sizing:border-box ⇒ 宽度只由单元格决定，且总能填满；
        ② size="1" 压掉 input 默认 size=20 的固有宽度（table-layout:auto 时会被撑到 ~180px）；
        ③ 宽屏 table-layout:fixed ⇒ 列宽只认 <th>，跟随是真跟随；窄屏（≤820）由 v5.05 的
           媒体查询强制回 auto 并走卡片化，size="1" 兜底不会把卡片撑爆。
   ⚠ 选择器钉在 #tpo_rows（第三方采购单明细表）⇒ 其它弹窗零波及。 */
@media screen and (min-width:821px){
  /* min-width：fixed 布局下列宽严格按 <th>，容器装不下就要**横向滚动**而不是把列压扁。
     实测容器 720px、定宽合计 968px ⇒ 不写 min-width 会被压回 720，宽列塌成 56px。 */
  #tpo_rows{table-layout:fixed;min-width:968px}
}
#tpo_rows input.tpo-in{width:100%;min-width:0;max-width:100%;box-sizing:border-box}

/* v5.71 需求⑨：新建订单「所属项目」输入框加到最宽顶到右边（老板 09-24）
   ⚠ 根因：站内搜索框统一被 srchClearApply 包一层 `.srchw`，而 `.srchw` 是 **inline-flex**
     ⇒ 宽度随内容，输入框看着没顶满、右侧留一条空白（这才是「加宽顶到右边」的成因）。
   ⚠ 只做 #op_picker 局部覆盖：`.srchw` 是全站共用的（工具栏里的搜索框本来就该是窄的），
     全局改成 flex/100% 会把别处布局撑破。
   ⚠ 这里不添加任何新按钮 —— 清除按钮复用既有的 `.srch-x`（v4.7-24 就有），
     只是它在这个场景里由 srchXPin() 判定为**常显**。 */
#op_picker .srchw{display:flex;width:100%}

/* ============ v5.77 ② 角色编辑器手机端优化（老板 09-25 上午「角色权限设置手机端操作不方便」） ============
   · 桌面端零变化：① .bp-cell 是从 app.js 行内样式**逐字平移**成 class（桌面效果等价）；
     ② 手机端变形全部收在 @media screen and (max-width:820px)（screen 关键字保打印安全）。
   · 采集链零改动：roleSave 只认 [data-pk]/[data-dash]/[data-kqcfg]，此处不动任何 name/data-pk。
   · 真浏览器取证（jsdom 不算布局）：Edge 无头 375px 截图 + 探针量勾选框可点区与卡片无横向溢出。 */
.bp-cell{display:inline-flex;align-items:center;gap:4px;margin:2px 12px 2px 0;font-size:12.5px;cursor:pointer}
/* v5.78④：按钮权限格子的位置小字直接可见（老板 09-25「按钮也没有显示位置在哪里」——
   位置原来只在 title 悬浮里，手机端没有悬浮永远看不到；现在格子内常显小字，title 保留） */
.bp-cell .bp-loc{font-style:normal;font-size:11px;color:var(--tx2,#8a94a6);margin-left:2px;white-space:nowrap}
@media screen and (max-width:820px){
  /* ── 主权限表 perm-grid：三列宽表 → 「一个权限项一张卡」（DOM 一行不改）── */
  table.perm-grid{display:block;width:100%;border-collapse:separate}
  table.perm-grid > tbody{display:block;width:100%}
  table.perm-grid > tbody > tr:first-child{display:none}   /* 表头行（模块/数据范围/操作权限，全 th）藏掉 */
  table.perm-grid > tbody > tr{display:block;border:1px solid var(--line);border-radius:8px;margin:0 0 8px;padding:6px 10px;background:var(--card,#fff)}
  table.perm-grid > tbody > tr.perm-grp-tr{margin-top:10px;background:var(--bg2,#f2f4f8)}   /* 分组头保留成标题条 */
  table.perm-grid > tbody > tr > td{display:block;border:none;padding:3px 0;text-align:left !important}
  table.perm-grid > tbody > tr > td:first-child{font-weight:600;white-space:normal}
  /* 二、三列块级后各印一行小标签（组头/子行的 colspan 格自带说明文字，不印） */
  table.perm-grid > tbody > tr > td:nth-child(2):before{content:'数据范围';display:block;font-size:11px;color:#8a97a6}
  table.perm-grid > tbody > tr > td:nth-child(3):before{content:'操作权限';display:block;font-size:11px;color:#8a97a6}
  table.perm-grid > tbody > tr > td[colspan]:before{content:normal}
  /* ── 勾选框 chip 化：可点区 ≥36px，勾选框放大到 17px ── */
  .perm-grid label, .bp-cell{display:inline-flex;align-items:center;vertical-align:middle;margin:3px 8px 3px 0;padding:5px 10px;background:var(--bg2,#f2f4f8);border-radius:8px;font-size:13px;min-height:36px;box-sizing:border-box}
  .perm-grid label input[type=radio], .perm-grid label input[type=checkbox], .bp-cell input[type=checkbox]{width:17px;height:17px;vertical-align:middle}
}

/* v6.04：日常巡场表列表「项目地址」的默认样式 —— 原来写在行内 style 里，挪到类上，
   好让上面 media (max-width:820px) 的卡片规则能覆盖它（行内样式是覆盖不动的）。
   桌面 = 小灰字（与改前完全一致）；窄屏卡片 = 加大加黑（见上面的 cardable 规则）。 */
.dinsp-addr{color:var(--tx2)}


/* v6.24：地图页「贴底」（老板 10-08：「地图底下还是有一个空白条」）
   —— `.main` 的底部内边距是桌面 22px / 手机 90px＋安全区（见上面两条 .main 规则），
      地图再怎么精算高度都会被它顶出一条 ⇒ 本页归零，配合 pmapFit() 让页面正好一屏、不出滚动条。
   ⚠ v6.29（老板 10-09）：「项目地图最下面留白和左右两边一样」⇒ **撤掉 v6.24 的「贴底」**：
     原先这里那条规则把地图页 .main 的底部内边距归零 ⇒ 地图一直贴到最底部，与左右留白不一致。
     现已删除该规则 ⇒ 地图页沿用 .main 的默认内边距（桌面 22px / 手机 90px＋安全区）。
     ⚠ 但手机端那个 90px **其实并不等于左右的 14px**（老板 10-15 又报「底下还空了一截」）
       ⇒ 真正的"与左右一致"由下面 v7.63 那条规则实现，本条只保留历史结论。
     `body.pg-pmap` 标记仍由 pageProjMap() 挂着（无副作用，留作将来再调时的挂点）。 */

/* ★ v7.63（老板 10-15 00:0x「项目地图手机端地图框底下还空了一截，能不能底下和左右留一样的距离固定不动」）：
   窄屏下 `.main` 的**底部**内边距是 90px＋底部安全区（见上面那条 ≤820px 的 .main 规则），而**左右**只有 14px
   ⇒ 地图容器按 pb 精算到"正好填满"后，底部仍比左右多出一大截（iPhone 上 90＋34＝124px vs 左右 14px）。
   ⚠ v6.29 当时撤掉 v6.24 的「贴底」只是把底部从 0 退回 90px，**并没有真的做到"与左右一致"** —— 本版才算做到。
   做法：**地图页**在窄屏下把底部内边距改成**与左右同一个表达式**（14px＋左右安全区）。
   ⚠ 这里特意取**左右**那侧的安全区（不是底部那侧），才能让"底部 ≡ 左右"严格相等：
     竖屏时两者都是 0 ⇒ 地图四周各留 14px；横屏（刘海在左）时底部等于左边，仍与左右同距。
     另：这样写也**不会在 styles.css 里多出**一条底部安全区的 env() —— 包自检会数那个串的出现次数。
   ⚠ 留白是**常量表达式**，不随滚动位置／地址栏伸缩变化 ⇒ 地图框底部与左右始终同距（老板要的「固定不动」）；
     pmapFit() 读的是 .main 的实测 paddingBottom，本条一改它自动跟着适配，**无需改 JS**。
   ⚠ 只作用于**地图页**+**窄屏**：桌面端（左右 26 / 底部 22）与其它页面的底部 90px 留白一字不动。 */
@media screen and (max-width:820px){
  body.pg-pmap .main{padding-bottom:calc(14px + env(safe-area-inset-left,0px))}
}

/* ★ v7.63②（老板 10-15 00:1x「手机端找项目里导航栏图标下面的找项目3个字和图标隐藏不要，
   搜产品名称和搜区域的搜索框分上下两条，宽度改成界面最宽，下面筛选时间的和在地图显示放一排中分各占一半」）：
   **只作用于「找项目」页的工具栏**（`.toolbar.fp-bar` 由 pageFindProj 打）**＋ 窄屏**：
     ① 标题标签「🔍 找项目」整块隐藏（手机端它占了整整一行，下面的搜索框才是真正要用的）；
     ② 两个搜索框各占一整行（上下两条、宽度铺满）；
     ③ 时间筛选 ＋「在地图显示」并排一行、各占一半。
   桌面端（≥820px）一行不变 —— 那三样加上标题在宽屏上排得下。
   ⚠ 搜索框的宽度必须**打在它外面那层壳上**：`srchClearApply()` 运行时会把这个 input 包进
     `span.srchw`（inline-flex）—— 只给 input 设宽会被那层壳挡住（`#op_picker` 早踩过同一处）。
     这里"包壳前 / 包壳后"两种形态都写（`>.srchw` 与 `>#fp_q,>#fp_aq`），谁在都不会漏。
   ⚠ 各占一半按 `.toolbar` 的 gap（10px）算 ⇒ calc(50% − 5px)。
   ⚠ 本块插在文件中段、不在末尾 —— styles.css 末尾还有 820px 段，追加到末尾会被后面的规则盖掉。
   ⚠ 时间下拉原来把宽度写在**行内样式**（`max-width:130px`）里 ⇒ 行内样式优先级最高，
     窄屏那条 `max-width:none` 根本压不动（实测：两半分别是 130 / 222，不是各半）⇒
     本版把它**挪到样式表**（下面这条顶层规则），窄屏才改得动。 */
.toolbar.fp-bar .fp-range{max-width:130px}
@media screen and (max-width:820px){
  .toolbar.fp-bar .fp-title{display:none}
  /* ★ v7.64：项目地图页那行标题（图标＋文字）同样藏掉 —— 手机端它纯占位置，右边的统计数字留着有用。 */
  .toolbar.pm-bar .pm-title{display:none}
  .toolbar.fp-bar>.srchw,
  .toolbar.fp-bar>#fp_q,
  .toolbar.fp-bar>#fp_aq{flex:1 1 100%;width:100%;max-width:none;min-width:0}
  .toolbar.fp-bar>.srchw{display:flex}
  .toolbar.fp-bar .srchw>input{flex:1 1 auto;width:100%;min-width:0}
  /* 各占一半；两者**等高**（select 与 button 的默认内边距不同 ⇒ 差 3~4px，看起来不齐） */
  .toolbar.fp-bar .fp-range,
  .toolbar.fp-bar .fp-mapbtn{flex:1 1 calc(50% - 5px);max-width:none;min-width:0;width:auto;
    height:38px;box-sizing:border-box;padding:0 10px;font-size:13px}
}

/* ============================================================ ★ v6.27⑩（老板 10-09 02:0x）
   「新增巡场记录里手机端，巡场时间的输入框超出了屏幕」。
   v5.58 曾修过同一字段的同一症状（`<input type=date>` 的**固有宽度**撑破半宽列 ⇒「右边没有收口」），
   本版再加一层**更硬的收口兜底** —— 只作用于弹窗（#modal-root .mb）里的表单，不动其它布局：
     ① `.field` 与其中的控件一律 `min-width:0`（flex/grid 子项默认 min-width:auto 是撑宽的老根因）；
     ② 控件宽度钉成 `100%` + `box-sizing:border-box`（即便某端 UA 对 date 有固有宽度也压得住）；
     ③ `.spick`（项目/销售检索组件）也纳入 —— 它内部 `input{flex:1}` 在窄屏下同样会顶宽父级。
   ⚠ 刻意**不加** `-webkit-appearance:none` —— 那会把手机上的原生日期选择器外观去掉（点了不弹日历），
     属于「越修越坏」；收口只需宽度约束即可。 */
#modal-root .mb .field{min-width:0;max-width:100%}
#modal-root .mb .field>input,#modal-root .mb .field>select,#modal-root .mb .field>textarea,
#modal-root .mb .field>.spick,#modal-root .mb .field>div{min-width:0;max-width:100%}
#modal-root .mb .field>input,#modal-root .mb .field>select,#modal-root .mb .field>textarea{
  width:100%;box-sizing:border-box}
(日期 / 时间类输入的收口已**并入 v5.58 那条四条一组的规则**（本版给它补了 `width:100%`）——
  故意不在末尾再加一条重复规则：包自检会数那条选择器的出现次数，重复写会让计数变 2 而当场报红。
  ⚠ 注释里同样**不抄那段选择器**（抄了照样算进计数 —— 注释自伤，本版第 5 次）。)
/* 窄屏（含手机）再压一层：检索组件的输入框不强制 150px 下限（窄屏下那是溢出的直接推手） */
@media screen and (max-width:820px){
  #modal-root .mb .spick>input{min-width:0}
  #modal-root .mb .grid2{grid-template-columns:minmax(0,1fr)}
}

/* ★ v6.27⑪（老板 10-09 02:13）：「手机端日常巡场表列表：搜索框单独一行加宽到底，
   下面一行放状态选择框 + 新建巡场记录，清筛选删掉」。
   只作用于该页工具栏（`.dinsp-bar` 由 pageDailyInspections 打，其它列表不受影响）；
   桌面端（≥820px）一字不变 —— 仍是「搜索 / 状态 / 明细计数 / 新增」一行排开。 */
@media screen and (max-width:820px){
  .toolbar.dinsp-bar{flex-wrap:wrap;gap:8px}
  /* ★ 搜索框要打在 **.srchw** 上：srchClearApply() 运行时会自动给搜索框包一层
     `<span class="srchw">`（styles.css 里它是 `display:inline-flex`）＝ toolbar 的真正 flex 子项；
     打在 input 上的 flex 是无效的（本版实测踩到，四处"独占一行"全没生效）。 */
  .toolbar.dinsp-bar>.srchw{flex:1 1 100%;width:100%;min-width:0;display:flex}
  .toolbar.dinsp-bar #ipt_dinspQ{width:100%;min-width:0}
  .toolbar.dinsp-bar .sp{display:none}                                   /* 手机端不要那个"撑开占位" */
  .toolbar.dinsp-bar>select{flex:1 1 auto;min-width:0}                   /* 状态选择与新建按钮同一行 */
}

/* ★ v6.27⑫（老板 10-09 02:2x）：「手机端项目管理的搜索框也改成一整行，清筛选按钮也删掉」。
   与日常巡场表同款做法（`.proj-bar` 由 pageProjects 打）——桌面端一字不变。 */
@media screen and (max-width:820px){
  .toolbar.proj-bar{flex-wrap:wrap;gap:8px}
  .toolbar.proj-bar>.srchw{flex:1 1 100%;width:100%;min-width:0;display:flex}  /* 搜索框独占一行（同 dinsp-bar） */
  .toolbar.proj-bar #ipt_projQ{width:100%;min-width:0}
  .toolbar.proj-bar .sp{display:none}
}

/* ===== ★★★ v7.27（老板 10-13 13:2x：「为什么底部调高低的短横抓手是在明细里面跟着明细一起动的，这个是bug吧」）=====
   真根因：拖拽柄原来是 position:absolute —— 而**绝对定位元素本身就是滚动内容的一部分**：
     ① 框自己内滚时，柄跟着内容一起滚走；
     ② 框不高（高度＝内容高、纵向滚动发生在外层弹窗正文）时，柄贴在**内容底部**（最后一行下面），
        外层一滚照样看不见 —— 老板看到的正是②。
   两步修（对齐住户电费结清区 .ems-box{overflow:auto} ＋ .ems-grip{position:sticky;bottom:0}
   这一套站内已验证的「sticky 常驻、滚动时也看得见」范式）：
     ① app.js 的 paneGrip 里柄改 position:sticky —— 钉在框的**滚动可视口底部**；
     ② 明细框给一个高度上限 ⇒ 明细很长时**框内滚动**，柄不会被推到内容末尾（也让「拖柄改高低」真正有意义）。
   ⚠ 上限只作用于 .smpd-wrap（小样明细表）这一个框；弹窗内其它可滚容器一字不动。
   ⚠ 手机端也给上限：卡片化布局不受影响（#smpd_rows{display:block} 那套降级仍在，只是明细区在框内滚）；
     这里只写 overflow-y，横向交给 v5.38 的 #modal-root .mb .table-wrap{overflow-x:hidden !important} 管，
     不去动那条全局规则。 */
@media (min-width:821px){ #modal-root .mb .smpd-wrap{max-height:58vh;overflow:auto} }
@media screen and (max-width:820px){ #modal-root .mb .smpd-wrap{max-height:68vh;overflow-y:auto} }

/* ★ v6.27⑬⑭（老板 10-09 02:3x）：
   ⑬「手机端订单列表搜索框改成一整行，其他 4 个一行」
   ⑭「手机端发货管理搜索框改成一整行；按创建时间筛选删掉、导出按钮取消、清筛选取消；另外三个放一排」
   做法与 ⑪⑫ 同款：两个工具栏各有专属类，控件由 `tbGrp()` 包成 flex 单元（`.ord-fl` / `.ship-fl`）。
   桌面端（≥820px）**一字不变**。 */
@media screen and (max-width:820px){
  .toolbar.ord-bar,.toolbar.ship-bar{flex-wrap:wrap;gap:8px}
  .toolbar.ord-bar>.srchw,.toolbar.ship-bar>.srchw{flex:1 1 100%;width:100%;min-width:0;display:flex} /* 搜索独占一行 */
  .toolbar.ord-bar #ipt_ordQ,.toolbar.ship-bar #ipt_shipQ{width:100%;min-width:0}
  .toolbar.ord-bar .sp,.toolbar.ship-bar .sp{display:none}
  /* 订单：状态 / 发货状态 / 列设置 / 清空筛选 —— ★ v6.34（老板 10-09 14:33
     「订单列表手机端里面几个分类全部挤到一起了」）：**改「两两一行」**。
     原先 `flex:1 1 0` 四等分 —— 手机可用宽约 350px ⇒ 每格仅约 81px，
     而「全部发货状态」6 字 ≈75px **再加下拉箭头**就超，「✕ 清空筛选」同理，
     ⇒ 四个控件被压扁、相邻贴死。现在每格 ≈ 50% − 半个 gap（约 171px），
     第一行「全部状态 | 全部发货状态」（两个筛选），第二行「⚙ 列设置 | ✕ 清空筛选」（两个操作）。
     `min-width` 是兜底：屏再窄就**整格换行**，绝不把文字压扁（宁可两行，不要挤）。 */
  .toolbar.ord-bar .ord-fl{flex:1 1 calc(50% - 4px);min-width:130px;display:flex}
  .toolbar.ord-bar .ord-fl>*{width:100%;min-width:0;box-sizing:border-box;font-size:12.5px;padding-left:6px;padding-right:6px}
  /* 发货：送货人 / 状态 / 列设置 —— 3 个均分一排 */
  .toolbar.ship-bar .ship-fl{flex:1 1 0;min-width:0;display:flex}
  /* ★ 空分组不占位（如管理员看不到「列设置」⇒ 该组为空，否则会白占 1/3 行宽） */
  .toolbar.ord-bar .ord-fl:empty,.toolbar.ship-bar .ship-fl:empty{display:none}
  .toolbar.ship-bar .ship-fl>*{width:100%;min-width:0;box-sizing:border-box;font-size:12.5px;padding-left:6px;padding-right:6px}

  /* ══ ★ v6.52④⑥⑦（老板 10-09 22:xx）：手机端「订单列表 / 项目列表 / 发货管理列表」
     都**只留「搜索框 + 新建」**，其余（筛选下拉 / 列设置 / 清空筛选 / 导出 / 导入 / 定位 /
     批量删除 / 统计条）一律隐藏。
     ⚠ 用「**除白名单外全隐藏**」而不是逐个黑名单 ⇒ 以后工具栏再加按钮，也自动守住这条口径
       （不用老板再提一次"怎么又多出来一个"）。
     ⚠ 白名单三样：`.srchw`（srchClearApply 运行时给搜索框包的壳）/ 裸 `input`（还没被包上的那一瞬）
       / `.m-add`（新建按钮 —— 渲染时显式打的类，见 pageOrders / pageProjects / pageShipments）。 */
  .toolbar.ord-bar>*:not(.srchw):not(#ipt_ordQ):not(.m-add),
  .toolbar.proj-bar>*:not(.srchw):not(#ipt_projQ):not(.m-add),
  .toolbar.ship-bar>*:not(.srchw):not(#ipt_shipQ):not(.m-add){display:none}
  /* ⚠ 搜索框要独占一行、拉满：**连裸 input 一起管** ——
     srchClearApply 只在运行时给输入框包 `.srchw`，而「点清空筛选 → 重渲染」那一瞬还没包上
     ⇒ 旧规则只写 `.toolbar.ord-bar>.srchw` 会落空 ⇒ 搜索框缩成一小条（老板 10-09 报的就是这个）。 */
  .toolbar.ord-bar>.srchw,.toolbar.ord-bar>#ipt_ordQ,
  .toolbar.proj-bar>.srchw,.toolbar.proj-bar>#ipt_projQ,
  .toolbar.ship-bar>.srchw,.toolbar.ship-bar>#ipt_shipQ{flex:1 1 100%;width:100%;min-width:0;display:flex}
  .toolbar.ord-bar #ipt_ordQ,.toolbar.proj-bar #ipt_projQ,.toolbar.ship-bar #ipt_shipQ{width:100%;min-width:0}
  /* ══ ★ v6.52⑤：手机端订单列表**卡片**里的「发货」按钮隐藏（同行的「编辑」等不受影响） */
  .cardable .m-shipbtn{display:none}
  /* ══ ★ v6.52⑧（老板 10-09 22:xx）：「手机端订单列表的状态显示隐藏」
     卡片态下每行的 td 由渲染出口 `cardifyTables()` **统一补 data-lbl**（值＝列名），
     而卡片态的表头行本就被 `tr:first-child{display:none}` 隐藏
     ⇒ 只隐藏 `td[data-lbl="状态"]` 就行；桌面端表格视图（>820px）**一字未动**。 */
  table.tbl.cardable > tbody > tr > td[data-lbl="状态"]{display:none}
}

/* ===== v6.30 版本更新记录（左下角版本号点进来） ===== */
.sidebar .foot .ver .verlink{cursor:pointer;text-decoration:underline dotted;text-underline-offset:2px}
.sidebar .foot .ver .verlink:hover{color:#fff}
/* ★ v6.53⑪：元素图库卡片作拖拽落点时的提示（拖到哪张卡，就传给哪个产品） */
.elem-drop.elem-over{outline:2px dashed var(--ac);outline-offset:2px;background:#f6faff}
.chg-list{display:flex;flex-direction:column;gap:10px}
.chg-card{border:1px solid var(--line);border-radius:var(--radius);background:var(--card);padding:10px 14px;box-shadow:var(--shadow)}
.chg-card.chg-now{border-color:#b7e0bb;background:#f7fdf8}
.chg-head{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:4px}
.chg-ver{font-weight:700;font-size:14px;color:var(--tx)}
.chg-date{font-size:12px;color:var(--tx2)}
.chg-badge{font-size:11px;line-height:16px;padding:0 7px;border-radius:9px;background:#e8f5e9;color:#2e7d32;border:1px solid #b7e0bb}
.chg-cnt{font-size:11.5px;color:var(--tx2);margin-left:auto}
.chg-items{margin:0;padding-left:20px;font-size:13px;line-height:1.85;color:var(--tx)}
.chg-items li{margin:0}
@media (max-width:820px){
  .chg-card{padding:9px 11px}
  .chg-items{font-size:12.5px;line-height:1.75}
}


/* ===== ★ v6.88⑧⑥（老板 10-11）：配置中心吸顶 =====
   老板原话：「配置中心往上滚动时 ① 选择功能模块及以上的部分固定不动」。
   滚动容器是整页（.layout / .main 都没有 overflow）⇒ sticky 相对视口直接可用。
   ① 面板的 top 由 JS 按**实测的 topbar 高度**设置（不写死像素，换机型也准）。 */
.main.cfg-sticky .topbar{position:sticky;top:0;z-index:31;background:var(--bg);padding:12px 0 10px;margin-bottom:8px}
.main.cfg-sticky [data-cfgbar]{position:sticky;top:0;z-index:30}
/* ★ v7.85（老板 10-16 20:1x）：「① 选择功能模块」由按钮排改成下拉选择 ⇒ 补一条下拉自己的样式
   （全局只有 .field select / .toolbar select 两套，这里不在 .field 里、也不是工具条，不补就是浏览器默认长相）。
   ⚠ 故意写在 cfg-sticky 这段旁边、**不追加到文件末尾** —— 末尾还有 820px 段会盖（铁律 12）。 */
.cfg-mod-sel{min-width:240px;width:240px;box-sizing:border-box;max-width:100%;padding:9px 12px;border:1px solid var(--line);border-radius:8px;
  font-size:14px;background:#fff;color:var(--tx);cursor:pointer}
.cfg-mod-sel:focus{outline:none;border-color:var(--green)}
/* ★ v8.04（老板 10-18 11:00）：「搜索框外框改成和右边的选择一样的形状和大小」——
   搜索框原来只有一段行内 style（max-width:190px），不在任何 .field/.toolbar 里 ⇒ 浏览器默认方框，
   跟右边那颗下拉完全两个样子。这里补一条与 .cfg-mod-sel **同 padding / 同圆角 / 同字号 / 同宽度**
   的规则（宽度对齐靠 select 也定成 240px，见上面那条）。
   ⚠ 与 .cfg-mod-sel 一样写在文件中部（铁律 12：别追加末尾，820px 段会盖）。 */
.cfg-mod-q{min-width:240px;width:240px;box-sizing:border-box;padding:9px 12px;border:1px solid var(--line);
  border-radius:8px;font-size:14px;background:#fff;color:var(--tx);margin-right:8px;vertical-align:middle}
.cfg-mod-q:focus{outline:none;border-color:var(--green)}
@media (max-width:820px){
  .cfg-mod-sel{width:100%;min-width:0}
  .cfg-mod-q{width:100%;min-width:0;margin:0 0 8px}
}
/* ★ v7.96（老板 10-17 12:48）：「登记入住 / 离开」里的输入框太方不好看 ⇒ 给它们补一套与全站一致的
   圆角 / 内边距 / 聚焦态。同 cfg-mod-sel 的经验：这层不在 .field 里、也不是工具条，
   **不补就是浏览器默认长相**（四四方方）。选择器统一挂在 .dorm-reg 下，不污染别处。
   ⚠ 同样写在文件**中部**、不追加末尾（末尾还有 820px 段会盖，铁律 12）。
   ⚠⚠ **一律不做原生外观重置**（那条硬属性名在此**一字不写**，v6.27⑩ 有断言扫全段）：
      重置后手机点了不弹原生日期选择器 / 滚轮，属"越修越坏"（test-v629/v630 守着）。
      所以这里只调边框 / 圆角 / 内边距 / 聚焦态，控件本身的系统外观一概不动。 */
.dorm-reg{max-width:420px}
.dorm-reg .rg-row{margin-bottom:12px}
.dorm-reg .rg-row>label{display:block;font-size:13px;color:var(--tx2);font-weight:600;margin:0 0 6px}
.dorm-reg select,.dorm-reg input{width:100%;box-sizing:border-box;padding:10px 12px;border:1px solid var(--line);
  border-radius:10px;font-size:14px;background:#fff;color:var(--tx);transition:border-color .15s,box-shadow .15s}
.dorm-reg select:focus,.dorm-reg input:focus{outline:none;border-color:var(--green);
  box-shadow:0 0 0 3px rgba(34,160,107,.13)}
.dorm-reg input[type=date]{min-height:42px;line-height:1.2}
@media (max-width:820px){
  .dorm-reg{max-width:none}   /* 手机上撑满（与其它表单一致） */
}
/* ★ v6.90①（老板 10-11 16:2x）：元素图库往下翻时**顶部标题栏 + 搜索/新建产品那一排**固定不动。
   与配置中心（.cfg-sticky）同一套打法：滚动容器是整页（.layout/.main 都无 overflow）⇒
   position:sticky 相对视口直接可用；工具栏的 top 由 pageElem() **实测 topbar 高度**后写进去（不写死像素）。
   ⚠ 这个类在 route() 开头会被统一清掉 —— 切到别的页面不该吸顶。 */
.main.elem-sticky .topbar{position:sticky;top:0;z-index:31;background:var(--bg);padding:12px 0 10px;margin-bottom:8px}
.main.elem-sticky [data-elembar]{position:sticky;top:0;z-index:30;background:var(--bg);padding-bottom:8px}

/* ===== ★ v6.90⑫：配置中心「元素图库 → 字段与选项」面板（卡片 + 选项药丸） ===== */
.ef_wrap{display:flex;flex-direction:column;gap:10px}
.ef_row{border:1px solid var(--line);border-radius:11px;padding:10px 12px;background:var(--card)}
.ef_top{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:8px}
.ef_top input.ef_label,.ef_top input.ef_key{padding:7px 10px;border:1px solid var(--line);border-radius:8px;
  font-size:13px;background:#fff;color:var(--tx);width:150px}
.ef_top input.ef_key{background:#f7f9fb;color:var(--tx2)}
.ef_top .sp{flex:1;min-width:8px}
.ef_tog{display:inline-flex;align-items:center;gap:5px;font-size:12.5px;color:var(--tx2);cursor:pointer;
  border:1px solid var(--line);border-radius:8px;padding:6px 10px;background:#fff;user-select:none}
.ef_tog input{margin:0}
.ef_tog:has(input:checked){color:var(--tx);border-color:#cfd9e4;background:#f4f8fd}
.ef_del{padding:6px 10px}
.ef_chips{display:flex;align-items:center;gap:6px;flex-wrap:wrap;min-height:38px;padding:6px 8px;
  border:1px solid var(--line);border-radius:9px;background:#fafbfc}
.ef_chip{display:inline-flex;align-items:center;gap:2px;background:#eef4fb;border:1px solid #d9e4f0;
  border-radius:999px;padding:2px 4px 2px 4px;font-size:12.5px;color:var(--tx);line-height:1.9}
.ef_chip .ef_v{padding:0 4px}
.ef_chip .ef_mv{cursor:pointer;color:#9fb0c2;font-size:9px;padding:0 2px;line-height:1}
.ef_chip .ef_mv:hover{color:var(--tx)}
.ef_chip .ef_x{cursor:pointer;color:#a8b6c6;font-size:14px;line-height:1;padding:0 5px 0 3px}
.ef_chip .ef_x:hover{color:var(--red)}
.ef_optin{flex:1;min-width:150px;border:0;outline:none;background:transparent;font-size:12.5px;
  color:var(--tx);padding:6px 4px}
.ef_add{padding:5px 12px}
.ef_foot{margin-top:12px}

/* ===== ★ v6.90b（老板 10-11 17:1x）：元素图库图片角标 =====
   `.elem-bbtn/.elem-xbtn` 此前**只有类名没有定位规则** ⇒ 里面的 `right:2px` 被忽略，
   角标其实一直挤在图片**左上角**排着。补上定位，删除固定图片右上角。 */
.elem-xbtn{position:absolute;top:2px;right:2px;z-index:3;width:22px;height:22px;display:flex;align-items:center;
  justify-content:center;border-radius:6px;background:rgba(255,255,255,.92);border:1px solid var(--line);
  font-size:12px;line-height:1;text-decoration:none;color:var(--red);cursor:pointer}
.elem-xbtn:hover{background:#fff;border-color:var(--red)}

/* ★ 2026-10-25（老板：「手机端大鼎主页点击进入单个软件后没有返回到主页的按钮」）：
   顶栏右侧加一个「⌂ 返回大鼎主页」，**只在窄屏显示**（桌面有侧栏，用不上这个）。
   点击直达 https://shgani.vip/ 那张六张卡的主页。 */
.homebtn{display:none}
@media(max-width:820px){
  .homebtn{display:inline-flex;align-items:center;justify-content:center;flex:none;
    width:34px;height:34px;border-radius:9px;background:#fff;
    border:1px solid var(--line);color:var(--tx);
    font-size:17px;line-height:1;text-decoration:none;margin-left:2px}
  .homebtn:active{background:var(--green-l)}
}


/* ═══════════════════════════════════════════════════════════════════
   v9.04（2026-10-25）：顶栏标题居中 + 手机端右侧小头像（进「我的账号」）
   · .topbar 变 position:relative，标题用绝对定位居中 —— 不受左右两侧按钮多少影响
   · 标题 max-width 55%，超长省略，避免压住右侧操作按钮
   · .tb-av 只在 <=768 显示；桌面端侧栏底部本来就有头像入口
   ═══════════════════════════════════════════════════════════════════ */
.topbar{position:relative}
.topbar .tb-title{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
  margin:0;max-width:55%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  text-align:center;pointer-events:none}
.tb-av{display:none;flex:0 0 auto;width:28px;height:28px;border-radius:50%;margin-left:6px;
  align-items:center;justify-content:center;overflow:hidden;cursor:pointer;
  background:linear-gradient(135deg,#fdeec0,#f3d493 42%,#dfae52 78%,#c8912f);
  color:#01276c;font-size:12px;font-weight:700;line-height:1}
.tb-av img{width:100%;height:100%;object-fit:cover;display:block}
@media (max-width:768px){ .tb-av{display:flex} .topbar .tb-title{max-width:50%} }


/* v9.06（2026-10-25）：手机端彻底去掉抽屉入口（汉堡按钮）
   老板：「手机端导航栏还是在」→ 底部已有分类导航。
   ⚠ 为什么带 !important：原有 717 行的 @media 里写了 `.hamburger{display:block}`，
     不加 !important 会被它盖掉（第一次就踩了这个：判断字符串命中 227 行的 display:none，误以为已加）。
   ⚠ 抽屉 DOM 保留，只隐藏按钮 —— 「我的账号」「退出登录」改从顶栏小头像进。 */
@media (max-width:768px){ .hamburger{display:none !important} }


/* v9.18（2026-10-25）：编辑效果图 —— 照片自适应（宽度撑满、高度按比例、最多 48vh） */
.fx_edit_ph{text-align:center}
/* v9.22：改成「高度优先、宽度自适应」—— width:100% 会让 16:9 的图在 760px 弹窗里撑到 427px 高，
   连同上面几个字段一起超过弹窗高度被截断。现在按 44vh 限高、宽度自动缩，比例不变也不会溢出。 */
.fx_edit_ph img{max-width:100% !important;max-height:44vh;width:auto;height:auto;
  object-fit:contain;display:block;margin:0 auto;border-radius:8px}


/* ═══════════════════════════════════════════════════════════════════
   v9.29（2026-10-25）：**五个系统的「回大鼎主页」按钮统一位置**
   老板：「5个软件的房子图标能不能放同一个位置」
   ⇒ 统一到**右下角固定圆钮**（38px，半透明），与 ev / hui / wb 三个独立系统完全一致；
     手机端抬高到 64px，避开底部那排分类 tabs。
   ⚠ 顶栏里的 .homebtn 原来靠 flex 排在右上角，这里用 position:fixed 把它从文档流里拎出来
     （不改 app.js 结构，风险最小）。
   ═══════════════════════════════════════════════════════════════════ */
.homebtn{position:fixed !important;right:14px !important;bottom:14px !important;left:auto !important;top:auto !important;
  width:38px !important;height:38px !important;min-width:0 !important;border-radius:50% !important;
  z-index:2147483000 !important;background:rgba(255,255,255,.94) !important;
  box-shadow:0 2px 12px rgba(16,30,54,.22) !important;display:flex !important;
  align-items:center !important;justify-content:center !important;color:#1f2733 !important;
  text-decoration:none !important}
.homebtn svg{width:19px !important;height:19px !important}
@media (max-width:768px){ .homebtn{bottom:64px !important} }   /* 避开手机底部 tabs */
/* ★ 2026-10-27 13:24（老板：「右下角的悬浮的主页图标还是在」）：
   手机端底栏（MobTabs）最右已经有「大鼎主页」这一项，右下角这颗悬浮圆钮是重复的。
   这里整颗隐藏；桌面端本来就 display:none（见上方 .homebtn 基础规则），所以等于全端不显示。 */
.homebtn{display:none !important}
/* ★ 2026-10-27（老板：「把电脑网页版左下角的退出登录改成返回主页」）
   侧栏底部那颗按钮现在是 <a href=主页>，补两行让链接长得跟原来的按钮一致。 */
.sidebar .foot a.logout-btn{text-decoration:none}
.sidebar .foot a.logout-btn:hover{background:rgba(120,180,255,.16);border-color:rgba(120,180,255,.38);color:#fff}
body.dd-ops .sidebar .foot a.logout-btn:hover{background:rgba(120,95,40,.18);border-color:rgba(120,95,40,.34);color:#4a3818}
