/* ==========================================================================
   ui-refresh.css — 初见民宿酒店管理系统 · 统一组件层（叠加覆盖）
   --------------------------------------------------------------------------
   定位：**只做视觉统一 + 移动端可用性修复**，不碰任何业务逻辑。
   加载顺序必须在 style.css 之后（同优先级靠"后来者胜"覆盖旧样式）：
       <link rel="stylesheet" href="/css/style.css">
       <link rel="stylesheet" href="/css/tokens.css">
       <link rel="stylesheet" href="/css/ui-refresh.css">

   回滚方式：删掉 index.html 里新增的这两个 <link>。
   style.css 保持原样未改，随时可回到改版前。

   本文件**不改动**任何业务语义：
     · 房态四色（空房干净/待打扫/入住中/维修中）保持原色相，仅换成令牌
     · 渠道三色（美团黄/抖音蓝/线下绿）保持原色相，仅换成令牌
     · 财务 欠款=红 / 应退=绿 / 收入=绿 / 支出=红，方向一律不反转

   已知限制：index.html 里有 360 处内联 style="..."，内联样式优先级高于本文件的
   类选择器，被内联写死的地方本轮不会变（需后续单独清理，不影响功能）。
   ========================================================================== */

/* ==========================================================================
   0. 旧变量重定向 —— 把 style.css 里的三套蓝收敛成一套
      （--primary 原 #4f7cff；页面里硬编码的 #1677ff / #1d6fd1 在下方逐类替换）
   ========================================================================== */
:root {
  --primary:        var(--brand);
  --primary-dark:   var(--brand-strong);
  --success:        var(--c-success);
  --warning:        var(--c-warning);
  --danger:         var(--c-danger);
  --bg:             var(--canvas);
  --card-bg:        var(--surface);
  --text:           var(--ink-1);
  --text-secondary: var(--ink-2);
  --border:         var(--line);
  --radius:         var(--r-md);
  --shadow:         var(--sh-1);

  /* 侧边栏 200 → 220px（.sidebar 宽度与 .main 的 margin-left 都吃这个变量，
     改一处即可，不会错位） */
  --sidebar-w: 220px;
}

/* 必须保留：手机端侧边栏是隐藏的，若不重置，.main 会白白留出 220px */
@media (max-width: 768px) {
  :root { --sidebar-w: 0px; }
}

/* ==========================================================================
   1. 基础排版
   ========================================================================== */
body {
  background: var(--canvas);
  color: var(--ink-1);
  line-height: 1.5;
}

/* 日历格子 / 胶囊 / 徽章等"高密度信息区"行高压回紧凑值，
   避免 line-height:1.5 把固定高度（44/46px）的格子撑破 */
.cal-cell, .cal-cell-content, .cal-cell-body, .cal-guest,
.cal-room-no, .cal-room-type, .cal-weekday, .cal-day,
.pill, .lg-pill, .cal-debt-tag, .cal-src-tag, .src-badge,
.badge, .room-status, .note-chip, .vip-tag, .gm-vip,
.fin-bar-value, .fin-bar-label, .debt-chip, .rent-reminder-chip,
.room-card, .stat-card {
  line-height: 1.25;
}

/* style.css 里唯一的 300 字重，收进 400/500/600/700 四档 */
.cal-plus { font-weight: 400; }

/* ==========================================================================
   2. 按钮 —— 统一高度 / 圆角 / 字重 / 四态
   ========================================================================== */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  min-height: 40px;
  padding: 0 var(--sp-4);
  border: 1px solid transparent;
  border-radius: var(--r-md);
  font-size: var(--fs-base);
  font-weight: 600;
  line-height: 1.2;
  white-space: nowrap;
  transition:
    background-color var(--dur) var(--ease),
    border-color     var(--dur) var(--ease),
    color            var(--dur) var(--ease),
    box-shadow       var(--dur) var(--ease),
    transform        var(--dur-fast) var(--ease),
    opacity          var(--dur) var(--ease);
}
.btn-sm { min-height: 34px; padding: 0 var(--sp-3); font-size: var(--fs-sm); }
.btn-lg { min-height: 48px; padding: 0 var(--sp-6); font-size: var(--fs-lg); }
/* 房间详情弹窗内的紧凑操作按钮（2026-09-12 老板要求缩小） */
.btn-xs { min-height: 30px; padding: 0 12px; font-size: 13px; border-radius: 8px; font-weight: 500; }
/* 弹窗内功能区虚线框分区 + 按钮按文字自适应宽度（2026-09-12 老板要求） */
.zone-box { border: 1.5px dashed #cdd6e4; border-radius: 10px; padding: 10px 12px; margin-top: 10px; }
.zone-label { font-size: 12px; font-weight: 700; color: #98a2b3; letter-spacing: .04em; margin-bottom: 8px; display: flex; align-items: center; gap: 8px; }
.zone-label::after { content: ''; flex: 1; height: 1px; background: linear-gradient(90deg, #e3e8f0, transparent); }
.btn-row { display: flex; flex-wrap: wrap; gap: 6px; }
.btn-row .btn-xs { flex: 0 1 auto; }

/* —— 主色 —— */
.btn-primary { background: var(--brand); border-color: var(--brand); color: #fff; }
.btn-primary:hover  { background: var(--brand-strong); border-color: var(--brand-strong); }
.btn-primary:active { transform: translateY(1px); }

/* —— 成功：原来是 :hover{opacity:.9}，统一成换背景 —— */
.btn-success { background: var(--c-success); border-color: var(--c-success); color: #fff; }
.btn-success:hover  { background: #12803A; border-color: #12803A; }
.btn-success:active { transform: translateY(1px); }

/* —— 警告：原来完全没有 :hover —— */
.btn-warning { background: var(--c-warning); border-color: var(--c-warning); color: #fff; }
.btn-warning:hover  { background: #B45309; border-color: #B45309; }
.btn-warning:active { transform: translateY(1px); }

/* —— 危险：原来是 :hover{opacity:.9}，统一成换背景 —— */
.btn-danger { background: var(--c-danger); border-color: var(--c-danger); color: #fff; }
.btn-danger:hover  { background: var(--c-danger-ink); border-color: var(--c-danger-ink); }
.btn-danger:active { transform: translateY(1px); }

/* —— 次要 —— */
.btn-default { background: var(--surface); border-color: var(--line-strong); color: var(--ink-1); }
.btn-default:hover  { background: var(--canvas); border-color: #C6CDDA; }
.btn-default:active { transform: translateY(1px); }

/* —— 禁用态：hover 时也不许换背景 —— */
.btn:disabled, .btn[disabled] { opacity: .5; cursor: not-allowed; transform: none; box-shadow: none; }
.btn:disabled:hover { transform: none; box-shadow: none; }
.btn-primary:disabled:hover { background: var(--brand); }
.btn-success:disabled:hover { background: var(--c-success); }
.btn-warning:disabled:hover { background: var(--c-warning); }
.btn-danger:disabled:hover  { background: var(--c-danger); }
.btn-default:disabled:hover { background: var(--surface); }

/* 财务周期切换按钮（不是 .btn，但视觉上要和按钮一致） */
.fin-period-btn {
  min-height: 34px;
  padding: 0 var(--sp-3);
  border-radius: var(--r-md);
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: var(--ink-1);
  font-weight: 600;
  transition: background-color var(--dur) var(--ease), border-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.fin-period-btn:hover { border-color: var(--brand); color: var(--brand); }
.fin-period-btn.active { background: var(--brand); border-color: var(--brand); color: #fff; }

/* ==========================================================================
   3. 卡片 —— 背景 / 圆角 / 阴影 / 极浅描边
   ========================================================================== */
.stat-card,
.room-group,
.board-top,
.guest-search-bar,
.settlement-panel,
.settings-section,
.fin-chart-section,
.fin-period-bar,
.empty-tip,
.cal-table-wrap,
.table {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-1);
}

.stat-card { padding: var(--sp-5) var(--sp-4); }
.stat-card .num   { font-size: var(--fs-3xl); font-weight: 700; color: var(--ink-1); }
.stat-card .label { font-size: var(--fs-sm); color: var(--ink-2); margin-top: var(--sp-1); }

/* —— .stat-cards 容器：style.css 里**从来没有定义过**（只有 .stats-grid 有网格），
      所以里面的卡片一直是上下堆叠的：三张卡竖着排要吃掉约 270px，
      这就是老板说的"入住次数/总间夜/房费合计占用地方太多"的真正原因。
      这里补上：默认横向等分排成一行 —— 入住记录页、客人档案弹窗都跟着变矮。 */
.stat-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(116px, 1fr));
  gap: var(--sp-3);
  margin-bottom: var(--sp-4);
}

/* 更紧凑的一行汇总条：标签 + 数字同行，整条只有 ~40px 高 */
.stat-cards--inline {
  display: flex;
  align-items: center;
  gap: 0;
  padding: var(--sp-2) var(--sp-4);
  margin-bottom: var(--sp-3);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  box-shadow: var(--sh-1);
}
.stat-cards--inline > .stat-card {
  display: flex;
  align-items: baseline;
  gap: var(--sp-2);
  flex: 1 1 0;
  min-width: 0;
  padding: 0 var(--sp-4);
  background: none;
  border: 0;
  box-shadow: none;
  text-align: left;
  white-space: nowrap;
}
.stat-cards--inline > .stat-card + .stat-card { border-left: 1px solid var(--line); }
.stat-cards--inline > .stat-card:first-child { padding-left: 0; }
.stat-cards--inline > .stat-card:last-child  { padding-right: 0; }
/* 数字在前、标签在后：用 order 换成"标签 数字"，读起来更符合中文习惯 */
.stat-cards--inline .num   { order: 2; font-size: var(--fs-xl); font-variant-numeric: tabular-nums; }
.stat-cards--inline .label { order: 1; margin-top: 0; font-size: var(--fs-xs); }

/* 手机上一条放不下就自动换行，不许横向溢出 */
@media (max-width: 560px) {
  .stat-cards--inline { flex-wrap: wrap; row-gap: 2px; padding: var(--sp-2) var(--sp-3); }
  .stat-cards--inline > .stat-card { flex: 0 0 auto; padding: 0 var(--sp-3); }
  .stat-cards--inline > .stat-card:first-child { padding-left: 0; }
  .stat-cards--inline > .stat-card + .stat-card { border-left: 0; }
}

/* —— 入住记录搜索栏：手机上"房间号 / 渠道 / 日期"折叠起来，
      点「更多筛选 ▼」才展开。默认只留最常用的姓名/手机号，
      搜索栏从 515px 降到约 160px，记录列表直接在首屏露出来。 */
.gsb-more { display: none; }
.gsb-more.is-open { display: block; }
/* 折叠着但里面已经有筛选条件时，按钮高亮，避免老板忘了自己筛过什么 */
.gsb-toggle.is-active {
  border-color: var(--brand);
  color: var(--brand-ink);
  background: var(--brand-soft);
}
@media (min-width: 769px) {
  /* 电脑屏幕够宽：条件永远铺开，不显示折叠按钮 */
  .gsb-more   { display: block !important; }
  .gsb-toggle { display: none   !important; }
}
@media (max-width: 768px) {
  .guest-search-btns { flex-wrap: wrap; }
  .guest-search-btns .btn { flex: 1 1 auto; }
}

/* —— 入住记录搜索栏 · 电脑端（≥769px）紧凑布局 ——
   改前两行：行1 = 输入框 + 搜索/重置按钮；行2 = 4 个筛选。
   改后两行：行1 = 输入框（自适应填满）+ 搜索/重置（右侧紧凑），
            行2 = 4 个筛选等距排开。
   改动小：仅 CSS Grid + 紧凑 padding，HTML 不变，手机端不受影响。 */
@media (min-width: 769px) {
  .guest-search-bar { padding: var(--sp-3) var(--sp-4); display: grid;
                      grid-template-columns: minmax(200px, 1fr) auto;
                      grid-template-areas: "input btns" "more more"; gap: 10px var(--sp-3); }
  .guest-search-bar > .form-row:first-of-type { display: contents; }
  .guest-search-bar > .form-row:first-of-type > .form-group:first-child {
    grid-area: input; margin-right: 0;
  }
  .guest-search-bar > .form-row:first-of-type > .form-group:first-child input { width: 100%; }
  .guest-search-bar > .form-row:first-of-type > .guest-search-btns {
    grid-area: btns; margin-bottom: 0;
  }
  /* 压缩 form-group 高度（电脑端有空间就别堆太多） */
  .guest-search-bar .form-group { margin-bottom: 0; }
  .guest-search-bar .form-group label { margin-bottom: 4px; font-size: var(--fs-xs); }
  /* 第二行 4 个字段等距，宽松间距 */
  .gsb-more > .form-row { gap: var(--sp-4); }
  .guest-search-bar input::placeholder { color: var(--ink-3); }
}

/* —— 入住记录列表：同住人姓名直接显示出来（2026-09-09）
      原来是"等1人"+悬停才看得到名字，手机上没法悬停就等于看不到。
      现在直接显示成"等1人：刘云铮"，名字过长自动截断，鼠标放上去仍能看到完整身份证。 */
.companion-inline {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 6px;
  border-radius: 8px;
  background: var(--c-neutral-soft);
  color: var(--c-neutral-ink);
  font-size: 11px;
  font-weight: 500;
  max-width: 170px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: middle;
}

/* 登记同住人时"手机号"旁边的浅色引导文字：
   提醒前台顺手填电话 —— 同住人下次自己来开房时才能自动带出档案 */
.companion-hint {
  font-weight: 400;
  color: var(--brand-ink);
  font-size: 11px;
}

/* 二级卡片（卡片里的卡片） */
.settlement-panel .settlement-platform-card,
.relay-segment,
.backup-list .backup-item {
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
}
.settlement-platform-card.overdue { border-color: var(--c-warning); background: var(--c-warning-soft); }

/* —— 房间卡片：顶部 4px 色条 = 房态，必须保留 —— */
.room-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-top-width: 4px;
  border-radius: var(--r-lg);
  box-shadow: var(--sh-1);
  transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.room-card:hover { box-shadow: var(--sh-2); }
.room-card.vacant_clean { border-top-color: var(--st-vacant-clean); }   /* 绿：空房干净 */
.room-card.vacant_dirty { border-top-color: var(--st-vacant-dirty); }   /* 黄：待打扫  */
.room-card.occupied     { border-top-color: var(--st-occupied); }       /* 蓝：入住中  */
.room-card.maintenance  { border-top-color: var(--st-maintenance); }    /* 红：维修中  */

/* 入住中卡片底色 = 渠道三色（美团黄 / 抖音蓝 / 线下绿），色相保持不变 */
.room-card.occupied.src-meituan { background: var(--ch-meituan-soft); }
.room-card.occupied.src-douyin  { background: var(--ch-douyin-soft); }
.room-card.occupied.src-offline { background: var(--ch-offline-soft); }

/* ==========================================================================
   4. 徽章 / 状态色 —— 6 套重复定义收敛到同一套令牌
      （只改颜色值，不删任何类名：JS 会拼接这些类名，删了会崩）
   ========================================================================== */

/* ---- 4.1 统一 .badge 变体 ---- */
.badge {
  display: inline-flex;
  align-items: center;
  padding: 2px var(--sp-2);
  border-radius: var(--r-full);
  font-size: var(--fs-xs);
  font-weight: 600;
  line-height: 1.5;
}
.badge-success { background: var(--c-success-soft); color: var(--c-success-ink); }
.badge-warning { background: var(--c-warning-soft); color: var(--c-warning-ink); }
.badge-danger  { background: var(--c-danger-soft);  color: var(--c-danger-ink); }
.badge-info    { background: var(--c-info-soft);    color: var(--c-info-ink); }
.badge-default { background: var(--c-neutral-soft); color: var(--c-neutral-ink); }
/* 新增：中性徽章（旧的 .badge-default 语义太宽，留着兼容，新增 neutral 供后续用） */
.badge-neutral { background: var(--c-neutral-soft); color: var(--c-neutral-ink); }

/* ---- 4.2 房态标签（.tag-*）：同样保持四色可区分 ---- */
.tag-vacant_clean { background: var(--st-vacant-clean-soft); color: var(--c-success-ink); }
.tag-vacant_dirty { background: var(--st-vacant-dirty-soft); color: var(--c-warning-ink); }
.tag-occupied     { background: var(--st-occupied-soft);     color: var(--c-info-ink); }
.tag-maintenance  { background: var(--st-maintenance-soft);  color: var(--c-danger-ink); }

/* ---- 4.3 渠道标签 / 色条 / 日历格子底色：三色保持可区分 ---- */
.src-badge { background: var(--c-neutral-soft); color: var(--c-neutral-ink); }
.src-meituan { background: var(--ch-meituan-soft); color: var(--ch-meituan-ink); }
.src-douyin  { background: var(--ch-douyin-soft);  color: var(--ch-douyin-ink); }
.src-offline { background: var(--ch-offline-soft); color: var(--ch-offline-ink); }

.sb-meituan { background: var(--ch-meituan); }
.sb-douyin  { background: var(--ch-douyin); }
.sb-offline { background: var(--ch-offline); }

.cal-src-tag.cal-src-meituan { background: var(--ch-meituan-soft); color: var(--ch-meituan-ink); border-color: #F3D9B0; }
.cal-src-tag.cal-src-douyin  { background: var(--ch-douyin-soft);  color: var(--ch-douyin-ink);  border-color: #C3D6FF; }
.cal-src-tag.cal-src-offline { background: var(--ch-offline-soft); color: var(--ch-offline-ink); border-color: #B7E5C6; }

.cal-cell.cal-occupied.cal-src-meituan { background: var(--ch-meituan-soft); }
.cal-cell.cal-occupied.cal-src-douyin  { background: var(--ch-douyin-soft); }
.cal-cell.cal-occupied.cal-src-offline { background: var(--ch-offline-soft); }
.cal-cell.cal-reserved.cal-src-meituan { border-left-color: var(--ch-meituan); }
.cal-cell.cal-reserved.cal-src-douyin  { border-left-color: var(--ch-douyin); }
.cal-cell.cal-reserved.cal-src-offline { border-left-color: var(--ch-offline); }

/* ---- Wave 1 / D 新增：微信渠道（客人从服务号自助下单，reservations.source='wechat'）----
   目的只有「看得见」：徽章 / 色条 / 日历格子 / 图例。
   ⚠️ 这里刻意**不新增** .src-opt-wechat —— 渠道选择器（改渠道）的白名单仍是
      meituan/douyin/offline（src/routes/rooms.js:703），本批次不许让老板改到 wechat。 */
.src-wechat { background: var(--ch-wechat-soft); color: var(--ch-wechat-ink); }
.sb-wechat  { background: var(--ch-wechat); }
.cal-src-tag.cal-src-wechat { background: var(--ch-wechat-soft); color: var(--ch-wechat-ink); border-color: #DDD0FB; }
.cal-cell.cal-occupied.cal-src-wechat { background: var(--ch-wechat-soft); }
.cal-cell.cal-reserved.cal-src-wechat { border-left-color: var(--ch-wechat); }

/* ---- 2026-09-30 收款口径修订新增：携程渠道 ----
   预订/补录/修改预订/续住/修改入住/日历点改 全部窗口加携程；后端白名单已含 ctrip。 */
.room-card.occupied.src-ctrip { background: var(--ch-ctrip-soft); }
.src-ctrip { background: var(--ch-ctrip-soft); color: var(--ch-ctrip-ink); }
.sb-ctrip  { background: var(--ch-ctrip); }
.cal-src-tag.cal-src-ctrip { background: var(--ch-ctrip-soft); color: var(--ch-ctrip-ink); border-color: #F9C7DD; }
.cal-cell.cal-occupied.cal-src-ctrip { background: var(--ch-ctrip-soft); }
.cal-cell.cal-reserved.cal-src-ctrip { border-left-color: var(--ch-ctrip); }

.src-opt-ctrip { color: var(--ch-ctrip-ink); }
.src-opt-ctrip:hover { background: var(--ch-ctrip-soft); }

.src-opt-meituan { color: var(--ch-meituan-ink); }
.src-opt-douyin  { color: var(--ch-douyin-ink); }
.src-opt-offline { color: var(--ch-offline-ink); }
.src-opt-meituan:hover { background: var(--ch-meituan-soft); }
.src-opt-douyin:hover  { background: var(--ch-douyin-soft); }
.src-opt-offline:hover { background: var(--ch-offline-soft); }

/* ---- 4.4 欠款三级预警：六套重复定义 → 同一套令牌（红=现付欠款 / 黄=平台待结算 / 蓝=长住挂账）---- */

/* (a) 日历金额胶囊 .pill-* / .lg-pill-*（实心底 + 白字） */
.pill-red,    .lg-pill-red    { background: var(--c-danger);  color: #fff; }
.pill-yellow, .lg-pill-yellow { background: var(--c-warning); color: #fff; }
.pill-blue,   .lg-pill-blue   { background: var(--c-info);    color: #fff; }
.pill-gray,   .lg-pill-gray   { background: var(--c-neutral-soft); color: var(--c-neutral-ink); }
.pill-res { background: var(--surface); color: var(--c-warning-ink); border: 1px solid #F3D9B0; }

/* (b) 房间卡片欠款 .rc-debt-*（文字色） */
.rc-debt-red    { color: var(--c-danger-ink); }
.rc-debt-yellow { color: var(--c-warning-ink); }
.rc-debt-blue   { color: var(--c-info-ink); }
.rc-debt-mode   { background: var(--c-info); color: #fff; border-radius: var(--r-sm); }

/* (c) 日历格子欠款角标 .cal-debt-*（浅底 + 深字 + 描边） */
.cal-debt-red    { background: var(--c-danger-soft);  color: var(--c-danger-ink);  border-color: #F5C2C2; }
.cal-debt-yellow { background: var(--c-warning-soft); color: var(--c-warning-ink); border-color: #F3D9B0; }
.cal-debt-blue   { background: var(--c-info-soft);    color: var(--c-info-ink);    border-color: #C3D6FF; }
.cal-debt-overdue{ background: var(--c-danger); color: #fff; border-color: var(--c-danger); }

/* (d) 详情弹窗内联欠款 .debt-inline-*（文字色） */
.debt-inline-red    { color: var(--c-danger-ink); }
.debt-inline-yellow { color: var(--c-warning-ink); }
.debt-inline-blue   { color: var(--c-info-ink); }

/* (e) 三级汇总条 .debt-sum-*（文字色） */
.debt-sum-red    { color: var(--c-danger-ink); }
.debt-sum-yellow { color: var(--c-warning-ink); }
.debt-sum-blue   { color: var(--c-info-ink); }

/* (f) 看板欠款条 / 催收条 / 账单明细：红=欠款，一律不反转 */
.debt-alert { background: var(--c-danger-soft); border-color: #F5C2C2; border-left-color: var(--c-danger); }
.debt-alert-title, .debt-alert-toggle { color: var(--c-danger-ink); }
.debt-chip { border-color: #F5C2C2; border-radius: var(--r-full); }
.debt-chip:hover { background: var(--c-danger-soft); }
.debt-chip-red { border-color: #F5A9A9; color: var(--c-danger-ink); }
.debt-chip .debt-toggle { color: var(--c-danger-ink); border-left-color: #F5C2C2; }
.debt-detail { border-color: #F5C2C2; }
.debt-detail-head b, .debt-final { color: var(--c-danger-ink); }
.debt-detail-head .debt-final { border-left-color: #F5A9A9; }
.debt-bill-unpaid .debt-bill-amt { color: var(--c-danger-ink); }
.debt-bill-info { background: var(--c-warning-soft); border-color: #F3D9B0; color: var(--c-warning-ink); }
.debt-bill-info b { color: var(--c-danger-ink); }
.debt-bill-pay-method { color: var(--c-info-ink); }
.rent-reminder-bar { background: var(--c-info-soft); border-color: #C3D6FF; border-left-color: var(--c-info); }
.rent-reminder-title { color: var(--c-info-ink); }
.rent-reminder-chip { border-color: #C3D6FF; color: var(--c-info-ink); border-radius: var(--r-full); }
.rent-reminder-chip .rent-reminder-amount { color: var(--c-danger-ink); }
.relay-warn { background: var(--c-danger-soft); border-color: #F5C2C2; color: var(--c-danger-ink); }
.order-alert-bar { background: linear-gradient(135deg, var(--c-danger), var(--c-danger-ink)); }

/* ---- 4.5 其它零散硬编码色 → 令牌 ---- */
.room-card .room-price { color: var(--brand-ink); }
.room-card .rc-action  { color: var(--brand-ink); }
.note-edit-link        { color: var(--brand-ink); }
.link                  { color: var(--brand-ink); }
.room-card .card-edit-guest { color: var(--brand-ink); border-color: #C3D6FF; }
.room-card .card-edit-guest:hover { background: var(--brand); color: #fff; }
.note-inline { background: var(--c-warning-soft); border-color: #F3D9B0; color: var(--c-warning-ink); }
.resv-alert, .room-card .rc-reserve { background: var(--c-warning-soft); border-color: #F3D9B0; }
.batch-bar { background: var(--c-warning-soft); border-color: #F3D9B0; }
.batch-range { color: var(--c-warning-ink); }
.lock-status.online  { background: var(--c-success-soft); color: var(--c-success-ink); border-color: #B7E5C6; }
.lock-status.offline { background: var(--c-danger-soft);  color: var(--c-danger-ink);  border-color: #F5C2C2; }
.login-card .login-error { background: var(--c-danger-soft); color: var(--c-danger-ink); }
/* #1677ff 残留（原第二套蓝） */
.cal-corner-note { background: var(--c-info-soft); color: var(--c-info-ink); border-color: #C3D6FF; }
.cal-corner-note:hover { background: #DCE9FF; }
.gm-item:hover, .gm-item.active { background: var(--brand-soft); }
.split-opt:hover { border-color: var(--brand); }
.split-opt.active { border-color: var(--brand); background: var(--brand-soft); }

/* ==========================================================================
   5. 表格 —— 表头 / 行 hover / 手机首列冻结
   ========================================================================== */
.table th {
  padding: var(--sp-3) var(--sp-4);
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--ink-2);
  background: var(--canvas);
  border-bottom: 1px solid var(--line-strong);
  white-space: nowrap;
}
.table td {
  padding: var(--sp-3) var(--sp-4);
  font-size: var(--fs-base);
  color: var(--ink-1);
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}
.table tr:last-child td { border-bottom: none; }
.table tbody tr:hover td { background: var(--brand-soft); }

/* 日历表（.cal-table 是独立体系，只做表头底色统一，不动冻结逻辑） */
.cal-table th { background: var(--canvas); }

/* 手机端：横向滚动时首列冻结（.table 为 div 包裹层的场景） */
@media (max-width: 768px) {
  div.table { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  div.table > table > thead > tr > th:first-child,
  div.table > table > tbody > tr > td:first-child {
    position: sticky;
    left: 0;
    z-index: 2;
    background: var(--surface);
    box-shadow: 1px 0 0 var(--line);
  }
  div.table > table > thead > tr > th:first-child { background: var(--canvas); z-index: 3; }
  /* 行 hover 时冻结列也要跟着高亮，否则会露出白块 */
  div.table > table > tbody > tr:hover > td:first-child { background: var(--brand-soft); }
}

/* ==========================================================================
   6. 表单 —— 统一高度 / 圆角 / 描边 / 品牌色聚焦环
      注意：排除 radio / checkbox / range / file，否则会把选择框撑成 40px
   ========================================================================== */
input:not([type="radio"]):not([type="checkbox"]):not([type="range"]):not([type="file"]):not([type="color"]),
select,
textarea {
  min-height: 40px;
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  background: var(--surface);
  color: var(--ink-1);
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
textarea { min-height: 76px; padding: var(--sp-3); line-height: 1.5; }
::placeholder { color: var(--ink-3); }

/* :focus 用 box-shadow 描边环（保留可见焦点，不写 outline:none 了事） */
input:not([type="radio"]):not([type="checkbox"]):not([type="range"]):not([type="file"]):not([type="color"]):focus,
select:focus,
textarea:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: var(--focus-ring);
}

/* 支付方式 / 换房选房卡：和表单同一套圆角与选中描边 */
.pay-method { border-radius: var(--r-md); border: 2px solid var(--line-strong); }
.pay-method:hover   { border-color: var(--brand); }
.pay-method.selected{ border-color: var(--brand); background: var(--brand-soft); color: var(--brand-ink); }
.pay-method.selected .pm-sub { color: var(--brand-ink); }
.rc-pick { border-radius: var(--r-md); border: 2px solid var(--line-strong); }
.rc-pick:hover    { border-color: var(--brand); }
.rc-pick.selected { border-color: var(--brand); background: var(--brand-soft); box-shadow: var(--focus-ring); }
.rc-pick-price { color: var(--c-warning-ink); }
.relay-price { color: var(--c-warning-ink); }

/* ==========================================================================
   7. 弹窗
   ========================================================================== */
.modal-overlay { background: rgba(16, 24, 40, .45); }
.modal {
  border-radius: var(--r-xl);
  box-shadow: var(--sh-3);
  max-height: 88vh;
}
.modal-header { padding: var(--sp-4) var(--sp-6); border-bottom: 1px solid var(--line); }
.modal-header h2 { font-size: var(--fs-xl); font-weight: 600; }
.modal-body { padding: var(--sp-5) var(--sp-6); }
.modal-footer {
  padding: var(--sp-3) var(--sp-6);
  gap: var(--sp-3);
  border-top: 1px solid var(--line);
  background: var(--surface);
}
/* 关闭按钮：实打实补到 44×44（视觉只是个 ×，不会变大） */
.modal-close {
  width: var(--tap);
  height: var(--tap);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: none;
  border-radius: var(--r-md);
  background: transparent;
  color: var(--ink-2);
  font-size: var(--fs-3xl);
  line-height: 1;
  transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.modal-close:hover  { background: var(--canvas); color: var(--ink-1); }
.modal-close:active { background: var(--line); }

/* ==========================================================================
   8. 导航
   ========================================================================== */
.sidebar { background: #1B2434; }
.sidebar-logo {
  padding: var(--sp-5) var(--sp-4);
  min-height: 64px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  font-size: var(--fs-xl);
  font-weight: 600;
  border-bottom: 1px solid rgba(255, 255, 255, .08);
}
.sidebar-menu a { padding: var(--sp-3) var(--sp-5); font-size: var(--fs-base); }
.sidebar-menu a.active { background: var(--brand); color: #fff; font-weight: 600; }
.sidebar-user .logout-link { color: #FF8A8A; }

/* 手机底部导航：56px + 安全区，选中态更明显 */
.bottom-nav {
  border-top: 1px solid var(--line);
  box-shadow: 0 -2px 10px rgba(16, 24, 40, .08);
}
.bottom-nav a { position: relative; font-size: 11px; color: var(--ink-2); }
.bottom-nav a.active { color: var(--brand); font-weight: 600; }
.bottom-nav a.active::before {
  content: "";
  position: absolute;
  top: 0; left: 50%;
  transform: translateX(-50%);
  width: 24px; height: 3px;
  border-radius: 0 0 3px 3px;
  background: var(--brand);
}

/* ==========================================================================
   9. 触控命中区补全（手机端）
      手法：视觉大小基本不变，用 ::after 伪元素扩大可点范围
   ========================================================================== */
@media (max-width: 768px) {
  .btn        { min-height: var(--tap); padding: 0 var(--sp-4); }
  .btn-sm     { min-height: var(--tap); padding: 0 var(--sp-3); }
  .btn-lg     { min-height: 48px; }
  .btn-xs     { min-height: 34px; padding: 0 12px; }  /* 详情弹窗紧凑按钮：手机上也保持小，不放大回44 */
  .fin-period-btn { min-height: var(--tap); padding: 0 var(--sp-3); }

  /* 顶栏 / 日历导航的小按钮：补到 44×44（原来只有 ~20×22） */
  .topbar-actions .btn,
  .cal-nav .btn {
    padding: 0 var(--sp-3);
    min-width: var(--tap);
  }

  /* 需要 relative 才能挂伪元素命中区（本来就已定位的不要动它的 position） */
  .note-chip, .fin-nav-btn, .src-opt, .user-btn { position: relative; }

  /* 统一的 44×44 命中区：以元素中心向外扩 */
  .note-chip::after,
  .fin-nav-btn::after,
  .src-opt::after,
  .user-btn::after {
    content: "";
    position: absolute;
    left: 50%; top: 50%;
    width: var(--tap);
    height: var(--tap);
    transform: translate(-50%, -50%);
    border-radius: var(--r-full);
  }

  /* 房间卡片右下角编辑按钮：命中区只向卡片内侧（左上）扩，
     避免伸到相邻卡片上抢走它的点击 */
  .room-card .card-edit-btn::after {
    content: "";
    position: absolute;
    right: 0; bottom: 0;
    width: var(--tap);
    height: var(--tap);
  }

  /* 日历格子里的 📝 备注角标：格子本身只有 46×46，
     再大就会吞掉"点格子开房间详情"的点击，所以只扩到 34×34 */
  .cal-corner-note::after {
    content: "";
    position: absolute;
    left: 50%; top: 50%;
    width: 34px;
    height: 34px;
    transform: translate(-50%, -50%);
    border-radius: var(--r-full);
  }

  /* 弹窗底部按钮吸底 + 安全区 */
  .modal { border-radius: 0; max-height: 100%; width: 100vw; max-width: 100vw; }
  .modal-header { padding: var(--sp-3) var(--sp-4); }
  .modal-body { padding: var(--sp-3) var(--sp-4) var(--sp-5); }
  /* 弹窗内表单两列在手机堆叠为单列，避免输入框被挤压溢出
     （办理入住里的"手机号/身份证号""入住天数/房价"等 form-row） */
  .form-row { flex-direction: column; gap: 0; }
  .form-row .form-group { width: 100%; flex: none; margin-bottom: var(--sp-3); }
  .form-row .form-group:last-child { margin-bottom: 0; }
  .modal-footer {
    position: sticky;
    bottom: 0;
    padding: var(--sp-3) var(--sp-4) calc(var(--sp-3) + env(safe-area-inset-bottom, 0px));
    background: var(--surface);
    box-shadow: 0 -2px 8px rgba(16, 24, 40, .08);
    z-index: 11;
  }

  /* 底部导航安全区 */
  .bottom-nav {
    height: auto;
    min-height: var(--bottomnav-h);
    padding-bottom: env(safe-area-inset-bottom, 0px);
  }
  .main {
    padding-bottom: calc(var(--bottomnav-h) + var(--sp-3) + env(safe-area-inset-bottom, 0px));
  }
}

/* ==========================================================================
   10. 无障碍：全局焦点环 + 降低动效
   ========================================================================== */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
summary:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
}

/* 系统开启"减少动态效果"时，关掉所有过渡/动画 */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    scroll-behavior: auto !important;
  }
}

/* ==========================================================================
   房态看板 + 房态日历 · 手机端瘦身（2026-09-09，老板反馈"上面太多东西"）
   - "应付合计"红字行 折叠（电脑保留）
   - 顶部"房态看板"h1 隐藏（侧栏 nav 已有，重复）
   - 房卡 4 行 压成 2 行
   - 提示行"红=要催收..." 折叠
   - 日历列宽 5 天版
   电脑端 (.cal-revenue-bar / 房卡 4 行 / h1) 完全不动。
   ========================================================================== */
@media (max-width: 640px) {
  /* 顶部"房态看板"h1 已经在 HTML 里加 x-show="!isMobile()"，但保险再写一条 CSS */
  .topbar h1 { display: none; }
  .topbar { margin-bottom: 6px; padding: 4px 8px; min-height: 44px; }
  /* 顶栏 actions 紧凑：去掉 padding，让 3 个圆按钮刚好排开 */
  .topbar-actions { gap: 6px; }
  /* 顶栏的 3 个圆按钮（门锁/铃声/用户）手机上缩小到 32x32，省 16px */
  .lock-status, .user-btn, .topbar-actions .btn { min-height: 32px; }
  .lock-status { padding: 0 8px; font-size: 14px; }
  .user-btn { width: 32px; height: 32px; font-size: 12px; }

  /* 日历：手机端每列宽度（2026-09-13 修正）
     之前这里 min-width:62px 与 style.css 的 min-width:480px 叠加，
     导致 .cal-table 撑到 480px+，整个页面被撑宽 → 老板要左右拖才能看完。
     现改为：表格宽度 100% 自适应视口，只有列数多到装不下时才横滑。 */
  .cal-table { min-width: 0; width: 100%; }
  .cal-date-col { min-width: 44px; }
  /* 微信 X5 内核 sticky/隐藏行的"残影"防呆：
     强制滚动容器独立合成层 + 禁滚动锚定，避免内核把已隐藏的行框
     或 sticky 表头错位绘制成"空白行"（老板截图中导航条下方那条）。 */
  .cal-table-wrap { transform: translateZ(0); overflow-anchor: none; }
  /* 2026-09-12：老板要求"预计收入"行手机端也显示（之前为省高度在手机隐藏）。
     手机端 X5 内核 sticky 已在下方禁用（position:static），无表头错位风险，故直接渲染。 */
  /* 2026-09-11：微信 X5 内核下 sticky 日期表头会错位盖住第一行
     （老板手机上 1-206 行被挡住半截、分组条整个消失）。
     本项目已第二次踩 X5 sticky 坑（第一次是隐藏行残影），手机端
     彻底放弃 sticky 表头：表头随页面一起滚，内容永远不可能被盖住。
     桌面端（min-width:769px）sticky 不受影响。 */
  .cal-table thead tr th {
    position: static !important;
    top: auto !important;
  }

  /* 房态看板视图：房卡从 4 行 压到 2 行
     - 行 1：房号 | 价格 | 状态徽章
     - 行 2：客人名 · 渠道 · 夜数 · 退房日 · 欠款
     电脑端 .room-card 的 4 行布局完全不动 */
  .room-group-cards .room-card {
    display: grid;
    grid-template-columns: auto 1fr auto;
    grid-template-areas: "no  price status" "info info info";
    gap: 2px 6px;
    align-items: center;
    padding: 6px 8px;
    min-height: 56px;
  }
  .room-group-cards .room-card .rc-line1 { display: contents; }
  .room-group-cards .room-card .rc-line1 .room-no {
    grid-area: no; font-size: 16px; font-weight: 700; color: var(--ink-1);
  }
  .room-group-cards .room-card .rc-line1 .room-status { grid-area: status; }
  .room-group-cards .room-card .rc-line2 {
    grid-area: price;
    display: flex; gap: 4px; align-items: center; justify-content: flex-end;
    font-size: 12px; color: var(--brand); font-weight: 700;
  }
  .room-group-cards .room-card .rc-line2 .room-type-small { display: none; }
  .room-group-cards .room-card .rc-line2 .price-base { font-size: 10px; color: var(--ink-3); text-decoration: line-through; font-weight: 400; }
  .room-group-cards .room-card .rc-info {
    grid-area: info;
    margin-top: 0; padding-top: 4px;
    border-top: 1px dashed #f0f0f0;
    display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
    font-size: 11px;
  }
  .room-group-cards .room-card .rc-info .rc-guest {
    display: flex; align-items: center; gap: 4px; flex: 1; min-width: 0;
  }
  .room-group-cards .room-card .rc-info .rc-guest .guest-name {
    font-size: 12px; font-weight: 600; color: var(--ink-1);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .room-group-cards .room-card .rc-info .rc-guest .guest-count,
  .room-group-cards .room-card .rc-info .rc-guest .note-chip { display: none; }
  .room-group-cards .room-card .rc-info .rc-dates { display: flex; gap: 6px; font-size: 11px; color: var(--ink-2); margin-top: 0; }
  .room-group-cards .room-card .rc-info .rc-debt { font-size: 11px; margin-top: 0; }
  .room-group-cards .room-card .rc-info .rc-phone { display: none; }
  .room-group-cards .room-card .rc-reserve { font-size: 11px; }
  .room-group-cards .room-card .rc-action { font-size: 11px; margin-top: 0; }
  .room-group-cards .room-card .card-edit-guest { display: none; }
  /* 房卡右上角的 🔑 card-badge（绝对定位）会和状态徽章（grid-area status）重叠，
     手机上让状态徽章独占右上角，🔑 徽章先隐藏（电脑保留） */
  .room-group-cards .room-card .card-badge { display: none; }
  .room-group-cards .room-card .card-edit-btn { display: none; }

  /* 三级欠款提示"红=要催收·黄=..." 折叠（欠款面板上小图标已经够说明） */
  .debt-sum-note { display: none; }
  /* 三级汇总条上下更紧 */
  .debt-summary-bar { padding: 4px 8px; gap: 6px; font-size: 12px; }

  /* 收租提醒条 / 客人订单条 手机上更紧凑 */
  .rent-reminder-bar { margin-bottom: 6px; }
  .rent-reminder-head { padding: 5px 10px; }
  .rent-reminder-title { font-size: 13px; }
  .rent-reminder-body { padding: 0 8px 6px; }

  /* 房型分组条更紧凑 */
  .room-group { padding: 6px 8px 8px; }
  .room-group-header { margin-bottom: 6px; }
}

/* —— 看板/日历视图切换 + 批修改（合并到一行）2026-09-09 —— */
.view-tabs {
  display: flex; align-items: center; gap: 8px;
  margin-bottom: 10px; flex-wrap: nowrap;
}
.view-tabs .btn { white-space: nowrap; }
/* 批修改按钮在 flex 里靠右（margin-left:auto），但日历视图外不占空间（x-show） */

/* 🔴 2026-09-13 整页横向溢出根治（真根因已由实测确认）：
   ⚠️ 曾误判为 .view-tabs 撑宽 —— 实测它 6 个按钮总宽只 362px，right 最大 368 < 375，
      本来就在视口内，改它 flex-wrap 对溢出「完全无效」（A/B 实测 +11px 不变）。
      真元凶是 .cal-table 的 min-width:600px（style.css 全局 + 手机块），
      以及桌面端 .cal-table-wrap 去掉 overflow 后表格自然宽度 1589px 外溢。
      前者已改 min-width:0，后者已恢复 wrap 的 overflow-x:auto。
   剩下两道保险（保留，兜住未来任何新元素溢出）：
   ① html/body 限宽 + overflow-x:hidden —— 任何单点溢出都不许把页面撑宽
   ② .view-tabs 手机端可换行 —— 按钮再多也不会挤出去
   注意：横滑交互在 .cal-table-wrap 内部，overflow-x:hidden 加在 body 不影响它。 */
html, body { max-width: 100%; overflow-x: hidden; }

@media (max-width: 640px) {
  /* 视图切换压扁：去掉 emoji 让按钮更窄，批修改移到顶栏右侧后日历导航独占一行 */
  .view-tabs { gap: 4px; padding: 0 6px; flex-wrap: wrap; margin-bottom: 6px; }   /* wrap：装不下就换行，不再撑宽页面 */
  .view-tabs .btn { font-size: 12px; padding: 3px 6px; min-height: 28px; }
  .topbar-mobile-only { padding: 0 6px; }
  /* 图例：让核心项尽量挤在一行，超过就换行 */
  .cal-legend { gap: 8px; font-size: 11px; padding: 0 6px; }
  /* 日历导航行：手机端独占一行，元素不被挤出去 */
  .cal-nav { flex-wrap: wrap; gap: 6px; margin-bottom: 6px; }
  .cal-nav .cal-range { font-size: 13px; }
}

/* —— 日历图例：手机端只显 6 项核心，ℹ️ 按钮展开完整说明 —— */
.cal-legend { position: relative; }   /* 让 .cal-legend-full 绝对定位的锚点 */
.legend-more-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; padding: 0; border: 1px solid var(--border, #e8e8e8);
  border-radius: 50%; background: var(--surface, #fff); color: var(--text-secondary, #888);
  font-size: 12px; cursor: pointer; line-height: 1;
  transition: all 120ms ease;
}
.legend-more-btn:hover { background: var(--c-primary-soft, #e6f0ff); color: var(--c-primary, #1677ff); border-color: var(--c-primary, #1677ff); }
.cal-legend-full {
  position: absolute; right: 0; bottom: calc(100% + 6px);
  display: flex; flex-wrap: wrap; gap: 10px 14px;
  max-width: min(360px, calc(100vw - 32px));
  padding: 10px 12px; background: var(--surface, #fff);
  border: 1px solid var(--border, #e8e8e8); border-radius: 8px;
  box-shadow: 0 4px 14px rgba(0,0,0,.10);
  font-size: 12px; color: var(--text-secondary, #666);
  z-index: 20;
}
.cal-legend-full span { white-space: nowrap; }
/* 电脑端：屏幕够宽，完整图例默认展开（覆盖 x-show 的 display:none），ℹ️ 按钮隐藏 */
@media (min-width: 769px) {
  .cal-legend-full { display: flex !important; position: static; max-width: none;
                     box-shadow: none; border: none; padding: 0; background: transparent;
                     gap: 14px; flex: 1 1 100%; order: 99; margin-top: 6px; }
  .legend-more-btn { display: none !important; }
}

@media (max-width: 640px) {
  /* 视图切换压扁：去掉 emoji 让按钮更窄，批修改移到顶栏右侧后日历导航独占一行 */
  /* 2026-09-13：flex-wrap / cal-nav 的规则已上移到 .view-tabs 定义处（避免同一媒体块分散两处） */
  .view-tabs { gap: 4px; padding: 0 6px; flex-wrap: wrap; }
  .view-tabs .btn { font-size: 12px; padding: 3px 6px; min-height: 30px; }
  .topbar-mobile-only { padding: 0 6px; }
  /* 日历图例：让 6 个核心项尽量挤在一行，超过就换行 */
  .cal-legend { gap: 8px; font-size: 11px; padding: 0 6px; }
}

/* —— topbar 3 圆按钮挪到 view-tabs 行（手机端），电脑端保留原 topbar —— */
.topbar-mobile-only { display: none; }   /* 默认隐藏，只在手机端显示 */
@media (max-width: 640px) {
  .topbar-mobile-only { display: inline-flex; align-items: center; justify-content: center;
                        min-width: 30px; min-height: 30px; padding: 0 8px; font-size: 14px; }
  .user-btn-mobile { font-weight: 600; }
  .lock-mobile { padding: 0 6px; }
  /* 手机端只隐藏 dashboard 内的 topbar（其它 tab 的 topbar 不动） */
  .topbar-dashboard { display: none !important; }
}

/* —— 日历表头两行都 sticky（日期表头 + 应付合计）2026-09-10 —— */
/* 三个必要条件，缺一不可（踩坑总结）：
   1) border-collapse: collapse 会破坏表格 sticky → 改 separate + border-spacing:0
      （separate 下相邻边框不合并会变双线，所以边框改成只画右+下）
   2) 任何祖先有 overflow 都会成为滚动容器，sticky 就相对它而不是视口
      —— 注意 CSS 规范：overflow-x:auto 会强制 overflow-y 也算 auto，
         所以"overflow-x:auto; overflow-y:visible"是无效的，wrap 仍是滚动容器
   3) th 的包含块是 tr，所以 tr 也要 sticky（否则 th 只能在 tr 那一行范围内活动） */
.cal-table {
  border-collapse: separate !important;
  border-spacing: 0 !important;
}
.cal-table th, .cal-table td {
  border: 0 !important;
  border-right: 1px solid #f0f0f0 !important;
  border-bottom: 1px solid #f0f0f0 !important;
}
@media (min-width: 769px) {
  /* 🔴 2026-09-13：桌面端「整页要左右拖才能看完」的根因就在这里 ——
     这几行把 wrap 的 overflow 去掉了（为了 sticky 相对视口），但没给表格任何宽度约束：
       .cal-table 是 table-layout:auto，浏览器按内容（姓名/胶囊/合计）分配列宽，
       21 天 × 14 房型分组算出来 1589px，而 wrap 只有 1172px、.main 只有 1220px，
       表格直接冲出去 → <body> 横向溢出 394px（documentElement.scrollWidth 1834 vs 1440）。
     ⚠️ 修法不能用 table-layout:fixed + width:100% ——
       那样 21 列被均分到 53px，姓名和 ¥74.65 胶囊当场截断（实测 clipped=true），
       比原来更糟。桌面端本来就有 1192px 可用，正确做法是让表格【保持自然宽度但
       允许在 wrap 内横滑】，即恢复 wrap 的横向裁剪能力。
     → 保留 overflow-y:visible（sticky 表头所需的 y 轴约束不动），
       只把 x 轴收回 auto，并把 max-width 钉在 wrap 宽度上，表格自然宽度超出时
       在 wrap 内部横滑，不再外溢到 <body>。 */
  .cal-table-wrap {
    overflow-x: auto !important;
    overflow-y: visible !important;
    max-height: none !important;
  }
  /* 日期表头行（房间 / 周三09-09 / ...）*/
  .cal-table thead tr:nth-of-type(1) { position: sticky; top: 0; z-index: 4; }
  .cal-table thead tr:nth-of-type(1) th {
    position: sticky; top: 0; z-index: 4; background: #fafafa;
  }
  /* 应付合计行（日期表头下方 46px = 日期表头高度） */
  .cal-table thead tr.cal-revenue-bar { position: sticky; top: 46px; z-index: 5; }
  .cal-table thead tr.cal-revenue-bar th {
    position: sticky; top: 46px; z-index: 5;
    background: #fff; box-shadow: 0 2px 4px rgba(0,0,0,.06);
  }
}

/* —— 房态日历表头 sticky：必须 body 滚动才能粘在视口 —— */
/* 取消 .cal-table-wrap 的 max-height（不再创建内部滚动，sticky 元素找到 body） */
.cal-table-wrap {
  max-height: none !important;          /* 覆盖 style.css 的 calc(100vh - 170px) */
  overflow-x: auto;                     /* 保留：横向滚动必需 */
  overflow-y: visible;
}
/* 取消 .main 的 overflow-y:auto，让 body 滚动（侧边栏 position:fixed 不受影响） */
.main { overflow-y: visible !important; }

/* ==========================================================================
   房间详情弹窗 rd-* 体系（2026-09-12 UI 重构：虚线分区 + 字阶统一 + 按钮主次）
   ========================================================================== */
.rd-sec { border: 1.5px dashed #d4dbe6; border-radius: 10px; padding: 10px 12px; margin-top: 10px; background: #fff; }
.rd-sec-title {
  font-size: 11px; font-weight: 700; color: #a0aab8; letter-spacing: .08em;
  margin: 0 0 8px; display: flex; align-items: center; gap: 8px;
}
.rd-sec-title::after { content: ''; flex: 1; height: 1px; background: linear-gradient(90deg, #e6ebf2, transparent); }

/* 信息行：标签 12px 灰、值 13px 深色，一行流式排 */
.rd-row { display: flex; flex-wrap: wrap; align-items: baseline; gap: 3px 12px; font-size: 13px; color: #1f2329; margin: 0 0 7px; }
.rd-label { font-size: 12px; color: #98a2b3; flex: 0 0 auto; }
.rd-val { font-weight: 600; }
.rd-hint { font-size: 11.5px; color: #a0aab8; margin: 0 0 7px; line-height: 1.6; }

/* 欠款：浅黄条降级显示（语义色仍由 debt-inline-* 控制） */
.rd-debt {
  display: block; margin: 8px 0 0; padding: 6px 10px;
  background: #fffbe6; border-radius: 8px;
  font-size: 12px; line-height: 1.7; color: inherit;
}
.rd-debt .debt-inline { font-weight: 500; }

/* 按钮排：流式自适应宽度 */
.rd-btns { display: flex; flex-wrap: wrap; gap: 6px; }
/* 等大网格：每行 3 个统一尺寸（2026-09-12 老板草图） */
.rd-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; }
.rd-grid .rd-btn { width: 100%; padding: 0 4px; font-size: 12.5px; }
@media (max-width: 400px) {
  .rd-grid .rd-btn { font-size: 11.5px; letter-spacing: -.02em; }
}
.rd-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 4px;
  height: 32px; padding: 0 12px; border-radius: 8px;
  font-size: 13px; font-weight: 600; border: 1px solid transparent;
  cursor: pointer; white-space: nowrap; background: #fff;
  transition: background-color .12s ease, border-color .12s ease, transform .06s ease;
}
.rd-btn:active { transform: translateY(1px); }
.rd-btn:disabled { opacity: .45; cursor: not-allowed; }
.rd-btn-go          { background: #fa8c16; color: #fff; }           /* 退房：橙色实心 */
.rd-btn-go:hover    { background: #d97706; }
.rd-btn-stay        { background: #52c41a; color: #fff; }           /* 续住：绿色实心 */
.rd-btn-stay:hover  { background: #389e0d; }
.rd-btn-checkout-sm { background: #fa8c16; color: #fff; flex: 1 1 auto; }  /* 接力分段退房 */
.rd-btn-checkout-sm:hover { background: #d97706; }
.rd-btn-ghost        { background: #fff; border-color: #dbe2ec; color: #42506b; }  /* 白底灰字 */
.rd-btn-ghost:hover  { border-color: #b9c5d6; background: #f8fafc; }
.rd-btn-ghost-blue   { background: #fff; border-color: #bcd0ff; color: #2F6BFF; }  /* 白底蓝字 */
.rd-btn-ghost-blue:hover { background: #f0f5ff; }
.rd-btn-ghost-danger { background: #fff; border-color: #ffc2c2; color: #cf1322; }  /* 白底红字 */
.rd-btn-ghost-danger:hover { background: #fff1f0; }
.rd-btn-sm { height: 28px; padding: 0 10px; font-size: 12.5px; }

/* 底部弹窗级操作：文字链接风（贴紧上方内容，不坠底） */
.rd-foot {
  display: flex; justify-content: space-between; align-items: center;
  margin-top: 6px; padding-top: 6px; border-top: 1px dashed #e6ebf2;
}
.rd-link-btn {
  background: none; border: none; padding: 6px 4px; cursor: pointer;
  font-size: 12.5px; color: #98a2b3; display: inline-flex; align-items: center; gap: 4px;
}
.rd-link-btn:hover { color: #2F6BFF; }
.rd-link-danger { color: #cf1322; opacity: .7; }
.rd-link-danger:hover { color: #cf1322; opacity: 1; }

/* ==========================================================================
   弹窗统一设计系统（Modal Design System v1）· 2026-09-13
   --------------------------------------------------------------------------
   目标：25 个弹窗共用同一套「标题 / 分区 / 字段 / 提示条 / 选项卡片 / 底部按钮」。
   本块在文件末尾 → 覆盖前面的旧规则；
   **不改变滚动模型**（仍是整窗滚动 + header/footer sticky，手机 X5 已验证，勿动）。
   回滚：删掉本块即可，零业务影响。
   ========================================================================== */

/* ---- 1. 标题：图标 + 操作名 - 房号 ---- */
.modal-header h2 { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.modal-header h2 .mdl-icon { font-size: 17px; line-height: 1; }
.modal-header h2 .mdl-sep  { color: var(--ink-3); font-weight: 400; }
.modal-header h2 .mdl-room { color: var(--brand); font-weight: 600; }

/* ---- 2. 分区：直接复用既有 .ci-section（ui-modern.css 里「办理入住」的那套发丝线分区）
   早期方案本来打算新建 .mdl-sec 画虚线框，实测和 .ci-section 视觉打架、且全站引用 0 次，
   已整体删除 —— 统一的目标是「只有一套」，不是「多一套更漂亮的」。 ---- */

/* ---- 3. 字段：统一标签 / 圆角 / 焦点 ---- */
.modal .form-group > label { display: block; font-size: var(--fs-xs); font-weight: 400; color: var(--ink-3); margin-bottom: 4px; }
.modal .form-group label .req { color: var(--c-danger); }
/* ⚠️ 护栏不可删：必须排除 checkbox/radio/range/file/color，与 §6 的全局表单规则一致。
   踩过坑：漏了护栏 → 复选框被当成文本框按「宽 100% + 高 38px」处理，
   「退房加收 / 钟点房 / 客人已退房」三处复选框被撑成 38px 高。 */
.modal .form-group input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]):not([type="color"]),
.modal .form-group select,
.modal .form-group textarea {
  width: 100%; border-radius: var(--r-md); border: 1px solid var(--line-strong);
  background: var(--surface); color: var(--ink-1); font-size: var(--fs-base);
}
/* 高度：input 的 40px 来自 §6 全局规则（min-height），这里只需补 select ——
   select 天然高度是 43px，会和同一排的 input 差 3px。textarea 单独放大到 64px。 */
.modal .form-group select { height: 40px; }
.modal .form-group textarea { min-height: 64px; padding: 8px 12px; }
.modal input:focus, .modal select:focus, .modal textarea:focus {
  border-color: var(--brand); box-shadow: var(--focus-ring); outline: none;
}

/* ---- 4. 提示条：四语义色（蓝=说明 / 黄=金额待核对 / 绿=已确认 / 红=警告） ---- */
/* 注意：display 必须是 block —— 多行提示块（换房弹窗的「客人 + 日期 + 说明」）
   在 flex 行布局下会被横向铺开破版；单行时 block 与 flex 表现一致。 */
.mdl-notice {
  display: block;
  border-left: 3px solid transparent; border-radius: var(--r-md);
  padding: 8px 12px; font-size: 12.5px; line-height: 1.6; margin: var(--sp-3) 0;
}
.mdl-notice:first-child { margin-top: 0; }
.mdl-notice-info  { background: var(--c-info-soft);    color: var(--c-info-ink);    border-left-color: var(--c-info); }
.mdl-notice-money { background: var(--c-warning-soft); color: var(--c-warning-ink); border-left-color: var(--c-warning); }
.mdl-notice-ok    { background: var(--c-success-soft); color: var(--c-success-ink); border-left-color: var(--c-success); }
.mdl-notice-warn  { background: var(--c-danger-soft);  color: var(--c-danger-ink);  border-left-color: var(--c-danger); }

/* ---- 5. 选项卡片组：已删除 ----
   原本设计了一套 .mdl-cards/.mdl-card（纵向卡片 + 大图标）用于渠道/收款方式，
   实测发现「选项组」在全站早已统一成 .pay-methods 分段控件（含手机端），
   再引入第二套卡片组件只会让风格更乱。全站引用 0 次，已整体删除。 ---- */

/* ---- 6. 底部按钮：统一右对齐（取消在左、主操作最右），不再整宽拉满 ---- */
.modal-footer { justify-content: flex-end; flex-wrap: wrap; }
.modal-footer .btn { flex: 0 0 auto !important; }   /* 抵消历史 inline style="flex:1"；手机端另见文末 v1.2 §10 */
.modal-footer .mdl-foot-start { margin-right: auto; }  /* 需要把某按钮推到最左时加这个类 */

/* ---- 7. 只读信息行（预订详情 / 信息类弹窗） ---- */
.mdl-def { display: flex; gap: 10px; padding: 7px 0; font-size: var(--fs-base); border-bottom: 1px dashed var(--line); }
.mdl-def:last-child { border-bottom: none; }
.mdl-def > .k { flex: 0 0 62px; font-size: var(--fs-xs); color: var(--ink-3); padding-top: 2px; }
.mdl-def > .v { flex: 1 1 auto; color: var(--ink-1); word-break: break-all; }

/* ==========================================================================
   弹窗统一设计系统 v1.1 · 旧组件归一化层 · 2026-09-13
   --------------------------------------------------------------------------
   目标：把历史遗留的旧组件（弹窗内选项组 / 灰底说明块 / 弹窗正文段落 /
         只读键值行 / 标题分隔符）收敛到 v1 的视觉语言 —— 同一套线、同一套圆角、同一套令牌。
   覆盖范围：前 3 段只改颜色 / 边框 / 圆角 / 动效，不动尺寸布局，避免波及非弹窗区域；
            第 1 段（PC 端分段控件）是唯一改布局的一段，且全部锁在
            `@media (min-width:769px)` + `.modal` 内，不外溢。
   注意：ui-modern.css 在 index.html 里排在本文件之后（第 16 行），等权重时它反超；
         故本块一律用 `.modal ...` 前缀把权重抬到 0,2,0 以上，且不写 !important。
   回滚：删掉本块即可，零业务影响。
   ========================================================================== */

/* ---- 1. 弹窗内选项组：PC 端补上「分段控件」（与手机端 / 办理入住 视觉对齐） ----
   现状是真的不统一：手机端所有弹窗、以及 PC 端的「办理入住」(.ci-section) 和
   「添加消费」(.bill-pay-methods) 都是分段控件，但 PC 端其余弹窗落到 style.css
   的旧样式（border:2px solid #e0e0e0 的卡片 + .icon display:block 大 emoji 独占一行）。
   本块把 PC 端补齐，让同一个控件在 PC/手机、在哪个弹窗里都长一样。
   手机端不归这里管：ui-modern.css §7 已实现，且是 X5 防溢出的组成部分，勿动。
   注：ui-modern.css 在本文件之后加载，故此处不与之对抗 ——
   它命中 PC 弹窗的只有 .ci-section .pay-method / .bill-pay-methods 两处（本就是分段控件）。 */
@media (min-width: 769px) {
  .modal .pay-methods {
    display: flex;
    flex-wrap: wrap;
    gap: 0;
    padding: 3px;
    background: var(--canvas);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
  }
  .modal .pay-methods .pay-method {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 4px;
    min-height: 38px;
    padding: var(--sp-1) var(--sp-3);
    border: none;
    border-radius: calc(var(--r-md) - 3px);
    background: transparent;
    color: var(--ink-2);
    font-size: var(--fs-sm);
    text-align: center;
    white-space: nowrap;
    cursor: pointer;
    transition: background-color var(--dur-fast) var(--ease),
                color            var(--dur-fast) var(--ease);
  }
  /* 老样式里 .icon 是 display:block + 20px（大 emoji 独占一行、把分段撑高），
     这里收回行内 15px，和手机端 / .ci-section 一致 */
  .modal .pay-methods .pay-method .icon {
    display: inline;
    font-size: 15px;
    line-height: 1;
    margin: 0;
  }
  .modal .pay-methods .pay-method .pm-sub {
    display: inline;
    font-size: 11px;
    margin: 0 0 0 2px;
    color: var(--ink-3);
    font-weight: 400;
  }
  .modal .pay-methods .pay-method:hover { background: var(--canvas); color: var(--ink-1); }
  .modal .pay-methods .pay-method.selected {
    background: var(--brand);
    color: #fff;
    font-weight: 600;
  }
  .modal .pay-methods .pay-method.selected .pm-sub { color: rgba(255,255,255,.85); font-weight: 400; }
}

/* ---- 2. 提示条第 5 变体：中性「只读说明 / 浅灰备注」（原 #fafafa / #f7f9fc / #f5f5f5） ---- */
.mdl-notice-plain { background: var(--canvas); color: var(--ink-2); border-left-color: var(--line-strong); }

/* ---- 3. 弹窗正文段落：兜住「没有内联样式」的 <p> ----
   :not([class*="mdl-notice"]) 是必要的护栏：.modal-body p 的权重 (0,1,1)
   高于 .mdl-notice (0,1,0)，若不排除会把提示条自身的 margin 改掉。 */
.modal-body p:not([class*="mdl-notice"]) { line-height: 1.65; }
.modal-body > p:not([class*="mdl-notice"]) { margin: 6px 0; }

/* ---- 4. 只读键值行「狭弹窗紧凑变体」：已删除 ----
   当初为窄弹窗预留的 .mdl-def-compact，全站引用 0 次，留着只会误导后续改动 → 删掉。 ---- */

/* ---- 5. 标题分隔符：压住 ui-modern.css:126 的 h2 > span{font-size:18px} ----
   标题里的「-」分隔符和房号都是 <span>，房号 18px 是对的，分隔符跟着变 18px 就太大。 */
.modal-header h2 .mdl-sep { font-size: var(--fs-sm); }

/* ---- 6 & 7. 窄屏：标题不挤爆 + 底部按钮不折行 ----
   .btn 基础高度 ≈ 38px（padding 9px 20px），手机端 ≈ 40px，
   故 min-height: 40px 只会「往上抬」，不会把已有按钮压矮。 */
@media (max-width: 560px) {
  /* 这里原本还写了 .modal-header h2{font-size:var(--fs-lg)} —— 是死代码，已删：
     ui-modern.css 在本文件之后加载，同为 (0,1,1) 权重、后者胜出，
     实测 h2 在 PC 与手机都是 14px（这正是想要的字号，见 v1.2 §9）。 */
  .modal-header h2 .mdl-icon { font-size: 16px; }
  .modal-footer { gap: var(--sp-2); }
  /* ⚠️ 手机端必须把按钮恢复成「均分拉满」：改前的历史 inline style="flex:1" 就是这个效果。
     若沿用 PC 的 flex:0 0 auto，4 个按钮在 375px 下会折成 2 行（footer 69px → 113px）。
     PC 端保持右对齐不拉伸。 */
  .modal-footer .btn { flex: 1 1 auto !important; min-height: 40px; }
}

/* ==========================================================================
   弹窗统一设计系统 v1.2 · 复核修正（2026-09-13）
   --------------------------------------------------------------------------
   来源：独立验证者对 v1.1 的对抗性复核，修掉 5 个由统一改版直接引入的缺陷。
   回滚：删掉本块即可，零业务影响。
   ========================================================================== */

/* ---- 9. 标题文字 span：中性化 ----
   记收入/记支出、编辑用户/添加用户 这两个弹窗的标题文字是动态的，必须用 <span x-text>，
   于是会命中 ui-modern.css:126 的 `.modal-header h2 > span{font-size:18px;font-weight:700;color:brand}`
   → 和紧邻的 17px 图标挤在同一行、两个字号。该类把它们拉回「裸文本」应有的样子
   （与其它 22 个弹窗里直接写的标题文字一致：14px / 500 / ink-2）。
   权重 (0,2,2) > (0,1,2)，且 .mdl-icon/.mdl-sep/.mdl-room 各自的规则优先于此。 */
.modal-header h2 > span.mdl-name { font-size: var(--fs-base); font-weight: 500; color: var(--ink-2); }

/* ---- 10. 居中标题变体（微信支付弹窗：无房号、整窗内容居中） ----
   该弹窗的 .modal-header h2 带 flex:1（ui-modern:118）会撑满整行，
   只给 .modal-header 设 justify-content:center 是无效的 —— 居中的是个已经占满的盒子。
   真正的做法是让 h2 内部的内容居中。残余偏差 ≈ 20px（左侧 4px 竖条 + 右侧 44px 关闭按钮
   占位不对称，除非把关闭按钮改成绝对定位），视觉上已居中。 */
.modal-header.mdl-center h2 { justify-content: center; }

/* ---- 11. 复选框 / 单选钮：只受「尺寸」管，不受「通栏宽度」管 ----
   根因：style.css:849 的 `.form-group input{width:100%}` 会命中 checkbox，
   把 13x13 的小方框拉成整行宽（实测 428px）。老代码是在每个复选框上手写
   inline `style="width:auto"` 打补丁 —— 漏一个就歪一个，"退房加收·已收加收费"那个就漏了。
   这里一次性兜住：权重 (0,2,2) 高于 style.css 的 (0,1,1)，以后不必再写 inline。
   注：已有的 inline `style="width:auto"` 可保留（无害），不必回头清理。 */
.modal .form-group input[type="checkbox"],
.modal .form-group input[type="radio"] { width: auto; height: auto; min-height: 0; }

/* ==========================================================================
   12. 整页横向溢出根治 —— 收银 / 入住记录 / 财务 / 系统设置 4 个页签
   --------------------------------------------------------------------------
   2026-09-14。老板（荣耀 MagicOS 10 手机）反馈"手机浏览器界面没适配好，
   我需要左右滑动才能看完这个页面的信息"。
   ⚠️ 前提：文件前面第 856 行有一条 `html, body { max-width: 100%; overflow-x: hidden; }`，
      它会把整页横向滚动**遮蔽掉**（所以 documentElement.scrollWidth == innerWidth，
      量不出来），但**内容本体依然被撑宽、右侧内容被切掉**——等于"看不全"。
      正确判据是量 `.main` 的**直接子节点**（每个页签的根 div）的 scrollWidth 与 clientWidth：
      它没有被裁剪，能真实反映内容有多宽。实测（375px 视口，真实生产数据）：

        页签        根div clientWidth  根div scrollWidth   超出
        收银管理        351                629            +278  ← 6 列统计卡
        入住记录        351               1674           +1323  ← .table-wrap 不裁剪
        财务管理        351                423             +72  ← 表格 overflow 失效
        系统设置        351                952            +601  ← label 里 span 仍 nowrap
        房态看板/进出货/门锁/操作记录   351    =351     0     ← 本来就正常，本块不触碰

      下面 4 组规则各自解决一个独立根因；每组都只在该页签生效，互不干扰。
      共用思路：**让内容在容器内部横滑或换行，绝不裁剪内容本身**。
   ========================================================================== */

/* ---- 12.1 入住记录（最严重，+1323px）：`.table-wrap` 没有横向裁剪能力 ----
   根因：index.html:2908 是 `<div class="table-wrap">` 包 `<table class="table">`，
        表自然宽度 1674px。但 `.table-wrap` 这个类**在全部 CSS 里都没有定义过**
        （style.css 里只有 `.table` 有 overflow-x:auto，见 style.css:900）。
        表格的 overflow 写在 `<table>` 元素上对 table 元素**无效**（table 是
        table-wrapper 盒，不产生滚动容器），所以 1674px 直接撑破 `.main`。
   修法：给容器层补上裁剪能力（外壳 + overflow-x:auto），表格在容器内部横滑。
        ⚠️ 必须把背景/描边/圆角/阴影一起移交给这个容器 —— 否则外壳会盖住
        表格自己的 `border-radius` 白底，圆角处露出灰角（视觉回归）。
        这里用的都是与 §3 `.table` 完全相同的令牌，视觉零变化。
        注：不写 width:100%，让它作为块级元素自然撑满父宽。 */
.table-wrap {
  display: block;
  width: 100%;
  max-width: 100%;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-1);
}
/* 已经在外壳里有背景/描边的表格，不要重复画第二层（避免"框中框"） */
.table-wrap > .table {
  background: transparent;
  border: 0;
  border-radius: 0;
  box-shadow: none;
}
/* 手机端：横向滑动时首列冻结，滑到右边也知道这一行的姓名/房号是谁
   （与 §5 里 div.table 的首列冻结同一套做法，两边保持视觉一致） */
@media (max-width: 768px) {
  .table-wrap > table > thead > tr > th:first-child,
  .table-wrap > table > tbody > tr > td:first-child {
    position: sticky;
    left: 0;
    z-index: 2;
    background: var(--surface);
    box-shadow: 1px 0 0 var(--line);
  }
  .table-wrap > table > thead > tr > th:first-child { background: var(--canvas); z-index: 3; }
  .table-wrap > table > tbody > tr:hover > td:first-child { background: var(--brand-soft); }
}

/* ---- 12.2 财务管理（+72px）：`.table` 自带 overflow-x:auto 却失效 ----
   根因：这一页的容器是 `<div class="table"><table>`（index.html:3200/3233/3258/3284
        等），`.table` 本身有 overflow-x:auto（style.css:900），本该能横滑。
        但它同时是 `.fin-section` 的子元素，而 `.fin-section` 是普通块级元素、
        没有约束 —— 真正的问题是 `.table` 作为滚动容器**没有可收缩的宽度上限**，
        在 auto 表格宽度（597px）面前它按内容撑到 597px，直接顶破 351px 的 `.main`。
   修法：给 `.table` 补上 `max-width:100%` + `min-width:0`，把它的宽度钉死在父容器内，
        超出部分由它自己的 overflow-x:auto 承担（横滑能力不变，内容不丢）。
        `min-width:0` 是必要的——否则在 flex/grid 祖先里 auto 最小值仍会顶开。 */
.table {
  max-width: 100%;
  min-width: 0;
}

/* ---- 12.3 系统设置（+601px）：竖排后 label 内部的 `<span>` 仍然 nowrap ----
   根因：style.css:1102 给 `.settings-section .setting-row label` 写了
        `white-space: nowrap`，style.css:1260 的手机断点只改了 `flex-direction: column`，
        **没有解除 nowrap**，也没管 label 内部那个带长说明的 `<span>`
        （如「读卡器地址（未接入请留空；买设备后把厂商提供的本机服务地址填到这里…）」）。
        span 继承 nowrap → label 实测被撑到 935px。
   修法：手机上解禁换行（label 与内部 span 一起），长说明自然折行。
        `overflow-wrap:anywhere` 兜住「http://192.168.x.x:8080/xxx」这种
        **没有任何空格可断**的超长英文串，否则它会整段溢出（正是老板看到的那个）。
        电脑端（≥769px）不动：一条 row 一行放下，nowrap 才是想要的紧凑效果。 */
/* 🔴 2026-09-14 二次修正：断点方案是错的 —— 改成【不设断点】的收缩兜底
   ------------------------------------------------------------------
   为什么不能再用断点：
     第一版断点 768px → 812 不生效（over=600）；第二版放宽到 1024px →
     812 好了，但边界测试发现**只是把悬崖挪到 1025**：
         1025×768 realOver=387
         1200×900 realOver=212
         1440×900 realOver=0
     定位（线上实测）：`.setting-row` 的父级 `.settings-section` 被
     style.css:1086 钉死 `max-width:640px`（实测 right=884）。
     而那个 nowrap label 实测宽 **935px**、它同行末尾的 input 宽 196px
     被推到 right=1412 —— **这两个宽度都跟视口无关**。
     只要 label 的固有宽度（935）> 节容器可用宽度（640−48 padding≈592），
     行内容就必然溢出，与窗口多宽毫无关系。1440 看着"没溢出"只是因为
     溢出量 1412−1440 < 0 被视口吸收了，属于**碰巧没露出来**，不是修好了。
     ⇒ 结论：这里根本不该用断点判断，应该让它在「装不下时」自己收缩。

   修法（无条件生效，不包 @media）：
     1) 解除 label 与内部说明 span 的 nowrap，并把无空格长串
        （`http://127.0.0.1:24010`）交给 `overflow-wrap:anywhere` 断开；
     2) label 与行内子项一律 `min-width:0` —— flex 子项默认 `min-width:auto`
        会按内容最小宽度顶住，不写这条即使能换行也照样撑宽；
     3) 行放开 `flex-wrap`，装不下时 input 自然落到下一行。
   ✅ 桌面视觉不变：能一行放下的短 label（民宿名称/默认房价/Wi-Fi 密码…）
        本来就短于容器，`white-space:normal` 下仍是一行、高度仍是 21px；
        只有那个 935px 的超长说明才会折成两行 —— 这正是我们想要的效果。
        ⚠️ 注意这与 style.css:1102 的 `white-space:nowrap` 是覆盖关系，
        本文件在 style.css 之后加载，等权重下后者生效，无需 !important。 */
.settings-section .setting-row label,
.settings-section .setting-row label > span {
  white-space: normal;
  overflow-wrap: anywhere;
  word-break: break-word;
}
.settings-section .setting-row label { max-width: 100%; min-width: 0; }
/* 说明性 span 退回普通字重/灰色，和本页其它提示文案一致（原本靠 inline style，保留） */
.settings-section .setting-row label > span { font-size: 12.5px; }
.settings-section .setting-row { flex-wrap: wrap; min-width: 0; }
.settings-section .setting-row > * { min-width: 0; }
/* 行折行后，输入框独占整行，避免半宽残留、也不再被 label 顶出右边界 */
.settings-section .setting-row input[type="text"],
.settings-section .setting-row input[type="password"],
.settings-section .setting-row input[type="number"] {
  max-width: 100%;
}
/* ≤768 手机端：由 style.css:1260 的手机断点把行改成竖排（column），
   这里只补一条「input 铺满整行」，与既有的手机视觉一致。
   >768 的桌面/平板保持横排一行（label 左、input 右），不动原设计。 */
@media (max-width: 768px) {
  .settings-section .setting-row input[type="text"],
  .settings-section .setting-row input[type="password"],
  .settings-section .setting-row input[type="number"] {
    width: 100%;
    flex: 1 1 100%;
  }
}

/* ---- 12.4 收银管理（+278px）：索引内联 `repeat(6,1fr)` 压过手机断点 ----
   根因：index.html:2622 是 `<div class="stats-grid" style="grid-template-columns:repeat(6,1fr)">`。
        style.css 里明明有手机断点（1152 块的 `repeat(2,1fr)`、1280 块的 480px 断点），
        但**内联行内样式的优先级高于任何选择器**（除非写 !important），
        所以 6 列在 375px 下原样生效 → 586px 宽、顶破 .main。
   （财务页 3142/3331、设置页 3651 也用了同一个内联写法，本组一并覆盖。）
   修法：手机端用 `!important` 把列数收回 2 列，并让每张卡片 `min-width:0`
        —— 否则卡片内的数字（如 ¥12345）会把网格轨道顶宽，2 列也照样溢出。
        只改列数，不改任何卡片内部结构，老板看到的仍是同一张卡、同一个数字。 */
/* 🔴 2026-09-14 断点修正 768px → 1024px（verifier-2 复验：812×375 实测 realOver=+91）
   根因：`index.html:2622` 的内联 `style="grid-template-columns:repeat(6,1fr)"`
        **优先级高于任何选择器**，只有断点内的 `!important` 才能压过它。
        上一轮断点写的是 `max-width:768px`，而手机横屏 innerWidth=812 > 768
        → 这条规则整段不生效 → 812 下仍是 6 列，实测 `.stats-grid` 宽 544px
        却要放 6 张卡，把同行下方的表格一起顶到 w=961 / right=1206，
        整页 **over=+91**（已确认**不是** `.table` 的问题，别去动 `.table`）。
   修法：断点放宽到 1024px 覆盖手机横屏，仍用 `!important` 压内联 6 列。
   ✅ 桌面硬约束：1440 不在本断点内，`.stats-grid` 仍是内联的 6 列。 */
@media (max-width: 1024px) {
  .stats-grid { grid-template-columns: repeat(2, minmax(0, 1fr)) !important; gap: 10px; }
  .stats-grid > .stat-card { min-width: 0; }
  /* 卡片内的大数字和标签都允许收缩/换行，长金额不再顶宽轨道 */
  .stats-grid > .stat-card .num,
  .stats-grid > .stat-card .label { min-width: 0; overflow-wrap: anywhere; }
}
/* 超小屏（≤380px，如老板手机的分屏 / 小窗）单列铺满，信息不挤 */
@media (max-width: 380px) {
  .stats-grid { grid-template-columns: minmax(0, 1fr) !important; }
}

/* ---- 12.5 房态看板（320×568 实测 +24px）：`.room-group` 撑到 332px ----
   根因：`.room-board` 是 `display:grid`（style.css:273），手机断点把列写成
        `grid-template-columns:1fr`（style.css:1180）。但 grid 轨道默认的
        `min-width:auto` 等于**内容的最小宽度**——组内 `.room-group-cards`
        （style.css:1190 的 `repeat(2,1fr)`）+ 卡片里的房号/客人名/金额
        让内容最小宽度到 330px，于是 `.room-group` 被撑到 **332px**，
        在 320px 视口里 **over=+24**，整页横滚。
        （verifier-2 递进注入实测：基线 24 → 只加第一条 7 → 再加第二条 0）
   修法：逐层解掉 `min-width:auto`，让 1fr 轨道真正按容器宽收敛；
        组头允许折行，房型名 + 「¥68/晚 · 4 间 · 4 住」不再并排硬撑。
   ⚠️ 只加收缩能力，不改任何字号/列数/文字，老板看到的仍是同一张卡。 */
.room-board > *.room-group { min-width: 0; }
.room-group-header { min-width: 0; flex-wrap: wrap; }
.room-group-cards { min-width: 0; }
.room-group-cards > .room-card { min-width: 0; }

/* ---- 12.6 看板顶部统计条（手机端 147px 高 → 64px）：5 项统计被竖排 ----
   老板反馈（2026-09-14 截图）：「内容不多只是排版有问题，占用太多纵向空间」。
   实测 375×812：`.board-top` 高 **147px**，其中 `.stats-bar` 占 129px，
   5 个统计项**每项占一行**，而整个卡片宽度用了 351px 却只显示 "3 空房干净" 这种短文案。

   根因（实测 computed style 得到）：
     style.css:1288 在 `@media (max-width:380px)` 里写了
       `.stats-bar { flex-direction: column; gap:4px; align-items:flex-start }`
     ——它**只对 ≤380px 生效**。但 375 的 `.stats-bar` 实测 flex-direction 也是 column、
     宽仅 **65px**，说明真正的原因是**收缩**：`.board-top` 是 `flex-wrap:wrap` 的 flex 容器，
     `.stats-bar` 官方值 `flex:0 1 auto`（basis auto）、而 `min-width` 是默认的 `auto`
     —— 旁边图例块固有宽 125px，容器内宽 351−32(padding)=319，
     319−125−8(gap)=186 可用；但 `.stats-bar` 按 nowrap 内容最小宽度参与分配时
     被压到 65px，于是 `flex-wrap:wrap` 把 5 个 `.stat-item` 逐个换行 → 129px 高。
     （432/560/768 下同样规则给出 329px 单行，所以这不是断点问题，是收缩问题。）

   修法（只动手机断点，且只动"排布"不动"数据"）：
     1) `.stats-bar` 补 `flex:1 1 auto` + `min-width:0` —— 解除 `min-width:auto`
        的内容顶宽，让它吃满图例左侧的剩余宽度；
     2) `flex-direction:row !important` 顶掉 ≤380px 那条 column；
     3) `.board-top` 设 `flex-wrap:nowrap`，统计条与图例稳定并排、不再互相挤行；
     4) 统计条自身 `flex-wrap:wrap` + `gap:4px 12px`，窄屏放不下时**只在统计条内部**折成 2 行。

   实测收益（线上真机，375×812）：`.board-top` **147 → 64px**，省 **83px**；
     320×568 147→89、360×800 147→64、414×896 67→64；
     560/768 档 41→39（略降，无回归）；六档 realOver 全为 **0**，无元素被裁切。
   ⚠️ 桌面（>768）不在本断点内，原样不动。 */
@media (max-width: 768px) {
  .board-top {
    flex-wrap: nowrap;
    align-items: center;
    gap: 8px;
    padding: 8px 12px;
  }
  .stats-bar {
    flex: 1 1 auto;
    flex-direction: row !important;
    flex-wrap: wrap;
    gap: 4px 12px;
    min-width: 0;
    align-items: center;
  }
  .stat-item { font-size: 12px; }
  .stat-item b { font-size: 14px; margin-right: 3px; }
  /* 图例块（美团/抖音/线下）保持固定宽度，不参与挤压 */
  .board-top > div:last-child { flex: 0 0 auto; gap: 8px !important; font-size: 11px !important; }
}
