/* ══════════════════════════════════════════════════════════════════
   shell.css —— 外壳：reset / 顶层 chrome / 导轨 / 会话列表 / 分隔条 / 状态栏 / 站切换 / 宽度降级
   纪律：本文件不得出现字面色值，一律 var() 令牌（令牌在 tokens.css）。
   来源：mockup/中台-统一原型.html 6–8 / 419–558 / 805–817 / 853–895 / 1027–1030 行。
   ══════════════════════════════════════════════════════════════════ */

/* ── reset（原型 6–8 行）── */
*{box-sizing:border-box}
html,body{margin:0;height:100%}
body{background:var(--e0);color:var(--t1);overflow:hidden;
  font-family:var(--font-ui);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;
  font-variant-numeric:tabular-nums;text-rendering:optimizeLegibility}

/* ── 头像底色 12 档（字面值已令牌化到 tokens.css）── */
.c1{background:var(--av1)}.c2{background:var(--av2)}.c3{background:var(--av3)}.c4{background:var(--av4)}
.c5{background:var(--av5)}.c6{background:var(--av6)}.c7{background:var(--av7)}.c8{background:var(--av8)}
.c9{background:var(--av9)}.c10{background:var(--av10)}.c11{background:var(--av11)}.c12{background:var(--av12)}

/* ── 顶层 chrome：单层 52px（原 40 + 28 + 52 = 120 三层 → 2026-10-05 收窄为仅 modtabs）── */
/* ★ 2026-10-05 删：`.titlebar`(40px) / `.tlogo` / `.tname` / `.menubar`(28px) 整层几何删除，
   三个氛围读数（.engine / .live / .tstat）并入 `.modtabs` 行右端 `.topops`（见下方 `.topops` 段）。 */
/* ★ 2026-10-05 删：此处原有 `.tsearch` / `.tsearch .tk` 两条（居中搜索框 + 右侧 `Ctrl K` 键帽）。
   删因：那是**零绑定的空槽** —— 槽在、键没接（现取：活跃 `app/js/*.js` 里 `tsearch` 命中 0、
   无 metaKey 注册）⇒ 写着的 `Ctrl K` 是**空许诺**，点它按它都不发生任何事。
   裁定：`_verify/_reports/TEAM-ui-menubar-20260919-1135.md`（Q2「全局搜索」行 + §1 第 3 条
   「要么接线要么别写 Ctrl K」）；护栏 `_verify/menubar.mjs` 断言 `.tsearch` 不存在。
   ⚠ 删它顺带**消掉一类缺陷**：该框原为 `position:absolute` 居中，窄窗下会被右侧引擎胶囊压住
   （2026-09-17 实测 W=1000 重叠 79px）—— 现在**结构上不再可能**。见下方「顶栏让位」段。
   保留的顶栏构成：`.tlogo` + `.tname`（左）· `.engine` + `.tstat`（右，靠 `.engine` 的 auto 推过去）。 */
/* tstat 的 margin-left:auto 归零 —— 见下方 .engine 上的说明（两个 auto 会各推一段）。 */
.tstat{margin-left:0;display:flex;align-items:center;gap:6px;font-size:12px;color:var(--t2);
  border:1px solid var(--line2);border-radius:10px;padding:3px 9px;white-space:nowrap}
.tstat i{width:6px;height:6px;border-radius:50%;background:var(--accent);display:block;flex:0 0 auto}

/* ══ ★ AI 引擎胶囊 —— 顶栏主视觉之一（2026-09-17 · 首屏「AI 感」方案）════════════════
   它回答的是「AI 现在活着吗、忙不忙」，不是业务数据 ⇒ 只占顶栏一枚胶囊，不占业务区域。
   ★ 零字面色值：底色 --ai-surface、边框 --ai-line、脉冲点 --accent-text，四主题自动跟随。
   ★ 高度 24 与下方 .tstat 的 padding 后等高（两枚胶囊不齐会像两组互不相干的状态）。
   ⚠ 2026-10-05 改：已并入 `.topops`（modtabs 行右端），**不再**抢 `margin-left:auto`
     （把本组推到行右端的是 `.tabright` 的 auto；本胶囊与 live/tstat 靠 `.topops` 的 gap 排列）。 */
.engine{display:flex;align-items:center;gap:8px;height:24px;padding:0 11px;
  border-radius:12px;background:var(--ai-surface);border:1px solid var(--ai-line);
  font-size:11.5px;color:var(--t2);white-space:nowrap;flex:0 0 auto}
.engine .dot{width:6px;height:6px;border-radius:50%;background:var(--accent-text);flex:0 0 auto;
  box-shadow:0 0 8px 1px var(--accent-text);animation:pulse 2.4s ease-in-out infinite}
.engine b{font-weight:600;color:var(--accent-text)}
.engine em{font-style:normal;font-family:var(--font-num);color:var(--t1);font-weight:600}
.engine .sp{width:1px;height:10px;background:var(--line3)}
/* 脉冲：唯一一处装饰动画。`prefers-reduced-motion` 下关掉（下同）。 */
@keyframes pulse{0%,100%{opacity:1;transform:scale(1)}50%{opacity:.42;transform:scale(.82)}}
@media (prefers-reduced-motion:reduce){.engine .dot{animation:none}}

/* ★★ 顶栏让位（2026-09-17 实测 → 2026-10-05 三度修订）：引擎胶囊的窄窗收缩 ★★
   历史成因（刻意留住，防后人照旧样重画那个框）：
     `.tsearch` 原为 `position:absolute` **绝对居中**（left:50% + translateX），
     右侧多了 engine 之后实测 W=1000 重叠 79px。2026-10-05 该空槽**整条删除**
     （裁定见 `_verify/_reports/TEAM-ui-menubar-20260919-1135.md`）⇒「压住搜索框」结构上不再可能。
     对象也随顶栏瘦身换成**站标签 `.seg`**（原 `.tname` 已删）：engine 并入标签行右端后，
     窄窗下压住的是**标签文字**而不是程序名。
   让位顺序与状态栏同一口径（先撤次要读数、最后只留脉冲点）：
     W ≤ 1310  撤「响应 42ms / 推理队列 3」两个读数（.sp/.mt）
     W ≤ 1160  再撤文字「AI 引擎在线」，只留脉冲点（读数移到 title，悬停仍可读）
   ★ 断点依据（2026-10-05 三次实测，可用区 = W − 26 padding）：
     10 站标签宽版 1018px；1310 收读数后胶囊 99px ⇒ 标签+胶囊 1122px，W≥1148 才装得下，
     取 1310 收读数（标签+完整胶囊 246px=1269px，W≥1295，留 15px 余量）；
     1160 收文字后只剩点 26px ⇒ 标签+点 1048px，W≥1074，取 1160 留余量。
   判据：任一宽度下 `.engine` 与站标签 `.seg` 矩形**不得相交**（_verify/_check_top.mjs 逐宽断言）。 */
@media (max-width:1310px){
  .engine .sp,.engine .mt{display:none}
}
@media (max-width:1160px){
  .engine b{display:none}
  .engine{padding:0 9px}
  /* 原本这里（≤949 段）曾有 `.tbtn` 收文字（`padding:0 9px` / `.tx{display:none}`）两条死规则，
     已删 —— 那两枚按钮整块在更宽的断点收起，收文字的规则成了永不生效的死代码。 */
}

/* ══ ★ 实时指标带（2026-10-05 并入顶栏右端 .topops）：采集机 / 知识库 / 今日托管 ═══════════════════
   三枚脉冲点 = AI 色（--ai-1），与 AI 引擎胶囊那枚青玉点**刻意不同色** —— 引擎胶囊说"引擎在跑"，
   这里说"数据在流"，两件事两个色，一眼能分开。 */
.topops .live{display:flex;align-items:center;gap:15px;font-size:11.5px;color:var(--t3)}
.topops .live .it{display:flex;align-items:center;gap:5px;white-space:nowrap}
.topops .live .it::before{content:"";width:5px;height:5px;border-radius:50%;background:var(--ai-1);
  box-shadow:0 0 6px var(--ai-1);flex:0 0 auto}
/* ★ 降级态（`available===false` ⇒ renderOps 加 `.off`）：**灰点 + 去辉光** —— RULES 78 的
   「同形」病的 **app 侧落点**：正常读数 = 青玉（`--ai-1`）发光点，降级 = 无辉光的灰点，
   两者同屏可辨（修复「采集机 未接入」与「知识库 1,284 条」圆点长得一模一样）。
   色值只用既有令牌（`--txt3` = tokens.css:50 兼容别名 → `--t3`），**不新增字面色值**；
   `--t3` 四主题单点派生 ⇒ 四主题下均成立。 */
.topops .live .it.off::before{background:var(--txt3);box-shadow:none}

/* ══ ★ 模块标签行右端的两枚按钮（新建广播 / 值班表）══════════════════════════════════
   落在标签行右端 = 该行结构上本就为空的区域（左对齐的标签打不到那儿，见 §7.3 实测）
   ⇒ 零遮挡。`.hi` 那枚走 AI 色系，表示"这是 AI 相关动作"。 */
.modtabs .tabright{margin-left:auto;display:flex;align-items:center;gap:8px;flex:0 0 auto}
/* ★ 顶栏右端三氛围读数组（2026-10-05 改）：engine / live / tstat 并入 modtabs 行右端。
   靠上方 `.tabright` 的 margin-left:auto 把「两按钮 + 本组」整体推到行最右，本组**不再**加 auto
   （两个 auto 会各抢一段、中间空一块）。
   ⚠ 窄窗让位顺序（先撤次要读数）：≤1439 收实时指标带（live，三读数里最次要）→
     ≤1279 再收实时指标（tstat）→ AI 引擎胶囊沿既有 1279/949 断点逐级收缩（见上「顶栏让位」段）。 */
.topops{display:flex;align-items:center;gap:10px;flex:0 0 auto;white-space:nowrap}
/* ★ 让位断点（2026-10-05 三次实测订正）：一行装「10 标签 + 按钮 + 三读数」，逐级收 ——
   ≤1964：收实时指标带 live（359px，三读数最宽，并单层后仅 ≥1965 超宽屏可见）；
   ≤1420：收实时指标 tstat（104px）；
   按钮 .tabright 的让位见下方 `.tbtn` 段；引擎胶囊收缩见「顶栏让位」段。
   ★ 断点依据（可用区 = W − 26）：标签 1018 + 按钮 182 + 完整胶囊 246 + tstat 104 + gap
     = 1570px ⇒ 按钮在 W≥1596 才放得下，故按钮断点 1600；tstat 需 W≥1409，取 1420 留余量。 */
@media (max-width:1964px){.topops .live{display:none}}
@media (max-width:1420px){.topops .tstat{display:none}}
.tbtn{height:28px;padding:0 12px;border-radius:8px;border:1px solid var(--line2);
  background:var(--e3);display:flex;align-items:center;gap:6px;font-size:12px;color:var(--t2);
  cursor:pointer;user-select:none;white-space:nowrap;transition:border-color 120ms,color 120ms}
.tbtn:hover{border-color:var(--line3);color:var(--t1)}
.tbtn.hi{border-color:var(--ai-line);background:var(--ai-surface);color:var(--ai-fg);font-weight:600}
/* ★ ≤1279 **整块收起**（2026-09-17 实测订正）。原因不是"挤"，而是它俩**正好压在
   演示浮层的窄档落点上**：≤1279 时浮层落点是 `.modtabs` 行右端（`top:76px`，见 `.demo` 段），
   而那条规则的立论是「标签行**右端结构上为空**、实测最右有框子孙恒停在 x=718」。
   这两枚按钮一加，最右有框子孙移到 x=937（W=950）⇒ 浮层左缘 742 与它重叠 195px，
   `overlay.mjs` B 组当场报 3 例 FAIL（W=950 / 1000 / 1200）—— 脚本先发现的，不是肉眼。
   ⇒ 只能留一个：浮层挪走的三条路早在 §7.3 逐条否掉（加宽右栏 / 左移压住输入框 / 收掉浮层），
     且浮层是本原型**唯一的主题切换入口** ⇒ 让位的是这两枚按钮。
   ≥1280 时浮层落点回到 `bottom:96px`，两者不在同一带，按钮照常显示（1440 设计稿不受影响）。
   ⚠⚠ 这条必须写在**基规则 `.tbtn{…}`、`.modtabs .tabright{display:flex}` 之后**：
      同为 0,2,0 特异性时**后面的赢**，写在前面会被基规则的 `display:flex` 静默盖掉 ——
      我第一版正是写在文件开头的 1279 段里，脚本报的还是"tabright 在 937"，查了半天才发现
      不是逻辑错、是**源序错**（RULES 57 的教科书形态）。
   ★ 2026-10-05 三次修订：隐藏断点 1279 → 1359 → **1600**。顶栏并单层后，一行装
     「标签 1018 + 按钮 182 + 完整胶囊 246 + tstat 104」= 1570px ⇒ W<1596 就溢出，
     按钮让位（读数优先、按钮次要）。断点取 1600 留余量（见「实时指标带」段注释）。 */
@media (max-width:1600px){
  .modtabs .tabright{display:none}
}
/* 标签上的计数徽标：红 = 待办（要说"有事"），软标 = 提示（说"有变化"）。
   与既有的 `.seg .mini`（纯文字后缀，如「待开发」）是两件事，不合并。
   ★ M3-D §4.5：中栏筛选行「待处理」徽标 `.fchip .pn` 与本行**共用同一段声明**
     （「同一个数、同一个样子」必须由**同一处**声明保证，不是"两处抄成一样"）。
     判据 m3.pn.same.size：`.pn` 与 `.seg .b` 的 minWidth/height/borderRadius/fontSize/padding 逐项相等。 */
.seg .b,.pn{min-width:17px;height:17px;padding:0 5px;border-radius:9px;background:var(--clay);
  color:var(--on-clay);font-size:10.5px;font-weight:700;display:flex;align-items:center;
  justify-content:center;flex:0 0 auto}
.seg .b.soft{background:var(--amber-bg);color:var(--amber)}

/* ★ 2026-10-05 删：`.menubar` 整层（28px 几何 + 假菜单 span 样式 + `.mr` 版本槽）已随 HTML 删除；
   实时指标带 `.live` 并入 `.modtabs` 行右端 `.topops`（见上方 `.topops .live` 段）。 */

/* ── 模块导航（8 站）── */
.modtabs{flex:0 0 52px;height:52px;display:flex;align-items:stretch;gap:5px;padding:0 13px;
  background:var(--e1);position:relative;z-index:5}
.seg{position:relative;isolation:isolate;display:flex;align-items:center;gap:8px;padding:0 14px;
  font-size:14px;font-weight:500;letter-spacing:.02em;color:var(--t-nav);
  cursor:pointer;user-select:none;transition:color 120ms linear;
  white-space:nowrap}
.seg::before{content:"";position:absolute;left:0;right:0;top:8px;bottom:8px;border-radius:10px;
  background:transparent;z-index:-1;transition:background 120ms linear}
.seg svg{color:currentColor;flex:0 0 auto}
.seg:hover{color:var(--t1)}
.seg:hover::before{background:var(--face-hover)}
.seg:active::before{background:var(--face-press)}
.seg.on{color:var(--t1)}
.seg.on::before{background:var(--accent-wash)}

/* ★ 2026-10-05 无边框桌面壳：顶栏拖拽区 + 窗口控制按钮（只在 .is-desktop 下显示）。
   拖拽区 = 站标签与右侧按钮之间那块 flex 空白（flex:1 占满、窄窗收缩为 0），
   靠 `data-tauri-drag-region` 属性被 Tauri 识别为窗口拖动触发区（无需 -webkit-app-region，
   Tauri 2 走 JS 事件监听）。
   窗口按钮 = 最小化 / 关闭（收进托盘），mac 无边框后替代红黄绿。 */
.dragzone{display:none}
body.is-desktop .dragzone{display:block;flex:1 1 auto;min-width:0;height:100%;cursor:default}
.winctl{display:none}
body.is-desktop .winctl{display:flex;align-items:center;gap:2px;flex:0 0 auto;margin-left:6px}
.wbtn{width:26px;height:26px;display:flex;align-items:center;justify-content:center;
  border:none;border-radius:6px;background:transparent;color:var(--t3);cursor:pointer;
  transition:background 120ms,color 120ms}
.wbtn:hover{background:var(--face-hover);color:var(--t1)}
.wbtn.wclose:hover{background:var(--danger);color:var(--on-clay)}
.seg.on svg{color:var(--accent-text)}
.seg .mini{font-size:11px;font-weight:400;letter-spacing:0;margin-left:2px;opacity:.66}

/* ── 主区：rail + list + spl + chat + spl + side（或站内容层）── */
/* ★ `position:relative` 是**必需**的（2026-09-17）：客户详情抽屉 `.dtl` 与窄窗右栏抽屉
   都靠 `position:absolute` 锚在 `.app` 上。漏了它，absolute 会一路上跑到 body/视口 ——
   落点"看着差不多、其实锚错了"，而且不报任何错。
   （文件末尾 949 断点里还有一份同名声明 —— 语义上已冗余，保留是因为它同时用来说明
    "窄窗下 .side 要脱离文档流"；两处值相同，不存在漂移。） */
.app{flex:1 1 auto;min-height:0;display:flex;background:var(--e1);overflow:hidden;position:relative}
/* ★ 窄窗右栏抽屉的遮罩（2026-09-17）：宽窗**永不存在**（窄窗规则见文件末尾 949 断点）。
   必须在这里显式 display:none —— 它是 `.app` 的 flex item，不声明就会占位、撑开布局。 */
#scrim{display:none}
.rail{flex:0 0 68px;width:68px;background:var(--e1);display:flex;flex-direction:column;
  align-items:center;padding:12px 0 12px;border-right:1px solid var(--line)}
.av{display:flex;align-items:center;justify-content:center;color:var(--on-clay);font-weight:500;flex:0 0 auto}
.acct{position:relative;width:46px;height:46px;display:flex;align-items:center;justify-content:center;
  margin-bottom:9px;cursor:pointer}
.acct .av{width:40px;height:40px;border-radius:50%;font-size:15px;opacity:.72;transition:opacity 120ms}
.acct:hover .av{opacity:1}
.acct.active .av{opacity:1;box-shadow:0 0 0 2px var(--e1),0 0 0 3px var(--accent)}
/* ★ 2026-10-03（用户令「把左侧微信切换做成真实的」）：`.acct` 现在是真按钮
   （role=button + tabindex=0），补一个键盘焦点环，与鼠标 hover 区分开。 */
.acct:focus-visible{outline:none}
.acct:focus-visible .av{box-shadow:0 0 0 2px var(--e1),0 0 0 3px var(--accent)}
.acct .badge{position:absolute;top:0;right:0;min-width:17px;height:17px;padding:0 5px;border-radius:9999px;
  background:var(--clay);color:var(--on-clay);font-size:11px;font-weight:500;display:flex;align-items:center;
  justify-content:center;box-shadow:0 0 0 2px var(--e1)}
.rail-div{width:26px;height:1px;background:var(--line2);margin:5px 0 11px}
.fn{position:relative;width:40px;height:40px;border-radius:10px;display:flex;align-items:center;
  justify-content:center;color:var(--t3);margin-bottom:5px;cursor:pointer;transition:background 120ms,color 120ms}
.fn:hover{background:var(--face-hover);color:var(--t2)}
.fn.active{background:var(--sel);color:var(--accent-text)}
.grow{flex:1}
.me{width:38px;height:38px;border-radius:50%;background:var(--e4);color:var(--t2);
  display:flex;align-items:center;justify-content:center;cursor:pointer}
.plat-h{font-size:10px;letter-spacing:.5px;color:var(--t3);margin:1px 0 7px}
/* ★ 左栏号头像红点（2026-10-04 · 用户令「红灯亮到 1号/2号各自头像上，不是『微信』标题上」）──
   · 落点 = 每个号头像 `.acct` 框的右上角（top/right 贴角，与 `.acct .badge` 同款定位）——
     8px 圆点正好压在 40px 头像圆右上边缘、一半里一半外（微信角标观感）。
   · 用色与 `.acct .badge` 同源（--clay，红角标族同一视觉语言）；描边 2px 用 --e1
     （左栏底色）—— 红点浮在头像上不与头像糊边（同 badge 的 box-shadow 做法）。
   · 显隐 = `[hidden]` 属性（UA 默认 display:none 直接生效 ⇒ CSS **零**显隐分支；
     JS 只翻 hidden —— 见 shell.js::renderRailDot）。
   · 判据件：`_verify/_probe_raildot.mjs`（同日改号级，`renderRailDot`/`applyAcctUnread` 出口直调）。 */
.acct .rail-dot{position:absolute;top:0;right:0;width:8px;height:8px;border-radius:50%;
  background:var(--clay);box-shadow:0 0 0 2px var(--e1)}
.slot{width:40px;height:40px;border-radius:50%;display:flex;align-items:center;justify-content:center;
  font-size:12px;color:var(--t3);margin-bottom:9px;cursor:default;
  box-shadow:inset 0 0 0 1px var(--line2)}
.slot:hover{color:var(--t2);box-shadow:inset 0 0 0 1px var(--line3)}
/* ★★ 「抖」位（2026-10-05）—— 三种形态，各自**如实**：
     .slot          未登记账号的占位（快手/小红书仍用它，不可点）
     .slot.off      抖音位「已接通但库里没这一号」⇒ 虚线灰化（区别于"未接入"）
     .acct.dy       抖音号本体（可点，走 auth.act('switch') 同微信一条路）
   ⚠ `.acct.dy[disabled]` 画成不可点但**保留 title 解释** ——
     点了不给出任何反馈才是「点了没反应但不报错」那类缺陷。 */
.slot.off{background:transparent;border-style:dashed;box-shadow:inset 0 0 0 1px var(--line2);
  color:var(--t3);cursor:help}
.acct.dy .av{background:var(--bg2);color:var(--t2);font-size:12px}
.acct.dy .rail-slot.off{background:transparent;border:1px dashed var(--line-strong);
  color:var(--t3);font-size:8px;line-height:1;letter-spacing:0;padding:0 2px;min-width:0;height:11px}
.acct.dy[disabled]{cursor:not-allowed;opacity:.55}
.acct.dy[disabled]:hover{opacity:.8}
/* ★★ 「外观」入口（2026-09-19 用户拍板 · 落在左栏底部、`.me` 正上方）★★
   ⚠⚠ **本项为用户拍板的红线例外** —— 本项目既有红线是「左栏图标不带文字」。用户原话是
      「改到左下角，头像上方，叫外观」，即**要求在按钮上看见「外观」二字**（设计规格 §二.1 乙案）。
      用户已明确认账破此红线；后人审计**不得**据此把文字改回纯图标（改回即违背用户指令）。
   · 尺寸 40(宽) × 46(高)：图标 19 + 2 间隔 + 10px 字 ≈ 31，加上下留白 = 46；
     68px 栏内左右各净 14px（68 − 40 = 28，`align-items:center` 均分），**不溢出**。
   · 与 `.me` 间距 8px（`.acct`/`.slot` 是 9px，底部簇取 8 更紧）；`.grow{flex:1}` 仍把本簇顶到栏底。
   · 颜色全走令牌（组件样式零字面色值）：默认 `--t3` / hover `--face-hover` / 展开 `--sel`+`--accent-text`。 */
.rail-app{width:40px;height:46px;margin-bottom:8px;border:0;background:none;cursor:pointer;
  border-radius:var(--r-md);color:var(--t3);font-family:inherit;
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;
  transition:background 120ms,color 120ms}
.rail-app:hover{background:var(--face-hover);color:var(--t2)}
.rail-app[aria-expanded="true"]{background:var(--sel);color:var(--accent-text)}
.rail-app svg{display:block}
.rail-app .rl-tx{font-size:10px;line-height:1;letter-spacing:.3px}
.rail-app:focus-visible{outline:2px solid var(--focus);outline-offset:1px}

/* ── 中栏列表 ── */
.list{flex:0 0 var(--list-w,296px);width:var(--list-w,296px);background:var(--e1);
  display:flex;flex-direction:column;min-height:0}
.list-hd{flex:0 0 auto;padding:12px 15px 8px}
.hd-top{display:flex;align-items:baseline;justify-content:space-between;margin-bottom:9px}
.ttl{font-size:15px;font-weight:500;color:var(--t1)}
.cnt{font-size:12px;color:var(--t3)}
.search{height:32px;border-radius:10px;background:var(--e4);display:flex;align-items:center;gap:8px;
  padding:0 10px;color:var(--t3);
  box-shadow:inset 0 0 0 1px var(--hairline);
  transition:box-shadow var(--dur-1)}
.search:focus-within{box-shadow:inset 0 0 0 1px var(--hairline),0 0 0 2px var(--focus)}
.search .search-ico{flex:0 0 auto}
.search input{flex:1 1 auto;min-width:0;width:100%;border:0;background:transparent;padding:0;
  outline:none;color:var(--t1);font-size:13px;font-family:var(--font-ui)}
.search input::placeholder{color:var(--t3);opacity:1}
.search .search-x{flex:0 0 auto;width:16px;height:16px;display:flex;align-items:center;
  justify-content:center;border:0;background:transparent;padding:0;border-radius:50%;
  cursor:pointer;color:var(--t3);font-size:14px;line-height:1;font-family:var(--font-ui)}
.search .search-x:hover{background:var(--face-hover);color:var(--t1)}
.search .search-x:focus-visible{outline:2px solid var(--focus);outline-offset:1px}
.search .search-x[hidden]{display:none}
.search-empty{display:flex;flex-direction:column;align-items:center;gap:8px;padding:36px 16px;text-align:center}
.search-empty .se-ico{color:var(--t3);flex:0 0 auto}
.search-empty .se-t1{font-size:13px;color:var(--t1);line-height:1.5}
.search-empty .se-t2{font-size:12px;color:var(--t3);line-height:1.6}
.filters{flex:0 0 auto;display:flex;gap:7px;padding:0 15px 8px}
.fchip{display:flex;align-items:center;padding:3px 10px;border-radius:9999px;background:var(--e4);
  color:var(--t2);font-size:12px;white-space:nowrap;cursor:pointer;transition:background 120ms,color 120ms}
.fchip:hover{color:var(--t1)}
.fchip.on{background:var(--sel);color:var(--accent-text);font-weight:500}
/* ★M3-D §4.1：待处理 chip 的计数徽标与文案之间留 6px（只影响多子元素的 todo chip，其余 chip 是纯文字） */
.fchip .pn{margin-left:6px}
.rows{flex:1 1 auto;min-height:0;overflow-y:auto;overflow-x:hidden}
.rows::-webkit-scrollbar{width:8px}
.rows::-webkit-scrollbar-thumb{background:var(--scroll);border-radius:4px}
.rows::-webkit-scrollbar-track{background:transparent}
.row{position:relative;min-height:66px;display:flex;align-items:center;gap:11px;padding:0 15px;
  cursor:pointer}
.row:hover{background:var(--face-hover)}
.row.on{background:var(--face-on)}
.row.on::before{content:"";position:absolute;left:0;top:0;bottom:0;width:2px;background:var(--accent)}
/* §10.10.4 焦点可见：键盘到会话行（role="option"）⇒ 既有 --focus 环（不新增色）。
   行通栏、且 `.rows` 有 overflow-x:hidden ⇒ 用负 offset 把环画在盒内，避免右缘被裁。 */
.row:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
.row .av{width:42px;height:42px;border-radius:10px;font-size:15px}
.row .mid{flex:1;min-width:0}
/* ★ 规格 §1.1：list 收到 min 252 时，.mid 可用宽 = 252 − 83 = 169px，名字不得压到右侧绝对定位的时间上
   （83 = .row 左右内边距 15+15 + .row .av 42 + .row gap 11；list.min 再变时按此式重算） */
.row .nm{font-size:15px;font-weight:500;margin-bottom:4px;white-space:nowrap;overflow:hidden;
  text-overflow:ellipsis;color:var(--t1);display:flex;align-items:center;padding-right:44px}
.row .pv{font-size:13px;color:var(--t3);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.row .pv .todo-src{font-size:11px;font-weight:500;padding:1px 6px;border-radius:6px;margin-right:6px}
/* ★ 名字行状态标（2026-09-17）：与待处理视图的 `.todo-src` **共用同一套色号** ——
   所以四组色对写在**同一个声明**里（一条规则服务两个消费者）。
   这是「同一个概念不能同屏两种颜色」的物理保证，不是风格选择：用户点一下「待处理」
   就会同时看到同一个来源的另一种颜色，两边分开写迟早会漂。
   要改色只改这一处。 */
.row .pv .todo-src.s1,.row .nm .st-chip.s1{background:var(--amber-bg);color:var(--amber)}
.row .pv .todo-src.s2,.row .nm .st-chip.s2{background:var(--info-bg);color:var(--info)}
.row .pv .todo-src.s3,.row .nm .st-chip.s3{background:var(--violet-bg);color:var(--violet)}
.row .pv .todo-src.s4,.row .nm .st-chip.s4{background:var(--stop-bg);color:var(--stop)}
/* 名字行里的状态标：尺寸与 `.ntag`（自动/手动）**逐项相同** ——
   名字行只挂一个状态（见 chat.js renderRows 的注释），两者永不并存，
   尺寸若不同就会出现"换个筛选、名字行高度跳一下"。 */
.row .nm .st-chip{display:inline-block;font-size:11px;font-weight:500;line-height:16px;
  padding:0 6px;border-radius:6px;margin-left:6px;vertical-align:1px;white-space:nowrap;flex:0 0 auto}
/* ★ 2026-10-15：待处理视图源④（催办）行内话术 + 发送按钮。话术单行截断（不撑高行），
   发送按钮恒在右端（与 `.rt` 时间列错开，按钮 `position` 靠 `.pv` 的 padding-right 让位）。 */
.row .pv .outreach-txt{color:var(--t2);margin-right:8px}
.row .pv .ck-send{position:absolute;right:15px;top:10px;font-size:12px;font-weight:500;
  padding:3px 10px;border-radius:6px;border:none;cursor:pointer;background:var(--accent);
  color:var(--on-accent);line-height:1}
.row .pv .ck-send:hover{opacity:.88}
.row .rt{position:absolute;right:15px;top:13px;font-size:11px;color:var(--t3)}
.row .badge{position:absolute;left:44px;top:8px;min-width:17px;height:17px;padding:0 5px;border-radius:9999px;
  background:var(--clay);color:var(--on-clay);font-size:11px;font-weight:500;display:flex;align-items:center;
  justify-content:center;box-shadow:0 0 0 2px var(--e1)}
.row .star{color:var(--amber)}
.empty-note{font-size:13px;color:var(--t3);padding:22px 16px;line-height:1.7}
.ntag{display:inline-block;font-size:11px;font-weight:500;line-height:16px;padding:0 6px;border-radius:6px;
  margin-left:6px;vertical-align:1px;white-space:nowrap;flex:0 0 auto}
.ntag.auto{background:var(--accent-wash);color:var(--accent-text)}
.ntag.manual{background:var(--info-bg);color:var(--info)}
/* ★ 2026-10-03：半自动档会话行尾「候选待选」轻标（候选卡已从「待处理」剥离，改由本标承接）。 */
.ntag.cand{background:var(--violet-bg);color:var(--violet)}
.hd-act{margin-left:auto;display:flex;align-items:center;gap:8px;flex:0 0 auto}
.todo-btn{display:flex;align-items:center;gap:6px;font-size:12px;font-weight:500;color:var(--amber);
  background:var(--amber-bg);border-radius:8px;padding:4px 10px;cursor:pointer;white-space:nowrap}
.todo-btn:hover,.todo-btn.on{box-shadow:inset 0 0 0 1px var(--amber)}
.todo-btn .n{font-variant-numeric:tabular-nums}
.list-ctl{flex:0 0 auto;padding:0 15px 6px;display:flex;flex-direction:column;gap:7px}
.list-ctl:empty{display:none}

/* ── 分隔条（规格 §1.2）：1px 布局占位，9px 热区靠 ::after 外扩（不占布局宽）── */
.spl{flex:0 0 1px;width:1px;position:relative;z-index:5;cursor:col-resize;background:transparent}
.spl::after{content:"";position:absolute;top:0;bottom:0;left:-4px;width:9px}
.spl::before{content:"";position:absolute;top:0;bottom:0;left:0;width:1px;
  background:transparent;transition:background var(--dur-1)}
.spl:hover::before{background:var(--line2)}
.spl.dragging::before{background:var(--accent)}
.spl.dragging{background:var(--sel)}
.spl:focus-visible::before{background:var(--accent)}
.spl{outline:none}
.spl:focus-visible{outline:2px solid var(--focus);outline-offset:-1px}
body.colresize{cursor:col-resize;user-select:none}

/* ── 站内容层（真：客户 / 假：.ph）——由 .app 上的模式类切换 ── */
#crmbody{display:none;flex:1 1 auto;min-width:0;min-height:0;background:var(--e2);overflow:hidden}
.app.crmmode > .list,
.app.crmmode > .chat,
.app.crmmode > .side,
.app.crmmode > .spl{display:none}
.app.crmmode > #crmbody{display:flex;flex-direction:column}
.card{background:var(--e3);border:1px solid var(--hairline);border-radius:var(--r-md)}

/* ── 状态栏 ── */
.statusbar{flex:0 0 26px;height:26px;display:flex;align-items:center;gap:8px;padding:0 13px;
  background:var(--e1);border-top:1px solid var(--line);font-size:12px;color:var(--t2)}
.statusbar .dot{width:7px;height:7px;border-radius:50%;background:var(--accent);flex:0 0 auto}
.statusbar .dot.off{background:var(--amber)}
.statusbar b{color:var(--t1);font-weight:500}
.statusbar .sep{color:var(--e5)}
.statusbar .sr{margin-left:auto;display:flex;align-items:center;gap:16px}
.statusbar .sr .warn{color:var(--amber);font-weight:500}
.statusbar .pause{border:1px solid var(--line2);border-radius:6px;padding:1px 8px;color:var(--t1);
  font-weight:500;cursor:pointer}
.statusbar .pause:hover{border-color:var(--line3)}
.statusbar .toast{color:var(--accent-text);font-weight:500}
.statusbar .narrow{color:var(--amber);font-weight:500}
/* ── ★ 状态栏 AI 读数的**分级让位**（2026-09-17）──────────────────────────────
   类名即它自己的下限宽度：`r1100` = 从 1100px 起才显示（与顶栏 `.engine` 同一套做法，
   那里是 1279 / 949 两档，这里是 1439 / 1279 / 1099 三档）。
   ★ 为什么必须分级：这行 12px 字 + gap 8，三段 AI 读数合计 ≈ 420px，与左侧的值班/连接、
     右侧的待处理/暂停**同屏放不下** —— 窄窗必然被 `overflow` **静默剪裁**（本项目头号忌讳：
     元素还在、只是尾巴没了、退出码 0）。判据由 `_verify/_check_sb.mjs` 逐宽度实测。
   ★ 断点只在 CSS 里判：JS **不判宽度**（判了就等于把断点抄成两处，迟早对不上；
     与本项目「窄窗右栏」那条纪律同源）。 */
.statusbar .num{font-variant-numeric:tabular-nums;font-family:var(--font-num)}
@media (max-width:1439px){ .statusbar .r1440{display:none} }
@media (max-width:1279px){ .statusbar .r1280{display:none} }
@media (max-width:1099px){ .statusbar .r1100{display:none} }

/* ── 【历史段头 · 右下浮层时期】演示控制浮层（右下角 · 原型 820–851 行同款；任务 #4 第 7 条 + 任务 #6 落点修正）──
   ★★ 2026-09-19 勘误（留痕，不删考证）：本段描述的「右下浮层 / right:14px / bottom:96px / 折叠把手 /
     窄窗整块 display:none」**均已作废** —— 用户拍板把入口迁入左栏底部、面板改为**左锚右展**
     （left:76 / width:244 / 默认关闭）。**现行口径见紧接本段之后的「2026-09-19 用户拍板」块**。
     下列成因考证保留，仅供考据（右锚时代的几何推导已不适用于左锚形态）。
   原为「顶栏常驻主题切换器」—— 违反「不新增常驻区域」⇒ 改为原型那种右下浮层（原型叫「演示控制」）。
   切换机制（theme.js + 四主题令牌）一字未动，只改挂载位置。
   ★★ 任务 #6 落点修正（复核 #5 实测：这是结构性冲突，不是坐标没调好）：
     浮层挂在【视口】右下，而右栏底部可点区 `.side-bottom` 挂在【内容】右下 ——
     当 `.app` 出横向滚动条时，内容底与视口底错开，两者必然叠在一起。
     旧值 bottom:38px ⇒ 折叠态也 5/5 挡住右栏「查看」链路；W≤1240 连「发送」按钮都压（12–13 点）。
     规格 §7.3 定值 **bottom:96px**：`.side-bottom` 高度随宽度变化（>1281px 时 31px；≤1240px 时 50px，
     `.xa` 换行）⇒ 最坏顶边在 视口底−88px；96 = 状态栏 26 + 最坏高 50 + 呼吸 20，两种高度下都留 ≥6px 真余量。
     ⇒ **只挪 right 没用，必须抬 bottom**（right:14px 按规格不动）。
   ★ 宽度 = 194px（= `side.min 208 − right 14`）—— 第一版是 236px（照原型），实测 W≤1240 时浮层左缘
     越过会话区右缘，**盖住输入框 `.inputbox` 右侧 21px、档位 `.seg3` 右端（auto 段）**（折叠态也盖）。
     规格 §7.3 对 `right:14px` 的意图是「在右栏横向投影内，避免压住中间三栏」，236+14=250 > 208 违背该意图。
     ⇒ 取 194 后：**W ≥ 950 时浮层左缘 = 会话区右缘，与 `.composer` / `#sp2` / 状态栏 0 相交**。
   ★ **W ≤ 949 无解 ⇒ 降级隐藏**（任务 #6 复核复验 + 本人 `overlay.mjs` ①b 复现）：
     W≤949 时 `.app` 出横向滚动条（§6.2 顺序 5），会话区右缘**固定在内容坐标 741**，而浮层锚在
     **视口**右下 ⇒ 左缘 = `W − 208` < 741，必然压进会话区：W=900 压 `.inputbox` **29×25px²**、
     W=880 压 **49×25px²**（**折叠态也压**），W=949 连 1px `#sp2` 都相交 35px²。
     ⇒ 在 949 断点处 `display:none`（见下方 `@media (max-width:949px)`），连演示件一起收掉：
     与 §6.2 顺序 5 同一断点、W≥950 正常切主题不受影响、下一轮迁入「设置」站后本行自动作废。
   ★ 默认折叠（`index.html` 上带 hide 类）：展开态在【客户站】必压表格右下「操作」列末行 ——
     这是**有意允许**的（用户主动调出的临时浮层，收起即恢复；规格 §7.3「展开态允许遮挡」），不是漏检。
     默认折叠 + bottom:96 + 宽度 194 + 窄宽降级后，对话站 W≥950 全宽全态 0 遮挡（`overlay.mjs` 逐点断言）。
   ★ 下一轮迁入「设置」站：把 <div class="demo"> 里的 #themebar 卡片搬进「设置」站内容层即可，
     theme.js 只认 #themebar，无需改逻辑（终局形态 = 结构性零遮挡）。
   注：本元素 id = `#demo`（沿用原型名）；判据行前缀 `demo.float.*`。规格 §7.3「元素命名（唯一写法）」块同此。 */
/* ★★ 2026-09-19 用户拍板：入口迁入左栏、面板**左锚右展**（原右下浮层）。几何**全部可算、无悬空值**：
   · left  = `LAYOUT.rail(68) + gap(8)` = 76px  ⇒ 与左栏右缘留 8px 呼吸，**永不盖住入口自身**。
   · width = `LAYOUT.listMin(252) − gap(8)` = 244px ⇒ 面板横跨 x∈[76,320]。
   · 核心不变量（`_verify/consistency.mjs` 单点对账）：`gap + width == LAYOUT.listMin`
     ⇒ 面板右缘 320 恒 ≤ 会话列表右缘 320 ⇒ 会话区左缘(= rail+listW+spl) ≥ 321 ⇒ **永不相交会话区/输入框**。
     宽窄窗**同一条式**（锚在左，与断点无关）—— 这正是原来 ≤949 必须整块收掉的成因被消除的地方。
   · bottom = 84px：对齐入口下缘（= 视口底 − 状态栏26 − rail下内边距12 − `.me`38 − 间距8 = 视口底−84）。
   · z-index 60：高于内容层（`.dtl` 42 / `#scrim` 40）；语义 = 内容之上的非模态浮层。
   · 默认**关闭**：`display:none`；`.app.appearopen` 时才显示（开合态由 shell.js 单点投影到 `.app`）。
   · 低窗保命：`max-height` + 主体滚动（面板顶 = 视口底 − 84 − H 须 ≥ 标题/标签区底 ⇒ H ≤ 100vh − 208）。
     用 flex 列 + `.db{overflow-y:auto}` 实现（标题栏固定、主体滚动），不引第二套浮层语言。 */
.demo{position:fixed;left:76px;bottom:84px;z-index:60;font-size:12px;color:var(--t2);
  background:var(--demo-bg);border:1px solid var(--line2);border-radius:14px;
  box-shadow:0 16px 40px -8px var(--sh-2);width:244px;display:none;
  max-height:calc(100vh - 208px);flex-direction:column;overflow:hidden}
.app.appearopen ~ .demo{display:flex}
/* 「外观」面板的**点外关闭**捕获层：默认不占位；仅在面板打开时覆盖全窗（透明，只接点击，见 index.html）。
   z-index 59 < 面板 60 ⇒ 面板自身仍可点；盖住其余一切 ⇒ 「点面板外任何地方」先收起面板。 */
#appcloak{display:none}
.app.appearopen ~ #appcloak{position:fixed;inset:0;z-index:59;display:block}
/* ★★ 详情抽屉打开时，「外观」面板**让路**（机制沿用 2026-09-17；成因随锚点变化而收窄）★★
   机制：`.app.dtlopen ~ .demo{display:none}` —— 详情一开、面板先隐；关详情即恢复。
   ★ 2026-09-19 复核（面板已改**左锚** left:76 / width:244 ⇒ 横跨 x∈[76,320]）：
     · **宽窗**：详情左缘 = `.app` 右缘 − 46vw（min520/max760）⇒ 与面板右缘 320 相差 300px 量级
       ⇒ 两者**结构上不再相交**；此时"让路"对几何无影响（顺手把演示件收起，无害）。
     · **窄窗**：详情**铺满可视宽**（见本文件 949 段）⇒ 会盖住左锚面板 ⇒ **此时让路是必需的**。
   ⇒ 保留本规则（宽窗无害 / 窄窗必需），优于"只保留窄窗支"（后者要再引一条断点，多一处可漂移）。
   ★ 仍用 display 让路而非重排几何：面板是演示件、终局迁入「设置」站；详情侧无处可让
     （左移压 `.composer`、上移压 `.chat-hd`）。特异性 0,3,0 与 `.app.appearopen ~ .demo`(0,3,0) 同级
     —— 靠**源顺序**（本条在后）压过它：**故本条必须排在 `.app.appearopen ~ .demo` 之后，勿上调**。
   出口：关掉详情（✕ / Esc / 切站）即恢复显示 —— `_verify/overlay.mjs` ⑦ 段逐条断言。 */
.app.dtlopen ~ .demo{display:none}

/* ★★ 2026-09-19：**本块原规则已删** —— `#demo` 不再右锚，故无需为 300px 副驾栏（`#crmSide`）左移让路。
   原规则（留痕，不删考证）：`.app.crmmode.poolside ~ #demo{right:calc(var(--crm-side-w) + 14px)}`。
   成因：旧浮层贴 `.app` 右缘(right:14px)，而 B 版在右侧常驻 300px 的 `#crmSide`
     ⇒ 浮层正好压住副驾栏底部那块（`.ap-foot` 口径句 / 末条 AI 动作日志）。
     当时改"左移"而非"藏"：切换 A/B 版的开关就在浮层里，藏了就没法切回去（藏 → 自锁）。
   现状：面板 left:76 / width:244 ⇒ 横跨 x∈[76,320]，与右缘副驾栏**结构上不可能相交**（差 300px 量级）
     ⇒「让路」概念随之作废 ⇒ 删除规则；`--crm-side-w` 的引用数因此 **2 → 1**（判据同步见 _verify/crmai.mjs）。 */

/* 标题栏：**纯标题**（不再是折叠把手）—— 关闭语义唯一（点面板外 / Esc），故不设指针/＋－。 */
.demo .dh{display:flex;align-items:center;gap:8px;padding:8px 14px;font-weight:500;font-size:12px;
  background:var(--e3);user-select:none;flex:0 0 auto}
.demo .dh i{width:6px;height:6px;border-radius:50%;background:var(--accent)}
/* 主体 = 面板内**唯一**可滚区（标题栏不动）：低窗时主体滚动，避免整面板出滚动条或静默裁切。 */
.demo .db{padding:12px 14px 14px;display:flex;flex-direction:column;gap:9px;
  min-height:0;overflow-y:auto}
.demo .gl{font-size:11px;color:var(--t3);letter-spacing:.3px}

/* 主题卡 2×2「明度 × 色温」（原型 .th 形态；色样条走 --sw-* 令牌，本文件仍无字面色值）
   ★ 2026-09-19：面板 194 → 244 后，卡宽**不再写字面值** —— 改 `flex-basis:calc(50% − gap/2)`：
     每行恒 2 张、随面板宽度自适应填满（既不会"面板宽了卡没跟上"留半空，也不必两处同步一个魔数）。
   ★ D1（2026-09-19 · 席悦目规格 §2）：卡**上屏全名 + 轴标**（名称 `.lb` 12px/--t1/500；副标 `.sb`
     11px/--t3）。文案由 `theme.js` 只读 `data.js` 的 `THEMES[].name/.sub` 生成 ⇒ **本文件零文案字面量**。
     卡内 padding 6→8、列间距 gap 6→8 ⇒ 卡宽 = (244 − 28 − 8)/2 = **104px**（规格 §2 表口径）。 */
.themebar{display:flex;flex-wrap:wrap;gap:8px}
.themebar .tb{flex:1 1 calc(50% - 4px);min-width:0;padding:8px;border:1px solid var(--line2);
  border-radius:10px;cursor:pointer;
  display:flex;flex-direction:column;gap:2px;background:transparent;
  transition:border-color 120ms,background 120ms}
.themebar .tb:hover{border-color:var(--line3)}
.themebar .tb.on{border-color:var(--accent);background:var(--accent-wash)}
.themebar .sw{display:flex;height:16px;border-radius:5px;overflow:hidden;margin-bottom:4px;
  box-shadow:inset 0 0 0 1px var(--line2)}
.themebar .sw i{flex:1}
.themebar .lb{font-size:12px;font-weight:500;color:var(--t1);line-height:1.3}
.themebar .tb.on .lb{color:var(--accent-text)}
.themebar .sb{font-size:11px;color:var(--t3);line-height:1.3}
.sw-A i:nth-child(1){background:var(--sw-A1)}.sw-A i:nth-child(2){background:var(--sw-A2)}.sw-A i:nth-child(3){background:var(--sw-A3)}
.sw-B i:nth-child(1){background:var(--sw-B1)}.sw-B i:nth-child(2){background:var(--sw-B2)}.sw-B i:nth-child(3){background:var(--sw-B3)}
.sw-C i:nth-child(1){background:var(--sw-C1)}.sw-C i:nth-child(2){background:var(--sw-C2)}.sw-C i:nth-child(3){background:var(--sw-C3)}
.sw-D i:nth-child(1){background:var(--sw-D1)}.sw-D i:nth-child(2){background:var(--sw-D2)}.sw-D i:nth-child(3){background:var(--sw-D3)}

/* ★ AI 推理链路档位：三段等宽（`.lv` 走 flex:1，宽度与面板解耦、无字面量），与主题卡同处本面板。
   ★ 用户已拍板**留在本面板内**（不移出、不删）。
   ⚠ 本块是面板内一处固定高度（约 50px）；面板 bottom 固定 84px ⇒ 变高只向上长，
     低窗由 `.db{overflow-y:auto}` 兜住（不静默裁切，见 .demo 段注释）。 */
.demo .gl2{margin-top:11px}
.lvbar{display:flex;gap:4px}
.lvbar .lv{flex:1;min-width:0;text-align:center;font-size:12px;color:var(--t2);
  padding:5px 0;border:1px solid var(--line2);border-radius:8px;cursor:pointer;
  user-select:none;transition:border-color 120ms,background 120ms,color 120ms}
.lvbar .lv:hover{border-color:var(--line3);color:var(--t1)}
.lvbar .lv.on{border-color:var(--accent);background:var(--accent-wash);
  color:var(--accent-text);font-weight:500}
.lvbar .lv:focus-visible{outline:2px solid var(--focus);outline-offset:1px}

/* ── 断点：中栏/右栏随窗口宽度伸缩（改的是声明级 --list-w/--side-w，不是 flex-basis）── */
@media (max-width:1279px){
  .list{--list-w:280px}
  .side{--side-w:208px}
}
@media (min-width:1600px){
  .list{--list-w:320px}
  .side{--side-w:320px}
}
/* 窄宽降级（顺序见规格 §6.2；数字与 js/data.js 的 LAYOUT 同源，改一处即可）
   · W≤1180 → .side 收至 side.min 208
   · W≤900  → .list 声明级给到 list.min 252（252 即地板：更小的声明值会被 splitter.js 的
              clamp 顶回 252，此处保留只是「不出现小于 min 的谎话声明」） */
@media (max-width:1180px){
  .side{--side-w:208px}
}
@media (max-width:900px){
  .list{--list-w:252px}
}

/* ★★ 窄档（≤1279 = 右栏最窄声明档 208）浮层改停「标签行右端」（2026-09-17）★★
   实测（`_verify/_probe_band.mjs` / `_probe_narrow_band.mjs`，10 会话 × 4 档宽）：
     右栏 208 时 amay 的内容被折行顶到 y≈846，而折叠浮层的固定带恒在 y[769,804]
     ⇒ **整块吞掉「跨账号提示」的段头**（div.sh y[774,791] 完整落在带内）
     = 中窄窗里那一段没有标题。不是"边缘被切"，是内容真丢。
   为什么三条路都堵死（都量过，不是推测）：
     · 加宽右栏：扫 208…320，只有 ≥280 才脱开；但 chat 有 min-width:420px 保底
       ⇒ W=950 时右栏最多 208。窄档加宽会把对话区压穿。
     · 左移浮层：会压住 .composer 的 textarea.inputbox（y[779,823]）—— 输入框比
       一条只读资料行严重得多。
     · 收掉浮层：W=950 必须能切主题（overlay.mjs 第 244/245 条断言的正是这件事）。
   ⇒ 换落点：`.modtabs` 行（y 68–120）**右端结构上为空** —— 标签左对齐，实测最右有框
   子孙恒停在 **x=768**，W=1000 时右余量 24px ≥ 浮层宽 194 所需的 208px 空档。
   ⚠ 这个数**变过**：立此规则时量到的是 718，本轮「对话 4 / AI 训练 2」两枚 tab 标把它推到 768
     （+50px）⇒ 950–975 这一带从"够用"变成"重叠 1–26px"。注释里的实测数字会随界面漂，
     所以下面是**下限阈值**而不是精确坐标，守卫交给 `overlay.mjs` B 组（逐档量"带内 0 点命中"）。
   ⚠ bottom 必须显式 auto：基础规则是 bottom:96px，只写 top 会两个都生效（被拉高）。
   ★★ 2026-09-19：**本块规则 `.demo{bottom:auto;top:76px}` 已删**（随「外观」面板改**左锚**而作废）。
     上面整段成因考证**保留** —— 它记的是"右下锚浮层"当年的实测结论，删掉会让"为什么曾经要 top:76"
     变成无主孤证。现面板 left:76 / width:244（横跨 x∈[76,320]）与右栏(≥331)/标签行右端无关
     ⇒ 落点**不再分档**：宽窄窗一律 left:76 / bottom:84。 */

/* ★ 让位规则不在本处 —— `.seg` 收内边距的那条**宽度断点 999px** 的媒体查询写在
   **文件最末**，因为同特异性下后面的规则赢，而它必须压过下面的低高度段（见文末说明）。
   （本条注释刻意不写字面 `@media`：`_verify/consistency.mjs` 那类跨块扫描的静态判据
     会被这种字样干扰 —— 它现在会先剥注释，但不值得给下一个人留坑。） */

/* ══ ★ 客户详情抽屉（2026-09-17 · 自统一原型移植；形态见 app/docs/交互规格-三栏拖拽.md §7.5）══
   锚 `.app` 右缘、宽 46vw（min 520 / max 760）、`z-index 41`、顶到 `.app` 上下。
   **非模态**：无遮罩、点外面不关 ⇒ 出口 = 头部 ✕ / Esc / 切站自动关。
   与窄窗右栏抽屉（`.side`）都锚右缘 —— **两者能同时开**（2026-09-17 实测订正：
   此前的注释写「永不同时开」，但窄窗下 `.dtl` 正是由**右栏抽屉内部**的「详情 ›」打开的，
   必然同开；`dtl.mjs` ⑧ 的 Esc 分层断言也早已依赖这个态）。
   叠加态由文件末尾 949 断点接管：窄窗下 `.dtl` **铺满可视宽** + `z-index 42` ⇒
   完全盖住 `.side`(41) 与 `#scrim`(40)，遮罩不再露出；收起 `.dtl` 就回到 `.side`。 */
.dtl{position:absolute;top:0;right:0;bottom:0;width:46vw;min-width:520px;max-width:760px;
  display:none;flex-direction:column;background:var(--e1);
  border-left:1px solid var(--line2);box-shadow:-14px 0 34px -14px var(--sh-2);z-index:41}
.dtl.on{display:flex}
/* `tabindex="-1"` 的面板被程序化聚焦时 Chrome 会画 `-webkit-focus-ring` —— 在左缘看着像"选中态"。
   该元素 Tab 不到、永不进 `:focus-visible` ⇒ 视觉必须无痕。 */
.dtl:focus{outline:none}
.dtl-hd{flex:0 0 auto;display:flex;align-items:center;gap:11px;padding:13px 18px;
  border-bottom:1px solid var(--line)}
.dtl-hd .av{width:34px;height:34px;border-radius:10px;font-size:13px}
.dtl-hd .dh-tx{min-width:0}
.dtl-hd .dh-nm{font-size:17px;font-weight:500;color:var(--t1);white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis}
.dtl-hd .dh-st{margin-top:3px;font-size:12px;color:var(--t2);display:flex;align-items:center;gap:6px}
.dtl-hd .dh-sp{margin-left:auto;display:flex;align-items:center;gap:6px;flex:0 0 auto}
/* 头部两个动作是**真 `<button>`**（键盘 Enter/Space 由浏览器给，不用另写 keydown）。
   按钮自带 UA 的 border/background/font ⇒ 必须逐项显式归零，否则会带回系统默认外观。 */
.dtl-hd .dh-b,.dtl-hd .dh-x{font-family:inherit;background:none;cursor:pointer;
  color:var(--t1);border:1px solid var(--line2);border-radius:var(--r-xs)}
.dtl-hd .dh-b{padding:4px 11px;font-size:12px}
.dtl-hd .dh-x{width:26px;height:26px;padding:0;display:flex;align-items:center;
  justify-content:center;color:var(--t2);font-size:15px;border-color:transparent}
.dtl-hd .dh-b:hover,.dtl-hd .dh-x:hover{background:var(--face-hover)}
.dtl-hd .dh-b:focus-visible,.dtl-hd .dh-x:focus-visible{outline:2px solid var(--focus);outline-offset:1px}
.dtl-bd{flex:1 1 auto;min-height:0;overflow-y:auto;padding:13px 18px 12px}
.dtl-bd:focus{outline:none}
.dtl-bd::-webkit-scrollbar{width:8px}
.dtl-bd::-webkit-scrollbar-thumb{background:var(--scroll);border-radius:4px}
.dtl-sec{margin-bottom:10px}
.dtl-sec:last-child{margin-bottom:0}
.dtl-sechd{display:flex;align-items:center;gap:8px;color:var(--t2);font-size:12px;margin-bottom:4px}
.dtl-sechd::after{content:"";flex:1;height:1px;background:var(--line)}
/* 详情表 = **两对「标签 + 值」一行**（宽容器里比单列省一半高度，一屏放得下，少出滚动条）。
   行距 5px 是实测值：6px 时「要紧的事」有两行的那屏内容高 28px ⇒ 出滚动条。 */
.dtl-tbl{display:grid;grid-template-columns:100px 1fr 100px 1fr;column-gap:14px}
.dtl-tbl .k{font-size:12px;color:var(--t2);padding:5px 0;border-bottom:1px solid var(--line);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.dtl-tbl .v{font-size:13px;color:var(--t1);padding:5px 0;border-bottom:1px solid var(--line);
  min-width:0;overflow-wrap:anywhere}
.dtl-tbl .v.wide{grid-column:2 / 5}
/* 阶段徽标：与客户表那一列**同一枚**（含「阶段数据异常」的中性灰 —— B §2.1 写死
   "读不到"不许显示成"出事了"，与「⚠ 需处理」的琥珀不是一回事）。`.err` 只标语义、不换色。 */
.dtl .stagechip{background:var(--stage-chip-bg);color:var(--stage-chip-fg);
  font-size:var(--fs-caption);padding:1px 9px;border-radius:var(--r-full);font-weight:400}
/* ★ 入口①：对话站右栏「这人是谁」段头的「详情 ›」。
   常驻、**不许 hover 才出现**（原型口径「常驻，不许 hover 才出现」）。 */
.sect .sh .dtl-go{flex:0 0 auto;font-size:11px;color:var(--accent-text);cursor:pointer;
  padding:1px 6px;border-radius:var(--r-xs);white-space:nowrap}
.sect .sh .dtl-go:hover{background:var(--face-hover);text-decoration:underline}
.sect .sh .dtl-go:focus-visible{outline:2px solid var(--focus);outline-offset:1px}
/* ★ 入口②：客户表操作列（.crm .ftbl .op 已在 customers.css 定义，这里只补键盘焦点态）。 */
.crm .ftbl .op:focus-visible{outline:2px solid var(--focus);outline-offset:1px}

/* ★ 窄宽兜底（任务 #4 · F3b）：W < 最小同屏宽 950 时三栏已装不下，
   若 .app 仍 {overflow:hidden} ⇒ .side 右侧被静默裁掉（W=880 实测裁 70px、无横滚条 = 静默剪裁）。
   949 = data.js LAYOUT.narrowMax（= rail + spl + listMin + chatMin + sideMin − 1 = 950 − 1）；
   `_verify/consistency.mjs` 会断言本字面量与 LAYOUT 派生值逐位相等。
   · 只作用于「对话站」三栏：排除客户站（.crmmode 已有 .tblwrap 自身横滚，再叠一层会出双横滚条）
     与假站（.phmode 内容居中、无溢出）。
   · overflow-x:auto 会把 overflow-y 计算值带成 auto，但 .app 纵向溢出恒为 0
     （子项全部 min-height:0 + 各自内部滚动），实测无双纵向滚动条。 */
@media (max-width:949px){
  .app:not(.crmmode):not(.phmode){overflow-x:auto}
  /* ★★ 2026-09-19：**原行首 `.demo{display:none}` 已删**（此项随「外观」面板迁入左栏而作废）。
     为什么不再需要它（成因考证，保留）—— 原浮层锚**视口右下**，而 W≤949 时 `.app` 出横滚、
     会话区右缘固定在**内容坐标** ⇒ 视口锚定的浮层必压进会话区（W=900 压 `.inputbox` 29×25px²、
     W=880 压 49×25px²，折叠态也压）。现面板**左锚**（left:76 / width:244）⇒ 右缘恒 320 ≤ 列表右缘
     ⇒ 会话区左缘 ≥321 ⇒ 该成因**结构性消失**；且入口在左栏（左栏恒在）⇒ 窄窗也不再隐藏面板。
     ⚠ 勘误（2026-09-19 实测）：本节旧注释自称"已被上方 `max-width:975px` 的 `.demo{display:none}` 覆盖"
       —— **全仓并无 975px 媒体查询**（把 949 误记成 975）。真实情况：窄窗降级**本来只有 949 这一条**；
       本行删除后，全仓再无「行首 `.demo{display:none}`」规则（判据同步见 _verify/consistency.mjs）。 */

  /* ══ ★★ 窄窗右栏抽屉（2026-09-17 新增）—— 修「右栏被挤出视口」★★
     改前实测：W=900 右栏可见 158px(76%) · W=820 可见 78px(38%) · **W≤740 可见 0px**。
     整栏落进视口外；`.app` 虽然可横滚（950 vs 740），但 macOS 滚动条默认浮层隐藏
     ⇒ 界面上没有任何线索说"往右还有东西"，用户视角 = 右栏凭空消失。
     修法：窄窗下 `.side` **脱离文档流**改覆盖式抽屉（锚 .app 右缘、不占横向空间），
     由会话头部 `.hd-side` 按钮开关，点遮罩或 Esc 关闭。
     · 宽窗（≥950）一切照旧 —— 本断点之外零改动，`.side` 仍是常驻第 3 栏。
     · 只作用于对话站：客户站 `.crmmode` / 假站 `.phmode` 里 `.side` 本就 `display:none`。
     · 遮罩用**真实元素** `#scrim` 而不是 `::after`：伪元素接不到点击，
       用元素才能实现「点外面关闭」（且能被冒烟脚本 click 到）。
     · 300 = `data.js LAYOUT.sideDrawerW`、68 = `LAYOUT.rail`（consistency.mjs 断言字面量）。 */
  .app:not(.crmmode):not(.phmode){position:relative}
  /* `#sp2` 一并收起：它分隔的 `.side` 已脱离文档流，留着就是一根拖不动的假分隔条
     （`--side-w` 也管不到抽屉宽度）⇒ 同屏最小宽随之降到 741（= LAYOUT.minRowScreenNoSide）。 */
  .app:not(.crmmode):not(.phmode) > #sp2{display:none}
  .app:not(.crmmode):not(.phmode) > #scrim{position:absolute;inset:0;z-index:40;
    display:none;background:var(--scrim)}
  .app:not(.crmmode):not(.phmode) > .side{
    position:absolute;top:0;right:0;bottom:0;
    width:min(300px, calc(100vw - 68px));max-width:100%;
    display:none;z-index:41;border-left:1px solid var(--line2);
    box-shadow:-14px 0 34px -14px var(--sh-2)}
  /* ⚠ 打开态写成 0,5,0 是**必须**的，不是冗余：`.app.crmmode > .side` 与
     `.app.phmode > .side`（placeholder.css，**本文件之后加载**）都是 0,3,0 ⇒ 同特异性下它们会赢
     （RULES 18 同族：源顺序决胜）。这里靠特异性一次性压过，不依赖文件加载顺序。 */
  .app.sideopen:not(.crmmode):not(.phmode) > .side{display:flex}
  .app.sideopen:not(.crmmode):not(.phmode) > #scrim{display:block}
  /* 会话头部：装饰图标 ↔ 右栏开关 由 CSS 交换（宽窗视觉零变化）。
     同理必须 0,3,0 —— chat.css（本文件之后加载）里的 `.chat-hd .hd-side{display:none}` 是 0,2,0。 */
  .chat-hd .hd-tools .hd-deco{display:none}
  .chat-hd .hd-tools .hd-side{display:inline-flex}
  /* ══ ★★ 客户详情抽屉在窄窗**铺满可视宽**（2026-09-17 实测补齐 · 规格 §7.5）★★
     起因：源码注释早已声明"窄窗下 `.dtl` 铺满（见文末 949 段）"，但**该规则从未写**——
     949 段内 `.dtl` 命中数 = 0（空头承诺），实测叠加态因此有三处错：
       ① `.dtl` 520 宽锚右缘（l:380..900），而 `#scrim` 仍 `inset:0` display:block
          ⇒ **露出 380px 宽的压暗区**，`elementFromPoint` 命中 `#scrim`（可点）。
          界面左半边被压暗 ⇒ 详情"看起来是模态的"，与「非模态、无遮罩」直接矛盾；
       ② 点那块遮罩 ⇒ 右栏抽屉关了、详情**还开着** ⇒ 详情悬空（它本从右栏开出来）；
       ③ `.side`(41) 与 `.dtl`(41) 同 z-index ⇒ 层序靠 DOM 顺序决胜
          ⇒ 改 HTML 两行顺序即「点详情毫无反应」（静默失败）。
     修法：铺满（盖住遮罩，非模态语义恢复、关掉即层级返回右栏）+ z-index 抬到 42 与 `.side` 解耦。
     ⚠ 特异性 0,3,0 必须高于基础段 `.dtl`(0,1,0)；窄窗内 `.dtl` 是最高**内容层**。
     ★ 2026-09-19 更正：此句原写"窄窗内 `.demo` 已 display:none" —— 面板迁入左栏后**不再**如此
       （`.demo` 现 z-index 60 > `.dtl` 42，但详情一开即由 `.app.dtlopen ~ .demo` 让路隐藏
        ⇒ 用户实际看到的最上层仍是详情，层序语义不变）。
     ★★ 只作用于**对话站**（排除 .crmmode/.phmode）—— 首版写成 `.app > .dtl` 是**一刀切**，
        把客户站也铺满了，那是一处**回退**：客户站窄窗**根本没有遮罩**
        （`#scrim` 只在 `.app.sideopen` 下 display:block，而客户站 `.side` 本就 display:none、
        永远不会有 sideopen）⇒ 客户站的详情是**纯非模态**，520 宽盖右半、
        左半表格**仍然可见可用**（且表格可横滚），比整屏盖住更有用。
        ⇒ 铺满的**唯一理由**是遮罩（只有对话站窄窗才有）⇒ 选择器必须按这条界线切。 */
  .app:not(.crmmode):not(.phmode) > .dtl{width:100%;min-width:0;max-width:100%;z-index:42}

  /* ★ 状态栏让位（2026-09-17 实测）：`.statusbar` 是 `flex:0 0 26px` 定高，
     W≤900 时内容超宽 ⇒ 每个 span 换行成 34px、`scrollHeight 32 > clientHeight 25`
     = **溢出 7px**（改前就有，只是文案里加入「右栏出路」后余量被吃光、暴露出来）。
     让位顺序（代价最小的先撤）：① 撤两条**冗余/次要**读数 ——
     「已连接 3 个微信号 · 采集正常」(158px) 与顶栏「3 个微信号已连接」重复；
     「今日 AI 回复 128 条」(103px) 是增量信息、窄窗可省。
     ② 保留「值班态 / 待处理 N / 暂停 / 窄宽提示」—— 前三者是可操作状态，后者是本次的出路提示。
     ⇒ 撤完 W=740 实测仍有 ≥200px 余量（见 _verify/narrow.mjs 的状态栏断言）。 */
  .statusbar #sbConn,
  .statusbar #sbAi{display:none}
}

/* ── 低高度降级（≤820px）：让位优先顺序 —— 先压输入区，消息区保底 150px ── */
@media (max-height:820px){
  .modtabs{flex:0 0 46px;height:46px}
  .seg{padding:0 12px;gap:7px}
  .seg::before{top:6px;bottom:6px}
  .chat-hd{flex:0 0 50px}
  .list-hd{padding:10px 15px 7px}
  .hd-top{margin-bottom:7px}
  .row{min-height:62px}
}

/* ── 极低高度降级（≤700px）：状态栏 26 → 22（任务 #4 · F1）
   .statusbar 的 flex 是简写 `flex:0 0 26px` ⇒ 必须同时覆盖 flex-basis 与 height：
   只改 height 会被 flex-basis:26px 顶住，只改 flex-basis 会被 height:26px 顶住（两者都要改）。
   实测 h=700/620/560/481/480 全部降为 22px（见 _verify/consistency.mjs 的 sbH 段）。 */
@media (max-height:700px){
  .statusbar{flex-basis:22px;height:22px}
}

/* ══ ★★ 文件**最后**一条规则：标签行收内边距（2026-09-17 实测补）══════════════════
   起因：演示浮层在 ≤1279 的落点是 `.modtabs` 行右端（`top:76px`），占右下 208px
   （宽 194 + right 14）⇒ 需要该行最右有框子孙 ≤ vw − 208。
   实测（`_verify/_probe_tabrow.mjs`）：该子孙恒停在 **x=768**（立规时是 718，本轮
   「对话 4 / AI 训练 2」两枚 tab 标把它推了 50px）⇒ 需要 vw ≥ 976 才不重叠。
   vw=975 余量 −1px、960 −16px、950 −26px —— 950–975 这一带必然重叠。
   ⇒ 让谁让？（三个选项都量过）
     ● 收掉浮层：**不行** —— `overlay.mjs` C 组断言「W=950 浮层仍要显示且 top=76px」
       （它是本原型唯一的主题切换入口）。收了 C 组立刻红 —— 先收后测会以为"修好了"。
     ● 收掉两枚 tab 按钮：**已在 1279 段做过**（见 `.tbtn` 段后那条），但它们只解决
       W≥1000 的部分；950–975 的余量缺口（−26 ~ −1px）得靠标签行自己让。
     ● 收 `.seg` 内边距：左右各 14 → 10px，7 个 tab 省 56px ⇒ x 从 768 回到约 712，
       W=950 下余量 30px。**信息一条不丢**，只是没那么松 —— 选它。
   ⇒ 两个契约（B 组不重叠 / C 组仍可切主题）同时成立。
   ⚠ 阈值取 999 而非 976：976 恰好是"余量 0"的临界点，贴边不算安全；
     取整到 999 让 1000px 起回到 ≥24px 余量，边界上不留 0 余量的悬空档。
   ⚠⚠ **必须写在文件最末**：`.seg{padding}` 共有三处 —— 基础段（0 14px）、低高度段
      （820px 高那条，0 12px）、本条。同特异性下**后面的赢**，
      而 950×800 这类窗口前两个媒体查询会同时命中 ⇒ 若本条排在低高度段之前，
      会被那条 0 12px 静默压回去（余量只剩 2px，看着"生效了"其实没有）。
      今天已经在同一类源序问题上栽过一次（`.tabright` 的 display 覆盖写在了基规则之前），
      这是 RULES 57 的第二次复现 —— 所以这次直接把它放成"最后一道话"。 */
@media (max-width:999px){
  .seg{padding:0 10px}
}
