/**
 * 设计令牌与全局样式（shopdiary 原型）
 * 职责：统一品牌视觉，覆盖 TDesign 主色，提供顶栏 KPI、卡片、空态等精致样式。
 * 说明：纯展示层，组件外观尽量交给 TDesign，此处仅做主题一致性与氛围提升。
 *
 * ==========================================================================
 * 全站视觉禁令（用户明确要求，不要违反）
 * ==========================================================================
 * ① **不要「模块左侧彩色竖线」**。即不准给卡片 / 说明块 / 列表项加
 *    `border-left: Npx solid <彩色>`（N ≥ 3）、也不准用 `box-shadow: inset Npx 0 0` 画等价效果。
 *    需要强调一个块时，统一用「**四边 1px 浅灰边框 + 圆角 +（可选）浅底**」这一套版式
 *    （`.act-formula` / `.mst-tip` / `.sm-caveat` / `.pr-rule-note` 都已改成这样）。
 *    需要表达「当前选中」时，用**更深的品牌浅底（--sd-brand-a12）+ 文字加粗**，
 *    不要用左侧色条（见 `.perm-staff-on` / `.tpl-item-on`）。
 *    ⚠️ 历史上这里散落过 7 处左侧竖条（`.act-card` / `.mst-tip` / `.pr-rule-note` /
 *    `.ra-healthy-line.is-attention` / `.sm-caveat` / `.tpl-item` / `.perm-staff`），
 *    已全部删除，**不要加回来**。守卫在 `verify_layout_fixed_e2e.js`（逐页扫描
 *    「border-left 比其它三边粗且非透明」的元素，命中即失败）。
 * ② 颜色一律走 `--sd-*` 变量，不要写死色值（否则换主题时这一块不会跟着变）。
 * ③ 不要加响应式断点（见下面 `.t-layout__content > *` 那段的长注释）。
 */

:root {
  /* 品牌与语义色（业务定制，覆盖 TDesign 默认）。
     这里的字面值 = 默认色调「淡蓝」在 src/theme.js 里算出来的结果，
     保证「JS 生效前 / 禁用 JS」时也是同一套配色，不会先渲染一个色再闪成另一个色。
     运行时会由 theme.js 按用户选择覆盖以下 --sd-brand* / --td-brand-color* 变量。 */
  --sd-brand: #4a90e2;
  --sd-brand-strong: #0e509d;
  --sd-brand-soft: #c7dcf3;
  /* 品牌色带透明度的浅底/描边（财务高亮卡、AI Tab 激活描边、输入框聚焦光圈），
     原先硬编码成蓝色 rgba，换主题后会「漏蓝」，故改为变量由 theme.js 一并下发。 */
  --sd-brand-a08: rgba(74, 144, 226, 0.08);
  --sd-brand-a12: rgba(74, 144, 226, 0.12);
  /* 侧栏 IconPark 双色图标的副色（默认态 / 激活态） */
  --sd-icon-fill: #c7dcf3;
  --sd-icon-fill-active: #a0bfe1;
  --sd-success: #1aa97f;
  --sd-warning: #d9822b;
  --sd-danger: #e35d5d;

  /* 表面与文本 */
  --sd-bg: #f4f6fb;
  --sd-surface: #ffffff;
  --sd-surface-2: #fafbfe;
  --sd-text: #1f2733;
  --sd-text-2: #5a6573;
  --sd-text-3: #909399;
  --sd-border: #e8ebf2;

  /* 大面积渐变（顶栏 KPI / AI 海报 / 排名条等）。默认给「淡蓝」兜底，
     切换主题时由 theme.js 动态覆盖为对应色调的渐变。 */
  --sd-brand-gradient: linear-gradient(120deg, #4a90e2 0%, #8fc0f0 100%);

  /* 报表饼图 / 环形图的分类色板（8 色）。这里的字面值 = 默认主题「淡蓝」经
     ThemeManager.buildPiePalette() 派生出来的结果，切换主题时由 theme.js 覆盖。
     图表统一用 var(--sd-pie-N) 取色，所以换主题时颜色会**即时跟着变**，不需要重渲染图表。
     为什么不是一套固定色板：旧实现写死了 8 个莫兰迪色（其中 5 个恰好是其它主题的主色、
     1 个还是已下线的雾霾蓝），结果饼图跟当前主题毫无关系、切换时纹丝不动。 */
  --sd-pie-1: #4a90e2;
  --sd-pie-2: #7b8ad5;
  --sd-pie-3: #449fbb;
  --sd-pie-4: #6b5ccc;
  --sd-pie-5: #80d1cc;
  --sd-pie-6: #773dc2;
  --sd-pie-7: #62c6a1;
  --sd-pie-8: #d5ab7b;

  /* 形状与阴影 */
  --sd-radius: 12px;
  --sd-radius-sm: 8px;
  --sd-shadow: 0 2px 14px rgba(22, 43, 88, 0.07);
  --sd-shadow-hover: 0 6px 22px rgba(22, 43, 88, 0.12);

  /* 内容区的最小宽度（= 窗口变窄时的排版下限）。
     低于它不重排、不挤压，改为内容区横向滚动。取值依据见
     .t-layout__content > * 那一段的长注释（实测：内容自然最小 1038px，
     开单购物篮表格在 <1091px 开始被压缩，故取 1120 留余量）。 */
  --sd-min-content-width: 1120px;

  /* ===== TDesign 设计令牌对齐（覆盖默认蓝绿，统一品牌色） ===== */
  --td-brand-color: #4a90e2;
  --td-brand-color-hover: #6aa4e7;
  --td-brand-color-active: #0e509d;
  --td-brand-color-light: #c7dcf3;
  --td-brand-color-light-hover: #b3cdeb;
  --td-brand-color-focus: #a0bfe1;
  --td-brand-color-disabled: #90b0d6;

  /* 品牌色阶（默认「淡蓝」的 10 阶；运行时由 theme.js 按所选色调重新生成） */
  --td-brand-color-1: #ddeafa;
  --td-brand-color-2: #c7dcf3;
  --td-brand-color-3: #b3cdeb;
  --td-brand-color-4: #a0bfe1;
  --td-brand-color-5: #90b0d6;
  --td-brand-color-6: #81a2c8;
  --td-brand-color-7: #4a90e2;
  --td-brand-color-8: #1a70d2;
  --td-brand-color-9: #0e509d;
  --td-brand-color-10: #053164;

  --td-success-color: #1aa97f;
  --td-success-color-hover: #3bbd95;
  --td-success-color-active: #148063;
  --td-success-color-light: #e6f6f1;
  --td-success-color-light-hover: #d4f0e8;

  --td-warning-color: #d9822b;
  --td-warning-color-hover: #e4974c;
  --td-warning-color-active: #b4661c;
  --td-warning-color-light: #fbf0e3;
  --td-warning-color-light-hover: #f7e5d3;

  --td-error-color: #e35d5d;
  --td-error-color-hover: #ea7a7a;
  --td-error-color-active: #c44242;
  --td-error-color-light: #fbe9e9;
  --td-error-color-light-hover: #f7dada;
}

html,
body {
  margin: 0;
  height: 100%;
  background: var(--sd-bg);
  color: var(--sd-text);
  font-family: "PingFang SC", "Microsoft YaHei", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

#app {
  height: 100%;
}

/* ============ 品牌区 ============ */
.brand {
  font-size: 18px;
  font-weight: 700;
  color: var(--sd-brand);
  padding: 18px 16px 14px;
  letter-spacing: 0.3px;
  white-space: normal;
}
.brand .tag {
  display: block;
  font-size: 12px;
  color: var(--sd-text-2);
  font-weight: 400;
  margin-left: 0;
  margin-top: 4px;
}

/* 侧边菜单氛围（TDesign 渲染类为 .t-layout__sider，注意不是 .t-aside） */
.t-layout__sider {
  background: var(--sd-surface) !important;
  border-right: 1px solid var(--sd-border);
  /* 纵向 flex 列：品牌区固定高度、菜单区占剩余高度并独立滚动。
     关键修复（深度）：TDesign 的 .t-default-menu 是 inline-block 且无自身高度，
     其内 .t-default-menu__inner 写 height:100% 依赖父有确定高度。
     原 overflow:hidden 会在菜单超高时把底部分类裁掉（视觉“消失”），
     而 ul.t-menu--scroll 因父高 unbounded 不出现滚动条。
     故在此钉死整条高度链：侧栏有确定高度(col flex + height:100%)，
     菜单根转为 block 并 flex:1/min-height:0 占剩余高度，
     滚动交给 ul.t-menu--scroll，彻底摆脱 JS 测高，任意视口下底部分类可见可滚。 */
  display: flex !important;
  flex-direction: column !important;
  height: 100%;
  overflow: hidden;
  /* 核心修复（真实 Edge 浏览器实测定位）：t-layout 横向 flex 布局下，点击菜单
     切到超宽内容视图（如开单）时，默认 flex-shrink:1 会把侧栏从 217px 挤压到约
     1px，表现为“整个菜单消失”（实测 siderRect.w 217→1.3）。flex:0 0 auto 禁止
     侧栏被挤压收缩，这是修复“点击菜单就消失”的关键，与此前修的纵向滚动无关。 */
  flex: 0 0 auto !important;
}
/* 右侧布局允许收缩到剩余宽度；内容超宽时在内容区内横向滚动，而不是挤压侧栏 */
.t-layout--with-sider > .t-layout {
  min-width: 0;
}
.t-layout__content {
  min-width: 0;
  overflow-x: auto;
}
/* ==========================================================================
   固定排版（窗口变窄不许重排）
   ==========================================================================
   需求：窗口缩小时整体页面**保持同一套排版**，不把双栏压成单栏、不把 5 列 KPI 压成 2 列。

   为什么把最小宽度加在「内容区的直接子元素」上，而不是媒体查询或 .t-layout__content 本身：
     ① 顶栏（.topbar）与各页容器都是 .t-layout__content 的**直接子元素**；
     ② .t-layout__content 本身就是滚动容器（overflow:auto / overflow-x:auto），
        给子元素设下限 → 内容不再被挤扁，改为在**内容区内**横向滚动，
        侧栏（.t-layout__sider，flex:0 0 auto）始终完整可见、不会被滚出视野；
     ③ 若把 min-width 加在 .t-layout__content 上，flex 行就再也压不动它，
        整个 .t-layout 会向外溢出 → 变成整页横滚、侧栏被滚走，反而更难用。

   1120px 这个值不是拍脑袋，是量出来的（`_probe_layout_width.js` 实测）：
     · 内容区的**自然最小宽度约 1038px**（由顶栏 KPI 一行决定，再窄就溢出）；
     · 开单页购物篮表格在内容 < 1091px 时开始被 TDesign 等比压缩列宽
       （列宽一压，t-input-number 的文本区会被压到 0 宽，是踩过的坑）；
     · 取 1120 留一点余量 → 1366 笔记本全屏（内容 1149px）**不会出现横向滚动条**，
       1280 及以下才出现，且出现的是横向滚动而不是排版变形。
   ⚠️ 不要再加 @media 断点，也不要调小这个值（那等于把"排版会被挤变形"放回来）。
   ========================================================================== */
.t-layout__content > * {
  min-width: var(--sd-min-content-width);
}
.t-layout__sider > .brand {
  flex: 0 0 auto;
}
.t-layout__sider > .t-default-menu {
  flex: 1 1 auto;
  min-height: 0;
  display: block !important;   /* 覆盖 TDesign inline-block，确保参与高度链 */
  width: 100% !important;      /* 覆盖 TDesign 232px 默认宽，填满侧栏不横向溢出 */
  overflow: hidden;            /* 容器自身不溢出侧栏 */
}
/* 菜单内部高度链：inner 跟随菜单根高度，ul 在有限高度内滚动 */
.t-default-menu__inner {
  height: 100% !important;
}
.t-default-menu__inner > .t-menu.t-menu--scroll {
  flex: 1 1 auto;
  min-height: 0;
  max-height: 100% !important;
  overflow-y: auto !important;
}
.t-menu {
  border-right: none !important;
  background: transparent !important;
  /* 关键：覆盖 TDesign 菜单默认宽度（约 232px），使其填满侧栏，不再溢出到内容区 */
  width: 100% !important;
  box-sizing: border-box;
}
.t-menu__item,
.t-submenu__title {
  border-radius: 8px;
  margin: 4px 10px;
  height: 44px;
  line-height: 44px;
  /* 防溢出：超长标签省略，绝不横向撑破侧栏 */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.t-menu__item.t-is-active {
  background: var(--sd-brand-soft) !important;
  color: var(--sd-brand-strong) !important;
  font-weight: 600;
}

/* ============ 侧栏 IconPark 双色图标 ============ */
/* 图标来源：字节官方 IconPark（Apache-2.0），由 build_icons.js 生成到 src/icons.js。
   主色写为 currentColor → 自动跟随菜单文字色（激活态变品牌蓝）；
   次色写为 var(--ip-fill) → 在下方按状态覆盖深浅。 */
.ip-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  /* 菜单项 .t-menu__item 是 flex 但无 gap，图标间距需自己给；
     数值与 TDesign 原生 .t-icon 的 margin-right 对齐，保证与其它菜单视觉一致 */
  margin-right: var(--td-comp-margin-s, 8px);
}
.ip-icon svg {
  width: 18px;
  height: 18px;
  display: block;
}
/* 默认态：次色用浅主色，与灰色主色形成柔和双色。
   取值走 --sd-icon-fill（由 theme.js 按所选色调下发）——否则换到粉色/绿色主题后，
   侧栏图标副色仍是蓝的，一眼就看出「没换干净」。 */
.t-menu__item,
.t-submenu__title {
  --ip-fill: var(--sd-icon-fill, #c7dcf3);
}
/* 激活态：主色已由 color 变品牌色，次色同步加深以保证对比度 */
.t-menu__item.t-is-active,
.t-submenu__title.t-is-active {
  --ip-fill: var(--sd-icon-fill-active, #a0bfe1);
}

/* ============ 顶栏 KPI ============ */
.topbar {
  display: flex;
  align-items: center;
  gap: 14px;
  background: var(--sd-brand-gradient);
  color: #fff;
  padding: 14px 22px;
  border-radius: var(--sd-radius);
  box-shadow: var(--sd-shadow);
}
.topbar .tb-title {
  font-size: 16px;
  font-weight: 700;
  margin-right: 8px;
  white-space: nowrap;
}
.topbar .kpi {
  display: flex;
  flex-direction: column;
  padding: 0 16px;
  border-left: 1px solid rgba(255, 255, 255, 0.25);
  min-width: 96px;
}
.topbar .kpi:first-of-type {
  border-left: none;
}
.topbar .kpi .k-label {
  font-size: 12px;
  opacity: 0.85;
}
.topbar .kpi .k-value {
  font-size: 20px;
  font-weight: 700;
  line-height: 1.2;
}
.topbar .kpi .k-value small {
  font-size: 12px;
  font-weight: 400;
  opacity: 0.85;
}
.topbar .spacer {
  flex: 1;
}
.topbar .store-pill {
  background: rgba(255, 255, 255, 0.18);
  border-radius: 999px;
  padding: 6px 14px;
  font-size: 13px;
}

/* ============ 卡片 / 主页动作卡 ============ */
.home-actions {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
  margin-bottom: 16px;
}
.action-card {
  cursor: pointer;
  transition: transform 0.08s, box-shadow 0.12s;
  border-radius: var(--sd-radius);
  border: 1px solid var(--sd-border);
}
.action-card:hover {
  transform: translateY(-3px);
  box-shadow: var(--sd-shadow-hover);
}
.action-card .big {
  font-size: 20px;
  font-weight: 700;
  color: var(--sd-brand);
}
.action-card .desc {
  font-size: 12.5px;
  color: var(--sd-text-2);
  margin-top: 6px;
  line-height: 1.6;
}

/* 通用卡片观感 */
.t-card {
  border-radius: var(--sd-radius) !important;
  border-color: var(--sd-border) !important;
}

/* 色码按钮容器：自动换行 + 统一间距，避免多款商品色码挤成一团溢出 */
.variant-list {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 10px;
}
/* 色码按钮：间距由容器 gap 控制，自身不再加 margin */
.variant-btn {
  margin: 0;
}

/* 库存状态着色 */
.stock-out {
  color: var(--sd-danger);
  font-weight: 600;
}
.stock-low {
  color: var(--sd-warning);
  font-weight: 600;
}

/* 空态文案居中留白 */
.empty-hint {
  padding: 40px 0;
  text-align: center;
  color: var(--sd-text-3);
}

/* 开单页商品区「未输入搜索词」引导空态：居中图标 + 主副文案，降低录入门槛 */
.pos-empty-guide {
  grid-column: 1 / -1; /* 占满双列网格整行 */
  padding: 48px 20px;
  text-align: center;
  color: var(--sd-text-3);
}
.pos-empty-guide .ip-icon {
  display: inline-block;
  width: 40px;
  height: 40px;
  margin-bottom: 12px;
  opacity: 0.5;
}
.pos-empty-guide .pos-empty-title {
  font-size: 15px;
  font-weight: 600;
  color: var(--sd-text-2);
  margin-bottom: 6px;
}
.pos-empty-guide .pos-empty-sub {
  font-size: 13px;
  color: var(--sd-text-3);
}

/* 区块小标题。
   原先这里用 `::before` 画了一条 4x16px 的品牌色小竖条（"标题左侧彩色竖线"），
   按用户要求全站去掉竖线，已删除 —— 见文件顶部「全站视觉禁令」。
   标题本身已是 15px / font-weight:700，靠字号与字重足够醒目，不需要色条辅助。
   ⚠️ 不要再加回来。 */
.section-title {
  font-size: 15px;
  font-weight: 700;
  color: var(--sd-text);
  margin: 18px 0 10px;
  display: flex;
  align-items: center;
  gap: 8px;
}

/* 开单：营销优惠实时预览（常显紧凑高亮块，呼应 AI 补库存长条，无左侧色条） */
.promo-bar {
  display: flex;
  align-items: center;
  gap: 8px 10px;
  margin: 10px 0;
  padding: 8px 12px;
  background: #f3fbf7; /* 浅绿底，呼应"优惠/成功"，非左侧色条 */
  border: 1px solid #b7e8d3;
  border-radius: var(--sd-radius-sm);
  flex-wrap: wrap;
}
.pb-icon {
  font-size: 15px;
  flex: none;
  line-height: 1;
}
.pb-label {
  font-size: 13px;
  font-weight: 700;
  color: var(--sd-success, #1aa97f);
  flex: none;
}
.pb-items {
  flex: 1;
  min-width: 160px;
  display: flex;
  flex-wrap: wrap;
  gap: 2px 10px;
  font-size: 12.5px;
  color: var(--sd-text-2);
}
.pb-item b {
  color: var(--sd-text);
}
.pb-save {
  font-size: 12.5px;
  color: var(--sd-text-2);
  flex: none;
}
.pb-final {
  font-size: 13px;
  color: var(--sd-text);
  font-weight: 600;
  flex: none;
}
.pb-final b {
  color: var(--sd-danger);
  font-size: 15px;
}

/* ===== 优惠明细表格（购物篮「已享优惠」模块） ===== */
/* 整体：浅绿渐变卡片，圆角 + 细边框，视觉上更像「优惠账单」而非横排标签 */
.promo-table {
  margin: 10px 0;
  border: 1px solid #bfe8d4;
  border-radius: var(--sd-radius);
  background: linear-gradient(180deg, #f3fbf7 0%, #fbfefc 100%);
  overflow: hidden;
}
/* 表头：奖杯图标 + 标题 + 项数 */
.pt-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 14px;
  font-size: 13px;
  font-weight: 700;
  color: #1a9d70;
  border-bottom: 1px dashed #d2efe0;
}
.pt-head .ip-icon {
  width: 16px;
  height: 16px;
}
.pt-head-count {
  margin-left: auto;
  font-size: 11px;
  font-weight: 500;
  color: #7bbf9d;
  background: #e6f7ef;
  border-radius: 10px;
  padding: 1px 8px;
}
/* 明细行：左规则名+描述，右单项节省金额 */
.pt-body {
  padding: 4px 0;
}
.pt-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 14px;
}
.pt-row + .pt-row {
  border-top: 1px solid #eef7f1;
}
.pt-main {
  flex: 1;
  min-width: 0;
}
.pt-name {
  font-size: 13px;
  font-weight: 600;
  color: var(--sd-text);
  line-height: 1.4;
}
.pt-detail {
  font-size: 12px;
  color: var(--sd-text-3);
  margin-top: 1px;
}
/* 单项节省金额：绿色负号醒目 */
.pt-save {
  flex: none;
  font-size: 13px;
  font-weight: 700;
  color: #1aa97f;
}
.pt-save.zero {
  font-size: 12px;
  font-weight: 500;
  color: var(--sd-text-3);
}
/* 底部汇总（.pt-foot / .pt-foot-save / .pt-foot-final）已删除 —— 原因：
   它把「已省 / 实付」又汇总了一遍，而购物篮下方还有一个「合计」，同一笔钱在界面上
   出现两处、且口径不同（实付在上、原价合计在下，合计反而更大更靠下，紧贴收款按钮），
   收银员容易照着大的那个数收钱。现在汇总统一归 .ss-block（见下方「结算汇总」一节），
   促销表只负责说明"哪几条规则生效、各条省了多少"。**不要加回来**。 */

/* 「代码已更新，请刷新」提示条（.build-stale）
   只在"磁盘代码比本页面新"时出现，正常使用看不到。
   配色走 --sd-warning 主题变量；两端对齐让"立即刷新"贴在右侧、不随文字长度飘。 */
.build-stale {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 12px;
  padding: 10px 14px;
  border: 1px solid var(--sd-warning);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface-2);
  font-size: 13px;
  line-height: 1.6;
  color: var(--sd-text);
}
.build-stale-text {
  min-width: 0;
}

/* ==========================================================================
   开单页「导购」模块（.emp-pick）
   ==========================================================================
   用户要求：做成一个**模块**，每个导购「头像 + 右侧名字」，**一排 5 个**（不是下拉）。
   理由是可用的：收银时下拉要先展开再找名字，头像一排则是"点一下认出是谁"。
   头像用「首字 + 主题色板（--sd-pie-N）」：项目没有员工头像素材，临时引图会破坏
   图标/配色的一致性；按 id 哈希稳定取色，同一个人永远同色，换主题也跟着变。
   ========================================================================== */
.emp-pick {
  margin-bottom: 12px;
  padding: 10px 12px;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface-2);
}
.emp-pick-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
}
.emp-pick-title {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 13px;
  font-weight: 600;
  color: var(--sd-text);
}
.emp-pick-warn {
  font-size: 12px;
  color: var(--sd-warning);
}
.emp-pick-picked {
  font-size: 12px;
  color: var(--sd-text-2);
}
.emp-pick-empty {
  padding: 4px 0;
  font-size: 12px;
  line-height: 1.7;
  color: var(--sd-text-3);
}
/* 一排 5 个：用 repeat(5, minmax(0,1fr)) 而不是 auto-fill —— 要的是"每行固定 5 个"，
   多于 5 位导购时换行。minmax(0,1fr) 保证窄容器下不被内容顶宽。 */
.emp-pick-grid {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 8px;
}
.emp-card {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  padding: 6px 8px;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface);
  cursor: pointer;
  text-align: left;
  transition: border-color 0.15s, background 0.15s;
}
.emp-card:hover {
  border-color: var(--sd-brand);
}
/* 选中态：品牌色描边 + 浅底（不靠颜色单一信号：描边变粗一档，灰度截图也看得出来） */
.emp-card-on {
  border-color: var(--sd-brand);
  box-shadow: 0 0 0 1px var(--sd-brand);
  background: var(--sd-brand-a12);
}
.emp-avatar {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  color: #fff;
  font-size: 13px;
  font-weight: 600;
  /* 背景由组件按 id 哈希注入 var(--sd-pie-N)，这里只兜底 */
  background: var(--sd-brand);
}
.emp-card-body {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.emp-card-name {
  font-size: 13px;
  color: var(--sd-text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.emp-card-role {
  font-size: 11px;
  color: var(--sd-text-3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.emp-card-on .emp-card-name {
  font-weight: 600;
  color: var(--sd-brand);
}

/* 开单页「① 选商品」顶部的「订单归属」字段行（顾客 / 导购）。
   放在最上面是因为真实收银顺序是"先认人、再拿货"；
   左侧标签固定宽度，保证两个字段的下拉框左边缘对齐、宽度一致。 */
.pos-field-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 10px;
}
.pos-field-label {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  flex: none;
  font-size: 13px;
  color: var(--sd-text-2);
}
.pos-field-control {
  width: 100%;
  max-width: 420px;
}
/* 「散客」按钮（顾客栏右侧）。
   flex:none 是必须的：左侧 .pos-field-control 是 width:100%，不写这句按钮会被压扁。
   为什么常驻可见、不随"有没有选会员"出现或消失：它的作用就是让收银员**随时一眼看到**
   当前是不是散客 —— 只在选中会员时出现就失去了这个意义。
   ⚠️ 本文件是普通 CSS（不是模板字符串），注释里可以正常写反引号。 */
.pos-field-btn {
  flex: none;
}
/* 按钮内的对勾：.ip-icon 默认 margin-right 为 8px，在 size="small" 的按钮里偏松 */
.pos-field-btn .ip-icon {
  margin-right: 4px;
}
/* 必选而未选时的小字提醒（导购未选时出现）：不占位、选了就消失，不给已完成的流程加噪音 */
.pos-field-warn {
  flex: none;
  font-size: 12px;
  color: var(--sd-warning);
}

/* ==========================================================================
   收款弹窗：收款方式做成「图标 + 文字」卡片
   ==========================================================================
   为什么不是单选框：收银场景要的是"一眼认出、不看错行"（用户原话
   「增加图标效果+文字的方式，这样不会选错」）。四宫格比四行单选更容易扫到。
   图标走 AccountStore.describeType 的映射（与收款账户设置页同源），配色一律用主题变量。
   ========================================================================== */
.pay-method-title {
  margin: 16px 0 6px;
  font-size: 13px;
  color: var(--sd-text-2);
}
.pay-methods {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}
.pay-method {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 9px 12px;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface);
  color: var(--sd-text);
  font-size: 13px;
  line-height: 1.2;
  text-align: left;
  cursor: pointer;
  transition: border-color 0.15s, background 0.15s;
}
.pay-method:hover {
  border-color: var(--sd-brand);
}
/* 选中态：品牌色描边 + 浅底 + 图标染品牌色（三处一起，小图上也能一眼看出选中谁） */
.pay-method-on {
  border-color: var(--sd-brand);
  background: var(--sd-brand-a12);
  color: var(--sd-brand);
  font-weight: 600;
}
.pay-method .ip-icon {
  flex: none;
  width: 20px;
  height: 20px;
}
.pay-method .ip-icon svg {
  width: 100%;
  height: 100%;
}
.pay-method-name {
  min-width: 0;
}
/* 储值额外说明（余额抵扣）：单独一行小字，不挤坏名称 */
.pay-method-sub {
  margin-left: auto;
  flex: none;
  font-size: 11px;
  color: var(--sd-text-3);
}
/* 不可用（未识别会员或余额为 0）：整体降饱和并禁点，但**仍显示** —— 
   让收银员知道"有储值这个方式，只是这单用不了"，而不是以为系统没有这个功能 */
.pay-method-disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.pay-method-disabled:hover {
  border-color: var(--sd-border);
}

/* 收款弹窗里的导购只读确认（选择入口在开单页左侧） */
.ck-emp {
  font-size: 13px;
  font-weight: 600;
  color: var(--sd-text);
}

/* ==========================================================================
   结算汇总（.ss-*）—— 开单/收款页购物篮底部的金额模块
   口径与格式均由 src/settleSummary.js 决定，本文件只管外观。
   配色一律走主题变量（--sd-*），换主题时自动跟随，不要写死色值。
   ========================================================================== */
.ss-block {
  margin: 10px 0 0;
  padding: 10px 14px;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius);
  background: var(--sd-surface-2);
}
.ss-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  padding: 3px 0;
  font-size: 13px;
}
.ss-label {
  display: flex;
  align-items: baseline;
  gap: 8px;
  min-width: 0;
  color: var(--sd-text-2);
}
/* 件数是补充说明，比行名更弱一档，避免与金额抢注意力 */
.ss-label-extra {
  font-size: 12px;
  font-style: normal;
  color: var(--sd-text-3);
}
.ss-amount {
  flex: none;
  font-weight: 600;
  color: var(--sd-text);
  /* 等宽数字：上下两行金额的个位/小数位对齐，扫一眼就能比对 */
  font-variant-numeric: tabular-nums;
}
/* 已优惠：绿色语义（省下的钱），与「已享优惠」促销表的绿色保持一致 */
.ss-row-discount .ss-amount {
  color: var(--sd-success);
}
/* 应付：整块最重要的一行。用分隔线把它与上面「合计/优惠」的推导过程分开，
   字号放大加粗做强调。
   为什么不用红色：红色在本项目里是 --sd-danger（错误/危险）语义，
   「应付」是正常金额不是异常，染色会造成误读；强调靠字号与字重表达。
   这也与收款弹窗里那个大号实付金额（默认文字色）保持一致。 */
.ss-row-payable {
  margin-top: 6px;
  padding-top: 8px;
  border-top: 1px dashed var(--sd-border);
}
.ss-row-payable .ss-label {
  color: var(--sd-text);
  font-weight: 600;
}
.ss-row-payable .ss-amount {
  font-size: 22px;
  font-weight: 800;
  line-height: 1.2;
}
.ss-warn {
  margin-top: 6px;
  font-size: 12px;
  line-height: 1.5;
  color: var(--sd-danger);
}

/* 收款区第二行：「收款并打印小票」+ 右侧「打印小票设置」入口。
   左侧按钮 flex:1 吃掉剩余宽度（与上方「去收款」等宽，两个主操作视觉上对齐），
   右侧入口按内容宽度贴着右边 —— 不要给左侧按钮固定宽度，否则窄卡里会被挤成两行。 */
.pos-pay-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 8px;
}
.pos-pay-print {
  flex: 1 1 auto;
  min-width: 0;
}

/* 会员权益列表 */

/* 步骤指示器（拿货两步流程） */
.step-bar {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 4px 0 4px;
}
.step-item {
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--sd-text-3);
  font-size: 13px;
}
.step-item.active {
  color: var(--sd-text);
  font-weight: 600;
}
.step-dot {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--sd-surface-2);
  border: 1px solid var(--sd-border);
  color: var(--sd-text-3);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  font-weight: 700;
  flex: none;
}
.step-item.active .step-dot {
  background: var(--sd-brand);
  border-color: var(--sd-brand);
  color: #fff;
}
.step-line {
  width: 28px;
  height: 1px;
  background: var(--sd-border);
}

/* 拿货：选款构建区双栏 */
.builder-grid {
  display: grid;
  grid-template-columns: 1.6fr 1fr;
  gap: 16px;
  align-items: start;
}
.builder-side {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.builder-cta {
  display: flex;
  flex-direction: column;
  gap: 14px;
  background: var(--sd-surface-2);
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius);
  padding: 16px;
  position: sticky;
  top: 12px;
}
.cta-label {
  font-size: 13px;
  color: var(--sd-text-2);
}
.cta-strong {
  font-size: 22px;
  font-weight: 800;
  color: var(--sd-brand);
}
/* 拿货单明细缩略图 */
.line-thumb {
  width: 30px;
  height: 30px;
  border-radius: 6px;
  object-fit: cover;
  border: 1px solid var(--sd-border);
  flex: none;
}
.perk-list {
  margin: 0 0 4px;
  padding-left: 18px;
  font-size: 13px;
  color: var(--sd-text-2);
  line-height: 1.9;
}
.perk-list li b {
  color: var(--sd-brand);
}

/* 会员中心：卡片化布局（等高 + 底栏对齐 + 响应式） */
.member-card {
  height: 100%;
}
.member-card .t-card__body {
  height: 100%;
  display: flex;
  flex-direction: column;
}
.member-head {
  display: flex;
  align-items: center;
  gap: 12px;
}
.member-avatar {
  width: 42px;
  height: 42px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  font-weight: 700;
  font-size: 18px;
  flex: none;
}
.member-id {
  flex: 1;
  min-width: 0;
}
.member-name {
  font-size: 15px;
  font-weight: 700;
  color: var(--sd-text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.member-phone {
  font-size: 12px;
  color: var(--sd-text-3);
  margin-top: 2px;
}
.member-tier {
  flex: none;
}
.member-body {
  flex: 1;
}
.perk-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.perk-chip {
  font-size: 12px;
  color: var(--sd-text-2);
  background: var(--sd-surface-2);
  border: 1px solid var(--sd-border);
  border-radius: 999px;
  padding: 2px 10px;
  line-height: 1.7;
}
.perk-chip b {
  color: var(--sd-brand);
}
.member-progress-tip {
  font-size: 12px;
  color: var(--sd-text-2);
  margin-top: 6px;
}
.member-max {
  font-size: 12px;
  color: var(--sd-success);
  margin-top: 6px;
}
.member-stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
}
.m-stat {
  background: var(--sd-surface-2);
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  padding: 8px 6px;
  text-align: center;
}
.m-stat-num {
  font-size: 15px;
  font-weight: 700;
  color: var(--sd-text);
}
.m-stat-lab {
  font-size: 11px;
  color: var(--sd-text-3);
  margin-top: 2px;
}
.member-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 10px;
  font-size: 12px;
  color: var(--sd-text-3);
}
.member-foot {
  margin-top: auto;
  padding-top: 8px;
}

/* 库存 KPI 卡（库存经营中心） */
.kpi-card {
  text-align: center;
  border-radius: var(--sd-radius) !important;
}
.kpi-card .k-num {
  font-size: 26px;
  font-weight: 700;
  line-height: 1.2;
}
.kpi-card .k-lab {
  font-size: 13px;
  color: var(--sd-text-2);
  margin-top: 2px;
}

/* 预警汇总：一排 5 个等宽卡片。
   TDesign 栅格为 24 列，无法整除 5（24/5=4.8），故改用 CSS grid 保证 5 等分。 */
.kpi-row-5 {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 12px;
}
/* 允许卡片在窄列内收缩，避免长数字撑破等宽栅格 */
.kpi-row-5 > .kpi-card {
  min-width: 0;
}
/* 窄屏降级：平板 3 列、手机 2 列 */
/* ==========================================================================
   本文件**刻意没有响应式断点**，也不要再加回来 —— 这是产品决策，不是遗漏。

   原因：这是桌面端进销存 / POS 系统，用户明确要求「窗口变窄时整体页面不许改变排版」。
   原先这里散着 31 个 @media (max-width: 520~1280px) 块，会在窗口变窄时把双栏 / 三栏网格
   压成单栏、把 5 列 KPI 压成 2 列、把 flex 行改成纵向 —— 收银员把窗口缩小一点（或笔记本
   分屏）版式就变样，正在点的按钮会跳位，这比「需要横向滚动」更糟。

   现在改为：窗口再窄也保持同一套排版，靠横向滚动查看（最小宽度约束见文件末尾
   「固定排版」那一节）。

   ⚠️ 要适配窄屏时**不要**恢复断点 —— 那等于把这个问题修回去。要么就用横向滚动，
   要么单独做一套移动端视图。
   ========================================================================== */

/* 款缩略图（通用） */
.style-thumb {
  width: 40px;
  height: 40px;
  border-radius: 8px;
  object-fit: cover;
  border: 1px solid var(--sd-border);
  flex: none;
}
.style-thumb-lg {
  width: 56px;
  height: 56px;
  border-radius: 10px;
  object-fit: cover;
  border: 1px solid var(--sd-border);
  flex: none;
}
/* 表格内小缩略图（库存搜索结果列表） */
.style-thumb-sm {
  width: 34px;
  height: 34px;
  border-radius: 6px;
  object-fit: cover;
  border: 1px solid var(--sd-border);
  flex: none;
}
/* 缩略图缺省占位 */
.thumb-ph {
  display: inline-flex;
  width: 34px;
  height: 34px;
  border-radius: 6px;
  align-items: center;
  justify-content: center;
  background: var(--sd-bg-2, #f3f3f3);
  color: #bbb;
  font-size: 12px;
}
/* 占位块尺寸跟随搭配的缩略图尺寸（.thumb-ph 自带 34px，会压小 .style-thumb 的 40px） */
.style-thumb.thumb-ph {
  width: 40px;
  height: 40px;
  font-size: 14px;
}
.style-thumb-sm.thumb-ph {
  width: 34px;
  height: 34px;
  font-size: 12px;
}
/* ===== 缩略图悬停放大组件（thumb-zoom）：小图悬停浮层放大预览 ===== */
.thumb-zoom-img {
  display: block;
  object-fit: cover;
  cursor: zoom-in;
}
.thumb-zoom-big {
  display: block;
  object-fit: cover;
  border-radius: 8px;
  border: 1px solid var(--sd-border);
  background: #fff;
}
.thumb-zoom-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--sd-surface-2);
  color: var(--sd-text-3);
  font-size: 13px;
  border-radius: 8px;
}

/* ===== 出库台账：顶部汇总改为与正文同字号的内联摘要（左对齐，置于表格左上方） ===== */
.summary-line {
  display: flex;
  flex-wrap: wrap;
  gap: 18px;
  margin-bottom: 12px;
  font-size: 13px;
  line-height: 1.6;
  color: var(--sd-text-2);
}
.summary-line b {
  margin-left: 2px;
  color: var(--sd-text);
  font-weight: 600;
}

/* ===================== 财务中心专用样式 ===================== */
.fin-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
}
.fin-strong {
  font-weight: 600;
  color: var(--td-brand-color, #2b6cff);
}
/* 经营现金流卡片 */
.fin-cashflow {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
  margin-bottom: 8px;
}
.fin-cf-card {
  background: var(--td-bg-color-page, #f3f3f3);
  border: 1px solid var(--td-component-border, #e7e7e7);
  border-radius: 8px;
  padding: 12px 14px;
  min-width: 0;
}
.fin-cf-card.hl {
  border-color: var(--td-brand-color, #2b6cff);
  background: var(--sd-brand-a08, rgba(74, 144, 226, 0.08));
}
.fin-cf-label {
  font-size: 12px;
  color: var(--td-text-color-secondary, #888);
}
.fin-cf-num {
  font-size: 20px;
  font-weight: 600;
  margin: 4px 0;
}
.fin-cf-num.in { color: #2ba471; }
.fin-cf-num.out { color: #d54941; }
.fin-cf-sub {
  font-size: 12px;
  color: var(--td-text-color-placeholder, #aaa);
}
/* 收款方式占比条 */
.fin-ratio {
  display: flex;
  align-items: center;
  gap: 8px;
}
.fin-ratio-bar {
  height: 8px;
  border-radius: 4px;
  background: var(--td-brand-color, #2b6cff);
  min-width: 2px;
  max-width: 100%;
}
.fin-ratio-txt {
  font-size: 12px;
  color: var(--td-text-color-secondary, #888);
  white-space: nowrap;
}
/* 轻量资产负债表 */
.fin-balance {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
  margin: 10px 0 4px;
}
.fin-bs-col {
  border: 1px solid var(--td-component-border, #e7e7e7);
  border-radius: 8px;
  padding: 12px 14px;
  min-width: 0;
}
.fin-bs-col.hl {
  border-color: var(--td-brand-color, #2b6cff);
  background: var(--sd-brand-a08, rgba(74, 144, 226, 0.08));
}
.fin-bs-h {
  font-size: 13px;
  font-weight: 600;
}
.fin-bs-v {
  font-size: 18px;
  font-weight: 600;
  margin: 4px 0;
}
.fin-bs-sub {
  font-size: 12px;
  color: var(--td-text-color-placeholder, #aaa);
}
/* 财务各模块：独立浅色背景 + 圆角，避免多表混排看花眼 */
.fin-module {
  background: linear-gradient(180deg, #f8fafd 0%, #f2f6fc 100%);
  border: 1px solid #e7edf6;
  border-radius: 14px;
  padding: 16px 18px;
  margin-top: 16px;
}
.fin-module .fm-title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  font-weight: 700;
  color: #1f2d4d;
  margin: 0 0 12px;
}
/* 原先这里用 `::before` 画了一条 4x14px 的品牌色小竖条，按用户要求全站去掉竖线，已删除。
   标题已是 14px / 700，不需要色条辅助。**不要再加回来**（守卫见 verify_layout_fixed_e2e.js）。 */
/* 费用表头（标题 + 新增按钮两端对齐） */
.fin-exp-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 12px;
}
.fin-exp-head .fm-title {
  margin-bottom: 0;
}
.style-pick {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* ===== 库存中心：AI 补库存常显长条（紧凑·无背景卡·无左侧色条） ===== */
.ai-restock-bar {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 14px;
  padding: 8px 12px;
  background: var(--sd-surface-2);
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  flex-wrap: wrap;
}
.arb-icon {
  font-size: 16px;
  flex: none;
  line-height: 1;
}
.arb-label {
  font-size: 13px;
  font-weight: 700;
  color: var(--sd-text);
  flex: none;
}
.arb-tag {
  flex: none;
}
.arb-items {
  flex: 1;
  min-width: 160px;
  display: flex;
  flex-wrap: wrap;
  gap: 4px 10px;
  font-size: 12.5px;
  color: var(--sd-text-2);
}
.arb-item {
  color: var(--sd-text);
  white-space: nowrap;
}
.arb-item b {
  color: var(--sd-danger);
  font-weight: 700;
}
.arb-empty {
  color: var(--sd-text-3);
  font-size: 12.5px;
}

/* 补货长条：按供货商分组（一供应商一组，呼应"不一定是一个供货商"） */
.arb-group {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 8px;
  width: 100%;
}
.arb-group + .arb-group {
  margin-top: 6px;
  padding-top: 6px;
  border-top: 1px dashed var(--sd-border);
}
.arb-supplier {
  font-size: 12px;
  font-weight: 700;
  color: var(--sd-brand);
  background: var(--sd-surface-2);
  border-radius: 10px;
  padding: 1px 8px;
  flex: none;
  white-space: nowrap;
}

/* ===== 首页 / 工作台仪表盘：两栏「模块卡片」=====
   用户 2026-09 明确要求（原话）：
     ①「我原来是双列，门店日报在左侧，门店日报里有 TOP 销量款、缺货色码，
        右侧是小模块 AI 经营助手…右侧下方是快捷入口，跟 AI 经营助手这个右上角模块是对齐的」
     ②「右侧 AI 经营助手的比例不对，占比应该是 1/4 的宽度」
   所以左右两栏是**分别渲染**的两个 .home-col，右栏占 1/4：
     若把两栏塞进同一个网格靠自动落位，左栏那张高卡会把右侧的行一起撑高，
     右栏第二张卡（快捷入口）就被顶出一大段空白、和 AI 经营助手对不齐 ——
     这正是用户说的"比例不对"。

   ⚠️ 刻意**不写任何 @media 断点**（全站固定排版铁律）：窗口变窄时仍是两栏，
   靠内容区的最小宽度兜住，不允许"窄了就变一列"——那会让正在点的按钮跳位。
   左右宽度比例只在这里改一处（调 --sd-home-col-left）。 */
.home-sections {
  --sd-home-col-left: 3fr; /* 左栏占 3/4（右栏即 1/4）；要改成 4:1 就写 4fr */
  display: grid;
  grid-template-columns: var(--sd-home-col-left) minmax(0, 1fr);
  gap: 16px;
  align-items: start; /* 否则同行的两张卡会被拉成一样高 */
  margin-top: 4px;
}
/* 一栏的容器：栏内卡片自上而下排列，顺序就是布局里的先后 */
.home-col {
  display: flex;
  flex-direction: column;
  gap: 16px;
  min-width: 0; /* 网格项默认 min-width:auto，内容宽了会把列撑破 */
}
/* ===== 通栏条带（v6）：两栏**下方**的一整行 4 列网格 =====
   用途：宽度选成 1/4 的卡片（默认是那 4 张提示卡）在这里排成一行 4 个。
   为什么要单独一个区、而不是塞进两栏网格：两栏是"各自独立、上下对齐"的
     （用户明确要求右栏卡片不被左栏高卡顶下去），插进两栏网格那个毛病立刻回来。
   ⚠️ `grid-column: 1 / -1` 必须写：它是 .home-sections 的第三个网格项，
      不写只会占左栏一格（与 .dash-section-add 同款处理）。
   ⚠️ 它必须排在两栏**之后**渲染，否则会插到两栏中间（DOM 顺序 = 网格落位顺序）。 */
.home-strip {
  grid-column: 1 / -1;
  display: grid;
  /* 4 等份，与「1/4 宽」的语义一一对应；minmax(0,1fr) 防止内容把格子撑破 */
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 16px;
  align-items: start; /* 否则同行的卡片会被拉成一样高（矮卡出现大片空白） */
  min-width: 0;
}
/* 条带里的卡片：占几格由模板的内联 `grid-column: span N` 决定（N 来自布局的宽度）。
   这里只兜住"最小宽度为 0"，避免卡片内的长文本把格子撑破。 */
.home-strip .dash-section {
  min-width: 0;
}
.dash-section {
  width: 100%;
  min-width: 0;
}
/* 编辑态：每张卡片一圈虚线框，明确"这张卡可以拖"（卡内不再有任何可编辑控件） */
.dash-section.mod-editing {
  outline: 1px dashed var(--sd-brand);
  outline-offset: 4px;
  border-radius: var(--sd-radius-sm);
}
/* 拖拽中的卡片淡化，给出"正在被搬动"的即时反馈 */
.dash-section.mod-dragging {
  opacity: 0.45;
}
/* ⚠️ t-card__header 是 flex + space-between：标题内容必须包一层 width:100% 的容器，
   否则图标与标题会被撑到两端（全站踩过，见「t-card__header 的 flex 坑」那条）。 */
.dash-sec-head {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 8px;
}
.dash-sec-name {
  font-weight: 500;
  color: var(--sd-text);
}
.dash-sec-tools {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 2px;
}
/* 编辑态的「宽度」下拉。宽度写死：它是固定几档的窄控件，
   跟着内容伸缩会让同一排的 ↑ ↓ 按钮在卡片之间左右跳。 */
.dash-sec-width {
  width: 118px;
  flex: none;
}
/* 编辑态允许卡片头部换行：条带里的卡片只有 ~320px 宽，
   「拖拽柄 + 图标 + 标题输入 + 宽度下拉 + ↑ ↓ 移除」一排放不下 ——
   不放行换行的话，控件会被压成看不清的窄条（比折行难用得多）。 */
.dash-section.mod-editing .dash-sec-head {
  flex-wrap: wrap;
  row-gap: 6px;
}
/* ⚠️ grid-column 必须写：.home-sections 是两列网格，不写只会占左栏半格宽 */
.dash-section-add {
  grid-column: 1 / -1;
  display: flex;
  justify-content: center;
  padding: 2px 0;
}

/* 「在途到货」卡 */
.dash-transit-row {
  padding: 8px 0;
  border-bottom: 1px dashed var(--sd-border);
}
.dash-transit-row:last-of-type {
  border-bottom: none;
}
.dash-transit-main {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}
.dash-transit-sup {
  font-weight: 500;
  color: var(--sd-text);
}
.dash-transit-meta {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
  margin-top: 2px;
  font-size: 12px;
}

/* 「库存预警」卡 */
.dash-low-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 0;
  border-bottom: 1px dashed var(--sd-border);
}
.dash-low-row:last-of-type {
  border-bottom: none;
}
.dash-alert-tag {
  border: 1px solid;
  border-radius: 4px;
  padding: 0 6px;
  font-size: 12px;
  line-height: 18px;
  white-space: nowrap;
}
.dash-low-name {
  flex: 1;
  color: var(--sd-text);
}
.dash-low-stock,
.dash-low-days {
  white-space: nowrap;
}
/* 门店日报卡 */
.dash-stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
}
.dash-stat {
  text-align: center;
  padding: 4px 0;
}
.ds-num {
  font-size: 24px;
  font-weight: 800;
  color: var(--sd-text);
  line-height: 1.2;
}
.ds-lab {
  font-size: 12.5px;
  color: var(--sd-text-2);
  margin-top: 2px;
}
.dash-sub {
  font-size: 13px;
  font-weight: 700;
  color: var(--sd-text-2);
  margin: 2px 0 8px;
}
/* 待办提醒模块：行动清单，点击跳转对应模块；悬停高亮引导点击 */
.todo-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  margin-bottom: 6px;
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface-2);
  cursor: pointer;
  transition: background 0.2s ease;
}
.todo-row:hover {
  background: var(--sd-brand-soft);
}
/* 待办行内图标用 gap 控制间距，覆盖菜单独有的 margin-right，避免双重间距 */
.todo-ico {
  margin-right: 0;
}
.todo-text {
  flex: 1;
  font-size: 13px;
  color: var(--sd-text);
}
.todo-go {
  color: var(--sd-text-3);
  font-size: 16px;
}
.dash-top-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 0;
  border-bottom: 1px solid var(--sd-border);
}
.dash-top-row:last-child {
  border-bottom: none;
}
.dash-top-no {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--sd-surface-2);
  color: var(--sd-text-2);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  font-weight: 700;
  flex: none;
}
.dash-top-name {
  flex: 1;
  font-size: 13.5px;
  color: var(--sd-text);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.dash-top-val {
  font-size: 13px;
  font-weight: 700;
  color: var(--sd-brand);
  flex: none;
}
/* ===== AI 经营助手（入口卡 + 会话 + 右下角常驻悬浮窗）=====
   原「小栗 AI 海报」位：那套是演示用的假出图（今日主推 / 换一张 / 保存海报），
   已整块换成 AI 经营助手入口 —— 配色沿用品牌渐变，老板认的是**位置**，不认实现。 */
.ai-entry {
  background: var(--sd-brand-gradient);
  color: #fff; /* 渐变底上必须用白字，与原来的海报位一致 */
  border-radius: var(--sd-radius);
  padding: 16px;
  box-shadow: var(--sd-shadow);
  cursor: pointer;
  transition: transform 0.12s, box-shadow 0.12s;
}
.ai-entry:hover {
  transform: translateY(-2px);
  box-shadow: var(--sd-shadow-hover);
}
.ai-entry-head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 12px;
}
.ai-entry-ava {
  width: 36px;
  height: 36px;
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.2);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 20px;
  flex: none;
}
.ai-entry-name {
  font-size: 14px;
  font-weight: 700;
}
.ai-entry-sub {
  font-size: 11.5px;
  opacity: 0.9;
}
.ai-entry-body {
  background: rgba(255, 255, 255, 0.14);
  border-radius: 10px;
  padding: 12px;
  margin-bottom: 12px;
}
.ai-entry-line {
  font-size: 13px;
  line-height: 1.6;
}
.ai-entry-line.is-dim {
  font-size: 12px;
  opacity: 0.85;
  margin-top: 4px;
}

/* 会话本体：弹窗与悬浮窗**共用同一份样式**，靠 .is-compact 区分尺寸 ——
   两套样式迟早会长歪（改了一处、另一处忘了改），一份最省心。 */
.aiasst {
  display: flex;
  flex-direction: column;
}
.aiasst-status {
  font-size: 12.5px;
  color: var(--sd-text-2);
  background: var(--sd-surface-2);
  border-radius: var(--sd-radius-sm);
  padding: 6px 10px;
  margin-bottom: 10px;
}
/* 降级（未接入 / 模拟模式）：用警示色，让人一眼看出现在不能用真实模型 */
.aiasst-status.is-degraded {
  color: var(--sd-warning);
}
.aiasst-thread {
  flex: 1;
  overflow-y: auto;
  padding: 2px;
  max-height: 340px;
}
.aiasst.is-compact .aiasst-thread {
  max-height: 240px;
}
.aiasst-msg {
  display: flex;
  margin-bottom: 10px;
}
.aiasst-msg.is-user {
  justify-content: flex-end;
}
.aiasst-bubble {
  max-width: 86%;
  background: var(--sd-surface-2);
  border-radius: var(--sd-radius-sm);
  padding: 8px 10px;
}
.aiasst-msg.is-user .aiasst-bubble {
  background: var(--sd-brand-soft);
}
.aiasst-bubble.is-error {
  border: 1px solid var(--sd-warning);
}
.aiasst-src {
  font-size: 11px;
  color: var(--sd-text-3);
  margin-bottom: 3px;
  display: flex;
  gap: 6px;
}
.aiasst-time {
  color: var(--sd-text-3);
}
.aiasst-text {
  font-size: 13px;
  line-height: 1.65;
  color: var(--sd-text);
  word-break: break-word;
}
.aiasst-typing {
  font-size: 12.5px;
  color: var(--sd-text-3);
  padding: 2px 4px 8px;
}
.aiasst-input {
  display: flex;
  gap: 8px;
  align-items: flex-end;
  margin-top: 8px;
}
/* 输入框用原生 textarea：Enter 发送 / Shift+Enter 换行的语义最直接 */
.aiasst-textarea {
  flex: 1;
  min-width: 0;
  resize: vertical;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface);
  color: var(--sd-text);
  padding: 8px 10px;
  font-size: 13px;
  line-height: 1.5;
  font-family: inherit;
}
.aiasst-textarea:focus {
  outline: none;
  border-color: var(--sd-brand);
}
.aiasst-actions {
  flex: none;
}
.aiasst-send {
  border: none;
  border-radius: var(--sd-radius-sm);
  background: var(--sd-brand);
  color: #fff;
  padding: 8px 14px;
  font-size: 13px;
  cursor: pointer;
}
.aiasst-send:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.aiasst-sug {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
  margin-top: 10px;
}
.aiasst-sug-title {
  font-size: 12px;
  color: var(--sd-text-3);
}
.aiasst-sug-item {
  border: 1px solid var(--sd-border);
  background: var(--sd-surface);
  color: var(--sd-text-2);
  border-radius: 999px;
  padding: 3px 10px;
  font-size: 12px;
  cursor: pointer;
}
.aiasst-sug-item:hover {
  border-color: var(--sd-brand);
  color: var(--sd-brand);
}

/* 右下角**常驻悬浮窗**（按需求：平常也常驻）
   z-index 刻意低于弹窗层：悬浮窗不该盖住正在开的对话框 */
.ai-float {
  position: fixed;
  right: 20px;
  bottom: 20px;
  width: 360px;
  background: var(--sd-surface);
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius);
  box-shadow: var(--sd-shadow);
  z-index: 900;
}
.ai-float.is-collapsed {
  width: auto;
}
.ai-float-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 8px 10px;
  cursor: pointer;
  background: var(--sd-brand-gradient);
  color: #fff;
  border-radius: var(--sd-radius) var(--sd-radius) 0 0;
}
.ai-float-title {
  font-size: 13px;
  font-weight: 700;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.ai-float-tools {
  display: inline-flex;
  gap: 4px;
}
.ai-float-btn {
  border: none;
  background: rgba(255, 255, 255, 0.18);
  color: #fff;
  border-radius: 6px;
  padding: 2px 8px;
  font-size: 12px;
  cursor: pointer;
}
.ai-float-body {
  padding: 10px;
}
/* 隐藏悬浮窗后的恢复入口：不给入口，藏了就再也找不回来 */
.ai-float-restore {
  position: fixed;
  right: 20px;
  bottom: 20px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: none;
  border-radius: 999px;
  padding: 8px 14px;
  background: var(--sd-brand);
  color: #fff;
  font-size: 13px;
  cursor: pointer;
  box-shadow: var(--sd-shadow);
  z-index: 900;
}

/* ==========================================================================
   AI 悬浮窗的「Q弹」动效
   （用户原话：「改成比较Q弹动感的按钮，点击，或者移动按钮都会有相应的动效」）

   ⚠️ 本块**统一**管理这几个元素的动效。上面 .ai-float-head / .ai-float-btn /
      .ai-float-restore 里**不要**再写 transform —— `transform` 只有一个属性，
      两处各写一份必然互相覆盖（本项目在别处踩过同类问题：同一能力两份实现 = 一处生效一处静默失效）。

   四条实测约束（改之前先读，尤其 ①②，它们不是"风格偏好"而是会**弄坏测试或版式**）：
   ① **点击目标不许有持续动画**：`verify_ai_assistant_e2e.js` 会点 float-toggle / float-hide /
      float-restore，而 Playwright 点击前要等元素"稳定"（连续两帧 boundingBox 不变）——
      一个一直在动的按钮会让它一直等到超时。所以常驻的"呼吸"动效**只挂在图标上**
      （子元素的 transform 不影响父元素的盒子，点击目标本身保持静止）。
   ② **位移必须小**：鼠标移动会让标题条真的位移，而 Playwright 是在 stability 检查**之后**
      才移动鼠标的 —— 位移过大就可能"鼠标刚落到按钮上、按钮已经跑开"，点击落空。
      故磁吸收敛在 ±3px（见 app.js 的 AI_MAGNET_MAX_OFFSET）、按压 scale 不小于 0.84。
   ③ 手感来自 `cubic-bezier(0.34, 1.56, 0.64, 1)`：这是"back-out"曲线，回弹时会**冲过头再落回**，
      也就是口语里的"Q弹"。换成 ease / ease-out 就变成普通的"渐入"，没有弹的手感了。
   ④ **只动画 transform / opacity**（GPU 友好）。宽度、高度、top/left、box-shadow 一律不动画 ——
      它们每帧都要重新布局/重绘，鼠标划过时会明显掉帧。
   ========================================================================== */

/* ---------- ① 标题条 + 恢复入口：磁吸位移 + 缩放（按下压扁 / 悬停放大） ----------
   三个量都合成在**同一个 transform** 里，各自独立、互不覆盖：
     · --ai-mag-x / --ai-mag-y  磁吸位移（JS 在鼠标移动时写）
     · --ai-scale               缩放（按下 0.965；恢复入口悬停 1.05）
     · --ai-hover-lift          悬停上浮
   transform-origin: center bottom —— 按下时**底边不动、上边向下收**。
   为什么必须是 bottom：展开态的标题条下面是会话体，若以中心为原点缩放，底边会上抬 1px，
   与下方内容之间就裂出一条缝（正是本项目"边距差几像素"那类毛病）。 */
.ai-float-head,
.ai-float-restore {
  transform: translate3d(var(--ai-mag-x, 0px), var(--ai-mag-y, 0px), 0)
    scale(var(--ai-scale, 1))
    translateY(var(--ai-hover-lift, 0px));
  transform-origin: center bottom;
  transition: transform 0.32s cubic-bezier(0.34, 1.56, 0.64, 1), filter 0.2s ease;
  /* 鼠标划过/连点时会带上文字选择，选中"AI 经营助手"几个字看起来像卡住了 */
  user-select: none;
}
.ai-float-head:hover,
.ai-float-restore:hover {
  filter: brightness(1.06);
}
.ai-float-restore:hover {
  --ai-scale: 1.05;
  --ai-hover-lift: -2px;
}
/* 按下：压到 96.5%（幅度刻意小 —— 这是 33px 高的小条，压太多会像"陷进去"而不是"弹"）。
   ⚠️ 这两条**必须写在 :hover 之后**：`.x:hover` 与 `.x:active` 特异性相同，
   靠源码顺序决胜负；写在前面会被悬停的 1.05 盖住 —— 按下去反而变大。 */
.ai-float-head:active,
.ai-float-restore:active {
  --ai-scale: 0.965;
}

/* ---------- ② 展开 / × 按钮：悬停放大、按下压扁、松手回弹 ---------- */
.ai-float-btn {
  transition: transform 0.34s cubic-bezier(0.34, 1.56, 0.64, 1), background 0.2s ease;
}
.ai-float-btn:hover {
  background: rgba(255, 255, 255, 0.32);
  transform: scale(1.1);
}
/* 按下用**更短**的时长（0.08s）：按下去要"立刻响应"，松手回弹才用那条 Q弹曲线。
   两段时长不一样是有意的 —— 都用 0.32s 会感觉"按了半天没反应"。 */
.ai-float-btn:active {
  transform: scale(0.84);
  transition-duration: 0.08s;
}

/* ---------- ③ 恢复入口（小圆按钮，是"藏起来之后"的唯一入口）
   悬停放大 + 上浮写在上面 ① 的 `.ai-float-restore:hover` 里（靠 CSS 变量与按压态合成），
   这里**不再单独写 transform** —— 两处各写一份必然互相覆盖。 */

/* ---------- ④ 待机「呼吸」：只挂在图标上 ----------
   ⚠️ 为什么是图标而不是按钮（约束①）：图标是子元素，它的 transform 不改变父元素的盒子，
      所以"看着一直在动"与"点击目标稳定"可以同时成立。
      动画幅度刻意很小（1 → 1.12 + 轻微摆动）：常驻元素动得越明显，越像出了故障。 */
@keyframes ai-float-breathe {
  0%,
  100% {
    transform: scale(1) rotate(0deg);
  }
  50% {
    transform: scale(1.12) rotate(-6deg);
  }
}
.ai-float-title .ip-icon,
.ai-float-restore .ip-icon {
  animation: ai-float-breathe 2.6s ease-in-out infinite;
  transform-origin: center;
}

/* ---------- ⑤ 会话体展开时的「弹入」 ---------- */
@keyframes ai-float-pop-in {
  from {
    opacity: 0;
    transform: translate3d(0, 8px, 0) scale(0.97);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
.ai-float-body {
  animation: ai-float-pop-in 0.32s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}
/* ⚠️ 刻意**只做进场、不做退场**：收起时会话体是 `v-if` 直接移除的。
   如果给它加退场动画，元素会在 DOM 里多留几百毫秒 —— 而
   `verify_ai_assistant_e2e.js` 有一条断言是「收起后只剩标题条（会话体隐藏）」，
   它查的是"会话体数量 === 0"，退场期间会是 1 → 这条断言会**偶发变红**。
   加特效不该让既有断言变成薛定谔的绿，所以收起就是立刻消失。 */

/* ---------- ⑥ 「减少动效」：整体关掉（类由 app.js 按 matchMedia 加在 <html> 上） ----------
   为什么不是 @media：本项目铁律 theme.css 的 @media 数量必须为 0，理由见 app.js 的 reduceMotion 注释。 */
html.is-reduce-motion .ai-float-head,
html.is-reduce-motion .ai-float-restore,
html.is-reduce-motion .ai-float-btn,
html.is-reduce-motion .ai-float-body,
html.is-reduce-motion .ai-float-title .ip-icon,
html.is-reduce-motion .ai-float-restore .ip-icon {
  /* 这里用 !important 是有意的：要盖住上面 `:hover` / `:active` 那几条同特异性的规则，
     靠源码顺序不可靠（将来有人在下面追加一条 hover 规则就会漏掉）。无障碍覆盖属于
     "必须赢"的场景，与项目里 VIP 描边那条 !important 同一类。 */
  animation: none !important;
  transition: none !important;
  transform: none !important;
}

/* 快捷入口
   —— 为什么这些数字是这么算出来的（改之前先读，别再"看着差不多"改）：

   ① 可用宽度是量出来的，不是估的。快捷入口卡在**占 1/4 宽的右栏**里：
        内容区最窄 1120px（--sd-min-content-width 兜底）→ 右栏 276px
        → 卡片 276 → 卡体 274（1px 边框）→ 扣掉卡体内边距 24+24
        → **网格可用只有 226px**。
      窗口继续变窄时内容区**不再收缩**（底部会出横向滚动条），所以 226px 就是下界，
      量它一次即可，不必为更窄的宽度留余量。

   ② 要 3 个入口排成一行，就得满足 `3 × 最小列宽 + 2 × 间距 ≤ 226`：
        68 × 3 + 8 × 2 = 220 ≤ 226 ✓（留 6px 余量，避免亚像素取整后差 0.25px 就掉成 2 列）
      曾经写的 84px 需要 272px —— 差 46px，于是必然折成 2+1（且最后一行那一个
      因为 auto-fit + 1fr 不会拉伸，看起来像布局坏了）。

   ③ 最小列宽还要装得下**最长的入口名**。快捷入口的名字最长 4 个汉字
      （完整日报 / 销售分析 / 补货参谋 / 滞销预警）。13px 字号下 = 52px，
      再扣掉左右内边距 6+6 与 1px 边框×2 → 实得 68 − 12 − 2 = 54px ≥ 52px ✓。
      这也是**为什么字号是 13px 而不是 14px**：14px 的 4 字名要 56px，
      在 68px 的格子里只剩 0 余量，字体度量稍有偏差就会折行。

   ④ 用 auto-fit 而不是固定 `repeat(3, 1fr)`：入口是按权限过滤的，
      没权限的页面不会出现；只剩 1~2 个入口时固定 3 列会留一片空档。

   ⑤ 上面的 226 / 68 / 13 都是量出来的。要复核（或改了右栏比例之后再算一遍）：
      跑 `node _probe_home_cols.js`（前置：server 已在 3000 端口），
      它会在 1560→1152 六个宽度下打出一张「内容区 / 右栏 / 卡体 / 网格宽 / 实际列数 /
      各入口宽 / 折成几行」的表 —— 这比拿卡片外宽反推内边距可靠得多（会少算一层 padding）。 */
.quick-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(68px, 1fr));
  gap: 8px;
}
.quick-item {
  cursor: pointer;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  padding: 12px 6px;
  text-align: center;
  font-weight: 600;
  font-size: 13px;
  color: var(--sd-text);
  background: var(--sd-surface-2);
  transition: transform 0.08s, box-shadow 0.12s, border-color 0.12s, color 0.12s;
}
.quick-item:hover {
  transform: translateY(-2px);
  box-shadow: var(--sd-shadow-hover);
  border-color: var(--sd-brand);
  color: var(--sd-brand);
}
.quick-label {
  /* 入口文字（浏览态）。单独包一个 span 是为了排版可控（块级，长名字可换行），
     与上一版「编辑态下要跟工具行共存」无关 —— 那个工具行已删除。 */
  display: block;
}

/* ===== 工作台布局编辑（模块卡片） =====
   产品规则（2026-09 定稿）：**卡片之间**可拖拽换位 / 增删 / 改标题，
   **卡片内部完全锁死** —— 用户原话：「模块跟模块之间的位置布局，而不是模块内的；
   每个模块是一个整体，模块内的不可以自定义布局」。
   所以这一节只剩「卡片级」样式：卡片虚线框、卡片拖拽手柄、卡片标题工具组（见上方 .dash-section*）。
   历史遗留的「卡内工具行 / 卡内拖拽手柄 / 卡内说明文字 / 卡内加号」样式
   （.mod-block* / .mod-tools* / .mod-desc / .mod-add）已随界面上对应控件一并删除：
   留着死样式会让后来人误以为卡内还能编辑，再"顺手"把控件加回来。 */
/* 顶部工具栏：编辑入口在页面右上角（可编辑范围跨所有卡片，按钮必须挂在共同祖先上） */
.dash-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 8px;
  min-height: 32px;
}
.dash-toolbar-hint {
  font-size: 12.5px;
  color: var(--sd-text-2);
}
.dash-toolbar-actions {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-left: auto; /* 即使左侧没有提示文字，按钮也始终靠右 */
}
/* 区块模块与上方内容的间距。
   ⚠️ .dash-block-mod 现在只出现在**容器卡的兜底路径**里（v4 迁移已把区块拆成独立卡片，
   独立卡片直接渲染 dash-block，不再套这层）。所以只需处理"上方有别的模块"这一种情形；
   若它是卡内第一个元素（老存档迁移不干净），不留顶部空隙。 */
.dash-block-mod {
  margin-top: 14px;
}
.dash-block-mod:first-child {
  margin-top: 0;
}
/* 卡片拖拽手柄：只在编辑态出现，光标提示可拖（挂在 .dash-sec-head 标题行最左侧） */
.mod-drag {
  flex: none;
  cursor: grab;
  color: var(--sd-text-3);
  font-size: 14px;
  line-height: 1;
  padding: 0 2px;
  user-select: none;
}
.mod-drag:active {
  cursor: grabbing;
}
/* 「添加模块」弹窗里的可选列表：整行可点，选中态用勾选框 + 底色 */
.mod-picker {
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-height: 320px;
  overflow-y: auto;
}
.mod-pick-item {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 9px 10px;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  cursor: pointer;
  transition: border-color 0.12s, background 0.12s;
}
.mod-pick-item:hover {
  border-color: var(--sd-brand);
}
.mod-pick-item.is-on {
  border-color: var(--sd-brand);
  background: var(--sd-brand-soft);
}
.mod-pick-box {
  flex: none;
  width: 16px;
  height: 16px;
  margin-top: 1px;
  border: 1px solid var(--sd-border);
  border-radius: 3px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--sd-surface);
}
.mod-pick-item.is-on .mod-pick-box {
  border-color: var(--sd-brand);
  color: var(--sd-brand);
}
.mod-pick-main {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.mod-pick-label {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--sd-text);
}
.mod-pick-desc {
  font-size: 12px;
  color: var(--sd-text-2);
}

/* ===== AI 摘要模块（AI 补货参谋 / AI 滞销预警）=====
   结构：一行结论（几个数并排）+ 前几条建议 + 口径说明。
   数字与列表全部来自各自引擎，这里只负责排版。 */
.ai-brief-line {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  margin-bottom: 8px;
  font-size: 13px;
  color: var(--sd-text-2);
}
.ai-brief-line b {
  color: var(--sd-text);
  font-weight: 600;
}
.ai-brief-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 0;
  border-bottom: 1px solid var(--sd-border);
  font-size: 13px;
}
.ai-brief-row:last-child {
  border-bottom: none;
}
.ai-brief-what {
  flex: 1;
  min-width: 0;
  color: var(--sd-text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ai-brief-tier {
  flex: none;
  font-size: 12px;
  color: var(--sd-text-2);
}
.ai-brief-more,
.ai-brief-note {
  font-size: 12px;
  margin-top: 6px;
  line-height: 1.5;
}

/* ===== 员工提效建议模块 =====
   一条建议 = 一行头部（姓名 + 结论 + 依据）+ 一行可执行的话。
   ⚠️ 只用主题里**已存在**的变量：本项目没有 --sd-danger-soft，
      所以"最紧急"靠**文字用警示色**表达，不另造浅底变量（造了就得在每个主题里都补一份，
      漏一个主题就会出现一块突兀的浅红）。 */
.staff-advice-row {
  padding: 8px 10px;
  margin-bottom: 6px;
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface-2);
}
.staff-advice-row:last-child {
  margin-bottom: 0;
}
.staff-advice-head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 2px;
}
.staff-advice-name {
  font-size: 13px;
  font-weight: 700;
  color: var(--sd-text);
}
.staff-advice-title {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--sd-text-2);
}
.staff-advice-basis {
  font-size: 11.5px;
}
.staff-advice-text {
  font-size: 13px;
  line-height: 1.6;
  color: var(--sd-text);
}
/* 最该管的（本月还没开单）用警示色文字突出，其余保持中性色 */
.staff-advice-row.adv-critical .staff-advice-name,
.staff-advice-row.adv-critical .staff-advice-title {
  color: var(--sd-danger);
}
/* 首页仪表盘窄屏自适应 */

/* ===== AI 中枢视图排版（身份头 + KPI + 双栏洞察 + 排名条） ===== */
.ai-view {
  display: flex;
  flex-direction: column;
  gap: 16px;
}
/* 渐变身份头 */
.ai-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  background: var(--sd-brand-gradient);
  color: #fff;
  padding: 16px 20px;
  border-radius: var(--sd-radius);
  box-shadow: var(--sd-shadow);
}
.ai-head-main {
  display: flex;
  align-items: center;
  gap: 14px;
}
.ai-avatar {
  width: 44px;
  height: 44px;
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.18);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 24px;
  flex: none;
}
.ai-head-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.ai-title {
  font-size: 17px;
  font-weight: 700;
}
.ai-sub {
  font-size: 12.5px;
  opacity: 0.9;
}
.ai-badge {
  flex: none;
}

/* AI 中枢功能 Tab 导航：4 个独立子页入口，横向排列 */
.ai-tabs {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 12px;
}
.ai-tab {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  padding: 14px 16px;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius);
  background: var(--sd-surface);
  cursor: pointer;
  text-align: left;
  transition: border-color 0.15s ease, box-shadow 0.15s ease, background 0.15s ease;
}
.ai-tab .ip-icon {
  width: 20px;
  height: 20px;
  color: var(--sd-text-2);
  margin-bottom: 2px;
}
.ai-tab .ip-icon svg {
  width: 100%;
  height: 100%;
}
.ai-tab-label {
  font-size: 15px;
  font-weight: 700;
  color: var(--sd-text);
}
.ai-tab-desc {
  font-size: 12px;
  color: var(--sd-text-3);
  line-height: 1.4;
}
.ai-tab:hover {
  border-color: var(--sd-brand);
}
.ai-tab.active {
  border-color: var(--sd-brand);
  background: var(--sd-brand-soft);
  box-shadow: 0 0 0 2px var(--sd-brand-a12, rgba(74, 144, 226, 0.12));
}
.ai-tab.active .ip-icon {
  color: var(--sd-brand);
}
.ai-tab.active .ai-tab-label {
  color: var(--sd-brand);
}

/* ============ AI 中枢子页切换 Tab 栏（2026-09-17 收敛：侧栏入口归并后，子页在此横向切换） ============
   与上方 .ai-tabs（旧的四宫格卡片导航）是两套：这里是无 icon、无 desc 的简洁胶囊横条，
   直接复用 TDesign 变量与品牌色，视觉与侧栏一致。 */
.ai-switch-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding: 8px;
  background: var(--sd-bg-container, #f5f7fa);
  border-radius: var(--sd-radius);
}
.ai-switch-tab {
  flex: none;
  padding: 7px 16px;
  border-radius: 8px;
  font-size: 13.5px;
  font-weight: 500;
  color: var(--sd-text-2);
  cursor: pointer;
  user-select: none;
  transition: background 0.15s ease, color 0.15s ease;
}
.ai-switch-tab:hover {
  color: var(--sd-brand);
}
.ai-switch-tab.is-active {
  background: #fff;
  color: var(--sd-brand);
  font-weight: 700;
  box-shadow: var(--sd-shadow);
}
/* 子页标题行（说明当前子页定位与规则） */
.ai-tab-heading {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 15px;
  font-weight: 700;
  color: var(--sd-text);
}
.ai-tab-heading .ip-icon {
  width: 18px;
  height: 18px;
  color: var(--sd-brand);
}
.ai-tab-heading .ip-icon svg {
  width: 100%;
  height: 100%;
}
.ai-tab-sub {
  font-size: 12px;
  font-weight: 400;
  color: var(--sd-text-3);
}

/* 文案助手：风格切换胶囊按钮 */
.ai-copy-styles {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 4px;
}
.ai-copy-style {
  padding: 5px 14px;
  border: 1px solid var(--sd-border);
  border-radius: 999px;
  background: var(--sd-surface);
  color: var(--sd-text-2);
  font-size: 13px;
  cursor: pointer;
  transition: color 0.15s ease, border-color 0.15s ease, background 0.15s ease;
}
.ai-copy-style:hover {
  color: var(--sd-brand);
  border-color: var(--sd-brand);
}
.ai-copy-style.active {
  color: #fff;
  background: var(--sd-brand);
  border-color: var(--sd-brand);
}
/* 经营概览 KPI 条 */
.ai-stats {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 12px;
}
.kpi-card.alert-danger .k-num {
  color: var(--sd-danger);
}
.kpi-card.alert-warn .k-num {
  color: var(--sd-warning);
}
/* 双栏网格（洞察 / 热销+文案） */
.ai-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
  align-items: start;
}
/* 洞察卡内行 */
.insight-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 0;
  border-bottom: 1px solid var(--sd-border);
}
.insight-row:last-child {
  border-bottom: none;
}
.insight-main {
  flex: 1;
  min-width: 0;
}
.insight-name {
  font-size: 14px;
  font-weight: 600;
  color: var(--sd-text);
}
.insight-cs {
  font-weight: 400;
  color: var(--sd-text-2);
  font-size: 12.5px;
  margin-left: 4px;
}
.insight-desc {
  font-size: 12.5px;
  color: var(--sd-text-3);
  margin-top: 2px;
}
/* 文案生成提示 */
.copy-tip {
  font-size: 13px;
  color: var(--sd-text-2);
  margin-bottom: 12px;
}
/* 热销排名条 */
.rank-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 9px 0;
}
.rank-no {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  font-weight: 700;
  flex: none;
  background: var(--sd-surface-2);
  color: var(--sd-text-2);
}
.rank-no.rank-1 {
  background: #fff1e8;
  color: #d9822b;
}
.rank-no.rank-2 {
  background: #eef2f7;
  color: #5a6573;
}
.rank-no.rank-3 {
  background: #fbf0e3;
  color: #c4661c;
}
.rank-main {
  flex: 1;
  min-width: 0;
}
.rank-name {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--sd-text);
  margin-bottom: 6px;
}
.rank-bar {
  height: 6px;
  border-radius: 4px;
  background: var(--sd-surface-2);
  overflow: hidden;
}
.rank-bar-fill {
  height: 100%;
  border-radius: 4px;
  background: var(--sd-brand-gradient);
}
.rank-val {
  font-size: 13px;
  font-weight: 700;
  color: var(--sd-brand);
  flex: none;
}
/* AI 中枢窄屏自适应 */

/* ===== 拿货视图排版（步骤化 + 卡片分组 + 视觉层级） ===== */
/* 头部标题与操作 */
/* 页头（拿货 / 采购进货等页的卡片标题行）。
   ⚠️ **必须显式 `width: 100%`**：TDesign 的 `.t-card__header` 本身是 **flex 容器**，
      内层 div 不撑满就会**按内容收缩** → 此时 `justify-content: space-between`
      只在"内容那么宽"的范围内生效，右侧的按钮看起来就是**紧跟在标题后面**，
      而不是贴住卡片右侧（用户两次反馈「这个按钮放这里不合理，应该放在右侧」，
      根因就是这个，不是"按钮顺序排错了"）。
      同类容器都已加这条：`.dash-sec-head` / `.inv-card-head` / `.sd-card-head`，
      本仓历史上踩过同一个坑，注释里都写着「不要顺手删」。 */
.view-head {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.view-title {
  font-size: 16px;
  font-weight: 700;
  color: var(--sd-text);
}
/* 页头右侧动作组：页头是 space-between 布局，**必须把右侧按钮包成一组**。
   为什么：直接并列放 3 个元素时，space-between 会把中间那个（曾经是「设置 AI 建议采购」）
   摆到正中间 —— 看着像"页头多了一个悬浮按钮"，而不是"右侧动作区"（用户已反馈过）。
   凡是要放在页头右侧的按钮，一律放进这个容器。 */
/* 页头右侧动作组：把要贴右的按钮**包成一组**。
   为什么包：不包的话每个按钮都是 `.view-head` 的直接子元素，`space-between` 会把它们
   均摊到整行（三个元素时中间那个落在页面正中，看着像平白多出一个悬浮按钮）。
   包一层之后语义才清楚：「左边=标题，右边=动作区」。 */
.view-head-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}
/* 选款构建块：每块的标题与内容竖向堆叠 */
.pick-block {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.pick-label {
  font-size: 13px;
  font-weight: 600;
  color: var(--sd-text-2);
}
.pick-empty {
  font-size: 13px;
  color: var(--sd-text-3);
}
.pick-actions {
  display: flex;
  gap: 8px;
  margin-top: 4px;
}
/* 右侧吸附区的字段与已选反馈 */
.cta-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.cta-selected {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin-top: 4px;
  padding-top: 10px;
  border-top: 1px dashed var(--sd-border);
}
.cta-value {
  font-size: 14px;
  font-weight: 700;
  color: var(--sd-text);
}
/* 拿货单子卡片 */
.take-card {
  margin-top: 18px;
  background: var(--sd-surface) !important;
}
.supplier-row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.supplier-tag {
  display: inline-block;
  margin-top: 8px;
  font-size: 12px;
  color: var(--sd-text-2);
}
.take-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: 16px;
}
.take-total {
  font-size: 15px;
  color: var(--sd-text-2);
}

/* ==========================================================================
   拿货 / 采购进货（.nh-*）
   本轮重构新增：已选反馈卡、进货价来源标签、供应商不一致提示、提交确认框。
   配色一律走主题变量，不要写死色值。
   ========================================================================== */
.nh-dim {
  color: var(--sd-text-3);
  font-size: 12px;
}
.nh-hint {
  margin-top: 4px;
  line-height: 1.5;
}
/* 需要用户注意但不构成错误：用 warning 色 + 浅底，与 popup 报错区分开 */
.nh-hint-warn {
  margin-top: 6px;
  padding: 4px 8px;
  border-radius: var(--sd-radius-sm);
  background: var(--sd-brand-a08);
  color: var(--sd-warning);
  font-size: 12px;
  line-height: 1.5;
}
.nh-inline {
  display: inline-block;
  margin-left: 8px;
}
/* 色码按钮：换行排列（款可能有 6~8 个色码，横排会溢出） */
.nh-variants {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
/* 已选反馈卡：缩略图 + 款名 + 色码 + 库存，扫一眼就知道选的是哪件 */
.nh-selected {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 44px;
  margin-top: 10px;
  padding: 6px 8px;
  border: 1px dashed var(--sd-border);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface-2);
}
.nh-selected-main {
  min-width: 0;
}
.nh-selected-name {
  font-size: 13px;
  font-weight: 600;
  color: var(--sd-text);
}
.nh-selected-meta {
  margin-top: 1px;
  font-size: 12px;
  color: var(--sd-text-3);
}
/* 进货价来源标签：待填=警示、估算=警示、已改=中性。
   刻意的取舍：档案真实进价**不显示任何标签** —— 绝大多数行都是可信的，
   全都挂个"档案价"反而把真正要留意的那两行淹掉了。 */
.nh-cost-tag {
  margin-top: 2px;
  font-size: 11px;
  line-height: 1.4;
  color: var(--sd-text-3);
}
.nh-cost-tag-pending,
.nh-cost-tag-estimated {
  color: var(--sd-warning);
  font-weight: 600;
}
.nh-cost-tag-custom {
  color: var(--sd-text-3);
}

/* ---------- 明细表「进货价 / 数量」两格：格内标签 + 两列之间的分隔线 ----------
   用户原话：「要标识 进货价 和 数量 …… 两列表达，中间要有间隔线 …… 区别表达，
   避免用户设置错」。它要解决的问题是真实的：这两列紧挨着、都是长得一模一样的
   t-input-number，唯一的区分只有表头那行字，而填的时候视线在格子上。
   ========================================================================== */
/* 格内标签 + 输入框的纵向容器。用 flex 而不是靠换行，是为了让"标签在输入框正上方"这件事
   在两种列宽下都成立（列宽是算出来的，见 nahuo.js 的 lineColumns 注释）。 */
.nh-field {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
/* 标签行：左侧字段名，右侧来源标签（待填 / 估算 / 已改）。
   min-height 与字号对齐，避免只有字段名时这一行塌掉导致两格输入框错位。 */
.nh-field-lab {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 6px;
  min-height: 15px;
}
/* 字段名：**带上单位**（进货价（元） / 数量（件））。单位写在这里而不是输入框里 ——
   本项目的 t-input-number 没有 prefix 槽（只有 suffix），写在标签里既不占输入框宽度，
   也让"哪个是钱、哪个是件数"在最省空间的地方一眼可辨。 */
.nh-field-name {
  font-size: 11px;
  line-height: 1.35;
  font-weight: 600;
  color: var(--sd-text-3);
  white-space: nowrap;
}
/* 标签行里的来源标签要贴基线，不能再带 margin-top（原来它是独立一行） */
.nh-field-lab .nh-cost-tag {
  margin-top: 0;
  white-space: nowrap;
}
/* 两列之间的分隔线。
   ⚠️ 刻意与「模块左侧彩色竖线」区分开：那条是装饰性色条，已被全站禁用；
      这条是**表格列边界**，用户明确要求"中间要有间隔线"，语义完全不同。
   ⚠️ 挂在「数量」的**左边框**：实测本表的 th / td 都是 `border-left: 1px; border-right: 0`
      （TDesign 的 bordered 表格把网格线画在每个单元格左侧），所以加粗左边框
      就是加粗这两列**之间**的那条线。
   ⚠️ 选择器**不许**写成 `td:nth-child(N)`：本表列数是**变的**（有 AI 建议时多 2 列、
      再填运费又多 1 列），按位置写会在这些状态下指错列 —— 本项目踩过同类坑。
      表头用 TDesign 自动加的 `t-table__th-<列key>`（跟列名走，与列序无关）；
      表体**没有**等价的列类名（实测 td.class 为空），只能用 `:has()` 认"装着数量输入框的那一格"，
      与开单页购物篮识认缩略图格是同一套写法。 */
.take-card .t-table thead th.t-table__th-qty,
.take-card .t-table__body td:has([data-nh-row-qty]) {
  border-left: 2px solid var(--sd-brand);
}
/* 兜底：t-input-number 的文本区在列宽不足时会被压成 0 宽（看得见点不进去）。
   本表还踩过更隐蔽的一档：列宽 108px 时文本区只有 **17px** —— 控件没坏、点得进去、
   也能提交，但三位数的数量显示不全，用户会以为"填了没生效"。
   与列宽设置属双保险，缺一个都可能在某档屏幕宽度下被压没。 */
.take-card .t-table .t-input__inner {
  min-width: 34px;
}
/* 提交确认框：字段名与值左右分栏，便于逐项核对 */
.nh-confirm {
  padding: 2px 0;
}
.nh-confirm-row {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 5px 0;
  font-size: 13px;
}
.nh-confirm-label {
  flex: none;
  width: 72px;
  color: var(--sd-text-2);
}
.nh-confirm-row .cta-strong {
  font-size: 16px;
}

/* 窄屏自适应：构建区单栏堆叠、吸附区取消黏性 */

/* ===================== 采购视图：采购单明细（替换原供应商卡片） ===================== */
.purchase-lines {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 4px 8px 4px 28px;
}
.purchase-line {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 13px;
}
.pl-name {
  font-weight: 600;
  color: var(--sd-text);
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.pl-cs {
  color: var(--sd-text-2);
  flex: none;
}
.pl-qty {
  color: var(--sd-text);
  font-weight: 600;
  flex: none;
}
.pl-cost {
  color: var(--sd-text-2);
  flex: none;
}
.pl-sub {
  color: var(--sd-brand);
  font-weight: 600;
  flex: none;
  margin-left: auto;
}

/* ===== 库存页：分析参数侧栏（移出顶部全宽，置于明细表右侧窄列） ===== */
/* 原生 flex 两栏：左主表自适应、右侧栏固定窄宽；窄屏自动堆叠回单列 */
.inv-layout {
  display: flex;
  align-items: flex-start;
  gap: 16px;
}
.inv-main {
  flex: 1 1 auto;
  min-width: 0;
}
.inv-side {
  flex: 0 0 300px;
  width: 300px;
  position: sticky;
  top: 12px;
}
.inv-params {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.inv-param-item {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.inv-param-actions {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 2px;
}
/* 库存明细工具行：搜索框 + 蓝牙扫码枪 + 列设置（靠右）。
   原先这一行是组件里的内联样式，且「列设置」在卡片标题行 —— 两处都改了：
   ① 样式统一收到 theme.css（项目约定，内联样式改不动也不便复用）；
   ② 列设置挪进来是为了**贴近表格**：它原先与表格垂直相距 76px（中间隔着这一行），
      用户反馈"离表格太远"。挪进来后与表格只隔 12px（margin-bottom）。 */
.inv-toolbar {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 12px;
}
/* 表格卡片标题行（现在只有左侧的区块标题）。
   ⚠️ 必须显式 width:100%：TDesign 的 .t-card__header 本身是 flex 容器，
   内层容器不撑满就会**按内容收缩**（实测只有 493px / 容器 1423px）——
   那样 space-between 只在几百 px 内生效，右侧控件看起来像「紧跟在标题后面」，
   而不是贴住右侧。这类「看着像没生效」的布局问题，根因通常是容器没撑开。
   （这个容器历史上踩过这个坑，所以宽度这条约束保留，不要顺手删。） */
.inv-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  width: 100%;
}
.inv-card-head-title {
  font-size: 14px;
  font-weight: 500;
  white-space: nowrap;
}
/* 「列设置」这一组靠右。margin-left:auto 是双保险 —— 即使父容器宽度异常，
   也把这一组顶到最右边，不用依赖父容器的 justify-content 是否真的生效。
   注：它现在位于 .inv-toolbar 里（不在标题行），类名保留是为了不改动既有选择器。 */
.inv-card-head-ops {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-left: auto;
}
/* ⚠️ 原「分析参数（分析天数 + 安全库存）」的 .inv-header-params / .ihp-label 样式已删除：
   这两个参数已按用户要求从界面上移除（取值改为引擎固定默认值，见 components/inventory.js）。
   样式留在这里只会让人以为"这页还有这两个参数"，属于会误导人的死代码。 */

/* 库存明细「条码」列的单元格。
   为什么必须等宽：条码是 13 位纯数字，逐行上下排在一起时，用比例字体每位数字宽度不同，
   每一列数字对不齐、看起来是歪的 —— 而盘点/找货时用户是**逐位读**这个数字的，
   歪一行就可能念错一位。tabular-nums 让所有数字等宽，配合字距把 13 位压实。
   颜色/字重都继承单元格默认值，只改"数字形态"，不额外强调 —— 它只是标识符，不是关键指标。 */
.inv-barcode {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Courier New", monospace;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.2px;
  font-size: 12px;
  white-space: nowrap;
}

/* ===== 财务：收款方式品牌图标 ===== */
.pay-method {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.pay-icon {
  width: 20px;
  height: 20px;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.pay-icon svg {
  width: 20px;
  height: 20px;
  display: block;
}

/* ===== 吊牌建款：摄像头拍照自动识别（演示态覆盖层） ===== */
.cam-mask {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, .55);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 2000;
}
.cam-panel {
  width: min(440px, 92vw);
  background: #fff;
  border-radius: 14px;
  padding: 16px;
  box-shadow: 0 12px 40px rgba(0, 0, 0, .25);
}
.cam-title {
  font-size: 15px;
  font-weight: 700;
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 12px;
}
.cam-badge {
  font-size: 11px;
  font-weight: 600;
  color: #fff;
  background: #d9822b;
  border-radius: 8px;
  padding: 1px 7px;
}
.cam-stage {
  width: 100%;
  aspect-ratio: 4 / 3;
  background: #0d0d0d;
  border-radius: 10px;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}
.cam-video,
.cam-photo {
  width: 100%;
  height: 100%;
  object-fit: contain;
}
.cam-error {
  color: #ffd9d9;
  font-size: 13px;
  padding: 16px;
  text-align: center;
  line-height: 1.6;
}
.cam-actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: 12px;
}
.cam-tip {
  margin-top: 10px;
  font-size: 12px;
  color: #909399;
  line-height: 1.5;
}

/* 吊牌建款：条码输入框右侧「自动生成条码」图标按钮 */
.barcode-row {
  display: flex;
  align-items: flex-start;
  gap: 8px;
}
.barcode-row .t-input {
  flex: 1 1 auto;
  min-width: 0;
}
/* 入库操作台：快速选择的下拉框也要占满剩余宽度（与扫描输入框一致） */
.barcode-row .t-select {
  flex: 1 1 auto;
  min-width: 0;
}
.gen-barcode-btn {
  flex: none;
  color: var(--sd-brand);
}
.gen-barcode-btn .ip-icon {
  margin-right: 0;
}
/* 摄像头拍照自动识别按钮（吊牌建款：条码输入框右侧同行）。
   与 .gen-barcode-btn 同理 flex:none —— 输入框是 flex:1 会抢宽，
   按钮若允许收缩，「摄像头拍照自动识别」9 个字会被压成两行、撑高整行。 */
.cam-recognize-btn {
  flex: none;
  white-space: nowrap;
}

/* 入库操作台：顶部操作区（扫描 + 快速选择）的浅底分区，与下方台账形成视觉分层 */
.intake-ops {
  background: var(--sd-surface-2);
  border: 1px dashed var(--sd-border);
  border-radius: 8px;
  padding: 14px;
  margin-bottom: 4px;
}

/* ===== 主题色切换（顶栏右上角按钮 + 色板弹窗） ===== */
/* 顶栏主题入口按钮：白底半透明胶囊，与门店标识同一行。
   ⚠️ 这里原来还有一组 `.aihelp-trigger` 选择器（当时的 AI客服顶栏按钮复用它）。
      2026-09-17 用户要求去掉顶栏那个按钮 → 按钮与选择器一起删。
      **别只删按钮留下选择器**：死样式不会报错，下次有人照着它写新按钮，
      就会得到一个"说不清是谁在用"的样式（本项目吃过同类亏）。 */
.theme-trigger {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: rgba(255, 255, 255, 0.18);
  border: 1px solid rgba(255, 255, 255, 0.25);
  border-radius: 999px;
  padding: 6px 14px;
  font-size: 13px;
  color: #fff;
  cursor: pointer;
  transition: background 0.15s ease;
}
.theme-trigger:hover {
  background: rgba(255, 255, 255, 0.3);
}
/* 按钮内三色圆点，直观提示“这是换色入口” */
.theme-trigger-swatches {
  display: inline-flex;
  gap: 3px;
}
.theme-trigger .swatch {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.6);
  box-sizing: border-box;
}
/* 弹窗首行：说明文案 + 右侧「恢复默认」 */
.theme-head {
  display: flex;
  align-items: flex-start;
  gap: 12px;
}
/* 弹窗内提示文案 */
.theme-tip {
  flex: 1;
  min-width: 0;
  margin: 0 0 16px;
  font-size: 13px;
  color: var(--sd-text-2);
  line-height: 1.6;
}
.theme-tip b {
  color: var(--sd-text);
}
/* 「恢复默认」：文字按钮，不抢主操作（主操作就是点色块本身） */
.theme-reset {
  flex: none;
  font-size: 13px;
  font-weight: 600;
  color: var(--sd-brand);
  cursor: pointer;
  white-space: nowrap;
  line-height: 1.6;
}
.theme-reset:hover {
  text-decoration: underline;
}
/* 存储被环境禁用时的告警条（与登录页 .auth-alert 同一套「必须让用户看见」的配色） */
.theme-warn {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin-bottom: 14px;
  padding: 9px 12px;
  border-radius: var(--sd-radius-sm);
  background: #fff8ec;
  border: 1px solid #ffe3b8;
  color: #8a5a12;
  font-size: 12px;
  line-height: 1.6;
}
.theme-warn b {
  color: #6b4409;
}
.theme-warn .ip-icon {
  flex: 0 0 auto;
  margin-top: 1px;
  font-size: 15px;
}
/* 主题色卡：每行 3 个，居中换行。
   用 flex 而不是 grid：色调数量不是 3 的整数倍时（如扩到 7 个），
   grid 会把最后一行的孤块甩在最左边，看着像没排完；flex 居中后是刻意的收尾。 */
.theme-grid {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 14px;
}
/* 单个主题卡：色块 + 名称，选中态高亮描边 */
.theme-card {
  flex: 0 0 calc((100% - 28px) / 3); /* 3 列，扣掉两个 14px 间距 */
  max-width: calc((100% - 28px) / 3);
  box-sizing: border-box;
  cursor: pointer;
  border: 2px solid transparent;
  border-radius: var(--sd-radius-sm);
  padding: 6px;
  text-align: center;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.theme-card:hover {
  background: var(--sd-surface-2);
}
.theme-card.active {
  border-color: var(--sd-brand);
  /* 浅到 8% 的品牌底色即可：色块本身就是高饱和的，卡片底色再重会盖过色块本身 */
  background: var(--sd-brand-a08, rgba(74, 144, 226, 0.08));
}
.theme-card.active .theme-card-name {
  color: var(--sd-brand-strong);
}
/* 色块：铺该色调的渐变起点色（即品牌主色），右上角放渐变终点色小圆点作双色示意 */
.theme-card-swatch {
  position: relative;
  height: 44px;
  border-radius: 6px;
  margin-bottom: 8px;
}
.theme-card-dot {
  position: absolute;
  right: 6px;
  top: 6px;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, 0.7);
  box-sizing: border-box;
}
/* 选中角标：白底圆牌 + 勾（勾由两条边框旋转而成，不额外引图标，省一次图标依赖） */
.theme-card-check {
  position: absolute;
  right: 6px;
  top: 6px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.18);
  box-sizing: border-box;
}
.theme-card-check::after {
  content: "";
  position: absolute;
  left: 5px;
  top: 2.5px;
  width: 4px;
  height: 7px;
  border: 2px solid var(--sd-brand);
  border-top: 0;
  border-left: 0;
  transform: rotate(45deg);
  box-sizing: border-box;
}
.theme-card-name {
  font-size: 13px;
  color: var(--sd-text);
  font-weight: 600;
}
/* 窄屏：色卡改为两列 */

/* ===================== 报表视图（销售分析 / 人群分析 / 季节建议 / AI 建议） ===================== */
/* 报表通用模块：独立浅色圆角卡，与财务中心 .fin-module 观感一致 */
.rp-module {
  background: linear-gradient(180deg, #f8fafd 0%, #f2f6fc 100%);
  border: 1px solid var(--sd-border);
  border-radius: 14px;
  padding: 16px 18px;
  margin-bottom: 16px;
}
.rp-title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  font-weight: 700;
  color: var(--sd-text);
  margin: 0 0 12px;
}
/* 原先这里用 `::before` 画了一条 4x14px 的品牌色小竖条，按用户要求全站去掉竖线，已删除。
   标题已是 14px / 700，不需要色条辅助。**不要再加回来**（守卫见 verify_layout_fixed_e2e.js）。 */
/* 双栏 / 三栏网格 */
.rp-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
  align-items: start;
}
.rp-grid-3 {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 16px;
  align-items: start;
}

/* 饼图：左图右图例 */
.rp-pie-wrap {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
}
.rp-pie-svg {
  width: 160px;
  height: 160px;
  flex: none;
}
.rp-legend {
  flex: 1;
  min-width: 140px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.rp-legend-row {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
}
.rp-legend-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  flex: none;
}
.rp-legend-name {
  flex: 1;
  color: var(--sd-text-2);
}
.rp-legend-val {
  color: var(--sd-text);
  font-weight: 700;
  flex: none;
}

/* 销售趋势曲线图 */
.rp-chart {
  position: relative;
  width: 100%;
}
.rp-svg {
  width: 100%;
  height: auto;
  display: block;
}
.rp-area {
  fill: var(--sd-brand-soft);
  opacity: 0.6;
}
.rp-line {
  stroke: var(--sd-brand);
  stroke-width: 2.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.rp-dot {
  fill: #fff;
  stroke: var(--sd-brand);
  stroke-width: 2;
  cursor: pointer;
}
.rp-axis {
  font-size: 10px;
  fill: var(--sd-text-3);
}
.rp-tooltip {
  position: absolute;
  pointer-events: none;
  background: var(--sd-text);
  color: #fff;
  font-size: 12px;
  padding: 4px 10px;
  border-radius: 6px;
  white-space: nowrap;
  transform: translateX(-50%);
  z-index: 10;
}

/* 热卖 TOP 排行 */
.rp-hot {
  display: flex;
  flex-direction: column;
}
.rp-hot-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 0;
  border-bottom: 1px solid var(--sd-border);
}
.rp-hot-row:last-child {
  border-bottom: none;
}
.rp-hot-no {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  font-weight: 700;
  flex: none;
  background: var(--sd-surface-2);
  color: var(--sd-text-2);
}
.rp-hot-no.no1 { background: #fff1e8; color: #d9822b; }
.rp-hot-no.no2 { background: #eef2f7; color: #5a6573; }
.rp-hot-no.no3 { background: #fbf0e3; color: #c4661c; }
.rp-hot-main {
  flex: 1;
  min-width: 0;
}
.rp-hot-name {
  font-size: 14px;
  font-weight: 600;
  color: var(--sd-text);
}
.rp-hot-sku {
  font-size: 12px;
  color: var(--sd-text-3);
  margin-left: 6px;
}
.rp-hot-sub {
  font-size: 12px;
  color: var(--sd-text-3);
  margin-top: 2px;
}
.rp-hot-right {
  text-align: right;
  flex: none;
}
.rp-hot-amount {
  font-size: 14px;
  font-weight: 700;
  color: var(--sd-text);
}
.rp-hot-trend {
  font-size: 12px;
  margin-top: 2px;
}
.rp-hot-trend.up { color: var(--sd-danger); }
.rp-hot-trend.down { color: var(--sd-success); }

/* 消费画像核心标签（人群分析顶部） */
.rp-persona {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 12px;
  margin-bottom: 16px;
}
.rp-persona-item {
  background: var(--sd-surface-2);
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  padding: 12px 14px;
  text-align: center;
}
.rp-persona-lab {
  font-size: 12px;
  color: var(--sd-text-3);
}
.rp-persona-val {
  font-size: 15px;
  font-weight: 700;
  color: var(--sd-text);
  margin-top: 4px;
}
/* 消费特征标签 */
.rp-traits {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}
.rp-trait {
  font-size: 13px;
  color: var(--sd-text-2);
  background: var(--sd-surface-2);
  border: 1px solid var(--sd-border);
  border-radius: 999px;
  padding: 6px 14px;
}

/* 季节建议列表 */
.rp-season-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.rp-season-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 10px 0;
  border-bottom: 1px solid var(--sd-border);
}
.rp-season-row:last-child {
  border-bottom: none;
}
.rp-season-tag {
  flex: none;
  font-size: 12px;
  font-weight: 600;
  border-radius: 8px;
  padding: 2px 10px;
  color: #fff;
}
.rp-season-tag.prio { background: var(--sd-brand); }
.rp-season-tag.warn { background: var(--sd-warning); }
.rp-season-tag.prep { background: var(--sd-success); }
.rp-season-main {
  flex: 1;
  min-width: 0;
}
.rp-season-name {
  font-size: 14px;
  font-weight: 600;
  color: var(--sd-text);
}
.rp-season-reason {
  font-size: 12.5px;
  color: var(--sd-text-2);
  margin-top: 3px;
  line-height: 1.6;
}
/* 色码小字（清仓候选里跟着款名的「藏青 M」） */
.rp-season-cs {
  font-weight: 400;
  font-size: 12px;
  color: var(--sd-text-3);
  margin-left: 4px;
}
/* 某类结论为空时的占位（"暂无需要清仓的款"），次要色，别让人误以为是加载失败 */
.rp-season-empty {
  padding: 10px 0;
  font-size: 12.5px;
  color: var(--sd-text-3);
}
/* 大模型增强建议的文本块（预格式，保留换行） */
.rp-llm-text {
  font-size: 13px;
  line-height: 1.7;
  color: var(--sd-text);
  white-space: pre-wrap;
}

/* AI 建议卡片 */
.rp-ai-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
}
.rp-ai-card {
  background: var(--sd-surface-2);
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius);
  padding: 16px 18px;
  transition: box-shadow 0.15s ease, border-color 0.15s ease;
}
.rp-ai-card:hover {
  box-shadow: var(--sd-shadow-hover);
  border-color: var(--sd-brand);
}
.rp-ai-head {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 15px;
  font-weight: 700;
  color: var(--sd-text);
  margin-bottom: 10px;
}
.rp-ai-head .ip-icon {
  color: var(--sd-brand);
}
.rp-ai-body {
  font-size: 13px;
  color: var(--sd-text-2);
  line-height: 1.7;
}

/* ===================== AI 季节建议（维度设置 + 分析依据 + 结论） ===================== */
/* 分析维度开关：4 个可勾选卡片，选中高亮 */
.rp-dim-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 12px;
}
.rp-dim-item {
  background: var(--sd-surface-2);
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius);
  padding: 12px 14px;
  cursor: pointer;
  transition: border-color 0.15s ease, box-shadow 0.15s ease, background 0.15s ease;
  user-select: none;
}
.rp-dim-item:hover {
  border-color: var(--sd-brand);
}
.rp-dim-item.on {
  background: color-mix(in srgb, var(--sd-brand) 8%, #fff);
  border-color: var(--sd-brand);
  box-shadow: 0 0 0 1px var(--sd-brand) inset;
}
.rp-dim-head {
  display: flex;
  align-items: center;
  gap: 8px;
}
.rp-dim-check {
  width: 18px;
  height: 18px;
  flex: none;
  border: 1px solid var(--sd-border);
  border-radius: 4px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  color: #fff;
  background: transparent;
  transition: background 0.15s ease, border-color 0.15s ease;
}
.rp-dim-item.on .rp-dim-check {
  background: var(--sd-brand);
  border-color: var(--sd-brand);
}
.rp-dim-label {
  font-size: 14px;
  font-weight: 600;
  color: var(--sd-text);
}
.rp-dim-desc {
  font-size: 12px;
  color: var(--sd-text-3);
  margin-top: 6px;
}
.rp-dim-count {
  margin-left: 12px;
  font-size: 13px;
  color: var(--sd-text-3);
}

/* AI 数据依据清单：每行「维度标签 + 依据文案」 */
.rp-basis-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.rp-basis-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  font-size: 13px;
}
.rp-basis-tag {
  flex: none;
  font-size: 12px;
  font-weight: 600;
  color: var(--sd-brand);
  background: color-mix(in srgb, var(--sd-brand) 10%, #fff);
  border-radius: 4px;
  padding: 2px 8px;
}
.rp-basis-text {
  color: var(--sd-text-2);
  line-height: 1.6;
}

/* ============================================================================
   「AI 季节建议」→ 季节决策台（2026-09-17 深度重构）
   改动前必读（四条都不是风格偏好，是产品逻辑的直接映射）：
   ① **不加 @media 断点**（全站固定排版铁律）：窗口变窄不重排，靠内容区最小宽度兜住。
   ② **不用「左侧彩色竖线」表达级别**（全站禁令）：行动卡的优先级改用
      「编号圆点 + 四边边框 + 浅底」，级别靠色深与标签文字区分。
   ③ 颜色一律走主题变量：换主题时这一页必须跟着变（写死色值 = 换主题后纹丝不动）。
   ④ 所有选择器都挂在 `.season-desk` 下，避免 `sd-` 这种短前缀污染别的页
      （本项目已有 rp- / nh- / mat- 等多套前缀，都遵守这条）。
   ⑤ 原先的 `.rp-ai-placeholder`（"未分析引导"虚线占位）已删除：
      现在**打开即有结论**，不再存在"还没分析"这个状态，留着就是死样式。
   ========================================================================== */

.season-desk {
  display: flex;
  flex-direction: column;
}

/* --- ① 决策头：季节位置 + 换季倒计时 --- */
.season-desk .sd-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 12px;
  padding: 14px 16px;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius);
  background: var(--sd-brand-a08);
}
.season-desk .sd-head-left {
  min-width: 0;
}
.season-desk .sd-season {
  display: flex;
  align-items: baseline;
  gap: 10px;
}
.season-desk .sd-season-name {
  font-size: 20px;
  font-weight: 700;
  color: var(--sd-text);
}
.season-desk .sd-season-week {
  font-size: 13px;
  color: var(--sd-text-2);
}
.season-desk .sd-phase {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 8px;
  flex-wrap: wrap;
}
.season-desk .sd-phase-advice {
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--sd-text-2);
}
.season-desk .sd-head-right {
  flex: none;
  width: 220px;
  text-align: right;
}
.season-desk .sd-countdown {
  margin-bottom: 8px;
  font-size: 13px;
  font-weight: 600;
  color: var(--sd-brand-strong);
}
.season-desk .sd-progress {
  height: 6px;
  border-radius: 3px;
  background: var(--sd-brand-a12);
  overflow: hidden;
}
.season-desk .sd-progress-bar {
  display: block;
  height: 100%;
  border-radius: 3px;
  background: var(--sd-brand);
  transition: width 0.3s ease;
}

/* --- ② 数据水位：这份结论站在多少数据上 --- */
.season-desk .sd-water {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin-bottom: 12px;
  padding: 10px 12px;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface-2);
}
/* 水位不足（样本太少）时整条转黄：这件事比结论本身更需要被看见 */
.season-desk .sd-water.is-thin {
  border-color: color-mix(in srgb, var(--sd-warning) 35%, var(--sd-border));
  background: color-mix(in srgb, var(--sd-warning) 8%, var(--sd-surface));
}
.season-desk .sd-water .ip-icon {
  margin-right: 0;
  margin-top: 1px;
}
.season-desk .sd-water .ip-icon svg {
  width: 15px;
  height: 15px;
}
.season-desk .sd-water-main {
  flex: 1;
  min-width: 0;
}
.season-desk .sd-water-text {
  font-size: 12.5px;
  line-height: 1.65;
  color: var(--sd-text);
}
.season-desk .sd-water-nums {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 14px;
  margin-top: 4px;
  font-size: 12px;
  color: var(--sd-text-3);
}

/* --- 数据缺口明细（按级别分组） --- */
.season-desk .sd-gaps {
  margin-bottom: 12px;
  padding: 12px 14px;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface);
}
.season-desk .sd-gap-group + .sd-gap-group {
  margin-top: 14px;
}
.season-desk .sd-gap-group-title {
  margin-bottom: 8px;
  font-size: 12px;
  font-weight: 700;
  color: var(--sd-text-2);
}
.season-desk .sd-gap-item {
  padding: 8px 10px;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface-2);
}
.season-desk .sd-gap-item + .sd-gap-item {
  margin-top: 8px;
}
.season-desk .sd-gap-title {
  font-size: 13px;
  font-weight: 600;
  color: var(--sd-text);
}
.season-desk .sd-gap-impact {
  margin-top: 3px;
  font-size: 12px;
  line-height: 1.65;
  color: var(--sd-text-2);
}
.season-desk .sd-gap-how {
  margin-top: 3px;
  font-size: 12px;
  line-height: 1.65;
  color: var(--sd-text-3);
}

/* --- ③ 行动清单（主区块） --- */
.season-desk .sd-actions {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.season-desk .sd-action {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 12px 14px;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface);
}
/* 级别表达：边框色 + 浅底 + 编号圆点（**不是**左侧彩色竖线，那是全站禁令） */
.season-desk .sd-action.lv-now {
  border-color: color-mix(in srgb, var(--sd-danger) 38%, var(--sd-border));
  background: color-mix(in srgb, var(--sd-danger) 6%, var(--sd-surface));
}
.season-desk .sd-action.lv-soon {
  border-color: color-mix(in srgb, var(--sd-warning) 38%, var(--sd-border));
  background: color-mix(in srgb, var(--sd-warning) 6%, var(--sd-surface));
}
.season-desk .sd-action.lv-watch {
  background: var(--sd-surface-2);
}
.season-desk .sd-action-order {
  flex: none;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  font-weight: 700;
  color: #fff;
  background: var(--sd-text-3);
}
.season-desk .sd-action.lv-now .sd-action-order {
  background: var(--sd-danger);
}
.season-desk .sd-action.lv-soon .sd-action-order {
  background: var(--sd-warning);
}
.season-desk .sd-action-body {
  flex: 1;
  min-width: 0;
}
.season-desk .sd-action-head {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.season-desk .sd-action-title {
  font-size: 14px;
  font-weight: 700;
  color: var(--sd-text);
}
.season-desk .sd-action-detail {
  margin-top: 4px;
  font-size: 12.5px;
  line-height: 1.7;
  color: var(--sd-text-2);
}
.season-desk .sd-action-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 16px;
  margin-top: 6px;
  font-size: 12px;
  color: var(--sd-text-3);
}
.season-desk .sd-action-basis {
  flex: 1;
  min-width: 0;
}

/* --- ④ 季节结构：横条 + 分档 --- */
.season-desk .sd-grid {
  margin-top: 16px;
}
.season-desk .sd-bars {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.season-desk .sd-bar-row {
  display: flex;
  align-items: center;
  gap: 10px;
}
.season-desk .sd-bar-label {
  flex: none;
  width: 58px;
  font-size: 12.5px;
  color: var(--sd-text-2);
}
.season-desk .sd-bar-row.is-current .sd-bar-label {
  font-weight: 700;
  color: var(--sd-brand-strong);
}
.season-desk .sd-bar-row.is-next .sd-bar-label {
  font-weight: 600;
  color: var(--sd-text);
}
/* 横条：宽度百分比需要有确定高度的容器（这里高度写死 10px，百分比宽度才有着落） */
.season-desk .sd-bar-track {
  flex: 1;
  min-width: 0;
  height: 10px;
  border-radius: 5px;
  background: var(--sd-surface-2);
  overflow: hidden;
}
.season-desk .sd-bar-track i {
  display: block;
  height: 100%;
  border-radius: 5px;
  background: var(--sd-text-3);
  opacity: 0.45;
}
.season-desk .sd-bar-row.is-current .sd-bar-track i {
  background: var(--sd-brand);
  opacity: 1;
}
.season-desk .sd-bar-row.is-next .sd-bar-track i {
  background: var(--sd-brand-strong);
  opacity: 0.7;
}
.season-desk .sd-bar-value {
  flex: none;
  width: 132px;
  text-align: right;
  font-size: 12px;
  color: var(--sd-text-3);
}
.season-desk .sd-bands {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.season-desk .sd-band {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 8px 12px;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface-2);
}
.season-desk .sd-band.tone-warn {
  border-color: color-mix(in srgb, var(--sd-warning) 30%, var(--sd-border));
  background: color-mix(in srgb, var(--sd-warning) 7%, var(--sd-surface));
}
.season-desk .sd-band.tone-danger {
  border-color: color-mix(in srgb, var(--sd-danger) 30%, var(--sd-border));
  background: color-mix(in srgb, var(--sd-danger) 7%, var(--sd-surface));
}
.season-desk .sd-band-name {
  font-size: 13px;
  font-weight: 600;
  color: var(--sd-text);
}
.season-desk .sd-band-num {
  font-size: 12px;
  color: var(--sd-text-3);
}

/* --- 通用小字与行内元信息 --- */
.season-desk .sd-note {
  margin-top: 10px;
  font-size: 12px;
  line-height: 1.7;
  color: var(--sd-text-3);
}
.season-desk .sd-line-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 3px 14px;
  margin-top: 4px;
  font-size: 12px;
  color: var(--sd-text-3);
}
.season-desk .sd-est {
  color: var(--sd-warning);
}

/* --- ⑥ 行业参考线（每条都要能看到出处） --- */
.season-desk .sd-bm {
  display: grid;
  grid-template-columns: 132px 186px 92px 1fr;
  align-items: baseline;
  gap: 6px 12px;
  padding: 9px 0;
  border-bottom: 1px solid var(--sd-border);
}
.season-desk .sd-bm:last-of-type {
  border-bottom: none;
}
.season-desk .sd-bm-label {
  font-size: 13px;
  font-weight: 600;
  color: var(--sd-text);
}
.season-desk .sd-bm-target {
  font-size: 12.5px;
  color: var(--sd-text-2);
}
.season-desk .sd-bm-flag {
  justify-self: start;
  padding: 1px 8px;
  border-radius: 8px;
  font-size: 11px;
}
.season-desk .sd-bm-flag.is-ok {
  color: var(--sd-success);
  background: color-mix(in srgb, var(--sd-success) 12%, transparent);
}
.season-desk .sd-bm-flag.is-no {
  color: var(--sd-warning);
  background: color-mix(in srgb, var(--sd-warning) 14%, transparent);
}
.season-desk .sd-bm-why {
  font-size: 12px;
  line-height: 1.65;
  color: var(--sd-text-3);
}
.season-desk .sd-bm-source {
  grid-column: 2 / -1;
  font-size: 11.5px;
  line-height: 1.6;
  color: var(--sd-text-3);
  opacity: 0.85;
}

/* --- ⑥ 数据来源接入状态 --- */
.season-desk .sd-src {
  padding: 9px 0;
  border-bottom: 1px solid var(--sd-border);
}
.season-desk .sd-src:last-of-type {
  border-bottom: none;
}
.season-desk .sd-src-line {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.season-desk .sd-src-dot {
  flex: none;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--sd-text-3);
}
.season-desk .sd-src-dot.is-on {
  background: var(--sd-success);
}
.season-desk .sd-src-dot.is-todo {
  opacity: 0.5;
}
.season-desk .sd-src-label {
  font-size: 13px;
  font-weight: 600;
  color: var(--sd-text);
}
.season-desk .sd-src-desc {
  font-size: 12px;
  color: var(--sd-text-3);
}
.season-desk .sd-src-state {
  margin-left: auto;
  padding: 1px 8px;
  border-radius: 8px;
  font-size: 11px;
}
.season-desk .sd-src-state.is-on {
  color: var(--sd-success);
  background: color-mix(in srgb, var(--sd-success) 12%, transparent);
}
.season-desk .sd-src-state.is-todo {
  color: var(--sd-text-3);
  background: var(--sd-surface-2);
  border: 1px solid var(--sd-border);
}
.season-desk .sd-src-note {
  margin-top: 3px;
  font-size: 12px;
  line-height: 1.65;
  color: var(--sd-text-3);
}
.season-desk .sd-src-api {
  margin-left: 6px;
  font-family: monospace;
  font-size: 11.5px;
  color: var(--sd-brand-strong);
}

/* --- ⑦ AI 深入分析（可选动作） --- */
.season-desk .sd-ai-hint {
  font-size: 12.5px;
  line-height: 1.75;
  color: var(--sd-text-2);
}
.season-desk .sd-ai-cost {
  margin-top: 6px;
  font-size: 12px;
  color: var(--sd-warning);
}

/* ===================== 资料 · 商品信息（左侧分类筛选 + 右侧商品卡片） ===================== */
.mat-layout {
  display: flex;
  align-items: flex-start;
  gap: 16px;
}
/* 左侧分类筛选栏 */
.mat-side {
  flex: 0 0 180px;
  width: 180px;
  position: sticky;
  top: 12px;
  background: var(--sd-surface-2);
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius);
  padding: 12px;
}
.mat-side-title {
  font-size: 13px;
  font-weight: 700;
  color: var(--sd-text-2);
  margin-bottom: 10px;
}
.mat-cat {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 9px 12px;
  border-radius: var(--sd-radius-sm);
  cursor: pointer;
  font-size: 13px;
  color: var(--sd-text);
  transition: background 0.15s ease, color 0.15s ease;
}
.mat-cat:hover {
  background: var(--sd-surface);
}
.mat-cat.active {
  background: var(--sd-brand-soft);
  color: var(--sd-brand-strong);
  font-weight: 600;
}
.mat-cat-name {
  flex: 1;
}
.mat-cat-count {
  font-size: 12px;
  color: var(--sd-text-3);
}
.mat-cat.active .mat-cat-count {
  color: var(--sd-brand-strong);
}
/* 右侧商品表格区 */
.mat-main {
  flex: 1 1 auto;
  min-width: 0;
}
/* 表格款名：加粗、超长省略 */
.mat-row-name {
  font-size: 14px;
  font-weight: 700;
  color: var(--sd-text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  display: block;
}
/* 表格价格区间：品牌主色加粗 */
.mat-row-price {
  font-size: 13px;
  font-weight: 700;
  color: var(--sd-brand);
}
/* 库存预警：低库存橙色、缺货红色（与库存页观感一致） */
.mat-stock-low {
  color: #d9822b;
  font-weight: 700;
}
.mat-stock-zero {
  color: #e35d5d;
  font-weight: 700;
}
/* 删除 / 隐藏商品确认弹窗：警示卡片（普通=软删提示，danger=物理删除） */
.mat-del-warn {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 14px;
  border-radius: var(--sd-radius-sm);
  background: #fff7e6;
  border: 1px solid #ffe0a3;
}
.mat-del-warn-danger {
  background: #fff0f0;
  border-color: #ffc9c9;
}
.mat-del-warn .ip-icon {
  flex: none;
  width: 22px;
  height: 22px;
  color: #d9822b;
}
.mat-del-warn-danger .ip-icon {
  color: #e35d5d;
}
.mat-del-warn .ip-icon svg {
  width: 22px;
  height: 22px;
  display: block;
}
.mat-del-title {
  font-size: 14px;
  font-weight: 700;
  color: var(--sd-text);
}
.mat-del-desc {
  font-size: 13px;
  color: var(--sd-text-2);
  line-height: 1.6;
  margin-top: 4px;
}
/* 窄屏：分类栏与卡片区上下堆叠 */

/* ===================== 资料 · 商品信息（续）：一键铺码 / 取色块 / 未收录提醒 =====================
   这些样式服务于「新建商品时不再手敲几十行色码」这条主线：
   铺码面板把颜色与尺码的取值来源固定在资料库上，取色块让用户一眼认出颜色，
   未收录提醒把「同物异名会把库存拆开」这个后果讲在录入当时，而不是等对账时才发现。 */
/* 字段下方的一行灰字说明（解释数据从哪来、为什么这么设计） */
.mat-hint {
  font-size: 12px;
  line-height: 1.6;
  color: var(--sd-text-3);
  margin-top: 4px;
}

/* ===== 商品图片编辑块（新建 / 编辑商品弹窗）=====
   为什么单独成块而不是并进基础信息那一行：图片是固定尺寸的方块，
   与输入框并排会把整行撑高、窄屏下还会被挤变形。
   视觉沿用全站的「四边 1px 细边 + 圆角 + 浅底」强调方式
   —— 不用左侧彩色竖线（全站禁令）。 */
.mat-thumb-edit {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  margin-bottom: 14px;
  padding: 12px;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius);
  background: var(--sd-surface-2);
}
/* 预览方块：固定 80×80，与实际生成/压缩出的图片尺寸一致（PLACEHOLDER_SIZE），
   这样占位图与真图切换时不会让整块跳动 */
.mt-thumb-box {
  flex: 0 0 auto;
  width: 80px;
  height: 80px;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  overflow: hidden;
  background: var(--sd-surface);
}
.mt-thumb-box img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.mt-thumb-info {
  flex: 1 1 auto;
  min-width: 0;
}
.mt-thumb-title {
  font-size: 13px;
  font-weight: 700;
  color: var(--sd-text);
}
.mt-thumb-hint {
  margin-top: 4px;
  font-size: 12px;
  line-height: 1.6;
  color: var(--sd-text-3);
}
.mt-thumb-ops {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
  flex-wrap: wrap;
}
.mt-thumb-dim {
  font-size: 12px;
  color: var(--sd-text-3);
}
/* 原生 file 控件隐藏但仍可被 click() 触发（不能用 display:none 之外的写法，
   display:none 在部分浏览器里会让 click() 静默失效） */
.mt-thumb-file {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

/* 一键铺码面板：浅底 + 分组行，与下方「色码行表格」在视觉上区分开 */
.mat-matrix {
  margin-top: 10px;
  padding: 12px 14px 10px;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius);
  background: var(--sd-surface-2);
}
.mat-matrix-head {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 10px;
}
.mat-matrix-title {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 13px;
  font-weight: 700;
  color: var(--sd-text);
  white-space: nowrap;
}
.mat-matrix-title .ip-icon,
.mat-matrix-title .ip-icon svg {
  width: 16px;
  height: 16px;
  display: block;
}
.mat-matrix-title .ip-icon {
  color: var(--sd-brand);
}
.mat-matrix-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin-bottom: 8px;
}
.mat-matrix-lab {
  flex: 0 0 116px;
  width: 116px;
  padding-top: 5px;
  font-size: 12px;
  font-weight: 600;
  color: var(--sd-text-2);
}
/* 「颜色库 / 尺码组」来源角标：让用户明确这些选项不是本页临时数据，而是可维护的主数据 */
.mat-matrix-src {
  display: inline-block;
  margin-left: 6px;
  padding: 0 5px;
  font-size: 10px;
  font-weight: 400;
  line-height: 16px;
  color: var(--sd-brand-strong);
  background: var(--sd-brand-a12);
  border-radius: 4px;
  vertical-align: 1px;
}
.mat-matrix-pick {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  min-width: 0;
}
.mat-matrix-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: 4px;
  padding-top: 10px;
  border-top: 1px dashed var(--sd-border);
}
.mat-matrix-foot .mat-hint {
  margin-top: 0;
}
.mat-matrix-foot b {
  color: var(--sd-brand);
}
/* 「统一价格」的一组：标签 + 数字框，横向排布、垂直居中对齐 */
.mat-bulk-price {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

/* 可点击的资料项（颜色 / 尺码 chip）：选中态用品牌色浅底 + 描边，扫一眼就知道勾了哪几个 */
.mat-chip-wrap {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  min-width: 0;
}
.mat-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 9px;
  border-radius: 999px;
  font-size: 12px;
  line-height: 20px;
  color: var(--sd-text-2);
  background: var(--sd-surface);
  border: 1px solid var(--sd-border);
  cursor: pointer;
  user-select: none;
  transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.mat-chip:hover {
  border-color: var(--sd-brand);
  color: var(--sd-brand);
}
.mat-chip-on {
  background: var(--sd-brand-soft);
  border-color: var(--sd-brand);
  color: var(--sd-brand-strong);
  font-weight: 600;
}
/* 纯展示用的小 chip（如尺码组内容的预览），不可点：屏蔽指针与 hover 变色，避免误以为能点 */
.mat-chip-sm {
  padding: 0 7px;
  line-height: 18px;
  pointer-events: none;
}

/* 取色块：有标准色值时由行内 style 覆盖 background；没有时露出下面的斜线灰底，
   表示「颜色库未收录，没有标准色值」。刻意不设默认色——把「未收录的青色」画成蓝色，
   比不画更容易让人看错，而取色块存在的意义正是让人一眼认出颜色。 */
.mat-swatch {
  flex: none;
  width: 14px;
  height: 14px;
  border-radius: 4px;
  border: 1px solid rgba(0, 0, 0, 0.16);
  box-sizing: border-box;
  background-color: #eef1f5;
  background-image: repeating-linear-gradient(45deg, #cfd6e0 0 2px, transparent 2px 5px);
}

/* 色码行表格的颜色单元格：取色块 + 输入框并排。
   用 `> :last-child` 而不是写 TDesign 的输入框根类名（.t-input / .t-input__wrap 在不同版本间
   不一致），这样无论内置 TDesign 把根类名定成哪个都能撑满剩余宽度。 */
.mat-cell-color {
  display: flex;
  align-items: center;
  gap: 6px;
}
.mat-cell-color > :last-child {
  flex: 1 1 auto;
  min-width: 0;
}

/* 色码行表格的兜底保护：TDesign 表格是 table-layout:fixed，当列宽合计超过容器时会等比压缩列，
   把 t-input-number 的文本区压成 0 宽 —— 界面上是个「看得见、点不进去」的空盒子
   （实测踩到过：弹窗 760px 装不下加宽后的 8 列）。
   正常情况靠把弹窗放宽到 940 + 列宽合计 820 解决；这里是第二道防线，
   万一以后又加列/改宽，至少输入框还留得住可点可填的最小宽度。 */
.mat-line-table .t-input__inner {
  min-width: 38px;
  box-sizing: border-box;
}

/* ===== 商品弹窗：内容区自己滚，操作按钮固定在滚动区之外 =====
   为什么必须这样做（不是美化）：
     一件上衣 5 色 × 5 码 = 25 行色码，弹窗会被撑到远超屏幕高度；
     而模态弹窗打开时 TDesign 会锁住页面滚动（body overflow:hidden），
     于是底部的「保存商品」按钮跑到视口外、且**滚不到** —— 用户根本保存不了商品。
     试过给表格内层限高，但 TDesign 表格内部还有一层自己的滚动容器，按类名去猜不可靠
     （改完保存按钮依旧在 y=1348 处，实测无效）。所以改为由本组件自己划出滚动区：
     内容在 .mat-dialog-body 里滚，按钮放在外面的 .mat-dialog-foot，位置固定可见。
     附带好处：不再出现「弹窗滚一层、表格再滚一层」的嵌套滚动条。
   数值说明：弹窗还要往下偏移 top(4vh) 才开始，加上标题栏(≈56) + 内边距(≈48) + 按钮行(≈63)
   ≈ 定值 167px；这里给内容区 80vh-160px，整体约 88vh 高，按钮稳稳落在视口内。
   曾用 88vh - 132px，实测按钮仍在 y=1008（超出 960 视口）——留白必须算进去。 */
.mat-dialog-body {
  max-height: calc(80vh - 160px);
  overflow-y: auto;
  /* 内滚时给滚动条留点空间，否则贴边内容会被压住 */
  padding-right: 4px;
}
.mat-dialog-foot {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 16px;
  padding-top: 14px;
  border-top: 1px solid var(--sd-border);
}

/* 未收录色/码提醒：常驻黄条（不是一闪而过的 toast），改完颜色立刻消失 */
.mat-unreg {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin-top: 10px;
  padding: 12px 14px;
  border-radius: var(--sd-radius-sm);
  background: #fff7e6;
  border: 1px solid #ffe0a3;
}
.mat-unreg .ip-icon,
.mat-unreg .ip-icon svg {
  width: 20px;
  height: 20px;
  display: block;
}
.mat-unreg .ip-icon {
  flex: none;
  color: var(--sd-warning);
}
.mat-unreg-title {
  font-size: 13px;
  font-weight: 700;
  color: var(--sd-text);
}
.mat-unreg-desc {
  font-size: 12px;
  line-height: 1.7;
  color: var(--sd-text-2);
  margin-top: 3px;
}

/* ===================== 资料 · 基础资料管理页（商品分类 / 颜色尺码 / 供应商 / 门店仓库，.mst-*） =====================
   四个页共用同一套 .mst-* 类。原因：它们的交互结构完全一致——
   概览数字 → 页面说明 → 「未收录一键收编」提醒 → 表格 → 新建/编辑弹窗 → 删除守卫弹窗。
   共用一套样式才能保证四页观感一致；若各写一套，以后改一次配色要改四遍，
   必然出现「这页比那页旧一点」的割裂感。
   注：这些页的 t-table 必须显式传 :columns，否则「按行渲染的展示列」（如排序按钮列）会静默不渲染。 */
/* 页面容器：资料页是数据管理页，卡片内边距收紧一点，让表格更靠上、一屏看到更多条目 */
.mst-page .t-card__body {
  padding-top: 8px;
}
/* 双页签（颜色库 / 尺码组）：页签与下方内容之间留一点间距 */
.mst-tabs {
  margin-bottom: 4px;
}

/* 概览数字条 */
.mst-stats {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 10px;
  margin: 4px 0 12px;
}
.mst-stat {
  padding: 12px 14px;
  border-radius: var(--sd-radius);
  background: var(--sd-surface-2);
  border: 1px solid var(--sd-border);
}
/* 只有需要用户处理时才变黄：静静躺着的「0」不该抢注意力，否则四张黄卡会让人无从下手 */
.mst-stat-warn {
  background: #fff7e6;
  border-color: #ffe0a3;
}
.mst-stat-num {
  font-size: 20px;
  font-weight: 700;
  line-height: 1.2;
  color: var(--sd-text);
}
.mst-stat-warn .mst-stat-num {
  color: var(--sd-warning);
}
.mst-stat-lab {
  margin-top: 2px;
  font-size: 12px;
  color: var(--sd-text-3);
}

/* 页面说明：讲清「这页维护的数据会被谁用」，否则用户不知道为什么要维护它。
   边框四边统一 1px（原先左侧是 3px 品牌色竖条，按用户要求全站去掉 —— 见文件顶部「全站视觉禁令」）。 */
.mst-tip {
  margin-bottom: 12px;
  padding: 10px 12px;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-brand-a08);
  font-size: 12px;
  line-height: 1.7;
  color: var(--sd-text-2);
}

/* 「未收录值一键收编」提醒条：黄条 + 可点按钮 */
.mst-adopt {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 12px;
  padding: 12px 14px;
  border-radius: var(--sd-radius);
  background: #fff7e6;
  border: 1px solid #ffe0a3;
}
.mst-adopt .ip-icon,
.mst-adopt .ip-icon svg {
  width: 20px;
  height: 20px;
  display: block;
}
.mst-adopt .ip-icon {
  flex: none;
  color: var(--sd-warning);
}
.mst-adopt-body {
  flex: 1 1 320px;
  min-width: 0;
}
.mst-adopt-title {
  font-size: 13px;
  font-weight: 700;
  color: var(--sd-text);
}
.mst-adopt-desc {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 6px;
}

/* 表格内通用文本 */
.mst-table {
  font-size: 13px;
}
.mst-name {
  font-size: 14px;
  font-weight: 700;
  color: var(--sd-text);
}
/* 已停用：删除线 + 降色。只用透明度会让浅色主题下几乎看不见，
   而「这一行是停用的」正是用户扫列表时最需要立刻分辨的信息 */
.mst-off {
  color: var(--sd-text-3);
  font-weight: 400;
  text-decoration: line-through;
}
/* 编码 / 色值 / 电话等结构化文本：等宽字体，便于逐位校对 */
.mst-code {
  display: inline-block;
  padding: 0 5px;
  font-family: ui-monospace, Menlo, Consolas, "Courier New", monospace;
  font-size: 12px;
  color: var(--sd-text-2);
  background: var(--sd-surface-2);
  border: 1px solid var(--sd-border);
  border-radius: 4px;
}
.mst-muted {
  font-size: 12px;
  color: var(--sd-text-3);
  line-height: 1.5;
}

/* 排序列：序号 + 上移/下移 */
.mst-sort {
  display: flex;
  align-items: center;
  gap: 2px;
}
.mst-sort-no {
  width: 18px;
  text-align: center;
  font-family: ui-monospace, Menlo, Consolas, monospace;
  font-size: 12px;
  color: var(--sd-text-3);
}

/* 取色块（表格小尺寸 / 弹窗大尺寸）；未设色值用斜线灰块 + 问号，
   不能用白色代替——白色是一个真实颜色（「白」），会把「没填色值」显示成「这个颜色是白的」 */
.mst-swatch {
  display: inline-block;
  width: 16px;
  height: 16px;
  border-radius: 4px;
  border: 1px solid rgba(0, 0, 0, 0.16);
  box-sizing: border-box;
  vertical-align: -3px;
}
.mst-swatch-lg {
  width: 28px;
  height: 28px;
  border-radius: 6px;
  vertical-align: middle;
}
.mst-swatch-none {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  color: var(--sd-text-3);
  background-color: #eef1f5;
  background-image: repeating-linear-gradient(45deg, #cfd6e0 0 2px, transparent 2px 5px);
}

/* 尺码组弹窗里的实时预览：让用户当场看到「解析出的规格体系 + 行业排序结果」，
   而不是保存后才发现 28/30/XL 被排成了 XL/28/30 */
.mst-preview {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  margin-top: 8px;
  padding: 8px 10px;
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface-2);
  border: 1px dashed var(--sd-border);
}

/* 弹窗表单 */
.mst-form {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.mst-label {
  margin-bottom: 6px;
  font-size: 13px;
  font-weight: 600;
  color: var(--sd-text-2);
}
.mst-field {
  width: 100%;
  min-width: 0;
}
/* 控件统一撑满字段宽度：默认宽度随内容，短占位符（如「如 藏青」）会让框变窄，
   同一个弹窗里几个输入框宽度不一致，看起来像没对齐。
   这里按类名逐个列出而不是用 `> *`：TDesign 不同版本的输入/选择根类名不完全一致
   （t-input__wrap / t-input / t-select-input / t-select 都见过），列全了才不会漏；
   不存在的选择器只是不匹配，没有任何副作用。 */
.mst-field > .t-input__wrap,
.mst-field > .t-input,
.mst-field > .t-select,
.mst-field > .t-select-input,
.mst-field > .t-textarea,
.mst-field > .t-textarea__inner {
  width: 100%;
}
.mst-field > * {
  max-width: 100%;
}
/* 左文案 + 右控件的横排字段（启用开关等） */
.mst-field-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
/* 横排字段：文案侧可压缩（长说明要能换行），控件侧不被挤压（开关/按钮宽度固定） */
.mst-field-row > :first-child {
  flex: 1 1 auto;
  min-width: 0;
}
.mst-field-row > :last-child {
  flex: 0 0 auto;
}
.mst-req {
  color: var(--sd-danger);
}
.mst-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 4px;
}

/* 只读标签 chip（未收录值列表、尺码组内容预览）：与 .mat-chip 同观感但不可点击 */
.mst-chip {
  display: inline-flex;
  align-items: center;
  padding: 1px 8px;
  border-radius: 999px;
  font-size: 12px;
  line-height: 18px;
  color: var(--sd-text-2);
  background: var(--sd-surface);
  border: 1px solid var(--sd-border);
}
.mst-chip-sm {
  padding: 0 7px;
  line-height: 16px;
}

/* 窄屏：概览从 4 列降为 2 列，避免数字卡被挤成竖排单字 */
/* 窄屏：铺码面板的「颜色 / 尺码组」标签转为上下排，给 chip 留出整行宽度 */

/* ===== 开单页：双列大图商品网格（服装突出图片，价格/库存醒目） ===== */
/* 搜索框左侧扫描图标：表意「此框可扫码」，主色主题蓝 */
.scan-icon {
  color: var(--sd-brand);
}
.scan-icon svg {
  width: 16px;
  height: 16px;
  display: block;
}
/* 扫码加购成功后的结果提示条（浅绿底，短暂醒目） */
.scan-result {
  font-size: 12px;
  color: #1a9d70;
  background: #e8f7ef;
  border: 1px solid #bfe8d4;
  border-radius: 6px;
  padding: 6px 10px;
  margin: -4px 0 10px;
}
/* 双列网格容器：左栏约 2/3 宽内排两列，窄屏自动收为一列 */
.pos-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 14px;
}
/* 单张商品卡片：白底圆角 + 悬停高亮；横向结构 = 左图 + 右侧全部信息 */
.pos-card {
  display: flex;
  flex-direction: row;
  background: var(--sd-surface);
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius);
  overflow: hidden;
  transition: box-shadow 0.15s ease, border-color 0.15s ease;
  cursor: default;
}
.pos-card:hover {
  box-shadow: var(--sd-shadow-hover);
  border-color: var(--sd-brand);
}
/* 图片区：固定宽度靠左，高度随右侧信息自动撑满（不设 aspect-ratio，避免竖版大图占位过高） */
.pos-img-wrap {
  position: relative;
  flex: none;
  width: 112px;
  min-height: 104px;
  background: var(--sd-surface-2);
  overflow: hidden;
}
.pos-img {
  width: 100%;
  height: 100%;
  object-fit: contain; /* 缩略图为带文字的方形 SVG，contain 不裁字；真实照片可改 cover */
  display: block;
  position: absolute;
  inset: 0;
}
/* 无图占位：居中首字，观感统一 */
.pos-img-ph {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 32px;
  font-weight: 700;
  color: var(--sd-text-3);
}
/* 售罄角标：覆盖在图片右上角 */
.pos-badge {
  position: absolute;
  top: 8px;
  right: 8px;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 600;
  color: #fff;
}
.pos-badge-out {
  background: rgba(227, 93, 93, 0.92);
}
/* 信息区：位于图片右侧，占满剩余宽度（min-width:0 防长文本撑破网格） */
.pos-body {
  flex: 1;
  min-width: 0;
  padding: 12px 14px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.pos-name {
  font-size: 15px;
  font-weight: 700;
  color: var(--sd-text);
  line-height: 1.4;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* 货号 + 价格：一行两端，价格醒目 */
.pos-meta {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
}
.pos-sku {
  font-size: 12px;
  color: var(--sd-text-3);
}
.pos-price {
  font-size: 18px;
  font-weight: 800;
  color: var(--sd-danger);
}
/* 色码标签：色码 + 库存，紧凑排列、可点加购 */
.pos-variants {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
/* 单个颜色分组：颜色名 + 该色下的一排尺码 */
.pos-color-group {
  display: flex;
  align-items: flex-start;
  gap: 8px;
}
/* 颜色标签：固定在左侧，与右侧尺码按钮对齐 */
.pos-color-tag {
  flex: none;
  min-width: 34px;
  padding: 4px 0;
  font-size: 12px;
  font-weight: 600;
  color: var(--sd-text-2);
  line-height: 1.4;
}
/* 该颜色下的尺码按钮横向排列 */
.pos-color-sizes {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.pos-variant {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface);
  font-size: 12px;
  color: var(--sd-text-2);
  cursor: pointer;
  transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
}
.pos-variant:hover:not(:disabled) {
  border-color: var(--sd-brand);
  color: var(--sd-brand);
}
.pv-label {
  font-weight: 600;
  color: var(--sd-text);
}
/* 库存数字：正常用主题色醒目，低库存转警示色，缺货置灰禁用 */
.pv-stock {
  font-weight: 700;
  color: var(--sd-brand);
}
.pos-variant.is-low .pv-stock {
  color: var(--sd-warning);
}
.pos-variant.is-out {
  opacity: 0.45;
  cursor: not-allowed;
}
.pos-variant.is-out .pv-stock {
  color: var(--sd-text-3);
}

/* 窄屏：双列收为一列（开单左栏较窄时避免卡片过挤） */

/* ===== 识别会员 · VIP 高级感（金色渐变按钮 + 等级卡片） ===== */
/* 未识别会员时的金色渐变 VIP 按钮（皇冠 + 主副文案） */
.vip-btn {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 10px 14px;
  border: none;
  border-radius: 10px;
  cursor: pointer;
  color: #fff;
  text-align: left;
  /* 金色渐变：亮金 → 深金，营造 VIP 尊贵质感 */
  background: linear-gradient(120deg, #f6d365 0%, #d4af37 45%, #b8860b 100%);
  box-shadow: 0 2px 10px rgba(212, 175, 55, 0.35);
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.vip-btn:hover {
  transform: translateY(-1px);
  box-shadow: 0 4px 16px rgba(212, 175, 55, 0.5);
}
.vip-btn:active {
  transform: translateY(0);
}
.vip-btn-icon {
  display: inline-flex;
  width: 22px;
  height: 22px;
  flex: none;
}
.vip-btn-icon svg {
  width: 100%;
  height: 100%;
}
.vip-btn-text {
  font-size: 15px;
  font-weight: 800;
  letter-spacing: 0.5px;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
}
.vip-btn-sub {
  margin-left: auto;
  font-size: 11px;
  opacity: 0.9;
}

/* ===== 开单页会员录入区（右栏顶部：手机号识别 / 非会员自动开卡） ===== */
.member-enroll {
  margin-bottom: 12px;
}
/* 手机号输入 + 识别/开卡 + 会员列表 按钮 一行排布（窄栏自动换行） */
/* ⚠️ .member-enroll-row / .member-enroll-tip 里的行布局已不再用于购物篮卡片：
   那行「手机号输入 + 识别 / 开卡 + 列表」按用户要求移进「识别会员」弹窗了
   （卡片上不再有搜索框）。行样式本身被 .member-dialog-enroll 取代，故删除以免留下死样式。 */

/* ===== 散客状态卡（购物篮卡片顶部，未选顾客时显示）=====
   与 .vip-card 同一套排版（图标 + 主体 + 右侧操作），但用**中性底色**：
   它是"当前没有会员"的常态显示，用等级色/醒目色会让人以为出了什么问题。
   为什么要做成一整张卡而不是留空：留空会让人分不清"系统按散客算"
   还是"上次那个会员还在生效" —— 后者会改变价格与积分归属，且当场看不出来。 */
.walkin-card {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border-radius: 10px;
  border: 1px solid var(--sd-border);
  background: var(--sd-surface-2);
}
.walkin-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  flex: none;
  color: var(--sd-text-3);
}
.walkin-body {
  flex: 1;
  min-width: 0;
}
/* 权重刻意低于 .vip-name（800）：散客不是"重要信息"，不该跟会员名抢注意力 */
.walkin-name {
  font-size: 14px;
  font-weight: 500;
  color: var(--sd-text);
}
.walkin-tip {
  margin-top: 2px;
  font-size: 11px;
  line-height: 1.5;
  color: var(--sd-text-3);
}
/* 弹窗里的「手机号识别 / 开卡」一行（原在购物篮卡片上，按用户要求移进弹窗） */
.member-dialog-enroll {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 12px;
}
.member-enroll-row .t-input__wrap,
.member-enroll-row .t-input {
  flex: 1;
  min-width: 130px;
}
.member-enroll-tip {
  margin-top: 6px;
  font-size: 12px;
  color: var(--sd-text-3);
  line-height: 1.5;
}
/* 开卡成功提示（浅绿底，与扫码结果提示同一视觉语言） */
.member-enroll-hint {
  margin-top: 8px;
  font-size: 12px;
  color: #1a9d70;
  background: #e8f7ef;
  border: 1px solid #bfe8d4;
  border-radius: 6px;
  padding: 6px 10px;
  line-height: 1.5;
}

/* ==========================================================================
   购物篮卡片的「VIP 视觉」（识别到会员时才生效）
   ==========================================================================
   需求原话：「如果是会员，要有 vip 视觉效果」（用户选中的是「② 会员 / 购物篮」标题）。
   做法：沿用已有的等级色体系（MemberTiers.TIER_COLORS 由组件注入 --vip-color：
   普通灰 / 银 / 金 / 钻石紫），**不新造色值**，三处一起让它"看得出来是会员在结算"：
     ① 卡片描边换成等级色 + 一圈同色柔光；
     ② 卡片头部一层同色淡渐变（从左往右淡出，像一道光带）；
     ③ 头部图标由篮子换成皇冠 / 钻石并染上等级色（组件里换的，见 app.js）。
   ⚠️ ②③ 里"换图标"是不依赖颜色的信号 —— 普通会员的等级色是灰的（#909399），
      只靠颜色的话那档会员看起来跟散客没差别。
   ========================================================================== */
.pos-basket-vip {
  /* !important：TDesign 的 .t-card--bordered 会设边框色，两边同为单类选择器，靠加载顺序不可靠 */
  border-color: var(--vip-color) !important;
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--vip-color) 22%, transparent);
}
.pos-basket-vip .t-card__header {
  background: linear-gradient(90deg, color-mix(in srgb, var(--vip-color) 16%, transparent) 0%, transparent 72%);
  border-bottom-color: color-mix(in srgb, var(--vip-color) 30%, transparent);
}
/* 头部标题行：图标 + 文字在同一条基线上，图标换色后间距不跳 */
.pos-basket-head {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

/* ==========================================================================
   购物篮「图」列：把上下内边距收紧，让长方形缩略图真正铺满行高
   --------------------------------------------------------------------------
   背景：用户反馈「这个图片这里不用正方形，可以用长方形铺满，现在上下都是有空间的」。
   改动前的实测：缩略图 36×36（含 1px 边框），所在行高 60px → 上下各空 12px。
   修法分两步，缺一不可：
     ① 缩略图改成竖向长方形（在 app.js 的 thumb-zoom 上传 :height="56"）；
     ② 本规则把该单元格上下内边距收到 2px —— 不改这一步，长方形图放进上下各 12px 的
        内边距里，留白依旧存在（只是从"图上下空"变成"行被顶高"，观感更差）。
   改后实测：缩略图 36×58、行高 62px、上下留白 4px（原 24px）。
   ⚠️ 行高从 60 微涨到 62 是缩略图 border 计入 content-box 所致（传 56 实渲 58），
      差 2px 可忽略；重点是留白从 24px 降到 4px。
   ⚠️ 选择器限定在 `.pos-basket` 内，只影响开单页购物篮这一张表，
      不碰库存明细 / 拿货 / 入库那几处方形缩略图列
      （资料页的「商品信息」列表已按同一套做法单独处理，见下面紧接着的那条规则）。
   ⚠️⚠️ 这里**不能写 `td:first-child`**（2026-09-16 修）：
      表格最左列已统一前置「序号」（src/tableSeq.js），首格不再是缩略图格 ——
      写成 first-child 会把 2px 内边距加到**序号格**上，缩略图格退回默认的 8px →
      行高 62 → 74px、缩略图上下各多出 8px 空白（用户早先提过的老问题原样复发）。
      改成 `:has(.style-thumb-sm)` 按"这一格里装着购物篮缩略图"来定位，列顺序再变也不会加错。
   ========================================================================== */
.pos-basket .t-table__body tr > td:has(.style-thumb-sm) {
  padding-top: 2px;
  padding-bottom: 2px;
}

/* ==========================================================================
   资料 → 商品信息 列表的缩略图格：与购物篮**同一套做法**（宽不变、改竖向长条、内边距收到 2px）

   用户原话：「这里应该是，长条图，目前我发现你是方形图，导致上下没有占满」。
   改前实测：该表行高 61px、缩略图 34×34（实渲 36×36）→ 上下各空 12~13px，
   4 行看过去都是"方图浮在行中间"。改后实测：图 36×57、上下留白 2+3px、行高 61 → **62px**。

   ⚠️ 这一行的行高**就是"缩略图 + 内边距"撑出来的**（同排的 tag/文字都比它矮），
      所以两件事必须一起做，只做一件反而更糟：
        · 只把图拉长而不收内边距 → 行高被顶高到 80+，整页变松；
        · 只收内边距而不拉长图 → 行高缩到 40 左右，图和文字挤在一起。
   ⚠️ 图高取 55 是**算出来的**，不是试出来的：
        行高 61 = 图高(55) + 上下 border(2) + 上下内边距(2 + 2) → 55 + 2 + 4 = 61。
      ⚠️ 但**实测结果是 62 而不是 61**（+1px）—— 图片 border 计入 content-box 导致的取整差，
         与购物篮当年"传 56 实渲 58、行高 60→62"是同一现象。4 行共 +4px，可忽略；
         写在这里是为了别让下一个人对着"应该正好 61"去反复调数字。
      （购物篮那张表传的是 56，因为它行高本来就是 62；两张表数字不同是行高不同，不是口径不一致。）
   ⚠️ 选择器限定在 `.mat-main`（商品信息列表的容器）内：
      库存明细 / 拿货 / 入库那几处仍是方形缩略图，别顺手把这条规则提到全局
      —— `.style-thumb-sm` 是它们共用的类名，提到全局会一起改掉。
   ========================================================================== */
.mat-main .t-table__body tr > td:has(.style-thumb-sm) {
  padding-top: 2px;
  padding-bottom: 2px;
}

/* 已识别会员的等级卡片（等级色渐变 + 皇冠/钻石图标 + 会员名/等级/积分） */
.vip-card {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border-radius: 10px;
  color: #fff;
  /* --vip-color 由组件按会员等级注入（普通灰/银/金/钻石紫），背景做同色系渐变 */
  background: linear-gradient(120deg, var(--vip-color) 0%, var(--vip-color) 100%);
  box-shadow: 0 2px 10px rgba(22, 43, 88, 0.12);
}
.vip-icon {
  display: inline-flex;
  width: 24px;
  height: 24px;
  flex: none;
}
.vip-icon svg {
  width: 100%;
  height: 100%;
}
.vip-body {
  flex: 1;
  min-width: 0;
}
.vip-name {
  font-size: 14px;
  font-weight: 800;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.vip-tier {
  font-size: 11px;
  opacity: 0.92;
  margin-top: 2px;
}
/* 会员选择弹窗里的等级徽章（皇冠 + 等级色） */
.vip-tier-tag {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 600;
  color: var(--vip-color);
  background: rgba(255, 255, 255, 0.6);
  border: 1px solid currentColor;
}
.vip-tier-tag .ip-icon {
  width: 13px;
  height: 13px;
}
.vip-tier-tag .ip-icon svg {
  width: 100%;
  height: 100%;
}

/* ===== 设置中心（单号规则设置 / 门店信息） ===== */
.set-tip {
  font-size: 13px;
  color: var(--sd-text-3);
  margin-bottom: 12px;
}
.set-preview {
  font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
  font-size: 13px;
  font-weight: 600;
  color: var(--sd-brand);
  background: var(--sd-surface-2);
  border: 1px dashed var(--sd-border);
  border-radius: 6px;
  padding: 2px 8px;
}

/* ===== 设置中心：门店 LOGO 上传 ===== */
.logo-upload {
  display: flex;
  align-items: center;
  gap: 14px;
}
.logo-preview {
  width: 72px;
  height: 72px;
  border-radius: 10px;
  overflow: hidden;
  border: 1px solid var(--sd-border);
  background: var(--sd-surface-2);
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
}
.logo-preview img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.logo-preview.empty .logo-placeholder {
  font-size: 12px;
  color: var(--sd-text-3);
  font-weight: 600;
  letter-spacing: 1px;
}
.logo-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}
.logo-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 6px 16px;
  border: 1px solid var(--sd-brand);
  border-radius: 6px;
  color: var(--sd-brand);
  font-size: 13px;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}
.logo-btn:hover {
  background: var(--sd-brand-soft);
}
.logo-rule {
  margin-top: 8px;
  font-size: 12px;
  color: var(--sd-text-3);
  line-height: 1.5;
}

/* ===== 会员列表（一个客户一行，精简展示） ===== */
.member-cell {
  display: flex;
  align-items: center;
  gap: 10px;
}
.member-avatar-sm {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  font-weight: 700;
  font-size: 14px;
  flex: none;
}
.member-cell-name {
  font-weight: 600;
  color: var(--sd-text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.member-tier-sub {
  font-size: 11px;
  color: var(--sd-text-3);
  margin-top: 4px;
}

/* ===== 表格列设置弹窗（拖拽排序 + 眼睛显隐） ===== */
.colset-tip {
  font-size: 13px;
  color: var(--sd-text-3);
  margin-bottom: 10px;
}
.colset-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-height: 420px;
  overflow-y: auto;
}
.colset-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border: 1px solid var(--sd-border);
  border-radius: 8px;
  background: var(--sd-surface);
  cursor: grab;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.colset-row:hover {
  border-color: var(--sd-brand);
}
.colset-row.colset-dragging {
  opacity: 0.5;
  border-style: dashed;
}
.colset-handle {
  display: inline-flex;
  width: 16px;
  height: 16px;
  flex: none;
  color: var(--sd-text-3);
  cursor: grab;
}
.colset-handle svg {
  width: 100%;
  height: 100%;
}
.colset-name {
  flex: 1;
  font-size: 14px;
  color: var(--sd-text);
}
.colset-actions {
  display: flex;
  align-items: center;
  gap: 4px;
  flex: none;
}
.colset-arrow {
  width: 24px;
  height: 24px;
  border: 1px solid var(--sd-border);
  border-radius: 5px;
  background: var(--sd-surface);
  color: var(--sd-text-2);
  cursor: pointer;
  font-size: 12px;
  line-height: 1;
}
.colset-arrow:hover:not(:disabled) {
  border-color: var(--sd-brand);
  color: var(--sd-brand);
}
.colset-arrow:disabled {
  opacity: 0.35;
  cursor: not-allowed;
}
.colset-eye {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 24px;
  border: none;
  background: transparent;
  color: var(--sd-brand);
  cursor: pointer;
}
.colset-eye.off {
  color: var(--sd-text-3);
}
.colset-eye .ip-icon {
  width: 18px;
  height: 18px;
}
.colset-eye .ip-icon svg {
  width: 100%;
  height: 100%;
}

/* ===== 登录 / 开店注册页（未登录时的全屏门禁） ===== */
/* ===== 登录门禁 =====
 * 版式参考「网上管家婆」登录页：品牌区 → 欢迎语 → 双 Tab → 填充式输入框 → 主按钮 → 辅助入口。
 * 落色仍走本项目的 --sd-* 设计变量，避免引入第二套色板。
 */
.auth-wrap {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  /* 顶部一条品牌色带 + 下方留白：既有品牌感，又不会用大面积深色压过表单 */
  background: linear-gradient(180deg, var(--sd-brand) 0%, var(--sd-brand-strong, #1f53d6) 46%, var(--sd-bg) 46%, var(--sd-bg) 100%);
}
.auth-card {
  width: 100%;
  max-width: 420px;
  background: var(--sd-surface);
  border-radius: 16px;
  box-shadow: 0 18px 48px rgba(22, 43, 88, 0.18);
  padding: 30px 32px 22px;
}

/* --- 品牌区 --- */
.auth-brand-row {
  display: flex;
  align-items: center;
  gap: 10px;
}
.auth-logo {
  width: 40px;
  height: 40px;
  flex: 0 0 40px;
  border-radius: 10px;
  background: var(--sd-brand-gradient);
  color: #fff;
  font-size: 15px;
  font-weight: 800;
  letter-spacing: 0.5px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.auth-brand {
  font-size: 18px;
  font-weight: 800;
  color: var(--sd-text);
  letter-spacing: 0.4px;
  line-height: 1.25;
}
.auth-brand-tag {
  font-size: 12px;
  font-weight: 500;
  color: var(--sd-text-3);
}

/* --- 欢迎语 --- */
.auth-hello {
  margin-top: 22px;
  font-size: 21px;
  font-weight: 700;
  color: var(--sd-text);
  letter-spacing: 0.3px;
}
.auth-sub {
  margin-top: 6px;
  font-size: 13px;
  color: var(--sd-text-3);
  line-height: 1.6;
}

/* --- 双 Tab（登录 / 开店注册）：下划线式切换 --- */
.auth-tabs {
  display: flex;
  gap: 26px;
  margin: 16px 0 18px;
  border-bottom: 1px solid var(--sd-border);
}
.auth-tab {
  position: relative;
  padding: 0 2px 10px;
  font-size: 15px;
  font-weight: 600;
  color: var(--sd-text-3);
  cursor: pointer;
  user-select: none;
  transition: color 0.18s;
}
.auth-tab:hover { color: var(--sd-text-2); }
.auth-tab.is-active { color: var(--sd-brand); }
.auth-tab.is-active::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 2px;
  border-radius: 2px;
  background: var(--sd-brand);
}

/* --- 环境提示（本地存储被禁用）：必须让用户看见，否则只会觉得「登录莫名其妙丢了」 --- */
.auth-alert {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin-bottom: 14px;
  padding: 10px 12px;
  border-radius: var(--sd-radius-sm);
  background: #fff8ec;
  border: 1px solid #ffe3b8;
  color: #8a5a12;
  font-size: 12px;
  line-height: 1.65;
}
.auth-alert b { color: #6b4409; }
.auth-alert .ip-icon { flex: 0 0 auto; margin-top: 1px; font-size: 15px; }
/* 信息态（蓝）：用于「静态预览模式」这类**环境说明**，与上方的黄色告警区分开 —— 
   黄色 = 你的环境有问题需要处理；蓝色 = 这是正常可用状态，只是没连服务端。 */
.auth-alert.is-info {
  background: var(--sd-brand-soft);
  border-color: var(--sd-brand-a12, rgba(74, 144, 226, 0.12));
  color: var(--sd-brand-strong);
}
.auth-alert.is-info b { color: var(--sd-brand-strong); }

/* --- 填充式输入框：浅底无边框，聚焦转白 + 蓝色描边（对齐参考图） --- */
.auth-card .t-input {
  height: 46px;
  margin-bottom: 14px;
  padding: 0 14px;
  background: var(--sd-surface-2);
  border: 1px solid transparent;
  border-radius: var(--sd-radius-sm);
  transition: background 0.18s, border-color 0.18s, box-shadow 0.18s;
}
.auth-card .t-input:hover { background: #f2f5fa; }
.auth-card .t-input.t-input--focused {
  background: var(--sd-surface);
  border-color: var(--sd-brand);
  box-shadow: 0 0 0 3px var(--sd-brand-a12, rgba(74, 144, 226, 0.12));
}
.auth-card .t-input__inner {
  background: transparent;
  font-size: 14px;
  color: var(--sd-text);
}
.auth-card .t-input__inner::placeholder { color: var(--sd-text-3); }

/* 密码可见性切换：由 TDesign 内置渲染（type=password 时自动出现 .t-input__suffix-icon）。
 * 不再自绘——自绘会与内置图标并排显示两个「眼睛」。此处只统一配色。 */
.auth-card .t-input__suffix-icon {
  color: var(--sd-text-3);
  cursor: pointer;
}
.auth-card .t-input__suffix-icon:hover { color: var(--sd-brand); }

/* --- 记住我 / 忘记密码 --- */
.auth-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin: 2px 0 18px;
}
.auth-remember {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 13px;
  color: var(--sd-text-2);
  cursor: pointer;
  user-select: none;
}
.auth-remember input[type="checkbox"] {
  width: 15px;
  height: 15px;
  margin: 0;
  accent-color: var(--sd-brand);
  cursor: pointer;
}
.auth-link {
  font-size: 13px;
  color: var(--sd-brand);
  cursor: pointer;
  font-weight: 600;
}
.auth-link:hover { text-decoration: underline; }

/* --- 主按钮 --- */
.auth-submit {
  height: 46px !important;
  font-size: 15px !important;
  font-weight: 600 !important;
  border-radius: var(--sd-radius-sm) !important;
  letter-spacing: 2px;
}

/* --- 页脚 --- */
.auth-foot {
  margin-top: 16px;
  font-size: 12px;
  color: var(--sd-text-3);
  text-align: center;
  line-height: 1.6;
}
.auth-foot a {
  color: var(--sd-brand);
  cursor: pointer;
  font-weight: 600;
}
.auth-dot { margin: 0 6px; color: var(--sd-border); }

/* --- 忘记密码弹窗 --- */
.forgot-body p { margin: 0 0 14px; font-size: 13px; line-height: 1.7; color: var(--sd-text-2); }
.forgot-item {
  padding: 11px 13px;
  margin-bottom: 10px;
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface-2);
  border: 1px solid var(--sd-border);
}
.forgot-title { font-size: 13px; font-weight: 700; color: var(--sd-text); margin-bottom: 4px; }
.forgot-desc { font-size: 12px; line-height: 1.7; color: var(--sd-text-2); }
.forgot-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 16px; }

/* 顶栏：当前登录身份胶囊（= 账号菜单入口，配合深色顶栏用半透明白） */
.topbar .user-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: rgba(255, 255, 255, 0.18);
  border-radius: 999px;
  padding: 6px 14px;
  font-size: 13px;
  color: #fff;
  /* 可点击 —— 它现在是账号菜单的触发器，不是纯展示标签 */
  cursor: pointer;
  user-select: none;
  transition: background 0.15s ease;
}
.topbar .user-pill:hover {
  background: rgba(255, 255, 255, 0.3);
}
/* 「这里能展开」的可视信号。
   为什么不用图标：展开指示是个纯几何小三角，用 CSS 画比再引一个图标更省，
   也不会因为图标库换版而变形。它是**不依赖颜色**的信号（白色三角在任何主题色下都在）。 */
.topbar .user-pill::after {
  content: "";
  width: 0;
  height: 0;
  margin-left: 2px;
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
  border-top: 5px solid rgba(255, 255, 255, 0.85);
}
.topbar .user-pill .user-role {
  font-size: 11px;
  padding: 1px 6px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.28);
}

/* ===== 账号下拉菜单（顶栏头像点开）=====
   浮层本体由 t-popup 负责定位/层级，TDesign 的 overlay 自带白底与阴影，
   这里只负责内部菜单的排版，避免和组件默认样式打架。 */
.acct-menu {
  min-width: 168px;
  padding: 4px;
  /* 消掉 TDesign overlay 自带的内边距差，让菜单项能贴满整行（点了才顺手） */
  margin: -4px;
}
.acct-item {
  display: flex;
  align-items: center;
  width: 100%;
  padding: 8px 10px;
  border: 0;
  border-radius: var(--sd-radius-sm);
  background: transparent;
  font-size: 13px;
  color: var(--sd-text);
  text-align: left;
  cursor: pointer;
  transition: background 0.12s ease;
}
.acct-item:hover {
  background: var(--sd-surface-2);
}
/* 图标在菜单项里统一 16px：.ip-icon 默认 18px，菜单里略大、显得挤 */
.acct-item .ip-icon svg {
  width: 16px;
  height: 16px;
}
/* 退出账户：与上面三项之间加一条分隔线，并用警示色 —— 它是不可逆的破坏性动作 */
.acct-item-danger {
  margin-top: 4px;
  border-top: 1px solid var(--sd-border);
  border-radius: 0 0 var(--sd-radius-sm) var(--sd-radius-sm);
  padding-top: 9px;
  color: var(--sd-danger);
}
.acct-item-danger:hover {
  background: color-mix(in srgb, var(--sd-danger) 8%, transparent);
}

/* ===== 门店切换下拉（顶栏「门店」标识 → 老板切店）=====
   复用账号菜单的观感，但语义不同：这里是"切换当前工作门店"，不是账号动作。 */
.store-pill-trigger {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  transition: background 0.15s ease;
}
.store-pill-trigger:hover {
  background: rgba(255, 255, 255, 0.3);
}
.store-pill-trigger .store-caret {
  opacity: 0.75;
}
.store-pill-trigger .store-caret svg {
  width: 12px;
  height: 12px;
}
.store-menu {
  min-width: 180px;
  padding: 4px;
  margin: -4px;
}
.store-item {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 8px 10px;
  border: 0;
  border-radius: var(--sd-radius-sm);
  background: transparent;
  font-size: 13px;
  color: var(--sd-text);
  text-align: left;
  cursor: pointer;
  transition: background 0.12s ease;
}
.store-item:hover {
  background: var(--sd-surface-2);
}
.store-item .ip-icon svg {
  width: 16px;
  height: 16px;
}
.store-item-name {
  flex: 1;
  text-align: left;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* 当前门店：更深的品牌浅底 + 加粗 + 尾部对勾，一眼可辨"现在在哪家店"（与项目"选中态"口径一致） */
.store-item-on {
  background: var(--sd-brand-a08);
  font-weight: 600;
}
.store-item-on:hover {
  background: var(--sd-brand-a12);
}
.store-item-on .store-check {
  color: var(--sd-brand);
}
.store-check svg {
  width: 14px;
  height: 14px;
}
.store-divider {
  height: 1px;
  margin: 4px 6px;
  background: var(--sd-border);
}
.store-item-new {
  color: var(--sd-brand);
}

/* ===== 账号菜单三个弹窗（编辑资料 / 意见反馈 / 下载电脑软件）内的排版 =====
   放在一起是因为它们都是"标签 + 值"的只读/可编辑混排，共用同一套间距。 */
.acct-form {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.acct-row {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 24px;
}
/* 纵向行：控件在上、说明文字在下。
   ⚠️ 必须显式改 column —— 说明文字与控件并列写在 flex 行里时，
   最长的那条会把控件挤变形（本项目在打印通道单选组上踩过同一个坑）。 */
.acct-row-block {
  flex-direction: column;
  align-items: stretch;
  gap: 6px;
}
.acct-row-label {
  flex: none;
  width: 72px;
  font-size: 13px;
  color: var(--sd-text-2);
}
/* 纵向行里的标签不需要固定宽度（它自己占一整行） */
.acct-row-block .acct-row-label {
  width: auto;
}
.acct-row-value {
  font-size: 13px;
  color: var(--sd-text);
  word-break: break-all; /* 长账号/店铺名不要撑破弹窗 */
}
.acct-row-hint {
  font-size: 12px;
  line-height: 1.7;
  color: var(--sd-text-3);
}
/* 字数统计靠右，与输入框右边缘对齐 */
.acct-row-count {
  text-align: right;
}

/* ===== 财务 → 收款账户设置 ===== */
/* 账户摘要卡：四栏网格，窄屏收为两栏 */
.acct-summary {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 12px;
  margin-bottom: 16px;
}
.acct-card {
  background: var(--td-bg-color-page, #f3f3f3);
  border: 1px solid var(--td-component-border, #e7e7e7);
  border-radius: 8px;
  padding: 12px 14px;
  min-width: 0;
}
.acct-card.hl {
  border-color: var(--td-brand-color, #2b6cff);
  background: var(--sd-brand-a08, rgba(74, 144, 226, 0.08));
}
.acct-card-label {
  font-size: 12px;
  color: var(--td-text-color-secondary, #888);
}
.acct-card-num {
  font-size: 20px;
  font-weight: 600;
  margin: 4px 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.acct-card-num.in { color: #2ba471; }
.acct-card-sub {
  font-size: 12px;
  color: var(--td-text-color-placeholder, #aaa);
}
/* 表格内账户名：类型图标 + 名称 */
.acct-name {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
/* 空值占位（未填开户人/开户行等） */
.acct-muted {
  color: var(--td-text-color-placeholder, #bbb);
}
/* 弹窗字段的「输入框 + 下方小字提示」纵向容器。
   为什么必须这样包（这是布局缺陷的修复，不是美化）：
     TDesign 的 t-form-item 内容区 .t-form__controls-content 是 display:flex + flex-wrap:nowrap。
     若把 t-input 与小字提示作为**并列**子元素直接塞进 t-form-item，两者会被排到同一行：
     实测提示文字被挤到输入框右侧并压成两行（44px 高），输入框只剩 194px 宽。
     包一层 column 容器后，输入框恢复整行宽度，提示落在其正下方。 */
.acct-field-col {
  display: flex;
  flex-direction: column;
  gap: 4px;
  width: 100%;
}
/* 弹窗字段下方的小字提示（间距由上面的容器 gap 提供，本类不再自带 margin-top：
   同一类还被用在「启用」行 —— switch 与短提示同处一行，那里不需要上方间距） */
.acct-tip {
  font-size: 12px;
  color: var(--td-text-color-placeholder, #909399);
}
/* 窄屏：摘要卡两栏，避免挤压 */

/* ==========================================================================
   员工提成（员工提成页 + 提成设置抽屉）
   说明：全部颜色走 TDesign CSS 变量，浅色/深色主题自动适配。
   ========================================================================== */

/* 卡片头部：左侧标题 + 右侧操作按钮（提成设置入口） */
.emp-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
}
.emp-header-title {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-weight: 600;
}
/* 头部右侧的操作按钮组：两个入口（提成设置 / 员工激励活动）必须**成组靠右**。
   ⚠️ 不要把它们作为 .emp-header 的并列子元素 —— 该头部是 space-between，
      三个子元素时中间那个会被撑到正中（实测「提成设置」飘在 header 中间）。 */
.emp-header-actions {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  flex: none;
}

/* 顶部摘要卡（合计提成 / 计提业绩 / 已套用方案） */
.comm-summary {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
  margin-bottom: 16px;
}
.comm-stat {
  background: var(--td-bg-color-page, #f3f3f3);
  border: 1px solid var(--td-component-border, #e7e7e7);
  border-radius: 8px;
  padding: 12px 14px;
  min-width: 0;
}
.comm-stat-label {
  font-size: 12px;
  color: var(--td-text-color-secondary, #888);
}
.comm-stat-num {
  font-size: 20px;
  font-weight: 600;
  margin: 4px 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* 提成金额用红色强调（中式财务习惯：收入/正向金额标红） */
.comm-stat-num.is-in { color: #e34d59; }
.comm-stat-sub {
  font-size: 12px;
  color: var(--td-text-color-placeholder, #aaa);
}
/* 表格内的提成金额 */
.comm-amount {
  font-weight: 600;
  color: #e34d59;
}

/* 提成设置抽屉的工具栏与模板卡片 */
.comm-toolbar {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 12px;
  flex-wrap: wrap;
}
.comm-toolbar-tip {
  font-size: 12px;
  color: var(--td-text-color-placeholder, #909399);
}
.comm-group { margin-bottom: 20px; }
.comm-group-title {
  font-size: 14px;
  font-weight: 600;
  margin-bottom: 10px;
  display: flex;
  align-items: center;
  gap: 8px;
}
.comm-group-sub {
  font-size: 12px;
  font-weight: 400;
  color: var(--td-text-color-placeholder, #999);
}
.comm-card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 12px;
}
.comm-card {
  background: var(--td-bg-color-container, #fff);
  border: 1px solid var(--td-component-border, #e7e7e7);
  border-radius: 8px;
  padding: 12px 14px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
}
.comm-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.comm-card-name {
  font-weight: 600;
  font-size: 14px;
}
.comm-card-remark {
  font-size: 12px;
  color: var(--td-text-color-secondary, #666);
  line-height: 1.5;
}
.comm-card-rules {
  font-size: 12px;
  color: var(--td-text-color-placeholder, #909399);
  line-height: 1.5;
  border-top: 1px dashed var(--td-component-border, #eee);
  padding-top: 6px;
}
.comm-card-foot {
  display: flex;
  align-items: center;
  gap: 4px;
  margin-top: auto;
}
.comm-empty {
  font-size: 12px;
  color: var(--td-text-color-placeholder, #999);
  padding: 10px 0;
}
.comm-muted { color: var(--td-text-color-placeholder, #aaa); }
.comm-tag {
  font-size: 12px;
  color: var(--td-text-color-secondary, #666);
}

/* 规则编辑区（模板弹窗 / 指派弹窗共用） */
.comm-rule-list {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.comm-rule {
  border: 1px solid var(--td-component-border, #e7e7e7);
  border-radius: 8px;
  padding: 10px 12px;
  background: var(--td-bg-color-page, #fafafa);
}
.comm-rule-head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 8px;
  flex-wrap: wrap;
}
.comm-rule-idx {
  font-size: 12px;
  font-weight: 600;
  color: var(--td-brand-color, #2b6cff);
}
.comm-rule-type {
  font-size: 13px;
  font-weight: 600;
}
.comm-rule-sum {
  font-size: 12px;
  color: var(--td-text-color-placeholder, #909399);
  margin-right: auto;
}
.comm-rule-body {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.comm-field-row {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.comm-field-label {
  font-size: 12px;
  color: var(--td-text-color-secondary, #888);
  min-width: 78px;
}
.comm-unit {
  font-size: 12px;
  color: var(--td-text-color-secondary, #888);
}
.comm-tip {
  font-size: 12px;
  color: var(--td-text-color-placeholder, #909399);
  line-height: 1.5;
}
.comm-add-rule {
  display: flex;
  align-items: center;
  gap: 8px;
  padding-top: 4px;
}

/* 提成明细弹窗 */
.comm-detail-head {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 12px;
  margin-bottom: 14px;
}
.comm-detail-value {
  font-size: 15px;
  font-weight: 600;
  margin-top: 2px;
}
.comm-detail-value.is-in { color: #e34d59; }
.comm-detail-foot {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  margin-top: 12px;
  font-size: 13px;
  color: var(--td-text-color-secondary, #666);
}
.comm-detail-total {
  margin-left: auto;
  font-size: 15px;
  font-weight: 600;
  color: #e34d59;
}

/* 窄屏自适应 */

/* 提成明细底部：把公式摊开（基础提成 ＋ 活动激励 ＝ 规则合计 → 保底/封顶 → 最终） */
.comm-detail-plus {
  color: var(--sd-text-3);
  font-weight: 600;
}
.comm-detail-act { color: var(--sd-warning); }
/* 提成榜单里「活动激励」列：有活动时标红加号，提示这笔钱是活动发的 */
.comm-amount.is-act { color: var(--sd-warning); font-weight: 600; }

/* 卡片头部按钮上的角标：已开启的激励活动数（提醒老板「有活动在花钱」） */
.emp-btn-badge {
  display: inline-block;
  min-width: 16px;
  height: 16px;
  margin-left: 6px;
  padding: 0 4px;
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.28);
  font-size: 11px;
  line-height: 16px;
  text-align: center;
}

/* ---------- 员工表单：字段下方说明文字（.emp-field-col / .emp-field-hint） ----------
   为什么必须自带一层纵向容器（这不是美化，是布局缺陷的修复）：
     TDesign 的 t-form-item 内容区 .t-form__controls-content 是 display:flex + flex-wrap:nowrap。
     若把「输入框 + 说明文字」作为两个并列子元素直接塞进 t-form-item，它们会被排到**同一行**：
     输入框被挤到 152px 宽、说明文字被压成两行（实测确认，就是用户反馈「突兀」的真实原因）。
     包一层 column 容器后，输入框恢复整行宽度，说明文字落在输入框正下方。 */
.emp-field-col {
  display: flex;
  flex-direction: column;
  gap: 4px;
  width: 100%;
}
.emp-field-hint {
  font-size: 12px;
  line-height: 1.5;
  color: var(--sd-text-3);
}

/* ==========================================================================
   员工激励活动（员工提成 → 员工激励活动内页）
   说明：本页的核心诉求是「让老板看懂钱是怎么加出来的」，所以**公式条与试算区是视觉重点**
        （字号更大、有底色块、分色区分基础/活动两段），活动卡片本身保持低调。
        颜色全部走 --sd-* 变量，与提成页共用同一套语义色。
   ========================================================================== */
.act-page { padding-top: 4px; }

/* ---------- ① 公式条：总提成 = 基础提成 + 活动激励 ---------- */
.act-formula {
  margin-bottom: 16px;
  padding: 16px 18px;
  /* 四边统一 1px 浅灰边框（圆角与其它卡片一致）。
     原先左边是 4px 品牌色装饰竖条，按用户要求去掉 —— 去掉的方式是**删除 border-left 覆盖**
     而不是「置为 none」：这样左边的 1px 边框由上面的 border 简写继续提供，四边才是齐的。 */
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface-2);
}
.act-formula-main {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  font-size: 15px;
  color: var(--sd-text);
}
.act-formula-title { font-weight: 600; font-size: 16px; }
.act-formula-eq { color: var(--sd-text-3); font-weight: 600; }
.act-seg {
  display: inline-flex;
  flex-direction: column;
  padding: 6px 12px;
  border-radius: 8px;
  line-height: 1.35;
}
.act-seg b { font-size: 14px; }
.act-seg em {
  font-style: normal;
  font-size: 12px;
  opacity: 0.8;
}
.act-seg-base {
  background: var(--sd-brand-a08);
  border: 1px solid var(--sd-brand-a12);
  color: var(--sd-brand-strong);
}
.act-seg-act {
  background: rgba(217, 130, 43, 0.1);
  border: 1px solid rgba(217, 130, 43, 0.22);
  color: var(--sd-warning);
}
.act-formula-note {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px dashed var(--sd-border);
  font-size: 12px;
  line-height: 1.7;
  color: var(--sd-text-2);
}
.act-formula-note b { color: var(--sd-text); }

/* ---------- ② 摘要卡 ---------- */
.act-summary {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 12px;
  margin-bottom: 16px;
}
.act-stat {
  padding: 12px 14px;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface);
}
.act-stat-label { font-size: 12px; color: var(--sd-text-3); }
.act-stat-num { margin-top: 6px; font-size: 20px; font-weight: 600; color: var(--sd-text); }
.act-stat-num.is-in { color: var(--sd-warning); }
.act-stat-num.is-total { color: var(--sd-danger); }
.act-stat-sub { margin-top: 4px; font-size: 11px; color: var(--sd-text-3); }

/* ---------- ③ 工具栏与预设条 ---------- */
.act-toolbar {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 12px;
}
.act-toolbar-tip { font-size: 12px; color: var(--sd-text-3); }
.act-toolbar-tip b { color: var(--sd-warning); }

.act-preset-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-bottom: 12px;
  padding: 12px 14px;
  border: 1px dashed var(--sd-border);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface-2);
}
.act-preset-bar-title { font-size: 12px; font-weight: 600; color: var(--sd-text-2); }
.act-preset-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 8px;
  border: 1px solid var(--sd-border);
  border-radius: 999px;
  background: var(--sd-surface);
}
.act-preset-chip-name { font-size: 12px; font-weight: 600; color: var(--sd-text); }
.act-preset-chip-desc {
  max-width: 260px;
  font-size: 11px;
  color: var(--sd-text-3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ---------- ④ 活动卡片 ---------- */
.act-list { display: flex; flex-direction: column; gap: 10px; margin-bottom: 18px; }
.act-card {
  padding: 14px 16px;
  /* 四边统一 1px（原先左侧是 4px 橙色竖条，按用户要求全站去掉 —— 见文件顶部「全站视觉禁令」）。 */
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface);
  box-shadow: var(--sd-shadow);
}
/* 未开启的活动整卡降饱和：一眼能看出「这张卡现在不花钱」 */
.act-card.is-off {
  background: var(--sd-surface-2);
  box-shadow: none;
}
.act-card.is-off .act-card-name { color: var(--sd-text-2); }
.act-card-head {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.act-card-name { font-size: 15px; font-weight: 600; color: var(--sd-text); }
.act-card-spacer { flex: 1 1 auto; }
/* 一句话公式：整张卡里最该被读到的一行 */
.act-card-formula {
  margin-top: 10px;
  padding: 8px 12px;
  border-radius: 6px;
  background: rgba(217, 130, 43, 0.08);
  font-size: 13px;
  line-height: 1.6;
  color: var(--sd-text);
}
.act-card.is-off .act-card-formula { background: var(--sd-bg); color: var(--sd-text-2); }
.act-card-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 20px;
  margin-top: 10px;
  font-size: 12px;
  color: var(--sd-text-2);
}
.act-meta-item b { color: var(--sd-text); font-weight: 600; }
.act-meta-sub {
  margin-left: 4px;
  font-style: normal;
  color: var(--sd-text-3);
}
.act-card-remark {
  margin-top: 8px;
  font-size: 12px;
  color: var(--sd-text-3);
}
.act-empty {
  padding: 24px 16px;
  text-align: center;
  font-size: 13px;
  color: var(--sd-text-3);
  border: 1px dashed var(--sd-border);
  border-radius: var(--sd-radius-sm);
}

/* ---------- ⑤ 实时试算面板 ---------- */
.act-sim {
  /* 与上面的活动清单拉开距离：上面是「配置真实活动」，这里是「只算数的推演台」，
     两块性质不同，靠间距 + 独立卡片明确分开。 */
  margin-top: 26px;
  padding: 16px 18px;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface);
}
.act-sim-head {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 10px;
}
.act-sim-title { font-size: 15px; font-weight: 600; color: var(--sd-text); }
/* 说明段：讲清「这是模拟、不改数据、用来算预算」，否则用户会怕它动到真实提成 */
.act-sim-intro {
  margin-bottom: 14px;
  padding: 10px 12px;
  border-radius: 6px;
  background: var(--sd-brand-a08);
  font-size: 12.5px;
  line-height: 1.75;
  color: var(--sd-text-2);
}
.act-sim-intro b { color: var(--sd-text); font-weight: 600; }
/* 输入区：一行放不下自动换行 */
.act-sim-form {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 18px;
  margin-bottom: 8px;
}
.act-sim-field { display: flex; align-items: center; gap: 6px; }
.act-sim-label { font-size: 12px; color: var(--sd-text-2); }
.act-sim-unit { font-size: 12px; color: var(--sd-text-3); }
.act-sim-tips {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin-bottom: 14px;
  font-size: 12px;
  line-height: 1.7;
  color: var(--sd-text-3);
}
.act-sim-tips b { color: var(--sd-text-2); }
/* 活动激励为 0 时的原因清单：逐条列出可能原因，避免「填了参数却是 0」被当成功能坏了 */
.act-sim-why {
  margin: 6px 0 0;
  padding-left: 18px;
  list-style: disc;
  font-size: 12px;
  line-height: 1.7;
  color: var(--sd-text-2);
}
.act-sim-line {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 6px 14px;
  padding-bottom: 10px;
  border-bottom: 1px dashed var(--sd-border);
  font-size: 13px;
  color: var(--sd-text-2);
}
.act-sim-emp { font-size: 14px; font-weight: 600; color: var(--sd-text); }
.act-warn { color: var(--sd-warning); font-size: 12px; }

.act-sim-block {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 12px;
}
.act-sim-tag {
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 600;
}
.act-sim-tag.is-base { background: var(--sd-brand-a12); color: var(--sd-brand-strong); }
.act-sim-tag.is-act { background: rgba(217, 130, 43, 0.14); color: var(--sd-warning); }
.act-sim-amount { font-size: 16px; font-weight: 600; color: var(--sd-text); }
.act-sim-rows { margin-top: 6px; }
.act-sim-row {
  display: flex;
  align-items: baseline;
  gap: 12px;
  padding: 6px 10px;
  border-radius: 6px;
  font-size: 12px;
  color: var(--sd-text-2);
}
/* 斑马纹：同一活动的多行明细靠底色区分，避免行长时看串行 */
.act-sim-row:nth-child(odd) { background: var(--sd-surface-2); }
.act-sim-row-label { flex: none; width: 190px; font-weight: 600; color: var(--sd-text); }
.act-sim-row.is-act .act-sim-row-label { color: var(--sd-warning); }
.act-sim-row-detail { flex: 1 1 auto; color: var(--sd-text-2); }
.act-sim-row-amount { flex: none; font-weight: 600; color: var(--sd-text); }
.act-sim-none {
  margin-top: 6px;
  font-size: 12px;
  line-height: 1.7;
  color: var(--sd-text-3);
}
/* 等式行：把「基础 + 活动 = 合计」明确写出来 */
.act-sim-eq {
  margin-top: 14px;
  padding: 10px 14px;
  border-radius: 6px;
  background: var(--sd-brand-a08);
  font-size: 14px;
  font-weight: 600;
  color: var(--sd-brand-strong);
}
.act-sim-foot {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  margin-top: 10px;
  font-size: 12px;
  color: var(--sd-text-2);
}
.act-sim-total {
  margin-left: auto;
  font-size: 15px;
  font-weight: 600;
  color: var(--sd-danger);
}

/* ---------- ⑥ 活动编辑弹窗 ----------
   与「资料 → 新建商品」弹窗同一套做法：内容区自己滚、按钮固定在滚动区之外。
   原因完全相同：活动表单要填的字段多（含阶梯档位可增行），弹窗容易被撑高；
   而模态弹窗打开时 TDesign 会锁住页面滚动，按钮跑到视口外就**滚不到**。 */
.act-dialog-body {
  max-height: calc(76vh - 150px);
  overflow-y: auto;
  padding-right: 4px;
}
.act-dialog-foot {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 16px;
  padding-top: 14px;
  border-top: 1px solid var(--sd-border);
}
.act-field {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.act-field + .act-field { margin-top: 8px; }
.act-field-col { display: flex; flex-direction: column; gap: 6px; width: 100%; }
.act-unit { font-size: 13px; color: var(--sd-text-2); }
.act-hint {
  font-size: 12px;
  line-height: 1.7;
  color: var(--sd-text-3);
}
.act-hint b { color: var(--sd-warning); }

/* 实时公式预览：改任一参数，这句话立刻跟着变（本页最核心的「看得懂」） */
.act-preview {
  margin-top: 16px;
  padding: 12px 14px;
  border: 1px solid rgba(217, 130, 43, 0.25);
  border-radius: var(--sd-radius-sm);
  background: rgba(217, 130, 43, 0.06);
}
.act-preview-label { font-size: 12px; color: var(--sd-text-3); }
.act-preview-text {
  margin-top: 6px;
  font-size: 14px;
  line-height: 1.6;
  font-weight: 600;
  color: var(--sd-text);
}
.act-preview-trial {
  margin-top: 8px;
  font-size: 12px;
  color: var(--sd-text-2);
}
.act-preview-trial b { font-size: 14px; color: var(--sd-warning); }
.act-preview-trial-sub { color: var(--sd-text-3); }

/* 窄屏自适应：摘要在窄屏两栏，卡片信息换行不挤压 */


/* 商品进价说明（资料 → 商品信息 → 新建/编辑商品弹窗） */
.cost-tip {
  margin-top: 8px;
  font-size: 12px;
  color: var(--sd-text-3);
}

/* ==================== 打印机设置（设置 → 打印机设置） ==================== */

/* 状态总览卡片（与收款账户设置的 acct-card 同构，保持全站观感一致） */
.pr-summary {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 12px;
  margin-bottom: 16px;
}
.pr-card {
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface);
  padding: 12px 14px;
}
.pr-card.hl {
  border-color: var(--sd-brand);
  background: var(--sd-brand-a08, rgba(74, 144, 226, 0.08));
}
.pr-card.ok .pr-card-val { color: var(--sd-success); }
.pr-card.off .pr-card-val { color: var(--sd-text-3); }
.pr-card-label {
  font-size: 12px;
  color: var(--sd-text-3);
  margin-bottom: 6px;
}
.pr-card-val {
  font-size: 17px;
  font-weight: 600;
  color: var(--sd-text);
  line-height: 1.3;
  word-break: break-all;
}
.pr-card-sub {
  margin-top: 5px;
  font-size: 12px;
  color: var(--sd-text-3);
}

/* 左配置 / 右预览 双栏 */
.pr-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 320px;
  gap: 20px;
  align-items: start;
}
.pr-left { min-width: 0; }
.pr-sec-title {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 18px 0 10px;
  font-size: 13px;
  font-weight: 600;
  color: var(--sd-text);
}
.pr-sec-title:first-child { margin-top: 0; }
.pr-dim {
  color: var(--sd-text-3);
  font-size: 12px;
}
.pr-tip { margin-left: 8px; }
/* 「输入框 + 下方说明」的纵向容器。
   为什么必须包（这是布局缺陷的修复，不是美化）：TDesign 表单项内容区
   .t-form__controls-content 是 display:flex + flex-wrap:nowrap，输入框与说明文字并列写会被排到
   **同一行** —— 实测说明文字压成两行（44px 高）、输入框只剩 316px 宽。
   容器内的 .pr-tip 需要清掉 margin-left：它在 column 布局里会变成无意义的左缩进。 */
.pr-field-col {
  display: flex;
  flex-direction: column;
  gap: 4px;
  width: 100%;
}
.pr-field-col > .pr-tip { margin-left: 0; }
/* 自定义纸宽：标签 + 数字框 + 单位 + 推导提示。
   允许换行（flex-wrap）：窄屏或字号放大时「每行约 N 个字符」这截提示要能掉到下一行，
   否则会把数字框挤变形（与「打印通道」同一类问题）。 */
.pr-custom-paper {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.pr-custom-label { font-size: 13px; color: var(--sd-text-2); }
.pr-spec {
  margin-left: 8px;
  font-size: 11px;
  color: var(--sd-text-3);
}
.pr-conn-ico {
  margin-right: 6px;
  font-size: 15px;
  vertical-align: -3px;
}
.pr-conn-cell {
  display: flex;
  align-items: center;
  gap: 2px;
}
.pr-rule-note {
  margin-top: 10px;
  padding: 8px 12px;
  border: 1px solid var(--sd-border);
  background: var(--sd-surface-2);
  border-radius: 4px;
  font-size: 12px;
  line-height: 1.7;
  color: var(--sd-text-2);
}
.pr-conn-row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.pr-conn-opts {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  margin-top: 10px;
}
.pr-inline {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: var(--sd-text-2);
}
.pr-connected {
  margin-top: 12px;
  padding: 10px 14px;
  border: 1px solid var(--sd-success);
  border-radius: var(--sd-radius-sm);
  background: rgba(26, 169, 127, 0.08);
}
.pr-connected-main {
  display: flex;
  align-items: center;
  gap: 10px;
}
.pr-connected-main .ip-icon { font-size: 20px; }
.pr-connected-name {
  font-size: 14px;
  font-weight: 600;
  color: var(--sd-text);
}
.pr-auth-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.pr-auth-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface);
}
.pr-auth-name {
  flex: 1;
  font-size: 13px;
  color: var(--sd-text);
  word-break: break-all;
}
.pr-empty {
  padding: 12px 14px;
  border: 1px dashed var(--sd-border);
  border-radius: var(--sd-radius-sm);
  font-size: 12px;
  line-height: 1.7;
  color: var(--sd-text-3);
}
.pr-history {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.pr-history-item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: 999px;
  background: var(--sd-surface-2);
  border: 1px solid var(--sd-border);
  font-size: 12px;
  color: var(--sd-text-2);
}
.pr-history-item em {
  font-style: normal;
  color: var(--sd-text-3);
  font-size: 11px;
}
.pr-toggle-list {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px 20px;
  max-width: 680px;
}
.pr-toggle-item {
  display: flex;
  align-items: flex-start;
  gap: 10px;
}
.pr-toggle-label {
  font-size: 13px;
  color: var(--sd-text);
  line-height: 1.4;
}
.pr-foot-actions {
  margin-top: 18px;
}

/* 右侧：小票预览 */
.pr-right {
  position: sticky;
  top: 0;
}
.pr-preview-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 8px;
  font-size: 13px;
  font-weight: 600;
  color: var(--sd-text);
}
.pr-stage {
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface-2);
  padding: 14px 10px;
  display: flex;
  justify-content: center;
  max-height: 560px;
  overflow: auto;
}
.pr-stage-inner { display: block; }
.pr-preview-actions {
  display: flex;
  gap: 10px;
  margin-top: 12px;
}
.pr-result {
  margin-top: 10px;
  padding: 8px 12px;
  border-radius: var(--sd-radius-sm);
  font-size: 12px;
  line-height: 1.6;
}
.pr-result.ok {
  border: 1px solid var(--sd-success);
  background: rgba(26, 169, 127, 0.08);
  color: var(--sd-text);
}
.pr-result.warn {
  border: 1px solid var(--sd-warning);
  background: rgba(217, 130, 43, 0.1);
  color: var(--sd-text);
}
.pr-preview-note { margin-top: 10px; line-height: 1.6; }

/* 窄屏：预览收到配置下方 */

/* ==================== 小票渲染（.rc-*） ====================
   注意：这套样式与 src/printer.js 的 renderPrintDocument() 内联样式是**同一份视觉契约**，
   一处改了另一处要同步（打印文档是独立 iframe 文档，无法继承本文件的变量，故只能内联复制）。
   小票一律「白纸黑字」：这是对物理出纸的模拟，不随明暗主题反转，否则无法判断实际打印效果。 */
.rc-paper {
  background: #fff;
  color: #000;
  padding: 12px 10px 26px;
  box-sizing: border-box;
  font-family: "Microsoft YaHei", "PingFang SC", "Hiragino Sans GB", SimSun, sans-serif;
  font-size: 11px;
  line-height: 1.55;
  box-shadow: 0 1px 10px rgba(0, 0, 0, 0.18);
}
.rc-paper.rc-w58 { width: 200px; }
.rc-paper.rc-w80 { width: 276px; }
.rc-c { text-align: center; }
.rc-lg { font-size: 15px; font-weight: 700; line-height: 1.35; }
.rc-b { font-weight: 700; }
.rc-line { white-space: pre-wrap; word-break: break-all; }
.rc-kv {
  display: flex;
  justify-content: space-between;
  gap: 8px;
}
.rc-v { white-space: nowrap; }
.rc-cols {
  display: flex;
  gap: 4px;
  align-items: flex-start;
}
.rc-rule {
  border-top: 1px dashed #000;
  margin: 3px 0;
}
.rc-qr { text-align: center; margin: 6px 0; }
.rc-qr-box {
  display: inline-block;
  border: 1px solid #000;
  padding: 12px 8px;
  font-size: 10px;
}
.rc-qr-txt {
  font-size: 9px;
  word-break: break-all;
  margin-top: 2px;
}
/* 条码示意（吊牌模板用）。
   ⚠️ 这是**视觉示意、不能真的扫码**（真实条码由打印机的 ESC/POS 指令在纸上生成），
   所以条纹下方必须保留「（示意）」字样，别为了好看把它去掉 —— 否则用户会拿扫码枪去试。 */
.rc-barcode { text-align: center; margin: 5px 0; }
.rc-barcode-bars { white-space: nowrap; overflow: hidden; line-height: 0; }
.rc-barcode-txt { font-size: 9px; letter-spacing: 1px; margin-top: 2px; }

/* ==========================================================================
   打印模板工作室（打印机设置 →「模板」页签）
   三栏：模板列表（固定窄） / 预览（固定窄，小票本体就窄） / 字段编辑器（吃掉剩余宽度）
   为什么编辑区要最宽：每行有「字段名 + 5 个小操作按钮」，窄了就会把按钮挤成两行。
   ========================================================================== */
.tpl-layout {
  display: grid;
  grid-template-columns: 216px 264px minmax(0, 1fr);
  gap: 12px;
  align-items: start;
}
.tpl-side,
.tpl-preview,
.tpl-editor {
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  padding: 10px;
  background: var(--sd-surface);
}
.tpl-side-head { margin-bottom: 8px; }

/* 类别筛选：胶囊按钮，点同一个再点一次取消筛选 */
.tpl-kinds { display: flex; flex-wrap: wrap; gap: 5px; margin-bottom: 8px; }
.tpl-kind {
  padding: 2px 8px;
  font-size: 12px;
  color: var(--sd-text-2);
  background: var(--sd-surface-2);
  border: 1px solid var(--sd-border);
  border-radius: 11px;
  cursor: pointer;
  white-space: nowrap;
}
.tpl-kind:hover { border-color: var(--sd-brand); color: var(--sd-brand); }
.tpl-kind-on { background: var(--sd-brand-a12); border-color: var(--sd-brand); color: var(--sd-brand); }

/* 模板列表：60 套要能滚，否则左侧会把整页撑得很长 */
.tpl-list { max-height: 452px; overflow-y: auto; margin: 0 -4px; padding: 0 4px; }
.tpl-item {
  padding: 7px 8px;
  border-radius: 6px;
  cursor: pointer;
}
.tpl-item:hover { background: var(--sd-surface-2); }
/* 选中态：品牌色浅底（a12 比 hover 更明显）。原先还配了一条 3px 左侧竖线，
   按用户要求全站去掉竖线后，选中只靠底色表达 —— 所以底色从 a08 提到 a12，
   否则会和 hover 的 --sd-surface-2 太接近、看不出当前选的是哪套模板。 */
.tpl-item-on { background: var(--sd-brand-a12); }
.tpl-item-line { display: flex; align-items: center; gap: 5px; }
.tpl-item-name { font-size: 13px; color: var(--sd-text); }
.tpl-item-desc {
  margin-top: 1px;
  font-size: 11px;
  line-height: 1.5;
  color: var(--sd-text-3);
  /* 描述最多两行，避免个别长描述把列表行高撑得参差不齐 */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.tpl-sec-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 8px;
  font-size: 13px;
  font-weight: 500;
  color: var(--sd-text);
}
.tpl-title { font-size: 13px; font-weight: 500; }
.tpl-ops { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.tpl-dim { font-size: 12px; color: var(--sd-text-3); font-weight: 400; }
.tpl-note { margin-top: 8px; line-height: 1.6; }
.tpl-stage { display: flex; justify-content: center; }

/* 只读/已保存提示：预设用中性色，自定义用绿字（"改动已保存"是正面反馈，不该像警告） */
.tpl-ro {
  margin-bottom: 8px;
  padding: 6px 8px;
  font-size: 12px;
  line-height: 1.6;
  color: var(--sd-text-2);
  background: var(--sd-surface-2);
  border-radius: 6px;
}
.tpl-ro-ok { color: var(--sd-success); background: rgba(26, 169, 127, 0.08); }

/* ============ 模板工作室简洁模式（2026-09-17 简化：默认折叠字段编辑，只留「选预设 + 设为使用」） ============ */
.tpl-simple {
  padding: 16px;
  background: var(--sd-surface-2);
  border-radius: 8px;
  text-align: center;
}
.tpl-simple-title { font-size: 16px; font-weight: 700; color: var(--sd-text); }
.tpl-simple-desc { font-size: 13px; color: var(--sd-text-2); margin-top: 6px; }
.tpl-simple-hint { font-size: 12px; color: var(--sd-text-3); margin-top: 10px; line-height: 1.6; }
/* 高级编辑模式下底部另存/重命名/删除按钮组 */
.tpl-ops-advanced { display: flex; align-items: center; gap: 6px; margin-top: 12px; }

/* 字段编辑器：每行「拖拽柄 + 勾选框 + 字段名 + 说明 + 操作按钮」 */
.tpl-fields { max-height: 420px; overflow-y: auto; }
.tpl-field {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 5px 6px;
  border-radius: 6px;
  border: 1px solid transparent;
}
.tpl-field:hover { background: var(--sd-surface-2); }
.tpl-field-off .tpl-field-hint,
.tpl-field-off .tpl-field-ops { opacity: 0.45; }
.tpl-field-dragging { border-color: var(--sd-brand); background: var(--sd-brand-a08); }
.tpl-handle { flex: 0 0 auto; color: var(--sd-text-3); cursor: grab; font-size: 12px; letter-spacing: -1px; }
.tpl-field .t-checkbox { flex: 0 0 auto; }
/* 字段说明吃掉剩余宽度并省略：它只是辅助信息，不该把右侧按钮挤到换行 */
.tpl-field-hint {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 11px;
  color: var(--sd-text-3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tpl-field-ops { flex: 0 0 auto; display: flex; align-items: center; gap: 3px; }
.tpl-op {
  min-width: 22px;
  height: 22px;
  padding: 0 4px;
  font-size: 12px;
  line-height: 1;
  color: var(--sd-text-2);
  background: var(--sd-surface);
  border: 1px solid var(--sd-border);
  border-radius: 4px;
  cursor: pointer;
}
.tpl-op:hover:not(:disabled) { border-color: var(--sd-brand); color: var(--sd-brand); }
.tpl-op:disabled { opacity: 0.4; cursor: not-allowed; }
.tpl-op-on { color: var(--sd-brand); border-color: var(--sd-brand); background: var(--sd-brand-a12); font-weight: 700; }
.tpl-op-del:hover:not(:disabled) { border-color: var(--sd-danger); color: var(--sd-danger); }

/* 可添加字段：点一下就追加到模板末尾 */
.tpl-add { margin-top: 8px; padding-top: 8px; border-top: 1px dashed var(--sd-border); display: flex; flex-wrap: wrap; gap: 5px; align-items: center; }
.tpl-add-item {
  padding: 2px 8px;
  font-size: 12px;
  color: var(--sd-brand);
  background: var(--sd-brand-a08);
  border: 1px solid var(--sd-brand-a12);
  border-radius: 11px;
  cursor: pointer;
}
.tpl-add-item:hover { background: var(--sd-brand-a12); }

.tpl-dialog { display: flex; flex-direction: column; gap: 8px; }
.tpl-dialog-foot { display: flex; justify-content: flex-end; gap: 8px; margin-top: 4px; }

/* 窄屏：三栏改为上下堆叠（列表 → 预览 → 编辑），列表不再限高 */


/* ==========================================================================
   退换货 / 回收：批量办结操作条（.as-*）
   说明：勾选售后单后才出现（v-if），所以样式只负责「选中态」的外观。
   用品牌色浅底 + 细边框把它与表格区分开，避免和上方的页面级按钮混淆。
   ========================================================================== */
.as-bulk-bar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px 12px;
  margin-bottom: 12px;
  padding: 10px 12px;
  border: 1px solid var(--sd-brand-soft);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-brand-a08);
}
.as-bulk-count { font-size: 13px; color: var(--sd-text-2); }
.as-bulk-count b { color: var(--sd-brand-strong); font-size: 14px; }
/* 库存影响提示：退款/回收回补库存、换货不回补，写在按钮边上，避免点错 */
.as-bulk-hint { font-size: 12px; color: var(--sd-text-3); }

/* 售后单「商品明细」行：给 t-input-number 的文本区兜一个最小宽度。
   为什么需要：input-number 左右各有一个加减按钮，列宽不足时中间的文本区会被压成 0 宽 ——
   界面上是个「看得见、点不进去」的空盒子（数量框实测 width=0、金额框只剩 33px）。
   与 app.js 里把数量/金额列宽放宽是双保险，缺一个都可能在某档屏幕宽度下again被压没。 */
.as-line-table .t-input__inner { min-width: 34px; }

/* ==========================================================================
   补货参谋 · 老板决策台（.ra-*）
   设计意图：这一页给老板/店长看的是「钱怎么花、值不值得花」，不是库存明细的复制品。
   因此样式上刻意与「库存 → 库存明细」区分：摘要卡用大号数字突出资金结论；
   明细表一行一件事，靠分层标签与红色风险文字表达紧急度，不靠图标让用户猜。
   ========================================================================== */

/* ① 决策摘要：四个数字并排 —— 投多少 / 能赚多少 / 不补亏多少 / 不用管几个。
      四列而不是两列：这四个数老板是「一起看」的，分开两行会失去对照关系。 */
.ra-summary {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 12px;
  margin-bottom: 14px;
}
.ra-card {
  padding: 14px 16px;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface);
}
/* 风险卡只在真的有钱会亏时才染色（用边框与数字颜色，不铺底色）：
   长期红着会让老板对风险提示麻木，反而在真出事时看不见。 */
.ra-card.is-alert { border-color: var(--sd-danger); }
.ra-card.is-alert .ra-card-num { color: var(--sd-danger); }
.ra-card-lab { font-size: 12px; color: var(--sd-text-3); }
.ra-card-num { margin: 4px 0 2px; font-size: 24px; font-weight: 700; line-height: 1.2; color: var(--sd-text); }
.ra-card-num.is-gain { color: var(--sd-success); }
.ra-card-sub { font-size: 12px; line-height: 1.5; color: var(--sd-text-3); }

/* 决策卡副标题里的「原因分解」：库存充足 3 · 有货无动销 7 · 断货待确认 2。
   三类里只有「断货待确认」需要老板动手，用警示色把它从另两类里挑出来 ——
   另两类系统已经给了结论（不用管 / 该清仓），这一类是"还在等你判断"。 */
.ra-watch-item b { font-weight: 700; color: var(--sd-text-2); }
.ra-watch-item.is-attention,
.ra-watch-item.is-attention b { color: var(--sd-warning); }

/* ===== 判据说明块：① 「为什么不建议补」（摘要区下方）② 参数卡的公式说明 =====
   两者的诉求是同一个 —— 老板必须能看懂这些数字是怎么来的，否则不敢照着下单，
   所以共用一套视觉：四边 1px 细边 + 圆角 + 浅底，字号小一档但颜色比普通提示重，
   确保它会被读到，而不是被当成装饰忽略掉。
   全站不用「左侧彩色竖线」做强调（那是被明令去掉的写法），
   需要突出某一条时靠字重与颜色，不靠色条。 */
.ra-formula,
.ra-why {
  padding: 10px 12px;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface-2);
  font-size: 12px;
  line-height: 1.7;
  color: var(--sd-text-2);
}
.ra-formula { margin-top: 12px; }
/* 摘要区自带 margin-bottom，这里不再加 margin-top，否则两块之间会空出近 30px */
.ra-why { margin-bottom: 14px; }
.ra-why-title { font-weight: 700; color: var(--sd-text); }
/* 判据口径（用了哪几个参数）跟在标题后面，字号不变但颜色降一档，不抢标题 */
.ra-why-scope { font-weight: 400; color: var(--sd-text-3); }
.ra-why-item { margin-top: 4px; }
/* 「断货待确认」与明细表里的 .ra-watch-reason.is-attention 用同一套视觉语言：
   同一个原因在两个地方出现时，颜色必须一致，否则老板会以为是两件事。 */
.ra-why-item.is-attention,
.ra-why-item.is-attention b { color: var(--sd-warning); }
/* 单独说明「断货待确认为什么也算进这张卡」：浅色虚线分隔，与上面的条目区分开 */
.ra-why-note {
  margin-top: 6px;
  padding-top: 6px;
  border-top: 1px dashed var(--sd-border);
  color: var(--sd-text-3);
}

/* ② 决策参数：两列网格，每项「标签 + 输入 + 单位 + 一句解释」 */
.ra-params { margin-bottom: 14px; }
.ra-param-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px 24px;
}
.ra-param { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ra-param-lab { min-width: 68px; font-size: 13px; color: var(--sd-text-2); }
.ra-param-unit { font-size: 12px; color: var(--sd-text-3); }
.ra-param-tip { font-size: 12px; color: var(--sd-text-3); }

/* 预算不足提示：属于「需要马上做决定」的信息，用警示色描边 + 加粗关键数字 */
.ra-budget-warn {
  margin-top: 10px;
  padding: 10px 12px;
  border: 1px solid var(--sd-warning);
  border-radius: var(--sd-radius-sm);
  font-size: 12px;
  line-height: 1.7;
  color: var(--sd-text-2);
}

/* ③ 分层筛选：胶囊按钮，选中态用品牌色 —— 与页面主色一致，一眼看出当前筛选 */
.ra-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 12px;
}
.ra-filters { display: flex; gap: 6px; flex-wrap: wrap; }
.ra-filter {
  padding: 5px 12px;
  border: 1px solid var(--sd-border);
  border-radius: 999px;
  background: var(--sd-surface);
  color: var(--sd-text-2);
  font-size: 13px;
  /* 原生 button 默认不继承字体，不写这两行会出现「按钮文字与页面字体不一致」的割裂感 */
  font-family: inherit;
  line-height: 1.5;
  cursor: pointer;
  transition: border-color 0.15s, background 0.15s, color 0.15s;
}
.ra-filter:hover { border-color: var(--sd-brand-soft); color: var(--sd-brand-strong); }
.ra-filter.on {
  border-color: var(--sd-brand);
  background: var(--sd-brand-a08);
  color: var(--sd-brand-strong);
  font-weight: 600;
}
.ra-filter b { font-weight: 700; }

/* ④ 批量操作条：勾选后才出现，当场把「将花多少钱、拆几家单」算给老板看 */
.ra-bulk {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 12px;
  padding: 10px 12px;
  border: 1px solid var(--sd-brand-soft);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-brand-a08);
}
.ra-bulk-count { font-size: 13px; color: var(--sd-text-2); }
.ra-bulk-count b { color: var(--sd-brand-strong); }
.ra-bulk-hint { font-size: 12px; color: var(--sd-text-3); }

/* ⑤ 明细表：单行信息密度高，靠颜色而不是图标表达「赚」与「险」 */
.ra-prod { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.ra-prod-name { font-size: 13px; color: var(--sd-text); }
.ra-prod-cs { font-size: 12px; color: var(--sd-text-3); }
.ra-stock { font-size: 13px; color: var(--sd-text); }
.ra-transit { margin-left: 6px; font-size: 12px; color: var(--sd-brand-strong); }
.ra-muted { font-size: 13px; color: var(--sd-text-3); }
.ra-margin { font-size: 13px; font-weight: 600; color: var(--sd-success); }
.ra-danger-text { font-weight: 600; color: var(--sd-danger); }
.ra-risk-text { margin-top: 2px; font-size: 11px; line-height: 1.4; color: var(--sd-danger); }
.ra-qty { display: flex; align-items: center; gap: 2px; }
/* 兜底：t-input-number 左右各有加减按钮，列宽不足时中间文本区会被压成 0 宽 ——
   界面上就是「看得见、点不进去的空盒子」（本项目在售后单明细行实测踩过同一个坑）。
   与列宽设置属双保险，缺一个都可能在某档屏幕宽度下被压没。 */
.ra-table .t-input__inner { min-width: 34px; }

/* 这里原来还有一条 .ra-watch-note（观察层就地的「为什么不建议补」说明）。
   它已被摘要区下方的 .ra-why 取代，样式定义一并删除 ——
   两块讲同一件事时会出现"同一个原因两段解释"，且旧块是手写文案、新块直接取引擎判据。
   需要给它加视觉时请改上面的 .ra-why，不要再恢复这一条。 */

/* 有「断货待确认」色码时给摘要卡加警示（用边框，不铺底色）：
   一列 12 个色码里可能有 2 个是断码，不提醒就会被当成「库存健康」放过去。 */
.ra-card.is-warn { border-color: var(--sd-warning); }
.ra-card-flag { display: block; margin-top: 2px; font-weight: 600; color: var(--sd-warning); }

/* 明细表「分层」列里的观察原因：正常原因低调显示，需要人工确认的用警示色 */
.ra-watch-reason { margin-top: 2px; font-size: 11px; line-height: 1.4; color: var(--sd-text-3); }
.ra-watch-reason.is-attention { font-weight: 600; color: var(--sd-warning); }

/* 无补货建议时的结论区：明确说出结论与「为什么断货色码也没自动补」，
   避免老板面对一张空表得出「功能坏了」或「库存很健康」的错误结论。 */
.ra-healthy {
  margin-top: 12px;
  padding: 12px 14px;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface-2);
}
.ra-healthy-title { font-size: 14px; font-weight: 600; color: var(--sd-text); margin-bottom: 6px; }
.ra-healthy-line { font-size: 12px; line-height: 1.75; color: var(--sd-text-2); }
/* 口径说明：它是「边界声明」而不是「警告」，但必须让人读到。
   原先用 3px 左侧竖线引导视线，现在改为**深一档的文字色**（--sd-text 对比周围的 --sd-text-2）
   来做强调 —— 不用色条，也不靠背景色，与旁边的普通说明行仍是同一套版式。 */
.ra-healthy-line.is-attention { margin-top: 6px; color: var(--sd-text); }
.ra-healthy-line.ra-muted { margin-top: 6px; color: var(--sd-text-3); }

/* 窄屏：摘要卡与参数改单列/双列，避免大号数字被挤成两行 */

/* ==========================================================================
   滞销预警 · 老板处置决策台（.sm-*）
   说明：结构与「补货参谋」的 .ra-* 相同（摘要卡 / 参数条 / 分层筛选 / 批量条 / 明细表），
   但**刻意不共用样式**：两页的视觉细节本来就会各自调整（比如风险色的用法），
   共用同一个类名会变成「改 A 页牵连 B 页」的隐形耦合。CSS 这点重复比耦合划算。
   ========================================================================== */

/* ① 决策摘要：压了多少钱 / 清掉回笼多少 / 每月多花多少 / 全店动销率 */
.sm-summary {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 12px;
  margin-bottom: 14px;
}
.sm-card {
  padding: 14px 16px;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface);
}
/* 只有「占用资金」这张默认带警示色 —— 它是本页的主矛盾（钱被压住了）。
   不做成四张全红：全线标红等于没有重点。 */
.sm-card.is-warn { border-color: var(--sd-warning); }
.sm-card.is-warn .sm-card-num { color: var(--sd-warning); }
.sm-card-lab { font-size: 12px; color: var(--sd-text-3); }
.sm-card-num { margin: 4px 0 2px; font-size: 24px; font-weight: 700; line-height: 1.2; color: var(--sd-text); }
.sm-card-sub { font-size: 12px; line-height: 1.5; color: var(--sd-text-3); }

/* 盈亏配色：红=亏、绿=赚 —— 这是经营盈亏的通用语义。
   （注意与股票行情的「红涨绿跌」不同，那是行情语义，这里不是行情。） */
.sm-gain { color: var(--sd-success); font-weight: 600; }
.sm-loss { color: var(--sd-danger); font-weight: 600; }

/* ② 处置参数 */
.sm-params { margin-bottom: 14px; }
.sm-param-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px 24px;
}
.sm-param { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.sm-param-lab { min-width: 68px; font-size: 13px; color: var(--sd-text-2); }
.sm-param-unit { font-size: 12px; color: var(--sd-text-3); }
.sm-param-tip { font-size: 12px; color: var(--sd-text-3); }

/* 算法说明与口径边界：这个页面最容易把估算包装成精确结论，所以两段都要显眼 */
.sm-formula {
  margin-top: 12px;
  padding: 10px 12px;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface-2);
  font-size: 12px;
  line-height: 1.7;
  color: var(--sd-text-2);
}
/* 口径说明用**四边 1px 边框**而不是左侧竖线：它是「边界声明」，不是「警告」，
   但必须让人读到。原先靠左侧 3px 竖线引导视线，按用户要求全站去掉竖线后，
   改为与 .act-formula / .mst-tip 同一套「四边 1px 浅灰边框 + 浅底」的说明块版式。 */
.sm-caveat {
  margin-top: 8px;
  padding: 10px 12px;
  border: 1px solid var(--sd-border);
  background: var(--sd-surface-2);
  font-size: 12px;
  line-height: 1.7;
  color: var(--sd-text-3);
}

/* ③ 分层筛选：胶囊按钮，与补货参谋同一套交互习惯 */
.sm-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 12px;
}
.sm-filters { display: flex; gap: 6px; flex-wrap: wrap; }
.sm-filter {
  padding: 5px 12px;
  border: 1px solid var(--sd-border);
  border-radius: 999px;
  background: var(--sd-surface);
  color: var(--sd-text-2);
  font-size: 13px;
  /* 原生 button 不继承字体，不写这两行会与页面其它文字有明显割裂感 */
  font-family: inherit;
  line-height: 1.5;
  cursor: pointer;
  transition: border-color 0.15s, background 0.15s, color 0.15s;
}
.sm-filter:hover { border-color: var(--sd-brand-soft); color: var(--sd-brand-strong); }
.sm-filter.on {
  border-color: var(--sd-brand);
  background: var(--sd-brand-a08);
  color: var(--sd-brand-strong);
  font-weight: 600;
}
.sm-filter b { font-weight: 700; }

/* ④ 批量条 */
.sm-bulk {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 12px;
  padding: 10px 12px;
  border: 1px solid var(--sd-brand-soft);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-brand-a08);
}
.sm-bulk-count { font-size: 13px; color: var(--sd-text-2); }
.sm-bulk-count b { color: var(--sd-brand-strong); }

/* ⑤ 明细表 */
.sm-prod { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.sm-prod-name { font-size: 13px; color: var(--sd-text); }
.sm-prod-cs { font-size: 12px; color: var(--sd-text-3); }
.sm-tied { font-size: 13px; font-weight: 600; color: var(--sd-text); }
.sm-danger-text { font-weight: 600; color: var(--sd-danger); }
.sm-risk-text { margin-top: 2px; font-size: 11px; line-height: 1.4; color: var(--sd-text-3); }
.sm-discount { display: flex; align-items: center; gap: 2px; }
.sm-discount-unit { font-size: 12px; color: var(--sd-text-3); }
/* 兜底：t-input-number 的文本区在列宽不足时会被压成 0 宽（看得见点不进去），
   本项目在售后单明细、补货明细都踩过同一个坑，这里一并防住。 */
.sm-table .t-input__inner { min-width: 28px; }

/* 无滞销时的结论区：明确给结论 + 给一条「想看更严口径」的引导 */
.sm-healthy {
  margin-top: 12px;
  padding: 12px 14px;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface-2);
}
.sm-healthy-title { font-size: 14px; font-weight: 600; color: var(--sd-text); margin-bottom: 6px; }
.sm-healthy-line { font-size: 12px; line-height: 1.75; color: var(--sd-text-2); }
.sm-healthy-line.sm-muted { margin-top: 6px; color: var(--sd-text-3); }

/* ⑥ 清仓执行清单弹窗 */
.sm-list-text {
  margin: 0;
  padding: 12px;
  max-height: 420px;
  overflow: auto;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface-2);
  font-size: 12px;
  line-height: 1.8;
  color: var(--sd-text);
  /* 清单要能直接复制给店长，所以保持等宽字体对齐、并允许折行 */
  font-family: Menlo, Consolas, "Microsoft YaHei", monospace;
  white-space: pre-wrap;
  word-break: break-all;
}
.sm-list-foot {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 14px;
}

  /* 窄屏 */

/* ==========================================================================
   销量洞察 · 老板决策台（.si-*）
   说明：本区块的命名空间刻意用 si- 前缀，避免通用的 .is-good / .is-up 等语义类
   与其它页面的同名类互相污染（只在 .si-* 选择器下定义，作用范围可控）。
   涨跌配色遵循中文语境：销售额上涨用暖色、下跌用绿色（与国内经营后台一致），
   且涨跌数字旁始终带 ↑/↓ 箭头，不会产生歧义。
   ========================================================================== */

/* ---------- ① 决策摘要四张卡 ---------- */
.si-summary {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 12px;
  margin-bottom: 14px;
}
.si-card {
  padding: 14px 16px;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface);
}
.si-card-lab {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  color: var(--sd-text-3);
}
.si-card-sub { font-size: 11px; color: var(--sd-text-3); font-weight: 400; }
.si-card-num {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 4px 0 2px;
  font-size: 24px;
  font-weight: 700;
  line-height: 1.2;
  color: var(--sd-text);
}
.si-card-num.is-muted { font-size: 20px; font-weight: 400; color: var(--sd-text-3); }
.si-card-foot {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  font-size: 12px;
  color: var(--sd-text-2);
}
.si-delta { font-weight: 600; }
.si-delta.is-up { color: var(--sd-danger); }
.si-delta.is-down { color: var(--sd-success); }
.si-delta.is-flat { color: var(--sd-text-3); }

/* 目标节奏徽标：落后 / 领先 / 持平 */
.si-pacing-tag {
  padding: 1px 8px;
  border: 1px solid var(--sd-border);
  border-radius: 10px;
  font-size: 12px;
  font-weight: 400;
  color: var(--sd-text-2);
}
.si-pacing-tag.is-ahead { color: var(--sd-success); border-color: var(--sd-success); }
.si-pacing-tag.is-behind { color: var(--sd-danger); border-color: var(--sd-danger); }

/* 第 4 张卡内的四个经营质量小项 */
.si-quality {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 6px 10px;
  margin-top: 6px;
}
.si-quality-item { display: flex; flex-direction: column; }
.si-quality-num { font-size: 15px; font-weight: 600; line-height: 1.3; color: var(--sd-text); }
.si-quality-lab { font-size: 11px; color: var(--sd-text-3); }

/* ---------- ② 分析参数 + 口径说明 ---------- */
.si-param {
  margin-bottom: 14px;
  padding: 12px 14px;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface-2);
}
.si-param-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.si-param-lab { font-size: 13px; color: var(--sd-text-2); }
.si-param-lab.is-plain { font-size: 12px; color: var(--sd-text-3); }
.si-param-value { font-size: 15px; font-weight: 600; color: var(--sd-text); }
.si-param-note { margin-top: 8px; font-size: 12px; line-height: 1.75; color: var(--sd-text-3); }
/* 口径公式单独一段：老板必须能看懂数字是怎么算出来的，所以不和"数据来源说明"挤在一起 */
.si-param-note.is-formula {
  padding-top: 8px;
  border-top: 1px dashed var(--sd-border);
  color: var(--sd-text-2);
}

/* ---------- ③ 行动建议 ---------- */
.si-advice {
  margin-bottom: 10px;
  padding: 10px 12px;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface);
}
.si-advice:last-child { margin-bottom: 0; }
/* 只改边框色、不铺底色也不加左侧竖条：
   铺底色会让多条建议堆成一片花色；左侧竖条已在「激励活动公式条」被用户否掉。
   边框色 + 标签配色（tag theme）已足够区分严重度。 */
.si-advice.is-warn { border-color: var(--sd-danger); }
.si-advice.is-info { border-color: var(--sd-brand); }
.si-advice.is-good { border-color: var(--sd-success); }
.si-advice-head { display: flex; align-items: center; gap: 8px; margin-bottom: 4px; }
.si-advice-title { font-size: 14px; font-weight: 600; color: var(--sd-text); }
.si-advice-detail { font-size: 12px; line-height: 1.75; color: var(--sd-text-2); }

/* ---------- ④ 销售趋势 ---------- */
.si-growth { font-size: 12px; color: var(--sd-text-2); }
.si-growth.is-muted { color: var(--sd-text-3); }
.si-growth b.is-up { color: var(--sd-danger); }
.si-growth b.is-down { color: var(--sd-success); }
.si-growth b.is-flat { color: var(--sd-text-3); }

.si-trend { display: flex; align-items: flex-end; gap: 6px; padding-top: 6px; overflow-x: auto; }
.si-trend-col {
  flex: 1 0 46px;
  min-width: 46px;
  display: flex;
  flex-direction: column;
  align-items: center;
}
.si-trend-val { margin-bottom: 4px; font-size: 11px; color: var(--sd-text-3); white-space: nowrap; }
/* 百分比高度的柱必须有"确定高度"的父容器才生效 ——
   父元素高度 auto 时 height:% 会解析成 0，整排柱子会集体消失（这是真实踩过的坑）。 */
.si-trend-bar-wrap {
  width: 100%;
  height: 120px;
  display: flex;
  align-items: flex-end;
  justify-content: center;
}
.si-trend-bar { width: 60%; max-width: 34px; border-radius: 3px 3px 0 0; background: var(--sd-brand); }
.si-trend-lab { margin-top: 6px; font-size: 11px; color: var(--sd-text-3); }

/* ---------- ⑤ 品类结构 ---------- */
.si-cat-name { font-size: 13px; font-weight: 600; color: var(--sd-text); }
.si-cat-bar { margin-top: 4px; height: 4px; border-radius: 2px; background: var(--sd-border); overflow: hidden; }
.si-cat-bar-fill { height: 100%; border-radius: 2px; background: var(--sd-brand); }
.si-cat-advice { font-size: 12px; line-height: 1.6; color: var(--sd-text-2); }

/* ---------- ⑥ 款式排行 ---------- */
.si-rank-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 0;
  border-bottom: 1px dashed var(--sd-border);
}
.si-rank-row:last-child { border-bottom: none; }
/* 第 4 名之后不沿用金银铜配色，避免"前 3 名之外"看起来也像获奖 */
.rank-no.rank-n { color: var(--sd-text-3); }
.si-rank-cat { margin-left: 6px; font-size: 11px; font-weight: 400; color: var(--sd-text-3); }
.si-rank-facts {
  min-width: 210px;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 2px;
}
.si-rank-sales { font-size: 14px; font-weight: 600; color: var(--sd-text); }
.si-rank-qty { font-size: 11px; color: var(--sd-text-3); }
.si-rank-margin { font-size: 11px; }
.si-rank-margin.is-good { color: var(--sd-success); }
.si-rank-margin.is-warn { color: var(--sd-warning); }

/* ---------- ⑦ 客流时段 ---------- */
.si-hour-row { display: flex; align-items: center; gap: 10px; padding: 5px 0; }
.si-hour-lab { flex: 0 0 48px; font-size: 12px; color: var(--sd-text-2); }
.si-hour-bar-wrap { flex: 1; height: 8px; border-radius: 4px; background: var(--sd-border); overflow: hidden; }
.si-hour-bar { height: 100%; border-radius: 4px; background: var(--sd-brand); }
.si-hour-facts { flex: 0 0 132px; text-align: right; font-size: 12px; color: var(--sd-text-2); }

/* ---------- 窄屏 ---------- */

/* ==========================================================================
   文案助手 · 内容工作台（.cs-*）
   说明：本区块统一用 cs- 前缀做命名空间，避免与其它页面的同名类互相污染。
   文案正文必须用 white-space: pre-wrap —— 文案里带换行（朋友圈 3 段式），
   默认 normal 会把换行压掉，老板复制出来就是一大坨。
   ========================================================================== */

/* ---------- 场景选择卡 ---------- */
.cs-ready { font-size: 12px; color: var(--sd-text-3); }
.cs-scenes {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(232px, 1fr));
  gap: 10px;
}
.cs-scene {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 10px 12px;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface);
  text-align: left;
  cursor: pointer;
  font: inherit;
  transition: border-color 0.15s, background 0.15s;
}
.cs-scene:hover { border-color: var(--sd-brand); }
.cs-scene.is-on {
  border-color: var(--sd-brand);
  background: var(--sd-brand-a08);
}
/* 不可用场景压暗但仍可点：点了会弹出"为什么不可用"，比完全禁掉更好用 */
.cs-scene.is-off { opacity: 0.62; background: var(--sd-surface-2); cursor: help; }
.cs-scene-label {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--sd-text);
}
.cs-scene-badge {
  padding: 0 6px;
  border: 1px solid var(--sd-border);
  border-radius: 9px;
  font-size: 11px;
  font-weight: 400;
  color: var(--sd-text-3);
}
.cs-scene-when { font-size: 12px; line-height: 1.6; color: var(--sd-text-3); }
.cs-scene-guide {
  margin-top: 12px;
  padding: 8px 12px;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface-2);
  font-size: 12px;
  line-height: 1.7;
  color: var(--sd-text-2);
}

/* ---------- 渠道筛选 + 合规汇总 ---------- */
.cs-toolbar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 14px;
  padding: 10px 12px;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface-2);
}
.cs-toolbar-lab { font-size: 13px; color: var(--sd-text-2); }
.cs-toolbar-lab.is-plain { font-size: 12px; color: var(--sd-text-3); }
.cs-spacer { flex: 1; }
.cs-chip {
  padding: 3px 12px;
  border: 1px solid var(--sd-border);
  border-radius: 14px;
  background: var(--sd-surface);
  font: inherit;
  font-size: 12px;
  color: var(--sd-text-2);
  cursor: pointer;
  transition: all 0.15s;
}
.cs-chip:hover { border-color: var(--sd-brand); }
.cs-chip.is-on {
  border-color: var(--sd-brand);
  background: var(--sd-brand);
  color: #fff;
}
.cs-alert {
  padding: 2px 10px;
  border-radius: 12px;
  border: 1px solid var(--sd-border);
  font-size: 12px;
}
.cs-alert.is-error { border-color: var(--sd-danger); color: var(--sd-danger); }
.cs-alert.is-warn { border-color: var(--sd-warning); color: var(--sd-warning); }
.cs-alert.is-ok { border-color: var(--sd-success); color: var(--sd-success); }

/* ---------- 文案卡片 ---------- */
.cs-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(420px, 1fr));
  gap: 12px;
  margin-bottom: 14px;
}
.cs-card {
  display: flex;
  flex-direction: column;
  padding: 12px 14px;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface);
}
.cs-card-head {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 4px;
}
.cs-card-title { font-size: 14px; font-weight: 600; color: var(--sd-text); }
.cs-badge {
  padding: 1px 8px;
  border: 1px solid var(--sd-border);
  border-radius: 10px;
  font-size: 11px;
  color: var(--sd-text-3);
}
.cs-badge.is-channel { border-color: var(--sd-brand); color: var(--sd-brand); }
.cs-badge.is-ok { border-color: var(--sd-success); color: var(--sd-success); }
.cs-badge.is-warn { border-color: var(--sd-warning); color: var(--sd-warning); }
.cs-badge.is-error { border-color: var(--sd-danger); color: var(--sd-danger); }
.cs-tone { margin-bottom: 8px; font-size: 11.5px; line-height: 1.6; color: var(--sd-text-3); }
/* pre-wrap：保住文案里的换行（朋友圈是 3 段式），否则复制出来是一坨 */
.cs-text {
  padding: 10px 12px;
  border: 1px dashed var(--sd-border);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface-2);
  font-size: 13px;
  line-height: 1.85;
  color: var(--sd-text);
  white-space: pre-wrap;
  word-break: break-word;
}
.cs-issues { margin-top: 8px; display: flex; flex-direction: column; gap: 4px; }
.cs-issue {
  padding: 6px 10px;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  font-size: 12px;
  line-height: 1.7;
  color: var(--sd-text-2);
}
.cs-issue b { margin-right: 6px; }
.cs-issue.is-error { border-color: var(--sd-danger); }
.cs-issue.is-error b { color: var(--sd-danger); }
.cs-issue.is-warn { border-color: var(--sd-warning); }
.cs-issue.is-warn b { color: var(--sd-warning); }
.cs-issue.is-info b { color: var(--sd-brand); }
.cs-facts { margin-top: 8px; font-size: 12px; color: var(--sd-text-2); }
.cs-facts summary { cursor: pointer; color: var(--sd-text-3); }
.cs-facts ul { margin: 6px 0 0; padding-left: 20px; line-height: 1.8; }
.cs-facts b { color: var(--sd-text); }
.cs-card-foot {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--sd-border);
}
.cs-card-scene { font-size: 12px; color: var(--sd-text-3); }

/* ---------- 一周发布计划 ---------- */
.cs-week-note { margin-bottom: 10px; font-size: 12px; line-height: 1.7; color: var(--sd-text-2); }
.cs-mix { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 12px; }
.cs-mix-item {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 8px 12px;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface-2);
  font-size: 12px;
}
.cs-mix-label { font-weight: 600; color: var(--sd-text); }
.cs-mix-count { color: var(--sd-text-2); }
.cs-mix-count.is-off { color: var(--sd-danger); }
.cs-mix-block { color: var(--sd-text-3); }
.cs-week { display: flex; flex-direction: column; gap: 6px; }
.cs-week-row {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 6px 10px;
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface-2);
  font-size: 12px;
}
/* 当天没有可用场景时压暗，并保留"可用其它场景替代"的提示 */
.cs-week-row.is-off { opacity: 0.62; }
.cs-week-day { flex: 0 0 40px; font-weight: 600; color: var(--sd-text); }
.cs-week-scene { flex: 0 0 92px; color: var(--sd-text-2); }
.cs-week-slots { flex: 1; color: var(--sd-text-3); line-height: 1.6; }

/* ---------- 数据边界声明 ---------- */
.cs-boundary {
  padding: 10px 12px;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  margin-bottom: 8px;
  background: var(--sd-surface);
}
.cs-boundary:last-child { margin-bottom: 0; }
.cs-boundary-title { font-size: 13px; font-weight: 600; color: var(--sd-text); margin-bottom: 2px; }
.cs-boundary-detail { font-size: 12px; line-height: 1.75; color: var(--sd-text-2); }

/* ---------- 窄屏 ---------- */

/* ==========================================================================
   表格序号列（src/tableSeq.js 的 .sd-seq-cell）
   覆盖范围：全站 45 张数据表格的第一列（前缀勾选框列的表，序号在勾选框之后）。
   设计取舍：序号是**辅助列**，不能让它的视觉权重盖过业务数据 —— 所以用次要文字色，
            并把数字设为等宽（tabular-nums）。等宽这条不是审美问题：1 与 1000 位数不同，
            比例字体下每一行的序号左右边界都不一样，整列看起来"歪着抖"，
            而用户是**纵向扫**这一列来找"第几行"的。
   ⚠️ 颜色走主题变量而不是写死 #909399：切主题时序号列要跟着变，
      写死会出现"别的文字都变了、只有序号列还是老灰"（本项目在图表配色上踩过同一类坑）。
   ========================================================================== */
.t-table .sd-seq-cell {
  color: var(--sd-text-3);
  font-variant-numeric: tabular-nums;
  font-size: 12px;
}

/* ==========================================================================
   表格列筛选（表头筛选单元 .tch-* + 筛选摘要条 .tf-*）
   覆盖范围：库存明细表等宽表的「每列表头 → 漏斗按钮 → 弹出面板」。
   设计取舍：面板整体比表头列宽大得多，但它是 TDesign Popup 的浮层（portal 到 body），
            不会被表格的宽度与裁剪影响，所以列宽仍保持紧凑。
   ========================================================================== */

/* ---------- 表头单元（列名 + 筛选按钮） ---------- */
.tch {
  display: flex;
  align-items: center;
  gap: 4px;
  /* ⚠️ 两行 min-width 是**刻意**的，别删上面那行或调换顺序：
     ① min-width:0 —— 极端窄屏下允许列名被压缩省略，而不是把漏斗/排序按钮挤出列外；
     ② min-width:max-content —— 但**表头内容优先于"把列压窄"**。
        加排序按钮后实测：表格是 table-layout:auto，在有搜索关键字/不同行数据的上下文里，
        浏览器会把固定列宽重新分配、压掉几像素，于是 尺码→「尺…」、近7天销量→「近7…」、
        预计可售→「预计…」 等 5 个列名被截断（表格总宽明明没变）。
        用 max-content 让列宽**至少装得下表头内容**，多出来的空间由唯一可伸缩的「款」列吸收。
        （实测：表头内容合计约 970px < 表格 1375px，所以不会把表格顶出横向滚动。） */
  min-width: max-content;
}
.tch-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tch-btn {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border-radius: 4px;
  cursor: pointer;
  /* 默认低调：11 个漏斗一起高亮会把表头变成一排醒目的图标，反而看不清列名 */
  color: var(--sd-text-3);
  transition: background 0.15s, color 0.15s;
}
.tch-btn:hover { background: var(--sd-brand-a12); color: var(--sd-brand); }
.tch-btn .ip-icon svg { width: 13px; height: 13px; }
/* 激活态（该列正在筛选）：品牌色 + 浅底，与摘要条呼应，让用户知道「这几列在筛」 */
.tch-btn-on { color: var(--sd-brand); background: var(--sd-brand-a12); }

/* 升 / 降序按钮（在筛选漏斗的右侧，用户原话：「给筛选按钮右侧增加」）。
   ⚠️ **上下摆放**（用户原话：「向上向下括号，应该上下摆放，而不是左右」）：
      这是排序控件的行业通行形态 —— Ant Design / TDesign / Element 的排序都是
      一枚上箭头压着一枚下箭头，横着的"↑↓"看着像两个独立按钮，指向也不清楚。
   ⚠️ 这两个数字是**实测**定出来的，改之前请先量这三个数，别凭手感调：
      ① **纵高预算**：表头单元格上下内边距各 8px，13 列那页表头高 39px
         → 内容区只剩 **23px**。两枚按钮上下叠起来必须 ≤22px，
         否则**全站所有带排序的表**表头都会被顶高（表头是共用样式，不是这一页的事）。
         所以单枚取 11px（11×2 = 22px），刚好卡在预算里，表头仍是 39px。
      ② **横占宽**：原来是左右并排 13×2 = 26px，改成上下后只占 **14px** ——
         每个可排序列多让出 12px 给列名。这张 13 列的表横向本就吃紧，
         所以这次改形态是"顺带把宽度也松了一口"，不是纯视觉调整。
      ③ **图标尺寸**：svg 必须 ≤ 按钮高（10 ≤ 11）。写大一点（如 11px 放进 11px 的
         按钮里）会被 flex 内容高度撑破，单枚实测会变成 12px → 叠起来 24px → 表头又变高。
      历史上这里踩过一次：把按钮从 16px 放大后，11 列里 **9 个列名被挤成省略号**
      （「颜色」→「颜…」）。改完务必量列名有没有被截断。 */
.tch-sorts {
  flex: 0 0 auto;
  display: inline-flex;
  /* column = 上下摆放（这是"向上向下"两个按钮的摆放方向，不是随便定的） */
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0;
}
.tch-sort {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  height: 11px;
  border-radius: 3px;
  cursor: pointer;
  color: var(--sd-text-3);
  transition: background 0.15s, color 0.15s;
}
.tch-sort:hover { background: var(--sd-brand-a12); color: var(--sd-brand); }
.tch-sort .ip-icon svg { width: 10px; height: 10px; }
/* 激活态（当前正按这个方向排序）：与漏斗激活态同一套表达，一眼看出「哪列在用哪个方向」 */
.tch-sort-on { color: var(--sd-brand); background: var(--sd-brand-a12); }

/* ---------- 弹出面板 ---------- */
/* 覆盖 TDesign Popup 内容区的默认内边距：面板自己控制留白，否则四周会多出一圈空隙 */
.tch-pop .t-popup__content { padding: 0; border-radius: 10px; overflow: hidden; }
.tch-panel { width: 236px; padding: 10px 12px 8px; }
.tch-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
.tch-title { font-size: 13px; font-weight: 600; color: var(--sd-text); }
.tch-link { font-size: 12px; color: var(--sd-brand); cursor: pointer; }
.tch-link:hover { text-decoration: underline; }
.tch-search { margin-bottom: 6px; }

/* 选项区：超过 6 个选项时会出现搜索框，所以这里限制高度并允许滚动 */
.tch-opts { display: block; max-height: 216px; overflow-y: auto; }
.tch-opt {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 3px 4px;
  border-radius: 6px;
}
.tch-opt:hover { background: var(--sd-surface-2); }
.tch-opt .t-checkbox { min-width: 0; }
/* 候选项的行数：勾选前先知道「这个值有多少条」，避免勾完才发现筛出来是空的 */
.tch-count {
  flex: 0 0 auto;
  font-size: 12px;
  color: var(--sd-text-3);
  font-variant-numeric: tabular-nums;
}
.tch-empty { padding: 12px 4px; font-size: 12px; color: var(--sd-text-3); text-align: center; }

/* ---------- 数值区间 ---------- */
.tch-range { display: flex; align-items: center; gap: 6px; }
.tch-range .t-input { flex: 1 1 0; min-width: 0; }
.tch-sep { flex: 0 0 auto; color: var(--sd-text-3); }
.tch-hint { margin-top: 6px; font-size: 12px; color: var(--sd-text-3); }

/* 快捷档位：门店最常点的几档，点一下立即生效并收起面板 */
.tch-quick { margin-top: 8px; display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.tch-quick-title { width: 100%; font-size: 12px; color: var(--sd-text-3); }
.tch-quick-item {
  padding: 2px 8px;
  font-size: 12px;
  color: var(--sd-text-2);
  background: var(--sd-surface-2);
  border: 1px solid var(--sd-border);
  border-radius: 12px;
  cursor: pointer;
  transition: all 0.15s;
}
.tch-quick-item:hover { border-color: var(--sd-brand); color: var(--sd-brand); }
.tch-quick-on { background: var(--sd-brand-a12); border-color: var(--sd-brand); color: var(--sd-brand); }

.tch-switch { margin-top: 8px; display: flex; flex-direction: column; gap: 4px; }
.tch-err {
  margin-top: 8px;
  padding: 6px 8px;
  font-size: 12px;
  line-height: 1.5;
  color: var(--sd-danger);
  background: rgba(227, 93, 93, 0.08);
  border-radius: 6px;
}

.tch-foot {
  margin-top: 10px;
  padding-top: 8px;
  border-top: 1px solid var(--sd-border);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.tch-picked { font-size: 12px; color: var(--sd-text-3); }
.tch-btns { display: flex; gap: 6px; }

/* ---------- 筛选摘要条 ---------- */
/* 为什么必须显眼：筛选之后表格只剩几行，用户需要一眼确认「是筛掉了，不是数据丢了」，
   并且能逐条取消（只撤其中一个条件，而不是整个人重来）。 */
.tf-bar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 10px;
  padding: 8px 10px;
  background: var(--sd-brand-a08);
  border: 1px solid var(--sd-brand-a12);
  border-radius: var(--sd-radius-sm);
}
.tf-bar-title {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 12px;
  font-weight: 600;
  color: var(--sd-brand-strong);
}
.tf-bar-title .ip-icon svg { width: 13px; height: 13px; }
.tf-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 6px 2px 8px;
  font-size: 12px;
  color: var(--sd-text);
  background: var(--sd-surface);
  border: 1px solid var(--sd-border);
  border-radius: 12px;
}
.tf-chip b { font-weight: 600; color: var(--sd-text-2); margin-right: 2px; }
.tf-chip-x {
  cursor: pointer;
  color: var(--sd-text-3);
  font-size: 13px;
  line-height: 1;
}
.tf-chip-x:hover { color: var(--sd-danger); }
.tf-hit { font-size: 12px; color: var(--sd-text-2); font-variant-numeric: tabular-nums; }

/* 窄屏：摘要条条件多时换行即可，chip 不做特殊处理 */

/* ==========================================================================
   员工权限设置页（.perm-*）与「无权限」兜底页（.no-perm-*）
   设计取舍：权限勾选项按业务分组排成两列 —— 20 个模块单列会让页面很长，
   老板要来回滚才能看全；两列在一屏内能看全大部分分组。
   ========================================================================== */
.perm-layout {
  display: grid;
  grid-template-columns: 272px minmax(0, 1fr);
  gap: 14px;
  margin-top: 14px;
  align-items: start;
}
.perm-side .t-card__header,
.perm-main .t-card__header { display: flex; align-items: center; justify-content: space-between; }

/* ---------- 左栏：员工列表 ---------- */
.perm-staff {
  padding: 8px 10px;
  border-radius: 8px;
  cursor: pointer;
  transition: background 0.15s;
}
.perm-staff:hover { background: var(--sd-surface-2); }
/* 选中态：品牌色浅底 + 姓名加粗（两重强化）。
   为什么不是左侧色条：产品要求全站不要「模块左侧彩色竖线」。
   但原注释里的顾虑仍然成立 —— 一列员工里「当前在配谁」必须一眼可辨，
   否则很容易给错人配权限、而且配错了不好发现。所以用 a12 底色（比 hover 明显）
   加姓名加粗来替代色条，辨识度不降。 */
.perm-staff-on {
  background: var(--sd-brand-a12);
}
.perm-staff-on .perm-staff-name {
  font-weight: 700;
}
.perm-staff-line { display: flex; align-items: center; gap: 6px; }
.perm-staff-name { font-size: 13px; font-weight: 500; color: var(--sd-text); }
.perm-post {
  font-size: 11px;
  color: var(--sd-text-2);
  background: var(--sd-surface-2);
  border: 1px solid var(--sd-border);
  border-radius: 4px;
  padding: 0 4px;
}
.perm-post-leave { color: var(--sd-warning); border-color: var(--sd-warning); }
.perm-staff-meta {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 2px;
  font-size: 12px;
  color: var(--sd-text-3);
  font-variant-numeric: tabular-nums;
}
.perm-tag-default { color: var(--sd-warning); }
.perm-side-note {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px dashed var(--sd-border);
  font-size: 12px;
  line-height: 1.6;
  color: var(--sd-text-3);
}

/* ---------- 右栏：权限矩阵 ---------- */
.perm-head-ops { display: flex; align-items: center; gap: 8px; }
.perm-warn {
  margin-bottom: 12px;
  padding: 8px 10px;
  font-size: 12px;
  line-height: 1.6;
  color: var(--sd-warning);
  background: rgba(217, 130, 43, 0.08);
  border: 1px solid rgba(217, 130, 43, 0.25);
  border-radius: 8px;
}
.perm-groups { display: flex; flex-direction: column; gap: 12px; }
.perm-group { border: 1px solid var(--sd-border); border-radius: 10px; overflow: hidden; }
.perm-group-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 6px 10px;
  background: var(--sd-surface-2);
  border-bottom: 1px solid var(--sd-border);
}
.perm-group-name { font-size: 13px; font-weight: 500; color: var(--sd-text); }
.perm-group-ops { display: flex; align-items: center; gap: 2px; }
.perm-items {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 2px 12px;
  padding: 8px 10px;
}
.perm-item {
  padding: 5px 6px;
  border-radius: 6px;
  min-width: 0;
}
.perm-item:hover { background: var(--sd-surface-2); }
.perm-item-desc {
  display: block;
  margin-top: 1px;
  padding-left: 24px;
  font-size: 12px;
  line-height: 1.5;
  color: var(--sd-text-3);
}
.perm-foot {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--sd-border);
}
.perm-foot-count { font-size: 13px; color: var(--sd-text-2); font-variant-numeric: tabular-nums; }
.perm-foot-count b { color: var(--sd-brand); font-size: 14px; }
.perm-foot-presets { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.perm-foot-label { font-size: 12px; color: var(--sd-text-3); }
.perm-dirty { font-size: 12px; color: var(--sd-warning); }
.perm-zero {
  margin-top: 10px;
  padding: 8px 10px;
  font-size: 12px;
  line-height: 1.6;
  color: var(--sd-text-2);
  background: var(--sd-surface-2);
  border-radius: 8px;
}

/* ---------- 无任何权限时的整页兜底 ---------- */
.no-perm-full {
  margin-top: 40px;
  padding: 48px 24px;
  text-align: center;
  background: var(--sd-surface);
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius);
}
.no-perm-title { font-size: 15px; font-weight: 500; color: var(--sd-text); margin-bottom: 8px; }
.no-perm-desc { font-size: 13px; line-height: 1.8; color: var(--sd-text-2); max-width: 460px; margin: 0 auto; }
.no-perm-meta { margin-top: 12px; font-size: 12px; color: var(--sd-text-3); }

/* 窄屏：左右两栏改为上下堆叠，权限项收为一列（两列会把描述文字挤成窄条） */

/* ==========================================================================
   员工业绩：统计区间切换 + 月目标达成 + 订单下钻
   ========================================================================== */
.emp-scope {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 12px;
}
.emp-scope-label { font-size: 13px; color: var(--sd-text-2); }
/* 口径说明必须挨着选择器：老板切换区间时最需要看到「两个页面的口径不一样」这句提醒 */
.emp-scope-note { flex: 1 1 280px; font-size: 12px; line-height: 1.6; color: var(--sd-text-3); }

/* 销售额做成链接样式：提示"可点开看明细"，又不像按钮那样抢走注意力 */
.emp-drill-link { color: var(--sd-brand); font-weight: 500; cursor: pointer; }
.emp-drill-link:hover { text-decoration: underline; }
.emp-gap { color: var(--sd-warning); font-size: 12px; }
.emp-reached { color: var(--sd-success); font-size: 12px; margin-left: 6px; }

.emp-drill-summary {
  display: flex;
  align-items: baseline;
  gap: 18px;
  flex-wrap: wrap;
  font-size: 13px;
  color: var(--sd-text-2);
  margin-bottom: 6px;
}
.emp-drill-summary b { color: var(--sd-text); font-size: 14px; font-variant-numeric: tabular-nums; }
.emp-drill-note { margin-bottom: 10px; font-size: 12px; line-height: 1.6; color: var(--sd-text-3); }

/* ==========================================================================
   AI客服（对内：系统使用帮助）页面
   --------------------------------------------------------------------------
   为什么三种来源只靠「背景色 + 文字标签」区分，而不用一条左侧色条：
     ① 本项目明令禁止「模块左侧彩色竖线」（还有守卫脚本专门扫 >=3px 的竖条），
        所以来源类型一律不引入任何竖条；
     ② 三种来源必须一眼可辨 —— 把「本地知识库」的回答看成「模型回答」
        是这一页最严重的错，只靠一行小字标签不够醒目，配一层底色才算真的区分开。
   ========================================================================== */
.aihelp-wrap {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
/* 顶部状态条：在用户提问前就说清「能不能用、为什么」 */
.aihelp-status {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 10px 12px;
  border-radius: var(--sd-radius-sm);
  border: 1px solid var(--sd-border);
  background: var(--sd-surface-2);
  font-size: 13px;
  line-height: 1.6;
  color: var(--sd-text-2);
}
/* 降级态用提示色而不是红色：未配置模型不是「出错」，是「能力没开」 */
.aihelp-status.is-degraded {
  background: color-mix(in srgb, var(--sd-warning) 8%, transparent);
  border-color: color-mix(in srgb, var(--sd-warning) 35%, transparent);
  color: var(--sd-text);
}
/* 对话区：固定可视高度 + 内部滚动，避免长会话把整页顶得很长 */
.aihelp-thread {
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-height: 300px;
  max-height: 54vh;
  overflow-y: auto;
  padding: 12px;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius);
  background: var(--sd-surface);
}
.aihelp-msg {
  display: flex;
}
.aihelp-msg.is-user {
  justify-content: flex-end;
}
.aihelp-bubble {
  max-width: 78%;
  padding: 10px 12px;
  border-radius: var(--sd-radius);
  background: var(--sd-surface-2);
}
.aihelp-msg.is-user .aihelp-bubble {
  background: var(--sd-brand-a12);
}
/* 三种「非模型」来源各给一层底色 + 一圈边框。
   ⚠️ 为什么要加边框：本项目的设计令牌里**没有 --sd-info**（只有 brand/success/warning/danger），
   所以"本地知识库"只能用中性色的 --sd-bg 表达 —— 它与 --sd-surface-2 的差别很小，
   只靠底色不够醒目；补一圈全边框同时给出**不依赖颜色**的信号（"这不是默认来源"）。
   ⚠️ 边框必须是**全边框**，不能用左侧色条 —— 本项目明令禁止左侧彩色竖线，且有守卫脚本在扫。 */
.aihelp-bubble.is-local {
  background: var(--sd-bg);
  border: 1px solid var(--sd-brand-soft);
}
.aihelp-bubble.is-mock {
  background: color-mix(in srgb, var(--sd-warning) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--sd-warning) 40%, transparent);
}
/* 报错必须长得像报错：绝不与一条正常回答一样 */
.aihelp-bubble.is-error {
  background: color-mix(in srgb, var(--sd-danger) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--sd-danger) 40%, transparent);
}
.aihelp-src {
  margin-bottom: 6px;
  font-size: 11px;
  color: var(--sd-text-3);
}
.aihelp-bubble.is-error .aihelp-src {
  color: var(--sd-danger);
}
/* pre-wrap：回答里带换行与分点，必须原样保留（用 div 而非 pre，避免等宽字体） */
.aihelp-text {
  font-size: 13px;
  line-height: 1.75;
  color: var(--sd-text);
  white-space: pre-wrap;
  word-break: break-word;
}
.aihelp-typing {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  color: var(--sd-text-3);
}
/* 输入区 */
.aihelp-input {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.aihelp-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}
/* 常见问题（快捷提问） */
.aihelp-sug {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}
.aihelp-sug-title {
  font-size: 12px;
  color: var(--sd-text-3);
}
.aihelp-sug-item {
  padding: 5px 12px;
  border: 1px solid var(--sd-border);
  border-radius: 999px;
  background: var(--sd-surface);
  font-size: 12px;
  color: var(--sd-text-2);
  cursor: pointer;
  transition: all 0.15s ease;
}
.aihelp-sug-item:hover {
  border-color: var(--sd-brand);
  color: var(--sd-brand);
}
/* 边界声明：不显眼但必须在，否则用户会把模型的推测当成系统行为 */
.aihelp-note {
  font-size: 12px;
  line-height: 1.7;
  color: var(--sd-text-3);
}

/* ===== AI 免责声明（组件 ai-disclaimer，文案见 src/aiDisclaimer.js）=====
   两种形态：note（整块说明）/ chip（小标签 + title 全文）。
   视觉刻意"轻"（浅底 + 细边框 + 小字）：它是提醒，不该抢走建议本身的注意力；
   但也绝不能藏起来 —— 用户会把"AI 建议进 30 件"当成指令照做。
   ⚠️ 不用左侧色条（全站禁令），用四边 1px 浅边框。 */
.ai-disclaimer {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 8px 12px;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface-2);
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--sd-text-2);
}
.ai-disclaimer .ip-icon {
  flex: none;
  margin-top: 1px;
  color: var(--sd-text-3);
}
.ai-disclaimer .ip-icon svg {
  width: 15px;
  height: 15px;
}
.ai-disclaimer-label {
  flex: none;
  font-weight: 700;
  color: var(--sd-text);
}
.ai-disclaimer-text {
  flex: 1;
  min-width: 0;
}
.ai-disclaimer-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 8px;
  border: 1px solid var(--sd-border);
  border-radius: 999px;
  background: var(--sd-surface-2);
  font-size: 12px;
  color: var(--sd-text-2);
  white-space: nowrap;
}
.ai-disclaimer-chip .ip-icon svg {
  width: 13px;
  height: 13px;
}

/* ==========================================================================
   拿货 / 进货：单据式布局（单据头 + 双模式录入区）
   --------------------------------------------------------------------------
   对齐行业标准：单据头（供应商 / 下单日期 / 预计到货 / 运费 / 备注）在最上，
   录入区居中，明细与合计在下。
   改动前是「两步指示器 + 选款区 + 明细卡」，单据头里只有一个供应商 ——
   下单时间与到货预期无处记录，采购页也就没法提醒「该到货了」。
   ========================================================================== */
.nh-doc {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-bottom: 12px;
  padding: 12px;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface-2);
}
.nh-doc-row {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.nh-doc-label {
  flex: none;
  font-size: 13px;
  color: var(--sd-text-2);
}

/* ============ 采购口径速查（拿货页页首）============
   背景：这里原本是一条通栏的 t-alert（实色蓝底 + 一整段 130 字）。两个问题：
     ① 实色底横幅压在工作页最上方，是整页最重的一块色；
     ② 那段文字其实混着 4 件互不相干的事（收货流程 / 价格口径 / 成本口径 / 边界声明），
        挤成一段就只能逐字读，扫不出来。
   现在改成「三段并列的要点条」：浅底 + 四边 1px 细边框（与本页 .nh-doc 同一套观感），
   每段一个小图标 + 加粗小标题 + 一句说明，一眼能扫完。
   高度从约 80px 降到约 56px —— 这是刻意的：它是「看一次就该记住」的内容，
   不该长期占着工作页最贵的上方空间。
   ========================================================================== */
.nh-rulebar {
  /* 用 grid 而不是 flex：三段要严格等分。flex 会按内容长短分配宽度，
     第二段文字最长时会把第一段挤窄，三栏就不齐了（看着像排版坏了）。
     minmax(0, 1fr) 里的 0 不能省：默认的 minmax(auto, 1fr) 会被长文本撑破容器。 */
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  margin-bottom: 12px;
  padding: 10px 0;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface-2);
}
.nh-rule-item {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  min-width: 0;
  padding: 0 14px;
}
/* 段间分隔：只给第 2、3 段加左边线。第 1 段不加，否则最左侧会多出一条紧贴外框的线。
   ⚠️ 这是「并列项之间的 1px 中性浅灰分隔线」，**不是**全站禁令里那种
      「模块左侧的彩色竖线」—— 禁令针对的是品牌色/橙色 4px 装饰竖条，外观与语义都不同，别混。 */
.nh-rule-item + .nh-rule-item {
  border-left: 1px solid var(--sd-border);
}
.nh-rule-ico {
  /* .ip-icon 自带 margin-right: 8px（那是给无 gap 的菜单项用的）。这里已经用 gap 控制间距，
     不覆盖的话图标与文字之间会变成 16px，明显偏松。 */
  margin-right: 0;
  margin-top: 1px;
}
/* ⚠️ 尺寸必须写在 svg 上：.ip-icon svg 的 18px 是写死的，
   在外面写 font-size 完全不起作用（登录页 .auth-alert 就踩了这一条，是处死样式）。 */
.nh-rule-ico svg {
  width: 15px;
  height: 15px;
}
.nh-rule-body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.nh-rule-name {
  font-size: 12px;
  font-weight: 700;
  color: var(--sd-text);
}
.nh-rule-desc {
  font-size: 12px;
  line-height: 1.6;
  color: var(--sd-text-3);
}

.nh-entry {
  margin-bottom: 12px;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface);
}
.nh-entry-head {
  display: flex;
  align-items: center;
  gap: 18px;
  padding: 10px 12px;
  border-bottom: 1px solid var(--sd-border);
}
/* 录入方式页签：用下划线而不是背景块 —— 背景块会与下方表格的底色打架 */
.nh-tab {
  cursor: pointer;
  padding: 2px 0;
  font-size: 13px;
  color: var(--sd-text-2);
  border-bottom: 2px solid transparent;
}
.nh-tab-on {
  color: var(--sd-brand);
  border-bottom-color: var(--sd-brand);
  font-weight: 500;
}
.nh-matrix-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  padding: 10px 12px;
}
/* 颜色×尺码矩阵：颜色或尺码多时横向会超出，给**横向滚动**而不是压缩格子 ——
   格子被压窄会让数字看不全，那比滚动更难用。 */
.nh-matrix-wrap {
  padding: 0 12px 12px;
  overflow-x: auto;
}
.nh-matrix {
  border-collapse: collapse;
  /* 有空间就用满：早先没设宽度 → 表格按内容收缩，列宽被输入框锁死，
     明明容器有 790px 却只用 389px，格子被压得很窄。 */
  width: 100%;
  /* 尺码多、放不下时**允许超出**（由 .nh-matrix-wrap 横向滚动），不许压缩格子 */
  min-width: max-content;
}
/* 分规格价时一格要塞两个输入（数量 + 进价），必须**上下堆叠**。
   ⚠️ 实测过：并排时每个输入的内层文本区只剩 18px，连 3 位数的进价 138
   都会显示成「1...」（scrollWidth 21 > clientWidth 18）——
   与 .nh-matrix-wrap 注释里写的"格子被压窄会让数字看不全"是同一个问题。 */
.nh-cell-stack {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
}
.nh-matrix th,
.nh-matrix td {
  padding: 4px 6px;
  border: 1px solid var(--sd-border);
  text-align: center;
}
.nh-matrix th {
  font-size: 12px;
  font-weight: 500;
  color: var(--sd-text-2);
  background: var(--sd-surface-2);
}
.nh-matrix-color {
  padding-left: 10px !important;
  font-size: 13px;
  color: var(--sd-text);
  text-align: left !important;
  white-space: nowrap;
}
.nh-matrix-op {
  white-space: nowrap;
}
/* 档案里没有的色码：置灰 —— 视觉上就说明「这格不能填」，而不是填完提交才被拦 */
.nh-cell-off {
  background: var(--sd-surface-2);
}
.nh-matrix-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  margin-top: 10px;
}
.nh-matrix-actions {
  display: inline-flex;
  gap: 8px;
}
.nh-lines-head {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 10px;
}

/* ==========================================================================
   AI 采购建议（拿货 / 采购进货页）
   --------------------------------------------------------------------------
   三条显示规矩：
     ① 建议数要"看得见但压得住" —— 用次级色 + 12px，不能比用户填的数量更抢眼
        （本页真正的主角是进货价与数量，AI 只是参考）；
     ② 「不建议补」与「无数据」必须能一眼分开：前者是结论（有数据、建议 0 件），
        后者是"这个色码还没有销量"，混在一起会让人以为是坏了；
     ③ 操作条上必须写下"改几行"，因为一键采纳会改一整张单的数量。
   ========================================================================== */
.nh-cell-advice {
  font-size: 12px;
  line-height: 1.4;
  color: var(--sd-brand);
  white-space: nowrap;
  cursor: default;
}
.nh-cell-advice.is-zero {
  color: var(--sd-text-3);
}
.nh-advice-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 10px;
  padding: 6px 10px;
  border: 1px dashed var(--sd-border);
  border-radius: 8px;
  background: var(--sd-surface-2);
}
/* 设置弹窗：参数行里的输入框与说明必须**上下排列** ——
   t-form-item 内容区是 flex + nowrap，并列写会把说明挤到同一行并把控件压变形。 */
.nh-advice-field {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.nh-advice-field-row {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

/* ==========================================================================
   入库操作台：两条录入路径**并列成两列**
   --------------------------------------------------------------------------
   用户要求「他俩可以并列成两个模块，双列」。
   ⚠️ 用 CSS grid 而不是 TDesign 的 t-row / t-col：后者的列是按**视口断点**响应式的，
      窄屏会自己堆叠，而本项目要求「窗口变窄不许改排版」（见 theme.css 顶部那一节）。
      `1fr 1fr` 是固定双列，任何宽度下都并排。
   ========================================================================== */
.intake-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
}
.intake-col {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface-2);
}
.intake-col-head {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  font-weight: 500;
  color: var(--sd-text);
}
/* 两列的按钮行：让「定位入库单 / 确认入库」在两个卡片里的**同一高度**上，
   否则两个卡片看起来一高一低（视觉上像是没对齐）。 */
.intake-col-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}
.intake-msg {
  margin-top: 12px;
}
/* 收货核对面板：与上面两列留出间距，视觉上属于"选中单之后才展开的一步" */
.intake-receive {
  margin-top: 16px;
  padding: 12px;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface);
}
.intake-receive-head {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 10px;
}
.intake-receive-title {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  font-weight: 500;
  color: var(--sd-text);
}
.intake-receive-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  margin-top: 12px;
}
/* 差异数值：用警示色（少送/多送都是要立刻看见的事） */
.nh-warn-inline {
  font-weight: 500;
  color: var(--sd-warning);
}

/* ===== 采购台账：表格上方的工具行 =====
   位置语义与「库存明细」的 .inv-toolbar 完全一致（工具行紧贴表格正上方），
   内容分两边：左＝状态摘要，右＝「＋ 新建采购单」（本页唯一的主操作）。
   为什么摘要与按钮**合成一行**、而不是各占一行：
     两者信息量都很小，各占一行会白丢约 44px 竖向空间，并把主操作按钮往外推 ——
     用户的要求正是「按钮紧贴表格右上角」，所以按钮必须在**最靠表格的那一行**上。
   摘要复用全站统一的 .summary-line（正文同字号 / 左对齐），
   与单据中心 / 出库台账 / 财务 / 调拨同一个视觉语言，**不要再冒出 t-statistic 大号数字**。 */
.po-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 12px;
}
/* ⚠️ `.summary-line` 自带 `margin-bottom:12px`（它单独成行时需要）。放进 flex 行里会让它整体
   偏上、与右侧按钮的垂直中心错开，所以在这里归零；行间距统一由 .po-toolbar 的 margin-bottom 提供。 */
.po-toolbar .summary-line {
  margin-bottom: 0;
}
/* 右侧操作组靠右。用 `margin-left:auto` 而不是只靠父容器的 justify-content ——
   本项目踩过「容器没撑开时 justify 看着像没生效，控件像紧跟在标题后面」的坑
   （见 .inv-card-head / .inv-card-head-ops 两处注释），这里是同一条经验的复用。 */
.po-toolbar-ops {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-left: auto;
}

/* ===== 采购台账：展开区 ===== */
/* 单据头补充信息（下单/预计到货/货款/运费/备注）：一行小字，
   放在明细之前 —— 先给"这笔单是什么"，再给"里面有什么"。 */
.po-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  margin-bottom: 8px;
  font-size: 12px;
  color: var(--sd-text-2);
}
/* 差异原因：与明细同一行显示，用警示色 —— 它是"少送了为什么"的唯一答案 */
.pl-reason {
  font-size: 12px;
  color: var(--sd-warning);
}

/* ===== 出库台账（销售出库记录） =====
   只有"表格里那一行"需要的几个小样式；摘要沿用全站统一的 .summary-line，
   明细沿用 .po-meta / .purchase-lines / .pl-*（与采购、入库台账同一个视觉语言）。 */
/* 卡片标题：图标 + 文字作为**一个整体**贴左。
   ⚠️ `t-card__header` 是 `display:flex; justify-content:space-between`，
      裸写 `<i>图标</i>标题` 会被当成**两个 flex item** 撑到两端 ——
      实测：图标孤零零留在最左(x=262)、标题被推到最右(x=1459，距左边距 **1221px**)。
      包一层 width:100% 的容器即可（与「库存明细」页 .inv-card-head 的做法一致）。 */
.sd-card-head {
  display: flex;
  align-items: center;
  gap: 6px;
  width: 100%;
}
/* 订单号：等宽感 + 次要色，避免和右侧的日期抢注意力 */
.ob-id {
  font-variant-numeric: tabular-nums;
  color: var(--sd-text-2);
}
/* 出库时间：日期是主体（跨天时最需要看清的就是它），时分退成次要 */
.ob-date {
  font-variant-numeric: tabular-nums;
  color: var(--sd-text);
}
.ob-clock {
  margin-left: 2px;
}
/* 支付方式：图标 + 文字（与「收款方式卡片」同一套图标），图标不参与压缩 */
.ob-pay {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  white-space: nowrap;
}
.ob-pay .ip-icon {
  flex: none;
}
/* 金额：等宽数字 + 加粗，便于纵列对齐比大小 */
.ob-amount {
  font-variant-numeric: tabular-nums;
  color: var(--sd-text);
}
/* 展开区末尾的口径说明 */
.ob-note {
  margin-top: 6px;
  font-size: 12px;
}

/* ==========================================================================
   微商城（线上橱窗）· PC 端第一版（.ms-*）
   --------------------------------------------------------------------------
   设计意图：把线下库存 + 会员体系搬上线的轻量橱窗，视觉上走「电商卡片流」——
   商品卡、分类胶囊、购物车侧栏，与线下后台的「表格重、卡片轻」形成对照，
   让老板一眼知道「这是给顾客看/下单的地方」。
   ⚠️ 遵守全站禁令：不用模块左侧彩色竖线，强调靠字重与颜色。
   ========================================================================== */
.ms-view {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
/* 顶栏：标题 + 会员识别 + 购物车入口 */
.ms-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}
.ms-title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 17px;
  font-weight: 700;
  color: var(--sd-text);
}
.ms-title .ip-icon {
  color: var(--sd-brand);
}
.ms-top-right {
  display: flex;
  align-items: center;
  gap: 12px;
}
.ms-member {
  display: flex;
  align-items: center;
  gap: 6px;
}
.ms-member-hint {
  font-size: 12.5px;
  color: var(--sd-text-3);
}
.ms-member-hint.is-ok {
  color: var(--sd-success);
}
/* 购物车入口：一个可点的胶囊，带件数角标 */
.ms-cart-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 14px;
  border: 1px solid var(--sd-border);
  border-radius: 999px;
  background: var(--sd-surface);
  color: var(--sd-text);
  font-size: 13px;
  cursor: pointer;
  transition: border-color 0.15s, color 0.15s;
}
.ms-cart-btn:hover {
  border-color: var(--sd-brand);
  color: var(--sd-brand);
}
.ms-cart-btn .ip-icon {
  margin-right: 0;
}

/* 分类筛选 + 搜索 */
.ms-filter {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}
.ms-kind {
  padding: 5px 12px;
  border: 1px solid var(--sd-border);
  border-radius: 999px;
  background: var(--sd-surface);
  color: var(--sd-text-2);
  font-size: 13px;
  font-family: inherit;
  line-height: 1.5;
  cursor: pointer;
  transition: all 0.15s;
}
.ms-kind:hover { border-color: var(--sd-brand-soft); color: var(--sd-brand-strong); }
.ms-kind-on {
  border-color: var(--sd-brand);
  background: var(--sd-brand-a08);
  color: var(--sd-brand-strong);
  font-weight: 600;
}
.ms-search {
  margin-left: auto;
  width: 240px;
}

/* 商品橱窗：响应式卡片流 */
.ms-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 14px;
}
.ms-card {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius);
  background: var(--sd-surface);
  overflow: hidden;
  transition: box-shadow 0.15s;
}
.ms-card:hover { box-shadow: var(--sd-shadow); }
.ms-card-thumb {
  position: relative;
  height: 168px;
  background: var(--sd-surface-2);
  overflow: hidden;
}
.ms-card-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.ms-soldout, .ms-low {
  position: absolute;
  top: 8px;
  right: 8px;
}
.ms-card-body {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 10px 12px 12px;
}
.ms-card-name {
  font-size: 14px;
  font-weight: 600;
  color: var(--sd-text);
  line-height: 1.4;
}
.ms-card-sku {
  font-size: 11.5px;
  color: var(--sd-text-3);
}
.ms-card-price {
  display: flex;
  align-items: baseline;
  gap: 6px;
}
.ms-price, .ms-price-member {
  font-size: 16px;
  font-weight: 700;
  color: var(--sd-danger);
  font-variant-numeric: tabular-nums;
}
.ms-price-origin {
  font-size: 12px;
  color: var(--sd-text-3);
  text-decoration: line-through;
}
/* 色码选择：颜色点 + 尺码胶囊 */
.ms-options {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.ms-colors {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}
.ms-color-dot {
  padding: 2px 8px;
  border: 1px solid var(--sd-border);
  border-radius: 10px;
  font-size: 11.5px;
  color: var(--sd-text-2);
  cursor: pointer;
}
.ms-color-dot:hover { border-color: var(--sd-brand); }
.ms-color-on { border-color: var(--sd-brand); color: var(--sd-brand); background: var(--sd-brand-a08); }
.ms-sizes {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
}
.ms-size {
  padding: 2px 7px;
  border: 1px solid var(--sd-border);
  border-radius: 6px;
  font-size: 11.5px;
  color: var(--sd-text-2);
  cursor: pointer;
}
.ms-size:hover { border-color: var(--sd-brand); }
.ms-size-off { opacity: 0.4; cursor: not-allowed; }
.ms-empty {
  grid-column: 1 / -1;
  padding: 48px 0;
  text-align: center;
  color: var(--sd-text-3);
  font-size: 13px;
}

/* 下单确认弹窗 */
.ms-checkout {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.ms-checkout-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-height: 260px;
  overflow-y: auto;
}
.ms-checkout-line {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 8px;
  border: 1px solid var(--sd-border);
  border-radius: 8px;
}
.ms-line-name {
  flex: 1;
  min-width: 0;
  font-size: 13px;
  color: var(--sd-text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ms-line-qty {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}
.ms-qty-btn {
  width: 22px;
  height: 22px;
  border: 1px solid var(--sd-border);
  border-radius: 6px;
  background: var(--sd-surface);
  color: var(--sd-text-2);
  cursor: pointer;
  font-size: 14px;
  line-height: 1;
}
.ms-qty-btn:hover { border-color: var(--sd-brand); color: var(--sd-brand); }
.ms-line-amt {
  flex: none;
  font-size: 13px;
  font-weight: 600;
  color: var(--sd-text);
  font-variant-numeric: tabular-nums;
}
.ms-line-del {
  flex: none;
  cursor: pointer;
  color: var(--sd-text-3);
  font-size: 15px;
  padding: 0 4px;
}
.ms-line-del:hover { color: var(--sd-danger); }
.ms-checkout-empty {
  padding: 24px 0;
  text-align: center;
  color: var(--sd-text-3);
  font-size: 13px;
}
.ms-checkout-meta {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.ms-checkout-total {
  display: flex;
  align-items: baseline;
  justify-content: flex-end;
  gap: 8px;
  font-size: 14px;
  color: var(--sd-text-2);
}
.ms-total-num {
  font-size: 22px;
  font-weight: 700;
  color: var(--sd-danger);
  font-variant-numeric: tabular-nums;
}
.ms-checkout-note {
  font-size: 12px;
  line-height: 1.6;
  color: var(--sd-text-3);
}
.ms-checkout-foot {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
}

/* ==========================================================================
   季节参谋 · 地区气温接入（.sd-weather-*，2026-09-17 接 Open-Meteo）
   --------------------------------------------------------------------------
   设计意图：把「填城市 → 抓气温 → 看趋势」这条链路做成一条轻量的输入条，
   视觉克制，不抢季节决策台主体的注意力。
   ⚠️ 遵守全站禁令：不用左侧彩色竖线。
   ========================================================================== */
.sd-weather {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px;
  border: 1px solid var(--sd-border);
  border-radius: var(--sd-radius-sm);
  background: var(--sd-surface-2);
}
.sd-weather-input {
  display: flex;
  align-items: center;
  gap: 8px;
}
.sd-weather-result {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  font-size: 13px;
  color: var(--sd-text);
}
.sd-weather-city {
  font-weight: 700;
}
.sd-weather-temp {
  font-size: 16px;
  font-weight: 700;
  color: var(--sd-brand-strong);
  font-variant-numeric: tabular-nums;
}
.sd-weather-trend {
  padding: 1px 8px;
  border-radius: 10px;
  font-size: 12px;
  border: 1px solid var(--sd-border);
  color: var(--sd-text-2);
}
.sd-weather-trend.is-warming { color: var(--sd-danger); border-color: var(--sd-danger); }
.sd-weather-trend.is-cooling { color: var(--sd-success); border-color: var(--sd-success); }
.sd-weather-days {
  font-size: 12px;
  color: var(--sd-text-2);
  font-variant-numeric: tabular-nums;
}
.sd-weather-src {
  font-size: 11px;
  color: var(--sd-text-3);
}
.sd-weather-fail {
  font-size: 12px;
  color: var(--sd-warning);
  line-height: 1.6;
}
.sd-weather-hint {
  font-size: 12px;
  line-height: 1.6;
  color: var(--sd-text-3);
}
