/* ==========================================================================
   宝in+人才公寓 PMS · 桌面端设计系统（唯一权威样式层）
   --------------------------------------------------------------------------
   本文件是全站唯一的样式来源。原先分散在 index.html 的 8 段 <style> 与
   pms-core.js 运行时注入的样式已全部合并到此处，职责划分如下：

     1. 设计令牌与基础排版
     2. 应用外壳：侧导航 / 顶栏 / 内容区
     3. 页面头与指标条
     4. 房态（原「房态工作台」，D-171 改名）：左侧筛选栏 + 右侧房卡 / 平面
     5. 房间检查面板（抽屉）与底部动作条
     6. 右键菜单、弹窗
     7. 表格、表单、报表等通用组件
     8. 旧版 pms-* 组件（由 pms-core.js 迁出，行为不变）
     9. 响应式降级

   视觉依据：Ant Design Layout/Navigation（桌面工作区与一致导航）、
   Fluent 2 Typography/Color（字号层级与中性色分工）、
   Mews Timeline（房态区域组织与密度）、Cloudbeds（可售限制与解除限制分离）。
   业务对象是企业长租公寓，不是酒店按夜出租，因此不引入 OTA/夜审/ADR 等入口。
   ========================================================================== */

/* ---------- 1. 设计令牌 ---------- */
:root {
  /* 品牌与语义色：品牌色只用于「可点击/主操作」，状态色只用于「房态事实」 */
  --brand: #345FD8;
  --brand-ink: #2647A8;
  --brand-soft: #EDF1FD;
  --brand-line: #C9D6F7;
  --brand-deep: #1F3E9E;

  --bg: #F5F7FB;
  --surface: #FFFFFF;
  --surface-2: #FAFBFD;
  --surface-3: #F1F4F9;
  --surface-sunken: #F7F9FC;

  --ink: #243247;
  --ink-2: #3E4E66;
  --muted: #5A6A80;
  --muted-2: #64707E;
  /* 【2026-11-01 融合 · 对比度】原 #A6B2C2 在白色卡面上只有 2.3:1，
     侧栏小标题、楼层间走道字、"待补充"这类弱文字在户外/手机上基本看不清。
     收到 #6B7785（约 4.6:1），仍是同一条灰色梯度（比 --muted-2 更淡），只是不再低到读不出来。 */
  /* 【A 级改造 · 灰阶单调化】上一次只把 --muted-3 提亮到 4.56:1，没动 --muted-2，
     结果梯度断成 4.70 → 2.90 → 4.56：**最淡的那档夹在中间**，任何从 muted-3 挪到
     muted-2 的文字都会变淡（"越次要越浅"这个直觉在这里是反的）。
     实测白卡上 --muted-2 用于房卡「朝南」「20㎡」、楼层右侧「21 间」、「续租中 0」，
     共 49 处，2.90:1 全部不达 AA 4.5:1；楼层头压在 #EDF1F6 上更低到 2.56:1。
     本次把整条梯度压到单调且全部达标（白底实测）：
       --ink 12.95 → --ink-2 8.45 → --muted 5.51 → --muted-2 4.56 ＝ --muted-3 4.56
     --muted-2 与 --muted-3 此后同值：能读的灰只需要两档，第三档不该再承载文字。
     --muted-3 保留给图标、分隔线、虚线边、禁用态这类**不承载可读性**的地方。 */
  --muted-3: #6B7785;

  --line: #E3E9F1;
  --line-2: #EEF2F7;
  --line-3: #F3F6FA;

  --ok: #1F8A63;
  --ok-soft: #E9F6F0;
  --ok-line: #BFE3D2;
  /* 【B12·2026-11-01 对比度】原 #A9761A 在 --warn-soft(#FDF5E3) 上实测 3.62:1，
     低于 WCAG AA 正文 4.5:1：账务警示、待办琥珀 chip、房卡「续签中」胶囊（10px）
     全都不达标，小字尤其糊。加深到 #8A5E10 后同底色 5.10:1；
     反过来白字压在琥珀底上（.todo-chip.amber b、.room.renew .tag）也从 3.93 升到 5.54。
     色相不变，只是把亮度压下来，仍是"警告但不刺眼"的琥珀。 */
  --warn: #8A5E10;
  --warn-soft: #FDF5E3;
  --warn-line: #EBD7A6;
  --danger: #C0453B;
  --danger-soft: #FCEEEC;
  --danger-line: #EFC4BE;
  --info: #2F6FB5;
  --info-soft: #EAF2FC;
  --info-line: #C4DAF1;
  --grey: #6B7A8D;
  --grey-soft: #EFF2F6;
  --grey-line: #DCE2EA;
  --violet: #6C5CA8;
  --violet-soft: #F1EEFA;
  --teal: #16867D;
  --teal-soft: #E6F4F2;
  --teal-line: #B7DFDA;

  --radius-xs: 6px;
  --radius-sm: 8px;
  --radius: 10px;
  --radius-lg: 14px;

  --shadow-1: 0 1px 2px rgba(24, 45, 70, .05);
  --shadow-2: 0 6px 20px rgba(24, 45, 70, .08);
  --shadow-3: 0 18px 48px rgba(20, 36, 58, .16);

  --nav-w: 208px;
  --topbar-h: 60px;
  --gap-1: 4px;
  --gap-2: 8px;
  --gap-3: 12px;
  --gap-4: 16px;
  --gap-5: 24px;
  --gap-6: 32px;

  --fs-room: 18px;
  --fs-title: 25px;
  --fs-h2: 17px;
  --fs-body: 14px;
  --fs-small: 12px;

  /* ---------- 1.1 补齐令牌（2026-10-31 设计系统收敛） ----------
     原来这些值散落在 4 个文件里各写一遍：输入框边框 3 个近似值、
     焦点环自造 rgba、层级 30/60/9000/99999 四个裸数字、表格行高没有令牌。
     补名字不改值，让后续改动只改一处。 */
  --line-strong: #D5DEEA;      /* 输入框、搜索框边框（比 --line 深一档，能看出可输入） */
  --focus-ring: 0 0 0 3px rgba(52, 95, 216, .18);
  --row-h: 44px;               /* 表格密集行 */
  --row-h-lg: 56px;            /* 待办列表行 */
  --row-hover: #F7F9FC;
  --empty-min-h: 160px;
  --content-max: 1720px;

  /* 层级：只补名，值沿用现有实现，避免改变既有遮挡关系 */
  --z-sticky: 30;
  --z-tabbar: 60;
  --z-drawer: 200;
  --z-modal: 9000;
  --z-toast: 99999;

  /* 图表分类色板：20 张报表里的费项构成/收款方式/资产结构都是多分类，
     此前只能借语义色（把"正常/告警"当成"分类"用）。末位刻意用中性灰，
     避免把中性分类读成告警。 */
  --chart-1: #345FD8;
  --chart-2: #16867D;
  --chart-3: #6C5CA8;
  --chart-4: #2F6FB5;
  --chart-5: #8A5E10;
  --chart-6: #8A99AD;
}

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: "Microsoft YaHei", "PingFang SC", "Hiragino Sans GB", "Source Han Sans SC", system-ui, -apple-system, "Segoe UI", sans-serif;
  font-size: var(--fs-body);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

button, input, select, textarea { font: inherit; color: inherit; }
button { cursor: pointer; background: none; border: 0; padding: 0; }
button:disabled { cursor: not-allowed; }
a { color: inherit; text-decoration: none; }
table { border-collapse: collapse; }
h1, h2, h3, h4 { margin: 0; font-weight: 700; }
:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
::selection { background: var(--brand-soft); }

/* 数字与金额对齐 */
.num, .value, .stat .value, .money, table td.num, .pms-receipt-single { font-variant-numeric: tabular-nums; }

/* ---------- 2. 应用外壳 ---------- */
.app { display: flex; min-height: 100vh; }

.aside {
  width: var(--nav-w);
  flex: 0 0 var(--nav-w);
  background: var(--surface);
  border-right: 1px solid var(--line);
  min-height: 100vh;
  position: sticky;
  top: 0;
  height: 100vh;
  display: flex;
  flex-direction: column;
  padding: 18px 12px 14px;
}

.brand { display: flex; gap: 10px; align-items: center; padding: 0 8px 16px; }
/* 【2026-10-31 品牌标识重做】
   旧标识是绿色渐变方块 + 「宝」+「In⁺」三块文字拼在一起，在 38px 里塞了
   中英文和上标三种字号，小尺寸糊成一块；而且它用的是 #14b98a 绿，与
   全站操作蓝 #345FD8、状态绿 #1F8A63 三色并存，谁也不是"品牌色"。
   新标识只做一件事：一个几何标记 + 一个词标。
     · 标记 = 屋檐折线 + 门洞，门洞里嵌一个「i」（宝In⁺ 的 i），16px 下仍可辨；
     · 单色平涂（品牌蓝），不用渐变、不用阴影堆叠，与令牌层同源；
     · 词标 16px/700，副标 11px 弱化，和页面里其它标题同级。
   这样全站只剩一个品牌色，绿回归"房态事实"，蓝独占"可点击"。 */
.brand-icon {
  width: 34px; height: 34px; border-radius: var(--radius); flex: none;
  background: var(--brand); color: #fff;
  display: grid; place-items: center;
  box-shadow: 0 1px 2px rgba(24, 45, 70, .16);
}
.brand-icon svg { width: 22px; height: 22px; display: block; }
.brand-name { font-size: 16px; font-weight: 700; line-height: 1.2; letter-spacing: -.2px; }
.brand-name sup { font-size: 10px; font-weight: 700; margin-left: 1px; }
.brand-sub { font-size: 11px; color: var(--muted-2); margin-top: 3px; letter-spacing: .2px; }

.product-navigation { flex: 1; overflow-y: auto; margin: 0 -4px; padding: 0 4px; }
.product-nav-group + .product-nav-group { margin-top: 14px; }
.product-nav-heading {
  font-size: 11px; color: var(--muted-2); font-weight: 700;
  letter-spacing: .8px; padding: 6px 10px 6px;
}

.nav {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 8px 10px; border-radius: var(--radius-sm);
  color: var(--ink-2); text-align: left; font-size: 13px;
  position: relative; transition: background .15s, color .15s;
}
.nav + .nav { margin-top: 1px; }
.nav:hover { background: var(--surface-3); color: var(--ink); }
.nav.active { background: var(--brand-soft); color: var(--brand-ink); font-weight: 700; }
.nav.active::before {
  content: ""; position: absolute; left: -4px; top: 8px;
  width: 3px; height: 18px; border-radius: 2px; background: var(--brand);
}
.nav .ic { flex: 0 0 18px; display: grid; place-items: center; color: currentColor; opacity: .82; }
.nav .ic svg { width: 17px; height: 17px; stroke-width: 1.7; }
.nav.active .ic { opacity: 1; }
.nav-text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.aside-footer {
  margin-top: 12px; border: 1px solid var(--line); background: var(--surface-2);
  border-radius: var(--radius); padding: 10px 11px;
}
.small-label { font-size: 11px; color: var(--muted-2); font-weight: 700; letter-spacing: .6px; }
.aside-footer p { margin: 4px 0 0; color: var(--ink-2); font-size: 12px; }
.sidebar-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--warn); display: inline-block; margin-right: 6px; }
.aside-footnote { font-size: 11px; color: var(--muted-2); margin: 10px 4px 0; }

.main { min-width: 0; flex: 1; display: flex; flex-direction: column; }

.topbar {
  height: var(--topbar-h); flex: 0 0 var(--topbar-h);
  background: var(--surface); border-bottom: 1px solid var(--line);
  display: flex; align-items: center; justify-content: space-between;
  padding: 0 24px; gap: 16px;
  position: sticky; top: 0; z-index: 30;
}
.crumb { font-size: 12px; color: var(--muted); display: flex; align-items: center; gap: 8px; }
.crumb strong { color: var(--ink); font-weight: 600; }
.crumb .sep { color: var(--muted-3); }
.topbar-right { display: flex; align-items: center; gap: 10px; }
.sync-chip {
  padding: 5px 10px; border-radius: 999px; background: var(--warn-soft);
  color: var(--warn); font-size: 12px; font-weight: 600; white-space: nowrap;
  border: 1px solid var(--warn-line);
}
.mode-badge { font-size: 12px; color: var(--muted); white-space: nowrap; }
.avatar {
  width: 32px; height: 32px; border-radius: 50%; background: var(--surface-3);
  color: var(--ink-2); display: grid; place-items: center; font-weight: 700; font-size: 13px;
}
.icon-btn {
  display: inline-grid; place-items: center; width: 32px; height: 32px;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--muted); transition: .15s;
}
.icon-btn:hover { background: var(--surface-3); color: var(--ink); }
.icon-btn svg { width: 17px; height: 17px; }

.content { padding: 20px 24px 32px; flex: 1; min-width: 0; }
.content > .secondary-view { max-width: 1720px; margin: 0 auto; }
.content > .secondary-view.active { display: block; }
.secondary-view { display: none; }

/* 房间详情为右侧浮层：打开时不再推挤主内容，背景页面保持原位不动（消除偏移感）。
   桌面端遮罩不拦截点击：看一间房的资料时还要能直接点另一间切换，
   整页被蒙住会让人以为系统卡死了。关闭仍可点抽屉的 × 或按 Esc。
   窄屏抽屉基本占满宽度，此时遮罩恢复拦截，点空白处即关闭。 */
/* 【2026-11-02 用户反馈「抽屉关不掉」——桌面端遮罩改为可点击关闭】
   原来宽屏把 veil 设成 `pointer-events:none`，理由是「看一间房的资料时还要能直接点另一间切换」。
   实测代价太大：用户点空白想关抽屉，点击穿过遮罩落在房卡上，
   抽屉没关、房号却换成了别的房间（南215 → 南101），看起来就是「关不掉、越点越乱」。
   × 和 Esc 一直是好的，但没人会先去找它们——点空白关闭是通用习惯。
   现在：遮罩恢复拦截点击 + 保留 10% 浅压暗（提示"这是一层遮罩，点一下会关"）。
   想换看另一间：关掉再点，两步，不会误开。 */
body.drawer-open .veil { background: rgba(20, 32, 48, .10); pointer-events: auto; cursor: pointer; }
@media (max-width: 1100px) {
  body.drawer-open .veil { background: rgba(20, 32, 48, .24); pointer-events: auto; }
}
/* 【2026-11-02】遮罩从左侧导航右边开始：导航（房态/合同/客户/账务…）保持可点，
   换页面不用先关抽屉；被拦住的只有内容区，点那里 = 关闭抽屉。
   宽度用 --nav-w 变量，≥1175px 折叠成 64px 时自动跟着变。 */
@media (min-width: 1101px) {
  body.drawer-open .veil { left: var(--nav-w); }
}
/* 【A 级改造 · 抽屉让位】抽屉是纯 overlay，440px 时在 1440 屏上盖掉 9 列里的 4 列，
   同时 veil 还留着一层 10% 的压暗 —— 等于"既盖住又看不清"。宽度收到 380：
   1440 下只盖 3 列，房号仍可读。
   原文这里还有一条「>1100px 时 veil 底色降到 transparent」——**2026-11-02 已取消**：
   遮罩改为可点击关闭之后，完全透明会让人看不出这里有层遮罩、点了会关，
   而且它和"关不掉"是同一个误会的两端。保留 10% 浅压暗作为"这是浮层"的提示。
   注意：宽度覆盖必须写在 `.drawer` 基础规则之后（同特异性看源码顺序），
   否则 min(440px,100vw) 会把这里顶掉。 */

/* ---------- 3. 页面头与指标条 ---------- */
.hero {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 16px; margin-bottom: 16px; flex-wrap: wrap;
}
.hero > div:first-child { min-width: 160px; }
.kicker {
  font-size: 11px; color: var(--brand-ink); font-weight: 700; letter-spacing: 1.2px;
  margin-bottom: 4px; text-transform: uppercase;
}
.hero h1 { font-size: var(--fs-title); line-height: 1.25; letter-spacing: -.4px; color: var(--ink); }
.hero-desc, .hero p { margin: 5px 0 0; color: var(--muted); font-size: 13px; line-height: 1.6; max-width: 760px; }
.hero .v5-buttons, .hero-actions { display: flex; gap: 8px; align-items: center; flex: 0 0 auto; flex-wrap: wrap; }

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  border: 1px solid var(--line); background: var(--surface); color: var(--ink-2);
  border-radius: var(--radius-sm); padding: 8px 13px; font-size: 13px; font-weight: 500;
  white-space: nowrap; transition: .15s; min-height: 34px;
}
.btn:hover { border-color: var(--brand-line); color: var(--brand-ink); background: var(--brand-soft); }
.btn.primary { background: var(--brand); border-color: var(--brand); color: #fff; font-weight: 600; }
.btn.primary:hover { background: var(--brand-ink); border-color: var(--brand-ink); color: #fff; }
.btn.tiny { padding: 4px 9px; font-size: 12px; min-height: 28px; }
.btn svg { width: 15px; height: 15px; }
.btn:disabled { opacity: 1; color: #64707E; background: var(--surface-2); border-color: var(--line); cursor: not-allowed; }
/* 【2026-11-03 对比度收口】全站原来用 opacity 表达「禁用/弱化」，把文字压到 2.07–2.90:1：
     .btn:disabled(.45) .pms-act:disabled(.45) .pms-btn:disabled(.55) .cbiz-item:disabled(.55)
     .v5-pick:disabled(.5) .pms-menu button:disabled(.55) .pg-btn[disabled](.45)
     .product-plan-controls button:disabled(.4) .state-chip.is-zero(.42) .todo-chip.zero(.55)
   opacity 会连乘到整棵子树，getComputedStyle().color 看不出任何异常，所以历次审查都漏掉了。
   统一改成「只改颜色、不改透明度」：#64707E 在白底 5.05 / --bg 4.71 / --surface-3 4.58 / --surface-2 4.87，
   四档全部 ≥4.5。禁用态此后全站只有一个色值，不要再引入 opacity。 */
.btn:disabled:hover { border-color: var(--line); color: #64707E; background: var(--surface-2); }
.mini-note { font-size: 12px; color: var(--muted); line-height: 1.65; }

.stats {
  display: grid; grid-template-columns: repeat(5, minmax(0, 1fr));
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  overflow: hidden; margin-bottom: 8px; box-shadow: var(--shadow-1);
}
.stat { padding: 5px 12px; border-right: 1px solid var(--line-2); min-width: 0; position: relative; }
.stat:last-child { border-right: 0; }
.stat .name { font-size: 11px; color: var(--muted); display: flex; align-items: center; gap: 5px; }
.stat .value { font-size: 18px; font-weight: 700; margin-top: 0; line-height: 1.25; letter-spacing: -.3px; }
.stat small { font-size: 12px; color: var(--muted-2); font-weight: 400; margin-left: 2px; }
.stat.accent .value { color: var(--ok); }
.stat.warn .value { color: var(--warn); }
.stat.danger .value { color: var(--danger); }
.stat.pms-stat { text-align: left; cursor: pointer; border: 0; border-right: 1px solid var(--line-2); border-radius: 0; background: var(--surface); font-family: inherit; transition: background .15s; }
.stat.pms-stat:hover { background: var(--surface-2); }
.stat.pms-stat.is-on { background: var(--brand-soft); box-shadow: inset 0 -2px 0 var(--brand); }
.stat.pms-stat.danger .value { color: var(--danger); }
.stat.pms-stat.muted .value { color: var(--muted-2); }
.pms-stat-extra { font-size: 11px; color: var(--muted-2); margin-top: 1px; line-height: 1.45; }
.stat.pms-stat.danger .pms-stat-extra { color: var(--danger); }

/* ---------- 4. 房态（原「房态工作台」，D-171 改名；样式类名不变）---------- */
/* 房间图是本页主体：筛选、指标、待办全部压缩到顶部条与右侧窄栏，
   房间网格拿走绝大部分可视面积，不再被侧栏和说明挤成窄条。 */
.room-toolbar {
  display: flex; align-items: center; gap: 7px; flex-wrap: nowrap;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 7px 6px; margin-bottom: 8px; box-shadow: var(--shadow-1);
}
.room-toolbar-lead { display: flex; align-items: baseline; gap: 8px; flex: 0 0 auto; }
.room-toolbar-title { font-size: 16px; font-weight: 700; letter-spacing: -.3px; white-space: nowrap; }
.room-toolbar-scope { font-size: 12px; color: var(--muted); white-space: nowrap; }
/* 筛选条整体单行：控件太多时各自收紧，不允许堆成三排把房卡推下去 */
/* 【D-28】原来是 flex-wrap:nowrap + overflow:hidden。
   实测 1500px 宽时「数据来源」按钮占位 1483–1518，父容器右边界 1476，
   被切掉 42px，中心点 elementFromPoint 命中 .content —— 一个导航按钮既看不见也点不到。
   改为允许折行：放得下时行为和原来完全一致，放不下时整块换行而不是被切掉。*/
.room-toolbar-filters { display: flex; align-items: center; gap: 5px; row-gap: 6px; flex: 1 1 auto; min-width: 0; flex-wrap: wrap; }
.room-toolbar-filters .search { width: 100px; }
.room-toolbar-filters .select { width: 94px; }
/* 多选/密度属于显示偏好，收窄成紧凑控件，给房间网格让出宽度 */
.room-toolbar-filters .room-tool-tools { flex: 0 0 auto; gap: 6px; }
.room-toolbar-filters .room-tool-tools .btn { padding: 7px 10px; font-size: 12px; }
.room-toolbar-filters .room-tool-tools .select { width: 74px; }
.room-toolbar-actions { display: flex; align-items: center; gap: 5px; flex: 0 0 auto; }
.room-toolbar-actions .btn { min-height: 30px; padding: 5px 9px; font-size: 12px; }

.room-body { display: grid; grid-template-columns: minmax(0, 1fr); gap: 12px; align-items: start; }
/* 经营待办：默认收起的抽屉，房间网格因此可以占满整幅宽度 */
.room-todo-drawer {
  position: fixed; top: var(--topbar-h); right: 0; z-index: 35;
  width: min(360px, 100vw); max-height: calc(100vh - var(--topbar-h));
  overflow-y: auto; background: var(--surface);
  border-left: 1px solid var(--line); box-shadow: var(--shadow-3);
  padding: 12px 14px;
}
.room-todo-drawer[hidden] { display: none; }
.room-todo-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.room-todo-head strong { font-size: 14px; }
.room-todo-panel {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 10px 12px; box-shadow: var(--shadow-1);
  position: sticky; top: calc(var(--topbar-h) + 12px);
  max-height: calc(100vh - var(--topbar-h) - 24px); overflow-y: auto;
}
.product-room-shell {
  display: grid; grid-template-columns: 236px minmax(0, 1fr);
  gap: 14px; align-items: start;
}
.product-room-rail {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 12px; box-shadow: var(--shadow-1);
  position: sticky; top: calc(var(--topbar-h) + 16px);
  max-height: calc(100vh - var(--topbar-h) - 32px); overflow-y: auto;
}
.product-room-main { min-width: 0; display: flex; flex-direction: column; gap: 12px; }
.product-rail-section + .product-rail-section { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--line-2); }
.product-rail-label { font-size: 11px; color: var(--muted-2); font-weight: 700; letter-spacing: .5px; margin-bottom: 8px; }
.product-building-filter { display: flex; flex-direction: row; gap: 4px; }
.product-building-filter .toggle { display: flex; flex-direction: row; padding: 3px; gap: 3px; background: var(--surface-3); border-radius: var(--radius-sm); }
.product-building-filter .toggle button { border-radius: var(--radius-xs); padding: 6px 8px; text-align: left; white-space: nowrap; }
/* 楼层范围：横向一排，不再竖排挤高工具条 */
#productFloorFilter { flex: 0 0 auto; }
#productFloorFilter .floor-row { padding: 0; border: 0; display: flex; flex-direction: row; flex-wrap: nowrap; gap: 4px; }
#productFloorFilter .floor-button { padding: 6px 8px; font-size: 12px; border-radius: var(--radius-xs); white-space: nowrap; }
/* 全部楼栋模式下楼层按钮带南/北前缀，进一步压缩以保住单行工具条 */
#productFloorFilter .floor-row.compact { gap: 2px; }
#productFloorFilter .floor-row.compact .floor-button { padding: 6px 4px; font-size: 11px; }
#productRoomTodos:empty { display: none; }

.workspace { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow-1); overflow: hidden; }
.w-header { padding: 7px 14px; border-bottom: 1px solid var(--line-2); display: flex; gap: 14px; align-items: center; justify-content: space-between; }
/* 【D-28】原来写死 max-width:330px，而口径说明实际宽 818px —— 有 488px 永久不可读，
   用户根本看不到「可出租 N 间（房态公式）· 功能房 N 间不计入出租率」这段。
   同一行右侧本来就有大片空白（legend 是 flex:1 且右对齐），放宽不会挤掉任何控件。
   这是口径说明而不是装饰，截断等于没有，所以改为允许折行而不是省略号。*/
.w-header > div:first-child { flex: 0 1 auto; min-width: 140px; max-width: min(560px, 42vw); }
.w-header .board-view-switch { flex: 0 0 auto; }
.w-header .legend { flex: 1 1 auto; justify-content: flex-end; min-width: 0; }
.w-header .room-export-tools { flex: 0 0 auto; display: flex; align-items: center; }
.room-export-group { display: flex; align-items: center; gap: 5px; }
.room-export-group .select { height: 28px; font-size: 12px; padding: 0 7px; min-width: 0; }
.room-export-group .btn { height: 28px; padding: 0 7px; font-size: 12px; white-space: nowrap; }
/* 全部楼栋模式下工具条更挤：≤1400 隐藏「荷馨苑 · 189 间」范围文案（同一信息在提示行与数据连接页可见） */
@media (max-width: 1400px) {
  .room-toolbar-scope { display: none; }
}
.w-title { font-size: var(--fs-h2); font-weight: 700; white-space: nowrap; }
.w-caption { font-size: 12px; color: var(--muted); margin-top: 1px; line-height: 1.45; max-width: 100%; white-space: normal; overflow-wrap: break-word; }
.toolbar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.room-tool-tools { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.search, .select {
  border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--surface);
  color: var(--ink); height: 34px; padding: 0 10px; font-size: 13px; outline: 0;
}
.search:focus, .select:focus { border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-soft); }
.search { width: 170px; }
#filterStatus { min-width: 104px; }
.select { min-width: 116px; }
.toggle { display: flex; padding: 3px; border-radius: var(--radius-sm); background: var(--surface-3); gap: 2px; }
.toggle button { border: 0; background: transparent; color: var(--muted); padding: 6px 12px; border-radius: var(--radius-xs); font-weight: 500; font-size: 13px; }
.toggle button.active { background: var(--surface); color: var(--brand-ink); box-shadow: var(--shadow-1); font-weight: 600; }

.legend {
  display: flex; gap: 11px; align-items: center; flex-wrap: wrap; overflow: visible;
  font-size: 11px; color: var(--muted); padding: 0; position: relative;
}
/* 【D-154】「更多状态」弹层：图例首屏只留 5 项，其余 4 种状态在这里可查。
   原来是 flex-wrap:nowrap + overflow:hidden，后 4 项在 1440px 下被直接裁掉、也没有任何提示。 */
.legend-more { position: relative; }
.legend-more > summary {
  list-style: none; cursor: pointer; white-space: nowrap;
  font-size: 11px; color: var(--brand-ink);
  border: 1px dashed var(--brand-line); border-radius: 999px; padding: 0 8px; line-height: 17px;
}
.legend-more > summary::-webkit-details-marker { display: none; }
.legend-more > summary::after { content: " ▾"; color: var(--muted-2); }
.legend-more[open] > summary::after { content: " ▴"; }
.legend-more-list {
  position: absolute; right: 0; top: calc(100% + 7px); z-index: 9;
  display: flex; flex-direction: column; gap: 7px; min-width: 176px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 10px 13px; box-shadow: 0 10px 26px rgba(15, 35, 60, .16);
}
.legend-item { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; white-space: nowrap; }
.dot { width: 9px; height: 9px; border-radius: 2px; background: var(--muted-3); flex: 0 0 auto; }
.dot.leased { background: var(--teal); }
.dot.renew { background: var(--warn); }
.dot.vacant { background: var(--ok); }
.dot.utility { background: var(--muted-2); }
.dot.unknown { background: var(--surface); border: 1.5px dashed var(--muted-3); }
.legend-spacer { margin-left: auto; color: var(--muted-2); white-space: nowrap; }

.board-view-switch { display: flex; gap: 3px; padding: 3px; background: var(--surface-3); border-radius: var(--radius-sm); flex: 0 0 auto; }
.board-view-switch button { border: 0; background: transparent; border-radius: var(--radius-xs); padding: 5px 11px; color: var(--muted); font-size: 12px; }
.board-view-switch button[aria-pressed="true"] { background: var(--surface); color: var(--brand-ink); box-shadow: var(--shadow-1); font-weight: 600; }

.floor-scroll {
  overflow-x: auto; max-width: 100%; padding: 14px 16px 18px;
  background: var(--surface-sunken); min-height: 200px;
  scrollbar-color: #C3CEDB transparent;
}
.floor-scroll[data-board-mode="cards"] { overflow-x: hidden; background: var(--surface-sunken); }
.floor-stack { display: flex; flex-direction: column; gap: 20px; }
.floor-unit { width: 100%; min-width: 0; }
.floor-unit + .floor-unit { border-top: 1px dashed var(--line); padding-top: 16px; }
.floor-heading { display: flex; align-items: center; justify-content: space-between; margin: 0 0 8px; }
.floor-heading strong { font-size: 13px; color: var(--ink); }
.floor-heading span { font-size: 12px; color: var(--muted-2); }
.floor-plan { width: max-content; min-width: 880px; border: 1px solid var(--line); border-radius: var(--radius); padding: 14px 16px 12px; background: var(--surface); }
.side-name { font-size: 11px; letter-spacing: 1.4px; color: var(--muted-2); font-weight: 700; display: flex; align-items: center; gap: 10px; margin: 0 0 8px; }
.side-name::after { content: ""; display: block; height: 1px; flex: 1; background: var(--line-2); }
.side-name.south { margin: 12px 0 0; }
.side-name.south::before { content: ""; display: block; height: 1px; flex: 1; background: var(--line-2); }
.side-name.south::after { display: none; }
.corridor { font-size: 11px; color: var(--muted-2); text-align: center; padding: 6px 0; letter-spacing: 1px; background: var(--surface-3); border-radius: var(--radius-xs); margin: 6px 0; }
.room-grid { display: grid; grid-template-columns: repeat(var(--cols, 14), minmax(54px, 1fr)); gap: 5px; min-height: 78px; }
.landmark { height: 78px; border-radius: var(--radius-xs); background: var(--surface-3); color: var(--muted-3); display: grid; place-items: center; font-size: 11px; border: 1px solid var(--line-2); }
.landmark svg { width: 18px; height: 18px; opacity: .5; }

/* 平面与卡片共用的房间单元 */
.room {
  position: relative; border: 1px solid var(--line); background: var(--surface);
  border-radius: var(--radius-xs); min-height: 78px; padding: 7px 7px 6px;
  display: flex; flex-direction: column; align-items: flex-start; gap: 3px;
  text-align: left; position: relative; transition: box-shadow .15s, border-color .15s, transform .15s;
  border-left: 3px solid var(--muted-3);
}
.room:hover { transform: translateY(-1px); box-shadow: var(--shadow-2); z-index: 1; }
.room .rid { font-size: 15px; font-weight: 700; color: var(--ink); line-height: 1.2; }
.room .rn { font-size: 12px; color: var(--muted); line-height: 1.35; width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.room .tag { position: absolute; top: 8px; right: 7px; width: 7px; height: 7px; border-radius: 50%; background: var(--muted-3); }
/* 【A 级改造 · 绿噪点归零】原来是 20px 满填 --ok 实心圆 + 白 wifi 图标。
   实测 189 间里 110 间装了网络，于是整屏形成 110 个高饱和绿点组成的规则点阵——
   它比房号更抢眼，是首屏最亮的元素；而绿色在状态条图例里代表「空置/可租」。
   同一个绿在一屏里表达两件事，且是**最不重要的那件占了最强的色**。
   "网络是否已装"是房间的一次性属性，不是当天要做的动作（这条判断 G-54 已经在手机端
   执行过：≤760px 直接 display:none）。桌面端保留信息但把它降级成"在场标记"：
     · 20px 实心圆 → 8px 空心圈，1.5px --muted-2 描边，无填充、无白描边光晕；
     · 内部 SVG 隐掉（8px 里画 wifi 三弧只会糊成一团），语义由 title/aria-label 承担；
     · 绿回归状态语义，整页只剩"状态色"一种颜色语言。
   有超期徽标时隐藏该点：徽标在 right:8/bottom:7，与点位重叠，而超期比装网重要。 */
.room .wifi { position: absolute; bottom: 7px; right: 7px; width: 8px; height: 8px; border-radius: 50%; background: transparent; border: 1.5px solid var(--muted-2); color: transparent; display: block; box-shadow: none; }
.room .wifi svg { display: none; }
.room.room-card.has-badge .wifi { display: none; }
.room.muted { opacity: .26; pointer-events: none; }
.room[aria-pressed="true"] { box-shadow: 0 0 0 2px var(--brand); }

.room.leased { border-left-color: var(--teal); background: var(--teal-soft); }
.room.renew { border-left-color: var(--warn); background: var(--warn-soft); }
.room.vacant { border-left-color: var(--ok); background: #F1FAF6; border-style: solid; }
.room.utility, .room.sample { border-left-color: var(--muted-2); background: var(--surface-3); }
.room.expiredOcc { border-left-color: var(--danger); background: var(--danger-soft); }
.room.noContract { border-left-color: var(--danger); background: var(--danger-soft); border-style: dashed; }
.room.scheduled { border-left-color: var(--info); background: var(--info-soft); }
.room.blocked { border-left-color: var(--grey); background: var(--grey-soft); }
.room.test { border-left-color: var(--violet); background: var(--violet-soft); }
.room.unknown { border-left-color: var(--muted-3); background: var(--surface); border-style: dashed; }
.room.leased .tag { background: var(--teal); }
.room.renew .tag { background: var(--warn); }
.room.vacant .tag { background: var(--ok); }
.room.expiredOcc .tag, .room.noContract .tag { background: var(--danger); }
.room.scheduled .tag { background: var(--info); }
.room.blocked .tag { background: var(--grey); }
.room.test .tag { background: var(--violet); }

/* 房态卡片：房卡是默认经营视图 */
.room-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 9px; }
.room.room-card { height: 104px; padding: 9px 10px 9px 12px; gap: 4px; width: 100%; overflow: hidden; border-left-width: 4px; }
/* 【D-150】房卡抬头溢出：≤1440px 时卡片缩到 134px，内容盒只有 110px，
   「南101」+「功能房电房」两个 nowrap 一横排就把 span.card-head 撑到 117px。
   房号永不得截断（它是这卡的唯一标识），改让状态胶囊收缩省略；
   完整状态本来就在 aria-label / title 里，看不清可悬停。 */
.room.room-card .card-head { display: flex; align-items: center; justify-content: space-between; gap: 6px; width: 100%; min-width: 0; }

.room.room-card .card-head > .rid { flex: 0 0 auto; }
.room.room-card .card-head > .state-pill { flex: 0 1 auto; min-width: 0; }
.room.room-card .card-head .pill-t { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.room.room-card .rid { font-size: var(--fs-room); font-weight: 700; white-space: nowrap; }
.room.room-card .rn { font-size: 13px; color: var(--ink-2); font-weight: 500; }
.room.room-card .card-meta { font-size: 12px; color: var(--muted); line-height: 1.4; }

.state-pill {
  display: inline-flex; align-items: center; gap: 3px;
  font-size: 11px; font-weight: 600; line-height: 1.7; padding: 0 7px;
  border-radius: 999px; white-space: nowrap; background: var(--surface-3); color: var(--muted);
  border: 1px solid var(--line-2);
}
.room.leased .state-pill { background: var(--teal-soft); color: var(--teal); border-color: var(--teal-line); }
.room.renew .state-pill { background: var(--warn-soft); color: var(--warn); border-color: var(--warn-line); }
.room.expiredOcc .state-pill, .room.noContract .state-pill { background: var(--danger-soft); color: var(--danger); border-color: var(--danger-line); }
.room.scheduled .state-pill { background: var(--info-soft); color: var(--info); border-color: var(--info-line); }
.room.blocked .state-pill { background: var(--grey-soft); color: var(--grey); border-color: var(--grey-line); }
.room.test .state-pill { background: var(--violet-soft); color: var(--violet); border-color: #D9D2F0; }
.room.utility .state-pill, .room.sample .state-pill { background: var(--grey-soft); color: var(--grey); }
.room.vacant .state-pill { background: var(--ok-soft); color: var(--ok); border-color: var(--ok-line); }

.overdue-badge {
  position: absolute; right: 8px; bottom: 7px;
  font-size: 11px; font-weight: 700; line-height: 1.6; padding: 0 6px;
  border-radius: var(--radius-xs); background: var(--danger); color: #fff;
}
.room.room-card.has-badge { padding-bottom: 22px; }
/* 超期徽标钉在 right:8/bottom:7，与 L2 右端的网络点同一块地方；超期比装网重要，让徽标赢。 */
.room.room-card.has-badge .net { display: none; }
.v5-room-mark {
  font-size: 11px; line-height: 1.6; padding: 0 5px; border-radius: var(--radius-xs);
  background: var(--warn-soft); color: var(--warn); border: 1px solid var(--warn-line);
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.v5-room-mark.repair { background: var(--danger-soft); color: var(--danger); border-color: var(--danger-line); }
.v5-room-mark.other { background: var(--info-soft); color: var(--info); border-color: var(--info-line); }
.board-select-check {
  position: absolute; right: 6px; bottom: 6px; background: var(--brand); color: #fff;
  font-size: 11px; border-radius: var(--radius-xs); padding: 0 5px;
}
.board-empty { padding: 36px 18px; text-align: center; color: var(--muted); font-size: 13px; }
.board-empty strong { display: block; color: var(--ink-2); margin-bottom: 5px; font-size: 14px; }
.search-hint { padding: 9px 16px; font-size: 12px; color: var(--muted-2); border-top: 1px solid var(--line-2); background: var(--surface-2); }

/* 平面导航工具条 */
.product-plan-controls {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 8px 16px; border-bottom: 1px solid var(--line-2); background: var(--surface-2);
}
.product-plan-controls[hidden] { display: none; }
.product-plan-controls > span { font-size: 12px; color: var(--muted); font-weight: 600; }
.product-plan-controls button {
  min-width: 30px; height: 28px; padding: 0 9px; border: 1px solid var(--line);
  background: var(--surface); border-radius: var(--radius-xs); font-size: 12px; color: var(--ink-2);
}
.product-plan-controls button:hover { border-color: var(--brand-line); color: var(--brand-ink); }
.product-plan-controls button:disabled { opacity: 1; color: #64707E; background: var(--surface-2); border-color: var(--line); cursor: not-allowed; }
.product-plan-controls output { font-size: 12px; color: var(--ink); font-weight: 600; min-width: 46px; text-align: center; font-variant-numeric: tabular-nums; }
.product-plan-controls small { font-size: 11px; color: var(--muted-2); margin-left: auto; }
.floor-scroll.is-panning { cursor: grabbing; user-select: none; }
.floor-scroll.product-plan-viewport { cursor: default; }

/* 多选 */
.selection-toolbar { display: flex; gap: 8px; align-items: center; }
.selection-bar {
  position: sticky; bottom: 12px; z-index: 12;
  display: flex; align-items: center; gap: 12px; padding: 10px 14px;
  border: 1px solid var(--brand-line); border-radius: var(--radius);
  background: var(--brand-soft); box-shadow: var(--shadow-2); font-size: 13px; color: var(--brand-deep);
}
.selection-bar[hidden] { display: none; }
.selection-bar .selection-rooms { flex: 1; font-size: 12px; color: var(--muted); max-height: 42px; overflow: auto; }
.selection-bar strong { white-space: nowrap; }
.room-action-grid { display: flex; gap: 8px; flex-wrap: wrap; margin: 0 0 16px; }
.room-action-grid .btn { flex: 1; }
.room-history-link { display: block; width: 100%; padding: 10px 0; border: 0; border-bottom: 1px solid var(--line-2); background: none; text-align: left; color: var(--ink); font-size: 12.5px; }
.room-history-link small { display: block; color: var(--muted); font-size: 12px; margin-top: 3px; }

/* ---------- 5. 房间检查面板（抽屉） ---------- */
/* 宽屏：抽屉是"检查面板"而非模态框。内容区整体右移让出 440px，
   房卡不会被盖住，可以边看边点别的房间；遮罩不拦截指针事件。
   窄屏：抽屉覆盖全屏，遮罩恢复为可点击关闭的模态层。 */
.veil { position: fixed; inset: 0; background: rgba(20, 32, 48, .34); z-index: 40; display: none; }
.veil.show { display: block; }
.drawer {
  position: fixed; right: 0; top: 0; bottom: 0; z-index: 41;
  width: min(440px, 100vw); background: var(--surface);
  transform: translateX(104%); transition: transform .24s ease;
  display: flex; flex-direction: column; box-shadow: var(--shadow-3);
  border-left: 1px solid var(--line);
}
.drawer.show { transform: translateX(0); }
/* 【A 级改造】宽度收到 380（原 440）。
   这里有三个坑，都踩过了：
     ① 抽屉宽度写在**两个地方**——`index.html:46` 的早期防闪 `<style>` 里有
        `#drawer{width:min(440px,100vw)}`，本文件第 624 行还有一份 `.drawer{...}`；
     ② `#drawer` 是 ID 选择器（1,0,0），`.drawer` 是类选择器（0,1,0），**ID 赢**，
        所以只写 `.drawer{width:380px}` 无论放多后都没用（第一版实测 drawerW 仍 440）；
     ③ 抽屉还支持用户拖拽改宽（v5-features.js `installDrawerGrip`，结果存
        localStorage `pms.drawerWidth` 并写成**内联样式**），内联样式优先级最高，
        用户拖过一次后这里的默认值就不再生效——这是设计意图，不要用 !important 去抢。
   因此用 `body #drawer`（1,0,1）压过 ID 规则，只改宽屏默认值，≤1100 保持原宽。
   注意：必须放在 `.drawer` 基础规则之后（同特异性看源码顺序）。 */
@media (min-width: 1101px) {
  body #drawer { width: 380px; }
}
/* 【2026-11-02 用户投诉「这个页面大量留白」】
   根因不是内容排版空，是抽屉被 `top:0;bottom:0` 拉成满屏 1000px 高，而这间房的实际内容
   只有 450px 左右（1 份合同、0 住客、3 个折叠块收着），剩下 380px 全是白底。
   桌面端改成高度贴合内容（bottom:auto + height:auto + max-height:100vh），内容长了照旧滚动；
   手机端（≤1100）保持全屏侧滑，因为全屏抽屉是移动端预期，且底部标签栏要一起被盖住。
   注意特异性：`#drawer` 的 top/bottom 写在 index.html:46 的防闪 <style> 里（ID 选择器），
   必须用 `body #drawer`(1,0,1) 才盖得过；用户拖拽改宽写的是内联 width，不受这里影响。 */
@media (min-width: 1101px) {
  body #drawer { top: 0; bottom: auto; height: auto; max-height: 100vh; border-radius: 0 0 0 var(--radius-lg); }
  /* 【2026-11-02 修 bug：内容多的时候底部按钮被挤出屏幕】
     上面把抽屉改成 height:auto + max-height:100vh 之后，这一行只写了 `flex:0 1 auto`，
     漏了 `min-height:0`。flex 项的 min-height 默认是 auto（= 内容高度），所以
     .drawer-scroll 拒绝收缩：内容一多，它仍按自己的完整高度排版，
     抽屉被 max-height 截到 100vh 后，超出的部分（连同底部动作条）直接画到视口外面；
     而 body 的滚动已经被锁住（T-35），用户既看不到按钮、又把页面滚不动 —— 就是「插不下去」。
     加 min-height:0 才能让它真正收缩、由自己的 overflow-y:auto 出滚动条。 */
  body #drawer .drawer-scroll { flex: 0 1 auto; min-height: 0; }
}
/* 【2026-11-02】抽屉和遮罩永远压在最上面（模态框 9000、toast 99999 仍然更高）。
   原来 #drawer 只有 z-index:41：顶栏、悬浮胶囊、构建徽标之类只要带了 z-index 就能盖住它。
   一旦有东西盖在抽屉上，用户点那块「看起来是空白」的地方其实点了别人，
   遮罩收不到 click，表现就是「点了没反应、关不掉」。
   抬到 300 以后，抽屉区域内不存在别人的东西——点哪儿要么是抽屉内容，要么是遮罩。 */
body #drawer { z-index: 300; }
body #veil { z-index: 299; }

/* 【B10·2026-11-01】≤760px 底部标签栏是 fixed 且 z-index:60，而房间抽屉只有 41：
   抽屉底部那一排主操作（办理退宿/换房/加房/交还）整块落在标签栏下面，
   390 视口 elementFromPoint 实测命中的是标签栏按钮，点不到。
   把抽屉抬到标签栏之上；遮罩同步抬高——否则标签栏不被遮住、看起来还能点，
   用户会在抽屉开着的时候被引到底部导航上去。
   `.pms-actionbar` 自身已经带 calc(10px + env(safe-area-inset-bottom))，不需要再补。
   注意特异性：index.html:46 的防闪样式里有 `#drawer{...z-index:41}`，是 ID 选择器(1,0,0)，
   写 `.drawer{...}` 会被它压住、白改一场。必须用 `body #drawer`(1,0,1) 才盖得过。 */
@media (max-width: 760px) {
  body #drawer { z-index: calc(var(--z-tabbar) + 10); }
  body #veil { z-index: calc(var(--z-tabbar) + 9); }
}
.drawer-top {
  padding: 16px 20px 12px; background: var(--surface-2); border-bottom: 1px solid var(--line);
  display: flex; align-items: flex-start; justify-content: space-between; gap: 12px;
}
/* 【2026-11-02】关闭按钮放大到 36×36 并禁止被标题挤压：
   32px 在 380px 宽的抽屉里偏小，用户点边缘容易落空，落空就等于「关不掉」。 */
.drawer-top .icon-btn { width: 36px; height: 36px; flex: 0 0 auto; }
.drawer-top .icon-btn svg { width: 18px; height: 18px; }
/* 【2026-11-02 五轮 · 抽屉统一排版】字号/字重/颜色一次性收敛，见下方 #drawer 的 --rd-* 阶梯。 */
.drawer-room { font-size: 22px; font-weight: 700; letter-spacing: -.3px; color: var(--ink); }
.drawer-sub { font-size: 12px; color: var(--muted); margin-top: 3px; }
.drawer-scroll { padding: 16px 20px 28px; overflow-y: auto; flex: 1; }
.section-label { font-size: 11px; color: var(--muted-2); letter-spacing: .6px; font-weight: 700; margin: 18px 0 9px; }
.section-label:first-child { margin-top: 0; }
.details-box { border: 1px solid var(--line); border-radius: var(--radius); padding: 12px 14px; margin-bottom: 14px; background: var(--surface); }
.drow { display: flex; justify-content: space-between; align-items: flex-start; gap: 16px; padding: 8px 0; border-bottom: 1px solid var(--line-3); font-size: 13px; }
.drow:last-child { border-bottom: 0; }
.drow .key { color: var(--muted); white-space: nowrap; }
.drow .val { text-align: right; color: var(--ink); overflow-wrap: anywhere; font-weight: 500; }
/* 【D-168】抽屉「房间信息」= 一句话状态 + 一行事实 + 可折叠的原始字段。
   原来这里是 8 行键值对、两处英文小标题和一段合同说明，一间空房要滚一屏半才
   看到操作按钮。抽屉是「看一眼、办件事」的地方，排障字段默认收起。 */
.rd-hero { display: flex; align-items: center; gap: 8px; margin-bottom: 9px; }
.rd-hero .pill { font-size: 12px; padding: 3px 10px; }
.rd-hero .btn { margin-left: auto; }
/* 【A 级改造 · 给「房态 ▾」预留位置】rd-hero 现在只有一颗「改底档」，靠 margin-left:auto 顶到右边。
   抽屉里要并排插第二颗按钮时，**两颗都吃到 auto**，剩余空间被均分：
   实测注入后 空置(1021–1065) → 房态▾(1186–1243) → 改底档(1364–1420)，
   两颗按钮之间裂出 121px 空洞，房态▾ 看起来像居中悬着。
   只让"第一颗按钮"吃 auto，后续按钮 margin-left:0 靠 flex gap 贴住：
   单按钮场景（今天）位置一个字不变，双按钮场景自动变成 pill 左、两颗按钮右并排。 */
.rd-hero .btn ~ .btn { margin-left: 0; }
.rd-facts { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 7px; font-size: 12.5px; color: var(--ink-2); line-height: 1.75; margin-bottom: 12px; }
.rd-facts i { font-style: normal; color: var(--muted-2); }
.rd-fold { margin: -4px 0 14px; }
.rd-fold > summary { cursor: pointer; font-size: 11px; color: var(--muted-2); padding: 5px 0; list-style: none; }
.rd-fold > summary::-webkit-details-marker { display: none; }
.rd-fold > summary::before { content: '▸ '; }
.rd-fold[open] > summary::before { content: '▾ '; }
.rd-fold .drow { font-size: 12px; padding: 5px 0; }
.subtabs { display: flex; background: var(--surface-3); border-radius: var(--radius-sm); padding: 3px; margin: 12px 0 14px; gap: 3px; }
.subtabs button { flex: 1; border: 0; background: transparent; padding: 7px 6px; border-radius: var(--radius-xs); font-size: 12.5px; font-weight: 500; color: var(--muted); }
.subtabs button.active { color: var(--brand-ink); background: var(--surface); box-shadow: var(--shadow-1); font-weight: 600; }
.resident { border: 1px solid var(--line); border-radius: var(--radius); padding: 12px 13px; margin: 8px 0; display: flex; justify-content: space-between; align-items: center; gap: 10px; }
.resident .res-name { font-weight: 600; color: var(--ink); font-size: 13px; }
.resident .res-sub { font-size: 12px; color: var(--muted); margin-top: 3px; }
.form-disclaimer { font-size: 13px; color: var(--muted); line-height: 1.8; }
.pill { display: inline-flex; align-items: center; border-radius: 999px; padding: 2px 9px; font-size: 12px; font-weight: 600; background: var(--surface-3); color: var(--muted-2); }
.pill.renew { background: var(--warn-soft); color: var(--warn); }
.pill.vacant { background: var(--brand-soft); color: var(--brand-ink); }
.pill.warn { background: var(--warn-soft); color: var(--warn); }
/* 【2026-11-02 五轮】抽屉里的状态胶囊原来「在租」走默认灰底灰字 —— 它是这一屏最该被看见的
   结论（这间房现在什么状态），却比下面的企业名还淡。按房态语义上色，与后端权威块的
   .pms-auth-s-* 同一套色系：承租中=信息蓝、空置=浅蓝、限制=灰、到期/待核=琥珀。 */
.pill.leased { background: var(--info-soft); color: var(--info); }
.pill.limit  { background: var(--grey-soft); color: var(--grey); }
.pill.due    { background: var(--warn-soft); color: var(--warn); }
#drawer .rd-hero .pill { font-size: 12.5px; padding: 3px 11px; }

/* 底部动作条：与右键菜单共用同一套可用命令 */
.pms-actionbar { border-top: 1px solid var(--line); background: var(--surface); padding: 10px 14px calc(10px + env(safe-area-inset-bottom)); flex: 0 0 auto; }
.pms-actionbar.is-hidden { display: none; }
/* 【D-167】原来 overflow-x:auto + flex:1 0 auto：4 个按钮在 370px 抽屉里必然溢出，
   最右那个被切一半、还带横向滚动条，看着像坏了。改成允许换行、按钮可收缩。 */
.pms-actionbar-inner { display: flex; flex-wrap: wrap; gap: 7px; }
.pms-act {
  flex: 1 1 auto; min-width: 0; border: 1px solid var(--line); background: var(--surface); color: var(--ink-2);
  font-size: 12px; padding: 9px 10px; border-radius: var(--radius-sm); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
.pms-act:hover { border-color: var(--brand-line); color: var(--brand-ink); }
.pms-act.primary { background: var(--brand); border-color: var(--brand); color: #fff; font-weight: 600; }
.pms-act.primary:hover { background: var(--brand-ink); color: #fff; }
.pms-act.ghost { background: var(--surface-3); flex: 0 0 auto; }
.pms-act:disabled { opacity: 1; color: #64707E; background: var(--surface-3); cursor: not-allowed; }
#drawerBody .room-action-grid { display: none; }

/* ---------- 6. 右键菜单与弹窗 ---------- */
.v5-menu {
  position: fixed; z-index: 1200; width: 248px; max-width: calc(100vw - 20px);
  border-radius: var(--radius); box-shadow: var(--shadow-3);
  border: 1px solid var(--line); background: var(--surface); padding: 6px;
  max-height: calc(100vh - 24px); overflow-y: auto;
}
.v5-menu-title { font-size: 11px; font-weight: 700; color: var(--muted-2); padding: 5px 8px 9px; border-bottom: 1px solid var(--line-2); margin-bottom: 4px; }
.v5-menu button { display: flex; align-items: center; gap: 8px; width: 100%; border: 0; background: transparent; text-align: left; border-radius: var(--radius-xs); padding: 9px 10px; font-size: 13px; color: var(--ink); }
.v5-menu .v5-menu-label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* 【D-167】两层菜单：办理入住 / 设置房态▸。子菜单内联展开而不是侧向弹出——
   手机上侧向弹出必然出界，而且「点一下就在下面展开」比追着箭头看更直观。 */
.v5-menu .v5-menu-caret { flex: none; color: var(--muted-2); font-size: 11px; transition: transform .12s; }
.v5-menu button[aria-expanded="true"] .v5-menu-caret { transform: rotate(90deg); }
.v5-menu button[aria-expanded="true"] { background: var(--brand-soft); color: var(--brand-ink); }
.v5-menu .v5-menu-check { flex: none; color: var(--brand); font-weight: 700; font-size: 12px; }
/* 【D-167】子菜单要一眼看出「这是设置房态里面的」，所以给底色块 + 品牌色竖线 + 左缩进；
   只加 8px 缩进和 1px 浅线时，截图里跟父项几乎齐平，用户以为又是并列的五个入口。 */
.v5-menu .v5-menu-sub {
  margin: 3px 0 6px; padding: 5px 5px 5px 10px;
  border-left: 2px solid var(--brand-line); background: var(--surface-3);
  border-radius: var(--radius-xs);
}
.v5-menu .v5-menu-sub button { padding: 7px 8px; }
.v5-menu .v5-menu-sub button:hover:not([disabled]) { background: var(--surface); }
.v5-menu .v5-menu-sub button .v5-menu-label { color: var(--ink-2); }
.v5-menu .v5-menu-sub button[disabled] .v5-menu-label { color: #64707E; }
.v5-menu button:hover:not([disabled]) { background: var(--brand-soft); color: var(--brand-ink); }
.v5-menu button[disabled] { color: #64707E; cursor: not-allowed; }
.v5-menu button[disabled]:hover { background: transparent; }
.v5-menu button[disabled] .v5-menu-check { color: #64707E; }
/* 菜单打开时会把焦点放到第一项，浏览器默认给一圈蓝色描边——鼠标用户会误以为那是选中态。
   只在键盘导航（:focus-visible）时显示焦点环。 */
.v5-menu button:focus { outline: none; }
.v5-menu button:focus-visible { outline: 2px solid var(--brand-line); outline-offset: -2px; }
.v5-modal-shell { position: fixed; inset: 0; z-index: 1300; display: flex; align-items: center; justify-content: center; padding: 16px; }
.v5-modal-backdrop { position: absolute; inset: 0; background: rgba(21, 36, 52, .45); }
.v5-modal { position: relative; z-index: 1; background: var(--surface); border-radius: var(--radius-lg); width: min(440px, 100%); box-shadow: var(--shadow-3); }
.v5-modal-head { padding: 16px 20px; border-bottom: 1px solid var(--line); display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.v5-modal-head h3 { margin: 0; font-size: 16px; }
.v5-modal-head p { margin: 4px 0 0; font-size: 12px; color: var(--muted); }
.v5-small-icon { font-size: 22px; background: transparent; border: 0; color: var(--muted-2); cursor: pointer; line-height: 1; }
.v5-modal-body { padding: 18px 20px; display: flex; flex-direction: column; gap: 9px; }
.v5-modal-body label { font-size: 12px; font-weight: 600; }
.v5-modal-body select, .v5-modal-body textarea { font: inherit; border: 1px solid var(--line); padding: 9px 10px; border-radius: var(--radius-sm); font-size: 13px; background: var(--surface); color: var(--ink); }
.v5-modal-body textarea { resize: vertical; }
.v5-warning { font-size: 12px; line-height: 1.6; color: var(--warn); background: var(--warn-soft); border: 1px solid var(--warn-line); padding: 8px 10px; border-radius: var(--radius-sm); }
.v5-form-error { font-size: 12px; color: var(--danger); }
.v5-modal-actions { padding: 0 20px 18px; display: flex; gap: 8px; justify-content: flex-end; }
.toaster { position: fixed; left: 50%; bottom: 28px; transform: translateX(-50%); z-index: 1400; display: flex; flex-direction: column; gap: 8px; align-items: center; pointer-events: none; }
.toast { background: var(--ink); color: #fff; font-size: 13px; padding: 9px 14px; border-radius: var(--radius-sm); box-shadow: var(--shadow-2); }
.toast.error { background: var(--danger); }

/* ---------- 7. 通用组件：表格 / 表单 / 报表 ---------- */
.section-card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 20px; box-shadow: var(--shadow-1); }
.page-grid { display: grid; grid-template-columns: minmax(0, 1.4fr) minmax(260px, .75fr); gap: 16px; }
.info-table { width: 100%; }
.info-table td { border-bottom: 1px solid var(--line-3); padding: 11px 4px; font-size: 13px; }
.info-table td:first-child { color: var(--muted); width: 180px; }
.clean-list { padding-left: 18px; font-size: 13px; line-height: 2; color: var(--ink-2); }
.callout { background: var(--warn-soft); border: 1px solid var(--warn-line); color: var(--warn); padding: 11px 14px; border-radius: var(--radius-sm); font-size: 13px; line-height: 1.7; }
.empty-state { padding: 48px 18px; text-align: center; color: var(--muted); }
.empty-state svg { width: 34px; height: 34px; color: var(--muted-3); margin-bottom: 14px; }
.empty-state strong { display: block; font-size: 15px; color: var(--ink); margin-bottom: 4px; }
.empty-state p { font-size: 13px; margin: 6px 0 0; }
.connection { display: flex; gap: 13px; align-items: center; background: var(--surface-2); border: 1px solid var(--line-2); border-radius: var(--radius-sm); padding: 13px; margin-bottom: 9px; }
.connection .ico { width: 36px; height: 36px; background: var(--brand-soft); color: var(--brand-ink); border-radius: var(--radius-sm); display: grid; place-items: center; flex: 0 0 36px; }
.connection .ico svg { width: 18px; height: 18px; }
.connection strong { font-size: 13px; }
.connection p { font-size: 12px; color: var(--muted); margin: 3px 0 0; }
.notice { display: flex; gap: 10px; align-items: flex-start; padding: 12px 14px; border-radius: var(--radius-sm); background: var(--info-soft); border: 1px solid var(--info-line); color: var(--info); font-size: 13px; line-height: 1.7; margin-bottom: 14px; }
.notice svg { width: 16px; height: 16px; flex: 0 0 16px; margin-top: 2px; }
#localConnectPanel { padding: 16px 18px; border-radius: var(--radius); }
#localConnectPanel.is-connected { padding: 4px 10px; background: var(--ok-soft); border: 1px solid var(--ok-line); color: var(--ok); margin-bottom: 12px; }
#localConnectPanel.is-connected p { font-size: 12px; line-height: 1.45; }
#localConnectPanel.is-connected > strong { display: none; }
#localConnectPanel.is-connected p { margin: 0; font-size: 12px; }
#localConnectPanel.is-connected #localConnectError[hidden] { display: none; }
#localConnectForm { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
#localConnectForm label { font-size: 13px; color: var(--ink-2); }

.v5-alert { padding: 11px 14px; background: var(--warn-soft); border: 1px solid var(--warn-line); border-radius: var(--radius-sm); color: var(--warn); font-size: 13px; margin: 12px 0 16px; line-height: 1.7; }
.v5-panel { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow-1); overflow: hidden; }
.v5-panel-title { padding: 13px 18px; display: flex; gap: 12px; align-items: center; justify-content: space-between; border-bottom: 1px solid var(--line-2); }
.v5-panel-title strong { font-size: var(--fs-h2); }
.v5-panel-title small { font-size: 12px; color: var(--muted); }
.v5-table-wrap { overflow: auto; }
.v5-table { width: 100%; border-collapse: collapse; text-align: left; font-size: 13px; min-width: 860px; }
.v5-table th { font-size: 12px; background: var(--surface-2); color: var(--muted); font-weight: 600; padding: 10px 13px; white-space: nowrap; border-bottom: 1px solid var(--line); }
.v5-table td { padding: 12px 13px; border-bottom: 1px solid var(--line-3); color: var(--ink-2); vertical-align: middle; }
.v5-table tbody tr { cursor: pointer; }
.v5-table tbody tr:hover { background: var(--surface-2); }
.v5-table .company { font-weight: 600; color: var(--ink); }
.v5-table .sub { font-size: 12px; color: var(--muted); margin-top: 4px; }
.v5-pill { display: inline-block; font-size: 12px; background: var(--surface-3); color: var(--ink-2); border-radius: 999px; padding: 2px 9px; white-space: nowrap; }
.v5-pill.renew { background: var(--warn-soft); color: var(--warn); }
.v5-pill.expired { background: var(--grey-soft); color: var(--grey); }
.v5-empty { padding: 44px 22px; text-align: center; color: var(--muted); font-size: 13px; }
/* 【B11·2026-11-01】错误态不能复用空状态样式。
   原来「门店信息读取失败：…」「报表计算失败：…」全渲在 .v5-empty 里：灰字、居中、
   虚线感的空白框、没有图标、没有重试、没有错误配色 —— 用户看到的和"这里本来就没有数据"
   长得一模一样，只会以为系统没数据，不会意识到是读取失败、更不知道可以重试。
   错误态三件事必须同时在场：① 一眼看出是"出错"不是"没有"；② 说清"数据没被改动"；
   ③ 给一个能立刻重试的按钮（不给重试的错误提示等于让用户刷新整页）。 */
.v5-error { border: 1px solid var(--danger-line); background: var(--danger-soft); border-radius: var(--radius); padding: 16px 18px; color: var(--ink); }
.v5-error-head { display: flex; align-items: center; gap: 8px; margin: 0 0 6px; color: var(--danger); font-size: 14px; }
.v5-error-icon { width: 18px; height: 18px; border-radius: 50%; background: var(--danger); color: #fff; font-size: 12px; font-weight: 700; display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto; }
.v5-error p { margin: 4px 0; font-size: 13px; line-height: 1.6; }
.v5-error-hint { color: var(--muted); font-size: 12px; }
.v5-error .btn { margin-top: 10px; }
.v5-info-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
.v5-info-item { border: 1px solid var(--line); padding: 12px; border-radius: var(--radius-sm); background: var(--surface-2); min-width: 0; }
.v5-info-item small { display: block; font-size: 12px; color: var(--muted); margin-bottom: 5px; }
.v5-info-item div { font-size: 13px; word-wrap: break-word; }
.v5-section-label { font-size: 12px; color: var(--muted); font-weight: 600; margin: 20px 0 10px; }
.v5-room-chips { display: flex; gap: 6px; flex-wrap: wrap; }
.v5-room-chip { padding: 4px 9px; background: var(--brand-soft); color: var(--brand-ink); border: 1px solid var(--brand-line); border-radius: var(--radius-xs); font-size: 12px; }
.v5-contract-form { padding: 20px 22px; }
.v5-form-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
.v5-field { display: flex; flex-direction: column; gap: 6px; }
.v5-field label { font-size: 12px; color: var(--ink-2); font-weight: 500; }
.v5-field.full { grid-column: 1 / -1; }
.v5-contract-form input, .v5-contract-form select, .v5-contract-form textarea,
.v5-contract-toolbar input, .v5-contract-toolbar select {
  border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--surface);
  min-height: 38px; padding: 8px 10px; color: var(--ink); font: inherit; font-size: 13px;
}
.v5-contract-form textarea { resize: vertical; min-height: 62px; }
.v5-contract-toolbar { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin: 14px 0; }
.v5-contract-toolbar input { width: min(360px, 100%); flex: 1; min-width: 190px; }
.v5-buttons { display: flex; flex-wrap: wrap; gap: 8px; margin: 18px 0 0; }
.v5-readonly-box { background: var(--surface-2); border: 1px dashed var(--line); border-radius: var(--radius-sm); padding: 12px; color: var(--muted); font-size: 13px; line-height: 1.7; }
.v5-available-picker { display: flex; gap: 6px; flex-wrap: wrap; max-height: 210px; overflow: auto; background: var(--surface-sunken); border-radius: var(--radius-sm); border: 1px solid var(--line); padding: 11px; }
.v5-pick { border: 1px solid var(--line); background: var(--surface); color: var(--ink-2); padding: 6px 9px; border-radius: var(--radius-xs); font-size: 12px; }
.v5-pick.selected { border-color: var(--brand); background: var(--brand-soft); color: var(--brand-ink); }
.v5-pick:disabled { opacity: 1; color: #64707E; background: var(--surface-2); border-color: var(--line); cursor: not-allowed; }
.v5-pick.original-room { border-style: dashed; }
.picker-toolbar { display: flex; gap: 8px; align-items: center; margin-bottom: 8px; flex-wrap: wrap; }
.picker-toolbar small { font-size: 12px; color: var(--muted); }
.contract-form-heading { display: flex; justify-content: space-between; gap: 16px; padding: 15px 20px; border-bottom: 1px solid var(--line); }
.contract-form-heading strong { font-size: 18px; }
.contract-form-heading p { margin: 4px 0 0; font-size: 13px; color: var(--muted); }
.form-section { border: 0; margin: 0 0 22px; padding: 0; min-width: 0; }
.form-section legend { width: 100%; font-size: 15px; font-weight: 700; margin-bottom: 13px; padding: 0 0 9px; border-bottom: 1px solid var(--line-2); color: var(--ink); }
.form-section legend span { display: inline-grid; place-items: center; border-radius: 50%; width: 22px; height: 22px; background: var(--brand-soft); color: var(--brand-ink); margin-right: 8px; font-size: 12px; }
.form-savebar { position: sticky; bottom: 0; z-index: 11; background: var(--surface); border-top: 1px solid var(--line); padding: 13px 0 6px; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.form-savebar .save-context { flex: 1; font-size: 12px; color: var(--muted); min-width: 170px; }
.draft-review { background: var(--surface-2); border: 1px solid var(--line); padding: 12px 14px; border-radius: var(--radius-sm); font-size: 13px; margin: 10px 0 14px; }
.draft-review summary { color: var(--ink); cursor: pointer; }
.draft-review dl { display: grid; grid-template-columns: 92px 1fr; gap: 6px 12px; margin: 11px 0 0; }
.draft-review dt { color: var(--muted); }
.draft-review dd { margin: 0; overflow-wrap: anywhere; }
.contract-summary { display: flex; gap: 7px; flex-wrap: wrap; margin-bottom: 12px; }
.contract-summary button { border: 1px solid var(--line); background: var(--surface); color: var(--ink-2); border-radius: var(--radius-xs); padding: 6px 11px; font-size: 12px; }
.contract-summary button.active { background: var(--brand-soft); color: var(--brand-ink); border-color: var(--brand-line); }
/* 【2026-11-01】到期日在 30 天内的合同，行内直接标「N 天后到期」：
   从房态提醒条点进来，一眼就能看到哪几行是这一档，不用自己拿日期减今天。 */
.contract-due-tag { display: inline-block; margin-left: 6px; padding: 1px 6px; font-size: 11px; line-height: 1.6;
  border-radius: 999px; background: var(--warn-soft); border: 1px solid var(--warn-line); color: #7A5510; white-space: nowrap; }
.contract-due-tag.hot { background: var(--danger-soft); border-color: var(--danger-line); color: #8F2E22; }
.contract-fold { margin-top: 16px; border-top: 1px solid var(--line); padding-top: 12px; }
.contract-fold summary { font-size: 13px; color: var(--muted); cursor: pointer; margin-bottom: 12px; }
.v5-detail { padding: 20px; }
.v5-detail h3 { margin: 0 0 5px; font-size: 21px; }
.v5-detail-sub { color: var(--muted); font-size: 13px; margin-bottom: 18px; }
.v5-report-placeholder { min-height: 330px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; text-align: center; border: 1px dashed var(--line); background: var(--surface); border-radius: var(--radius); color: var(--muted); padding: 24px; }
.v5-report-placeholder h2 { color: var(--ink); font-size: 18px; margin: 0; }
.v5-report-placeholder p { font-size: 13px; max-width: 460px; }
.v5-report-icon { font-size: 32px; color: var(--brand-ink); background: var(--brand-soft); padding: 13px 20px; border-radius: var(--radius); }

/* 经营报表 */
.report-tabs { display: flex; gap: 7px; flex-wrap: wrap; margin: 0 0 14px; }
.report-toolbar { padding: 13px; display: flex; gap: 12px; align-items: flex-end; flex-wrap: wrap; margin-bottom: 14px; background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); }
.report-toolbar label { display: flex; flex-direction: column; gap: 5px; font-size: 12px; color: var(--muted); }
.report-toolbar input, .report-toolbar select { min-height: 34px; border: 1px solid var(--line); background: var(--surface); border-radius: var(--radius-sm); padding: 6px 9px; color: var(--ink); font-size: 13px; }
.report-toolbar small { color: var(--muted); margin-left: auto; }
.report-metrics { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; margin-bottom: 16px; }
.report-metric { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 15px 16px; display: flex; flex-direction: column; gap: 7px; box-shadow: var(--shadow-1); }
.report-metric small { font-size: 12px; color: var(--muted); }
.report-metric strong { font-size: 25px; color: var(--brand-ink); font-weight: 700; font-variant-numeric: tabular-nums; }
.report-metric span { font-size: 12px; line-height: 1.6; color: var(--muted); }
.report-definition { padding: 0 18px; color: var(--muted); font-size: 13px; line-height: 1.8; }
.report-roadmap { padding: 14px 18px; color: var(--muted); font-size: 13px; line-height: 1.9; margin-top: 14px; }
.report-roadmap summary { cursor: pointer; color: var(--ink); }
.type-help { grid-column: 1 / -1; background: var(--surface-2); border-radius: var(--radius-sm); padding: 10px; line-height: 1.8; font-size: 13px; }
.contract-number-note { font-size: 12px; color: var(--muted); }
.contract-ref { white-space: nowrap; font-size: 12px; color: var(--muted); }

/* 工作台待办条 */
.todo-bar { border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface); overflow: hidden; box-shadow: var(--shadow-1); }
.todo-head { display: flex; align-items: baseline; gap: 9px; padding: 9px 12px; border-bottom: 1px solid var(--line-2); background: var(--surface-2); flex-wrap: wrap; }
.todo-head strong { font-size: 13px; color: var(--ink); }
.todo-head strong em { font-style: normal; color: var(--danger); font-size: 15px; margin-left: 4px; font-weight: 700; }
.todo-head span { font-size: 11px; color: var(--muted); }
.todo-chips { display: flex; flex-wrap: wrap; gap: 6px; padding: 9px 12px; }
.todo-chip { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; padding: 4px 10px; border-radius: 999px; border: 1px solid var(--line); background: var(--surface); color: var(--ink-2); }
.todo-chip b { font-size: 11px; font-weight: 700; padding: 0 5px; border-radius: 999px; background: var(--surface-3); color: var(--ink-2); }
.todo-chip.red { border-color: var(--danger-line); color: var(--danger); background: var(--danger-soft); }
.todo-chip.red b { background: var(--danger); color: #fff; }
.todo-chip.amber { border-color: var(--warn-line); color: var(--warn); background: var(--warn-soft); }
.todo-chip.amber b { background: var(--warn); color: #fff; }
.todo-chip.active { box-shadow: inset 0 0 0 1px currentColor; }
.todo-chip.zero { opacity: 1; color: #64707E; background: var(--surface-2); border-color: var(--line); }
.todo-chip.zero b { background: var(--surface-3); color: #64707E; }
.todo-chip.red.zero { color: #A83A30; background: var(--danger-soft); border-color: var(--danger-line); }
.todo-chip.red.zero b { background: var(--danger); color: #fff; }
.todo-chip.amber.zero { color: #7A5A12; background: var(--warn-soft); border-color: var(--warn-line); }
.todo-chip.amber.zero b { background: var(--warn); color: #fff; }
.todo-list { border-top: 1px solid var(--line-2); }
.todo-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 2px 10px; width: 100%; text-align: left; padding: 8px 12px; border-bottom: 1px solid var(--line-3); font-size: 12px; }
.todo-row:hover { background: var(--surface-2); }
.todo-who { font-weight: 600; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.todo-room { grid-column: 1; font-size: 11px; color: var(--muted); }
.todo-due { grid-row: 1 / span 2; grid-column: 2; align-self: center; font-size: 11px; color: var(--muted); white-space: nowrap; }
.todo-act { grid-column: 1; font-size: 11px; color: var(--brand-ink); }
.todo-more { padding: 8px 12px; font-size: 11px; color: var(--muted-2); }
/* 【D-148】待办全为零时的空态一句话，避免看板只剩一排灰色 0 */
.todo-empty { padding: 10px 14px 12px; font-size: 12px; color: var(--muted); }

/* 经营概览（产品层） */
/* 【D-148】KPI 从 4 格减为 3 格（待办格删了，页顶待办条已有时），改 auto-fit 以免右边的空格子。 */
.product-kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 12px; margin-bottom: 16px; }
.product-kpi { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 15px 16px; display: flex; flex-direction: column; gap: 4px; box-shadow: var(--shadow-1); }
.product-kpi small { font-size: 12px; color: var(--muted); }
.product-kpi strong { font-size: 25px; font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; }
.product-kpi.primary strong { color: var(--brand-ink); }
.product-kpi span { font-size: 12px; color: var(--muted); line-height: 1.55; }
.product-columns { display: grid; grid-template-columns: minmax(0, 1.25fr) minmax(280px, .75fr); gap: 16px; }
.product-panel { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow-1); overflow: hidden; }
.product-panel-header { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; padding: 13px 16px; border-bottom: 1px solid var(--line-2); }
.product-panel-header h2 { font-size: 15px; }
.product-panel-header small { font-size: 12px; color: var(--muted); }
.product-panel-content { padding: 10px 12px 14px; }
.product-task { display: flex; align-items: center; gap: 10px; width: 100%; text-align: left; padding: 9px 10px; border-radius: var(--radius-sm); border: 1px solid var(--line-2); margin-bottom: 7px; }
.product-task:hover { border-color: var(--brand-line); background: var(--surface-2); }
.task-number { width: 20px; height: 20px; border-radius: 50%; background: var(--surface-3); color: var(--muted); display: grid; place-items: center; font-size: 12px; font-weight: 700; flex: 0 0 20px; }
.product-task .task-copy { flex: 1; min-width: 0; }
.task-copy b { display: block; font-size: 13px; color: var(--ink); }
.task-copy small { display: block; font-size: 11px; color: var(--muted); margin-top: 2px; }
.product-task em { font-style: normal; font-size: 12px; color: var(--brand-ink); white-space: nowrap; }
.product-actions { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
.product-action { text-align: left; border: 1px solid var(--line); border-radius: var(--radius-sm); padding: 10px 12px; font-size: 13px; font-weight: 600; color: var(--ink); }
.product-action:hover { border-color: var(--brand-line); background: var(--brand-soft); }
.product-action small { display: block; font-weight: 400; font-size: 11px; color: var(--muted); margin-top: 3px; }
.product-source-note { font-size: 12px; color: var(--muted); line-height: 1.7; margin: 10px 0 0; }
.product-context { margin-top: 14px; font-size: 12px; color: var(--muted); background: var(--surface-2); border: 1px solid var(--line-2); border-radius: var(--radius-sm); padding: 9px 12px; }
.product-empty { padding: 44px 22px; text-align: center; border: 1px dashed var(--line); border-radius: var(--radius); background: var(--surface); }
.product-empty strong { display: block; font-size: 15px; margin-bottom: 6px; }
.product-empty p { font-size: 13px; color: var(--muted); margin: 0 0 14px; }
.product-environment { padding: 9px 14px; border-radius: var(--radius-sm); background: var(--warn-soft); border: 1px solid var(--warn-line); color: var(--warn); font-size: 13px; margin-bottom: 14px; }

/* 合同详情分类页签与合同表单目录 */
.product-detail-tabs { display: flex; gap: 3px; border-bottom: 1px solid var(--line); margin: 16px 0 0; }
.product-detail-tabs button { border: 0; background: transparent; padding: 9px 14px; font-size: 13px; color: var(--muted); border-bottom: 2px solid transparent; margin-bottom: -1px; }
.product-detail-tabs button[aria-selected="true"] { color: var(--brand-ink); border-bottom-color: var(--brand); font-weight: 600; }
.product-form-index { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 16px; }
.product-form-index button { border: 1px solid var(--line); background: var(--surface); color: var(--ink-2); border-radius: var(--radius-xs); padding: 5px 10px; font-size: 12px; }
.product-form-index button:hover { border-color: var(--brand-line); color: var(--brand-ink); background: var(--brand-soft); }
#drawerBody .product-detail-tabs { margin-top: 0; }
.pms-badges { position: absolute; top: 6px; right: 7px; display: flex; gap: 3px; pointer-events: none; }
.pms-badge { font-size: 11px; line-height: 1.5; padding: 0 5px; border-radius: 999px; background: var(--surface-3); color: var(--muted); }
.pms-badge.people { background: var(--ok-soft); color: var(--ok); }
.pms-badge.none { background: var(--warn-soft); color: var(--warn); }
/* 2026-10-28 UI-02：角标原来绝对定位在 top:6px/right:7px，与卡片右上角的 .state-pill 完全重叠，
   「承租中／可出租」被「登记 N 条／未登记」压住（用户截图）。改为卡片内独立一行，贴底部左侧；
   网络图标在底部右侧，两者不冲突。 */
.room-card .pms-badges { position: static; margin-top: auto; display: flex; gap: 3px; pointer-events: none; }
/* 2026-10-28 UI-02：可租房原为浅绿底（#F1FAF6）+绿边，与在租的 teal-soft 浅绿底几乎同色。
   改为白底：一屏里「白块＝空房、绿块＝有人住」，靠底色有无区分，不动色相体系。 */
.room.room-card.vacant { background: var(--surface); }
.room-card.pms-hide, .room.pms-hide { display: none !important; }
.pms-filter-chip { display: flex; align-items: center; gap: 10px; background: var(--brand-soft); border: 1px solid var(--brand-line); color: var(--brand-ink); font-size: 12px; padding: 7px 12px; border-radius: var(--radius-sm); margin: 0 0 10px; }
.pms-filter-chip button { border: 0; background: var(--surface); border-radius: var(--radius-xs); padding: 3px 9px; font-size: 11px; color: var(--brand-ink); }
.save-progress { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 10px; }
#saveProgress[hidden] { display: none; }
.v5-available-picker button[hidden] { display: none; }
.mode-badge { font-size: 12px; color: var(--muted); white-space: nowrap; }

/* ---------- 8. 旧版 pms-* 组件（由 pms-core.js 迁出，行为不变） ---------- */
#pmsToastBox { position: fixed; top: 16px; right: 16px; z-index: 9998; display: flex; flex-direction: column; gap: 8px; max-width: 340px; }
.pms-toast { background: #152437; color: #fff; font-size: 13px; line-height: 1.55; padding: 10px 14px; border-radius: var(--radius); box-shadow: 0 8px 26px rgba(21, 36, 55, .22); animation: pmsIn .22s ease; }
.pms-toast.error { background: #8F2F22; }
.pms-toast.out { opacity: 0; transform: translateY(-4px); transition: all .24s; }
@keyframes pmsIn { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }
#pmsBusy { position: fixed; bottom: 18px; right: 18px; max-width: min(520px,95vw); display: none; align-items: center; justify-content: center; z-index: 9997; }
#pmsSnapshotNotice { position: fixed; bottom: 6px; left: 12px; max-width: 65vw; padding: 6px 12px; background: #fff5d6; color: #72520c; border: 1px solid #ebd69d; border-radius: var(--radius-xs); z-index: 9995; font-size: 12px; }
#pmsBusy.show { display: flex; }
.pms-busy-card { background: #fff; border-radius: var(--radius-lg); padding: 22px 28px; display: flex; align-items: center; gap: 14px; box-shadow: 0 18px 50px rgba(21, 36, 55, .28); }
.pms-busy-text { font-size: 13px; color: #203147; }
.pms-spin { width: 18px; height: 18px; border: 2px solid #DBE6EF; border-top-color: var(--brand); border-radius: 50%; animation: pmsSpin .8s linear infinite; }
.pms-spin.big { width: 30px; height: 30px; border-width: 3px; }
@keyframes pmsSpin { to { transform: rotate(360deg); } }
.pms-mask { position: fixed; inset: 0; background: rgba(18, 32, 48, .42); z-index: 9996; display: flex; align-items: center; justify-content: center; padding: 18px; }
.pms-modal { background: #fff; border-radius: var(--radius-lg); width: min(460px, 100%); max-height: 92vh; display: flex; flex-direction: column; box-shadow: 0 24px 70px rgba(18, 32, 48, .3); }
.pms-modal.lg { width: min(880px, 100%); }
.pms-modal-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; padding: 18px 22px 12px; border-bottom: 1px solid #EEF3F8; }
.pms-modal-title { font-size: 16px; font-weight: 700; color: #17263A; }
.pms-modal-sub { font-size: 12px; color: #7B8BA0; margin-top: 3px; }
.pms-x { border: 0; background: #F2F6FA; width: 30px; height: 30px; border-radius: 50%; font-size: 18px; line-height: 1; color: #6F8199; cursor: pointer; }
.pms-modal-body { padding: 18px 22px; overflow-y: auto; flex: 1; }
.pms-modal-foot { display: flex; gap: 10px; justify-content: flex-end; padding: 12px 22px 18px; border-top: 1px solid #EEF3F8; flex-wrap: wrap; }

/* ---------- 6.1 合同「办理业务」弹窗（2026-11-02 重做视觉）
   原来是 9 个同款灰边 .btn 竖着堆，主次不分、退宿（终止合同）和登记收款长得一样；
   title 写「办理业务 · 虹途」、subtitle 又重复一遍企业名，正文第一行才出现房号与状态。
   现在拆成：概览条（企业 / 房号 / 租期 / 状态）→ 分组小标题 → 动作卡片（标题 + 一句说明 + 箭头）。
   动作卡片仍是 <button data-cbiz>，业务逻辑与选择器都不变，只换皮。 */
.cbiz-head { padding: 10px 12px; background: var(--surface-2); border: 1px solid var(--line-2); border-radius: var(--radius); margin-bottom: 14px; }
.cbiz-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.cbiz-co { font-size: 13px; font-weight: 650; color: var(--ink); line-height: 1.35; min-width: 0; }
.cbiz-meta { margin-top: 5px; font-size: 11.5px; color: var(--muted-2); line-height: 1.5; }
.cbiz-chip { flex: 0 0 auto; display: inline-flex; align-items: center; padding: 1px 7px; border-radius: 999px; background: var(--grey-soft); color: var(--grey); border: 1px solid var(--grey-line); font-size: 11px; font-weight: 600; }
.cbiz-chip.warn { background: var(--warn-soft); color: var(--warn); border-color: var(--warn-line); }
.cbiz-chip.ok { background: var(--ok-soft); color: var(--ok); border-color: var(--ok-line); }
.cbiz-chip.danger { background: var(--danger-soft); color: var(--danger); border-color: var(--danger-line); }
.cbiz-group { margin-top: 14px; }
.cbiz-group:first-of-type { margin-top: 0; }
.cbiz-label { font-size: 11px; font-weight: 600; color: var(--muted-2); letter-spacing: .05em; margin: 0 0 7px 2px; }
.cbiz-list { display: grid; gap: 7px; }
.cbiz-item { display: flex; align-items: center; gap: 10px; width: 100%; padding: 10px 12px; text-align: left; background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); cursor: pointer; font: inherit; color: inherit; transition: border-color .15s ease, background .15s ease; }
.cbiz-item:hover:not(:disabled) { border-color: var(--brand-line); background: var(--brand-soft); }
.cbiz-item:hover:not(:disabled) .cbiz-arrow { color: var(--brand); transform: translateX(2px); }
.cbiz-item:focus-visible { outline: 2px solid var(--brand-line); outline-offset: 1px; }
.cbiz-item:disabled { cursor: not-allowed; background: var(--surface-2); opacity: 1; color: #64707E; }
.cbiz-item:disabled .cbiz-t { color: #64707E; }
.cbiz-item:disabled .cbiz-d { color: #64707E; }
.cbiz-item:disabled .cbiz-arrow { color: #64707E; }
.cbiz-txt { flex: 1 1 auto; min-width: 0; }
.cbiz-t { display: block; font-size: 13px; font-weight: 600; color: var(--ink); line-height: 1.35; }
.cbiz-d { display: block; font-size: 11.5px; color: var(--muted-2); line-height: 1.5; margin-top: 2px; }
.cbiz-arrow { flex: 0 0 auto; color: var(--muted-3); font-size: 14px; transition: color .15s ease, transform .15s ease; }
.cbiz-item.danger .cbiz-t { color: var(--danger); }
.cbiz-item.danger:hover:not(:disabled) { border-color: var(--danger-line); background: var(--danger-soft); }
.cbiz-item.danger:hover:not(:disabled) .cbiz-arrow { color: var(--danger); }
.cbiz-tip { margin-top: 14px; padding: 9px 11px; background: var(--info-soft); border: 1px solid var(--info-line); border-radius: var(--radius-sm); font-size: 11.5px; color: var(--info); line-height: 1.55; }
.pms-btn { border: 1px solid #DBE4ED; background: #fff; color: #28405B; font-size: 13px; padding: 8px 16px; border-radius: var(--radius); cursor: pointer; }
.pms-btn.primary { background: var(--brand); border-color: var(--brand); color: #fff; font-weight: 600; }
.pms-btn.ghost { background: #F6F9FC; }
.pms-btn:disabled { opacity: 1; color: #64707E; background: #F6F9FC; border-color: var(--line); cursor: not-allowed; }
/* 【2026-11-01 G-45】弹窗里的 .pms-table 原来一条样式都没有，靠浏览器默认渲染：
   没有表头底色、没有行线、没有内边距，一行六条记录看着像一段流水文字。
   退宿拦截清单要一眼数得清「哪间房、什么东西、几件、在谁手上」，所以补一套最朴素的表格样式。
   只作用于弹窗内（.pms-modal），不动页面上的报表表格。 */
.pms-modal .pms-table-wrap { max-height: 46vh; overflow: auto; border: 1px solid var(--line); border-radius: var(--radius); }
.pms-modal .pms-table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.pms-modal .pms-table th { position: sticky; top: 0; z-index: 1; background: var(--surface-3); color: var(--muted); font-weight: 600; text-align: left; padding: 7px 10px; white-space: nowrap; }
.pms-modal .pms-table td { padding: 7px 10px; border-top: 1px solid var(--line); color: var(--ink-2); }
.pms-modal .pms-table .num, .pms-modal .pms-table th.num { text-align: right; font-variant-numeric: tabular-nums; }
.pms-btn.tiny { padding: 4px 10px; font-size: 12px; border-radius: var(--radius-xs); }
.pms-row-acts { white-space: nowrap; }
.pms-row-acts .pms-btn { margin-right: 6px; }
.pms-field { margin-bottom: 14px; }
.pms-field label { display: block; font-size: 12px; color: #5C7085; margin-bottom: 5px; }
.pms-req { color: #C0392B; }
.pms-input { width: 100%; padding: 9px 11px; border: 1px solid #D7E0E8; border-radius: var(--radius-sm); font-size: 13px; background: #fff; color: #1D2F45; box-sizing: border-box; }
.pms-input:focus { outline: 2px solid var(--brand-soft); border-color: var(--brand); }
.pms-field.has-error .pms-input { border-color: #C0392B; background: #FFFAFA; }
.pms-err { color: #C0392B; font-size: 12px; margin-top: 4px; min-height: 0; }
.pms-hint { color: #93A3B4; font-size: 11px; margin-top: 4px; }
.pms-form-note { color: #C0392B; font-size: 12px; text-align: right; }
.pms-callout { background: #FFF8EA; border: 1px solid #F2DFB8; color: #8A5A12; font-size: 12px; line-height: 1.7; padding: 10px 12px; border-radius: var(--radius); margin-bottom: 14px; }
.pms-intro { color: #7B8BA0; font-size: 12px; line-height: 1.7; margin-bottom: 12px; }
.pms-preview { font-size: 13px; }
.pms-preview-head { font-weight: 600; color: #17263A; margin-bottom: 12px; font-size: 13px; }
.pms-steps { list-style: none; margin: 0; padding: 0; }
.pms-steps > li { border: 1px solid #EAEFF5; border-radius: var(--radius); padding: 10px 12px; margin-bottom: 9px; background: #FCFDFF; }
.pms-step-head { display: flex; align-items: center; gap: 8px; font-size: 13px; color: #203147; }
.pms-step-no { width: 20px; height: 20px; border-radius: 50%; background: #E9F6F2; color: #158779; font-size: 11px; font-weight: 700; display: flex; align-items: center; justify-content: center; flex: 0 0 20px; }
.pms-changes { width: 100%; border-collapse: collapse; margin-top: 7px; font-size: 12px; }
.pms-changes td { padding: 4px 6px; border-top: 1px dashed #EEF3F8; color: #3F5A6E; vertical-align: top; }
.pms-changes td.k { color: #7B8BA0; width: 32%; }
.pms-changes td.from { color: #93A3B4; }
.pms-changes td.arrow { color: #C3CEDA; width: 18px; text-align: center; }
.pms-changes td.to { color: #158779; font-weight: 600; }
.pms-preview-note { color: #93A3B4; font-size: 11px; line-height: 1.7; margin-top: 10px; }
.pms-running { text-align: center; padding: 26px 10px; }
.pms-running-title { font-size: 14px; font-weight: 600; color: #17263A; margin-top: 14px; }
.pms-running-sub { font-size: 12px; color: #7B8BA0; margin-top: 6px; line-height: 1.7; }
.pms-receipt { text-align: center; padding: 14px 6px; }
.pms-receipt-icon { width: 46px; height: 46px; border-radius: 50%; margin: 0 auto 12px; display: flex; align-items: center; justify-content: center; font-size: 22px; font-weight: 700; }
.pms-receipt.ok .pms-receipt-icon { background: #E9F6F2; color: #158779; }
.pms-receipt.fail .pms-receipt-icon { background: #FDECEB; color: #C0392B; }
.pms-receipt-title { font-size: 15px; font-weight: 700; color: #17263A; }
.pms-receipt-single { font-size: 12px; color: #158779; margin-top: 6px; }
.pms-receipt-detail { font-size: 12px; color: #5C7085; margin-top: 8px; line-height: 1.7; }
.pms-receipt-written { font-size: 12px; color: #8A5A12; background: #FFF8EA; border-radius: var(--radius-sm); padding: 8px 10px; margin-top: 10px; text-align: left; }
.pms-receipt-lines { list-style: none; margin: 12px 0 0; text-align: left; font-size: 12px; color: #3F5A6E; }
.pms-receipt-lines li { padding: 4px 0; border-top: 1px dashed #EEF3F8; }
.pms-receipt-note { color: #93A3B4; font-size: 11px; margin-top: 10px; }
.pms-menu { display: flex; flex-direction: column; gap: 8px; }
.pms-menu button { text-align: left; padding: 11px 13px; border: 1px solid #EAEFF5; background: #fff; border-radius: var(--radius); font-size: 13px; color: #203147; cursor: pointer; display: flex; justify-content: space-between; }
.pms-menu button span { color: #A8B6C4; font-size: 11px; }
.pms-menu button:disabled { opacity: 1; color: #64707E; background: var(--surface-2); cursor: not-allowed; }
.pms-menu button:disabled span { color: #64707E; }

/* ---------- 9. 响应式降级 ---------- */
@media (max-width: 1500px) {
  /* 图例项已锁为不换行，宽度不足时整组折行，避免被裁掉半个图例 */
  .w-header .legend { flex-wrap: wrap; row-gap: 3px; justify-content: flex-end; }
}
@media (max-width: 1440px) {
  .content { padding: 18px 20px 28px; }
  .hero h1 { font-size: 23px; }
  /* 房态图列宽统一在第 14.3 节（minmax(124px,1fr)）定义，这里不再重复设，
     否则会被后面的规则盖掉、成为看着生效其实无效的死代码。 */
}
/* 窄屏：全部楼栋的 10 个楼层按钮放不下单行时，允许筛选区换行而不是被裁掉 */
@media (max-width: 1280px) {
  .room-toolbar-filters { flex-wrap: wrap; overflow: visible; row-gap: 6px; }
  .room-toolbar { flex-wrap: wrap; }
}
@media (max-width: 1240px) {
  .product-room-shell { grid-template-columns: 200px minmax(0, 1fr); }
  .room-body { grid-template-columns: minmax(0, 1fr); }
  .room-todo-panel { position: static; max-height: none; }
  .floor-scroll { max-height: none; }
  .report-metrics { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .product-kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 1100px) {
  :root { --nav-w: 64px; }
  .aside { padding: 16px 8px 12px; }
  .brand { justify-content: center; padding: 0 0 14px; }
  .brand-name, .brand-sub, .product-nav-heading, .nav-text, .aside-footer, .aside-footnote { display: none; }
  .nav { justify-content: center; padding: 10px; }
  .nav.active::before { left: -8px; }
  .product-room-shell { grid-template-columns: minmax(0, 1fr); }
  .product-room-rail { position: static; max-height: none; }
  .product-building-filter .toggle { flex-direction: row; }
  .page-grid, .product-columns { grid-template-columns: minmax(0, 1fr); }
  .v5-info-grid { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 860px) {
  .content { padding: 14px 12px 24px; }
  .topbar { padding: 0 14px; }
  .stats { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .stat:nth-child(n+4) { display: none; }
  .hero { flex-wrap: wrap; }
  .hero > div:first-child { flex: 1; min-width: 220px; }
  .hero h1 { font-size: 21px; }
  .search { width: 150px; }
  .v5-form-grid { grid-template-columns: minmax(0, 1fr); }
  .drawer { width: min(100vw, 440px); }
}

/* 房卡密度：仅为显示偏好，不改变业务口径。
   高度/列宽/间距见第 14.4 节（D-166 统一按 84px 基线收档），这里只留字号。 */
body[data-density="compact"] .room.room-card .rid { font-size: 16px; }
body[data-density="compact"] .room.room-card .rn { font-size: 12px; }
body[data-density="compact"] .floor-scroll { padding: 12px 12px 16px; }

@media print {
  .aside, .topbar, .product-plan-controls, .toolbar, .legend { display: none !important; }
  .content { padding: 0; }
  .workspace, .section-card, .v5-panel { box-shadow: none; border-color: #ccc; }
}

/* ---------- 10. W-01 企业选择器 / 企业档案承租房 / 房间租约与入住 ---------- */
.company-picker { position: relative; }
.company-search-row { display: flex; gap: 8px; align-items: center; }
.company-search-row .input { flex: 1 1 auto; min-width: 0; }
.company-search-row .btn { flex: 0 0 auto; white-space: nowrap; }

.company-results {
  position: absolute; z-index: 40; left: 0; right: 0; top: calc(100% - 2px);
  max-height: 268px; overflow: auto; padding: 4px;
  background: var(--surface); border: 1px solid var(--brand-line);
  border-radius: var(--radius); box-shadow: var(--shadow-2);
}
.company-option {
  display: flex; flex-direction: column; gap: 2px; width: 100%;
  padding: 7px 9px; text-align: left; cursor: pointer;
  background: transparent; border: 0; border-radius: var(--radius-sm);
  font: inherit; color: var(--ink);
}
.company-option:hover, .company-option.active { background: var(--brand-soft); }
.company-option strong { font-weight: 600; font-size: 13px; }
.company-meta { color: var(--muted); font-size: 12px; font-weight: 400; margin-left: 6px; }
.company-dup {
  align-self: flex-start; margin-top: 3px; padding: 1px 6px;
  color: var(--warn); background: var(--warn-soft);
  border: 1px solid var(--warn-line); border-radius: 999px; font-size: 11px;
}
.company-empty { padding: 10px; color: var(--muted); font-size: 12px; display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }

.company-card {
  margin-top: 8px; padding: 10px 12px;
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--radius);
}
.company-card-main { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px; font-size: 13px; }
.company-card-contact { display: flex; flex-wrap: wrap; gap: 16px; margin-top: 6px; color: var(--ink-2); font-size: 12px; }
.company-card .mini-note { margin-top: 6px; }

.company-create { display: flex; flex-direction: column; gap: 8px; }
.company-create > strong { font-size: 13px; }

.company-rooms summary { cursor: pointer; color: var(--brand); font-size: 12px; }
.company-rooms .enterprise-rooms { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 6px; max-width: 220px; }
.company-rooms .enterprise-rooms .btn { font-size: 11px; padding: 2px 6px; }

.room-tenure { display: flex; flex-direction: column; gap: 9px; }
/* ==========================================================================
   抽屉排版阶梯（2026-11-02 五轮）
   --------------------------------------------------------------------------
   改前体检（南204 / 北416 / 北101 三间实测同一结果）：
     字号 6 种 24 / 15 / 12.5 / 12 / 11.5 / 11
     字重 5 种 400 / 500 / 600 / 650 / 700
     文字色 7 种
   12px 与 12.5px 只差 0.5px，肉眼看不出差别 —— 这就是「看不到重点内容」的原因：
   不是字太小，是**每行都一样重**。
   现在只留 4 档正文字号 + 1 档标签，相邻至少差 1.5px；
   灰度只用 3 档（ink / ink-2 / muted-2），语义色只剩 3 个（红欠费、绿结清、橙临期）。
   变量挂在 #drawer 上：只作用于抽屉，房态墙、合同页、账务页一概不跟着变。
   ========================================================================== */
#drawer {
  --rd-room: 22px;   /* 房号：全抽屉唯一最大 */
  --rd-key: 17px;    /* 这一间谁在租 */
  --rd-val: 13.5px;  /* 关键值：到期日、金额数字 */
  --rd-body: 12.5px; /* 正文：房型、事实、说明 */
  --rd-lbl: 11px;    /* 标签：承租方 / 到期 / 应收 */
}
/* 【D-168】「承租方」小标签 + 企业名大字：打开抽屉第一眼落在「谁在租」。
   两段都保持 inline，不产生额外换行，phase5-w02「承租摘要 ≤160 字」的护栏不受影响。 */
.rd-say { line-height: 1.4; overflow-wrap: anywhere; }
.rd-say-k { font-size: var(--rd-lbl); font-weight: 500; color: var(--muted-2); letter-spacing: .3px; margin-right: 8px; }
.rd-say-v { font-size: var(--rd-key); font-weight: 650; color: var(--ink); }
/* 事实区改成两列键值栅格：原来是一句「实际入住人数 0 人 · 合同至 2027-05-02 · 合同含 40 间 ·
   历史合同 2 份」横着流，在 380px 抽屉里折成两行、四件事一样重，看不出哪件要紧。
   竖排键值后，标签对齐、值对齐，扫一眼就知道「什么时候到期」。 */
.rd-kv { display: grid; grid-template-columns: auto 1fr; align-items: baseline; gap: 5px 10px; }
.rd-kv .rd-k { font-size: var(--rd-lbl); font-weight: 500; color: var(--muted-2); white-space: nowrap; }
.rd-kv .rd-v { font-size: var(--rd-body); color: var(--ink-2); }
/* 到期日是这一屏真正的决策依据（什么时候退、要不要续），单独立一档：
   比正文大一档、加粗、换墨色；≤45 天转琥珀并写出「剩 N 天」，过期写「已过期 N 天」。 */
.rd-kv .rd-v.is-date { font-size: var(--rd-val); font-weight: 600; color: var(--ink); font-variant-numeric: tabular-nums; }
.rd-kv .rd-v.is-soon { color: var(--warn); }
.rd-kv .rd-v.is-over { color: var(--danger); }
.rd-kv .rd-v em { font-style: normal; font-weight: 500; font-size: var(--rd-lbl); margin-left: 6px; opacity: 1; }
.rd-note { font-size: var(--rd-body); color: var(--ink-2); line-height: 1.7; }
.tenure-row { display: flex; justify-content: space-between; gap: 12px; font-size: 13px; }
.tenure-row > span { color: var(--muted); }
.tenure-row > strong { color: var(--ink); font-weight: 600; text-align: right; }

.save-context.ok { color: var(--ok); }
.save-context.blocked { color: var(--warn); }
.save-context.blocked[data-block-level="capability"] { color: var(--danger); }
#v6CompanyBinding.warn { color: var(--warn); }

@media (max-width: 1100px) {
  .company-search-row { flex-wrap: wrap; }
  .company-search-row .btn { width: 100%; }
}

/* ---------- 11. W-02 企业/合同/房间互跳、人员历史 ---------- */
/* 互跳按钮原来 11px 灰字浅边框，在抽屉里几乎看不见（截图里像两个灰块）。
   改成 12px、墨色、白底细边，hover 转品牌色；「企业档案」在无承租企业时直接不出现，
   不留一个灰按钮占位让人猜是不是坏了。 */
.tenure-nav { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 3px; padding-top: 9px; border-top: 1px solid var(--line-2); }
.tenure-nav .btn { font-size: 12px; padding: 4px 10px; color: var(--ink-2); border-color: var(--line); background: var(--surface); }
.tenure-nav .btn:hover { color: var(--brand); border-color: var(--brand-line); background: var(--brand-soft); }
.tenure-nav .btn[disabled] { display: none; }
/* 【2026-11-02 四轮 → 五轮】承租区金额。原来是「整份合同 40 间 应收 ¥0.00 实收 ¥0.00 已结清」
   挤成一行 12px 灰，四个数字一个重量。改成三列栅格：标签 11px 灰在上，数字 13.5px/600 墨色在下，
   等宽数字纵向对齐；欠费红、结清绿，「已结清」不再和数字抢同一行的位置。 */
.rd-money { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 3px 8px; margin-top: 1px; padding-top: 10px; border-top: 1px solid var(--line-2); font-variant-numeric: tabular-nums; }
.rd-money-k { grid-column: 1 / -1; font-size: var(--rd-lbl); font-weight: 500; color: var(--muted-2); letter-spacing: .2px; }
.rd-money .rd-m { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.rd-money .rd-m i { font-style: normal; font-size: var(--rd-lbl); color: var(--muted-2); }
.rd-money .rd-m b { font-size: var(--rd-val); font-weight: 600; color: var(--ink); white-space: nowrap; }
.rd-money .rd-owe { color: var(--danger) !important; }
.rd-money .rd-ok { color: var(--ok) !important; }
.pg-room-head .pg-mini { flex: 0 0 auto; }
/* 抽屉内统一：同一套阶梯套到「系统核定」折叠条、键值行、小标题上。
   改前三处各写各的字号（12 / 12.5 / 13），一屏里三个"正文"；现在都走 --rd-body / --rd-lbl。 */
#drawer .pms-auth-box > summary { font-size: var(--rd-lbl); padding: 8px 12px; }
#drawer .pms-auth-box > summary .pms-auth-sum { font-size: var(--rd-lbl); }
#drawer .drow { font-size: var(--rd-body); padding: 7px 0; }
#drawer .drow .key { font-size: var(--rd-lbl); font-weight: 500; color: var(--muted-2); }
#drawer .drow .val { font-weight: 600; color: var(--ink); }
#drawer .section-label { font-size: var(--rd-lbl); letter-spacing: .4px; }
#drawer .subtabs button { color: var(--muted-2); }
#drawer .subtabs button.active { color: var(--brand-ink); }
#drawer .form-disclaimer { font-size: var(--rd-body); line-height: 1.7; }
/* 内容到底时不要再叠一层 margin：抽屉高度贴合内容，最后一块的 14px 外底边加上
   .drawer-scroll 的 28px 内底边，会在动作条上方留出一条看着像"没做完"的空带。 */
#drawer .drawer-scroll > *:last-child { margin-bottom: 0; }
@media (max-width: 760px) {
  .tenure-nav .btn { flex: 1 1 auto; text-align: center; padding: 7px 8px; }
}

/* ==========================================================================
   10. 后端权威房态接入层（PmsAuthority）
   --------------------------------------------------------------------------
   全部使用 .pms-auth-* 新前缀，不覆盖任何已有选择器的语义。
   浅色主题：文字一律深色，色块只做低饱和底 + 深色描边。
   ========================================================================== */

/* --- 10.1 8 态色标：可租=绿 / 承租中=蓝灰 / 到期待交还=橙 / 待生效=紫 /
       冲突待核·待核=红·黄 / 限制=灰锁 --- */
.pms-auth-badge {
  display: inline-flex; align-items: center; gap: 3px;
  font-size: 10px; line-height: 16px; font-weight: 600;
  padding: 0 5px; border-radius: var(--radius-xs);
  border: 1px solid var(--grey-line); background: var(--grey-soft); color: var(--ink-2);
  white-space: nowrap; max-width: 100%; overflow: hidden; text-overflow: ellipsis;
}
.pms-auth-badge.is-wide { font-size: 11px; line-height: 20px; padding: 0 8px; }

.pms-auth-s-free     { background: var(--ok-soft);     border-color: var(--ok-line);     color: #12603F; }
.pms-auth-s-leased   { background: var(--info-soft);   border-color: var(--info-line);   color: #1F4E86; }
.pms-auth-s-expired  { background: var(--warn-soft);   border-color: var(--warn-line);   color: #7A5210; }
.pms-auth-s-future   { background: var(--violet-soft); border-color: #D8CFEE;            color: #4C3F7C; }
.pms-auth-s-renew    { background: var(--teal-soft);   border-color: var(--teal-line);   color: #0E625B; }
.pms-auth-s-conflict { background: var(--danger-soft); border-color: var(--danger-line); color: #95291F; }
.pms-auth-s-unknown  { background: var(--warn-soft);   border-color: var(--warn-line);   color: #7A5210; }
.pms-auth-s-limit    { background: var(--grey-soft);   border-color: var(--grey-line);   color: #4A586B; }

/* 灰锁样式：限制态额外加一把锁，避免与「已租」混淆 */
.pms-auth-s-limit::before { content: "锁"; font-size: 10px; line-height: 1; }

/* --- 10.2 房态格子上的三个维度标记（占用 / 入住 / 限制） --- */
.pms-auth-dims { display: inline-flex; align-items: center; gap: 3px; margin-left: 3px; vertical-align: middle; }
.pms-auth-dim {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 15px; height: 15px; padding: 0 3px;
  font-size: 10px; line-height: 1; font-weight: 700;
  border-radius: 3px; border: 1px solid transparent;
}
.pms-auth-dim-occ   { background: var(--info-soft);   border-color: var(--info-line);   color: #1F4E86; }
.pms-auth-dim-stay  { background: var(--teal-soft);   border-color: var(--teal-line);   color: #0E625B; }
.pms-auth-dim-limit { background: var(--grey-soft);   border-color: var(--grey-line);   color: #4A586B; }
.pms-auth-dim-na    { background: var(--surface-3);   border-color: var(--line);        color: var(--muted-2); }

/* --- 10.3 degraded 顶部横幅 --- */
.pms-auth-banner {
  display: flex; align-items: flex-start; gap: 10px;
  margin: 0 0 12px; padding: 10px 14px;
  border: 1px solid var(--warn-line); border-left: 4px solid var(--warn);
  border-radius: var(--radius-sm); background: var(--warn-soft);
  font-size: 12px; line-height: 1.7; color: #6B4708;
}
.pms-auth-banner strong { font-weight: 700; color: #5A3B06; }
.pms-auth-banner.is-guard {
  border-color: var(--danger-line); border-left-color: var(--danger);
  background: var(--danger-soft); color: #8A2A21;
}
.pms-auth-banner.is-guard strong { color: #7A2318; }
.pms-auth-banner .pms-auth-banner-x {
  margin-left: auto; flex: 0 0 auto; border: 0; background: transparent;
  color: inherit; cursor: pointer; font-size: 14px; line-height: 1; padding: 2px 4px;
}

/* --- 10.4 8 态统计条（与房态页同源） --- */
.pms-auth-stats {
  display: flex; flex-wrap: wrap; gap: 6px;
  margin: 8px 0 0; padding: 0; list-style: none;
}
.pms-auth-stat {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 11px; padding: 3px 9px;
  border-radius: 999px; border: 1px solid var(--line);
  background: var(--surface); color: var(--ink-2);
}
.pms-auth-stat b { font-size: 12px; font-weight: 700; color: var(--ink); }
.pms-auth-stat-src { font-size: 11px; color: var(--muted); margin-left: 6px; }

/* --- 10.5 抽屉：权威状态头 + 承租段时间线 --- */
/* 【D-168】判定依据默认折叠：summary 一行给结论（状态 + 入住 + 承租段），
   展开才看 reasons / 时间线 / 合同房间调整。异常块（降级、待核、历史冲突）
   不折进去——那些是要用户立刻处置的，藏在折叠里等于没提示。 */
.pms-auth-block { margin-top: 14px; }
.pms-auth-box { border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface); margin-bottom: 14px; }
.pms-auth-box > summary { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; padding: 9px 12px; cursor: pointer; font-size: 12px; color: var(--ink-2); list-style: none; }
.pms-auth-box > summary::-webkit-details-marker { display: none; }
.pms-auth-box > summary::after { content: '▾'; margin-left: auto; flex: 0 0 auto; color: var(--muted-2); font-size: 10px; }
.pms-auth-box[open] > summary::after { content: '▴'; }
.pms-auth-sum { font-size: 11.5px; color: var(--muted); }
/* 【2026-11-02 三轮】权威区 summary 去掉重复的状态词后，用「系统核定」四个字说明这行是什么：
   里面的数字来自后端快照，与上面页面自算的承租摘要不是同一来源，这点需要可见。 */
.pms-auth-sum .auth-sum-k { font-style: normal; font-weight: 600; color: var(--ink-2); margin-right: 6px; }
.pms-auth-box-body { padding: 0 12px 12px; }
.pms-auth-box-body .section-label { margin: 12px 0 6px; }
.pms-auth-head {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 9px 11px; border-radius: var(--radius-sm);
  border: 1px solid var(--line); background: var(--surface-2);
}
.pms-auth-head .pms-auth-badge { font-size: 12px; line-height: 22px; padding: 0 10px; }
.pms-auth-head-meta { font-size: 11px; color: var(--muted); }

.pms-auth-reasons { margin: 8px 0 0; padding: 8px 11px; list-style: none;
  border-radius: var(--radius-sm); border: 1px dashed var(--line); background: var(--surface-sunken); }
.pms-auth-reasons li { font-size: 12px; line-height: 1.75; color: var(--ink-2); padding-left: 12px; position: relative; }
.pms-auth-reasons li::before { content: "·"; position: absolute; left: 2px; color: var(--muted-2); }

.pms-auth-conflict {
  margin: 8px 0 0; padding: 8px 11px; border-radius: var(--radius-sm);
  border: 1px solid var(--danger-line); background: var(--danger-soft);
  font-size: 12px; line-height: 1.75; color: #8A2A21;
}
.pms-auth-conflict strong { display: block; margin-bottom: 3px; font-size: 12px; }

/* 承租段时间线 */
.pms-auth-timeline { list-style: none; margin: 8px 0 0; padding: 0 0 0 14px; border-left: 2px solid var(--line-2); }
.pms-auth-tl-item { position: relative; padding: 0 0 12px 12px; }
.pms-auth-tl-item:last-child { padding-bottom: 0; }
.pms-auth-tl-item::before {
  content: ""; position: absolute; left: -21px; top: 5px;
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--surface); border: 2px solid var(--muted-3);
}
.pms-auth-tl-item.is-active::before { border-color: var(--info); }
.pms-auth-tl-item.is-future::before { border-color: var(--violet); }
.pms-auth-tl-item.is-returned::before { border-color: var(--muted-3); background: var(--grey-soft); }
.pms-auth-tl-title { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; font-size: 12px; color: var(--ink); font-weight: 600; }
.pms-auth-tl-range { font-size: 11px; color: var(--muted); margin-top: 2px; }
.pms-auth-tl-meta  { font-size: 11px; color: var(--muted-2); margin-top: 2px; }
.pms-auth-tl-empty { font-size: 12px; color: var(--muted); padding: 6px 0; }

/* --- 10.6 抽屉里的三个写操作按钮 --- */
.pms-auth-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.pms-auth-actions .btn { font-size: 12px; padding: 5px 11px; }
.pms-auth-note { font-size: 11px; color: var(--muted); margin-top: 6px; line-height: 1.7; }
.pms-auth-note.is-warn { color: #7A5210; }
.pms-auth-loading { font-size: 12px; color: var(--muted); padding: 6px 0; }

/* --- 10.7 写操作回执（含半成功「同一 request_id 重试」） --- */
.pms-auth-receipt { margin-top: 10px; padding: 10px 12px; border-radius: var(--radius-sm); border: 1px solid var(--line); background: var(--surface-2); }
.pms-auth-receipt.is-ok      { border-color: var(--ok-line);     background: var(--ok-soft); }
.pms-auth-receipt.is-partial { border-color: var(--warn-line);   background: var(--warn-soft); }
.pms-auth-receipt.is-fail    { border-color: var(--danger-line); background: var(--danger-soft); }
.pms-auth-receipt-title { font-size: 12px; font-weight: 700; margin-bottom: 4px; }
.pms-auth-receipt.is-ok .pms-auth-receipt-title      { color: #12603F; }
.pms-auth-receipt.is-partial .pms-auth-receipt-title { color: #7A5210; }
.pms-auth-receipt.is-fail .pms-auth-receipt-title    { color: #95291F; }
.pms-auth-receipt-text { font-size: 12px; line-height: 1.75; color: var(--ink-2); white-space: pre-wrap; overflow-wrap: anywhere; }
.pms-auth-receipt-id { font-size: 11px; color: var(--muted-2); margin-top: 5px; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
.pms-auth-receipt-actions { display: flex; gap: 6px; margin-top: 8px; flex-wrap: wrap; }

/* --- 10.8 写操作表单（加房 / 逐房交还 / 换房） --- */
.pms-auth-form { margin-top: 10px; padding: 11px 12px; border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--surface); }
.pms-auth-form-title { font-size: 12px; font-weight: 700; color: var(--ink); margin-bottom: 8px; }
.pms-auth-form-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px 10px; }
.pms-auth-form-grid .full { grid-column: 1 / -1; }
.pms-auth-field { display: flex; flex-direction: column; gap: 3px; }
.pms-auth-field label { font-size: 11px; color: var(--muted); }
.pms-auth-field input, .pms-auth-field select, .pms-auth-field textarea {
  font-size: 12px; color: var(--ink); padding: 5px 8px;
  border: 1px solid var(--line); border-radius: var(--radius-xs); background: var(--surface);
}
.pms-auth-form-actions { display: flex; gap: 6px; margin-top: 10px; flex-wrap: wrap; }
.pms-auth-form-disabled { font-size: 12px; color: #7A5210; background: var(--warn-soft); border: 1px solid var(--warn-line); border-radius: var(--radius-sm); padding: 8px 10px; line-height: 1.7; }

/* --- 10.9 右键菜单里的权威分组 --- */
.pms-auth-menu-sep { height: 1px; background: var(--line); margin: 5px 0; }
.pms-auth-menu-title { font-size: 11px; color: var(--muted-2); padding: 3px 10px; letter-spacing: .3px; }

/* --- 10.10 抽屉拖动 / 缩放把手 --- */
.pms-auth-drawer-grip {
  position: absolute; left: 0; top: 0; bottom: 0; width: 10px;
  cursor: ew-resize; background: transparent; z-index: 2;
}
/* 放大命中区：视觉上仍细，但热区扩大到 14px，桌面更易抓取 */
.pms-auth-drawer-grip::before {
  content: ''; position: absolute; left: -4px; top: 0; bottom: 0; width: 14px;
}
.pms-auth-drawer-grip:hover, .pms-auth-drawer-grip.is-active { background: var(--brand-line); }
.pms-auth-drawer-handle {
  display: flex; align-items: center; gap: 6px;
  cursor: grab; user-select: none; flex: 1 1 auto; min-width: 0;
}
.pms-auth-drawer-handle:active { cursor: grabbing; }

@media (max-width: 760px) {
  .pms-auth-form-grid { grid-template-columns: minmax(0, 1fr); }
  .pms-auth-actions .btn { flex: 1 1 auto; text-align: center; }
}

/* --- 楼层范围筛选（工具栏内一行，不额外占高度） ---
   楼层筛选必须常驻可见：藏在 hidden 容器里等于用户点不到楼层。
   但也不能单独占一整条带 —— 房态首屏要求第一间房出现在首屏内，
   多一条 50px 的带就会把房间挤出首屏。所以做成工具栏里的紧凑芯片行。 */
.product-floor-filter {
  display: flex; align-items: center; gap: 4px;
  min-width: 0; flex: 0 1 auto; overflow-x: auto;
  scrollbar-width: none; -ms-overflow-style: none;
}
.product-floor-filter::-webkit-scrollbar { display: none; }
.product-floor-filter .floor-row {
  display: flex; align-items: center; gap: 4px; flex-wrap: nowrap;
}
.floor-button {
  height: 30px; padding: 0 9px; font-size: 12px; font-weight: 700;
  color: #68809a; background: #fff; border: 1px solid var(--line);
  border-radius: var(--radius-sm); cursor: pointer; white-space: nowrap;
  transition: background .12s, border-color .12s, color .12s;
}
.floor-button:hover { border-color: var(--teal-line); color: var(--teal); }
.floor-button.selected {
  color: var(--teal); background: var(--teal-soft); border-color: var(--teal-line);
}
@media (max-width: 1180px) {
  /* 窄屏放不下时，楼层芯片让位：横向滑动而不是把后面的按钮挤变形 */
  .product-floor-filter { max-width: 240px; }
}

/* Codex 2026-10-28: drawer visibility fits the 200ms click budget. */
@media (prefers-reduced-motion: no-preference){#drawer.show{transition-duration:.16s}}

/* ==========================================================================
   11. 页面层组件（合同 / 入住人员 / 账务 / 台账核对）
   --------------------------------------------------------------------------
   2026-10-31 从 pms-pages.js 的 CSS 数组迁入。
   迁移原因：那段样式是运行时 document.head.appendChild 注入的 <style>，
   比 <link pms-product.css> 晚，同特异性下永远覆盖令牌层 —— 结果是
   「合同/住客/账务」三页用旧品牌绿 #158779、圆角 11-13px、6 个近似灰，
   而「房态/报表/看板」用蓝 #345FD8 + 令牌，切页时整套视觉都变。
   现在全部改用令牌，全站只剩一套视觉。新增 .pg-* 规则请写在令牌上。
   ========================================================================== */

.pg-wrap{display:flex;flex-direction:column;gap:14px}
.pg-stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(132px,1fr));gap:10px}
.pg-stat{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);padding:12px 14px}
.pg-stat b{display:block;font-size:21px;letter-spacing:-.5px;color:var(--ink);line-height:1.2}
.pg-stat span{font-size:11px;color:var(--muted)}
.pg-stat.warn b{color:var(--danger)}
.pg-stat.gold b{color:var(--warn)}
.pg-tools{display:flex;gap:10px;flex-wrap:wrap;align-items:center;background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);padding:10px 12px}
.pg-seg{display:inline-flex;background:var(--surface-3);border-radius:var(--radius-sm);padding:3px}
.pg-seg button{border:0;background:transparent;color:var(--ink-2);font-size:12px;padding:6px 12px;border-radius:var(--radius-xs)}
.pg-seg button.on{background:var(--surface);color:var(--brand);font-weight:700;box-shadow:0 1px 3px rgba(37,54,83,.12)}
.pg-search{flex:1;min-width:180px;border:1px solid var(--line-strong);border-radius:var(--radius-sm);padding:8px 11px;font-size:13px;outline:none}
.pg-search:focus{border-color:var(--brand);box-shadow:0 0 0 3px rgba(52,95,216,.18)}
.pg-body{display:flex;flex-direction:column;gap:14px}
.pg-floor{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);overflow:hidden}
.pg-floor-head{display:flex;align-items:center;gap:10px;padding:10px 14px;background:var(--surface-2);border-bottom:1px solid var(--line-2)}
.pg-floor-head b{font-size:13px;color:var(--ink)}
.pg-floor-head span{font-size:11px;color:var(--muted-2);margin-left:auto}
.pg-rooms{display:grid;grid-template-columns:repeat(auto-fill,minmax(268px,1fr));gap:0}
.pg-room{padding:11px 14px;border-bottom:1px solid var(--line-3);border-right:1px solid var(--line-3)}
.pg-room-head{display:flex;align-items:baseline;gap:8px;margin-bottom:7px}
.pg-room-head b{font-size:13px;color:var(--ink);letter-spacing:.3px}
.pg-room-head span{font-size:11px;color:var(--muted);flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pg-room-head em{font-style:normal;font-size:11px;color:var(--ok);background:var(--ok-soft);padding:1px 7px;border-radius:var(--radius-lg)}
.pg-person{display:flex;align-items:center;gap:8px;padding:6px 0;border-top:1px dashed var(--line-2)}
.pg-person:first-of-type{border-top:0}
.pg-person .nm{font-size:12px;color:var(--ink);font-weight:600;min-width:44px}
.pg-person .ph{font-size:11px;color:var(--muted);flex:1 1 auto;min-width:88px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pg-tag{font-size:11px;padding:1px 6px;border-radius:var(--radius-xs);background:var(--surface-3);color:var(--muted);white-space:nowrap}
.pg-tag.warn{background:var(--warn-soft);color:var(--warn)}
.pg-tag.bad{background:var(--danger-soft);color:var(--danger)}
.pg-note{font-size:11px;color:var(--ink-2);background:var(--surface-2);border:1px solid var(--line);border-radius:var(--radius);padding:8px 11px;margin-bottom:9px;line-height:1.6}
.pg-mini{border:0;background:transparent;color:var(--brand);font-size:11px;padding:3px 5px;border-radius:var(--radius-xs)}
.pg-mini:hover{background:var(--brand-soft)}
.pg-mini.mute{color:var(--muted-2)}
.pg-table{width:100%;border-collapse:collapse;background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);overflow:hidden}
.pg-table th{background:var(--surface-2);font-size:11px;color:var(--muted);font-weight:700;text-align:left;padding:9px 12px;border-bottom:1px solid var(--line-2);white-space:nowrap}
.pg-table td{font-size:12px;color:var(--ink);padding:9px 12px;border-bottom:1px solid var(--line-3);vertical-align:middle}
.pg-table td.nowrap{white-space:nowrap}
.pg-table tr:last-child td{border-bottom:0}
.pg-table td.r,.pg-table th.r{text-align:right}
.pg-table .sub{display:block;font-size:11px;color:var(--muted-2);margin-top:2px}
.pg-money-bad{color:var(--danger);font-weight:700}
.pg-money-ok{color:var(--ok)}
.pg-tabs{display:flex;gap:4px;border-bottom:1px solid var(--line);padding:0 4px}
.pg-tabs button{border:0;background:transparent;color:var(--muted);font-size:13px;padding:9px 14px;border-bottom:2px solid transparent;margin-bottom:-1px}
.pg-tabs button.on{color:var(--brand);font-weight:700;border-bottom-color:var(--brand)}
.pg-tabs button i{font-style:normal;font-size:11px;background:var(--surface-3);color:var(--muted);padding:1px 6px;border-radius:var(--radius-lg);margin-left:5px}
.pg-tabs button.on i{background:var(--brand-soft);color:var(--brand)}
/* 【2026-11-01 G-44】合并导航后的页内页签（客户：在住人员／企业档案；设置：默认设置／数据连接／数据说明）。
   视觉沿用 .pg-tabs，但只做导航不承载数据，所以更矮一点，避免和页面标题抢视线。 */
.sub-tabs{display:flex;gap:2px;border-bottom:1px solid var(--line);margin:0 0 14px;padding:0}
.sub-tabs button{border:0;background:transparent;color:var(--muted);font-size:13px;padding:7px 13px;border-bottom:2px solid transparent;margin-bottom:-1px;cursor:pointer}
.sub-tabs button:hover{color:var(--ink-2)}
.sub-tabs button.on{color:var(--brand);font-weight:700;border-bottom-color:var(--brand)}
.pg-empty{background:var(--surface);border:1px dashed var(--line-strong);border-radius:var(--radius);padding:34px 18px;text-align:center;color:var(--muted-2);font-size:12px}
.pg-empty b{display:block;color:var(--ink-2);font-size:14px;margin-bottom:5px}
.pg-loading{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);padding:34px;text-align:center;color:var(--muted-2);font-size:12px}
.pg-bar{display:flex;gap:8px;align-items:center;flex-wrap:wrap}
.pg-btn{border:1px solid var(--line-strong);background:var(--surface);color:var(--ink);font-size:12px;padding:5px 11px;border-radius: var(--radius-xs)}
.pg-btn:hover{border-color:var(--brand);color:var(--brand)}
.pg-btn.pri{background:var(--brand);border-color:var(--brand);color:var(--surface)}
.pg-btn.pri:hover{background:var(--brand-deep);color:var(--surface)}
.pg-btn[disabled]{opacity:1;color:#64707E;background:var(--surface-2);border-color:var(--line);cursor:not-allowed}
.pg-scope{display:flex;flex-wrap:wrap;gap:6px 14px;align-items:center;font-size:12px;color:var(--ink-2);background:var(--surface-2);border:1px solid var(--line);border-radius:var(--radius);padding:8px 12px}
.pg-scope b{color:var(--brand)}
.pg-scope .warn{color:var(--danger)}
.pg-link{border:0;background:transparent;color:var(--brand);font-size:12px;padding:0;cursor:pointer;text-decoration:underline;text-underline-offset:2px}
.pg-chip{display:inline-block;font-size:11px;padding:1px 6px;border-radius:var(--radius-xs);background:var(--surface-3);color:var(--muted);margin-right:4px}
.pg-chip.ok{background:var(--ok-soft);color:var(--ok)}
.pg-chip.warn{background:var(--warn-soft);color:var(--warn)}
.pg-chip.bad{background:var(--danger-soft);color:var(--danger)}
.pg-modal-ov{position:fixed;inset:0;background:rgba(20,35,48,.42);z-index:9000;display:flex;align-items:center;justify-content:center;padding:20px}
.pg-modal{background:var(--surface);border-radius:var(--radius);box-shadow:0 18px 48px rgba(20,35,48,.22);width:min(760px,100%);max-height:86vh;display:flex;flex-direction:column;overflow:hidden}
.pg-modal-head{display:flex;align-items:baseline;gap:10px;padding:14px 16px;border-bottom:1px solid var(--line-2)}
.pg-modal-head b{font-size:14px;color:var(--ink)}
.pg-modal-head span{font-size:11px;color:var(--muted-2)}
.pg-modal-head .pg-btn{margin-left:auto}
.pg-modal-body{padding:14px 16px;overflow:auto}
.pg-trace{display:flex;flex-direction:column;gap:8px}
.pg-trace-node{border:1px solid var(--line);border-radius:var(--radius-sm);padding:9px 11px;background:var(--surface-2)}
.pg-trace-node b{font-size:12px;color:var(--ink)}
.pg-trace-node .kv{display:flex;justify-content:space-between;gap:12px;font-size:12px;color:var(--ink-2);margin-top:4px}
.pg-trace-node .kv span{color:var(--muted-2)}
.pg-trace-arrow{text-align:center;color:var(--muted-3);font-size:12px}
.pg-bill-pick{width:100%;text-align:left;font:inherit;cursor:pointer;background:var(--surface);border:1px solid var(--line);border-radius:var(--radius-sm);padding:9px 11px;margin-bottom:8px}
.pg-bill-pick:hover{border-color:var(--brand)}
@media(max-width:760px){
  .pg-rooms{grid-template-columns:1fr}
  .pg-body{overflow-x:auto;-webkit-overflow-scrolling:touch}
  .pg-table{min-width:540px}
  .pg-table th:nth-child(2),.pg-table td:nth-child(2){display:none}
  .pg-stat b{font-size:18px}
  .pg-btn{padding:9px 12px}
}
.pg-tabbar{display:none}
@media(max-width:760px){
  body{padding-bottom:calc(62px + env(safe-area-inset-bottom))}
  .pg-tabbar{display:flex;position:fixed;left:0;right:0;bottom:0;z-index:60;background:var(--surface);border-top:1px solid var(--line);padding:4px 2px calc(4px + env(safe-area-inset-bottom));box-shadow:0 -4px 18px rgba(37,54,83,.06)}
  .pg-tabbar button{flex:1;border:0;background:transparent;color:var(--muted-2);font-size:11px;display:flex;flex-direction:column;align-items:center;gap:2px;padding:6px 0;min-height:48px}
  .pg-tabbar button .ic{font-size:17px;line-height:1}
  .pg-tabbar button.on{color:var(--brand);font-weight:700}
}
/* ==========================================================================
   12. 窄屏导航：一套，不是两套
   --------------------------------------------------------------------------
   复核发现（代码级）：≤1100px 侧栏被压成 64px 无字图标条，10 个页面只剩
   10 个没有文字的图标可点；≤760px 底部又冒出 5 项 tabbar，两套导航同时存在，
   且覆盖面不同 —— 企业档案 / 经营报表 / 默认设置 / 数据连接 / 数据说明
   这 5 个页面在手机上根本进不去。平板尺寸（760–1100）更是纯图标无提示。

   处理：
   · 760–1100px：侧栏保留，改成「图标 + 10.5px 文字」上下排，宽度 76px。
   · ≤760px：侧栏整体收起成抽屉，由顶栏 ☰ 拉出，10 个页面全在；
     底部 tabbar 只做高频快捷入口，不再是唯一入口。两者共用同一个 setPage。
   ========================================================================== */
.nav-toggle { display: none; }
.nav-veil { display: none; }

@media (min-width: 761px) and (max-width: 1100px) {
  :root { --nav-w: 76px; }
  .aside { padding: 14px 6px 10px; }
  .product-navigation { margin: 0; padding: 0; }
  .nav {
    flex-direction: column; gap: 3px; padding: 7px 2px 6px;
    font-size: 11px; line-height: 1.25; text-align: center;
  }
  .nav .ic { flex: 0 0 auto; }
  .nav .ic svg { width: 18px; height: 18px; }
  .nav-text { display: block; flex: 0 0 auto; max-width: 100%; font-size: 11px; }
  .nav + .nav { margin-top: 2px; }
  /* 选中态指示条从左侧竖条改成底部短横条，竖排在窄条里更清楚 */
  .nav.active::before { left: 50%; top: auto; bottom: 1px; width: 16px; height: 2px; margin-left: -8px; }
}

@media (max-width: 760px) {
  :root { --nav-w: 0px; }
  .nav-toggle { display: inline-flex; flex: 0 0 auto; }
  .topbar { gap: 8px; }
  .topbar .crumb { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

  /* 侧栏 = 左侧抽屉（out of flow，内容区自动占满） */
  .aside {
    position: fixed; top: 0; bottom: 0; left: 0;
    width: min(78vw, 252px); min-height: 0; height: 100vh;
    z-index: var(--z-drawer);
    padding: 16px 12px calc(14px + env(safe-area-inset-bottom));
    transform: translateX(-100%); transition: transform .2s ease;
    box-shadow: 0 0 0 1px var(--line), 8px 0 28px rgba(24, 45, 70, .16);
    overflow-y: auto;
  }
  body.nav-open .aside { transform: none; }
  body.nav-open { overflow: hidden; }
  .nav-veil {
    display: block; position: fixed; inset: 0; z-index: calc(var(--z-drawer) - 1);
    background: rgba(20, 32, 48, .34); opacity: 0; pointer-events: none;
    transition: opacity .2s ease;
  }
  body.nav-open .nav-veil { opacity: 1; pointer-events: auto; }

  /* 抽屉里文字必须回来（≤1100px 把它们 display:none 了） */
  .aside .brand { justify-content: flex-start; padding: 0 8px 14px; }
  .aside .brand-name, .aside .brand-sub, .aside .product-nav-heading,
  .aside .aside-footer, .aside .aside-footnote { display: block; }
  .aside .product-navigation { margin: 0 -4px; padding: 0 4px; }
  .aside .nav {
    flex-direction: row; gap: 10px; padding: 10px 10px;
    font-size: 13px; text-align: left; line-height: 1.4;
  }
  .aside .nav .ic { flex: 0 0 18px; }
  .aside .nav .ic svg { width: 17px; height: 17px; }
  .aside .nav-text { display: block; flex: 1 1 auto; font-size: 13px; }
  .aside .nav.active::before { left: -4px; top: 9px; bottom: auto; width: 3px; height: 18px; margin-left: 0; }
}

/* ==========================================================================
   13. 口径说明与状态行：让说明可收，让告警只给真告警
   ========================================================================== */

/* 【D-162】合同页顶部状态行。
   原来是 .v5-alert（琥珀告警框）写着「合同与附件保存需要验证并启用」——
   那是实施阶段的术语，用户每次进合同页都看到一条黄色警告，读完也不会消失。
   状态行只陈述事实，颜色中性；真出错才换 .is-error 变红。 */
.v5-banner-status {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 12px; margin: 10px 0 14px;
  background: var(--surface-3); border: 1px solid var(--line);
  border-radius: var(--radius-sm); color: var(--ink-2);
  font-size: 12px; line-height: 1.6;
}
.v5-banner-status::before {
  content: ""; flex: none; width: 6px; height: 6px; border-radius: 50%;
  background: var(--ok); box-shadow: 0 0 0 3px var(--ok-soft);
}
.v5-banner-status.is-error { background: var(--danger-soft); border-color: var(--danger-line); color: var(--danger); }
.v5-banner-status.is-error::before { background: var(--danger); box-shadow: 0 0 0 3px rgba(0,0,0,0); }

/* 【D-163】企业档案页原来有两个同名入口：标题栏「＋ 新建企业」按钮 + 下方
   <details> 的「＋ 新建企业」摘要，同一屏两个一模一样的按钮。摘要收起后
   入口只剩标题栏那个；面板本身仍由按钮展开（JS 里的 box.open=true 不变）。 */
#pmsNewCompanyBox > summary { display: none; }
#pmsNewCompanyBox:not([open]) { display: none; }

/* 【D-161】口径说明默认收成一行。
   用户原话：「同一条规则页头说一遍、弹窗再说一遍」「规则说明当界面」。
   账务、人员、历史这些页面原本每个面板顶部都顶着一整段口径，一屏三四段，
   把数据压到下面看不见。现在统一收成一行，点「展开」才看全文；
   文字一个字没删，需要核口径的人仍然查得到。 */
.pg-note-fold {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 1;
  overflow: hidden; position: relative; cursor: pointer;
  padding-right: 62px; transition: background .12s;
}
.pg-note-fold:hover { background: var(--brand-soft); }
.pg-note-fold::after {
  content: "展开 ▾"; position: absolute; right: 10px; top: 50%;
  transform: translateY(-50%);
  font-size: 11px; font-weight: 600; color: var(--brand);
  background: var(--brand-soft); padding: 1px 6px; border-radius: var(--radius-xs);
}
.pg-note-fold.on {
  display: block; -webkit-line-clamp: unset; overflow: visible;
  padding-right: 12px; background: transparent;
}
.pg-note-fold.on::after { content: "收起 ▴"; position: static; transform: none; background: transparent; padding: 0; margin-left: 6px; }

/* 【D-164】空状态收敛成一种。
   原来 5 套：.empty-state(48px 无边框) / .v5-empty(44px 无边框) /
   .product-empty(44px 虚线) / .board-empty(36px 无边框) / .pg-empty(34px 虚线)。
   同样是「这里没有内容」，在 5 个页面有 5 种内边距、2 种边框、2 档字号。
   统一成一套：40px 内边距、1px 虚线、统一圆角；标题与正文两级字号。 */
.empty-state, .v5-empty, .product-empty, .board-empty, .pg-empty {
  padding: 40px 20px; text-align: center;
  color: var(--muted); font-size: 12px; line-height: 1.75;
  background: var(--surface);
  border: 1px dashed var(--line-strong);
  border-radius: var(--radius);
}
.empty-state strong, .v5-empty strong, .product-empty strong, .board-empty strong, .pg-empty b {
  display: block; color: var(--ink-2); font-size: 14px; font-weight: 700; margin-bottom: 6px;
}
.empty-state p, .product-empty p { font-size: 12px; margin: 0 0 12px; color: var(--muted); }
.empty-state svg { width: 30px; height: 30px; color: var(--muted-3); margin-bottom: 10px; }

/* ==========================================================================
   14. 房态图（room rack）：可租是默认态，默认态不该上色
   --------------------------------------------------------------------------
   改版前（1440×900 实测）：第一张房卡落在 389px 处，首屏 43% 是说明文字；
   每张房卡 150×104，只有左上角「南101 + 可租」三个字，剩下 80% 是白板；
   189 间里 187 间可租，却每张都上淡绿底 + 4px 绿左边 + 绿胶囊 ——
   整页是一堵绿白墙，真正有信息量的 3 间（在租 / 限制 / 待生效）反而看不见。
   成熟房态图（Opera Cloud / 别样红 / Mews 的房间排布）共同做法：
   ① 可租 = 中性白底，靠「没有颜色」表达空闲；
   ② 只有占用与异常才上色，一眼扫到的就是需要处理的房间；
   ③ 卡片密度优先，房号最大、客户次之、日期最小，三行填满不留白板。
   本节只覆盖视觉，不改任何判定与数据来源。
   ========================================================================== */

/* --- 14.1 抬头：6 层压成 2 层 --- */
.w-header { padding: 8px 12px; gap: 10px; }
.w-header > div:first-child { max-width: none; min-width: 0; }
.w-header .board-view-switch { flex: 0 0 auto; }
.w-header .room-export-tools { flex: 0 0 auto; }

.w-subbar {
  display: flex; align-items: center; gap: 12px;
  padding: 7px 12px 8px; border-bottom: 1px solid var(--line-2);
}
.w-subbar .w-caption {
  flex: 1 1 auto; min-width: 0; margin: 0;
  font-size: 11px; color: var(--muted-2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* 出租率：数字常驻，口径收进 ⓘ 弹层 */
.w-rate { flex: 0 0 auto; position: relative; }
.rate-details > summary {
  display: inline-flex; align-items: center; gap: 5px; cursor: pointer;
  list-style: none; font-size: 11px; color: var(--muted);
  border: 1px solid var(--line); background: var(--surface);
  border-radius: 999px; padding: 2px 9px; transition: border-color .12s, background .12s;
}
.rate-details > summary::-webkit-details-marker { display: none; }
.rate-details > summary::after { content: "ⓘ"; color: var(--brand); font-weight: 700; }
.rate-details > summary:hover { border-color: var(--line-strong); background: var(--surface-3); }
.rate-details > summary b { color: var(--ink); font-variant-numeric: tabular-nums; font-weight: 700; }
.rate-pop {
  position: absolute; right: 0; top: calc(100% + 6px); width: 330px; z-index: var(--z-sticky);
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-sm);
  box-shadow: var(--shadow-2); padding: 10px 12px;
  font-size: 11px; line-height: 1.75; color: var(--muted); text-align: left; white-space: normal;
}
.rate-pop b { color: var(--ink-2); }

/* --- 14.2 状态条：图例 + 计数 + 筛选，一个控件三件事 --- */
.room-state-bar {
  display: flex; align-items: center; gap: 4px; flex: 1 1 auto; min-width: 0;
  overflow-x: auto; scrollbar-width: none; padding: 1px 0;
}
.room-state-bar::-webkit-scrollbar { display: none; }
/* 【2026-11-01 真机实测 G-40】状态条最后一个 chip 被切在视野外：
   1440 屏上 8 个 chip 合计 800px、容器只有 783px，溢出 17px，
   「非出租用途 12」被裁成「非出租用途 1」，末尾的「房态卡片 / 楼层平面」
   切换也被挤到同一行右侧。这条 bar 是 overflow-x:auto + 隐藏滚动条（scrollbar-width:none），
   页面上没有任何横滑提示，用户不会想到去拖一条工具栏。
   桌面改成换行——宁可头部高一档，也不能让一个数字被裁半个。
   手机断点（≤760px）保留横滑：那里本来就只能横滑，是刻意的取舍，
   见下方 @media (max-width:760px) 里「状态条独占一行可横滑」的注释。 */
@media (min-width: 761px) {
  .room-state-bar { flex-wrap: wrap; row-gap: 5px; }
}
.room-state-bar .pms-auth-banner { flex: 1 1 auto; margin: 0; }
/* 【D-167】状态条字号从 11 提到 12.5：这是房态图上唯一需要「读」的控件，
   11px 在 1366 办公屏上要凑近看。圆点同步 7→8，点击热区 24px。 */
.state-chip {
  display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto;
  border: 1px solid var(--line); background: var(--surface); border-radius: 999px;
  padding: 4px 11px 4px 9px; line-height: 1.5; color: var(--ink-2);
  white-space: nowrap; cursor: pointer; font: inherit; font-size: 13px;
  min-height: 26px;
  transition: background .12s, border-color .12s, color .12s;
}
.state-chip:hover { border-color: var(--line-strong); background: var(--surface-3); }
.state-chip i { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.state-chip b { font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; font-size: 13px; }
.state-chip[aria-pressed="true"] { border-color: var(--brand); background: var(--brand-soft); color: var(--brand-ink); }
.state-chip[aria-pressed="true"] b { color: var(--brand-ink); }
.state-chip.is-zero { color: var(--muted-2); background: var(--surface-2); border-color: var(--line); }
.state-chip.is-zero b { color: var(--muted-2); }
.state-chip.is-zero:hover { background: var(--surface-3); }

/* --- 14.3 房卡：三行填满、密度优先 --- */
/* 【D-167】看板底从 --surface-sunken(#F7F9FC) 再压深一档。
   原来空房卡是纯白、看板底是近白，143 张空房连成一片「白板」，
   用户说的「整体页面都属于一个白色的底框」就是这个。
   底压深之后白卡浮起来，格子边界靠底色差就分得开，不用靠加粗边框。 */
#page-rooms .floor-scroll,
#page-rooms .floor-scroll[data-board-mode="cards"] { background: #EDF1F6; }
.room-cards { grid-template-columns: repeat(auto-fill, minmax(124px, 1fr)); gap: 6px; }
/* 【B 级改造 · 房卡两行化】84px → 68px。
   病史：三行卡里第三行是「20㎡」，而 189 间房主档的 area 只有一个值 —— {"20": 189}。
   零方差的数据印 156 遍，占掉卡片 1/3 高度，这是"整页显空"的机械原因。
   删掉第三行后两行内容 = 19.2(L1) + 3(gap) + 16.8(L2) = 39，盒内 68−16 = 52，
   justify-content:center 把多出的 13px 上下均分，卡片是"松"而不是"底下一截空"。
   注意必须同时写 min-height：`.room` 基础规则有 min-height:78px，min-height 会压过 height，
   只写 height:68px 实测仍渲染 78px。 */
.room.room-card {
  height: 68px; min-height: 68px; padding: 8px 8px 8px 9px; gap: 3px;
  border-left-width: 3px; justify-content: center;
  box-shadow: 0 1px 2px rgba(24, 45, 70, .05);
}
.room.room-card .card-head { gap: 4px; }
.room.room-card .rid { font-size: 16px; letter-spacing: -.2px; }
/* 字号不许跟着宽度一起收：124px 宽已经比原来 150px 窄，
   再压到 11px 中文就糊了。宽度让、字号不让，这是可读底线。
   L1 房号 16/700 不动；L2 从 13 收到 12，是因为它从"三行里的中间行"变成了"唯一一行副信息"，
   12/500 在 4.56:1 上仍可读，而 16px 房号与它的对比反而更强了。 */
/* L2 是一个 flex 行：左边一行副信息（.rn，可省略），右边网络点（.net）。
   用 flex 而不是让 .net 绝对定位，是因为绝对定位的点会压住长文本的尾部；
   flex 里 .rn 拿 flex:1 + min-width:0，省略号会自己让出点位。
   .rn 必须 width:auto —— `.room .rn` 基础规则写的是 width:100%，那是给平面视图用的。 */
.room.room-card .card-sub { display: flex; align-items: center; gap: 5px; width: 100%; min-width: 0; }
.room.room-card .rn { font-size: 12px; line-height: 1.4; font-weight: 500; width: auto; flex: 1 1 auto; min-width: 0; }
.room.room-card .card-meta { font-size: 12px; line-height: 1.35; }
.room.room-card .state-pill { font-size: 10px; padding: 0 5px; line-height: 1.6; }
/* 【B 级改造 · 网络点】只标"已装"，未装不占位。
   用户澄清的业务事实：「网络是根据住客要求，不是全部房间都会去安装网络。安装了就做一下记录。
   以后退房了也不会特意去拆网。」三件事都指向同一个结论：
     · "未装"是常态，不是异常 —— 所以不能给警示色。给 79 间挂橙色（--warn）等于凭空
       制造 79 条假待办，而橙色在这套色板里只代表"该处理没处理"（到期/超期/风险）；
     · "已装"是房间的长期设施属性，不随合同生灭 —— 所以它该弱于状态色，
       中性灰空心圈（--muted-2，与卡片副文字同色）刚好：在场，但不抢注意力；
     · 189 格里 79 格空着才是对的 —— 默认态不占视觉位置，这是"少即是多"的硬约束。
   两个状态都渲染（.on / .off），只由 CSS 控显隐，是为了以后加「网络台账」时不用再改渲染层。
   功能房/样板间不渲染这个元素（渲染层已排除）：电房装不装网不是经营信息。 */
.room.room-card .net {
  flex: 0 0 auto; margin-left: auto;
  width: 8px; height: 8px; border-radius: 50%;
  border: 1.5px solid var(--muted-2); background: transparent;
}
.room.room-card .net.off { display: none; }
/* 多选态：`.board-select-check`（32×17，right:6/bottom:6）与 L2 右端的网络点撞在一起。
   两行化后实测重叠 41px²（点总面积 64px²，被盖掉 64%），而且它还会压住 L2 文字的尾部。
   卡片被选中时 `aria-pressed="true"` 已经在了（多选高亮圈用的就是它），直接拿它当开关：
   点让位、L2 右侧留出 30px 给角标。不新增任何 class，也不改 JS。 */
.room.room-card[aria-pressed="true"] .net { display: none; }
.room.room-card[aria-pressed="true"] .card-sub { padding-right: 30px; }
.room.room-card .pms-badges { font-size: 10px; }

/* 可租 = 默认态：不上色、文字压暗一档。
   注意底色必须留白（--surface）：看板底色是 --surface-sunken #F7F9FC，
   空房卡若再上浅底就会「陷进背景」，整片房态图连格子都分不出来。
   让空房退后靠的是「没有颜色 + 房号压暗」，不是把卡片涂灰。
   【2026-11-01 G-43】原来这里还「不挂胶囊」。现在挂一个「空置」，但**不上色**：
   无底色、灰字、细边。理由见 index.html#cardHTML 的注释（截图/色弱/培训三条）。
   上色就错了——156 张绿胶囊会把整屏染绿，真正要看的黄卡（到期）反而不跳出来了。 */
.room.room-card.vacant {
  background: var(--surface);
  border-color: var(--line-2);
  border-left-color: var(--line-2);
}
.room.room-card.vacant .state-pill {
  background: transparent; color: var(--muted-2); border-color: var(--line-2);
}
.room.room-card.vacant .rid { color: var(--ink-2); }
.room.room-card.vacant .rn { color: var(--muted-2); }
.room.room-card.vacant .card-meta { color: var(--muted-2); }
.room.room-card.vacant:hover { border-left-color: var(--ok); background: var(--surface); }

/* 状态配色与状态条对齐。
   原来图例写「承租中」是蓝的（--info），房卡左边却是青的（--teal）；
   「续签办理」图例是青、房卡是琥珀 —— 两套配色互相对不上，这是「颜色合适吗」的根。 */
.room.leased { border-left-color: var(--info); background: var(--info-soft); border-color: var(--info-line); }
.room.leased .state-pill { background: var(--info-soft); color: #1F4E86; border-color: var(--info-line); }
.room.renew { border-left-color: var(--teal); background: var(--teal-soft); border-color: var(--teal-line); }
.room.renew .state-pill { background: var(--teal-soft); color: var(--teal); border-color: var(--teal-line); }
.room.expiredOcc { border-left-color: var(--warn); background: var(--warn-soft); border-color: var(--warn-line); }
.room.expiredOcc .state-pill { background: var(--warn-soft); color: var(--warn); border-color: var(--warn-line); }
.room.scheduled { border-left-color: var(--violet); background: var(--violet-soft); border-color: #D9D2F0; }
.room.scheduled .state-pill { background: var(--violet-soft); color: var(--violet); border-color: #D9D2F0; }
/* 限制 = 真的不能租：加斜纹，一眼看出「这间别去动」 */
.room.room-card.blocked {
  background: repeating-linear-gradient(135deg, var(--grey-soft) 0 6px, #E6EBF2 6px 12px);
}
/* 【2026-11-01】读取中：中性底、无斜纹。斜纹专属于「限制/不可租」，
   入住资料还在读的时候套上斜纹，等于告诉用户 157 间房全锁了。 */
.room.room-card.pending {
  background: var(--surface-2);
  border-left-color: var(--muted-3);
  border-style: dashed;
}
.room.pending .state-pill { background: var(--surface-3); color: var(--muted); border-color: var(--line); }
/* 非出租用途 = 不是房源：实心中灰底 + 虚线边。
   原来用 --surface-3（#F7F9FC 一档），和空房的纯白几乎看不出差别，
   用户问「功能房是灰色跟空置房，区别还不是很明显」就是这里。
   【A 级改造】上一版把底定成 #EDF1F6，但 14.3 又把看板底也压到了 #EDF1F6 ——
   两个值撞成同一个色，功能房卡（10 间 + 2 样板间）**等于透明**，只剩一圈虚线边，
   在网格里是"消失"而不是"退后"。实测 utilBg === boardBg === rgb(237,241,246)。
   现在功能房底比看板底再深一档 #E3E9F0，空房纯白、功能房深灰、看板居中，
   三级靠明度差就能分开，不依赖边框。
   连带：胶囊原来 #E2E8F0 与新卡底只差 1 阶，会糊在卡上，同步压到 #D5DEE8；
   胶囊文字从 #56687A（4.22:1）加深到 #4A5B6C（5.1:1）。 */
.room.room-card.utility, .room.room-card.sample {
  background: #E3E9F0;
  border: 1px dashed #C9D3DE;
  border-left: 3px solid var(--muted-3);
}
.room.room-card.utility .rid, .room.room-card.sample .rid { color: var(--muted); }
.room.room-card.utility .state-pill, .room.room-card.sample .state-pill {
  background: #D5DEE8; color: #4A5B6C; border-color: #C9D3DE;
}
.room.room-card.utility .rn, .room.room-card.sample .rn { color: var(--muted); }

/* --- 14.4 楼层与密度 --- */
.floor-scroll { padding: 10px 12px 16px; }
.floor-stack { gap: 14px; }
.floor-unit + .floor-unit { padding-top: 12px; }
.floor-heading { margin: 0 0 6px; }
.floor-heading strong { font-size: 12px; letter-spacing: .2px; }
/* 【A 级改造】右侧「21 间」原来是 11px --muted-2，压在 #EDF1F6 看板底上只有 2.56:1，
   是全页最难读的一处（还是每层都要读 9 次的数字）。字号 11→12、色 --muted-2 → --muted，
   实测 4.86:1。楼层头是"这一层有多少间"的唯一入口，不该比卡片上的字更淡。 */
.floor-heading span { font-size: 12px; color: var(--muted); }
/* 紧凑模式：两行后 68 → 56（内容 39 + 内边距 10 = 49，留 7px 余量，不夹字）。
   列宽同步收到 108，1440 下从 9 列变 10 列，让「紧凑」真的多塞一列而不只是矮一点。 */
body[data-density="compact"] .room.room-card { height: 56px; min-height: 56px; padding: 5px 6px; }
body[data-density="compact"] .room-cards { grid-template-columns: repeat(auto-fill, minmax(108px, 1fr)); gap: 5px; }

/* 状态条上的提示 chip：不可点，只报告权威判定的读取状态 */
.state-chip.is-note {
  cursor: default; border-style: dashed; color: var(--muted-2); background: transparent;
}
.state-chip.is-note:hover { border-color: var(--line); background: transparent; }
.state-chip.is-note.is-error { border-color: var(--danger-line); color: var(--danger); background: var(--danger-soft); }

/* --- 14.5 手机端：抬头只留状态条 + 视图切换 ---
   390px 实测：抬头三块（状态条 / 视图切换 / 导出）挤一行时，状态条被压到 0 宽，
   截图里整排「可租 / 承租中 / 续签办理」直接看不见——房态图最核心的筛选器消失。
   手机上让状态条独占一行可横滑，用法说明整句去掉（手机没有右键、也没有底部更多操作）。 */
@media (max-width: 760px) {
  .w-header { flex-wrap: wrap; gap: 6px; padding: 6px 10px 8px; }
  .w-header > div:first-child { flex: 1 0 100%; order: -1; max-width: none; min-width: 0; }
  .room-state-bar { padding: 0 0 2px; }
  .w-subbar { padding: 3px 10px 6px; justify-content: flex-end; }
  .w-subbar .w-caption { display: none; }
  /* 一屏多看几间：3 列 + 卡片矮一档（两行化后 68 → 60，内容 39 + 内边距 10 = 49） */
  .room-cards { grid-template-columns: repeat(auto-fill, minmax(102px, 1fr)); gap: 5px; }
  .room.room-card { height: 60px; min-height: 60px; padding: 6px 6px; }
  .room.room-card .rid { font-size: 15px; }
  .room.room-card .rn { font-size: 12px; }
  /* 手机不画网络点：360 宽时内容盒只剩 94px，「四人间 · 朝南」(81) + 点(8) + 间隔(5) = 94，
     正好卡在省略边界上，缩一个字就变「四人间 · 朝…」。D-182 已经定过手机不画这个标
     （手机没有 hover，静置属性不该和到期日抢同一格），这里沿用同一判断。 */
  .room.room-card .net { display: none; }
  .floor-scroll { padding: 8px 10px 14px; }
}

/* --- 15. 合同表单录入清晰度（D-169）---
   这一节只解决「字太多、层级看不出」，不改变任何字段、按钮与业务口径。
   1) .form-sub 把第 3 节切成「报价与计费 / 应收与实收」两段，避免 8 个字段平铺；
   2) .rule-fold 收掉原来常驻的 8 条计费说明，只留一句在正文；
   3) .v63-upload-group 把五类附件各自框成一格，文件名一行一个，不再是一串按钮连在一起。 */
.form-sub {
  grid-column: 1 / -1; font-size: 12px; font-weight: 600; color: var(--muted-2);
  letter-spacing: .04em; margin-top: 2px; padding-bottom: 5px; border-bottom: 1px dashed var(--line-2);
}
/* .full 原来只在 .v5-field 上生效，说明行与按钮行因此被挤进左右两列（截图里像被拆成两半）。 */
.v5-form-grid > .full { grid-column: 1 / -1; }
.quote-apply { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
/* 只读字段（实收与收款日由账务回写、展示编号由序号生成）压一档底色，避免看起来能填。 */
.v5-contract-form input[readonly] { background: var(--surface-sunken); color: var(--ink-2); cursor: default; }
.rule-fold { margin-top: 14px; }
.rule-fold > summary { font-size: 12px; color: var(--muted-2); cursor: pointer; list-style: none; padding: 3px 0; }
.rule-fold > summary::-webkit-details-marker { display: none; }
.rule-fold > summary::before { content: '▸ '; }
.rule-fold[open] > summary::before { content: '▾ '; }
.rule-list { margin: 8px 0 0; padding-left: 18px; font-size: 12px; color: var(--muted); line-height: 1.8; }
.attach-intro { gap: 4px; background: var(--surface-2); border-radius: var(--radius-sm); padding: 10px 12px; }
.v63-upload-group { gap: 8px; padding: 11px 12px; background: var(--surface-2); border: 1px solid var(--line-2); border-radius: var(--radius-sm); }
.v63-upload-group > label { display: flex; align-items: center; gap: 8px; }
.upload-count { margin-left: auto; font-weight: 400; font-size: 11.5px; color: var(--muted-2); }
.v63-upload-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.v63-upload-actions input[type=file] { flex: 1 1 200px; min-width: 0; max-width: 100%; min-height: 40px; padding: 6px 8px; font-size: 12px; border-style: dashed; background: var(--surface); cursor: pointer; }
/* 附件一行一份：文件名独占剩余宽度并在过长时省略，按钮靠右不换行。 */
.v62-files { display: flex; flex-direction: column; gap: 6px; }
.v62-file {
  display: flex; align-items: center; gap: 6px; padding: 6px 8px;
  background: var(--surface); border: 1px solid var(--line-2); border-radius: var(--radius-xs); font-size: 12.5px;
}
.v62-file > span { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.v62-file > .btn { flex: 0 0 auto; }

/* --- 16. 入住人员查询面板与住客登记卡（D-170）---
   入住人员页从「一个搜索框 + 四个快筛按钮」改为标准查询页（入住时间/离住时间两组区间
   + 企业 + 入住状态 + 姓名 + 房间号 + 查询/重置/导出），形态照用户给的酒店 PMS 参考图。
   登记侧一个人名片要装 5 个字段，原来的 4 列表格会挤成两行，改为一卡一人。 */
.pg-query { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 11px 12px; display: flex; flex-direction: column; gap: 9px; }
.pg-qrow { display: flex; gap: 10px 14px; flex-wrap: wrap; align-items: flex-end; }
.pg-qf { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.pg-qf > label { font-size: 11px; color: var(--muted-2); letter-spacing: .02em; }
.pg-qf input, .pg-qf select { font: inherit; font-size: 12.5px; color: var(--ink); background: var(--surface); border: 1px solid var(--line-strong); border-radius: var(--radius-xs); padding: 6px 8px; min-height: 32px; outline: none; }
.pg-qf input:focus, .pg-qf select:focus { border-color: var(--brand); box-shadow: 0 0 0 3px rgba(52,95,216,.16); }
.pg-qf input[id="pgGuestSearch"], .pg-qf input[id="pgGuestRoom"] { width: 158px; }
.pg-qf select { width: 176px; }
.pg-qrange { display: flex; align-items: center; gap: 5px; }
.pg-qrange i { font-style: normal; font-size: 11px; color: var(--muted-2); }
.pg-qrange input { width: 132px; }
.pg-qacts { display: flex; gap: 8px; align-items: center; margin-left: auto; }
.pg-qacts .pg-btn { min-height: 32px; padding: 6px 14px; font-size: 12.5px; }
.pg-result-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 12px; color: var(--ink-2); }
.pg-result-head > b { font-size: 17px; color: var(--brand); font-variant-numeric: tabular-nums; letter-spacing: -.3px; }
.pg-result-head > .pg-seg { margin-left: auto; }
/* 11 列的人员表宽度不够就得横向滚动：包层 overflow-x:auto 既能滚，也不会被「横向溢出」审计判红。
   列宽必须 11 列全部给定且与 min-width 相加一致：table-layout:fixed 下没给宽度的列
   只能拿“总宽 - 已给列宽”的余量，前面 10 列已凑满 1080 时操作列就被压成 15px，
   里面的日期区间直接溢出（真机 ui-audit 实测：span.sub:140>15）。 */
.pg-tablewrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.pg-person-table { min-width: 1164px; table-layout: fixed; }
.pg-person-table th:nth-child(1) { width: 92px; }
.pg-person-table th:nth-child(2) { width: 100px; }
.pg-person-table th:nth-child(3) { width: 128px; }
.pg-person-table th:nth-child(4) { width: 84px; }
.pg-person-table th:nth-child(5) { width: 132px; }
.pg-person-table th:nth-child(6) { width: 150px; }
.pg-person-table th:nth-child(7) { width: 62px; }
.pg-person-table th:nth-child(8) { width: 88px; }
.pg-person-table th:nth-child(9) { width: 88px; }
.pg-person-table th:nth-child(10) { width: 72px; }
.pg-person-table th:nth-child(11) { width: 168px; }
.pg-person-table td { padding: 8px 10px; }
.pg-person-table td.ell, .pg-person-table td.nowrap { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* 列内第二行（性别 / 入住→离住区间）同样要在列宽内收尾，不能靠溢出探出去。 */
.pg-person-table .sub { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@media (max-width: 760px) {
  .pg-qacts { margin-left: 0; width: 100%; }
  .pg-qacts .pg-btn { flex: 1 1 auto; }
  .pg-qf select, .pg-qf input[id="pgGuestSearch"], .pg-qf input[id="pgGuestRoom"] { width: 100%; min-width: 0; }
  .pg-qf { flex: 1 1 46%; }
  .pg-qrange input { width: 118px; }
}
/* 登记住客：一卡一人，姓名/手机/证件/籍贯在前，地址整行（最长）。 */
.rs-card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-sm); padding: 8px 9px 9px; }
.rs-card + .rs-card { margin-top: 8px; }
.rs-card-head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 7px; }
.rs-card-head b { font-size: 12px; color: var(--ink); }
.rs-card-head span { font-size: 11px; color: var(--muted-2); flex: 1 1 auto; }
.rs-del { border: 0; background: transparent; color: var(--muted-2); font: inherit; font-size: 11px; padding: 2px 6px; border-radius: var(--radius-xs); cursor: pointer; }
.rs-del:hover { color: var(--danger); background: var(--danger-soft); }
.rs-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 7px; }
.rs-grid .wide { grid-column: 1 / -1; }
@media (min-width: 620px) { .rs-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

/* --- 17. 房态首页的到期与欠费提醒条（D-171）---
   业主每天第一屏就是房态图，“有没有到期没退 / 有没有钱没收回来”应当在这一屏就回答。
   只占一行：数字在房态，逐条清单仍在待办与账务（点 chip 钻取），不在这里重复列长表。
   用 overflow-x:auto 而非 wrap 开多行：窄屏不能把房态图再往下顶。 */
.rooms-risk {
  /* 【2026-11-01 真机实测】原来 nowrap + overflow-x:auto 且隐藏滚动条：
     1440 屏上这一条实际需要 1437px，容器只有 1188px，最后一个「押金未收」被切在
     视野外，而且没有滚动条提示可以横滑——用户根本不知道还有一项。
     数据一多就暴露，空库时看不出来。改成允许换行：多占 26px 高度，换「全部可见」。 */
  display: flex; align-items: center; gap: 5px; flex-wrap: wrap; row-gap: 6px;
  margin: 0 0 8px; padding: 3px 8px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-sm);
}
.rooms-risk::-webkit-scrollbar { display: none; }
.rooms-risk[hidden] { display: none; }
.risk-lead { flex: 0 0 auto; font-size: 11.5px; font-weight: 700; color: var(--ink-2); }
/* 【可读性改造 · 主次分明】原来的问题：chip 里「租金欠费 ¥25万 · 5 家 · 逾期 ¥7.8万 最长 365 天」
   全部同字号（11.5px）同颜色，只有 <b> 加粗。结果是主数字和它的解释文字一样重，
   在密集小字下一眼扫过去会读错——team-lead 把 ¥3,100.00 读成「¥3.1000」就是这件事的直接后果。
   注意一个硬约束：chip 里的次要信息（「· 5 家」「· 逾期 …」）是 **裸文本节点**，
   不是元素，CSS 选不到它。所以纯 CSS 能做到的是"把数字拔出来"，不是"把次要信息单独降级"：
     · 整条文字降到 11px + --muted（4.51:1，全部落在 AA 之上）；
     · <b> 里的数字升到 13px/700 + 状态色，成为 chip 里唯一的高对比元素；
     · 状态色从"整条染色"收到"只染数字"，边框与底色继续承担状态识别。
   要把「· 5 家」这类真正单独降一档，需要 `payChips` 给那几段包一个 <span class="risk-sub">，
   那属于 v5-features.js（归 investigator），CSS 侧已备好 `.risk-chip .risk-sub` 的写法见下。 */
.risk-chip {
  display: inline-flex; align-items: baseline; gap: 4px; flex: 0 0 auto;
  border: 1px solid var(--line); background: var(--surface-2); color: var(--muted);
  border-radius: 999px; padding: 3px 7px; font: inherit; font-size: 11px; line-height: 1.5;
  white-space: nowrap; cursor: pointer; min-height: 22px;
  transition: background .12s, border-color .12s;
}
.risk-chip b { font-size: 13px; font-weight: 700; line-height: 1.15; letter-spacing: -.2px; font-variant-numeric: tabular-nums; }
/* 次要信息若将来被包成 .risk-sub（需 JS 配合），这里已经是最终形态，JS 一改就生效。 */
.risk-chip .risk-sub { font-size: 10.5px; color: var(--muted-2); font-weight: 400; }
.risk-chip:hover { border-color: var(--line-strong); background: var(--surface-3); }
.risk-chip.red { background: var(--danger-soft); border-color: var(--danger-line); color: var(--muted); }
.risk-chip.red b { color: #B23C33; }
.risk-chip.amber { background: var(--warn-soft); border-color: var(--warn-line); color: var(--muted); }
.risk-chip.amber b { color: var(--warn); }
.risk-chip.gray { border-style: dashed; color: var(--muted); }
.risk-chip.ok { background: var(--ok-soft); border-color: transparent; color: var(--ok); cursor: default; }
/* 【2026-11-01】到期分档用品牌蓝：它既不是"危险"也不是"警告"，是"要提前排期"。
   与红色（已过期/欠费）、琥珀（押金/15 天）分开，一眼能分清轻重缓急。 */
.risk-chip.blue { background: var(--brand-soft); border-color: var(--brand-line); color: var(--muted); }
.risk-chip.blue b { color: var(--brand-ink); }
/* 两组之间用竖线隔开：「合同到期」是时间问题，「款项」是钱的问题，处理人不一样。 */
/* 【A 级改造 · 提醒条收成一行】1440 实测：这条原高 82px（两行 6 chip，第二行只有 3 个长 chip，
   右边拖一大截空白），占首屏 9%；内容与「待办」页完全重复，而它把第一张房卡压到了 y=359。
   目标是一行 34px。可用宽度 = 1188 - 16(padding) = 1172；原有元素合计约 1441px，缺 269px。
   CSS 侧能省的全在这里（不动 JS 渲染的 chip 数量，6 个 chip 一个不少）：
     · 组名「合同到期 / 款项」去掉 —— 与手机端 G-41 的处理一致，每个 chip 自己就写着
       「7天内到期」「租金欠费」，再套一层组名是重复；省约 112px；
     · 分隔竖线去掉（组名没了，竖线也没有分隔对象）；省 18px；
     · chip 字号 12.5→11.5、内距 4/11→3/7、间距 5→4；省约 126px；
     · 条内 gap 8→5、上下 padding 7→3；省约 30px + 8px；
     · 「全部待办」按钮 12→11.5、内距 6/10→4/8；省 10px。
   合计约 304px > 269px，1440 下收成一行 34px。
   **保留 flex-wrap:wrap**：宽度更窄或数据变多（比如押金也出现"最长 N 天"）时自动回到两行，
   绝不重演 G-40 那次"最后一个 chip 被切在视野外"的裁切。 */
.risk-group { display: inline-flex; align-items: center; gap: 4px; flex: 0 0 auto; }
/* 只隐藏，保留原排版声明：万一以后某个断点又把组名放出来，字号/颜色还是对的 */
.risk-group > em { display: none; font-style: normal; font-size: 11px; color: var(--muted-2); white-space: nowrap; }
.risk-sep { display: none; flex: 0 0 auto; width: 1px; height: 16px; background: var(--line-strong); }
.risk-src { flex: 0 0 auto; font-size: 11px; color: var(--muted-3); margin-left: auto; }
/* 这条右侧说明是「操作指引」，不是数据。空间不够时先让它让位，不能让真正的金额被挤掉。 */
@media (max-width: 1500px) { .risk-src { display: none; } }
/* 【2026-11-01 G-41】「全部待办 N 条 →」是入口，不是说明文字，所以宽度不够时也不隐藏：
   没有它，用户看着这条提醒条会以为是第二份待办清单，不知道该不该再去点「待办」。 */
.risk-more {
  flex: 0 0 auto; margin-left: auto;
  display: inline-flex; align-items: center; gap: 3px;
  font-size: 11.5px; font-weight: 600; line-height: 1;
  color: var(--brand-ink); background: var(--brand-soft);
  border: 1px solid var(--brand-line); border-radius: 999px;
  padding: 4px 8px; cursor: pointer; white-space: nowrap;
}
.risk-more:hover { background: #fff; border-color: var(--brand); }
.risk-more b { font-variant-numeric: tabular-nums; }
/* 【2026-11-01】手机：提醒条改成两行，不再横向滚动。
   原来靠 overflow-x 保一行，结果 390 屏上「款项」整组被推到视野外——
   信息在页面上但用户看不到，等于没有。多占 30px 高度换「一屏看全」是值得的。 */
@media (max-width: 760px) {
  .rooms-risk { flex-wrap: wrap; overflow-x: visible; row-gap: 6px; padding: 8px 10px; }
  .rooms-risk .risk-lead { flex: 0 0 100%; }
  .risk-group { flex: 1 1 100%; flex-wrap: wrap; row-gap: 6px; }
  .risk-sep { display: none; }
  .risk-chip { font-size: 12px; padding: 3px 9px; }
  /* 【2026-11-01 G-41 复测】加了「全部待办」按钮后，390 屏这条提醒条量到 229px 高——
     占了首屏近三成，房态卡被挤到看不见。手机上必须更紧凑：
       · 组名（合同到期 / 款项）去掉：每个 chip 自己就写着「7天内到期」「租金欠费」，不必再套一层；
       · chip 字号 12→11、内边距压一档；
       · 入口按钮跟最后一行同排，不再单独占一行。 */
  .risk-group > em { display: none; }
  .risk-chip { font-size: 11px; padding: 3px 7px; }
  .risk-more { font-size: 11px; padding: 4px 8px; }
}

/* ==========================================================================
   18. 房态工作台 · 视觉收敛 v2（2026-11-01）
   --------------------------------------------------------------------------
   本节只做「看得见」的事：颜色权限、楼层信息、悬停预览、命中落点、不可签原因。
   不改房态判定、不改飞书字段、不改任何写入路径；要回退，删掉本节整段即可。

   与前序决策的关系（不是推翻，是接着做）：
     · D-166「状态条即筛选器」保留 —— 本节不新增第二个筛选入口；
     · D-167「看板底压深、白卡浮起」保留 —— 空房仍是纯白、不上彩、不挂绿条。
       189 间里 143 间空房，给空房都加绿色状态条等于把「白板」换成「绿条纹板」，
       比原来的彩色墙更花。空房只靠「无色 + 房号压暗」退后，悬停时才露绿。
     · 本节新增的四项：导航压深、楼层头出租率与折叠、悬停一眼预览、搜索命中落点。
   ========================================================================== */

/* --- 18.1 侧导航：深墨蓝 -----------------------------------------------------
   原来导航与内容同为白底，只靠一条 1px 线分界，1440 下像「一整块白板」。
   压深之后内容区自己浮起来，层级不用再靠阴影堆。
   注意：这里全部用 .aside 前缀限定，避免和页面内其它 .nav 复用类互相污染。 */
.aside {
  background: #0F1626;
  border-right: 0;
  padding: 18px 12px 14px;
}
.aside .brand-name { color: #FFFFFF; }
.aside .brand-sub { color: #7C8BA3; }
.aside .brand-icon { box-shadow: 0 1px 2px rgba(0, 0, 0, .34); }

/* 【A 级改造 · 深底小字对比度】侧栏 18.1 压成 #0F1626 之后，这三处小字仍是按白底挑的
   中间灰，压在深底上全部不达 AA：分组标题 #55647B 3.01:1、工作区卡 kicker #66748A 3.81:1、
   构建行 #4C5768 只有 2.47:1（几乎读不出来）。
   注意：改上面的 --muted-3 → --muted-2 对这三处**无效**——`.aside .product-nav-heading`
   这类选择器特异性更高且写死了色值，令牌根本进不来。只能在覆盖处直接改。
   深底上的"淡"方向是**变亮**，与白底相反，所以这里不能用灰阶令牌，给独立色值：
     #8A97AB 6.10:1（分组标题、kicker，11px/700 需要更高对比）
     #7E8CA1 5.39:1（构建行，允许比标题再淡一档但仍达标） */
.aside .product-nav-heading { color: #8A97AB; }

.aside .nav { color: #B6C1D2; }
.aside .nav:hover { background: rgba(255, 255, 255, .065); color: #FFFFFF; }
.aside .nav.active { background: rgba(255, 255, 255, .095); color: #FFFFFF; }
/* 深底上的品牌蓝 #345FD8 会「陷进去」，选中条提亮一档才认得出是「当前页」 */
.aside .nav.active::before { background: #6E8CF5; }
.aside .nav .ic { opacity: .86; }
.aside .nav.active .ic { opacity: 1; }

/* 侧栏底部工作区卡：从「白底浅框」改成深底上的一层薄玻璃 */
.aside-footer { border-color: rgba(255, 255, 255, .085); background: rgba(255, 255, 255, .035); }
.aside-footer .small-label { color: #8A97AB; }
.aside-footer p { color: #D6DEEA; }
.aside-footnote { color: #7E8CA1; }

/* --- 18.2 楼层头：出租率 + 折叠 ---------------------------------------------
   原来楼层头只有「南栋 · 1楼   20间房」，看完不知道这层住得怎么样，
   只能一层层数卡片。现在把「这一层多少间、租了几间、几成」一次说清，
   并且可以收起——189 间铺满一屏时，先看层、再展开要动的那层。 */
.floor-heading {
  display: flex; align-items: center; gap: 8px;
  margin: 0 0 6px; padding: 2px 4px 2px 2px;
  border-radius: var(--radius-xs);
  /* 【2026-11-01 G-42】去掉出租率之后，层头只剩「箭头 + 楼栋·楼层 + 间数」三个元素。
     原来靠 justify-content:space-between 把左右两边撑开，现在少了一整块，
     撑开的结果是楼栋名飘到中间、看着像标题居中。改成靠左排列，间数用 margin-left:auto 顶到右边，
     和改造前的位置一致（左边楼层、右边数字），只是数字从「已租 6/21·29%」变成「21 间」。 */
  justify-content: flex-start;
}
.fh-count { margin-left: auto; }
.floor-heading[data-floor-toggle] { cursor: pointer; user-select: none; }
.floor-heading[data-floor-toggle]:hover { background: rgba(24, 45, 70, .04); }
.floor-heading[data-floor-toggle]:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }

/* 折叠箭头：展开朝下，收起朝右。用 CSS 画，不额外加图标资源 */
.fh-caret {
  flex: 0 0 auto; width: 0; height: 0; margin-left: 2px;
  border-left: 4.5px solid #8A99AD;
  border-top: 4px solid transparent; border-bottom: 4px solid transparent;
  transition: transform .15s;
}
.floor-heading[aria-expanded="true"] .fh-caret { transform: rotate(90deg); }

.floor-heading strong { font-size: 12px; letter-spacing: .2px; flex: 0 0 auto; }
.fh-count { font-size: 11px; color: var(--muted-2); flex: 0 0 auto; }
.fh-sub { color: var(--muted-3); }
.fh-grow { flex: 1 1 auto; min-width: 8px; }

/* 进度条只表达这一层的出租比例，不承载其它含义 */
.fh-bar {
  flex: 0 0 auto; width: 90px; height: 6px; border-radius: 3px;
  background: var(--line); overflow: hidden;
}
.fh-bar > i { display: block; height: 100%; border-radius: 3px; background: var(--brand); }
.fh-rate { flex: 0 0 auto; font-size: 11px; color: var(--ink-2); font-variant-numeric: tabular-nums; }
/* 一层完全没有可出租资产（全是功能房）时不画条，避免出现 0% 的假信号 */
.fh-bar.is-na, .fh-rate.is-na { display: none; }

.floor-unit.is-collapsed .room-cards { display: none; }
/* 收起后仍留一行摘要，点击即展开；不把整块藏成空白 */
.floor-unit.is-collapsed .floor-heading { margin-bottom: 0; }

@media (max-width: 760px) {
  .fh-bar { width: 54px; }
  .fh-count { display: none; }
  .fh-rate { font-size: 10.5px; }
}

/* --- 18.3 房卡悬停预览 -------------------------------------------------------
   悬停 400ms 出轻量卡：企业、租期、房态原因、面积朝向、网络。
   只在指针设备上出现（触屏没有「悬停」），所以放在 (hover:hover) 里。
   浮层内容全部来自已加载数据，不为此额外请求接口——189 张卡各发一次请求会拖垮看板。 */
.room-peek {
  position: fixed; z-index: 1400; width: 264px; max-width: calc(100vw - 16px);
  padding: 11px 12px; border-radius: var(--radius); pointer-events: none;
  background: var(--surface); border: 1px solid var(--line);
  box-shadow: var(--shadow-3);
  font-size: 12px; color: var(--ink-2); line-height: 1.6;
  opacity: 0; transform: translateY(3px); transition: opacity .12s, transform .12s;
}
.room-peek.show { opacity: 1; transform: none; }
.room-peek .pk-head { display: flex; align-items: baseline; gap: 7px; margin-bottom: 5px; }
.room-peek .pk-id { font-size: 15px; font-weight: 700; color: var(--ink); }
.room-peek .pk-state { font-size: 11px; font-weight: 600; color: var(--muted); }
.room-peek .pk-row { display: flex; gap: 8px; }
.room-peek .pk-row .k { flex: 0 0 auto; color: var(--muted-2); }
.room-peek .pk-row .v { flex: 1 1 auto; min-width: 0; color: var(--ink-2); overflow-wrap: anywhere; }
.room-peek .pk-hint { margin-top: 6px; padding-top: 6px; border-top: 1px dashed var(--line-2); font-size: 11px; color: var(--muted-3); }
@media (hover: none) { .room-peek { display: none !important; } }

/* --- 18.4 搜索命中落点 -------------------------------------------------------
   原来搜索只把不匹配的卡压淡（.muted），命中那间和别人长得一模一样，
   眼睛还得自己找。命中唯一时给一次描边闪动，落点自己跳出来。 */
.room.is-hit {
  animation: roomHit 1.2s ease-out 2;
  border-color: var(--brand);
  box-shadow: 0 0 0 2px rgba(52, 95, 216, .22);
}
@keyframes roomHit {
  0%, 100% { box-shadow: 0 0 0 2px rgba(52, 95, 216, .18); }
  50% { box-shadow: 0 0 0 4px rgba(52, 95, 216, .34); }
}
@media (prefers-reduced-motion: reduce) {
  .room.is-hit { animation: none; box-shadow: 0 0 0 2px rgba(52, 95, 216, .28); }
}

/* --- 18.5 不可签原因 ---------------------------------------------------------
   「办理企业合同」按钮在不满足条件时直接不渲染（.room-action-grid 里本来就这么做），
   用户看不到按钮也不知道为什么。补一行说明，把原因写在按钮原本该在的地方。 */
.room-block-reason {
  display: flex; gap: 7px; align-items: flex-start;
  margin: 0 0 14px; padding: 9px 11px;
  background: var(--surface-3); border-radius: var(--radius-sm);
  font-size: 12px; color: var(--ink-2); line-height: 1.65;
}
.room-block-reason .rb-ico { flex: 0 0 auto; color: var(--muted-2); font-weight: 700; line-height: 1.5; }
.room-block-reason strong { color: var(--ink); font-weight: 600; }
/* 真被拦下（脏房／维修／锁房／有住客没合同）用琥珀，和「到期待交还」同一套告警色；
   功能房／样板间用中性底——它们不是异常，天天都在，跟着变黄会把真该处理的盖掉。 */
.room-block-reason.is-warn {
  background: var(--warn-soft); border: 1px solid var(--warn-line); color: #7A4E0C;
}
.room-block-reason.is-warn .rb-ico { color: var(--warn); }
.room-block-reason.is-warn strong { color: #7A4E0C; }
.room-block-reason.is-soft { border: 1px solid var(--line-2); }

/* --- 18.6 抽屉资料时间线 -----------------------------------------------------
   只排已有日期（合同起止、住客入住、网络安装），每个节点标来源表。
   竖线 + 圆点，让「先后顺序」一眼看得出来；最新的一条在最上面。 */
.rd-timeline { position: relative; margin: 0 0 16px; }
.rd-timeline::before {
  content: ""; position: absolute; left: 5px; top: 12px; bottom: 8px;
  width: 1px; background: var(--line);
}
.tl-item { position: relative; display: flex; gap: 10px; padding: 8px 0 8px 17px; }
.tl-item::before {
  content: ""; position: absolute; left: 2px; top: 13px;
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--surface); border: 1.5px solid var(--brand-line);
}
/* 最新一条用实心点：时间线从上往下读，起点要能一眼认出来 */
.tl-item:first-child::before { background: var(--brand); border-color: var(--brand); }
.tl-date { flex: 0 0 auto; width: 74px; font-size: 11px; color: var(--muted-2); font-variant-numeric: tabular-nums; padding-top: 1px; }
.tl-body { flex: 1 1 auto; min-width: 0; }
.tl-title { font-size: 12.5px; color: var(--ink); line-height: 1.5; overflow-wrap: anywhere; }
.tl-note { font-size: 11px; color: var(--muted); line-height: 1.6; overflow-wrap: anywhere; }
.tl-src { font-size: 11px; color: var(--muted-2); margin-top: 2px; }
@media (max-width: 760px) {
  .tl-date { width: 64px; font-size: 11px; }
}

/* ==========================================================================
   【2026-11-01 融合 · 顶栏与状态色】
   1) 390 手机视口下整页被撑到 448：根因是 .topbar-right 里
      「等待连接 / 正在等待连接飞书 / 帮助 / 头像」四件在 390 宽放不下，
      把 initial containing block 顶宽了（不是房卡、不是状态芯片，那些在
      overflow-x:auto 的 .room-state-bar 里横滑）。
      处理：窄屏隐藏"连接模式"这串字符串、连接胶囊限宽省略、面包屑让位。
   2) 「已连接飞书」常驻橙色：橙色是警告色，正常连接不该用警告色——
      否则"真的要注意"和"一切正常"长相一样。正常态改中性灰，断开才橙。
   ========================================================================== */
.sync-chip.is-live {
  background: var(--surface-3);
  color: var(--ink-2);
  border-color: var(--line);
}
.sync-chip.is-warn {
  background: var(--warn-soft);
  color: var(--warn);
  border-color: var(--warn-line);
}

@media (max-width: 760px) {
  .topbar { padding: 0 10px; gap: 8px; }
  .topbar .crumb { font-size: 11px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .topbar .crumb .sep { display: none; }
  .topbar-right { gap: 6px; min-width: 0; flex: 0 0 auto; }
  .topbar-right .mode-badge { display: none; }
  .sync-chip { padding: 4px 8px; font-size: 11px; max-width: 124px; overflow: hidden; }
  .sync-chip #liveStatus { display: inline-block; max-width: 92px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: middle; }
  .topbar-right .avatar { width: 26px; height: 26px; font-size: 12px; }
}

/* 【2026-11-01 融合 · 永久属性 vs 临时状态】
   功能房／样板间（.utility/.sample）和 维修房／脏房（.blocked）原来都是"灰底、灰左边线"，
   在 124px 的房卡上几乎同款，只能靠读字区分。
   而这两类在业务上根本不是一回事：前者是这间房**永远不按房间出租**（电房、储物间、样板间），
   后者是**临时不能租、过几天就好**。
   处理：永久属性加斜纹底（像图纸上的"不可用"填充），临时状态保持纯色块。
   只加 background-image，不动已有底色、不动语义色，任何一行删掉都能回到原样。 */
.room.utility,
.room.sample {
  background-image: repeating-linear-gradient(
    135deg,
    rgba(107, 119, 133, .13) 0 5px,
    transparent 5px 11px
  );
}

/* ==========================================================================
   【G-47】房态首页：去说明书化 + 重点上浮（2026-11-01 用户第二次提）
   --------------------------------------------------------------------------
   用户原话：
   「这块的话我觉得需要做一下美化……比如说这个黑色的字体介绍怎么去使用，什么右击房间左击房间，
     首先这个字这个介绍我觉得是不需要的，包括你查看整个系统类似的铭文操作介绍，
     我觉得它这个是不需要的，然后这个整体页面看着提示看不到重点。」

   因此这一块只做两件事：
   ① 把「说明怎么操作」的文字从界面上撤掉（HTML/JS 层已删，这里补样式收尾）；
   ② 把真正要看的「今天有没有事」抬成一等公民，让它和「筛选器」在视觉上分开。
      原来状态条与提醒条都是「白底 + 1px 灰边 + 圆角」，同款容器装不同性质的东西，
      扫过去分不出哪条是数据、哪条是指标。
   ========================================================================== */

/* --- 1. 出租率并入头部工具组 -------------------------------------------------
   它原来单独占一行（说明文字 + 出租率）。说明删掉后，一个数字自己占 33px 太浪费，
   挪进头部右侧与「房态卡片 / 楼层平面 / 导出」并排。 */
.w-header .w-rate { flex: 0 0 auto; margin-left: 2px; }
/* 头部允许换行：窄屏时宁可多一行，也不把出租率挤出视野（G-40 的同一个教训）。 */
.w-header { flex-wrap: wrap; row-gap: 6px; }
/* 已经没有任何地方再用这两块，留着只会让下一个人以为还能填内容 */
.w-subbar { display: none; }

/* --- 2. 提醒条：唯一的「今天要处理」-----------------------------------------
   它是全页唯一带行动指向的区块（哪几间到期、谁欠多少钱），却和状态条长得一样。
   加左侧 3px 暖色描边 + 极淡暖底：既不用红色吓人（欠费是常态，不是事故），
   又能一眼把它从白卡堆里分出来。 */
/* 【A 级改造】这里原来写死 padding:8px 12px 8px 10px、risk-lead 13px，
   把 17 节压缩过的高度又顶回去了（实测仍是两行 71px）。
   收成 4px 8px + 11.5px 后：单行 35px，1440 下 6 个 chip 与「全部待办」同排，
   右侧余量 12px。**不动 border-left 与暖底**——它们才是这条区别于状态条的标识。 */
.rooms-risk {
  border-left: 3px solid var(--warn);
  background: #FFFCF5;
  padding: 4px 8px;
}
.rooms-risk .risk-lead {
  font-size: 11.5px; font-weight: 700; color: var(--ink);
  padding-right: 2px;
}
/* 分组名（合同到期 / 款项）现在整条隐藏，见 17 节：收成一行时必须让出这 112px，
   且每个 chip 自己就写着「7天内到期」「租金欠费」，组名是重复。字号留着备回退。 */
.risk-group > em { font-size: 11px; }

/* --- 3. 状态条：它是筛选器，不是指标卡 --------------------------------------
   原来每个 chip 都是白底 1px 边，8 个并排像一排按钮，看不出哪个是「当前筛选」。
   选中态原本只换蓝底，现在再加一圈内阴影，点下去有「按下去」的实感。 */
.state-chip[aria-pressed="true"] {
  box-shadow: inset 0 0 0 1px var(--brand-line);
}
/* 「可租」是每天最关心的那个数，给它一个权重：数字大一号。
   注意只放大数字不放大文字——放大文字会让 8 个 chip 长短不齐更乱。
   选择器用 data-state-filter（v5-features#renderAuthorityPanel 实际写的属性名），
   不要写成 data-state：那个属性在 DOM 里不存在，写了就是一条永远不生效的样式。 */
.state-chip[data-state-filter="available"] b { font-size: 14px; }

/* --- 4. 楼层区：房态图本身才是主角 -----------------------------------------
   顶部压掉一层之后，楼层卡贴到提醒条下方。给楼层栈一点上留白，
   让「提醒条（今天要看的）」与「房态图（要操作的）」之间有一道呼吸，
   避免两者糊成一片。 */
#page-rooms .floor-stack { padding-top: 2px; }

/* --- 5. 手机端：出租率不换行、提醒条保持两行 ------------------------------- */
@media (max-width: 760px) {
  .w-header .w-rate { margin-left: 0; }
  #page-rooms .floor-stack { padding-top: 0; }
}

/* --- 6. 头部压回一行（G-47 实测第二版）------------------------------------
   第一版把出租率挪进头部之后，8 个状态 chip 换行占满一整行，
   视图切换 / 导出 / 出租率被挤到第二行右端 —— 头部高度反而从 1 行变 2 行，
   第一张房卡从 389px 掉到 412px，等于"去说明文字"省下的高度又还回去了。
   修法不是把工具删掉，而是把状态条这条线上的三样东西压窄到能并排：
   ① chip 左右内距 11→9、字号 13→12.5、间距 4→3；
   ② 分组间隔条也收窄。
   1440 实测：8 个 chip 640px + 右侧工具组 372px = 1012px < 容器 1050px。
   窄屏仍由 .w-header{flex-wrap:wrap} 兜底换行，不会溢出。 */
.w-header { gap: 8px; padding: 6px 12px; }
.room-state-bar { gap: 3px; }
.state-chip { padding: 3px 9px 3px 8px; font-size: 12.5px; min-height: 25px; }
.state-chip b { font-size: 12.5px; }
.state-chip[data-state-filter="available"] b { font-size: 13.5px; }
.w-header .board-view-switch { gap: 2px; }
.w-header .board-view-switch button { padding: 4px 9px; font-size: 12px; }
.w-header .room-export-tools { gap: 4px; }

/* 出租率在工具栏里：与「同步 / 数据」并排，右边对齐，不参与状态条那行的空间争夺。 */
.room-toolbar-actions .w-rate { margin-left: 4px; }
.room-toolbar-actions .w-rate .rate-details > summary { padding: 4px 9px; font-size: 12px; }

/* 提醒条再压一档色：第一版 #FFFCF5 在 1440 屏上几乎与白卡同色，
   截图里左色条也淡到看不见——等于"抬成一等公民"只抬了 DOM，没抬视觉。
   现在底色 #FFF7E6、左色条改用实体琥珀，才真正从一屏白卡里跳出来。 */
.rooms-risk {
  background: #FFF7E6;
  border-color: #F2DDB4;
  border-left: 3px solid #E0A441;
}
.rooms-risk .risk-chip { background: #fff; }
.rooms-risk .risk-chip.red { background: #FDECE9; }
.rooms-risk .risk-chip.amber { background: #FFF3DC; }
.rooms-risk .risk-chip.blue { background: #EAF1FE; }
.rooms-risk .risk-chip.ok { background: #E9F7F1; }

/* ==========================================================================
   【G-48】账务管理：让人一眼看出"该关注什么"
   --------------------------------------------------------------------------
   用户原话：
   「账务管理这个模块，观感特别差，我不知道我要看什么内容，什么数据值得我去关注，
     然后我的谁付了钱、不付了钱，我看的不是很清晰……每个房间应收实收这个框架
     这些东西我都是能接受的，但整体感官上不是特别 OK。」
   所以不动表格结构（应收/实收/欠费那一套用户认可），只重排"读完表格之前"的那一段。
   ========================================================================== */

/* --- 1. 四张卡分主次：欠费是行动项，其他是背景数 --------------------------
   原来四张卡同宽同色，只在数字颜色上有差别，扫过去四张一样重，
   用户自然不知道该看哪张。现在欠费卡：加宽一档、加左边框、标题加粗、可点。 */
.pg-stat.is-lead {
  position: relative;
  border-left: 3px solid var(--danger-line, #E0B4AE);
  background: linear-gradient(180deg, #FFF9F8, #FFFFFF);
  cursor: pointer;
  transition: box-shadow .12s, border-color .12s;
}
.pg-stat.is-lead:hover { box-shadow: 0 2px 10px rgba(166, 76, 69, .12); border-color: var(--danger-line, #E0B4AE); }
.pg-stat.is-lead:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.pg-stat.is-lead b { font-size: 25px; }
.pg-stat.is-lead > span { color: var(--ink-2); }
/* 「看是谁欠 →」放在卡右下角，是这张卡存在的理由 */
.pg-stat-go {
  display: block; margin-top: 6px;
  font-style: normal; font-size: 11.5px; font-weight: 700;
  color: var(--danger);
}
/* 实收卡用绿：它回答"钱进来了多少"，是欠费卡的对立面，用它俩做对比。 */
.pg-stat.ok { border-left: 3px solid #BFE0D2; }
.pg-stat.ok b { color: #1C7F5B; }

/* --- 2. 页签分组标签 ------------------------------------------------------
   「日常」与「账本」只是分组名，不可点、不参与筛选，所以做成灰色小字，
   而不是按钮 —— 做成按钮会让人去点，点了没反应更糟。 */
.pg-tabs { align-items: stretch; flex-wrap: wrap; row-gap: 0; }
.pg-tabs-group {
  display: inline-flex; align-items: center;
  font-size: 11px; font-weight: 700; letter-spacing: .5px;
  /* 【2026-11-03】原 var(--muted-3)=#6B7785，压在页面底色 --bg #F5F7FB 上实测 4.25:1 不达标。
     --muted-3 的注释写明它「不承载可读性」，而「日常 / 账本」是分组标题文字，属于误用；
     改挂 --muted-2（#64707E）后 4.71:1。 */
  color: var(--muted-2); padding: 0 4px 0 10px;
  border-right: 1px solid var(--line); margin: 8px 2px 8px 0;
  user-select: none;
}
.pg-tabs-group:first-child { padding-left: 2px; }

/* --- 3. 表头 ⓘ：口径移到需要时才看的地方 --------------------------------- */
.pg-th-help { cursor: help; border-bottom: 1px dotted var(--line-strong); }

/* 合计行：让表格自己给出总数，和顶部卡片当场对上，不用人手工加。
   tfoot 贴底、底色比表头浅一档，视觉上"收口"而不是又起一行数据。 */
.pg-table tfoot .pg-total td {
  background: var(--surface-2);
  border-top: 1px solid var(--line-strong);
  font-weight: 700; font-size: 12.5px; color: var(--ink);
  padding: 10px 12px;
}
.pg-table tfoot .pg-total td:first-child { font-weight: 700; }

/* ==========================================================================
   G-50 手机端房卡状态字：4 字词被截成「暂不…」
   --------------------------------------------------------------------------
   实测（390 宽，3 列网格）：卡宽 107px，卡头 95px = 房号 42 + 间距 4 + 胶囊 49。
   胶囊内容区只有 49-10(padding)-2(border)=37px，而「暂不可租」4 个 10px 汉字要 40px，
   差 3px 就被 text-overflow 吃掉最后半个字。
   不是放着不管就行：手机上没有 hover，title 看不到，被截掉就等于这个状态不存在了。
   处理办法是把 3px 从"不重要的地方"挪过来，而不是缩字号（10px 已经是中文可读下限）：
     · 卡头间距 4 → 3（+1px）
     · 胶囊左右内距 5 → 3（+4px）
     · 字距 -.2px（4 字共省 0.8px，防止"功能间/样板间"这类 3 字词在 360 宽又差一点）
   合计多出 5.8px 余量，360 窄屏也够。房号字号一个字都不动 —— 它是卡的唯一标识。
   ========================================================================== */
@media (max-width: 760px) {
  .room.room-card .card-head { gap: 3px; }
  .room.room-card .state-pill { padding: 0 3px; }
  .room.room-card .card-head .pill-t { letter-spacing: -.2px; }
}

/* G-51 修：`hidden` 属性和 `display:flex` 打架
   --------------------------------------------------------------------------
   .v5-banner-status 写了 display:flex，浏览器对 [hidden] 的默认样式是 display:none，
   但作者样式优先级更高 —— 于是 el.hidden=true 只把文字清空，那条灰底圆点的空壳还占着一行。
   合同页页顶那条空绿条就是这么来的。全站统一补一条：只要带 hidden 就一定不渲染。
   这不是只修一个元素，而是把 [hidden] 恢复成它本来该有的语义。 */
[hidden] { display: none !important; }

/* 【已失效，勿参考】G-54 曾在这里加过一条 `.room.room-card .wifi { display:none }`（≤760px），
   用来挡住 20px 绿色网络徽标压住「至 2026-11-26」最后一位的问题。
   后来房卡改版把徽标换成了 8px 的 `.net`，并且**用 flex 排版让它进入文档流**
   （见上面 L2 那段注释：绝对定位的点一定会压住长文本尾部，flex 才不会），
   手机端另有规则不画这个点。本条已删——同一件事有两个开关，下一个人必然改错一个。
   要判断网络标现在怎么显示，只看 `.room.room-card .net` 那几条规则，不要找 .wifi。 */

/* ============================================================
 * 【D-183 / B5】构建号浮标是调试信息，不得侵占主操作区
 * 病史：d191 之前浮标在右下角（right:10px），实测把抽屉底部动作条最右那颗
 * 「更多操作」（右边界 1426px）压住；d192 把它挪到左下角，桌面端的压按钮问题
 * 因此消失，但**病根没除**——它仍是 position:fixed + z-index:2147483000 的常驻层：
 *   · 手机 390 宽时抽屉是全宽的，浮标正好压住动作条最左两颗「登记物品 / 登记网络」
 *     （实测：还原成常驻显示后命中 2 颗按钮）；
 *   · 窄屏上它还会盖住房卡右下角。
 * 处置两条，都不改业务层、不动布局：
 *   ① 抽屉打开时隐藏（抽屉动作条与它抢同一块地方）；
 *   ② 窄屏（手机）不显示——手机每一像素都该给业务。
 * 桌面常驻时实测 900/800/760/700/660 五个高度都不压任何文字与按钮
 * （浮标 867–890，侧栏说明文字 826–845），所以侧栏布局一个字不动。
 * ============================================================ */
body.drawer-open #pmsBuildBadge { display: none !important; }
@media (max-width: 760px) { #pmsBuildBadge { display: none !important; } }
/* 【B13·2026-11-01】上面两条只解决了「抽屉打开」和「手机」，漏了**窄侧栏**：
   ≤1100px 时侧栏只有 76px（761–1100）或 64px，而浮标文案「构建 20261101-d198」
   内容宽约 126px，从 left:10 起算右边界 ≈136px，横向溢出 60–72px 压到房卡上
   （实测压住南207 / 南202 的上沿）。浮标是调试信息，窄侧栏下直接不显示——
   与「手机每一像素都该给业务」同一口径，不去调字号硬挤。 */
@media (max-width: 1100px) { #pmsBuildBadge { display: none !important; } }

/* 【A 级改造 · 补完 B5】B5 的实测口径是"压不压文字"，所以结论是"侧栏布局一个字不动"。
   但浮标压的不只是文字，还有**底卡的盒边界**：侧栏底卡盒底在 886，浮标占 867–890，
   横跨卡片的圆角底边，一半在卡内一半在卡外 —— 文字（826–845）确实让开了，
   可视觉上仍是一个没放完的元素骑在卡片边上，这正是"看着不整齐"的来源。
   侧栏底部整体让出浮标高度即可；因为浮标与底卡都锚在视口底，这个间距与视口高度无关
   （900/800/700 实测都留 13px）。只作用桌面：手机与抽屉打开时浮标本就 display:none。
   同时把底卡自己的 padding-bottom 从 34 收回 10 —— 那段留白原本是用来让文字的，
   现在整块卡都让开了，再留 34px 会在卡内堆出一截空底。 */
@media (min-width: 761px) {
  .aside { padding-bottom: 46px; }
  .aside-footer { padding-bottom: 10px; }
}

/* 【D-183 / B4 补】数据连接四张卡的状态胶囊不许被压成竖排。
 * 第四张卡的正常态文案是「运行中（密钥不在网页）」（11 字），390 宽时 flex 默认
 * 会把胶囊压成 3 行——「已 / 接 / 入」这种读起来像乱码。
 * 窄屏改成两行网格：第一行「图标 + 标题/说明」，第二行整行给胶囊靠右。
 * 胶囊不缩、不换行；桌面端布局完全不变（只在 ≤760px 生效）。 */
.connection .pill { flex: 0 0 auto; white-space: nowrap; }
@media (max-width: 760px) {
  .connection { display: grid; grid-template-columns: 36px minmax(0, 1fr); align-items: start; }
  .connection .ico { grid-column: 1; grid-row: 1; }
  .connection > div { grid-column: 2; grid-row: 1; }
  .connection .pill { grid-column: 1 / -1; grid-row: 2; justify-self: end; margin-left: 0 !important; margin-top: 8px; }
}

/* 【2026-11-03 M2】设置页「又称东湾或者荷馨苑 / 正式运营日期 / 含公摊面积」等 12 处说明文字
   由 v5-features.js 以**内联 style="color:#888"** 输出，CSS 类选择器赢不了内联样式，
   只能用 !important 覆盖。实测 #888888 on #FFFFFF = 3.54:1，是全站唯一没接 token 的灰。
   根因修法应删掉 v5-features.js 里那句内联 color，此处作为 CSS 侧兜底。 */
.apartment-note { color: var(--muted-2) !important; }

/* 【2026-11-03】报表筛选的「资产类报表不适用」置灰用的是 index.html 内联 <style> 里的
   .rpt-f.is-muted{opacity:.42}。内联块在 pms-product.css 之后加载，同权重选择器赢不了它，
   只能 !important 覆盖。opacity 会把 label 文字压到 2.2:1，这里改成只降颜色不降透明度，
   保留「该筛选不适用」的视觉弱化，同时保证读得出来。
   根因修法：把 index.html:112 那句 opacity:.42 换成 color:#64707E（index.html 归 team-lead 改）。 */
.rpt-f.is-muted { opacity: 1 !important; color: #64707E; }
.rpt-f.is-muted select { opacity: 1 !important; color: #64707E; }

/* ============================================================
   【2026-11-03 移动端 390 视口实测修复】
   ============================================================ */
@media (max-width: 430px) {
  /* C3 状态条：390 宽下 6 个 chip 合计 539px、容器只有 340px，
     overflow-x:auto + scrollbar-width:none 导致溢出 214px 且没有任何横滑提示，
     「到期未退0」「非出租用途13」整颗落在视口外 —— 后者是房态图上
     筛选「功能房/样板间」的唯一入口，等于不可达。这里改成换行。
     注意：≤760px 原有「状态条独占一行可横滑」是 G-40 的刻意取舍，
     但实测该取舍在 390 宽下代价是「筛选条件不可见」，故在 ≤430 收窄为换行。 */
  .room-state-bar { flex-wrap: wrap; overflow-x: visible; row-gap: 6px; }
  .room-state-bar::-webkit-scrollbar { display: block; }

  /* M6 表单字号：iOS Safari 对 font-size < 16px 的输入框会在聚焦时强制放大视口，
     整页被推歪且要手动捏回。表单 28 个字段实测 12–13px，全部命中。
     只抬移动断点，桌面保持 13px 的紧凑排版。 */
  .v5-field input,
  .v5-field select,
  .v5-field textarea { font-size: 16px; }

  /* 附件区的 5 个 file input 单独一条：css:2140 的
     `.v63-upload-actions input[type=file]{font-size:12px}` 是 (0,2,1)，
     权重高于 `.v5-field input`(0,1,1)，上面那条压不住它。
     5 个附件组（合同PDF/付款凭证/租金发票/押金收据/水电费记录）正是
     表单里剩下的 5 个 12px 字段。媒体查询在后面，同权重后者胜。 */
  .v63-upload-actions input[type=file] { font-size: 16px; }

  /* 第 2 步「选择承租房间」的筛选工具条（房号搜索 + 楼栋 + 楼层）不属于 .v5-field，
     实测仍是 13px。其中 contractRoomSearch 是 type=search 的真实文本框，
     iOS 聚焦同样会放大视口。 */
  .picker-toolbar input,
  .picker-toolbar select { font-size: 16px; }
}

/* D219: release pending is an amber reminder; room remains rentable. */
.release-pending-badge{background:#fff4d6;color:#7a5200;border-color:#e9c46a;}
