/* ============================================================================
   移动端样式（跟进人 / 组长 · 窄屏）
   ----------------------------------------------------------------------------
   来源：design-mobile/app.css「分区二」，已按线上 token 重构：
     --p/--t1/--bd → --primary/--text/--border（不再自建一套变量）
   所有 class 带 m 前缀（mcard/mkpi/mhero…），与 style.css 无冲突。
   生效条件：渲染层给 .app 加了 .mobile-mode（见 js/mobile.js 的 isMobile()）。
   ============================================================================ */

/* ---------- 手机模式：隐藏桌面外壳，把视口让给 .mapp ---------- */
/* 桌面端是 .app > .sidebar + .main > .topbar + .content 的左右布局；
   手机模式去掉侧栏与顶栏（导航改由 .mapp 自己的页头 + 底部 Tab 承担），
   .mapp 撑满视口，内部 flex 再分成「页头 / 滚动主体 / 底栏」三段。

   ⚠️ 高度必须用 dvh，不能用 vh：
   vh 是「地址栏收起时的最大高度」，地址栏一显示，底部 Tab 栏就被盖在地址栏后面
   （表现为「往下滑 Tab 栏不见了 / 被遮挡」）。dvh 跟随可视视口实时变化。
   老浏览器不认 dvh 时退回 vh，仍可用。 */
.app.mobile-mode { display: block; height: 100vh; height: 100dvh; overflow: hidden; }
.app.mobile-mode > .sidebar { display: none; }
.app.mobile-mode > .main { width: 100%; height: 100vh; height: 100dvh; }
.app.mobile-mode > .main > .topbar { display: none; }
.app.mobile-mode > .main > .content {
  padding: 0; height: 100vh; height: 100dvh; overflow: hidden;
  display: flex; flex-direction: column;
}

.mapp {

  width: 100%; height: 100%; display: flex; flex-direction: column;
  background: var(--bg); color: var(--text);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Microsoft YaHei", "PingFang SC", sans-serif;
  font-size: 14px; line-height: 1.5; position: relative; overflow: hidden;
}
.mapp .mi { display: inline-block; vertical-align: -3px; flex: none; }
.mapp .mono, .mapp .num { font-variant-numeric: tabular-nums; }

/* ---------- 页面切换过渡 ----------
   ⚠️ 只做**入场**，不做退场。paint() 是一次性重写 content.innerHTML，没有「旧 DOM 还在」的
      阶段；做退场必须先把旧内容留住、延后重建 —— 代价是切换变慢 + 状态管理变复杂，
      收益不值。入场淡入/滑入已经足以消除「硬跳」的突兀感。
   ⚠️ 方向由 JS 给（.m-nav-fade / push / back），与「前进 / 后退」的心理模型一致：
      Tab 是同级 → 淡入；进二级页（详情/填报/登记）→ 从右侧滑入；返回 → 从左侧滑入。
   ⚠️ `.mapp` 自带 overflow:hidden，所以位移动画不会带出横向滚动条。
   ⚠️ 数据刷新（筛选切换、静默重取）**不带方向**，即不动画 —— 高频操作每次都动反而烦。 */
.mapp.m-nav-fade { animation: m-nav-fade var(--dur-base) var(--ease-out) both; }
.mapp.m-nav-push { animation: m-nav-push var(--dur-slow) var(--ease-out) both; }
.mapp.m-nav-back { animation: m-nav-back var(--dur-slow) var(--ease-out) both; }
@keyframes m-nav-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes m-nav-push { from { opacity: 0; transform: translateX(16%); } to { opacity: 1; transform: none; } }
@keyframes m-nav-back { from { opacity: 0; transform: translateX(-16%); } to { opacity: 1; transform: none; } }

/* ---------- 状态栏（仅设计稿展示） ---------- */
.mstatus {
  height: 30px; display: flex; align-items: center; justify-content: space-between;
  padding: 0 20px 0 24px; font-size: 12px; font-weight: 600; flex: none;
  color: var(--text); background: var(--bg);
}
.mstatus.on-dark { color: #fff; background: transparent; }
.mstatus .r { display: flex; align-items: center; gap: 5px; font-size: 11px; letter-spacing: 1px; }
.mstatus i { display: inline-block; width: 15px; height: 9px; border: 1.4px solid currentColor; border-radius: 2.5px; position: relative; }
.mstatus i::after { content: ""; position: absolute; inset: 1.4px; right: 4px; background: currentColor; border-radius: 1px; }

/* ---------- 滚动主体 ---------- */
/* ⚠️ min-height:0 不能省。flex 子项默认 min-height:auto，会拒绝收缩到内容高度以下，
   结果 .mbody 被内容撑开 → 底下固定的 Tab 栏 / 操作条被顶出可视区、内容被裁切。
   加了它 .mbody 才真的是「唯一滚动区」，页头与底栏都不动。 */
.mbody { flex: 1; min-height: 0; overflow-y: auto; overflow-x: hidden; -webkit-overflow-scrolling: touch; }
.mbody::-webkit-scrollbar { width: 0; }
.mpad { padding: 12px 14px 18px; }

/* ---------- 顶栏（一级页） ---------- */
.mtop {
  display: flex; align-items: center; gap: 10px; padding: 2px 16px 12px;
  background: var(--bg);
}
.mtop h2 { margin: 0; font-size: 20px; font-weight: 700; letter-spacing: -.3px; flex: 1; }
.mtop .act {
  width: 34px; height: 34px; border-radius: 10px; border: 1px solid var(--border);
  background: #fff; color: var(--text-2); display: flex; align-items: center; justify-content: center;
  position: relative; cursor: pointer;
}
.mtop .act .dot {
  position: absolute; top: -3px; right: -3px; min-width: 16px; height: 16px; padding: 0 4px;
  border-radius: 9px; background: var(--red); color: #fff; font-size: 10px; font-weight: 700;
  display: flex; align-items: center; justify-content: center; border: 1.5px solid var(--bg);
}

/* 门店页顶部：徽标 + 标题 + 一句结论（.mtop-sum）
   原来顶部只有一行 20px 的标题，在列表页里撑不起层次、也看不出「这页要不要往下看」。
   ⚠️ 用双类 .mtop-sum 而不是改 .mtop 本身：经营页仍在用纯标题版，
      改基类会连带把它一起改掉（老板只要求优化门店页）。 */
.mtop-sum { padding-top: 6px; gap: 11px; }
.mtop-sum .hb {
  width: 34px; height: 34px; border-radius: 11px; flex: none;
  display: flex; align-items: center; justify-content: center;
  background: #eff6ff; color: var(--primary);
}
.mtop-sum .tt { flex: 1; min-width: 0; }
.mtop-sum .sb { display: block; font-size: 12.5px; color: var(--text-2); margin-top: 3px; }
.mtop-sum .sb b { color: var(--orange-text); font-weight: 700; }

/* ---------- 二级页头（push 页） ---------- */
.mnav {
  display: flex; align-items: center; gap: 8px; padding: 2px 14px 12px; flex: none;
  background: #fff; border-bottom: 1px solid var(--surface-muted);
}
.mnav .back {
  width: 34px; height: 34px; border-radius: 10px; border: 1px solid var(--border); background: #fff;
  display: flex; align-items: center; justify-content: center; color: var(--text); cursor: pointer;
}
.mnav .ttl { font-size: 16px; font-weight: 700; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mnav .ttl small { display: block; font-size: 11.5px; font-weight: 400; color: var(--text-2); }

/* ---------- 深色 hero（工作台首页头） ---------- */
/* 信息架构对齐线上 PC 端 hero：标题 / 标签 / 当前可见门店 / KPI 格。
   手机上四处重排：① 标题只留页面名，姓名下移到标签；② 可见门店与需要跟进并成一行；
   ③ KPI 只留 4 格（2×2），原「需要跟进」上移；④ 渐变只写在 .mhero-wrap 上，.mhero 透明。 */
.mhero {
  color: #fff; padding: 0 15px 14px; position: relative; overflow: hidden; flex: none;
}
.mhero::after {
  content: ""; position: absolute; width: 210px; height: 210px; right: -78px; top: -110px;
  background: radial-gradient(circle, rgba(255,255,255,.20), transparent 68%); border-radius: 50%;
}
.mhero .row1 { display: flex; align-items: center; gap: 9px; position: relative; z-index: 1; }
.mhero .ht { display: flex; align-items: center; gap: 7px; font-size: 16.5px; font-weight: 700; letter-spacing: .1px; }
.mhero .hic {
  width: 25px; height: 25px; border-radius: 8px; background: rgba(255,255,255,.18);
  border: 1px solid rgba(255,255,255,.16); display: flex; align-items: center; justify-content: center;
}
.mhero .bell { margin-left: auto; width: 32px; height: 32px; border-radius: 10px; background: rgba(255,255,255,.14); display: flex; align-items: center; justify-content: center; position: relative; cursor: pointer; }
.mhero .bell .dot { position: absolute; top: -4px; right: -4px; min-width: 17px; height: 17px; padding: 0 4px; border-radius: 9px; background: #f43f5e; font-size: 10px; font-weight: 700; display: flex; align-items: center; justify-content: center; border: 1.5px solid #24459e; }
.mhero .tags { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 10px; position: relative; z-index: 1; }
.mhero .mtag {
  display: inline-flex; align-items: center; gap: 5px; font-size: 11.5px; color: #eaf1fe;
  background: rgba(255,255,255,.14); border: 1px solid rgba(255,255,255,.13);
  border-radius: 20px; padding: 4px 10px;
}
/* 当前可见门店（左）+ 需要跟进（右）：左下数字最大，右侧是入口按钮 */
/* 当前可见门店（左）+ 需要跟进（右）。
   用 grid 而非 flex：flex 下 .visr 是 flex-basis:auto 子项，宽度在「容器←→内容」之间
   循环求解，实测被压到 57px、文字溢出（「需要跟进」要 42px 只给 22px）。
   grid 的 auto 列按 max-content 撑开，不存在这个问题。 */
.mhero .vis { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: end; gap: 10px; margin-top: 11px; position: relative; z-index: 1; }
.mhero .visl { min-width: 0; display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.mhero .visl .vk { font-size: 12.5px; color: #c7d8f5; }
.mhero .visl .vv { font-size: 29px; font-weight: 700; line-height: 1; letter-spacing: -1px; font-variant-numeric: tabular-nums; }
.mhero .visl .vv small { font-size: 13px; font-weight: 400; margin-left: 2px; letter-spacing: 0; color: #c7d8f5; }
.mhero .visl .vs { flex-basis: 100%; font-size: 11.5px; color: #a9bcdb; }
/* 「需要跟进」入口。
   ⚠️ 三个坑都踩过：
   ① 底色不能用「红色半透明」—— 叠在深蓝渐变上会变紫灰，看不出是警示色。改用不透明深红。
   ② 宽度不能依赖自动计算。它里面是两行 nowrap 文字，flex/grid 的 auto 列
      在「容器宽←→内容宽」之间循环求解，实测只给 57px（「需要跟进」要 42px+22px padding），
      文字被压成竖排。**直接给 min-width 最可靠**。
   ③ 文字必须 nowrap，否则会折成「需要/跟进」两行。 */
.mhero .visr {
  justify-self: end; font-family: inherit; cursor: pointer; text-align: center; color: #fff;
  white-space: nowrap; min-width: 88px;
  background: #b91c1c; border: 1px solid rgba(255,255,255,.22);
  border-radius: 10px; padding: 6px 12px 8px;
  box-shadow: 0 2px 8px rgba(127,29,29,.35);
}
.mhero .visr .vrk { display: block; font-size: 11.5px; color: #fecaca; white-space: nowrap; }
.mhero .visr .rv { display: block; font-size: 21px; font-weight: 700; line-height: 1.15; font-variant-numeric: tabular-nums; white-space: nowrap; }
.mhero .visr .rv small { font-size: 11.5px; font-weight: 400; color: #fecaca; margin-left: 1px; }
/* 我的评分（本月）：hero 里「跟进门店 / 需要跟进」下方一条横条。
   点击 → 底部 sheet（复用 .modal，窄屏自动变 sheet，见本文件下方的 @media (max-width:900px)）。
   ⚠️ 必须放在 .mhk 之前 —— 视觉顺序是 vis → 评分条 → KPI 格。 */
.mhero .mscore {
  display: flex; align-items: center; gap: 10px; margin-top: 10px;
  background: rgba(255,255,255,.12); border: 1px solid rgba(255,255,255,.15);
  border-radius: 11px; padding: 9px 11px; position: relative; z-index: 1; cursor: pointer;
}
.mhero .mscore .l { min-width: 0; flex: 1; }
.mhero .mscore .lb { font-size: 11.5px; color: #d3e0f7; }
.mhero .mscore .vv { font-size: 23px; font-weight: 700; line-height: 1.15; letter-spacing: -.6px; font-variant-numeric: tabular-nums; }
.mhero .mscore .vv small { font-size: 12px; font-weight: 400; color: #c7d8f5; margin-left: 2px; letter-spacing: 0; }
.mhero .mscore .sb { font-size: 11.5px; color: #a9bcdb; }
.mhero .mscore .sb b { color: #fbbf24; font-weight: 600; }
.mhero .mscore .go { flex: none; display: flex; color: #9fb0cb; }

/* KPI 格：4 格 2×2。
   ⚠️⚠️ **不要再用 `grid-auto-rows: 1fr`**（2026-09-18 老板第二次反馈「底部空那么多」后去掉）。
      容器高度是 auto 时，`1fr` 会让**所有行都等于最高那一行**的高度；
      而「累计成交金额」那张卡比别的多一个走势图块（`margin-top:5 + svg 26 = 31px`），
      于是另外三张卡底部各留 31px 空白 —— 四张卡看起来「下面都空一块」。
      改成默认 `auto` 后每行贴合自己的内容：第二行收紧、底部空白消失。
      代价：两行可能不等高（第一行因走势图略高），比「整块底部空一片」好接受。
      （顺带修掉了走势图的隐形占位：`mobile.js spark()` 原来在只有 1 个数据点时
        会返回一个画不出线、却仍占 26px 的 svg，已加 `length < 2` 护栏。）
   ⚠️ 卡片内用 flex-column + `space-between` 兜底：同一行若被更高的邻居拉伸，
      多出来的高度会分到「标签 ↔ 数值 ↔ 副说明」之间，而不是全堆在底部。 */
.mhero .mhk { display: grid; grid-template-columns: 1fr 1fr; gap: 7px; margin-top: 10px; position: relative; z-index: 1; }
.mhero .mhk .c {
  background: rgba(255,255,255,.12); border: 1px solid rgba(255,255,255,.15); border-radius: 11px; padding: 10px 11px; min-width: 0;
  display: flex; flex-direction: column; justify-content: space-between;
}
.mhero .mhk .k { display: flex; align-items: center; gap: 5px; font-size: 12px; color: #d3e0f7; }
.mhero .mhk .k i { width: 7px; height: 7px; border-radius: 50%; flex: none; }
.mhero .mhk .v { font-size: 23px; font-weight: 700; line-height: 1.15; letter-spacing: -.6px; margin-top: 5px; font-variant-numeric: tabular-nums; }
.mhero .mhk .v small { font-size: 12px; font-weight: 400; color: #c7d8f5; margin-left: 2px; letter-spacing: 0; }
.mhero .mhk .s { font-size: 11.5px; color: #a9bcdb; margin-top: 5px; }
/* 卡内「今日 / 总计」上下两层：上=今日，下=总计（降一档），中间一条斜线分隔。
   ⚠️ 桌面同族样式在 style.css 的 .dhero-kpis 段落 —— 两端卡容器不同，两处都要写。
   ⚠️ 字号/单位必须显式写全：原 `.mhero .mhk .v` / `.v small` 覆盖不到新增的 .vv / .vv2。 */
.mhero .mhk .c .kkrow { margin-top: 5px; }
.mhero .mhk .c .kkrow .vv {
  font-size: 22px; font-weight: 700; line-height: 1.12; letter-spacing: -.6px;
  font-variant-numeric: tabular-nums;
}
.mhero .mhk .c .kkrow .vv2 {
  font-size: 16px; font-weight: 600; line-height: 1.2; letter-spacing: -.3px;
  color: #cbd8ec; font-variant-numeric: tabular-nums;
}
.mhero .mhk .c .kkrow .vt { font-size: 10.5px; font-weight: 400; margin-left: 5px; letter-spacing: 0; color: #a9bcdb; }
.mhero .mhk .c .kkrow .vv2 .vt { color: #8fa6cd; }
.mhero .mhk .c .kkrow .vv .u { font-size: 12px; font-weight: 400; color: #c7d8f5; margin-left: 2px; letter-spacing: 0; }
.mhero .mhk .c .kkrow .vv2 .u { font-size: 11px; font-weight: 400; color: #8fa6cd; margin-left: 2px; letter-spacing: 0; }
.mhero .mhk .c .slash {
  display: block; height: 1.5px; margin: 8px 0 6px;
  background: linear-gradient(90deg, rgba(255,255,255,.50) 0%, rgba(255,255,255,.20) 52%, rgba(255,255,255,.03) 100%);
  transform: skewY(-3.4deg); transform-origin: 0 50%;
}
.mhero .spk { margin-top: 5px; }
/* sparkline 按格子宽度自适应（spark() 出的是固定 width 的 svg，这里用 CSS 覆盖） */
.mhero .spk svg { display: block; width: 100%; height: 26px; }

/* 深色页头容器：把「状态栏 + hero / 个人信息卡」合成一块，滚动时一起固定 */
.mhero-wrap {
  flex: none; border-radius: 0 0 16px 16px; overflow: hidden;
  background: linear-gradient(160deg, #1b3070 0%, #24459e 48%, #2f6ada 100%);
}
/* 渐变由 wrap 统一提供：hero 与「我的」页个人信息卡都不再各写一套，
   否则两层渐变叠加会出现色差。 */
.mhero-wrap .mhero, .mhero-wrap .mprof { border-radius: 0; background: none; }

/* 一级页的固定区不参与收缩（顶部栏 / 搜索框 / 筛选条） */
.mtop, .msearch, .mchips { flex: none; }

/* ---------- 卡片 / 分节 ---------- */
.mcard { background: var(--card); border-radius: var(--radius-md); box-shadow: var(--shadow); margin: 0 0 12px; overflow: hidden; }
.mcard-h { display: flex; align-items: center; gap: 8px; padding: 13px 14px 10px; }
.mcard-h .ic {
  width: 26px; height: 26px; border-radius: 8px; display: flex; align-items: center; justify-content: center;
  background: #eff6ff; color: var(--primary); flex: none;
}
.mcard-h .ic.g { background: var(--green-soft); color: var(--green-text); }
.mcard-h .ic.o { background: var(--orange-soft); color: var(--orange-text); }
.mcard-h .ic.r { background: var(--red-bg); color: var(--red-text); }
.mcard-h .ic.p { background: #f3e8ff; color: var(--purple); }
.mcard-h h3 { margin: 0; font-size: 15px; font-weight: 700; flex: 1; }
.mcard-h .more { font-size: 12px; color: var(--text-2); display: flex; align-items: center; gap: 2px;
  border: 0; background: none; padding: 0; cursor: pointer; font-family: inherit; min-height: 32px; }
.mcard-b { padding: 0 14px 13px; }
.msub { font-size: 11.5px; color: var(--text-2); padding: 0 14px 12px; line-height: 1.7; }

/* ---------- 底栏（底部 Tab） ---------- */
.mtab {
  flex: none; display: flex; background: rgba(255,255,255,.96); backdrop-filter: blur(12px);
  border-top: 1px solid var(--border);
  /* ⚠️ 底部要躲开 Home 指示条：viewport 已设 viewport-fit=cover（为用满全面屏），
     不加 safe-area padding 的话 Tab 文字会压在 Home 条上。 */
  padding: 6px 4px max(10px, env(safe-area-inset-bottom));
}
.mtab button {
  flex: 1; border: 0; background: none; font-family: inherit; cursor: pointer;
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  font-size: 10.5px; color: var(--text-2); padding: 5px 0 0; position: relative;
}
.mtab button.on { color: var(--primary); font-weight: 600; }
.mtab button .badge {
  position: absolute; top: 0; left: 50%; margin-left: 3px; min-width: 16px; height: 16px;
  border-radius: 9px; background: var(--red); color: #fff; font-size: 10px; font-weight: 700;
  display: flex; align-items: center; justify-content: center; padding: 0 4px;
}
/* 底部固定操作条 */
.mfoot {
  flex: none; display: flex; gap: 9px; padding: 10px 14px 18px;
  background: #fff; border-top: 1px solid var(--surface-muted);
}
.mtab + .mfoot, .mfoot:last-child { padding-bottom: max(18px, env(safe-area-inset-bottom)); }

/* ---------- 通用小件 ---------- */
.mbtn {
  flex: 1; height: 46px; border-radius: var(--radius-md); border: 1px solid var(--border); background: #fff;
  color: var(--text); font-size: 15px; font-weight: 600; font-family: inherit;
  display: inline-flex; align-items: center; justify-content: center; gap: 6px; cursor: pointer;
}
.mbtn.pri { background: var(--primary); border-color: var(--primary); color: #fff; box-shadow: 0 4px 12px rgba(37,99,235,.28); }
.mbtn.pri:active { background: var(--primary-dark); }
.mbtn.sm { height: 32px; font-size: 12.5px; flex: none; padding: 0 12px; border-radius: 9px; }
.mbtn.gh { background: var(--bg); border-color: var(--border); color: var(--text-2); }
.mbtn.blk { width: 100%; flex: none; }
/* 卡片底部动作条里的「查看全部 N 家 / N 人」入口：全宽 + 比默认小一档。
   ⚠️ 这个类是为了修一个布局遗漏：这两处按钮原来靠**内联** height/font-size，
      而它们的父容器（padding:11px 14px 的那层 div）不是 flex 容器 →
      .mbtn 自带的 flex:1 失效 → 按钮收缩到内容宽度；
      偏偏 .mbtn 基础样式**没有水平 padding**（它默认总在 flex 容器里被撑开）→
      文字左右紧贴按钮边框，就是「挤到边边」（老板 2026-09-18 反馈）。
      给父容器加 display:flex 也能救，但那样是靠祖先救后代；
      这里直接让按钮自己声明全宽（border-box 下 width:100% 正好等于容器内容宽）。
   ⚠️ 与 .mcard-h .more（卡片右上角那行说明文字，是 span）同名不同物，互不影响。 */
.mbtn.more { width: 100%; flex: none; height: 38px; font-size: 13.5px; }


.mnote {
  display: flex; gap: 9px; border-radius: var(--radius-sm); padding: 10px 11px; font-size: 12px;
  line-height: 1.7; background: #f1f5f9; color: var(--text-2); border: 1px solid var(--surface-muted);
}
.mnote .ic { color: var(--text-3); flex: none; margin-top: 1px; }
.mnote b { color: var(--text); }
.mnote.ok { background: #f0fdf4; border-color: #dcfce7; color: #166534; }
.mnote.ok .ic { color: var(--green); }
.mnote.warn { background: #fff7ed; border-color: #fed7aa; color: #9a3412; }
.mnote.warn .ic { color: var(--orange); }
.mnote.bad { background: #fef2f2; border-color: #fecaca; color: #991b1b; }
.mnote.bad .ic { color: var(--red); }
.mnote.info { background: #eff6ff; border-color: #dbeafe; color: #1e40af; }
.mnote.info .ic { color: var(--primary); }

/* 筛选 / 切换 chips：**放不下就换行，不横滑**
   ⚠️ 2026-09-18 改（老板反馈「容易让人误以为后面没有选项」）：
      原来是 `flex-wrap: nowrap` + `overflow-x: auto` + 右侧 22px 渐隐，
      实际效果是第 5 个 chip 被**整块裁掉且没有任何可见提示** ——
      渐隐终点写的是 var(--bg)，在浅灰底上几乎与背景同色，等于没提示（老板截图实测）。
      ⇒ 改成 wrap：宁可多占一行，也不要让用户以为「就这几个」。
        指标切换 / 日期档是**并列选项**，看全比省高度重要；手机上横滑 chip 还有个通病 ——
        看不到的选项等于不存在，用户根本不会去滑。
   ⚠️ 必须显式写 flex-wrap: wrap 覆盖 style.css 的 .mchips（那边的规则是 wrap，这里原本特意改成 nowrap）。
   ⚠️ 规则带 `.mapp` 前缀 → 只作用于手机端；桌面端 app.js/desk.js 用的是 style.css 的 .mchips，不受影响。 */
.mapp .mchips {
  display: flex; flex-wrap: wrap; gap: 8px;
  padding: 2px 14px 12px; margin: 0;      /* margin 归零：线上 .mchips 有 margin-bottom:12px */
}
.mapp .mchip {
  flex: none; height: 32px; padding: 0 13px; border-radius: 20px; border: 1px solid var(--border);
  background: #fff; color: var(--text-2); font-size: 13px; font-family: inherit; cursor: pointer;
  display: inline-flex; align-items: center; gap: 5px;
}
.mapp .mchip.on { background: var(--primary); border-color: var(--primary); color: #fff; font-weight: 600; }
.mapp .mchip b { font-weight: 700; opacity: .75; }
.mapp .mchip i { width: 7px; height: 7px; border-radius: 50%; }

/* 搜索框（展示态） */
.msearch {
  display: flex; align-items: center; gap: 8px; height: 40px; margin: 0 14px 12px;
  background: #eef1f6; border-radius: 10px; padding: 0 12px; color: var(--text-2); font-size: 13.5px;
}

/* ---------- 日期范围条 ---------- */
/* 线上 PC 经营看板是「开始日期 ~ 结束日期 + 查询 + 近30天」。
   手机放不下两个日期输入框，改成预设区间 chips；下方一行给出当前实际区间与天数，
   免得「近 30 天」给人错觉以为真有 30 天数据（库里可能只有 23 天）。 */
.mrange { flex: none; padding: 0 14px 10px; }
.mrange .mchips { padding: 0; }
.mrange .cur {
  display: block; font-size: 11px; color: var(--text-2); margin-top: 7px;
  font-variant-numeric: tabular-nums;
}

/* ---------- 门店卡片（列表） ---------- */
.mstore { padding: 11px 14px; border-bottom: 1px solid var(--surface-muted); position: relative; }
.mstore:last-child { border-bottom: 0; }
.mstore::before { content: ""; position: absolute; left: 0; top: 11px; bottom: 11px; width: 3px; border-radius: 0 3px 3px 0; }
.mstore.s1::before { background: var(--red); }
.mstore.s2::before { background: var(--orange); }
.mstore.s3::before { background: #f59e0b; }
.mstore.s4::before { background: transparent; }
.mstore .l1 { display: flex; align-items: flex-start; gap: 6px; }
/* 字重交给 .store-name（门店名统一样式），这里只管字号与布局 */
.mstore .nm { font-size: 14.5px; line-height: 1.35; flex: 1; }
.mstore .l2 { font-size: 11.5px; color: var(--text-2); margin-top: 4px; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.mstore .l2 .sep { color: var(--border); }
.mstore .l3 { display: flex; align-items: flex-end; gap: 10px; margin-top: 6px; }
.mstore .l3 .amt { font-size: 18px; font-weight: 700; letter-spacing: -.5px; font-variant-numeric: tabular-nums; flex: none; }
.mstore .l3 .amt.zero { color: var(--text-3); font-weight: 600; }
.mstore .l3 .amt small { font-size: 10.5px; font-weight: 400; color: var(--text-2); margin-left: 2px; letter-spacing: 0; }
.mstore .l3 .kvs { display: flex; gap: 11px; margin-left: auto; }
.mstore .l3 .kvs div { text-align: right; }
.mstore .l3 .kvs b { display: block; font-size: 12.5px; font-weight: 650; font-variant-numeric: tabular-nums; }
.mstore .l3 .kvs b.z { color: var(--text-3); font-weight: 500; }
.mstore .l3 .kvs span { font-size: 10px; color: var(--text-2); }
.mstore .l4 { display: flex; align-items: center; gap: 8px; margin-top: 7px; }
.mstore .l4 .last { font-size: 11.5px; color: var(--text-2); flex: 1; }
.mstore .l4 .last b { font-weight: 650; }
.mstore .l4 .last .hot { color: var(--red-text); }
.mstore .l4 .last .mid { color: var(--orange-text); }
/* 卡片上的「···」更多操作：方形图标按钮。与管理端手机端门店操作的「···」同一语义，
   这里窄，所以只占 34px、不带文字。 */
.mstore .l4 .mbtn.dots { flex: none; width: 34px; padding: 0; font-size: 15px; font-weight: 700; letter-spacing: 1px; line-height: 1; }

/* ---------- KPI 小格 ---------- */
.mkpis { display: grid; grid-template-columns: repeat(3, 1fr); gap: 9px; }
.mkpi { background: #fff; border-radius: var(--radius-md); box-shadow: var(--shadow); padding: 11px 12px; }
.mkpi .lb { font-size: 11px; color: var(--text); display: flex; align-items: center; gap: 5px; }
.mkpi .lb i { width: 7px; height: 7px; border-radius: 50%; }
.mkpi .v { font-size: 21px; font-weight: 700; letter-spacing: -.6px; margin-top: 6px; font-variant-numeric: tabular-nums; }
.mkpi .v small { font-size: 11px; font-weight: 400; color: var(--text-2); margin-left: 2px; letter-spacing: 0; }
.mkpi .s { font-size: 10.5px; color: var(--text-2); margin-top: 3px; }

/* 大 KPI（成交金额） */
.mbig {
  background: #fff; border-radius: var(--radius-md); box-shadow: var(--shadow); padding: 14px;
  display: flex; align-items: center; gap: 12px; margin-bottom: 12px;
}
.mbig .l { flex: 1; min-width: 0; }
.mbig .l .lb { font-size: 11.5px; color: var(--text); }
.mbig .l .v { font-size: 29px; font-weight: 700; letter-spacing: -1px; margin-top: 4px; font-variant-numeric: tabular-nums; }
.mbig .l .v small { font-size: 12px; font-weight: 400; color: var(--text-2); letter-spacing: 0; margin-left: 2px; }
.mbig .l .s { font-size: 11px; color: var(--text-2); margin-top: 4px; }
.mbig .sp { flex: none; }

/* ---------- 活跃度分段条 ---------- */
.msegbar { display: flex; height: 12px; border-radius: 7px; overflow: hidden; gap: 2px; background: var(--surface-muted); }
.msegbar i { display: block; height: 100%; }
.mlegend { display: grid; grid-template-columns: 1fr 1fr; gap: 9px 12px; margin-top: 13px; }
.mlegend .li { display: flex; align-items: center; gap: 7px; font-size: 12px; }
.mlegend .li i { width: 9px; height: 9px; border-radius: 3px; flex: none; }
.mlegend .li b { margin-left: auto; font-variant-numeric: tabular-nums; font-size: 14px; font-weight: 700; }
.mlegend .li span { color: var(--text-2); }

/* ---------- 待办行 ---------- */
.mtodo { padding: 12px 14px; border-bottom: 1px solid var(--surface-muted); display: flex; gap: 10px; align-items: flex-start; }
.mtodo:last-child { border-bottom: 0; }
/* 手机端「可点元素」统一禁掉文本选中与系统点击高亮。
   ⚠️ 这些元素（待办行、门店条目、筛选片、卡片按钮…）是**可点对象**，不是待选文本：
      手指按下时只要有轻微滑动、或连点两下，浏览器就会把它们当文本选中，
      在卡片上留下一块蓝底白字的选区 —— 视觉上像「整块坏了」。
      `-webkit-tap-highlight-color` 去掉 Android 点击时系统叠的浅蓝高亮；
      它**不吃继承**，所以这里靠属性选择器直接命中每个可点元素本身。
      两套前缀并存：跟进人手机端用 data-m-act，管理端手机端用 data-am。
      管理端卡片（.tb-carded）另有一条更彻底的规则在「表格 → 卡片」一节。 */
[data-m-act], [data-am] {
  -webkit-user-select: none; -moz-user-select: none; user-select: none;
  -webkit-tap-highlight-color: transparent;
  -webkit-touch-callout: none;
}
.mtodo .sev { width: 3px; border-radius: 3px; align-self: stretch; flex: none; margin-top: 2px; }
.mtodo.s1 .sev { background: var(--red); }
.mtodo.s2 .sev { background: var(--orange); }
.mtodo.s3 .sev { background: #f59e0b; }
.mtodo .bd { flex: 1; min-width: 0; }
.mtodo .t1 { font-size: 14px; font-weight: 650; line-height: 1.4; }
.mtodo .t2 { font-size: 11.5px; color: var(--text-2); margin-top: 5px; line-height: 1.6; }
.mtodo .t2 b { color: var(--text-2); font-weight: 600; }
.mtodo .go {
  flex: none; align-self: center; height: 34px; padding: 0 13px; border-radius: 10px;
  background: var(--primary); color: #fff; border: 0; font-size: 13px; font-weight: 600; font-family: inherit;
  display: inline-flex; align-items: center; gap: 4px; cursor: pointer;
}

/* ---------- 填报日历 ---------- */
.mcal { display: grid; grid-template-columns: repeat(10, 1fr); gap: 4px; }
.mcal i {
  aspect-ratio: 1; border-radius: 5px; background: var(--surface-muted);
  display: flex; align-items: center; justify-content: center;
  font-size: 9.5px; font-style: normal; color: #475569; font-weight: 600;
}
.mcal i.f1 { background: #dbeafe; }
.mcal i.f2 { background: #93c5fd; }
.mcal i.f3 { background: #3b82f6; color: #fff; }
.mcal i.f4 { background: var(--primary-dark); color: #fff; }
.mcal-x { display: flex; gap: 11px; font-size: 10.5px; color: var(--text-2); margin-top: 11px; flex-wrap: wrap; }
.mcal-x span { display: flex; align-items: center; gap: 4px; }
.mcal-x i { width: 9px; height: 9px; border-radius: 3px; }

/* ---------- 地域分布地图（工作台）----------
   ⚠️ 高度走 class，不写内联 style —— 内联优先级高于任何选择器，断点里改不动。
   手机屏窄，300px 是「能看出省份分布、又不把工作台撑太长」的折中；
   卡片宽约 362px（390 - 左右各 14），这个尺寸下 echarts 的省份标签会偏挤但可读。 */
.mmap { width: 100%; height: 300px; }
@media (max-width: 360px) { .mmap { height: 260px; } }   /* 小屏再降一档，避免地图占满整屏 */

/* ---------- 省份 / 城市下钻面板（点地图圆钉弹出）----------
   .province-panel 的基础样式在 style.css：position:fixed + width:440px + z-index:100。
   ⚠️ 440px 在 390 屏上会横向溢出（右侧被裁掉一截），手机端必须占满屏。
   ⚠️ 选择器用 `.app.mobile-mode ~ .province-panel`（相邻兄弟）而不是裸 `.province-panel`：
      面板挂在 body 下、与 .app 同级（不在 .mapp 内部），所以不能靠祖先类限定；
      而写裸选择器会让「桌面端窄屏」（.app:not(.mobile-mode)）也吃到手机样式。
      —— 这与项目「桌面隔离」的三重保障同一条思路。 */
.app.mobile-mode ~ .province-panel { width: 100%; max-width: 100%; }
.app.mobile-mode ~ .province-panel .head {
  padding: 14px 16px; padding-top: max(14px, env(safe-area-inset-top));
}
.app.mobile-mode ~ .province-panel .head h3 { font-size: var(--fs-md); }
.app.mobile-mode ~ .province-panel .body {
  padding: 14px 16px max(16px, env(safe-area-inset-bottom));
}
/* 面板里每行门店是「可点对象」而非可选文本 —— 触屏上防误选（与 .mstore 同一处理） */
.app.mobile-mode ~ .province-panel .store-row {
  user-select: none; -webkit-tap-highlight-color: transparent;
  padding: 10px 12px; font-size: var(--fs-base);
}
/* （原 `.store-row .nm { font-weight: 600 }` 已删：门店名统一走 .store-name） */
/* 手机上「在门店列表查看」按钮要能点中（默认 .btn-sm 偏小） */
.app.mobile-mode ~ .province-panel .head .btn { min-height: 34px; padding: 0 12px; }

/* ---------- 明细行（key-value） ---------- */
.mkv { display: flex; align-items: flex-start; gap: 12px; padding: 11px 14px; border-bottom: 1px solid var(--surface-muted); }
.mkv:last-child { border-bottom: 0; }
.mkv .k { width: 76px; flex: none; font-size: 12.5px; color: var(--text); display: flex; align-items: center; gap: 5px; }
.mkv .v { flex: 1; font-size: 13.5px; line-height: 1.6; word-break: break-all; font-weight: 500; }
.mkv .v.mono { font-variant-numeric: tabular-nums; }
.mkv .v .cp { color: var(--primary); font-size: 12px; margin-left: 6px; display: inline-flex; align-items: center; gap: 2px; }

/* 指标网格（门店详情） */
.mgrid3 { display: grid; grid-template-columns: repeat(3, 1fr); }
.mgrid3 > div { padding: 12px 10px; text-align: center; border-right: 1px solid var(--surface-muted); border-bottom: 1px solid var(--surface-muted); }
.mgrid3 > div:nth-child(3n) { border-right: 0; }
.mgrid3 > div:nth-child(n+4) { border-bottom: 0; }
.mgrid3 .k { font-size: 11px; color: var(--text); }
.mgrid3 .v { font-size: 17px; font-weight: 700; margin-top: 5px; font-variant-numeric: tabular-nums; letter-spacing: -.4px; }
.mgrid3 .v.zero { color: var(--text-3); font-weight: 600; }
.mgrid3 .v small { font-size: 10.5px; font-weight: 400; color: var(--text-2); letter-spacing: 0; }

/* ---------- 表单 ---------- */
.mfield { padding: 13px 14px; border-bottom: 1px solid var(--surface-muted); display: flex; align-items: center; gap: 10px; }
.mfield:last-child { border-bottom: 0; }
/* 字段名宽度要按「图标 24 + 间距 6 + 最长 5 个中文 + 星号」算，不能拍脑袋给 84px ——
   空间不够时文字会在 span 内部折成两行（「成交金/额」）。
   white-space: nowrap 兜底：宁可占宽一点，也不允许折行。 */
.mfield .k {
  width: 116px; flex: none; font-size: 13.5px; color: var(--text);
  display: flex; align-items: center; gap: 6px; white-space: nowrap;
}
/* 字段名前的彩色图标（颜色由 JS 按字段给，尺寸/居中在这里统一定） */
.mfield .kic {
  width: 24px; height: 24px; border-radius: 7px; flex: none;
  display: flex; align-items: center; justify-content: center;
}
.mfield .k em { color: var(--red); font-style: normal; margin-left: 2px; flex: none; }
/* 数字输入框：直接键盘输入（曾用「只读数值 + ± 按钮」，金额根本填不准，已废）
   ⚠️ 选择器限定成 type="number" —— .mfield 是通用容器，写泛匹配会连带命中日期控件，
      而日期按老板要求保持原生、不做任何覆盖。
   ⚠️ font-size 必须 ≥16px —— 小于 16px 时 iOS 聚焦会自动放大整页，且放大后不还原。
      项目里弹窗控件踩过同一个坑（见下方「弹窗」一节）。 */
.mfield input[type="number"] {
  flex: 1; min-width: 0; border: 0; outline: 0; background: none; font-family: inherit;
  font-size: 16px; font-weight: 600; color: var(--text); text-align: right; padding: 6px 0;
  font-variant-numeric: tabular-nums;
}
.mfield input::placeholder { color: var(--text-3); font-size: 16px; font-weight: 400; }
/* 去掉 number 输入框自带的上下小箭头（手机端无用，还会挤压输入区） */
.mfield input[type="number"] {
  -webkit-appearance: none; -moz-appearance: textfield; appearance: textfield;
}
.mfield input[type="number"]::-webkit-outer-spin-button,
.mfield input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.mfield .unit { font-size: 12px; color: var(--text-2); flex: none; }

/* 多行文本域（工作日报的三段：今日完成 / 明日计划 / 问题与支持）。
   放在 .mcard-b 里（左 14px / 右 14px 由容器给），所以这里只管自身盒子。
   ⚠️ font-size 必须 ≥16px —— 与 .mfield input 同一个坑：小于 16px 时 iOS 聚焦会自动
      放大整页，且放大后不还原。 */
.mta {
  display: block; width: 100%; min-height: 88px; resize: none; outline: 0;
  border: 1px solid var(--border); border-radius: var(--radius-md);
  background: var(--bg); padding: 10px 12px;
  font-family: inherit; font-size: 16px; line-height: 1.65; color: var(--text);
}
.mta:focus { border-color: var(--primary); }
.mta::placeholder { color: var(--text-3); font-size: 14px; }

/* ---------- 趋势图 ---------- */
.mchart { padding: 4px 2px 0; }
.mchart svg { display: block; width: 100%; height: auto; }

/* ---------- 经营链路（纵向） ---------- */
.mlink { padding: 2px 0; }
.mlink .li { display: flex; align-items: center; gap: 11px; padding: 10px 0; position: relative; }
.mlink .li::before {
  content: ""; position: absolute; left: 15px; top: 38px; bottom: -6px; width: 1.5px; background: var(--border);
}
.mlink .li:last-child::before { display: none; }
.mlink .ic {
  width: 31px; height: 31px; border-radius: 10px; flex: none;
  display: flex; align-items: center; justify-content: center; color: #fff; z-index: 1;
}
.mlink .bd { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.mlink .bd .n { font-size: 13px; color: var(--text-2); }
.mlink .bd .v { font-size: 19px; font-weight: 700; letter-spacing: -.5px; font-variant-numeric: tabular-nums; }
.mlink .bd .v small { font-size: 11px; font-weight: 400; color: var(--text-2); margin-left: 2px; letter-spacing: 0; }
.mlink .rate { font-size: 11.5px; color: var(--text-2); flex: none; text-align: right; }
.mlink .rate b { display: block; font-size: 13px; }

/* ---------- 排行 ---------- */
.mrank { display: flex; align-items: center; gap: 11px; padding: 11px 14px; border-bottom: 1px solid var(--surface-muted); }
.mrank:last-child { border-bottom: 0; }
/* 排行序号方块。
   ⚠️ 这里的 .rk 指 style.css 的「名次徽标」（`<span class="rk">`，764 行）。
      排行条行容器已独立命名 .rkrow，不会再漏 padding / 下边框过来，
      所以本块只声明自己要覆盖的属性，不需要再归零。 */
.mrank .rk {
  width: 23px; height: 23px; border-radius: 7px; flex: none; background: #eef2f7; color: var(--text-2);
  display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 700;
}
.mrank .rk.g1 { background: #fef3c7; color: #b45309; }
.mrank .rk.g2 { background: #e5e7eb; color: #4b5563; }
.mrank .rk.g3 { background: #ffedd5; color: #c2410c; }
.mrank .bd { flex: 1; min-width: 0; }
.mrank .bd .nm { font-size: 13.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mrank .bd .sb { font-size: 11px; color: var(--text-2); margin-top: 3px; }
.mrank .amt { font-size: 15px; font-weight: 700; font-variant-numeric: tabular-nums; flex: none; text-align: right; }
.mrank .amt small { display: block; font-size: 10.5px; font-weight: 400; color: var(--text-2); }

/* ---------- 「我的」页 ---------- */
.mprof {
  background: linear-gradient(158deg, #1e3a8a, #2563eb 88%); color: #fff;
  padding: 16px; border-radius: 0 0 20px 20px; display: flex; align-items: center; gap: 13px;
}
.mprof .av {
  width: 54px; height: 54px; border-radius: 17px; background: rgba(255,255,255,.2);
  border: 1.5px solid rgba(255,255,255,.35); flex: none;
  display: flex; align-items: center; justify-content: center; font-size: 23px; font-weight: 700;
}
.mprof .nm { font-size: 18px; font-weight: 700; }
.mprof .sb { font-size: 12px; color: #c7d8f5; margin-top: 4px; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.mprof .sb .tag { background: rgba(255,255,255,.16); border-radius: 20px; padding: 2px 9px; }

.mmenu { background: #fff; border-radius: var(--radius-md); box-shadow: var(--shadow); margin: 0 0 12px; overflow: hidden; }
.mmenu a, .mmenu button {
  width: 100%; border: 0; background: none; font-family: inherit; cursor: pointer; text-align: left;
  display: flex; align-items: center; gap: 11px; padding: 14px; color: var(--text);
  font-size: 14.5px; border-bottom: 1px solid var(--surface-muted); text-decoration: none;
}
.mmenu a:last-child, .mmenu button:last-child { border-bottom: 0; }
.mmenu .ic {
  width: 30px; height: 30px; border-radius: 9px; flex: none; background: #eff6ff; color: var(--primary);
  display: flex; align-items: center; justify-content: center;
}
.mmenu .ic.g { background: var(--green-soft); color: var(--green-text); }
.mmenu .ic.o { background: var(--orange-soft); color: var(--orange-text); }
.mmenu .ic.r { background: var(--red-bg); color: var(--red-text); }
.mmenu .ic.n { background: #eef2f7; color: var(--text-2); }
.mmenu .tx { flex: 1; }
.mmenu .tx small { display: block; font-size: 11.5px; color: var(--text-2); margin-top: 3px; }
.mmenu .ar { color: var(--text-3); flex: none; display: flex; }
.mmenu button.danger { color: var(--red-text); }

/* ---------- 小组长：口径切换器 ---------- */
/* 组长是双身份，页头放一个切换器把「我名下」与「整个品牌」两个口径分开。
   深底（页头内）与白底（列表页顶部）两套配色，避免在浅色区出现无法辨认的深色胶囊。 */
.mseg {
  display: flex; gap: 3px; padding: 3px; margin-top: 12px; position: relative; z-index: 1;
  background: rgba(255,255,255,.13); border: 1px solid rgba(255,255,255,.18); border-radius: 11px;
}
.mseg button {
  flex: 1; border: 0; background: none; font-family: inherit; cursor: pointer;
  display: flex; align-items: center; justify-content: center; gap: 5px;
  font-size: 12.5px; font-weight: 600; color: #c7d8f5; padding: 7px 0; border-radius: 8px;
}
.mseg button b { font-weight: 700; opacity: .7; font-variant-numeric: tabular-nums; }
.mseg button.on { background: #fff; color: #1b3070; }
.mseg button.on b { opacity: .55; }
/* 浅色背景下的口径切换器（门店 / 经营页顶部）。
   ⚠️ 必须显式重设 background：深色版是 rgba(255,255,255,.13)，
   放在浅色页面上会变成「白底上的半透明白」，未选中项几乎看不见（实测文字灰 + 底也白）。
   ⚠️ 这里同时要和下方的筛选片（.mchip）**明确区分**：筛选片是 32px 高、
      20px 圆角的独立胶囊；切换器是满宽、带轨道的分段控件、选中块是带阴影的实心蓝。
      两者长得一样的话，用户分不清「这是切换口径」还是「这是又一个筛选」。 */
.mseg.light {
  margin: 0 14px 12px;
  background: #e4eaf2; border: 1px solid #d3dbe6;
}
.mseg.light button { color: var(--text); padding: 8px 0; font-size: 13.5px; }
.mseg.light button b { opacity: .6; }
.mseg.light button.on {
  background: var(--primary); color: #fff; font-weight: 700;
  box-shadow: 0 2px 6px rgba(37, 99, 235, .3);
}
.mseg.light button.on b { opacity: .85; }

/* ---------- 按跟进人分组（组长品牌模式的「盯人」清单） ---------- */
.mgrp { border-bottom: 1px solid var(--surface-muted); }
.mgrp:last-child { border-bottom: 0; }
.mgrp > summary {
  list-style: none; cursor: pointer; padding: 12px 14px;
  display: flex; align-items: center; gap: 10px;
}
.mgrp > summary::-webkit-details-marker { display: none; }
.mgrp .av {
  width: 34px; height: 34px; border-radius: 11px; flex: none;
  display: flex; align-items: center; justify-content: center;
  font-size: 14px; font-weight: 700; background: #eef2f7; color: var(--text-2);
}
.mgrp .av.warn { background: var(--orange-soft); color: var(--orange-text); }
.mgrp .av.bad { background: var(--red-bg); color: var(--red-text); }
.mgrp .bd { flex: 1; min-width: 0; }
.mgrp .bd .n { font-size: 14px; font-weight: 650; }
.mgrp .bd .s { font-size: 11px; color: var(--text-2); margin-top: 3px; }
.mgrp .rt { flex: none; text-align: right; font-size: 10.5px; color: var(--text-2); }
.mgrp .rt b {
  display: block; font-size: 16px; font-weight: 700; color: var(--red-text);
  line-height: 1.15; font-variant-numeric: tabular-nums;
}
.mgrp[open] > summary { background: var(--surface-hover); }
.mgrp .ct { padding: 0 14px 12px; }
/* 分组内嵌的门店行（点击跟进人名字展开后的列表）。
   ⚠️ 必须显式给左内边距 —— `.mstore::before` 那条状态竖线定位在 `left:0`，
      宽度 3px，所以行内的文字要往右让开它。
      这里原来靠内联 `style="padding:10px 0"` 压掉了默认的 `padding:11px 14px`，
      左内边距变成 0 → **门店名正好压在红线上**（老板截图反馈）。
      改成由 CSS 统一控制：内边距不再散落在 JS 里，两处调用（品牌待办 / 品牌门店列表）一起生效。 */
.mgrp .ct .mstore { padding: 10px 0 10px 13px; }
/* 竖线的上下位置跟着新的纵向内边距走，别再用默认的 top/bottom:11px */
.mgrp .ct .mstore::before { top: 10px; bottom: 10px; }

/* ---------- 门店状态条（一行 8 格，点一下定位门店） ---------- */
/* 口径同线上 PC 端 `statusCards`：8 档，点击进门店列表并按状态筛选。
   390px 一行 8 格 → 每格 41.8px，「违规申诉」装不下，缩为「申诉」。 */
.mstbar { display: grid; grid-template-columns: repeat(8, 1fr); gap: 4px; }
.mstbar .cell {
  border: 0; background: none; font-family: inherit; cursor: pointer;
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  padding: 7px 0 5px; border-radius: 9px; min-width: 0;
}
.mstbar .cell:active { background: var(--surface-muted); }
/* 字号（2026-09-18 老板要求放大）：
   数字 16→18px、标签 10→11px。原来数字下方还有明显余量，放大后正好填满。
   ⚠️ 标签原值 10px 上还叠了 `transform: scale(.96)`（实际 9.6px）—— 那是早期为了
      在 390px 里塞下「未参加 / 跟进中」三字标签做的压缩。现在直接写 11px 并**去掉 scale**：
      11px × 3 字 = 33px，格宽 41.8px，左右各余 4.4px，够；两套缩放叠加反而容易糊。
   ⚠️ 8 列不能改（口径与 PC 端 statusCards 一致），所以标签列只能靠字号控制宽度。 */
.mstbar .cell b { font-size: 18px; font-weight: 700; line-height: 1.15; font-variant-numeric: tabular-nums; }
.mstbar .cell span {
  font-size: 11px; color: var(--text-2); white-space: nowrap;
}
/* 数量为 0 的档位压暗 —— 0 不代表「没有这家店」，而是「这一档没有」 */
.mstbar .cell.dim b { font-weight: 600; }
.mstbar .cell.dim span { color: var(--text-3); }

/* ---------- 当前筛选提示（状态条带过来的） ---------- */
.mfilterhint {
  display: flex; align-items: center; gap: 8px; margin: 0 14px 10px;
  padding: 8px 12px; border-radius: 10px;
  background: #eff6ff; border: 1px solid #dbeafe; color: #1e40af; font-size: 12.5px;
}
.mfilterhint b { font-weight: 700; }
.mfilterhint button {
  margin-left: auto; border: 0; background: none; font-family: inherit; cursor: pointer;
  color: var(--primary); font-size: 12.5px; font-weight: 600; padding: 2px 4px;
}

/* ---------- 日期范围：自定义区间面板 ---------- */
.mdate { display: flex; align-items: center; gap: 7px; margin-top: 9px; }
.mdate .dt {
  flex: 1; min-width: 0; height: 38px; padding: 0 9px;
  border: 1px solid var(--border); border-radius: 9px; background: #fff;
  font-family: inherit; font-size: 12.5px; color: var(--text);
  font-variant-numeric: tabular-nums;
}
.mdate .dt:focus { outline: none; border-color: var(--primary); }
.mdate .sep2 { font-size: 11.5px; color: var(--text-2); flex: none; }
.mrange .cur.bad { color: var(--red-text); }
.mrange .cur .rn { font-style: normal; margin-left: 6px; color: var(--text-2); }

/* ---------- 空态 ---------- */
.mempty { padding: 40px 20px; text-align: center; color: var(--text-2); }
.mempty .ic { width: 46px; height: 46px; border-radius: 14px; background: #eef2f7; color: var(--text-3); display: inline-flex; align-items: center; justify-content: center; }
.mempty .t { font-size: 14px; color: var(--text-2); margin-top: 11px; font-weight: 600; }
.mempty .d { font-size: 12px; margin-top: 5px; line-height: 1.7; }

/* 折叠说明 */
.mfold > summary {
  list-style: none; cursor: pointer; font-size: 12.5px; color: var(--primary); font-weight: 600;
  padding: 11px 14px; display: flex; align-items: center; gap: 6px;
}
.mfold > summary::-webkit-details-marker { display: none; }
.mfold > summary::after { content: "展开"; margin-left: auto; font-size: 11px; color: var(--text-2); font-weight: 400; }
.mfold[open] > summary::after { content: "收起"; }
.mfold .fc { padding: 0 14px 13px; }

/* ============================================================================
   管理员 / 访客手机端（admin-mobile.js）
   ----------------------------------------------------------------------------
   外壳复用上面的 .app.mobile-mode + .mapp + .mtab，不另起一套。
   这里只解决一件事：桌面那 9 个页面产出的是「宽表格 + 多列栅格」，
   在手机上要重排成「卡片 + 单/双列」，否则横向溢出、内容看不出层次。

   兜底思路：一律靠 td 的 data-label 属性选择器，
   所以不需要运行时 JS 打标（charts.js 的 tbl() 已经输出 data-label）。
   只有「比率进度条」需要把百分比写进 --p，那一处由 admin-mobile.js 处理。
   ============================================================================ */

/* 断点用 900：与 style.css 里 .dgrid-2 收单列的断点对齐，避免 769~900 之间出现「栅格单列但表格未卡片化」的中间态 */
@media (max-width: 900px) {
  /* ⚠️ 本段 106 条规则统一带 :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) 前缀。
     两个作用，缺一不可：
       ① 特异性必须到 id 级 (1,0,0) —— style.css 里有「双类」写法（.kv.kv2 = 0,0,2），单类压不动。
          :is() 取参数中的最高特异性，所以加多少个 id 都仍等价于 (1,0,0)。
       ② 作用域 —— 下面这些 id 是「会加载本文件的页面」的根容器：
          #boardWrap → index.html 管理看板 / brand.html 品牌看板
          #app       → mentor.html 辅导老师看板
          #amBody    → 管理员手机端内容容器
          #modalBox / #drawerBox / #provincePanel → 各弹窗与抽屉内容
      ⚠️ 加新独立页时，必须先确认它的根容器 id 在这个列表里。
          漏过的后果已经发生过：mentor.html 的根容器是 #app，不在列表 → cardize.js 照常加 .tb-carded，
          但本文件一条规则都不命中，表格仍是 display:table（实测 928px，容器仅 366px）→ 整页横向滚动 959px。
      不受影响：跟进人端内容容器是 .mapp，不带这些 id。 */

  /* ---------- 1. 页头（管理员一级页） ---------- */
  /* 复用原有的 .mtop / .mtop .act（见本文件 58~72 行），这里不重复定义。
     管理员的一级页头就是 .mtop + <h2>标题</h2>，与跟进人端同构。 */

  /* ---------- 2. 底部 Tab 的「更多」页菜单 ---------- */
  /* 复用原有的 .mmenu（见本文件 485~503 行：.ic / .tx / .tx small / .ar / .ic.n 都已有定义），
     这里同样不重复。admin-mobile.js 生成的「更多」页结构与之完全对齐。 */

  /* ---------- 3. 表格 → 卡片 ---------- */
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded { display: block; width: 100%; }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded thead { display: none; }                 /* 列名改由每格自带 */
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded tbody, :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded tfoot { display: block; }
  /* 表格外层原来带 .scroll（内滚）；卡片不需要内滚，去掉限高与外框 */
  .scroll:has(> .tb-carded), .scroll:has(> table.tb-carded) {
    max-height: none; overflow: visible; border: 0; border-radius: 0; background: none;
  }

  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded tbody tr {
    display: flex; flex-wrap: wrap; align-items: baseline;
    position: relative; overflow: hidden;
    background: var(--white); border: 1px solid var(--border); border-radius: 12px;
    padding: 11px 13px 10px 15px; margin-bottom: 8px;
    box-shadow: 0 1px 2px rgba(16, 24, 40, .04);
  }
  /* 卡片是「可点对象」，不是待选文本 —— 必须禁掉选中与点击高亮。
     ⚠️ 不加这条会有一个很怪的现象：点击（或手指按下时轻微滑动、双击）会把标题
        当成文本选中，出现一块蓝底白字的选区残留在卡片上；卡片里又混着药丸/tag 的
        浅蓝底，整体看起来像「整张卡坏了」。
     ⚠️ **这里刻意不加 `:is(#app,#boardWrap,#amBody,...)` 作用域前缀。**
        本项目绝大多数手机端规则都带那个前缀（防止污染桌面端），但这几条不能带 ——
        卡片可能被渲染进任何容器（实测：管理员端在 #amBody，
        但换角色 / 换页面时容器会变，前缀一不命中整条规则就静默失效，
        表现为「改了没用」，极难排查）。
        之所以敢不带前缀：`.tb-carded` 这个类**只由 cardize.js 在 ≤900px 时添加**，
        桌面端永远不会存在带这个类的元素 → 规则在桌面空转，不会影响表格文字的复制。
     · user-select:none        禁用选区（卡片上要复制的完整信息在「查看详情」弹窗里）
     · tap-highlight-color     去掉 Android 点击时系统叠的那层浅蓝高亮。
                               ⚠️ 它不吃继承，必须逐个元素设，所以用子元素通配。
     · touch-callout           iOS 长按不再弹出「拷贝/查询」系统菜单 */
  .tb-carded tbody tr,
  .tb-carded tbody tr * {
    -webkit-user-select: none; -moz-user-select: none; user-select: none;
    -webkit-tap-highlight-color: transparent;
    -webkit-touch-callout: none;
  }
  /* 兜底：部分 Android WebView / 国产浏览器内核会**忽略 `user-select: none`**，
     那些环境下依然能拖出选区。这里把选中底色也抹掉 —— 与上一条是两个独立机制，
     上一条失效时这条仍能挡住「蓝底白字」的视觉。 */
  .tb-carded tbody tr::selection,
  .tb-carded tbody tr *::selection {
    background: transparent; color: inherit;
  }
  /* 明细段容器：整行 + 内部两列。
     标题（td-t）/ 药丸（badge）/ 主指标（num-big）/ 比率（rate）都在容器外各占自己的行。 */
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded tbody tr > .flds {
    flex: 0 0 100%; width: 100%; order: 4;
    display: grid; grid-template-columns: 1fr 1fr; column-gap: 10px;
    padding: 0; margin: 0; border: 0;
  }
  .tb-carded tbody tr > .flds::before { content: none; }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded tbody tr > .flds > td {
    width: auto; min-width: 0;
    display: grid; grid-template-columns: 4.6em minmax(0, 1fr);
    align-items: baseline; column-gap: 8px;
    padding: 2px 0; border: 0; white-space: normal; text-align: left;
    font-size: var(--fs-sm); font-weight: 600; color: var(--text);
  }
  .tb-carded tbody tr > .flds > td::before {
    content: attr(data-label);
    font-size: var(--fs-sm); font-weight: 500; color: var(--text-2);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  /* 长文本字段（说明 / 该谁处理）独占整行 */
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded tbody tr > .flds > td.wide { grid-column: 1 / -1; }
  /* 地址（`td.addr-cell`）：跨整行 + 完整显示。
     作用对象是**表格里带 addr-cell 的格**（如待审核列表，app.js 的 renderAudit）。
     ⚠️ 门店管理的地址**不走这里** —— 它在副行 `.s2kv` 里，由 app.js 的 `SUB_WIDE` 标 `wide` 跨行。
        两处是不同结构，别指望一条规则管两边（最初我把这条当成门店卡的修复，写错了对象）。
     ⚠️ 截断来自 `style.css` 的 `td.addr-cell { max-width:260px + nowrap + ellipsis }`
        （桌面一行排 13 列必须截断）→ 窄屏卡片内解除。 */
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded td.addr-cell {
    grid-column: 1 / -1;
    max-width: none; white-space: normal; overflow: visible; text-overflow: clip;
    overflow-wrap: anywhere;
  }

  /* 副行（门店管理的两行式记录）：cardize 已把副行并进上一张卡，格子上带 .s2kvtd。
     ⚠️ 必须覆盖 .flds > td 的 `grid-template-columns: 4.6em 1fr` ——
        那是「标签列 + 值」的两列，会把整个 .s2kv 挤到第二列去（第一列空着）。
     ⚠️ 它虽带 colspan，但 cardize 的合并格分支已把它排除（.s2kvtd），
        所以不会多出一行「哪几列合并」的标签 —— 它每项自带字段名。 */
  /* ⚠️⚠️ `flex: 0 0 100%` 不能省 —— `.tb-carded td` 带 `min-width: 0`，
     而 tr 是 flex 容器：`s2kvtd` 作为 flex item 会被**压到内容宽度以下**。
     实测 375 屏下这个格子只有 **65px**（grid 列固定 148px 都装不下 → 溢出），
     所以里面每一项的值列只剩约 75px，一条地址被挤成 4 行
     （老板反馈「地址框长度太短」——根因在这里，不在地址本身，也不是 `.wide` 没生效）。
     `.flds`（明细段）与操作条都已写这句，只有 `s2kvtd` 漏了。
     ⚠️⚠️ `order: 5` 同样不能省：flex 的 `order` 默认 **0**，与 `td.td-t` 同序，
        而 s2kvtd 在 DOM 里是 append 到行末尾的 → 它会插在**标题和状态药丸之间**，
        把药丸顶到下一行（老板截图：药丸掉到了副行明细的下方）。
        取 5 = 排在 `.flds`(4) 之后、操作条(9) 之前。 */
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded td.s2kvtd {
    display: block; flex: 0 0 100%; width: 100%; order: 5; padding: 4px 0 2px;
  }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded td.s2kvtd::before { content: none; }
  /* 内部自适应列：宽了排两列、窄了自动落单列（不必再写断点，也不会挤成两行文字） */
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded .s2kv {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(148px, 1fr));
    column-gap: 10px; row-gap: 3px;
  }
  /* 标签列 5.4em（原 4.6em）：加了图标后 4.6em 装不下 ——
     图标 12 + 间距 4 + 最长 4 个汉字（如「登记时间」）= 68px，
     而 4.6em × 13px = 59.8px → 标签会被省略号截成「登记时…」。
     5.4em × 13px = 70.2px 刚好放开；值列仍有约 87px，11 位手机号放得下。
     ⚠️ 这里**不能用 `baseline`**：`overflow: hidden` 会合成假基线 → 标签被整体压低约 4px
        （改 `center` 解决的）。
     ⚠️ 现改为 `start`：值在本次改动中已允许换行（见 `.s2kv b`），
        值折成多行时 `center` 会把单行标签飘到值块的**垂直中间**，看着像错位；
        `start` 让标签始终对齐值的**首行**。单行值下两者无差别，故无副作用。 */
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded .s2kv > span {
    display: grid; grid-template-columns: 5.4em minmax(0, 1fr);
    align-items: start; column-gap: 8px; min-width: 0;
  }
  /* 与同卡片其它字段同构：字段名次级色、值主色加粗
     ⚠️ `.s2kv i` 不再是 `inline-flex`（图标改走行内流 + `vertical-align`），
        `overflow/text-overflow` 仍然有效 —— 它是 grid 项，会被块化。 */
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded .s2kv i {
    font-style: normal; font-weight: 500; color: var(--text-2);
    min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded .s2kv i svg { display: inline-block; vertical-align: -0.125em; margin-right: 4px; }
  /* 值：**完整展示**，不截断。
     ⚠️ 原来是 `nowrap + overflow:hidden + ellipsis` —— 地址 / 备注这类长值会被切掉后半段，
        卡片上看到的和库里存的不是一回事（老板 2026-09-18 反馈「文本长度被限制，显示不完整」）。
        改法：解除 nowrap，长值换行显示；`overflow-wrap:anywhere` 兜住无空格的长串
        （地址里常见的「广东省深圳市南山区XX路XX号201」中间没有可断点，否则会溢出格子）。
     ⚠️ 与 `.wide`（备注）那条同源 —— 放开后它对 `b` 已无实际作用，故同步删掉。 */
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded .s2kv b {
    font-weight: 600; color: var(--text); min-width: 0;
    white-space: normal; overflow-wrap: anywhere;
  }
  /* 备注：自由文本，跨整行。`align-items: start` 已由上面 `.s2kv > span` 基类提供
     （值现在一律可换行，标签统一对齐首行），这里只需要跨列。 */
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded .s2kv > span.wide { grid-column: 1 / -1; }
  /* 备注项：图标 + 字段名 + 值 全部绿色，值加下划线（老板要求）。
     ⚠️ 手机端**必须单独写一份** —— 上面给 `.s2kv i` / `.s2kv b` 设色时带了
        `:is(#app,…)` 前缀（特异性 121），只写 style.css 那份（特异性 34）会被盖掉。
        这里加上 `> span.kv-note` 后特异性升到 131 → 稳压上面两条 ✓
     ⚠️ 类名挂在整项 <span> 上，图标 / 文字才能一起变绿（挂 <svg> 只管得到图标）。 */
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded .s2kv > span.kv-note i { color: var(--green); }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded .s2kv > span.kv-note i svg { color: var(--green); }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded .s2kv > span.kv-note b { color: var(--green); text-decoration: underline; }

  /* 缴费项（2026-10-23）：内容比其它字段长（药丸 + 金额 + 连缴月数），
     在半列里必换行 → 跨整行，标签列仍走 5.4em、值列拿剩余宽度。
     ⚠️ 必须另写一份：上面 `:is(#app,…) .s2kv b` 特异性 121，
        style.css 里的 `.s2kv > span.kv-fee b`（34）会被盖掉。 */
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded .s2kv > span.kv-fee { grid-column: 1 / -1; }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded .s2kv > span.kv-fee i { color: var(--text); font-weight: 700; }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded .s2kv > span.kv-fee b {
    display: inline-flex; white-space: normal; font-weight: 400;
  }

  /* 值长的表改单列。
     ⚠️ 「外部只读」这张表字段少但值长（「可查看的看板范围」是多个权限标签、
        「登录方式」是「按用户名登录」）—— 两列平分时每列仅约 160px，
        结果标签被截成「可查看…」、权限标签竖排成三行（实测截图确认）。
        用 :has() 判定：明细格里有 .tag / .pill 这类长标签的，整表改单列。 */
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded tbody tr > .flds:has(.tag, .pill) {
    grid-template-columns: 1fr;
  }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded tbody tr > .flds:has(.tag, .pill) > td {
    grid-template-columns: 5.4em minmax(0, 1fr);   /* 标签列加宽，「可查看的看板范围」不被截断 */
  }
  /* 值里的标签组：横向换行排列，别挤成竖排。
     ⚠️⚠️ `justify-self: start` 不能省 —— 它的父 td 是 `display:grid`（标签列 + 值列），
        标签作为 grid item 默认 `stretch`，会把背景色**撑满整个值列**：
        一行只有一个品牌时，看着就是「背景色拉满整行」，而不是一个标签。
        `display:inline-block` 治不了这个（stretch 是 grid 层面的，与 display 无关）。
        加了它，背景才只贴合文字宽度；行内多个标签仍按 flex 流自然排列。 */
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded .flds .tag,
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded .flds .pill {
    display: inline-block; justify-self: start;
    margin: 0 4px 3px 0; white-space: nowrap;
  }
  /* 合并格（colspan>1）：整行呈现，并在值前标出它是哪几列合并来的（如「职能 · 管辖」）。
     原来是「没有字段名的整行」，卡片上只剩一串光秃秃的文字。 */
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded td.merged {
    grid-template-columns: 1fr; padding: 3px 0 1px;
  }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded td.merged::before {
    content: attr(data-merged); display: block;
    font-size: var(--fs-xs); color: var(--text-2); margin-bottom: 2px;
  }

  /* 详细列：卡片上不显示，收进「详情」弹窗。
     用户要求「只显示基本的用户信息，详细的用弹窗去调整」。 */
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded td.det { display: none !important; }

  /* 声明不卡片化的行（如 mentor.html 的「展开明细」行）：
     去掉卡片外观，作为「附着在上一张卡下面的整块」，视觉上仍属同一条目。
     ⚠️ 显隐必须用 `.open` 自己控制 —— 卡片化的 `.tb-carded tbody tr { display: flex }`
        特异性(0,1,3) 高于原页面的 `.expand-row { display: none }`(0,1,0)，
        会把「默认隐藏」覆盖掉 → 所有展开面板一开始就全露出来。
        这里用 `.tr-nocard` / `.tr-nocard.open` 两个更高特异性的规则接管。 */
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded tbody tr.tr-nocard {
    display: none; background: none; border: 0; box-shadow: none;
    padding: 0; margin: -8px 0 10px;
  }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded tbody tr.tr-nocard.open {
    display: block;
  }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded tbody tr.tr-nocard::before { display: none; }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded tbody tr.tr-nocard > td {
    display: block; width: 100%; padding: 0 12px 12px 15px;
    background: var(--surface-2); border: 1px solid var(--border); border-top: 0;
    border-radius: 0 0 12px 12px;
  }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded tbody tr.tr-nocard > td::before { content: none; }

  /* 「查看详情」按钮：放在操作条最前，整宽一条，与下方动作区分开 */
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded td[data-label-op] .btn-det {
    flex: 1 1 100%; height: 40px; margin-bottom: 2px;
    background: var(--surface-2); border-color: var(--border); color: var(--primary-dark);
    font-weight: 600; gap: 5px;
  }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded td[data-label-op] .btn-det::before {
    content: ''; width: 13px; height: 13px; flex: none;
    background: currentColor; opacity: .85;
    -webkit-mask: no-repeat center/contain;
    mask: no-repeat center/contain;
    /* 眼睛图标（MingCute eye-line 同规格，24 画布） */
    -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' d='M2.5 12S6 5.5 12 5.5 21.5 12 21.5 12 18 18.5 12 18.5 2.5 12 2.5 12Z'/%3E%3Ccircle cx='12' cy='12' r='3' fill='none' stroke='%23000' stroke-width='2'/%3E%3C/svg%3E");
    mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' d='M2.5 12S6 5.5 12 5.5 21.5 12 21.5 12 18 18.5 12 18.5 2.5 12 2.5 12Z'/%3E%3Ccircle cx='12' cy='12' r='3' fill='none' stroke='%23000' stroke-width='2'/%3E%3C/svg%3E");
  }

  /* 详情弹窗的字段行 */
  .dlg { padding-top: 4px; }
  .dlg-r { padding: 11px 0; border-bottom: 1px solid var(--surface-muted); }
  .dlg-r:last-child { border-bottom: 0; }
  .dlg-r .k { display: block; font-size: var(--fs-xs); color: var(--text); margin-bottom: 4px; }
  .dlg-r .v { display: block; font-size: var(--fs-md); color: var(--text); line-height: 1.6; font-weight: 500; }
  .dlg-r .v .tag, .dlg-r .v .pill { display: inline-block; margin: 0 5px 4px 0; }
  .tb-carded tbody tr::before {                       /* 左侧状态色条 */
    content: ''; position: absolute; left: 0; top: 11px; bottom: 11px; width: 3px;
    border-radius: 0 3px 3px 0; background: var(--surface-muted);
  }
  .tb-carded tbody tr.st-ok::before   { background: var(--green); }
  .tb-carded tbody tr.st-warn::before { background: var(--orange); }
  .tb-carded tbody tr.st-bad::before  { background: var(--red); }

  /* 通用格：默认按明细样式（真正的排布交给 .flds 容器与各自变体） */
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded td {
    width: auto; min-width: 0;
    padding: 0; border: 0; white-space: normal; text-align: left;
    font-size: var(--fs-sm); font-weight: 600; color: var(--text);
  }
  /* 单元格自带的右对齐（.r）在卡片里没意义，归零 */
  .tb-carded td.r, .tb-carded td[style*="text-align:right"] { text-align: left !important; }
  /* 没有任何 data-label 的格（跨列说明）→ 整行 */
  .tb-carded td:not([data-label]) { flex: 1 1 100%; display: block; }

  /* ---------- 4. 卡片标题（首格）+ 状态药丸：同一行 ---------- */
  /* ⚠️ 药丸原来用 position:absolute 定位到右上角，实测被标题格盖住 ——
     elementFromPoint 在药丸位置返回的是 td.td-t，即 in-flow 兄弟盖住了 absolute 子项。
     两个都是 z-index:auto，绘制顺序在 flex 容器里不好把握。
     改成正常流：标题 flex-grow 占满左侧、药丸 flex:none 靠右，同一行 —— 结构上就不存在层叠问题。 */
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded td.td-t {
    flex: 1 1 auto; min-width: 0; order: 0;
    display: block; padding: 0; margin: 0 0 5px; border: 0;
    font-size: var(--fs-md); font-weight: 650; line-height: 1.35; color: var(--text);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  /* 有状态药丸的行：标题收紧成内容宽，让药丸**紧跟店名之后**，不再被推到最右。
     ⚠️ 原来 `.td-t` 是 `flex: 1 1 auto` 撑满左侧 → 药丸永远贴右边缘，
        标题短时中间空一大段，看不出「药丸描述的就是这个标题」。
     ⚠️ 安全边界：`min-width: 0` + 标题自带 ellipsis 保证长名仍截断；
        `tr` 是 `flex-wrap: wrap`，极端情况药丸换行而非溢出容器。
     ⚠️ 用 `:has(> td.badge)` 限定，没药丸的行（如用户管理的内部员工）保持原样。 */
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded tr:has(> td.badge) > td.td-t {
    flex: 0 1 auto;
  }
  .tb-carded td.td-t::before { content: none; }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded td.td-t .sub { display: block; margin-top: 2px; font-size: 11.5px;
    font-weight: 400; color: var(--text-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

  /* ---------- 5. 状态药丸：跟在标题右侧 ---------- */
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded td.badge {
    flex: none; width: auto; order: 1; margin: 0 0 5px 8px;
    display: inline-flex; align-items: center;
    padding: 2px 8px; border-radius: 999px;
    font-size: 11px; font-weight: 600; line-height: 1.6;
    background: var(--surface-muted); color: var(--text); white-space: nowrap;
  }
  .tb-carded td.badge::before { content: none; }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded tr.st-ok td.badge   { background: var(--green-soft); color: var(--green-text); }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded tr.st-warn td.badge { background: var(--orange-soft); color: var(--orange-text); }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded tr.st-bad td.badge  { background: var(--red-bg); color: var(--red-text); }

  /* ---------- 6. 主指标（金额 / 数量）：标签与值同行相邻 ---------- */
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded td.num-big {
    flex: 0 0 100%; order: 2; align-self: flex-start;
    display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px;
    padding: 0; margin: 0 0 9px; border: 0;
    font-size: 21px; font-weight: 700; letter-spacing: -.5px;
    color: var(--primary); font-variant-numeric: tabular-nums;
  }
  .tb-carded td.num-big::before { flex: 0 0 auto; font-size: 12px; font-weight: 600; color: var(--text-2); }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded tr.st-warn td.num-big { color: var(--orange-text); }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded tr.st-bad td.num-big  { color: var(--red-text); }

  /* ---------- 7. 比率（覆盖率 / 占比）：值 + 进度条 ---------- */
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded td.rate {
    flex: 0 0 100%; order: 3;
    display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between;
    padding: 0; margin: 0 0 8px; border: 0;
    font-size: var(--fs-sm); font-weight: 700; color: var(--text);
  }
  .tb-carded td.rate::before { flex: none; font-size: 11.5px; font-weight: 500; color: var(--text-2); }
  .tb-carded td.rate::after {
    content: ''; flex: 0 0 100%; height: 4px; margin-top: 5px; border-radius: 2px;
    background: var(--surface-muted) no-repeat;
    background-image: linear-gradient(90deg, var(--primary) 0, var(--primary) calc(var(--p, 0) * 1%), transparent calc(var(--p, 0) * 1%));
  }
  .tb-carded tr.st-warn td.rate::after {
    background-image: linear-gradient(90deg, var(--orange) 0, var(--orange) calc(var(--p, 0) * 1%), transparent calc(var(--p, 0) * 1%));
  }
  .tb-carded tr.st-bad td.rate::after {
    background-image: linear-gradient(90deg, var(--red) 0, var(--red) calc(var(--p, 0) * 1%), transparent calc(var(--p, 0) * 1%));
  }

  /* ---------- 8. 手机上不展示的列 ---------- */
  /* ⚠️ 砍哪些列只由 admin-mobile.js 的 DROP 决定（它给命中的 td/th 打 .col-drop）。
     CSS 这边不再自己维护一份列名清单 —— 两套列表必然会走偏：
     实测「操作」列已在 JS 里移出 DROP，CSS 里还留着 [data-label="操作"]{display:none}，
     结果门店的操作条被隐藏、宽度算成 0。单一来源，别重复。 */
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded .col-drop { display: none !important; }

  /* ---------- 9. 长表截断 + 展开 ---------- */
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded tr.tr-fold { display: none; }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-fold {
    display: block; width: 100%; margin-top: 8px; cursor: pointer;
    border: 1px dashed var(--border); background: var(--surface-3); color: var(--text-2);
    font: 500 var(--fs-base)/1.5 inherit; padding: 10px; border-radius: var(--radius-lg);
  }
  .tb-fold:active { background: var(--surface-muted); }

  /* ---------- 10. 栅格：无内联后由断点接管 ---------- */
  /* ⚠️ 这里必须带 :is(...) 前缀（id 特异性 1,0,0）—— style.css 里的固定列数变体是「双类」（.kv.kv2 = 0,0,2），
     单类选择器压不动它，栅格会保留桌面列数。:is() 取参数中的最高特异性，加多少个 id 都仍是 (1,0,0)。（作用域见 @media 开头的说明） */
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .kv2, :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .kv4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .dgrid-5, :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .dgrid-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .dgrid-2, :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .dgrid-2-1, :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .dgrid-1-2,
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .dash-grid, :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .dash-grid-fit { grid-template-columns: 1fr; }
  /* 网格/弹性子项默认 min-width:auto 会拒绝收缩，里面一有宽表格就把栅格顶开 */
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .dgrid-2 > *, :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .dgrid-3 > *, :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .dgrid-5 > *, :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .dgrid-2-1 > *,
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .dgrid-1-2 > *, :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .kv2 > *, :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .kv4 > *,
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .dash-grid > *, :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .dash-grid-fit > * { min-width: 0; }

  /* ---------- 11. 4 屏切换（数据看板）吸顶等宽 ---------- */
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .dtab { position: sticky; top: 0; z-index: 30; margin-bottom: 12px; overflow: visible;
    background: var(--bg); border-bottom: 1px solid var(--border); }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .dtab button { flex: 1 1 0; min-width: 0; justify-content: center; padding: 9px 2px; font-size: var(--fs-sm); }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .dtab button .mi { display: none; }

  /* ---------- 12. 条形排行行：.lb 原本 flex-shrink:0，拒绝收缩把行撑破 ---------- */
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .barrow { min-width: 0; }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .barrow .lb { flex: 1 1 auto; min-width: 0; }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .barrow .lb .nm2, :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .barrow .lb .sb2 { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .barrow .hbar { min-width: 24px; }

  /* ---------- 13. 品牌对比行：1fr 被内容撑开 ---------- */
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .cmp-row { grid-template-columns: minmax(0, 1fr) 72px minmax(0, 1fr); gap: 6px; }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .cmp-row .cmp-b { min-width: 0; }

  /* ---------- 14. 卡片标题竖排、图表不溢出 ---------- */
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .card-title { flex-direction: column; align-items: flex-start; gap: 3px; }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .card-title .card-hint { font-size: var(--fs-xs); text-align: left; line-height: 1.6; }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .card svg, :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .card canvas, :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .card img { max-width: 100%; }
  /* 环形图：窄屏下卡片被 .dgrid-3 排成两列（内容区仅 ~148px），环必须能缩。
     尺寸来自 svgRing 传的 --ring（见 style.css .ring），这里只覆盖宽度、高度按等比算。 */
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .ring {
    width: min(100%, var(--ring, 160px)); height: auto; aspect-ratio: 1;
  }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .card { overflow-x: clip; }

  /* ---------- 15. 警示块只留一句 ---------- */
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .u-alert { padding: 10px 12px; gap: 9px; align-items: flex-start; background: var(--surface-2); border-color: var(--border); }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .u-alert .ic { background: var(--surface-muted); color: var(--text-2); }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .u-alert .tx { font-size: var(--fs-base); line-height: 1.5; color: var(--text-2); }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .u-alert .tx > span { display: none; }             /* 细节段收起 */
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .u-alert .tx b { font-size: var(--fs-base); color: var(--text); }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .u-alert.danger { background: var(--red-bg); border-color: var(--red-border); }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .u-alert.danger .ic { background: #fff; color: var(--red-text); }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .u-alert.danger .tx, :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .u-alert.danger .tx b { color: var(--red-text); }

  /* ---------- 16. 分类红降噪：红绿留给真告警 ---------- */
  /* app.js 用 pill-r(红)/pill-g(绿) 编码「品牌」与「市场」——那是分类色，不是警示。
     一屏能刷出 20+ 红标，真问题反被淹；中文语境红绿还容易读成涨跌。
     ⚠️ 必须限定在 #amBody（管理员手机端的内容容器）！
        跟进人端（mobile.js）也用 .pill-r 表示风险分档（从未填过 / 多日未填）——
        那是**真警示**，全局降噪会把它的语义也抹掉。
        :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) 是 admin-mobile.js 独有的 id，用它作作用域最准确。 */
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .pill.pill-r, :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .pill.pill-g { background: var(--surface-muted); color: var(--text-2); }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .mono[style*="--red"] { color: var(--text) !important; }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .v[style*="--red"], :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .v[style*="red-text"] { color: var(--red) !important; }   /* KPI 大数字保留红 */

  /* ---------- 18. 表单：手机上保持两列并排 ---------- */
  /* ⚠️ 我原来写的是 grid-template-columns: 1fr（单列），导致弹窗里每个字段各占一行、
     一路竖排下去 —— 用户反馈「录入信息还是一行行下去」就是这个。
     .form-row 原本就是 1fr 1fr（两列）。390 屏减去内边距后每列约 170px，
     放「姓名 / 手机号 / 城市 / 负责人」这类短字段完全够 —— 不该改成单列。
     只有 .form-row.full（地址 / 备注 / textarea 这类长字段）才整行。 */
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .form-row { grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 10px; }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .form-row.full { grid-template-columns: 1fr; }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .toolbar .spacer { display: none; }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .toolbar input,
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .toolbar select { flex: 1 1 40%; min-width: 0; }
  /* 「显示列」弹窗的勾选项：整行可点，但原行高只有 38px（小于 44px 的触控下限） */
  :is(#modalBox) .colpick-i { min-height: 44px; padding: 10px 12px; }
  :is(#modalBox) .colpick { grid-template-columns: 1fr; }   /* 手机上单列，标签才不折行 */
  /* 极窄屏（≤360）两列会挤，那时才退回单列 */
  @media (max-width: 360px) {
    :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .form-row { grid-template-columns: 1fr; }
  }

  /* ==========================================================================
     19. 弹窗 / 抽屉 → 手机端形态
     --------------------------------------------------------------------------
     现状：.modal 是「居中卡片 + width:94vw + max-height:90vh + overflow-y:auto」，
     手机上表现为：上下都留空、内容在卡片内部滚、底部按钮跟着内容滚出屏幕 ——
     「修改信息」这类表单根本不好用。
     改成底部 sheet：贴底、全宽、圆角只在上面；标题吸顶、按钮吸底、中间内容滚。
     ⚠️ 弹窗挂在 body 下（#modalMask 在 .app 外面），所以这里的选择器**不能**带
        .app/.mapp 之类祖先限制，否则不生效。用 .modal-mask / .modal 本身即可。
     ========================================================================== */
  .modal-mask { align-items: flex-end; }                       /* 贴底 */
  .modal {
    width: 100%; max-width: none; max-height: 88vh;
    border-radius: 16px 16px 0 0; padding: 0;
    overflow-y: auto; -webkit-overflow-scrolling: touch;
    box-shadow: 0 -8px 32px rgba(0, 0, 0, .22);
  }
  /* ⚠️⚠️ 必须写成 `.modal-mask.show .modal`（0,3,0），不能写 `.modal`（0,1,0）：
     桌面端 style.css 里 `.modal-mask.show .modal { animation: modal-in }` 特异性更高，
     会把低特异性的 `.modal { animation: sheet-in }` **完全压掉** ——
     上线以来移动端的底部上滑入场**一直没生效过**（实际跑的是桌面的 modal-in）。
     改成同等特异性后，靠 mobile.css 在 style.css 之后加载来胜出。 */
  .modal-mask.show .modal { animation: sheet-in .22s var(--ease-out); }
  /* 顶部拖拽条：告知「这是可下拉的底部面板」 */
  .modal::before {
    content: ''; display: block; width: 36px; height: 4px; border-radius: 2px;
    background: var(--border); margin: 8px auto 0;
  }
  @keyframes sheet-in { from { transform: translateY(100%); } to { transform: translateY(0); } }
  /* 标题吸顶 */
  .modal h3 {
    position: sticky; top: 0; z-index: 2; margin: 0;
    padding: 12px 16px 10px; background: var(--white);
    font-size: var(--fs-lg); border-bottom: 1px solid var(--surface-muted);
  }
  /* ⚠️⚠️ 有返回栏时，标题的吸顶位置必须**下移到返回栏下方**（2026-10-01 修）。
     原先两者都 `top: 0` 且 z-index 同为 2 → DOM 在后的标题把返回栏整个盖住，
     手机端表现为「返回键不跟随滑动 / 看不见」（桌面端标题不吸顶，所以那里正常）。
     偏移量引用 style.css 里 .modal 的 `--mnav-h`，与返回栏自身高度**同一个变量**。 */
  .modal.has-nav h3 { top: var(--mnav-h); }
  /* 内容区左右留白（modal 自己 padding 已归零，交给各段） */
  .modal > *:not(h3):not(.modal-foot) { padding-left: 16px; padding-right: 16px; }
  .modal > .form-row:first-of-type, .modal > .detail-body:first-of-type { padding-top: 14px; }

  /* 底部按钮吸底、全宽平分，触控高度 ≥46px */
  .modal-foot {
    position: sticky; bottom: 0; z-index: 2; margin: 0;
    padding: 12px 16px max(12px, env(safe-area-inset-bottom));
    background: var(--white); border-top: 1px solid var(--border);
    gap: 9px;
  }
  .modal-foot .btn { flex: 1; justify-content: center; height: 46px; font-size: var(--fs-md); }
  .modal-foot.danger { flex-direction: row-reverse; }           /* 危险操作：安全项仍在最右 */

  /* 表单控件放大到可点：字号 16px 是为了避免 iOS 聚焦时页面自动放大 */
  .modal .form-item input, .modal .form-item select, .modal .form-item textarea,
  .modal .f-item input, .modal .f-item select, .modal .f-item textarea,
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .confirm-req input, :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .form-item input, :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .form-item select, :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .form-item textarea {
    padding: 11px 12px; font-size: 16px; min-height: 46px;
  }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .form-item label { font-size: var(--fs-base); }

  /* === 下拉框已换成自绘组件（public/js/ui-select.js + style.css）===
     原生 select 被该组件隐藏并替换成自绘触发器，所以这里**不再需要**
     「改原生 select 外观」的规则 —— 那套已被替代，留着只会两套并存、互相干扰。
     手机端 sheet 的外观在 style.css 末尾的 @media (max-width:900px) 里。 */

  /* === 日期控件保持浏览器原生（不做任何外观覆盖）===
     老板明确要求：日期用原生，不要改。
     所以这里**没有** input[type="date"] 的任何规则 —— 原生外观、原生日历弹层全部保留。
     系统日历图标是这个控件里最有价值的可点提示，动它只会变差。 */

  /* --- 数字：去掉系统上下小箭头（手机上根本点不准，还挤占输入区） --- */
  #amBody input[type="number"], #modalBox input[type="number"], #drawerBox input[type="number"] {
    -webkit-appearance: none; -moz-appearance: textfield; appearance: textfield;
  }
  #amBody input[type="number"]::-webkit-outer-spin-button, #amBody input[type="number"]::-webkit-inner-spin-button,
  #modalBox input[type="number"]::-webkit-outer-spin-button, #modalBox input[type="number"]::-webkit-inner-spin-button,
  #drawerBox input[type="number"]::-webkit-outer-spin-button, #drawerBox input[type="number"]::-webkit-inner-spin-button {
    -webkit-appearance: none; margin: 0;
  }
  .modal .detail-row { padding: 9px 0; }
  .modal .detail-row .dl { width: 84px; font-size: var(--fs-sm); }
  .modal .ph-opt, .modal .ph-chip { min-height: 42px; }         /* 电话选择项 */
  /* 弹窗里的表格也走卡片化（门店/用户的批量操作弹窗里有表） */
  .modal .scroll { max-height: none; overflow: visible; border: 0; }

  /* ---------- 评分维度条：窄屏改成两行，维度名不限长度 ----------
     桌面版是「标签 112px | 进度条 | 得分 64px」一行三列。手机上两侧固定列吃掉 188px，
     标签只剩 112px —— 维度名可由管理员在「权重设置」里自由改名，长一点就折行或截断。
     改成两行：第一行「维度名 + 得分」左右分列（名长自己换行，完整显示），
     第二行进度条通栏。得分右对齐，读数位置与桌面一致。 */
  .sc-mb .it {
    grid-template-columns: 1fr auto;
    grid-template-areas: "lb vv" "bar bar";
    gap: 6px 10px;
  }
  .sc-mb .it .lb { grid-area: lb; }
  .sc-mb .it .vv { grid-area: vv; white-space: nowrap; }
  .sc-mb .it .bar { grid-area: bar; }

  /* 抽屉（门店详情 / 轻编辑）：手机上整屏，头部与底部固定、中间滚 */
  .drawer { width: 100%; }
  .drawer .dh { padding: 14px 16px; flex: none; }
  .drawer .db { padding: 14px 16px; }
  .drawer .df { padding: 12px 16px max(12px, env(safe-area-inset-bottom)); flex: none; gap: 9px; }
  .drawer .df .btn { flex: 1; justify-content: center; height: 46px; }

  /* ==========================================================================
     20. 操作入口 → 卡片底部操作条 + 底部 sheet 菜单
     --------------------------------------------------------------------------
     ⚠️ 「操作」列不能在手机上砍掉 —— 它承载「编辑 / 品牌授权 / 重置密码 / 看门店 / 删除」，
        砍掉就等于手机上没法改任何东西。正确做法是把它从表格里挪到卡片底部。
     ⚠️ 门店的「操作」是一个「···」按钮（.btn-more），点开是 .popmenu；该菜单原本是
        position:absolute 挂在 td 上、top:calc(100% - 6px) —— 手机上会跑到卡片外
        并被卡片 overflow:hidden 裁掉。这里改成固定贴底的 sheet（fixed 不受祖先 overflow 裁剪）。
     ========================================================================== */
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded td[data-label-op] {
    flex: 0 0 100%; width: 100%; order: 9;
    display: flex; flex-wrap: wrap; gap: 6px;
    margin-top: 9px; padding-top: 9px; border-top: 1px dashed var(--border);
  }
  .tb-carded td[data-label-op]::before { content: none; }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded td[data-label-op] .btn {
    flex: 1 1 calc(50% - 3px); min-width: 0;
    height: 40px; font-size: var(--fs-sm); justify-content: center; padding: 0 8px;
  }
  /* 卡片上的「···」：不要把动作全枚举一遍 —— 点开还是这三项，纯冗余。
     原按钮里只有三个点（font-size:0 藏掉），这里换成一句概括。 */
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .tb-carded td[data-label-op] .btn-more {
    flex: 1 1 100%; height: 42px;
    font-size: 0; font-weight: 600; letter-spacing: 0;
  }
  .tb-carded td[data-label-op] .btn-more::after {
    content: '更多操作'; font-size: var(--fs-base); letter-spacing: 0;
  }

  /* 行内「···」菜单 → 底部动作面板（action sheet）
     结构：标题（门店名）→ 动作行 → 取消行。
     原来是三个各自带圆角的按钮，看着像三个浮块，不像一个操作菜单。 */
  .popmenu {
    position: fixed; left: 0; right: 0; bottom: 0; top: auto;
    min-width: 0; border-radius: 16px 16px 0 0; overflow: hidden;
    padding: 0 0 max(6px, env(safe-area-inset-bottom));
    /* ⚠️ 遮罩不能用 `::before { position:fixed; inset:0; z-index:-1 }`：
       .popmenu 是 position:fixed + z-index:300，**自己构成 stacking context**。
       在这个上下文里，z-index:-1 的子元素画在「本元素背景之后、内容之前」——
       也就是遮罩盖住了面板自己的白底 → 整个面板被压成灰的（实测截图整片灰）。
       改用 box-shadow 大扩散：**外阴影画在元素背景之下**，天然充当遮罩，
       且不受 stacking context 影响。100vmax 保证铺满视口。 */
    box-shadow: 0 -8px 32px rgba(0, 0, 0, .24), 0 0 0 100vmax rgba(17, 24, 39, .55);
    z-index: 300;
    animation: sheet-in .2s var(--ease-out);
  }
  /* 标题：说明「这些操作作用于哪家店」—— 列表几十家，不写清楚容易点错 */
  .popmenu-t {
    display: block; padding: 14px 18px 12px;
    background: var(--surface-3); border-bottom: 1px solid var(--border);
    font-size: var(--fs-md); font-weight: 600; color: var(--text); text-align: center;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .popmenu button {
    display: block; width: 100%; border-radius: 0;
    padding: 16px 18px; font-size: 16px; text-align: center; line-height: 1.2;
    border-bottom: 1px solid var(--surface-muted);
  }
  .popmenu button:active { background: var(--surface-hover); }
  .popmenu button.danger { color: var(--red-text); font-weight: 600; }
  /* 取消项：与动作组拉开一段距离（用背景色当间隔），并加重字重 */
  .popmenu-cancel {
    display: block; margin-top: 0;
    border-top: 6px solid var(--bg) !important;
    border-bottom: 0 !important;
    font-weight: 600; color: var(--text);
  }

  /* ==========================================================================
     21. 页内 Tab 条（用户管理三 Tab / 看板分屏）→ 横向滚动、不折行
     --------------------------------------------------------------------------
     现状：.utabs 是 flex + gap:0，手机上三项挤在 390 里 →
     「内部员工 115」被压成两行（「内部/员工」+ 数字另起一行），看着像坏了。
     改成不换行 + 横向滚动，并去掉图标省宽度。
     ========================================================================== */
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .utabs { overflow-x: auto; flex-wrap: nowrap; scrollbar-width: none; }
  .utabs::-webkit-scrollbar { height: 0; }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .utab { flex: none; padding: 10px 12px; font-size: var(--fs-base); white-space: nowrap; }
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .utab .ic { display: none; }                 /* 手机上只留文字，三项就能放下 */
  :is(#app,#boardWrap,#amBody,#modalBox,#drawerBox,#provincePanel) .utab .cnt { font-size: var(--fs-xs); }
}

/* ==========================================================================
   22. 顶部下拉刷新指示器（pull-refresh.js）
   --------------------------------------------------------------------------
   挂 body 下的固定元素（外壳每次渲染都重建，不能放进 .mapp 里）。
   平时藏在上沿外（-46px），下拉时被 JS 逐步推下来；ready 态变主色并加粗，
   spin 态图标转圈。pointer-events:none —— 纯展示，不挡任何触摸。
   ========================================================================== */
#ptrInd {
  position: fixed; top: 0; left: 50%; z-index: 1200;
  transform: translate(-50%, -46px);
  display: flex; align-items: center; gap: 6px;
  padding: 7px 15px; border-radius: 0 0 15px 15px;
  background: var(--white); box-shadow: var(--shadow);
  font-size: 11.5px; color: var(--text-2); white-space: nowrap;
  transition: transform .18s ease-out;
  pointer-events: none;
}
#ptrInd .ptr-ic { display: flex; color: var(--primary); }
#ptrInd .ptr-ic svg { transition: transform .3s; }
#ptrInd.ready { color: var(--primary); font-weight: 600; }
#ptrInd.spin .ptr-ic svg { animation: ptr-spin .8s linear infinite; }
@keyframes ptr-spin { to { transform: rotate(360deg); } }

/* ============================================================
   41. 用户管理 · 手机端专用卡（v2 重设计，2026-09-20）
   ------------------------------------------------------------
   用户管理的四张表在窄屏不走 table→cardize 的机械转换 ——
   app.js 的 drawXxxPane 在 ≤900px 直接输出 .um-* 紧凑卡结构
   （标题+状态+两行元信息+小按钮行），事件与 PC 同一套函数。
   类名全局唯一（.um-*），PC 端不输出，无需 :is(#amBody…) 作用域。
   ============================================================ */
.um-stats { display: flex; gap: 8px; overflow-x: auto; padding: 2px 0 12px; -webkit-overflow-scrolling: touch; }
.um-stat { flex: 0 0 auto; min-width: 92px; background: var(--card); border: 1px solid var(--border); border-radius: 12px; padding: 10px 12px; }
.um-stat b { display: block; font-size: 20px; line-height: 1.2; color: var(--text); }
.um-stat span { font-size: var(--fs-xs); color: var(--text-2); }
.um-stat.um-warn b { color: var(--orange-text); }
.um-tools { display: flex; flex-wrap: wrap; gap: 8px; margin: 2px 0 12px; align-items: center; }
.um-tools input { flex: 1 1 150px; min-width: 0; padding: 9px 12px; border: 1px solid var(--border); border-radius: 10px; font-size: var(--fs-base); outline: none; }
.um-tools input:focus { border-color: var(--primary); }
.um-tools .btn { flex: 0 0 auto; }
.um-card { position: relative; background: var(--card); border: 1px solid var(--border); border-radius: 14px; padding: 12px 14px; margin-bottom: 10px; }
.um-hd { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.um-nm { font-size: 15px; color: var(--text); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.um-nm b { font-weight: 600; }
.um-subtag { display: inline-block; margin-left: 6px; padding: 1px 8px; border-radius: var(--radius-pill); background: var(--surface-muted); color: var(--text-2); font-size: var(--fs-xs); font-weight: 400; vertical-align: 1px; }
.um-meta { margin-top: 6px; font-size: var(--fs-sm); color: var(--text-2); line-height: 1.5; word-break: break-all; }
.um-meta .tag { margin-top: 2px; }
.um-ops { position: relative; display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.um-ops .btn { flex: 0 0 auto; margin: 0; }
.um-more-all { margin-top: 2px; }

/* 看板日期条（手机端）：留出屏边间隙，日期框不顶满 */
.dash-rangebar { padding: 0 4px; }
.dr-custom { width: 100%; }
.dr-custom input[type="date"] { flex: 1 1 110px; min-width: 0; }

/* ==========================================================================
   填报质量（2026-10-07）
   --------------------------------------------------------------------------
   两处用：① 门店详情页的「填报质量」摘要卡（.mq-head + .mq-more）
           ② 「填报分析」二级页（.mq-tabs / .mq-ov / .mq-cal / .mq-rows …）
   算法与口径见 mobile.js 的「⑤-0 填报质量 · 共用算法」—— 天数全在那边算，这里只管长相。
   ⚠️ 电脑端（app.js 的 __storeBoard）用的是「字段 × 30 天」矩阵，是另一套类（.sq-*，在 style.css），
      与这里的胶囊列表**有意不同**：手机端窄，胶囊可读性更好；PC 端宽，矩阵密度更高。
   ========================================================================== */
/* ① 摘要卡：三格迷你 KPI（漏填 / 0 成交 / 0 团购） */
.mq-head { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1px; background: var(--surface-muted); }
.mq-head > div { background: var(--card); padding: 11px 6px 12px; text-align: center; }
.mq-head .lb { font-size: 11px; color: var(--text-2); }
.mq-head .vv { font-size: 21px; font-weight: 700; margin-top: 5px; letter-spacing: -.6px; font-variant-numeric: tabular-nums; color: var(--text); }
.mq-head .vv small { font-size: 10.5px; font-weight: 400; color: var(--text-2); margin-left: 1px; letter-spacing: 0; }
.mq-head > div.bad .vv { color: var(--red-text); }
.mq-head > div.warn .vv { color: var(--orange-text); }
.mq-head > div.good .vv { color: var(--green-text); }
.mq-head > div.dim .vv { color: var(--text-3); }
.mq-more { display: flex; align-items: center; justify-content: center; gap: 5px; width: 100%; height: 40px;
  border: 0; border-top: 1px solid var(--surface-muted); background: var(--card); color: var(--primary);
  font-size: 13px; font-weight: 600; font-family: inherit; cursor: pointer; }
.mq-more:active { background: var(--bg); }

/* ② 分析页：档位条 —— 拼在 navBar 后面，一起固定（不随 .mbody 滚） */
.mq-tabs { padding-top: 10px; }
/* 概览：大数 + 已填/漏填堆叠条 + 三个数 */
.mq-ov { padding: 2px 14px 14px; }
.mq-ov .big { display: flex; align-items: baseline; gap: 7px; }
.mq-ov .big b { font-size: 33px; font-weight: 700; letter-spacing: -1.3px; line-height: 1.05; font-variant-numeric: tabular-nums; }
.mq-ov .big small { font-size: 12.5px; color: var(--text-2); }
.mq-ov.bad .big b { color: var(--red-text); }
.mq-ov.ok .big b { color: var(--green-text); }
.mq-stack { display: flex; height: 9px; border-radius: 5px; overflow: hidden; background: var(--surface-muted); margin: 13px 0 9px; }
.mq-stack i { display: block; height: 100%; }
.mq-stack .s-fill { background: var(--primary); }
.mq-stack .s-miss { background: var(--red); }
.mq-leg { display: flex; flex-wrap: wrap; gap: 6px 15px; font-size: 11.5px; color: var(--text-2); }
.mq-leg span { display: inline-flex; align-items: center; gap: 6px; }
.mq-leg i { width: 9px; height: 9px; border-radius: 3px; flex: none; }
.mq-leg b { color: var(--text); font-variant-numeric: tabular-nums; }
/* 逐日格：红=漏填 / 斜纹橙=已填但七项全 0 / 蓝=已填有数据 */
.mq-cal { display: grid; grid-template-columns: repeat(15, 1fr); gap: 4px; padding: 0 14px; }
.mq-cal i { display: block; aspect-ratio: 1; border-radius: 5px; background: var(--surface-muted); }
.mq-cal i.fill { background: #2563eb; }
.mq-cal i.zero { background: repeating-linear-gradient(45deg, #fb923c, #fb923c 3px, #fed7aa 3px, #fed7aa 6px); }
.mq-cal i.miss { background: var(--red-bg); border: 1px solid #fecaca; }
.mq-calx { display: grid; grid-template-columns: repeat(15, 1fr); gap: 4px; padding: 6px 14px 0; }
.mq-calx span { font-size: 9px; color: var(--text-3); text-align: center; font-variant-numeric: tabular-nums; }
.mq-lg { display: flex; flex-wrap: wrap; gap: 6px 14px; padding: 11px 14px 13px; font-size: 11.5px; color: var(--text-2); }
.mq-lg span { display: inline-flex; align-items: center; gap: 6px; }
.mq-lg i { width: 11px; height: 11px; border-radius: 3.5px; flex: none; }
/* 各项空窗：一项一块 —— 标题行（项目 + 为 0 总天数）+ 段胶囊 + 零散 + 诊断 */
.mq-rows { padding: 2px 14px 4px; }
.mq-row { padding: 10px 0; border-bottom: 1px solid var(--surface-muted); }
.mq-row:last-child { border-bottom: 0; }
.mq-row .hd { display: flex; align-items: baseline; gap: 8px; }
.mq-row .k { font-size: 12.5px; color: var(--text); flex: 1; }
.mq-row .tot { font-size: 11.5px; color: var(--text-2); font-variant-numeric: tabular-nums; }
/* 段胶囊：一段一个，「09/13~09/21（9天）」，段多自动换行 */
.mq-segs { display: flex; flex-wrap: wrap; gap: 5px 6px; margin-top: 6px; }
.mq-segs span { font-size: 11px; color: var(--red-text); background: var(--red-bg);
  border: 1px solid #fecaca; border-radius: 6px; padding: 2px 7px; font-variant-numeric: tabular-nums; line-height: 1.5; }
.mq-segs span b { font-weight: 700; }
.mq-none { font-size: 11.5px; color: var(--text-3); margin-top: 5px; }
/* 诊断提示：由规则算出（看这段空窗里上游环节有没有值），不是套模板 */
.mq-tip { font-size: 11.5px; color: #1e40af; background: var(--info-bg); border-radius: 7px;
  padding: 6px 9px; margin-top: 6px; line-height: 1.6; }
/* 底栏的「今日」条 */
.mq-today { display: flex; align-items: center; gap: 7px; flex: 1; font-size: 12.5px; color: var(--text-2); }
.mq-today b { color: var(--text); }

/* ==========================================================================
   跟进记录汇总页 · 手机端（2026-10-22）
   --------------------------------------------------------------------------
   桌面规则在 style.css（.sn-* 全量），这里只做**尺寸与布局**覆盖，不重复定义颜色。
   ⚠️ 右栏三块在手机上**不渲染**（renderStoreNoteList 已按 shouldUseMobile 分支），
      这里的 display:none 只是兜底，防止哪天路由改了冒出个 300px 的空栏。
   ⚠️ 表格视图在手机上走 .sn-row 紧凑行（不是 table）—— 表格横滑在手机上等于看不全，
      与 charts.js 的 cardizeTables 同一个判断。
   ========================================================================== */
@media (max-width: 900px) {
  .sn-page { gap: 11px; }
  .sn-kpis { grid-template-columns: 1fr 1fr; gap: 9px; }
  .sn-kpi { padding: 11px 12px; gap: 10px; }
  .sn-kpi .ic { width: 30px; height: 30px; border-radius: 9px; }
  .sn-kpi .v { font-size: 20px; letter-spacing: -.3px; }
  .sn-kpi .lb { font-size: 11px; margin-top: 3px; }

  .sn-bar { padding: 10px 11px; gap: 8px; }
  .sn-search { width: 100%; }
  .sn-search input { width: 100%; height: 38px; border-radius: var(--radius-lg); }
  .sn-chips { width: 100%; flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch;
    scrollbar-width: none; padding-bottom: 2px; }
  .sn-chips::-webkit-scrollbar { display: none; }
  .sn-chip { flex: none; height: 29px; }
  .sn-seg { width: 100%; margin-left: 0; }
  .sn-seg button { flex: 1; justify-content: center; height: 32px; }

  .sn-grid { grid-template-columns: 1fr; gap: 11px; }
  .sn-side { display: none; }

  /* 时间线：竖轴左移，卡片更紧凑 */
  .sn-flow { padding: 2px 12px 14px; }
  .sn-day { padding-top: 12px; }
  .sn-day-h { padding: 2px 0 10px 16px; margin-bottom: 12px; }
  .sn-items::before { left: 4px; }
  .sn-item { padding-left: 16px; padding-bottom: 9px; }
  .sn-dot { left: -16px; top: 16px; width: 11px; height: 11px; box-shadow: 0 0 0 2px var(--bg); }
  .sn-card { padding: 11px 12px; border-radius: var(--radius-md); }
  .sn-txt { -webkit-line-clamp: 3; line-height: 1.7; }
  .sn-tm { font-size: 11px; }
  .sn-f { font-size: 11px; }

  .sn-rows { border-radius: var(--radius-lg); }
  .sn-pager { padding-top: 9px; }
}

/* ============================================================================
   每日经营体检（2026-10-23）
   ----------------------------------------------------------------------------
   入口卡 `.mhl`：跟进人首页 hero 之后第一张。数字（家需处理）异步填，
   没生成时显示「—」，不显示 0 —— 0 会被误读成「没问题」。
   弹窗内容复用 style.css 的 `.hl-*`，这里只做小屏压缩。
   ========================================================================== */
.mhl{ cursor: pointer; }
.mhlb{ display: flex; align-items: center; gap: 10px; }
.mhl-n{ font-size: 11.5px; color: var(--text-2); white-space: nowrap; flex: none; }
.mhl-n b{ font-size: 25px; font-weight: 700; color: var(--red-text); margin-right: 4px; vertical-align: -3px; font-variant-numeric: tabular-nums; }
.mhl-t{ flex: 1; min-width: 0; font-size: 11.5px; color: var(--text-3); }
.mhl-go{ flex: none; color: var(--text-3); display: flex; }

@media (max-width: 900px) {
  .hl-hd{ padding: 12px 13px; gap: 10px; border-radius: 12px; }
  .hl-hd .t{ font-size: var(--fs-md); }
  .hl-hd .m{ font-size: 11px; line-height: 1.6; }
  .hl-hd .m .sep{ margin: 0 3px; }
  .hl-ring{ width: 58px; height: 58px; }
  .hl-ring .in b{ font-size: 18px; }
  .hl-ring .in span{ font-size: 9px; }
  .hl-sum{ font-size: 12.5px; padding: 11px 12px; line-height: 1.7; }
  .hl-ai{ font-size: 11px; padding: 8px 10px; }
  .hl-sec{ margin-bottom: 15px; }
  .hl-sec-h .hint{ display: none; }
  .hl-item{ padding: 10px 11px 10px 14px; border-radius: 10px; }
  .hl-item-hd .nm{ font-size: 12.8px; line-height: 1.45; }
  .hl-chips span{ font-size: 11px; padding: 1px 6px; }
  .hl-say{ font-size: 12.2px; line-height: 1.7; }
  .hl-advice{ font-size: 12px; padding: 11px 12px; line-height: 1.72; }
  .hl-hrow{ padding: 10px 11px; gap: 9px; }
  .hl-hrow .d{ width: 58px; }
  .hl-hrow .sc{ width: 42px; }
  .hl-hrow .tx{ font-size: 11.5px; }
  /* 弹窗底部按钮平分（mobile.css 里 .modal-foot 已有吸底处理，这里只管宽度） */
  #modalBox .modal-foot .btn{ flex: 1; }
}
/* 没报告时的占位文案：替换掉大号「—」（25px 红粗体会变成一根红杠，看着像故障） */
.mhl-wait{ font-size: 12.5px; color: var(--text-3); font-weight: 400; }

@media (max-width: 900px) {
  .hl-group{ padding: 10px 11px 10px 14px; border-radius: 10px; }
  .hl-group-hd b{ font-size: 12.8px; }
  .hl-group-say{ font-size: 12.2px; line-height: 1.7; }
  .hl-group-more{ padding: 8px; font-size: 12px; }
  /* 手机上店名 + 跟进人 + 数字挤不进一行 → 数字换到第二行 */
  .hl-grow{ flex-wrap: wrap; gap: 3px 8px; padding: 7px 2px; }
  .hl-grow .nm{ max-width: 100%; flex: 1 1 auto; }
  .hl-grow .cs{ margin-left: 0; text-align: left; flex: 1 1 100%; }
}

@media (max-width: 900px) {
  .hl-action, .hl-copy{ font-size: 12px; padding: 6px 9px; }
  .hl-acts li{ font-size: 12.2px; }
  .hl-todo{ padding: 12px 13px; }
  .hl-todo-h{ font-size: 13px; }
  .hl-todo-row .bd b{ font-size: 12.8px; }
}
