/* ==========================================================================
   medradar 亮色主题
   --------------------------------------------------------------------------
   分层靠「浅灰画布 + 纯白卡片 + 阴影阶梯」，不靠深色块、也**不靠描边**
   （2026-09-23 边框饮食：容器一律不描边，边框只留输入框、表行分隔线、聚焦环）。
   表格是这套系统的主体，所以表头吸顶、行悬停、数值列等宽数字这些细节
   比配色更影响观感，都单独处理过。
   ========================================================================== */

:root {
  /* ======================================================================
     设计变量：**与 Arco Design 同构**（2026-09-18）
     ----------------------------------------------------------------------
     分两层，各有各的用途：
       ① 第一层用 **Arco 的名字与刻度**（色阶是 RGB 三元组、语义别名、
          圆角 0/2/4/8、字号 12/14/16/20/24）——这样两条产品线（geohub 用 Arco）
          在视觉上是一个系统；将来这一页真要换成 Arco 组件时，这层能 1:1 喂给
          Arco 的 `ConfigProvider` token，不用重新调色。
       ② 第二层是 **medradar 既有的变量名，全部保留为别名**——app.js 与 theme.css
          里几千处引用的是它们，改名等于全站重写一遍，收益为零。

     取值来源：Arco 编译产物（`@arco-design/web-react@2 dist/css/arco.css`）
 **抄录**，不是凭印象写的。两处**有意偏离**都写在下面各自的注释里。
     ====================================================================== */

  /* ── Arco 中性色阶（RGB 三元组：可拼 rgba(var(--gray-6), .5) 叠透明度）── */
  --gray-1: 247, 248, 250;
  --gray-2: 242, 243, 245;
  --gray-3: 229, 230, 235;
  --gray-4: 201, 205, 212;
  --gray-5: 169, 174, 184;
  --gray-6: 134, 144, 156;
  --gray-7: 107, 119, 133;
  --gray-8: 78, 89, 105;
  --gray-9: 39, 46, 59;
  --gray-10: 29, 33, 41;

  /* ── 品牌主色：医知雷达绿 #0f9d78 的 Arco 式色阶 ──────────────────────
     不是 Arco 默认蓝（--arcoblue-6 = 22,93,255 / #165DFF）——主色是品牌资产，
     改成蓝等于换品牌。1..10 按 Arco 的明度阶梯生成（1-5 向白、7-10 向黑），
     这样它和 Arco 的其它色阶放在一起是同族的。 */
  --primary-1: 231, 245, 242;
  --primary-2: 178, 224, 212;
  --primary-3: 130, 204, 185;
  --primary-4: 87, 186, 161;
  --primary-5: 51, 172, 140;
  --primary-6: 15, 157, 120;
  --primary-7: 13, 133, 102;
  --primary-8: 11, 110, 84;
  --primary-9: 8, 86, 66;
  --primary-10: 6, 63, 48;

  /* ── 语义色（Arco 的 danger/warning/success 就是 red/orange/green 色阶）── */
  --danger-1: 255, 236, 232;
  --danger-6: 245, 63, 63;
  --warning-1: 255, 247, 232;
  --warning-6: 255, 125, 0;
  --success-1: 232, 255, 234;
  --success-6: 0, 180, 42;
  /**
 * info 蓝。这是 **Arco 默认主色 arcoblue-6**（22,93,255）
   * 注意：它**不是**我们的主色（主色是品牌绿，见下面的 --primary-*），只在需要"进行中/信息"
   * 这一档语义色时用（目前唯一用处：机构线索的阶段点「已联系」）。
   * 补它的理由：阶段点原先是 5 个硬编码 hex，其中一个就是这个蓝——色值在令牌体系之外，
   * 单测不了、也换不了；登记成令牌之后，色盲档、暗色主题、对比度复核才有地方下手。
   */
  --info: 22, 93, 255;
  /**
   * 蓝的浅底档（arcoblue-1）。与 `--info` 配对做"信息"类徽标，**这对配对就是 Arco 的**
 * （Arco 的蓝色 Tag 就是 blue-6 文字 + blue-1 底）。换过来之前过对比度
   * `arcoblue-6` 在 `arcoblue-1` 上 **4.62:1**（过 AA 4.5）；旧值手调的 `#3b5bdb` 是 5.05——
   * 略降但仍在门槛上，换来的是"不必再自己调一档蓝"。
   */
  --info-weak: 232, 243, 255;

  /* ── 语义别名（映射关系同样抄自 Arco：text-1..4 = gray-10/8/6/4 等）── */
  --color-text-1: rgb(var(--gray-10));
  --color-text-2: rgb(var(--gray-8));
  --color-text-3: rgb(var(--gray-6));
  --color-text-4: rgb(var(--gray-4));
  --color-bg-1: #ffffff;
  --color-bg-2: rgb(var(--gray-1));
  --color-bg-3: rgb(var(--gray-2));
  --color-bg-4: rgb(var(--gray-3));
  --color-border-1: rgb(var(--gray-2));
  --color-border-2: rgb(var(--gray-3));
  --color-border-3: rgb(var(--gray-4));
  --color-border-4: rgb(var(--gray-6));
  --color-fill-1: rgb(var(--gray-1));
  --color-fill-2: rgb(var(--gray-2));
  --color-fill-3: rgb(var(--gray-3));
  --color-fill-4: rgb(var(--gray-4));

 /* ── 圆角：Arco 的刻度名，取值从 0/2/4/8 抬到 0/4/6/12─────
     改的是**取值**不是名字——名字本来就是 Arco 的，将来喂 ConfigProvider 是同一次改动。
     为什么抬：2~4px 的控件圆角是"老一辈后台"的第一眼来源；面板用 8px 又配 1px 边框时，
     看着像表格的分格线，不像一张卡片。全站引用 --radius-sm / --radius 的地方一起生效。 */
  --border-radius-none: 0;
  --border-radius-small: 4px;
  --border-radius-medium: 6px;
  --border-radius-large: 12px;
  --border-radius-circle: 50%;

  /* ── 字号与间距：按 Arco 的刻度定义 ───────────────────────────────────
     Arco 没把字号/间距导出成 CSS 变量（编译产物里是字面 px，实测），所以这两个刻度
     用的是它的规范值、名字是我们自己的。 */
  --font-size-sm: 12px;
  /**
   * 刻度下面还有一档「微型」：卡片副数、单页脚注、成交标记这类**附属说明**。
   * 它比正文低一档才读得出主次，所以登记成令牌、而不是散回 12px —— 全站只有这一档 11px。
   * 装饰性的东西（排序箭头那种纯记号）不占这一档，直接用 `--font-size-sm`。
   */
  --font-size-xs: 11px;
  --font-size-base: 14px;
  --font-size-lg: 16px;
  --font-size-xl: 20px;
  --font-size-2xl: 24px;
  /** 概览 KPI 的数字：比 2xl 大一档，作为一屏的主视觉 */
  --font-size-kpi: 28px;
  /**
   * **第二处有意偏离：全站正文 13px**（Arco 的正文是 14px）。
   * 本项目表格密集，13px 是刻意的密度选择——登记成变量而不是散落的字面量：
   * 偏离是有意的，而且要能一处改。
   */
  --font-size-table: 13px;

  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space-8: 32px;
  --space-10: 40px;

  /* ══ 以下为 medradar 既有变量名（别名，保持不变）══════════════════════
     旧名 → Arco 层。颜色都走 rgb(var(--…))，与 Arco 的合成方式一致。 */
  /**
   * `--bg` 的角色是**浅填充**：悬停底、chip 底、进度槽、代码底，全站 27 处引用它。
   * 页面画布另走 `--color-bg-3`（见 body 一条）——原先两者共用一个值，于是卡片与背景
 * 几乎同色，只能靠 1px 边框分开。拆开之后边框才能退场。
   */
  --bg: var(--color-bg-2);
  --card: var(--color-bg-1);
  /* 原 #fbfcfd（几乎纯白）→ Arco 的 fill-1：表头/斑马纹的底，略微加深一档 */
  --card-2: var(--color-fill-1);
  --border: var(--color-border-2);
  --border-strong: var(--color-border-3);

  --text: var(--color-text-1);
  --dim: var(--color-text-2);
  /**
   * 次要文字。**不再直接指向 Arco 的 `--color-text-3`（gray-6）**：那档在浅填充上只有
   * 2.92:1、白底 3.24:1，不到正文要求的 4.5 —— 而它承载的是「未认领」「非定点」「下辖 N 家」
   * 这类**要读的信息**，不是装饰。所以单独定一档（取 gray-6 与 gray-7 之间），
   * 在浅填充上 4.81、白底 5.34。
   * `--color-text-3` 本身不动：它是 Arco 的别名，Arco 组件按自己的刻度走。
   * 唯一例外是品牌标识里那个英文名，见 `.brand .en`。
   */
  --dim-2: rgb(97, 108, 122);

  /**
 * 品牌绿**分两个角色**（按对比度定的，见各值后面的数字）
   *   · `--accent`      = -6 #0f9d78：**填充与图形**（图标、进度条、选中底、logo 信号点、
   *                        边框、焦点环）。3.43:1 —— 图形/大字够（门槛 3.0），**当正文不行**
   *   · `--accent-text` = -8 #0b6e54：**浅底上的绿字**（链接、当前导航、hover、
   *                        绿底徽标上的字）。6.23:1 —— 过 AA（门槛 4.5）
   *   · `--accent-strong` = -7 #0d8566：**绿底白字的填充**（主按钮）。白字在其上 4.60:1 ✓
   * 原先三者是同一个绿：绿字只有 3.43、白字在绿底上也只有 3.43，**都没过 AA**。
   * 所以这不是调色偏好，是把「色块」和「文字」拆开——同一个色相，不同的明度职责。
   */
  --accent: rgb(var(--primary-6));
  --accent-text: rgb(var(--primary-8));
  --accent-strong: rgb(var(--primary-7));
  /**
   * Arco 的 hover 取 -5（更亮）；我们取更深一档。
   * 有意偏离：主色按钮是「绿底白字」，浅底上悬停变亮会压低对比度，加深更稳。
   *
   * ⚠️ 2026-10-13 修：这里原先是 `--primary-7`，而主色按钮的底色 `--accent-strong` 也是 -7
   * —— 两个令牌同值，于是**悬停是个空操作**（实测 `button.primary:hover` 前后背景色一模一样）。
   * 现在取 -8（比底色再深一档），意图（"悬停加深"）才真的成立，且对比度更高。
   */
  --accent-hover: rgb(var(--primary-8));
  --accent-weak: rgb(var(--primary-1));

  /* 分数档位（高/中/低）借用 Arco 的语义色，不再是各自拍的颜色 */
  --high: rgb(var(--danger-6));
  --high-weak: rgb(var(--danger-1));
  --mid: rgb(var(--warning-6));
  --mid-weak: rgb(var(--warning-1));
  --low: var(--color-text-3);
  --low-weak: var(--color-fill-2);
  /**
   * 状态色的**文字档**：橙/红/灰当**文字**用（徽标、分数、「高意向」这类指路字）时，
   * 鲜亮档只有 2.41~3.71，不到正文要求的 4.5。
   *
   * 鲜亮档**不删**：色点、色条、左侧那道警示色条、按钮描边这些**不承载文字**的地方继续用它
   * （那类只需 3:1，鲜亮更好认）。这与主色那一组是同一个办法——`--accent`（填充与图形）
   * 对 `--accent-text`（文字）。
   *
   * 取值的下限是"在各自的浅底上也过 4.5"：橙在 `--mid-weak` 上 5.05、红在 `--high-weak` 上 4.52。
   * 灰那一档（`--low`）本来与次要文字同色，所以文字档直接指向 `--dim-2`，不另拍一个灰。
   */
  --warning-7: 176, 77, 0;
  --danger-7: 209, 42, 42;
  --mid-text: rgb(var(--warning-7));
  --high-text: rgb(var(--danger-7));
  --low-text: var(--dim-2);
  /** 小计行底色：要明显区别于白底，否则小计会被当成明细读 */
  --row-sum: var(--color-fill-2);

  /**
 * 阴影阶梯：原来只有一档 `0 1px 2px rgba(29,33,41,.04)`
   * 4% 透明度的一像素阴影在浅底上等于不可见，卡片于是只能靠边框立起来，
   * "边框密集"的观感就是这么来的。三层各管一件事，别互相顶替：
   *   -1 静置卡片 / -2 悬浮与下拉 / -3 弹层与抽屉。
   * 旧名 `--shadow`、`--shadow-lg` 保留为别名，全站上千处引用不用动。
   */
  --shadow-1: 0 1px 2px rgba(16, 24, 40, 0.06), 0 1px 3px rgba(16, 24, 40, 0.1);
  --shadow-2: 0 4px 16px -4px rgba(16, 24, 40, 0.12);
  --shadow-3: 0 12px 32px -8px rgba(16, 24, 40, 0.18);
  --shadow: var(--shadow-1);
  --shadow-lg: var(--shadow-3);
  /**
   * 胶囊（整圆）半径。原来三处各写一个魔数：徽标 20px、进度条轨道 100px、标签 999px ——
   * 在 19~24px 高的元素上三者渲染完全一样，留着只是让下一个人猜"该用哪个"。
   * 统一取 999px（比元素高度大就一定渲染成整圆）。
   */
  --radius-pill: 999px;
  --radius: var(--border-radius-large);
  --radius-sm: var(--border-radius-medium);

  /**
   * 动效：原来 12 处各写各的（.12s / .15s / .22s / ease / linear）。
   * 统一到两档时长 + 一条缓出曲线——"手感"就来自这两件事，
   * 不统一时同一屏会出现快慢不一的三种节奏。文件末尾有 prefers-reduced-motion 兜底。
   */
  --motion-fast: 120ms;
  --motion-base: 180ms;
  --ease: cubic-bezier(0.4, 0, 0.2, 1);
  /** 顶栏高度。侧栏的 sticky/height 都要减掉它，改这里一处即可 */
  --topbar-h: 52px;
  /**
   * 吸顶表头的实际高度。**键盘让位要用它**（见文末「聚焦让位」那条 `scroll-padding-top`）。
   *
   * 数值 = 表头单元格 上下内距 11px×2 + `--font-size-sm` 12px×行高 1.5 = 18px + 下边框 1px。
   * 三档视口实测都是 41px（连名册那种行高 37px 的表也是 41 —— 行高只改数据行，不改表头）。
   * 所以改表头内距或 `--font-size-sm` 时，**这里要跟着改**，否则让位量会正好差一截。
   */
  --thead-h: 41px;
}

* { box-sizing: border-box; }

html, body { height: 100%; }

body {
  margin: 0;
  /* 画布比浅填充（--bg）再深一档：白卡片靠明度差自己浮起来，不必再描边 */
  background: var(--color-bg-3);
  color: var(--text);
  font: 14px/1.6 -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC",
    "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
}

a { color: var(--accent-text); text-decoration: none; }
a:hover { text-decoration: underline; }

/* ------------------------------- 布局 ------------------------------- */

/* 整屏顶栏 + 下方「侧栏 | 内容」两栏。
   顶栏横跨整个宽度（含侧栏上方），品牌在左、账号在右——对齐 Beisen 那类
   后台的骨架：顶栏是全局条（我是谁、跨模块入口），侧栏是模块内导航。
   历史：账号身份原先浮在内容区右上角，为了不和页头按钮撞车，页头得留 216px
   右内边距，窄屏还会把主操作挤到第二行。上顶栏后这些避让全部不需要了。 */
.shell { display: flex; flex-direction: column; min-height: 100vh; }

.topbar {
  position: sticky; top: 0; z-index: 30;
  flex: none; height: var(--topbar-h);
  display: flex; align-items: center; gap: 12px;
  padding: 0 18px;
  background: var(--card);
  border-bottom: 1px solid var(--color-border-1);
}
.topbar .spacer { flex: 1; }
/* .brand 的基础布局必须挂在通用选择器上：登录页也用 .brand，而它自己的
   `.brand { justify-content: center; gap: 11px }` 是**假设**这里已经是 flex 才写的。
   历史上 display:flex 只挂在 .side 下，所以登录页那两条声明一直是失效的——
   品牌块实际是行内布局、左对齐，只是行内基线 + .names 的间距碰巧看着不别扭，才没被发现。
   把它收窄回 .topbar 会让登录页退回那个状态（品牌块不再居中）。 */
.brand { display: flex; align-items: center; gap: 10px; font-weight: 700; }
.topbar .brand { gap: 9px; }
.topbar .brand .cn { font-size: var(--font-size-lg); }
.topbar .brand .en { font-size: var(--font-size-sm); }

.layout { flex: 1; display: flex; min-height: 0; }

.side {
  width: 208px;
  flex: 0 0 208px;
  background: var(--card);
  border-right: 1px solid var(--color-border-1);
  display: flex;
  flex-direction: column;
  position: sticky;
  top: var(--topbar-h);
  height: calc(100vh - var(--topbar-h));
}

/* 移动端导航抽屉：≤820 时侧栏变成覆盖层，内容区不再被 208px 挤成一条 */
.nav-toggle {
  display: none; align-items: center; justify-content: center;
  width: 32px; height: 32px; padding: 0; border: 0; border-radius: var(--radius-sm);
  background: transparent; color: var(--text); cursor: pointer;
}
.nav-toggle:hover { background: var(--bg); }
.nav-backdrop { position: fixed; inset: var(--topbar-h) 0 0; z-index: 35; background: rgba(16, 24, 40, .35); }
@media (max-width: 820px) {
  .nav-toggle { display: inline-flex; }
  .side {
    position: fixed; top: var(--topbar-h); left: 0; bottom: 0;
    z-index: 40; width: 240px; height: auto;
    transform: translateX(-100%);
    transition: transform var(--motion-base) var(--ease);
    box-shadow: var(--shadow-3);
  }
  .side.open { transform: none; }
  .main { padding: 16px 16px 48px; }
  body.nav-open { overflow: hidden; }
}

/* 品牌名排版：中文名为主、英文名为辅。
   英文名跟着 .names 自己的 4px 间距走，不吃 .brand 的 gap——
   之前英文名跟着 flex gap 走，和中文名之间隔了 17px，看着像两个不相干的词。 */
/* 品牌标记：雷达量程圈 + 医疗十字（见 docs/reference/logo.md）
   缺口环缓慢旋转，读作「波束在扫」；再慢也必须有 prefers-reduced-motion 开关——
   主题动效缺失这一条在 web-design-guidelines 里是明确要修的。 */
.brand .names { display: inline-flex; align-items: baseline; gap: 4px; }
.brand .cn { font-weight: 700; letter-spacing: .2px; white-space: nowrap; }
/* 英文名**不跟着 --dim-2 一起加深**：它与中文名合起来读作一个标记（WCAG 1.4.3 对 logotype 有豁免），
   而且是陪衬——按 4.5 的深度写会跟中文名抢注意力。这是本页唯一保留浅灰的文字。 */
.brand .en { font-weight: 400; letter-spacing: .2px; white-space: nowrap; color: rgb(var(--gray-6)); }
.login-page .brand .cn, .p-brand .cn { font-size: var(--font-size-lg); }

/* 标记配色：结构（同心环 + 虚线外环）走 --mark-structure，中心点走 currentColor。
   深色底上把 --mark-structure 换成浅色即可，点色不用动。 */
.brand .mark { color: var(--accent); --mark-structure: #101a2e; flex: none; }
/* 转的是虚线外环（.ring-scan）——自转读作「正在扫描」，而且虚线没有明确的前缘，
   天生不会像「圆心射出的实线」那样被看成钟表指针。
   transform-box 必须是 view-box：fill-box 会拿虚线环自己的包围盒当原点，
   而它的包围盒中心不在圆心（虚线有缺口），一转就歪出画面。 */
.brand .mark .ring-scan {
  transform-box: view-box;
  transform-origin: 32px 32px;
  animation: sweep 6s linear infinite;
}
@keyframes sweep {
  to { transform: rotate(360deg); }
}
@media (prefers-reduced-motion: reduce) {
  .brand .mark .ring-scan { animation: none; }
}

/* hidden 属性必须真的能藏东西。
   浏览器对 [hidden] 的默认 display:none 来自 UA 样式表，**任何作者样式里的 display 都会盖掉它**——
   于是 nav.hidden = true 这种写法"属性生效了、视觉没生效"（.nav a 有 display:flex）。
   实测踩到：团队入口本该只对主管可见，销售登录后照样看得见、点进去被弹回机构线索。
   这条兜底一劳永逸，别再逐个元素去猜它有没有 display 规则。 */
[hidden] { display: none !important; }

/* 顶部留白原来由品牌块撑开（品牌块已上顶栏），所以这 14px 得自己给 */
.nav { padding: 14px 10px 4px; display: flex; flex-direction: column; gap: 2px; }
.nav a {
  display: flex; align-items: center; gap: 9px;
  padding: 10px 12px; border-radius: var(--radius-sm);
  color: var(--dim); font-weight: 500;
}
.nav a:hover { background: var(--bg); color: var(--text); text-decoration: none; }
.nav a.active { background: var(--accent-weak); color: var(--accent-text); font-weight: 600; }
.nav .ico { width: 16px; text-align: center; font-size: var(--font-size-base); }

/* 顶栏右侧：账号入口
   --------------------------------------------------------------------------
   位置：侧栏底部 → 内容区右上角浮层 → 顶栏。
   形态：带框的账号 chip → 素圆头像（对齐 geohub 的做法）。
   右上角不该出现一个"单独的框"：那是把「设置面板」的语汇放进了全局条，
   而且 260px 的带框块和左侧品牌块各占一头，看着像两个平级的模块。
   一个圆头像就够了——它读起来是「操作者」，点开才是「账号与操作」。

   账号名和角色移进下拉（头部），头像 title 上也带着。
   这个取舍是有代价的：多账号下「我现在是哪个账号」不再一眼可见——
   如果后面觉得销售容易拿错账号，就在头像左边补一行不带框的小字（.idwho），
   不要退回带边框的块。 */
.idbar { position: relative; flex: none; }
.idav {
  display: flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; padding: 0;
  border: 0; border-radius: 50%;
  /* 实心饱和圆（对齐 geohub）：淡底 + 同色字太轻，会被读成"在线状态点"而不是账号入口 */
  background: var(--accent); color: #fff;
  cursor: pointer;
}
.idav .av { font-size: var(--font-size-sm); font-weight: 700; line-height: 1; }
/* hover 用加深而不是加边框：边框一出现就又变回"框"了 */
.idav:hover { background: var(--accent-hover); }
.idav:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.idmenu {
  position: absolute; top: calc(100% + 8px); right: 0; width: 208px;
  background: var(--card); border: 1px solid var(--border-strong);
  border-radius: var(--border-radius-large); padding: 4px; box-shadow: var(--shadow-2);
}
.idmenu .hd { padding: 8px 10px 7px; border-bottom: 1px solid var(--border); margin-bottom: 4px; }
.idmenu .hd .n { font-size: var(--font-size-table); font-weight: 600; overflow: hidden; text-overflow: ellipsis; }
.idmenu .hd .r { font-size: var(--font-size-sm); color: var(--dim-2); margin-top: 1px; }
.idmenu button {
  /* Arco 竖排菜单项是 `padding: 0 12px + line-height: 40px`；菜单不是表格，字号回到 14px */
  display: flex; align-items: center; width: 100%; height: 40px; text-align: left; border: 0; background: none;
  font: inherit; font-size: var(--font-size-base); padding: 0 12px; border-radius: var(--border-radius-medium);
  cursor: pointer; color: var(--text);
}
.idmenu button:hover { background: var(--accent-weak); color: var(--accent-text); }
.idmenu button.danger:hover { background: var(--low-weak); color: var(--low-text); }

.side .links button:hover { color: var(--accent-text); }
.side .links button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.main { flex: 1; min-width: 0; padding: 24px 24px 64px; }

/* 子页分段切换（跟进 / 看盘）：一级菜单只留一个入口，组内切换放在内容区顶部 */
.subnav { margin: 0 0 14px; }
.subnav[hidden] { display: none; }

/* 数据健康 · 趋势：三条内联折线，不引图表库 */
.trend-row { display: flex; gap: 28px; flex-wrap: wrap; }
.trend-item { min-width: 200px; }
.trend-item .k { font-size: var(--font-size-sm); color: var(--dim-2); }
.trend-item .v { font-size: 20px; font-weight: 650; line-height: 1.4; }
.trend-item svg { display: block; margin: 4px 0; color: var(--accent); }

/* 概览 · 该处理的事：数字 | 事项 | 动作 一行一条（这套类名此前没有样式，三行被排成了三坨） */
.todo-row { display: flex; align-items: center; gap: 14px; padding: 12px 0; border-top: 1px solid var(--border); }
.todo-row:first-child { border-top: 0; }
.todo-row .todo-n { min-width: 64px; text-align: right; font-size: 22px; font-weight: 650; color: var(--high-text); }
.todo-row .todo-main { flex: 1; min-width: 0; }
.todo-row .todo-label { font-weight: 600; color: var(--text); }
.todo-row .todo-why { font-size: var(--font-size-sm); color: var(--dim-2); margin-top: 2px; line-height: 1.4; }
.team-sum { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; font-size: var(--font-size-table); color: var(--dim); }
.team-sum b { font-size: var(--font-size-xl); color: var(--text); font-weight: 650; font-variant-numeric: tabular-nums; }
.team-sum .mini { margin-left: auto; }
/* 待处理队列：批量条 + 问题行。表格沿用 .dh-table */
.queue-batch { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding: 8px 0 12px; border-bottom: 1px solid var(--border); }
.queue-batch label { display: inline-flex; align-items: center; gap: 6px; font-size: var(--font-size-table); }
.queue-batch input[type='checkbox'], .queue-table input[type='checkbox'] { width: auto; height: auto; }
.queue-batch select { width: 160px; }
.queue-table td, .queue-table th { white-space: nowrap; }
.queue-table td:nth-child(2) { white-space: normal; }
.queue-issue { display: flex; align-items: center; gap: 14px; padding: 12px 0; border-top: 1px solid var(--border); }
.queue-issue:first-child { border-top: 0; }
.queue-issue .q-n { min-width: 64px; text-align: right; font-size: 22px; font-weight: 650; color: var(--high-text); }
.queue-issue .q-main { flex: 1; min-width: 0; }
.queue-issue .q-label { font-weight: 600; color: var(--text); }
.admin-pager .mini[aria-disabled='true'] { opacity: .4; pointer-events: none; }

/* 名册页标题：之前 h1 被压成 1×1，首屏只剩筛选条，用户失去页面上下文 */
.ent-page-title { margin: 0 0 8px; font-size: 22px; line-height: 1.25; font-weight: 650; color: var(--text); }

/* 名册结果摘要：总数 + 当前筛选 + 当前排序，放在筛选条与表格之间 */
.ent-result-summary {
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  padding: 8px 18px; border-top: 1px solid var(--border); border-bottom: 1px solid var(--border);
}
.ent-result-summary b { font-size: var(--font-size-base); color: var(--text); font-weight: 650; }
#entTableHost .org-sub { display: none; }
.ent-result-summary .note { font-size: var(--font-size-sm); color: var(--dim); }

/* 移动端：结果区必须跟页面一起滚，不能锁 560px；分页在完整列表之后 */
@media (max-width: 820px) {
  body.ent-page .panel.pageflow #entTableHost,
  body.ent-page .panel.pageflow #entTableHost .table-wrap,
  body.ent-page .panel.pageflow .body.flush {
    height: auto; flex: 0 0 auto; max-height: none; overflow: visible;
  }
  #entTableHost { display: block; }
  .ent-result-summary { padding: 8px 12px; }
  /* 移动端每页 50 行，量不大：取消屏外行的 content-visibility 占位，避免整页截图/快速滚动出现空白段 */
  body.ent-page #entRows tr { content-visibility: visible; contain-intrinsic-size: none; }
  #entTableHost .org-sub { display: block; flex: 1 1 100%; margin-top: 3px; font-size: var(--font-size-sm); color: var(--dim); font-weight: 400; line-height: 1.35; }
  #entTableHost .org-sub .owner { color: var(--text); }

  /* 名册/线索在窄屏不锁一屏：卡片行需要整页滚，不能把 50 行塞进固定高度的表框 */
  body.ent-page .shell { height: auto; min-height: 100vh; overflow: visible; }
  body.ent-page .main { overflow: visible; padding-bottom: 64px; }
  body.ent-page .main > #view { flex: none; }
  .panel.pageflow { flex: none; }
  .panel.pageflow #entTableHost .table-wrap,
  .panel.pageflow #orgTableHost .table-wrap { overflow: visible; max-height: none; min-height: 0; height: auto; }

  /* 名册：8 列在 375 上不可能并排；改成卡片行，名称一行、属性一行、电话一行 */
  .table-wrap.fit-screen { overflow: visible; max-height: none; height: auto; }
  #entTableHost table { display: block; width: 100%; min-width: 0; table-layout: auto; }
  #entTableHost thead { display: none; }
  #entTableHost tbody { display: block; }
  #entTableHost tbody tr { display: flex; flex-wrap: wrap; gap: 4px 8px; padding: 10px 12px; border-bottom: 1px solid var(--border); }
  #entTableHost tbody td { display: block; border: 0; padding: 0; white-space: normal; }
  #entTableHost tbody td.org-name { flex: 1 1 100%; font-size: var(--font-size-base); font-weight: 600; }
  #entTableHost tbody td:not(.org-name) { font-size: var(--font-size-sm); color: var(--dim); }
  #entTableHost tbody td:nth-child(8) { flex: 1 1 100%; color: var(--text); }
  .queue-table thead { display: none; }
  .queue-table, .queue-table tbody { display: block; }
  .queue-table tr { display: flex; flex-wrap: wrap; gap: 4px 8px; padding: 10px 12px; border-bottom: 1px solid var(--border); }
  .queue-table td { display: block; border: 0; padding: 0; white-space: normal; }
  .queue-table td:nth-child(1) { flex: 0 0 18px; }
  .queue-table td:nth-child(2) { flex: 1 1 calc(100% - 26px); font-weight: 600; }
  .queue-table td:nth-child(3), .queue-table td:nth-child(4), .queue-table td:nth-child(5) { font-size: var(--font-size-sm); color: var(--dim); }
  .queue-table td:nth-child(6) { flex: 1 1 100%; font-size: var(--font-size-sm); color: var(--dim); }
}
.todo-row .mini { flex: 0 0 auto; }

/* 折叠区：低频的东西（渠道覆盖、名单导入）收在这里，默认收起，不占导航层级 */
details.fold {
  background: var(--card); border: 1px solid transparent; border-radius: var(--radius);
  margin-bottom: 16px; box-shadow: var(--shadow-1); overflow: hidden;
}
details.fold > summary {
  padding: 13px 16px; cursor: pointer; font-size: var(--font-size-base); font-weight: 600;
  display: flex; align-items: center; gap: 9px; list-style: none; user-select: none;
}
details.fold > summary::-webkit-details-marker { display: none; }
/**
 * **展开区不带三角**（用户口径，全局）：`▸` / `▼` 这类展开指示一律不用。
 * 点标题照旧展开/收起，可读性靠标题文字本身（它与 `.sub` 的层级已经说明了这段是什么）。
 * 原生 `<details>` 的三角由下面那条 `summary` 规则统一摘掉，两处口径必须一致。
 */
details.fold > summary::before { content: none; }
details.fold > summary:hover { background: var(--card-2); }
details.fold > summary .sub { font-weight: 400; color: var(--dim-2); font-size: var(--font-size-sm); }
details.fold[open] > summary { border-bottom: 1px solid var(--color-border-1); }
/* 折叠体自己带内距：`.body` 的 16/18 是挂在 `.panel` 下的，折叠区里得单独给一次，
   否则内容贴着边（2026-09-23 把折叠区收进原语时补的） */
details.fold > .body { padding: 14px 18px; }
details.fold .table-wrap { max-height: 460px; }
/* 页头右侧不再需要给身份块留白：身份已上顶栏，页头整行都归它自己 */
.page-head { display: flex; align-items: center; gap: 12px; margin-bottom: 16px; flex-wrap: wrap; }
.page-head h1 { margin: 0; font-size: var(--font-size-xl); font-weight: 650; letter-spacing: -.2px; }
.page-head .sub { color: var(--dim-2); font-size: var(--font-size-sm); }
.page-head .spacer { flex: 1; }

/**
 * 胶囊选择组（记一笔的「这次是什么接触」与「拨打结果」）。
 *
 * 为什么用胶囊而不是下拉：这两个选择**都要一眼看全**——下拉只显示当前项，
 * 而"记电话时能选什么结果"本身就是在教销售该记什么（接通/没人接/空号/转错人/一听就挂）。
 * 单选、可再点一次取消（允许"先记内容，结果待补"）。
 */
.chips { display: flex; flex-wrap: wrap; gap: 6px; margin: 8px 0 6px; }
.chip {
  font: inherit; font-size: var(--font-size-sm); font-weight: 500; line-height: 1.5;
  padding: 3px 10px; border-radius: var(--radius-pill); cursor: pointer;
  border: 1px solid var(--border-strong); background: var(--card); color: var(--dim);
}
.chip:hover { border-color: var(--accent); color: var(--accent-text); }
.chip.active { background: var(--accent-weak); border-color: var(--accent-weak); color: var(--accent-text); font-weight: 600; }
.chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/**
 * 侧栏：5 个一级菜单（三改，按用户给的参照形态）。
 *
 * 走过的三个版本，记着别再绕回去：
 *   一版：13 项平铺 → 靠小灰字分组。问题：**层次倒挂**（组标题 13px 最浅灰，条目 14px 带图标），
 *         一级像装饰、二级像主体。
 *   二版：组标题变可折叠的父级、子项缩进。层次对了，但组标题**没有图标**、
 *         子项一直摊开——还是"一列文字"，不是"可扫的功能入口"。
 *   三版（本版）：一级项统一成「图标 + 标题」（带子项的右边一个 ›），子项默认折起。
 *         与同类产品的侧栏形态一致：一级是可扫的入口，点开才看到子项。
 *
 * 两条护栏保留（改了这块别拆）：
 *   ① **当前页所在组自动展开**（app.js 的 setActiveTab）——折起后看不到自己在哪
 *      是折叠唯一的真代价，用自动展开消掉；
 *   ② 空组自动隐藏（app.js 按角色算）——销售不该看到空的「管理」。
 * 折叠状态按组名存 localStorage：个人偏好，不进 URL、不跟账号走。
 */
.nav-group { margin-top: 2px; }
.nav-group[hidden] { display: none; }

/* 一级项：图标 + 标题 + 右侧 ›，与叶子节点（a）同一套尺寸，读起来是同一层级 */
.nav-t {
  display: flex; align-items: center; gap: 9px; width: 100%;
  padding: 9px 11px; margin: 0; background: none; border: 0;
  border-radius: var(--radius-sm); cursor: pointer; text-align: left;
  font: inherit; font-weight: 500; color: var(--dim);
}
.nav-t:hover { background: var(--bg); color: var(--text); }
.nav-t:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.nav-t-label { flex: 1; min-width: 0; }
/* › 朝右=折起，展开时转 90° 朝下 */
/* 侧栏分组的 › 不显示（用户口径：这类箭头一律不要）。分组仍可点开合，
   开合状态由子项是否可见看出来，所以不靠箭头也不丢信息。 */
.nav-caret { display: none; }
/* 组内有当前页时，一级项也标出"这组里有你所在的位置"——否则折起时完全看不出来 */
.nav-group.has-active > .nav-t { color: var(--text); font-weight: 600; }
.nav-group-b { display: none; flex-direction: column; gap: 2px; }
.nav-group.open .nav-group-b { display: flex; }
/**
 * 子项：**带图标**，且图标与一级标题的**文字**对齐（缩进 36px）。
 *
 * 上一版我把子项图标藏了（`.nav-group-b a .ico { display: none }`），想做成"子项只留文字"——
 * 用户一眼看出不对：**子项是功能入口，光有四个字认起来慢**；而且那版还藏漏了一个
 * （医院名册用的是 `<span class="nav-ico">`，不受 `.ico` 规则管），于是"有的有图标有的没有"，
 * 看起来像 bug。现在统一：一级、子项都有图标，子项缩进到一级标题文字下方，
 * 形成"父带图标 → 子在文字列下再带图标"的两级网格。
 * 字号保持 sm：层级靠缩进与字号，不靠"把子项做小到看不清"。
 */
.nav-group-b a { padding-left: 36px; font-size: var(--font-size-sm); }
.nav-group-b a .ico { width: 15px; height: 15px; }

/* ------------------------------ 控件 ------------------------------ */

/**
 * 按钮几何按 Arco Button 的默认尺寸对齐：`height: 32px; padding: 0 15px;
 * font-size: 14px; white-space: nowrap`（`.arco-btn-size-default`）。
 *
 * `7px 13px` ⇒ 约 **38px** 高，而**本应用自己的筛选栏/页头按钮
 * 早就被覆写成 32px**（那几处 `height: 32px` 就在下面），刚对齐的输入框也是 32px
 * ——于是"同一条筛选栏里，按钮比输入框高 6px"。这一处把三者拉到同一条线上。
 * `white-space: nowrap` 也是 Arco 的做法：不给会因为某个窄容器里的长文案把按钮撑成两行、
 * 固定高度就会切字。圆角仍走 `--radius-sm`（6px，Arco 是 2px——全站刻度的既有偏离）。
 */
/**
 * 次按钮的形态**与 Arco 的 `secondary` 对齐**（2026-10-13）：填充 `--color-fill-2`、无描边、
 * hover 换 `--color-fill-3`。
 *
 * 原来我们是"白底 + `--border-strong` 描边"，而筛选条里那批岛上的 Arco 按钮是"填充灰、无描边"
 * —— 同一屏两套次按钮。选 Arco 这套的理由：产品在往 Arco 收，而 Arco 的默认按钮本来就是
 * secondary；且筛选条里的输入框/下拉本身就是填充灰，填充式按钮与它们同族，描边式反而突兀。
 * 破坏性（`.danger`）仍保留描边红：它是"别按"的信号，实心填充更像主操作（见下面那条）。
 */
button {
  font: inherit; cursor: pointer; border-radius: var(--radius-sm);
  border: 1px solid transparent; background: var(--color-fill-2); color: var(--text);
  height: 32px; padding: 0 15px; white-space: nowrap;
  transition: background var(--motion-fast) var(--ease), border-color var(--motion-fast) var(--ease);
}
/**
 * 上面那条 `button { height: 32px }` 的**豁免清单**（基线时逐个查出来的）。
 *
 * 这些"按钮"其实不是控件，而是**文本或格子形态**；让它们跟标准按钮同高会撑坏所在的行：
 *   · 表格里当链接用的：`button.link-btn`（0 内距）、`.undo`、`.th-sort`（表头排序）、
 *     `.row-toggle`、`button.telview`（电话虚线号）
 *   · 格子/矩阵里的：`button.cell-btn`（热力格）、`td.regioncell button.region-btn`（地区格）
 *   · 标签形态：`.chip`
 *   · 侧栏分组开关：`.nav-t`（必须跟同排的 `.nav a` 同高，否则侧栏一列里两种行高）
 *
 * 以后新增"看起来像按钮、其实是文本或格子"的控件，**照着这里加一条 `height: auto`**，
 * 并在同一条里补一行说明它是哪一类。
 */
.chip,
.nav-t,
.th-sort,
.row-toggle,
.undo,
button.link-btn,
button.telview,
button.cell-btn,
td.regioncell button.region-btn { height: auto; }

button:hover { background: var(--color-fill-3); }
/* 底色用 -7 而不是 -6：白字在 -6 上只有 3.43:1（不过 AA），在 -7 上是 4.60:1 ✓ */
button.primary { background: var(--accent-strong); border-color: var(--accent-strong); color: #fff; font-weight: 600; }
button.primary:hover { background: var(--accent-hover); }
/* 破坏性确认（停用账号、降级权限…）：描边+文字用高危色，**不做实心填充**——
   实心红底配白字对比度只有 3.4:1 左右，不过 AA；而描边形态与全站 button.mini.danger 一致 */
button.danger { background: none; border-color: var(--high); color: var(--high-text); }
button.danger:hover { background: var(--high-weak); }
button.ghost { border-color: transparent; background: transparent; color: var(--dim); }
button.ghost:hover { background: var(--bg); color: var(--text); }
button:disabled { opacity: .55; cursor: not-allowed; }
/* 跳转类动作（打开报告）用 <a>：它是导航不是提交，浏览器该给的右键/新标签/复制链接
   都只有链接形态才有。造型与 button 对齐，免得同一条按钮行里两种样子。
   primary 用 -7 同 button.primary：白字在 -6 上不过 AA（见上） */
a.btn {
  display: inline-flex; align-items: center; gap: 4px; font: inherit; cursor: pointer;
  border-radius: var(--radius-sm); border: 1px solid transparent;
  background: var(--color-fill-2); color: var(--text); padding: 7px 13px; text-decoration: none;
  transition: background var(--motion-fast) var(--ease), border-color var(--motion-fast) var(--ease);
}
a.btn:hover { background: var(--color-fill-3); }
a.btn.primary { background: var(--accent-strong); border-color: var(--accent-strong); color: #fff; font-weight: 600; }
a.btn.primary:hover { background: var(--accent-hover); }

/**
 * 输入控件几何按 Arco Input 对齐
 *   Arco：`.arco-input` 上下内距 4px、左右 12px、`line-height: 1.5715` ⇒ 默认尺寸**高 32px**。
 *   `7px 10px` ⇒ 实际约 38px 高——而**本应用自己的登录页早就是 32px**
 *   （`login.html` 的 `.field input { height: 32px; padding: 0 var(--space-3) }`）。
 *   也就是同一套系统里输入框有两种高度，这一处对齐消掉的就是它。
 * 两处刻意不照搬：圆角仍是 `--radius-sm`（6px，Arco 是 2px——全站刻度的既有偏离），
 * 边框与聚焦方式也保持我们的（描边 + 光晕），因为那是按对比度定过的。
 */
input, select, textarea {
  font: inherit; background: var(--card); color: var(--text);
  border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  padding: 4px 12px; line-height: 1.5715; outline: none;
  transition: border-color var(--motion-fast) var(--ease), box-shadow var(--motion-fast) var(--ease);
}
/**
 * 高度**直接给**（Arco 的尺寸类 `.arco-input-size-default` 也是给 `height: 32px`，不是靠行高算）。
 * 为什么不能只靠上面那条 `line-height: 1.5715`：表单控件的行高受 UA 默认样式影响，
 * computed 行高是 16.1px（= 14px × 1.15，UA 的 normal），不是 1.5715×14=22px；
 * 于是 4+4 内距 + 16.1 = **25.4px**，不是 32px。这条是量出来才发现的，别再用行高推高度。
 * `textarea` 不给：它是多行控件，高度由 rows / min-height 决定。
 */
input:not([type='checkbox']):not([type='radio']), select { height: 32px; }
/* 勾选/单选不参与：它们该由 UA 给的小尺寸决定，撑成 32px 会在一排文字里显得错位
   （`.filters` 里本来有一条 `height: auto` 兜着，但散落在别处的勾选没有，所以收在选择器上）。 */

input:focus, select:focus, textarea:focus {
  /* 边框走 -8、光晕用同档的淡色：焦点环的边框与光晕必须同一档，否则看起来是两套绿 */
  border-color: var(--accent-text); box-shadow: 0 0 0 3px rgba(11, 110, 84, .12);
}
textarea { width: 100%; min-height: 110px; font: 12px/1.55 ui-monospace, SFMono-Regular, Menlo, monospace; resize: vertical; }

.filters { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.filters input[type=search] { min-width: 210px; }
.note { color: var(--dim-2); font-size: var(--font-size-sm); }

/* 分段切换（监测页内部）。
   窄屏（≤992）要能换行：五段并排约 211px，而 375 视口下内容区只有 ~111px
   ——它自己不换行就整组顶出右边界（实测页面横向溢出 72px），而表格是被 `.table-wrap` 装住的、不背这个锅。
   换行后高度由内容决定（`inline-flex` 默认不换行，所以显式给 `flex-wrap`）。 */
.seg { display: inline-flex; flex-wrap: wrap; gap: 2px; background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius); padding: 3px; }
.seg a {
  padding: 6px 13px; border-radius: var(--border-radius-medium); color: var(--dim); font-size: var(--font-size-table); font-weight: 500;
}
.seg a:hover { color: var(--text); text-decoration: none; }
.seg a.active { background: var(--card); color: var(--text); font-weight: 600; box-shadow: var(--shadow); }

/* ------------------------------ 卡片 ------------------------------ */

.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 16px; margin-bottom: 16px; }
.card {
  background: var(--card); border: 1px solid transparent; border-radius: var(--radius);
  padding: 14px 16px; box-shadow: var(--shadow-1);
}
.card .k { font-size: var(--font-size-sm); color: var(--dim); margin-bottom: 4px; }

/*
  名册页那五张指标卡（`#entCards`）。它们占了近四分之一屏，而其中「无官网 / 医保定点」
  是低频参考 —— 压扁一档（内距 + 主数字号），把高度让给下面的表格（表格才是这页的工作面）。
  只收这一处，其他页面的 .card 不动。
*/
#entCards .card { padding: 10px 14px; }
#entCards .card .k { margin-bottom: 2px; }
#entCards .card .v { font-size: calc(var(--font-size-2xl) - 4px); }
.card .v { font-size: var(--font-size-2xl); font-weight: 700; letter-spacing: -.5px; font-variant-numeric: tabular-nums; }
.card .v.hot { color: var(--high-text); }
.card .v.good { color: var(--accent-text); }
.card .v.dim { color: var(--dim-2); }
/* 缺口/逾期这类「要盯」的值。介于 hot（红，出事了）与 dim（灰，没数）之间一档 */
.card .v.warn { color: var(--mid-text); }
.card .hint { font-size: var(--font-size-sm); color: var(--dim-2); margin-top: 2px; }

/* 词值卡：值是**短句**不是数字（如「投放判断：有投放页，但不是持续型」）。
   24px 会把句子折成两行、还比旁边的数字卡抢眼；降一档字号 + 给行高即可。 */
.card .v.say { font-size: 17px; line-height: 1.4; letter-spacing: 0; }
/**
 * 名册页那 5 张决策卡压成一行窄条（用户选型）。
 *
 * 为什么是「压矮」而不是「挪到名单下面」：这 5 张卡本身就是筛选入口
 * （点「已停投」= ?signal=paused、「待补口子」= ?thinCity=1…），挪到分页之后会明显变难找；
 * 压矮只损失第三行那串次要说明的显示宽度 —— 它单行省略、完整文字仍在卡片的 title 里。
 * 一张从约 140px 降到约 85px，名单整体上移约 80px。
 *
 * 窄屏（≤1200px）退回换行：5 张挤进 700px 会窄到读不出标签，那时换行比硬挤好用。
 */
.cards.strip { display: flex; flex-wrap: nowrap; gap: 8px; margin-bottom: 12px; }
.cards.strip > .card { flex: 1 1 0; min-width: 0; padding: 8px 12px; }
.cards.strip > .card .k { font-size: 12px; margin-bottom: 2px; }
.cards.strip > .card .v { font-size: 19px; letter-spacing: -.2px; }
.cards.strip > .card .note {
  margin-top: 2px; font-size: var(--font-size-xs);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
@media (max-width: 1200px) {
  .cards.strip { flex-wrap: wrap; }
  .cards.strip > .card { flex: 1 1 180px; }
}

/* 卡片里的动作入口（业绩卡的「设定目标」）：贴着 hint 往下排，不跟数值抢行 */
.card-act { margin-top: 8px; }

/**
 * 统计条：把三张大卡压成一条。
 *
 * 为什么压：投放分布页的主体是矩阵，而「侧栏 + 页头 + 3 张卡 + 筛选栏 + 两行表头」
 * 之后首屏只剩十行左右数据。KPI 是陪衬，不该吃掉矩阵的纵向空间。
 * 所以卡片变矮（数字与说明同一行）、间距收紧，但**保留"每个数都带基准"这个原则**——
 * 单独一个「2814」没有决策价值，要有分母或一句判断。
 */
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; margin-bottom: 20px; }
/**
 * 概览头部（2026-10-22）：子导航已经写着「概览」，不再放一个重复的 h1；
 * 这一行只留“数据多新 / 盘子多大 / 去工作台”。
 */
.ov-page { max-width: 100%; margin: 0; }
.ov-head { display: flex; align-items: center; gap: 8px 14px; flex-wrap: wrap; margin: 0 0 12px; }
.ov-head .note { display: inline-flex; align-items: center; gap: 6px; }
.ov-head a { color: var(--accent-text); text-decoration: none; }
.ov-head a:hover { text-decoration: underline; }
.ov-head .spacer { flex: 1; }
/* 六张行动卡在 1280 也尽量排成一行（默认 200px 会变成 5+2） */
.stats.ov-kpi { grid-template-columns: repeat(auto-fit, minmax(155px, 1fr)); margin-bottom: 16px; }
/* 概览两栏：左（要处理的 + 团队），右（上手清单 + 业绩 + 采集） */
.ov-cols { display: grid; grid-template-columns: minmax(0, 2fr) minmax(320px, 1fr); gap: 16px; align-items: start; margin-bottom: 16px; }
.ov-cols > div { min-width: 0; }
.ov-cols > div > .panel,
.ov-cols > div > .fold { margin-bottom: 0; }
.ov-cols > div > * + * { margin-top: 16px; }
@media (max-width: 1100px) { .ov-cols { grid-template-columns: minmax(0, 1fr); } }
@media (max-width: 600px) { .stats.ov-kpi { grid-template-columns: minmax(0, 1fr); } }
.stat {
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  background: var(--card); border: 1px solid transparent; border-radius: var(--radius);
  padding: 14px 16px; box-shadow: var(--shadow-1); color: inherit; text-decoration: none;
}
a.stat:hover { border-color: var(--border-strong); box-shadow: var(--shadow-2); }
.stat .k { font-size: var(--font-size-sm); color: var(--dim); }
/* 只有一个数用强调色（"先看这个"），其余中性——三张卡各用一个色系会变成红绿灯，读者无法判断优先级 */
/* 数就是主角：20px 时和页头 h1 一样大，"先看这个"的层级出不来，抬到 24px */
.stat .v { font-size: var(--font-size-2xl); font-weight: 700; letter-spacing: -.6px; font-variant-numeric: tabular-nums; }
.stat.accent .v { color: var(--accent-text); }
.stat .hint { font-size: var(--font-size-sm); color: var(--dim-2); width: 100%; }
/* 概览六张行动卡：固定高度 + 标签 / 数字 / 说明三段，避免大卡里只有左上角一点信息 */
.ov-kpi .stat {
  position: relative; overflow: hidden;
  min-height: 96px; flex-direction: column; align-items: flex-start; justify-content: center;
  gap: 2px; padding: 12px 16px;
}
/* 卡底极淡的趋势线：不占布局，提示"这一格在变" */
.kpi-spark {
  position: absolute; top: 10px; right: 10px; width: 58px; height: 18px;
  color: var(--accent); opacity: .5; pointer-events: none;
}
.kpi-spark svg { display: block; width: 100%; height: 100%; }
@media (max-width: 1599px) { .kpi-spark { display: none; } }
.ov-kpi .stat .hint { position: relative; z-index: 1; }
.kpi-delta { display: inline-block; margin-left: 6px; font-size: var(--font-size-xs); font-weight: 600; color: var(--dim-2); }
.ov-kpi .stat .v { font-size: var(--font-size-kpi); line-height: 1.1; }
.ov-kpi .stat .hint { font-size: var(--font-size-xs); width: auto; }

/**
 * 概览「本月业绩」压成的单行摘要。
 *
 * 那四个数（本月已成交 / 未结 / 预计合计 / 距目标）是 `/api/crm/report/forecast` 的
 * 同一批字段，「经营分析 → 业绩」已经用四张卡画过一遍；在概览上再来四张等于把那边重印，
 * 而且没数据时四张卡全是 0、各自还拖一句解释。一行就够，细节点进经营分析。
 * 分隔符用独立的 `<i>` 而不是文本里的 ` · `：窄屏折行时分隔号不会孤零零留在行尾。
 */
.sum-line { display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 8px; font-size: var(--font-size-table); }
.sum-line > span { color: var(--dim); }
.sum-line b { color: var(--text); font-weight: 600; font-variant-numeric: tabular-nums; }
.sum-line > i { color: var(--dim-2); font-style: normal; }
.sum-line .mini { margin-left: auto; }

/* 一行里成对的动作按钮：推到最右。**不能给两个 .mini 各挂 margin-left:auto** ——
   前一个会把整行余量吃光，后一个被挤到下一 flex 行（2026-10-09 实测：预览留在上一行、
   下载孤零零掉到下一行）。所以包一层 .act-pair（既有原语），auto 只挂在这层上。 */
.sum-line > .act-pair { margin-left: auto; margin-right: 0; }
.sum-line > .act-pair .mini { margin-left: 0; }

/* 引用来源那一格：内容是清单（谁在替它说话、排第几），不是单个数字，
   所以不用 .card .v 那套 24px 大字，走正文字号。 */
.card .cites { font-size: var(--font-size-table); color: var(--text); margin-top: 2px; }
/* 只给读屏用的文本（空值、图标含义等） */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/**
 * 列分组表头：把平台列分成「付费投放证据」与「仅收录」两组。
 *
 * 这是全系统最核心的一条区分（付费位 = 已经花过钱、是要打的理由；弱信号 = 只是被收录），
 * 但原来两类列长得一模一样，一屏扫下去分不出哪几列有付费证据。
 * 分组行给出边界，弱信号那组再整体降饱和 + 浅底，形成"主/次"的视觉层级。
 */
table.matrix thead tr.group-row th {
  position: sticky; top: 0; z-index: 3;
  font-size: var(--font-size-sm); font-weight: 600; color: var(--dim);
  background: var(--card-2); border-bottom: 1px solid var(--border);
  text-align: center; padding: 4px 8px;
}
table.matrix thead tr.group-row th.g-paid { color: var(--accent-text); }
table.matrix thead tr.group-row th.g-weak { color: var(--dim-2); }
/* 第二行表头（平台名）跟着往下挪一格 sticky 位置 */
table.matrix thead tr.col-row th { position: sticky; top: var(--matrix-group-h, 26px); z-index: 2; }

/**
 * 覆盖矩阵**不吃通用行内距**（从反推的例外）。
 *
 * 通用规则把 `tbody td` 的内距从 6px 抬到 10px，那是给"阅读型表格"的（一行是一条记录）。
 * 但矩阵是**热力格子**：10 行 × N 列要落在一屏内，行高每加 8px 就多出 80px，
 * 而它的第二行表头还有 `top: 26px` 这个按第一行表头实高算出来的 sticky 偏移——
 * 行高一变，那套吸顶位置就得跟着重算。所以这里把它按原值钉住，改通用规则时不受影响。
 */
table.matrix tbody td { padding: 6px 11px; }
table.matrix thead tr.col-row th { padding: 9px 11px; }

/* 弱信号列的整列降饱和：数字仍可读，但视觉上退到第二层 */
table.matrix td.col-weak, table.matrix th.col-weak { background: var(--card-2); }
table.matrix tr.region-row > td.col-weak { background: #e6ebf0; }
table.matrix td.col-weak .cell-btn { color: var(--dim); }

/**
 * 热力：按当前视图的最大值分四档，给非零格加底色。
 *
 * 为什么这个比"把数字调大"有用：2814 家 × 11 平台的稀疏矩阵里绝大多数格是空的，
 * 原来满屏的「·」和真数据反差很小，想找"哪里有数"得逐格扫。加底色之后
 * 一屏就能看出"江苏 × 家庭医生在线 最深"，不用读数字。
 * 单色深浅（accent 的透明度阶梯）而不是彩虹色——彩虹会把"大"和"类别"混起来。
 */
.cell-btn.heat-1 { background: rgba(15, 157, 120, 0.06); }
.cell-btn.heat-2 { background: rgba(15, 157, 120, 0.13); }
.cell-btn.heat-3 { background: rgba(15, 157, 120, 0.22); }
.cell-btn.heat-4 { background: rgba(15, 157, 120, 0.34); }
/* 弱信号列的热力减半，避免它和付费证据抢注意力 */
table.matrix td.col-weak .cell-btn.heat-1 { background: rgba(15, 157, 120, 0.04); }
table.matrix td.col-weak .cell-btn.heat-2 { background: rgba(15, 157, 120, 0.08); }
table.matrix td.col-weak .cell-btn.heat-3 { background: rgba(15, 157, 120, 0.13); }
table.matrix td.col-weak .cell-btn.heat-4 { background: rgba(15, 157, 120, 0.2); }

/**
 * 「未知地区」行：**它不是地区，是数据缺口**——矩阵把地区信息缺失的记录都归到这一行，
 * 而市级视图下它是最大的一行（441 家，对比江苏 188 家）。
 *
 * 所以它要有别于真实地区的行：标签走虚线下划线（表示"这不是个名字"）、
 * 加一个「数据缺口」小标、再给个 ⓘ 说明是什么、该补哪里。
 * 用虚线边框而不是把整行涂灰：涂灰会被读成"次要"，而它其实是**要看的那一行**。
 */
table.matrix tr.row-gap > td { border-top-style: dashed; }
table.matrix tr.row-gap .row-link { border-bottom-style: dashed; }
.gap-tag {
  margin-left: 6px; font-size: var(--font-size-sm); font-weight: 400;
  color: var(--mid-text); border: 1px dashed var(--mid); border-radius: var(--radius-pill); padding: 0 6px;
}
.gap-i { color: var(--mid-text); }

/**
 * 列头排序按钮（④）：列头整格可点，箭头指示当前排序列与方向。
 * 做成 button 而不是「th + 点击回调」——规范里"非交互元素挂点击处理器"是明确的反模式，
 * 且 th 不可聚焦，键盘用户根本排不了。
 */
table.matrix .col-sort {
  display: inline-flex; align-items: center; gap: 3px;
  background: none; border: 0; padding: 2px 4px; font: inherit; color: inherit;
  border-radius: 4px; cursor: pointer;
}
table.matrix .col-sort:hover { background: var(--bg); color: var(--accent-text); }
table.matrix .col-sort:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
table.matrix th.sorted .col-sort { color: var(--accent-text); font-weight: 600; }
table.matrix .sort-arrow { font-size: 10px; line-height: 1; }
table.matrix tbody .row-link { color: inherit; text-decoration: none; border-bottom: 1px solid transparent; }
table.matrix tbody .row-link:hover { color: var(--accent-text); border-bottom-color: var(--accent); }
/** 覆盖平台数：行的覆盖广度，比成交/输单标记更轻，不抢注意力 */
.cover-tag {
  margin-left: 6px; font-size: var(--font-size-sm); color: var(--dim-2); font-weight: 400;
  border: 1px solid var(--border); border-radius: var(--radius-pill); padding: 0 6px;
}
/** 表脚：左边是"读表须知"（口径 + 空格含义），右边是列数说明与操作 */
/**
 * 分页条（从内联 `style="display:flex;align-items:center;gap:12px"` 收成类）。
 * 几何按 Arco Pagination 的项：`min-width: 32px; 居中`（`.arco-pagination-item` + 尺寸档），
 * 当前页态用 `primary-6` 文字 + 浅主色底（`--accent-weak`）——我们只有"上一页/下一页"，
 * 所以没有页码项可上色，**总数文案**照 Arco 的 total-text 取 12px。
 */
.pager { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.pager button { min-width: 32px; padding: 0 11px; text-align: center; }
.pager .note { font-size: var(--font-size-sm); }
/* 右边那两件（每页条数 / 跳至第几页，岛渲染， 加）
   `spacer` 把它们挤到行尾；上面那条 `flex-wrap` 是为此加的——1024 以下不折行会顶出面板。
   容器内的 `.mdui-controls` 自己就是 inline-flex + gap 8，这里不重复写。 */
.pager .spacer { flex: 1; }

/* 内容面板骨架：几行文字占位（见 util.js 的 skeletonLines） */
.sk-lines { display: flex; flex-direction: column; gap: 10px; padding: 2px 0; }
.sk-lines > .sk { height: 14px; }   /* 比表格里的 11px 略高：整行文字不是单元格 */

.table-foot { display: flex; align-items: flex-start; gap: 12px; flex-wrap: wrap; }
.table-foot .foot-main { display: flex; flex-direction: column; gap: 2px; min-width: 260px; }
.table-foot .foot-side { display: flex; flex-direction: column; align-items: flex-end; gap: 6px; }
.table-foot .foot-acts { display: flex; gap: 8px; }

/* 平台名后的小信息符号：把"弱信号/权重"收成一个可聚焦的小标记，说明放 title */
.plat-i {
  display: inline-block; margin-left: 3px; color: var(--dim-2);
  font-weight: 400; font-size: var(--font-size-sm); cursor: help;
}

/* 筛选栏的分组与「更多筛选」：8 个控件等权平铺时找不到主次 */
.filters .fgroup { display: inline-flex; align-items: center; gap: 8px; }
.filters .fgroup + .fgroup { padding-left: 10px; margin-left: 2px; border-left: 1px solid var(--border); }
.filters button.link { background: none; border: 0; color: var(--dim); font: inherit; padding: 4px 6px; cursor: pointer; text-decoration: underline; text-underline-offset: 2px; }
.filters button.link:hover { color: var(--accent-text); }
.filters details.more { display: inline-block; }
.filters details.more > summary { cursor: pointer; color: var(--dim); font-size: var(--font-size-sm); padding: 6px 4px; list-style: none; }
.filters details.more > summary::-webkit-details-marker { display: none; }
.filters details.more > summary::after { content: " ▾"; }
.filters details.more[open] > summary::after { content: " ▴"; }
.filters details.more > .more-body { display: flex; align-items: center; gap: 8px; padding: 8px 0 2px; flex-wrap: wrap; }

/**
 * 「一行放下所有筛子」（名册页，）。
 *
 * 名册页有 12 个筛选控件（名称 + 11 个下拉），默认的 flex-wrap 会折成两行 ——
 * 用户要的是"一眼看全所有筛子"，而且第二行会把名单往下推。
 *
 * 做法：不换行 + 各控件 flex:1 等分压缩。收起态的选中文案用省略号截断，
 * **但展开下拉仍是完整选项**（.sel-menu 按内容撑开，不受这里约束），
 * 所以截断只影响那一行字的显示，不影响选择。
 *
 * 窄屏（≤1200px）退回换行：12 个控件压进 900px 会窄到点不中，那时换行比挤成一排好用。
 */
.filters.one-row { flex-wrap: nowrap; }
.filters.one-row > input,
.filters.one-row > select,
.filters.one-row > .sel { flex: 1 1 0; min-width: 0; }
.filters.one-row > input[type='search'] { min-width: 0; }
.filters.one-row > a.note { flex: none; }
.filters.one-row .sel-trigger { width: 100%; }
/* 「更多筛选」展开的那一行：三个控件等分、但限宽 —— 不限宽会拉成三个巨宽下拉，
   限宽后左边对齐、右边留白，读起来是"第二页筛子"而不是半行碎片 */
#moreFilters > .sel { flex: 1 1 0; max-width: 240px; }
.filters.one-row .sel-label {
  flex: 1 1 auto; min-width: 0; text-align: left;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
@media (max-width: 1200px) {
  .filters.one-row { flex-wrap: wrap; }
  .filters.one-row > input,
  .filters.one-row > select,
  .filters.one-row > .sel { flex: 0 1 150px; }
}

/**
 * 「机构范围」chip：页头里唯一一个"决定你能看到什么"的元素，所以它不能是最弱的那个。
 *
 * 形态取「字段名小字 + 值加粗」而不是一个灰 tag：销售打开页面第一件该确认的事
 * 就是"我这个范围对不对"，而灰 tag 会被当成装饰跳过。未分配时走警示色——
 * 那种情况下「机构线索」是空的，是要处理的事，不是背景信息。
 */
.scope-chip {
  display: inline-flex; align-items: baseline; gap: 6px;
  padding: 3px 10px; border-radius: var(--radius-pill);
  background: var(--accent-weak); border: 1px solid transparent;
}
.scope-chip .k { font-size: var(--font-size-sm); color: var(--dim); }
.scope-chip .v { font-weight: 600; color: var(--accent-text); }
.scope-chip.unset { background: var(--mid-weak); }
.scope-chip.unset .v { color: var(--mid-text); }

/**
 * 筛选栏的分组标签（范围 / 口径）。
 *
 * 光有竖线分隔不够：控件一换行，竖线就落在行尾、看不出分界（7 个下拉排一行
 * 「更多筛选 / 重置」折到第二行时，两组的分隔完全失效）。**文字标签在换行后依然成立**，
 * 所以这里以标签为主、竖线为辅。
 */
.filters .flabel {
  font-size: var(--font-size-sm); color: var(--dim-2); font-weight: 600;
  margin-right: -2px; white-space: nowrap;
}
.filters .fgroup { display: inline-flex; align-items: center; gap: 8px; }

/* 可点的卡片：商机页的四块 KPI 用 <a class="card"> —— 点进去就是筛选后的行。
   数字不能点，就只是装饰；这几行只是把链接"长得跟卡片一样"，并给一个明确的悬停反馈。 */
a.card { display: block; color: inherit; text-decoration: none; transition: border-color var(--motion-fast) var(--ease), box-shadow var(--motion-fast) var(--ease); }
a.card:hover { border-color: var(--border-strong); box-shadow: var(--shadow-2); }
a.card .k { cursor: pointer; }

/* 容器：**不描边**，靠画布底色差 + 阴影阶梯立起来（边框饮食）。
   用 border-color 而不是 border: 0——保留那 1px 的布局占位，免得去掉边框后整页元素位移。 */
.panel {
  background: var(--card); border: 1px solid var(--border); border-radius: var(--radius);
  margin-bottom: 16px; overflow: hidden; box-shadow: var(--shadow-1);
  transition: box-shadow var(--motion-base) var(--ease);
}
.panel > h2 {
  margin: 0; padding: 12px 16px; font-size: var(--font-size-base); font-weight: 600;
  border-bottom: 1px solid var(--color-border-1); display: flex; align-items: center; gap: 9px;
}
.panel > h2 .sub { font-weight: 400; color: var(--dim-2); font-size: var(--font-size-sm); }
.panel .body { padding: 16px; }
.panel .body.flush { padding: 0; }

/* ══════════════════════ 组件原语══════════════════════════════
   说好的"8 个原语"里，**6 个早就有主了**，所以不新造名字：
     容器 = .panel   · 页头 = .page-head      · 指标 = .stats / .stat
     工具条 = .filters · 徽标 = .tag / .badge  · 空态 = .empty
   再立一套 .surface / .toolbar 只会让全站出现两种词汇，下一个人不知道该用哪个。
   这一节做两件事：① 把上面 6 个的"表面处理"收进同一条规则（观感一致才是原语的意义，
   不然改一次要改六处）；② 补上真正缺的两个：.section（分组标题）、.list-row（窄屏行卡片）。
   .section / .list-row 目前**尚未被页面使用**——留给"逐页构图"那一步，先定义不接线。 */
.panel, .stat, .card, details.fold {
  border-color: var(--border);
  box-shadow: var(--shadow-1);
}
a.panel:hover, a.stat:hover, a.card:hover, details.fold:hover { box-shadow: var(--shadow-2); }

/* 分组标题：层级靠字重与留白，不靠描一条线（要线的地方才用线） */
.section {
  margin: var(--space-6) 0 var(--space-3); font-size: var(--font-size-base);
  font-weight: 600; letter-spacing: .1px; color: var(--text);
}
.section .sub, .section > .sub { font-weight: 400; color: var(--dim-2); font-size: var(--font-size-sm); }
.section:first-child { margin-top: 0; }

/* 窄屏用行卡片代替横向表格：一条记录一块，字段竖向排 */
.list-row {
  display: flex; align-items: center; gap: var(--space-3);
  padding: 12px 14px; border-bottom: 1px solid var(--color-border-1);
  transition: background var(--motion-fast) var(--ease);
}
.list-row:last-child { border-bottom: 0; }
.list-row:hover { background: var(--color-fill-1); }
.list-row .grow { flex: 1; min-width: 0; }
.list-row .k { font-weight: 600; }
.list-row .meta { font-size: var(--font-size-sm); color: var(--dim-2); }
@media (max-width: 992px) {
  .list-row { flex-wrap: wrap; align-items: flex-start; }
}

/* 纵向堆叠 + 统一间距。代替遍地 `style="margin-bottom:8px"` 这类内联留白——
   留白要成阶（--space-2/3/4），散在标记里的字面量没法一处调。 */
.stack { display: flex; flex-direction: column; gap: var(--space-2); }

/* 提示块：左侧一道色条，其余是普通表面。
   原先这种块在 app.js 里写成 `<div class="panel" style="border-left:3px solid var(--high)">`，
   三处各写一遍内联样式——同一形态写三遍必然有三处不一样，而且容器内联描边正是
   这一轮要去掉的东西。语义只分两档：`.warn` 需要你去处理，`.tip` 知道一下就行。 */
.callout {
  border-left: 3px solid var(--color-border-3);
  border-radius: var(--border-radius-medium);
  background: var(--card); padding: 15px 16px;   /* Arco Alert 带标题档是 padding: 15px 15px */
  box-shadow: var(--shadow-1); margin-bottom: 16px;
}
.callout.warn { border-left-color: var(--high); }
.callout.tip { border-left-color: var(--mid); }
/* 提示块里要横排（标签 + 一句话 + 说明）时套一层，别在标记里写内联 display:flex */
.callout .callout-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.callout h2 { margin: 0 0 6px; font-size: var(--font-size-base); font-weight: 600; }
.callout p { margin: 0; }

/* ------------------------------ 表格 ------------------------------ */
/* 表头吸顶：长列表滚动时列名不丢，是本系统最常用的表 */

/* max-height 要减掉顶栏：不减的话表格底部会伸到视口外，多出一截要额外滚动 */
.table-wrap { overflow: auto; max-height: calc(100vh - var(--topbar-h) - 250px); }

/**
 * 名册表专用：让「表格 + 它自己的滚动条 + 翻页器」整体落在**一屏之内**。
 *
 * 为什么需要：名册一页 100 行，沿用上面那个 250px 时框底会伸到首屏之外
 * 于是同一屏里同时存在两条滚动条（先滚页面把框底挪上来、再滚框看行），手感和方向感都乱。
 * 250px 是按"页头 + 筛选条"估的，名册页在表格上面还压着 6 张卡片，所以不够用。
 *
 * 关键在 `--fit-above`：把表格**上方**那些东西（页头、卡片、筛选面板、表头说明）
 * 加上下方的翻页器一起算进去，让框底和翻页器始终留在首屏内——任何时刻只面对一条滚动条。
 *
 * 下界 360px 不是凑数：窗口矮的时候（笔记本分屏、投影）算出来只剩 100 多像素、
 * 只能显示 2~3 行，那比"框超出首屏"更没法用。宁可那时让框伸出去，也不能压到读不了。
 */
.table-wrap.fit-screen {
  --fit-above: 452px;
  max-height: max(360px, calc(100vh - var(--topbar-h) - var(--fit-above)));
}
/**
 * 名册那张列表面板：**页面不滚，表格框自己滚** —— 框高交给弹性布局算，不靠 JS 量。
 *
 * 链条（只在名册这一屏生效）：`.layout` 定成「视口 − 顶栏」→ `.main` 不滚 → `#view` 竖排、
 * 面板撑满 → 表格框吃掉剩余高度。页头 / 卡片 / 筛选条因此在**任何滚动位置都在原位** ——
 * 翻到第 3,435 页时还能直接改筛选，不必先滚回顶部。
 * 取舍：框内滚动每屏少 3~5 行（8~10 行 vs 不限高时的 13 行）。这一页是长列表 + 频繁调筛选，
 * 控件的可达性优先于每屏多几行。
 *
 * 祖先那四层（`.layout`/`.main`/`#view`）是全站共用的，所以整组挂在 `body.ent-page` 下 ——
 * 那个类由 `route()` 进名册列表时加上，别的页不受影响。
 * ⚠️ 窄屏（<1200px）表格横向也要滚：wrap 同时是横向滚动容器，纵向吸顶照样归它管。
 */
/* 外壳也得定高：`.shell` 只有 `min-height: 100vh`，内容一长它跟着长，
   下面那条 flex 链就失去参照（量出来 4556px，框自然收不住）。 */
body.ent-page .shell { height: 100vh; min-height: 0; overflow: hidden; }
body.ent-page .main {
  display: flex; flex-direction: column; min-height: 0; overflow: hidden;
  /* 底内距那 64px 是给「整页滚」留的呼吸位；这一屏不滚，留着只会把框压短 */
  padding-bottom: 0;
}
body.ent-page .main > #view { display: flex; flex-direction: column; flex: 1; min-height: 0; }
body.ent-page .main > #view > *:not(.panel) { flex: 0 0 auto; }
.panel.pageflow { display: flex; flex-direction: column; flex: 1; min-height: 0; overflow: visible; }

/**
 * **视口矮的时候放弃"一屏锁死"，退回整页滚动**（2026-10-13，实测 1.4.4 内容丢失）。
 *
 * 上面那条链把「页头 / 筛选条 / 表格框 / 翻页器」锁进 `100vh`，前提是**筛选条只占一两行**。
 * 而浏览器放大到 200%（或投影、分屏、小窗口）时视口只剩 ~400px 高，筛选条在窄宽度下折成四行，
 * 于是表格框被压到底——实测名册的表框只剩 **15px**，机构线索更严重：**一行都看不见**，
 * 而 `.main` 是 `overflow: hidden`、页面也滚不动（`shell` 锁 100vh）→ **内容既看不到也够不着**。
 *
 * 处置：高度不够时不再锁一屏——外壳交回文档流（页面可以滚），表框给一个最小高度（约 8 行）。
 * 判据取 `max-height: 620px`：1280×800 的窗口放大到 200% 时正是 400px 高；620 留了余量，
 * 正常笔记本（768+）不受影响。
 */
@media (max-height: 620px) {
  body.ent-page .shell { height: auto; min-height: 100vh; overflow: visible; }
  body.ent-page .main { overflow: visible; padding-bottom: 64px; }
  body.ent-page .main > #view { flex: none; }
  .panel.pageflow { flex: none; }
  .panel.pageflow #entTableHost .table-wrap,
  .panel.pageflow #orgTableHost .table-wrap { min-height: 320px; }
}
/**
 * 框内的左右内缩**统一到 14px** —— 与表格的格内距、卡片的内距同一个值。
 * 这两行（筛选条、翻页器）原来是 18px，它们的控件因此比卡片里的字、比表格首列的字各偏右 4px，
 * 一屏里就此多出一条基准线（实拍：输入框 255 / 卡片的字 251 / 表头的字 251）。
 */
.panel.pageflow > .body:not(.flush) {
  flex: 0 0 auto;
  padding-left: 14px;
  padding-right: 14px;
}
.panel.pageflow > .body.flush { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }
.panel.pageflow > .pager { flex: 0 0 auto; }
.panel.pageflow #entTableHost,
.panel.pageflow #orgTableHost { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }
.panel.pageflow #entTableHost .table-wrap,
.panel.pageflow #orgTableHost .table-wrap { flex: 1 1 auto; min-height: 0; max-height: none; overflow: auto; }
/**
 * **预留滚动条槽**（`scrollbar-gutter: stable`）：滚动条出现/消失时可用宽度差 ~15px，
 * 而这两张表是 `table-layout: auto` —— 宽度一变，浏览器会**重新分配列宽**，
 * 读起来就是"上下滚动时列的左右位置会变"（用户 2026-10-15 报的第二个现象）。
 * 这两张表的框本来就一直有滚动条，所以预留槽不改变任何现有观感，只是把这一步固定下来。
 */
.panel.pageflow #entTableHost .table-wrap,
.panel.pageflow #orgTableHost .table-wrap { scrollbar-gutter: stable; }
/**
 * ⚠️ **两层 `.table-wrap` 只让外层滚**（2026-10-13 实测纠正）。
 *
 * 结构是「模板里的外层框 > 宿主 > 岛渲染的内层框 > 表」——上面那条规则命中的是**内层**
 * （宿主是它的祖先），于是内层成了 `overflow:auto` 的滚动容器。后果不是"多一条滚动条"，
 * 而是**表头吸顶失效**：`position:sticky` 认的是最近的 scrollport = 内层，而内层不滚，
 * 滚的是外层 —— 实测滚 240px 后表头被带到 y≈9 的视口顶（压在顶栏底下看不见了），
 * 用户读到的就是"顶栏不见了/是透明的"。
 *
 * 处置：外层给足高度并当唯一滚动容器（下面那条），**内层退回 `overflow: visible`** ——
 * 它不是滚动容器，sticky 才会去认外层，表头也就钉在框顶。
 */
.panel.pageflow .body.flush > .table-wrap { flex: 1 1 auto; min-height: 0; max-height: none; overflow: auto; }
/**
 * 内层那条选择器**必须写成 ID 形式**（`.panel.pageflow #orgTableHost .table-wrap`，特异性 1,2,0）——
 * 上面那条"宿主的框"规则同样是 1,2,0，写成 `.table-wrap .table-wrap`（0,2,0）压不过它，
 * 内层就还是 `overflow:auto`（实测：两条框都算"可滚"，表头继续认内层当 scrollport）。
 * 靠"后写"取胜：这条排在前面那条之后。
 *
 * ⚠️ **必须限定成"框里还有框"（`.table-wrap .table-wrap`），不能只写一层**（2026-10-13 补）。
 * 上面那条假设结构恒为「外层框 > 宿主 > 内层框」，但**名册页在 1280 宽下只有一层**：
 * 那个唯一的 `.table-wrap` 被这条命中、退回 `overflow: visible`，于是
 *   ① 没有任何一层裁剪 → 行一直画到 `.shell` 的视口底，**翻页器下方露出半行数据**；
 *   ② 整页**没有可滚的容器** → 用户滚不动列表（实测只有 `.main` 能程序化滚，而它是 `overflow: hidden`）。
 * 写成"框里还有框"之后：单层的情况它不匹配，那一层就保留上面给的 `overflow: auto`
 * （既裁剪又能滚，表头 sticky 也认它）；两层的情况内层照旧退回 visible，外层当唯一滚动容器。
 */
.panel.pageflow #entTableHost .table-wrap .table-wrap,
.panel.pageflow #orgTableHost .table-wrap .table-wrap { overflow: visible; max-height: none; }
.panel.pageflow #entTableHost thead th,
.panel.pageflow #orgTableHost thead th { top: 0; }
/**
 * 矮视口（**整页滚**那一档）下，表框必须交回文档流 —— 否则表头钉不住。
 *
 * `position: sticky` 只对**最近的滚动祖先**生效：表框只要还是 `overflow: auto`
 * （哪怕它自己永远不滚），表头就钉在表框的 padding 边上、跟着页面一起走。
 * 实测（1280×560，程序化滚页面 400px）：表头从 y=183 掉到 **-217**，`is-stuck` 也一次不亮
 * —— 用户读到的就是"上下移动时表头没固定"。
 *
 * ⚠️ **这几条必须排在上面那两条基础规则之后**：选择器同特异性（1,3,0 / 1,2,2），
 * 写在前面会被"后写的赢"盖掉（第一版写在 `@media` 那段里，实测没生效）。
 * 交回文档流后表头钉在顶栏下沿（`top: var(--topbar-h)`，与文档的 `scroll-padding-top` 同值）。
 * 横向滚动不受影响：三档视口的横向溢出实测都是 0。
 */
@media (max-height: 620px) {
  .panel.pageflow #entTableHost .table-wrap,
  .panel.pageflow #orgTableHost .table-wrap { overflow: visible; }
  .panel.pageflow #entTableHost thead th,
  .panel.pageflow #orgTableHost thead th { top: var(--topbar-h); }
  /**
   * 这一档是**页面级滚动**，表头必须取消 `will-change: transform`（上面那条基础规则给的）。
   *
   * 上面那段注释记过成因：页面级滚动里 sticky 每帧重排，而 `will-change: transform`
   * 把表头提成**独立合成层** —— 位置与底色的提交会被分成两拍，那一帧里"行先到、表头后到"，
   * 读起来就是**表头透了过去、行字压在表头标签上**。当初的处置是"别让页面滚"（锁一屏、
   * 让表框自己滚）；矮视口这一档不得不放开页面滚，那就反过来：**这一档不提层**。
   * 层级（z-index 3）与那 1px 同色描边仍在，吸顶该有的样子一样不少。
   */
  .panel.pageflow #entTableHost thead th,
  .panel.pageflow #orgTableHost thead th { will-change: auto; }
}
.panel.pageflow .pager {
  border-top: 1px solid var(--color-border-1);
  border-radius: 0 0 var(--radius) var(--radius);
  /**
   * **必须有不透明白底**（2026-10-13 补）：分页器贴在表格框的下沿，行滚到框底时
   * 亚像素上会和分页器那一行"叠"在一起（用户截图里 上一页/下一页 与一行机构名交错着，
   * 就是行从底下透上来）。粘底那版本来带 `background: var(--card)`，改成弹性布局时丢了底色，
   * 只剩边框 —— 这里补回来，并给一层 `z-index` 保证它压在行上面。
   */
  background: var(--card);
  position: relative;
  z-index: 1;
}
@media (max-width: 1199px) {
  .panel.pageflow #entTableHost .table-wrap,
  .panel.pageflow #orgTableHost .table-wrap { overflow-x: auto; }
}
/**
 * 名册表：中间七列居中 + **不折行**（列上的 `c` 类，见 app.js 的 entTableColumns）。
 * 名称（两行的块）与电话（可点的号码块 + 吸右）不居中——那两列左对齐才扫得动。
 *
 * 为什么加 `nowrap`：列宽是 auto，1280 宽下「科室」只有 ~52px、「所有制」~
 * 64px，于是「综合」被折成"综 / 合"、公立徽标变成竖排两字、「精神心理」折成三行 ——
 * 看着像坏了，而且**行高因此参差不齐**（那正是"吸顶时只有半截行"的观感来源之一）。
 * 宁可让表格横向滚动（这一页本来就有横向滚动条），也不要折行。
 * 吸顶那一刻再加一层投影（`.is-stuck` 由 app.js 在滚动时挂上）：
 * 让"表头浮在行上面"看起来是一层，而不是"行的上半截被切掉了"。
 */
#entTableHost thead > tr > th.c,
#entTableHost tbody tr > td.c { text-align: center; white-space: nowrap; }
/**
 * 吸顶表头：**滚动中不许"漏边"**（用户报"吸顶异常）。
 *
 * 现象：滚到中间时，表头下面那行的字像是跟表头标签叠在一起。
 * （生产、1280 宽）：表头底色不透明、层级也在行之上（在表头带里取 `elementsFromPoint`
 * 从上到下拿到的是 TH → TD），列宽与表体逐列对齐 —— 所以不是错位，是**滚动过程中
 * 表头与行之间的绘制边界不稳**：
 *   · `position: sticky` 在页面级滚动里每帧都要重排，表头的底色偶尔比行晚一帧提交，
 *     那一帧里行的字就从表头底下透出来；
 *   · 浏览器缩放不是 100% 时亚像素舍入，表头与行之间还会出现一条 1px 的缝。
 *
 * 两条对症处置（不动任何间距/字号，所以视觉上只有"更干净"）：
 *   ① `will-change: transform` 给表头一个**独立合成层**：位置与底色一起提交，
 *      不再出现"行先到、表头后到"的那一帧；
 *   ② 底色向下**多铺 1px**（同色描边）补掉亚像素缝。
 * 另：层级从 2 提到 3 —— 行里的号码格是 `z-index: 2` 的吸右格，同值时靠 DOM 顺序定胜负，
 * 表头不该跟它赌这个（号码格在表头带里"压住表头"正是同一类画错）。
 */
#entTableHost thead th,
#orgTableHost thead th {
  z-index: 3;
  will-change: transform;
}
.panel.pageflow #entTableHost.is-stuck thead th,
.panel.pageflow #orgTableHost.is-stuck thead th {
  /* 1px 同色描边补缝 + 原来那层 6px 投影（"表头浮在行上"要看着是一层） */
  box-shadow: 0 1px 0 var(--card-2), 0 6px 8px -8px rgba(15, 23, 42, 0.3);
}

/**
 * 名称列那一行：名称 + 「所属 / 下辖」 + 徽章。
 *
 * **别名不再进这一行**（别名做成 hover 的形式，不要这样都放在第一列里
 * 很占空间"）：它在机构名的 `title` 里，悬停才出现（见 app.js 的 entRowCells）。
 * 那一格因此只剩名称、徽章与两条导航小链接，列宽宽裕得多（省下的宽度让表格不用横向滚动）。
 * 分工：名称与「疑似停业」徽章**不缩**（`flex: 0 0 auto`）；`所属 / 下辖` 也是不缩的小链接。
 *
 * ⚠️ 不用 `td { display: flex }`：那会让 td 不再是 table-cell，整张表的列对齐会散掉，
 * 所以这一行是单元格**里面**的一层 `span`（`display: flex` 对 span 完全合法）。
 */
#entTableHost td.org-name .org-line {
  display: flex; align-items: baseline; gap: 0 8px; min-width: 0; overflow: hidden;
}
/* `所属 / 下辖` 的小链接：同一行里**不缩**（结构信息不该被别名挤掉），
   而且排在别名之前——列宽不够时被裁的永远是别名 */
/* 「下辖 N 家 / 所属」是**另一个动作**（筛同集团），与机构名那个链接要分得开：
   名称是正文蓝、这个小一号且更淡。 */
#entTableHost td.org-name .org-meta-inline {
  flex: 0 0 auto;
  /* 比机构名再淡一档（dim-2）：它是**另一个动作**（筛同集团），不该跟名称一样抢眼 */
  font-size: var(--font-size-sm); color: var(--dim-2);
}

/* ------------------------- 机构线索：号码列吸右 -------------------------
 *
 * 问题：这一页 9 列**真实需要的宽度是 1387px**，而容器在 1440 视口下只有 1144px
 * （溢出 243px），于是主角动作——看号码——落在 x=1653、必须横向滚动才够得着。
 *
 * 试过别的路，都不可取（都量过）：
 *   · 压列宽 `table-layout: fixed` → 表能塞进 1144、号码也回到首屏，
 *     但 9 格里 **7 格被截**（电话格需要 249px 只给了 210px——**号码自己就被截了**）；
 *   · 减列（把「专科」「最新发文」并进机构格第二行）→ 合计仍差 63px，而且那两列是扫读/筛选的轴。
 * 所以这里**不压宽度、不删列**，改成"号码列固定吸右"：其余列左右滚动时它始终在，
 * 代价是它覆盖住下面那几列（标准做法，配底色 + 左侧一道很淡的投影做分隔）。
 *
 * ⚠️ `tr:not(.expand-row)` 不能少：展开行是一个 `colSpan` 铺满的 `<td>`，
 * 它也是 `:last-child`，被吸住会让整行明细只剩右边一条。
 * 两页的表最后一列都是电话（名册是给医院打电话、线索页是给线索打电话），所以一条规则管两处：
 * 名册在宽屏本来就装得下，吸右对它无副作用；窄屏（比如把窗口拖窄）它同样能把号码留在视野内。
 */
#orgTableHost tr:not(.expand-row) > td:last-child,
#orgTableHost thead > tr > th:last-child,
#entTableHost tr:not(.expand-row) > td:last-child,
#entTableHost thead > tr > th:last-child {
  position: sticky;
  right: 0;
  background: var(--card);
  box-shadow: -8px 0 8px -8px rgba(0, 0, 0, 0.18);
}
/* 表头那格要压在吸顶表头之上（两个 sticky 轴交叉时靠 z-index 定谁在上） */
#orgTableHost thead > tr > th:last-child,
#entTableHost thead > tr > th:last-child { z-index: 4; }
#orgTableHost tr:not(.expand-row) > td:last-child,
#entTableHost tr:not(.expand-row) > td:last-child { z-index: 2; }
/* 行悬停时吸右那格也要跟着高亮，否则看着像"这一列不响应悬停" */
#orgTableHost tbody tr:hover:not(.expand-row) > td:last-child,
#entTableHost tbody tr:hover:not(.expand-row) > td:last-child { background: var(--color-fill-1); }

/* 虚拟滚动的**占位行**（见 `DataTable` 顶部"虚拟滚动"那段注释）：
   它只是一段高度，所以既不该有边框/内距，也不该被上面的"号码列吸右"吸住
   （它是 colSpan 铺满的一格，吸住会在占位区画出一条常驻的底色条）。
   写在上面那几条之后：同特异度时靠顺序胜出（`tr.kd-pad > td:last-child` 与
   `tr:not(.expand-row) > td:last-child` 的特异度相同）。 */
#orgTableHost tr.kd-pad > td,
#entTableHost tr.kd-pad > td,
#orgTableHost tr.kd-pad > td:last-child,
#entTableHost tr.kd-pad > td:last-child {
  position: static;
  box-shadow: none;
  background: transparent;
  border: 0;
  padding: 0;
}

/* ------------------------- 名册：机构列不许被挤成一字一行 -------------------------
 *
 * 用户截图里的"坏相"：窗口一窄（或面板拖窄），`table-layout: auto` 会把所有列压到
 * 各自的 **min-content** —— 而「机构」这一列的 min-content 只有 1 个字（名字允许换行），
 * 于是「北京圣育中医院」竖着排成 5 行，"别名"也竖着排。那看着就是坏的。
 *
 * 所以给它一个下界（名字 + 别名两行要 ~240px）：装不下就让**表格整体横向滚动**，
 * 而不是把这一列压成一条。配合上面的号码列吸右，窄屏下"名字看得清 + 号码够得着"。
 */
/**
 * ⚠️ 排除勾选列（`:not(.pick-cell)`）：这条 `min-width: 240px` 是给**机构名**那一列的
 * （名字两行要 ~240px，见上面那段）。而「批量操作」进入选择模式后，表格最前面多出一列勾选框
 * ——它成了 `:first-child`，于是整列被撑到 240px：勾选框孤零零挂在最左、机构名被顶到 x≈345
 * （用户截图："前面不要顶这么后面"）。勾选列的宽度归 `.pick-cell` 的 34px。
 */
#entTableHost thead > tr > th:first-child:not(.pick-cell),
#entTableHost tbody tr:not(.expand-row) > td:first-child:not(.pick-cell) { min-width: 240px; }
table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: var(--font-size-table); line-height: 1.5; }
thead th {
  position: sticky; top: 0; z-index: 2;
  text-align: left; font-weight: 500; font-size: var(--font-size-sm); color: var(--dim);
  padding: 11px 14px; background: var(--card-2);
  /**
   * 表头与行之间那道线**全站一个档**（2026-10-13）。原来是 `--color-border-1`（242,243,245），
   * 比表头自己的底色（247,248,250）还浅 —— 等于没画：行滑到表头下面时看不出边界（机构线索
   * 那页因此被报过"表头透明"，见 §二十四）。现在统一用 `--border-strong`（201,205,212）。
   */
  border-bottom: 1px solid var(--border-strong); white-space: nowrap;
}
/* 行内距 6px → 10px、垂直居中：13px 字号是刻意的密度选择（保留）
   但上下只有 6px 时行与行会糊在一起——密度不等于拥挤，用行高换呼吸感，字号不动。
   名册表自带 `#orgRows td` 的钉死行高，ID 选择器优先级更高，不受这条影响。 */
tbody td { padding: 10px 14px; border-bottom: 1px solid var(--color-border-1); vertical-align: middle; }

/**
 * 机构详情页的编排（2026-10-13 第二版）：
 *   ① **结论带**——这一页要回答的第一个问题是"要不要打、怎么打"，所以把「投放判断」与
 *      「建议动作」提到最上面，右边并排状态与主按钮（原来它们散在页头、搜索可见度里、
 *      决策与对标下面三处，读完一屏也拼不出结论）；
 *   ② **三栏事实**——身份 / 搜索可见度 / 市场对标并排，三件事各占一栏，不再是一长条白卡从上堆到下；
 *   ③ **折叠行**——两级明细（对标明细 / 更多资料）并排一行收尾。
 * `auto-fit + minmax` 让三栏在窄屏自动落成两栏、一栏，不用另写断点。
 */
/**
 * 名册页：筛选条与右侧动作**同一行**（用户："右侧的分配线索和分布看板放在和筛选行同一行，整体上移"）。
 * 做法是让岛的筛选条占满剩余宽度、动作贴在右端：筛选条自己有 `flex-wrap`
 * （控件多时它自己换行），所以它那一栏要给 `flex: 1 1 auto; min-width: 0`，
 * 否则它会把动作挤出这一行。
 */
/* 与机构线索页那一行同一套：居中对齐、筛选条吃满剩余宽度（`min-width: 0` 不能省 ——
   少了它，筛选条里控件的最小宽度会把右侧动作顶出去）、动作贴右端。 */
.ent-filter-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.ent-filter-row > #entFiltersHost { flex: 1 1 auto; min-width: 0; }
.ent-row-actions { flex: none; display: flex; align-items: center; gap: 8px; margin-left: auto; }

/* 选择模式的开关：按下去时给它一个"开着"的样子（与 tab/chip 的选中态同一套语言：浅底 + 主色字）。
   名册与机构线索各有一个（`#btnEntBatch` / `#btnOrgBatch`）——两页同一套交互，样式也必须同一套。 */
#btnEntBatch[aria-pressed="true"],
#btnOrgBatch[aria-pressed="true"] { background: var(--accent-weak); color: var(--accent-text); font-weight: 600; }
/* 选择条：已选计数 + 对这一批做什么。与筛选 chip 行同一族容器（.filters），只调间距 */
.ent-pick-bar { margin: 0; }
.ent-pick-bar b { font-weight: 600; }
.ent-pick-bar .spacer { flex: 1 1 auto; }

.detail-lead {
  display: flex; gap: 20px; align-items: flex-start; flex-wrap: wrap;
  /* 默认中性：这一条写的是"它投不投"，而"没在投"是常态（全库 53% 已停 + 29% 无投放页），
     不该用主色浅绿告诉人"情况正常"。上色见下面那条 is-live。 */
  background: var(--card-2);
  border-left: 3px solid var(--border-strong);
  border-radius: var(--radius);
  padding: 14px 18px; margin-bottom: 16px;
}
/* 「在投且在更新 / 近期新出现」（全库 8%）—— 这才是销售要抢的那一档，才给它主色 */
.detail-lead.is-live { background: var(--accent-weak); border-left-color: var(--accent); }
/*
 * 抽屉里的 Tab 面板（「更多资料」那 6 块）。两条规则都只对**挂载成功**的态生效 ——
 * 选择器用的是 Arco 自己的类名（`.arco-tabs-content-item`），岛没挂上时它不存在。
 *
 * ① 藏掉每格里的卡内标题：标题已经由 Tab 标签说了，再写一遍就是"同一件事说两遍"。
 * ② 顺手把卡片壳去掉（阴影 / 边框 / 16px 下外边距）：抽屉底是白的，卡也是白的，
 *    卡片壳在 Tab 里只会增加一层没意义的框，内容靠 `.panel .body` 自己的内距就够。
 */
.arco-tabs-content-item .panel > h2 { display: none; }
.arco-tabs-content-item .panel { margin-bottom: 0; border: 0; box-shadow: none; }
.detail-lead-main { flex: 1 1 520px; min-width: 0; }
.detail-lead-side { flex: 0 1 260px; display: flex; flex-direction: column; gap: 10px; align-items: flex-start; }
.detail-lead-k { font-size: var(--font-size-sm); color: var(--dim); margin-bottom: 3px; }
.detail-lead-say { font-size: 16px; font-weight: 600; line-height: 1.45; }
/* 状态行：三个「标签 + 词」并排。标签有高度，所以按 baseline 对齐会歪，这里用 center */
.detail-lead-side .lead-state { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 14px; font-size: var(--font-size-sm); color: var(--dim); }
.detail-lead-side .lead-state-i { display: inline-flex; align-items: center; gap: 6px; }
.detail-lead-side .lead-state .stage-tag { color: var(--text); }
.detail-lead .action-list { margin-top: 12px; }

.detail-cols {
  display: grid; gap: 16px; align-items: start;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
}
/**
 * **三栏里的内容要按"窄栏"重排**（2026-10-13）：
 *   · 键值带 `.dl-trio` 原本是**整宽面板**的写法（一行三对），塞进 1/3 栏后
 *     「北京 · 昌平区」「2016-03-18」都被折成三行 —— 窄栏里一行两对；
 *     ⚠️ 选择器要写到 `.dl.dl-trio` 这一级：`.dl-trio` 那条(0,2,0)在文件后面，
 *     同级时后写的赢，只写 `.detail-cols .dl-trio` 会被它盖回去（实测：单元格照样折三行）；
 *   · 卡片数值 24px 是给整宽卡片定的，「最近 2026-02-22」在 300px 栏里会顶出卡片 —— 窄栏里降一档。
 */
.detail-cols .dl.dl-trio { grid-template-columns: 56px minmax(0, 1fr) 56px minmax(0, 1fr); }
:root .detail-cols .card .v,
:root .detail-cols .card .arco-statistic-value { font-size: 20px; }
:root .detail-cols .card .v { white-space: normal; }
@media (max-width: 820px) {
  .detail-cols .dl.dl-trio { grid-template-columns: 56px minmax(0, 1fr); }
}

.detail-folds { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 16px; }
.detail-folds > .fold { flex: 1 1 320px; margin: 0; }

/* 表格密度：**两档，且只有两档**（2026-10-13 定）
   --------------------------------------------------------------------------
   · 列表页的行（`.table-wrap` 里那张长表）：跟着这一页"一屏看多少行"定 ——
     机构线索 62px（一页 400 行、要密）、名册 37px（一页 100 行、名字两行就得这么高）；
   · 页内小表（详情页里的 3~11 行附表）：41px（`tbody td` 的 10/14 内距 + 13px 字号），
     它的读者是"一行一行读"而不是"扫一屏"，不必再压。
   新加一张表时从这两档里挑，别再新造第三个数（今天之前是 62/37/41 三个数各自散着，
   谁也说不清规矩是什么）。 */

/**
 * **说明类正文给行宽上限**（2026-10-13）：面板里那些"一句话讲清一件事"的段落
 * （建议动作的每条、空态说明、口径说明）原来铺满整个面板宽度 —— 详情页上量到
 * **一行 1119px**（13px 字，约 86 个汉字），远超"一行 30~45 字"的可读区间，读起来会串行。
 * `34em` 在 13px 下约 44 个字，正好落在区间里；右侧留白比串行好读。
 * 只收**段落与列表项**：表格、卡片、键值带那些是"格与格的对齐"，不需要行宽。
 */
.action-list li,
.panel .body > p,
.panel .body > .note {
  max-width: 34em;
}

/* 机构线索：行高钉死、内容不换行
   --------------------------------------------------------------------------
   光压内距和徽标数压不平：早期实测 79% 的行到 42px、21% 是 54~63px，而没有任何单一元素
   超过 29px —— 是机构名 + 徽标这类行内元素的基线对齐把行盒撑高了。
   与其继续猜，不如钉死：单元格不换行、机构名超出用省略号（title 里是全名），
   表格每行齐平，扫读时视线不用重新找行。
   2026-10-13 再把高度**钉成整数** 62（原来是"42 内容高 + 上下内距 20 + 边框 1"算出的 61.5）：
   小数的行高在吸顶表头下沿会留一条亚像素的缝，那半像素里透出下面行的字；
   `box-sizing: border-box` 让 62 就是整行（含内距与边框）。 */
#orgRows td {
  height: 62px;
  box-sizing: border-box;
  white-space: nowrap;
  overflow: hidden;
}
/* 机构名过长由省略号收尾（链接自己的 `title` 与这一格的 `title` 里都是全名）。
   上一版这里写的是 `#orgRows td:first-child { max-width: 244px }`——那是"别把表格撑出
   横向滚动"的手工闸门，加了「改名」按钮后名字只分到 244px。现在整表不再横向滚动
   （`table-layout: fixed` + 列宽百分比），这道闸门连同它的 244 一起废掉了，
   名字能拿到的宽度由列宽决定（1440 视口下 ~276px），比原来宽。 */
#orgRows td:first-child .org-link {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
#orgRows .badge { vertical-align: bottom; }
#orgRows .plat { vertical-align: bottom; }
tbody tr:hover td { background: var(--color-fill-1); }
tbody tr:last-child td { border-bottom: 0; }
.num { text-align: right; font-variant-numeric: tabular-nums; }
.org-name { font-weight: 600; }
/**
 * 0 值降一档。经营分析那几张表里 0 是多数，和真实数字同字号同颜色时，
 * 一屏的 0 会把注意力从「哪格有数」上引开——投放分布主表用 `.z` 处理的是同一件事
 * （那边空格子用淡点，这里是明确的 0）。
 */
.zero { color: var(--dim-2); }

/* 机构名链接：颜色区分出「这里能点」，悬停才加下划线，长列表里不会一片花 */
a.org-link {
  color: inherit;
  font-weight: 600;
  text-decoration: none;
  border-bottom: 1px solid transparent;
}
a.org-link:hover { color: var(--accent-text); border-bottom-color: var(--accent-text); }
a.org-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--border-radius-small); }
.truncate { max-width: 260px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: inline-block; vertical-align: bottom; }
.nowrap { white-space: nowrap; }
/**
 * 可点排序的表头。它必须是「表头」而不是「按钮」：第一版直接用 `<button>` 的默认外观
 * 截图里「指数」变成一个带边框的方块、比旁边表头重得多，看着像筛选器。
 * 这里抹掉按钮外观（透明底、无边框、继承字体），可点性靠 hover 变色 + 聚焦环 + 方向箭头表达。
 */
.th-sort {
  background: none; border: 0; padding: 0; margin: 0; cursor: pointer;
  font: inherit; color: inherit; letter-spacing: inherit;
  display: inline-flex; align-items: center; gap: 3px;
  position: relative;
}
/**
 * **表头排序的热区补到整格高**（WCAG 2.5.8，实测 51×18 → 不到 24）。
 *
 * 排序按钮是"表头文字"形态（无内距、无边框，见上面那条注释），所以它自己的盒子只有文字那么高
 * （18px）。而单元格上下各有 11px 内距——**那 22px 是白点上去不生效的死区**。
 * 用一层伪元素把那 22px 补进热区（`top/bottom: -11px` 正好等于 `thead th` 的上下内距），
 * 视觉一点不变：伪元素透明、没有背景与边框，只吃指针。
 */
.th-sort::after {
  content: ''; position: absolute; left: 0; right: 0; top: -11px; bottom: -11px;
}
.th-sort:hover { color: var(--text); }
.th-sort:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--border-radius-small); }
.th-sort .caret { color: var(--accent-text); font-size: var(--font-size-sm); line-height: 1; }
/**
 * **全局：`<details>` 的原生展开三角一律不显示**（用户口径）。
 * 只隐藏标记、不动交互——`summary` 仍可点，展开状态由内容本身看出来。
 * 覆盖两个入口：标准的 `list-style` 与 WebKit 的伪元素（只写后者在其它引擎上会漏出三角）。
 */
summary { list-style: none; }
summary::-webkit-details-marker { display: none; }
/** 机构单元格第二行：地区（曾独立一列，合并后放这儿；仍是可点改的按钮） */
.org-meta { margin-top: 2px; line-height: 1.4; }
/** 付费比与投放徽标同格：它们是同一份数据的两种画法，原来占两列 */
td.plats .paidratio { margin-right: 8px; }
/** 行内展开控件（第 8 项）：取代用户截图里那个"看起来能点"的箭头 */
/*
  名册行首那个 ▸ 三角去掉：整行本来就是可点的（`rowClassName: 'clickable'`、指针是手型），
  再挂一个三角是多余入口。按钮**留在 DOM 里**——它背着这一行的 `data-ent`，点行的处理逻辑
  是从它身上取 id 的，只是不再画出来（删掉它会连带把"点行开资料"一起弄坏）。
*/
/* 名册与机构线索的行首 ▸ 都不显示：整行本来就是可点的（`tr.clickable`、指针手型），
   那枚三角既不增加信息、又和展开区那套三角混在一起。用户口径：这类指示一律不要。 */
#entTableHost td.org-name .row-toggle,
#orgTableHost .row-toggle { display: none; }

.row-toggle {
  background: none; border: 0; padding: 0 4px 0 0; margin: 0; cursor: pointer;
  color: var(--dim-2); font: inherit; line-height: 1; vertical-align: 1px;
}
.row-toggle:hover { color: var(--accent-text); }
.row-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--border-radius-small); }
tr.expand-row td { background: var(--bg-soft, rgba(0,0,0,.02)); }
.expand-body { padding: 6px 2px; font-size: var(--font-size-sm); color: var(--dim); line-height: 1.7; }
.expand-body b { color: var(--text); }
.expand-body .sep { margin: 0 6px; color: var(--dim-2); }
tbody tr.clickable { cursor: pointer; }
/**
 * 惰性渲染（审查后加）：机构线索一次取 400 行 × 10 列，全部铺成 DOM 后
 * 滚动和勾选都会卡。content-visibility:auto 让屏外行跳过布局与绘制；
 * contain-intrinsic-size 给占位高度（≈一行 52px），否则滚动条长度会跳。
 * 只作用于行，不影响列宽计算。
 */
#orgRows tr { content-visibility: auto; contain-intrinsic-size: auto 52px; }
/* 名册页同理：一页 100 行 × 7 列，展开行也是就地插入的（同一套行内展开） */
#entRows tr { content-visibility: auto; contain-intrinsic-size: auto 52px; }

/* ------------------------------ 徽标 ------------------------------ */

/**
 * 徽标（按 Arco Badge 的数字档对齐）。
 *
 * Arco 的取值：`.arco-badge-number` 高 20px、`min-width: 20px`、行高 20px、内距 `0 6px`、
 * 字号 12px、字重 500；状态点 `.arco-badge-dot` 6×6（我们的 `.chip .dot` 本来就是 6px ✓）。
 * "行高 1.6 + 1px 7px 内距 + 描边"会让高度随字号飘、还带一圈边框——
 * 边框是容器才需要的东西（见文件头的边框饮食），徽标靠底色区分就够。
 *
 * 带文字的色片这一家族在 Arco 那边其实是 **Tag**（`height 24 / size-small 20 / padding 0 8 /
 * 12px / 字重 500`）：`.tag` 早就是 500，`.badge` / `.pk` / `.chip` 这轮才补上——
 * 同一个家族里三种字重会让并排的色片看着像三个来源。600 那档留给强调态（`.paid` / `.high` …）。
 */
.badge {
  display: inline-block; box-sizing: border-box; font-size: var(--font-size-sm); font-weight: 500;
  height: 20px; min-width: 20px; line-height: 20px; padding: 0 6px; text-align: center;
  border-radius: var(--radius-pill); border: 1px solid transparent; background: var(--color-fill-2);
  color: var(--dim); white-space: nowrap;
}
/** 数字型徽标（只有计数）：正方形，与 Arco 一致 */
.badge.num { padding: 0; }
/** 状态点：6px，与 Arco 的 dot 同尺寸 */
.badge.dot { width: 6px; height: 6px; min-width: 0; padding: 0; border-radius: 50%; }
.badge.paid { background: var(--accent-weak); border-color: transparent; color: var(--accent-text); font-weight: 600; }
/* 重度投放：名下 ≥2 个付费渠道。比「付费位」重一档——2084 家里只有 210 家到这一级，
   而「在多个平台同时买位子」才是真正的投流客户（口径见 docs/analysis/2026-09-17-付费位口径复核.md）。
   用 `--accent-strong` 而不是 `--accent-text`：契约里前者才是「绿底白字」的填充色（4.60:1） */
.badge.heavy { background: var(--accent-strong); border-color: transparent; color: #fff; font-weight: 600; }
.badge.weak { background: var(--low-weak); border-color: transparent; color: var(--low-text); }
.badge.high { background: var(--high-weak); border-color: transparent; color: var(--high-text); font-weight: 600; }
.badge.mid { background: var(--mid-weak); border-color: transparent; color: var(--mid-text); font-weight: 600; }
.badge.low { background: var(--low-weak); border-color: transparent; color: var(--low-text); }
/**
 * 蓝档（信息 / 在处理中）：**同一个色对，两个名字**。
 * `matched` 是对上名册那类"已知事实"，`info` 是「申请中（待批）」那类"在处理中"。
 * 写进同一条规则是为了不出现两份色值 —— 分了家迟早一处改了另一处没改。
 */
.badge.matched, .badge.info { background: rgb(var(--info-weak)); border-color: transparent; color: rgb(var(--info)); font-weight: 600; }
.badge.stalled { background: var(--low-weak); border-color: transparent; color: var(--low-text); }

/* 在投平台徽标：填色＝付费位，描边＝仅平台收录。
   用徽标而不是「一平台一列」的透视：那 10 列里 73% 的格子是空的、值几乎都是 1，
   真正的信息是「投了哪几个平台」，徽标一次性把这件事说清楚。 */
.plats { line-height: 1.6; }
.plat {
  display: inline-block; font-size: var(--font-size-sm); padding: 1px 6px; margin: 0 2px 1px 0;
  border-radius: var(--radius-pill); border: 1px solid var(--border-strong); color: var(--dim); white-space: nowrap;
}
/**
 * 付费平台徽标：**去掉品牌绿**（2026-10-12）。
 * 一行里的彩色原来有五六处（意向档 / 由头徽标 / 付费徽标 ×2-3 / 投放页 / 共用号），
 * 互相抢注意力，判断反而看不清。现在**绿只留给"判断结论"**（意向档、由头徽标），
 * 这里改中性填充——「**填色 vs 虚线描边**」这条非颜色线索照旧（图例里就是这么写的）。
 */
.plat.paid { background: var(--low-weak); border-color: transparent; color: var(--dim); font-weight: 600; }
/* 「+N 收录」：仅被收录的平台折成一个 chip，名字进 tooltip。为的是行高——
   收录徽标一多就换行，把行撑到 55px，一屏少看 5~6 家。 */
.plat.weak { color: var(--dim-2); border-style: dashed; }

/**
 * 页头动作里的**主次分隔**（2026-10-12）：机构线索页头原来四个动作平铺
 * （图例 / 新建客户 / 导出 CSV / 立即扫描），主操作与次要动作只靠颜色区分。
 * 一条 1px 竖线把"改这份名单"与"立即扫描"分开。
 */
.ph-sep { width: 1px; height: 18px; background: var(--border); margin: 0 2px; flex: none; }

/**
 * 居中弹窗的标题条（2026-10-12）：机构名 + **紧挨着它的**「打开线索详情」。
 * 那个入口原来在正文最后一行——它是"去另一个页面"，跟标题同高才对得上。
 *
 * **不把它推到最右**（用户："你贴着标题放啊"）：贴右会和 Arco 的关闭键挤在一起，
 * 而且"标题→入口"本来是一句话连着的。所以这里只靠 `gap` 分隔，不用 `margin-left: auto`
 * （也顺带避开 `float: right`：机构名长时 float 会让文字绕排、对不齐）。
 * 名字过长时由 `.dlg-title-t` 先省略，保证入口不会被挤没。
 *
 * 上面那句省略靠下面这条 Arco 覆写才成立：`.arco-modal-title` 是 `flex: 1 1 0%`，
 * 但 Arco 没给它 `min-width: 0`，而 flex 的默认 `min-width: auto` 会让它不肯收窄、直接涨出去
 * （实测机构名 32 字时标题条 754px > 标题栏 720px，多出的部分压住右上角的关闭键）。
 * 补上 `min-width: 0`，`.dlg-title-t` 的 ellipsis 才会真的生效。
 * `padding-right` 是给关闭键让位：它是 `position: absolute; right: 20px`，不占 flex 空间，
 * 标题收窄后仍会铺满整条、把入口顶到关闭键底下（实测入口右端 1060 = 关闭键左端 1048）。
 */
.arco-modal-header .arco-modal-title { min-width: 0; padding-right: 24px; }
.dlg-title { display: flex; align-items: center; gap: 10px; min-width: 0; }
.dlg-title-t { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dlg-open { flex: none; font-size: var(--font-size-sm); color: var(--accent-text); white-space: nowrap; }
.dlg-open:hover { text-decoration: underline; }

/**
 * 机构线索表：不再横向滚动（2026-10-13 用户："机构线索不要这个横向滚动"）。
 *
 * 为什么是 `table-layout: fixed` 而不是"再砍点内容"：这张表十列的内容宽度加起来有
 * 1352px 的硬地板——浏览器实测 1600 视口往下，表格一律 1352px（各列 min-content 之和），
 * 而容器只有 1174px（1440 视口），于是固定多出 178px 必须横滚。砍内容只能把地板推到
 * 下一个屏幕宽度，换个窗口（1280 视口容器 1014）又滚起来。
 *
 * fixed + 百分比列宽改成"按容器分配"：宽了各列一起松、窄了各列一起紧，
 * 任何窗口宽度都不产生横向滚动。代价是窄屏由省略号收尾（`text-overflow`），
 * 所以每格的关键信息都另有去处：机构名与次要标记在单元格 `title`、完整口径在详情弹窗。
 *
 * 列宽必须写成百分比：px 加总超过容器时 fixed 表格会**按 px 把表撑宽**（照样横滚），
 * 百分比加总不足 100% 则由「机构」那一列（不设宽度）吃掉余额，这才保证填满且永不溢出。
 *
 * 列宽是按**每一列内容的实际宽度**（浏览器量的 min-content，写在每组上面）反推的百分比，
 * 每组按它要服务的那个容器宽度配：1440 视口容器 1174 / 1280 视口 1014 / 1600 视口 1334。
 * 数字不是拍脑袋——改了列内容（换徽标、改文案）要跟着重量一次，量法见
 * `docs/ops/2026-10-09-全站同类问题清理.md` 第二十一节（验收台里隐藏 Arco 弹层后量 `scrollWidth`）。
 */
/* 选择器不写 `.table-wrap` 那层：岛就位前的骨架表在它外层（`#orgTableHost > table`），
   就位后岛自己在里面又包一层 `.table-wrap`——两种都要管，否则骨架那一瞬还是横滚的 */
#orgTableHost table { table-layout: fixed; width: 100%; }
#orgTableHost table th,
#orgTableHost table td { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* 内距 14 → 10：八列各让 8px 出来给「机构」那一列（列宽的账见下面那组百分比） */
#orgTableHost table th,
#orgTableHost table td { padding-left: 10px; padding-right: 10px; }

/**
 * 吸顶表头要**看得出是一层**（2026-10-13 用户："表头吸顶 透明 这不对吧"）。
 *
 * 底下没有真的透明：表头底色 `--card-2` = rgb(247,248,250) 是实色，行滑到它下面确实被盖住
 * （用把表头刷成纯红的方式验过：整个表头带是一片红，没有一处透出行的字）。
 * 但**看起来像透明**——两个原因，都是"这一层没有边界"：
 *   · 表头底色 247,248,250 与行的白底只差 3%，行滑到下面时分不出"上面是表头"；
 *   · 下边框原来是 `--color-border-1` = 242,243,245，比表头底色还浅 —— 这条线等于没画。
 * 于是行被表头切掉半行字的时候，读起来就是"行透过了表头"。
 *
 * 处置两条（只加"边界"，不动底色与间距）：
 *   ① 下边框换成 `--border-strong`（229,230,235）：线看得见，表头与行才分得开；
 *   ② 一条很浅的下投影（与名册吸顶时那条同款参数）：读起来"表头浮在行上面"。
 * 另外保留那一层 `0 1px 0 var(--card-2)` 同色描边——它治的是亚像素缝里透字（另一件事）。
 */
/**
 * ⚠️ 这层投影**只在真吸住时**画（2026-10-13 改）。
 *
 * 原来是无条件挂的：表头平时也顶着一圈阴影，在浅底上看着像一条脏带
 * （用户把这处连同"顶栏不透明"一起提出来了）。名册那边本来就是"真吸住才画"
 * （`.is-stuck` 由 app.js 在滚动时挂上）—— 这里对齐它，判据同一套。
 */
.panel.pageflow #orgTableHost.is-stuck thead th {
  box-shadow: 0 1px 0 var(--card-2), 0 6px 8px -8px rgba(15, 23, 42, 0.3);
}

/**
 * 悬停清单卡片（`client/src/ui/InfoCell.tsx`，2026-10-13）。
 *
 * 机构 / 投放意向 / 投放证据三格的悬停说明走这里：Arco `Popover` + `Descriptions`。
 * 原来这三处走全站的 `.tip-layer`（黑底一句话，把"媒体付费位 +50、近 30 天有发文 +25、
 * 跨媒体重叠 +15 —— 行首 ▸ 看完整明细"塞成一行），用户要求"这些 hover 也都用 arco 列表"。
 * 卡片宽度给上限：平台名可能很长（`39就医助手`），不设上限会排成一条长龙。
 */
.info-pop { max-width: 320px; }
.info-card-t { font-weight: 600; color: var(--text); margin-bottom: 6px; }
/* 触发元素是真实盒子（Arco 要量它的 rect 定位浮层，`display: contents` 会量成 0×0） */
.info-trigger { display: block; min-width: 0; }

/**
 * 阶段格**只读**标签（2026-10-13）：形状与原来那枚 chip 一致（色点 + 文字），
 * 但**没有边框、没有悬停态、没有箭头**——它是"这一家到哪一步了"的展示，
 * 不是能点的东西（改阶段进详情抽屉的编辑态）。
 * 之所以还留色点：阶段之间的差别靠颜色一眼可比，这一层信息与"能不能点"无关。
 */
.stage-tag {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: var(--font-size-sm); color: var(--dim);
}
.stage-tag .dot { width: 6px; height: 6px; border-radius: 50%; flex: none; }

/**
 * 详情抽屉里的编辑表单（`client/src/ui/lead-drawer-host.tsx`）。
 * 标签列宽与 `Descriptions` 的标签列一致（168px），所以"看"和"改"两种状态切换时
 * 左边缘不跳。控件走岛的 Arco 件（`Input` / `Select` / `Button`），这里只排位置。
 */
.lead-form { margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--border); }
.lead-field { display: flex; align-items: center; gap: 10px; min-height: 34px; }
.lead-field-t { flex: none; width: 168px; color: var(--dim-2); font-size: var(--font-size-base); }
.lead-field-v { display: flex; align-items: center; gap: 8px; min-width: 0; flex: 1 1 auto; }
/* 「地区」那格沿用列表里"平时像纯文本、悬停才发现能改"的老做法（`.region-cell` 的同一套语言） */
.lead-region {
  font: inherit; color: inherit; background: none; border: 0; padding: 0;
  cursor: pointer; display: inline-flex; align-items: center; gap: 8px;
}
.lead-region:hover { color: var(--accent-text); border-bottom: 1px dotted var(--border-strong); }
.lead-region-x { font-size: var(--font-size-sm); color: var(--dim-2); }
.lead-err { margin: 8px 0 0 178px; color: var(--high-text); font-size: var(--font-size-sm); }
/* 抽屉里的地区面板：`.geo-panel` 是 `position: fixed; z-index: 60`，而 Arco 抽屉在 1000 之上 */
.geo-panel.over-drawer { z-index: 1010; }

/**
 * 详情弹窗「看」的那一层：按**打一通电话的顺序**排成四块，档案收进折叠。
 * 内容由岛渲染（`client/src/ui/lead-drawer-host.tsx`），颜色与圆角一律走令牌。
 *
 * 一条贯穿的取舍：**块与块之间用留白和一条细线分开，不用卡片边框套卡片** ——
 * 弹窗本身已经是一张卡片，里面再画四个框，一屏就有五层边框（判据同 DESIGN 的"一屏一主角"）。
 */
/* 标题栏那枚「编辑」推到最右：`.ent-title-row` 是 flex，所以靠 margin 而不是 float */
.lead-hd-acts { margin-left: auto; }
/* ── ① 结论：这一屏的主角，字号只在这里放大一级 ── */
.lead-lead {
  display: flex; align-items: flex-start; gap: 16px;
  padding: 12px 14px; background: var(--accent-weak); border-radius: var(--radius);
}
.lead-lead-t { min-width: 0; }
.lead-verdict { font-size: 17px; font-weight: 700; color: var(--accent-text); line-height: 1.4; }
.lead-why { margin-top: 4px; color: var(--dim); font-size: var(--font-size-sm); }
.lead-rank { margin-left: auto; flex: none; text-align: right; }
.lead-rank-n { font-size: 22px; font-weight: 700; color: var(--accent-text); font-variant-numeric: tabular-nums; letter-spacing: -.5px; }
.lead-rank-k { color: var(--dim-2); font-size: var(--font-size-sm); }

/* ── ② 号码：这一块要"拿起来就能打"，所以号码本身就是最大的那行字 ── */
.lead-call {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-top: 12px; padding: 10px 14px;
  border: 1px solid var(--border-strong); border-radius: var(--radius);
}
.lead-call-num { font-size: 18px; font-weight: 700; font-variant-numeric: tabular-nums; }
.lead-call-acts { margin-left: auto; display: flex; gap: 8px; flex: none; }
/* 「人工核过」：这是可信标记（人写进去的号），不是又一个状态徽章，所以用文字 + 绿，不加底色 */
.lead-ok { color: var(--accent-text); font-size: var(--font-size-sm); font-weight: 500; }

/* ── ③ 谈资与构成 ── */
.lead-fact { margin-top: 12px; padding: 12px 14px; border: 1px solid var(--border); border-radius: var(--radius); }
.lead-sec-h {
  display: flex; align-items: center; gap: 10px; margin-bottom: 8px;
  font-size: var(--font-size-base); font-weight: 650;
}
.lead-sec-h .arco-btn { margin-left: auto; }
.lead-one { font-size: var(--font-size-base); line-height: 1.7; }
/**
 * 构成环（KPI 扇形）的**通用**样式 —— 抽屉（岛渲染）与手写页共用这一套类名。
 *
 * 为什么不叫 `.lead-ring`：它不只属于详情弹窗。凡是"一个整体由几份构成、要看构成"
 * 的地方都用它（详情弹窗的意向分、任务进度、同城同科室的跟进比例），
 * 各写一份样式必然漂移 —— 环的粗细、圆角、四档深浅是最容易被抄歪的东西。
 *
 * 两处约定，两条实现（React 那版与 `app.js` 的 `ringHtml`）都必须守：
 *   · 半径 `100 / (2π)`，使周长正好 100 → `stroke-dasharray` 直接写**数值**，不换算百分比；
 *   · 段的顺序即环上顺时针的顺序，色档 s1→s4 由深到浅（第 1 段最该被看见）。
 */
.kpi-row {
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  margin-top: 11px; padding-top: 11px; border-top: 1px solid var(--border);
}
/**
 * 环右侧的「各平台付费位」横向条形。
 *
 * 为什么是条形、而不是把这串平台名写在句子里：四个平台各"1 处"排成一行读不出
 * **哪家占得多**（而"铺在哪几家、集中还是分散"正是这一格要回答的判断）。
 * 长度即处数，一眼可比；平台名逐行给全（省了的话悬停有全名），不静默丢。
 *
 * 与左边「环 + 四项图例」**分组**：两者是两件事（分数怎么来的 / 投在哪几家），
 * 挨在一起读成一行会以为是一张表，所以左边留出间距 + 一道竖线。
 * `flex-wrap` 加在 `.kpi-row` 上：窄屏时整块掉到环下面，也是靠这道竖线认得出是一组。
 */
.kpi-bars {
  flex: 0 1 300px; min-width: 200px; display: flex; flex-direction: column; gap: 5px;
  margin-left: 6px; padding-left: 18px; border-left: 1px solid var(--border);
}
.kpi-bars-hd { color: var(--dim-2); font-size: var(--font-size-sm); }
.kpi-bar { display: flex; align-items: center; gap: 8px; font-size: var(--font-size-sm); }
/* 行是深链时：文字颜色跟着走（不用主色，一列四条都染绿会盖过条形本身），悬停才给反馈 */
a.kpi-bar-link { text-decoration: none; color: inherit; border-radius: var(--border-radius-small); }
a.kpi-bar-link:hover { background: var(--color-fill-1); }
a.kpi-bar-link:hover .kpi-bar-n { color: var(--accent-text); text-decoration: underline; }
a.kpi-bar-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* 平台名可省（省略号 + 悬停全名），条形与数字不可省 */
.kpi-bar-n { flex: 0 1 104px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--dim); }
.kpi-bar-t { flex: 1 1 auto; min-width: 40px; height: 8px; border-radius: var(--radius-pill); background: var(--color-fill-2); overflow: hidden; }
.kpi-bar-t > i { display: block; height: 100%; border-radius: var(--radius-pill); background: var(--accent); }
.kpi-bar-v { flex: none; font-variant-numeric: tabular-nums; font-weight: 650; }
/* 外链标记（↗）：告诉人"这一行能点、点了去别处"。平时不上色，悬停才跟着平台名一起亮 */
.kpi-bar-x { flex: none; color: var(--dim-2); font-size: 11px; }
a.kpi-bar-link:hover .kpi-bar-x { color: var(--accent-text); }
.kpi-ring { position: relative; flex: none; width: 78px; height: 78px; }
/* 小号：塞进列表/卡片右侧的进度用（任务头、抽屉那一行） */
.kpi-ring.sm { width: 54px; height: 54px; }
.kpi-ring svg { display: block; width: 100%; height: 100%; }
.kpi-ring circle { fill: none; stroke-width: 4.4; }
.kpi-ring.sm circle { stroke-width: 5; }
.kpi-ring .track { stroke: var(--color-fill-3); }
.kpi-ring .s1 { stroke: var(--accent-strong); }
.kpi-ring .s2 { stroke: var(--accent); }
.kpi-ring .s3 { stroke: color-mix(in srgb, var(--accent) 42%, #fff); }
.kpi-ring .s4 { stroke: color-mix(in srgb, var(--accent) 22%, #fff); }
.kpi-ring-mid {
  position: absolute; inset: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center;
}
.kpi-ring-n { font-size: 21px; font-weight: 700; font-variant-numeric: tabular-nums; line-height: 1; }
.kpi-ring.sm .kpi-ring-n { font-size: 15px; }
.kpi-ring-k { margin-top: 3px; font-size: 10px; color: var(--dim-2); }
.kpi-ring.sm .kpi-ring-k { margin-top: 2px; font-size: 9px; }
.kpi-leg { flex: 0 1 240px; min-width: 0; display: flex; flex-direction: column; gap: 5px; font-size: var(--font-size-sm); }
.kpi-leg-r { display: flex; align-items: center; gap: 8px; }
.kpi-leg-r i { width: 9px; height: 9px; border-radius: 2px; flex: none; }
.kpi-leg-r i.s1 { background: var(--accent-strong); }
.kpi-leg-r i.s2 { background: var(--accent); }
.kpi-leg-r i.s3 { background: color-mix(in srgb, var(--accent) 42%, #fff); }
.kpi-leg-r i.s4 { background: color-mix(in srgb, var(--accent) 22%, #fff); }
.kpi-leg-n { color: var(--dim); }
.kpi-leg-v { margin-left: auto; font-variant-numeric: tabular-nums; font-weight: 650; }
/* 没拿到的那份——它在环上就是那段空档，所以整行退到次要层，不跟拿了分的抢注意力 */
.kpi-leg-r.zero .kpi-leg-n, .kpi-leg-r.zero .kpi-leg-v { color: var(--dim-2); font-weight: 400; }
.kpi-leg-r.zero i { background: var(--color-fill-3); }
/* 给"还没轮到我"的那份换个底纹：与「没拿到」不同，它是待办，不是缺失 */
.kpi-leg-r.todo i { background: repeating-linear-gradient(45deg, #f0f2f5, #f0f2f5 3px, #e7eaee 3px, #e7eaee 6px); }
.kpi-note { margin: 9px 0 0; }

/* ── ④ 归属与记录 ── */
.lead-own { margin-top: 12px; padding: 12px 14px; border: 1px solid var(--border); border-radius: var(--radius); }
.lead-own-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.lead-last {
  margin-top: 8px; padding: 7px 10px; border-radius: var(--border-radius-small);
  background: var(--color-fill-1); color: var(--dim); font-size: var(--font-size-sm);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.lead-write { display: flex; align-items: stretch; gap: 8px; margin-top: 10px; }
.lead-note {
  flex: 1 1 auto; min-width: 0; min-height: 56px; padding: 8px 10px; resize: vertical;
  font: inherit; color: inherit;
  background: var(--card); border: 1px solid var(--color-border-2); border-radius: var(--border-radius-small);
}
.lead-note:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.lead-note[aria-invalid='true'] { border-color: var(--high); }
/* 表单里那枚错误要缩进到标签列之后（`.lead-err` 自带），块内这两处不是表单，退掉那个缩进 */
.lead-err-inline { margin-left: 0; }

/* ── ⑤ 档案：折叠 ── */
.lead-arch { margin-top: 12px; border-top: 1px solid var(--border); padding-top: 10px; }
.lead-arch > summary { cursor: pointer; color: var(--dim); font-size: var(--font-size-sm); padding: 3px 0; }
.lead-arch > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.lead-arch-body { padding: 8px 0 0; }
.lead-arch-r { display: flex; gap: 12px; padding: 3px 0; font-size: var(--font-size-sm); }
.lead-arch-k { flex: none; width: 120px; color: var(--dim-2); }
.lead-arch-r > span:last-child { min-width: 0; }
.lead-arch-foot { margin: 8px 0 0; }

/**
 * 「机构」格：名字可以省，▸ / 徽标 /「改名」不能省。
 * 这一格收窄时如果按顺序切，先没的是排在最后的「改名」按钮（点不着）和集团标签；
 * 排成 flex 之后**只有名字**参与收缩（`flex: 0 1 auto` + `min-width: 0`），其余一律 `flex: none`。
 * 名字的全名在这个链接自己的 `title` 与这一格的 `title` 里（`orgRowCells` 的 `nameTitle`）。
 */
#orgTableHost td.col-name .org-line { display: flex; align-items: center; gap: 6px; min-width: 0; }
#orgTableHost td.col-name .org-line > * { flex: none; }
#orgTableHost td.col-name .org-link { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }

/**
 * 「专科」列只在 1600 视口以上出现。
 *
 * 为什么先退它（而不是砍机构名）：一行装不下时退场的判据是"它的事实还有没有别的去处"——
 * 专科是筛选维度（页头就能按专科筛），▸ 详情弹窗里也有「专科」那一行；
 * 而机构名是这一行的身份、地区那格是改地区的唯一入口——它们不能退。
 *
 * ⚠️ 收起这一列必须用零宽的真列，不能用 `display: none`：后者在固定表格布局里**仍算一个
 * "未指定宽度"的列**，浏览器会把余下的空间平分给所有未指定宽度的列（含这一列）——
 * 实测 1440 下被收起的那列悄悄吃掉 124px，「机构」列也只剩 124px（名字被切到 5 个字）。
 * 给成 `width: 0`（配 `padding: 0`，内容由 `overflow: hidden` 收掉）就没有这个洞。
 */
#orgTableHost .col-specialty { width: 0; padding-left: 0; padding-right: 0; }

/**
 * 基础（1600 视口以下，覆盖 1440 与 1366 两档；设计点取 1140，落在 1366 的容器 1100
 * 与 1440 的容器 1174 之间）。
 *
 * 每列的百分比 = 这一列的**最小内容宽度**（浏览器量的 89 / 169 / 117 / 195 / 66 / 218，
 * 含左右各 10px 内距；表内字号 `--font-size-table` 13px）÷ 1140，余下的百分比全给
 * 「机构」——这一页最宽的一列（名字 + 公立 / 集团 / 任务几个标签），名字被切短就读不出是谁。
 *
 * 「归属 / 阶段」169px 与「最近观测」66px 是这两列的**下限**：前者要装下
 * 「负责人 · 阶段 · 相对时间」（最大的那一档是两位数的天数，如「未认领 · 未跟进 · 28 天前」），
 * 后者由 4 个字的表头「最近观测」撑起——内容「今天」只有 35px，比表头窄。
 */
#orgTableHost .col-score { width: 7.54%; }
#orgTableHost .col-stage { width: 16.6%; }
#orgTableHost .col-region { width: 10.26%; }
#orgTableHost .col-evidence { width: 17.11%; }
#orgTableHost .col-seen { width: 6.6%; }
#orgTableHost .col-phone { width: 19.12%; }
/* 机构线索表格：统一中线、统一左右起点，列内只保留一层主信息 + 一层辅助 */
#orgTableHost table th,
#orgTableHost table td { vertical-align: middle; }
#orgTableHost thead th { padding-top: 12px; padding-bottom: 12px; }
#orgRows td { height: 72px; padding-top: 12px; padding-bottom: 12px; }
#orgTableHost td.col-name .org-line { gap: 8px; align-items: center; }
#orgTableHost td.col-name .org-link { font-weight: 600; color: var(--text); }
#orgTableHost td.col-region { white-space: nowrap; line-height: 1.35; }
#orgTableHost .region-text { display: block; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#orgTableHost td.col-evidence { white-space: normal; line-height: 1.35; }
#orgTableHost .evidence-main { display: block; font-size: var(--font-size-base); font-weight: 600; color: var(--text); }
#orgTableHost .evidence-main:empty { display: none; }
#orgTableHost .evidence-sub { display: block; margin-top: 4px; font-size: var(--font-size-sm); color: var(--dim); }
#orgTableHost td.col-stage { line-height: 1.35; }
#orgTableHost .own-stage { gap: 4px; }
#orgTableHost .own-stage-top { gap: 4px; }
#orgTableHost .own-stage-when { color: var(--dim); font-size: var(--font-size-sm); }
#orgTableHost .col-score { text-align: left; }
#orgTableHost td.sc .heatbar { width: 96px; max-width: 100%; }
#orgTableHost td.col-phone,
#orgTableHost thead th.col-phone { text-align: right; padding-right: 14px; }
#orgTableHost td.col-phone .telwrap { display: flex; flex-wrap: wrap; width: 100%; justify-content: flex-end; align-items: center; gap: 2px 5px; }
#orgTableHost td.col-phone .tel-area { flex-basis: 100%; text-align: right; color: var(--dim); font-size: var(--font-size-sm); font-weight: 400; line-height: 1.3; }
#orgTableHost td.col-phone .tel,
#orgTableHost td.col-phone .tel-mask { font-family: ui-monospace, SFMono-Regular, Consolas, "Liberation Mono", monospace; letter-spacing: 0; }
#orgTableHost td.col-phone .tel-mask { display: inline-block; min-width: 92px; text-align: right; }
.org-toolbar-panel > .org-toolbar-body { padding: 16px 18px 10px; }
.org-toolbar-main { align-items: center; gap: 12px; margin-bottom: 8px; }
.org-toolbar-main #orgFiltersHost { min-width: 0; }
.org-toolbar-main .primary,
.org-toolbar-main > button { flex: none; }
.org-toolbar-main .primary { height: 34px; }
.org-toolbar-meta { border-top: 1px solid var(--border); margin-top: 6px; padding-top: 8px; }
.org-summary { display: flex; align-items: baseline; justify-content: flex-end; gap: 8px; margin: 0 0 2px; color: var(--dim); font-size: var(--font-size-sm); }
.org-summary b { color: var(--text); font-size: var(--font-size-base); }
.org-summary .sep { color: var(--border-strong); }
/* 快捷组合 / 最近搜索：一行 chip，不抢筛选条与结果表的层级 */
.org-quickbar { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 6px 0 2px; }
.org-quickbar .qb-label { color: var(--dim-2); font-size: var(--font-size-sm); }
.org-quickbar .qb-divider { width: 1px; height: 16px; margin: 0 4px; background: var(--border); }
.org-quickbar .qb-chip { height: 24px; }

/**
 * **不到设计点（容器 1140）就不再压，改成横向滚**（2026-10-13 补）。
 *
 * 上面那组百分比是"最小内容宽 ÷ 1140"算出来的，所以它只在容器 ≥1140 时才**恰好**够用；
 * 再窄下去每列都会等比缩水——实测 375 视口下容器 109px，八列被压成 13~38px 的细条，
 * 名字只剩两三个字，比"横向滚一下"难用得多（名册在窄屏正是靠最小宽滚的，两页行为应当一致）。
 *
 * 给表一个 1140 的**最小宽**：每列从此恰好等于它自己的最小内容宽（与设计点同值，不再缩水），
 * 溢出交给 `.table-wrap` 滚——顺带让"电话列吸右"那条规则**真的有事可做**
 * （原来它一直是死规则：表永远不滚，就没有可吸的横向滚动）。
 */
@media (max-width: 1200px) {
  #orgTableHost table { min-width: 1140px; }
}

/**
 * 宽屏（1600 视口以上，容器 ≥1334）：专科回来，而且回来的定义是
 * **每一列都拿到自己的最小内容宽度**——百分比按 1334 这一档配（设计点 1334），
 * 到 1920（容器 1654）各列一起放宽，不会出现"某列独吞余量"的怪样子。
 */
@media (min-width: 1600px) {
  #orgTableHost .col-specialty { padding-left: 10px; padding-right: 10px; }
  #orgTableHost .col-score { width: 6.45%; }
  #orgTableHost .col-stage { width: 13.2%; }
  #orgTableHost .col-region { width: 8.77%; }
  #orgTableHost .col-specialty { width: 6%; }
  #orgTableHost .col-evidence { width: 14.62%; }
  #orgTableHost .col-seen { width: 5.9%; }
  #orgTableHost .col-phone { width: 16.34%; }
}


/* 日报页：按天折叠的简报 */
.report { border-bottom: 1px solid var(--border); padding: 10px 0; }
.report:last-child { border-bottom: 0; }
/* 天与天之间画一道分隔线：只靠外边距时，上一段表格的最后一行会被当成下一天的开头 */
.report-day-block + .report-day-block { margin-top: 26px; padding-top: 20px; border-top: 1px solid var(--border); }
/* 分档小节：标题一行 + 信号行（行复用 `.wb-row.sig`，观感与「今天」页一致） */
.report-body { margin: 10px 0 4px; }
.report-sec + .report-sec { margin-top: 16px; }
/* 分档标题两端对齐：档名贴左、家数贴右（家数成了这张表的注脚，不再跟在档名后面飘着） */
.report-sec > h4 {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
  margin: 0 0 6px; font-size: var(--font-size-base); color: var(--text);
}
.report-sec > h4 .ttl { font-weight: 600; }
.report-sec > h4 .note { font-weight: 400; }
/**
 * 「未认领」用哪枚标签：**`.tag-high`**（"需要动作"那一档）—— 两页（日报、机构线索）
 * 共用同一枚，别另起类名。它是浅红底红字（`--high-text` on `--high-weak` = 4.52:1，
 * 12px 小字按 4.5 卡），不是填满红块：一屏十几行填满会糊成一片，而这一枚在表格里是常客。
 */
.report-sec .tag { margin-left: 0; }
/**
 * 帮助问号：页头口径说明的入口（原来是一整句常驻小字）。
 * 是"可聚焦的说明"而不是装饰：键盘也能停上去，读屏念 aria-label。
 */
.help-q {
  display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px;
  margin-left: 6px; border-radius: 50%; border: 1px solid var(--border-strong);
  color: var(--dim); font-size: var(--font-size-xs); font-weight: 600; line-height: 1;
  vertical-align: 2px; cursor: help;
}
.help-q:hover, .help-q:focus-visible { color: var(--accent-text); border-color: var(--accent-text); outline: none; }
.report-sec .wb-list { border: 1px solid var(--border); border-radius: var(--border-radius-medium); overflow: hidden; }
/* 旧数据（没有结构化正文）退回整段文本 */
.report-text {
  margin: 8px 0 0; padding: 10px 12px; background: var(--card-2);
  border-radius: var(--border-radius-medium); font-size: var(--font-size-table);
  line-height: 1.75; white-space: pre-wrap; word-break: break-word;
}

/* 图例：默认收起，点 ? 才展开。前一轮按把口径长文从页面上删了
   但 权重/弱信号/≥N/徽标填色 这些标记总得有地方说清楚——收进这里，不占版面。 */
.legend-btn {
  width: 24px; height: 24px; padding: 0; border-radius: 50%;
  color: var(--dim); font-weight: 600; flex: none;
}
.legend-btn[aria-expanded='true'] { background: var(--accent-weak); color: var(--accent-text); border-color: transparent; }
.panel.legend { margin-bottom: 16px; }
.legend-list { display: grid; grid-template-columns: 132px 1fr; gap: 8px 16px; margin: 0; font-size: var(--font-size-sm); }
.legend-list dt { color: var(--text); font-weight: 600; }
.legend-list dd { margin: 0; color: var(--dim); }
.legend-list dd b { color: var(--text); }

.score { font-weight: 700; font-size: var(--font-size-lg); font-variant-numeric: tabular-nums; }
.score.high { color: var(--high-text); }
/**
 * 意向分色块（报表页用）：**底色取"文字档"的色值**（`--high-text` / `--mid-text`）而不是鲜亮档——
 * 白字压在鲜亮档（danger-6 / warning-6）上只有 3.3~3.9:1，12px 小字必须按 4.5 卡；
 * 换成 -7 档后红底 5.9:1、橙底 8.2:1 都过。低分不填色（灰底灰字，弱下去是对的）。
 */
.score-tag {
  display: inline-block; min-width: 40px; padding: 1px 8px; text-align: center;
  border-radius: var(--radius-pill); font-size: var(--font-size-sm); font-weight: 600;
  font-variant-numeric: tabular-nums; color: #fff;
}
.score-tag.high { background: var(--high-text); }
.score-tag.mid { background: var(--mid-text); }
.score-tag.low { background: var(--low-weak); color: var(--low-text); }
.score.mid { color: var(--mid-text); }
.score.low { color: var(--low-text); }

/* 指数列：数字 + 热力条
   --------------------------------------------------------------------------
   原来「高意向」用红色数字表示，但红色在这个产品里既读作「好」又像「警告」，
   而且一列红数字扫过去看不出强弱差别（100 和 70 都是红的）。
   现在：数字回到中性色（可读、可比较），强弱交给条长，分档交给条的颜色深浅。 */
td.sc { min-width: 62px; }
td.sc .score { color: var(--text); font-size: var(--font-size-base); }
/* 指数格改「档 + 数」：档位词在前（档位承担判断），数字在后。
   为什么只给**档位词**上色、数字仍保持中性：上面那条注释里的结论成立——一列彩色数字扫过去
   看不出强弱差别；而档位只有三档，颜色是"命名"不是"刻度"。数字留着是因为它是指数列的
   排序键、也是横向对比的依据；强弱仍由条长表达。 */
td.sc .sc-band { font-size: var(--font-size-sm); font-weight: 600; margin-right: 5px; }
td.sc .sc-band.high { color: var(--high-text); }
td.sc .sc-band.mid { color: var(--mid-text); }
td.sc .sc-band.low { color: var(--low-text); }
td.sc .heatbar {
  display: block;
  height: 3px;
  margin-top: 4px;
  border-radius: var(--border-radius-small);
  background: var(--bg);
  overflow: hidden;
}
td.sc .heatbar i { display: block; height: 100%; border-radius: var(--border-radius-small); background: var(--accent); }
td.sc.mid .heatbar i { background: var(--mid); }

/**
 * 占比条（经营分析的漏斗）：数字右边挂一条按占比折算的横条。
 *
 * 为什么条长按「当前最大的那个阶段」而不是按占全部的百分比：漏斗里 99.9% 与 0.1%
 * 并存时，按百分比画会让 2,812 和 2 都变成贴边的线，等于没画。归一到最大值之后，
 * 差距才看得见（这正是这张表想说的话）。
 * 行内块而不是 flex：单元格是 `text-align: right`，条跟着百分比一起靠右，
 * 不会飘到单元格左边去。
 */
.share {
  display: inline-block;
  width: 64px; height: 6px; margin-right: 8px;
  border-radius: var(--border-radius-small);
  background: var(--bg);
  overflow: hidden;
  vertical-align: middle;
}
.share i { display: block; height: 100%; border-radius: var(--border-radius-small); background: var(--accent); }
td.sc.low .heatbar i { background: var(--low); }

/* 付费/平台：付费位平台数 / 全部在投平台数。它同时是列表的次级排序依据，
   做成可见的一列，销售不用猜「凭什么这家排前面」。 */
.paidratio { font-variant-numeric: tabular-nums; color: var(--text); font-weight: 600; }
.paidratio .sep { color: var(--dim-2); font-weight: 400; margin: 0 1px; }

/* 行内跟进控件减重：原来 select/input 带边框，视觉重量比数据列还大。
   改成「平时像文字、悬停和聚焦才显形」，让数字回到主体位置。 */
tbody select.stage,
tbody input.owner {
  border-color: transparent;
  background: transparent;
  padding: 4px 6px;
  color: var(--dim);
  font-size: var(--font-size-sm);
  max-width: 100%;
}
tbody select.stage:hover,
tbody input.owner:hover { border-color: var(--border-strong); background: var(--card); }
tbody select.stage:focus,
tbody input.owner:focus { border-color: var(--accent-text); background: var(--card); color: var(--text); }
tbody input.owner { width: 74px; }

/* 骨架屏：先给结构再填数。列表要取 172 行，慢的时候空白会停留可见的一瞬。 */
.sk {
  display: block;
  height: 12px;
  border-radius: var(--border-radius-medium);
  /* 中间那道高光是唯一还写死的颜色，换成令牌（旧值是 #e9edf1） */
  background: linear-gradient(90deg, var(--bg) 25%, rgb(var(--gray-3)) 50%, var(--bg) 75%);
  background-size: 200% 100%;
  /**
   * 两条动画：微光（无限）+ **出现延迟 200ms**。
   *
   * 延迟解决的是"闪一下"：真实响应多数在百毫秒内回来，骨架先画出来的话，
   * 用户看到的是一块灰条闪一下就没了——比不显示骨架更糟（像页面抽了一下）。
   * `both` 填充让它在延迟期间停在 `from`（opacity 0），所以**200ms 内到达的数据
   * 根本不会让它露面**；慢的时候才正常显示。这条比"每个取数点都写一个 setTimeout"可靠：
   * 一处生效、不依赖谁记得写。
   */
  animation: shimmer 1.4s linear infinite, sk-appear 0s linear 200ms both;
}
@keyframes sk-appear { from { opacity: 0; } to { opacity: 1; } }
/**
 * 「仍在加载」的兜底：**延迟 8 秒**才显形，同样用 `both` 停在 opacity 0。
 * 慢或断网时骨架会一直转，用户分不清"在加载"还是"坏了"——这句把两者分开。
 * 8 秒的取法：正常最慢的接口（资产健康那 8 次全表 COUNT）线上冷启约 2.3 秒，
 * 留三倍余量；再短会在正常慢请求上误报。
 */
.sk-slow { display: block; margin-top: 8px; font-size: var(--font-size-sm); color: var(--dim-2); animation: sk-appear 0s linear 8s both; }
.sk-title { width: 120px; height: 18px; margin: 4px 0 18px; }
.sk-ctl { display: inline-block; width: 112px; height: 30px; margin-right: 8px; }
.sk-th { height: 10px; width: 60%; }
.sk-td { height: 11px; }
/*
  骨架屏旁边那句"正在加载…"。
  只有灰条 + 微光动效时，用户分不清"在加载"还是"页面坏了"。跟 `.sk-title` 并排。
*/
/* 名册表：行高压缩。表格是这页的主要工作面，一屏多放两行比多留白有用。 */
#entTableHost tbody tr:not(.expand-row) > td { padding-top: 6px; padding-bottom: 6px; }

.sk-note {
  margin-left: 10px; color: var(--dim-2); font-size: var(--font-size-sm);
}
@keyframes shimmer {
  to { background-position: -200% 0; }
}
@media (prefers-reduced-motion: reduce) {
  /* 只去掉无限微光，**保留那条 200ms 出现延迟**：闪一下对减动效的用户更难受
     （一块灰底突然出现又消失），而这条延迟本身不是动效、只是一次延时显形。 */
  .sk { animation: sk-appear 0s linear 200ms both; }
}

/* 分数构成明细 */
.parts { list-style: none; margin: 6px 0 0; padding: 0; font-size: var(--font-size-sm); }
.parts li { display: flex; justify-content: space-between; padding: 3px 0; color: var(--dim); }
.parts li b { color: var(--text); font-variant-numeric: tabular-nums; }
.parts li.total { border-top: 1px solid var(--border); margin-top: 5px; padding-top: 6px; color: var(--text); font-weight: 600; }

/* --------------------------- 进度/条形 --------------------------- */

.bars { display: flex; flex-direction: column; gap: 7px; }
.bar { display: flex; align-items: center; gap: 9px; font-size: var(--font-size-sm); }
.bar .n { color: var(--dim); min-width: 64px; }
.bar .track { flex: 1; height: 6px; background: var(--color-fill-3); border-radius: var(--radius-pill); overflow: hidden; }   /* Arco Progress：胶囊 + fill-3 轨道 */
/* `display:block` 不能省：填充是 `<span>`，而 inline 元素**忽略 width/height**——
   写成 inline 时轨道是一条空的浅灰线、fill 宽度完全不生效（浏览器实测：
   99.7% 与 2% 画出来一模一样）。这个坑只有看图才发现，所以记在这里。 */
.bar .fill { display: block; height: 100%; background: var(--accent); border-radius: var(--border-radius-medium); }
.bar .c { color: var(--dim-2); min-width: 34px; text-align: right; font-variant-numeric: tabular-nums; }

/* 名册页把比例条做成链接（点一条即按该来源筛）。条变成链接后有两件事要补：
   全局 `a:hover` 会给整条加下划线——一条横线穿过数字和条形，很脏；
   而条本身没有可悬停的反馈，"能不能点"就没有线索。 */
a.bar { color: inherit; text-decoration: none; }
a.bar:hover { text-decoration: none; }
a.bar:hover .n { color: var(--accent-text); }
a.bar:hover .fill { filter: brightness(.9); }
a.bar:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--border-radius-medium); }

/* ------------------------------ 事件流 ------------------------------ */

.ev { display: flex; gap: 11px; padding: 8px 0; border-bottom: 1px solid var(--border); font-size: var(--font-size-table); }
.ev:last-child { border-bottom: 0; }
.ev .t { color: var(--dim-2); font-size: var(--font-size-sm); white-space: nowrap; min-width: 92px; font-variant-numeric: tabular-nums; }
.ev .ty { font-size: var(--font-size-sm); padding: 1px 7px; border-radius: var(--radius-pill); white-space: nowrap; height: 19px; align-self: flex-start; }
.ty.placement_new { background: var(--accent-weak); color: var(--accent-text); }
.ty.activity_surge { background: var(--mid-weak); color: var(--mid-text); }
.ty.activity_stall { background: var(--low-weak); color: var(--low-text); }
.ty.org_matched { background: rgb(var(--info-weak)); color: rgb(var(--info)); }
.ty.overlap_spotted { background: var(--high-weak); color: var(--high-text); }
/* 走势类：持续投入的证据，比单次加量更有说服力，所以用主色（与 placement_new 一致的最强档） */
.ty.activity_rising { background: var(--accent-weak); color: var(--accent-text); }


/* ══════════════════ 栅格 / 布局 / 间距：参照常量══════════════════
 * 这三项在 Arco 那边是**组件**（Grid / Layout / Space），而本应用是手写 SPA，没有组件可挂。
 * 所以这一节落的不是组件，是**照它的一套常量与规则，让以后新增的东西有地方对齐**：
 *
 * ① 栅格：**24 列**，槽宽 **16px**（`.arco-col-N` 最大到 24，自本地 Arco 产物）。
 *    本应用不引 24 列系统（手写布局用不上），但两件跟着它走：
 *      · 栅格**槽宽一律 16**（`.cards` / `.stats` / `.grid-2` 已统一，紧排的 `.cards.strip` 用 8）；
 *      · 多列容器的最小宽度**用刻度值**，别再拍 146 / 210 这类魔法数。
 *
 * ② 断点：**576 / 768 / 992 / 1200 / 1600**（Arco 的 sm/md/lg/xl/xxl，同源）。
 *    CSS 变量进不了 media query，所以断点只能写字面量——那就**只写这五个**。
 * 归一的四处旧值：1180→1200、1080→992、900→992、720→768。
 *    顺带说明 1080→992 是**提前折叠**（1000~1080 宽时两列只剩各自 ~390px，本来就挤），不是退步。
 *
 * ③ 间距：Arco 的 Space 尺寸档是 mini 4 / small 8 / medium 16 / large 24；
 *    我们的 `--space-1..10` = 4/8/12/16/20/24/32/40 已覆盖它们。**规则是三个角色分开**：
 *      · **区块之间**（两张面板、两个分组）→ 24（`--space-6`）
 *      · **容器之内**（面板内距、卡片内距）→ 16（`--space-4`）
 *      · **控件之间**（一排按钮/输入框）→ 8（`--space-2`）
 *    排序依据是"越外层的留白越大"，所以 24 > 16 > 8 不能反过来——反了就会出现
 *    "卡片之间比卡片里面还挤"这种一眼能看出但说不出哪里怪的样子。
 *    文件里仍有 1/2/3/6/7/9/10/14px 这类非刻度留白（历史遗留），**新增的一律取刻度**；
 *    存量按"改到哪页顺手收哪页"处理，不为了统一而全站重排（那是大改版的活）。
 */

/* ------------------------------ 杂项 ------------------------------ */

/**
 * 空态（按 Arco Empty 的解剖对齐）。
 *
 * Arco 的结构是「居中容器 + 图位 + 说明」：`.arco-empty-image` 48px、说明 14px、上下 10px。
 * 这里只借**解剖与间距**，两处刻意不照搬，理由都写在下面。
 */
.empty { padding: 32px 16px; text-align: center; color: var(--dim-2); font-size: var(--font-size-base); }
/**
 * 图位。**不引入插图**：Arco 那套插画在数据密集的后台里会占掉一整行表的高度，
 * 收益不成比例。留一个行内图标位，要加图标时加在这里，别再各自手写。
 *
 * ⚠️ 必须是**直接子元素**（`.empty > .ico`）：`.ico` 是图标系统给**每一个**图标挂的类
 * （见 `icons.js`），所以 `.empty .ico` 会连**空态里按钮/链接内部的图标**一起命中，
 * 把它变成 `display: block` 顶出正文流——症状是按钮只剩文字那么宽、图标溢出到按钮外面
 * （2026-10-13 空态里加了个带图标的「重试」按钮，就是这么坏掉的）。
 */
.empty > .ico { display: block; margin: 0 auto 8px; color: var(--dim-2); }
/**
 * **颜色刻意不照搬**：Arco 说明文字用 `gray-5`（169,174,184），白底上约 2.6:1。
 * 空态那句话是用户真的要读的内容（"为什么这里是空的、我该做什么"），不是装饰，
 * 所以继续用 `--dim-2`（gray-6，约 3.4:1）——这条比"和 Arco 逐像素一致"重要。
 */
/**
 * 空态里的"下一步"动作。**必须是块级**：`inline-block` 只会让链接跟说明挤在同一行
 * （截出来是"这里还没有数据去看看怎么开始"，两句读成一件事）。
 */
.empty .act { display: block; margin-top: 12px; }
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
@media (max-width: 992px) { .grid-2 { grid-template-columns: 1fr; } }

/**
 * 描述列表（`<dl>`， 按 Arco Descriptions 对齐）。
 *
 * Arco 的取值：label `color-text-3 / font-weight 500 / 不换行`，value `color-text-1 / 400`，
 * 每项下方 12px（`.arco-descriptions-item { padding: 0 4px 12px 0 }`）。
 * 我们原先是 `gap: 6px 12px` + 只在别处覆盖宽度（88px / 抽屉里 76px），
 * 结果同一份 `<dl>` 在不同容器里行距与标签色不一致——这里按 Arco 收一次。
 */
.dl { display: grid; grid-template-columns: 88px 1fr; gap: 12px; font-size: var(--font-size-table); }
.dl dt { color: var(--dim-2); font-weight: 500; }
.dl dd { color: var(--text); }
/* 有序动作清单（详情页「建议动作」）。**不要复用 .dl** —— 那是给 <dl> 的两列网格
   （grid-template-columns: 88px 1fr），拿来做 <ol> 会把每条动作挤进 88px 的第一列
   （2026-09-29 实测：文案被压成一条竖着的窄条）。 */
.action-list { margin: 0; padding-left: 1.5em; font-size: var(--font-size-table); line-height: 1.65; }
.action-list li { margin-bottom: 6px; }
.action-list li:last-child { margin-bottom: 0; }
.action-list b { font-weight: 600; }
.dl dd { margin: 0; }
/* 值里的长串（十几个号码 / 长邮箱 / 长地址）必须在格子里断行：字段表是 `minmax(0, 1fr)`
   的网格，值不断行会把整列撑宽，在抽屉的窄栏里就是一条横向滚动条 */
.dl dd { overflow-wrap: anywhere; }
/*
  两列紧凑键值。`.dl` 的 dt 只有 88px、值又短（等级／所有制／科室／规模），
  一行一项时右侧是一大片留白 —— 信息密度低、抽屉显得又长又空。
  改成一行两组 `dt dd dt dd`；窄屏退回单列（两列会把值挤断）。
*/
/* 标签列 88px：与其余几张字段表同一个值（原来这张是 78px，标签比别处左偏 10px） */
.dl.dl-tight { grid-template-columns: 88px minmax(0, 1fr) 88px minmax(0, 1fr); gap: 10px 14px; }
@media (max-width: 560px) {
  .dl.dl-tight { grid-template-columns: 88px 1fr; }
}
/*
  三列键值（Arco Descriptions 的默认 `column` 就是 3）。详情页顶部那条「身份」有五项
  （所有制／地区／科室／规模／成立），两列会排成三行、每行各留一大截空白；三列刚好两行。
  窄屏逐级退回两列 / 一列 —— 与 `.dl-tight` 同一个退化点，不另立门槛。
*/
.dl.dl-trio { grid-template-columns: 56px minmax(0, 1fr) 56px minmax(0, 1fr) 56px minmax(0, 1fr); gap: 10px 14px; }
/* 身份带里那排徽章（定点 / 等级 / 官网）的**标签列**：与上面键值带的标签列对齐
   （`56px` + 14px 间距取自 .dl-trio 的网格），读起来是"标记：这几个徽章"，
   而不是上一条字段的续行。 */
.dl-badges { display: flex; align-items: baseline; gap: 14px; margin-top: 8px; }
.dl-badges-t { flex: none; width: 56px; color: var(--dim-2); font-size: var(--font-size-sm); }
@media (max-width: 760px) { .dl.dl-trio { grid-template-columns: 78px minmax(0, 1fr) 78px minmax(0, 1fr); } }
@media (max-width: 560px) { .dl.dl-trio { grid-template-columns: 88px 1fr; } }
/*
  「为什么是优先客户」里的分数列。**定宽是它的全部意义**：几行的加分竖着对齐，
  才好一眼比出哪一项贡献大。原来写成「+24　搜索侧判定…」，分数和说明挤在一起对不齐。
*/
.pts { display: inline-block; min-width: 34px; font-weight: 600; color: var(--mid-text); }
.pts.big { min-width: 0; font-size: 16px; margin-right: 2px; }

.modal { position: fixed; inset: 0; background: rgba(var(--gray-10), .6);   /* Arco --color-mask-bg = rgba(29,33,41,.6) */ display: flex; align-items: center; justify-content: center; z-index: 50; }
.modal .box { background: var(--card); border-radius: var(--radius); padding: 24px; width: 384px; box-shadow: var(--shadow-3); }
.modal h3 { margin: 0 0 5px; font-size: var(--font-size-lg); font-weight: 500; }   /* Arco Modal title 16px/500 */
.modal p { margin: 0 0 14px; color: var(--dim); font-size: var(--font-size-table); }
.modal input { width: 100%; margin-bottom: 11px; }
.modal .actions { display: flex; gap: 8px; justify-content: flex-end; }
/* 口令弹层里的两个工具按钮（随机生成 / 复制）：单独一行左对齐，不跟「取消/确定」混在一起——
   混在一起时用户会以为「随机生成」也是一种"确定"。 */
.pw-tools { display: flex; gap: 8px; margin: -3px 0 9px; }

/* 表单弹层的字段（联系人姓名/职务/电话；设目标的四个数字）。标签在上、控件在下：
   标签左置时列宽会随最长标签抖动，而这里字段少、竖向本来就富余。 */
.dlg-fields { display: flex; flex-direction: column; gap: 9px; margin-bottom: 4px; }
.dlg-field { display: block; }
.dlg-field > span { display: block; font-size: var(--font-size-sm); color: var(--dim); margin-bottom: 3px; }
.dlg-field input, .dlg-field textarea { width: 100%; }
.dlg-field textarea { resize: vertical; min-height: 62px; }

/* 成员表「活跃」列：上排＝最近跟进，下排＝登录与设备数。
   下排走 note（更小更淡）——它是补充信息，跟上排同权重会让人以为两件事同等重要。 */
.act-main { line-height: 1.35; }
.act-sub { line-height: 1.3; }

/* 团队页成员列表的「操作」列：次要动作平时不显，悬停该行或键盘聚焦进来才出现。
   为什么这么做：四个按钮常显时 6 行就是 20 个按钮，「停用」这种破坏性和「重置密码」
   同权重，一屏全是按钮而真正要看的信息（谁卡住了）被淹掉。
   **必须带 `:focus-within`**——只用 hover 的话键盘用户 Tab 进去什么都看不到
   （`.rename-btn` 那套只处理了单个按钮，这里是整组）。 */
.row-actions { opacity: 0; transition: opacity var(--motion-fast) var(--ease); }
tr:hover .row-actions,
.row-actions:focus-within { opacity: 1; }
/* 触屏与窄屏没有 hover：不让操作彻底消失 */
@media (hover: none) {
  .row-actions { opacity: 1; }
}

/* 成员表的「待处理」摘要行（未配范围 / 7 天没跟进 / 已停用）。
   数字做成可点的筛选链接——不能点的话就只是三个读数，看完还得自己回去找那几行。 */
.sum-row td { background: var(--card-2); padding-top: 6px; padding-bottom: 6px; }
.sumbadge {
  display: inline-block; margin-right: 8px; padding: 1px 8px;
  border: 1px solid var(--border); border-radius: var(--border-radius-large);
  font-size: var(--font-size-sm); color: var(--text); text-decoration: none;
}
.sumbadge b { font-weight: 600; }
.sumbadge:hover { border-color: var(--accent-text); color: var(--accent-text); }
/* 当前生效的筛选：实心一档，让人一眼知道"表里为什么只剩这几行" */
.sumbadge.active { background: var(--accent-weak); border-color: transparent; color: var(--accent-text); font-weight: 600; }
.sumbadge.off { color: var(--dim-2); border-style: dashed; }

/* 已停用账号那一组：计数在按钮上，展开后是正常行 */
.more-row td { background: var(--card-2); }

/* 折叠起来的行。用 class 而不是 `hidden` 属性——作者样式里的 display 会盖掉 UA 的 [hidden] */
.row-hidden { display: none; }

/* 对某些角色"不适用"的格子（不受限的主管、客户运营的名下与范围）：淡一档，
   读的人一眼能分清这是"这一格对他没有含义"而不是"数据缺失" */
td.na { color: var(--dim-2); font-size: var(--font-size-sm); }

/* 可折叠的说明。原生 <details>：键盘可用、零 JS，不自己造开关 */
details.help { display: inline-block; font-size: var(--font-size-sm); color: var(--dim); }
details.help > summary {
  cursor: pointer; color: var(--dim); border-bottom: 1px dotted var(--border-strong);
  display: inline-block; list-style: none;
}
details.help > summary::-webkit-details-marker { display: none; }
details.help > summary::before { content: 'ⓘ '; }
details.help > summary:hover { color: var(--accent-text); border-bottom-color: var(--accent-text); }
details.help[open] > summary { color: var(--accent-text); }
details.help > div {
  margin-top: 6px; padding: 8px 10px; background: var(--bg);
  border-left: 2px solid var(--border-strong); line-height: 1.6; max-width: 760px;
}

/* 角色下拉（成员表「角色」列）。做成下拉而不是"点一下就改"的按钮，见 app.js 的注释。
   保留一圈可见边框：**它是个能改的控件，不是一段文本**——常显的按钮就是为了让人看得见，
   所以这里不能为了"轻"而把边框去掉（改权限的入口藏起来是另一种问题）。 */
select.role-pick {
  padding: 2px 4px; max-width: 112px;
  font-size: var(--font-size-table);
  border-color: var(--border); background: var(--card); cursor: pointer;
}
select.role-pick:hover { border-color: var(--border-strong); }
.msg2 { min-height: 17px; font-size: var(--font-size-sm); color: var(--high-text); margin-bottom: 8px; }

.back { font-size: var(--font-size-table); color: var(--dim); }
.back:hover { color: var(--text); }

/* ==========================================================================
   投流主表（地区 × 科室 × 平台）
   --------------------------------------------------------------------------
   两个要求决定写法：① 平台列会随接入渠道增加（已 4 个、还会多），必须横向可滚；
   ② 地区/科室是行身份，滚到右边还得看得见 —— 所以前两列做 sticky。
   region-row 是小计行，用浅底色把它和明细行分开，避免把小计当明细读。
   ========================================================================== */

table.matrix td.sticky-l,
table.matrix th.sticky-l {
  position: sticky;
  left: 0;
  background: var(--card);
  z-index: 2;
  white-space: nowrap;
}
table.matrix td.sticky-l2,
table.matrix th.sticky-l2 {
  position: sticky;
  left: 78px;
  background: var(--card);
  z-index: 2;
  box-shadow: 1px 0 0 var(--border);
}
table.matrix thead th.sticky-l,
table.matrix thead th.sticky-l2 {
  background: var(--card-2);
  z-index: 4;
}
table.matrix th.sticky-l { min-width: 78px; }
table.matrix th.sticky-l2 { min-width: 120px; }
/**
 * 矩阵的数据格**居中**。
 *
 * 全局 `.num` 是右对齐（对机构列表、金额表那些"位数不齐、要比较大小"的列是对的，
 * 右对齐能让个位对齐、差值一眼可见）。但矩阵不一样：每格都是 1-2 位的小整数，
 * 右对齐会让数字贴到列右缘，与**居中的表头**错位——扫一列时眼睛要在两个轴上跳。
 * 居中之后"表头—数字"同轴，扫列快得多。
 *
 * 只作用于 `table.matrix`（选择器权重高于全局 `.num`），不影响其它表的数字列。
 */
table.matrix th.num,
table.matrix td.num { text-align: center; }

table.matrix th { white-space: nowrap; }

table.matrix tr.region-row > td {
  /* 小计行原来用 card-2(#fbfcfd) 做底，跟白底几乎看不出差别，扫读时容易把小计当明细读。
     换成明显一档的灰底 + 上边框，三层粒度（地区小计 / 科室 / 总计）才分得开。 */
  background: var(--row-sum);
  font-weight: 600;
  border-top: 1px solid var(--border-strong);
}
table.matrix tr.region-row > td.sticky-l,
table.matrix tr.region-row > td.sticky-l2 {
  background: var(--row-sum);
}
table.matrix td.spec { color: var(--dim); padding-left: 16px; }
table.matrix tr.grand > td {
  background: var(--accent-weak);
  font-weight: 600;
  border-top: 2px solid var(--border-strong);
}
table.matrix tr.grand > td.sticky-l,
table.matrix tr.grand > td.sticky-l2 {
  background: var(--accent-weak);
}
/* 空格子用淡点表示，避免一片 0 把眼睛吸走；列头的 BR / 弱 角标 */
table.matrix .z { color: var(--dim-2); opacity: 0.55; }
table.matrix .br {
  /* 独占一行，不再跟平台名挤成一条长列名——列名一长，列就被撑宽，横向滚动更早出现 */
  display: block;
  margin: 3px 0 0;
  padding: 0;
  background: none;
  color: var(--dim-2);
  font-size: var(--font-size-sm);
  font-weight: 500;
  letter-spacing: 0.2px;
}

/* 主表下钻：可点格子
   --------------------------------------------------------------------------
   颜色语义（2026-09-15 调整）：原先「可点」用主色绿表示，但绿色在这个产品里已经表示
   「付费位/正向」，两种含义打架，扫读时读不出哪个是业务信号。
   现在「可点」只用形状线索表达（等宽数字下的虚线 + 悬停底色 + 焦点环），
   颜色留给业务语义（付费位/弱信号/公立）。
   格子是真 <button>，不是带点击回调的 td —— td 不可聚焦，键盘到不了下钻。 */
table.matrix td.cell { padding: 0; }
button.cell-btn {
  display: block;
  width: 100%;
  padding: 9px 11px;
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  text-align: right;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
  border-radius: 0;
}
button.cell-btn b {
  font-weight: 600;
  border-bottom: 1px dashed var(--border-strong);
  padding-bottom: 1px;
}
button.cell-btn:hover { background: var(--bg); }
button.cell-btn:hover b { border-bottom-color: var(--accent-text); }
button.cell-btn:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}
button.cell-btn.on { background: var(--accent-weak); }
button.cell-btn.on b { border-bottom-style: solid; border-bottom-color: var(--accent-text); }

/* 通用文字按钮（机构名等异步解析入口，没有 href 可给，所以用 button 而不是 a） */
button.link-btn {
  border: 0;
  background: transparent;
  padding: 0;
  font: inherit;
  font-weight: 600;
  color: var(--text);
  text-align: left;
  cursor: pointer;
  border-bottom: 1px solid transparent;
}
button.link-btn:hover { color: var(--accent-text); border-bottom-color: var(--accent-text); }
button.link-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--border-radius-small); }

/* 投放分布：主表与下钻并排
   --------------------------------------------------------------------------
   原来下钻是插在主表下方的一整块面板：钻完那一格要往下滚，主表还被顶出视野，
   「从这个格子来的」这层关系看不出来。并排之后点格子右侧就出明细，主表原地不动，
   格局与明细可以对着看。窄屏（<1200px）退回上下堆叠。 */
.dist-split { display: flex; gap: 16px; align-items: flex-start; }
.dist-main { flex: 1; min-width: 0; }
.dist-side {
  flex: 0 0 470px;
  max-width: 470px;
  position: sticky;
  /* 吸顶位置要让开顶栏，否则侧栏会钻到顶栏底下（顶栏 z-index 更高，看着就是被吞掉一截） */
  top: calc(var(--topbar-h) + 6px);
  max-height: calc(100vh - var(--topbar-h) - 78px);
  overflow: auto;
}
@media (max-width: 1200px) {
  .dist-split { display: block; }
  .dist-side { position: static; max-height: none; max-width: none; flex: none; }
}

/* 表格下方那行：长尾平台折叠开关 */
.table-foot {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 16px;
  border-top: 1px solid var(--border);
}
.table-foot .spacer { flex: 1; }
.table-foot button { font-size: var(--font-size-sm); padding: 5px 11px; }

.detail-head {
  display: flex;
  align-items: baseline;
  gap: 10px;
  flex-wrap: wrap;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 10px;
}
.detail-head .spacer { flex: 1; }
.detail-head b { font-size: var(--font-size-base); }
td.plats a { margin-right: 10px; white-space: nowrap; }

/* 机构类型标签（下钻名单里区分公立/付费/疑似） */
/**
 * 行内标签（`.tag`）与徽标（`.badge`）、平台标（`.plat`）、号码形态（`.pk`）**同一个形状：胶囊**。
 *
 * 2026-10-13 统一：此前一对一行里会同时出现两种形状（机构线索「电话」格：`.pk` 手机/共用 是 6px 圆角，
 * 隔壁「重度投放」`.badge` 是胶囊；「投放证据」格里 `.plat` 与 `.badge` 也一样）——
 * 一行里两种圆角，看着像两套组件。定下来的规则是：**行内芯片一律胶囊**，
 * "状态 / 资料"这层区分交给颜色（`--high/--mid/--low/--accent` 那几档底色），不再靠形状分。
 * 圆角矩形留给**块级容器**（`.panel` / `.card` 的 12px）。
 */
/* 高度按 Arco Tag 的 size-small 定死（20 / 行高 18 / 12px）：与 `.badge`、`.pk` 同档——
   原来它没写高度，行盒给到 18，同一个色片家族里就它矮 2px。
   `vertical-align: 1px` 是行内落在文字基线旁时的高低微调，与高度无关，保留。 */
.tag { display: inline-block; margin-left: 6px; padding: 0 5px; height: 20px; line-height: 18px; box-sizing: border-box; border-radius: var(--radius-pill); font-size: var(--font-size-sm); font-weight: 500; vertical-align: 1px; }
.tag-public { background: var(--low-weak); color: var(--dim-2); }
/**
 * 「归属 / 阶段」格：两行 —— 上面两枚状态标签（归属 + 阶段），下面"最后一次动过是多久前"。
 *
 * 为什么要两行：原来三个数（谁的单子 / 走到哪一步 / 放了多久）用 ` · ` 串成一行，
 * 扫描时读不出来；而放多久正是决定"现在要不要打"的那个数，它值得独占一行、退到次要层。
 * `align-items: flex-start` 让两枚标签靠左对齐（表格是固定布局，格子比内容宽）。
 */
.own-stage { display: inline-flex; flex-direction: column; gap: 2px; align-items: flex-start; }
.own-stage-top { display: inline-flex; align-items: center; gap: 5px; }
/* `.tag` 自带 `margin-left: 6px`（它在别处是跟在文字后面的），这里间距交给 gap */
.own-stage-top .tag { margin-left: 0; }
.own-stage-when { font-size: var(--font-size-sm); }
/* 「需要动作」那一档：原来在 app.js 里用内联 style 写死了两处（色值一样、写法不同）。
   收成类之后，"未配范围"这类警示标签全站一个样子（2026-09-23）。 */
.tag-high { background: var(--high-weak); color: var(--high-text); }
/* 经营分析「账号」列的角色标记（主管 / 销售 / 客户运营）。
   中性一档：它是身份说明，不是状态——不与旁边「已停用」的警示色抢注意力。
   用户名与角色同名（admin / admin）时前端不渲染这枚标记，免得读成重复。 */
.tag-role { background: var(--low-weak); color: var(--dim); }
.tag-paid { background: var(--accent-weak); color: var(--accent-text); }
.tag-likely { background: var(--mid-weak); color: var(--mid-text); }

/**
 * 商机表里的阶段选择器：它同时是"当前阶段"的显示处。
 *
 * 原来阶段另有一列彩色 tag（已成交绿 / 已输单灰），而下拉里显示的**本来就是当前阶段**——
 * 同一行画了两遍。删掉那列之后，把颜色线索移到下拉自己身上：
 * 数据属性 `data-cur` 是渲染时就写好的，所以这是纯 CSS，不用 JS 同步。
 * 商机页（app.js 那张表）用同一个类名，一并生效。
 */
select.opp-stage[data-cur='won'] { color: var(--accent-text); font-weight: 600; }
select.opp-stage[data-cur='lost'] { color: var(--dim-2); }
/* 名册所有制标记（公立/民营）。与上面 pageTag 的「目录条目/主题页」不是同一维度，故另起类名 */
/**
 * 所有制三档要能**扫列一眼分开**（公立 / 民营 / 民营（集团））。
 *
 * 原来公立是"淡灰"、民营是"淡琥珀"：在 12px 的密集表里两片浅底读起来一个样，
 * 而这一列的意义就是"公私一眼分"（民营是我们的客户、公立是本地的对标标杆）。
 *
 * 取值按页面已有的那条规矩：**常态安静、例外有颜色，且一个颜色只表示一件事**。
 *   · 公立 = 蓝（`--info-weak` / `--info`，与 `.badge.matched` 同款）—— 结构性信息，不是机会；
 *   · 民营 = 灰（目标客户的常态，标亮等于没标 —— 与「定点」占 71% 不标同一个道理）；
 *   · 民营（集团）= 绿徽章（不变，由 `.badge.paid` 出）—— 那才是值得圈出来的客户。
 * 不采用"民营=橙"：那是给常态上色，且会与「非定点」的琥珀撞色（颜色只表示一件事）。
 */
.tag-own-public { background: rgb(var(--info-weak)); color: rgb(var(--info)); }
/**
 * 带解释的 hover 提示（配 `title`）。
 * 为什么不只写 title：**看不出来能 hover 等于没写**——虚线下划线 + help 光标是唯一提示，
 * 所以凡是有话要补充的地方都套这个类，而不是悄悄挂个 title。
 */
.hlp { border-bottom: 1px dotted var(--border-strong); cursor: help; }

/**
 * 「按钮 + 它的状态徽标」绑成一个视觉单元。抽屉「动作」那行把徽标紧贴按钮时
 * `[重新生成配置][申请中（待批）][发起 AI 诊断]`——`.mini` 只在自己**右侧**留 4px，
 * 徽标于是紧贴右边那个按钮，读起来像「申请中（待批）」在修饰「发起 AI 诊断」（用户截图）。
 * 包一层 inline-flex 并给单元之间留 8px，归属就一眼看清了。
 */
.act-pair { display: inline-flex; align-items: center; gap: 4px; margin-right: 8px; }
.act-pair .mini { margin-right: 0; }

/**
 * 抽屉「一行多个值」（其他电话 / 邮箱）收起来的那截。
 * 默认显示前 3 个 + 「还有 N 个」（`button.link-btn`），点开看全部——15 个号码连排会折四行。
 * 折叠**用 class 而不是 `hidden` 属性**，与 `.row-hidden` 同一个理由：
 * 作者样式里的 `display` 会盖掉 UA 的 `[hidden]`。
 */
.mv-hidden { display: none; }
.mv-toggle { white-space: nowrap; }

/**
 * 抽屉「我方」段里那排动作按钮（开监测 / 跑诊断）。：「我方」与「动作」并成一块
 * 按钮跟在线索状态下面，必须**与上面的字段行拉开一档**，否则贴着像同一个字段的值。
 */
/**
 * 链接式的 mini 按钮要能跟 button 一样上主色。
 * `<a>` 不认 `button.primary`（那条只写给了 button 与 `a.btn`），所以详情抽屉里
 * 「打开诊断」这种"是链接但要是主按钮"的地方会**静默地没变色** —— 看不出报错，只是主次不分。
 */
a.mini.primary { background: var(--accent-strong); border-color: var(--accent-strong); color: #fff; font-weight: 600; }
a.mini.primary:hover { background: var(--accent-hover); }

.ent-acts { margin-top: 10px; display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }
/* 组内已经靠 flex `gap` 分隔，`.act-pair` 自己的右间距在这里去掉，免得翻倍 */
.ent-acts .act-pair { margin-right: 0; }

/* 主表：属于当前账号访问范围的行（范围 = 城市 × 科室，按行上的 data-region/data-spec 匹配） */
.matrix tr.in-scope td:first-child { box-shadow: inset 3px 0 0 var(--accent); }
.tag-own-private { background: var(--low-weak); color: var(--low-text); }

/* 机构名单的平台透视列：十来列要放得下，列头竖排短名 + 数字窄列 */
table th.plat-h {
  font-size: var(--font-size-sm);
  font-weight: 600;
  letter-spacing: 0;
  padding-left: 5px;
  padding-right: 5px;
  white-space: nowrap;
  color: var(--muted, #6b7280);
}
table td.num { white-space: nowrap; }

/* 地区格里的城市输入框：常驻可编辑，但平时无边框——看着像文字，悬停/聚焦才显出是输入框。
   起因：只在「缺城市」时才渲染输入框，导致城市里存了省名的行（黑龙江）看着像已填、
   用户反而没法改成真正的市。常驻输入框让任何一行都能直接改。 */
td.regioncell input.city {
  width: 96px;
  border: 1px solid transparent;
  background: transparent;
  padding: 1px 4px;
  border-radius: var(--border-radius-medium);
  font: inherit;
}
td.regioncell input.city:hover { border-color: var(--border-strong); }
td.regioncell input.city:focus { border-color: var(--accent-text); background: #fff; outline: none; }
td.regioncell input.city::placeholder { color: var(--dim); }
td.regioncell .provtag { margin-right: 4px; }

/* ==========================================================================
   自绘下拉（.sel）
   --------------------------------------------------------------------------
   原生 <select> 的弹层由操作系统绘制，CSS 完全触及不到 —— 筛选栏一排原生下拉
   是界面「手工感」的头号来源。这里换成 button + role=listbox 的自绘实现：
   键盘齐备（Enter/Space 展开、上下键移动、Enter 选中、Esc 关闭并把焦点还给触发器），
   aria 齐备（aria-haspopup / aria-expanded / role=listbox / aria-selected）。
   ========================================================================== */
.sel { position: relative; display: inline-block; }
.sel-trigger {
  display: inline-flex; align-items: center; gap: 6px;
  height: 32px; padding: 0 10px 0 12px;
  font-size: var(--font-size-table); color: var(--text);
  background: var(--card); border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  cursor: pointer; white-space: nowrap;
}
.sel-trigger:hover { border-color: var(--dim-2); }
.sel-trigger[aria-expanded='true'] { border-color: var(--accent-text); }
.sel-trigger:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.sel-trigger .ico { color: var(--dim-2); flex: none; }
.sel-menu {
  position: absolute; z-index: 30; top: calc(100% + 4px); left: 0;
  min-width: 100%; max-height: 320px; overflow: auto;
  background: var(--card); border: 1px solid var(--border); border-radius: var(--border-radius-large);
  box-shadow: var(--shadow-lg); padding: 4px;
}
/**
 * 选项行高按 Arco Select 的 `.arco-select-option` 对齐（`padding: 0 7px` + 36px 行高）。
 * 为什么这条必须对齐：同一页上可能同时出现**岛的 Arco 下拉**与**这套手写下拉**
 * （阶段 chip 的菜单），行高差 2px、横向内距差 3px 时，两个下拉看着像两家的。
 */
.sel-opt {
  display: flex; align-items: center; height: 36px; padding: 0 12px;
  border-radius: var(--border-radius-medium); font-size: var(--font-size-table); color: var(--text);
  cursor: pointer; white-space: nowrap;
}
.sel-opt:hover { background: var(--bg); }
.sel-opt[aria-selected='true'] { background: var(--accent-weak); color: var(--accent-text); font-weight: 600; }
.sel-opt:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* 阶段 chip：点一下推进，右侧小箭头展开全部阶段 */
.stage-cell { display: inline-flex; align-items: center; gap: 2px; }
.chip {
  display: inline-flex; align-items: center; gap: 5px;
  height: 24px; padding: 0 9px; border-radius: var(--radius-pill);
  border: 1px solid var(--border); background: var(--card);
  font-size: var(--font-size-sm); color: var(--dim); cursor: pointer; white-space: nowrap;
}
.chip:hover:not(:disabled) { border-color: var(--accent-text); color: var(--text); }
.chip:disabled { cursor: default; }
.chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.chip .dot { width: 6px; height: 6px; border-radius: 50%; flex: none; }
.chip-caret {
  height: 24px; width: 20px; padding: 0; border: 0; background: none; color: var(--dim-2);
  display: inline-flex; align-items: center; justify-content: center; cursor: pointer; border-radius: var(--border-radius-medium);
}
.chip-caret:hover { background: var(--bg); color: var(--text); }
.chip-caret:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.chip-menu { min-width: 112px; }
.undo {
  border: 0; background: none; color: var(--accent-text); font: inherit; cursor: pointer;
  text-decoration: underline; padding: 0 2px; border-radius: var(--border-radius-small);
}
.undo:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* 长列表的行区分度
   --------------------------------------------------------------------------
   行高钉到 42px 齐平、171 行只靠 hover 区分时，横向扫读很容易串行。
   隔行给一层几乎看不见的底色（不是明显的斑马纹，只是让眼睛有个参照）。 */
#orgRows tr:nth-child(even) td { background: #fcfdfe; }

/* 加载失败：不能只说"失败了"，得给出下一步 */
/**
 * 异常/失败块按 Arco Result 的解剖对齐：容器 `padding: 32px 32px 24px`
 * 标题 14px/500 居中、说明 14px（text-2）居中、动作与说明之间 16px。
 * 我们本来就是"居中 + 纵向排列"，这轮只是把间距与字重对齐；
 * **不引它的 92×92 圆形插图**——理由与空态同一条：数据密集的后台里它占掉一整行，
 * 收益不成比例（Result 那套是给"表单提交成功/404"这类独立页面用的）。
 * 标记无需改动：第一个 `<p>` 当标题、`.note` 当说明、`button` 当动作。
 */
.errbox { display: flex; flex-direction: column; align-items: center; gap: 16px; padding: 32px 24px; }
/**
 * 失败块被放进**表格空位**（`.empty`）时去掉自己的内距：`.empty` 已经有 32px 16px，
 * 两层层叠会变成一大块空白，把"一句人话 + 重试"顶散。取值仍走 `.errbox` 那一处（= Arco
 * `Result` 的解剖：间距 16、标题 14px/500 text-1、说明 14px text-2，见第五批对照）。
 * 用 `.empty .errbox`（后代）而不是 `>`：岛的 `RawHtml` 中间还有一层包裹 div，
 * 直接子选择器在这里匹配不到（曾因此让标题退回继承 `.empty` 的 --dim-2）。
 */
.empty .errbox { padding: 0; }
.errbox p { margin: 0; color: var(--color-text-1); font-size: var(--font-size-base); font-weight: 500; }
.errbox p.note { color: var(--color-text-2); font-weight: 400; }
.errbox button { display: inline-flex; align-items: center; gap: 6px; }
/* 失败框里那截收起的原始异常：默认不露脸，报障时展开就能复制 */
.errbox details.fold { width: 100%; max-width: 680px; text-align: left; }
.errraw { margin: 0; font-size: 12px; color: var(--color-text-2); white-space: pre-wrap; word-break: break-all; }

/* 版本哨兵：跑着的标签页发现线上前端换了就提示刷新（见 app.js 的 checkBuild）。
   浮在底部居中，不占文档流、不推挤版面；深色底会与 .mini 的描边形态打架，所以走白底。 */
/**
 * 「有新版本，刷新后生效」这条提示。
 *
 * 位置改到**右上角、顶栏之下**：原来在底部居中（`bottom: 22px; left: 50%`），
 * 正好压在翻页器那一行上 —— 提示是"顺手刷新一下"，却挡着人翻页这件正事。
 * 右上角是这一屏唯一长期空着的位置：正文从顶栏下面开始，这一条又不进布局流。
 *
 * 两处细节：
 *   · `top: calc(var(--topbar-h) + 10px)` 而不是贴顶 —— 顶栏右端是身份下拉（名下 N 家），
 *     压住它比压翻页器更糟；
 *   · `right: 16px` 与顶栏内距对齐，不与右边缘齐平（齐平看着像被切掉）。
 */
.verbar {
  position: fixed; z-index: 80; top: calc(var(--topbar-h) + 10px); right: 16px;
  display: flex; align-items: center; gap: 10px; padding: 8px 10px 8px 16px;
  border-radius: var(--radius-pill); background: var(--card); color: var(--text);
  border: 1px solid var(--border); box-shadow: var(--shadow-2); font-size: var(--font-size-table);
}
.verbar .spacer { width: 6px; }

/* 侧栏信息块：两个菜单项下面原本一大片空白，把空白换成信息 */

/* 控件高度统一到 32px
   --------------------------------------------------------------------------
   实测原先控件高度是混的：阶段下拉 27px、负责人输入框 29px、按钮 30px 出头。
   高度不齐会让一整排控件看起来"手工拼的"，这是观感标准化里最便宜的一刀。 */
.filters input[type='search'],
.filters button,
/* 固定高度的按钮必须自己居中，不能靠继承的行高：
   body 的 line-height 是 1.6（14px → 22.4px），而 32px 高 − 上下 padding 14px − 边框 2px = 内容盒只有 16px，
   行盒比内容盒高，浏览器把行盒顶端贴到内容盒顶端，文字于是比盒中心低 2.3px（实测盒中心 88、文字中心 90.3）。 */
.page-head button {
  height: 32px;
  display: inline-flex; align-items: center; justify-content: center;
  line-height: 1.2;
}
/* 图例按钮是圆：.page-head button 的 height:32px 优先级比 .legend-btn 高（类+类型 > 类），
   会把 24×24 撑成 24×32 的椭圆——所以这里要用同级选择器写回来。 */
.page-head .legend-btn { width: 24px; height: 24px; padding: 0; }
/* 期间选择器（经营分析）与页头按钮同高。
   不写的话它是 38px：input 的 7px 上下内距 + 1.6 行高（14 → 22.4）+ 边框 2px，
   比旁边的 h1 和按钮都高一头，页头看着就不齐。 */
.page-head input[type='month'] {
  height: 32px; width: 150px; padding-top: 0; padding-bottom: 0;
}
.filters input[type='search'] { padding-top: 0; padding-bottom: 0; }
.filters label.note { height: 32px; }
.filters input[type='checkbox'] { height: auto; }

/* ---------------- 地区三级选择面板（省 / 市 / 区） ----------------
   为什么不用 <select> 做三级联动：页面里 enhanceSelects 会把原生 select 整个替换成自绘组件，
   动态改选项非常别扭；而区县有 3407 个，可搜索的列表本来就比下拉好用。
   每点一级即时落库，于是「能到市就到市、不能就到省」是自然结果，不需要额外的确认动作。 */
td.regioncell button.region-btn {
  border: 1px solid transparent;
  background: transparent;
  padding: 2px 6px;
  border-radius: var(--border-radius-medium);
  font: inherit;
  color: inherit;
  cursor: pointer;
  text-align: left;
  max-width: 190px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
td.regioncell button.region-btn:hover { border-color: var(--border-strong); }
td.regioncell button.region-btn.empty { color: var(--dim); }
td.regioncell button.region-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.geo-panel {
  position: fixed;
  z-index: 60;
  width: 620px;
  max-width: calc(100vw - 24px);
  background: #fff;
  border: 1px solid var(--border-strong);
  border-radius: var(--border-radius-large);
  box-shadow: 0 12px 32px rgba(16, 24, 40, .16);
  padding: 10px;
}
/* flex 而非 grid：藏掉一列时其余列自动铺满，不会留下空格子（只填一级的「全国」、海外国家只到市时会藏列） */
.geo-cols { display: flex; gap: 8px; }
.geo-col { flex: 1 1 0; display: flex; flex-direction: column; min-width: 0; }
.geo-head { font-size: var(--font-size-sm); color: var(--dim); margin-bottom: 4px; }
.geo-search {
  border: 1px solid var(--border-strong);
  border-radius: var(--border-radius-medium);
  padding: 4px 7px;
  font: inherit;
  font-size: var(--font-size-sm);
  margin-bottom: 5px;
  width: 100%;
}
.geo-search:focus { outline: none; border-color: var(--accent-text); }
.geo-list { height: 232px; overflow-y: auto; border: 1px solid var(--border-strong); border-radius: var(--border-radius-medium); padding: 3px; }
.geo-item {
  display: block;
  width: 100%;
  text-align: left;
  border: 0;
  background: transparent;
  font: inherit;
  font-size: var(--font-size-sm);
  padding: 3px 7px;
  border-radius: var(--border-radius-medium);
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.geo-item:hover { background: var(--accent-weak); }
.geo-item.on { background: var(--accent-weak); color: var(--accent-text); font-weight: 600; }
.geo-empty { color: var(--dim); font-size: var(--font-size-sm); padding: 6px 7px; }
.geo-foot { display: flex; align-items: center; gap: 8px; margin-top: 8px; }
.geo-foot .spacer { flex: 1; }

/* 机构名就地改名
   --------------------------------------------------------------------------
   平时低对比（opacity .35）、行悬停或键盘聚焦时才清晰：186 行里常显就是噪声。
   没有做成完全隐藏，是因为「看不见但可点」的靶子容易误触，也让人发现不了这个功能。
   用文字按钮而不是图标：契约禁的是「手绘字形」，文字不算字形，而且「改名」比铅笔图标更不易误解。 */
td .rename-btn {
  margin-left: 6px;
  height: 20px;
  padding: 0 6px;
  font-size: var(--font-size-sm);
  border-radius: var(--border-radius-medium);
  border-color: transparent;
  background: none;
  color: var(--dim-2);
  opacity: 0.35;
  transition: opacity var(--motion-fast) var(--ease), color var(--motion-fast) var(--ease), border-color var(--motion-fast) var(--ease);
}
#orgRows tr:hover .rename-btn,
td .rename-btn:hover { opacity: 1; }
td .rename-btn:hover { color: var(--accent-text); border-color: var(--accent-text); }
td .rename-btn:focus-visible { opacity: 1; outline: 2px solid var(--accent); outline-offset: 1px; }
.rename-wrap { display: inline-flex; align-items: center; gap: 6px; }
.rename-input { width: 240px; height: 28px; font-size: var(--font-size-table); }
.rename-wrap button { height: 28px; padding: 0 10px; font-size: var(--font-size-sm); }

/* 团队页：小号操作按钮 + 阶段分布标签 */
/** 抽屉头部「打开完整页 →」：**文本链接，不带边框**。
    它原来是 `a.mini`（24px 带边框的小按钮），在抽屉头部看着像一个空的输入框；
    而它其实是"去另一个页面"，用链接的形态更准，也不用跟真正的动作按钮抢注意力。 */
.ent-open-full {
  display: inline-block; margin-top: 6px;
  font-size: var(--font-size-sm); color: var(--accent-text); text-decoration: none;
}
.ent-open-full:hover { text-decoration: underline; }
.ent-open-full:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--border-radius-small); }

/* 小号操作按钮：与 32px 那档**同一个形态**（填充 fill-2、无描边），只小一号 ——
   同一产品里两档尺寸一套形态，别再是"大的填充、小的描边"。 */
button.mini {
  font: inherit; font-size: var(--font-size-sm); height: 24px; padding: 0 9px; margin-right: 4px;
  border: 1px solid transparent; background: var(--color-fill-2); color: var(--dim);
  border-radius: var(--border-radius-medium); cursor: pointer;
}
button.mini:hover:not(:disabled) { background: var(--color-fill-3); color: var(--accent-text); }
button.mini.danger { background: none; border-color: var(--low); color: var(--low-text); }
button.mini.danger:hover:not(:disabled) { background: var(--low-weak); color: var(--low-text); }
button.mini:disabled { opacity: .45; cursor: not-allowed; }
/* 「导出记录」是个下载链接，不是按钮，但要跟旁边的小按钮长得一样 */
a.mini {
  font: inherit; font-size: var(--font-size-sm); height: 24px; padding: 0 9px; margin-right: 4px;
  border: 1px solid transparent; background: var(--color-fill-2); color: var(--dim);
  border-radius: var(--border-radius-medium); cursor: pointer; display: inline-flex; align-items: center; text-decoration: none;
}
a.mini:hover { background: var(--color-fill-3); color: var(--accent-text); }

/* 数据审计里需要被一眼看见的数字/行：导出、看完整号码 */
.warnnum { color: var(--mid-text); }
tr.warnrow td { background: var(--mid-weak); }
.stagetag {
  display: inline-block; font-size: var(--font-size-sm); padding: 1px 7px; margin: 0 4px 3px 0;
  border-radius: var(--border-radius-medium); background: var(--accent-weak); color: var(--accent-text); white-space: nowrap;
}

/* ---------------------------------------------------------------------------
   我的工作台（销售视角）
   参考主流 CRM 的 rep home：待办清单 / 跟进时间线 / 行内动作 / 我的漏斗。
   样式尽量复用已有 token 与 .card/.mini，避免再造一套视觉语言。
   --------------------------------------------------------------------------- */

/* 按角色收起的入口：触发抓取与全量导出都是内部的事。
   放 CSS 而不是视图里判 amAdmin()——视图可能在 ME 取回来之前就渲染完了，
   那时条件渲染会把按钮藏到下一次重渲染为止。后端两边也各自校验一遍。

   ⚠️ 选择器必须逐个列出角色名。原来只写 'sales'，加客户角色时**这就是最易漏的一处**：
   漏了它，`body[data-view-role='customer']` 不匹配任何规则，扫描与导出按钮会重新出现
   （即使后端会 403，界面上也等于给客户看了一个点了没用的按钮，还暴露了"有导出"这件事）。 */
body[data-view-role='sales'] #btnScan,
body[data-view-role='sales'] #btnExport,
body[data-view-role='customer'] #btnScan,
body[data-view-role='customer'] #btnExport { display: none; }

/* ------------------------------ 客户门户（外部只读） ------------------------------ */
/* 客户只有一页，所以不做侧栏导航的多 tab 形态，改用上下分段。
   与内部页面的关键差别是**没有任何写操作入口**：没有扫描、没有导出全量、没有阶段/负责人。 */
.pf-hero {
  display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap;
  padding: 16px 18px; background: var(--card); border: 1px solid var(--border);
  border-radius: var(--radius); margin-bottom: 16px; box-shadow: var(--shadow);
}
.pf-hero .who { font-size: var(--font-size-lg); font-weight: 650; letter-spacing: -.2px; }
.pf-hero .meta { color: var(--dim); font-size: var(--font-size-sm); }

/* 机会清单：客户最该先看的一段，所以给主色左边框 */
.pf-opp { border-left: 3px solid var(--accent); }
.pf-opp-row { display: flex; align-items: center; gap: 10px; padding: 10px 14px; border-top: 1px solid var(--border); }
.pf-opp-row:first-child { border-top: 0; }
.pf-opp-row .plat { font-weight: 600; min-width: 160px; }
.pf-opp-row .n { color: var(--accent-text); font-weight: 600; font-variant-numeric: tabular-nums; }
.pf-opp-row .why { color: var(--dim); font-size: var(--font-size-sm); }

/* 月度一页纸：屏幕上不显示，打印时**只留它**（见下面 @media print 的规则）。
   客户/销售拿这张纸去开会——它必须自带出处、口径与样本天数，否则脱离系统就没人知道
   这些数字是哪天、什么范围的（外流时也能追到来源）。 */
.pf-onepager { display: none; }
.pf-onepager .op-h1 { font-size: 19px; font-weight: 700; letter-spacing: -.2px; }
.pf-onepager .op-sub { color: var(--dim); font-size: var(--font-size-sm); margin-top: 3px; }
.pf-onepager .op-sec { margin-top: 14px; }
.pf-onepager .op-h2 { font-size: 14px; font-weight: 650; margin-bottom: 4px; }
.pf-onepager .op-li { font-size: 12.5px; line-height: 1.65; }
.pf-onepager .op-note { color: var(--dim); font-size: 11.5px; margin-top: 3px; }
.pf-onepager .op-foot { margin-top: 16px; padding-top: 8px; border-top: 1px solid var(--border); color: var(--dim-2); font-size: var(--font-size-xs); line-height: 1.6; }

/* 开号前体检的结果行（团队页 → 客户账号档，「主管」与「客户运营」共用） */
.ob-row { display: flex; gap: 8px; align-items: flex-start; padding: 5px 0; border-top: 1px solid var(--border); font-size: 12.5px; }
.ob-row:first-child { border-top: 0; }
.ob-i { flex: none; width: 14px; font-weight: 700; }
.ob-i.ok { color: var(--accent-text); }
.ob-i.warn { color: var(--mid-text); }
.ob-i.bad { color: var(--high-text); }
.ob-l { flex: none; width: 130px; color: var(--dim); }
.ob-d { flex: 1; }

/* 并排对比：我为 0 而竞品 >0 的格子要一眼看出来——那就是差距 */
table.pf-cmp td.gap { background: var(--mid-weak); color: var(--mid-text); font-weight: 600; }
table.pf-cmp td.mine { font-variant-numeric: tabular-nums; }
.pf-trend { font-size: var(--font-size-sm); border-radius: var(--radius-pill); padding: 1px 8px; flex: none; }
.pf-trend.rising { color: var(--accent-text); background: var(--accent-weak); }
.pf-trend.falling { color: var(--mid-text); background: var(--mid-weak); }
.pf-trend.flat { color: var(--dim); background: var(--bg); }
.pf-trend.unknown { color: var(--dim-2); background: var(--bg); }

/* 未配置范围的空态要说得清楚，而不是给一个空白页 */
.pf-unset { max-width: 620px; }

/* 打印 / 另存为 PDF：客户会把这个对比表拿去开会。
   不引 PDF 库（Node 侧要么上 puppeteer 要装 Chromium，要么手写 PDF 难维护），
   改成打印友好的样式，让浏览器自己出 PDF——版式可控、零依赖。
   打印时收掉所有导航与操作入口，只留正文。 */
@media print {
  .topbar, .side, .page-head button, .page-head .spacer, .note#flash { display: none !important; }
  /* 打印时**只留一页纸**：其余面板全收掉（"一页装下"靠这里保证，而不是靠碰运气） */
  #view > *:not(.pf-onepager) { display: none !important; }
  .pf-onepager { display: block !important; }
  @page { margin: 12mm; }
  .shell, .layout { display: block !important; }
  .main { padding: 0 !important; }
  .panel { break-inside: avoid; box-shadow: none; }
  .table-wrap { max-height: none !important; overflow: visible !important; }
  thead th { position: static !important; }
  a { color: inherit; text-decoration: none; }
  /* 打印时把 URL 显示出来，纸质件上也能核对来源 */
  .pf-cmp + * a[href^='http']::after { content: ''; }
}

.wb-list { list-style: none; margin: 0; padding: 0; }
.wb-row { display: flex; align-items: center; gap: 12px; padding: 9px 14px; border-top: 1px solid var(--border); }
.wb-row:first-child { border-top: 0; }
.wb-row:hover { background: var(--row-sum); }
.wb-main { flex: 1; min-width: 0; display: flex; align-items: center; gap: 9px; }
.wb-main .org-link { font-weight: 600; color: var(--text); text-decoration: none; max-width: 260px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wb-main .org-link:hover { color: var(--accent-text); text-decoration: none; }
/* 「为什么排在这里」：待办和清单的区别就在这一枚标记上，所以给它颜色 */
.wb-why { font-size: var(--font-size-sm); color: var(--dim); background: var(--bg); border-radius: var(--radius-pill); padding: 1px 8px; flex: none; }
/**
 * **这条刻意不用 Arco 的 danger 档**（对比度）
 *   · 现在这对 `#c0392b` on `#fdecea` = **4.76:1**（过 AA 4.5）
 *   · 换成 `danger-6` on `danger-1`（245,63,63 / 255,236,232）= **3.25:1** → **不过**
 * 这枚是 12px 的小字标签，不是大字号，必须按 4.5 卡。所以"和 Arco 一致"在这里要让位给可读性，
 * 色值保持手调——但**别再有人顺手把它'对齐'掉**，改之前先算一遍。
 */
.wb-why.overdue { color: #c0392b; background: #fdecea; }
.wb-why.signal { color: var(--accent-text); background: var(--accent-weak); }
/* 由头强度：排序已经按强度来，颜色只是让人扫一眼能分辨「这条更值得先打」 */
.wb-why.signal.lv-low { color: var(--dim); background: var(--bg); }
/* 可领池的紫：Arco 的 purple-6/-1（114,46,209 / 245,232,255）未代入复核过，
   现值 `#6b4ff2` on `#f1edff` = 4.55:1（刚过线）。要换先量，别凭"像"就换。 */
.wb-why.pool { color: #6b4ff2; background: #f1edff; }
.wb-act { display: flex; align-items: center; gap: 6px; flex: none; }
.wb-act .mini { display: inline-flex; align-items: center; gap: 4px; text-decoration: none; }

/* 动态条目：比普通待办多两行（话术、出处），所以整体改成上下结构。
   上行 = 机构 + 由头 + 建议动作；中行 = 话术（销售打电话照着说的那句）；
   下行 = 出处（雷达原话，可核对）与操作。 */
.wb-row.sig { display: block; padding: 11px 14px; }
.wb-row.sig .wb-main { flex-wrap: wrap; }
.wb-row.sig .wb-act { margin-top: 9px; flex-wrap: wrap; }
.sig-act { font-size: var(--font-size-sm); font-weight: 600; color: var(--accent-text); flex: none; }
/**
 * 话术块：**这是销售唯一会照着念的那句话**，所以给它引用样式（左侧竖线 + 浅底），
 * 让它一眼和"出处/时间"这类辅助信息区分开；右侧挂一个复制按钮——
 * 要粘到微信/短信里，让浏览器替他选中再自己按 Ctrl+C 太别扭。
 */
.sig-script {
  display: flex; align-items: flex-start; gap: 8px;
  margin-top: 7px; padding: 7px 10px 7px 11px;
  border-left: 2px solid var(--accent); border-radius: 0 6px 6px 0; background: var(--bg);
  font-size: var(--font-size-sm); color: var(--text); line-height: 1.6;
}
.sig-script .txt { flex: 1; min-width: 0; }
.sig-script .copy { flex: none; }
.sig-script b { color: var(--dim-2); font-weight: 500; margin-right: 7px; font-size: var(--font-size-sm); }
/* 时效：排序是按「强度 × 时效」算的，那就得让它看得见——否则"为什么这条排前面"对销售不透明 */
.sig-when { flex: none; }
/* 同机构其它由头的徽章：一个机构一张卡，次要由头降级到这里 */
.sig-extra { font-size: var(--font-size-sm); color: var(--dim); background: var(--card-2); border-radius: var(--radius-pill); padding: 1px 8px; flex: none; }
.sig-handled { font-size: var(--font-size-sm); color: var(--dim); font-weight: 600; flex: none; }
/**
 * 次要操作收进 hover：一屏 20 条 × 5 个常显按钮 = 上百个按钮，主操作（拨打）被淹没。
 * 团队页的 `.row-actions` 是同一个做法，但它选择器写的是 `tr:hover`，这里得给 li 一条。
 */
.wb-row.sig .row-actions { display: inline-flex; align-items: center; gap: 6px; opacity: 0; transition: opacity var(--motion-fast) var(--ease); }
.wb-row.sig:hover .row-actions,
.wb-row.sig .row-actions:focus-within { opacity: 1; }
@media (hover: none) { .wb-row.sig .row-actions { opacity: 1; } }

/* 本周必打的名单：序号自己渲染，所以关掉 ol 的默认编号——否则屏幕上会出现「1. 1」 */
.mc-list { list-style: none; margin: 0; padding: 0; }
.mc-row { display: flex; align-items: flex-start; gap: 10px; padding: 9px 14px; border-top: 1px solid var(--border); }
.mc-row:first-child { border-top: 0; }
.mc-row:hover { background: var(--row-sum); }
.mc-no { flex: none; width: 18px; text-align: right; color: var(--dim); font-variant-numeric: tabular-nums; font-size: var(--font-size-sm); line-height: 1.75; }
.mc-body { flex: 1; min-width: 0; }
.mc-phone { font-variant-numeric: tabular-nums; }
/* 出处是雷达原话（可能带未清洗的 SEO 堆词标题），截断显示，完整内容留给 title 属性 */
.sig-src { margin-top: 4px; font-size: var(--font-size-sm); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* 侧栏角标：未读动态数。等宽数字，两位数时宽度不跳 */
.nav-badge {
  margin-left: auto; min-width: 18px; padding: 0 5px;
  border-radius: var(--radius-pill); background: var(--accent); color: #fff;
  font-size: var(--font-size-sm); font-weight: 600; line-height: 18px; text-align: center;
  font-variant-numeric: tabular-nums;
}

/* 电话点一下就能拨：销售的主战场在电话上 */
.tel { color: var(--text); font-variant-numeric: tabular-nums; }
.tel:hover { color: var(--accent-text); }

/* 掩码电话：点一下才取全号（那一次会记入数据审计）。
   做成本地按钮而不是链接——掩码号拨不通，做成 tel: 链接会让人以为能拨。 */
button.telview {
  display: inline-flex; align-items: center; gap: 5px; white-space: nowrap;
  font: inherit; font-variant-numeric: tabular-nums; color: var(--dim);
  background: none; border: 0; border-bottom: 1px dashed var(--border-strong);
  padding: 0 0 1px; cursor: pointer;
}
button.telview .tel-take {
  padding-left: 5px; border-left: 1px solid var(--border-strong);
  color: var(--accent-text); font-size: var(--font-size-sm); font-weight: 600;
}
button.telview:hover:not(:disabled) { color: var(--accent-text); border-bottom-color: var(--accent-text); }
button.telview:disabled { opacity: .6; cursor: default; }

/* 电话形态徽标：手机能直接找到人，固话要过前台，客服总机基本见不到决策人。
   颜色只用三档冷热度，不加新色相——这一列的目标是"一眼分出来"，不是好看。 */
.telwrap { display: inline-flex; align-items: center; gap: 5px; }
.pk {
  font-size: var(--font-size-sm); font-weight: 500; line-height: 1; padding: 3px 5px; border-radius: var(--radius-pill);
  border: 1px solid var(--border-strong); color: var(--dim); white-space: nowrap;
}
.pk-mobile { color: var(--accent-text); background: var(--accent-weak); border-color: var(--accent-weak); }
.pk-landline { color: var(--text); }
.pk-hotline, .pk-other { color: var(--dim); opacity: .85; }
/* 人工核过：机器扒的号和人打过的号价值不同，这个标记是给销售的信任信号 */
.pk-verified { color: var(--accent-text); font-size: var(--font-size-sm); font-weight: 600; }
/* 共用号：这个号挂在好几家机构上，打过去是外包客服。用警示色（--mid 是既有的"要注意"色） */
.pk-shared { color: var(--mid-text); background: var(--mid-weak); border-color: var(--mid-weak); }
/* 改号入口：**常驻可见（淡）**，而不是 hover 才出现。
   一开始写成 opacity:0 + 行 hover 时显形，看着干净，但"人工改号码"是这套电话体系
   唯一的纠错入口——藏起来的纠错入口等于没有（销售根本不知道能改，就只会继续打错号）。
   淡态不抢号码本身的注意力，hover 时加深。 */
.pkedit {
  display: inline-flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; padding: 0; background: none;
  border: 0; border-radius: var(--border-radius-medium); color: var(--dim); opacity: .45; cursor: pointer;
  transition: opacity var(--motion-fast) var(--ease), background var(--motion-fast) var(--ease);
}
tr:hover .pkedit { opacity: .8; }
.pkedit:hover, .pkedit:focus-visible { opacity: 1; color: var(--accent-text); background: var(--bg); }

/* 跟进记录（谁在什么时候做了什么） */
.tl { list-style: none; margin: 0; padding: 0; }
.tl-row { display: flex; gap: 12px; padding: 8px 14px; border-top: 1px solid var(--border); font-size: var(--font-size-table); }
.tl-row:first-child { border-top: 0; }
.tl-time { color: var(--dim); font-variant-numeric: tabular-nums; flex: none; width: 134px; }
.tl-who { font-weight: 600; flex: none; width: 92px; overflow: hidden; text-overflow: ellipsis; }
.tl-body { min-width: 0; }
.due { font-variant-numeric: tabular-nums; }
.due.overdue { color: #c0392b; }

/* 行内快捷动作浮层（排期 / 记一笔）：就地完成一次跟进，不必进详情页 */
.quick-panel {
  position: absolute; z-index: 60; min-width: 220px;
  background: var(--card); border: 1px solid var(--border-strong);
  border-radius: var(--border-radius-large); box-shadow: 0 12px 32px rgba(16, 24, 40, .16); padding: 10px;
}
.quick-hd { font-size: var(--font-size-sm); font-weight: 600; margin-bottom: 7px; display: flex; gap: 6px; align-items: baseline; }
.quick-opts { display: flex; flex-wrap: wrap; gap: 6px; }
.quick-note { width: 258px; min-height: 62px; }
.quick-foot { display: flex; justify-content: flex-end; margin-top: 7px; }

/* ---------------------- 跨系统绑定（客户账号 ↔ medbuild） ---------------------- */

.link-cell { display: flex; flex-direction: column; gap: 3px; align-items: flex-start; }
.link-cell code { font-size: var(--font-size-sm); color: var(--text); background: var(--bg); padding: 1px 5px; border-radius: var(--border-radius-medium); }
.link-acts { display: flex; flex-wrap: wrap; gap: 4px; }
.note.err { color: #c0392b; }

/* 确认区：整行展开在客户账号那一行下面，不弹窗——换页/重渲染都不会留孤儿 */
tr.draft-row > td { background: var(--row-sum); padding: 0; }
.link-draft { padding: 12px 14px; display: flex; flex-direction: column; gap: 8px; }
.link-draft .filters { gap: 8px; }
.lk-grid { display: grid; grid-template-columns: minmax(240px, 1fr) minmax(320px, 1.4fr); gap: 16px; align-items: start; }
.lk-t { font-size: var(--font-size-sm); color: var(--dim-2); font-weight: 600; margin-bottom: 5px; letter-spacing: .2px; }
.lk-dl { margin: 0; display: grid; grid-template-columns: auto 1fr; gap: 4px 10px; font-size: var(--font-size-sm); }
.lk-dl dt { color: var(--dim); }
.lk-dl dd { margin: 0; color: var(--text); }
.link-draft select { width: 100%; }

/* ---------------------- 团队页下钻 + 批量指派 ---------------------- */

/* 卡片可以点进去看名单（不做死数字）：整块可点，不留"看着像能点其实点了没反应" */
a.card.link { display: block; color: inherit; text-decoration: none; }
a.card.link:hover { border-color: var(--accent-text); text-decoration: none; }
a.card.link .k::after { content: ' →'; color: var(--dim-2); }

/* 阶段标签变成链接时保持原样；数量为 0 的降级成灰字，一眼分清可点与不可点 */
a.stagetag { text-decoration: none; }
a.stagetag:hover { background: var(--card); border: 1px solid var(--accent); color: var(--text); margin: -1px 3px 2px -1px; }
.stagetag.off { background: var(--bg); color: var(--dim-2); }

/* 批量指派的勾选列与选择条 */
.pick-cell { width: 34px; }
.pick-cell input { width: 15px; height: 15px; cursor: pointer; vertical-align: middle; }
#pickBar { margin-top: 8px; padding: 7px 10px; background: var(--accent-weak); border-radius: var(--radius-sm); }
#pickBar b { color: var(--accent-text); }

/* 客户看板：「我的 AI 可见度」（数据来自 geohub，另一台服务器，单独异步取） */
.vis-row { display: flex; flex-wrap: wrap; gap: 20px; }
.vis-item { min-width: 112px; }
.vis-k { font-size: var(--font-size-sm); color: var(--dim-2); }
.vis-v { font-size: var(--font-size-2xl); font-weight: 700; letter-spacing: -.4px; font-variant-numeric: tabular-nums; }
.vis-u { font-size: var(--font-size-sm); font-weight: 500; color: var(--dim-2); margin-left: 2px; }
.vis-bar { display: block; height: 6px; border-radius: var(--radius-pill); background: var(--color-fill-3); overflow: hidden; margin-top: 4px; }
.vis-bar i { display: block; height: 100%; border-radius: var(--radius-pill); background: var(--accent); }
.vis-chips { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-top: 10px; }

/* 竞品表里的「你填的」标记：把客户在 geohub 填过的竞品与雷达发现的区分开 */
.src-badge {
  display: inline-block; font-size: var(--font-size-sm); padding: 0 6px; border-radius: var(--radius-pill);
  background: var(--accent-weak); color: var(--accent-text); white-space: nowrap; vertical-align: 1px;
}

/* 数据体检卡（团队页）：一行一项，左边数字、中间"为什么要管"、右边去看 */
.dh-row { display: flex; align-items: flex-start; gap: 14px; padding: 10px 0; border-top: 1px solid var(--border); }
.dh-row:first-of-type { border-top: 0; }
.dh-n { font-size: var(--font-size-2xl); font-weight: 700; font-variant-numeric: tabular-nums; min-width: 54px; text-align: right; letter-spacing: -.4px; }
.dh-n.dim { color: var(--dim-2); font-weight: 600; }
.dh-n.mid { color: #c0392b; }
.dh-n.good { color: var(--accent-text); }
.dh-main { flex: 1; min-width: 0; }
.dh-label { font-weight: 600; margin-bottom: 2px; }
.dh-act { flex: none; padding-top: 3px; }

/* 电话构成条：一次看清名单整体"能不能打到人"。四档用冷热度分色，
   手机（能直接找到人）是唯一给强调色的那一档——它不是"其中一种"，它是目标。 */
.pm { margin: 4px 0 12px; }
.pm-hd { font-weight: 600; margin-bottom: 7px; }
.pm-bar { display: flex; height: 26px; border-radius: var(--radius-sm); overflow: hidden; border: 1px solid var(--border); }
.pm-seg {
  display: flex; align-items: center; justify-content: center; min-width: 0;
  font-size: var(--font-size-sm); font-variant-numeric: tabular-nums; color: var(--dim);
  text-decoration: none; border-right: 1px solid var(--card); background: var(--bg);
}
.pm-seg:last-child { border-right: 0; }
a.pm-seg:hover { filter: brightness(.96); }
.pm-seg.s0 { background: var(--accent-weak); color: var(--accent-text); font-weight: 600; }
.pm-seg.s1 { background: #eef2f6; color: var(--text); }
.pm-seg.s2 { background: #f6f7f9; color: var(--dim); }
.pm-seg.s3 { background: repeating-linear-gradient(45deg, #f0f2f5, #f0f2f5 4px, #e7eaee 4px, #e7eaee 8px); color: var(--dim-2); }
.pm-n { font-size: var(--font-size-sm); }
.pm-legend { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 6px; }
.pm-lg { display: inline-flex; align-items: center; gap: 5px; font-size: var(--font-size-sm); color: var(--dim); }
.pm-lg i { width: 9px; height: 9px; border-radius: var(--border-radius-small); display: inline-block; }
.pm-lg.s0 i { background: var(--accent-weak); border: 1px solid var(--accent); }
.pm-lg.s1 i { background: #eef2f6; border: 1px solid var(--border-strong); }
.pm-lg.s2 i { background: #f6f7f9; border: 1px solid var(--border-strong); }
.pm-lg.s3 i { background: #f0f2f5; border: 1px dashed var(--border-strong); }

/* 业绩报表里的小表（阶段 / 按人 / 来源转化率）：比主表紧凑，不抢视觉 */
.dh-table { width: 100%; border-collapse: collapse; font-size: var(--font-size-sm); }
.dh-table th, .dh-table td { text-align: left; padding: 5px 8px; border-bottom: 1px solid var(--border); white-space: nowrap; }
.dh-table th { color: var(--dim-2); font-weight: 600; }
.dh-table td.num, .dh-table th.num { text-align: right; font-variant-numeric: tabular-nums; }

/* 投放分布主表：行上的成交/输单标记。
   成交是机构的属性、不是平台的属性，所以标在行（地区 × 科室）上而不是格子里 ——
   运营看这一列是为了回答"哪类机构好卖"，那是雷达该多扫哪里的输入。 */
.deal-tag { display: inline-flex; gap: 6px; margin-left: 8px; font-size: var(--font-size-xs); vertical-align: 1px; }
.deal-tag b.won { color: var(--accent-text); font-weight: 600; }
.deal-tag .lost { color: var(--dim-2); }

/* 配范围面板里的「实时库存」与「哪里有货」点选榜。
   库存那一行要能一眼看出"这个范围是不是空的"，所以数字加粗、警示用 warn 色；
   点选榜是多行小按钮，让它独立成行、不要跟输入框挤在同一条 flex 里。 */
.scope-stock { flex-basis: 100%; margin-top: 2px; font-size: var(--font-size-sm); }
.scope-supply { flex-basis: 100%; margin-top: 6px; }
.scope-supply .stock-row { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-top: 4px; }
.scope-supply .stock-row .note { margin-right: 2px; }
.warn { color: var(--high-text); }

/* ------------------------- AI 可见度体检报告面板 -------------------------
   这块是"给客户看的那份报告"的出口，所以它要一眼看出三件事：有没有报告、
   分数是多少、报告生成得怎么样了。刻意不用进度条：一次诊断几分钟，
   假进度条（按时间猜）比没有更糟——它会在卡住时表现得像正常。 */

.dx-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 10px; }
.dx-score {
  font-size: 30px; font-weight: 700; line-height: 1; font-variant-numeric: tabular-nums;
  color: var(--dim);
}
.dx-score.high { color: var(--accent-text); }
.dx-score.mid { color: var(--mid-text); }
.dx-score.low { color: var(--high-text); }

.dx-run { display: flex; align-items: center; gap: 8px; color: var(--dim); }
/* 转圈：纯装饰，所以 aria-hidden（见 app.js 的 span），并尊重系统的"减少动效" */
.spin {
  width: 13px; height: 13px; flex: 0 0 auto; border-radius: var(--border-radius-circle);
  border: 2px solid var(--border-strong); border-top-color: var(--accent);
  animation: dx-spin .8s linear infinite;
}
@keyframes dx-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  /* 减少动效时不能停成"一个静止的圈"——那看起来像个句号。换成呼吸式明暗 */
  .spin { animation: dx-pulse 1.4s ease-in-out infinite; border-top-color: var(--border-strong); }
  @keyframes dx-pulse { 50% { opacity: .35; } }
}

/* 生成前的如实交代（名字太通用会让分数虚高、缺官网会少一维）。用警示色的描边块，
   不用纯文字 note：这两条是"看报告的人会被误导"的话，值得占点视觉权重。 */
.dx-warn {
  margin-top: 9px; padding: 8px 11px; border-left: 3px solid var(--mid);
  background: var(--mid-weak); border-radius: 0 var(--border-radius-small) var(--border-radius-small) 0;
  font-size: var(--font-size-sm); color: var(--text); display: grid; gap: 5px;
}
.dx-fail {
  margin-bottom: 9px; padding: 8px 11px; border-left: 3px solid var(--high);
  background: var(--high-weak); border-radius: 0 var(--border-radius-small) var(--border-radius-small) 0;
  font-size: var(--font-size-sm);
}
.dx-hist { margin-top: 10px; font-size: var(--font-size-sm); }
.dx-hist summary { cursor: pointer; color: var(--dim); }
.dx-hist ul { margin: 6px 0 0; padding-left: 18px; display: grid; gap: 4px; }


/* ------------------------------ 容量与覆盖矩阵 ------------------------------ */

/**
 * 覆盖矩阵（名册页）。与投流矩阵（`table.matrix`）**刻意不同**：那张是"我们发了什么"、
 * 列是平台；这张是"市场多大 vs 我们碰了多少"、列是科室。所以不复用 `table.matrix` 的
 * 列分组与两行 sticky 表头——那套是为 11 个平台列设计的，搬过来只会有多余的复杂度。
 *
 * 底色沿用 accent 透明度阶梯（与 `.cell-btn.heat-1..4` 同一组值）：同一套语义
 * （越深越多）就该是同一个色，不该为这张表再发明一套色阶。
 */
.cov-top { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 8px; }
.cov-top .spacer { flex: 1; }
.cov-top .lgd { display: inline-flex; align-items: center; gap: 4px; font-size: var(--font-size-sm); color: var(--dim); }
.cov-top .lgd i { width: 12px; height: 12px; border-radius: var(--border-radius-small); border: 1px solid var(--border); }
/* 图例的色块与格子用同一组背景值，所以这里也走同样的四个梯度 */
.cov-top .lgd i.c0 { background: var(--card); }
.cov-top .lgd i.c1 { background: rgba(15, 157, 120, 0.06); }
.cov-top .lgd i.c2 { background: rgba(15, 157, 120, 0.13); }
.cov-top .lgd i.c3 { background: rgba(15, 157, 120, 0.22); }

/* 矩阵自身不高：10 行 × 7 列，别继承 `.table-wrap` 那个为长表准备的 max-height */
.cov-wrap { max-height: none; overflow-x: auto; }
table.cov { width: 100%; font-size: var(--font-size-table); }
table.cov th, table.cov td { padding: 5px 8px; border-bottom: 1px solid var(--border); white-space: nowrap; }
table.cov thead th { color: var(--dim); font-weight: 600; background: var(--card-2); }
table.cov th.rowhead, table.cov tfoot th { text-align: left; font-weight: 600; color: var(--text); }
table.cov tbody tr:hover th.rowhead { color: var(--accent-text); }
table.cov tfoot td, table.cov tfoot th { background: var(--card-2); border-bottom: 0; font-variant-numeric: tabular-nums; }
table.cov td.cell { text-align: right; font-variant-numeric: tabular-nums; }
/* 合计列与"没碰过"的格子同色：它是行汇总，不该比明细格更响 */
table.cov td.cnone { background: var(--card); text-align: right; font-variant-numeric: tabular-nums; }
/* 四档底色。c0 不设背景色（留白＝没碰过，那是要看的那一片白），只把数字压暗 */
table.cov td.c1 { background: rgba(15, 157, 120, 0.06); }
table.cov td.c2 { background: rgba(15, 157, 120, 0.13); }
table.cov td.c3 { background: rgba(15, 157, 120, 0.22); }
table.cov td.cell a { color: inherit; text-decoration: none; display: block; }
table.cov td.cell a:hover b { color: var(--accent-text); text-decoration: underline; text-underline-offset: 2px; }
table.cov td.cell a:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; border-radius: var(--border-radius-small); }
table.cov td.cell b { font-weight: 600; }
/* 右半边的「池子里」是弱信息：同一个格子里两个数必须能分开读，否则会被当成一个分数 */
table.cov td.cell .u, table.cov tfoot .u { color: var(--dim-2); font-weight: 400; }
table.cov td.c0 b { color: var(--dim-2); font-weight: 400; }

/* ------------------------------ 下钻抽屉（右侧滑出） ------------------------------ */

/**
 * 名册的下钻从「行内展开」改成右侧抽屉：七组信息塞不进一个表格行
 * 而且行内展开会把表格撑开、扫读位置跳。抽屉不动列表，符合"扫一列 → 点一家看细节 → 继续扫"。
 *
 * **不用 `[hidden]` 控制显隐**：作者样式里的 `display` 会盖掉 UA 的 `[hidden]`
 * （本文件里已记过同款踩雷），而这里本来就靠 transform 做位移，索性用类控制。
 * `visibility: hidden` 同时解决两件事——滑出屏外之后不被 Tab 聚焦、也不被读屏读到
 * （只写 transform 的话，隐藏时仍是可聚焦的，键盘用户会 Tab 进一块看不见的面板）。
 */
.ent-drawer {
  position: fixed; top: var(--topbar-h); right: 0; bottom: 0;
  width: min(520px, 92vw);
  display: flex; flex-direction: column;
  background: var(--card); border-left: 1px solid var(--border);
  box-shadow: -8px 0 24px rgba(15, 23, 42, 0.08);
  transform: translateX(100%); visibility: hidden;
  transition: transform var(--motion-base) var(--ease), visibility var(--motion-base);
  z-index: 40;
}
.ent-drawer.open { transform: none; visibility: visible; }
/* 分布看板抽屉要装「城市 × 科室」的矩阵（省级单位 + 6 科室 + 未分类 + 合计 = 9 列），
   实测 940px 下「合计」会被切掉半个 → 给到 1100（96vw 是给小屏的上限） */
.ent-drawer-wide { width: min(1100px, 96vw); }
/**
 * 抽屉头部按 Arco Drawer 对齐：`height: 48px; padding: 0 16px;
 * border-bottom: 1px solid neutral-3`。同时**去掉原来那层 `--card-2` 底色**——
 * 岛上的 Arco 抽屉头部是素底，两套抽屉摆在一起时，一个带底色一个不带就像两家做的。
 */
.ent-drawer-hd {
  display: flex; align-items: center; gap: 8px; flex: none;
  height: 48px; padding: 0 16px; border-bottom: 1px solid var(--color-border-2); background: var(--card);
}
.ent-drawer-hd .spacer { flex: 1; }
.ent-drawer-t { font-weight: 600; }
.ent-drawer-x {
  background: none; border: 0; font: inherit; color: var(--dim);
  /* 点击热区：原来 2px 8px（约 26×22）紧贴头部右边距，实测不好点。
     32×34 是"手指点得着"的下限；上下用负外边距把那几个像素还回去，
     48px 的头部高度与标题基线都不动，右边缘也仍旧留 16px。 */
  cursor: pointer; padding: 6px 10px; margin: -6px 0; border-radius: 6px; line-height: 1.4;
}
.ent-drawer-x:hover { background: var(--bg); color: var(--text); }
.ent-drawer-x:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.ent-drawer-bd { overflow: auto; padding: 12px 16px; }   /* Arco Drawer content 的 12px 16px */

/* 机构详情的头部现在只有「打开完整页」这一个入口（副标题已删：等级与机构类型在「医疗属性」里
   各有一行，重复说一遍只是噪音）—— 右对齐放成一行，紧贴标题下方。 */
.ent-hd { display: flex; justify-content: flex-end; padding-bottom: 6px; border-bottom: 1px solid var(--border); margin-bottom: 8px; }
.ent-hd-n { font-size: var(--font-size-lg); font-weight: 600; line-height: 1.4; }
/*：机构名搬进抽屉标题栏后，这句"等级 · 科室 · 形态"成了正文首行
   所以从"名字下方的小字"抬到 13px、去掉上边距（`.ent-hd` 只被抽屉用）。 */
.ent-hd-m { color: var(--dim); font-size: var(--font-size-table); margin-top: 0; }
/**
 * 段落：`padding` 上下**不对称**。
 *
 * 原来上下都是 10px，看着"标题贴着上一条"—— 因为上一条的最后一行与虚线之间只有 10px，
 * 而虚线到标题也是 10px，视觉上两组紧挨着。现在上方 14px、下方 10px：
 * 虚线更靠近它**下面**那一组（标题+内容），段与段之间的归属一眼能看出来。
 */
.ent-sec { padding: 14px 0 10px; border-bottom: 1px dashed var(--border); }
.ent-sec:last-child { border-bottom: 0; }
.ent-sec-t { margin: 0 0 8px; font-size: var(--font-size-sm); font-weight: 600; color: var(--dim); }


/*
  可折叠段落（原生 `details`）：低频块默认收起，让抽屉回到"快速看一眼"。
  `list-style: none` + 去掉 webkit 三角，用 `::before` 自己画一个能转的箭头。
*/
.ent-fold > summary { cursor: pointer; list-style: none; display: flex; align-items: center; gap: 6px; }
.ent-fold > summary::-webkit-details-marker { display: none; }
.ent-fold > summary::before { content: '\25B8'; font-size: 10px; color: var(--dim-2); transition: transform .15s; }
.ent-fold[open] > summary::before { transform: rotate(90deg); }
.ent-fold > summary:hover { color: var(--accent-text); }
/**
 * 折叠摘要里那枚百分比（「同城同科室 · 已跟进 38%」）。
 * 由页面侧 `fillSameCity` 在拿到同城那笔数之后写进去 —— 这一块默认收起，
 * 比例不写在这里就等于没有（环在折叠体里，展开才看得见）。
 */
.ent-fold > summary .ent-fold-pct { color: var(--dim-2); font-size: var(--font-size-sm); font-variant-numeric: tabular-nums; }

/*
  来源标记：收进 hover 的小标签。来源属于"要核的时候才看"，铺在值后面会把值淹掉。
*/
.src-tag { margin-left: 6px; font-size: var(--font-size-sm); color: var(--dim-2); }

/* 无标题的指标表（抽屉里的同城同科室：标题在上一行的 sum-line 里） */
.dl-flush { margin-top: 0; }
/**
 * 抽屉段落按**用途**分档（规范化）。原来 10 段各是「12px 灰标题 + 虚线」
 * 视觉权重完全相同，而它们的用途分三类：属性 / 判断 / 动作。
 * 现在四档，用底色与间距区分、**不加边框**（§一判据"层级靠字重与颜色，不靠边框"）：
 *
 *   · `t-key`   属性：身份 / 位置 —— 素底 + 虚线分段（继承 .ent-sec 原样，无需额外规则）
 *   · `t-judge` 判断：医疗属性 / 投放 / 同城同科室 —— 浅底块，视觉上明确成一组
 *   · `t-act`   动作：触达 / 我方 / 开监测跑诊断 —— 顶部一条实线，与上面"看"的分开
 *   · `t-meta`  底账：来源与观测 —— 小字降级，最后一块、不带分隔线
 *
 * ⚠️ 抽屉内容的样式**两个容器都要写**：岛版住在 Arco 自己的 `.arco-drawer` 里，
 * 手写版才是 `<aside class="ent-drawer">`。这次用的是 `.ent-sec`（两条路径共用同一个类名），
 * 所以不受影响；但**以后按容器写选择器时别忘了**——易错点见
 * docs/design/2026-10-09-roster-facets-search-drilldown.md「第三轮」。
 */
.ent-sec.t-judge {
  background: var(--low-weak);
  border-bottom: 0;
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  margin-bottom: 10px;
}
.ent-sec.t-act { border-bottom: 0; border-top: 1px solid var(--border); padding: 12px 0 0; }
.ent-sec.t-meta { border-bottom: 0; padding: 10px 0 0; }
/* 小节标题统一一种颜色/字重：分档只区分底与间距，标题本身不必再降一档 */
.card .k .scope { font-size: var(--font-size-sm); font-weight: 400; color: var(--dim-2); }
/* 抽屉比页面窄，`dl` 的 88px 标签列在这里偏宽，压到 76px 让值有更多位置 */
/**
 * 抽屉里的字段表：**标签列要给够 + 不折行**。
 *
 * 基础 `.dl` 是 `88px 1fr`，装不下「统一社会信用代码」（8 字 ≈ 104px），于是标签自己
 * 折成「统一社会信用 / 代码」两行、把值那一列也挤歪 —— 用户截图里问的就是这个。
 * 标签一律不折行（它是键，折了不像键）；要折的应该是**值**（长地址本来就有 `word-break`）。
 *
 * ⚠️ **两个容器都要写**：抽屉有两条渲染路径 —— 岛版住 Arco 自己的 `.arco-drawer` 里，
 * 手写那版才是 `<aside class="ent-drawer">`。只写 `.ent-drawer` 的话，**岛版下这条规则
 * 根本不匹配**（改了之后量出来还是 88px，标签照样折 —— 上一版就是这么白改的）。
 */
/**
 * 「集团客户」面板（抽屉里，）：六列塞进 520px 的抽屉 → 第一列被压成**一字一行**。
 * 与名册表同一条规矩（见上面"机构列不许被挤成一字一行"）：给表格一个下界，
 * 装不下就让容器横向滚动，而不是把一列压成一条。
 */
.ent-groups table { min-width: 470px; }
.ent-groups th:first-child,
.ent-groups td:first-child { min-width: 180px; }

.ent-drawer .dl,
.arco-drawer .dl { grid-template-columns: 104px 1fr; }
.ent-drawer .dl dt,
.arco-drawer .dl dt { white-space: nowrap; }
.ent-drawer .dl dd,
.arco-drawer .dl dd { word-break: break-word; }
.ent-drawer .tag { margin-right: 0; }

/* 动效敏感的用户：只去掉位移过渡，显隐仍然要生效（不能整块拿掉 transition） */
@media (prefers-reduced-motion: reduce) {
  .ent-drawer { transition: none; }
}

/* ══════════════════════ 收尾：聚焦环兜底══════════════════════
   边框饮食之后，能"框住"元素的东西只剩三样：输入框描边、表行分隔线、聚焦环。
   所以聚焦环必须每个可交互元素都有——它是键盘用户唯一能看见自己位置的东西。

   文件里已有 21 条各自写的 `:focus-visible`（颜色一致、偏移不同，偏移是有意的）。
   下面这条补的是**没有单独立规则**的那部分（普通 button、a，以及以后新增的控件）；
   已写规则的元素由更具体的选择器接管（`.chip:focus-visible` 之类优先级更高），不受影响。

   表单控件排除在外：它的焦点提示是"描边 + 光晕"（见 input:focus），
   再叠一圈 outline 会变成两套焦点样式。 */
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
input:focus-visible, select:focus-visible, textarea:focus-visible { outline: none; }

/* ══════════════════ 聚焦让位（`scroll-padding-top`）═══════════════════════
   上面那条管"焦点看得见"，这条管"焦点不被盖住"——**是同一件事的两半**。

   现场：键盘 Tab 走到吸顶区下方的元素时，浏览器把它滚进视野，**上边缘正好落在吸顶元素
   压着的那一条**，于是焦点环在元素上、元素在吸顶元素底下（WCAG 2.4.11 说的就是这种：
   有焦点但看不见，与"没有焦点样式"后果相同）。
   （实拍复现：名册第一行的第一格被吸顶表头压住，指针点它会被表头接走 —— 指针与键盘
    在这一点上是同一个原因。）

   做法是给**每个纵向滚动容器**声明 `scroll-padding-top`，把"有效可视区上边缘"下推一格：
     · 文档滚动容器 → 让开顶栏
     · 表格自己的滚动框（`.table-wrap`，同时是吸顶表头的 scrollport）→ 让开表头
   两处都绑令牌，不写数值：顶栏/表头改了，让位量跟着改。

   不在此列的三类，别顺手加：
     · 侧栏 `.side` 是吸附元素、本身不滚；
     · `.kcol-body`（看板每列）里没有吸顶元素，滚到哪都看得见；
     · 抽屉/弹层内部由 Arco 自己管（`placement=right` 的抽屉没有吸顶头）。
   `.ent-foot`（抽屉里吸底的那块）**刻意没加**：它的高度随内容变（实测 252px，375px 一档
   还会整块不渲染），绑不到固定值；真要修得先把它改成固定高度的动作条。 */
html { scroll-padding-top: var(--topbar-h); }
.table-wrap { scroll-padding-top: var(--thead-h); }

/* 关于动效：本轮把 12 处散落的时长统一到 --motion-fast / --motion-base，**没有新增动画**，
   所以上面这些既有的 prefers-reduced-motion 仍然覆盖全部动效，不另加全局兜底——
   全局压时长会与抽屉那条"显隐仍要生效"的用意冲突。 */

/* ------------------------- 工作台看板（收成四列） -------------------------
   四个桶 = 四列并排、各列自己滚。由来：用户口径「量不小、要列式、别一行行往下堆
   （浪费左右宽度、还得往下滑才看得到）」。列宽按重要性分，列序按紧急度
   （逾期 → 今天 → 有动静 → 没排期）——**列的位置固定，人才知道往哪看**，
   所以空列也保留，空着正好说明「这一类今天没事」。

   2026-10-08 逐页构图：原来第五列「可领的高意向」与这四列等重，而它是**主动挑的**
   （不是"今天该做的"），于是从并排里摘出去、降成下面的折叠块（见 app.js 的 poolFold）。
   并排的列数随之回到四，`.kanban` 的轨道也就只留四条。 */
.kanban {
  display: grid; gap: 12px; align-items: start;
  grid-template-columns: 1.15fr 1.15fr 1.15fr 1fr;
}
@media (max-width: 1200px) { .kanban { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 768px) { .kanban { grid-template-columns: minmax(0, 1fr); } }
.kcol {
  background: var(--card); border: 1px solid var(--border); border-radius: var(--border-radius-large);
  display: flex; flex-direction: column; min-width: 0;
}
.kcol-head { padding: 9px 12px; border-bottom: 1px solid var(--border); }
.kcol-head b { font-size: var(--font-size-base); }
.kcol-head .n { font-weight: 600; font-variant-numeric: tabular-nums; }
/* 列头那句解释**必须只占一行**：列只有 ~200px 宽，让它自己换行会撑到 3~4 行，
   把列高从 570 啃到 200（实测），列里就只剩两三行——解释换成了"看不见"。全文改挂 title 悬浮。 */
.kcol-head .hint {
  display: block; font-size: var(--font-size-sm); color: var(--dim-2); margin-top: 2px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* 关键：每列自己出滚动条，页面整体不用往下滑 */
.kcol-body { padding: 6px; overflow-y: auto; max-height: calc(100vh - 320px); }
.kcol-body .wb-list { margin: 0; }
/* 列只有 178~240px：一行塞不下「名字 + 分数 + 城市·科室 + 由头 + 四个按钮」，
   所以行内改为竖排（按钮落到下一行），机构名独占一行并且**允许换行**——
   它是这一行最重要的信息，被截成「合肥…」等于没有名字（实测最窄那列就是这样）。 */
.kcol .wb-row { flex-direction: column; align-items: stretch; gap: 5px; padding: 9px 10px; }
.kcol .wb-main { flex-wrap: wrap; gap: 4px 6px; }
.kcol .wb-main .org-link { flex: 1 1 100%; max-width: none; white-space: normal; overflow: visible; }
.kcol .wb-act { flex-wrap: wrap; }
/* 动态卡片的话术在列里压到 3 行：列是队列（一屏看全），全文在「客户动态」页 */
.kcol .sig-script .txt {
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.kcol-foot { padding: 6px 12px 10px; border-top: 1px solid var(--border); font-size: var(--font-size-sm); }
.kempty { font-size: var(--font-size-sm); color: var(--dim-2); padding: 12px 4px; text-align: center; }

/*
 * 只剩一列（名下没有任何要跟的事，只有「可领的高意向」可干）时铺满宽度。
 * 必须写在上面两条 @media 之后：同样是两段选择器，靠后者胜，才盖得住窄屏的 repeat(2)。
 */
.kanban.solo { grid-template-columns: minmax(0, 1fr); }

/* ══════════ 组件规范对照（第二批：input / radio / select / popover / table / tooltip / list）══════════
 * 取值同样**量自本地 Arco 编译产物**（规范页是 SPA，抓不到正文）。逐项：Arco 值 → 我们的值 → 结论。
 *
 *   Input    Arco: padding 4px 12px + line-height 1.5715 ⇒ 默认尺寸高 32px；圆角 2px
 *            我们: 已改成同一套几何（原 7px 10px ⇒ 约 38px 高）；圆角保持 --radius-sm(6px)
 *            结论: **对齐几何**。原状态是"同一套系统里输入框两种高度"——登录页早就是 32px。
 *
 *   Radio    Arco: 14px 圆 + 2px 边框；选中填 rgb(var(--primary-6))
 *            我们: **全站 0 处使用**（选择用勾选与 chip 表达）
 *            结论: 无需对齐。记在这里是为了下一轮不再重复盘它。
 *
 *   Select   Arco: 选项 padding 0 7px、高 36px；弹层 padding 4px 0、边框 fill-3、圆角 4px、影 0 4px 10px
 *            我们: 选项已对齐 0 7px / 36px；菜单内距 4px、圆角 12px、影 --shadow-2
 *            结论: **对齐选项行高**（同一页会同时出现岛上的 Arco 下拉与这套手写下拉，行高差 2px 就像两家的）；
 *                  弹层圆角与阴影保持我们的刻度与三档阶梯。
 *
 *   Popover  Arco: padding 12px 16px；title 16px/500；影 0 4px 10px rgba(0,0,0,.1)
 *            我们: .idmenu 内距 4px + --shadow-2（原来那颗写死的 0 10px 28px 已收进令牌）
 *            结论: 阴影用我们自己的阶梯——三档自洽比逐像素一致重要，观感差不可辨。
 *
 *   Table    Arco: td padding 9px 16px、字号 14px
 *            我们: td 10px 14px、字号 13px
 *            结论: **刻意偏离，两处都有理由**：13px 是既有的密度选择（文件头写过）；
 *                  横向留 14px 而不是 16px，是为了让名册在 1440 宽**不出现横向滚动**
 *                  （每列加 2px×2 边，7~9 列就是 30px 上下，正好把不滚变成滚）。
 *
 *   List     Arco: item 是 flex + 分隔线，`justify-content: space-between`
 *            我们: .list-row（12px 16px + 分隔线）
 *            结论: 形态一致，无需改。
 *
 *   Tooltip  Arco: padding 8px 12px、底 gray-10、白字、圆角 2px、影 0 4px 10px
 *            我们: **213 处原生 `title=`**
 *            结论: **本轮唯一"量了但不做"的一项**，理由不是工作量而是可达性：
 *                  换成 hover 自绘提示，会把信息对**触屏（无 hover）与键盘（要额外接 focus）**藏起来，
 *                  而原生 title 至少还能被读屏当描述读。要做就单独一轮做完整（触发方式 + 可达性一起），
 *                  别顺手换一半——那比不换更差。
 */

/* ══════════════════ 自绘提示层（Tooltip，）══════════════════════
 * 视觉取值量自 Arco 的 `.arco-tooltip-content`：
 *   `padding: 8px 12px`、底 `rgb(var(--gray-10))`、`color: #fff`、字号 14px、
 *   小圆角、影 `0 4px 10px rgba(0,0,0,.1)`、`line-height: 1.5715`。
 * 圆角仍走我们的小档（4px，Arco 是 2px——全站刻度的既有偏离）。
 *
 * **必须 `position: fixed` 且挂在 body 上**（app.js 的提示模块负责挂）：
 * 全站 21 处 `overflow: hidden` + 4 处 `overflow: auto` 的容器（`.panel`/`.table-wrap`/`.fold`）
 * 会把容器内部的提示裁掉——三页 50 处 `title` 里 41 处（82%）落在这种容器里
 * 所以"用伪元素顺手做"在这套容器结构下不成立。
 * z-index 取 2000：要压在 `.modal`(50) 与岛的 Arco 弹层(1000) 之上——弹层里也会有带提示的元素。
 * `pointer-events: none` 是硬要求：否则提示会挡住它自己的触发元素，鼠标一移上去就闪。
 */
/**
 * **结构化提示**（`data-tip-rows`）：标签列 + 值列，按 Arco Descriptions 的解剖排。
 * 标签列宽度取**最长的那条标签**（`max-content`），所以一组说明里的值都从同一条竖线开始；
 * `display: contents` 让每条 row 只是逻辑分组、两个 span 才是网格项 —— 这样才对齐得起来。
 * 纯文本提示（只有 `title`）不套这一层，仍是原来那块深色底板。
 */
.tip-layer.tip-rich {
  display: grid; grid-template-columns: max-content minmax(0, 1fr);
  column-gap: 12px; row-gap: 4px; max-width: 400px; padding: 8px 12px;
}
.tip-layer.tip-rich .tip-row { display: contents; }
.tip-layer.tip-rich .tip-k { color: rgba(255, 255, 255, 0.6); }
.tip-layer.tip-rich .tip-row-wide .tip-v { grid-column: 1 / -1; }

.tip-layer {
  position: fixed; z-index: 2000; max-width: 320px; pointer-events: none;
  padding: 8px 12px; background: rgb(var(--gray-10)); color: #fff;
  font-size: var(--font-size-base); line-height: 1.5715; word-break: break-word;
  border-radius: var(--border-radius-small); box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
  /**
 * **`pre-line`：认换行**。有些说明是"一项一行"的并列口径
   * （如抽屉「同城同科室」的 名册家数／池子里／已认领），文案里用 `\n` 分行；
   * 默认的 `normal` 会把 `\n` 折成空格，一行一行就没了——用户拿截图问过这个。
   * 用 `pre-line` 而不是 `pre`：只认换行、其余仍按正常折行（`pre` 会连空格一起保留）。
   */
  white-space: pre-line;
}

/* ══════ 组件规范对照（第三批：alert / drawer / message / modal / notification / popconfirm / progress / dropdown）══════
 * 取值同样量自本地 Arco 编译产物。这一批**大量是"无事可做"**，也一并记下来——不然下一轮还会重新盘一遍。
 *
 *   Alert        Arco: padding 8px 15px（无标题）/ 15px 15px（带标题）；圆角 2px；图标 22px + 右距 8px
 *                我们: `.callout`（左侧色条 + 行内排布）
 *                结论: **内距对齐到 15px 16px**；形态不照搬——我们这种是"左侧一道色条 + 行内内容"，
 *                      适合承载"标签 + 一句话 + 说明"的告警行（见 unassignedBannerHtml），
 *                      与 Arco 那种整块底色 + 居中图标的 Alert 不是同一种信息密度。
 *
 *   Drawer       Arco: header `height: 48px; padding: 0 16px; border-bottom: neutral-3`；content `padding: 12px 16px`
 *                我们: header 10/14 + `--card-2` 底色；content 14px
 *                结论: **几何全对齐**（48 / 0 16 / 12 16），并**去掉头部底色**——岛上那份是 Arco 抽屉（素底），
 *                      两套抽屉摆一起时一个带底色一个不带就像两家做的。
 *
 *   Message      Arco: padding 10px 16px、圆角 2px、描边 neutral-3、影 0 4px 10px
 *                我们: `flash()` 在岛就绪时**直接调 `MDUI.message[kind]`**（就是 Arco 的 Message）
 *                结论: **无需对齐**。岛没到时的兜底是写进页头那一行（有意保留，见该函数注释）。
 *
 *   Modal        Arco: width 520px、top 100px、圆角 4px、header 48px + `padding: 0 20px`、
 *                      content `padding: 24px 20px`、title 16px/500、mask `rgba(29,33,41,.6)`
 *                我们: box 384px / 圆角 16px / 内距 24px（派活面板自己覆写成 max-width 620px）
 *                结论: **遮罩对齐**（原 `.45` → Arco 的 `.6`）、**标题字重对齐 500**；
 *                      宽度 384 与圆角 16 **保留**——384 与登录卡片同宽是本系统的既有契约
 *                      （login.html 里写明"卡片外宽 432 ＝ 内宽 384，与浮层同宽"），
 *                      为了对齐 Arco 把它改成 520 会顺手破坏这层一致性。
 *
 *   Notification Arco: width 340px、padding 20px、圆角 4px、影 0 4px 12px rgba(0,0,0,.15)
 *                我们: **全站未使用**（没有"角落常驻通知"这种场景）
 *                结论: 无需对齐。记下来，别再盘。
 *
 *   Popconfirm   Arco: content padding 12px 16px、title 14px + 下距 16px
 *                我们: 确认走岛上的 **Arco `Modal.confirm`**（`confirmDialog` → `MDUI.confirm`）
 *                结论: 无需对齐（用的是 Arco 的另一个件）；它的 content 内距与我们浮层一致，不冲突。
 *
 *   Progress     Arco: 填充 `rgb(var(--primary-6))`、胶囊圆角 100px、轨道 `--color-fill-3`；
 *                      **线形高度是 size prop，CSS 里读不到**
 *                我们: `.bar .track` 7px（轨道 `--bg`）、`.vis-bar` 6px（圆角 99px）
 *                结论: **形状对齐**（胶囊 + fill-3 轨道），高度按我们自己的刻度**统一成 6px**——
 *                      原来两种高度两种轨道色，同一屏里两种进度条看着像两个来源。
 *
 *   Dropdown     Arco: Dropdown 复用 Menu，竖排菜单项 `padding: 0 12px; line-height: 40px`（字号 14）
 *                我们: `.idmenu button` 8px 10px + 13px 字号（约 35px）
 *                结论: **对齐 40px / 0 12px / 14px**。菜单不是表格，字号回到 14px 是对的。
 *
 * ── 顺带修正上一批的一处错 ──
 * `.sel-opt` 的横向内距上一批设成了 `0 7px`：那是我按 `.arco-select-option-wrapper` 量的值，
 * 而**真正的选项规则是 `.arco-select-popup .arco-select-option { padding: 0 12px }`**（两个不同元素）。
 * 高度 36px 是对的，内距已改回 12px。教训：量选择器要量到**真正承载该属性的那条规则**，
 * 前缀像不代表是同一条（`.arco-select-option-wrapper` 与 `.arco-select-option` 只差一个词）。
 */

/* ══════ 组件规范对照（第四批：button / icon / link / typography / avatar）══════
 *
 *   Button     Arco: 默认 `height 32 / padding 0 15 / 14px / 字重 400 / 圆角 2 / nowrap`；
 *                    small 28、mini `24 / 0 11 / 12px`、large `36 / 0 19`
 *              我们: 原 `padding: 7px 13px` ⇒ 约 **38px**——而同站筛选栏与页头按钮早已被覆写成 32、
 *                    刚对齐的输入框也是 32，于是"同一条筛选栏里按钮比输入框高 6px"
 *              结论: **默认对齐 32 / 0 15 / nowrap**，`.mini` 与 `a.mini` 对齐 **24 / 0 11**。
 *                    两处刻意不照搬：① 主按钮底色用 `--accent-strong`(primary-7) 而不是 Arco 的 primary-6
 *                    ——白字在 -6 上只有 3.43:1（不过 AA），-7 是 4.60:1 ✓；
 *                    ② **字重保留 600**（Arco 是 400）——我们的按钮小、中文笔画密，400 在这个尺寸下发虚。
 *                    ⚠️ 加这条基线时逐个查过**豁免清单**（见上面那条注释）：`.chip` / `.th-sort` /
 *                    `.cell-btn` / `.link-btn` / `.telview` / `.row-toggle` / `.undo` / `.region-btn` / `.nav-t`
 *                    都是"文本或格子形态"，已 `height: auto` 还原。
 *
 *   Icon       Arco: `width/height: 1em; vertical-align: -2px; stroke: currentColor`（按字号缩放）
 *              我们: `icons.js` 显式给 15/16/18px 宽高
 *              结论: **不照搬**。显式 px 的代价是换尺寸要显式写，好处是图标不随上下文字号漂
 *                    ——我们字号档位多（13px 表格 / 14px 正文 / 12px 辅助），1em 会漂出三种大小。
 *
 *   Link       Arco: `color: link-6; padding: 1px 4px; 圆角 2px; 无下划线`，hover 只换色
 *              我们: `color: --accent-text`(primary-8) 且 **hover 加下划线**
 *              结论: **两处刻意保留**：① 颜色用 -8（6.23:1），Arco 的 link-6 是 3.43:1——文字过不了 AA；
 *                    ② hover 下划线是"不只靠颜色区分链接"的兜底（WCAG 1.4.1 的精神）。
 *
 *   Typography Arco: h1 36/1.23、h2 32/1.25、h3 28/1.29、h4 24/1.33、h5 20/1.4、h6 16/1.5，字重 500
 *              我们: 页标题 20px、面板标题 14px、弹层标题 16px——正好落在 Arco 的 **h5/h6 两档**
 *              结论: **不照搬那套字号**（密集后台不需要 28~36 的营销级标题），层级改由**字重**承担（600/650）
 *                    ——小字号下 500 撑不起层级。能对齐的那一条上一批已改（弹层标题 16px/500）。
 *
 *   Avatar     Arco: 默认 40×40、字号 20；其余是 size prop；圆角 50%
 *              我们: 顶栏账号入口 `28×28`、圆角 50%
 *              结论: **28 正是 Arco 的 small 档**，紧凑度刚好；无需改。
 */

/* ══════ 组件对照（第五批：skeleton / pagination / result / spin）══════
 *
 *   Skeleton   Arco: 文本行 `height: 16px; background: fill-2; 圆角 2px`；头像位 48（小 36 / 大 60）；带流光
 *              我们: `.sk` 基高 12px、表格里 `.sk-td` 11px、标题位 `.sk-title` 18px；
 *                    已有 shimmer，且**已接 `prefers-reduced-motion`**（那条早就写了）
 *              结论: **保留各自的形状高度**——我们在 13px 的表格里，骨架按行取 11px 才对，统一成 16px 会撑高行；
 *                    对齐的是**颜色来源**：中间那道高光不用硬编码 `#e9edf1`，改用 `rgb(var(--gray-3))`。
 *                    另补 `skeletonLines()`（util.js）：非表格的内容块原来只有一行「取数中…」灰字（全站 5 处），
 *                    现在换成骨架——"加载时白屏或一行字"是最显旧的地方，骨架先立住版式、数据回来只换内容。
 *
 *   Pagination Arco: 项尺寸由档位给（mini 24 / 默认 32）、圆角 2px、当前页 `color: primary-6` + 底 `primary-light-1`、
 *                    hover `fill-1`；总数文案 mini 12px
 *              我们: 只有"上一页 / 下一页" + 一行 `.note`（第 X / Y 页 · 本页 N 家），布局还是内联 `style="display:flex…"`
 *              结论: **几何对齐**（`min-width: 32px` + 居中 + 总数 12px），内联样式收成 `.pager` 类（留白成阶的要求）。
 *                    没有页码项，所以没有"当前页"态可上——这是分页本来的形态，不是漏做。
 *
 *   Result     Arco: 容器 `padding: 32px 32px 24px`；标题 14px/500 居中；说明 14px（text-2）居中；
 *                    图标位 `margin-bottom: 16px`，404/403 的圆是 92×92、普通提示圈 45×45
 *              我们: `.errbox` 本来就是"居中 + 纵向排列"，但间距 12、标题是 13px 弱色
 *              结论: **间距与字重对齐**（gap 16；标题 14px/500 用 text-1、说明 14px 用 text-2）；
 *                    **不引 92px 圆形插图**——与空态同一条理由：数据密集的后台里它占掉一整行，收益不成比例。
 *                    标记无需改动（第一个 `<p>` 当标题、`.note` 当说明、`button` 当动作）。
 *
 *   Spin       Arco: 三点跳动（`.arco-spin-dot` 8px + primary 色 + 逐个 delay）
 *              我们: `.spin` 一个转圈，且 **reduced-motion 下有 `dx-pulse` 呼吸式兜底**
 *                    （那条注释写着"不能停成一个静止的圈——那看起来像个句号"）
 *              结论: **不换**。我们这套已经处理了 Arco 没处理的那条（减动效时的观感），
 *                    而且"加载"在这套系统里已由骨架屏承担（骨架比转圈更接近内容形状）。
 */

/* ══════ 组件对照（第六批：按 Arco 全表盘一遍，只记结论与"还没量"的）══════
 *
 * 前五批是把**在用的**那些逐项量过。这一批换个做法：照 Arco 的组件清单**全表盘一遍**，
 * 把剩下的分成三类，只记需要决定的与**已盘但还没量**的——不然下一轮还会从头再盘一次。
 *
 * ── 一、Arco 件与我们的对应物（岛用 Arco 的 / 手写双路）──
 *   Descriptions  岛用 Arco（InfoCell、lead-drawer-host）；手写 `.dl` 是双路的另一份
 *   Statistic     岛用 Arco（stat-host）；手写 `.card .v`（取值已对齐过：24px 那轮）
 *   Tag           岛用 Arco（chips）；手写 `.tag` / `.pk` / `.chip`
 *   Tabs          岛用 Arco（tabs-host）；**无手写对应物**（所以它天然只有一套）
 *   Cascader / Checkbox / Form / Menu / Dropdown / Select
 *                 岛用 Arco；手写那份是 `.sel-*` / `.pick` / `.field` / `.idmenu`
 *   Progress / Modal / Drawer / Message / Alert / Popconfirm
 *                 第三批已量（`.bar` / `.modal` / `.ent-drawer` / `flash()` / `.callout` / `MDUI.confirm`）
 *   Result        `.errbox`（第五批已量：间距 16、标题 14/500 text-1、说明 14 text-2、不引插图）
 *
 * ── 二、空态（Arco `Empty`）与失败态是**同一个解剖、两个件**──
 *   Empty   Arco: 插图 + 一行说明
 *   我们:   `.empty`（说明 + 可选的就地动作）。**不引那套插图**——与 Result 同一条理由
 *           （数据密集后台里占掉一整行，收益不成比例，见 `.empty > .ico` 那段）。
 *   位置关系: 表格空位由岛画成 `<div class="empty">`，内容经 `RawHtml` 注入（中间多一层包裹 div，
 *           所以相关选择器一律用后代而不是 `>`）；失败块进来时复用 `.errbox`（`.empty .errbox`
 *           那条去掉了它的内距，两层 32px 会叠成一大块空白）。
 *
 * ── 三、这五项**量过了**：三项无事可做，一项只差字重，一项无对应物 ──
 *   Badge     **早已对齐**：取值逐项同 Arco 的数字档（高/最小宽/行高 20、内距 0 6、字号 12、
 *             状态点 6×6）。本轮补的是**字重 500**——它原来没设、继承 400。带文字的色片那一族
 *             在 Arco 那边其实是 `Tag`（24 / size-small 20 / 0 8 / 12px / **500**）：`.tag` 早就是
 *             500，`.badge` / `.pk` / `.chip` 这轮一起补上；600 那档留给强调态（`.paid` / `.high`）。
 *             顺带把 `.tag` 的高度也按 Tag 的 size-small 定死（20 / 行高 18）：它原来没写高度、
 *             行盒给到 18，同一个家族里就它矮 2px。名册行高仍是 37、机构线索仍是 62（实测未动）。
 *   Card      **无事可做**：Arco 的 Card 是"灰底（bg-2）+ 无圆角，描边款才圆角 2"，
 *             我们是"白卡浮在灰画布上 + 圆角 12 + 带影"——这一层是文件头写明的取舍（明度差代替描边），
 *             圆角 12 是既有刻度。内距我们 14/16（面板 14/18）vs Arco 16/16，差的是密度选择，不动。
 *   Collapse  **无事可做**：形态不同（我们是"折叠的卡片"，Arco 是"平铺描边 + 灰内容底"）。
 *             能对照的那一处正好同值：展开态头部底色我们 `--card-2` = Arco 的 active header `bg-2`
 *             （都是 gray-1）。头部高 48 vs Arco 40、内容内距 14/18 vs Arco `8px 13px 8px 34px`
 *             （它把内容缩进到标题下方）——都是密度选择；缩进会挪动折叠区里表格的左边缘，不换。
 *   Timeline  **无对应物**：`.ev` 是"行 + 分隔线"的列表（更接近 Arco `List`），不是"点 + 竖线"的
 *             时间轴。唯一同级的是时间标签（12px + 次要色，与 Arco 的 timeline label 同级 ✓）。
 *   PageHeader **无事可做**：标题 20px 与 Arco 的 title 逐项相同；字重 650 vs 600 是既有的字重约定
 *             （文件头：层级由字重承担）；Arco 的 PageHeader **没有 subtitle**，所以我们的 `.sub`
 *             在这一件里没有对应物。
 *
 *   `.stage-tag`（状态点 + 文字，对应 Arco Badge 的 status 那一档）——三处**刻意偏离**，别改：
 *     点 6×6 ✓ 同值；间距 5 vs Arco 的 8、字号 12 vs Arco 的 14、颜色 `--dim`(text-2) vs Arco 的 `text-1`。
 *     理由：它长在 13px 的表格里，14px 的正文档比整格的字还大；`text-1` 又会跟机构名抢主次。
 *
 * ── 四、Arco 有、我们没有对应物也**尚未使用**（记下来，别再盘）──
 *   布局  Divider / Grid / Layout / Space —— 分隔一律用容器上的 border-bottom；栅格直接用 CSS Grid；
 *         间距用 `gap`。都不是"组件"，不存在对齐问题。
 *   展示  Calendar / Carousel / Comment / Image / Tree
 *   输入  AutoComplete / ColorPicker / DatePicker / InputNumber / InputTag / Mentions / Rate /
 *         Slider / Switch / TimePicker / Transfer / TreeSelect / Upload / VerificationCode
 *         （唯二沾边的两处：月份筛选用原生 `<input type="month">`；导入用原生 `<input type="file">`）
 *   反馈  Watermark
 *   导航  Breadcrumb / Steps —— 层级用 `.page-head` + 侧栏高亮表达
 *   其他  Affix（顶栏是自有 sticky）/ Anchor / BackTop / ResizeBox / Trigger（提示层是自绘 tip-layer）
 */

/* ══════════ 「搜索可见度」面板头：与其他面板一致（2026-10-12 收回） ══════════
   2026-10-09 曾把这条标题涂成报告封面那套深褐 + 金（理由：面板里展开的报告是那套色，
   两块并排像两个产品）。2026-10-12 用户看整页截图：满页白卡里这一条深色「很突兀」。
   收回成与其他面板一样的白底 —— **报告自己的配色留在报告里**（抽屉里那份不受影响），
   面板不替它做标识。`scripts/verify-serp-report.mjs` 里那两条「面板头与报告同色」的断言随之删掉。
   ⚠️ 别再为了"让它看起来像一份报告"把标题涂回去：这一页上面还有建议动作、下面有决策与对标，
   它们都是白卡，单独一条深色只会把页面切碎。 */

/* ══════════════════════ 解释入口：右上角一个圆圈感叹号 ══════════════════════════
   2026-10-09 用户定的规范，原话：「hover 应该要有一个右上角的感叹号那种形式，就是行业标准的，
   而不是直接在文字上，这样体验友好一点。所有的都要走这个逻辑。」

   为什么不继续用 `.hlp`（虚线 + cursor:help 那版）：虚线只表示"这段文字有解释"，
   **扫一眼看不出来**；图标是眼睛能看到的入口。两条都留、各管一处：
     · 表格表头 → `.hlp`（整格都是标题，再塞图标会挤，且表头窄）
     · 面板/区块标题、字段标签、按钮 → **`.hlp-ic`**（本文这一版，一律用这个）
   面板标题是 flex，`margin-left:auto` 把它推到最右，就是"右上角"那个位置。 */
.hlp-ic {
  display: inline-flex; align-items: center; justify-content: center;
  width: 15px; height: 15px; border-radius: 50%;
  border: 1px solid var(--border-strong); color: var(--dim-2);
  font-size: var(--font-size-sm); line-height: 1; cursor: help; user-select: none; flex: none;
}
.hlp-ic:hover { background: var(--bg); color: var(--text); border-color: var(--text); }
.hlp-ic:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/**
 * 说明图标的落点：**紧跟它解释的那个标题**（2026-10-13）。
 *
 * 原来这两条都是"推到最右端"：flex 标题里 `margin-left: auto`、块级标题（`<h2 class="section">`）
 * 里 `float: right`。在机构详情页上量出来的样子是——面板宽 1334，图标离标题左端 **1032px**：
 * 一屏里几个「!」孤零零挂在右边缘，看不出它在解释哪个标题（标题越长越明显，而且面板一多
 * 就没法配对）。Arco / Ant 的惯例都是紧跟 label；本仓也只有这一处放右端，所以统一回内联。
 *
 * `.panel > h2` 本来就是 `display: flex; gap: 9px`，去掉 auto 就落在标题后面（不用再补间距）；
 * `.section` 那种标题不是 flex，给它一个 6px 的左距。
 */
.section > .hlp-ic { margin-left: 6px; }

/*
  手写兜底那版机构详情也做成**居中弹窗**（岛没加载出来时走它）：形态与岛版的 Dialog 一致。
  排除了 `.ent-drawer-wide`（分布看板那个宽抽屉仍从右侧滑出）—— 那种"对着矩阵一直看"的场景
  留抽屉更合适。
*/
.ent-drawer:not(.ent-drawer-wide) {
  top: 50%; left: 50%; right: auto; bottom: auto;
  width: min(880px, 94vw); max-height: 84vh;
  /**
   * 圆角与**面板**同档（12px）—— 2026-10-13 定的"浮层圆角"：
   * 面板/卡片/弹窗/抽屉都取 `--radius`，不再出现"同一个弹窗 6、兜底 14、面板 12"三种。
   * 岛那侧（Arco Modal）在 `client/src/island.css` 里同样覆写成 `--radius`，两边一致。
   */
  border-left: 0; border-radius: var(--radius);
  box-shadow: 0 12px 40px rgba(15, 23, 42, 0.18);
  transform: translate(-50%, -46%) scale(0.98); opacity: 0;
  transition: transform var(--motion-base) var(--ease), opacity var(--motion-base), visibility var(--motion-base);
}
.ent-drawer:not(.ent-drawer-wide).open { transform: translate(-50%, -50%) scale(1); opacity: 1; }

/*
  标题栏里的「打开完整页」：与机构名同一行、推到最右。入口从正文首行挪上来的 ——
  正文那行只为放它一个链接就占了整行。
*/
.ent-title-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.ent-title-row .ent-open-full { font-size: var(--font-size-sm); font-weight: 400; }

/*
  详情正文两栏：**左栏放身份与属性**（基本信息 / 医疗属性 / 工商登记），
  **右栏放客户价值与投放**（客户优先级 / 投放 / 同城同科室）。
  这两类信息单列排下去要滚好几屏，而它们本来就互不依赖。
  窄屏（<760px）退回单列。
*/
.ent-body { display: grid; grid-template-columns: 1fr 1fr; gap: 0 28px; align-items: start; }
@media (max-width: 760px) { .ent-body { grid-template-columns: 1fr; } }
/*
  底部常驻：触达（电话 / 官网）与我方动作 —— 这两块是"看完就要动手"的，
  不该压在几屏之下。sticky 让它跟着滚动容器钉在底部。
*/
/* 详情右上角那一行动作（「编辑」→「保存 / 取消」）。与标题栏里的「打开完整页」同一侧、同一行高 */
.ent-tools { display: flex; justify-content: flex-end; gap: 8px; padding: 0 0 8px; }
/* 字段表里的输入控件：高度与岛上的 Arco 输入框对齐（32px），颜色走 token。
   两处共用这一套 —— 抽屉的编辑表单（`.ent-sec`）与机构详情页那三处可改字段（`.panel`）。
   选择器用后代而不是直接子级：这些控件外面还会套一层「一行多件」（见下面的 .row-flex）。 */
.ent-sec .dl dd input[type="text"], .panel .dl dd input[type="text"],
.ent-sec .dl dd select, .panel .dl dd select {
  height: 32px; padding: 0 10px; font-size: var(--font-size-base);
  border: 1px solid var(--color-border-1); border-radius: var(--border-radius-small);
  background: var(--card); color: var(--color-text-1);
}
.ent-sec .dl dd input[type="text"], .panel .dl dd input[type="text"] { width: 100%; max-width: 320px; }
/*
 * 字段表里的「一行多件」：标签 + 输入 + 按钮、标签 + 徽章 + 说明并排。
 * 为什么不直接拿 `.filters`（筛选条的行）顶：那是筛选条自己的件 —— 标签写成 `span.note`、
 * 间距跟筛选条走，同一张卡里其它字段的标签列（`.dl` 的 88px）于是对不上。
 * 机构详情页原来就是这么写的，"标签字号两种、左边缘参差"正是从这里来的。
 */
.dl dd > .row-flex { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-2); }
.dl dd > .row-flex > input[type="text"] { width: auto; flex: 0 1 240px; min-width: 160px; }
/* 跟进备注是给人读的散文；`textarea` 的全局值是 12px 等宽（那一档是给配置文本用的） */
#dNote { font: inherit; font-size: var(--font-size-table); line-height: 1.6; min-height: 88px; }
.ent-sec .dl dd > select { min-width: 88px; }

/**
 * 抽屉/弹窗末尾那块（「触达」+「我方」）。
 *
 * **刻意不吸底**（2026-10-13 改）：原来是 `position: sticky; bottom: 0`，键盘焦点走到正文末尾时
 * 会被它盖住（WCAG 2.4.11）——实测窗口高 500/620 时，末尾那行「数据来源与观测」的上沿正好落在
 * 它有 284px 高的覆盖区里。
 *
 * 试过的两条"保住吸底"的路都不行：① 给宿主补等于它高度的下内距 —— 会连带把**居中弹窗**重新居中，
 * 在"刚好装得下"的窗口里反而制造新的重叠（实测 800 高时末尾元素落到它后面）；
 * ② 写死一个高度 —— 它随机构变（284px，窄屏整块不渲染），写死注定差一截。
 * 而它的内容（电话 + 改号/复制、我方关系与动作）**不需要常驻可见**：电话就在这一块里，
 * 滚到这里就看到了，设计上要的"改号紧挨着号码"不受影响。所以取消吸底、按普通末块排。
 */
.ent-foot { background: var(--card); border-top: 1px solid var(--border); }

/*
  「生效中」那行：一个 lead 加几个 chip，让它们**排成一行**。
  原来没有样式，串在容器里各占一行高度，屏幕上一大块空白只为说三五个字。
*/
.mdui-chips { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 6px 0 0; }
.mdui-chips .note { font-size: var(--font-size-sm); }

/* 标题行里那枚「打开完整资料 →」要紧跟机构名（原来被推到最右端、与名字隔了半个弹窗）。
   两个抽屉（机构线索 / 名册）共用这一个类，所以一处改、两处一起生效。 */
.ent-title-row { display: flex; align-items: baseline; justify-content: flex-start; gap: 10px; }
.ent-title-row .ent-open-full { margin-left: 0; }

/*
 * 机构线索「生效中的筛选」那行：**开**。
 *
 * 一度被这里 `display: none` 关掉，理由是"筛选条控件里都看得见，这行是重复信息"。
 * 那条理由有两处不成立：`×` 移除是**控件里没有的动作**（清一个下拉要么重开它选中项、
 * 要么按「重置」把别的条件一起清掉），而设在「更多筛选」里的条件本来就不在条上显示。
 * 现在只在**非默认条件**上出现（默认那两条是这一屏的基准，见 `renderChips` 的过滤），
 * 所以第一屏不会多出这一行。
 */

/* 机构线索：页头那一行（标题已撤，只剩一个 flash 占位）不再展示，内容顶到最上面 */
/* 「立即扫描」不在界面上暴露：它只给主管、且定时任务本来每天在跑（接口仍在，运维侧可用） */


/* 机构线索：页头那一行（标题已撤，只剩一个 flash 占位）不再展示，内容顶到最上面。
   判据就是这一行没有标题 —— 别的页的页头都有 h1，所以不受影响。 */
.page-head:not(:has(h1)) { display: none; }
/* 「立即扫描」不在界面上暴露：它只给主管、且定时任务本来每天在跑（接口仍在，运维侧可用） */
#btnScan, #scanNote { display: none; }


/* 窄屏把名册/线索的外壳交回文档流：必须在 pageflow 基础规则之后覆盖 */
@media (max-width: 820px) {
  body.ent-page .shell { height: auto; min-height: 100vh; overflow: visible; }
  body.ent-page .main { overflow: visible; padding-bottom: 64px; }
  body.ent-page .main > #view { flex: none; }
  .panel.pageflow { flex: none; }
  .panel.pageflow #entTableHost .table-wrap,
  .panel.pageflow #orgTableHost .table-wrap { overflow: auto; max-height: min(68vh, 560px); min-height: 0; height: auto; }
}

@media (max-width: 820px) {
  .filters, .ent-row-actions { flex-wrap: wrap; }
  .ent-row-actions { flex: 1 1 100%; margin-left: 0; }
}



.org-mobile-list { display: none; }
@media (max-width: 820px) {
  #orgMobileList { display: block; }
  #orgTableHost { display: none; }
  body.org-pick #orgMobileList { display: none; }
  body.org-pick #orgTableHost { display: block; }
  .org-mobile-card { padding: 12px; border-bottom: 1px solid var(--border); }
  .org-mobile-card:last-child { border-bottom: 0; }
  .org-mobile-hd .org-line { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 6px; }
  .org-mobile-hd .org-link { font-size: var(--font-size-base); font-weight: 600; }
  .org-mobile-card .row-toggle { display: none; }
  .org-mobile-top { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 6px; }
  .org-mobile-own { font-size: var(--font-size-sm); color: var(--dim); }
  .org-mobile-own .own-stage { display: inline-flex; align-items: baseline; gap: 6px; flex-wrap: wrap; }
  .org-mobile-meta, .org-mobile-evidence { margin-top: 5px; font-size: var(--font-size-sm); color: var(--dim); }
  .org-mobile-evidence .plat { margin-right: 4px; }
  .org-mobile-phone { margin-top: 8px; }
}

@media (max-width: 820px) {
  #orgTableHost { display: none !important; }
  body.org-pick #orgTableHost { display: flex !important; }
  #orgMobileList { max-height: none; overflow: visible; }
  #orgFiltersHost { flex: 1 1 100% !important; width: 100%; min-width: 0; }
  .filters { flex-wrap: wrap; }
  .mdui-filters { display: grid !important; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 8px; align-items: center; width: 100% !important; }
  .mdui-filters .mdui-filter-q { grid-column: 1 / -1; width: 100% !important; min-width: 0; order: -1; }
  .mdui-filters .mdui-filter-q .arco-input-inner-wrapper, .mdui-filters .mdui-filter-q .arco-input-group { width: 100% !important; }
  .mdui-filters .mdui-filter-q input { width: 100% !important; }
  .mdui-filters .mdui-filter-geo { width: 100%; }
  .mdui-filters .arco-select { width: 100% !important; }
  .mdui-filters .arco-checkbox { grid-column: 1 / -1; white-space: nowrap; }
  .mdui-filters-more { grid-column: 1 / -1; }
  .filters > .ph-sep { display: none; }
  .filters > #btnNewOrg, .filters > #btnExport, .filters > #btnOrgBatch { flex: 1 1 auto; }
  .org-summary { margin: 8px 0 4px; justify-content: flex-start; }
  .org-toolbar-panel > .org-toolbar-body { padding: 12px 12px 8px; }
  .org-toolbar-main { gap: 8px; }
  .org-toolbar-main .primary { height: 32px; }
  .org-mobile-card { padding: 14px 14px 12px; }
  .org-mobile-reason { margin-top: 6px; font-size: var(--font-size-sm); color: var(--text); }
  .org-mobile-actions { display: flex; justify-content: flex-end; margin-top: 8px; }
  .org-mobile-cta { font-size: var(--font-size-sm); color: var(--accent-text); font-weight: 600; }
}

@media (max-width: 820px) {
  .push-table { display: block; width: 100%; min-width: 0; table-layout: auto; }
  .push-table thead { display: none; }
  .push-table tbody { display: block; }
  .push-table tr { display: flex; flex-wrap: wrap; gap: 8px 10px; padding: 12px; border-bottom: 1px solid var(--border); }
  .push-table td { display: block; border: 0; padding: 0; white-space: normal; font-size: var(--font-size-table); }
  .push-table td:nth-child(1) { flex: 1 1 100%; font-weight: 600; }
  .push-table td:nth-child(2) { display: flex; align-items: center; gap: 6px; color: var(--dim); font-size: var(--font-size-sm); }
  .push-table td:nth-child(2)::before { content: "启用"; }
  .push-table td:nth-child(3) { flex: 1 1 100%; display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
  .push-table td:nth-child(3)::before { content: "频率"; color: var(--dim); font-size: var(--font-size-sm); }
  .push-table td:nth-child(4) { flex: 1 1 100%; display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
  .push-table td:nth-child(4)::before { content: "推送到"; color: var(--dim); font-size: var(--font-size-sm); }
  .push-table td:nth-child(5) { flex: 1 1 100%; color: var(--dim); font-size: var(--font-size-sm); }
  .push-table td:nth-child(5)::before { content: "最近发送 "; }
}

@media (max-width: 820px) {
  #oppq { flex: 1 1 100%; width: 100%; min-width: 0; }
}

@media (max-width: 820px) {
  .matrix-wrap { display: block; width: 100%; max-width: 100%; overflow-x: auto; overflow-y: hidden; contain: paint; -webkit-overflow-scrolling: touch; }
  .matrix-wrap table.matrix { min-width: 780px; }
}

@media (max-width: 820px) {
  #subnav .seg { flex-wrap: nowrap; overflow-x: auto; max-width: 100%; }
  #subnav .seg a { flex: 0 0 auto; }
}

@media (max-width: 820px) {
  #btnLegend { display: none; }
}

/* ---------------------- 机构线索详情 ---------------------- */
.org-detail-head {
  position: sticky;
  top: var(--topbar-h);
  z-index: 20;
  margin: -24px -24px 16px;
  padding: 14px 24px;
  background: rgba(255, 255, 255, .96);
  border-bottom: 1px solid var(--border);
  box-shadow: var(--shadow-1);
  backdrop-filter: blur(10px);
}
.org-head-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.org-head-actions .mini { display: inline-flex; align-items: center; gap: 5px; }

.lead-summary {
  margin-bottom: 16px;
  padding: 16px 18px;
  background: var(--card);
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius);
  box-shadow: var(--shadow-1);
}
.lead-summary-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 24px; flex-wrap: wrap; }
.ls-intent { display: flex; align-items: baseline; gap: 8px; min-width: 190px; }
.ls-kicker { color: var(--dim-2); font-size: 12px; }
.ls-intent b { font-size: 32px; line-height: 1; letter-spacing: -1px; font-variant-numeric: tabular-nums; }
.ls-facts { display: flex; flex: 1; min-width: 420px; justify-content: flex-end; gap: 24px; flex-wrap: wrap; }
.ls-fact { display: grid; gap: 2px; min-width: 92px; }
.ls-fact span { color: var(--dim-2); font-size: 12px; }
.ls-fact b { font-size: 22px; line-height: 1.15; font-variant-numeric: tabular-nums; }
.ls-fact small { max-width: 150px; overflow: hidden; color: var(--dim); font-size: 12px; text-overflow: ellipsis; white-space: nowrap; }
.ls-fact-date b { font-size: 16px; }
.lead-summary-foot {
  display: flex;
  align-items: center;
  gap: 20px;
  flex-wrap: wrap;
  margin-top: 14px;
  padding-top: 10px;
  border-top: 1px solid var(--border);
  color: var(--dim);
  font-size: 13px;
}
.lead-summary-foot b { color: var(--text); font-weight: 600; }
.ls-foot-note { margin-left: auto; color: var(--dim-2); }

.detail-subnav { display: flex; gap: 6px; flex-wrap: wrap; margin: 0 0 16px; }
.detail-subnav .chip { cursor: pointer; }
.detail-jump-target { scroll-margin-top: calc(var(--topbar-h) + 76px); }

.recent-changes { margin-bottom: 24px; }
.section-head { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.section-head h2 { margin: 0; font-size: 16px; font-weight: 600; }
.section-head .sub { color: var(--dim-2); font-size: 12px; }
.section-head .spacer { flex: 1; }
.recent-list { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
.recent-list .ev {
  display: block;
  padding: 10px 12px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: 13px;
}
.recent-list .ev:last-child { border-bottom: 1px solid var(--border); }
.recent-list .ev .t { display: block; min-width: 0; margin-bottom: 4px; }
.recent-list .ev .ty { display: inline-block; margin-bottom: 4px; }
.recent-list .ev button { margin-top: 6px; }

.detail-layout { display: grid; grid-template-columns: minmax(0, 1fr) 360px; gap: 24px; align-items: start; }
.detail-main, .detail-rail { min-width: 0; }
.detail-main .panel, .detail-rail .panel { margin-bottom: 16px; }
.detail-section-head { display: flex; align-items: baseline; gap: 8px; margin: 0 0 10px; }
.detail-section-head h2 { margin: 0; font-size: 17px; font-weight: 600; }
.detail-section-head .sub { color: var(--dim-2); font-size: 12px; }
.detail-rail .dl { grid-template-columns: 72px 1fr; gap: 8px 10px; }
.detail-rail .row-flex { flex-wrap: wrap; align-items: center; }
.detail-rail input[type="text"] { width: auto; min-width: 0; flex: 1 1 120px; }
.detail-rail textarea { width: 100%; min-width: 0; }
.detail-rail .row-flex input[type="text"] { flex: 1 1 120px; }
.detail-rail .dl dd > .row-flex > input[type="text"] { min-width: 0; flex: 1 1 80px; }
.detail-rail .row-flex .primary { flex: none; }
.detail-meta-fold { margin-top: 10px; }

.detail-evidence-table-wrap { max-height: none; overflow: auto; }
.detail-evidence-table th:nth-child(2), .detail-evidence-table td:nth-child(2) { min-width: 240px; max-width: 420px; }
.evidence-badge-line { margin-top: 3px; }
.evidence-badge-line .badge { font-size: 11px; }
.table-foot { padding: 10px 16px; border-top: 1px solid var(--border); }

.market-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; }
.market-subtitle { margin: 0 0 8px; font-size: 14px; font-weight: 600; }
.market-note { margin-top: 8px; }
.market-grid .table-wrap { max-height: none; }

.empty-act { margin-top: 8px; }
.crm-form { margin: 0 16px 16px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--card); }
.crm-form > summary { padding: 10px 12px; color: var(--dim); cursor: pointer; font-size: 13px; }
.crm-form .body { padding: 10px 12px 12px; border-top: 1px solid var(--border); }
.crm-form-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 8px; align-items: center; }
.crm-form-grid input { width: 100%; }
.crm-check { display: flex; align-items: center; gap: 6px; white-space: nowrap; }
.crm-check input { width: auto; }

.detail-drawer { width: min(760px, 92vw); }
.detail-drawer .ent-drawer-bd { flex: 1; min-height: 0; padding: 16px; background: var(--bg); }
#detailDrawerBody.detail-drawer-events { display: flex; flex-direction: column; }
#detailDrawerBody.detail-drawer-events .ev-list { flex: 1 1 0; min-height: 0; overflow: auto; }
#detailDrawerBody.detail-drawer-events .drawer-pager { flex: none; margin-top: auto; }
.detail-drawer-backdrop { position: fixed; inset: var(--topbar-h) 0 0; z-index: 39; background: rgba(16, 24, 40, .28); }
.detail-drawer-backdrop[hidden] { display: none; }
body.detail-drawer-open { overflow: hidden; }
.evidence-list { display: grid; gap: 12px; }
.evidence-card { padding: 12px 14px; background: var(--card); border: 1px solid var(--border); border-radius: var(--radius-sm); }
.evidence-card-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.evidence-card-title { margin-top: 6px; }
.evidence-meta { display: grid; grid-template-columns: 72px 1fr; gap: 6px 10px; margin: 10px 0 0; font-size: 13px; }
.evidence-meta dt { color: var(--dim-2); }
.evidence-meta dd { margin: 0; color: var(--text); word-break: break-word; }

@media (max-width: 1200px) {
  .detail-layout { grid-template-columns: 1fr; }
  .detail-rail { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
  .detail-rail .panel { margin-bottom: 0; }
  .market-grid { grid-template-columns: 1fr; }
}
@media (max-width: 900px) {
  .org-detail-head { position: static; margin: 0 0 16px; }
  .recent-list { grid-template-columns: 1fr; }
  .ls-facts { min-width: 0; justify-content: flex-start; }
  .ls-foot-note { width: 100%; margin-left: 0; }
  .detail-rail { display: block; }
  .detail-rail .panel { margin-bottom: 16px; }
  .detail-drawer { width: min(520px, 100vw); }
}.detail-crm-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; align-items: start; margin-bottom: 16px; }
.detail-crm-grid .panel { margin-bottom: 0; }
@media (max-width: 1200px) { .detail-crm-grid { grid-template-columns: 1fr; } }.drawer-form { display: grid; gap: 12px; }
.drawer-form label { display: grid; gap: 6px; color: var(--dim); font-size: 13px; }
.drawer-form label > span { color: var(--dim-2); }
.drawer-form input { width: 100%; }
.drawer-form .drawer-check { display: flex; align-items: center; gap: 6px; }
.drawer-form .drawer-check input { width: auto; }
.drawer-actions { display: flex; justify-content: flex-end; padding-top: 4px; }
.changes-panel { margin-bottom: 16px; }
.changes-panel > h2 .spacer { flex: 1; }
.event-summary-list .ev { padding: 8px 16px; }
.event-summary-list .ev:last-child { border-bottom: 0; }
.drawer-pager { position: sticky; bottom: 0; z-index: 1; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 10px 0; background: var(--card); border-top: 1px solid var(--border); }
.drawer-pager-info { color: var(--dim); font-size: 12px; }
.drawer-pager-nums { display: flex; gap: 4px; margin-left: auto; }
.drawer-pager .mini[disabled] { opacity: .45; cursor: not-allowed; }
.drawer-pager .pager-num { min-width: 30px; justify-content: center; }
.drawer-pager .pager-num.is-current { background: var(--accent-weak); border-color: var(--accent); color: var(--accent-text); }.event-summary-list .ev { align-items: center; }
.event-summary-list .ev > span:nth-child(3) { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.detail-drawer .ev,
.detail-drawer .ev-head {
  display: grid;
  grid-template-columns: 84px 110px minmax(0, 1fr) auto;
  gap: 2px 10px;
}
.detail-drawer .ev {
  align-items: start;
  padding: 4px 0;
}
.detail-drawer .ev-head {
  position: sticky; top: 0; z-index: 2;
  align-items: center; padding: 6px 0 7px;
  border-bottom: 1px solid var(--border); background: var(--card);
  color: var(--dim); font-size: var(--font-size-sm); font-weight: 500;
}
.detail-drawer .ev-head > span:last-child { text-align: right; }
.detail-drawer .ev .t {
  grid-column: 1; grid-row: 1; min-width: 0;
  color: var(--dim-2); font-size: var(--font-size-sm); white-space: nowrap; font-variant-numeric: tabular-nums;
}
.detail-drawer .ev .ty {
  grid-column: 2; grid-row: 1; justify-self: start; align-self: start;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: var(--font-size-sm); line-height: 1.45;
}
.detail-drawer .ev .ev-body {
  grid-column: 3; grid-row: 1; min-width: 0;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; overflow-wrap: anywhere; line-height: 1.45;
}
.detail-drawer .ev > button {
  grid-column: 4; grid-row: 1; justify-self: end; align-self: start;
  margin: 0; white-space: nowrap;
}
.detail-drawer .ev-link {
  color: var(--accent-text); text-decoration: underline; text-underline-offset: 2px;
  overflow-wrap: anywhere;
}
/* 地区列给足单行宽度；更长的省市名仍由省略号 + title 兜底 */
#orgTableHost .col-region { width: 11.4%; }
