/* ==========================================================================
   达播工作台 — overlays.css
   抽屉 / 弹窗 / 提示 / 表单布局 / 工具栏 / 提示条
   ========================================================================== */

/* ---- 抽屉 ---- */
.drawer-overlay {
  position: fixed;
  inset: 0;
  background: color-mix(in srgb, var(--color-gray-900) 40%, transparent);
  z-index: var(--z-modal);
  animation: fade-in var(--motion-base) var(--motion-ease) both;
}
/* 退场（JS 加 [data-closing]）—— 遮罩与抽屉**同时**退，且用同一条时长 */
.drawer-overlay[data-closing] {
  animation: fade-out calc(var(--motion-base) * 0.62) var(--motion-ease-exit) both;
}

.drawer {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: 560px;
  max-width: 92vw;
  background: var(--color-surface);
  box-shadow: var(--shadow-raised);
  z-index: var(--z-modal);
  display: flex;
  flex-direction: column;
  animation: slide-in-right var(--motion-base) var(--motion-ease) both;
}
/* 退场：只滑 24% 就淡出，比进场快一档（理由见文件上方「进出场成对」那段） */
.drawer[data-closing] {
  animation: slide-out-right calc(var(--motion-base) * 0.62) var(--motion-ease-exit) both;
  pointer-events: none;
}

.drawer__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-6);
  border-bottom: 1px solid var(--color-border-soft);
  flex-shrink: 0;
}

.drawer__title {
  font-size: var(--font-size-lg);
  font-weight: var(--font-weight-semibold);
  color: var(--color-fg);
}

/* 标题右侧的额外操作位（`openDrawer({ headerExtra })` 的落点）——
   ⚠️ `margin-left: auto` 把本元素及其后的关闭按钮**一起推到右边**：
      不写的话，header 是 `space-between`，三元素时会被均分（extra 跑到正中间、离关闭按钮很远）。
   ⚠️ 不传 headerExtra 时本元素是空 span、宽为 0，加上 auto 后布局与改前一致（title 左 / 关闭右）。
   ⚠️ 用 `inline-flex` 而不是 block：里面的按钮组要横排、居中。 */
.drawer__header-extra {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

.drawer__body {
  flex: 1;
  overflow-y: auto;
  padding: var(--space-5) var(--space-6);
}

.drawer__footer {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-6);
  border-top: 1px solid var(--color-border-soft);
  flex-shrink: 0;
}

/* ══════════════════════════════════════════════════════════════════════════════
   2026-09-26 动效升级：**进出场成对**（乐哥：「要求轻盈灵动，不要傻快傻快」）
   ──────────────────────────────────────────────────────────────────────────────
   ⛔ 改前的真实问题：**只进不出**。弹窗/抽屉/提示条的关闭都是直接
      `ov.remove(); modal.remove();` —— 动画只在"进场"那一侧有，
      关闭是**瞬间消失**。这是"机械感"的一大来源（进来很讲究、出去很粗暴）。
   ✅ 现在每对都齐了，且**退场刻意比进场快、位移更小**：
      · 退场用 --motion-ease-exit（easeInQuad，干脆收走）而不是进场的 easeOut
        —— 离场拖泥带水会让人觉得"卡"；进场才需要"慢下来"的从容感。
      · 退场时长 = 进场 × 0.62，位移只走一小段（抽屉退场只滑 24% 宽，不滑满整屏）。
      · 弹窗退场**下沉**（往下走一点）而不是上浮 —— 与进场方向一致，
        读起来是"缩回去"而不是"又飞出去了"。

   ⚠️ 用 `[data-closing]` 属性而不是再加一个 `.is-closing` 类：
      本项目类名已经很多，且这个标记**只服务于"正在退场"这一个瞬间状态**，
      用属性更不容易与业务类名撞车（`is-closing` 这种通用词很容易被别处复用）。
   ⚠️ 退场动画的 duration 必须与 JS 里的 setTimeout 一致（JS 用的是令牌派生值，
      见 ui.js 的 `_exitDur()`）—— 两处不一致会出现"动画没播完就被删"或"白等一截"。
   ══════════════════════════════════════════════════════════════════════════════ */
@keyframes slide-in-right {
  from { transform: translateX(100%); }
  to { transform: translateX(0); }
}
/* 抽屉退场：只滑 24% 就淡出（不必滑满整屏宽才消失，那样像"被推走"） */
@keyframes slide-out-right {
  from { transform: translateX(0); opacity: 1; }
  to { transform: translateX(24%); opacity: 0; }
}

@keyframes fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}
@keyframes fade-out {
  from { opacity: 1; }
  to { opacity: 0; }
}

/* ---- 弹窗 ---- */
.modal-overlay {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: color-mix(in srgb, var(--color-gray-900) 40%, transparent);
  z-index: var(--z-modal);
  padding: var(--space-4);
  animation: fade-in var(--motion-base) var(--motion-ease) both;
}
/* 退场：遮罩与弹窗**同时**退，用同一条时长（错开会出现"内容没了、黑底还在"的瞬间） */
.modal-overlay[data-closing] {
  animation: fade-out calc(var(--motion-base) * 0.62) var(--motion-ease-exit) both;
}

.modal {
  /* 默认宽度 720 → 880（乐哥 2026-09-13：「宽一点、高一点，不用那么小家子气」）。
     720px 下多列表单/明细要横向滚动、字段被挤成两行，观感局促；
     880px 在 1440 宽屏上仍留足两侧留白，且能容纳 3~4 列并排字段。
     ⚠️ 各处调用若显式传了 width（如 inventory 的 720）会覆盖本默认值，需同步改。 */
  width: 880px;
  max-width: 100%;
  max-height: 90vh;
  display: flex;
  flex-direction: column;
  background: var(--color-surface);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-raised);
  /* ⭐ 2026-09-26：进场曲线由 ease-standard 改为 **--motion-ease-pop（弹入）** + 起点带一点缩放。
     为什么加 scale：纯"上浮 + 淡入"读起来是"页面里冒出一块"；
     配 .94 → 1 的微缩放 + 弹入曲线之后才有"从远处推近并轻微过冲"的空间感
     （这是"灵动"的关键一笔）。
     ⚠️ 曲线必须是 pop 那条（y1=1.56）：ease 那条的过冲只有 2px 级，
        用在这里读不出来，弹窗会显得"只是滑了上来"。
     幅度刻意只到 .94 —— 到 .9 以下就成了弹跳玩具。 */
  animation: modal-in var(--motion-base) var(--motion-ease-pop) both;
}
/* 退场（JS 加 [data-closing] 后触发，播完才 remove） */
.modal[data-closing] {
  animation: modal-out calc(var(--motion-base) * 0.62) var(--motion-ease-exit) both;
  pointer-events: none;   /* 正在消失时不再接受点击，避免"点了两次" */
}

.modal__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  /* ⭐⭐ 2026-09-28 左右内边距统一 24px（乐哥拍板：「弹窗：只统一左右到 24px」）——
     改前是 `var(--space-5)`（20px），比卡片的 24px 窄 4px ⇒ 弹窗里的表单与页面表单
     **左缩进对不齐**（同一套表单控件在弹窗里比在页面上靠左 4px）。
     ⚠️ **垂直不动**：头/脚保持 `--space-4`(16)、body 保持 `--space-5`(20) ——
        这是刻意的分层（标题栏与按钮栏更紧凑，不与内容区等重），拉到 24 反而松垮。
     ⚠️ 抽屉（.drawer__*）**一起改**：它与弹窗是同一套浮层语言（同圆角 `--radius-xl`、
        同头/身/脚结构），只改弹窗会让两者差 4px、又造出一处新的不一致。 */
  padding: var(--space-4) var(--space-6);
  border-bottom: 1px solid var(--color-border-soft);
  flex-shrink: 0;
}

.modal__title {
  font-size: var(--font-size-lg);
  font-weight: var(--font-weight-semibold);
}

.modal__body {
  padding: var(--space-5) var(--space-6);
  overflow-y: auto;
  flex: 1;
  /* 最小高度 360 → 420（2026-09-13）：内容少的弹窗也不至于「扁扁一条」。
     ⚠️ 确认框与内容自适应的弹窗会显式传 bodyMinHeight:0 覆盖本值（见 ui.js openModal），不受影响。 */
  min-height: 420px;
}

.modal__footer {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-6);
  border-top: 1px solid var(--color-border-soft);
  flex-shrink: 0;
}

/* ⭐⭐⭐ 2026-09-28 弹窗 / 抽屉的操作按钮统一到**浮岛尺寸**（乐哥拍板：
   「弹窗的取消，确认按钮尺寸不对，整个系统这俩类似按钮都查下」+「都按照浮岛尺寸改」）。
   ──────────────────────────────────────────────────────────────────────────
   ▸ 普查数据（全站真机扫，按容器归类）：
       卡内工具栏  34px × 60+ 处   ← 全站绝对主导
       表单浮岛    34px × 4 页
       弹窗 footer **44px**        ← 唯一的两处例外之一（乐哥肉眼看出"不对"）
       页头主操作  **44px**        ← 另一处（**本次不改**，见下方说明）
     ⇒ 所以"不对"的根因是**弹窗是全站唯一还留着 44 的成对操作按钮** ——
        它不是"弹窗做小了"，而是"弹窗没跟上全站已经统一的 34"。
   ▸ 为什么取浮岛而不是反过来把浮岛提到 44：
     ① 34 是**全站绝大多数**（60+ 处卡内工具栏），改一处比改六十处稳；
     ② `--control-height-sm`(34) 的语义就是"紧凑档：列表工具栏 / 表格行内" ——
        弹窗 footer 与它们同级（都是"顺手的次级操作"，不是页面主操作）；
     ③ 乐哥上轮刚把浮岛定成 34 并说"尺寸和 tab 一样"，反向改会让那条决定作废。
   ▸ 为什么**不动页头主操作**（`.page-actions .btn--lg`，仍是 44）：
     它不是"取消/确认"这类成对操作，而是**页面级主操作**（「新增排期」「导入」），
     且与页头 tab 容器同排 —— 那里 44 是 2026-09-28 早上刚定的"与 tab 容器齐平"。
     乐哥说的是"这俩类似按钮"（取消/确认），不包含页面主操作。
   ⚠️ 宽度也走浮岛的 `min-width: 88px`（乐哥说"按照浮岛尺寸"）——
      两字按钮（取消/确定）会从 62px 撑到 88px，与浮岛的取消/创建**逐像素同宽**。
   ⚠️ 只作用于 **footer 内**：弹窗 body 里的按钮（表格行内"详情""添加"等）不受影响 ——
      它们本来就该跟表格走（34，已在卡内工具栏那档）。
   ⚠️ 确认框（`confirmDialog`）与所有 `openModal({footer})` 调用**自动生效**（同一个 footer）。 */
.modal__footer .btn,
.drawer__footer .btn {
  height: var(--control-height-sm);
  min-height: var(--control-height-sm);
  min-width: 88px;
}

@keyframes modal-in {
  /* ⭐ 2026-09-26：起点缩放 .97 → .94、位移 rise 由 8px 提为 10px（见 design-tokens）。
     这样配 --motion-ease-pop 的过冲，弹出来是"先冲过一点、再收回落定"，
     而不是上一版的"匀速滑上来"。 */
  from { opacity: 0; transform: translateY(var(--motion-rise)) scale(0.94); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}
/* 退场：往下沉一小段（与进场同向，"缩回去"），比进场快一档 */
@keyframes modal-out {
  from { opacity: 1; transform: translateY(0) scale(1); }
  to { opacity: 0; transform: translateY(4px) scale(0.985); }
}

/* ---- 提示 toast ---- */
/* ── 行内「更多」菜单（2026-09-14 新增，配合 UI.rowMenu）──────────────────────────────
   用途：操作列动作多、表格宽度紧张时，把次要动作收进「⋯」菜单。
   与全站浮层的层级关系：dropdown(1000) < modal(1200) < toast(1300)
     —— 菜单是最轻的浮层，不该盖住弹窗。
   ⚠️ `position: fixed`（不是 absolute）：组件把它挂在 body 上、用 JS 算屏幕坐标，
      所以这里必须是相对视口的定位，否则坐标会错。
   ⚠️ 无 emoji、用统一描边图标；颜色全部走语义 token。 */
.row-menu {
  position: fixed;
  z-index: var(--z-dropdown);
  min-width: 136px;
  padding: var(--space-1) 0;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  /* ⭐ 2026-09-26：进场 —— 从锚点按钮方向轻轻展开（缩放 .96 + 上浮 4px）。
     ⚠️ `transform-origin: top left`：菜单挂在「⋯」按钮下方，从**左上角**长出来
        才符合"从这里点开的"因果；用中心缩放会看起来像凭空冒出来。
     ⚠️ 位移只用 4px（比 --motion-rise 的 8px 小）：菜单紧贴按钮，走太远会与按钮脱开。 */
  transform-origin: top left;
  animation: row-menu-in var(--motion-fast) var(--motion-ease) both;
}
/* 向上翻的菜单（下方空间不够时 JS 会把它放到按钮上方，并打 data-flip）——
   展开原点跟着翻到左下，否则方向感是反的。 */
.row-menu[data-flip] { transform-origin: bottom left; }
.row-menu[data-closing] {
  animation: row-menu-out calc(var(--motion-fast) * 0.7) var(--motion-ease-exit) both;
  pointer-events: none;
}
@keyframes row-menu-in {
  from { opacity: 0; transform: translateY(-4px) scale(0.96); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}
@keyframes row-menu-out {
  from { opacity: 1; transform: translateY(0) scale(1); }
  to { opacity: 0; transform: translateY(-3px) scale(0.97); }
}
.row-menu__item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  padding: var(--space-2) var(--space-3);
  border: 0;
  background: none;
  cursor: pointer;
  font-family: inherit;
  font-size: var(--font-size-sm);
  color: var(--color-fg);
  text-align: left;
  white-space: nowrap;
}
.row-menu__item:hover:not(:disabled) { background: var(--color-surface-2); }
.row-menu__item:focus-visible { outline: 2px solid var(--color-accent); outline-offset: -2px; }
/* 禁用态：不只是变灰，还给出原因（title 里写清"需先确认"这类前置条件） */
.row-menu__item:disabled { color: var(--color-disabled); cursor: not-allowed; }
.row-menu__item svg { flex: none; color: var(--color-muted); }
.row-menu__item:disabled svg { color: var(--color-disabled); }
/* 危险动作（删除）：红色区分，避免与"编辑"误点 */
.row-menu__item--danger,
.row-menu__item--danger svg { color: var(--color-danger); }

.toast-root {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  z-index: var(--z-toast);
  pointer-events: none;
}

.toast {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  background: var(--color-gray-900);
  color: var(--color-gray-0);
  font-size: var(--font-size-sm);
  box-shadow: var(--shadow-raised);
  /* ⭐ 2026-09-26：`.toast-root` 是 `top:50%` 的居中容器（整组一起垂直居中），
     所以进出场的位移只能写在这条上 —— 基点用 `translateY`，**不要**再写 translate(-50%)
     （那是 root 的职责，重复写会把这条 toast 自己挪到屏幕左上）。 */
  animation: toast-in var(--motion-base) var(--motion-ease) both;
}
/* 退场：向上淡出（与进场"从上方落下"呼应），比进场快一档 */
.toast[data-closing] {
  animation: toast-out calc(var(--motion-base) * 0.62) var(--motion-ease-exit) both;
}

.toast--success { background: var(--color-success); color: var(--color-gray-0); }
.toast--error { background: var(--color-danger); color: var(--color-gray-0); }

@keyframes toast-in {
  from { opacity: 0; transform: translateY(calc(var(--motion-rise) * -1)); }
  to { opacity: 1; transform: translateY(0); }
}
@keyframes toast-out {
  from { opacity: 1; transform: translateY(0); }
  to { opacity: 0; transform: translateY(-6px); }
}

/* ══════════════════════════════════════════════════════════════════════════════
   2026-09-26 动效：**打勾"描线画出"**（成功提示）
   ──────────────────────────────────────────────────────────────────────────────
   改前：成功提示里的 ✓ 是**直接出现**的（和文字一起淡入）。
   改后：✓ 的笔画**从左往右画出来**，延迟 80ms 起跑（让 toast 先落位，再"盖章"）。
   为什么这一笔值得做：提示是"操作已完成"的唯一确认，画出来的勾比跳出来的勾
   **多一层"正在为你完成"的叙事感** —— 这是"灵动"里性价比最高的一处（纯 CSS，零 JS）。

   ⚠️ 判据用 `.toast:not(.toast--error)`：错误提示的图标是圆圈感叹号（alertCircle），
      给它做描线会变成"圆圈慢慢画出来"，看起来像加载中，反而误导。
   ⚠️ `stroke-dasharray/offset: 28` 是对 `I.icon('check', 16)` 那条折线的实测长度
      （M20 6 9 17l-5-5）。改图标路径时这个值要跟着改，否则会出现"画完了还有一截没画"
      或"提前画完"。28 留了一点余量，宁可略长（多出的部分在 offset 为 0 时不可见）。
   ⚠️ 不给 `.toast` 加 `both` 之外的 fill：动画结束态就是 `stroke-dashoffset: 0`（完整可见），
      与"没有动画时"的渲染结果一致 ⇒ 动画被禁用（减少动效偏好）也不会导致勾消失。 */
.toast:not(.toast--error) svg path {
  stroke-dasharray: 28;
  stroke-dashoffset: 28;
  animation: toast-check-draw 440ms var(--motion-ease) 80ms both;
}
@keyframes toast-check-draw {
  to { stroke-dashoffset: 0; }
}

/* ---- 表单布局 ---- */
.form-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-4);
}

.form-grid > * {
  min-width: 0; /* 防止长内容（如地点地址）撑宽 grid 列 */
}

/* 表单网格里的控件默认撑满列宽（输入框/下拉/文本域），避免左右留白 */
.form-grid .input,
.form-grid .select,
.form-grid .textarea {
  width: 100%;
}

.form-grid .span-2 {
  grid-column: span 2;
}

/* 三等宽（替代 2fr 1fr 1.4fr / 1fr 1fr 1fr / 220px 1fr 1fr 1fr 混用） */
.form-grid--3 {
  grid-template-columns: repeat(3, 1fr);
}

/* 开关+输入一行（替代 auto 1fr / auto 1fr 1fr） */
.form-grid--auto {
  grid-template-columns: auto 1fr;
}

/* 基本信息卡片：图片居左 + 右侧字段网格（货品/套餐新增编辑页） */
.sf-basic {
  display: flex;
  gap: var(--space-6);
  align-items: flex-start;
}
.sf-basic__media {
  flex: 0 0 160px;
}
.sf-basic__main {
  flex: 1;
  min-width: 0;
}

/* 移动端：表单多列降为单列 */
@media (max-width: 768px) {
  .form-grid,
  .form-grid--3,
  .form-grid--auto {
    grid-template-columns: 1fr;
  }
  .form-grid .span-2 {
    grid-column: span 1;
  }
  .sf-basic {
    flex-direction: column;
  }
  .sf-basic__media {
    flex: 0 0 auto;
  }
}

.form-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-3);
  margin-top: var(--space-5);
}

/* ---- 工具栏 ---- */
.toolbar {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  margin-bottom: var(--space-4);
}

/* 工具栏与列表「合并为同一模块」（2026-09-14 乐哥：「微信小店…他们和列表都属于同一个模块」）
   ============================================================================
   ⛔ 先说被否决的方案（务必不要改回去）：**用 JS 把工具栏搬进卡片**。
      实测会在 100ms 后**丢失控件** —— 时间线铁证（华大对账）：
        73.5s 工具栏在卡内 ✓ → 73.6s 工具栏消失 ✗
      根因：工具栏进卡后，卡片区域的任何局部重渲染都会连它一起抹掉，
      而页面只在整页渲染时创建工具栏 → 不可自愈。详见 app.js 的说明。

   ✅ 现行：**纯 CSS 视觉合并**，零 DOM 操作。
      用 `:has(+ .card)` 精准选中「后面紧邻卡片」的工具栏，把两者拼成一张卡：
        · 工具栏：白底 + 上圆角 + **无下边框**
        · 紧邻的卡片：**无上边框** + 下圆角
      → 视觉上是一个从筛选区连贯到表格的整体卡片，与微信小店的模块感一致。

   ⚠️ 为什么不用纯 CSS 的 `:has(+ .card)`：**漏得多**。实测只能覆盖 3~4 页，漏掉两类：
      · 卡片被包在容器里（`.toolbar → #cs-body → .card`）
      · 中间隔着 **0px 空容器**（`.kw-scope` / `.batch-bar`，不可见但会打断相邻选择器）
      → 所以改由 JS 用**实测垂直间距**判断（见 app.js 的 markToolbarJoin），CSS 只负责表现。
   ⚠️ JS **只加类、绝不搬 DOM**：搬 DOM 会让工具栏随卡片的局部重渲染一起被销毁
      （实测 100ms 后控件消失，不可自愈）。
   ⚠️ 中间隔着**实质内容**的页面（如达人结算：警示条 37px + KPI 137px + 口径 38px）
      **有意不合并** —— 工具栏在那些"页面级概览"之上，硬拼进表格卡会切断概览的从属关系。
      判据是几何量（间距 > 24px 就不合并），不是结构量。 */
.toolbar--join-card {
  /* ⭐⭐ 2026-09-26 乐哥拍板：「只管一张表的工具栏，并进表格卡当卡头」——
     推翻 2026-09-25 Gashora 试妆时的「两卡分离」。
     ──────────────────────────────────────────────────────────────────────────
     ▸ 当时为什么分离：乐哥看参考图后说「工具栏是一个框，下面的表格又是一个框」，
       于是工具栏恢复完整四角圆角 + 与表格卡之间留 16px 缝隙（露出白大卡底）。
     ▸ 现在为什么并回去：真机普查发现「工具栏有 3 种摆法」——
       9 页"只管一张表"的页面里，**7 页是"自成一卡"、1 页（业绩总表）并进表格卡**，
       同一个语义两种形态。而"自成一卡"（带边框圆角）让它读起来是**第三张卡**，
       与它上方的页面概况模块、下方的表格卡并列 ⇒ 内容区看起来"一堆盒子"。
       乐哥 2026-09-26：「2 3 按照你说的做」——按「它管的是谁」收敛：
         · **只管一张表**（上方无 KPI/图表、与表格间距只差一个 margin）→ **并进卡**（本条）
         · **管整页**（上方有 KPI/图表，如看板首页/排期日历）→ 保持裸放独立成条、**不做成卡**
     ▸ 判据仍是几何量（`markToolbarJoin` 实测垂直间距 ≤ 24px 才加本类），不新增任何判断。
     ──────────────────────────────────────────────────────────────────────────
     `margin-bottom: 0`：与下方表格卡**零缝隙**贴合。实测（4 页）原 16px 归零后
     gap 直接变 0，**中间没有空容器占位**，因此**不需要**去动 markToolbarJoin 那段
     踩过两次坑的负 margin 逻辑 —— 本次是纯 CSS 改动。 */
  margin-bottom: 0;
  padding: var(--space-3) var(--space-5);
  background: var(--color-surface);
  /* ⭐⭐ 2026-09-26 边框色修正（乐哥实报「工具栏和表格栏的边框不一样」）：
     原为 `--color-border-soft`(#EDF0F4，带蓝调) ⇒ 与下方表格卡的
     `--color-frame-border`(#EBECEF，中性) 差一个色调，两块并排能看出"两个灰"。
     ✅ 改用 `--color-frame-border` —— 与 `.card` / `.module-card` 同源。
     ⚠️ 别改回 --color-border-soft：那个令牌是"分隔线/描边"的通用灰（182 处在用），
        而本元素是**一张卡**，应当跟卡片族走（--color-frame-border）。 */
  border: 1px solid var(--color-frame-border);
  /* 下边框去掉：与下方表格卡拼成**一张卡**后，交界处不该有线（否则读起来还是两个框）
     ⚠️ 必须写 `border-bottom-width: 0`，**不能写 `border-bottom: 0`** ——
        后者是简写、会把 `border-bottom-color` 一并重置成 `currentColor`（文字色 #1C1E21）。
        宽度是 0 时看不见，但**读计算样式会读到一个深色**（实测探针按"卡片描边应全站同色"
           断言时被误报成 rgb(28,30,33) 异色）⇒ 只清零宽度，颜色留着由 .card 定。 */
  border-bottom-width: 0;
  /* 上圆角跟卡片、下圆角归零 —— 外轮廓只有最上面一对角 */
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  box-shadow: none;   /* 与 .card 对齐：Gashora 落地后卡片一律"靠描边、不靠阴影" */

  /* ── 并成一排（2026-09-14 乐哥选 A 方案）──────────────────────────────
     背景：合并前表格上方堆了**两排筛选控件**（微信小店只有一排）——
       第1排 .toolbar : 搜索 + 下拉筛选
       第2排 .table-toolbar : 分段 + 共N条/每页
     两排都是筛选控件、视觉重量相近 → 看着重复、层级不清（乐哥：「怎么感觉怪怪的」）。
     做法：JS 把第2排的分段与右侧工具搬进本容器（见 app.js），此处负责排布。

     ⚠️ 用 `flex-wrap: wrap` 而**不是 nowrap**：实测订单明细要 1658px 而只有 1296px
        （它第1排本就有 8 个筛选器），nowrap 会溢出。wrap 让它在同一容器内折行，
        仍是一个连续工具栏，比"两个分离工具栏"好，且绝不溢出。
     ⚠️ 统一控件高度（原 44/42/30 三种混排 → 边缘参差）：
        下拉/输入/按钮 34px、分段项 30px（分段是"标签"，略矮于输入框才协调）。
     ⚠️ 不要给分段加撑满高度的规则 —— 它内部有 padding，撑高会把文字压在中线以下。 */
  flex-wrap: wrap;
  row-gap: var(--space-2);
}
/* ⭐ 2026-09-28 分段控件**恢复外框**（乐哥：「所有页面 tab 胶囊这个 ui 不统一，
   有的有外框，有的没有，需要补进去」）——
   原为 `background: transparent; border: 0; padding: 0`（2026-09-14「两排并一排」时为
   视觉融合而去框）；但同排的下拉/搜索框都是「白底 + 1px 边框」，分段却透明无框 ⇒
   一排控件里"有的有框有的没有"。
   现回基类形态（`.seg` / `.pill-filter` 白底 + 1px 边框）。
   ⚠️ **高度必须与同排控件齐平（34px）**：容器 = 项 32 + 1px×2 边框 + 上下 padding 0
      ⇒ `padding: 0 2px`（上下 0 才能压到 34；左右 2px 给选中块留一丝呼吸）。
      实测：基类 padding 4 会让容器高 42px，比旁边 select(34) 高 8px —— 那是"新的不齐"。
   ⚠️ `height: auto; align-self: center` **保留**：容器按内容自适应、与同排控件中线对齐。 */
.toolbar--join-card > .pill-filter,
.toolbar--join-card > .seg {
  height: auto;
  align-self: center;
  padding: 0 2px;
}
.toolbar--join-card > .table-toolbar__right {
  margin-left: auto;      /* 右侧工具组推到最右 */
  align-self: center;
}
/* 控件高度统一 */
.toolbar--join-card > .select,
.toolbar--join-card > .input,
.toolbar--join-card > .input--search,
.toolbar--join-card > .btn {
  height: var(--control-height-sm);
  min-height: var(--control-height-sm);
}
.toolbar--join-card > .input--search > .input { height: var(--control-height-sm); }
/* ⚠️ 日期选择器（月份）也要一起降（2026-09-14 乐哥：「日期选择的控件尺寸和其他的不对」）
   它是 `.mp__trigger`、被包在 `<span class="mp-host">` 里，**不是**工具栏的直接子元素，
   所以上面那条 `.toolbar--join-card > .select` 覆盖不到它 —— 实测它仍是 44px，
   与同排的 34px 下拉并排就"高出一截"。

   ⚠️⚠️ 必须用**纯后代选择器**，不能写 `.mp-host > .mp__trigger`（实测踩坑）：
      真实 DOM 层级是 `SPAN.mp-host > DIV.mp > BUTTON.mp__trigger` ——
      `.mp__trigger` 是 `.mp-host` 的**孙子**（中间还夹着一层 `DIV.mp`）。
      第一版写了 `>`，选择器**一条都没匹配上**，改了半天没反应
      （排查方式：在页面里遍历 document.styleSheets，用 el.matches(selector) 逐条验证）。
      → 教训：**不要凭想象写选择器的层级**，尤其本项目 DOM 层级不统一；
        能用后代选择器就别用子选择器。 */
.toolbar--join-card .mp__trigger {
  height: 34px;
  min-height: 34px;
}
/* 紧随的卡片去掉上边框与上圆角，与工具栏拼成一体 */
.toolbar--join-target {
  /* ⭐⭐ 2026-09-26 乐哥拍板「工具栏并进表格卡」—— 本块从「恢复独立卡片」改回「拼接态」。
     与 `.toolbar--join-card` 配对：上方那条去掉下边框/下圆角，本条去掉上边框/上圆角，
     两个盒子的外轮廓合起来才是**一张卡**（中间只有一条由自身内容分隔，不画线）。
     ──────────────────────────────────────────────────────────────────────────
     ⚠️ 为什么**不是**改 `.toolbar--join-card` 的 `margin-bottom: 0` 就完事：
        还要同时压掉 `!important`？—— 不，恰恰相反：**必须保留 `margin-top: 0 !important`**。
     ⚠️⚠️ 内联负 margin 仍挂在 DOM 上（实测 -16 / -18.22 / -17.9275px，由 markToolbarJoin
        的补偿逻辑写入），本条 `!important` 把它压住不生效。**这是刻意的**：
        · 不动那段 JS：它 2026-09-18 踩过两次坑（清补偿、双重补偿），注释里写得很细，
          属"已验证但不轻易碰"的代码；
        · 本次实测（4 页）`margin-bottom: 0` 后交界 gap 直接为 0，**没有需要负 margin 去吃的缝**
          ⇒ 让 `!important` 继续压着，行为最可预测。
        ⛔ 若将来有人去掉这个 `!important`：那些 -18.22px 会立刻生效，
           把表格卡**压进工具栏**、盖住末行控件（正是 2026-09-18 修复的那个故障）。
     ⭐ 2026-09-26 边框色：本条原写 `--color-border-soft`（与同卡其余三边不一致，被乐哥实报）。
       现在上边框直接归零、不再画线，该不一致自然消失；其余三边由 `.card` 统一为
       `--color-frame-border`。
     ⚠️ 同 `.toolbar--join-card`：用 `border-top-width: 0` 而非 `border-top: 0`
        （后者会把颜色重置成 currentColor，读计算样式会读到深色文字色）。 */
  border-top-width: 0 !important;
  border-radius: 0 0 var(--radius-lg) var(--radius-lg) !important;
  margin-top: 0 !important;
}
/* 合并后卡内首个 .table-toolbar 不需要顶部分隔（工具栏与它本属同一控制区） */
.toolbar--join-target > .table-toolbar:first-child {
  border-top: 0;
}

.toolbar__spacer {
  flex: 1;
}

/* 工具栏右侧分组（2026-09-20 补 — 财务模块需要"左侧筛选 + 右侧操作"的工具栏）。
   ⚠️ 为什么在 overlays.css 补而不是 pages-finance.css：`.toolbar` 是本文件里的
      **标准组件**，给它加一个通用子元素应当加在这里，其它页面才能复用。
      （原来只有 `.toolbar__spacer`，用它把右侧内容顶过去、但分组本身没有容器，
        多控件时得各自写 inline style。）
   ⚠️ `margin-left: auto` 与 `.toolbar__spacer` 效果等价、二选一即可 ——
      **不要两个都写**（会让间距出现双重推力，窄屏换行时位置变得难以预测）。 */
.toolbar__right {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-left: auto;
}

/* ---- 冲突 / 提示条 ---- */
.alert {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  font-size: var(--font-size-sm);
}

.alert--warning {
  background: var(--color-warning-bg);
  color: var(--color-warning);
}

.alert--danger {
  background: var(--color-danger-bg);
  color: var(--color-danger);
}

.alert--info {
  background: var(--color-info-bg);
  color: var(--color-info);
}
