/* =============================================================
   酒店代订管理系统 2.0 · 统一设计系统
   -------------------------------------------------------------
   布局：桌面端左侧固定侧边栏 / 手机端抽屉式菜单
   原则：任何页面元素都复用本文件的组件类，不写行内样式
   ============================================================= */

/* ---------- 设计令牌 ---------- */
:root {
  /* 字体栈（2026-10-11 补令牌）：中文 webfont 太重（数 MB），
     内部工具继续用系统字体栈；mono 统一走 --font-mono，
     别再各处自己写一遍。 */
  --font-sans: -apple-system, BlinkMacSystemFont, "SF Pro Display", "SF Pro Text",
               "PingFang SC", "Helvetica Neue", "Microsoft YaHei", sans-serif;
  --font-mono: ui-monospace, "SF Mono", "Cascadia Mono", Consolas, Menlo, monospace;

  /* 主色（2026-10-28 按《Apple 风格后台 UI 规范》换成 Apple 蓝）
     ---------------------------------------------------------
     原来是靛蓝 #4F46E5。换成 #0071E3 的理由不是好看，是"全站唯一强调色"
     —— 靛蓝和页面里那些老蓝(#2563EB)混在一起，看起来像两套系统。
     白标客户仍然可以在后台改主题色，这里只是默认值。
     accent-soft/deep 分别对应胶囊浅底和浅底上的文字。 */
  --brand-50:  #E8F2FE;
  --brand-100: #D6E7FC;
  --brand-200: #B9D7FA;
  --brand-500: #147CE5;
  --brand-600: #0071E3;
  --brand-700: #0058B0;

  /* 中性色（同规范：页面底不用纯白、正文不用纯黑、分割线不用实色灰） */
  --bg:        #F5F5F7;
  --surface:   #FFFFFF;
  --surface-2: #FAFAFC;
  --surface-3: #F0F0F3;
  /* 7% 黑而不是 #E5E7EB —— 实色灰在浅底上会"发绿"，叠在白卡上又太硬。
     注意：线变淡后必须靠阴影分层，所以 --sh-* 同步改成了远而淡。 */
  --line:      rgba(0, 0, 0, .07);
  --line-2:    rgba(0, 0, 0, .13);

  --text:      #1D1D1F;
  --text-2:    #6E6E73;
  --text-3:    #86868B;
  --text-4:    #AEAEB2;   /* 占位/禁用 */

  /* 语义色（规范取值；红绿按财务口径：正=绿、负=红） */
  --ok:        #1D9E60;
  --ok-bg:     #E8F7EE;
  --warn:      #FF9500;
  --warn-bg:   #FFF4E3;
  --danger:    #E5484D;
  --danger-bg: #FDECEC;
  --info:      #0071E3;
  --info-bg:   #E8F2FE;
  /* 浅底胶囊上的"文字色"，比主色深一档保证对比度。 */
  --ok-text:     #0F7B45;
  --warn-text:   #9A5B00;
  --danger-text: #B22B2B;
  --info-text:   #0058B0;

  /* 导航选中态：深色实心胶囊（2026-10-28 用户指定）
     为什么不用品牌色：品牌色是白标客户可改的，改成浅色系（比如米金、
     浅绿）之后"浅底 + 同色字"的选中态会直接糊掉。近黑在浅底上永远成立，
     也不跟品牌色打架。 */
  --nav-active-bg: #1D1D1F;
  --nav-active-fg: #FFFFFF;
  --nav-active-shadow: 0 2px 8px rgba(0,0,0,.16);

  /* -------------------------------------------------------------
     按钮色调层（2026-10-28 统一按钮，全站唯一来源）
     -------------------------------------------------------------
     四档语义共用同一套比例，改一处全站跟着变：
         底 14% / 边 28~30% / 字深一档；hover 22~42%；按下 28%。

     🔴 为什么用「变量 + 后面的 @supports」而不是每个类各写两遍
        rgba + color-mix：全站有 20 多个按钮类（.btn-primary / .gbtn /
        .chat-send / .sch-go / .st-go / .price-fetch-btn …），逐个写两遍
        属性值 = 改一次要动 20 处，必然漏一处。
        这里是**变量值**在支持 color-mix 的浏览器里被换成 color-mix 表达式
        （从而跟随白标客户改品牌色），不支持的浏览器继续吃静态 rgba。
     🔴 不能写成「同一 :root 里 --x 声明两次」：CSS 变量在 computed-value
        阶段才失效，第二条无效就整条 guaranteed-invalid，
        **不会回退到第一条**（这个坑 10-28 踩过一次）。
     ------------------------------------------------------------- */
  --tint-brand:        rgba(0, 113, 227, .14);
  --tint-brand-h:      rgba(0, 113, 227, .22);
  --tint-brand-a:      rgba(0, 113, 227, .28);
  --tint-brand-line:   rgba(0, 113, 227, .28);
  --tint-brand-line-h: rgba(0, 113, 227, .38);
  --tint-brand-fg:     #0058B0;

  --tint-ok:        rgba(29, 158, 96, .14);
  --tint-ok-h:      rgba(29, 158, 96, .22);
  --tint-ok-a:      rgba(29, 158, 96, .28);
  --tint-ok-line:   rgba(29, 158, 96, .30);
  --tint-ok-line-h: rgba(29, 158, 96, .44);
  --tint-ok-fg:     #0F7B45;

  --tint-danger:        rgba(229, 72, 77, .12);
  --tint-danger-h:      rgba(229, 72, 77, .20);
  --tint-danger-a:      rgba(229, 72, 77, .26);
  --tint-danger-line:   rgba(229, 72, 77, .28);
  --tint-danger-line-h: rgba(229, 72, 77, .42);
  --tint-danger-fg:     #B22B2B;

  /* 圆角（只有四档，2026-10-28 按规范定稿）
       --r-xl   弹窗（26px，比卡片再大一级）
       --r-lg   卡片 / 面板（20px，视觉主体）
       --r      按钮 / 输入框 / 下拉 / 小方块（12px）
       --r-sm   内嵌小块（12px，视觉等同 --r，保留别名兼容老代码）
       --r-full 胶囊 / 圆点 / 徽标
     🔴 规范原话："卡片圆角小于 16px 就不对"。之前是 14px。 */
  --r-xl: 26px;
  --r-lg: 20px;
  --r:    12px;
  --r-sm: 12px;
  --r-full: 999px;

  /* 阴影（规范原话：远、散、淡 —— 第二层 offset-y ≥10px、blur ≥30px。
     原来 0 2px 8px 那种"近而深"的阴影是廉价感最直接的来源。） */
  --sh-1: 0 1px 2px rgba(0, 0, 0, .04), 0 10px 30px rgba(0, 0, 0, .05);
  --sh-2: 0 2px 8px rgba(0, 0, 0, .06), 0 16px 40px rgba(0, 0, 0, .07);
  --sh-3: 0 2px 8px rgba(0, 0, 0, .06), 0 20px 48px rgba(0, 0, 0, .10);
  --sh-hover: 0 2px 8px rgba(0, 0, 0, .06), 0 16px 40px rgba(0, 0, 0, .08);

  /* 动效令牌（按规范统一）
     规范区间：按钮/输入 200–300ms、卡片入场 500ms、页面切换 320ms。
     全站过渡只允许用这三档，别再各处手写 0.2s ease。 */
  --dur-1: 200ms;   /* 颜色类微交互、hover */
  --dur-2: 300ms;   /* 位移/阴影/输入 focus */
  --dur-3: 320ms;   /* 页面/卡片入场 */
  --ease:  cubic-bezier(.4, 0, .2, 1);

  /* 尺寸 */
  --sidebar-w: 216px;
  --topbar-h:  56px;
  --tabbar-h:  58px;

  /* 毛玻璃（2026-10-28）
     ⚠️ 只在「背后有内容流过」的位置用 —— 吸顶栏、底部标签栏、抽屉遮罩。
        普通卡片背后是纯色，加了 backdrop-filter 等于白耗 GPU，
        还会在滚动时重新计算模糊，低端安卓上直接掉帧。
     ⚠️ 白标的主题色只改 --brand-*，这里用白色半透明是安全的；
        如果哪天要出深色模式，必须把这三行一起换掉。 */
  /* 🔴 2026-10-28 二改（用户反馈「手机上没毛玻璃效果」）
     实测结论：**代码是生效的**（computed backdrop-filter = blur(20px) saturate(1.8)），
     看不见的原因是「背后没东西可模糊」——页面 var(--bg) + 白卡片，白色糊完还是白色。
     玻璃要让背景的颜色/层次透出来才成立，纯色底上永远只是一块淡淡的灰白矩形。
     所以解法不是继续调 blur，而是两步：
       ① 补一层"壁纸" —— 见下面 --amb-*，让底部有颜色可透；
       ② 玻璃调薄 + 加强 —— 透明度 .78→.66、blur 20→30、饱和 180→200%，
          再补一道边缘高光（--glass-rim），没有那道亮边玻璃就像塑料片。 */
  --glass-bg:   rgba(246,248,253,.70);
  --glass-blur: 40px;
  --glass-sat:  saturate(210%);
  --glass-rim:  inset 0 1px 0 rgba(255,255,255,.9),
                inset 0 -1px 0 rgba(255,255,255,.25);

  /* 氛围底（"壁纸"）：几团极淡的品牌色光晕。
     全部由 --brand-* 派生 → 客户换主题色自动跟着变，不会串色。
     浏览器不认 color-mix 时，整条 background-image 声明失效
     → 自动退回纯 --bg，不会有副作用。 */
  --amb-a: color-mix(in srgb, var(--brand-500) 8%, transparent);
  --amb-b: color-mix(in srgb, var(--brand-600) 5%, transparent);
  --amb-c: color-mix(in srgb, var(--brand-200) 7%, transparent);

  /* =========================================================
     语义色的「边框 / 强调底 / hover 深色」三档（2026-10-28 补）
     ---------------------------------------------------------
     起因：用户说「颜色不一致」。审计后发现同一个"警示黄"在
     项目里有 var(--warn) / var(--warn) / var(--warn-hover) / var(--warn-text) / var(--warn-bg-2) /
     var(--warn-border) / var(--warn-border) 七种写法，红绿蓝同理 —— 并排出现就是
     一眼便宜。这里把每一档固定下来，页面里只准引用，不准写死。
       --*-bg      极淡底（大面积铺）
       --*-bg-2    强调底（提醒条、需要抢视线的块）
       --*-border  描边
       --*-text    浅底上的文字（比主色深一档保对比度）
       --*-hover   按钮 hover 的深色
     ========================================================= */
  --ok-bg-2:       #F0FDF4;
  --ok-border:     #A7F3D0;
  --ok-hover:      #047857;
  --warn-bg-2:     #FEF3C7;
  --warn-border:   #FDE68A;
  --warn-hover:    #B45309;
  --danger-border: #FECACA;
  --danger-hover:  #B91C1C;
  --info-border:   #BAE6FD;
  --info-hover:    #0369A1;
  --text-inverse:  #FFFFFF;

  /* 圆角档位说明（2026-10-28 定，别再自己算像素）
     ---------------------------------------------------------
       --r-lg   卡片 / 面板 / 弹窗      （视觉主体，最圆）
       --r      按钮 / 输入框 / 下拉     （所有按钮不分大小都用这一档）
       --r-sm   小标签内嵌块 / 小图标按钮（≤24px 的元素）
       --r-full 胶囊 / 圆点 / 徽标
     曾有 .btn(10px) / .btn-copy(6px) / .btn-cal(8px) / .btn-sm(6px)
     四个值同时存在 → 看板上「复制订单信息」是方的、「去补齐」是圆的。
  */

  /* =========================================================
     兼容别名（2026-10-28 补）
     ---------------------------------------------------------
     全项目扫出 10 个「引用了但从未定义」的变量。带 fallback 的
     只是写法不一致，没带的就是**颜色根本没生效** —— 这类静默失效
     比报错更难查（页面看着"还行"，其实是浏览器用了初始值）。
     这里一次性补成别名指向真 token，各处引用不用动；
     以后新写样式请直接用上面的真名，别再引别名。
     ========================================================= */
  --brand-400: #4A9FE8;              /* accent 浅一档，hover/描边 */
  --brand-900: #312E81;              /* indigo-900，深色强调 */
  --text-1: var(--text);             /* admin.css 旧写法 */
  --bg-2: var(--surface-2);          /* admin.css 旧写法 */
  --r-md: var(--r);                  /* 中间档圆角，等同 --r */
  --primary: var(--brand-600);       /* 老页面叫 primary */
  --success: var(--ok);              /* booking.css 叫 success */
  --fs-base: 14px;                   /* 基础字号 */
  --color-border-secondary: var(--line);
}

/* ---------- 基础重置 ---------- */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

html { -webkit-text-size-adjust: 100%; }

/* 无障碍：系统开了「减少动态效果」就关掉全站过渡。
   2026-10-28 加 —— 之前一个都没有，前庭功能敏感的用户会被
   卡片上浮、抽屉滑入这类动画搞到头晕。 */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

body {
  font-family: var(--font-sans);
  background: var(--bg);
  color: var(--text);
  font-size: 14px;
  line-height: 1.55;
  /* 负字距（规范硬要求）：中文标题/正文不加负字距，字间松散，一眼廉价。
     只调 letter-spacing，不动字号 —— 改 15px 会把现有表格和卡片撑破。 */
  letter-spacing: -0.01em;
  /* 100dvh：手机 Safari 地址栏收展时 100vh 会跳动（iOS 实测坑，
     与项目「iOS 用 100dvh」约定一致）；老浏览器不认 dvh 回退 100vh。 */
  min-height: 100vh;
  min-height: 100dvh;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--brand-600); text-decoration: none; }
a:hover { text-decoration: underline; }

/* ---------- 浏览器原生部件品牌化（2026-10-11，impeccable craft-floor）----------
   选择高亮/输入光标/滚动条/勾选框这些部件，浏览器各有一套默认样式，
   不主题化就是"组装感"最重的来源（Windows 灰滚动条、默认蓝勾选框）。
   全部走品牌令牌，一处管全部页面；换 brand_theme 时自动跟着变。 */
::selection { background: var(--brand-100); color: var(--brand-700); }

input, textarea { caret-color: var(--brand-600); }

/* 原生控件（勾选框/单选框/进度条）跟随品牌主色 */
:root { accent-color: var(--brand-600); }

/* 按钮色调层跟随白标品牌色（2026-10-28）
   -------------------------------------------------------------
   只在浏览器认识 color-mix 时替换上面的静态 rgba 值 —— 不认识的浏览器
   整块跳过，继续吃静态值，**不会出现"变量为空导致按钮透明"**。
   比例与静态值一一对应，改比例请两处一起改（上方 :root 注释里有表）。 */
@supports (background: color-mix(in srgb, red 10%, transparent)) {
  :root {
    --tint-brand:        color-mix(in srgb, var(--brand-600) 14%, transparent);
    --tint-brand-h:      color-mix(in srgb, var(--brand-600) 22%, transparent);
    --tint-brand-a:      color-mix(in srgb, var(--brand-600) 28%, transparent);
    --tint-brand-line:   color-mix(in srgb, var(--brand-600) 28%, transparent);
    --tint-brand-line-h: color-mix(in srgb, var(--brand-600) 38%, transparent);
    --tint-brand-fg:     var(--brand-700);

    --tint-ok:        color-mix(in srgb, var(--ok) 14%, transparent);
    --tint-ok-h:      color-mix(in srgb, var(--ok) 22%, transparent);
    --tint-ok-a:      color-mix(in srgb, var(--ok) 28%, transparent);
    --tint-ok-line:   color-mix(in srgb, var(--ok) 30%, transparent);
    --tint-ok-line-h: color-mix(in srgb, var(--ok) 44%, transparent);
    --tint-ok-fg:     var(--ok-text);

    --tint-danger:        color-mix(in srgb, var(--danger) 12%, transparent);
    --tint-danger-h:      color-mix(in srgb, var(--danger) 20%, transparent);
    --tint-danger-a:      color-mix(in srgb, var(--danger) 26%, transparent);
    --tint-danger-line:   color-mix(in srgb, var(--danger) 28%, transparent);
    --tint-danger-line-h: color-mix(in srgb, var(--danger) 42%, transparent);
    --tint-danger-fg:     var(--danger-text);
  }
}

/* 滚动条：细圆条贴中性色，hover 加深；Firefox 走 scrollbar-width */
* { scrollbar-width: thin; scrollbar-color: var(--line-2) transparent; }
::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-thumb { background: var(--line-2); border-radius: var(--r-full); }
::-webkit-scrollbar-thumb:hover { background: var(--text-3); }
::-webkit-scrollbar-track { background: transparent; }

/* 键盘焦点环统一：按钮/订单卡/单选片已有，这里补齐链接等其余可聚焦元素 */
a:focus-visible, [tabindex]:focus-visible {
  outline: 2px solid var(--brand-500);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

/* 🔴 hidden 属性兜底（2026-10-28 补）
   -------------------------------------------------------------
   hidden 的隐藏效果来自**浏览器默认样式** `[hidden]{display:none}`，
   特异性 (0,1,0)、且在任何作者样式之前 —— 只要某个类自己写了 display
   （.notice{display:flex} / .modal{display:flex} / .gtable tr …），
   就把 hidden 压掉了：JS 里 el.hidden=true 只改了 DOM 属性，
   **视觉上完全不隐藏**。
   实际踩到的例子：结账页 `<div class="notice" id="modeTip" hidden>`
   在页面顶部渲染成一条 24px 高的空蓝色横条，谁也不知道那是什么。
   这条兜底一劳永逸，比逐个类补 `[hidden]{display:none}` 可靠。 */
[hidden] { display: none !important; }

/* ---------- 品牌图片 logo（2026-10-11 白标定制）----------
   settings.brand_logo 设了图片时替代字标。字标 .logo 是渐变底+白字，
   图片版去掉底色，object-fit 适配任意比例的图。 */
img.logo {
  object-fit: cover;
  background: var(--surface);
  border: 1px solid var(--line);
}

/* =============================================================
   布局骨架
   ============================================================= */
.layout { display: flex; min-height: 100vh; }

/* ---------- 侧边栏 ---------- */
.sidebar {
  width: var(--sidebar-w);
  flex-shrink: 0;
  background: var(--surface);
  border-right: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  position: fixed;
  top: 0; left: 0; bottom: 0;
  z-index: 60;
}

.sidebar-brand {
  height: var(--topbar-h);
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 0 16px;
  border-bottom: 1px solid var(--line);
  flex-shrink: 0;
}
.sidebar-brand .logo {
  min-width: 30px; width: auto; height: 30px; padding: 0 4px;
  border-radius: var(--r-sm);
  background: linear-gradient(135deg, var(--brand-500), var(--brand-700));
  color: #fff;
  display: grid; place-items: center;
  font-size: 13.5px; font-weight: 700;
  flex-shrink: 0;
  white-space: nowrap; letter-spacing: -1px;
}
.sidebar-brand .logo-img { width: 30px; padding: 0; }
/* ⚠️ 必须能省略号截断：白标客户的系统名可以很长
   （"李老板的订单可视化系统" 11 个字），侧边栏只有 216px，
   不加这三行会折成两行、压到 56px 的高度里、和左边的 logo 叠在一起。
   2026-10-28 实测截图确认过。 */
.sidebar-brand .name {
  font-size: 14.5px; font-weight: 700; letter-spacing: -.2px;
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sidebar-brand .ver {
  font-size: 10px; color: var(--text-3); font-weight: 500;
  background: var(--surface-3); padding: 1px 5px; border-radius: var(--r-full);
  margin-left: auto;
}

.sidebar-nav {
  flex: 1;
  overflow-y: auto;
  padding: 12px 10px 20px;
}

.nav-group-title {
  font-size: 10.5px;
  font-weight: 700;
  color: var(--text-3);
  letter-spacing: .8px;
  text-transform: uppercase;
  padding: 14px 10px 6px;
}
.nav-group-title:first-child { padding-top: 2px; }

.nav-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 11px;
  border-radius: var(--r);
  color: var(--text-2);
  font-size: 13.5px;
  font-weight: 500;
  text-decoration: none;
  margin-bottom: 2px;
  position: relative;
  transition: background .13s, color .13s;
}
.nav-item:hover { background: var(--surface-3); color: var(--text); text-decoration: none; }
.nav-item .ico { width: 18px; text-align: center; font-size: 15px; flex-shrink: 0; }
.nav-item .txt { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* 选中 = 深色实心胶囊（2026-10-28）。原来用 --brand-50 浅底 + 左侧 3px 竖条，
   浅底在品牌色偏浅时几乎看不出"选了哪个"；实心块不用看颜色也能定位。 */
.nav-item.active {
  background: var(--nav-active-bg);
  color: var(--nav-active-fg);
  font-weight: 600;
  box-shadow: var(--nav-active-shadow);
}
/* 胶囊本身就表达选中了，左侧竖条会和胶囊边缘打架 → 撤掉 */
.nav-item.active::before { display: none; }
/* **emoji 图标不能跟着加粗** —— 合成粗体的 emoji 在 Chromium 里
   会渲染变形（被压扁）。用户 2026-10-06 报的「点酒店与集团图标扭曲，
   点 AI 就正常」—— AI 的🤖 没加粗，酒店的 🏨 加粗了。
   办法：文字保持 600，图标单独回到 400。 */
.nav-item.active .ico {
  font-weight: 400;
  /* 顺手固定尺寸，避免不同 emoji 的字形宽度不一样导致抖动 */
  width: 18px;
  font-size: 15px;
  display: inline-block;
  /* ⚠️ 别加 line-height —— 实测踩过：
     line-height:1 会把 🏨 的字形高度从 23px 压到 15px（被压扁）。
     图标宽度是 CSS 固定的 18px，高度却被压扁，就变形了。
     对比其他未选中的图标：都是 18×23，只有选中的成了 18×15。 */
}
.nav-item.active::before {
  content: '';
  position: absolute;
  left: -10px; top: 50%;
  transform: translateY(-50%);
  width: 3px; height: 20px;
  background: var(--brand-600);
  border-radius: 0 3px 3px 0;
}
.nav-item .pill {
  font-size: 10px; font-weight: 700;
  background: var(--brand-600); color: #fff;
  padding: 1px 6px; border-radius: var(--r-full);
  min-width: 18px; text-align: center;
}
/* 灰色徽标：原来是「浅灰底 + 白字」，对比度约 1.5:1，几乎看不清
   （用户 2026-10-06 报的）。改成「浅底 + 深字」才够清楚。 */
.nav-item .pill.gray {
  background: var(--surface-3);
  color: var(--text-3);
  font-weight: 600;
}
/* 选中态底是深色，徽标必须反过来：深灰底白字在近黑上根本看不见，
   品牌蓝底白字也会被吃掉。改成「半透明白底 + 白字」和「纯白底 + 深字」。 */
.nav-item.active .pill.gray {
  background: rgba(255,255,255,.20);
  color: #fff;
}
.nav-item.active .pill {
  background: #fff;
  color: var(--nav-active-bg);
}

.sidebar-foot {
  border-top: 1px solid var(--line);
  padding: 10px 12px;
  flex-shrink: 0;
  font-size: 12px;
}
.sidebar-user { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.sidebar-user .avatar {
  width: 28px; height: 28px; border-radius: var(--r-full);
  background: var(--brand-100); color: var(--brand-700);
  display: grid; place-items: center;
  font-size: 12px; font-weight: 700; flex-shrink: 0;
}
.sidebar-user .who { flex: 1; min-width: 0; }
.sidebar-user .who b { display: block; font-size: 12.5px; font-weight: 600; }
.sidebar-user .who span { font-size: 10.5px; color: var(--text-3); }

/* ---------- 主区 ---------- */
.main {
  flex: 1;
  min-width: 0;
  margin-left: var(--sidebar-w);
  display: flex;
  flex-direction: column;
}

.topbar {
  height: var(--topbar-h);
  background: var(--glass-bg);
  backdrop-filter: var(--glass-sat) blur(var(--glass-blur));
  -webkit-backdrop-filter: var(--glass-sat) blur(var(--glass-blur));
  /* 上亮下暗的竖向渐变 + 1px 内高光 = 玻璃的"厚度"（没有这两样就像塑料片） */
  background-image: linear-gradient(180deg, rgba(255,255,255,.62), rgba(255,255,255,.05));
  box-shadow: var(--glass-rim), 0 1px 2px rgba(16,24,40,.04);
  border-bottom: 1px solid rgba(16,24,40,.07);
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 0 20px;
  position: sticky;
  top: 0;
  z-index: 50;
}
.topbar h1 { font-size: 17px; font-weight: 700; letter-spacing: -.3px; }
.topbar .sub { font-size: 12px; color: var(--text-3); margin-top: -1px; }
/* 顶栏「返回」——回后台首页（/admin）的快捷入口（2026-10-29）。
   手机端侧边栏是抽屉，没有这颗按钮得「点汉堡 → 找系统总览」两步。
   配色跟全站主按钮同一套 tinted 令牌，别单写色值。 */
.tb-back {
  display: inline-flex; align-items: center; gap: 3px;
  height: 30px; padding: 0 12px 0 8px;
  border: 1px solid var(--tint-brand-line);
  border-radius: var(--r-full);
  background: var(--tint-brand);
  color: var(--tint-brand-fg);
  font-size: 13px; font-weight: 500; line-height: 1;
  text-decoration: none; white-space: nowrap;
  transition: background var(--dur-1) var(--ease),
              border-color var(--dur-1) var(--ease);
}
.tb-back:hover { background: var(--tint-brand-h); border-color: var(--tint-brand-line-h); }
.tb-back:active { transform: scale(.98); }
.tb-back svg { width: 14px; height: 14px; }
@media (max-width: 640px) {
  /* 窄屏顶栏挤：只留箭头，文字交给 title 提示 */
  .tb-back span { display: none; }
  .tb-back { padding: 0 8px; }
}
.topbar .spacer { flex: 1; }
.hamburger { display: none; }

.content { flex: 1; padding: 20px; max-width: 1320px; width: 100%; }

.page-head { margin-bottom: 16px; }
.page-head h2 { font-size: 19px; font-weight: 700; letter-spacing: -.3px; }
.page-head p { font-size: 13px; color: var(--text-2); margin-top: 3px; }

/* =============================================================
   卡片
   ============================================================= */
.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-1);
  margin-bottom: 20px;   /* 规范：板块间距 20px，靠留白分层而不是靠边框 */
  overflow: hidden;
}
.card-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 14px 16px;
  border-bottom: 1px solid var(--line);
  flex-wrap: wrap;
}
.card-head h3 { font-size: 14.5px; font-weight: 650; }
.card-head .desc { font-size: 12px; color: var(--text-3); font-weight: 400; }
.card-head .spacer { flex: 1; }
.card-body { padding: 16px; }
.card-body.tight { padding: 0; }

.grid { display: grid; gap: 16px; }
.grid.cols-2 { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
.grid.cols-3 { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }

/* =============================================================
   统计卡
   ============================================================= */
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(132px, 1fr)); gap: 12px; }
.stat {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: 14px 15px;
  box-shadow: var(--sh-1);
  position: relative;
  overflow: hidden;
}
/* 左侧彩条只用于语义态（.ok/.warn/.danger）。
   原来每张卡都画一条，看板上 6 张零值卡并排就是 6 条灰杠，
   又脏又没信息量（用户 2026-10-28：「6 个灰竖条看着脏」）。
   默认态改由数字颜色表达，卡片本身保持干净。 */
.stat::after {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
  background: transparent;
}
/* 彩条只在「有问题」时出现，且统一用红 —— 见下面数字配色的说明。
   ⚠️ .stat.ok 不再画条：正常状态不该有颜色信号。
   ⚠️ 两套前缀都要写：后台用 `.stat.warn`、看板用 `.stat-warn`，
      少写一套就会出现「这页有红条、那页只有红字」的不一致。 */
.stat.warn::after,   .stat.danger::after,
.stat-warn::after,   .stat-danger::after { background: var(--danger); }
.stat .num {
  font-size: 25px; font-weight: 700; line-height: 1.15;
  font-variant-numeric: tabular-nums; letter-spacing: -.5px;
  /* 显式写死黑色：统计卡常被包在 <a> 里（配置概况那四张），
     不写就会继承链接的品牌蓝 —— 用户 2026-10-28 报的「27/5/230 是蓝的」
     就是这个原因，不是有人给数字上的色。 */
  color: var(--text);
}
.stat .label { font-size: 12.5px; color: var(--text-2); margin-top: 2px; }

/* 统计数字配色（2026-10-28 用户定）
   ---------------------------------------------------------------
   原话：「没必要都用彩色的，就用黑色的，有问题的红色就行了」。
   改前 ok=绿 / warn=橙 / danger=红 三色并存，一屏数字五颜六色，
   反而看不出哪个要处理 —— 颜色只该用来标记「异常 / 待处理」。
   规则：**默认黑；只有待办和异常红**。绿色不再出现（正常不是"奖"）。
   ⚠️ 两套类名前缀都要覆盖：后台总览用 `.stat.warn`（空格），
      看板用 `.stat-warn`（连字符，来自 board.py 的 CARD_DEFS）。 */
.stat.warn .num,   .stat.danger .num,
.stat-warn .num,   .stat-danger .num { color: var(--danger); }

/* =============================================================
   按钮
   ============================================================= */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  padding: 8px 15px;
  border: 1px solid var(--line-2);
  border-radius: var(--r);
  background: var(--surface);
  color: var(--text);
  font-family: inherit;
  font-size: 13.5px;
  font-weight: 500;
  line-height: 1.4;
  cursor: pointer;
  white-space: nowrap;
  text-decoration: none;
  /* 时长/曲线走令牌（规范 200–300ms，原来 130ms 按下去发"急"） */
  transition: background var(--dur-1) var(--ease),
              border-color var(--dur-1) var(--ease),
              box-shadow var(--dur-1) var(--ease),
              color var(--dur-1) var(--ease),
              transform var(--dur-1) var(--ease);
}
.btn:hover { background: var(--surface-2); border-color: var(--text-3); text-decoration: none; }
/* 按下缩小而不是下移（规范）：下移会带动相邻元素的重绘错觉 */
.btn:active { transform: scale(.98); }
.btn:focus-visible { outline: 2px solid var(--brand-500); outline-offset: 2px; }
.btn:disabled, .btn.disabled { opacity: .45; cursor: not-allowed; transform: none; }

/* 主按钮 = 「半透明品牌底 + 品牌深字」（tinted，2026-10-28 用户要求）
   ---------------------------------------------------------------
   实心亮蓝在白卡上像贴上去的一张色纸，比周围内容还抢眼。改成 14% 半透明底
   后层级降一档，但仍一眼能认出这是主操作；hover 才加深到 22%。

   色值全部来自 --tint-brand* 令牌（见文件顶部）。
   🔴 **别再在任何一个 css 里手写 rgba(0,113,227,…) 或 --brand-600 实心底**
      —— 那就是 10-28 之前"三种蓝并存"的来源。 */
.btn-primary {
  background: var(--tint-brand);
  border-color: var(--tint-brand-line);
  color: var(--tint-brand-fg);
  box-shadow: none;
}
.btn-primary:hover { background: var(--tint-brand-h); border-color: var(--tint-brand-line-h); }
.btn-primary:active { background: var(--tint-brand-a); }

/* 肯定操作 = tinted 绿（2026-10-28 统一）
   原来 .btn-ok / .learn-ok / .btn-confirm / .member-card .go-booking 四处
   各写一个实心绿，深浅还都不一样。现在全部走同一组 --tint-ok* 令牌。 */
.btn-ok { background: var(--tint-ok); border-color: var(--tint-ok-line); color: var(--tint-ok-fg); }
.btn-ok:hover { background: var(--tint-ok-h); border-color: var(--tint-ok-line-h); }
.btn-ok:active { background: var(--tint-ok-a); }

/* 破坏性操作 = tinted 红（2026-10-28 统一）
   ⚠️ 曾经同一语义有三种外观：本文件是"白底红边红字"、ai.css 里同名
      .btn-danger 被覆盖成"实心红"、.gbtn.is-danger 又是第三种实心红。
      现在只保留这一份，ai.css 里那份已删。
      实心红只留给「删除确认弹窗的最终那一下」（.btn-danger-solid）。 */
.btn-danger { background: var(--surface); border-color: var(--tint-danger-line); color: var(--tint-danger-fg); }
.btn-danger:hover { background: var(--tint-danger); border-color: var(--tint-danger-line-h); }
.btn-danger:active { background: var(--tint-danger-a); }
.btn-danger-solid { background: var(--danger); border-color: var(--danger); color: #fff; }
.btn-danger-solid:hover { background: var(--danger-text); border-color: var(--danger-text); }

.btn-ghost { border-color: transparent; background: transparent; color: var(--text-2); }
.btn-ghost:hover { background: var(--surface-3); border-color: transparent; color: var(--text); }
.btn-ghost:active { background: var(--surface-3); }

/* 尺寸三档（2026-10-28 锁定；不许在页面里再手写 padding 造第四档）
      lg  11/20 · 15px       md  8/15 · 13.5px（= .btn 默认）      sm  5/10 · 12.5px
   🔴 三档圆角都是 --r，小按钮也不缩圆角 —— 12px 在 ~26px 高的按钮上
      视觉已接近胶囊，再缩回 6px 就明显发方（10-28 之前就是这样）。 */
.btn-sm { padding: 5px 10px; font-size: 12.5px; border-radius: var(--r); }
.btn-lg { padding: 11px 20px; font-size: 15px; border-radius: var(--r); }
.btn-block { width: 100%; }
.btn-icon { padding: 0; width: 32px; height: 32px; }

.btn-group { display: flex; gap: 8px; flex-wrap: wrap; }

/* =============================================================
   表格
   ============================================================= */
.table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
table { width: 100%; border-collapse: collapse; font-size: 13.5px; }
th, td { padding: 11px 14px; text-align: left; vertical-align: middle; }
th {
  font-size: 11.5px; font-weight: 650; color: var(--text-2);
  background: var(--surface-2);
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
  letter-spacing: .2px;
  position: sticky; top: 0; z-index: 1;
}
tbody tr { border-bottom: 1px solid var(--line); }
tbody tr:last-child { border-bottom: none; }
tbody tr:hover { background: var(--brand-50); }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }
td.actions { text-align: right; white-space: nowrap; }
td.actions .btn { margin-left: 6px; }
td strong { font-weight: 650; }
.cell-sub { font-size: 11.5px; color: var(--text-3); margin-top: 1px; }

/* =============================================================
   标签 / 徽标
   ============================================================= */
/* =============================================================
   emoji 图标单色化（2026-10-28）
   底部标签栏 / 顶部入口 / 空状态一共 7 个彩色 emoji 当图标
   （🏠💰📅📝✏️🤖⚙️），一屏花花绿绿，是"廉价感"最大的来源。
   这些 emoji 散落在 30 个模板里，逐个换 SVG 成本高又容易漏，
   所以先用 CSS 统一去色。想恢复彩色：删掉下面这一条即可。
   ⚠️ 只作用于 .ico（纯装饰容器），不影响语义色文字。
   ============================================================= */
/* 残留的 emoji 压成灰（2026-10-28）
   ⚠️ 不能写成 `.ico { filter: grayscale(1) }` —— 新图标是 SVG，
      颜色走 currentColor，父级一加灰度，**激活态的品牌色就被滤掉了**
      （底栏当前项会变成灰色，用户一眼能看出来）。
   ⚠️ :has() 老浏览器不支持时整条规则失效 = emoji 保持原色，
      属于"退化成改之前"，不会坏。 */
.ico:not(:has(svg)) { filter: grayscale(1); }

/* 线性图标（2026-10-28）：模板里 {{ ic.svg() }} 渲染出的 <svg>。
   没传 size 的按父元素 font-size 缩放（1em）—— 同一个图标在底栏 20px、
   侧边栏 15px、按钮 15px 各就各位，不用为每个位置单独调。 */
.svg-ico { display: block; flex-shrink: 0; }
.svg-ico:not([width]) { width: 1em; height: 1em; }
.nav-item .ico, .fnav-item .ico, .tab-item .ico {
  display: flex; align-items: center; justify-content: center;
}
/* 图标直接塞进文字流时（按钮里、提示条里），SVG 是 block 元素，
   会自己占一行并把文字挤下去 —— 必须转 inline 并手动抬一点对齐基线。 */
.btn .svg-ico,
.btn-copy .svg-ico,
.notice .svg-ico,
.empty .svg-ico { display: inline-block; vertical-align: -2px; }
.btn-copy .svg-ico { margin-right: 5px; }
/* 统计卡里的图标（label 前的小图标、num 位置的占位图标） */
.stat .label .svg-ico { display: inline-block; vertical-align: -2px; margin-right: 3px; }
.stat .num .svg-ico { display: inline-block; color: var(--text-3); }
/* 提示条/空状态里的图标块居中 */
.notice .ico, .empty .ico { display: flex; align-items: center; justify-content: center; }
/* .empty .ico 在 626 行有 display:block（后定义，优先级更高），
   所以那个大图标靠 margin 自动居中，不靠 flex。 */
.empty .ico .svg-ico { margin: 0 auto; }

/* 胶囊标签（全站唯一来源，2026-10-28 统一）
   ⚠️ admin.css 里曾重复定义过一份 4px 方角的，把它盖成了小方块，
   导致前台是胶囊、后台是方块。那份已删，别再往别处复制这段。 */
.tag {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 3px 9px;
  border-radius: var(--r-full);
  font-size: 11.5px; font-weight: 500;
  white-space: nowrap;
  line-height: 1.5;
}
.tag-brand  { background: var(--brand-50); color: var(--brand-700); }
.tag-ok     { background: var(--ok-bg);     color: var(--ok-text); }
.tag-warn   { background: var(--warn-bg);   color: var(--warn-text); }
.tag-danger { background: var(--danger-bg); color: var(--danger-text); }
.tag-info   { background: var(--info-bg);   color: var(--info-text); }
.tag-gray   { background: var(--surface-3); color: var(--text-3); }

.color-dot {
  width: 9px; height: 9px; border-radius: 50%;
  display: inline-block; flex-shrink: 0;
  border: 1px solid rgba(0,0,0,.08);
}

.badge-count {
  display: inline-grid; place-items: center;
  min-width: 18px; height: 18px; padding: 0 5px;
  border-radius: var(--r-full);
  background: var(--brand-600); color: #fff;
  font-size: 10.5px; font-weight: 700;
}
.badge-count.gray { background: var(--line-2); }

/* =============================================================
   表单
   ============================================================= */
.form-row { margin-bottom: 14px; }
.form-row:last-child { margin-bottom: 0; }
.form-row > label {
  display: block;
  font-size: 12.5px; font-weight: 600;
  color: var(--text-2);
  margin-bottom: 5px;
}
.form-row .hint { font-size: 11.5px; color: var(--text-3); margin-top: 5px; line-height: 1.5; }
.form-row .req { color: var(--danger); margin-left: 2px; }

input[type=text], input[type=number], input[type=password], input[type=date],
input[type=email], input[type=search], select, textarea {
  width: 100%;
  padding: 9px 11px;
  border: 1px solid var(--line-2);
  border-radius: var(--r);
  font-family: inherit;
  font-size: 14px;
  color: var(--text);
  background: var(--surface);
  transition: border-color .13s, box-shadow .13s;
  -webkit-appearance: none;
  appearance: none;
}
select {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='none' stroke='%239CA3AF' stroke-width='1.6' d='M2.5 4.5L6 8l3.5-3.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 11px center;
  padding-right: 30px;
}
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--brand-500);
  box-shadow: 0 0 0 3px var(--brand-50);
}
input:disabled, select:disabled, textarea:disabled {
  background: var(--surface-3); color: var(--text-3); cursor: not-allowed;
}
input[type=color] {
  width: 42px; height: 38px; padding: 3px;
  border: 1px solid var(--line-2); border-radius: var(--r-sm);
  background: var(--surface); cursor: pointer; flex-shrink: 0;
}
textarea { resize: vertical; min-height: 82px; line-height: 1.6; }

.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.form-grid.cols-3 { grid-template-columns: repeat(3, 1fr); }
.form-grid .full { grid-column: 1 / -1; }

.checkbox {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 13.5px; cursor: pointer; user-select: none;
}
.checkbox input[type=checkbox] {
  width: 17px; height: 17px; flex-shrink: 0;
  accent-color: var(--brand-600); cursor: pointer; margin: 0;
}
.checkbox-list { display: flex; flex-direction: column; gap: 2px; }
.checkbox-list label {
  display: flex; align-items: center; gap: 8px;
  padding: 6px 8px; border-radius: var(--r);
  font-size: 13.5px; cursor: pointer;
}
.checkbox-list label:hover { background: var(--surface-2); }

.picker {
  max-height: 190px; overflow-y: auto;
  border: 1px solid var(--line); border-radius: var(--r);
  padding: 6px; background: var(--surface-2);
}

/* =============================================================
   提示条
   ============================================================= */
.notice {
  display: flex; align-items: flex-start; gap: 9px;
  padding: 11px 14px;
  border-radius: var(--r);
  font-size: 13.5px;
  margin-bottom: 16px;
  border: 1px solid transparent;
  line-height: 1.6;
}
.notice .ico { flex-shrink: 0; font-size: 15px; line-height: 1.4; }
.notice-info   { background: var(--brand-50); color: #3730A3; border-color: var(--brand-200); }
.notice-warn   { background: var(--warn-bg); color: var(--warn-text); border-color: var(--warn-border); }
.notice-danger { background: var(--danger-bg); color: var(--danger-text); border-color: var(--danger-border); }
.notice-ok     { background: var(--ok-bg); color: var(--ok-text); border-color: var(--ok-border); }

/* =============================================================
   空状态
   ============================================================= */
.empty {
  text-align: center;
  padding: 44px 20px;
  color: var(--text-3);
}
.empty .ico { font-size: 32px; opacity: .5; display: block; margin-bottom: 8px; }
.empty p { font-size: 13.5px; }

/* =============================================================
   工具栏 / 筛选
   ============================================================= */
.toolbar {
  display: flex; gap: 10px; align-items: center;
  flex-wrap: wrap; margin-bottom: 14px;
}
.toolbar input[type=text], .toolbar input[type=search], .toolbar select {
  width: auto; min-width: 150px; flex: 0 1 220px;
}
.toolbar .spacer { flex: 1; }
.toolbar .count { font-size: 12.5px; color: var(--text-3); }

/* =============================================================
   弹窗
   ============================================================= */
.modal {
  position: fixed; inset: 0; z-index: 200;
  /* 遮罩别超 25%（规范）：50% 黑一压上来整页发闷，还盖住了入场动画 */
  background: rgba(0, 0, 0, .25);
  backdrop-filter: blur(2px);
  display: flex; align-items: center; justify-content: center;
  padding: 16px;
  animation: fade .15s ease-out;
}
.modal[hidden] { display: none; }
@keyframes fade { from { opacity: 0 } to { opacity: 1 } }
/* 弹窗入场（规范：200ms、scale .97→1、translateY 8px→0，别用回弹曲线） */
@keyframes pop { from { opacity:0; transform: translateY(8px) scale(.97) } to { opacity:1; transform:none } }

.modal-box {
  background: var(--surface);
  /* 弹窗圆角比卡片再大一级（26px）—— 规范里这是"层级"的一部分 */
  border-radius: var(--r-xl);
  width: 100%; max-width: 580px;
  max-height: 88vh;
  display: flex; flex-direction: column;
  box-shadow: var(--sh-3);
  animation: pop var(--dur-1) ease-out;
}
.modal-box.wide { max-width: 780px; }
.modal-head {
  display: flex; align-items: center; gap: 10px;
  padding: 15px 18px;
  border-bottom: 1px solid var(--line);
  flex-shrink: 0;
}
.modal-head h3 { font-size: 15.5px; font-weight: 650; flex: 1; }
.modal-body { padding: 18px; overflow-y: auto; flex: 1; }
.modal-foot {
  display: flex; gap: 10px; justify-content: flex-end;
  padding: 13px 18px;
  border-top: 1px solid var(--line);
  flex-shrink: 0;
  background: var(--surface-2);
  border-radius: 0 0 var(--r-lg) var(--r-lg);
}
/* 弹窗关闭键。`.modal-x` 是看板弹窗用的另一个名字（2026-10-28 发现它
   **从来没被定义过** —— 显示成原生 <button> 的系统灰方块）。合并到同一组，
   以后再来"又一个 X 按钮"直接复用，别新建类。 */
.close-x, .modal-x {
  background: none; border: none;
  font-size: 22px; line-height: 1;
  color: var(--text-3); cursor: pointer;
  padding: 0 4px; border-radius: var(--r);
  font-family: inherit;
  transition: color var(--dur-1) var(--ease), background var(--dur-1) var(--ease);
}
.close-x:hover, .modal-x:hover { color: var(--text); background: var(--surface-3); }

/* =============================================================
   Toast
   ============================================================= */
.toast {
  position: fixed;
  left: 50%; bottom: 26px;
  transform: translateX(-50%) translateY(16px);
  background: var(--text); color: #fff;
  padding: 11px 20px;
  border-radius: var(--r);
  font-size: 13.5px;
  z-index: 999;
  opacity: 0;
  pointer-events: none;
  transition: opacity .2s, transform .2s;
  max-width: 88vw;
  text-align: center;
  box-shadow: var(--sh-3);
}
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
.toast.error { background: var(--danger); }
.toast.success { background: var(--ok); }

/* =============================================================
   登录页
   ============================================================= */
.login-wrap {
  min-height: 100vh;
  min-height: 100dvh;
  display: grid; place-items: center;
  padding: 20px;
  background:
    radial-gradient(1000px 500px at 15% -10%, var(--brand-100), transparent 60%),
    radial-gradient(800px 400px at 100% 100%, var(--brand-100), transparent 55%),
    var(--bg);
}
.login-box {
  background: var(--surface);
  border-radius: var(--r-lg);
  padding: 32px 28px;
  width: 100%; max-width: 372px;
  box-shadow: var(--sh-3);
  border: 1px solid var(--line);
}
.login-logo {
  width: 48px; height: 48px;
  border-radius: var(--r-lg);
  background: linear-gradient(135deg, var(--brand-500), var(--brand-700));
  color: #fff;
  display: grid; place-items: center;
  font-size: 22px; font-weight: 700;
  margin: 0 auto 14px;
}
.login-box h1 { font-size: 19px; text-align: center; letter-spacing: -.3px; }
.login-box .sub { font-size: 12.5px; color: var(--text-3); text-align: center; margin: 3px 0 24px; }
.login-err {
  background: var(--danger-bg); color: var(--danger-text);
  padding: 10px 13px; border-radius: var(--r);
  font-size: 13.5px; margin-bottom: 16px;
  border: 1px solid var(--danger-border);
}
.login-box .btn { margin-top: 18px; }
.login-hint {
  margin-top: 20px; padding-top: 16px;
  border-top: 1px dashed var(--line-2);
  font-size: 12px; color: var(--text-3);
  text-align: center; line-height: 1.8;
}

/* =============================================================
   杂项
   ============================================================= */
.mono { font-family: var(--font-mono); font-size: 12.5px; }
.muted { color: var(--text-3); }
.small { font-size: 12px; }
.nowrap { white-space: nowrap; }
.right { text-align: right; }
.center { text-align: center; }
.mt-0 { margin-top: 0 } .mt-1 { margin-top: 8px } .mt-2 { margin-top: 16px }
.mb-0 { margin-bottom: 0 } .mb-1 { margin-bottom: 8px } .mb-2 { margin-bottom: 16px }
.flex { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.flex-between { display: flex; gap: 10px; align-items: center; justify-content: space-between; }
.flex-col { display: flex; flex-direction: column; gap: 10px; }
.stat .num.sm { font-size: 17px; }
.num-inline { width: auto; padding: 6px 9px; }
.logo-danger { background: linear-gradient(135deg, #EF4444, var(--danger-text)); }

.divider { height: 1px; background: var(--line); margin: 16px 0; border: none; }

.secret-field { display: flex; gap: 6px; align-items: center; }
.secret-field input { flex: 1; }

.spinner {
  display: inline-block;
  width: 14px; height: 14px;
  border: 2px solid var(--brand-200);
  border-top-color: var(--brand-600);
  border-radius: 50%;
  animation: spin .7s linear infinite;
  vertical-align: -2px;
}
@keyframes spin { to { transform: rotate(360deg) } }

.loading-block { padding: 40px; text-align: center; color: var(--text-3); }

/* 遮罩（手机端抽屉） */
.scrim {
  position: fixed; inset: 0; z-index: 55;
  background: rgba(17,24,39,.45);
  animation: fade .15s ease-out;
}
.scrim[hidden] { display: none; }

/* =============================================================
   看板配置页专用
   ============================================================= */

/* 板块卡片：可拖动排序 */
.sec-card {
  border: 1px solid var(--line);
  border-radius: var(--r);
  margin-bottom: 12px;
  background: var(--surface);
  overflow: hidden;
}
.sec-card.off { background: var(--surface-2); }
.sec-card.off .sec-card-body { opacity: .5; }

.sec-card-head {
  display: flex; align-items: center; gap: 12px;
  padding: 12px 14px;
  background: var(--surface-2);
  border-bottom: 1px solid var(--line);
}
.sec-card.off .sec-card-head { border-bottom-color: transparent; }
.sec-card-head .grip {
  cursor: grab; color: var(--text-3); font-size: 15px;
  user-select: none; padding: 0 2px; line-height: 1;
}
.sec-card-head .grip:active { cursor: grabbing; }
.sec-card-head .title { font-size: 13.5px; font-weight: 650; flex: 1; }
.sec-card-head .title small { display: block; font-size: 11.5px; color: var(--text-3); font-weight: 400; }
.sec-card-body { padding: 14px; }

.sec-card.dragging { opacity: .4; }
.sec-card.drag-over { border-color: var(--brand-500); box-shadow: 0 0 0 2px var(--brand-50); }

/* 可勾选条目（统计卡 / 字段 / 待办类型） */
.opt-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(148px, 1fr));
  gap: 6px;
}
.opt-item {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 11px;
  border: 1px solid var(--line);
  border-radius: var(--r);
  font-size: 13px;
  cursor: pointer;
  background: var(--surface);
  transition: border-color .13s, background .13s;
  user-select: none;
}
.opt-item:hover { border-color: var(--line-2); background: var(--surface-2); }
.opt-item input { width: 16px; height: 16px; margin: 0; accent-color: var(--brand-600); flex-shrink: 0; }
.opt-item.checked { border-color: var(--brand-500); background: var(--brand-50); }
.opt-item .label { flex: 1; min-width: 0; }
.opt-item .label small { display: block; font-size: 11px; color: var(--text-3); }

/* 兼容：JS 里按 class 查询的钩子 */
.card-item, .todo-type, .biz-item, .ofield { display: contents; }
.sec-enabled, .sec-sort { flex-shrink: 0; }
.hotel-picker { max-height: 190px; overflow-y: auto; border: 1px solid var(--line);
                border-radius: var(--r); padding: 6px; background: var(--surface-2); }
.hotel-picker label {
  display: flex; align-items: center; gap: 8px;
  padding: 6px 8px; border-radius: var(--r);
  font-size: 13px; cursor: pointer;
}
.hotel-picker label:hover { background: var(--surface); }
.hotel-picker input { width: 16px; height: 16px; margin: 0; accent-color: var(--brand-600); }

.todo-limit { width: 92px; }

/* =============================================================
   兼容别名（旧类名 → 新组件）
   保留以便页面渐进改造过程中不出现样式断裂
   ============================================================= */
.page-title { font-size: 19px; font-weight: 700; letter-spacing: -.3px; margin-bottom: 3px; }
.page-desc  { font-size: 13px; color: var(--text-2); margin-bottom: 16px; }

.dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%;
       margin-right: 5px; vertical-align: middle;
       border: 1px solid rgba(0,0,0,.08); }

.tag-on   { background: var(--ok-bg);     color: var(--ok-text); }
.tag-off  { background: var(--surface-3); color: var(--text-3); }

.color-input { display: flex; align-items: center; gap: 8px; }
.color-input .mono { color: var(--text-2); }

/* 圆形头像（旧 .user 相关） */
.badge-role {
  background: var(--brand-50); color: var(--brand-700);
  padding: 1px 8px; border-radius: var(--r-full);
  font-size: 11px; font-weight: 600;
}

@media (max-width: 860px) {
  .page-title { font-size: 17px; }
}

/* =============================================================
   响应式：手机端
   ============================================================= */
@media (max-width: 860px) {
  .sidebar {
    transform: translateX(-100%);
    transition: transform .22s cubic-bezier(.4,0,.2,1);
    box-shadow: var(--sh-3);
    width: 250px;
  }
  .sidebar.open { transform: translateX(0); }

  .main { margin-left: 0; }

  .hamburger {
    display: inline-flex; align-items: center; justify-content: center;
    width: 36px; height: 36px;
    border: 1px solid var(--line);
    background: var(--surface);
    border-radius: var(--r);
    cursor: pointer;
    font-size: 17px;
    /* 2026-10-28：原来没设 color，图标吃到浏览器默认纯黑 → 比旁边的
       .btn 深一档，看着像另一个组件的按钮。 */
    color: var(--text-2);
    flex-shrink: 0;
  }

  .content { padding: 14px 12px calc(var(--tabbar-h) + 20px); }
  .topbar { padding: 0 12px; }
  .topbar h1 { font-size: 15.5px; }
  .topbar .sub { display: none; }

  .form-grid, .form-grid.cols-3 { grid-template-columns: 1fr; }

  .stats { grid-template-columns: repeat(2, 1fr); gap: 10px; }
  .stat { padding: 12px 13px; }
  .stat .num { font-size: 21px; }

  /* 手机端不缩小卡片圆角（2026-10-28）：原来这里降成 --r(10px)，
     而同一页的订单卡是 --r-lg(14px) → 一屏里两种卡片圆角不同，
     用户一眼看出"这块是方的、那块是圆的"。 */
  .card { border-radius: var(--r-lg); }
  .card-head { padding: 12px 13px; }
  .card-body { padding: 13px; }

  th, td { padding: 10px 11px; }
  .hide-mobile { display: none !important; }

  .modal { padding: 0; align-items: flex-end; }
  .modal-box {
    max-width: 100%; max-height: 92vh;
    border-radius: var(--r-xl) var(--r-xl) 0 0;
  }
  .modal-foot { flex-direction: column-reverse; border-radius: 0; }
  .modal-foot .btn { width: 100%; }

  .toolbar input[type=text], .toolbar input[type=search], .toolbar select {
    flex: 1 1 100%;
  }
}

@media (min-width: 861px) {
  .scrim { display: none !important; }
}

@media (min-width: 1100px) {
  .content { padding: 24px; }
}

/* 可点击的统计卡 */
.stat-link { text-decoration: none; display: block; transition: box-shadow .13s, transform .06s; }
.stat-link:hover { text-decoration: none; box-shadow: var(--sh-2); transform: translateY(-1px); }

/* 吸底保存条 */
.sticky-save { position: sticky; bottom: 12px; z-index: 20; box-shadow: var(--sh-3); }
@media (max-width: 860px) { .sticky-save { bottom: 8px; } }

/* 板块开关占满 */
.sec-toggle { flex: 1; min-width: 0; }

/* =============================================================
   看板专用
   ============================================================= */

/* 统计卡补充说明文字 */
.stat .stat-desc {
  font-size: 11px;
  color: var(--text-3);
  margin-top: 3px;
  line-height: 1.4;
}
/* 零值卡：不画条、不投影、数字降灰 —— 让它"退到后面去"，
   一屏里读者先看到有数的卡。 */
.stat-muted { box-shadow: none; }
.stat-muted .num { color: var(--text-3); }

/* 经营数据格 */
.biz-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(168px, 1fr));
  gap: 14px;
  /* 每张卡等高、内容顶部对齐 —— 否则「内容多的卡」把整行撑高，
     旁边的卡跟着出现大片空白（用户截图里就是这个现象） */
  align-items: start;
}
.biz-item {
  display: flex;
  flex-direction: column;
}
.biz-item {
  padding: 12px 14px;
  background: var(--surface-2);
  border-radius: var(--r);
  border: 1px solid var(--line);
}
.biz-label { font-size: 12px; color: var(--text-2); font-weight: 500; }
.biz-value {
  font-size: 24px; font-weight: 700; line-height: 1.25;
  font-variant-numeric: tabular-nums; letter-spacing: -.5px;
  margin-top: 2px;
}
.biz-profit { color: var(--ok); }
.biz-sub { font-size: 11.5px; color: var(--text-3); margin-top: 2px; }

/* 渠道占比条 */
.bar-row {
  display: flex; align-items: center; gap: 10px;
  margin-bottom: 8px; font-size: 13px;
}
.bar-name { width: 72px; flex-shrink: 0; color: var(--text-2); }
.bar-track {
  flex: 1; height: 8px;
  background: var(--surface-3);
  border-radius: var(--r-full);
  overflow: hidden;
  min-width: 60px;
}
.bar-fill {
  height: 100%;
  background: var(--brand-500);
  border-radius: var(--r-full);
  transition: width .3s;
}
.bar-val {
  width: 190px; flex-shrink: 0; text-align: right;
  font-size: 12px; color: var(--text-2);
  font-variant-numeric: tabular-nums;
}

/* 看板筛选栏 */
.board-filters {
  border-bottom: 1px solid var(--line);
  background: var(--surface-2);
}
/* ---- 桌面端：筛选框一直看得见（2026-10-07 用户要求）----
   用户原话：「看板页面订单列表下面的筛选，能不能加一个框，
   现在连个框都没有，很突兀」

   ⚠️ 根因：`.filter-panel { display: none }` 和
   `.filter-toggle { display: flex !important }`
   **都写在 max-width:760px 的媒体查询里**，
   所以桌面端上「筛选」按钮没有样式（点了没反应），
   筛选框也没有任何样式 —— 看起来就像「连个框都没有」。
   现在把桌面端的基础样式提到媒体查询外面。 */
.filter-panel {
  display: block;
  padding: 12px 14px;
}
.filter-panel .filter-form {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  align-items: center;
}
/* 桌面端不显示那个「筛选 ▾」折叠按钮 */
.filter-toggle { display: none !important; }
.filter-form {
  display: flex; gap: 8px; flex-wrap: wrap; align-items: center;
}
/* 输入框给个明显的边框，不然在浅灰底上像没有 */
.filter-form input[type=text],
.filter-form select {
  border: 1px solid var(--line, var(--line));
  border-radius: var(--r);
  background: var(--surface, #fff);
  padding: 7px 10px;
  font-family: inherit;
  font-size: 13px;
  color: var(--text, var(--text));
}
.filter-form input[type=text]:focus,
.filter-form select:focus {
  outline: 2px solid var(--brand-400, var(--info));
  outline-offset: -1px;
}
.filter-form input[type=text]::placeholder { color: var(--text-3, var(--text-3)); }
.filter-form input[type=text] { flex: 1 1 180px; min-width: 140px; }
.filter-form select { flex: 0 1 auto; width: auto; min-width: 104px; }
.filter-form .btn { flex-shrink: 0; }

/* 订单表 */
.order-table td { vertical-align: top; }
.order-table .num { font-weight: 500; }

/* 毛利正负 */
.profit-plus { color: var(--ok); font-weight: 600; }
.profit-zero { color: var(--text-3); }

/* 复制按钮（表格内的小复制，如订单详情）
   ⚠️ 2026-10-28：原来**没有 border-radius** —— 这就是用户看到的"直角按钮"
      之一。小按钮也必须圆，且圆角跟按钮同一档 --r-sm。
   ⚠️ 这个类名在 booking.css 里也有一份（那边是 100% 宽的整行大按钮）。
      两者不冲突：booking.css 只在 booking 页加载，那边后加载会覆盖这里。 */
.copy-btn {
  padding: 2px 7px; font-size: 11px;
  border: 1px solid var(--line-2); border-radius: var(--r-sm);
  background: var(--surface); color: var(--text-3); line-height: 1.5;
  font-family: inherit; cursor: pointer;
  transition: color var(--dur-1) var(--ease),
              border-color var(--dur-1) var(--ease),
              background var(--dur-1) var(--ease);
}
.copy-btn:hover {
  color: var(--tint-brand-fg);
  border-color: var(--tint-brand-line);
  background: var(--tint-brand);
}

/* 手机端：订单表改卡片式显示，避免横向滚动 */
@media (max-width: 720px) {
  .bar-row { flex-wrap: wrap; }
  .bar-name { width: auto; }
  .bar-val { width: 100%; text-align: left; }
  /* 🔴 手机端筛选栏：flex 换成 column 后，桌面那条 `input{flex:1 1 180px}`
     的 basis 作用在**垂直**轴 → 搜索框被撑成 180px 高（看板上那个占半屏的
     大空白框就是这么来的）。
     ⚠️ 光写 `.filter-form > *` 压不住：桌面那条是 `.filter-form input[type=text]`
     （特异性 0,1,1）比 `> *`（0,1,0）高，必须用同样的选择器覆盖。 */
  .filter-form > * { flex: 0 0 auto; width: 100%; }
  .filter-form input[type=text],
  .filter-form select { width: 100%; flex: 0 0 auto; }
  .biz-grid { grid-template-columns: repeat(2, 1fr); }
  .biz-value { font-size: 20px; }
}

/* =============================================================
   订单详情页
   ============================================================= */
.detail-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 14px 18px;
}
.detail-item .detail-label {
  font-size: 11.5px;
  color: var(--text-3);
  font-weight: 500;
  margin-bottom: 2px;
}
.detail-item .detail-value {
  font-size: 14px;
  color: var(--text);
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}
.detail-item .detail-value .cell-sub { margin: 0; }
.detail-item.full { grid-column: 1 / -1; }

/* =============================================================
   手机端卡片式
   -------------------------------------------------------------
   手机是主要使用场景，表格在窄屏必须横向滚动且字小。
   -------------------------------------------------------------
   【已被新方案取代】早期做法是同一份数据渲染两遍（表格 + 卡片），
   用 CSS 按屏宽切换。现在订单列表只有卡片一套（照搬旧版版式），
   电脑端也用卡片，不再渲染表格。

   下面保留的只有统计卡放大、筛选折叠、待补卡片等手机端适配。
   ============================================================= */

/* ---------- 手机端 ---------- */
@media (max-width: 760px) {

  /* 🔴 iOS 输入聚焦放大（2026-10-11）：Safari 对字号 <16px 的输入框
     聚焦时会强制缩放页面。全局输入框是 14px，必须在这里提到 16px。
     （项目自检规范铁律：iOS 输入 ≥16px。） */
  input[type=text], input[type=number], input[type=password],
  input[type=date], input[type=email], input[type=search],
  select, textarea { font-size: 16px; }

  /* 统计卡：两列大卡，字号放大 */
  .stats {
    grid-template-columns: repeat(2, 1fr);
    gap: 10px;
  }
  .stat { padding: 13px 14px; }
  .stat .num { font-size: 26px; }
  .stat .label { font-size: 13px; font-weight: 500; }
  .stat .stat-desc { font-size: 11.5px; }

  /* 筛选面板收进可展开区 */
  .board-filters { padding: 0; }
  .filter-form { padding: 0; }
  /* 手机端：默认收起用「筛选 ▾」展开（桌面端不需要） */
  .filter-toggle {
    display: flex !important;
    align-items: center; gap: 8px;
    width: 100%;
    padding: 12px 14px;
    background: none; border: none;
    font-family: inherit; font-size: 14px; font-weight: 500;
    color: var(--text);
    cursor: pointer;
    text-align: left;
  }
  .filter-toggle .caret { margin-left: auto; color: var(--text-3); transition: transform .2s; }
  .filter-toggle[aria-expanded="true"] .caret { transform: rotate(180deg); }
  .filter-panel { display: none; padding: 0 14px 14px; }
  .filter-panel.show { display: block; }
  .filter-form { flex-direction: column; align-items: stretch; }
  /* 同上：column 方向下必须收掉 grow/basis，否则高度不受控 */
  .filter-form > * { flex: 0 0 auto; width: 100%; }
  .filter-form input[type=text],
  .filter-form select { width: 100%; min-width: 0; flex: 0 0 auto; }
  .filter-form .btn { padding: 11px; font-size: 15px; }

  /* ---------- 订单卡片 ---------- */
  .order-card {
    display: block;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    padding: 13px 14px;
    margin-bottom: 10px;
    text-decoration: none;
    color: inherit;
    box-shadow: var(--sh-1);
  }
  .order-card:active { background: var(--surface-2); }

  .oc-head {
    display: flex; align-items: center; gap: 8px;
    margin-bottom: 6px;
  }
  .oc-who { display: flex; align-items: baseline; gap: 6px; flex: 1; min-width: 0; }
  .oc-name { font-size: 16px; font-weight: 600; }
  .oc-team { font-size: 12px; color: var(--text-3); }

  .oc-where {
    display: flex; align-items: baseline; gap: 8px;
    font-size: 13.5px; margin-bottom: 3px;
  }
  .oc-hotel { font-weight: 500; }
  .oc-room { color: var(--text-2); font-size: 12.5px; }

  .oc-dates {
    display: flex; align-items: center; gap: 8px;
    font-size: 13px; color: var(--text-2);
    margin-bottom: 9px;
  }
  .oc-nights {
    background: var(--surface-3);
    padding: 1px 6px; border-radius: var(--r-full);
    font-size: 11.5px; color: var(--text-2);
  }

  /* 三个金额并排，字大 */
  .oc-money {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 8px;
    padding: 10px 0;
    border-top: 1px solid var(--line);
    border-bottom: 1px solid var(--line);
    margin-bottom: 9px;
  }
  .oc-item { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
  .oc-label { font-size: 11px; color: var(--text-3); }
  .oc-value {
    font-size: 15px; font-weight: 600;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
  }
  .oc-sub { font-size: 10.5px; color: var(--text-3); }
  .oc-partial { font-size: 12.5px; color: var(--text-2); font-weight: 500; }
  .oc-none { font-size: 12.5px; color: var(--text-3); font-weight: 400; }
  .oc-warn { color: var(--warn); font-size: 13px; }

  .oc-foot {
    display: flex; align-items: center; gap: 6px;
    flex-wrap: wrap;
    font-size: 12px;
  }
  .oc-spacer { flex: 1; }
  .oc-profit { color: var(--text-2); font-weight: 500; }
  .oc-profit.up { color: var(--ok); }
  .oc-conf { font-size: 12px; color: var(--text-2); }

  /* ---------- 待补卡片 ---------- */
  .todo-card {
    background: var(--surface);
    border: 1px solid var(--line);
    border-left: 3px solid var(--warn);
    border-radius: var(--r);
    padding: 12px 13px;
    margin-bottom: 10px;
  }
  .tc-head {
    display: flex; align-items: center; gap: 8px;
    margin-bottom: 4px;
  }
  .tc-who { display: flex; align-items: center; gap: 6px; flex: 1; min-width: 0; }
  .tc-name { font-size: 15.5px; font-weight: 600; }
  .tc-date { font-size: 12.5px; color: var(--text-2); }
  .tc-hotel {
    font-size: 13px; color: var(--text-2);
    margin-bottom: 8px;
  }
  .tc-nights {
    background: var(--surface-3);
    padding: 1px 6px; border-radius: var(--r-full);
    font-size: 11px; margin-left: 4px;
  }
  .tc-missing { display: flex; gap: 5px; flex-wrap: wrap; margin-bottom: 10px; }
  .tc-btn { padding: 10px; font-size: 14.5px; }

  /* 看板配置类页面在手机上也放大一点 */
  .opt-grid { grid-template-columns: 1fr; }
  .biz-grid { grid-template-columns: repeat(2, 1fr); }
}

/* =============================================================
   前台业务外壳（看板 / 查价 / 排班 / 订单）
   -------------------------------------------------------------
   与后台管理完全分离：这里没有侧边栏，只有顶部导航 + 底部标签栏。
   看板是纯展示页，任何写操作都不在这里发生。
   ============================================================= */

.front {
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  background: var(--bg);
}

/* ---------- 氛围底（毛玻璃的"壁纸"，2026-10-28） ----------
   毛玻璃要有东西可模糊才看得见：纯 var(--bg) 上 backdrop-filter 等于没开。
   这几团光晕就是 iOS 里壁纸的角色 —— 模糊之后变成一层很淡的色晕，
   吸顶栏/底栏"透出颜色"才能被眼睛识别成玻璃。
   ⚠️ 必须同时给 body 和 .front：.front 自带不透明底色，会把 body 的背景盖掉；
      两者都用 background-attachment:fixed，弧度才在视口里对齐（不跟着滚动跑）。
   ⚠️ 光晕浓度是调出来的：太淡等于没有，太浓会让白卡片边上发脏。 */
body, .front {
  background-color: var(--bg);
  background-image:
    radial-gradient(1180px 700px at 10% -12%, var(--amb-a), transparent 68%),
    radial-gradient(980px 620px at 104% 0%, var(--amb-b), transparent 70%),
    radial-gradient(900px 600px at 46% 108%, var(--amb-c), transparent 72%);
  background-repeat: no-repeat;
  background-attachment: fixed;
}

/* 手机窄屏单独降浓度：光斑半径是写死的 px，视口越小占比越大 ——
   同一套配方在 390px 上会铺满整屏（实测整页发紫，像换了主题色）。
   配方不变、只把剂量减半，手机和电脑才是"同一种感觉"而不是两套配色。 */
@media (max-width: 760px) {
  :root {
    --amb-a: color-mix(in srgb, var(--brand-500) 5%, transparent);
    --amb-b: color-mix(in srgb, var(--brand-600) 3%, transparent);
    --amb-c: color-mix(in srgb, var(--brand-200) 4%, transparent);
  }
  body, .front {
    background-image:
      radial-gradient(520px 360px at 4% -8%, var(--amb-a), transparent 70%),
      radial-gradient(480px 320px at 106% -4%, var(--amb-b), transparent 72%),
      radial-gradient(440px 300px at 50% 104%, var(--amb-c), transparent 74%);
  }
}

/* ---------- 顶栏 ---------- */
.front-top {
  position: sticky; top: 0; z-index: 30;
  display: flex; align-items: center; gap: 10px;
  height: var(--topbar-h); padding: 0 16px;
  background: var(--glass-bg);
  backdrop-filter: var(--glass-sat) blur(var(--glass-blur));
  -webkit-backdrop-filter: var(--glass-sat) blur(var(--glass-blur));
  background-image: linear-gradient(180deg, rgba(255,255,255,.62), rgba(255,255,255,.05));
  box-shadow: var(--glass-rim), 0 1px 2px rgba(16,24,40,.04);
  border-bottom: 1px solid rgba(16,24,40,.07);
}
.front-brand { display: flex; align-items: center; gap: 9px; min-width: 0; }
.front-brand .logo {
  /* ⚠️ 宽度用 min-width 而不是定宽：字标 brand_short 可能是 1 个字
     （"李"）也可能是 2 个字（"订单"）。定宽 28px 会把第二个字裁掉
     （2026-10-28 放大截图确认）。改成"最小 28px + 左右内边距"，
     1 个字仍是正方块，2 个字自然长成小圆角矩形，都不会裁。 */
  min-width: 28px; width: auto; height: 28px; padding: 0 4px;
  flex-shrink: 0;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--brand-600); color: #fff;
  border-radius: var(--r-sm);
  font-size: 13.5px; font-weight: 700;
  white-space: nowrap; letter-spacing: -1px;
}
/* 图片 logo 必须回到定宽（不然 img 会按原图尺寸撑开，那张是 816×1080） */
.front-brand .logo-img { width: 28px; padding: 0; }
.front-brand .ftitle {
  font-size: 16px; font-weight: 700; letter-spacing: -.3px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* 上传的白标 logo 不一定是正方形，contain 保证不变形。
   ⚠️ 模板里这个 img 的 alt 必须是空字符串（装饰性图片）——
     之前写 alt="{{ app_name }}"，一旦图片 404，浏览器会把
     "李老板的订单可视化系统" 这串字直接画在 28px 的框里盖住标题。 */
.logo-img { object-fit: contain; }
.front-top-right {
  margin-left: auto;
  display: flex; align-items: center; gap: 8px;
}
/* 顶栏两个入口（2026-10-08）：「改单系统」+「AI 助手」
   —— 之前 AI 助手被塞在「管理」按钮的 <a> 内部，非法嵌套导致
   浏览器拆开标签、按钮跑版；手机端 .front-admin-btn{display:none}
   还把它一起隐藏了。现在两个都是独立的 <a>，各有自己的类。
   ⚠️ 这两个类是**并列**的，手机端只隐藏 .front-admin-btn，
   两个入口都要留着（底部标签栏也有，但顶栏是用户指定的入口位置）。 */
.front-ai-btn,
.front-oe-btn {
  display: inline-flex; align-items: center; gap: 5px;
  text-decoration: none;
}
.front-ai-btn .ico,
.front-oe-btn .ico { font-size: 14px; line-height: 1; }
/* 屏幕很窄时（手机横屏/分屏）只留图标，省得把标题挤没 */
@media (max-width: 400px) {
  .front-ai-btn .txt,
  .front-oe-btn .txt { display: none; }
}

/* ---------- 电脑端导航 ---------- */
.front-nav {
  display: flex; gap: 2px;
  padding: 0 16px;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  position: sticky; top: 54px; z-index: 25;
  overflow-x: auto;
}
/* 顶栏导航原来是「品牌色文字 + 2px 下划线」，和侧边栏的选中态是两套语言。
   2026-10-28 统一成同一枚深色胶囊（下划线在滚动时还会被 sticky 遮罩切掉）。 */
.fnav-item {
  display: flex; align-items: center; gap: 6px;
  padding: 7px 13px;
  margin: 6px 2px;
  border-radius: var(--r);
  font-size: 14px; font-weight: 500;
  color: var(--text-2);
  text-decoration: none;
  white-space: nowrap;
  transition: background var(--dur-1) var(--ease), color var(--dur-1) var(--ease);
}
.fnav-item:hover { background: var(--surface-3); color: var(--text); text-decoration: none; }
.fnav-item.active {
  background: var(--nav-active-bg);
  color: var(--nav-active-fg);
  font-weight: 600;
  box-shadow: var(--nav-active-shadow);
}
.fnav-item.active:hover { background: var(--nav-active-bg); color: var(--nav-active-fg); }
.fnav-item .ico { font-size: 15px; }

/* ---------- 主内容 ---------- */
.front-main {
  flex: 1;
  width: 100%;
  max-width: 1320px;
  margin: 0 auto;
  padding: 20px 20px 40px;
}

/* ---------- 手机端底部标签栏 ---------- */
.tabbar {
  display: none;
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
  background: var(--glass-bg);
  backdrop-filter: var(--glass-sat) blur(var(--glass-blur));
  -webkit-backdrop-filter: var(--glass-sat) blur(var(--glass-blur));
  background-image: linear-gradient(180deg, rgba(255,255,255,.55), rgba(255,255,255,.10));
  border-top: 1px solid rgba(16,24,40,.07);
  padding-bottom: env(safe-area-inset-bottom);
  /* 顶边一道高光 + 向上扩散的软阴影，让它"浮"在内容上 */
  box-shadow: var(--glass-rim), 0 -8px 24px rgba(16,24,40,.07);
}
.tab-item {
  position: relative;          /* 选中态深色胶囊的定位基准 */
  flex: 1;
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  padding: 7px 2px 6px;
  text-decoration: none;
  color: var(--text-3);
  min-height: var(--tabbar-h);
  justify-content: center;
  /* 底栏在毛玻璃上，点击反馈用透明度而不是灰底（灰底会把玻璃弄脏） */
  -webkit-tap-highlight-color: transparent;
}
.tab-item .ico { font-size: 20px; line-height: 1; }
/* ⚠️ 底栏第一项的文字是 board_label（白标可改成任意名字）。
   名字一长（比如「订单可视化」5 个字）就会折成 3 行、撑破 58px 的
   高度、从底栏溢出来压住左边 —— 3.6 实测截图确认过。
   这里三重保险：不换行、超出裁掉、占满不撑破。
   （白标客户如果名字实在太长，可以让他在设置里改短一点，
     长名字留给页面标题用。） */
.tab-item { min-width: 0; }
.tab-item .lb {
  font-size: 11px; font-weight: 500;
  max-width: 100%;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* 底栏选中态 = 深色实心胶囊（2026-10-28，与侧边栏同一套语言）
   做法：用 ::before 画一块深色圆角当背景，而不是给 .tab-item 自己上底色 ——
   底色会铺满整格（贴到底栏上下边缘），胶囊必须四周留白才像"被选中"。
   ⚠️ ::before 不能加 z-index:-1：.tabbar 有 backdrop-filter，会创建新的
      层叠上下文，负层级会跑到玻璃后面整个消失。所以给内容加 z-index:1。 */
.tab-item.active::before {
  content: '';
  position: absolute;
  /* 上下 5 / 左右 6：块 44×48，比正方形略瘦，接近"竖着的小药丸" */
  inset: 5px 6px;
  background: var(--nav-active-bg);
  /* 圆角用 --r(12px)：这块只有 44×48，试过 --r-lg(20px)，
     弧线吃掉两侧留白，「看板」两个字贴到弧上反而挤。
     侧边栏那种宽菜单项才用得上更大的圆角比例。 */
  border-radius: var(--r);
  box-shadow: var(--nav-active-shadow);
}
.tab-item > .ico, .tab-item > .lb { position: relative; z-index: 1; }
.tab-item.active { color: var(--nav-active-fg); }
/* 深底上 700 会糊成一团，600 就够 —— 白字本来对比度就高 */
.tab-item.active .lb { font-weight: 600; }
.tab-item:active { background: var(--surface-2); }
.tab-item.active:active { background: transparent; }
.tab-item:active::before { opacity: .85; }

/* ---------- 手机端抽屉 ---------- */
.front-drawer {
  display: none;
  position: fixed; top: 0; bottom: 0; left: 0; z-index: 60;
  width: 262px;
  background: var(--surface);
  box-shadow: var(--sh-3);
  flex-direction: column;
  transform: translateX(-100%);
  transition: transform .22s cubic-bezier(.4,0,.2,1);
}
.fd-head {
  display: flex; align-items: center; gap: 8px;
  padding: 14px 14px 12px;
  border-bottom: 1px solid var(--line);
}
.fd-head .sidebar-user { margin-bottom: 0; flex: 1; }
.fd-close {
  width: 30px; height: 30px; flex-shrink: 0;
  border: none; background: none;
  font-size: 16px; color: var(--text-3);
  cursor: pointer; border-radius: var(--r);
}
.fd-close:hover { background: var(--surface-3); color: var(--text); }
.fd-nav { flex: 1; overflow-y: auto; padding: 10px 0; }
.fd-nav .sidebar-foot { padding: 10px 14px 14px; border-top: 1px solid var(--line); }

/* =============================================================
   前台 · 手机端
   ============================================================= */
@media (max-width: 860px) {
  .front-nav { display: none; }
  .front-top { height: 50px; padding: 0 12px; }
  .front-brand .ftitle { font-size: 15.5px; }
  .front-main {
    padding: 13px 12px calc(var(--tabbar-h) + 24px);
  }
  .tabbar { display: flex; }
  .front-drawer { display: flex; }
  /* 抽屉默认 translateX(-100%) 移出屏幕，打开时必须移回来。
     漏了这条手机上点汉堡按钮没反应。 */
  .front-drawer.open { transform: translateX(0); }
  /* 手机端隐藏「管理」按钮（底部标签栏已有入口），
     但**必须保留 AI 助手** —— 之前 AI 助手被嵌套在这个按钮的
     <a> 内部，一起被隐藏了，用户在手机上找不到 AI 入口。
     .front-ai-btn 不在这里隐藏。 */
  .front-admin-btn { display: none; }
  .scrim {
    position: fixed; inset: 0; z-index: 55;
    background: rgba(17,24,39,.42);
  }
  /* 顶栏操作按钮在手机端放大，方便点 */
  .front-top .btn { padding: 7px 12px; font-size: 13px; }
}

@media (min-width: 1100px) {
  .front-main { padding: 24px 24px 48px; }
}

/* ---------- 业务页占位（功能开发中） ---------- */
.placeholder-card { max-width: 620px; margin: 30px auto; }
.placeholder-body { text-align: center; padding: 34px 24px 30px; }
.ph-ico { font-size: 40px; line-height: 1; display: block; margin-bottom: 12px; }
.placeholder-body h2 { font-size: 19px; font-weight: 700; margin-bottom: 8px; letter-spacing: -.3px; }
.ph-desc { font-size: 14px; color: var(--text-2); line-height: 1.65; margin-bottom: 22px; }
.ph-plan {
  text-align: left;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r);
  padding: 14px 16px;
  margin-bottom: 22px;
}
.ph-plan-title { font-size: 12px; font-weight: 600; color: var(--text-3); margin-bottom: 8px; }
.ph-plan ul { margin: 0; padding-left: 18px; }
.ph-plan li { font-size: 13.5px; color: var(--text-2); line-height: 1.8; }

/* 后台侧边栏顶部的「返回看板」
   ⚠️ 原来这里是「品牌浅底 + 品牌字」，和下面选中项的深色胶囊并列时
      **两个块都在说"我是当前"** —— 用户看不出到底选中的是哪个。
      选中态已经交给深色胶囊了，这个入口就退回中性描边。 */
.sidebar-back {
  margin: 0 10px 10px;
  width: calc(100% - 20px);
  border: 1px solid var(--line);
  background: var(--surface-2);
  color: var(--text-2);
  font-weight: 600;
}
.sidebar-back:hover { background: var(--surface-3); color: var(--text); text-decoration: none; }
.sidebar-back.active { background: var(--nav-active-bg); color: var(--nav-active-fg); border-color: transparent; }

/* =============================================================
   管理页底部导航条（2026-10-08）
   -------------------------------------------------------------
   ⚠️ 用户要的是**底部那排图标按钮**（跟看板页最下面一样）。
      我第一轮做成了「侧边栏顶部两列网格」，用户看过效果后明确
      选了底部 —— 侧边栏那版已撤，**别再加回来**。

   🔴 tabbar 是 `position: fixed; bottom: 0`，会盖住内容。
      手机断点里 `.content` 已经有 `padding-bottom: calc(var(--tabbar-h)+20px)`
      （app.css:815），桌面端 tabbar 不显示（display:none）所以不用管。
      这里只给管理页做**手机端**的额外保险 + 桌面端明确关闭。
   ============================================================= */
/* 桌面端不显示（跟前台一样，tabbar 只在手机端出现） */
.admin-tabbar { display: none; }

@media (max-width: 760px) {
  .admin-tabbar { display: flex; }
  /* 🔴 管理页的 .content 是**另一条规则**（app.css:246 padding:20px），
     上面那个 760 断点里的 padding-bottom 只管 .front-main（前台），
     管不到这里 -> 底栏会盖住最后一行的按钮（保存/提交）。
     所以必须给管理页的 .content 单独留一个 tabbar 高度。
     ⚠️ 不用 `:has()` / 兄弟选择器兜底 —— tabbar 是最后一个元素、
        `~ *` 匹配不到任何东西，`:has()` 兼容性也不稳。
        直接在模板里给 .content 加 content-with-tabbar 最可靠。 */
  .content-with-tabbar { padding-bottom: calc(var(--tabbar-h) + 24px); }
  /* 6 个按钮比前台的 5 个更挤，字号略缩，不然「改单」要换行 */
  .admin-tabbar .lb { font-size: 10.5px; }
  .admin-tabbar .tab-item { padding: 7px 1px 6px; }
}

/* =============================================================
   每日订单趋势小卡片
   ============================================================= */
.trend {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 6px;
}
.trend-day {
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  padding: 8px 2px 7px;
  border-radius: var(--r);
  background: var(--surface-2);
  border: 1px solid var(--line);
  text-decoration: none; color: inherit;
  transition: background .12s, transform .06s;
}
.trend-day:hover { text-decoration: none; background: var(--surface-3); }
.trend-day:active { transform: scale(.97); }
.trend-day.today { background: var(--brand-50); border-color: var(--brand-200); }
.td-num {
  font-size: 17px; font-weight: 700; line-height: 1.1;
  font-variant-numeric: tabular-nums;
  color: var(--text);
}
.trend-day.today .td-num { color: var(--brand-700); }
.td-bar-wrap { height: 31px; display: flex; align-items: flex-end; }
.td-bar {
  width: 16px; border-radius: 3px 3px 0 0;
  background: var(--brand-200);
  min-height: 3px;
}
.trend-day.today .td-bar { background: var(--brand-500); }
.td-label {
  font-size: 11px; font-weight: 600;
  color: var(--text-2);
  font-variant-numeric: tabular-nums;
}
.td-week { font-size: 9.5px; color: var(--text-3); }

/* 手机端：7 列在窄屏太挤，改成可横向滑动 */
@media (max-width: 760px) {
  .trend {
    display: flex;
    overflow-x: auto;
    gap: 7px;
    padding-bottom: 4px;
    -webkit-overflow-scrolling: touch;
  }
  .trend-day { flex: 0 0 54px; }
  .td-bar { width: 22px; }
  .td-num { font-size: 18px; }

  /* 卡片在手机上字号再放大一档 */
  .member-name { font-size: 16px; }
  .hotel-tag { font-size: 14px; }
  .card-dates { font-size: 13px; }
  .card-info-row { font-size: 13px; gap: 4px 12px; }
  .card-info-row .info-label { font-size: 12px; }
  .preauth-stamp { font-size: 11px; padding: 3px 8px; }
  .btn-copy { padding: 11px; font-size: 14px; }
  .price-compare { font-size: 13px; }
}

/* =============================================================
   待办忽略
   -------------------------------------------------------------
   「去补齐」旁边加一个「忽略」：这一项不用补（房卡房/老客/其他），
   忽略后从待补区消失，但记录留着可查。
   ============================================================= */

/* 手机端两个按钮并排，都够大能点 */
.tc-actions { display: flex; gap: 8px; }
.tc-actions .tc-btn { flex: 1; }

/* 弹窗 */
.modal-sm { max-width: 400px; }
.ig-target {
  font-size: 13px; color: var(--text-2);
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r);
  padding: 9px 11px;
  margin-bottom: 14px;
}
.ig-target b { color: var(--text); }

/* 理由选项：整行可点，不用精准点小圆点 */
.ig-reasons { display: flex; flex-direction: column; gap: 7px; }
.ig-opt {
  display: flex; align-items: center; gap: 9px;
  padding: 11px 12px;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--surface);
  cursor: pointer;
  font-size: 14px;
  transition: border-color .12s, background .12s;
}
.ig-opt:hover { background: var(--surface-2); }
.ig-opt input { flex-shrink: 0; width: 17px; height: 17px; margin: 0; accent-color: var(--brand-600); }
.ig-opt:has(input:checked) {
  border-color: var(--brand-500);
  background: var(--brand-50);
  font-weight: 500;
}
.ig-req { color: var(--danger); font-size: 11px; font-weight: 400; }
.ig-hint {
  font-size: 11.5px; color: var(--text-3);
  line-height: 1.6; margin-top: 12px;
}
.ig-err { font-size: 13px; color: var(--danger); padding: 8px 0; }

/* 🔴 2026-10-28 删掉了这里重复定义的第二份 .btn-ghost。
   它把上面的 `background: transparent; border-color: transparent` 覆盖成
   「白底 + 1px 描边」，于是全站 18 处 .btn-ghost（前台顶栏四个入口、
   看板「忽略」）**实际渲染成普通次要按钮**，ghost 这个语义等于不存在。
   —— 同名类在同一个文件里定义两次，后一份赢，这是最容易漏的一类坑
   （和 .tag / .btn-danger 是同一个毛病）。幽灵按钮只有上面那一份。 */

/* 「忽略」按钮的额外微调：低调，不跟「去补齐」抢注意力 */
.tc-actions .btn-ghost { color: var(--text-3); }

@media (max-width: 760px) {
  .tc-actions .tc-btn { padding: 11px; font-size: 14.5px; }
  .ig-opt { padding: 13px 12px; font-size: 15px; }   /* 触摸目标够大 */
}

/* =============================================================
   订单卡片 · 三层信息结构
   -------------------------------------------------------------
   用户明确要求（手机为主，99% 时间在手机上）：
     放大：酒店 / 会员名 / 同住人 / 入住退房日期 / 确认号
     缩小：房型 / 金额 / 结账 / 渠道 / 备注 / 比价时间

   所以不是「一个信息行里平铺所有字段」，而是分三层：
     L1  谁 · 住哪     —— 酒店名 + 会员名 + 同住人（最大字号）
     L2  行程 + 确认号 —— 日期区间 + 晚数 + 确认号（次大）
     L3  其他细节      —— 全部小字淡色（弱化，不抢视线）
   ============================================================= */

.order-cards { display: flex; flex-direction: column; gap: 9px; }
/* 🔴 卡片套卡片：父卡的 overflow:hidden 会把子卡的 20px 圆角**削成直角**。
   2026-10-28 实测待补区 `.card-body.tight > .order-cards` 的 left 和父卡完全贴合，
   用户看到的就是"这块卡片怎么是方的"。给一圈 12px 让子卡圆角完整露出来。
   只作用于「直接子级」—— 订单列表里包在 .day-block 内的那套已有自己的 padding。 */
.card-body.tight > .order-cards { padding: 12px; }

.order-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: 12px 14px 11px;
  position: relative;
  overflow: hidden;
  box-shadow: var(--sh-1);
}
/* 键盘可达性：Tab 聚焦订单卡（是 <a>）时给可见焦点圈 */
.order-card:focus-visible {
  outline: 2px solid var(--brand-500);
  outline-offset: 2px;
}
/* 左侧色条：比价结果 */
.order-card::before {
  content: '';
  position: absolute; top: 0; left: 0; bottom: 0;
  width: 3px; background: var(--line-2);
}
.order-card.pc-bad::before  { background: var(--danger); }
.order-card.pc-good::before { background: var(--ok); }
.order-card.pc-na::before   { background: var(--warn); }

/* ========== L1：谁 · 住哪 ========== */
.oc-l1 {
  display: flex; align-items: flex-start;
  justify-content: space-between; gap: 10px;
}
.oc-l1-left { min-width: 0; flex: 1; }

/* 酒店名：最显眼的定位信息 */
.oc-where {
  display: flex; align-items: center; gap: 6px;
  margin-bottom: 3px;
}
.oc-hotel {
  font-size: 17px; font-weight: 700;
  letter-spacing: -.2px;
  line-height: 1.25;
  color: var(--text);
}
.oc-badge-today {
  flex-shrink: 0;
  font-size: 10.5px; font-weight: 600;
  background: var(--ok); color: #fff;
  padding: 1px 6px; border-radius: var(--r-full);
}

/* 会员名 + 同住人 */
.oc-who {
  display: flex; align-items: baseline; gap: 9px;
  flex-wrap: wrap;
}
.oc-name {
  font-size: 15.5px; font-weight: 600;
  color: var(--text-2);
  letter-spacing: -.1px;
}
/* 同住人：显示在会员名字下面一行（用户要求） */
.oc-co {
  display: flex; align-items: baseline; gap: 5px;
  margin-top: 3px;
  font-size: 14px; color: var(--text-2);
}
.oc-co-label {
  font-size: 10.5px; color: var(--text-3);
  font-weight: 500;
  background: var(--surface-3);
  padding: 1px 6px; border-radius: var(--r-full);
  flex-shrink: 0;
}

/* 预授权印章 */
.preauth-stamp {
  flex-shrink: 0;
  font-size: 10.5px; font-weight: 700;
  color: rgba(220, 38, 38, .72);
  border: 2px solid rgba(220, 38, 38, .42);
  border-radius: var(--r-sm);
  padding: 2px 7px;
  transform: rotate(-6deg);
  letter-spacing: 1px;
  background: rgba(254, 242, 242, .3);
  white-space: nowrap;
}
/* 已刷 =绿色章（事情办完了）；待刷 =红色章（要去办）。
   ⚠️ 只有两档，别再加「待确认」——
   用户 2026-10-07 原话：「就不应该出现未表态的选项，
   要不就是刷了，要不就是没刷，弄一个未表态干啥？」
   之前造了个黄章当第三档，它既不提醒也不消失，纯属噪声。 */
.preauth-stamp.done {
  color: rgba(22, 163, 74, .78);
  border-color: rgba(22, 163, 74, .45);
  background: rgba(240, 253, 244, .45);
}
/* 🔴 2026-10-11 修复：这里原来拖着孤立的 `pointer-events: none; }`
   两行（历史编辑残留），游离在任何规则之外，会把下一条 .oc-l2
   规则的 CSS 解析搞坏（订单卡日期行样式随机失效）。已删。 */

/* ========== L2：行程 + 确认号 ========== */
.oc-l2 {
  display: flex; align-items: center;
  justify-content: space-between; gap: 10px;
  flex-wrap: wrap;
  margin-top: 7px;
  padding-top: 7px;
  border-top: 1px solid var(--line);
}
.oc-dates {
  display: flex; align-items: baseline; gap: 4px;
  font-size: 15px; font-weight: 600;
  color: var(--text);
  font-variant-numeric: tabular-nums;
  letter-spacing: -.2px;
}
.oc-arrow { color: var(--text-3); font-weight: 400; }
.oc-nights {
  font-size: 12px; font-weight: 500;
  color: var(--text-2);
  background: var(--surface-3);
  padding: 1px 7px; border-radius: var(--r-full);
  margin-left: 3px;
}

/* 确认号前缀标签：光一个数字容易被当成订单号/价格，必须写明「确认号」 */
.oc-conf-label {
  font-size: 11.5px; color: var(--text-3); font-weight: 400;
  margin-right: 3px;
}
/* 确认号 —— 2026-10-07 用户反馈「手机上字太小」，加大一号
   15px -> 17px。确认号要跟酒店对账，是订单卡上最该看清的字段之一。
   2026-10-11：改 mono + tabular —— 确认号是纯数字/字母串，
   等宽字形逐位对齐，电话里报号逐位念不会看串行。 */
.oc-conf-num {
  font-size: 17px; font-weight: 700;
  color: var(--text);
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: .6px;
}
.oc-conf-none {
  font-size: 12.5px; color: var(--text-3);
}
.oc-conf-wait {
  font-size: 12.5px; font-weight: 600;
  color: var(--warn);
  background: var(--warn-bg);
  padding: 2px 8px; border-radius: var(--r-full);
}

/* ========== L3：小字细节 ========== */
.oc-l3 {
  display: flex; flex-wrap: wrap;
  gap: 3px 11px;
  margin-top: 7px;
}
.oc-s {
  display: inline-flex; align-items: baseline; gap: 3px;
  /* 2026-10-07：11.5px 手机上太小，提到 12.5px（标签 i 也一起提） */
  font-size: 12.5px; line-height: 1.55;
  color: var(--text-2);
}
.oc-s i {
  font-style: normal;
  font-size: 11.5px; color: var(--text-3);
}
.oc-s b { font-weight: 600; color: var(--text); }
.oc-s b.up { color: var(--ok); }
/* 亏损单必须标红 —— 旧口径漏算手续费，会把亏损单显示成盈利 */
.oc-s b.down { color: var(--danger); }

/* 毛利（2026-10-07）
   手机端为主，所以**不做 hover 展开**（手机上没有鼠标，永远不触发）。
   亏损单加个浅红底 + 左侧红边，扫一眼就能挑出来，不依赖颜色分辨。 */
.oc-s-profit.is-loss {
  background: var(--danger-bg);
  border-radius: var(--r-full);
  padding: 0 4px;
  margin: 0 -4px;
  box-shadow: inset 2px 0 0 var(--danger);
}

/* 毛利拆分（现金 / 积分）—— 2026-10-07
   看板的「毛利」现在是「现金 + 积分」合计（跟利润页的合计列一致）。
   ⚠️ 2026-10-07 用户反馈：**第一版做成分两行，手机上排版乱了**。
      手机端一行放不下「现金 ¥-10.32」+「积分 ¥84.04（2001 分）」，
      竖排会把卡片撑得很高、和其他字段错位。
      -> 改成**一行横排**、允许换行时整块整体换行，不拆散。
      -> 字号从 10.5 提到 11.5（跟 .oc-s 一致），太小看不清。 */
.oc-s-profit-split {
  flex-basis: 100%;        /* 整块占一行，但内部横排 */
  gap: 3px 10px;
  align-items: baseline;
  line-height: 1.6;
}
.oc-s-profit-split .ps-line {
  font-family: var(--font-mono);
  font-size: 11.5px;
  color: var(--text-3);
  white-space: nowrap;
}
.oc-s-profit-split .ps-pts { color: var(--brand-700); }
.oc-s-profit-split i {
  font-size: 10.5px; color: var(--text-3);
}
/* 手机端屏幕窄，扣掉左右 padding 后放不下两个金额就整块换行，
   不要让「现金」和「积分」各自孤零零地掉到不同行。 */
@media (max-width: 400px) {
  .oc-s-profit-split { gap: 2px 8px; }
  .oc-s-profit-split .ps-line { font-size: 11px; }
}

.oc-s em { font-style: normal; color: var(--text-3); }
.oc-s em.ok   { color: var(--ok); }
.oc-s em.warn { color: var(--warn); }
/* 备注占整行 */
.oc-s-full { flex: 1 0 100%; }
.oc-s-full i { margin-top: 0; }

/* ========== 比价条 ========== */
.price-compare {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px;
  margin-top: 8px;
  padding: 6px 10px;
  border-radius: var(--r);
  font-size: 12.5px;
}
/* 🔴 浅底上的文字一律用 -text 档：主色是给白底/深底用的，
   放在自己的浅底上对比度反而不够（#FF9500 压在 #FFF4E3 上几乎糊掉）。 */
.price-compare.bad  { background: var(--danger-bg); border: 1px solid var(--danger-border); color: var(--danger-text); }
.price-compare.good { background: var(--ok-bg);     border: 1px solid var(--ok-border);   color: var(--ok-text); }
.price-compare.na   { background: var(--warn-bg);   border: 1px solid var(--warn-border); color: var(--warn-text); }
.price-compare.bad b, .price-compare.good b, .price-compare.na b { color: inherit; }
.price-compare .pc-left {
  display: flex; align-items: baseline; gap: 5px;
  flex-wrap: wrap; min-width: 0;
}
/* 「平台 · 档位」标签（2026-10-29 按价格来源比价）：
   一条比价条先说清是哪个平台的哪一档 —— 「洲际 · 联名卡价 663」
   和「携程 · BAR价 848」是两回事，不能说成同一个「比价」。 */
.price-compare .pc-plat {
  font-family: var(--font-sans, sans-serif);
  font-size: 10.5px; font-weight: 600;
  padding: 1px 7px; border-radius: var(--r-full);
  background: var(--surface-3); color: var(--text-2);
  white-space: nowrap; letter-spacing: -0.01em;
}
.price-compare b { font-weight: 700; }
.price-compare .pc-ico { font-size: 11px; }
.price-compare .pc-detail {
  font-size: 10.5px; font-weight: 400; opacity: .72;
  font-family: var(--font-mono);
}
/* ---- 携程数据陈旧提醒（2026-10-07 用户要求）----
   「携程数据更新于 XX:XX」后面跟一个新鲜度标签，
   超过后台设的 price_stale_hours 标黄、超过 price_expire_hours 标红。
   阈值在 settings 里改，模板不写死。
   语气比 .pc-detail 亮一档—— 那个是 opacity .72 的辅助信息，
   这个是要让人一眼看到的提醒，不能比它还淡。 */
.pc-fresh {
  margin-left: 3px;
  padding: 1px 5px;
  border-radius: var(--r-full);
  font-family: var(--font-sans, sans-serif);
  font-size: 10px;
  font-weight: 500;
  line-height: 1.5;
  white-space: nowrap;
  vertical-align: baseline;
  opacity: 1;
}
.pc-fresh-ok     { background: var(--ok-bg);     color: var(--ok); }
.pc-fresh-warn   { background: var(--warn-bg);   color: var(--warn); }
.pc-fresh-danger { background: var(--danger-bg); color: var(--danger); }
.pc-fresh-gray   { background: var(--surface-3); color: var(--text-3); }
/* 过期（>24h）除了颜色，加个边框再强调一次——
   有些屏幕色弱，红/橙分辨度低，靠颜色不够。 */
.pc-fresh-danger { box-shadow: inset 0 0 0 1px currentColor; }
.price-compare .pc-diff {
  font-size: 13px; font-weight: 700; flex-shrink: 0;
  white-space: nowrap;
}

/* ========== 复制按钮（看板卡片整行主操作）==========
   2026-10-28 统一：原来手写 --brand-50 底 + --brand-200 边（相当于自己
   造了一份 8%/16% 的 tint），和 .btn-primary 的 14%/28% 肉眼能看出不是一个东西。
   现在直接吃 --tint-brand*，跟全站主按钮完全同色。 */
.card-footer { margin-top: 9px; }
.btn-copy {
  display: inline-flex; align-items: center; justify-content: center; gap: 5px;
  width: 100%;
  padding: 9px 10px;
  background: var(--tint-brand);
  color: var(--tint-brand-fg);
  border: 1px solid var(--tint-brand-line);
  border-radius: var(--r);
  font-size: 13px; font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  transition: background var(--dur-1) var(--ease),
              border-color var(--dur-1) var(--ease),
              transform var(--dur-1) var(--ease);
}
.btn-copy:hover  { background: var(--tint-brand-h); border-color: var(--tint-brand-line-h); }
.btn-copy:active { background: var(--tint-brand-a); transform: scale(.98); }

/* =============================================================
   手机端：重点信息再放大一档
   ============================================================= */
@media (max-width: 760px) {
  .order-card { padding: 13px 14px 12px; }
  /* 酒店名和会员名是认单的关键，字号要够大 */
  .oc-hotel  { font-size: 18px; }
  .oc-name   { font-size: 16.5px; }
  .oc-co     { font-size: 15.5px; }
  /* 日期和确认号也是重点 */
  .oc-dates  { font-size: 16px; }
  .oc-nights { font-size: 12.5px; }
  .oc-conf-label { font-size: 12.5px; }
  .oc-conf-num { font-size: 16px; }
  .oc-conf-none, .oc-conf-wait { font-size: 13px; }
  /* 细节小字保持小，不跟重点抢 */
  .oc-s { font-size: 12px; }
  .oc-s i { font-size: 11px; }
  .price-compare { font-size: 13.5px; padding: 7px 11px; }
  .price-compare .pc-diff { font-size: 14px; }
  .btn-copy { padding: 12px; font-size: 14.5px; }
  .preauth-stamp { font-size: 11px; padding: 3px 8px; }
}

/* =============================================================
   比价条：文案中性化后的补充样式
   -------------------------------------------------------------
   用户要求：不写内部黑话（新鲜/省/多花），写完整句子。
   「比携程便宜 14 元」是事实陈述，不是「省」这种主观速记。
   ============================================================= */

.price-compare.same { background: var(--surface-2); border: 1px solid var(--line); color: var(--text-2); }
.price-compare.same .pc-diff { color: var(--text-2); font-weight: 600; }

/* 左侧图标已移除，比价条靠文字本身表达 */
.price-compare .pc-left { gap: 7px; }

/* =============================================================
   待补卡片
   -------------------------------------------------------------
   版式与订单卡一致，只多两样：
     左侧色条用橙色（表示待处理）
     右上角缺失项标签
   ============================================================= */
.order-card.todo-card::before { background: var(--warn); }
.order-card.todo-card .oc-hotel { color: var(--text); }

.todo-missing {
  display: flex; flex-direction: column; gap: 3px;
  align-items: flex-end; flex-shrink: 0;
}
.todo-missing .tag { font-size: 10.5px; padding: 1px 6px; white-space: nowrap; }

/* 操作按钮在卡片底部 */
.order-card .tc-actions { margin-top: 9px; }

@media (max-width: 760px) {
  .todo-missing .tag { font-size: 11px; padding: 2px 7px; }
  .order-card .tc-actions .tc-btn { padding: 12px; font-size: 15px; }
}

/* =============================================================
   查价页
   -------------------------------------------------------------
   流程：选酒店（横向按钮）→ 选日期（横向日期条）→ 看价格
   手机优先：全部用横向滑动，不用日历弹窗（手机上日历难点）。
   ============================================================= */

/* ---------- 酒店选择 ---------- */
.hotel-picker {
  display: flex; gap: 7px;
  overflow-x: auto;
  padding-bottom: 4px;
  -webkit-overflow-scrolling: touch;
}
.hotel-chip {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; gap: 6px;
  padding: 9px 13px;
  border-radius: var(--r-full);
  border: 1px solid var(--line);
  background: var(--surface);
  font-size: 13.5px; font-weight: 500;
  color: var(--text-2);
  cursor: pointer;
  font-family: inherit;
  white-space: nowrap;
  transition: background .12s, border-color .12s, color .12s;
}
.hotel-chip:hover { background: var(--surface-2); }
.hotel-chip:active { transform: scale(.97); }
.hc-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--chip-color, var(--brand-500));
  flex-shrink: 0;
}
.hc-name { color: var(--text); }
.hc-rate {
  font-size: 11px; font-weight: 600;
  color: var(--ok);
  background: var(--ok-bg);
  padding: 1px 5px; border-radius: var(--r-full);
}
.hotel-chip.active {
  background: var(--brand-600);
  border-color: var(--brand-600);
  color: #fff;
}
.hotel-chip.active .hc-name,
.hotel-chip.active .hc-rate { color: #fff; }
.hotel-chip.active .hc-rate { background: rgba(255,255,255,.22); }

/* ---------- 日期条 ---------- */
.date-strip {
  display: flex; gap: 5px;
  overflow-x: auto;
  padding-bottom: 4px;
  -webkit-overflow-scrolling: touch;
}
.ds-item {
  flex: 0 0 46px;
  display: flex; flex-direction: column; align-items: center; gap: 1px;
  padding: 7px 2px 6px;
  border-radius: var(--r);
  border: 1px solid var(--line);
  background: var(--surface);
  cursor: pointer;
  font-family: inherit;
  position: relative;
  transition: background .12s, border-color .12s;
}
.ds-item:hover { background: var(--surface-2); }
.ds-item:active { transform: scale(.95); }
.ds-week { font-size: 10px; color: var(--text-3); }
.ds-day  { font-size: 15px; font-weight: 600; color: var(--text); line-height: 1.2; }
.ds-item.is-today .ds-day { color: var(--brand-600); }
/* 有订单的日期打个小点 */
.ds-dot {
  position: absolute; bottom: 3px; left: 50%; transform: translateX(-50%);
  width: 3px; height: 3px; border-radius: 50%;
  background: var(--brand-400, var(--brand-200));
}

.date-summary {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px;
  margin-top: 10px; padding-top: 10px;
  border-top: 1px solid var(--line);
}
.ds-placeholder { font-size: 13px; color: var(--text-3); }
.ds-range {
  font-size: 15.5px; font-weight: 600;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}
.ds-nights { font-size: 12px; color: var(--text-2); margin-top: 1px; }

/* ---------- 房型价格卡 ---------- */
.rate-list { display: flex; flex-direction: column; gap: 9px; }
.rate-card {
  border: 1px solid var(--line);
  border-radius: var(--r);
  padding: 11px 12px 10px;
  background: var(--surface);
}
.rate-card.partial { background: var(--surface-2); }

.rc-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 10px; margin-bottom: 9px;
}
.rc-room { font-size: 14.5px; font-weight: 600; color: var(--text); }
/* 2026-10-07 删掉 .rc-total / .rc-nights / .rcn-* / .rc-avg ——
   每晚价格那一整块已从页面移除（用户明确：报价/找酒店下单/比价全用均价）。
   新的价格行样式见文件下方「查价结果卡」一节。 */

.rc-tip { font-size: 12px; color: var(--warn); }

@media (max-width: 760px) {
  .hotel-chip { padding: 11px 15px; font-size: 14.5px; }
  .ds-item { flex: 0 0 52px; padding: 9px 2px 8px; }
  .ds-day { font-size: 16.5px; }
  .ds-week { font-size: 11px; }
  .rc-room { font-size: 15.5px; }
  .rc-price-9 { font-size: 24px; }
  .rc-price-row { gap: 4px; }
  .rc-foot .btn { padding: 9px 14px; font-size: 14px; }
}

/* =============================================================
   日历弹窗（照抄旧版 RTMP/hotel_price_query.html 的样式）
   ============================================================= */

.calendar-overlay {
  display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0;
  background: rgba(0,0,0,0.3); z-index: 200;
  justify-content: center; align-items: center;
}
.calendar-overlay.show { display: flex; }
.calendar-popup {
  background: white; border-radius: var(--r-lg);
  width: 92%; max-width: 400px; max-height: 80vh;
  overflow-y: auto; padding: 16px;
  animation: calFadeIn 0.2s ease-out;
}
@keyframes calFadeIn {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}

.cal-header {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 8px;
}
.cal-header .cal-title { font-size: 16px; font-weight: 600; }
.cal-nav {
  background: none; border: 1px solid var(--line); border-radius: var(--r);
  width: 32px; height: 32px; cursor: pointer; font-size: 16px;
  display: flex; align-items: center; justify-content: center; color: var(--text-2);
}
.cal-nav:active { background: var(--surface-3); }

.quick-nights { display: flex; gap: 6px; margin-bottom: 10px; flex-wrap: wrap; }
.quick-night-btn {
  padding: 5px 12px; border: 1px solid var(--line); border-radius: var(--r-full);
  font-size: 12px; background: white; color: var(--text-2); cursor: pointer;
}
.quick-night-btn:active, .quick-night-btn.active {
  background: var(--brand-50); border-color: var(--brand-600); color: var(--brand-600); font-weight: 600;
}

.cal-hint {
  text-align: center; font-size: 13px; color: var(--text-2);
  padding: 4px 0 8px;
}
.cal-weekdays {
  display: grid; grid-template-columns: repeat(7, 1fr);
  text-align: center; font-size: 11px; color: var(--text-2);
  margin-bottom: 4px; font-weight: 500;
}
.cal-days { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.cal-day {
  text-align: center; padding: 10px 0; font-size: 14px;
  border-radius: var(--r-sm); cursor: pointer; transition: all 0.15s;
}
.cal-day:active { transform: scale(0.9); }
.cal-day.empty { pointer-events: none; }
.cal-day.past { color: var(--line-2); pointer-events: none; }
.cal-day.selected { background: var(--brand-600); color: white; font-weight: 600; }
.cal-day.in-range { background: var(--brand-50); color: var(--brand-600); border-radius: 0; }
.cal-day.range-start {
  background: var(--brand-600); color: white; font-weight: 600; border-radius: var(--r-sm) 0 0 var(--r-sm);
}
.cal-day.range-end {
  background: var(--brand-600); color: white; font-weight: 600; border-radius: 0 var(--r-sm) var(--r-sm) 0;
}
.cal-day.range-start.range-end { border-radius: var(--r-sm); }
.cal-day.today:not(.selected):not(.range-start):not(.range-end) {
  border: 1px solid var(--brand-600); color: var(--brand-600);
}

.calconfirm-bar { display: flex; gap: 10px; margin-top: 14px; }
.calconfirm-bar .btn-cal {
  flex: 1; padding: 10px;
  border: 1px solid transparent; border-radius: var(--r);
  font-size: 14px; font-weight: 600; cursor: pointer;
  font-family: inherit;
  transition: background var(--dur-1) var(--ease),
              border-color var(--dur-1) var(--ease);
}
/* 取消 = 中性描边（不能实心灰：和旁边 tinted 主按钮并排时，
   实心灰比主按钮还"重"，视线先落在"取消"上，顺序是反的） */
.btn-cal.cancel {
  background: var(--surface);
  border-color: var(--line-2);
  color: var(--text-2);
}
.btn-cal.cancel:hover { background: var(--surface-2); border-color: var(--text-3); }
.btn-cal.confirm {
  background: var(--tint-brand);
  border-color: var(--tint-brand-line);
  color: var(--tint-brand-fg);
}
.btn-cal.confirm:hover:not(:disabled) { background: var(--tint-brand-h); border-color: var(--tint-brand-line-h); }
.btn-cal.confirm:disabled {
  background: var(--surface-3); border-color: transparent;
  color: var(--text-4); cursor: not-allowed;
}

@media (max-width: 400px) {
  .calendar-popup { width: 100%; max-width: 100%; padding: 14px; }
  .cal-day { font-size: 15px; padding: 11px 0; }
}

/* =============================================================
   日期显示条（照抄旧版）
   ============================================================= */
.date-display {
  display: flex; align-items: center; gap: 10px;
  padding: 12px 16px; background: var(--surface-2);
  border: 1px solid var(--line); border-radius: var(--r);
  cursor: pointer; transition: border-color 0.15s;
}
.date-display:active { border-color: var(--brand-600); }
.date-display .date-text { flex: 1; font-size: 15px; font-weight: 600; color: var(--text); }
.date-display .date-text.placeholder { color: var(--text-3); font-weight: 400; }
.date-display .nights-badge {
  background: var(--brand-50); color: var(--brand-600); font-size: 12px;
  padding: 2px 8px; border-radius: var(--r-full); font-weight: 600;
}
.date-display .nights-badge.hidden { display: none; }
.date-display .cal-icon { width: 20px; height: 20px; color: var(--text-3); }

/* =============================================================
   日期区间显示条（入住 / 退房 两格）
   -------------------------------------------------------------
   查价页、排班页、订单制作页共用，放在这里避免各页样式不一致。
   ============================================================= */
/* ---------- 日期区间（入住 / 退房 两格）---------- */
.date-range-box {
  display: flex; align-items: center; gap: 12px;
  padding: 12px 14px;
  background: var(--surface-2);
  border: 1px solid var(--line-2);
  border-radius: var(--r);
  cursor: pointer; user-select: none;
  transition: border-color .12s, background .12s;
}
.date-range-box:active { border-color: var(--brand-500); }
.date-cell { flex: 1; min-width: 0; }
.date-cell .date-label { font-size: 11.5px; color: var(--text-3); }
.date-cell .date-value {
  font-size: 15px; font-weight: 600; color: var(--text); margin-top: 1px;
}
.date-cell .date-value.placeholder { color: var(--text-3); font-weight: 400; }
.date-separator { color: var(--text-3); font-size: 15px; flex-shrink: 0; }
.date-range-box .nights-badge {
  background: var(--brand-50); color: var(--brand-700);
  font-size: 12px; padding: 2px 8px; border-radius: var(--r-full);
  font-weight: 600; flex-shrink: 0;
}
.date-range-box .nights-badge.hidden { display: none; }

/* =============================================================
   价格数据新鲜度提示（查价页）
   -------------------------------------------------------------
   价格随时会变，显示查询时间让人工判断要不要更新。
   ============================================================= */
.fresh-tip {
  font-size: 12.5px; color: var(--text-2);
  background: var(--surface-2);
  border-left: 3px solid var(--brand-500);
  border-radius: var(--r);
  padding: 9px 12px;
  margin-bottom: 12px;
  line-height: 1.7;
}
.fresh-tip b { color: var(--text); font-weight: 600; }

/* =============================================================
   查价结果卡（2026-10-07 按 1.0 口径重做）
   -------------------------------------------------------------
   **不再显示每晚价格**（用户明确：报价/找酒店下单/比价全用均价）。
   改成对齐 1.0 看板的 price-compare 口径：
   「9折价」大字 + 「原价」小字，两个价都摆出来，不用猜。
   ============================================================= */

/* 房型名右边的折扣角标 */
.rc-disc {
  font-size: 11px; font-weight: 600;
  color: var(--brand-600);
  background: var(--brand-50, var(--surface-3));
  border: 1px solid var(--line);
  border-radius: var(--r-full);
  padding: 1px 8px;
  flex-shrink: 0;
  white-space: nowrap;
}

/* 主价格行：左边 9 折大字，右边原价小字 */
.rc-price-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  flex-wrap: wrap;
}
.rc-price-main {
  display: flex;
  align-items: baseline;
  gap: 6px;
  min-width: 0;
}
.rc-price-label {
  font-size: 11.5px; color: var(--text-3);
  flex-shrink: 0;
}
.rc-price-9 {
  font-size: 22px; font-weight: 700;
  color: var(--text);
  font-variant-numeric: tabular-nums;
  line-height: 1.15;
  white-space: nowrap;
}
.rc-price-9unit {
  font-size: 11.5px; font-weight: 600;
  color: var(--text-3);
  flex-shrink: 0;
}
.rc-price-sub {
  font-size: 12px; color: var(--text-2);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* 页脚：N晚总价（9折） + 原价总价 + 查询时间 + 按钮 */
.rc-total-line {
  font-size: 12.5px; color: var(--text-2);
  font-variant-numeric: tabular-nums;
}
.rc-total-line b {
  font-size: 14px; font-weight: 700; color: var(--text);
}
.rc-total-line em {
  font-style: normal; font-size: 11.5px;
  color: var(--text-3); margin-left: 4px;
}

/* 每个房型卡片上的查询时间 */
.rc-time {
  font-size: 11.5px; color: var(--text-3);
  font-family: var(--font-mono, monospace);
  margin-left: auto; flex-shrink: 0;
}

/* =============================================================
   订单按日期分组 + 分割线
   -------------------------------------------------------------
   看板是每天用得最多的页面，订单按入住日期框起来更清晰。
   ============================================================= */
.day-block { margin-bottom: 18px; }
.day-block:last-child { margin-bottom: 0; }

/* 日期标题：居中 + 大字号 + 鲜艳分界线（用户要求） */
.day-sep {
  display: flex; align-items: center; gap: 12px;
  margin: 28px 0 14px;
}
/* 日期分隔 = 同一套 tinted 语言（2026-10-28）
   按钮全部淡化之后，这块实心亮蓝成了整页唯一的高饱和色块，特别跳。 */
.day-sep-title {
  display: inline-flex; align-items: center; gap: 10px;
  flex-shrink: 0;
  border: 1px solid rgba(0, 113, 227, .28);
  background: rgba(0, 113, 227, .14);
  color: var(--brand-700);
  border-color: color-mix(in srgb, var(--brand-600) 28%, transparent);
  background: color-mix(in srgb, var(--brand-600) 14%, transparent);
  padding: 7px 18px;
  border-radius: var(--r-full);
  box-shadow: none;
}
.day-sep-date {
  font-size: 26px; font-weight: 800; color: var(--brand-700);
  font-variant-numeric: tabular-nums;
  letter-spacing: -.5px;
  line-height: 1.1;
}
.day-sep-today {
  font-size: 11.5px; font-weight: 700;
  background: rgba(0, 113, 227, .16);
  background: color-mix(in srgb, var(--brand-600) 16%, transparent);
  color: var(--brand-700);
  padding: 2px 9px; border-radius: var(--r-full);
  flex-shrink: 0;
}
/* 分隔线跟着胶囊一起淡下去：原来是实心品牌蓝渐变，比胶囊还抢眼 */
.day-sep-line {
  flex: 1; height: 3px;
  background: linear-gradient(90deg,
    rgba(0, 113, 227, .40), rgba(0, 113, 227, .12));
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--brand-600) 40%, transparent),
    color-mix(in srgb, var(--brand-600) 12%, transparent));
  min-width: 20px;
  border-radius: 3px;
}
/* 右边那条线反过来渐变，视觉上把标题夹在中间 */
.day-sep-line:last-child {
  background: linear-gradient(90deg,
    rgba(0, 113, 227, .12), rgba(0, 113, 227, .40));
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--brand-600) 12%, transparent),
    color-mix(in srgb, var(--brand-600) 40%, transparent));
}
.day-sep-count {
  font-size: 12.5px; color: var(--brand-700);
  font-weight: 600;
  flex-shrink: 0;
}

/* 每天的订单块给个浅底，和相邻日期区分开 */
.day-block .order-cards {
  background: var(--surface-2);
  border-radius: var(--r-lg);
  padding: 10px;
  gap: 8px;
}
.day-block .order-card { background: var(--surface); }

@media (max-width: 760px) {
  .day-sep { margin: 22px 0 10px; gap: 8px; }
  .day-sep-title { padding: 5px 14px; gap: 7px; }
  .day-sep-date { font-size: 21px; }
  .day-sep-count { font-size: 11.5px; }
  .day-sep-line { height: 3px; min-width: 12px; }
  .day-block .order-cards { padding: 8px; }
}

/* =============================================================
   统计卡缩小（用户要求手机上别占太多面积）
   ============================================================= */
/* =============================================================
   统计卡（看板顶部数字卡）— 2026-10-28 重排
   ⚠️ 原来是 auto-fit minmax(84px)，390px 宽能塞下 4 个，
   6 张卡就变成「4 + 2」，第二行右侧空一大块 —— 手机上看板最丑的
   就是这一处。手机端改成固定 3 列：6 张 → 3×2、9 张 → 3×3 都排满。
   顺带每格从 85px 变 112px，「待刷预授权」五个字才放得下。
   ============================================================= */
.stats {
  grid-template-columns: repeat(auto-fit, minmax(112px, 1fr));
  gap: 10px;
  margin-bottom: 20px;
}
/* ⚠️ 这里原来又写了一遍 border-radius: var(--r)（10px），
   把上面 .stat 的 --r-lg 覆盖了 → 统计卡比同页的订单卡方一档。
   圆角只准在组件定义处写一次，这里只管内边距。 */
.stat { padding: 10px 12px; }
.stat .num { font-size: 21px; font-weight: 620; letter-spacing: -.3px; }
.stat .label { font-size: 11.5px; margin-top: 2px; line-height: 1.45; }
.stat-desc { font-size: 11px; }

/* 桌面才做交互反馈；手机触屏没有 hover，加了反而会"点一下卡住" */
@media (hover: hover) and (pointer: fine) {
  .stat { transition: box-shadow var(--dur-2) var(--ease), border-color var(--dur-2) var(--ease); }
  .stat:hover { box-shadow: var(--sh-hover); border-color: var(--line-2); }
}

@media (max-width: 760px) {
  /* 固定 3 列（不是 auto-fit）—— 理由见上方「统计卡重排」注释 */
  .stats { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
  .stat { padding: 11px 12px; }
  .stat .num { font-size: 20px; }
  .stat .label { font-size: 11.5px; }
  /* 左侧 3px 色条在 112px 宽的小卡上太抢眼，压到 2px 保留状态编码 */
  .stat::after { width: 2px; }
}

/* ---------- 查历史订单：正在看某一天的提示 ---------- */
.day-view-tip {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-top: 10px; padding: 9px 12px;
  background: var(--brand-50);
  border-left: 3px solid var(--brand-600);
  border-radius: var(--r);
  font-size: 13px; color: var(--text-2);
}
.day-view-tip b {
  color: var(--text); font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.day-view-tip a {
  color: var(--brand-700); font-size: 12.5px;
  text-decoration: underline; margin-left: auto;
}

/* =============================================================
   同住人：卡片上直接编辑
   -------------------------------------------------------------
   库里没录时显示「同住 点这里填写」占位，点一下就能填，
   不用跳到订单详情页。
   ============================================================= */
.oc-co {
  cursor: pointer;
  border-radius: var(--r);
  transition: background .12s;
  align-self: flex-start;
  padding: 1px 4px 1px 0;
}
.oc-co:hover { background: var(--surface-3); }
.oc-co-text {
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* 空的状态：虚线框 + 淡色，提示「可以填」 */
.oc-co.empty {
  border: 1px dashed var(--line-2);
  padding: 2px 8px;
  background: var(--surface-2);
}
.oc-co.empty .oc-co-text {
  color: var(--text-3);
  font-size: 12.5px;
}
.oc-co.empty:hover {
  border-color: var(--brand-400);
  background: var(--brand-50);
}
.oc-co.empty:hover .oc-co-text { color: var(--brand-700); }

/* =============================================================
   看板卡片底部：复制 + 修改订单（2026-10-06）
   -------------------------------------------------------------
   .btn-copy 是 width:100%（整行），所以「修改订单」放进同一个
   .card-footer 里会被挤到下一行 —— 这里用 flex 让两个并排。
   ============================================================= */
.card-footer:has(.btn-edit-order) {
  display: flex;
  gap: 8px;
}
.card-footer .btn-copy,
.card-footer .btn-edit-order {
  flex: 1 1 0;
  min-width: 0;
  width: auto;
}

/* 「修改订单」= 次要按钮，跟 .btn 完全同款（2026-10-28 统一）
   原来它是 --surface-2 灰底、hover 变品牌蓝 —— 而旁边的 .btn-copy 是 tinted 蓝。
   两个并排的按钮里，一个 hover 变蓝一个变灰，用户会觉得"这两个不是一套的"。
   现在：底色/边框/字色/hover 行为全部对齐 .btn。 */
.btn-edit-order {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
  padding: 9px 10px;
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--line-2);
  border-radius: var(--r);
  font-size: 13px;
  font-weight: 600;
  font-family: inherit;
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--dur-1) var(--ease),
              border-color var(--dur-1) var(--ease),
              color var(--dur-1) var(--ease),
              transform var(--dur-1) var(--ease);
}
.btn-edit-order:hover {
  background: var(--surface-2);
  border-color: var(--text-3);
  color: var(--text);
  text-decoration: none;
}
.btn-edit-order:active { transform: scale(.98); }
.btn-edit-order .ico {
  font-size: 13px;
  font-weight: 400;   /* emoji 图标别跟着加粗，会变形 */
  line-height: 1;
}

@media (max-width: 480px) {
  /* 手机上太挤，改成上下两行（btn-copy 保持整行好看） */
  .card-footer:has(.btn-edit-order) { flex-wrap: wrap; }
  .card-footer .btn-copy,
  .card-footer .btn-edit-order { flex: 1 1 100%; }
}

/* =============================================================
   看板「本月毛利」卡片（2026-10-06）
   现在和利润计算页口径一致，并且能点进明细。
   ============================================================= */
.biz-item-link {
  display: block;
  text-decoration: none;
  color: inherit;
  cursor: pointer;
  transition: background-color .12s ease;
}
.biz-item-link:hover {
  background: var(--surface-2, var(--surface-2));
}
/* ⚠️ 原来这里是 `.biz-label`（没限定），**和上面 891 行那条撞了**，
     而且把display:flex 泄漏给了**所有**卡片。
     结果（用户 2026-10-07 截图反馈「经营数据UI有问题」）：
     「本月单量」这类普通卡片的标题被 flex 拉成一行、
     内容掉出格子，跟「本月毛利」那张对不齐。

     改成只作用于 `.biz-item-link`（毛利卡）——
     只有那张卡里才有「明细 →」需要 flex 排布。*/
.biz-item-link .biz-label {
  display: flex;
  align-items: center;
  gap: 6px;
}
.biz-go {
  font-size: 11px;
  color: var(--text-3, var(--text-3));
  opacity: 0;
  transition: opacity .12s ease;
}
.biz-item-link:hover .biz-go { opacity: 1; }
.biz-sub-2 {
  font-size: 11px;
  color: var(--text-3, var(--text-3));
  margin-top: 2px;
}

/* =============================================================
   额外积分快捷区（2026-10-06 第四次）
   -------------------------------------------------------------
   用户要求：「你弄成点击的，点一次500，点3次就是1500分」
   所以做成档位按钮 + 可累加 + 再点撤销。

   注意：改单页也有同名的输入框（欢迎礼遇分/额外积分），
   那里的样式在 edit.css，这里是录单页的。
   ============================================================= */
.bonus-field {
  /* 整块淡黄底，和上面的渠道区分开 —— 这是「可填可不填」的可选项 */
  background: color-mix(in srgb, var(--warn-bg, var(--warn-bg-2)) 45%, transparent);
  border: 1px dashed var(--line-2, var(--line));
  border-radius: var(--r, 8px);
  padding: 10px 12px;
}
.bonus-field label {
  display: flex;
  align-items: center;
  gap: 5px;
}
.lbl-tip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--text-3, var(--text-3));
  color: #fff;
  font-size: 10px;
  cursor: help;
  line-height: 1;
}

.bonus-row {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 7px;
}
.bonus-input {
  width: 110px;
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.bonus-unit {
  font-size: var(--fs-sm);
  color: var(--text-2);
}

/* ---- 档位按钮 ---- */
.bonus-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 7px;
}
.bonus-chip {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 4px 12px;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--text);
  background: var(--surface, #fff);
  border: 1px solid var(--line-2, var(--line));
  border-radius: var(--r-full);
  cursor: pointer;
  transition: all .12s ease;
  user-select: none;
}
.bonus-chip:hover {
  border-color: var(--brand-400, var(--info));
  background: var(--brand-50, var(--info-bg));
}
.bonus-chip.is-on {
  background: var(--brand-600, var(--info));
  border-color: var(--brand-600, var(--info));
  color: #fff;
}
.bonus-chip.is-on:hover {
  background: var(--brand-700, var(--info-text));
}
.bonus-chip.is-clear {
  color: var(--text-3, var(--text-3));
  border-style: dashed;
  padding: 5px 10px;
}
.bonus-chip.is-clear:hover {
  color: var(--danger, var(--danger-text));
  border-color: var(--danger, var(--danger-text));
  background: transparent;
}
/* 「×3」这样的次数徽标 */
.chip-times {
  font-size: 10px;
  opacity: .85;
  font-variant-numeric: tabular-nums;
}

.bonus-note {
  width: 100%;
  font-size: var(--fs-sm);
  margin-bottom: 5px;
}
#bonusHint {
  line-height: 1.6;
}
#bonusHint b { color: var(--brand-700, var(--info-text)); }

/* 窄屏：按钮排两行也不该溢出 */
@media (max-width: 520px) {
  .bonus-chip { padding: 5px 10px; font-size: var(--fs-xs); }
  .bonus-input { width: 90px; }
}

/* =============================================================
   导入设置区（2026-10-06 第七次）
   -------------------------------------------------------------
   用户原话：「把这个下单渠道下放到网页里面吧，确认页有点太挤了」
   → 渠道/结账状态/额外积分全部搬到主界面，确认弹窗只留摘要。
   字段用 radio 而不是下拉 —— 三个固定选项一眼看全，不用点开。
   ============================================================= */
.import-settings {
  margin-top: 12px;
  padding: 12px;
  background: var(--surface-2, var(--surface-2));
  border: 1px solid var(--line-2, var(--line));
  border-radius: var(--r, 8px);
}
.is-title {
  font-size: var(--fs-sm);
  font-weight: 500;
  margin-bottom: 10px;
  padding-bottom: 6px;
  border-bottom: 1px dashed var(--line-2, var(--line));
}
.is-title .muted {
  font-size: 11px;
  font-weight: 400;
  color: var(--text-3, var(--text-3));
  margin-left: 5px;
}

/* 必填星号 */
.req {
  color: var(--danger, var(--danger-text));
  font-weight: 500;
  margin-left: 2px;
}

/* ---- radio 按钮组（替代下拉）---- */
.radio-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 6px;
}
.radio-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 5px 11px;
  font-size: var(--fs-sm);
  color: var(--text, var(--text));
  background: var(--surface, #fff);
  border: 1px solid var(--line-2, var(--line));
  border-radius: var(--r-full);
  cursor: pointer;
  user-select: none;
  transition: all .12s ease;
}
.radio-chip:hover {
  border-color: var(--brand-400, var(--info));
  background: var(--brand-50, var(--info-bg));
}
/* 隐藏原生 radio，用 chip 的样式表示选中 */
.radio-chip input[type="radio"] {
  width: 0;
  height: 0;
  opacity: 0;
  position: absolute;
}
.radio-chip:has(input:checked) {
  background: var(--brand-600, var(--info));
  border-color: var(--brand-600, var(--info));
  color: #fff;
}
.radio-chip:has(input:focus-visible) {
  outline: 2px solid var(--brand-400, var(--info));
  outline-offset: 1px;
}
.radio-chip.sm {
  padding: 3px 9px;
  font-size: var(--fs-xs);
}

.sub-label {
  display: block;
  font-size: var(--fs-xs);
  color: var(--text-3, var(--text-3));
  margin-bottom: 5px;
}

/* 只读备注（由来源按钮自动生成）*/
.bonus-note[readonly] {
  background: var(--surface, #fff);
  color: var(--text-2, var(--text-2));
  font-style: italic;
}

/* ---- 缺项提示 ---- */
.is-missing {
  margin-top: 8px;
  padding: 6px 10px;
  font-size: var(--fs-xs);
  color: var(--warn-text, var(--warn-text));
  background: var(--warn-bg, var(--warn-bg-2));
  border-radius: var(--r-sm, 4px);
  line-height: 1.5;
}
.is-missing b { color: var(--danger, var(--danger-text)); }

/* ---- 确认弹窗里的摘要表 ---- */
.cf-summary {
  margin: 10px 0 4px;
  padding: 10px 12px;
  background: var(--surface-2, var(--surface-2));
  border: 1px solid var(--line-2, var(--line));
  border-radius: var(--r, 8px);
  max-height: 46vh;
  overflow-y: auto;
}
.cf-sum-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-sm);
}
.cf-sum-table td {
  padding: 5px 2px;
  border-bottom: 1px solid var(--line, var(--line));
  vertical-align: top;
}
.cf-sum-table tr:last-child td { border-bottom: none; }
.cf-sum-table td:first-child {
  width: 84px;
  color: var(--text-3, var(--text-3));
  font-size: var(--fs-xs);
  white-space: nowrap;
}
.cf-sum-table td:last-child {
  font-weight: 500;
  color: var(--text, var(--text));
}
@media (max-width: 520px) {
  .radio-chip { padding: 5px 9px; font-size: var(--fs-xs); }
}

/* =============================================================
   二次确认预览框 + 外援扣款（2026-10-06 第八次）
   -------------------------------------------------------------
   用户要求：「导入订单加一个预览框，可以看到主要的信息，
   做二次确认」→ 缺项要显红，不能让人稀里糊涂点确认。
   ============================================================= */

/* ---- 弹窗里的会员逐行列 ---- */
.cf-member {
  padding: 2px 0;
  line-height: 1.6;
}
.cf-member .tag {
  margin-left: 5px;
  font-size: 10px;
  vertical-align: middle;
}

/* 次要信息（渠道/结账/额外积分）压成一行小字 */
.cf-extra {
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px dashed var(--line-2, var(--line));
  font-size: var(--fs-xs);
  color: var(--text-3, var(--text-2));
  line-height: 1.7;
}

/* 缺项标红 —— 「还没选房型」这类必须显眼 */
.cf-bad {
  color: var(--danger, var(--danger-text));
  font-weight: 500;
}

/* 补充说明（会员号、同住人挂在名字后面）*/
.cf-mut {
  color: var(--text-3, var(--text-3));
  font-size: var(--fs-xs);
  font-weight: 400;
  margin-left: 3px;
}

/* 预览框整体：稍微加重边框，提醒这是「核对区」 */
.cf-summary {
  border-color: var(--brand-200, #A8CBEC);
  background: var(--surface, #fff);
}
.cf-sum-table td:first-child {
  width: 62px;
  vertical-align: top;
  padding-top: 6px;
}

/* 确认前的警示条 */
.cf-warn {
  margin-top: 10px;
  padding: 7px 11px;
  font-size: var(--fs-xs);
  color: var(--warn-text, var(--warn-text));
  background: var(--warn-bg, var(--warn-bg-2));
  border-left: 3px solid var(--warn, var(--warn));
  border-radius: var(--r-sm, 4px);
  line-height: 1.6;
}

/* ---- 外援扣款区：出现时用浅橙底区分 ---- */
.outsource-field {
  margin-top: 10px;
  padding: 10px;
  background: var(--warn-bg, var(--warn-bg-2));
  border: 1px solid var(--warn-border);
  border-radius: var(--r-sm, 6px);
}
.outsource-field label {
  color: var(--warn-text, var(--warn-text));
  font-weight: 600;
}
/* 在橙底上快捷按钮要反色，否则看不清 */
.outsource-field .bonus-chip {
  background: #fff;
  border-color: #E5C07B;
}
.outsource-field .bonus-chip:hover {
  border-color: var(--warn, var(--warn));
  background: #FFFDF5;
}
.outsource-field .bonus-chip.is-on {
  background: var(--warn, var(--warn));
  border-color: var(--warn, var(--warn));
  color: #fff;
}
.outsource-field .cf-hint {
  color: var(--warn-text, var(--warn-text));
  line-height: 1.7;
  margin-top: 6px;
}

/* 跑腿人输入框（2026-10-07）
   ----------------------------------------------
   用户要求：「跑腿人必须填写，默认的跑腿人就是本人，但是可以修改」——
   99% 情况不用改，所以要一眼看出「这里已经有默认值了」。 */
.errand-field .errand-by {
  margin-top: 8px;
  font-weight: 600;
}
.errand-field .errand-by::placeholder {
  font-weight: 400;
  color: var(--text-3, var(--text-3));
}
.errand-field .cf-hint {
  color: var(--text-2, var(--text-2));
  line-height: 1.7;
  margin-top: 6px;
  font-size: 12.5px;
}

/* =============================================================
   当天未刷预授权 —— 置顶提醒（2026-10-07）
   -------------------------------------------------------------
   用户要求：「每天打开看板，要看当天哪个富力艾美酒店的订单
   没有刷预授权」→ 放在**所有配置区块之外**，不依赖看板配置。
   ============================================================= */
.preauth-alert {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  padding: 14px 18px;
  margin-bottom: 16px;
  background: var(--warn-bg-2);
  border: 1px solid var(--warn-border);
  border-left: 4px solid var(--warn);
  border-radius: var(--r-lg);
}
.pa-icon {
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--warn);
  color: #fff;
  font-size: 17px;
  font-weight: 500;
  display: flex;
  align-items: center;
  justify-content: center;
}
.pa-body { flex: 0 0 auto; min-width: 190px; }
.pa-title {
  font-size: 15px;
  font-weight: 500;
  color: var(--warn-text);
  line-height: 1.5;
}
.pa-title b {
  font-size: 22px;
  margin: 0 4px;
  font-variant-numeric: tabular-nums;
}
.pa-desc {
  margin-top: 3px;
  font-size: 12px;
  color: var(--warn-text);
  line-height: 1.55;
}
.pa-list {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.pa-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 10px;
  background: #fff;
  border: 1px solid var(--warn-border);
  border-radius: var(--r);
  font-size: 13px;
  color: var(--text);
  text-decoration: none;
  transition: border-color .12s ease;
}
.pa-item:hover {
  border-color: var(--warn);
  background: #FFFDF5;
}
.pa-id {
  font-family: var(--font-mono, monospace);
  font-size: 12px;
  color: var(--text-2);
  flex: 0 0 auto;
}
.pa-name { font-weight: 500; flex: 0 0 auto; }
.pa-hotel { color: var(--text-2); flex: 0 0 auto; }
.pa-room {
  color: var(--text-3);
  font-size: 12px;
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.pa-go {
  flex: 0 0 auto;
  font-size: 12px;
  color: var(--warn);
}
@media (max-width: 720px) {
  .preauth-alert { flex-direction: column; }
  .pa-body { min-width: 0; }
}

/* =============================================================
   毛玻璃降级（2026-10-28）
   =============================================================
   只在三处用了 backdrop-filter：吸顶栏 / 底部标签栏 /（抽屉遮罩没加，
   因为遮罩本身是黑色半透明，加模糊没人看得出来，纯浪费 GPU）。

   两段降级缺一不可：
   ① 浏览器不支持 —— 退回不透明底色，否则栏会变成"半透明没模糊"，
      文字压在滚动的内容上，比不支持还难看。
   ② 用户在系统里开了「降低透明度」—— iOS/macOS/Windows 都有这个
      无障碍开关，前庭敏感的人会开。尊重它，别硬上。
   ============================================================= */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .topbar, .front-top, .tabbar {
    background: var(--surface);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
}
@media (prefers-reduced-transparency: reduce) {
  .topbar, .front-top, .tabbar {
    background: var(--surface);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
}

/* =============================================================
   统一交互层（2026-10-28）
   =============================================================
   用户的原话是「让人家这套动画、UI、过渡是一整套东西，看起来像一个整体」。
   所以这一层不追求某个特效，而是把**散在各页的小动作收敛成同一套规则**：

     · 同一个缓动曲线（--ease）、同一档时长（--dur-1/--dur-2）
     · 同一个焦点环（以前每个组件各写各的，键盘 Tab 时样式会跳）
     · 同一套数字对齐（对账时上下行的位数要对得上）
     · 同一档表格悬停底色（原来 app.css 用品牌紫、admin_design 用中性灰，两种观感）

   改这一层不会动任何业务逻辑，也不会动模板结构。
   ============================================================= */

/* 1) 页面进入：整个内容区一次性淡入。
      不给每张卡片分别加延迟 —— 那样每页都在逐个跳，反而更乱。
      🔴🔴 这里**绝对不能动 transform**（2026-10-29 用户报的 bug 的根因）：
         原写法 `from{transform:translateY(10px)} to{transform:none}` + `both`，
         动画结束后 `.front-main` 的计算值变成 `matrix(1,0,0,1,0,0)` ——
         数学上是"没偏移"，**但浏览器只认 computed value 是不是 `none`**，
         于是 `.front-main` 成了新的**包含块**，里面的 `position:fixed`
         全部降级为"相对 main 定位"。
         症状：录单页点「导入订单」，确认框被甩到整页顶部（top:-2281），
         手机和电脑都得往上滑才看得见（用户原话「以前是直接跳过去的」）。
         只动 opacity 就永远不会创建包含块，一劳永逸。 */
@keyframes pageIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}
.content, .front-main {
  animation: pageIn var(--dur-3) var(--ease) both;
}

/* 2) 焦点环兜底。:where() 优先级为 0，
      所以各组件的自定义焦点样式不会被它盖掉，只补没有写的那部分。 */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--brand-500);
  outline-offset: 2px;
}

/* 3) 数字一律等宽（tabular-nums）。
      对账的时候上下两行的位数能对齐，这是"专业感"最便宜的一招，
      对金额、日期、间夜数都有效。 */
table, .num, .stat .num, .money, .amt, td.num {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* 4) 表格悬停统一成中性灰。
      原来 app.css 的 table 用品牌紫 --brand-50、admin_design 用 --surface-2，
      同一套系统两种观感；品牌色只留给"选中/当前"这类真正有语义的状态。 */
tbody tr {
  transition: background-color var(--dur-1) var(--ease);
}
tbody tr:hover { background: var(--surface-2); }

/* 5) 指标卡悬停抬升 1px —— 克制，不做弹跳。
      手机上没有 hover，这条只对鼠标用户生效。 */
.stat {
  transition: box-shadow var(--dur-2) var(--ease),
              transform var(--dur-2) var(--ease),
              border-color var(--dur-2) var(--ease);
}
.stat:hover { transform: translateY(-1px); box-shadow: var(--sh-2); }

/* =============================================================
   更新日志（2026-10-29 用户要求「后台可查更新日志」）
   -------------------------------------------------------------
   只给 /admin/changelog 用。类名统一 ver- 前缀，不与 apple.css
   的 body .tag / body .card-head 撞权重（那些是新类名没有的）。
   ============================================================= */
.ver-now { margin-bottom: 14px; }
.ver-now-row {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 15px 16px 2px;
}
.ver-no {
  font-size: 21px; font-weight: 800; letter-spacing: .2px;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}
.ver-no.sm {
  display: inline-block;
  font-size: 12.5px; font-weight: 700;
  color: var(--text-2); background: var(--surface-3);
  border-radius: var(--r-full);
  padding: 2px 9px; margin-right: 8px;
  vertical-align: 1px;
}
.ver-date {
  font-size: 12px; color: var(--text-3);
  font-variant-numeric: tabular-nums;
}
.card-head .ver-date { margin-left: auto; }
.ver-title { font-weight: 650; }
.ver-list { list-style: none; margin: 0; padding: 6px 16px 4px; }
.ver-list li {
  display: flex; align-items: flex-start; gap: 9px;
  padding: 7px 0;
  border-bottom: 1px solid var(--line);
}
.ver-list li:last-child { border-bottom: 0; }
.ver-list .tag { flex: none; margin-top: 2px; }
.ver-txt {
  font-size: 13.5px; line-height: 1.65; color: var(--text-2);
  min-width: 0;
}
.ver-note {
  margin: 6px 16px 15px;
  padding: 10px 12px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r);
  font-size: 12.5px; line-height: 1.65; color: var(--text-2);
}
.ver-note b { color: var(--text); margin-right: 7px; font-weight: 650; }
/* 侧边栏底部版本号：点进去是更新日志 */
.sidebar-ver {
  display: block;
  margin-top: 9px;
  text-align: center;
  font-size: 10.5px;
  color: var(--text-3);
  text-decoration: none;
  font-variant-numeric: tabular-nums;
  letter-spacing: .2px;
}
.sidebar-ver:hover { color: var(--brand-600); text-decoration: none; }

@media (max-width: 640px) {
  .ver-no { font-size: 18px; }
  .ver-list { padding: 4px 13px 2px; }
  .ver-txt { font-size: 13px; }
}

/* =============================================================
   折叠卡（2026-10-29 用户：「操作日志太长，折叠一下弄成下拉框」）
   -------------------------------------------------------------
   用原生 <details>，不引 JS：禁脚本也能展开，浏览器自带键盘支持。
   🔴 apple.css 的 `body .card` 把 overflow 改成了 visible，而它**后加载**，
      同权重下赢过这里的 .card —— 所以下面必须写 .card.fold-card 提权重，
      否则圆角裁不住内部表格，header hover 底色会从圆角处漏出来。
   ============================================================= */
body .card.fold-card { overflow: hidden; }
.fold > .fold-sum {
  display: flex; align-items: center; gap: 10px;
  padding: 14px 16px;
  cursor: pointer;
  list-style: none;              /* Firefox 默认三角 */
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}
.fold > .fold-sum::-webkit-details-marker { display: none; }  /* Safari/Chrome */
.fold > .fold-sum::marker { content: ""; }
.fold-sum h3 { font-size: 14.5px; font-weight: 650; margin: 0; }
.fold-sum .desc { font-size: 12px; color: var(--text-3); font-weight: 400; }
.fold-arrow {
  margin-left: auto;
  font-size: 11px; line-height: 1;
  color: var(--text-3);
  transition: transform var(--dur-2) var(--ease);
}
.fold[open] > .fold-sum { border-bottom: 1px solid var(--line); }
.fold[open] .fold-arrow { transform: rotate(180deg); }
@media (hover: hover) {
  .fold > .fold-sum:hover { background: var(--surface-2); }
}
