/* 商脉CRM 样式 —— 由 tools/split_index.py 从 index.html 抽出（v1.19.27）。
   内容与抽出前逐字一致；改样式请直接改本文件，别再往 HTML 里内联。 */

:root{
  --primary:#4f6ef7; --primary-2:#7b5cf0; --bg:#f4f6fb; --card:#fff;
  --text:#1f2430; --muted:#677086; --border:#e8ecf4; --danger:#ef5b77; --ok:#22b07d;
  /* 文字色补齐对比度: --muted(#8a93a6 on #fff) 只有 2.9:1, 达不到 AA(4.5:1),
     小字用它会糊。所以再分一档 --faint 专供「时间戳、计数」这类必须读的弱文字,
     --muted 只留给真正的装饰性文字。 */
  --faint:#677086;
  /* 2026-10-03: 上面那档补丁只落到 2 个地方, 另外 154 处还在用 --muted,
     自检扫出 88 条对比度告警(2.85~3.09:1)。逐个改 154 处既慢又会漏,
     所以直接把 --muted 提到 AA 线以上 —— 它是**文字色**, 就该可读。
     选 #677086 是因为它要在三种底色上都过线:
       #fff 4.95:1   #f7f9fd 4.70:1   #f4f6fb 4.58:1
     (只按白底挑到 4.5 的话, 落在 --surface-2 灰块上会掉到 4.29, 反而不合格。)
     装饰性用法只有一处(.ai-card .bar 那根 3px 竖条), 见下面已单独写死浅灰。 */
  --muted-deco:#8a93a6;   /* 仅供非文字的装饰(竖条/圆点), 不用于任何 color */
  /* --primary 是品牌色, 主要当**底色**用(按钮、选中态)。当它被当作
     小字颜色压在白底上时只有 4.28:1, 11px 字号下差一点点。
     不能为了这一处把品牌色整体压暗 —— 会连带改掉所有按钮的样子。
     所以单独给"主色文字"一档更深的蓝: #fff 上 4.66:1。
     用法: 任何 color:var(--primary) 且字号 <18px 的地方都该换成它。 */
  --primary-text:#4a68ee;
  --radius:12px; --shadow:0 2px 12px rgba(31,42,80,.08);
  /* ---- 圆角层级 ---- 苹果的做法是"同心圆角": 一个 16px 的大卡片,
     里面 16px 内边距的子块不该又是 16px —— 那样内外弧线不平行, 看着"歪"。
     子块圆角 = 父圆角 − 内边距。所以固定成三档, 配套使用:
       --r-lg 16 外层卡片/抽屉   --r-md 12 卡片内的块   --r-sm 8 输入框/小按钮
     内嵌一层就用小一档, 视觉上弧线才是同心圆。 */
  --r-lg:16px; --r-md:12px; --r-sm:8px; --r-xs:6px;
  /* ---- 阴影层级 ---- 原来全站 8 种写法, 谁也不知道哪个该更"浮"。
     收敛成 3 档, 只按「层级」选, 不按「好不好看」选:
       --sh-1 贴地(卡片、筛选项)  --sh-2 抬起(悬浮条、下拉)  --sh-3 浮层(抽屉、弹窗) */
  --sh-1:0 1px 3px rgba(31,42,80,.06), 0 1px 2px rgba(31,42,80,.04);
  --sh-2:0 4px 14px rgba(31,42,80,.10), 0 1px 3px rgba(31,42,80,.06);
  --sh-3:0 12px 32px rgba(31,42,80,.18), 0 2px 8px rgba(31,42,80,.08);
  /* ---- 动效规范 ---- 三档时长 + 一条主缓动曲线, 全站统一用这几个, 别各写各的 */
  --dur-1:120ms;        /* 微反馈: 按下、hover、颜色变化 */
  --dur-2:200ms;        /* 常规: 展开、淡入、位移 */
  --dur-3:300ms;        /* 大块: 抽屉、弹窗、页面切换 */
  --ease:cubic-bezier(.4,0,.2,1);          /* 通用: 快进慢出 */
  --ease-out:cubic-bezier(0,0,.2,1);       /* 进场: 减速停住 */
  --ease-spring:cubic-bezier(.34,1.4,.64,1); /* 回弹: 按下松手 */
  /* ---- 语义色 ---- 收敛原来散落的 12 种灰, 只留这几档 */
  --text-2:#5b6478;     /* 次级文字 */
  --text-3:#677086;     /* 弱化文字(= --muted)。原来是 #8a93a6, 只有 2.9:1, 已提到 AA 线以上 */
  --line:#e8ecf4;       /* 描边(= --border) */
  --line-soft:#f0f2f7;  /* 更弱的分隔线 */
  --surface-2:#f7f9fd;  /* 浅底: 输入框、代码块 */
  --primary-soft:#eef2ff;  /* 主色浅底: 选中态 */
  --danger-soft:#fff5f7;
  /* 深色模式补丁: 这些是浅色模式下写死的几个色, 单独抽出来才能整体换肤 */
  --scrim:rgba(20,25,40,.45);      /* 遮罩 */
  --scrim-soft:rgba(20,25,40,.35); /* 抽屉遮罩(轻一点) */
  --bar-bg:#232a3d;                /* 今日拜访悬浮条 */
  --hover-soft:#f7f9ff;            /* 表格行 hover */
  --shadow-lg:0 8px 24px rgba(0,0,0,.25);
  --scheme:light;
}

/* ================= 深色模式 =================
   做法: 只覆盖变量, 组件 CSS 一行不改 —— 前提是组件都用了变量。
   之前散落的写死色值(遮罩/悬浮条/表格 hover 等)已抽成上面的变量。
   触发方式二选一: 系统偏好(prefers-color-scheme) 或 手动 [data-theme]。 */
[data-theme="dark"]{
  --primary:#6d88ff; --primary-2:#9b7cf5;
  /* 深色下 --primary #6d88ff 落在 #1a1e29 上已有 5.24:1, 本来就够, 不用再压暗。
     但必须显式指回来 —— 否则会继承浅色那档更深的 #4a68ee, 在深底上反而看不清。 */
  --primary-text:#6d88ff;
  --bg:#12151d; --card:#1a1e29;
  --text:#e8ecf6; --muted:#8b93a7; --border:#2a3040;
  --danger:#ff6b85; --ok:#35c48f;
  --shadow:0 2px 12px rgba(0,0,0,.4);
  --faint:#98a1b5;
  --sh-1:0 1px 3px rgba(0,0,0,.30);
  --sh-2:0 4px 14px rgba(0,0,0,.38);
  --sh-3:0 12px 32px rgba(0,0,0,.50);
  --text-2:#a9b1c4; --text-3:#8b93a7;
  --line:#2a3040; --line-soft:#232838;
  --surface-2:#212734; --primary-soft:#232a44; --danger-soft:#33222a;
  --scrim:rgba(0,0,0,.6); --scrim-soft:rgba(0,0,0,.5);
  --bar-bg:#222839;
  --hover-soft:#202634;
  --shadow-lg:0 8px 24px rgba(0,0,0,.5);
  --scheme:dark;
  color-scheme:dark;  /* 让原生控件(滚动条/下拉/日期选择)也变暗 */
}
/* 换肤时给个短暂过渡, 避免颜色"啪"地跳变(只过渡颜色, 不碰布局) */
html.theme-anim,html.theme-anim *{
  transition:background-color var(--dur-2) var(--ease),
             border-color var(--dur-2) var(--ease),
             color var(--dur-2) var(--ease)!important;
}
*{margin:0;padding:0;box-sizing:border-box}
/* 按下反馈: 全站统一。iOS 上 :active 需要有个 touch 监听才生效,
   这里用 -webkit-tap-highlight-color 去掉系统默认的灰色块, 换成自己的。 */
button,.btn,.tab,.tabbar-item,.nx-chip,.q-chip,.sheet-item,.rt-pick,.card-tap{
  -webkit-tap-highlight-color:transparent}
/* 热区撑到 40x40,视觉尺寸不变 */
.rt-pick::after{content:"";position:absolute;left:50%;top:50%;
  width:40px;height:40px;transform:translate(-50%,-50%)}

button:active,.btn:active{transform:scale(.97)}
/* 图标按钮/小圆点按下去缩放小一点, 免得看着晃 */
.btn-icon:active,.rt-pick:active,.m-close:active,.sheet-close:active{transform:scale(.9)}
/* 主按钮按下时压暗一点点, 给"按实了"的感觉 */
.btn-primary:active{transform:scale(.97);filter:brightness(.94)}
.btn-danger:active{transform:scale(.97);background:var(--danger-soft)}
/* 纯文字/链接类按钮不做缩放, 只变浅 */
.btn-text:active,.link:active{opacity:.55}
/* 通用过渡: 只过渡会变的那几个属性, 不用 all(容易过渡到布局属性导致卡顿) */
.btn,.tab,.nx-chip,.q-chip,.sheet-item,.btn-claim{
  transition:transform var(--dur-1) var(--ease-spring),
             background-color var(--dur-1) var(--ease),
             border-color var(--dur-1) var(--ease),
             color var(--dur-1) var(--ease),
             box-shadow var(--dur-1) var(--ease),
             opacity var(--dur-1) var(--ease);
}
/* 只写 width:100%,不用 min-width:100vw —— 折叠屏展开瞬间/安卓带滚动条时
   100vw 比可视宽度大一截,会把页面撑出横向滚动条(右侧一条黑边或白边)。
   width:100% 跟随包含块,不会超宽。 */
html,body{width:100%;margin:0}
/* emoji 字体必须显式列在后面。界面里有 65 个按钮拿 emoji 当图标（📞 🔋 📋 ⋯），
   而 Android / Linux 上默认字体栈里没有彩色 emoji 字体，落到最后就渲染成
   空心方块「□」—— 实测「📞 13800000000 呼叫 ›」那行就是一个方块。
   这三个覆盖 iOS / Windows / Android，按系统优先级排，不影响中文字形。 */
body{font-family:-apple-system,BlinkMacSystemFont,"PingFang SC","Microsoft YaHei",
  "Apple Color Emoji","Segoe UI Emoji","Noto Color Emoji",sans-serif;
  background:var(--bg);color:var(--text);font-size:14px;
  /* 全站等宽数字。默认字体里 "1" 比 "8" 窄, 于是「12 条」变成「13 条」时
     整行文字会左右挪一下 —— 表格、统计卡、角标都会抖。
     tabular-nums 让所有数字占同样的宽度, 数字变化时页面纹丝不动。
     这是苹果系统级界面里最不起眼、但最能体现"稳"的一条。 */
  font-variant-numeric:tabular-nums;
  -webkit-font-smoothing:antialiased}
/* 中文标题加一点点字距, 视觉上更"站得住"; 数字和英文不受影响 */
h1,h2,h3,.d-title-row h2,.home-hero h1{letter-spacing:.2px}
/* 阻止左右滑动触发浏览器的"侧滑返回",避免看板横滑时误退出 */
html,body{overscroll-behavior-x:contain;overscroll-behavior-y:none}
/* 兜底:任何元素意外超宽也不允许整页左右拖动。
   先给所有浏览器 hidden,再对支持的浏览器升级为 clip(clip 不创建滚动容器,不影响顶栏 sticky) */
html,body{overflow-x:hidden}
html,body{overflow-x:clip}
/* manipulation: 禁掉"双击放大",但保留正常滚动与点击 */
html,body{touch-action:manipulation}
/* 地图区域：touch-action 必须是 pan-y，**不是 auto 也不是 none**。

   ## 为什么原来是 auto（那是错的）
   auto 等于告诉浏览器「单指平移、双指捏合，这些手势你也能用」——
   而 Leaflet 自己也要独占这些手势（.leaflet-touch-drag/-zoom 的
   touch-action:none / pinch-zoom）。两边都觉得自己该管 →
   浏览器和 Leaflet **抢同一个双指捏合** → 安卓上缩放不跟手、卡顿。

   ## 为什么是 pan-y（正好是用户要的那套语义）
     · 单指上下 → 交给浏览器滚页面        ✅ 用户明确说这个要保留
     · 单指左右 → 不给浏览器，事件到 JS    ✅ Leaflet 横向平移
     · 双指捏合 → 全部给 Leaflet           ✅ 浏览器不再抢 → 跟手
     · 双指平移 → 全部给 Leaflet           ✅
   垂直方向明确让给浏览器，就等于把「手势仲裁」这件事从
   「两边抢」变成「各管一段」。

   ## ⚠️⚠️ 必须用 `#mapBoard`（ID），**不能**只写 `.leaflet-container`
   这是本轮（1.19.13）实测踩到的坑，也是**原来那条 `!important` 真正在保的东西**：
     一开始把规则写成 `#mapDiv,.amap-container,.amap-maps,.leaflet-container{...}`
     并去掉 !important，以为「有 id 选择器优先级就够」。结果**根本没用** ——
     浏览器里量出来 `#mapBoard` 的 computed touch-action 还是 `none`。
     原因：那是一条**选择器列表**，`#mapBoard` 是靠列表里的 `.leaflet-container`
     分支命中的，于是它实际拿到的优先级是 **.leaflet-container = (0,1,0)**，
     而 Leaflet 1.9.4 有这么一条：
         .leaflet-container.leaflet-touch-drag.leaflet-touch-zoom { touch-action:none }
     优先级 **(0,3,0)** —— 三个类比一个类高，我们的 pan-y **被压掉了**。
     （`.leaflet-touch-drag`、`.leaflet-touch-zoom` 是 Leaflet 给带触摸的地图
      容器加的类，手机上必然同时存在。）
     修法：**把 `#mapBoard` 单独写成一条 ID 规则**（(1,0,0) > (0,3,0)），
     这样既赢过 Leaflet，又**不需要 !important** —— 不误伤其它元素。
   ⚠️ 验证方法（改完必须这么量，别只看源码）：
         getComputedStyle(document.getElementById('mapBoard')).touchAction
      必须是 "pan-y"；是 "none" 就说明又被 Leaflet 压掉了。
   ⚠️ 绝对不要写 `touch-action:none` —— 那会把「单指上下滚页面」也抢走，
      用户在 68vh 高的地图上就滚不动页面了，直接卡在地图里。明确否掉。
   ⚠️ 也不要用 !important 图省事：它会把 Leaflet 所有 touch-action 声明
      一起压掉（包括面板层需要的），而且以后谁想在地图上再调手势
      都调不动了。能用优先级解决就别动 !important。
   ⚠️ `html,body{touch-action:manipulation}` 和
      `body.locked{touch-action:none}` 都不要动 —— 前者管双击缩放，
      后者是弹窗时锁页面，都是对的。
   ⚠️ AMap 那几个容器选择器一起改没关系：那条线（工作地图）现在是休眠的。 */
#mapDiv,.amap-container,.amap-maps,.leaflet-container{touch-action:pan-y}
#mapBoard{touch-action:pan-y}
/* 抽屉/弹窗打开时:彻底锁住页面本身,只允许弹窗内部滚动 */
body.locked{overflow:hidden;touch-action:none;overscroll-behavior:none}
/* 遮罩与弹窗自身也不把滚动/滑动传导给页面 */
.modal-mask,.modal,.drawer{overscroll-behavior:contain}
/* 尊重系统"减少动态效果"设置:动画瞬时完成,对低性能设备也更稳 */
@media (prefers-reduced-motion: reduce){
  *,*::before,*::after{transition-duration:.01ms!important;animation-duration:.01ms!important}
}
button{font-family:inherit;cursor:pointer;border:none}
/* 复选框/单选框不该吃满宽度 —— 全局那条 width:100% 也会命中它们，
   结果协作人按钮里的姓名被压成一列竖排（实测 span 高 56px，按钮 64px）。
   这里统一恢复自然尺寸，宽度由内容决定。 */
input[type="checkbox"],input[type="radio"]{
  width:auto;min-width:0;padding:0;border-radius:3px;
  accent-color:var(--primary);flex:none}
input,select,textarea{font-family:inherit;font-size:14px;color:var(--text);
  border:1px solid var(--border);border-radius:8px;padding:8px 12px;outline:none;background:#fff;
  transition:border-color var(--dur-1) var(--ease),box-shadow var(--dur-1) var(--ease),
             background-color var(--dur-1) var(--ease);width:100%}
input:hover,select:hover,textarea:hover{border-color:#d3daea}
input:focus,select:focus,textarea:focus{border-color:var(--primary);box-shadow:0 0 0 3px rgba(79,110,247,.12)}
input::placeholder,textarea::placeholder{color:#b6bdcc}
/* 安卓/iOS 的浏览器在「字号 < 16px」的输入框获得焦点时会自动放大整页,
   放大后页面就左右溢出、要手动缩回去 —— 这是折叠屏"合起来时也有显示问题"
   的常见成因。窄屏把表单字号提到 16px 就永远不会触发自动放大。 */
@media(max-width:699px){input,select,textarea{font-size:16px}}
label{font-size:12px;color:var(--muted);display:block;margin-bottom:4px}
.hidden{display:none!important}

/* ---- 登录层 ---- */
/* 登录层: 原来是一整屏紫蓝渐变 + 白卡, 像 2015 年的落地页。
   苹果的做法是「安静的中性底 + 一张干净的卡片」——背景不抢戏,
   用户的视线直接被吸到唯一的输入区。所以这里改成极浅的品牌色底。 */
#authLayer{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;
  background:
    radial-gradient(1200px 600px at 50% -10%, var(--primary-soft), transparent 70%),
    var(--bg);
  z-index:100}
.auth-card{background:var(--card);border-radius:20px;padding:40px 36px;width:360px;
  box-shadow:var(--sh-3);border:1px solid var(--line)}
.auth-logo{width:52px;height:52px;border-radius:14px;background:var(--primary);
  display:flex;align-items:center;justify-content:center;font-size:26px;margin:0 auto 14px}
.auth-card h1{font-size:20px;text-align:center;margin-bottom:4px}
.auth-card .sub{color:var(--muted);text-align:center;font-size:13px;margin-bottom:22px}
.auth-card input{margin-bottom:12px}
.auth-btn{width:100%;padding:11px;border-radius:9px;background:var(--primary);
  color:#fff;font-size:15px;font-weight:600;transition:opacity .15s}
.auth-btn:hover{opacity:.9}
.auth-err{color:var(--danger);font-size:12px;text-align:center;min-height:16px;margin-top:8px}
.auth-tip{font-size:11.5px;color:var(--muted);text-align:center;margin-top:6px;line-height:1.6}

/* ---- 主框架 ---- */
#app{display:none;width:100%;min-height:100vh;min-height:100dvh;flex-direction:column}
.topbar{background:#fff;border-bottom:1px solid var(--border);padding:12px 22px;
  display:flex;align-items:center;gap:14px;flex-wrap:wrap;position:sticky;top:0;z-index:30}
.brand{display:flex;align-items:center;gap:9px;font-size:17px;font-weight:700}
.brand .logo{width:34px;height:34px;border-radius:9px;background:var(--primary);
  display:flex;align-items:center;justify-content:center;font-size:18px}
.brand small{font-weight:400;color:var(--muted);font-size:11px;display:block}
/* 顶栏搜索框。2026-10-04 挪进 .top-actions（用户要求「把搜索栏放在
   新建客户同一行」），样式跟着改 —— **flex:1 必须去掉**。

   原来它在 .topbar 里当「填充物」：flex:1 撑满 logo 和按钮之间的空当，
   max-width:380px 封顶。挪进 .top-actions 之后，flex:1 会被理解成
   「在操作区内部瓜分空间」，而 .top-actions 本身是 margin-left:auto
   撑到右边的固定内容 —— 结果就是搜索框把「⋯」和「新建」往右推，
   甚至可能把它们挤出视口。

   改成 flex:none + 固定宽度：它现在是**一个控件**，不是填充物。
   宽度给 300px —— 比原来的 380 略窄，因为它现在紧挨着按钮，
   太宽会显得像「半张顶栏都是输入框」。
   min-width:180px 留着：手机窄屏时靠它保证不会压成一条缝。 */
.top-search{flex:none;width:300px;min-width:180px}
/* 窄屏：搜索框**自己占一整行**，按钮排第二行靠右。
   2026-10-04 挪进 .top-actions 时漏了这个 —— 桌面和 768px 都对，
   390px 上「＋新建客户」被推出屏幕 74px（截图里只露出个「+」）。

   根因不是「搜索框太宽」，是 .top-actions **作为一个整体**比视口宽：
   搜索 300 + ⋯ 43 + 新建 97 + 三个 gap 16 = 456px > 390px。
   .topbar 的 flex-wrap:wrap 换行是按**子元素**换的，
   .top-actions 是一个子元素 —— 它整体换到第二行，
   但第二行还是 456px，一样溢出。

   flex:1 1 100% 的意思是「宽度撑满、且优先换行」：
   搜索框在 .top-actions 内部变成第一行（100%），
   ⋯ 和新建被挤到第二行。**这是在容器内部换行**，
   .topbar 那层根本不用管。
   min-width:0 是必须的 —— flex 项目默认 min-width:auto，
   不写的话 placeholder 文字的固有宽度会把它顶回去，
   100% 就不成立了。 */
/* 窄屏（2026-10-04 搜索框挪进 .top-actions 时补的）。

   第一版写的是 `.top-search{flex:1 1 100%}` —— 搜索框在**操作区内部**
   独占一行。溢出是修好了（新建按钮回到屏内），
   但顶栏从两行变成**三行**（logo / 搜索 / ⋯+新建），
   高度 112 → 194px。手机上顶栏吃掉近 200px 太奢侈，
   而「logo 一行 + 操作一行」本来是好布局 —— .topbar 上有 flex-wrap:wrap，
   .top-actions 整体换到第二行即可，本来就满足。

   所以这里改成：搜索框**不独占行**，只让它在操作区里能收缩 ——
   flex:1（可缩）+ min-width:0（能被压到比内容窄）+ 去掉固定 width。
   .top-actions 整体超宽时由 .topbar 的 wrap 换行，
   而 wrap 之后第二行仍然可能超宽 —— 所以还要给
   min-width:0，否则 placeholder 的固有宽度（"搜索客户名称 / 联系人 / 电话…"）
   会把 flex 容器顶出一行。 */
@media (max-width:699px){
  .top-search{flex:1 1 0;width:auto;min-width:0}
  /* 窄屏再压一点 gap：390px 下即使搜索框能收缩，
     .top-actions 三个 gap 仍是固定 6px（上面那条 .top-actions{gap:6px}
     的窄屏规则已经改过了），三段加起来仍比可用宽度多 7px ——
     「＋新建客户」右边被切掉一小截。
     减到 4px 正好装下，而且视觉上仍然分得开三段。
     ⚠️ 这条必须挂在 .top-actions 上，不能写 .top-actions{gap:4px}
     之外的简写 —— 上面 max-width:699px 那块里已经有 .top-actions{gap:6px}，
     同优先级下后写的赢，位置摆错就会被盖掉。 */
}
/* .top-stats / .stat 系列已随顶栏统计一起撤掉（2026-10-04）。
   「加粗 + 等宽数字把答案和标签拉开层级」这个做法本身没错，
   搬到 .home-stat 上继续用 —— 只是不在顶栏常驻了。 */
/* max-width:100% 是 2026-10-04 补的（搜索框挪进来之后）：
   .top-actions 是 .topbar 的子元素，.topbar 有 flex-wrap:wrap ——
   但 wrap 换行**只保证子元素自己放得下**，不保证子元素内部的
   flex 布局收缩到容器宽度。默认情况下 flex 容器的 min-width 是 auto，
   它会被内容（本例：搜索框 + ⋯ + 新建）顶出容器宽度。

   实测：390px 下即使搜索框设了 flex:1 1 0、min-width:0、gap 收到 4px，
   「＋新建客户」右边仍切掉 3px —— 差的这几像素来自 .top-actions
   自己被内容顶宽。max-width:100% + 窄屏的 min-width:0 一起给，
   内部那个 flex:1 1 0 才真的生效。

   之前靠反复调 gap 数字（6→4）去凑，那是**用错误的手段解正确的问题**：
   gap 决定视觉间距，不该被拿来承担宽度约束的职责。 */
.top-actions{display:flex;gap:8px;align-items:center;margin-left:auto;max-width:100%}
/* 「⋯」更多菜单：低频操作全部收在这里，顶栏只留 提醒 / ⋯ / 新建 三个。
   2026-10-04 改成**两端同一套形态** —— 原来 .more-menu 默认 display:flex
   （宽屏下 4 个按钮直接摊在顶栏），只有 <700px 才变下拉。同样的内容两种排法，
   视觉不统一、宽屏还平白多占 300px。现在宽窄屏都是点 ⋯ 弹下拉。 */
.more-wrap{position:relative;display:flex;align-items:center}
/* 底色用 --card（项目里真实存在的变量）。原来这里写的是 --surface，
   而 --surface **根本没有定义** —— var() 拿到空值，background 直接失效，
   下拉变成全透明，手机上能透出后面筛选栏的字。已修。
   深色主题那条 [data-theme="dark"] .more-menu 用的也是 --card，两边一致。 */
.more-menu{position:absolute;top:calc(100% + 8px);right:0;z-index:80;
  display:none;flex-direction:column;gap:1px;min-width:172px;padding:6px;
  background:var(--card);border:1px solid var(--border);border-radius:12px;
  box-shadow:var(--sh-3)}
.more-menu.show{display:flex}
/* 窄屏：「⋯」下拉改成**靠左对齐**（2026-10-04 搜索框挪进 .top-actions 时修）。

   原来 right:0 就够用，因为「⋯」一直在顶栏**最右端** ——
   菜单 172px 往左展开，正好落在屏幕内。

   搜索框挪进 .top-actions 之后，「⋯」在手机上跑到了这一行的**最左边**
   （wrap 的 left=12）。right:0 仍让菜单右边缘贴 wrap 右边缘（55），
   于是 left = 55 - 172 = **-117px**，整块菜单跑到屏幕外 117px。
   实测出来的，不是看代码猜的。

   靠左对齐（left:0;right:auto）之后菜单从 x=12 往右铺到 184，
   完全在屏内，而且「⋯」在左边、菜单也往右展开，读起来顺。

   为什么不用 min-width 缩小菜单来凑：那要让用户在手机上
   读五个带文字的菜单项，字一小就难读了。宁可挪对齐方向。 */
@media (max-width:699px){
  .more-menu{left:0;right:auto}
}
/* 菜单项：左对齐铺满，右侧留白给「跟随系统」这类状态字 */
.more-menu .btn{width:100%;text-align:left;border:none;background:transparent;
  border-radius:8px;padding:9px 11px;box-shadow:none;justify-content:flex-start}
.more-menu .btn:hover{background:var(--bg);border-color:transparent;color:var(--text)}
.more-menu .btn-danger{color:var(--danger)}
.more-menu .btn-danger:hover{background:var(--danger-soft);color:var(--danger)}
.more-menu .gray-num{margin-left:auto;font-size:12px;color:var(--muted)}
.more-menu .theme-ico{display:inline-flex;flex:none}
.more-menu .theme-ico svg{width:16px;height:16px;stroke:currentColor}
/* 分组细线：把「外观 / 业务功能 / 退出」三段分开，替代原来的空白间距 */
.more-sep{height:1px;background:var(--border);margin:5px 8px}
.btn-more{display:inline-flex;padding:6px 13px;font-size:18px;line-height:1.1}
.btn{padding:8px 14px;border-radius:9px;font-size:13px;font-weight:500;background:#fff;
  border:1px solid var(--border);color:var(--text);white-space:nowrap;
  box-shadow:var(--sh-1);display:inline-flex;align-items:center;
  justify-content:center;gap:5px;line-height:1.4;min-height:34px}
.btn:hover{border-color:#c5d0f7;color:var(--primary);box-shadow:0 2px 6px rgba(79,110,247,.1)}
.btn-primary{background:linear-gradient(135deg,var(--primary),var(--primary-2));color:#fff;border:none;
  box-shadow:0 2px 8px rgba(79,110,247,.26)}
.btn-primary:hover{color:#fff;box-shadow:0 4px 12px rgba(79,110,247,.34);filter:brightness(1.04)}
.btn-danger{color:var(--danger);border-color:#ffd7de}
.btn-danger:hover{background:var(--danger-soft);border-color:var(--danger);color:var(--danger)}
/* 小按钮仍保证 30px 可点高度 —— 手机上低于 30 就很难点准 */
.btn-sm{padding:5px 10px;font-size:12px;border-radius:7px;min-height:30px}
.btn-xs{padding:3px 9px;font-size:11.5px;border-radius:6px;min-height:26px}
.btn-icon{padding:6px 10px;font-size:15px;line-height:1.2}
/* 铃铛（1.9.0 从 emoji 改成 SVG）：SVG 跟着 currentColor 走，
   .off 用一道斜杠表示"提醒没开" —— 换 emoji 的方案在缺字体时是豆腐块。
   position:relative 给 ::after 那道斜杠当定位基准。 */
.btn-bell{position:relative;padding:6px 9px}
.btn-bell svg{width:17px;height:17px;fill:none;stroke:currentColor;
  stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round;display:block}
.btn-bell.off{color:var(--muted)}
.btn-bell.off svg{opacity:.75}
.btn-bell.off::after{content:"";position:absolute;left:50%;top:50%;
  width:20px;height:1.6px;margin:-.8px 0 0 -10px;background:currentColor;
  border-radius:1px;transform:rotate(-38deg);pointer-events:none}
/* 幽灵按钮: 无边框无底色, 用于"取消""跳过"这类弱操作 */
.btn-ghost{background:transparent;border-color:transparent;box-shadow:none;color:var(--text-2)}
.btn-ghost:hover{background:var(--surface-2);border-color:transparent;color:var(--text)}
/* 禁用态: 明确"现在不能点", 而不是模棱两可的浅色 */
.btn:disabled,.btn[disabled]{opacity:.45;cursor:not-allowed;box-shadow:none;transform:none}
.btn:disabled:hover{filter:none;border-color:var(--border);color:var(--text)}
.avatar{width:30px;height:30px;border-radius:50%;color:#fff;display:inline-flex;
  align-items:center;justify-content:center;font-size:12px;font-weight:600;flex:none}

/* ---- Tabs & 筛选 ---- */
.main{flex:1;padding:18px 22px;max-width:1400px;width:100%;margin:0 auto}
/* PC 顶部:带图标的圆角胶囊导航 */
.tabs{display:flex;gap:8px;margin-bottom:18px;flex-wrap:wrap}
.tab{display:inline-flex;align-items:center;gap:7px;padding:9px 17px;border-radius:22px;
  background:#fff;border:1px solid var(--border);font-size:13.5px;font-weight:500;color:var(--text-2);
  box-shadow:var(--sh-1)}
.tab svg{width:16px;height:16px;stroke:currentColor;stroke-width:1.7;fill:none;flex:none;
  stroke-linecap:round;stroke-linejoin:round;transition:stroke-width var(--dur-1) var(--ease)}
.tab:hover{border-color:#c5d0f7;color:var(--primary);transform:translateY(-1px);
  box-shadow:0 3px 8px rgba(79,110,247,.12)}
.tab:active{transform:translateY(0) scale(.97)}
.tab.active{background:var(--primary-soft);color:var(--primary);font-weight:600;
  border-color:transparent;box-shadow:0 4px 12px rgba(79,110,247,.3)}
.tab.active:hover{transform:none}
.tab.active svg{stroke-width:2.1}
.tab.active:active{transform:scale(.97)}
/* 导航「更多」下拉（1.9.0）：低频的四个视图藏在这里。
   .nav-more 顶到 tabs 行末，下拉往左开(right:0) —— 靠右对齐才不会从
   屏幕右侧溢出。里面那四项沿用 .tab 的胶囊样式，但去掉 hover 上浮：
   已经在下拉里了，再浮一下会显得在二层。

   min-width 给到 196px 是量出来的：顶栏那个 .more-menu 是 172px（文案更短），
   导航这四项更挤 ——「成交客户」一行要放下 16px 图标 + 4 个字 + 两个角标，
   172px 装不下，实测「商机看板」被折成两行、角标叠到文字上。
   white-space:nowrap 兜住：宁可让下拉再宽一点，也不许折行。 */
.nav-more{position:relative;display:inline-flex}
.nav-more #btnNavMore{padding-right:13px}
#navMoreMenu{top:calc(100% + 8px);min-width:196px;padding:7px}
#navMoreMenu .tab{width:100%;justify-content:flex-start;box-shadow:none;
  background:transparent;white-space:nowrap;padding:9px 11px;gap:8px}
#navMoreMenu .tab:hover{transform:none;background:var(--surface-2);border-color:transparent}
#navMoreMenu .tab.active{background:var(--primary-soft);color:var(--primary);
  font-weight:600;box-shadow:none}
/* 角标在横排时贴到行尾，别让它把文字顶开 */
#navMoreMenu .tab .tab-num{margin-left:auto;flex:none}
#navMoreMenu .tab .tab-num + .tab-num{margin-left:0}
/* 【2026-10-08 丁件已删除】手机上隐藏「客户类型」「搜索范围」的规则：
     @media(max-width:699px){ #fType,#fScope{display:none} }
   为什么删：那两个控件已经**搬进筛选弹窗**，不在外面这一行了 ——
   弹窗里的每一项都该能选（用户主动点开的，藏一半没有道理）。留着这条
   规则会造成一个很隐蔽的故障：手机上点开筛选，类型和范围两项**看不见**，
   用户以为没有这两项筛选。而这个"看不见"只在手机宽度下出现，
   桌面自测完全正常。
   ⚠️ 这一条**必须删**，不能只是"反正元素已经搬走了、规则留着也没事" ——
   规则里的选择器是按 id 匹配的，元素搬进弹窗后选择器照样命中。 */
.filters{display:flex;gap:10px;margin-bottom:14px;flex-wrap:wrap;align-items:center}
.filters select{width:auto;min-width:110px}
.filters .count{margin-left:auto;color:var(--muted);font-size:13px}

/* ---- 地图看板的筛选行：压缩（2026-10-04 用户反馈）----
   手机上「全部阶段 / 全部负责人 / 全部工作地图」三个下拉，
   每个 min-width 110px，加起来一行放不下，第三个干脆独占一行 ——
   竖着占掉小半屏，地图被推得很矮，看都看不全。
   用户原话：「含公海这个按钮保留，其他三个筛选项必须要占这么大的位置吗」。

   改法：
   - 三个下拉**按各自文字长度分配宽度**（flex-grow 加权），不是一刀切平分 ——
     「全部阶段」4 个字、「全部工作地图」6 个字，一视同仁地各占 1/3
     只会把长的那个挤成「全部工作..」，而短的这边空着一大片
   - min-width 从 110px 降到 0，靠 padding 给足点击面积（别为了省像素
     把可点区域也缩了，44px 是手指下限）
   - 「含公海」那个 checkbox **不动**（用户明确说保留）——
     它是勾选框不是下拉，本来就只占一小块
   - 字号跟着降到 12.5px，长选项（如「外勤拓客地图（329）」）才装得下*/
#mapView .filters{gap:6px}
/* flex:1 1 0 会让三个下拉在平板上各撑到 219px，比手机上还宽 ——
   筛选器不是内容区，摊太开反而把"地图"这个主角挤得没了存在感。
   max-width 130px 收住：够装「全部工作地图（329）」，也不至于拉成一行横幅。

   ⚠️ 这里还有个**更深的坑**：Chromium 的原生下拉箭头实际占掉约 70px
   （不是 padding 写的 22px —— 那个 22px 是叠在箭头之上的）。
   用浏览器的 `max-content` 探针量到的真实需求是 110 / 126 / 142px，
   三个加起来 378px，**390px 的手机上物理上放不下**。
   所以光调 flex 系数是没用的，必须先把箭头占位去掉。

   办法：`appearance:none` 关掉原生控件，让 select 用我们自己的
   背景箭头（一个 9px 的 CSS 三角形，跟 padding 一起算）。
   这样 padding-right 24px 就是箭头的全部成本，
   真实需求降到 78 / 90 / 102px，三个加「含公海」一起 294px，390px 装得下。

   加权系数仍按文字宽比例给（÷48 归一）：
     全部阶段   48/48 = 1
     全部负责人 60/48 = 1.25
     全部工作地图 72/48 = 1.5

   踩过的三个坑，记在这儿免得重踩：
   1. **canvas measureText 量 select 的宽度是错的。** 量出 48/60/72，
      实际渲染需要 110/126/142 —— 差值全在原生箭头上。
      以后要量 select 宽度，用克隆节点 + `width:max-content` 探针，
      让浏览器自己算，别自己加 padding 猜。
   2. min-width 做「下限」在这套布局里**完全没生效** ——
      `flex:1 1 0` 的 flex-basis:0 让 flex 算法在 min-width 之前
      就把空间分完了。加权才是对的解法。
   3. `text-overflow:ellipsis` 加上去反而更糟 ——
      Chromium 的 select 省略号逻辑跟箭头占位打架，
      三个下拉全被截成「全部..」。原生裁剪本就按箭头右侧边界算，
      宽度配够了文字就完整，别插手它的截断算法。 */
#mapView .filters select{
  /* 关掉原生控件 —— 这是这一段能成立的前提。
     关掉之后 padding-right 里那 24px 就是箭头的全部成本，
     而不再额外背 70px 的原生箭头。 */
  -webkit-appearance:none;appearance:none;
  min-width:0;font-size:12.5px;
  padding:5px 24px 5px 8px;max-width:130px;
  background-image:linear-gradient(45deg,transparent 50%,currentColor 50%),
                   linear-gradient(135deg,currentColor 50%,transparent 50%);
  background-position:calc(100% - 14px) calc(50% + 1px),
                     calc(100% - 9px) calc(50% + 1px);
  background-size:5px 5px,5px 5px;
  background-repeat:no-repeat;
  /* 焦点框在 appearance:none 下会消失，键盘用户看不到焦点位置 ——
     自己补一个，比留着原生控件但占 70px 更划算。 */
  outline:none}
#mapView .filters select:focus-visible{
  border-color:var(--primary);
  box-shadow:0 0 0 3px var(--primary-soft,#eef1fb)}
/* flex-basis:0（整行瓜分）+ flex-grow 加权 → 按文字长度分配 */
/* ⚠️ min-width 兜底（2026-10-07 B2）。这三个下拉只有 flex-basis:0 + grow，
   宽度全靠分配；窄屏上「含公海」勾选框和 #mWiaMapBtn 一起挤进来之后
   每个只剩 ~40px，「全部阶段」「全部负责人」被截成「全部阶…」「全部负…」——
   用户看不清当前筛的是什么，改成别的之后也不知道改了什么。
   ⚠️ flex-basis 刻意**不动**（仍是 0，也不再给它们写 flex:none）：
   flex-basis:0 + min-width 才是一条同时满足「均分」和「不被压到不可读」
   的组合 —— 单给 min-width 而保留 flex:1 就还是会被 min-width 顶掉，
   右边留一块空白。
   ⚠️ 不动 .mb-mapbtn 的 flex:none（有意保留）：按钮要贴着地图名长度，
   硬拉成等宽会在右边留一块空白。窄屏让它自然换行（.filters 有 flex-wrap），
   比压扁下拉好。
   78px 装得下「全部阶段」4 字 + 箭头；90px 装得下「全部负责人」5 字 + 箭头。 */
#mapView #mStage{flex:1 1 0;min-width:78px}
#mapView #mState{flex:1 1 0;min-width:78px}
#mapView #mOwner{flex:1.25 1 0;min-width:90px}
/* 工作地图按钮（2026-10-07 取代原先那个工作地图下拉）。
   宽度不参与 flex 分配 —— 按钮要按**地图名**的宽度长（「外勤拓客地图」7 个字，
   「全部工作地图」6 个字），flex:1.5 会把它拉成和下拉一样宽、名字却比下拉短，
   右边留一块空白。现在 flex:none，让它贴着内容。 */
.mb-mapwrap{position:relative;display:inline-flex;flex:none}
.mb-mapbtn{display:inline-flex;align-items:center;gap:4px;height:28px;padding:0 9px;
  border:1px solid var(--border);border-radius:7px;background:var(--card);
  font-size:12.5px;color:var(--text-2);cursor:pointer;max-width:44vw;white-space:nowrap}
.mb-mapbtn:hover{border-color:var(--primary);color:var(--text)}
.mb-mapbtn #mWiaMapName{overflow:hidden;text-overflow:ellipsis;max-width:24vw}
.mb-mapbtn-c{color:var(--text-3);font-size:10px;flex:none}
/* 选中的工作地图一家客户都没有时的横幅。塞在筛选栏和地图之间，
   不压地图 —— 图钉本来就该在那儿，让人看清"是真的没有"而不是"被挡住了"。 */
.mb-mapnote{flex:none;margin:0 10px 8px;padding:7px 11px;font-size:12.5px;
  line-height:1.6;color:var(--text-2);background:var(--surface-2);
  border:1px solid var(--border);border-left:3px solid var(--primary);
  border-radius:7px}
.mb-mapnote b{color:var(--text)}
/* 三选一弹窗。z-index 要压住地图上的浮层（图例/我的位置/统计），
   又不能压住抽屉 —— 地图页这时抽屉是关着的，不冲突。 */
/* ⚠️ 宽度别只给 190px —— 地图名是用户自己起的，长的能到十几个字
   （实测「外勤拓客地图」6 个字就被截成「外勤拓客…」）。
   min-width 给足、max-width 封顶（超长名走省略号，别把浮层撑到屏幕外），
   w 也跟着一起长 —— 容器是 absolute 定位，不设宽度的话内容多宽它就多宽。
   ⚠️ 对齐用 left:0（从按钮**左**边往右展开）而不是 right:0：
   按钮右边紧跟着的就是浮层，左边是筛选栏剩下的空白。
   往左伸的话 260px 会正好盖住「全部阶段/全部状态/全部负责人」三个下拉 ——
   用户想关掉浮层得点空白处，点下拉上反而是关不掉。 */
.mb-mpop{position:absolute;top:calc(100% + 6px);left:0;z-index:70;
  min-width:260px;max-width:min(340px,72vw);width:max-content;
  background:var(--card);border:1px solid var(--border);border-radius:10px;
  box-shadow:0 8px 26px rgba(15,23,42,.18);padding:5px;overflow:hidden}
.mb-mpop-t{font-size:11px;color:var(--text-3);padding:4px 8px 6px}
.mb-mpop-i{display:flex;align-items:center;gap:7px;padding:7px 8px;border-radius:7px;
  cursor:pointer;font-size:12.5px;color:var(--text-2)}
.mb-mpop-i:hover{background:var(--surface-2)}
.mb-mpop-i.on{color:var(--text);font-weight:600}
.mb-mpop-i input{margin:0;flex:none}
.mb-mpop-i span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mb-mpop-n{margin-left:auto;color:var(--text-3);font-size:11px;font-weight:400;
  flex:none;padding-left:8px}
/* 「筛选」弹窗里的行（P 件，1.19.9）：标签 + 控件两列。
   ⚠️ 不给 select 写 flex 的话它会按**内容**撑开（「全部负责人」比「全部阶段」
   宽），四行宽度参差不齐，看着像两个控件。这里让控件吃掉剩下的宽度。 */
.mb-frow{display:flex;align-items:center;gap:8px;padding:6px 8px;
  border-bottom:1px solid var(--border)}
.mb-frow:last-child{border-bottom:none}
.mb-flb{flex:none;width:52px;font-size:11.5px;color:var(--text-3)}
.mb-frow select{flex:1 1 0;min-width:0;height:32px;padding:0 8px;font-size:12.5px;
  border:1px solid var(--border);border-radius:7px;background:var(--card);
  color:var(--text);outline:none}
/* 「已选 N 项」那个小圆标，贴在筛选按钮上。
   为什么要有：筛了之后按钮还是写着「筛选」，用户看不出自己筛没筛、
   筛了几项 —— 尤其弹窗收起之后，唯一的痕迹就是这个数字。 */
.mb-fcnt{flex:none;min-width:16px;height:16px;padding:0 4px;border-radius:8px;
  background:var(--primary);color:#fff;font-size:10.5px;font-weight:600;
  display:inline-flex;align-items:center;justify-content:center}
/* 沿途检索面板里那个同名按钮的包装。⚠️ pointer-events 是**必须**的：
   .rtm-ctrls 为了让地图"保持完整"整块设了 pointer-events:none，
   只有 .rtm-ctrl 自己开了 auto。弹窗不是 .rtm-ctrl，不显式打开就**点不动**
   （点哪儿都没反应，用户只会以为这个按钮坏了）。 */
.rtm-mapwrap{position:relative;display:inline-flex;pointer-events:none}
.rtm-mapwrap>.rtm-ctrl,.rtm-mapwrap>.mb-mpop{pointer-events:auto}
/* 沿途检索那个浮层挂在地图**右下角**浮层条上，右边就是屏幕边 ——
   宽屏下 260px 没问题，窄屏下 max-content 会顶出屏幕。
   所以窄屏收窄、名字该省略还是省略（总比弹窗被切掉看不见强）。 */
/* 沿途检索那个浮层挂在地图**右下角**浮层条上，右边就是屏幕边 ——
   方向跟地图页**相反**：那边往右展开（left:0），这边必须往左（right:0），
   否则宽屏下直接顶出屏幕。
   ⚠️ 而且得**往上**展开（bottom:100%）：那排按钮贴着地图底边，
   往下展开的话浮层会被弹窗底部裁掉，最后一项只露半行 ——
   而「续费到期」恰好常是 0 家客户、排在最后一行，正好被切。
   窄屏再收一档宽度：右下角空间本来就紧。 */
.rtm-mapwrap>.mb-mpop{left:auto;right:0;top:auto;bottom:calc(100% + 6px);
  min-width:0;width:min(280px,64vw);max-width:64vw}
@media (max-width:520px){
  .rtm-mapwrap>.mb-mpop{width:min(240px,66vw);max-width:66vw}
  .rtm-mapwrap>.mb-mpop .mb-mpop-i span{max-width:110px}
}
#mapView .filters .lg-item{flex:none;font-size:12.5px;white-space:nowrap}
#mapView .filters .lg-item input{margin-right:3px}
/* 窄屏（手机）下三个下拉挤一行更彻底：连字都缩一档。
   ≥400px 时恢复默认字号 —— 再小就认不出「全部工作地图」写的是什么了。
   加权系数不用动：字号变小 → 文字也变窄 → 需要的空间同比缩小，
   比例关系不变（系数是按文字宽比例给的，不是绝对像素）。 */
@media (max-width:400px){
  #mapView .filters select{font-size:12px;padding:5px 18px 5px 6px}
  #mapView .filters .lg-item{font-size:12px}
}

/* ---- 今日工作台 ----
   全站唯一保留渐变的地方: 它是首屏的视觉焦点, 渐变在这里是有信息量的
   (告诉用户"这是起点")。其余按钮/标签一律纯色, 焦点才不会被冲淡。 */
.home-hero{background:linear-gradient(135deg,var(--primary) 0%,var(--primary-2) 100%);
  border-radius:16px;
  padding:18px 20px;color:#fff;display:flex;align-items:center;gap:14px;margin-bottom:14px;
  box-shadow:0 6px 18px rgba(79,110,247,.20)}
.home-hero .hi{font-size:17px;font-weight:700;line-height:1.3}
.home-hero .sub{font-size:12px;opacity:.86;margin-top:3px}
.home-stats{display:flex;gap:20px;margin-left:auto}
.home-stat{text-align:center;min-width:52px}
.home-stat b{display:block;font-size:21px;line-height:1.2;font-weight:700}
.home-stat span{font-size:11px;opacity:.86}
/* AI 建议卡片。优先级用左侧色条区分，扫一眼就知道先做哪个 */
/* 可点的店名：给一点可点的视觉提示，但不用蓝色链接色（列表里蓝色
   已经表示"电话"了，两种蓝色会混淆）。用下划虚线 + 手型即可。 */
.ai-card .nm.link{cursor:pointer;
  text-decoration:underline dotted rgba(79,110,247,.45);
  text-underline-offset:3px}
.ai-card .nm.link:active{opacity:.65}
.ai-card .ai-claim{margin-top:8px}
/* 建议弹窗的筛选行 */
.ai-opts{display:flex;flex-wrap:wrap;gap:8px 12px;align-items:center;
  padding:9px 10px;margin-bottom:10px;border-radius:9px;
  background:var(--bg-soft,#f5f7fb);border:1px solid var(--border)}
.ai-opt{display:inline-flex;align-items:center;gap:6px;font-size:12px;
  color:var(--muted);margin:0}
.ai-opt select{width:auto;min-width:132px;padding:5px 8px;font-size:12px}
.ai-opt.chk input{margin:0;accent-color:var(--primary)}
.ai-opt.chk span{color:var(--text)}
/* 卡片上的小标签：品类 / 连锁 / 建档 */
.ai-tags{display:flex;flex-wrap:wrap;gap:4px;margin-top:5px}
.ai-tags .t{display:inline-block;padding:1px 6px;border-radius:5px;
  font-size:10.5px;background:#eef1fb;color:#5a6bd8}
.ai-tags .t.warn{background:#fdeaea;color:#c0392b;font-weight:600}
.ai-tags .t.dim{background:transparent;color:var(--muted);padding-left:0}
/* 表单里的行内复选框（连锁品牌） */
.inline-chk{display:inline-flex;align-items:center;gap:6px;font-size:13px;
  color:var(--text);margin:0;cursor:pointer;
  border:1px solid var(--border);border-radius:8px;padding:8px 12px;
  background:#fff;width:100%;white-space:nowrap}
.inline-chk input{margin:0;accent-color:var(--primary)}
.ai-card{display:flex;gap:10px;padding:10px 0;border-bottom:1px solid var(--border)}
.ai-card:last-child{border-bottom:0}
/* 这根 3px 竖条不是文字, 不需要过对比度 —— 用 --muted-deco 保住原来的浅灰观感。
   如果跟着 --muted 一起变深, 整列卡片的左边缘会显脏。 */
.ai-card .bar{flex:none;width:3px;border-radius:2px;background:var(--muted-deco)}
.ai-card.p-高 .bar{background:var(--danger)}
.ai-card.p-中 .bar{background:#f0a030}
.ai-card.p-低 .bar{background:#9aa6bd}
.ai-card .main{flex:1;min-width:0}
/* 店名是可点的（点它进客户详情）。字号 14px、行高 1.2 → 撑出来只有 20px 高，
   低于 WCAG 2.5.8 的 24px 最小可点尺寸。跟 .rt-pick 一样用 ::after 把热区
   撑到 28px，**视觉尺寸一点不变** —— 只是上下各多 4px 的透明可点区。
   父级 .ai-card 有 min-width:0，热区横向超出会被裁，所以只撑纵向就够了。 */
.ai-card .nm{font-size:14px;font-weight:600;cursor:pointer;position:relative}
.ai-card .nm.link::after{content:"";position:absolute;left:0;top:50%;
  width:100%;height:28px;transform:translateY(-50%)}
.ai-card .nm:hover{color:var(--primary)}
.ai-card .rs{font-size:12.5px;color:var(--muted);margin-top:3px;line-height:1.45}
.ai-card .sg{font-size:12.5px;color:var(--primary);margin-top:4px}
.ai-card .lv{flex:none;font-size:11px;padding:1px 6px;border-radius:5px;
  background:#eef1fb;color:#5a6bd8;height:18px;line-height:16px}
.ai-card.p-高 .lv{background:#fdeaea;color:#d33}
.ai-card.p-中 .lv{background:#fdf3e3;color:#b06a00}
.ai-sum{font-size:12.5px;color:var(--muted);padding:6px 0 10px;line-height:1.5}
.ai-load{font-size:12.5px;color:var(--muted);padding:8px 0}
.home-sec{background:var(--card);border-radius:var(--r-lg);box-shadow:var(--sh-1);padding:16px;margin-bottom:12px}
/* 高德新点位提醒横幅。用琥珀色而不是蓝色 —— 蓝色是主色，用在导航/主按钮上；
   这里要的是「有事等你处理」的提示感，跟主色区分开才醒目。 */
#wiaBanner{display:flex;align-items:center;gap:11px;cursor:pointer;
  background:linear-gradient(135deg,#fff7e8 0%,#ffefd6 100%);
  border:1px solid #ffd591;border-radius:var(--r-lg);
  padding:12px 15px;margin-bottom:12px}
#wiaBanner.hidden{display:none}
#wiaBanner .wb-ic{display:inline-flex;flex:none;width:32px;height:32px;border-radius:9px;
  background:#ff9f2e;align-items:center;justify-content:center}
#wiaBanner .wb-ic svg{width:17px;height:17px;stroke:#fff;stroke-width:2;fill:none;
  stroke-linecap:round;stroke-linejoin:round}
#wiaBanner .wb-tx{flex:1;min-width:0;font-size:13.5px;color:#8a5200;font-weight:600}
#wiaBanner .wb-tx b{font-size:16px;font-weight:700;color:#c96a00;margin:0 2px}
#wiaBanner .wb-sub{font-size:11.5px;color:#a9772f;font-weight:400;margin-top:2px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
#wiaBanner .wb-go{flex:none;font-size:12px;color:#c96a00;font-weight:600}
[data-theme="dark"] #wiaBanner{background:linear-gradient(135deg,#3a2d17 0%,#3d2f16 100%);
  border-color:#5c451f}
[data-theme="dark"] #wiaBanner .wb-tx{color:#f0c078}
[data-theme="dark"] #wiaBanner .wb-tx b{color:#ffb454}
[data-theme="dark"] #wiaBanner .wb-sub{color:#b99560}
[data-theme="dark"] #wiaBanner .wb-go{color:#ffb454}
/* 菜单项上的角标（高德同步用） */
.sheet-item .badge{position:absolute;right:14px;top:50%;transform:translateY(-50%);
  min-width:19px;height:19px;padding:0 5px;border-radius:10px;background:var(--danger);
  color:#fff;font-size:11px;font-weight:700;display:inline-flex;align-items:center;
  justify-content:center}
.home-sec h3{font-size:13.5px;font-weight:600;margin-bottom:4px;display:flex;align-items:center;gap:7px;
  /* 1.9.7：允许换行 —— 「我的路线规划」标题行右边现在挂了 4 个按钮
     （生成路线/调整顺序/恢复默认/清空），390px 的手机宽度放不下 4 个
     sec-add（每个约 74px + 8px 外边距 = 328px，再加标题和图标就爆了）。
     不加 wrap 的话 flex 会把它们硬挤在一行，标题被压到看不见。 */
  flex-wrap:wrap}
.home-sec h3 .h3-ic{display:inline-flex;flex:none}
.home-sec h3 .h3-ic svg{width:15px;height:15px;stroke:var(--primary);stroke-width:1.9;fill:none;
  stroke-linecap:round;stroke-linejoin:round}
.home-sec h3 .n{margin-left:auto;font-size:12px;font-weight:500;color:var(--muted)}
/* 计数里那半截红色的「K 逾期」：loadHomeRemind() 用 <b class="od"> 写进去的。
   只上色,不加粗也不改字号 —— 它和后面的「N 个」同处一行，跳字号会割裂。 */
.home-sec h3 .n b.od{color:var(--danger);font-weight:600}
/* 区块标题右侧的小操作按钮(目前给「我的待办 → ＋ 新增」用) */
/* min-height 40px:原来是 26px,手机上点不准,而这个类挂在「＋ 新增」和
   「🎤 语音」上 —— 语音记待办是外勤最高频的操作之一 */
.sec-add{margin-left:8px;flex:none;font:inherit;font-size:12px;font-weight:600;
  padding:8px 12px;min-height:40px;border-radius:12px;cursor:pointer;
  line-height:1.5;display:inline-flex;align-items:center;
  border:1px solid var(--primary);background:var(--primary-soft);color:var(--primary);
  -webkit-tap-highlight-color:transparent}
.sec-add:active{transform:scale(.95)}
/* ---------- 语音记待办 ---------- */
.sec-add.mic{background:#fff;border-color:var(--primary);padding:3px 9px}
.btn.rec{background:var(--danger)!important;border-color:var(--danger)!important;color:#fff!important}
/* 中间那颗会呼吸的大麦克风 */
.va-mic{width:74px;height:74px;border-radius:50%;margin:2px auto 14px;display:flex;
  align-items:center;justify-content:center;font-size:30px;cursor:pointer;color:#fff;
  background:var(--primary);border:none;box-shadow:0 6px 18px rgba(79,110,247,.28);
  -webkit-tap-highlight-color:transparent}
.va-mic:active{transform:scale(.94)}
.va-mic.on{animation:vaPulse 1.3s ease-out infinite;background:var(--danger);
  box-shadow:0 6px 18px rgba(232,74,95,.3)}
@keyframes vaPulse{0%{box-shadow:0 0 0 0 rgba(232,74,95,.5)}
  70%{box-shadow:0 0 0 20px rgba(232,74,95,0)}100%{box-shadow:0 0 0 0 rgba(232,74,95,0)}}
.va-state{text-align:center;font-size:12.5px;color:var(--muted);min-height:18px}
/* 转写出来的字:final 用正常色,临时结果用浅色,一眼能分清哪段还没定稿 */
.va-txt{min-height:70px;max-height:150px;overflow-y:auto;background:var(--bg-soft,#f6f8fc);
  border:1px solid var(--border);border-radius:10px;padding:11px 12px;margin:12px 0 10px;
  font-size:15px;line-height:1.65;word-break:break-word}
.va-txt .it{color:#b0b8c8}
.va-txt .ph{color:#c2c9d6}
.va-tags{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:6px;min-height:24px}
.va-tag{font-size:11.5px;padding:3px 9px;border-radius:11px;font-weight:600;
  background:var(--primary-soft);color:var(--primary)}
.va-tag.u{background:#fdecef;color:var(--danger)}
.va-tag.d{background:#eaf6ee;color:#1e8e4e}
.va-tag.p{background:#fff4e5;color:#c67a12}
/* 识别出来的话按标点切段:每段一个气泡,右上角的 ✕ 只删这一段 ——
   说错了不用整句重来,这是 v1.5.0 最要紧的一条 */
.va-txt{line-height:2.1}
.va-seg{position:relative;display:inline-block;margin:0 3px 4px 0;padding:3px 21px 3px 8px;
  border-radius:9px;background:var(--primary-soft);color:var(--text);font-size:14px}
.va-seg .x{position:absolute;right:3px;top:1px;border:none;background:transparent;
  color:var(--muted);font-size:11px;padding:0 2px;cursor:pointer;line-height:1.4}
.va-seg .x:active{color:var(--danger)}
.va-seg.it{background:transparent;color:#b0b8c8;padding-right:8px}
.va-tools{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:9px}
.va-tools button{font-size:11.5px;padding:4px 10px;border-radius:12px;
  border:1px solid var(--border);background:var(--card);color:var(--text-2);
  cursor:pointer;font-family:inherit;-webkit-tap-highlight-color:transparent}
.va-tools button:active{transform:scale(.95);border-color:var(--primary);color:var(--primary)}
.va-who,.va-fix2{display:flex;align-items:center;gap:6px;flex-wrap:wrap;
  margin-bottom:7px;font-size:12.5px}
.va-lb{color:var(--muted);flex:none}
.va-who select,.va-fix2 select,.va-fix2 input{font-size:12px;padding:3px 6px;
  border:1px solid var(--border);border-radius:8px;background:var(--card);color:var(--text);
  font-family:inherit;max-width:150px}
.va-wchip{display:inline-flex;align-items:center;gap:4px;padding:3px 8px;border-radius:12px;
  background:var(--primary-soft);color:var(--primary);font-size:12px;font-weight:600}
.va-wchip .x{border:none;background:none;color:var(--muted);cursor:pointer;font-size:11px;padding:0}
/* 「听成了 X,已对到 Y」 —— 自动纠正必须让用户看见,否则比认错还可怕 */
.va-fix{font-size:11.5px;color:#b26a00;background:#fff7e8;border-radius:8px;
  padding:5px 8px;margin-bottom:6px;display:flex;align-items:center;gap:6px;flex-wrap:wrap}
/* AI 纠错提示：和本地规则的提示同一区域，用左侧色条区分 */
.va-ai{font-size:12px;color:var(--muted);padding:6px 0}
.va-fix.ai{border-left-color:#7c5cff}

.va-fix .btn{padding:2px 8px;font-size:11px}
.va-hint{font-size:11.5px;color:var(--muted);line-height:1.6;margin-top:8px}
/* 1.9.6 起了 position:relative 是为了给当初那个绝对定位的「已在今日拜访」
   标记（.rt-mark，left:30px）做定位基准。
   ⚠️ 本次（2026-10-06）标记整个删了（用户要求「这两个不要出现」），
   这条 position:relative 现在**没有已知用处**，但保留无害 ——
   .home-row 里可能有别的想要贴着这一行定位的东西，留着当锚点。
   历史（防止以后有人当成 bug 删掉又加回来）：
     · 当初加的时候忘了 position:relative，标记直接飘到外层定位祖先那儿；
     · 让位规则有两条重复的（82px 和 66px），后者按规则 7 收小后生效。 */
.home-row{display:flex;align-items:center;gap:10px;border-bottom:1px solid #f1f4fa;
  position:relative;
  /* 2026-10-06：逾期项并进「今天该跟 / 已逾期」那节，排最前，靠左侧一条
     3px 红线分组（独立的「已经逾期」区块整个删掉了）。
     ⚠️ 透明左边框是**给没逾期的行占位用的** —— 不占的话逾期行会比别的行
     凸出去 12px，店名左边缘参差。9px 内边距同理，两条都加，只换颜色。 */
  padding:10px 0 10px 9px;border-left:3px solid transparent}
.home-row:last-child{border-bottom:none}
.home-row .rn{font-weight:600;font-size:14px;flex:1;min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* ⚠️ 本次删掉了 `.home-row:has(> .rt-mark) .rn{padding-left:82px}` ——
   那两条让位规则（82px / 66px）都是给「已在今日拜访」标记让位的，
   标记没了，规则也命中不了任何元素。见 .rt-mark 那段的说明。 */
.home-row .rt{font-size:12px;color:var(--muted);flex:none}
/* 逾期的那几条：左边线点亮成红色 + 右侧日期也变红加粗。
   左边线是 .home-row 的 border-left，这里只换颜色,不改排版。 */
.home-row.od{border-left-color:var(--danger)}
.home-row.od .rt{color:var(--danger);font-weight:600}

/* ---- 第一百零一轮：桌面端路线规划布局 ----
   手机上那一行是按 390px 掐的：padding 10/9、gap 10、字 14，加上
   「← 左滑某一站」的手势提示。这些原样搬到桌面上有两个问题：
   · 手势提示说的是**触屏左滑**，鼠标用户根本没这个操作（排序态才用得上
     拖动），一行灰字挂在那儿只会误导；
   · 1300px 宽的行还是 10px 的 padding、14px 的字，左边序号到店名
     挤成一坨，右边按钮孤零零贴边 —— 宽屏应该更舒展。
   ⚠️ 断点用 1024px（桌面/折叠屏展开态的界线，与全站一致）。
   ⚠️ .rt 组里「打卡」按钮的文字不许换行（wrap 会出现「打卡」两个字
     竖着摞），用 white-space:nowrap 锁住；行内其余部分本来就靠
     .rn 的 min-width:0 + ellipsis 收缩，不会横向溢出。 */
@media(min-width:1024px){
  .sw-hint{display:none}                    /* 左滑是触屏手势，桌面别提示 */
  .home-row{padding:13px 18px 13px 12px;gap:16px}
  .home-row .rn{font-size:15px}
  .home-row .rt{white-space:nowrap;gap:12px;display:flex;align-items:center}
}
.home-empty{font-size:12.5px;color:var(--muted);padding:8px 0}

/* ---------- 1.9.7 工作台「我的路线规划」的排序模式 ----------
   用户反馈：「我的路线规划那里拖动客户排序并没有看到」。
   1.9.5 的拖动只长在地图页的路线面板（.rt-leg）里，工作台是另一套
   精简渲染（.sw-row + .home-row），压根没接拖动 —— 用户天天看的是工作台，
   所以要在工作台也做一份。

   排序模式是**显式开关**（点「调整顺序」才进），不是常驻：
   常驻的话每行左边永远挂个 34px 圆圈，十几行看下去全是圆点，
   而绝大多数时候用户只是想看顺序、不想改。 */
.hr-sort{display:flex;align-items:center;gap:10px;padding:8px 0;
  border-bottom:1px solid #f1f4fa;position:relative;background:var(--card)}
.hr-sort .rn{flex:1;min-width:0;font-weight:600;font-size:14px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* 排序态里把序号提到最前面当手柄。
   .rn 要让出 34px+10px 的位置，否则店名会顶到序号圆圈底下。 */
.hr-sort .hr-txt{flex:1;min-width:0;overflow:hidden}
/* 店名和地址挤在一行（地址单起一行会让每行高 20px，
   8 站就多占 160px，一屏看不全 —— 排序时该看的是次序不是地址） */
.hr-sort .hr-txt .rn{display:inline;font-size:13.5px}
.hr-sort .hr-txt .rt{display:inline;font-size:11.5px;color:var(--text-3);
  margin-left:8px;vertical-align:baseline}
/* 排序态的次要信息（右边的里程/导航）淡掉 —— 排序时该看的是次序，不是里程 */
.hr-sort .rt{font-size:12px;color:var(--text-3);flex:none}
/* 排序模式的整块底色，跟平时的白底区分开：
   不然用户点了「调整顺序」却看不出模式已经切了 */
#homeRouteList.sorting{background:var(--surface-2,#f7f9ff);border-radius:var(--r-xs);
  padding:0 10px}
#homeRouteList.sorting .hr-sort{border-bottom-color:transparent}
#homeRouteList.sorting .hr-sort:last-of-type{border-bottom:none}
/* 排序模式的提示条 */
.hr-tip{font-size:11.5px;color:var(--primary);background:var(--primary-soft);
  border-radius:8px;padding:7px 9px;margin:2px 0 6px;line-height:1.6}

/* ---- 工作台「我的待办」横排 ----
   竖向那几块已经把工作台撑得很长了,待办再竖着排一屏就滑不到底,
   所以做成横向滑动的一排卡片:左右扫一眼就看完全部,想细看再点进去。
   卡片宽度卡住不拉伸,手机上一条差不多占 3/4 屏,不用缩成一小条。 */
.td-strip{display:flex;gap:10px;overflow-x:auto;overflow-y:hidden;
  padding:10px 2px 4px;margin:0 -2px;-webkit-overflow-scrolling:touch;
  scroll-snap-type:x proximity;scrollbar-width:none}
.td-strip::-webkit-scrollbar{display:none}
.td-chip{flex:none;width:210px;max-width:76vw;scroll-snap-align:start;
  background:var(--surface-2);border:1px solid var(--border);border-radius:var(--r-md);
  padding:11px 12px;cursor:pointer;display:flex;flex-direction:column;gap:6px;
  text-align:left;-webkit-tap-highlight-color:transparent;
  transition:border-color .15s, box-shadow .15s}
.td-chip:active{border-color:var(--primary);box-shadow:0 4px 14px rgba(79,110,247,.12)}
.td-chip .td-c-t{font-size:13.5px;font-weight:600;line-height:1.4;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.td-chip .td-c-m{font-size:11.5px;color:var(--muted);display:flex;gap:8px;flex-wrap:wrap}
.td-chip .td-c-m .od{color:var(--danger);font-weight:600}
.td-chip .td-c-f{display:flex;align-items:center;gap:6px;margin-top:auto}
.td-chip .td-c-f .st{font-size:11px}
.td-chip .td-c-f .btn{margin-left:auto;padding:4px 10px;font-size:12px}

/* ---- 左滑露出操作 ---- 像 iOS 备忘录那样:按住行往左拖,右侧滑出「删除」。
   只给「临时清单」类列表用(现在是首页路线站点),别到处套。
   .sw-body 必须有不透明背景,否则拖动时会透出底下的红色按钮。 */
.sw-row{position:relative;overflow:hidden;border-radius:var(--r-xs)}
/* ⚠️ will-change 不能常驻在这里 —— 见下面 .dragging 那条的解释。
   合成层被 iOS Safari 提升后可能绕过父级 .sw-row 的 overflow:hidden 裁剪。 */
.sw-row .sw-body{position:relative;z-index:1;background:var(--card);
  transition:transform var(--dur-2) var(--ease-out)}
/* 只在「真的在拖」的这一刻才提升合成层。
   原来 will-change:transform 是无条件挂着的，两个代价：
     ① iOS Safari 上合成层可能绕过父级 .sw-row 的 overflow:hidden 裁剪 ——
        红色 .sw-del 自己是方角（全靠父级裁成圆角），裁剪一失效，
        右上/右下两个方角就戳在 6px 圆角卡片外面，真机上看就是
        「红色按钮溢出行」。这是 5.3 报的那个现象唯一说得通的路径
        （沙箱 Chromium 的裁剪是好的，四组字号 × 收起/展开/去裁剪对照
        全量过都是 0 溢出，量不出「该裁没裁」这种渲染层问题）。
     ② MDN 明确不建议 will-change 常驻：它一直占着合成层内存，
        路线一长就是一整列合成层。
   拖动时才加正是 will-change 的正确用法。 */
.sw-row.dragging .sw-body{transition:none;will-change:transform}
.sw-row .sw-del{position:absolute;top:0;right:0;bottom:0;z-index:0;width:76px;
  border:none;padding:0;background:var(--danger);color:#fff;cursor:pointer;
  font-size:13px;font-weight:600;font-family:inherit;
  /* 圆角自己带上，不再只靠父级 .sw-row 的 overflow:hidden 去裁。
     正常裁剪时看不出差别（本来就该是这个形状），但万一哪天裁剪没生效
     （iOS 合成层、以后有人改了 overflow、换渲染引擎），它至少还是
     跟卡片同弧度的圆角，不会冒出两个方角。 */
  border-radius:0 var(--r-xs) var(--r-xs) 0;
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;
  -webkit-tap-highlight-color:transparent}
.sw-row .sw-del:active{filter:brightness(.92)}
.sw-row .sw-del svg{width:15px;height:15px;stroke:currentColor;stroke-width:1.9;fill:none;
  stroke-linecap:round;stroke-linejoin:round}
.sw-hint{font-size:11px;color:var(--text-3);padding:2px 0 6px}

/* 工作台「我的路线规划」底部那排动作（在地图上看 / 切换骑行）。
   ⚠️ 2026-10-06 用户标注「大小样式看齐」—— 两个按钮都用 .btn btn-sm，
   这里只负责排布：等宽、左对齐、间距一致。原来是内联 style 写的
   flex + wrap，几个按钮宽度参差（主按钮 + 普通按钮字号/内距不同），
   看着不像一组。用 display:flex + align-items:center 让它们等高。 */
.rt-actions{display:flex;align-items:center;gap:8px;margin-top:8px;flex-wrap:wrap}

/* ---- 手机底部标签栏 ---- */
.tabbar{display:none;position:fixed;left:0;right:0;bottom:0;z-index:40;
  background:rgba(255,255,255,.97);border-top:1px solid var(--border);
  padding:6px 2px calc(6px + env(safe-area-inset-bottom,0px));
  box-shadow:0 -2px 16px rgba(31,42,80,.07)}
.tabbar-item{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:3px;padding:4px 2px;border-radius:12px;color:var(--text-3);font-size:11px;font-weight:500;
  background:none;position:relative;min-height:48px;
  transition:color var(--dur-1) var(--ease),background-color var(--dur-1) var(--ease),
             transform var(--dur-1) var(--ease-spring)}
.tabbar-item svg{width:22px;height:22px;stroke:currentColor;stroke-width:1.7;fill:none;
  stroke-linecap:round;stroke-linejoin:round;
  transition:stroke-width var(--dur-2) var(--ease),transform var(--dur-2) var(--ease-spring)}
.tabbar-item:active{transform:scale(.92)}
/* 选中态的小字用 --primary-text(#4a68ee, 4.66:1) 而不是 --primary(4.28:1)——
   11px 的字差这一点就不过 AA。图标是描边图形, 不受这条限制, 仍用 --primary。
   这里刻意用变量而不是硬编码色值: [data-theme="dark"] 会把它覆写成 #6d88ff,
   固定成 #4a68ee 在深底上反而掉到 3.4:1。 */
.tabbar-item.active{color:var(--primary-text)}
.tabbar-item.active svg{stroke-width:2.2;transform:translateY(-1px)}
.tabbar-item .dot{position:absolute;top:3px;right:calc(50% - 18px);min-width:15px;height:15px;
  padding:0 4px;border-radius:8px;background:var(--danger);color:#fff;font-size:10px;
  line-height:15px;text-align:center;font-weight:600}
/* 「更多」抽屉:放低频入口 */
.sheet-mask{position:fixed;inset:0;background:var(--scrim);z-index:60;display:flex;
  align-items:flex-end;animation:fade var(--dur-2) var(--ease-out)}
.sheet{width:100%;background:#fff;border-radius:18px 18px 0 0;padding:8px 0 max(14px,env(safe-area-inset-bottom,0px));
  animation:up var(--dur-3) var(--ease-out);max-height:74vh;max-height:74dvh;overflow-y:auto;overscroll-behavior:contain}
.sheet-title{font-size:12px;color:var(--muted);padding:6px 18px 10px}
.sheet-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:4px;padding:0 10px}
.sheet-item{display:flex;flex-direction:column;align-items:center;gap:6px;padding:12px 4px;
  border-radius:12px;font-size:12px;color:var(--text-2);background:none}
.sheet-item:active{background:var(--primary-soft);transform:scale(.96)}
.sheet-item svg{width:24px;height:24px;stroke:var(--primary);stroke-width:1.7;fill:none;
  stroke-linecap:round;stroke-linejoin:round;
  transition:transform var(--dur-2) var(--ease-spring)}
.sheet-item:hover svg{transform:translateY(-2px)}
.sheet-item .dot{position:absolute;top:6px;right:calc(50% - 20px);min-width:16px;height:16px;
  padding:0 4px;border-radius:8px;background:var(--danger);color:#fff;font-size:10px;
  line-height:16px;text-align:center;font-weight:600}
/* 回收站计数:低调的灰色小字,不抢眼、不像待办 */
.sheet-item .gray-num{position:absolute;top:12px;right:calc(50% - 22px);
  font-size:10.5px;color:var(--muted);font-weight:500}
.sheet-item{position:relative}
.sheet-close{width:100%;padding:13px;font-size:14px;color:var(--muted);background:#fafbfe;
  border-top:1px solid var(--border);margin-top:10px}
@keyframes up{from{transform:translateY(100%)}to{transform:translateY(0)}}
@keyframes fade{from{opacity:0}to{opacity:1}}
@media (max-width:699px){
  .tabs{display:none}
  .tabbar{display:flex}
  body{padding-bottom:calc(64px + env(safe-area-inset-bottom,0px))}
  .main{padding:12px 12px 8px}
  /* 手机顶栏收窄:低频按钮收进底部「更多」抽屉 */
  #btnLogout,#btnBell{display:none}
  /* 「＋新建客户」保留在顶栏。
     曾经有一段时期它在手机断点被隐藏、改用右下角悬浮按钮，用户反馈顶栏更合适
     —— 于是恢复顶栏按钮并撤掉悬浮按钮（同一个功能不留两个入口）。
     顶栏空间不足时 flex-wrap 会换到第二行，不会再把它挤没。

     2026-10-04 从 6px 收到 4px：搜索框挪进 .top-actions 之后（1.9.4），
     390px 下这一行装不下「搜索 + ⋯ + 新建」，「＋新建客户」
     右边被切掉 7px。三段 4px 正好装下，视觉上仍分得开。
     ⚠️ 这条是**唯一**给窄屏 .top-actions 定 gap 的地方 ——
     之前我怕它被这条盖掉，反而在前面又写了一条 gap:4px，
     结果同优先级下**后面这条 6px 赢**，改了个寂寞。
     教训：与其在前面加一条指望能覆盖，不如**直接改后面那条**。 */
  .top-actions{gap:4px;min-width:0}
}

/* ---- 表格 ---- */
/* 列表永不横向滚动：原来手机上表格 715px、容器 366px，要先横滑才能看到
   后面的列。改成卡片后不该再有 —— 这里再兜一层，任何新加的列都不会
   重新引入横滑（用户明确要求"不要有能滑动的选项"）。 */
.table-card{overflow-x:hidden}
.table-card table{touch-action:pan-y}
.table-card{background:var(--card);border-radius:var(--radius);box-shadow:var(--sh-1);
  overflow:auto;-webkit-overflow-scrolling:touch;overscroll-behavior-x:contain}
table{width:100%;border-collapse:collapse;min-width:min(100%,900px)}
/* 手机端：把表格行改成卡片，不再横滑。
   实测 390px 下表格 715px、容器 366px，7 列只有「客户」看得到，
   而这是底栏主列表 —— 看"什么阶段/谁在跟"要先把手指移进表格横滑，
   还要和侧滑返回抢手势。700-1023px 断点早就关掉了横滑，手机断点漏了。 */
@media(max-width:699px){
  /* ⚠️ 手机端必须是 table-layout:fixed，这是横滑的最终解法。
     卡片模式下 tr 是 display:block、td 是 display:flex，
     表格已经不再是"表格"，只是用来承载卡片的外壳；
     但 table-layout:auto 仍会按 min-content 算列宽，只要有任一格的内容
     （长地址 / 一串电话 / 两个标签 / 甚至店名）顶出容器，
     表格就比 .table-card 宽，overflow:auto 于是横滑，
     右侧的状态标签被推出屏幕外 —— 用户看到的正是这个。
     试过并**无效**的办法（都试过，记下来免得下个人再走一遍）：
       给 .c-tags / .c-name / .addr-link 加 min-width:0
       给它们加 overflow:hidden / overflow-wrap:anywhere / word-break
       给 c-top 加 max-width:100%
       给 td 加 overflow:hidden
     它们都改不动 auto 算法算出来的 min-content。换 fixed 之后列宽直接
     按容器分配，360/375/390 三个常见机宽全部不再横滑。
     因为最后一列之外都被 display:none，fixed 不会把信息压扁。 */
  .table-card table{min-width:0;table-layout:fixed}
  .table-card thead{display:none}          /* 表头在卡片模式下没有意义 */
  .table-card tbody tr{display:block;padding:10px 12px;
    border-bottom:1px solid var(--border)}
  .table-card tbody td{display:flex;align-items:flex-start;gap:8px;
    padding:2px 0;border:0;white-space:normal}
  /* 卡片模式只保留两格：客户（店名+阶段/类型标签+地址+电话）/ 联系方式。
     阶段列已删（标签移进店名行，重复了）；来源、标签、最近跟进在详情里都有。
     顺序：客户 1 / 联系方式 2 / 来源 3 / 标签 4 / 最近跟进 5 */
  .table-card tbody td:nth-child(3),
  .table-card tbody td:nth-child(4),
  .table-card tbody td:nth-child(5){display:none}

  /* ⚠️ 地址和电话这两行必须解除 nowrap，否则手机端表格横滑。
     上面 td 虽然写了 white-space:normal，但 .addr-link 自己又声明了
     white-space:nowrap（那是给桌面端窄列用的），它优先级更高，
     于是「◎ 拾年烤铺(金沙洲店) 大沥镇芭蕾雨时尚中心宝轮广场」这种长地址
     无法折行，把这一列的 min-content 顶到 435px；
     表格是 table-layout:auto，宽度取 max(100%, 各列 min-content)，
     结果表格 435 > 容器 390，.table-card{overflow:auto} 就横滑 45px ——
     右侧的客户状态标签被推出屏幕外，正是用户反馈的现象。
     实测：只把地址行改回可折行，表格立刻从 435 回落到 390。
     桌面端（≥700px）不受影响，那里的地址是一行省略号，位置够。 */
  .table-card .addr-link{white-space:normal;
    display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;
    overflow:hidden;
    /* 光能折行还不够：地址里是「拾年烤铺(金沙洲店)大沥镇…」这种
       中文夹全角括号、又混着英文数字的长串，默认断行点太少，
       整串的 min-content 仍然很大（实测 260~379px），
       表格就还是被顶宽。overflow-wrap:anywhere 允许在任意字之间断开，
       min-content 才真正塌到 1 个字宽 —— 这是它和 break-word 的区别：
       break-word 只在"单词放不下"时才断，对没有空格的 CJK 长串无效。 */
    overflow-wrap:anywhere;word-break:break-word}
  /* 同理，电话那行「📞 16673327777 呼叫 ›」也是不折行的长串 */
  .table-card td:nth-child(2) .c-sub{overflow-wrap:anywhere;word-break:break-word}

  /* 联系方式这一格在卡片模式改回竖排。
     它吃的是全局 `td{display:flex}`，于是「姓名」和下面的「号码 呼叫 ›」
     被排成一行 —— 号码那串有 nowrap，min-content 约 104px，加上第一列
     最长的 380px，整表就被顶到 380+ 而容器只有 366（390px 屏）。
     卡片模式本来就该"一行一条信息"，竖排既省宽度也更符合阅读顺序。
     实测：改竖排后 360/375/390 三个常见机宽全部不再横滑。
     ⚠️ 2026-10-06 补一刀 width:auto：td 上有内联 width:26%（桌面窄列用的），
     竖排后它还在生效 —— 390px 屏上把这一格压到 ~95px，电话号码被从中间
     折断（「158888888 / 8」），号码都没法整段读。内联样式只能 !important 压。
     放开后「陈 · 15888883388 呼叫 ›」一行放得下（约 135px < 366px），
     卡片又矮一行。 */
  .table-card tbody td:nth-child(2){display:block;width:auto !important}
}
th{font-size:12px;color:var(--muted);font-weight:500;text-align:left;padding:11px 14px;
  border-bottom:1px solid var(--border);background:#fafbfe;white-space:nowrap}
td{padding:12px 14px;border-bottom:1px solid var(--border);vertical-align:middle}
tr:last-child td{border-bottom:none}
tbody tr{transition:background-color var(--dur-1) var(--ease);cursor:pointer}
tbody tr:hover{background:var(--hover-soft)}
/* 店名 + 右侧标签同一行。店名可收缩（min-width:0 才能真的收缩），
   标签不收缩也不换行 —— 否则窄屏上标签会被压成两行、比原来更乱。 */
/* 店名行：店名占满左侧可收缩，标签贴右固定不收缩。
   标签区固定 gap、右对齐，行与行之间右侧起始位置会随内容变化，
   但右边缘始终对齐 —— 视觉上是一条竖直的边界线。

   ⚠️ 修过两处（缺任何一个都白改）：
   1) .c-tags 原来没有 margin-left:auto —— 标签只是紧贴店名；
   2) 光加 margin-left:auto 仍然无效，因为包裹层没伸展：
      td 是 display:flex，里面那个无名 <div>（店名+电话那一列）是
      flex:0 1 auto，宽度被内容定住（实测 147px，而 td 有 342px），
      于是内层 flex:1 拿不到余量，.c-top 只有 114px，标签无处可推。
      给这个包裹层 flex:1 + min-width:0 才会真正占满。

   实测（修前 → 修后）阶段标签 x 坐标跨度：
      99 / 114 / 146 / 147 / 175  →  全部对齐到同一 x */
.c-top{display:flex;align-items:center;gap:8px}
.c-top .c-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap}
/* 标签贴右需要两件事同时成立（缺一件都白改）：
   1) .c-tags 有 margin-left:auto —— 否则标签只是紧跟店名；
   2) 包裹列会伸展 —— td 是 display:flex，里面那一列（店名/地址/电话）
      原先 flex:0 1 auto，宽度被内容定住（实测 147px 而 td 有 342px），
      内层 flex:1 拿不到余量，.c-top 只剩 114px，标签无处可推。
   实测阶段标签 x 跨度：99/114/146/147/175 → 修后全部对齐。
   用 :has 精确锁定客户列表那列：td > div 会误伤骨架屏的 td > div.sk
   （骨架屏 td 也吃全局 td{display:flex}），那里 flex:1 会和它自己的
   width:80% 之类打架。:has 认不出时前面的兜底规则仍生效。 */
td > div{flex:1;min-width:0}
td:has(> div > .c-top) > div{flex:1;min-width:0}
.c-tags{display:flex;align-items:center;gap:6px;flex:none;margin-left:auto}
/* 标签为空时不占位（没有类型的客户不会在右侧留一条空隙） */
.c-tags:empty{display:none}
/* ⚠️ 标签**必须能被压缩到比内容窄**。
   不是审美问题，是 390px 手机上表格会横滑出 45px 的直接原因：
   flex 项默认 min-width:auto，就是"不许比内容窄"，于是
   「客满德酒系统」(6 字) + 「意向」这一行把 min-content 顶到 150px，
   整张表的 min-content 就跟着变成 435px > 容器 390px，
   .table-card{overflow:auto} 于是横滑——状态标签被推出屏幕外。
  实测：把这一行隐藏，表格立刻回落到 390px，可见就是它顶的。
   给 0 之后标签可以在极端情况下被压窄，再由 max-width + 省略号收尾；
   正常宽度下(≥360px)还是完整显示，不会真的被截。 */
.c-tags{min-width:0}
.c-tags .ctype-chip,.c-tags .chip{flex:0 1 auto;min-width:0}
/* 店名是列表里最该被先看到的一行：16px/600、近黑（#11141d，对白底 18.4:1）。
   原来用 --text(#1f2430) 也达标，但参考图里店名明显更"重"，
   和右侧两个彩色标签并排时才压得住，所以单独给一档更深的字色。 */
.c-name{font-weight:600;font-size:16px;line-height:1.4;color:#11141d}
[data-theme="dark"] .c-name{color:#eef1f7}
.c-sub{font-size:12px;color:var(--muted);margin-top:2px}
/* 2026-10-06：联系人 · 电话并成一行后的分隔点。
   span 是行内的，点两侧各留一点呼吸位就行；没有它「陈158888…」会黏在一起。 */
.c-sub .c-dot{margin:0 4px;color:var(--faint,#b6bdc9)}
.addr-link{color:var(--text-2);font-size:12px;text-decoration:none;display:inline-block;
  max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:middle}
.addr-link:hover{color:var(--primary);text-decoration:underline}
.city-chip{padding:4px 12px;border-radius:14px;border:1px solid var(--border);background:#fff;
  font-size:12.5px;color:#5a6480;min-height:30px;display:inline-flex;align-items:center;
  transition:transform var(--dur-1) var(--ease-spring),background-color var(--dur-1) var(--ease),
             border-color var(--dur-1) var(--ease),color var(--dur-1) var(--ease),
             box-shadow var(--dur-1) var(--ease)}
.city-chip:hover{border-color:var(--primary);color:var(--primary)}
.city-chip:active{transform:scale(.95)}
.city-chip.on{background:var(--primary-soft);
  color:var(--primary);border-color:var(--primary);font-weight:600}
.info-grid .full{grid-column:1/-1}
.badge{display:inline-block;padding:3px 10px;border-radius:20px;font-size:12px;font-weight:500}
/* 统一的标签基类：阶段、客户类型都用它 —— 之前两者各写一套，
   字号/内边距/圆角/行高不一致，并排时高低不齐，看起来"乱"。
   高度写死 22px 而不是靠 padding 撑，是为了让"线索"和"客满德酒系统"
   这种长短差很多的内容在视觉上完全等高。
   2026-10-03 用户给了参考图，标签从 11px/18px 胶囊放大到 12.5px/22px：
   参考图里标签和店名几乎等高，是列表里的一等信息，不是脚注。
   圆角给足（999px 全圆角）：参考图是"药丸"而不是"圆角矩形"。 */
.chip,.ctype-chip{display:inline-flex;align-items:center;height:22px;
  padding:0 9px;border-radius:999px;font-size:12.5px;line-height:1;
  font-weight:500;white-space:nowrap;vertical-align:middle;
  max-width:9em;overflow:hidden;text-overflow:ellipsis}
/* 只留颜色与字重差异 —— 尺寸/内边距/圆角全部交给上面的统一基类。
   这里**不能**再写 display / padding / font-size / border-radius：
   `.ctype-chip` 原来写了 display:inline-block，因为后定义覆盖了基类的
   inline-flex，两个标签才会高低不齐（这就是"排版乱"的直接原因）。 */
.chip{background:#eef1fb;color:#5a6bd8;margin-right:4px}
.ctype-chip{font-weight:600;margin-right:4px}
/* ---- 客户列表卡片右侧的两个标签(2026-10-03 按参考图改) ----
   列表里"客户类型 + 客户状态"要看齐成一条竖线，且要一眼分得清：
     · 类型 = 绿底深绿字，实心填充，是"这家店是什么"；
     · 状态 = **按阶段变色**，实心填充，是"进展到哪一步"。

   状态色的来龙去脉（免得下次又走回去）：
     第一版：状态统一成灰底 —— 参考图里那一行恰好是灰的，就照做了。
     第二版（现在）：用户要求"不同阶段不同颜色"。
     两种都对，但**颜色必须过 AA** —— 第一版那五档没有一档达标
     （线索 4.15 / 意向 3.67 / 报价 2.45 / 成交 3.10 / 释放 3.71，
     最差的「报价」只有 2.45:1），所以顺手把文字压深了。
     现在的五档：6.61 / 6.15 / 4.95 / 5.54 / 6.21，色相和语义都没变。

   下面这 5 条类是**按阶段名硬写的**，不是运行时从 JS 注入 ——
   因为 <style> 在 <body> 之前，运行时注入会让首屏闪一下原色。
   新增阶段时要同时加两处：STAGE_STYLE 里一条 + 这里一条，
   色值必须一致。漏了也不会白字：有 .chip-st-unknown 兜底。
   注意：这里能盖住颜色，是因为列表渲染调的是
   ctypeChip(t, true) / stageChip(s, true) —— 不带内联 style 的版本。
   只要还写内联 style，这些规则全部无效（内联优先级最高）。
   ⚠️ 2026-10-08（丙件）：**类型标签不再走 plain 了** ——
   列表渲染改成 ctypeChip(c.ctype)（带内联色），所以下面这段
   1.14.0 的「类型标签灰化」已被撤销。**注释保留**：它记录了当时的
   理由（卡片上两个彩块看着吵），将来若有人想再灰化，先看这段再动手。
   阶段标签 stageChip(s, true) 照旧，上面这 5 条类规则继续生效。 */
/* 【2026-10-08 已撤销，勿再参考结论】1.14.0：列表里的「客户类型」改灰。
   撤销原因：用户看过详情页的彩色类型标签后，明确要求「列表里跟详情一个色」。
   保留这段是因为它把当年的取舍写清楚了（属性 vs 状态、谁该抢颜色），
   下次有人觉得"列表太花"想再灰化时，直接看这里的权衡即可 ——
   别再凭直觉改一遍，用户已经拍过一次板了。
   ---- 以下为 1.14.0 原文 ----
   原来它和阶段标签一样是实心彩色（绿底），于是一张卡片上出现两个彩块：
   绿的"收钱吧" + 黄的"报价"，一屏 3 张卡就是 6 个色块，看着吵。
   但两者的语义完全不同：
     阶段（线索/意向/报价/成交）是**会变的**状态，值得用颜色区分；
     客户类型是**属性**（他现在用哪套系统），基本不变，不该抢颜色。
   所以属性用灰、状态用色 —— 一眼扫过去，彩色只落在"该关注的那一个"上。
   ⚠️ 这里只改列表卡片（.c-tags 作用域）。详情页/编辑弹窗里的客户类型
   还是原来那套彩色 —— 那儿是单独一个字段，不存在和阶段抢颜色的问题。
   ---- 原文结束 ----
   ⚠️ 对应的 CSS 规则（.c-tags .ctype-chip 与暗色版）已**删除**：
   既然列表不再输出 plain 版本，那两条规则再没有生效对象。
   留着是颗哑弹 —— 将来谁在 .c-tags 下再放一个 plain 类型标签，
   会被这条早已无人记得的规则盖灰。 */
.c-tags .chip{margin-right:0}
/* 状态标签按阶段上色（实心药丸，文字色已压到 AA 以上） */
.c-tags .chip-st-线索{background:#eef1f6;color:#4d5566}
.c-tags .chip-st-意向{background:#e8edff;color:#2f4bc4}
.c-tags .chip-st-报价{background:#fff3e0;color:#9a5b00}
.c-tags .chip-st-成交{background:#e3f7ee;color:#0a6f4d}
.c-tags .chip-st-释放{background:#f2eefc;color:#6a35c9}
.c-tags .chip-st-未知店名{background:#e2f6ea;color:#0a7a45}
/* 兜底：万一有新阶段没加进来，至少不是"看不见的字" */
.c-tags .chip-st-unknown{background:#f1f0f5;color:#5c636b}
/* 深色主题：浅底会把眼睛晃掉，改成"深底 + 亮字"，保持同一套色相。
   这几档都按深底 #20242e 复算过，依次 7.4 / 7.6 / 7.4 / 7.9 / 7.1。 */
/* 【2026-10-08 已撤销】1.14.0 的暗色版类型标签灰化规则，理由同上面浅色那条
   （列表已不再输出 plain 类型标签 → 这条没有生效对象）。删掉而不是留着，
   避免"浅色改了、暗色忘了"这种只有切主题才发现的漂移。
   ⚠️ 类型标签现在的颜色来自 ctypeColor(t) 的**内联 hsl**，浅色暗色共用
   同一份 —— 暗色主题下它照样是彩色，这是**符合预期**的（用户要的
   "跟详情一个色"，详情在暗色下也是彩色）。 */
[data-theme="dark"] .c-tags .chip-st-线索{background:rgba(255,255,255,.10);color:#c2c8d4}
[data-theme="dark"] .c-tags .chip-st-意向{background:rgba(70,100,232,.24);color:#9fb2ff}
[data-theme="dark"] .c-tags .chip-st-报价{background:rgba(224,138,0,.22);color:#f0b95e}
[data-theme="dark"] .c-tags .chip-st-成交{background:rgba(15,157,110,.22);color:#6fd8ae}
[data-theme="dark"] .c-tags .chip-st-释放{background:rgba(139,92,246,.24);color:#c4a8fb}
[data-theme="dark"] .c-tags .chip-st-未知店名{background:rgba(22,163,74,.26);color:#7ddb9d}
[data-theme="dark"] .c-tags .chip-st-unknown{background:rgba(255,255,255,.10);color:#c2c8d4}
.empty{text-align:center;padding:60px 0;color:var(--muted)}
.empty .icon{font-size:40px;margin-bottom:10px;opacity:.75}
/* ---- 骨架屏: 替代生硬的「加载中…」文字 ---- */
.sk{background:linear-gradient(90deg,#eef1f7 25%,#f6f8fc 37%,#eef1f7 63%);
  background-size:400% 100%;animation:sk 1.4s ease infinite;border-radius:6px}
@keyframes sk{0%{background-position:100% 50%}100%{background-position:0 50%}}
.sk-list{display:flex;flex-direction:column;gap:10px;padding:4px 0}
.sk-row{display:flex;align-items:center;gap:10px}
.sk-row .sk:first-child{width:34px;height:34px;border-radius:50%;flex:none}
.sk-line{height:13px}
/* 骨架行: 不参与 hover / 不做 rise 动画 —— 它是"占位", 不是内容 */
.sk-tr{pointer-events:none}
.sk-tr:hover{background:transparent}
.sk-tr td{padding-top:14px;padding-bottom:14px}
/* 列表/卡片进场:轻微上浮淡入,让内容出现得不那么突兀 */
@keyframes riseIn{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}
.rise{animation:riseIn var(--dur-3) var(--ease-out) both}
/* 视图切换: 新面板淡入 + 轻轻上浮。用 animation 而不是 transition,
   因为视图是 display:none/block 切换的, transition 抓不到起始状态。
   同时加 fill-mode:both, 动画结束后停在终态, 不留残影。 */
.view-in{animation:viewIn 320ms var(--ease-out) both}
@keyframes viewIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}
.pager{display:flex;gap:6px;justify-content:flex-end;padding:12px 14px;align-items:center}
.pager button{border:1px solid var(--border);background:#fff;border-radius:7px;padding:5px 11px;
  font-size:13px;min-height:32px;
  transition:transform var(--dur-1) var(--ease-spring),background-color var(--dur-1) var(--ease),
             border-color var(--dur-1) var(--ease),color var(--dur-1) var(--ease)}
.pager button:active{transform:scale(.95)}
.pager button.cur{background:var(--primary);color:#fff;border-color:var(--primary)}
.pager button:disabled{opacity:.4;cursor:default;transform:none}
/* ---- 按钮 loading 态 ---- */
.btn.is-loading{position:relative;color:transparent!important;pointer-events:none}
.btn.is-loading>*{visibility:hidden}
.btn.is-loading::after{content:"";position:absolute;top:50%;left:50%;
  width:14px;height:14px;margin:-7px 0 0 -7px;border-radius:50%;
  border:2px solid currentColor;border-color:rgba(255,255,255,.35);border-top-color:#fff;
  animation:btnSpin .62s linear infinite}
/* 白底按钮上的转圈得用主色, 否则白圈看不见 */
.btn:not(.btn-primary):not(.btn-danger).is-loading::after{
  border-color:var(--border);border-top-color:var(--primary)}
@keyframes btnSpin{to{transform:rotate(360deg)}}

/* ---- 滚动加载状态行 ---- */
.load-more{text-align:center;padding:14px 0 18px;font-size:12.5px;color:var(--muted);
  display:flex;align-items:center;justify-content:center;gap:8px}
.load-more.done{color:#c2c9d6;font-size:12px;letter-spacing:.4px}
/* 转圈: 只在还有下一页时出现 */
.lm-sp{width:13px;height:13px;border-radius:50%;flex:none;
  border:1.8px solid var(--border);border-top-color:var(--primary);
  animation:lmSpin .7s linear infinite}
@keyframes lmSpin{to{transform:rotate(360deg)}}

/* ---- 看板 ---- */
.board{display:flex;gap:14px;overflow-x:auto;padding-bottom:10px;align-items:flex-start;
  overscroll-behavior-x:contain;-webkit-overflow-scrolling:touch;scroll-snap-type:x proximity}
.b-col{background:#eef1f8;border-radius:var(--radius);min-width:250px;width:250px;flex:none;
  padding:10px;transition:background-color var(--dur-2) var(--ease);scroll-snap-align:start}
.b-col.dragover{background:#e2e9ff}
.b-head{display:flex;align-items:center;gap:8px;padding:4px 6px 10px;font-weight:600;font-size:14px}
.b-dot{width:9px;height:9px;border-radius:50%}
.b-count{margin-left:auto;background:#dde3f3;color:#5a6480;border-radius:12px;padding:1px 9px;font-size:12px}
.b-cards{display:flex;flex-direction:column;gap:8px;min-height:60px}
/* 成交列的「已派单 / 待派单」小标 —— 待派单用暖色,别让签下来的单子没人干活 */
.b-won{display:inline-block;margin-left:6px;font-size:11px;font-weight:600;padding:1px 7px;
  border-radius:5px;background:#e3f7ee;color:#0f7a55;vertical-align:1px}
.b-won.todo{background:#fff2e0;color:#96550a}
.b-won.ing{background:#e8edff;color:#3a54d6}
/* 成交列底部那行「已安装完成 N 家」——装完的收起来,别占着要盯的活 */
.b-done{display:flex;align-items:center;gap:6px;margin-top:2px;padding:7px 10px;border-radius:8px;
  background:#e3f7ee;color:#0f7a55;font-size:12.5px;font-weight:600;cursor:pointer;
  user-select:none;-webkit-tap-highlight-color:transparent}
.b-done:active{filter:brightness(.95)}
.b-card.is-done{opacity:.72}
.b-card{background:var(--card);border-radius:10px;padding:12px;box-shadow:var(--sh-1);
  cursor:grab;border:1px solid transparent;
  transition:box-shadow var(--dur-2) var(--ease),transform var(--dur-2) var(--ease),
             border-color var(--dur-2) var(--ease)}
.b-card:hover{box-shadow:var(--sh-2);transform:translateY(-2px)}
.b-card:active{transform:translateY(-2px) scale(.99);cursor:grabbing}
.b-card.dragging{opacity:.45;transform:rotate(1.5deg) scale(1.02)}
.b-title{font-weight:600;font-size:16px;line-height:1.4;margin-bottom:5px}
.b-meta{font-size:12px;color:var(--muted);display:flex;align-items:center;gap:6px;flex-wrap:wrap}
/* 下一阶段按钮：手机上唯一可行的改阶段方式（拖拽在触摸屏不成立）。
   占满宽度、够高，避免和卡片的"点开详情"热区打架。 */
.b-next{display:block;width:100%;margin-top:8px;min-height:32px;
  font-size:12.5px;background:var(--primary-soft,#eef1fb);
  border-color:var(--primary);color:var(--primary)}
.b-next:hover{background:var(--primary);color:#fff}
.b-foot{display:flex;align-items:center;margin-top:8px;font-size:11px;color:var(--muted)}

/* ---- 抽屉 ---- */
.mask{position:fixed;inset:0;background:var(--scrim-soft);z-index:40;opacity:0;
  transition:opacity var(--dur-2) var(--ease),visibility var(--dur-2);
  pointer-events:none;visibility:hidden}
.mask.show{opacity:1;pointer-events:auto;visibility:visible}
/* 100dvh = 真实可视高度(不含手机浏览器底栏),避免底部按钮被浏览器工具栏挡住 */
/* 宽屏:固定 500px;窄屏 / 折叠屏展开态:跟着视口走,最小 560 上限、92vw 封顶 ——
   统一写在这里,就不会出现"基础样式 500px、折叠屏断点又改成 60vw"两处打架。 */
/* 隐藏改用 transform:translateX(100%)（第一百零一轮）。
   旧写法 right:-520px 是按「抽屉 560px 宽」硬算的偏移，宽度一旦变成
   min(560px,92vw) 的实际值（比如视口 608px 时抽屉只有 560），560-520=40px
   就永远露在外面 —— 桌面每个宽度右侧都挂一条 40px 白竖条（实测 1568/1440/
   1280/1024/900/700 全中）。translateX(100%) 是按自身宽度推出去，
   数学上不可能漏。宽屏要露出底下一角的话另说，目前没有这个需求。 */
.drawer{position:fixed;top:0;right:0;width:min(560px,92vw);max-width:92vw;overflow-x:hidden;
  height:100vh;height:100dvh;background:#fff;
  z-index:50;box-shadow:var(--sh-3);
  transform:translateX(100%);
  transition:transform var(--dur-3) var(--ease-out);display:flex;flex-direction:column;
  will-change:transform}
.drawer.show{transform:none}
/* 装成 App(standalone)后顶部状态栏会压住标题,这里预留安全区 */
.d-head{padding:18px 22px;padding-top:calc(18px + env(safe-area-inset-top,0px));
  border-bottom:1px solid var(--border);
  /* 头部会换行：390px 上标题+阶段+7 个动作按钮+关闭键一行放不下。
     允许换行并给行间距，比硬挤或靠 margin-left:auto 单推一个按钮都稳。 */
  display:flex;flex-wrap:wrap;align-items:center;row-gap:10px}
.d-title-row{display:flex;align-items:center;gap:10px}
.d-title-row h2{font-size:20px;flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* 点店名才出现的小按钮（2026-10-08）。flex:none 不让它参与 h2 的
   flex:1 分配 —— h2 会随标题长短伸缩，按钮跟着飘会让「点哪一下」
   每次都变位置。cursor:pointer 是 h2 可点的唯一提示（标题不换色，
   避免跟「未知店名」绿标抢注意力）。 */
.d-title-row h2{cursor:pointer}
.d-name-copy{flex:none;font-size:12px;line-height:1;padding:5px 9px;
  border:1px solid var(--border);border-radius:14px;background:var(--card);
  color:var(--muted);cursor:pointer;white-space:nowrap}
.d-name-copy:hover{border-color:var(--primary);color:var(--primary)}
/* 2026-10-06 撤掉 margin-left:auto —— 实测它才是"左侧大量留白"的原因。

   1.14.0 加它的理由是"让 ⋯ 永远贴在行尾，别掉到第二行占一整行"。
   但实测（生产 390px，客户有编辑权时）：
       编辑 191-265
       ⋯    296-340     ← 与编辑隔了 31px，自己折到第二行
   在这两行上，auto 把它顶到最右，于是它左侧 296px 全空 ——
   用户看到的正是"这个能移到编辑旁边吗，这样左侧就不会大量留白"。

   结论：auto 没解决"掉行"，只是把"掉行后占一整行"换成了"掉行后左侧全空"。
   撤掉它，改为让整个头部可换行（见 .d-head），操作按钮整组换行、靠 gap 排满。 */
.d-head{row-gap:10px;flex-wrap:wrap}
.d-head .d-title-row{flex-wrap:wrap;row-gap:10px}
/* 1.14.0：「今日已访 19:24」—— 这是**信息**不是按钮，所以不做成 btn。
   绿点 + 浅绿底：跟「已跑完」那类状态标签同一套语言（见 .vtag.ok）。
   不可点：再点一次打卡是低频动作，收进 ⋯ 菜单了。 */
.visit-state{display:inline-flex;align-items:center;gap:5px;font-size:12.5px;
  font-weight:600;color:var(--ok);background:#e5f4ee;border-radius:var(--r-sm);
  padding:7px 11px;white-space:nowrap}
.visit-state i{width:6px;height:6px;border-radius:50%;background:var(--ok);display:inline-block}
/* 「⋯」展开的危险操作菜单 —— 从标题行下方滑出, 视觉上明确是"第二层",
   不会和上面的常用操作混淆 */
.cur-menu{margin-top:12px;border-top:1px solid var(--line-soft);padding-top:8px;
  display:flex;flex-direction:column;gap:2px}
.cur-menu button{display:flex;align-items:center;gap:8px;width:100%;text-align:left;
  background:none;border:none;padding:11px 10px;border-radius:var(--r-sm);
  font-size:14px;color:var(--text);min-height:44px;
  transition:background-color var(--dur-1) var(--ease)}
@media (hover:hover){.cur-menu button:hover{background:var(--hover-soft)}}
.cur-menu button:active{background:var(--surface-2)}
.cur-menu .cur-danger{color:#c33a36}
@media (hover:hover){.cur-menu .cur-danger:hover{background:var(--danger-soft)}}
/* 内容区与滚动区:滚到边界不要把滚动传给页面(否则整页会被拖动) */
/* 内容区与滚动区:滚到边界不要把滚动传给页面(否则整页会被拖动)。
   overflow-y:auto 会让 overflow-x 变成 auto,内容稍宽就能横向拖动 → 基本资料左右跳动,
   这里显式锁死横向:只许上下滚。 */
.d-body{flex:1;overflow-y:auto;overflow-x:hidden;overscroll-behavior:contain;touch-action:pan-y;
  padding:18px 22px}
.d-sec{margin-bottom:20px}
/* 兜底:详情区里任何元素都不许比容器宽(图片 / 标签 / 链接) */
#dBody img,#dBody a,#dBody .chip,#dBody .ava-row{max-width:100%}
.d-sec h3{font-size:13px;color:var(--muted);font-weight:600;margin-bottom:10px;
  display:flex;align-items:center;gap:6px}
/* grid 项默认 min-width:auto,长内容会把列撑宽从而顶出容器 → 必须归零 */
.info-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px 16px}
/* 抽屉基本资料左列的「可选字段组」（负责人 / 协作人）。
   为什么要包一层：负责人和协作人的**有无是变化的**，直接依次 push 会让
   auto-flow 把后面的「客户类型」推到不确定的列上 —— 实测就会掉到下一行左列，
   与「下次跟进」不在同一列（用户反馈的"没竖排对齐"）。
   包成一格后它只占左列，右列永远是客户类型。
   内部再套一层单列 grid，多个可选字段之间保持与外部一致的间距。 */
/* 「负责人(+协作人) | 客户类型」这一组：横跨整行、内部两列。
   为什么不用 .info-grid 的 auto-flow 直接排（试过三次都不行）：
   负责人/协作人的有无是变化的，auto-flow 只按 DOM 顺序往前推进，
   多余项会被丢到**下一行**，不会回填空位 —— 实测客户类型总是落到
   下一行左列（x53-202, top=341），与「下次跟进」不同列。
   包成整行容器后行数确定，客户类型就稳定在右列、与下次跟进同列。 */
.info-pair-row{grid-column:1/-1;display:grid;grid-template-columns:1fr 1fr;
  gap:10px 16px;min-width:0}
.info-pair-left,.info-pair-right{min-width:0;display:grid;
  grid-template-columns:1fr;gap:10px;align-content:start}
.info-pair-right:empty{display:none}
/* 客户类型固定在**右列**（用户 2026-10-06 要求：与「下次跟进」同列、
   纵向对齐；负责人右侧若被协作人占了，就落到联系人右侧，仍在右列）。

   ⚠️ 为什么必须显式写 grid-column:2：
   .info-grid 是 auto-flow，只按 DOM 顺序往前推进，**不会回填**空位。
   实测不加这条时（有 电话/下次跟进/联系人/负责人/类型）：</br>
     top=245  电话(左) | 下次跟进(右)
     top=286  联系人(左) | 负责人(右)
     top=341  客户类型(左)     ← 被推到下一行左列
   加上 grid-column:2 后，浏览器会在**当前行**给它找右列的位置，
   于是它回到 top=286 的右列，与「下次跟进」同列。 */
.info-item{min-width:0}
/* 抽屉信息格里的地址：放宽成可折行、最多两行。
   ⚠️ 为什么要覆盖：.addr-link（L1000）是给**表格/列表的窄列**写的，
   带 white-space:nowrap + text-overflow:ellipsis（桌面端窄列一行省略号），
   手机媒体查询里还有一句 max-width:120px。这两条都命中了抽屉里
   **全宽**的地址 → 实测内容 480px 被塞进 315px，截掉 165px。
   而抽屉恰恰是"唯一能把地址看全的地方"（列表里已经截断）。
   这里用两个类压过一个类，不用 !important；表格/列表不受影响。
   两行截断而不是无限换行：地址可能很长，格子不该被撑成三四行。 */
.info-item .addr-link{display:-webkit-box;white-space:normal;
  max-width:100%;overflow:hidden;text-overflow:ellipsis;
  -webkit-line-clamp:2;-webkit-box-orient:vertical;
  overflow-wrap:anywhere;word-break:break-all;
  vertical-align:baseline;font-size:13.5px;line-height:1.5}
.info-item.full{grid-column:1/-1}
.info-item .k{font-size:11px;color:var(--muted);margin-bottom:2px}
.info-item .v{font-size:13.5px;word-break:break-all;overflow-wrap:anywhere}
.d-notes{background:var(--surface-2);border-radius:var(--r-md);padding:12px 14px;font-size:13px;color:var(--text-2);
  white-space:pre-wrap;overflow-wrap:anywhere;margin-top:4px}
.f-item{position:relative;border-left:2px solid #e3e9fb;padding:0 0 16px 16px;margin-left:6px}
.f-item:before{content:"";position:absolute;left:-5px;top:4px;width:8px;height:8px;border-radius:50%;
  background:var(--primary)}
.f-item:last-child{padding-bottom:4px}
.f-item .f-top{display:flex;align-items:center;gap:8px;margin-bottom:4px}
.f-method{font-size:11px;background:#eef1fb;color:#5a6bd8;border-radius:4px;padding:1px 7px}
.f-op{font-size:11px;color:var(--muted)}
.f-del{margin-left:auto;color:#c6ccda;cursor:pointer;font-size:15px;line-height:1}
.f-del:hover{color:var(--danger)}
.f-content{font-size:13.5px;white-space:pre-wrap;overflow-wrap:anywhere;color:#333c52}
.f-time{font-size:11px;color:var(--faint);margin-top:3px}
/* 安卓软键盘弹出时:VisualViewport 会缩,但 layout viewport 不变,
   导致固定在底部的输入区被键盘盖住。JS 会把键盘高度写进 --kb,
   下面两处底部条据此抬高自己(值来源见 initKeyboardInset)。 */
.f-form{border-top:1px solid var(--border);padding:14px 22px;
  padding-bottom:calc(14px + env(safe-area-inset-bottom,0px));
  transition:padding-bottom var(--dur-1) var(--ease)}
body.kb-on .f-form{padding-bottom:calc(var(--kb,0px) + 14px)}
.f-form textarea{min-height:64px;resize:vertical}
/* 第一百零一轮：flex-wrap 从窄屏断点提到基础样式 —— 跟进人选择器删掉后
   这排只剩「方式 / 语音 / 保存」，正常都放得下；但长屏(20:9)小宽度或
   字体放大时不能把按钮挤成折叠，放不下就整颗换行（.rn 的教训同款：
   被裁掉比换行更伤）。 */
.f-form .row{display:flex;flex-wrap:wrap;gap:8px;margin-top:8px}
.f-form .row select{flex:1;min-width:120px}
/* 1.14.0：短语行从「横滑」改成「换行」。
   原来是 overflow-x:auto —— 横滑的前提是"看得见还有更多"，可这行没有
   渐隐提示，390px 屏上第 4 个短语直接被裁掉一半，用户根本不知道后面还有。
   现在常显 4 个 + 「更多」，展开后 8 个，两种状态都换行放得下，
   横滑这条路径整个不要了（touch-action/scrollbar 隐藏一并删掉）。 */
.quick-row{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:2px}
.q-chip{flex:none;padding:6px 12px;border-radius:16px;border:1px dashed #d7def0;
  background:#fafbff;color:var(--text-2);font-size:12px;white-space:nowrap}
.q-chip:hover{border-color:var(--primary);color:var(--primary);border-style:solid}
.q-chip:active{background:var(--primary-soft);border-color:var(--primary);color:var(--primary)}
/* 1.14.0：「更多 ▾」—— 浅底实线边框，跟虚线的内容短语区分开
   （虚线 = 填内容，实线 = 控制显隐，两类别混淆） */
.q-chip.q-more{border-style:solid;border-color:#e2e7f5;color:var(--muted);background:#fff}
.q-chip.q-more:hover{border-color:var(--primary);color:var(--primary)}
#btnMic.rec{background:#fdeaea;border-color:var(--danger);color:var(--danger);
  animation:pulse 1.1s infinite}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.55}}

/* ---- 阶段下拉(详情里直接改) ---- */
.stage-sel{border:none;border-radius:20px;padding:5px 12px;font-size:12.5px;font-weight:600;
  outline:none;cursor:pointer;-webkit-appearance:none;appearance:none;
  max-width:130px;font-family:inherit}
.stage-sel option{background:#fff;color:#333}
/* 「是否在公海展示」勾选框（2026-10-07 加）。
   放标题行里，必须能撑住窄屏：手机 390px 时店名占满、阶段标签紧随，
   这个开关再挤上去就会换行把标题顶开。所以 max-width 之外还给了
   flex:none + 窄屏缩字号，而不是让它去压缩店名的空间。 */
.pool-sw{display:inline-flex;align-items:center;gap:4px;font-size:11.5px;
  color:var(--muted);white-space:nowrap;cursor:pointer;flex:none;
  padding:3px 8px;border-radius:20px;background:var(--bg-soft,#f2f4f8)}
.pool-sw input{width:auto;margin:0;flex:none}
.pool-sw:hover{color:var(--text)}
@media (max-width:420px){.pool-sw{font-size:11px;padding:2px 6px}}
/* ---- 下次拜访快捷选择 ---- */
.nx-row{display:flex;align-items:center;gap:6px;margin-top:9px;flex-wrap:wrap}
.nx-col{flex-direction:column;align-items:stretch}
.nx-label{font-size:11.5px;color:var(--muted);margin-right:2px}
/* 顶栏/更多抽屉里的外观图标: 尺寸跟随上下文, 颜色跟随文字 */
[data-theme-icon]{display:inline-flex;align-items:center;justify-content:center}
[data-theme-icon] svg{width:16px;height:16px}
.sheet-item .theme-ico svg{width:24px;height:24px;stroke:var(--primary)}
.sheet-item .theme-ico{display:inline-flex}
.nx-chip{border:1px solid var(--border);background:#fff;color:var(--text-2);border-radius:16px;
  padding:7px 13px;font-size:12.5px;cursor:pointer;font-family:inherit;line-height:1.35;
  min-height:32px;display:inline-flex;align-items:center;justify-content:center;
  box-shadow:var(--sh-1)}
.nx-chip:hover{border-color:#c5d0f7;color:var(--primary)}
.nx-chip.on{background:var(--primary);border-color:var(--primary);color:#fff;
  box-shadow:0 2px 8px rgba(79,110,247,.24)}
.nx-chip.nx-danger.on{background:var(--danger);border-color:var(--danger);
  box-shadow:0 2px 8px rgba(239,91,119,.22)}
.nx-chip.nx-danger{color:var(--danger)}
.nx-chip.nx-danger:hover{border-color:#ffd7de;background:var(--danger-soft)}
.nx-date{width:auto;min-width:130px;padding:6px 9px;font-size:12.5px}.nx-col .nx-chip{padding:11px 13px;font-size:13.5px;text-align:left;justify-content:flex-start}
/* ---- 电话 / 下次跟进 / 系统记录 ---- */
.tel-link{color:#0f9d6e;font-size:13px;text-decoration:none}
.tel-link:hover{text-decoration:underline}
/* 无权查看完整号码:弱化展示,明确不是"没录电话"而是"被保护了" */
.phone-masked{color:#98a2b3;font-size:13px;font-variant-numeric:tabular-nums;cursor:help}
/* 公海池「认领并查看」:小尺寸,跟在打码号码后面,不抢主视觉 */
/* min-height 36px:公海池每一行都有这个「认领」,原来只有 22px。
   它是会改变归属的动作,点错代价比一般按钮大,更不该难点。 */
.btn-claim{margin-left:6px;padding:6px 12px;min-height:36px;font-size:12.5px;
  display:inline-flex;align-items:center;line-height:1.3;border:1px solid var(--primary);
  background:#fff;color:var(--primary);border-radius:10px;cursor:pointer;white-space:nowrap;vertical-align:middle}
.btn-claim:hover{background:var(--primary);color:#fff}
.nf-val{cursor:pointer}
/* 抽屉「下次跟进」现在**只显示、不可点**（2026-10-07 去重），
   但上面那条 cursor:pointer 是当初给"点一下改期"入口加的 ——
   留着的话鼠标放上去仍是手型，看起来还能点。
   为什么用作用域覆盖而不是删上面那条：.nf-val 还被 nfHtml() 用在列表行副标题
   （那处本来也不可点），删全局规则会连带改到列表观感，属于本次需求之外。 */
.info-item .nf-val{cursor:default}
/* 门店编号那一格（2026-10-08 乙件）。
   值可点（就地编辑）→ cursor:pointer 提示；
   后面的 ⧉ 是复制按钮，**跟值分开两个节点** —— 理由跟甲件一样：
   值是"点一下改"，⧉ 是"点一下复制"，两个动作共用一个节点
   必然要猜用户想干嘛（历史上抽屉标题就踩过这个坑）。 */
.sno-val{cursor:pointer;border-bottom:1px dashed var(--border)}
.sno-val.sno-empty{color:#b6bdc9;border-bottom-style:dotted}
.sno-copy{margin-left:6px;color:var(--muted);cursor:pointer;font-size:13px;
  vertical-align:middle}
.sno-copy:hover{color:var(--primary)}
/* 「逾期 / 今天」这两个小字是**催办信号** —— 恰恰是最不能看不清的。
   原来 --danger #ef5b77 只有 3.26:1, 橙 #e08a00 只有 2.69:1(全站最低)。
   越紧急的状态越糊, 这在跟单场景里是要出事的。色相不变, 只压亮度到过线。 */
.nf-overdue{color:#d43a58;font-weight:600}    /* 4.62:1 */
.nf-today{color:#a66300;font-weight:600}      /* 4.77:1 */
.f-item.f-sys{border-left-color:#e6e9f2}
.f-item.f-sys:before{background:#c3cad9}
.f-item.f-sys .f-content{color:#5f6b82;font-size:12.5px}
.f-sys-tag{background:#f0f2f7;color:#5f6b82}
.tag-edit{cursor:pointer;color:#aab2c4;font-size:12px;border:1px dashed var(--border);
  border-radius:12px;padding:2px 9px}
.tag-edit:hover{color:var(--primary);border-color:var(--primary)}
/* ---- 待跟进 / 提醒条 ---- */
.due-btn.on{background:var(--primary);border-color:var(--primary);color:#fff}
.remind-bar{display:flex;align-items:center;gap:10px;background:#fff8e8;border:1px solid #ffe2ab;
  color:#8a6100;border-radius:10px;padding:9px 13px;font-size:12.5px;margin-bottom:12px}
.remind-bar .btn{flex:none}

/* ---- 弹窗 ---- */
.modal-mask{position:fixed;inset:0;background:var(--scrim);z-index:60;
  display:flex;align-items:center;justify-content:center;padding:20px;
  animation:fade var(--dur-2) var(--ease-out)}
.modal{background:var(--card);border-radius:var(--r-lg);position:relative;width:min(560px,92vw);max-width:100%;
  max-height:88vh;max-height:88dvh;
  display:flex;flex-direction:column;animation:pop var(--dur-3) var(--ease-spring)}
@keyframes pop{from{transform:scale(.94);opacity:0}to{transform:scale(1);opacity:1}}
.m-head{display:flex;align-items:center;padding:18px 22px 0;gap:8px}
.m-head h2{font-size:17px;flex:1}
/* 设置页「管理员只读」小标签 */
.ro-tag{font-size:11px;font-weight:500;color:var(--muted);background:var(--hover);
  border-radius:6px;padding:2px 7px;margin-left:8px;vertical-align:2px;white-space:nowrap}
/* 确认框正文与错误行 */
.cf-p{font-size:13px;color:var(--text-2);line-height:1.6;margin:6px 0 0}
/* 确认框里的可勾选项（目前只有「释放时顺便标没意向」用）。
   整行可点 —— 手机上只有那个 16px 的小方块能点的话很容易点空。
   勾选框本身用 accent-color 跟主色走，免得默认的蓝色跟界面不搭。 */
.cf-check{display:flex;gap:9px;align-items:flex-start;margin:14px 0 0;
  padding:10px 12px;border-radius:9px;background:var(--bg-2);cursor:pointer}
.cf-check input{margin:2px 0 0;width:16px;height:16px;flex:none;
  accent-color:var(--primary);cursor:pointer}
.cf-check span{font-size:13px;color:var(--text);line-height:1.5}
.cf-check i{font-style:normal;font-size:12px;color:var(--text-2)}
.cf-err{font-size:12px;color:#c33a36;margin-top:6px;min-height:16px}
/* 没意向原因弹窗（第七版）。六个快捷选项分两组摆开——它们不是随便排的，
   「我们不做」是范围问题、「这家不行」是这家店的问题，语义不同，
   分组标题直接写在界面上，不用去猜哪个按钮是哪一类。
   组内按扫街遇到频率排：已有竞品在用排第一（库里 ctype 字段就是它）。 */
.inv-rs{display:flex;flex-wrap:wrap;gap:6px;margin:8px 0 10px}
.inv-g{flex:0 0 100%;font-size:11.5px;font-weight:700;color:var(--muted);
  margin-top:6px;padding-top:7px;border-top:1px dashed var(--border)}
.inv-g0{margin-top:0;padding-top:0;border-top:none}
.inv-g i{font-weight:400;font-style:normal;opacity:.75;margin-left:3px}
.inv-r{font-size:12px;line-height:1.4;padding:6px 10px;border-radius:16px;
  border:1px solid var(--border);background:var(--surface-2,rgba(0,0,0,.03));
  color:var(--text);cursor:pointer;text-align:left}
.inv-r:hover{border-color:var(--primary);color:var(--primary)}
/* 第一项是扫街时最常点的（已有竞品在用），给个底色让它一眼能找到 */
.inv-r.on{background:var(--primary-soft,rgba(23,105,255,.09));
  border-color:rgba(23,105,255,.32)}
#invNote{width:100%;resize:vertical;font-size:13px;line-height:1.6}
.cf-tag{font-size:11px;font-weight:600;padding:2px 8px;border-radius:20px;
  background:var(--primary-soft);color:var(--primary);flex:none}
.cf-tag.is-danger{background:var(--danger-soft);color:#c33a36}
/* 「指派给谁」弹窗里的成员列表（S5，1.19.9）。
   为什么点选而不是手打名字：成员名差一个字就是另一个人，
   而 prompt() 里手打没法给你看"库里到底有谁"。
   一行一个成员，整行可点（热区 ≥44px 手指才点得准），
   选中态靠 .on 高亮 + 右边一个 ✓。 */
.asg-list{max-height:44vh;overflow-y:auto;margin-top:2px;
  border:1px solid var(--border);border-radius:9px}
.asg-i{display:flex;align-items:center;gap:9px;padding:9px 11px;cursor:pointer;
  font-size:13.5px;color:var(--text-2);border-bottom:1px solid var(--border)}
.asg-i:last-child{border-bottom:none}
.asg-i:hover{background:var(--surface-2, #f2f5fb)}
.asg-i .asg-n{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap}
/* 选中态：整行染成主色浅底 + 名字加粗 + ✓ 显形。
   三个一起给是因为只靠颜色的话，色弱用户在深色模式下分辨不出。 */
.asg-i.on{background:var(--primary-soft);color:var(--text);font-weight:600}
.asg-i .asg-ok{flex:none;color:var(--primary);font-weight:700;visibility:hidden}
.asg-i.on .asg-ok{visibility:visible}
/* 上传进度环 */
.up-ring{width:44px;height:44px;margin:0 auto;border-radius:50%;
  border:3px solid var(--line);border-top-color:var(--primary);
  animation:upSpin .8s linear infinite}
@keyframes upSpin{to{transform:rotate(360deg)}}
/* 关闭按钮: 原来只有约 26×28, 手指点不准 —— 不到 44px 的触摸目标
   在苹果的人机界面规范里是明确不允许的。用 min-width/height 撑到 44,
   视觉大小不变(靠 padding 撑开), 只是"可点区域"变大。 */
.m-close{font-size:20px;color:var(--muted);background:none;padding:4px 8px;
  min-width:44px;min-height:44px;display:inline-flex;align-items:center;
  justify-content:center;border-radius:var(--r-sm)}
@media (hover:hover){.m-close:hover{color:var(--text);background:var(--surface-2)}}
/* 右下角小胶囊:告诉用户"现在是本地快照,真数据马上到" */
.snap-tip{position:fixed;right:14px;bottom:calc(72px + env(safe-area-inset-bottom,0px));
  z-index:45;display:none;align-items:center;gap:6px;background:var(--card);
  color:var(--text-2);font-size:12px;padding:7px 12px;border-radius:20px;
  box-shadow:var(--sh-2);border:1px solid var(--border)}
.snap-dot{width:7px;height:7px;border-radius:50%;background:var(--primary);
  animation:snapPulse 1.2s ease-in-out infinite}
@keyframes snapPulse{0%,100%{opacity:.35}50%{opacity:1}}
/* 下拉刷新指示器:跟手位移 + 到阈值转 180°,松手变转圈 */
.pull-ind{position:fixed;top:calc(env(safe-area-inset-top,0px) + 10px);left:0;right:0;
  z-index:40;display:flex;justify-content:center;pointer-events:none;
  opacity:0;transition:opacity var(--dur-2) var(--ease)}
.pull-ind.on{opacity:1}
.pull-ind .pi-box{width:34px;height:34px;border-radius:50%;background:var(--card);
  box-shadow:var(--sh-2);display:flex;align-items:center;justify-content:center;
  font-size:16px;color:var(--primary);box-sizing:border-box;
  transition:transform var(--dur-2) var(--ease)}
/* 转圈时把箭头换成描边圆环:只有顶部一段是主色,转起来才是"在加载" */
.pull-ind .pi-box.spin{border:2.5px solid var(--line);border-top-color:var(--primary);
  animation:upSpin .7s linear infinite}
/* 选点时的地名联想浮层:盖在地图上而不是挤开地图,免得点着点着位置就跳了 */
.poi-pop{position:absolute;top:100%;left:0;right:0;z-index:20;display:none;
  max-height:232px;overflow-y:auto;overscroll-behavior:contain;
  background:var(--card);border:1px solid var(--border);
  border-radius:0 0 var(--r-md) var(--r-md);box-shadow:var(--sh-2)}
.poi-pop.on{display:block}
.poi-item{padding:10px 16px;cursor:pointer;font-size:13px;
  border-bottom:1px solid var(--line);display:flex;align-items:baseline;gap:8px}
.poi-item:last-child{border-bottom:none}
.poi-item .nm{font-weight:600;flex:none;max-width:58%;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.poi-item .ad{color:var(--muted);font-size:12px;flex:1;min-width:0;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.poi-item .src{font-size:10.5px;color:var(--faint);flex:none}
.poi-empty{padding:10px 16px;font-size:12px;color:var(--muted)}
@media (hover:hover){.poi-item:hover{background:var(--surface-2)}}
/* 个人信息里的头像配色:选中态靠描边 + 轻微放大,不靠颜色深浅(深色下不好判) */
.pf-dot{width:32px;height:32px;border-radius:50%;padding:0;flex:none;cursor:pointer;
  border:2px solid transparent;box-shadow:0 0 0 1px var(--line) inset;
  transition:transform var(--dur-1) var(--ease),border-color var(--dur-1) var(--ease)}
.pf-dot.on{border-color:var(--text);transform:scale(1.1)}
/* 个人信息弹窗：底部「取消 / 保存」靠左（2026-10-04 用户要求）。
   ⚠️ **只能挂 .pf-modal 限定，不能改全局 .m-foot** —— 全站 22 处弹窗共用
   .m-foot，默认是 justify-content:flex-end（靠右）。这是项目的通用约定：
   弹窗是"问你一件事"，按钮靠右跟视线落点一致。
   个人信息页不一样，它是一张**表单**，上面的输入框、头像色块全部左对齐，
   按钮靠右会和上面的内容各说各话。所以这里单独左对齐，
   让按钮的起点和输入框的起点在同一条竖线上。 */
.pf-modal .m-foot{justify-content:flex-start}
/* 个人信息页里的「修改密码」折叠区（1.9.2）。
   默认收着 —— 改密码几个月一次，显示名/头像色是天天看的，
   铺开三个密码框会让这一页一半篇幅都是 rarely 用的东西。
   分隔线用 border-top 而不是加一条独立标题，理由和 1.9.0 收表格列一样：
   多一个元素就多一处要维护的视觉噪音。 */
.pf-pwd{margin-top:14px;border-top:1px solid var(--border);padding-top:4px}
.pf-pwd-h{display:flex;align-items:center;gap:6px;width:100%;
  padding:10px 0;background:none;border:none;box-shadow:none;
  font-size:13.5px;color:var(--text);cursor:pointer;text-align:left}
.pf-pwd-h:hover{color:var(--primary)}
/* 箭头朝下 = 「点开」。rotate 180° 翻过来，transition 让它是转过去的，
   不是跳过去的 —— 这种小细节决定了「顺手」还是「卡」。 */
.pf-pwd-arrow{margin-left:auto;font-size:17px;line-height:1;color:var(--muted);
  transition:transform var(--dur-1) var(--ease)}
.pf-pwd-h[aria-expanded="true"] .pf-pwd-arrow{transform:rotate(90deg)}
.pf-pwd-box{padding:2px 0 4px}
/* 确认按钮不撑满整行 —— 它是这一小段的收尾动作，占满宽度会显得
   像页面底部的主按钮，而页面底部已经有一个「保存」了。 */
.pf-pwd-save{margin-top:10px}
@media (hover:hover){.pf-dot:hover{transform:scale(1.08)}}
/* 弹窗内容区: 必须显式 min-height:0 —— 它是 flex 列容器的子项, 默认
   min-height:auto 会撑到内容实际高度而不肯收缩, 结果弹窗被 max-height
   截断、底部内容既看不见也滚不动(设置页就是这个毛病)。 */
.m-body{padding:18px 22px;overflow-y:auto;overflow-x:hidden;overscroll-behavior:contain;
  touch-action:pan-y;flex:1 1 auto;min-height:0}
/* 弹窗底部按钮同样避开手机底部安全区 */
/* 订单弹窗的 foot：主操作 + 关闭横向排，动作按钮优先占宽 */
.o-foot{display:flex;flex-wrap:wrap;gap:8px;align-items:center}
.o-foot .btn{flex:1 1 auto;min-height:38px;margin:0}
.o-foot .btn:first-child{flex:0 0 auto}   /* 「关闭」不抢宽度 */
.m-foot{padding:0 22px 20px;padding-bottom:calc(20px + env(safe-area-inset-bottom,0px));
  display:flex;gap:10px;justify-content:flex-end;
  transition:padding-bottom var(--dur-1) var(--ease)}
body.kb-on .m-foot{padding-bottom:calc(var(--kb,0px) + 12px)}
/* 悬浮条层级(z-index 55)高于弹窗(50), 但它固定在底部导航栏之上,
   而弹窗是垂直居中的 —— 实测两者不会重叠(手机上弹窗底 808, 悬浮条顶 743,
   且 .m-foot 的按钮排在 foot 顶部, 更靠上)。所以这里不需要额外让位,
   保持弹窗内容区完整高度。若以后弹窗改高了再回来加。 */
/* 间距 12→8：表单字段多，每处省 2px 累计就是几十 px。
   标签间距也在下面全局收紧（原来写成 .form-grid>div>label，打不到
   .type-biz 内部的 label —— 那两处就还是旧间距，看着不齐）。
   注意 input 高度不能再压：手机上字号 16px 是为了不触发浏览器自动放大。 */
.form-grid{display:grid;grid-template-columns:1fr 1fr;gap:8px 14px;
  align-items:start}
.form-grid .full{grid-column:1/-1}
/* 图片独立占整行（2026-10-06）。
   原来它挤在右列（.pic-side）：右列最窄只有 136px（360px 视口），逼得左列
   必须把「客户类型/在用系统/门店品类/连锁」四个字段竖着叠起来（.type-biz），
   两侧高度差太大 —— 左侧每个字段只占半宽、右半全空，图片自己孤悬一整列。
   实测 390px：内容区 960px 要滚 1.54 屏，footer 压住「协作人」和「备注」。
   挪出来独立占一行后两侧不再互相牵制，新建/编辑的 DOM 也统一了。
   ⚠️ 78px 是算过的：78px 方块放得下 22px 的删除点视觉 + 40px 热区，
   所以不用再绕「52px 缩略图 + 点开大图墙去删」那套中转 —— 52px 里塞 40px
   热区几乎盖满整图，走动中误触删除的概率很高（用户踩过）。 */
.pic-full-box{display:flex;flex-wrap:wrap;align-items:flex-start;gap:8px;
  margin-top:6px}
.pic-full-box .img-add{width:78px;height:78px;flex:0 0 78px;margin-top:0}
/* 次要信息折叠块 */
.form-more{margin-top:2px}
/* ⚠️ 必须跨整行。实测（2026-10-06）：
   <details> 在 .form-grid 里是**普通单元格**，不会像 .full 那样跨列 ——
   它只拿到 146px（外层两列之一），内部的子 .form-grid 再两列分，
   每列只剩 66px → 中文一个字一行、下拉框只剩箭头、输入框点不到。
   原来字段少时不明显，扩到 5 个字段后暴露。
   用 CSS 固定跨列，比在 HTML 上加 .full 稳（别处复用 .form-more 也不会漏）。 */
.form-grid>.form-more{grid-column:1/-1;min-width:0}
.form-more .form-grid>div{min-width:0}
.form-more>summary{cursor:pointer;font-size:12px;color:var(--primary);
  padding:6px 0;list-style:none;user-select:none}
.form-more>summary::-webkit-details-marker{display:none}
.form-more>summary::before{content:"▸ ";font-size:10px}
.form-more[open]>summary::before{content:"▾ "}
.form-more>summary:hover{text-decoration:underline}
/* details 内部的 .form-grid 是嵌套的，会按外层 grid 的列宽算 ——
   结果日期框被压得很窄。这里明确它自己也是两列，且间距略小。 */
.form-more .form-grid{display:grid;grid-template-columns:1fr 1fr;
  gap:8px 14px}
@media(max-width:699px){
  /* 手机上详情区仍是两列（和外层表单一致），日期框才够宽 */
  #mk .form-more .form-grid{grid-template-columns:1fr 1fr}
}
#dupTip{margin-top:8px;padding:9px 12px;border-radius:9px;background:#fff7ed;
  border:1px solid #ffe0b2;font-size:12px;color:#8a5a00;line-height:1.7;
  overflow-wrap:anywhere}
#dupTip .dup-row{line-height:1.6;margin:2px 0;white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis}
#dupTip .dup-row a{color:var(--primary);text-decoration:none;font-weight:600}
#dupTip .dup-more{font-size:11px;color:#a67c4e;margin:3px 0}
#dupTip .dup-foot{margin-top:5px;font-size:11px;color:#8a5a00}
/* 连锁提示（2026-10-07 加）。跟 #dupTip 刻意**长得不一样**：
   dupTip 是「撞单了，要你处理」的警告（橙底），连锁提示只是「供参考」的
   信息（灰字）—— 两者语气不同，做成同一个样子会让人以为连锁提示也是个
   待处理警告。而且它就跟 dupTip 上下挨着出现，两块同色橙底会糊成一片。
   同理它不带底色：有底色的块在表单里是"需要注意"，而这条只是顺带说一句。 */
#eChainHint{margin-top:7px;font-size:11.5px;color:var(--muted);
  line-height:1.7;overflow-wrap:anywhere}
#eChainHint b{color:var(--text);font-weight:600}
#eChainHint .ch-fix{color:var(--primary);cursor:pointer;margin-left:5px;
  border-bottom:1px dashed var(--primary)}
#eChainHint .ch-manual{color:var(--primary);font-weight:600}
.m-err{color:var(--danger);font-size:12px;min-height:16px;margin-top:10px;
  font-weight:600}
/* 出错的字段描红，让用户一眼知道该改哪一个（而不是只看一句提示） */
.field-err{border-color:var(--danger) !important;
  box-shadow:0 0 0 3px rgba(224,62,62,.12) !important}
/* ============ 成员管理弹窗(2026-10-02 重构) ============
   原来:组织说明大段文字 + 组织列表 + 成员列表 + 添加区,全塞在一个滚动区里,
        成员行一行 6 个控件。实测内容 1909px / 可见 730px,要滚 2.6 屏。
   现在:三块分 Tab(成员/组织/权限说明),成员行两行式,低频操作收进「⋯」菜单。 */
.mtabs{display:flex;gap:4px;padding:14px 22px 0;border-bottom:1px solid var(--border)}

.mtab{background:none;border:none;border-bottom:2px solid transparent;font-size:13px;
  font-weight:600;color:var(--muted);padding:7px 12px 9px;cursor:pointer;
  transition:color var(--dur-1) var(--ease),border-color var(--dur-1) var(--ease)}
.mtab:hover{color:var(--text-2)}
.mtab.on{color:var(--primary);border-bottom-color:var(--primary)}
.mtab-n{display:inline-block;min-width:16px;font-size:10.5px;font-weight:600;
  background:var(--surface-2);color:var(--muted);border-radius:8px;padding:1px 5px;
  margin-left:3px;vertical-align:1px}
.mtab.on .mtab-n{background:var(--primary-soft);color:var(--primary)}

.mtab-pane.hidden{display:none}
/* 成员行:第一行是身份 + 角色 + 菜单,信息用 chip 呈现 */
.member-row{padding:11px 2px;border-bottom:1px solid var(--border)}
.member-row:last-child{border-bottom:none}
.member-row.off{opacity:.55}

/* 菜单相对**这一行**定位。以前 .mr-l1 没有 position,菜单只能往外找定位祖先
   （最后是弹窗），于是它的落点跟按钮对不上：列表靠下的成员点了「⋯」，
   菜单会整块掉到屏幕外，手机上根本点不到菜单项（t_org_ui.py 卡在
   「调整组织」就是撞上这个）。给它定位上下文，菜单才真正长在这一行下面。 */
.mr-l1{display:flex;align-items:center;gap:9px;position:relative}

.member-row .del{color:#c6ccda;cursor:pointer;font-size:16px;padding:2px 6px;border-radius:6px;
  transition:color var(--dur-1) var(--ease),background-color var(--dur-1) var(--ease)}
.member-row .del:hover{color:var(--danger);background:var(--danger-soft)}

.mr-info{display:flex;flex-direction:column;gap:3px;min-width:0;flex:1 1 auto;overflow:hidden}
.mr-name{display:flex;align-items:center;gap:5px;min-width:0}
.mr-name b{font-size:14px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mr-self{font-size:10px;background:var(--primary);color:#fff;border-radius:5px;
  padding:1px 5px;flex:none}
.mr-sub{display:flex;gap:5px;flex-wrap:wrap;min-height:15px}

.mr-chip{font-size:11px;color:var(--muted);background:var(--surface-2);
  border-radius:5px;padding:1.5px 6px;white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis;max-width:150px}
.mr-chip-a{background:var(--primary-soft);color:#4a5680}
.mr-chip-n{background:none;color:#c0c6d2;padding-left:0}

.mr-off{font-size:11px;color:var(--danger);background:var(--danger-soft);
  border-radius:5px;padding:1.5px 6px}
/* 「⋯」菜单:低频操作(别名/组织/密码/停用/删除)收进来 */
.btn-ico{background:none;border:none;color:var(--muted);font-size:17px;line-height:1;
  padding:3px 7px;border-radius:7px;cursor:pointer;flex:none;
  transition:background-color var(--dur-1) var(--ease),color var(--dur-1) var(--ease)}
.btn-ico:hover{background:var(--surface-2);color:var(--text)}

/* top:100% = 贴在行的下沿（默认往下弹）；放不下时 JS 加 .up 翻到上方 */
.mem-menu{position:absolute;right:22px;top:100%;margin-top:4px;z-index:5;background:var(--card);
  border:1px solid var(--border);border-radius:var(--r-md);box-shadow:0 8px 24px rgba(20,30,60,.13);
  padding:5px;min-width:132px}
.mem-menu.up{top:auto;bottom:100%;margin-top:0;margin-bottom:4px}
.mem-menu a{display:block;font-size:12.5px;color:var(--text-2);padding:7px 11px;
  border-radius:6px;cursor:pointer;white-space:nowrap;
  transition:background-color var(--dur-1) var(--ease)}
.mem-menu a:hover{background:var(--surface-2);color:var(--text)}
.mem-menu a.danger{color:var(--danger)}
.mem-menu a.danger:hover{background:var(--danger-soft)}
.mem-empty{color:#b6bdc9;padding:10px 0;font-size:13px}
/* 添加成员:独立卡片,字段竖排带标签,不再和列表混在一起 */
.add-mem-card{margin-top:16px;background:var(--surface-2);border-radius:var(--r-md);padding:14px}
.add-mem-t{font-size:13px;font-weight:600;margin-bottom:11px}
.add-mem-t span{font-size:11.5px;font-weight:400;color:var(--muted);margin-left:6px}
.add-mem-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px}

.amg{display:flex;flex-direction:column;gap:4px;min-width:0}
.amg>span{font-size:11.5px;color:var(--muted)}
.amg input,.amg select{width:100%}

.add-mem-foot{display:flex;align-items:center;gap:10px;margin-top:12px}

.amh-tip{font-size:11.5px;color:var(--muted);flex:1;line-height:1.5}
.amh-tip em{font-style:normal;color:var(--text-2)}
/* 权限说明 Tab:角色卡片,替代原来那一大段 12.5px 文字 */
.role-cards{display:flex;flex-direction:column;gap:9px}
.role-card{background:var(--surface-2);border-radius:var(--r-md);padding:11px 13px}
.role-card p{font-size:12.5px;color:var(--text-2);line-height:1.6;margin:7px 0 0}

.role-note{margin-top:14px;font-size:12px;color:var(--muted);line-height:1.65;
  background:var(--primary-soft);border-radius:var(--r-md);padding:11px 13px}

.cf-cur{font-size:12px;color:var(--muted);margin-bottom:6px}
.cf-cur b{color:var(--text-2)}
/* 窄屏:添加区改单列,角色标签收窄给姓名让路 */
@media (max-width:520px){
  .add-mem-grid{grid-template-columns:1fr}
  .mtabs{padding-left:16px;padding-right:16px}
  .mem-menu{right:16px;min-width:124px}
  .role-sel,.role-tag{max-width:74px;font-size:11.5px;padding:4px 6px}
  .mr-l1{gap:7px}
  .mr-info{flex:1 1 auto;min-width:60px}
  .mr-chip{max-width:96px}
}

.role-sel,.role-tag{border:none;border-radius:7px;font-size:12px;font-weight:600;
  padding:5px 9px;outline:none;flex:none;font-family:inherit;
  max-width:104px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  transition:filter var(--dur-1) var(--ease)}
/* role-sel 现在是 button(点开弹选),role-tag 只是展示 */
button.role-sel{cursor:pointer}
button.role-sel:hover{filter:brightness(.95)}

.role-tag{cursor:default}
/* 组织:成员行里的组织下拉 / 标签,以及组织管理区的行 */
.org-sel,.org-tag{border:1px solid var(--border);border-radius:7px;font-size:11.5px;
  padding:4px 7px;outline:none;flex:none;background:var(--bg);color:var(--text-2)}
.org-tag{cursor:default;border-style:dashed;color:var(--muted)}
.org-row{display:flex;align-items:center;gap:8px;padding:6px 2px;font-size:12.5px;
  border-bottom:1px dashed var(--border)}
.org-row .del{color:#c6ccda;cursor:pointer;font-size:15px;padding:2px 6px;border-radius:6px}
.org-row .del:hover{color:var(--danger);background:var(--danger-soft)}
.org-n{color:var(--muted);flex:1}
.org-mgr{flex:none;font-size:11px;color:var(--primary);background:var(--primary-soft);
  border-radius:4px;padding:1px 6px;max-width:120px;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.org-lv{flex:none;font-size:10.5px;color:var(--muted);background:var(--surface-2);
  border-radius:4px;padding:1px 5px}
.role-tip{background:var(--primary-soft);border-radius:var(--r-md);padding:12px;font-size:12.5px;
  color:#4a5680;line-height:1.7;margin-bottom:12px}

/* 头像圆点。1.9.3 起只有个人信息弹窗在用（#pfAvatar 覆盖了尺寸）——
   顶栏那个 .me-box 连同 .me-name / .me-role 一起删了，
   身份信息搬进「关于」弹窗的 .ab-me（见 openAbout）。
   留着死样式的理由和 1.9.0 删 .top-stats 一样：
   以后有人会以为顶栏还有身份块，然后照着它加功能。 */
.me-avatar{width:26px;height:26px;border-radius:50%;color:#fff;font-size:12px;font-weight:600;
  display:inline-flex;align-items:center;justify-content:center}
.tab-num{display:inline-block;min-width:16px;height:16px;line-height:16px;border-radius:8px;
  background:var(--danger);color:#fff;font-size:10.5px;padding:0 4px;margin-left:5px;text-align:center}
.tab.active .tab-num{background:rgba(255,255,255,.35)}
/* 待派单提醒:橙色,跟普通的数量角标区分开 */
.tab-alert{background:#f59f00 !important}

/* ---- 安装订单(派单给实施员) ---- */
.order-list{display:flex;flex-direction:column;gap:12px}
.order-card{background:var(--card);border:1px solid var(--border);border-radius:var(--r-lg);padding:16px;
  cursor:pointer;
  transition:box-shadow var(--dur-2) var(--ease),border-color var(--dur-2) var(--ease),
             transform var(--dur-2) var(--ease-spring)}
.order-card:hover{border-color:var(--primary);box-shadow:0 4px 14px rgba(79,110,247,.1);
  transform:translateY(-1px)}
.order-card:active{transform:translateY(0) scale(.995)}
.order-card.od-done{background:#fafbfd}
/* 成交了还没派单 —— 用暖色描边提醒销售别忘派活 */
.order-card.od-todo{border-color:#f0b27a;background:#fffaf5}
/* 有人报了困难：卡片整块描红边 —— 派单人在列表里一眼能挑出来，
   不用每条点进去看对话。困难解决后安装人会再发一条进展，红边自动消掉。 */
.order-card.od-bad{border-color:#e08b8b;background:#fffafa}
.order-card.od-todo:hover{border-color:var(--danger)}
.o-top{display:flex;align-items:center;gap:8px;margin-bottom:8px}
.o-name{font-weight:600;font-size:15px;flex:1;min-width:0;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.o-addr{font-size:12.5px;color:#7a86a0;margin-bottom:8px;word-break:break-all}
.o-meta{display:flex;gap:14px;flex-wrap:wrap;font-size:12px;color:var(--muted)}
.o-act{display:flex;gap:8px;margin-top:12px;flex-wrap:wrap}
/* 状态徽标 */
.st{font-size:11.5px;padding:2px 9px;border-radius:5px;font-weight:600;white-space:nowrap}
/* 紧急程度标:窄一点、方一点,跟状态标签区分开 —— 「加急」和「待接单」
   摆在一起时要能一眼分辨哪个是催办的、哪个是状态。 */
.urg{display:inline-block;font-size:10.5px;line-height:1.5;padding:1px 6px;
  border-radius:4px;font-weight:700;white-space:nowrap;margin-right:5px;
  vertical-align:1px;flex:none}
.urg-mid{background:#fff2e0;color:#96550a;border:1px solid #f3d5a8}
.urg-hi{background:#fdeaea;color:#b3261e;border:1px solid #f6c9c6}
/* 状态标签配色: 逐一算过对比度(小字需 ≥4.5:1)。
   原来的色都偏亮, 在各自的浅底上只有 3.1~4.0:1 —— 数值本身是对的,
   但"看不清"会让用户误判订单状态, 在安装场景里是要出事的。
   下面这批色降低亮度、保持色相, 全部 ≥4.5:1。 */
.st-待接单{background:#fff2e0;color:#96550a}
.st-已接单{background:#e8f0ff;color:#3557bd}
.st-安装中{background:#e6f7ff;color:#0b7a94}
.st-已完成{background:#e6f7ec;color:#137f42}
.st-异常{background:#fdeaea;color:#c33a36}
.o-note{background:#f8f9fd;border-radius:8px;padding:9px 11px;font-size:12.5px;
  color:#4a5268;margin-top:8px;white-space:pre-wrap;overflow-wrap:anywhere}
.o-photos{display:flex;gap:6px;flex-wrap:wrap;margin-top:8px}
.o-photos img{width:60px;height:60px;object-fit:cover;border-radius:7px;border:1px solid var(--border)}
.o-log{border-left:2px solid #e3e9fb;padding:0 0 12px 12px;margin-left:4px;font-size:12.5px}
.o-log .t{color:#4a5268;overflow-wrap:anywhere}
.o-log .m{font-size:11px;color:#b0b8c9;margin-top:2px}

/* ---- 订单对话（2026-10-04）----
   原来是"订单时间线"（.o-log，一条条竖排），派单人和实施员没法一来一回地
   说清楚进展和困难。改成对话气泡：自己靠右、对方靠左，困难红、进展绿。
   容器限高 + 内部滚动：装一台机器能聊几十条，不限制会把"完成安装"按钮
   顶到屏幕外（那按钮在 m-foot，但中间内容太长同样要滚很久）。 */
.och-list{max-height:260px;overflow-y:auto;display:flex;flex-direction:column;gap:7px;
  padding:4px 2px;background:var(--bg);border-radius:10px}
.och-msg{max-width:84%;padding:7px 10px;border-radius:12px;font-size:13px;line-height:1.55;
  overflow-wrap:anywhere;background:var(--card);border:1px solid var(--border);align-self:flex-start}
.och-msg.is-me{align-self:flex-end;background:#e8edff;border-color:#cfd9ff}
.och-msg.is-pro{background:#eafaf1;border-color:#cbead9}
.och-msg.is-bad{background:#fdecec;border-color:#f7cccc}
.och-who{font-size:11px;color:#8a93a6;margin-bottom:2px}
.och-msg.is-me .och-who,.och-msg.is-me .och-t{text-align:right}
.och-t{font-size:10.5px;color:#b0b8c9;margin-top:3px}
.och-sys{align-self:center;max-width:94%;text-align:center;font-size:11px;color:#aab2c4;
  padding:2px 0}
.och-send{margin-top:9px;display:flex;flex-direction:column;gap:6px}
.och-kinds{display:flex;gap:6px}
.och-k{flex:1;font-size:11.5px;padding:5px 4px;border-radius:8px;cursor:pointer;
  border:1px solid var(--border);background:var(--card);color:var(--muted)}
.och-k.on{background:#e8edff;border-color:#b9c6ff;color:#2f4bc4;font-weight:600}
.och-k.on.is-badk{background:#fdecec;border-color:#f6c9c9;color:#c0392b}
.och-live{font-size:11px;color:var(--muted);display:flex;align-items:center;gap:4px}
.och-live i{width:6px;height:6px;border-radius:50%;background:#22c55e;display:inline-block}
/* 订单卡片上的对话/困难标记 */
.o-chat{font-size:11px;color:var(--muted)}
.o-bad{font-size:11px;color:#c0392b;font-weight:600}

/* ---- 待办事项(同事互派) ---- */
.todo-list{display:flex;flex-direction:column;gap:12px}
.todo-card{background:var(--card);border:1px solid var(--border);border-radius:var(--r-lg);padding:16px;
  cursor:pointer;
  transition:box-shadow var(--dur-2) var(--ease),border-color var(--dur-2) var(--ease)}
.todo-card:hover{border-color:var(--primary);box-shadow:0 4px 14px rgba(79,110,247,.1)}
.todo-card.od-done{background:#fafbfd}
.o-acts{display:flex;gap:8px;margin-top:12px;flex-wrap:wrap}
.td-sec{font-size:12px;font-weight:600;color:var(--text-2);margin:14px 0 8px}
.td-list{display:flex;flex-direction:column;gap:6px}
.td-row{display:flex;justify-content:space-between;gap:10px;font-size:12.5px;
  background:var(--surface-2);border-radius:8px;padding:8px 10px}
.td-log{display:flex;justify-content:space-between;gap:10px;font-size:12px;
  padding:4px 0;border-bottom:1px dashed var(--border);overflow-wrap:anywhere}
/* 多人选择:头像 + 名字的芯片,点一下选中 */
.ava-wrap{display:flex;gap:8px;flex-wrap:wrap}
.ava-chip{display:inline-flex;align-items:center;gap:6px;flex:none;padding:5px 11px;border-radius:16px;
  border:1px solid var(--border);background:var(--surface-2);color:var(--text-2);font-size:12.5px}
.ava-chip.on{border-color:var(--primary);background:var(--primary-soft);color:var(--primary);font-weight:600}
/* 派给谁：按组织分块摆，上面两个下拉按「组织 / 角色」筛（2026-10-06）。
   12 个人平铺成一排时找人得从头扫到尾 —— 分块 + 两个筛子先缩到三四个再挑。
   .ava-grp-t::after 那条细线是组标题的延伸，让「哪几个属于这一组」一眼看到头。 */
.ava-filt{display:flex;gap:8px;margin-bottom:9px}
.ava-filt select{flex:1;min-width:0;font-size:12px;padding:5px 8px}
.ava-grp{margin-bottom:9px}
.ava-grp:last-child{margin-bottom:0}
.ava-grp-t{font-size:11.5px;color:var(--muted);margin-bottom:6px;
  display:flex;align-items:center;gap:7px}
.ava-grp-t::after{content:"";flex:1;height:1px;background:var(--border)}
/* 「默认派给自己」这句提示必须跟着标题走 —— 默认勾了人却不说，
   用户一点「派出去」就派给自己了，还以为派给了同事。 */
.td-sec-tip{font-weight:400;color:var(--muted);margin-left:6px}
.ava-dot{width:19px;height:19px;border-radius:50%;color:#fff;display:inline-flex;align-items:center;
  justify-content:center;font-size:10.5px;font-weight:600}
.q-chips{display:flex;gap:8px;flex-wrap:wrap}
.q-chip.on{border-style:solid;border-color:var(--primary);background:var(--primary-soft);
  color:var(--primary);font-weight:600}

/* ---- 派单类型(收银 / 充电宝) ---- */
.otype-tag{flex:none;font-size:11.5px;font-weight:600;padding:2px 8px;border-radius:5px;
  white-space:nowrap;background:#fff2e0;color:#96550a}
.otype-tag.otype-pb{background:#e6f4ff;color:#0b6d9e}
/* ---- 成交客户卡片：状态徽章（2026-10-04 重做）----
   原来一个订单渲染成两个并排胶囊「[💰 收银] [待接单]」，两种单子都有时
   右边还跟一串，四个胶囊挤在客户名后面，颜色又都差不多（用户：「看着有点乱」）。
   现在合成一个徽章：左边一个粗体符号标状态，右边「收银·待接单」写全。
   颜色只跟"要不要动"挂钩，不跟类型走 —— 用户扫卡片时的问题是
   "这家的货装到哪一步了"，不是"这是收银还是充电宝"。

   四档的语义（.wst-*）：
     todo    还没开始   橙—— 要派单、要去装
     doing   在路上     蓝—— 实施员正在做，不用管
     done    装完了     绿—— 这单不用再看了
     err     出异常     红—— 最需要被看见，单独一档
   「异常」不跟「进行中」混在一起：一个装到一半卡住的单子和正常安装中
   需要的是两种反应，混在一档就等于把异常藏起来了。 */
.wchip{flex:none;display:inline-flex;align-items:center;gap:4px;
  font-size:11.5px;font-weight:600;padding:2px 8px;border-radius:6px;
  white-space:nowrap;line-height:1.5}
.wchip b{font-size:11px;font-weight:800;opacity:.9}
.wst-todo{background:#fff2e0;color:#96550a}
.wst-doing{background:#e6f0ff;color:#1a5fb4}
.wst-done{background:#e7f7ee;color:#1c7a45}
.wst-err{background:#ffe9e9;color:#c0392b}
.wst-none{background:#fdeaea;color:#c0392b}
/* 暗色下用深底亮字，饱和度和亮色版对齐 —— 直接把亮色底色搬过来会
   在深色背景上形成一块亮斑，比徽章本身还抢眼。 */
[data-theme="dark"] .wst-todo{background:#3a3018;color:#f0c07b}
[data-theme="dark"] .wst-doing{background:#1c2b45;color:#8ab4f8}
[data-theme="dark"] .wst-done{background:#16351f;color:#7fd6a2}
[data-theme="dark"] .wst-err,[data-theme="dark"] .wst-none{background:#3a1f1f;color:#f0938b}
/* 卡片右上角的删除（管理员以上）。平时隐去，悬停整卡才显出来 ——
   删除是低频且不可逆的动作，不该跟店名抢同一个视觉层级。

   2026-10-04 扩了触摸热区。实测（elementFromPoint 从中心向外扫）：
     元素框   24×24（视觉没变）
     热区     41×41（四向 上21 下20 左21 右20）
     按钮距卡片上边/右边各 17px，外侧有余量，所以扩展是对称的
   注意实测是 41 而不是推算的 44 —— 没查出那 3px 差在哪，
   但 41 已明显好于 24，且四向均衡，所以按实测值记录，不按推算写。 */
.won-del{margin-left:auto;flex:none;width:24px;height:24px;padding:0;
  border:1px solid var(--border);border-radius:6px;background:var(--surface);
  color:var(--muted);font-size:12px;line-height:1;cursor:pointer;
  opacity:0;transition:opacity var(--dur-1) var(--ease),color var(--dur-1);
  /* 触摸热区（2026-10-04）：视觉仍是 24×24 的小方块，可点范围扩到 41×41。
     它是删除成交客户的**破坏性**操作，24px 太容易误触。
     手法与 .rt-pick::after 一致 —— 只撑热区，不改外观。
     position:relative 必须始终在（不能只写在 :hover 里），否则
     ::after 没有定位上下文，会跑到错误的位置。 */
  position:relative}
.won-del::after{content:"";position:absolute;
  top:-10px;right:-10px;bottom:-10px;left:-10px}
.order-card:hover .won-del{opacity:1}
.won-del:hover{color:var(--danger);border-color:var(--danger)}
@media (hover:none){.won-del{opacity:.7}}
/* 派单弹窗 */
.dp-cust{display:flex;flex-direction:column;gap:2px;font-size:13.5px;margin-bottom:8px}
.dp-cust span{font-size:12px;color:var(--muted)}
.dp-addr{font-size:12.5px;color:var(--muted);background:var(--surface-2);border-radius:7px;
  padding:7px 10px;margin-bottom:10px;word-break:break-all}
.dp-addr.dp-warn{background:#fff5e6;color:#96550a}
.dp-types{display:flex;gap:8px;margin-bottom:12px}
.dp-t{flex:1;display:flex;flex-direction:column;gap:2px;border:1.5px solid var(--border);
  border-radius:10px;padding:9px 11px;cursor:pointer;background:var(--card);
  transition:border-color var(--dur-2) var(--ease),background var(--dur-2) var(--ease)}
.dp-t input{margin-right:0}
.dp-t b{font-size:13px}
.dp-t span{font-size:11.5px;color:var(--muted)}
.dp-t.on{border-color:var(--primary);background:var(--primary-soft)}
.dp-t.on span{color:var(--primary)}
.dp-t input{position:absolute;opacity:0;pointer-events:none}
.dp-hint{margin-top:10px;font-size:11.5px;color:var(--muted);line-height:1.6}
/* 安装套数（1.12.0）：档位下拉 + 「自己填」的输入框并排，选中才露出输入框 */
.dp-sets{display:flex;gap:6px}
.dp-sets select{flex:0 0 auto;min-width:130px}
.dp-sets input{flex:1;min-width:0}
.sets-tbl{width:100%;border-collapse:collapse;font-size:13px;margin-top:10px}
.sets-tbl th,.sets-tbl td{padding:7px 8px;text-align:left;border-bottom:1px solid var(--line)}
.sets-tbl th{color:var(--muted);font-weight:500;font-size:12px}
.sets-tbl td+td,.sets-tbl th+th{text-align:right}
.sets-total td{border-bottom:none;font-weight:600;padding-top:10px}
/* 订单里的客户资料区 */
.op-head{display:flex;align-items:center;gap:8px;margin-top:14px;font-size:12px;
  font-weight:600;color:var(--muted)}
.op-head select{padding:4px 6px;border:1px solid var(--border);border-radius:6px;font-size:12px;
  background:var(--card);color:var(--text)}
.op-info{margin-top:7px;background:var(--surface-2);border-radius:8px;overflow:hidden}
.op-info div{display:flex;gap:10px;padding:7px 10px;font-size:12.5px;
  border-bottom:1px solid var(--border)}
.op-info div:last-child{border-bottom:none}
.op-info span{color:var(--muted);flex:none;width:70px}
.op-info b{font-weight:600;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}
.op-info.op-empty{padding:8px 10px;font-size:12px;color:var(--muted);line-height:1.6}
.op-group{margin-top:10px}
.op-t{font-size:12px;color:var(--muted);margin-bottom:2px}
.op-group .o-photos{margin-top:4px}

/* ---- 地图看板 ---- */
/* isolation:isolate 是把地图内部的层级"关进盒子":高德/Leaflet 自己的 pane 用了
   z-index 400~800,不隔离的话它们会盖到详情抽屉(z-index 50)上面 —— 点地图上的
   客户,详情弹出来却被地图控件压住,就是这个原因。 */
.map-wrap,#mapBoard,#mapDiv{position:relative;isolation:isolate}
.map-wrap{position:relative;border-radius:12px;overflow:hidden;border:1px solid var(--border);background:#eef1f8}
#mapBoard{height:min(68vh,560px);height:min(68dvh,560px);min-height:300px;width:100%;background:#eef1f8}
/* 图例（2026-10-04 第三次调整，用户原话）：
   「地图侧对于颜色的秒速现在集中在左上角，挡住了我放大缩小的+-号，
     不用写公海或私海，只需要表达未认领多少个等这种样式就行，
     放右侧缩小一点再透明一点，左下角的显示多少个这里就可以不用啦」

   **为什么挪到右侧**：高德/Leaflet 自带的缩放控件（+/-）钉死在左上角，
   实测图例 (11,11,218×68) 和缩放控件 (11,11,34×64) **100% 重叠** ——
   两个 +/- 按钮各被盖掉 900px²，点都点不到，等于地图缩放功能废了。
   右侧是全地图唯一空着的位置：刷新/我的位置在右下角，缩放在左上角，
   右上角除了 attribution 什么都没落。

   **为什么变窄**：366px 宽的手机上原来一个图例就占 218px（60%），
   横向 flex-wrap 排两行、占 68px 高，看着像个横幅。改成右侧竖排一列：
   宽约 92px（占 25%）、高约 90px，横向还出 126px 给地图。

   **为什么更透明**：.62 → .42，毛玻璃同步从 blur(3px) 收到 blur(2px)。
   底图从浅灰路面到深绿公园都有，白底越透明越不挡路名和门店。

   **文字简化**：去掉「公海·」「私海·」前缀，只留状态本身 ——
   用户要的是「未认领多少个」这种计数视角，不是「哪几个颜色归公海哪几个归私海」
   这种归属视角（归属在图钉颜色和客户列表里已经能看出来了）。
   每项变成「● 未认领 310」这种短句。 */
.map-legend{position:absolute;right:8px;top:8px;bottom:auto;z-index:5;
  background:rgba(255,255,255,.42);
  -webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);
  border:1px solid rgba(0,0,0,.06);
  border-radius:7px;padding:5px 7px;display:flex;flex-direction:column;
  gap:3px;
  max-width:calc(100% - 60px);
  box-shadow:0 1px 3px rgba(0,0,0,.06);font-size:10.5px;color:#4a5680;
  line-height:1.4}
/* 2026-10-04：**删掉左下角那个计数浮层**（.map-meta /「显示 313 个 · 含公海 310 个」）。
   用户原话：「左下角的显示多少个这里就可以不用啦」。
   理由他也说了 —— 挪到右上角的图例里每项都带数字了
   （未认领 310 / 待蹲店名 0 / 线索意向 2 …），
   总数自己加一下就有，再单独挂一行「显示 N 个」是重复信息，
   而且它为了避开左下角的比例尺被抬到 bottom:28px，本来就悬着。
   撤掉之后左下角彻底干净：只剩底图自带的比例尺和版权。
   样式和 DOM 一起删干净，不留「display:none 占着位置」的死代码。 */
.lg-item{display:flex;align-items:center;gap:4px}
.lg-item i{width:7px;height:7px;border-radius:50%;display:inline-block;flex:none}
.lg-item b{color:var(--text);font-size:10.5px;
  font-variant-numeric:tabular-nums}
.map-empty{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;
  justify-content:center;gap:6px;background:#f4f6fb;color:var(--muted);font-size:13.5px;
  text-align:center;padding:20px;z-index:6}
.map-loading{position:absolute;top:12px;left:50%;transform:translateX(-50%);z-index:7;
  background:rgba(35,42,61,.85);color:#fff;font-size:12px;padding:6px 14px;border-radius:20px}
/* 「重新加载地图」悬浮在「我的位置」正上方，跟定位按钮成一列。
   圆形 38×38 图标按钮，省空间，视觉重量和定位按钮一致。 */
.map-reload-btn{position:absolute;right:10px;bottom:54px;z-index:6;
  width:38px;height:38px;padding:0;display:inline-flex;align-items:center;justify-content:center;
  border:1px solid rgba(0,0,0,.08);border-radius:50%;
  background:rgba(255,255,255,.96);color:#2b3448;
  box-shadow:0 2px 10px rgba(0,0,0,.16),0 1px 3px rgba(0,0,0,.10);
  cursor:pointer;-webkit-tap-highlight-color:transparent;
  transition:transform var(--dur-1) var(--ease-spring),box-shadow var(--dur-1) var(--ease)}
.map-reload-btn:hover{box-shadow:0 4px 16px rgba(0,0,0,.20),0 1px 3px rgba(0,0,0,.10)}
.map-reload-btn:active{transform:scale(.94)}
.map-reload-btn svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:1.9;
  stroke-linecap:round;stroke-linejoin:round}
[data-theme="dark"] .map-reload-btn{background:rgba(38,43,56,.96);color:#e6e9f0;
  border-color:rgba(255,255,255,.14)}
/* 「我的位置」悬浮在地图右下角 —— 原来它在上面那排筛选器里，手机上单手够不到，
   而定位恰恰是站在店门口、想确认"我在哪、这家店在哪个方位"时最想点的。
   放右下角是因为右手拇指的自然落点就在那，且这个位置在地图上本来就是空的。
   z-index 6 要压过图例(5)，又要低于空态/加载提示(6/7) —— 地图没加载出来时
   这个按钮不该浮在"地图加载中…"上面。
   胶囊形 + 白底 + 投影：底图颜色从浅灰到深绿都有，做成实心主题色会在浅色
   地图上糊掉；白底投影在任何底图上都读得清，也和高德的定位按钮观感一致。 */
.map-loc-btn{position:absolute;right:10px;bottom:10px;z-index:6;
  height:38px;padding:0 14px;display:inline-flex;align-items:center;gap:5px;
  border:1px solid rgba(0,0,0,.08);border-radius:19px;
  background:rgba(255,255,255,.96);color:#2b3448;
  font-size:13px;font-weight:600;font-family:inherit;line-height:1;
  box-shadow:0 2px 10px rgba(0,0,0,.16),0 1px 3px rgba(0,0,0,.10);
  cursor:pointer;-webkit-tap-highlight-color:transparent;
  transition:transform var(--dur-1) var(--ease-spring),box-shadow var(--dur-1) var(--ease)}
.map-loc-btn:hover{box-shadow:0 4px 16px rgba(0,0,0,.20),0 1px 3px rgba(0,0,0,.10)}
/* 按下缩一下 —— 手机上手指盖住按钮，缩放是唯一能感知"点到了"的反馈 */
.map-loc-btn:active{transform:scale(.94)}
.map-loc-btn:disabled{opacity:.72;cursor:default;transform:none}
.map-loc-btn svg{width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:1.9;
  stroke-linecap:round;stroke-linejoin:round;flex:none}
[data-theme="dark"] .map-loc-btn{background:rgba(38,43,56,.96);color:#e6e9f0;
  border-color:rgba(255,255,255,.14)}
/* 1.19.9（补丁5）：.map-q 这条随 #mapQ 一起删了（没人用了） */
/* 「展开全部」——分批渲染的入口 */
.mp-more{padding:10px;text-align:center;font-size:12.5px;color:var(--primary);
  cursor:pointer;border-radius:8px;background:var(--primary-soft,#eef1fb);
  margin-top:6px;min-height:38px;display:flex;align-items:center;
  justify-content:center}
.mp-more.done{color:var(--muted);background:transparent;cursor:default}
.map-list{margin-top:12px;display:flex;flex-direction:column;gap:6px;
  max-height:300px;overflow-y:auto;overscroll-behavior:contain;
  -webkit-overflow-scrolling:touch}
/* ---- 地图图钉上的门店名 ----
   有坐标的门店有两百多家，全部常显会把地图糊死，所以只在放大到街区级时
   才显示（见 drawMarkers / mbApplyLabels）。

   2026-10-03 用户要求：去掉白底，只留文字 —— "有点挡地图"。
   但直接改成深色字是不行的：底图从浅灰路面到深绿公园都有，纯色字在深底上
   就没了。所以改成**白字 + 深色描边**：描边让字在自己和底图之间造出一条
   对比边界，白字压在深底上清楚，描边落在浅底上也能把字勾出来，
   两种底图上都读得清，而中间是透的、不遮地图。
   实现用 4 向 text-shadow 而不是 -webkit-text-stroke：stroke 描的是字形轮廓，
   转角处会向内吃掉笔画，11.5px 的小字会变瘦发虚；text-shadow 是往四周糊，
   笔画反而更实。再加一层大半径阴影当"外发光"，把密集区域的字和底图分开。 */
/* 标签：纯文字 + 描边（高德原生观感）。
   宽度上限从 156px 收到 132px —— 去掉了「10.2 」日期前缀之后，
   店名本身很少超过这个宽度，收窄能让密集区少压几个标签。 */
/* **不要**在这里写 transform:translate(-50%)。
   AMap 的 label(direction:"bottom") 已经把内容水平居中了，
   再加一层自居中会左偏半个标签宽 —— 就是"店名和点位对不上"的原因。
   横向位置交给引擎，这里只管外观。 */
/* 1.19.8（轮7）：标签照着高德小程序改 —— **纯黑、无描边、12px、bold**。
   实测（PIL 从截图取的 43 个像素，全是 #000000）：
     · 颜色 #000000（原来 rgba(255,255,255,.82)）
     · **没有描边**（原来九层黑描边 + 3px 模糊）
     · 字号 ≈12px（数字字面高 8.7 CSS；原来 11.5px）
     · 无背景、无内边距、无圆角
   为什么黑字在浅色底图上清楚：底图是浅色的（见下面"地图始终浅色"那节），
   白字压浅底本来就糊 —— 原来靠九层描边硬撑，字看着脏。
   高德的做法更简单：底图浅就用黑字，不加任何描边。 */
.mb-lb{display:inline-block;max-width:132px;
  background:none;color:#000;
  font-size:12px;font-weight:700;line-height:1.25;
  padding:0;border-radius:0;white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis;
  border:none;
  text-shadow:none;
  pointer-events:none}
/* 日期在纯黑字里靠**降透明度**分层（白字那套"偏亮"的做法反过来用）。
   黑字没有描边兜底，透明度别压太狠，压到 .62 是实测还能读的下限。 */
.mb-lb .dt{color:rgba(0,0,0,.62);font-weight:500;margin-right:2px}
.mb-lb .pool{color:rgba(0,0,0,.62);font-weight:500;margin-left:3px}
/* 公海（蓝）和无店名（绿）的标签降一档 —— 这两类在地图上常常成片出现，
   全用纯黑会糊成一片，把重点（私海/报价中/没意向）压下去。 */
.mb-lb.is-pool{color:rgba(0,0,0,.72);font-weight:600}
/* 已确认没意向（黑）：最该低调的一类 —— 这家店已经跟过、没戏了，
   不需要再吸引注意。压到 .55 的不透明度就够。
   ⚠️ 这里**不用删除线**：删除线会让人以为"这条要没了"，
   但"没意向"只是个业务状态，客户还在、数据一点都不少。
   上一版（黑=待删除）用删除线是对的，这一版语义变了，线就得撤。 */
.mb-lb.is-invalid{color:rgba(0,0,0,.55);font-weight:600}

/* ---- Leaflet 模式下，把自带的 tooltip 白底/白边/箭头全部拆掉 ----
   Leaflet 的 bindTooltip 会套一层 .leaflet-tooltip（白底、白边框、
   下方小三角）。上面 .mb-lb 虽然写了 background:none，但那只是**内层**
   元素透明 —— 外面那层白框照旧，于是就呈现成一块白底方块，把地图挡得
   只剩缝隙（用户截图里就是这个样子）。
   之前代码传了 className:"mb-tip" 却没写这条样式，等于挂了个空钩子。
   这里把底色/边框/阴影/圆角全清掉，让它退化成"纯文字 + 描边"，
   跟高德原生的标注观感一致。
   1.19.8：color 跟着 .mb-lb 改成**黑**（原来 #fff）—— Leaflet 模式下
   店名标签走的就是这一层，只改 .mb-lb 不改这里，屏幕上仍旧是白字。
   字号也一并跟到 12px。
   注意不要改 .leaflet-tooltip 全局 —— 别的 tooltip（比如"我的位置"）
   还要用默认外观。 */
.mb-tip.leaflet-tooltip{
  background:none;border:none;box-shadow:none;
  padding:0;border-radius:0;
  color:#000;
  font-size:12px;line-height:1.25;
  white-space:nowrap;
  pointer-events:none}
/* 下方那个小三角也一并拿掉 —— 光去掉白底会留下一个孤零零的尖角 */
.mb-tip.leaflet-tooltip::before{
  border:none !important;display:none}
/* ⚠️ 1.19.23 标签显隐从「unbind/bind」改成「CSS 类开关」：
   实测一轮往返缩放的 DOM churn 里标签占 41增/41删（见 docs 100.8），
   全是 bindTooltip/unbindTooltip 在档位切换时反复建删节点。
   绑上就不再解绑，只在 tooltip 元素上加/摘这个类 —— classList.toggle
   不产生节点增删，MutationObserver 口径的 churn 归零，真机上
   也省掉每次 unbind 的布局计算。 */
.mb-tip.leaflet-tooltip.mb-tip-off{display:none}
/* 深色主题下 Leaflet 白框同样要去掉（变量覆盖管不到它） */
[data-theme="dark"] .mb-tip.leaflet-tooltip{background:none;border:none;box-shadow:none}
/* ⚠️ 原来这里有一条 `[data-theme="dark"] .mb-lb{color:#fff; text-shadow:八层黑描边}`，
   1.19.8 **整条删掉**。它的注释写着"深色主题下底图本来就是暗的"——
   这句**实测不成立**：底图在两种主题下都是浅色（见 amapTileUrl 那节），
   保留它等于暗色主题里把店名刷成白字压在浅色底图上，一个字都看不见。
   现在地图固定浅色、标签固定黑字，不再需要按主题分叉。 */

.mp-row{display:flex;align-items:center;gap:9px;background:#fff;border:1px solid var(--border);
  border-radius:10px;padding:9px 12px;cursor:pointer}
.mp-row:hover{border-color:var(--primary);background:#f8faff}
.mp-dot{width:9px;height:9px;border-radius:50%;flex:none}
.mp-main{flex:1;min-width:0}
.mp-main b{font-size:14px;display:block}
.mp-sub{font-size:11.5px;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.mp-empty{padding:22px;text-align:center;color:var(--muted);font-size:13px}
/* 没坐标的客户单独一块,避免导入后在地图上凭空消失 */
.no-geo{margin-top:14px;border-top:1px dashed var(--border);padding-top:10px}
.no-geo-head{display:flex;align-items:center;gap:8px;font-size:12.5px;color:var(--muted);
  margin-bottom:8px;flex-wrap:wrap}
.no-geo-head b{color:var(--text)}
.no-geo-list{display:flex;flex-direction:column;gap:6px;max-height:210px;overflow-y:auto}
.no-geo .mp-row{border-style:dashed;opacity:.9}

/* ---- 今日拜访路线 ---- */
/* 1.10.0：路线行右侧的打卡按钮。
   做得比常规按钮小一号（12px / 内边距压到 3px）—— 这一行右边还要放
   「🚗 导航 ›」，手机上横向空间是死的，按钮一大就把导航挤出屏幕。
   已打卡换成绿字 + 绿边：跑完的店一眼扫过去就该跟没跑的分开，
   不需要用户停下来读文字。 */
.vst-btn{padding:3px 8px;font-size:12px;line-height:1.4;border-radius:8px;
  border:1px solid var(--border);background:#fff;color:var(--text);
  cursor:pointer;white-space:nowrap;-webkit-tap-highlight-color:transparent}
.vst-btn.done{color:#1a9c5b;border-color:#1a9c5b;background:rgba(26,156,91,.08)}
.vst-nav{margin-left:7px;white-space:nowrap}
/* 1.10.0：到店记录里那枚「正常 / 超 480 米 / 无定位 / 强制」的小标签。
   绿=在围栏内，橙=定位不准或没定位，红=超范围且是硬打的。
   颜色只用来扫一眼，判定的事实写在文字里 —— 色弱同事靠文字也能读。 */
.vtag{display:inline-block;padding:1px 6px;border-radius:6px;font-size:11px;
  line-height:1.6;border:1px solid transparent;white-space:nowrap}
.vtag.ok{color:#1a9c5b;border-color:rgba(26,156,91,.4);background:rgba(26,156,91,.08)}
.vtag.warn{color:#c07b12;border-color:rgba(192,123,18,.4);background:rgba(192,123,18,.09)}
.vtag.bad{color:#d0342c;border-color:rgba(208,52,44,.4);background:rgba(208,52,44,.08)}
.vops{margin-top:5px;display:flex;gap:8px;flex-wrap:wrap}
.vops a{font-size:12px;color:var(--brand,#3b6ef6);text-decoration:none}
.vph{margin-top:5px}
/* 1.11.0：安装设备清单。一列品类+型号+数量，实施员照着拿货，
   所以要能一眼扫完，行高压紧、品类用浅标签、数量用主色加粗。 */
.dev-list{margin-top:6px}
.dev-row{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:13px;
  padding:5px 0;border-bottom:1px dashed var(--border)}
.dev-row:last-child{border-bottom:0}
.dev-cat{font-size:11px;color:var(--muted);border:1px solid var(--border);
  border-radius:6px;padding:1px 6px;white-space:nowrap}
.dev-qty{color:var(--primary,#3b6ef6);font-weight:600}
.dev-note{font-size:12px;color:var(--muted)}
.dev-edit-row{display:flex;align-items:center;gap:6px;margin-bottom:7px;flex-wrap:wrap}
.dev-edit-row select{flex:1;min-width:96px}
.dev-del{color:#c0392b;cursor:pointer;padding:0 6px;font-size:14px}
/* 1.13.0：清单每行的备注框（「厨房一台」「放前台」这类位置信息）。
   宽度给 min-width 而不是固定值：手机上窄屏它会自动换行到第二行，
   不会把品类/型号/数量挤得看不见。 */
.dev-note-in{flex:1;min-width:140px;font-size:13px}
/* 1.13.0：团队拜访统计。
   上面五个数字是"今天这个团队干了多少"，下面每人一行是"谁没干"。
   数字块用 flex:1 均分，不写死宽度 —— 手机上 5 个块会挤成 5 列小字，
   但都比换成横向滚动好读（横滚要动手滑，扫一眼就过去才是重点）。 */
.vt-sum{display:flex;gap:6px;margin:4px 0 10px}
.vt-sum>div{flex:1;text-align:center;padding:7px 2px;border-radius:8px;
  background:var(--surface-2,#f7f9ff)}
.vt-sum b{display:block;font-size:17px;line-height:1.25}
.vt-sum span{font-size:11px;color:var(--muted)}
.vt-row{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:13px;
  padding:6px 0;border-bottom:1px dashed var(--border)}
.vt-row:last-child{border-bottom:0}
.vt-row b{min-width:52px}
.vt-num{color:var(--primary,#3b6ef6);font-weight:600;white-space:nowrap}
/* ===== 底部悬浮条 .rt-bar（1.9.5 加 → 1.9.6 整条删 → 本次只加回左段）=====

   沿革（别做反了）：
     1.9.5  两段式：左段「已勾选 N 家 · 加入今日拜访 ›」
                    右段「今日拜访 N 家 · 清空 · 生成路线 ›」
     1.9.6  用户说「取消点击客户之后下方生成路线这个横条」，**整条删掉**，
            流程并成一步（点勾选直接进清单）；连带删了 body.rt-on 的
            padding-bottom、:root{--rt-h} 和 rtMeasure()。
     1.9.8  用户要回两步，但载体选了**居中弹窗**（rtAsk 体系）。
     本次   负责人 2026-10-06 明确推翻居中弹窗，改回底部横条 ——
            **只做左段**，不要右段。

   ⚠️ 为什么不要右段：1.9.6 删它的原话就是「分成两段还老让人拿不准该点哪个」。
   只留左段（勾了才出现、只有「加入」一个动作）就没有这个问题。
   「生成路线」的入口仍在工作台「我的路线规划」那一节（loadHomeRoute），
   「清空」跟着那边走 —— 本次不往回搬。

   ⚠️ 1.9.5 为这条栏踩过的两个 flex 坑，本次**照抄解法**，别重踩：
     · 按钮必须显式 `flex-basis:content`。光写 flex:none（=0 0 auto）不够 ——
       auto 会让浏览器把 max-content 算成「容器可用空间」，实测 1440px 下
       「加入今日拜访 ›」被算成 1400px 宽，把右边全挤出屏幕，而
       getComputedStyle 读回来明明写着 "0 0 auto"。
     · 窄屏（<700px）绝不能强挤一行：实测栏高会从 53px 涨到 162px
       （三行），按钮还被压到 39px 宽，字都装不下。手机上一行一段才对。
       —— 本次只有一段，窄屏连换行都不用，flex-wrap 一并省掉。

   ⚠️ 让位方式改了，别照抄 1.9.5：
   1.9.5 是常驻条，给 body 挂 `.rt-on` 加死 padding；本次的条**只在勾选时
   出现**，挂常驻 padding 会在没勾选时白留一条空白。改成**动态**补 ——
   条亮起来时 `document.body.style.paddingBottom` 设成条高，收起时清空。
   需要让位的两处：会滚到最底的列表分页器（手机端条被抬到导航栏之上，
   所以要算「条自身高 + 它到底边那段」）、以及详情抽屉/弹窗里贴底的
   「保存跟进」按钮（那是 fixed 独立层，不吃 body 的 padding，靠同一份
   高度值单独顶）。高度由 rtBarSync() 现量，量法见那里的注释。 */
/* ⚠️ 2026-10-06：z-index 55 → 75。
   用户报「全选或勾选之后，没有弹出加入今日拜访的弹窗」——
   横条其实弹了（.on 加上了、translateY 也归零了），但**被路线弹窗盖住了**：
   .rtm-mask 是全屏 fixed z-index:70，横条 55 在它下面，肉眼就是「没弹」。
   75 > 70，横条浮到弹窗之上 —— 规则 10 本来就要求「底部同一条横条」，
   弹窗里勾的候选也要走这同一条（数据源切换见 rtBarSync 里的判断）。 */
/* ⚠️ 第六十三轮：上下 padding 10px → 6px、按钮 32px → 30px。
   用户原话「这个横条也很挡位置，给放到最底下呈现吧，上下缩窄一点」——
   条本身变薄（52px → 约 42px），在弹窗里还要**正好压住 .rtm-foot 那一排**
   （见 rtBarSync 里的落点算法），不再在检索栏上方另叠一层。 */
.rt-bar{position:fixed;left:0;right:0;bottom:0;z-index:75;display:flex;align-items:center;gap:10px;
  padding:6px 14px calc(6px + env(safe-area-inset-bottom,0px));background:var(--bar-bg);color:#fff;
  box-shadow:0 -2px 14px rgba(31,42,80,.2);
  transform:translateY(calc(100% + env(safe-area-inset-bottom,0px)));
  transition:transform var(--dur-3) var(--ease-out)}
/* .on 才浮上来。隐藏态用 translateY 推出屏幕 ——
   ⚠️ 只写 translateY(120%) 不够：120% 是「自身高」的比例，自身高约 53px，
   位移只有 63px，而手机端它离屏幕底还有 64px（被抬到导航栏之上），
   藏起来的位置正好压在导航栏上，视觉上就是「导航栏没了」。
   必须把 bottom 那段距离一起算进去。 */
.rt-bar.on{transform:translateY(0)}
@media (max-width:699px){
  /* 手机端抬到导航栏之上（导航栏高 64px），否则被它盖住 */
  .rt-bar{bottom:calc(64px + env(safe-area-inset-bottom,0px))}
  .rt-bar{transform:translateY(calc(100% + 64px + env(safe-area-inset-bottom,0px)))}
}
.rt-bar .rt-n{font-size:13.5px;white-space:nowrap}
.rt-bar .rt-n b{color:#ffd166;font-size:16px;margin:0 3px}
.rt-bar .rt-sp{flex:1}
/* 按钮不被拉伸（理由见上面第一段注释的 flex-basis 坑） */
.rt-bar button{flex:none;flex-basis:content}
.rt-bar button{padding:5px 12px;font-size:12.5px;border-radius:8px;cursor:pointer;min-height:30px;
  border:1px solid rgba(255,255,255,.35);background:transparent;color:#fff;
  transition:transform var(--dur-1) var(--ease-spring),background-color var(--dur-1) var(--ease),
             border-color var(--dur-1) var(--ease),opacity var(--dur-1) var(--ease)}
.rt-bar button:hover{background:rgba(255,255,255,.12)}
.rt-bar button:active{transform:scale(.95)}
.rt-bar button.main{background:var(--primary);border-color:var(--primary);font-weight:600}
.rt-bar button.main:hover{background:#6a84f9}
/* 详情抽屉 / 弹窗是 fixed 独立层，不吃 body 的 padding-bottom，
   贴底的「保存跟进」「快捷日期」会被横条压住 → 靠 --rt-h 单独让位。
   --rt-h 由 rtBarSync() 现量写入，条收起时置 0px（下面两条规则就自动失效）。 */
:root{--rt-h:0px}
body.rt-on .drawer .d-body{padding-bottom:calc(var(--rt-h, 54px) + 18px)}
body.rt-on .drawer .f-form{padding-bottom:calc(14px + var(--rt-h, 54px) + env(safe-area-inset-bottom,0px))}
body.rt-on .modal .m-body{padding-bottom:calc(var(--rt-h, 54px) + 18px)}

/* 「这几家没定位」名单（方案 A）。
   ⚠️ 必须 max-height + overflow:auto：勾 20 家、有 10 家没坐标时，
   全列出来弹窗会撑出屏幕，最下面那几家连「去补定位」都点不到。 */
.ng-list{max-height:200px;overflow:auto;margin-top:8px;
  border:1px solid var(--border);border-radius:10px}
.ng-row{display:flex;align-items:center;gap:10px;padding:8px 10px;
  border-bottom:1px solid var(--border)}
.ng-row:last-child{border-bottom:0}
.ng-name{flex:1;min-width:0;font-size:13px;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
/* 勾选框:列表和地图里都能点,不触发整行的打开详情 */
/* 视觉仍是 24px 方块（变大就把列表行撑高了），但热区用伪元素撑到 40px。
   未选中态的描边色也提深一档 —— 原来是 --border(#e8ecf4),
   对白底只有 1.18:1 的对比度,阳光下基本看不见。 */
.rt-pick{width:24px;height:24px;flex:none;border-radius:7px;
  border:1px solid var(--faint, #8a93a6);
  background:#fff;display:inline-flex;align-items:center;justify-content:center;
  font-size:13px;  line-height:1;color:transparent;cursor:pointer;user-select:none;
  position:relative;
  -webkit-tap-highlight-color:transparent;
  transition:transform var(--dur-1) var(--ease-spring),background-color var(--dur-1) var(--ease),
             border-color var(--dur-1) var(--ease),color var(--dur-1) var(--ease)}
.rt-pick:hover{border-color:var(--primary);color:#c5d0f7}
/* ⚠️ 本次（2026-10-06）删掉了 `.rt-pick.on` 那条规则（实心蓝底白勾）。
   用户看图标注要求：**已在今日拜访的行不要再出现勾选框** ——
   于是 rtBox 在 on 态直接返回空串，这条规则再也命中不了任何元素。
   留着它只会误导后来人以为「有个实心态」，一并删掉。
   同批删掉的还有 `.rt-mark` 和两处 `:has(> .rt-mark)` 让位规则：
   那个「已在今日拜访」小字是跟着 on 态勾选框一起渲染的，
   勾选框没了它也没了 —— 三个列表（客户/公海/工作台）都不再挂标记。 */

/* .rt-hide = **已在清单里的行**把勾选框藏起来（用户要求「这两个不要出现」）。
   ⚠️ 用 class 藏而不是 remove 元素：移出是可逆的 —— 在抽屉里点「今日拜访」
   移出后，这一行的勾选框要重新露出来。删掉元素还得记住原位再插回去，
   挂/摘 .rt-hide 就干净多了。 */
.rt-pick.rt-hide{display:none}
/* 1.9.8：.sel = **勾了、但还没确认加入**（等底部横条上那个「加入」）。
   1.9.5 有这个态，1.9.6 随两阶段流程一起删了，1.9.8 两阶段回来，
   本次（2026-10-06）载体从居中弹窗换成底部横条。

   ⚠️ 本次之后 .sel 是勾选框**唯一**的非空状态了 ——
   原来还有 .on（已在清单，实心蓝底白勾），用户要求「已在今日拜访的行
   不要出现勾选框」，于是 on 态直接不画框，.on 那条规则也删了。
   于是现在只剩两种视觉：
     无     还没勾
     .sel  勾了待加入（白底 + 蓝描边 + 蓝勾 + 淡蓝光晕）
   区分「已加入」和「没加入」不再靠勾选框（已加入的行压根没框），
   而是靠**抽屉里那个「✓ 已加入」按钮**和工作台的「我的路线规划」那节。
   ⚠️ 别指望 border-width 加粗（第一版写了 1.5px）：Chromium 会把边框宽度
   取整到设备像素，1x 屏上 getComputedStyle 读回来还是 1px —— 写着像加了
   粗细，实际一点没变。
   深色主题不用另写：那条 [data-theme="dark"] .rt-pick 把底色换成
   --card，蓝描边蓝勾在深底上照样分得清。 */
.rt-pick.sel{border-color:var(--primary);color:var(--primary);
  background:#fff;box-shadow:0 0 0 3px rgba(79,110,247,.14)}
/* 点下去的回弹：.sel 是「待定」，按下去要有点动静，
   不然用户会以为这个框点不动（它确实不会立刻让清单变化） */
.rt-pick.sel:active{transform:scale(.88);box-shadow:0 0 0 4px rgba(79,110,247,.2)}
/* ⚠️ 本次（2026-10-06）删掉了整块 `.rt-mark`（「已在今日拜访」行内文字标记）
   连同两处让位规则 `td > div:has(> .rt-mark) > .rt-cell` 和
   `.home-row:has(> .rt-mark) .rn`。
   用户看图标注「这两个不要出现」—— 指的就是勾选框 + 这个标记，
   已在清单的行两样都不画了（rtBox 在 on 态返回空串）。

   留一段历史免得以后有人「当成 bug」加回来：
     · 1.9.6 加它，是因为 24px 勾选框在几百行里太不显眼，扫不出「今天要跑」；
     · 1.9.8 改成绝对定位（原 flex 项会把 .home-row 的店名顶没）；
     · 本次按规则 7 收小到 10px / padding 1px 4px（让位 82 → 66px）；
     · 然后用户直接要求整块拿掉 —— 现在「是否已加入」由抽屉里的
       「✓ 已加入」按钮 + 工作台「我的路线规划」那节来体现，
       不再在列表行里表达。 */
.rt-panel{background:#fff;border:1px solid var(--border);border-radius:var(--radius);
  margin-top:12px;padding:16px;box-shadow:var(--sh-1)}
.rt-head{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:13px}
.rt-head b{font-size:15.5px}
.rt-tag{font-size:11px;padding:2px 8px;border-radius:20px;background:#eaf1ff;color:var(--primary)}
.rt-mode{display:inline-flex;border:1px solid var(--border);border-radius:8px;overflow:hidden}
.rt-mode button{border:0;background:#fff;color:var(--muted);font-size:12px;padding:4px 10px;cursor:pointer}
.rt-mode button.on{background:var(--primary);color:#fff}
.rt-tag.warn{background:#fff4e6;color:#96550a}
.rt-tip{font-size:11.5px;color:var(--muted);margin-top:6px;line-height:1.6}
/* 1.9.9：`.rt-leg{...}` 删了 —— 那是地图页路线面板的每一站行
   （flex + 上边框分隔线）。面板整块删了，这条规则再也不命中任何元素。
   ⚠️ 下面 .rt-seq 那条**要留着** —— 序号圆现在归工作台每行用
   （<span class="rt-seq rt-grip">），1.9.7 就是从 .rt-leg .rt-seq
   把「能拖」的样式抽成 .rt-grip 公用的，两条都在用。 */
.rt-seq{width:25px;height:25px;flex:none;border-radius:50%;background:var(--primary);color:#fff;
  font-size:12px;font-weight:700;display:flex;align-items:center;justify-content:center}

/* 1.9.5：序号变成可拖的手柄。
   视觉上必须看得出「能拖」—— 加了上下小箭头（CSS 画的双箭头，
   不用字符：字符箭头在部分安卓字体里会缺字形，1.9.0 踩过）。
   ⚠️ 手柄从 25px 放大到 34px：截图截出来后才发现 25px 的圆圈里
   那个双箭头几乎看不见（缩到 1/3 大小时只剩两个灰点），
   而「这行能拖」是这一版最需要被看见的信息。
   34px 是权衡：再大就跟店名抢视觉重量了。

   1.9.7：这些属性整体搬进 .rt-grip，不再绑死在 `.rt-leg` 上。
   为什么要抽出来 —— 用户反馈「我的路线规划那里拖动客户排序并没有看到」：
   拖动手柄只长在地图页的路线面板里，工作台那套精简渲染完全没有，
   而用户天天看的是工作台。不抽出来就得把整套样式**抄一遍**，
   抄一份的结果是以后改手柄大小要记得改两个地方，漏一个就两边不一样大。
   抽成类之后两处共用同一份，`.rt-leg .rt-seq` 只留「连线留白」
   （margin-bottom 那条是给 rt-leg 的竖线留位置的，跟手柄无关）。 */
.rt-grip{width:34px;height:34px;font-size:14px;cursor:grab;
  touch-action:none;position:relative;box-shadow:0 2px 5px rgba(79,110,247,.26)}
.rt-grip::before{content:"";position:absolute;inset:-4px;border-radius:50%;
  transition:box-shadow var(--dur-1) var(--ease)}
/* 3 倍缩放截图后才发现两件事：
   ① 双箭头画在圆圈**里面**时几乎看不见，只在圆圈下沿露出两个灰点；
   ② 挪到外面之后更糟 —— `background: linear-gradient(currentColor,…)`
      里的 currentColor 在伪元素上继承的是 `.rt-seq` 的 `color:#fff`
      （白字），于是箭头是**白色**的，落在白底上等于隐形。
      查出来才发现的：getComputedStyle 返回 rgb(255,255,255)。
      教训：伪元素里用 currentColor 前先想一下它继承的是谁的 color。
      这里显式写死成序号圆的蓝。 */
.rt-grip::after{content:"";position:absolute;left:50%;top:100%;margin-top:3px;
  width:12px;height:6px;transform:translateX(-50%);
  background:
    linear-gradient(var(--primary),var(--primary)) 0 0/12px 2px no-repeat,
    linear-gradient(var(--primary),var(--primary)) 0 100%/12px 2px no-repeat;
  opacity:.6}
/* 给手柄多留 12px 的下边距，那对箭头有地方站 ——
   不留的话它会压到下一行的店名上。
   1.9.9：`.rt-leg .rt-seq{margin-bottom:11px}` 那两条删了 ——
   它们是给地图页路线面板的**竖向连线**留位置的，面板删了就没用了。
   工作台的行是横排、没有连线，留白反而会撑出空档。 */
.rt-grip:hover::before{box-shadow:0 0 0 5px rgba(79,110,247,.16)}
.rt-grip:active{cursor:grabbing}
/* 拖动中的那一行：抬起来 + 投影，让用户知道它「离开」了原位。
   transition 只加 transform 那一维 —— 加了会追不上手指（延迟感）。
   1.9.7：加 .hr-sort.dragging —— 工作台那套横排的行用的是同一个拖动
   逻辑（rtDragStart），不共用这一条的话，工作台拖起来会「没反应」
   （行跟着走了但看着还是贴在原地，用户以为没拖动成功）。

   ⚠️ .hr-sort 单独给一份底色：不能跟 var(--surface-2) 共用。
   那个浅底正是**排序模式整块的底色**（#homeRouteList.sorting），
   拖起来还是同一个色 = 白纸黑字贴在同色背景上，看着「没抬起来」。
   拖动态要给的是**比排序底色更深一档**的白，加上投影才浮得起来。

   1.9.9：`.rt-leg.dragging` 那条删了（面板没了）。
   下面这段注释原来是在解释「为什么 .hr-sort 不能跟 .rt-leg 共用底色」，
   现在 .rt-leg 不在了，但结论照样成立 —— 所以留着。 */
.hr-sort.dragging{transition:none;box-shadow:0 10px 26px rgba(31,42,80,.26);
  border-radius:10px;background:var(--card,#fff);border-bottom-color:transparent;
  /* 拖起来的行要盖住相邻行 —— 不给 z-index 的话，投影会被下一行的底色吃掉 */
  z-index:30}
/* 1.9.9：地图页那块路线面板删了，它**专用**的 CSS 一并清掉。
   留着的话是永远命不中的死规则 —— 而且下一个人看到 .rt-leg 会以为
   地图页还有这块 UI，正是删面板时注释里担心的那件事。
   清掉的是这几条（都用 `grep -n "rt-leg\|rt-ops" static/index.html`
   核过：除了这处样式定义和说明注释，没有任何 DOM 或 JS 再引用）：
     · .rt-leg / .rt-leg .rt-seq / .rt-leg .rt-leg:last-child
     · .rt-leg.dragging
     · .rt-leg .rt-main / .rt-nm / .rt-ad / .rt-dt
     · .rt-ops / .rt-ops > .btn / .rt-ops > .btn:nth-child(5)
   ⚠️ **别顺手删**这几个：它们名字像但工作台还在用 ——
     · .rt-seq（序号圆本身，工作台每行左边那个）
     · .rt-seq.start（起点那站）
     · .rt-grip（可拖手柄，1.9.7 从 .rt-leg .rt-seq 抽出来的公共类）
     · .rt-tip / .rt-tag / .rt-mode（工作台/别处都在用）
   判据是「有没有 DOM 引用」，不是「名字里带 rt-」。
   1.9.9 之前这里一直有这些规则，要恢复直接 git 回去，别凭记忆重写。 */
.rt-seq.start{background:var(--ok);font-size:11px}
/* 地图上的编号图钉(Leaflet 用 divIcon,高德直接塞 content) */
.rt-pin{width:26px;height:26px;border-radius:50%;background:var(--primary);color:#fff;
  font-size:12px;font-weight:700;display:flex;align-items:center;justify-content:center;
  border:2px solid #fff;box-shadow:0 1px 5px rgba(0,0,0,.3)}
.rt-pin.s{background:var(--ok);font-size:11px}
/* 1.9.9：路线弹窗里的目标点位改成**醒目的红**。
   用户原话：「弹窗地图侧的勾选客户后的 1、2、3 等目标点位的颜色
   需要改为更醒目的红色」。
   为什么原来那个蓝（--primary #4f6ef7）不够醒目：
   底图上的主干道、水域、POI 标记本身就有大量蓝青色，
   蓝点钉在中间要靠找；红色在这张底图上是**没有竞争色**的，
   一眼就能扫到。用 #e53935（比 --danger 再亮一档）而不是
   var(--danger)：danger 是「错误/删除」的语义色，用在这里
   会让人以为线路出错了。这是「位置标记红」不是「报错红」，
   所以单独定一个色，不蹭语义色变量。
   白边框 + 更重的投影：红底和深色路面撞色时靠白圈把边界分出来。 */
.rt-pin.red,.rt-pin.sel{background:#e53935;color:#fff}
.rt-pin.red{border-color:#fff;box-shadow:0 2px 8px rgba(229,57,53,.55)}
/* 选中态：再放大一圈 + 黄色描边（在红点堆里认出"我正在看这个"） */
.rt-pin.sel{width:32px;height:32px;font-size:14px;border:3px solid #ffd54f;
  box-shadow:0 3px 12px rgba(229,57,53,.7);z-index:500}
/* 起点「起」还是绿的（跟目标点区分开）。
   ⚠️ 2026-10-06：沿途候选点**不再是空心圈** —— 用户第三次纠错：
      「检索出来的点位要跟这边地图看板侧的一样啊」。
      看板侧是 pinUrl() 画的水滴图钉（状态色 SVG），弹窗里检索出来的
      候选点现在直接用**同一个** pinUrl(mapPointColor(p))，
      形状/颜色/尺寸逐字一致（见 rtmDrawCands）。
      .rt-pin.hollow 那套空心圈样式随之删掉 —— 没有使用方了，
      留着只会让人以为「候选点还是空心圈」。 */
/* 地图上候选 pin 的**选中态**：图是 <img>，改不了 SVG 填充色，
   所以用金色光晕圈出来（跟 .rt-pin.sel 的黄描边同一语义 ——
   「我正在看这个」），不引入第三种颜色。 */
.rt-pin-sel{filter:drop-shadow(0 0 3px #ffd54f) drop-shadow(0 0 1.5px #ffd54f);
  z-index:500}

/* ---- 1.9.9 路线地图弹窗 ---- */
/* 全屏铺开，不用 .modal 那个 560px 的窄卡 —— 地图窄了就没法看。
   高度用 dvh（动态视口高度）：手机上地址栏收起/展开时 100vh 会变，
   用 vh 的话底部那几个按钮会被地址栏盖掉。 */
.rtm-mask{position:fixed;inset:0;background:var(--scrim);z-index:70;
  display:flex;align-items:center;justify-content:center;padding:14px}
/* ⚠️ 2026-10-06（第六十二轮）：路线弹窗**开着**的时候，客户详情抽屉要
   **盖在弹窗上面**，不能又把弹窗关掉。
   默认的层级是 .mask 40 / .drawer 50，而 .rtm-mask 是 70、.rt-bar 是 75 ——
   抽屉压不过，所以第六十一轮是「先关弹窗再开抽屉」，代价就是用户这次报的
   「点关闭之后又回到工作台首页了，这个不合理，应该留在检索门店页」。
   现在弹窗开着时（body.rtm-open）临时抬到 78 / 79：
     79 > 75(横条) > 70(弹窗) —— 抽屉盖住弹窗，关掉抽屉就回到检索页；
     79 < 80(确认框) —— 成交/作废这类确认框该在抽屉之上的还是在上面。
   ⚠️ 只加 rtm-open 这一个作用域，首页/看板那份 40/50 **不动**：
      普通弹窗 .modal-mask 是 60，全局抬到 79 会盖住它们。 */
body.rtm-open .mask{z-index:78}
body.rtm-open .drawer{z-index:79}
.rtm{background:var(--card);border-radius:var(--r-lg);overflow:hidden;
  width:min(1180px,100%);height:min(760px,88dvh);display:flex;flex-direction:column;
  box-shadow:var(--sh-3,0 18px 48px rgba(20,28,54,.28))}
.rtm-head{display:flex;align-items:center;gap:9px;padding:11px 14px;
  border-bottom:1px solid var(--border);flex-wrap:wrap}
.rtm-head b{font-size:15px}
.rtm-head .rtm-sub{font-size:12px;color:var(--text-3)}
.rtm-x{margin-left:auto;border:0;background:none;font-size:20px;line-height:1;
  color:var(--muted);cursor:pointer;padding:2px 6px;border-radius:7px}
.rtm-x:hover{background:var(--surface-2);color:var(--text)}
.rtm-body{flex:1;min-height:0;display:flex}
#rtmMap{flex:1;min-width:0;background:var(--surface-2)}
/* 右侧抽屉式的候选点列表。宽屏并排、窄屏收到地图下面（见媒体查询） */
.rtm-side{width:308px;flex:none;border-left:1px solid var(--border);
  display:flex;flex-direction:column;min-height:0}
.rtm-side-h{padding:9px 12px;border-bottom:1px solid var(--border);flex:none}
.rtm-side-h b{font-size:13.5px}
.rtm-side-h .rtm-cnt{font-size:11.5px;color:var(--text-3);margin-left:6px}
/* 1.9.11：「离路线多远还算顺路」的范围选择。
   为什么做成**分档按钮**而不是滑块：手机上滑块难拖准，而且这个数本来
   就是档位语义（「隔一条街」「隔两三个街区」）—— 给他 300/500/800/1.5km
   比让他拖到 637 米更实用。
   ⚠️ 切档是**本地过滤**，不重新请求：后端一次就把 3km 内所有候选连同
      near 一起发回来了，改范围只是重新筛一遍，瞬变（不用再等 1~3 秒）。 */
.rtm-rng{display:flex;align-items:center;gap:4px;flex-wrap:wrap;flex:none;
  padding:7px 12px;border-bottom:1px solid var(--border)}
.rtm-rng .rtm-rl{font-size:11.5px;color:var(--text-3);margin-right:2px}
.rtm-rb{padding:3px 8px;border-radius:11px;border:1px solid var(--border);
  background:var(--surface-2);color:var(--text-2);font-size:11.5px;
  cursor:pointer;line-height:1.5;font-variant-numeric:tabular-nums}
.rtm-rb.on{background:var(--primary);border-color:var(--primary);color:#fff;
  font-weight:600}
/* 1.9.12：出行方式切换（开车 / 电动车）。
   ⚠️ 样式跟顺路度档位(.rtm-rb)刻意做出区别：档位是**本地过滤**（切了
      立刻变），出行方式是**换一条路线**（要重新请求 1~3 秒）。长得一样
      的话用户会以为点一下就该秒变，结果卡两秒以为卡死了。 */
.rtm-trav{background:var(--surface-2)}
.rtm-tb{padding:4px 10px;border-radius:12px;border:1px solid var(--border);
  background:var(--surface);color:var(--text-2);font-size:12px;
  cursor:pointer;line-height:1.5;display:flex;align-items:center;gap:4px}
.rtm-tb.on{background:var(--primary);border-color:var(--primary);color:#fff;
  font-weight:600}
.rtm-list{flex:1;overflow-y:auto;padding:6px 8px}
/* 表头全选行（本次规则 10）。跟客户列表那个全选行为一致，
   只是这里长在侧栏里、不是 <th>，所以单独一条样式。 */
.rtm-allrow{display:flex;align-items:center;gap:6px;padding:2px 10px 6px;
  font-size:12px;color:var(--text-3)}
.rtm-allrow input{width:15px;height:15px;flex:none;cursor:pointer}
.rtm-allrow label{cursor:pointer}
.rtm-alln{margin-left:auto;font-size:11.5px}
.rtm-row{display:flex;gap:9px;align-items:flex-start;padding:8px;border-radius:9px;
  cursor:pointer;border:1px solid transparent}
.rtm-row:hover{background:var(--surface-2)}
.rtm-row.on{background:var(--primary-soft);border-color:var(--primary)}
/* 2026-10-06（第四次改，用户纠错）：行首是**方框勾选框**，不是水滴图钉。
   用户原话：「检索出门店之后下方的门店需要有勾选框，勾选之后才弹出加入
   今日拜访」—— 第三次改我理解成「跟看板侧一样的图标」，做成了 pinUrl 水滴图；
   但用户要的是**能点着打勾的勾选框**（跟客户列表那个 .rt-pick 同款行为）。
   所以现在换成方框：
     · 尺寸/圆角/居中手法跟客户列表 .rt-pick 一致（好让两处心智统一）；
     · **边框取门店状态色**（--pc = mapPointColor）—— 保留「蓝公海 /
       绿待蹲店名 / 橙私海线索 / 红报价中 / 黑没意向」这层信息，
       勾选后是状态色描边 + 状态色勾 + 状态色淡光晕。
   ⚠️ 勾的颜色用 --pc 而不是白色：白底上白勾看不见。
   ⚠️ 别再改回水滴图 —— 用户要的是「勾选框」，图标再好看也点不出「我要加它」。 */
.rtm-row .rtm-ck{flex:none;margin-top:1px;width:22px;height:22px;
  border-radius:6px;border:1.5px solid var(--pc,#8a93a6);
  background:#fff;display:inline-flex;align-items:center;justify-content:center;
  font-size:12px;font-weight:700;line-height:1;color:transparent;
  cursor:pointer;user-select:none;position:relative;
  -webkit-tap-highlight-color:transparent;
  transition:transform var(--dur-1) var(--ease-spring),
             box-shadow var(--dur-1) var(--ease)}
.rtm-row .rtm-ck.sel{color:var(--pc);
  box-shadow:0 0 0 3px color-mix(in srgb, var(--pc) 22%, transparent)}
.rtm-row .rtm-ck.sel:active{transform:scale(.88)}
.rtm-row .rtm-ck:active{transform:scale(.92)}
[data-theme="dark"] .rtm-row .rtm-ck{background:var(--card)}
/* 1.9.10：`.rtm-row .rtm-i` 及它的 `.hollow` 都删了 ——
   1.9.10 时它的语义是「库里没有的陌生店」，而沿途检索现在只返回库里已有的门店。
   ⚠️ 2026-10-06 追记：地图点位的 `.rt-pin.hollow`（空心圈）**也删了** ——
      当天用户第三次纠错「检索出来的点位要跟地图看板侧的一样」，
      候选点改成了与看板侧同一个 pinUrl() 水滴图，空心圈没有使用方了。
      （此前那条「要留着」的备注作废，别按它把空心圈加回来。）
   ⚠️ 2026-10-06 再追记：`.rtm-row .rt-drop`（行首水滴图背景）**也删了** ——
      当天用户第四次纠错「下方门店需要有勾选框」，行首改成 .rtm-ck 方框勾选框。 */
.rtm-row .rtm-t{flex:1;min-width:0}
.rtm-row .rtm-n{font-size:13px;font-weight:600;display:block;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rtm-row .rtm-a{font-size:11.5px;color:var(--text-3);display:block;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rtm-row .rtm-tag{font-size:10.5px;padding:1px 6px;border-radius:20px;flex:none;
  background:#eef1f8;color:var(--text-3)}
.rtm-row .rtm-tag.pool{background:#fff3e0;color:#c76a00}
/* 行右侧「第几公里遇到 + 离路线多远 + 来源标签」竖着排。
   为什么不并进地址那一行：地址经常长到要省略号，
   距离被挤没了就没法判断这家顺不顺路 —— 而这是整张列表最该看清的数。 */
.rtm-row .rtm-r{flex:none;display:flex;flex-direction:column;align-items:flex-end;gap:3px}
/* 「离路线多少米」用红系配色，跟空心红圈是同一个语义（沿途候选）。
   ⚠️ 1.9.12 起这个位置显示的是「绕多少米」，配色沿用（还是红系）。 */
.rtm-row .rtm-near{font-size:10.5px;font-weight:600;color:#e53935;
  white-space:nowrap;font-variant-numeric:tabular-nums}
[data-theme="dark"] .rtm-row .rtm-near{color:#ff7b72}
/* .real = 这个绕路数是调高德真实路径算出来的（不是直线三角估的）。
   同一份清单里两种算法会混着：只有最顺路的几十家轮得到精算。
   不标出来的话，用户拿「绕 80m」和「绕 80m」比，其实一个是真路况、
   一个是直线估，排序就不公平了。加个小绿点，hover 有说明。 */
.rtm-row .rtm-near.real::after{content:"";display:inline-block;width:5px;height:5px;
  border-radius:50%;background:#2e7d32;margin-left:4px;vertical-align:middle}
[data-theme="dark"] .rtm-row .rtm-near.real::after{background:#56d364}
/* 「沿线第几公里」是 1.9.11 新增的主排序依据 —— 列表就是按它排的，
   所以给它一个中性色（不是红系）跟「离路线多远」区分开：
   前者是**顺序**（跑的时候第几个遇到），后者是**远近**（要不要绕）。 */
.rtm-row .rtm-along{font-size:10.5px;color:var(--text-3);white-space:nowrap;
  font-variant-numeric:tabular-nums}
/* 1.19.17（C）：「看着近、其实要绕一大圈」的提醒行。
   占满整行、在店名/地址下方 —— 不塞进右侧那列是因为右边已经有
   顺路度/第几公里/绕路/来源四个徽标，再挤会换行错位。
   用橙黄系（不是红色）：红色在这套界面里的语义是「错误 / 要折返」，
   而这条只是**提醒**——绕远不等于不能去，用户自己决定。
   ⚠️ 只在 rtmIsDetourTrap(p) 为真时才渲染这一行（多数行没有）。 */
.rtm-row .rtm-warn{margin-top:3px;font-size:10.5px;line-height:1.35;
  color:#b26a00;background:#fff8e6;border-radius:5px;padding:2px 6px;
  display:inline-block;max-width:100%}
[data-theme="dark"] .rtm-row .rtm-warn{color:#ffc46b;background:#3a2f16}
.rtm-empty{padding:22px 14px;text-align:center;color:var(--text-3);font-size:12.5px;
  line-height:1.8}
/* ⚠️ 2026-10-06（rule 11）：底部那张「展开的客户卡」.rtm-card **整块删掉了**。
   起因是用户截图报「这个地方已经无法滑动了，叠加太多了」——
   侧栏底部同时压着三层固定元素：#rtmCard（卡片）+ .rtm-foot（操作栏）
   + .rt-bar（全局横条），列表的可滚动区域被吃光。
   而卡片里的「加入今日拜访（N 家）」「取消勾选」与底部横条**功能重复**
   —— 规则 10 已经定「底部同一条横条」，卡片那套是第二个入口。
   现在看详情改成**右侧抽屉**（rtmOpenDetail，与首页客户列表点行的交互一致），
   卡片没有存在的必要了。
   ⚠️ 别再把这张卡加回来：它一出现就又把列表挤没了，而且和横条抢同一个动作。 */
.rtm-foot{padding:10px 14px;border-top:1px solid var(--border);display:flex;
  align-items:center;gap:8px;flex-wrap:wrap;flex:none}
.rtm-foot .rtm-tip{font-size:11.5px;color:var(--text-3);margin-right:auto;line-height:1.5}
.rtm-busy{position:absolute;inset:0;background:rgba(255,255,255,.82);z-index:600;
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:11px;
  font-size:13px;color:var(--text-2);text-align:center;padding:20px;line-height:1.7}
.rtm-wrap{position:relative;flex:1;min-width:0;display:flex}
[data-theme="dark"] .rtm-busy{background:rgba(26,30,41,.85)}
/* ⚠️ 2026-10-06（第六十二轮）：「怎么去 / 顺路度」搬进地图右下角的浮层。
   用户原话：「可以把怎么去的栏和顺路度放在地图的右下角」；起因是他报
   「下方的客户滑动区域太小了，最多只能显示两个」—— 这两行原来是侧栏里
   两块 flex:none 的固定高度，把列表的滚动区吃光了。
   白底半透明是用户选的：不点的时候不挡地图，只有按钮本身吃点击。
   ⚠️ z-index 1000 是必须的：地图内部 Leaflet 的 pane 是 400~800
      （.rtm-wrap 有 isolation:isolate，把它们关在这个盒子里），
      浮层低于它们就会被压到地图底下，肉眼看不见。
   ⚠️ 检索转圈时整块藏掉：`.rtm-busy:not(.hidden) ~ .rtm-ctrls` ——
      这条只认「浮层排在 busy 后面」，DOM 顺序别动（改 HTML 时留意）。 */
.rtm-ctrls{position:absolute;right:9px;bottom:9px;z-index:1000;
  display:flex;flex-direction:column;align-items:flex-end;gap:6px;
  pointer-events:none}
/* ⚠️ 第六十三轮：.rtm-ctrl 从「装一排小按钮的容器 div」变成**按钮本身**
   （两个单独按钮，点一下循环切换）。button 默认样式得自己重置：
   font-family 不继承、默认有 border/背景/内边距、也不给 cursor:pointer。 */
.rtm-ctrls .rtm-ctrl{pointer-events:auto;display:flex;align-items:center;gap:4px;
  padding:5px 7px;border-radius:10px;border:1px solid var(--border);
  background:rgba(255,255,255,.9);box-shadow:0 2px 10px rgba(31,42,80,.14);
  font-family:inherit;font-size:12px;font-weight:600;color:var(--text-1);
  line-height:1.25;white-space:nowrap;cursor:pointer;
  -webkit-appearance:none;appearance:none}
.rtm-ctrls .rtm-one{padding:6px 11px;font-size:12.5px}
/* 点下去有个回弹感 —— 循环切换没有「选中态」可看，全靠这一下反馈
   告诉用户「我收到了，正在换」。 */
.rtm-ctrls .rtm-one:active{transform:scale(.95)}
[data-theme="dark"] .rtm-ctrls .rtm-ctrl{background:rgba(38,43,58,.92)}
.rtm-ctrls .rtm-rl{font-size:11px;color:var(--text-3);flex:none}
.rtm-busy:not(.hidden) ~ .rtm-ctrls{display:none}
/* 窄屏：右侧列表收到地图下面，各占一半高度。
   ⚠️ 用 flex-direction:column 而不是把 .rtm-side 设成 width:100% ——
   .rtm-body 是 flex 行，只改宽度的话列表会被挤成零高度（min-height:0
   加 flex:1 的父元素里，交叉轴上的高度不跟着走）。 */
@media (max-width: 780px){
  .rtm{height:min(860px,94dvh);width:100%}
  .rtm-body{flex-direction:column}
  /* ⚠️ 2026-10-06（第六十二轮）：窄屏上列表原本只能看 2 行，两处一起改：
     ① 地图/列表的比例从 52:48 调成 46:54 —— 列表多拿 8% 的高度；
     ② 行右侧那列「沿线第几公里 / 绕多少米 / 来源」原来是竖排三行
        （.rtm-r 是 flex-direction:column），一行就顶到 90px 高。
        窄屏改成横排一行，行高掉到 ~60px，同样的高度能多看一行半。 */
  .rtm-wrap{flex:1 1 46%}
  .rtm-side{width:auto;flex:1 1 54%;border-left:0;border-top:1px solid var(--border)}
  .rtm-row{padding:6px 7px}
  .rtm-row .rtm-r{flex-direction:row;align-items:baseline;gap:6px}
  .rtm-list{max-height:100%}
  .rtm-head .rtm-sub{display:none}
  /* 窄屏地图本来就矮，右下角浮层再按桌面那个尺寸会占掉小半个地图 ——
     压一档：间距/内边距/字号全缩一号，按钮文字（开车/电动车/95%）保留。 */
  .rtm-ctrls{right:7px;bottom:7px;gap:5px}
  .rtm-ctrls .rtm-one{padding:5px 9px;font-size:11.5px}
}

/* ---- 公海池 ---- */
.pub-tip{background:#f6f2ff;border-radius:9px;padding:10px 14px;font-size:12.5px;
  color:#5a4a8a;line-height:1.7;margin-bottom:12px}
.pub-tip b{color:#6d43d1}
/* 公海池顶部筛选条（R 件，1.19.9 重写）。
   历史：这一条原来是「搜索框 + 四个下拉 + 计数」的横滑容器，
   2026-10-07 删了搜索框、改了 flex-wrap。现在（R 件）四个下拉全部
   收进弹窗，这一排只剩：筛选按钮 + 计数 + 批量操作按钮。
   ⚠️ 于是 overflow-x:auto 和 flex-wrap 都**不需要**了：
      三个元素加起来远小于任何屏宽，横滑和换行都是白挂。
      留着 overflow-x:auto 反而有害 —— 它会让这个容器成为新的
      "按钮可能被推出屏幕"的隐患（哪天再加一个元素就重演 2026-10-07
      那次「批量操作在屏幕外」的 bug）。改成纯 flex + nowrap，
      元素多了就自己挤，挤不下是"看得见的挤"，不是"看不见的滚"。
   1) 全局有 `input,select,textarea{width:100%}`（第 186 行）。下拉
      必须显式写 width:auto（弹窗里由 .pub-frow select 接管）。
   2) 弹窗里的 select 要 flex:1 吃掉剩余宽度（见 .pub-frow）。 */
/* ⚠️⚠️ 2026-10-08（R 件补）：.pub-row 是「第一排」的外壳，.pub-bar 只装
   左边的 [筛选] +「共 N 家」，**「批量操作」按钮是 .pub-row 的直接子元素、
   不在 .pub-bar 里**。
   为什么非要分两层：2026-10-07 修过一个 bug —— 那时 .pub-bar 是
   overflow-x:auto（四个下拉 412px 塞不进 390px 屏），横滑容器上
   **手机没有滚动条提示**，最右的「批量操作」被推到屏幕外，用户以为
   没有这个功能。所以当时把按钮挪出了 .pub-bar（t_topsearch 的 C 段守着）。
   R 件收两排时我一度把它又塞回 .pub-bar 里：那会儿 overflow-x 已经去掉了，
   按钮量出来确实在屏幕内（1440 / 768 / 390 都对），但**结构上的保险丢了** ——
   哪天有人给 .pub-bar 加回 overflow-x:auto，同一个 bug 立刻复活，
   而这次没人会注意到屏幕外的按钮。
   所以：视觉上还是同一排（.pub-row 是 flex，按钮 margin-left:auto 靠右），
   结构上按钮不在滚动容器里。两层各管一件事，别再并成一层。 */
.pub-row{display:flex;align-items:center;gap:8px;margin:10px 0 0}
.pub-bar{display:flex;gap:8px;align-items:center;min-width:0}
/* 第二排（R 件）：全选框靠右，跟「批量操作」对齐成一列。
   为什么靠右：全选是"对整批下手"的动作，跟上面那个「批量操作」
   是同一类，右边缘是它们一致的位置；放左边会和"筛选"混成一组，
   而筛选是"缩小范围"、全选是"扩大选择" —— 方向相反。 */
.pub-bulkline{display:flex;justify-content:flex-end;margin:8px 0 0}
/* 弹窗（R 件）：跟地图页那套 .mb-* 弹窗同一个长相。
   position:absolute 相对 .pub-mapwrap（它是 position:relative），
   所以弹窗挂在按钮正下方。z-index 要压过表格（表格没有 z-index，
   但抽屉/遮罩那套有 —— 这里是列表上方的浮层，给 30 足够）。 */
.pub-mapwrap{position:relative;display:inline-flex;flex:none}
.pub-mapbtn{display:inline-flex;align-items:center;gap:4px;height:34px;
  padding:0 11px;border:1px solid var(--border);border-radius:8px;
  background:var(--card);font-size:12.5px;color:var(--text-2);
  cursor:pointer;white-space:nowrap}
.pub-mapbtn:hover{border-color:var(--primary);color:var(--text)}
.pub-mapbtn-c{color:var(--text-3);font-size:10px;flex:none}
.pub-mpop{position:absolute;top:calc(100% + 4px);left:0;z-index:30;
  min-width:250px;background:var(--card);border:1px solid var(--border);
  border-radius:10px;box-shadow:0 8px 26px rgba(15,23,42,.18);
  padding:5px;overflow:hidden}
.pub-mpop-t{font-size:11px;color:var(--text-3);padding:4px 8px 6px}
/* 弹窗里的行：标签 + 控件两列。
   ⚠️ 不给 select 写 flex 的话它会按**内容**撑开（「全部标注时间」比
   「全部来源」宽），四行宽度参差不齐，看着像两个控件。这里让控件
   吃掉剩下的宽度。 */
.pub-frow{display:flex;align-items:center;gap:8px;padding:6px 8px;
  border-bottom:1px solid var(--border)}
.pub-frow:last-child{border-bottom:none}
.pub-flb{flex:none;width:56px;font-size:11.5px;color:var(--text-3)}
.pub-frow select{flex:1 1 0;min-width:0;height:32px;padding:0 8px;font-size:12.5px;
  border:1px solid var(--border);border-radius:7px;background:var(--card);
  color:var(--text);outline:none}
/* 「已选 N 项」那个小圆标，贴在筛选按钮上。
   为什么要有：筛了之后按钮还是写着「筛选」，用户看不出自己筛没筛、
   筛了几项 —— 尤其弹窗收起之后，唯一的痕迹就是这个数字。 */
.pub-fcnt{flex:none;min-width:16px;height:16px;padding:0 4px;border-radius:8px;
  background:var(--primary);color:#fff;font-size:10.5px;font-weight:600;
  display:inline-flex;align-items:center;justify-content:center}
/* 下拉给固定宽度而不是靠内容撑 —— flex:none 会把 flex-basis 变成 0，
   元素缩到最小内容宽（实测 57px），「最久未处理」被截断成「最久未…」。
   96px 够放 5 个汉字 + 箭头。
   ⚠️ 2026-10-07 之后这里的口径变过两次，别再按旧注释算账：
        · 搜索框（116px）已删；
        · 四个下拉（含「工作地图」）已收进弹窗，这一排只剩按钮+计数。 */
.pub-sel{flex:none;width:96px;height:34px;padding:0 4px;
  font-size:12.5px;border:1px solid var(--border);border-radius:8px;
  background:var(--card);color:var(--text)}
/* 高德接口要联网（/api/wia/maps），拉不到时下拉该显示「地图（取不到）」
   而不是留一个空框让人以为筛完是 0 家 —— 空框和「筛完真的 0 家」
   在界面上长得一模一样，两种完全不同的处境必须能分辨。 */
#pubMap:disabled{opacity:.55}
.pub-cnt{flex:none;font-size:12px;color:var(--muted);white-space:nowrap}
/* 「批量操作」按钮：同一排里推到最右边（R 件，1.19.9）。
   margin-left:auto 是 flex 容器里的"吃掉剩下的空白"写法 —— 比
   justify-content:space-between 稳，因为它只管这一个元素，
   以后在前面加东西不用重算分配。 */
.pub-bar-right{margin-left:auto;flex:none}
.pub-bulk{display:flex;gap:8px;align-items:center;flex-wrap:wrap;
  margin:10px 0 0;padding:8px 10px;border-radius:10px;
  background:var(--surface-2, #f2f5fb);font-size:13px}
.pub-bulk .btn{min-height:32px}
.pub-chk{width:22px;height:22px;flex:none;cursor:pointer}
/* 勾选框外面那层（S3，1.19.9）。它是 <label> 包 <input> ——
   点文字/空白也能勾上，手指不用精确点在 22px 的方块上。
   ⚠️ 显隐规则跟着 .bulk-mode 走（下面那两条），这里只管命中区和光标。
   ⚠️ 加 padding 是为了把热区撑大：22px 的方块在手机上偏小，
      但**不能**把 input 本身调大（它会被 c-name 那排的 flex 顶歪），
      所以在外面包一层加 padding 是正解。 */
.pub-chk-wrap{display:none;align-items:center;justify-content:center;
  padding:4px;cursor:pointer;border-radius:8px}
.pub-chk-wrap:hover{background:var(--surface-2, #f2f5fb)}
/* 「全选」那一小块（S4，1.19.9）：跟「批量操作」同一列、在它正下方。
   只在批量模式下出现 —— 不批量的时候勾选框本身都不渲染，
   留一个全选框在那儿点不动（勾了也没有目标）反而让人困惑。
   ⚠️ 显隐由 pubSyncBulk() 统一管（它已经有 pubBulkMode 这个状态），
      不用 CSS 猜 —— CSS 挂 class 的方式在这里会和 renderPublic
      重画时机打架。 */
.pub-allbox{display:none;align-items:center;gap:5px;cursor:pointer;
  user-select:none;font-size:12.5px;color:var(--text-2)}
/* 2026-10-07：公海池的批量勾选框默认隐藏，点「批量操作」才出现。
   为什么要这样（用户原话）：「批量删除顶加上个按钮，点击之后客户前面才出现勾选框」。
   打开公海池就每行挂一个空勾选框是噪音，而批量删除/释放是低频动作。
   ⚠️ S3（1.19.9）改了**位置**（从最右一列 → 店名那排右侧）之后，
      这套显隐规则跟着改成作用于 .pub-chk-wrap —— 原来挂的是
      .pub-chk-col（那一列已经整个撤掉了，留着这条 CSS 等于没有效果，
      勾选框会**一直显示**，公海池每行都挂个空框）。 */
#publicView .table-card .pub-chk-wrap{display:none}
#publicView .table-card.bulk-mode .pub-chk-wrap{display:flex}
.btn-release{color:#7c4ddb;border-color:#e2d6fb}
.btn-release:hover{background:#f6f2ff;border-color:#7c4ddb;color:#7c4ddb}
.ava-row{display:inline-flex;gap:4px;flex-wrap:wrap}
.colab-box{display:flex;flex-wrap:wrap;gap:6px;margin-top:6px}
/* 做小一点：成员多时一屏能看全。原来 padding 5/10 + 12.5px 字号，
   手机上视觉很占地方（用户反馈"协作人的按键也非常大"）。
   用 12px 字号 + 3/9 padding —— 仍然满足点击（宽高都 > 24px），
   但不再像一排大按钮。 */
.colab-chip{display:inline-flex;align-items:center;gap:4px;
  padding:4px 10px;border-radius:14px;font-size:12px;line-height:1.4;
  min-height:0;              /* 不吃全局那条 40px 触摸底线 */
  white-space:nowrap;        /* 姓名不许换行 —— 换行会把按钮撑高 */
  border:1px solid var(--border);background:#fff;font-size:12.5px;cursor:pointer;user-select:none}
.colab-chip input{margin:0;accent-color:var(--primary)}
.colab-chip.on{background:#eef2ff;border-color:var(--primary);color:var(--primary);font-weight:600}
.imp-tips{background:#f4f7ff;border-radius:9px;padding:12px 14px;font-size:13px;color:#4a5680;line-height:1.7;margin-bottom:14px}
.file-box{border:2px dashed var(--border);border-radius:10px;padding:26px;text-align:center;
  color:var(--muted);cursor:pointer;transition:border .15s}
.file-box:hover{border-color:var(--primary);color:var(--primary)}
.file-box.has{border-color:var(--ok);color:var(--ok);font-weight:600}
.imp-result{margin-top:12px;font-size:13px;line-height:1.7}
.imp-result.ok{color:var(--ok)} .imp-result.bad{color:var(--danger)}
/* ---- 批量导入预览 ---- */
.imp-map{margin-top:12px;font-size:12.5px;color:var(--muted);line-height:1.8;
  background:#f7f9fc;border-radius:8px;padding:8px 10px;word-break:break-all}
.imp-stat{margin-top:10px;font-size:13.5px}
.imp-stat .ok{color:var(--ok)} .imp-stat .bad{color:var(--danger)}
.imp-opts{display:flex;flex-wrap:wrap;gap:8px 14px;align-items:center;margin-top:10px;font-size:12.5px;color:var(--muted)}
.imp-opts label{display:flex;align-items:center;gap:4px}
.imp-opts select,.imp-opts input[type=text],.imp-opts input:not([type]){padding:4px 6px;border:1px solid var(--border);border-radius:6px;font-size:12.5px}
.imp-warn{margin-top:8px;font-size:12px;color:#b7791f;background:#fff8e6;border-radius:7px;padding:7px 10px;line-height:1.6}
.imp-table{width:100%;border-collapse:collapse;font-size:12.5px}
.imp-table th{text-align:left;color:var(--muted);font-weight:600;padding:5px 6px;border-bottom:1px solid var(--border)}
.imp-table td{padding:5px 6px;border-bottom:1px solid #f0f2f7;vertical-align:top}
.dup-tag{display:inline-block;margin-left:5px;font-size:11px;color:#b7791f;background:#fff3d6;
  border-radius:4px;padding:1px 5px;white-space:nowrap}

/* ---- 关于弹窗(点左上角「商」标) ---- */
.ab-body{max-height:60vh;max-height:60dvh}
/* 「关于」弹窗里的个人信息入口（1.9.3）。
   从顶栏那个常驻的块搬过来的。样式不复用原来的 .me-box ——
   那个是给顶栏设计的（圆角胶囊、贴着一排按钮），
   在这里要的是「一张可点的卡片」：有边框、有底色、
   箭头在右端提示能点。硬套 .me-box 会看起来像顶栏掉下来一块。 */
.ab-me{display:flex;align-items:center;gap:11px;padding:11px 12px;margin-top:12px;
  border:1px solid var(--border);border-radius:11px;background:var(--surface-2);
  cursor:pointer;transition:border-color var(--dur-1) var(--ease),
  background-color var(--dur-1) var(--ease),transform var(--dur-1) var(--ease-spring)}
.ab-me:hover{border-color:var(--primary);background:var(--primary-soft,rgba(23,105,255,.05))}
.ab-me:active{transform:scale(.99)}
/* 键盘焦点也要看得见 —— role=button 加了 tabindex 就得配上，
   否则键盘用户 Tab 到这里完全不知道自己在哪 */
.ab-me:focus-visible{outline:2px solid var(--primary);outline-offset:2px}
.ab-me-av{width:36px;height:36px;border-radius:50%;color:#fff;font-size:15px;
  font-weight:600;display:inline-flex;align-items:center;justify-content:center;flex:none}
.ab-me-tx{min-width:0;flex:1;display:flex;flex-direction:column;gap:2px}
.ab-me-n{font-size:14px;font-weight:600;color:var(--text);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ab-me-s{font-size:11.5px;color:var(--muted);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* 箭头不用 › 字符 —— 项目里这类指示符统一走 ::after 画的，
   字符版本在部分 Android 字体下会缺字形（顶栏铃铛那次的豆腐块） */
.ab-me-go{flex:none;color:var(--muted);
  transition:transform var(--dur-1) var(--ease),color var(--dur-1) var(--ease)}
.ab-me-go::after{content:"";display:block;width:7px;height:7px;
  border-right:1.8px solid currentColor;border-top:1.8px solid currentColor;
  transform:rotate(45deg)}
/* 折角是 45° 斜的，视觉上会偏上偏右一点 —— 手动压一下居中，
   不然它看起来比整行的中心高那么两像素。 */
.ab-me-go{display:flex;align-items:center;padding:0 2px 2px 0}
.ab-me:hover .ab-me-go{color:var(--primary);transform:translateX(2px)}

.ab-hero{display:flex;align-items:center;gap:14px;padding:4px 0 14px;
  border-bottom:1px solid var(--border);margin-bottom:12px}
.ab-logo{width:52px;height:52px;border-radius:15px;flex:none;background:var(--primary);color:#fff;
  display:grid;place-items:center;font-size:24px;font-weight:700;
  box-shadow:0 6px 18px rgba(23,105,255,.28)}
.ab-name{font-size:19px;font-weight:700;letter-spacing:-.2px}
.ab-ver{font-size:12.5px;color:var(--muted);margin-top:3px;
  font-variant-numeric:tabular-nums;letter-spacing:.2px}
.ab-sec-t{font-size:12px;font-weight:700;color:var(--muted);letter-spacing:.4px;
  margin:14px 0 7px;display:flex;align-items:center;gap:6px}
.ab-hint{font-weight:400;color:var(--muted);opacity:.75}
.ab-dev{display:grid;grid-template-columns:1fr 1fr;gap:1px;background:var(--border);
  border-radius:9px;overflow:hidden}
.ab-dev-row{background:var(--card);padding:8px 10px;font-size:12.5px;
  display:flex;flex-direction:column;gap:2px;min-width:0}
.ab-dev-row span{color:var(--muted);font-size:11.5px}
.ab-dev-row b{font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ab-hl{display:grid;grid-template-columns:1fr 1fr;gap:8px}
.ab-hl-card{display:flex;gap:9px;align-items:flex-start;background:var(--surface-2);
  border-radius:9px;padding:9px 10px}
.ab-hl-i{font-size:17px;line-height:1.2;flex:none}
.ab-hl-card b{font-size:12.5px;display:block}
.ab-hl-d{font-size:11.5px;color:var(--muted);margin-top:2px;line-height:1.5}
.ab-log{display:flex;flex-direction:column;gap:6px}
/* 关于页底部的回归报告入口:脱敏公网页,不登录也能转发给别人看 */
.ab-rep{margin-top:14px;padding:10px 12px;border:1px dashed var(--border);border-radius:9px;
  font-size:12.5px;color:var(--primary);cursor:pointer;display:flex;align-items:center;gap:6px;
  justify-content:space-between;background:var(--surface-2)}
.ab-rep:active{opacity:.6}
.ab-rep-x{font-size:11.5px;color:var(--muted)}
.ab-rel{border:1px solid var(--border);border-radius:9px;overflow:hidden;background:var(--card)}
.ab-rel.on{border-color:var(--primary);background:var(--primary-soft,rgba(23,105,255,.05))}
.ab-rel-h{display:flex;align-items:center;gap:8px;padding:9px 10px;cursor:pointer;font-size:12.5px}
.ab-rel-v{flex:none;font-weight:700;color:var(--primary);
  background:var(--primary-soft,rgba(23,105,255,.1));border-radius:5px;padding:1px 6px;
  font-variant-numeric:tabular-nums;font-size:11.5px}
.ab-rel-t{font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ab-rel-d{color:var(--muted);font-size:11.5px;margin-left:auto;flex:none;
  font-variant-numeric:tabular-nums}
.ab-rel-x{flex:none;color:var(--muted);font-size:11px}
.ab-rel.on .ab-rel-x::after{content:"▾"}
.ab-rel:not(.on) .ab-rel-x::after{content:"▸"}
.ab-rel-ul{display:none;margin:0;padding:0 12px 11px 12px;list-style:none}
.ab-rel.on .ab-rel-ul{display:block}
.ab-rel-ul li{font-size:12.5px;color:var(--muted);line-height:1.7;padding-left:13px;position:relative}
.ab-rel-ul li::before{content:"";position:absolute;left:2px;top:9px;width:4px;height:4px;
  border-radius:50%;background:var(--primary);opacity:.55}
@media(max-width:520px){.ab-dev,.ab-hl{grid-template-columns:1fr}}

/* ---- 高德同步面板 ----
   2026-10-05 从产品经理视角重排；当晚再拆成顶部两个 tab。
   第一版的问题是信息层级：337 个点挤在 2 行半高的表格里、4 个大数字
   KPI 占掉 100px 高（其中"10/337 负责人已读"是内部指标，用户不关心），
   一屏 7 个可交互区互相抢注意力，而用户来这里只有一件事：
   看这批点、勾掉不要的、导进去。
   第二版的问题是：既然用户来这儿的**主流程只有"拉点"**，
   那"推到高德"就不该和它上下堆在同一个面板里 —— 一个要占满高度
   看 337 行表，一个只是偶尔用的筛选，挤一起结果是两个都难用。
   于是拆成顶部两个 tab：一个界面只干一件事，滑动区域也就只剩一层。 */

/* 顶栏两个 tab。左右各占一半、等宽对称 —— 手机上两边的点击目标一样大，
   不会出现"左边那块好点、右边得瞄准"的手感差异。 */
.wia-tabs{display:flex;gap:0;border-bottom:1px solid var(--border);
  background:var(--surface-2);flex:none;padding:0 6px}
.wia-tab{flex:1 1 0;min-width:0;display:flex;align-items:center;justify-content:center;
  gap:5px;padding:11px 6px;font-size:13px;font-weight:600;color:var(--muted);
  background:transparent;border:none;border-bottom:2px solid transparent;
  cursor:pointer;transition:color .15s,border-color .15s}
.wia-tab-i{font-size:12px;opacity:.8}
.wia-tab.on{color:var(--primary);border-bottom-color:var(--primary)}
/* tab 右端的数字：点进去之前就知道那边有多少东西 */
.wia-tab-n{font-size:11px;font-weight:700;padding:1px 5px;border-radius:9px;
  background:var(--surface-3,rgba(0,0,0,.06));color:var(--muted);
  font-variant-numeric:tabular-nums;min-width:18px;text-align:center}
.wia-tab.on .wia-tab-n{background:var(--primary);color:#fff}

/* 面板主体纵向 flex：状态条、摘要、折叠卡片高度固定，列表吃剩下的，
   于是「只有列表会滚」。overflow-y:auto 兜底 —— 设置全展开时还能往上翻。 */
.wia-body{display:flex;flex-direction:column;overflow-y:auto;padding:10px 14px}
/* 要让列表吃满剩余高度，得从面板一路 flex 到列表，中间每一层都得是
   flex 容器：卡片 → #wiaPullBox → .wia-list → .wia-list-scroll。
   中间哪一层不是，flex:1 就传不下去 —— 之前踩过：列表明明写了
   flex:1 1 auto，却因为它是 #wiaPullBox 的孙子而被原样摊开成 1400px。 */
.wia-body>*{flex:none}
/* 推送侧整块吃满面板高度，名单才有地方滚。
   不写这行的话 #wiaPanePush 会被上面那条 flex:none 变成内容高度，
   名单区 .wia-push-list 的 flex:1 传不下去，只能按内容摊开。 */
#wiaPanePush{flex:1 1 auto;min-height:0}
.wia-body>.wia-card-pull{flex:1 1 auto;min-height:0;display:flex;flex-direction:column}
.wia-body>.wia-card-pull>*{flex:none}
#wiaPullBox{flex:1 1 auto;min-height:0;display:flex;flex-direction:column}
#wiaPullBox>*{flex:none}
#wiaPullBox>.wia-list{flex:1 1 auto}
/* 还没读点时，拉点按钮占满整行 —— 这时候面板里只有这一件事可做 */
.wia-pull-btn{width:100%;padding:11px;font-size:14px;border-radius:9px}
.wia-loading{padding:14px;color:var(--muted);font-size:13px;text-align:center}
.wia-err{padding:12px;color:var(--danger);font-size:13px;text-align:center}
/* 这个面板比别的弹窗宽：337 行店名 + 搜索 + 筛选下拉，窄了没法用。
   别写 max-width:100vw —— .modal-mask 两侧各有 20px padding，
   贴到屏幕边缘会顶掉系统手势区，留 2px 余量即可。 */
.wia-modal{width:720px;max-width:calc(100vw - 8px);max-height:95vh;max-height:95dvh}
/* 标题栏紧贴下面的 tab 栏：默认 m-head 是 18px 上内边距，
   面板已经有 tab 当主导航了，标题栏再占一截纯浪费垂直空间。 */
.wia-modal .m-head{padding-top:14px}
.wia-modal .m-foot{padding-left:14px;padding-right:14px}
#wiaPullBox{flex:1 1 auto;min-height:0;display:flex;flex-direction:column}
#wiaPullBox>*{flex:none}
#wiaPullBox>.wia-list{flex:1 1 auto}
.wia-bar{display:flex;align-items:center;gap:8px;font-size:12.5px;
  background:var(--surface-2);border:1px solid var(--border);border-radius:9px;
  padding:7px 10px;margin-bottom:9px;line-height:1.6}
.wia-bar.is-bad{background:var(--danger-soft);border-color:#ffd7de;color:var(--danger)}
.wia-bar .ok{color:var(--ok)} .wia-bar .bad{color:var(--danger)}
/* 头部那条里的换图下拉 + 「当前工作地图」提示（2026-10-07）。
   下拉复用 .wia-map-sel（原本给结果区那个，现在只有头部这一处了）；
   tip 在窄屏可以整体换行，不把下拉挤没。 */
.wia-bar .wia-bar-tip{color:var(--text-3);font-size:11.5px;min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* 结果区那行「《地图名》」—— 换图控件搬走之后，这一行负责告诉用户
   下面这些点是哪张图来的（原来那行是个下拉，选完就被自己覆盖了）。 */
.wia-sum-map{color:var(--text-2);font-weight:600}
.wia-sum-map b{color:var(--text)}
/* 空地图提示里那句"下一步怎么走"（`.mp-empty` 是共用 class，别改它本身） */
.mp-empty-tip{display:inline-block;margin-top:7px;font-size:12px;line-height:1.7;
  color:var(--text-3);max-width:34em}
.wia-card{border:1px solid var(--border);border-radius:10px;padding:10px 12px;margin-bottom:10px}
.wia-card-h{display:flex;align-items:center;gap:8px;margin-bottom:9px}
.wia-step{width:19px;height:19px;border-radius:50%;background:var(--primary);color:#fff;
  font-size:11.5px;font-weight:700;display:grid;place-items:center;flex:none}
.wia-card-h b{font-size:13.5px}
.wia-card-s{font-size:11.5px;color:var(--muted);margin-left:auto}
.wia-row{display:flex;flex-wrap:wrap;gap:8px 12px;align-items:center;font-size:12.5px}
.wia-row select{padding:4px 6px;border:1px solid var(--border);border-radius:6px;font-size:12.5px;
  background:var(--card);color:var(--text);max-width:150px}
.wia-ck-l{display:flex;align-items:center;gap:4px;cursor:pointer}
/* 拉点结果的摘要：压成一行，替代上一版的四格大数字。
   337 个点里 127 个缺坐标（38%）是唯一影响数据质量的风险，
   单独用警示色标出来 —— 之前它混在一句 12px 灰字里，没人看得见。 */
.wia-sum{display:flex;align-items:center;flex-wrap:wrap;gap:5px 12px;
  margin:9px 0 0;padding:7px 11px;background:var(--surface-2);border-radius:9px;
  font-size:12.5px;color:var(--muted);line-height:1.6}
.wia-sum b{color:var(--text);font-weight:700;font-variant-numeric:tabular-nums;
  font-size:14px;margin-right:1px}
.wia-sum-ok b{color:var(--ok)}
.wia-sum-bad{color:var(--danger)}
.wia-sum-bad b{color:var(--danger)}
/* 工作地图下拉。放在摘要行最左边 —— 它决定「下面这些点是谁」，
   是这一屏的前置条件，不该藏在设置里。宽度按最长地图名给足，
   别让「外勤拓客地图」被截成「外勤拓客…」。 */
.wia-map-sel{flex:none;max-width:190px;height:26px;padding:0 24px 0 8px;
  font-size:12.5px;font-weight:600;border-radius:6px;cursor:pointer;
  background:var(--surface);color:var(--text);
  border:1px solid var(--line);appearance:none;
  background-image:linear-gradient(45deg,transparent 50%,var(--muted) 50%),
                   linear-gradient(135deg,var(--muted) 50%,transparent 50%);
  background-position:calc(100% - 13px) 11px,calc(100% - 8px) 11px;
  background-size:5px 5px,5px 5px;background-repeat:no-repeat}
.wia-map-sel:focus{outline:2px solid var(--brand);outline-offset:1px}
.wia-sum .btn-xs{margin-left:auto;padding:3px 9px;font-size:11.5px}
.wia-blk{margin-top:8px;padding:8px 10px;background:var(--surface-2);border-radius:9px}
.wia-blk-t{font-size:12.5px;font-weight:600;margin-bottom:6px;
  display:flex;align-items:baseline;gap:6px;flex-wrap:wrap}
/* 一行紧凑表单:标签贴控件,控件间距统一,不再一个一个竖着堆 */
.wia-frm{display:flex;flex-wrap:wrap;gap:6px 12px;align-items:center;font-size:12.5px}
.wia-frm label{display:flex;align-items:center;gap:5px;white-space:nowrap}
.wia-frm select{padding:4px 6px;border:1px solid var(--border);border-radius:6px;font-size:12.5px;
  background:var(--card);color:var(--text)}
/* 这里刻意不用 flex —— 一旦 display:flex,行内混排的文本节点和 <b> 会各自
   变成独立 flex item,"点位状态默认蓝；<b>待蹲店名</b>留给…" 会被拆成
   三段并各自换行,粗体那两个字还会被撑成一行大字。按钮本来就是
   inline-block,文字照样能跟它同行。 */
.wia-line{font-size:12px;color:var(--muted);line-height:1.7;margin-top:6px}
.wia-line .btn{vertical-align:middle}
.wia-line.is-ok{color:var(--ok)}
/* 「落到哪儿」三选一:横排分段按钮。原来是三个 radio 竖着堆,
   每条后面还缀一段解释,一眼看过去全是字,选哪个反而不显眼。 */
.wia-seg{display:flex;gap:6px;flex-wrap:wrap}
.wia-seg-i{position:relative;cursor:pointer}
/* radio 铺满整个按钮而不是 width:0;height:0。两种写法用户点起来一样,
   但后者在无障碍上是残的(有框才有焦点样式)、自动化里也点不到 ——
   Playwright 判"可见"看的是 bounding box 非空,零尺寸直接算不可见,
   t_wia.py 的 check() 会一直重试到超时。pointer-events:none 让点击
   穿透到下面的 radio,不用依赖 label 冒泡。 */
.wia-seg-i input{position:absolute;inset:0;width:100%;height:100%;opacity:0;
  margin:0;cursor:pointer}
.wia-seg-i span{pointer-events:none}
.wia-seg-i span{display:block;padding:6px 11px;border:1px solid var(--border);border-radius:8px;
  font-size:12.5px;background:var(--card);color:var(--text);white-space:nowrap}
.wia-seg-i input:checked+span{border-color:var(--primary);color:var(--primary);font-weight:600;
  background:var(--primary-soft,rgba(23,105,255,.08))}
.wia-seg-i input:focus-visible+span{outline:2px solid var(--primary);outline-offset:1px}
/* 列表吃掉面板里所有剩下的高度 —— 面板主体不再滚，列表是唯一会滚的地方。
   之前是「主体滚 + 表格内滚」两层，手机上不知道该推哪一层。
   min-height 是下限的兜底：把「导入设置」整个展开时（三个块 + 映射表，
   要 260px），列表不能被压成两三行就废了，至少留 4 行 ≈ 108px，
   再矮用户就只能合上设置再看了。 */
.wia-list{border:1px solid var(--border);border-radius:10px;margin-top:7px;overflow:hidden;
  flex:1 1 auto;min-height:150px;display:flex;flex-direction:column;position:relative}
/* 工具条必须**一行**排下。手机上它换到 3 行就吃掉 66px，
   相当于少看两个点 —— 而"一屏能看几行"是这个面板的命门。
   手段：搜索框 min-width:0 让它能被压扁，两个下拉限窄 + 不换行，
   计数用 margin-left:auto 顶到中间的空隙里。 */
.wia-list-bar{display:flex;align-items:center;gap:4px;flex-wrap:nowrap;padding:5px 7px;
  border-bottom:1px solid var(--border);background:var(--surface-2);flex:none}
/* 337 行里找一家店靠肉眼翻不动 —— 搜索框是这个列表能用的前提 */
.wia-q{flex:1 1 60px;min-width:52px;padding:5px 7px;border:1px solid var(--border);
  border-radius:7px;font-size:12.5px;background:var(--card);color:var(--text)}
.wia-q:focus{outline:none;border-color:var(--primary)}
.wia-list-bar select{flex:0 0 auto;width:86px;padding:5px 3px;border:1px solid var(--border);
  border-radius:7px;font-size:12px;background:var(--card);color:var(--text);
  text-overflow:ellipsis;min-width:0}
.wia-list-bar .btn-sm{flex:none;padding:5px 8px;font-size:12px;min-height:28px}
/* 筛选出多少、勾了多少。挤在按钮前面的空隙里，不单占一行。 */
.wia-list-n{font-size:11px;color:var(--muted);font-variant-numeric:tabular-nums;
  white-space:nowrap;overflow:hidden;flex:0 1 auto}
.wia-list-n b{color:var(--text)}
.wia-list-scroll{flex:1 1 auto;min-height:0;overflow:auto;overscroll-behavior:contain}
/* 底部渐隐：没有它，最后一行会被 border-radius + overflow:hidden 拦腰切断，
   看着像渲染出错。24px 渐变提示"下面还有"，比硬切更像有意为之。 */
.wia-list-fade{position:absolute;left:1px;right:1px;bottom:1px;height:22px;
  border-radius:0 0 9px 9px;pointer-events:none;
  background:linear-gradient(to top,var(--card) 12%,transparent)}
.wia-tb thead th{position:sticky;top:0;z-index:2;background:var(--card);
  box-shadow:0 1px 0 var(--border)}
/* 店名单行显示 + 溢出省略。真实店名带日期前缀（"8.14two pigs bakery"），
   长 39~44 字符，不截断的话整张表宽到没法看。
   ⚠ 行高是"一屏能看几行"的直接乘数：337 行里每行矮 8px，一屏就多看两行。
   所以这里压到单行 13px + 5px 内边距，负责人等次要信息放进 title，
   不再为它单开一行 —— 之前每行 34px，一屏只装得下 7 行。 */
.wia-tb td{padding:4px 6px;vertical-align:middle;white-space:nowrap}
.wia-nm{display:block;font-size:12.5px;line-height:18px;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
/* 折叠卡片(② 推送 / 负责人映射):默认收起,summary 就是标题行 */
.wia-fold{border:1px solid var(--border);border-radius:10px;margin-bottom:10px;background:var(--card)}
.wia-fold>summary{display:flex;align-items:center;gap:8px;padding:10px 12px;cursor:pointer;
  font-size:13px;font-weight:600;list-style:none}
.wia-fold>summary::-webkit-details-marker{display:none}
.wia-fold>summary::after{content:"▾";margin-left:auto;color:var(--muted);font-size:12px}
.wia-fold[open]>summary{border-bottom:1px solid var(--border)}
.wia-fold-s{font-weight:400;font-size:11.5px;color:var(--muted)}
.wia-fold-b{padding:8px 12px 10px}
/* 导入设置浮层。2026-10-04 用户报"点开导入设置后双层叠加导致无法使用"：
   以前它是 inline 的 <details>，展开占 303px 高度，把 337 行的点位表
   从 412px 压到 151px（13 行 → 4 行），两个都要独立空间的界面挤在一屏。
   改成浮层后：设置不参与高度分配，盖在上面，列表始终保持 13 行。
   「看列表」和「改设置」变成两件互不干扰的事。 */
.wia-setbar{flex:none;padding:0 0 8px}
.wia-setbtn{width:100%;display:flex;align-items:center;gap:8px;padding:8px 11px;
  border:1px solid var(--border);border-radius:9px;background:var(--card);
  font-size:12.5px;font-weight:600;color:var(--text);cursor:pointer;text-align:left}
.wia-setbtn span:first-child{flex:none}
.wia-setbtn .wia-fold-s{flex:1 1 auto;text-align:right;min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* 导入设置浮层。⚠️ 高度别写死 inset:0 —— 那样四项设置会撑满一屏，
   下面空出四百多像素空白，看起来像没加载出来。改成顶部对齐、高度跟着
   内容走，内容超高才内部滚动（映射表展开后有 20+ 个标注人）。 */
.wia-setpane{position:absolute;left:0;right:0;top:0;z-index:5;background:var(--card);
  display:flex;flex-direction:column;max-height:100%;max-height:100dvh;
  border-radius:var(--r-lg);overflow:hidden;
  box-shadow:0 8px 28px rgba(15,23,42,.18)}
.wia-setpane-hd{flex:none;display:flex;align-items:center;gap:8px;
  padding:13px 15px;border-bottom:1px solid var(--border)}
.wia-setpane-hd b{flex:1 1 auto;font-size:14.5px}
.wia-setpane-bd{flex:1 1 auto;min-height:0;overflow-y:auto;padding:11px 14px;
  overscroll-behavior:contain}
.wia-setpane-ft{flex:none;padding:10px 14px;
  padding-bottom:calc(10px + env(safe-area-inset-bottom,0px));border-top:1px solid var(--border)}
/* 浮层里的块不再需要"并排省高度"—— 它们独占一屏了 */
#wiaSetBox .wia-set2{grid-template-columns:1fr;gap:9px}
#wiaSetBox .wia-blk{padding:9px 11px}
#wiaSetBox .wia-fold{margin:9px 0 0}
.wia-map-row{display:flex;align-items:center;gap:8px;padding:5px 0;font-size:12.5px;
  border-bottom:1px solid #f0f2f7}
.wia-map-row:last-child{border-bottom:none}
.wia-nick{font-weight:600;min-width:0;flex:1 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.wia-cnt{color:var(--muted);font-size:11.5px;white-space:nowrap}
.wia-map-row select{padding:3px 6px;border:1px solid var(--border);border-radius:6px;
  font-size:12.5px;background:var(--card);color:var(--text);max-width:120px}
/* 底栏主按钮按 tab 二选一显示。必须用 class + !important，
   光靠 .btn 的 display 压不住 —— 见 wiaTab() 里的注释。 */
.wia-hid{display:none!important}
/* 底栏：左边写「已选几个、落到哪儿」，右边按钮。列表在滚动区里，
   勾选时看不到底栏就没法确认，所以把结果搬到常驻的底栏上。
   底栏跟着 tab 换：拉点 tab 是「导入 N 个」，推送 tab 是「推送 N 个」——
   共用一个按钮会让人在推送 tab 上看到"导入选中"然后奇怪为什么点不动。 */
.wia-foot-tip{flex:1 1 auto;margin-right:auto;font-size:12.5px;color:var(--muted);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* ══ 推送 tab ══
   推送是外部不可逆写入，这一侧从下到下就是一条线：
   选条件 → 看数量和名单 → 按推送。中间那步以前根本没有，
   只给一个「推送 12 个」的按钮让人盲点。 */
#wiaPanePush{padding:12px 14px}
.wia-push-frm{border:1px solid var(--border);border-radius:10px;padding:9px 11px;
  background:var(--surface-2);flex:none}
.wia-push-frm .wia-frm+.wia-frm{margin-top:7px}
.wia-push-frm select{width:112px}
/* 数量 + 条件一句话说清，超 300 时整条变警示色（推不了，要先筛） */
.wia-push-sum{flex:none;margin-top:9px;padding:9px 11px;border-radius:9px;
  background:var(--primary-soft,rgba(23,105,255,.08));color:var(--text);
  font-size:12.5px;line-height:1.6}
.wia-push-sum b{font-size:15px;font-weight:700;font-variant-numeric:tabular-nums}
.wia-push-cond{display:block;margin-top:2px;font-size:11.5px;color:var(--muted)}
.wia-push-sum.is-over{background:var(--danger-soft);color:var(--danger)}
/* 要推的名单。最多渲染 50 条，再多给一句"还有 N 个"——
   300 行铺满屏就等于没筛选。 */
.wia-push-list{flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain;
  margin-top:8px;border:1px solid var(--border);border-radius:10px;background:var(--card)}
.wia-push-item{display:flex;align-items:baseline;gap:8px;padding:7px 11px;
  border-bottom:1px solid #f0f2f7;min-width:0}
.wia-push-item:last-child{border-bottom:none}
.wia-push-item b{flex:1 1 auto;min-width:0;font-size:12.5px;font-weight:600;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.wia-push-item span{flex:none;font-size:11.5px;color:var(--muted);white-space:nowrap}
.wia-push-more{padding:8px 11px;font-size:11.5px;color:var(--muted);text-align:center;
  border-top:1px solid var(--border)}
.wia-hint{display:inline-block;margin-left:6px;font-size:11.5px;color:var(--muted);font-weight:400}
[data-theme="dark"] .wia-bar{background:#20263a}
[data-theme="dark"] .wia-sum,[data-theme="dark"] .wia-blk{background:#20263a}

[data-theme="dark"] .wia-list-bar,[data-theme="dark"] .wia-tb thead th{background:#1b2136}
[data-theme="dark"] .wia-fold>summary{border-bottom-color:#2a3048}
[data-theme="dark"] .wia-map-row{border-bottom-color:#2a3048}
/* 极窄屏：工具条那一行更挤了，优先保搜索框和两个下拉，按钮让位 */
@media(max-width:400px){
  .wia-list-bar select{width:74px;font-size:11.5px}
  .wia-list-bar .btn-sm{padding:5px 6px;font-size:11.5px}
  .wia-list-n{display:none}
}

/* ---- 图片 ---- */
.img-grid{display:flex;flex-wrap:wrap;gap:8px;margin-top:6px}
.img-cell{position:relative;width:78px;height:78px;border-radius:9px;overflow:hidden;
  border:1px solid var(--border);background:#f4f6fb}
.img-cell img{width:100%;height:100%;object-fit:cover;display:block;cursor:zoom-in}
/* 视觉仍 22px（变大就压住图片），热区用伪元素撑到 40px。
   它和「点图放大」只隔几像素,22px 的删除点在走动中很容易误触。 */
/* 视觉仍 22px（变大就压住图片），热区用伪元素撑到 40px。
   它和「点图放大」在同一个 78px 方块里只隔几像素，走动中很容易点错。 */
.img-del{position:absolute;top:3px;right:3px;width:22px;height:22px;border-radius:50%;
  background:rgba(0,0,0,.55);color:#fff;font-size:13px;line-height:22px;text-align:center;
  cursor:pointer;z-index:2}
.img-del::after{content:"";position:absolute;left:50%;top:50%;
  width:40px;height:40px;transform:translate(-50%,-50%)}
.img-del::after{content:"";position:absolute;left:50%;top:50%;
  width:40px;height:40px;transform:translate(-50%,-50%)}
.img-add{width:78px;height:78px;border-radius:9px;border:1px dashed var(--border);
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;
  color:var(--muted);cursor:pointer;background:#fafbfe;font-size:11px}
.img-add b{font-size:22px;font-weight:400;line-height:1}
.img-add:hover{border-color:var(--primary);color:var(--primary);background:#f4f7ff}
.img-view{background:rgba(0,0,0,.86);z-index:120}
.img-view img{max-width:94vw;max-height:88vh;max-height:88dvh;border-radius:10px;box-shadow:0 8px 30px rgba(0,0,0,.5)}

/* ---- Toast ---- */
/* top 要带安全区：PWA 独立窗口下顶部 44-47px 是状态栏/刘海，
   写死 20px 会让提示被状态栏压住看不见（浏览器里则盖住搜索框）。
   .pull-ind 和 .d-head 早就这么做了，toast 漏了。 */
#toast{position:fixed;top:calc(env(safe-area-inset-top,0px) + 12px);left:50%;z-index:200;
  background:var(--bar-bg);color:#fff;padding:10px 22px;border-radius:10px;font-size:13.5px;
  box-shadow:var(--sh-3);max-width:80vw;
  opacity:0;transform:translateX(-50%) translateY(-14px) scale(.96);pointer-events:none;
  transition:opacity var(--dur-2) var(--ease),transform var(--dur-2) var(--ease-spring)}
#toast.show{opacity:1;transform:translateX(-50%) translateY(0) scale(1)}
#toast.ok{background:#0f9d6e} #toast.err{background:#e5484d}

@media(max-width:699px){
  .topbar{padding:10px 12px} .main{padding:12px}
  /* .info-grid **不跟着**压成一列 —— 基础样式本来就是两列，
     而且紧跟着的注释写着「只覆盖弹窗里的表单，.info-grid 不受影响」。
     实测压成一列后基本资料占 471px（10 个字段 10 行），手机一屏 359px 要滚 10 屏。 */
  .form-grid{grid-template-columns:1fr}
  /* 客户表单例外：它要保持两列，否则「图片放在客户类型右侧」在手机上
     就不成立（单列会把图片挤到字段下面、跑到左边）。
     用 #mk 提高优先级，只覆盖弹窗里的表单，.info-grid 不受影响。 */
  #mk .form-grid{grid-template-columns:1fr 1fr}
  /* .more-wrap / .more-menu / .btn-more 的样式已提到全局 ——
     「⋯」下拉现在宽窄屏是同一套，这里不再重复一遍。
     （原来这里另写了一份 left:0/min-width:120px/圆角 10px，
       和全局那份 right:0/min-width:172px 打架，靠后者覆盖前者。） */
  .drawer{width:100vw}
  /* 窄屏:抽屉标题独占一行,操作按钮换行,关闭按钮固定在右上 */
  .d-head{position:relative;padding-right:50px}
  .d-title-row{flex-wrap:wrap;gap:8px}
  /* 客户名放大 + 加大行高,手机上更好看清、更好点中 */
  .d-title-row h2{flex:1 1 100%;font-size:20px;min-width:0;line-height:1.4}
  .d-title-row .m-close{position:absolute;right:12px;top:12px;font-size:22px}
  .d-title-row .btn{padding:8px 14px;font-size:14px}
  .c-name{font-size:17px}
  .b-title{font-size:17px}        /* 看板卡片店名同步放大 */
  .b-card{padding:14px}
  td{padding:14px 12px}          /* 列表行加高,整行更好点 */
  /* 窄屏:看板一屏一列,横向滑动切换,避免误触 */
  .b-col{min-width:82vw;width:82vw}
}

/* ================= 折叠屏中间态: 700px–1023px =================
   OPPO Find N2 展开后 CSS 视口约 775–830px, 比普通手机宽很多,
   但又没宽到桌面大屏。原来的 768px 断点把它切成"手机版"会浪费
   横向空间；保持桌面版又出现表格 min-width 导致横向滚动。
   这里专门做一版：顶栏/导航用桌面样式，内容自适应两列/压缩列宽。
*/
@media (min-width:700px) and (max-width:1023px){
  /* 折叠屏展开态明确走"桌面骨架":顶部横排 tabs 常显,底部 tabbar 收掉,
     同时取消为 tabbar 预留的底部内边距 —— 否则展开后底部会多出一块空白,
     内容被一个已经不存在的 tabbar 顶上去。 */
  .tabs{display:flex!important;gap:6px;margin-bottom:14px;overflow-x:auto;
    scrollbar-width:none;-webkit-overflow-scrolling:touch}
  .tabs::-webkit-scrollbar{display:none}
  .tabbar{display:none!important}
  body{padding-bottom:0}
  .main{padding:14px 16px}
  .topbar{padding:10px 16px}
  .tab{padding:7px 12px;font-size:13px}
  /* 表格:不再强制 900px 最小宽, 表头允许换行, 列自动压 */
  table{min-width:100%}
  th{white-space:normal;padding:9px 10px;font-size:11px}
  td{padding:10px 8px}
  .c-name{font-size:15px}
  .addr-link{max-width:120px}
  /* 看板:两列自适应, 不横向滚动 */
  .board{gap:10px;overflow-x:visible;scroll-snap-type:none;flex-wrap:wrap}
  .b-col{min-width:calc(50% - 5px);flex:1 1 calc(50% - 5px);width:auto;max-width:50%}
  /* 抽屉/地图板的基础样式已按视口自适应,这里只需给展开态多一点高度 */
  #mapBoard{height:min(66vh,600px);height:min(66dvh,600px);min-height:380px}
}
/* ================= 深色模式: 逐组件覆盖 =================
   只覆盖变量搞不定的那些「写死的浅色底」。
   原则: 白底容器 → --card / 浅灰底 → --surface-2 / 浅彩底 → 同色系暗底。
   放在样式表最后, 靠顺序覆盖前面的浅色规则。 */
[data-theme="dark"] input,[data-theme="dark"] select,[data-theme="dark"] textarea,
[data-theme="dark"] .auth-card,[data-theme="dark"] .topbar,[data-theme="dark"] .btn,
[data-theme="dark"] .tab,[data-theme="dark"] .home-sec,[data-theme="dark"] .sheet,
[data-theme="dark"] .city-chip,[data-theme="dark"] .pager button,[data-theme="dark"] .b-card,
[data-theme="dark"] .drawer,[data-theme="dark"] .nx-chip,[data-theme="dark"] .btn-claim,
[data-theme="dark"] .modal,[data-theme="dark"] .order-card,[data-theme="dark"] .mp-row,
[data-theme="dark"] .rt-pick,[data-theme="dark"] .rt-panel,[data-theme="dark"] .colab-chip,
[data-theme="dark"] .more-menu,[data-theme="dark"] .stage-sel option{
  background:var(--card);color:var(--text);border-color:var(--border)}
[data-theme="dark"] .tabbar{background:rgba(26,30,41,.97);border-top-color:var(--border)}
[data-theme="dark"] .sheet-close,[data-theme="dark"] th{background:var(--surface-2);color:var(--text-3)}
[data-theme="dark"] .chip{background:#252b3d;color:#93a4ff}
[data-theme="dark"] .f-method{background:#252b3d;color:#8fa0ff}
[data-theme="dark"] .b-col,[data-theme="dark"] .map-wrap,[data-theme="dark"] #mapBoard,
[data-theme="dark"] .map-empty,[data-theme="dark"] .img-cell,[data-theme="dark"] .img-add,
[data-theme="dark"] .o-note,[data-theme="dark"] .imp-map{background:var(--surface-2)}
[data-theme="dark"] .b-col.dragover{background:#242c46}
[data-theme="dark"] .b-count{background:#2d3448;color:var(--text-2)}
[data-theme="dark"] .q-chip{background:var(--surface-2);border-color:#333b52}
[data-theme="dark"] .f-sys-tag{background:#2d3448;color:var(--text-3)}
[data-theme="dark"] #btnMic.rec,[data-theme="dark"] .st-异常{background:#3a2430}
[data-theme="dark"] .remind-bar{background:#3a3018;color:#f0c96b;border-color:#5a4a20}
[data-theme="dark"] #dupTip{background:#3a2c18;border-color:#5a4420;color:#f0c07b}
[data-theme="dark"] .role-tip,[data-theme="dark"] .imp-tips,[data-theme="dark"] .pub-tip,
[data-theme="dark"] .imp-warn,[data-theme="dark"] .dup-tag{background:#2c2438;color:#c3aef0}
[data-theme="dark"] .imp-warn{background:#3a3018;color:#f0c96b}
[data-theme="dark"] .dup-tag{background:#3a3018;color:#f0c07b}

[data-theme="dark"] .order-card.od-done{background:#1c2130}
[data-theme="dark"] .order-card.od-todo{background:#2a2318;border-color:#6b5228}
[data-theme="dark"] .st-待接单,[data-theme="dark"] .rt-tag.warn{background:#3a3018;color:#f0c07b}
[data-theme="dark"] .st-已接单,[data-theme="dark"] .rt-tag{background:#232a44;color:#93a4ff}
[data-theme="dark"] .st-安装中{background:#1e2c38;color:#6bc4e8}
[data-theme="dark"] .st-已完成{background:#1c2e26;color:#5fd4a0}
/* .map-meta（坐标统计浮层）2026-10-04 已整块删除，这里也去掉它的选择器。
   深色底板同样调透明（.62 → .42），跟浅色主题一个档 —— 原来两档不一致，
   深色下反而更实，糊住深色地图的路名。 */
[data-theme="dark"] .map-legend{background:rgba(26,30,41,.42);
  border-color:rgba(255,255,255,.07);color:#aab2c8}
[data-theme="dark"] .mp-row:hover,[data-theme="dark"] .img-add:hover,
[data-theme="dark"] .btn-release:hover,[data-theme="dark"] .city-chip:hover{background:var(--surface-2)}
[data-theme="dark"] .colab-chip.on{background:var(--primary-soft)}
[data-theme="dark"] .btn-release:hover{color:#a98bf0;border-color:#7c4ddb}
[data-theme="dark"] .btn-release:hover{background:#2a2440}
/* 1.9.6 曾把 `.rt-bar button:hover` 从这条里去掉（横条删了）。
   本次横条加回左段，它又需要这条深色 hover —— 一起接回来。 */
[data-theme="dark"] .b-card.dragging,[data-theme="dark"] .rt-bar button:hover{background:rgba(255,255,255,.12)}
[data-theme="dark"] #offlineBar{background:#3a3018!important;color:#f0c96b!important}
/* 深色下图片的白色底框会刺眼, 提亮一点点边框即可 */
[data-theme="dark"] .img-cell{border-color:var(--border)}
/* 深色下阶段徽章的浅彩底很扎眼 —— 用 filter 压暗, JS 里那份 STAGE_STYLE 就不用维护两套 */
[data-theme="dark"] .badge{filter:brightness(.42) saturate(1.6) contrast(1.4)}
[data-theme="dark"] .stage-sel{background:#252b3d!important;color:var(--text)!important}
/* 深色的主按钮在暗底上偏闷, 提亮一点(保持和浅色同一套 token, 不再另写一串硬编码) */
[data-theme="dark"] .btn-primary{background:var(--primary)}
[data-theme="dark"] .auth-btn{background:var(--primary)}

/* ---- 启动画面: 消除冷启动白屏 ----
   单文件应用必须下载完整个 HTML 才能画第一帧, 在此之前是白屏。
   这个纯 CSS 的启动画面紧跟在 <style> 之后, HTML 一到就能绘制,
   用户点开后立刻看到"商脉互联", 而不是一张空白页。 */
#bootSplash{position:fixed;inset:0;z-index:9999;background:#f4f6fb;
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;
  transition:opacity .3s ease, transform .3s ease}
#bootSplash.gone{opacity:0;pointer-events:none;transform:translateY(-4px)}
#bootSplash .bs-logo{width:62px;height:62px;border-radius:18px;background:#4f6ef7;color:#fff;
  display:flex;align-items:center;justify-content:center;font-size:30px;font-weight:700;
  box-shadow:0 8px 24px rgba(79,110,247,.3);
  position:relative;overflow:hidden;animation:bsIn .28s ease-out both}
/* 1.19.10: logo 换成真实图标; 图没加载出来(或被删)时露出底下的「商」字兜底 */
#bootSplash .bs-img{position:absolute;inset:0;width:100%;height:100%;border-radius:18px;object-fit:cover}
#bootSplash .bs-name{font-size:16px;font-weight:600;color:#2b3448;letter-spacing:.5px;
  animation:bsNameIn .3s ease-out .08s both}
#bootSplash .bs-spin{width:20px;height:20px;border-radius:50%;
  border:2.5px solid #dfe5f2;border-top-color:#4f6ef7;animation:bsSpin .7s linear infinite}
@keyframes bsSpin{to{transform:rotate(360deg)}}
@keyframes bsIn{from{opacity:0;transform:scale(.92)}to{opacity:1;transform:scale(1)}}
@keyframes bsNameIn{from{opacity:0}to{opacity:1}}
/* 用户系统里关了动画就不要动(也只是不动, 画面照常显示) */
@media (prefers-reduced-motion: reduce){
  #bootSplash{transition:none}
  #bootSplash .bs-logo,#bootSplash .bs-name{animation:none}
}
#bootSplash .bs-tip{font-size:12px;color:#5f6b82}
[data-theme="dark"] #bootSplash{background:#141821}
[data-theme="dark"] #bootSplash .bs-name{color:#e6e9f0}
[data-theme="dark"] #bootSplash .bs-spin{border-color:#2b3346;border-top-color:#6b86ff}
[data-theme="dark"] #bootSplash .bs-tip{color:#6b7486}
