/**
 * 嘉雨智控 · UI 设计令牌 · CSS 变量层
 * ─────────────────────────────────────────────────────────────
 * 配套文档：项目根《UI设计基准.md》 v1.0（2026-10-03）
 * 配套 TS ：frontend/src/theme/zg-theme.ts（AntD ThemeConfig）
 *
 * 用途：
 *   1) 非 AntD 场景（自绘组件 / 打印模板 / HTML 片段 / 大屏）直接 var(--zg-*) 取值
 *   2) 后续子项目 @import 本文件即可复用同一套令牌
 *   3) 为「index.css 763 行硬编码 #hex」的逐步替换提供目标变量（S3/S4 阶段）
 *
 * ★命名统一 --zg- 前缀，避免与 AntD 运行时注入的样式冲突。
 * ★本文件新增于 S1 阶段，不改动任何现有代码。
 */

:root {
  /* ── 品牌蓝 ────────────────────────────────────────────── */
  --zg-brand-900: #0b2547; /* 侧边栏渐变起点 / 品牌文字 */
  --zg-brand-700: #123a6b; /* 主色（白字 11.39:1 ✅）   */
  --zg-brand-600: #1a4e88; /* hover / active（8.43:1 ✅）*/
  --zg-brand-400: #5d8cc6; /* 暗色模式主色             */
  --zg-brand-100: #e3edf8; /* 选中浅底                 */
  --zg-brand-50: #f0f5fb; /* 行 hover                 */

  /* ── 功能色（实心档承载白字；浅底档用于 Tag/状态行）────── */
  --zg-success-solid: #237804;
  --zg-success-bg: #f6ffed;
  --zg-success-text: #389e0d;
  --zg-success-border: #b7eb8f;

  --zg-warning-solid: #ad4e00;
  --zg-warning-bg: #fffbe6;
  --zg-warning-text: #d46b08;
  --zg-warning-border: #ffe58f;

  --zg-error-solid: #a8071a;
  --zg-error-bg: #fff2f0;
  --zg-error-text: #cf1322;
  --zg-error-border: #ffccc7;

  /* ── 中性灰阶 ─────────────────────────────────────────── */
  --zg-text-primary: #1f1f1f; /* 16.48:1 */
  --zg-text-secondary: #595959; /* 7.00:1  ← 替代 #8C8C8C 承载正文 */
  --zg-text-placeholder: #8c8c8c; /* 仅占位/禁用 */
  --zg-text-disabled: #bfbfbf;
  --zg-border-default: #d9d9d9;
  --zg-border-light: #e8e8e8;
  --zg-divider: #f0f0f0;
  --zg-bg-light: #fafafa;
  --zg-header-bg: #f7f8fa; /* 表格表头专用 */

  /* ── 尺寸 ─────────────────────────────────────────────── */
  --zg-control-height: 32px;
  --zg-control-height-sm: 24px;
  --zg-radius-sm: 4px;
  --zg-radius-base: 6px;
  --zg-radius-lg: 8px;
  --zg-radius-pill: 999px;

  /* ── 间距（4 的倍数）───────────────────────────────────── */
  --zg-space-1: 4px;
  --zg-space-2: 8px;
  --zg-space-3: 12px;
  --zg-space-4: 16px;
  --zg-space-6: 24px;
  --zg-space-8: 32px;
  --zg-space-12: 48px;

  /* ── 字号 / 行高 ──────────────────────────────────────── */
  --zg-font-size-xs: 12px;
  --zg-font-size-sm: 13px;
  --zg-font-size-base: 14px;
  --zg-font-size-md: 16px;
  --zg-font-size-lg: 18px;
  --zg-font-size-xl: 24px;
  --zg-line-height-base: 1.571; /* 14 → 22px */

  /* ── 阴影（品牌深蓝透明版）────────────────────────────── */
  --zg-shadow-card-hover: 0 4px 12px rgba(11, 37, 71, 0.08);
  --zg-shadow-modal: 0 6px 24px rgba(11, 37, 71, 0.14);
  --zg-shadow-focus: 0 0 0 2px rgba(18, 58, 107, 0.1);

  /* ── 过渡 ─────────────────────────────────────────────── */
  --zg-motion-fast: 150ms ease;
  --zg-motion-base: 200ms ease;
  --zg-motion-slow: 300ms cubic-bezier(0.4, 0, 0.2, 1);
}

/* ==========================================================================
   大屏暗色模式（受控例外）
   仅用于车间看板 / 大屏类页面：在根节点加 data-zg-theme="dark"
   ========================================================================== */
[data-zg-theme='dark'] {
  --zg-brand-700: #5d8cc6; /* 深底上的蓝需更亮 */
  --zg-brand-600: #7ba3d2;
  --zg-text-primary: rgba(255, 255, 255, 0.88);
  --zg-text-secondary: rgba(255, 255, 255, 0.72);
  --zg-border-default: #434343;
  --zg-border-light: #303030;
  --zg-divider: #303030;
  --zg-bg-light: #1f1f1f;

  --zg-success-solid: #49aa19;
  --zg-warning-solid: #d89614;
  --zg-error-solid: #d32029;
}

/* ==========================================================================
   无障碍：尊重「减少动效」偏好，关闭全部过渡
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  :root {
    --zg-motion-fast: 0ms;
    --zg-motion-base: 0ms;
    --zg-motion-slow: 0ms;
  }
}
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

html,
body,
#root {
  height: 100%;
}

body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,
    'PingFang SC', 'Microsoft YaHei', sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* ★禁双击缩放（治手机双击整屏放大、按钮跑出屏）；保留单指滚动与双指捏合（不伤无障碍） */
  touch-action: manipulation;
}

/* 细滚动条，贴合深色侧边栏与浅色主题 */
::-webkit-scrollbar {
  width: 8px;
  height: 8px;
}
::-webkit-scrollbar-thumb {
  background: rgba(11, 37, 71, 0.22);
  border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
  background: rgba(11, 37, 71, 0.38);
}
::-webkit-scrollbar-track {
  background: transparent;
}

/* ★列表页「筛选/操作工具栏」吸顶：滚动长表格时冻结，便于随时改筛选/操作（老板 2026-10-06 令，TSP 反馈）。
   仅作用于滚动容器（MainLayout 的 Content，overflowY:auto）的直接/间接子元素；白底与容器无缝。 */
/* ★2026-10-06（老板令：TAB 也要吸顶）：TAB 页签条吸顶（StickyTabs 组件挂此类）。
   --zg-tab-h 由 StickyTabs 实测自身高度写入 <html>；下方搜索栏、表头都按它让位，
   TAB 换行变高自动跟随。没包 StickyTabs 的页面该变量为 0，一切位置照旧。 */
:root { --zg-tab-h: 0px; }
.sticky-tabbar {
  position: sticky;
  top: 0;
  z-index: 18;
  background: #fff;
}
/* ★2026-10-22（老板：履约助手等多页「这里为什么会透过去了」）全局修复：
   Content 滚动容器自带 padding-top(桌面24/手机12)，Chrome 把 sticky 栏钉在 padding 下缘
   （最小实验实测：钉位 = 容器顶 + padding-top），上方这条 padding 带永远露出滚上来的表格行。
   修法＝给吸顶栏挂一块同色「顶棚」伪元素，随栏一起钉住、盖住 padding 带；
   纯绘制层修复，不改变任何钉扎几何/布局；静止时白底上不可见，多盖的部分被容器 overflow 裁掉。 */
.sticky-tabbar::before,
.sticky-toolbar::before {
  content: '';
  position: absolute;
  top: -24px;
  left: -24px;
  right: -24px;
  height: 24px;
  background: #fff;
  pointer-events: none;
}
.sticky-toolbar {
  position: sticky;
  top: var(--zg-tab-h, 0px);
  z-index: 15;
  background: #fff;
  padding: 4px 2px;
  box-shadow: 0 4px 6px -4px rgba(11, 37, 71, 0.18);
}
/* ★2026-10-22 同类问题的「通用补丁」：抽屉 / 弹窗内部用行内 style 写的 sticky 条（进不了上面两个类）
   同样会被钉在 padding 下缘、留出缝隙。给这类元素挂 .zg-sticky-cap（底边用 -bottom 版）即可：
   background:inherit 自动跟随自身底色（白 / 浅绿批量条都适用），左右各外扩 24px 盖住容器左右边距。 */
.zg-sticky-cap::before,
.zg-sticky-cap-bottom::after {
  content: '';
  position: absolute;
  left: -24px;
  right: -24px;
  height: 24px;
  background: inherit;
  pointer-events: none;
}
.zg-sticky-cap::before { top: -24px; }
.zg-sticky-cap-bottom::after { bottom: -24px; }
/* ★2026-10-25 修订：首行冻结改走 antd 原生 sticky（ResponsiveTable / ProTable 的
   stickyHeader 开关透传 offsetHeader），不再用 CSS position:sticky——
   旧方案在 antd scroll.x 的「独立表头 + 内部 overflow」结构下无法吸顶
   （rc-table/es/Table.js:283-286 给表头容器加了 overflowY:'hidden' / overflowX:'auto'，
   CSS sticky 被截断、吸不住）。下方只保留表头白底兜底，防止宽屏去横滚时透出下方行。 */
.sticky-thead .ant-table-header,
.sticky-thead .ant-table-content .ant-table-thead > tr > th {
  background: #fff;
}

/* ★2026-10-26（老板：多维分析等页「表头只冻一行、背后透出下方数据行」）：
   根因＝antd 原生 sticky 表头吸顶在 Content 顶部（offsetHeader=0，页面无吸顶筛选块），
   Chrome 把吸顶栏钉在 Content 的 padding-top 下缘，上方 24px 缝隙透出滚上来的数据行。
   10-22 的同类修复只给 .sticky-tabbar/.sticky-toolbar 加了白底 ::before，antd 原生 sticky
   表头不走这两个类被漏掉。这里给 antd 吸顶表头层同样补白底 + 向上伪元素盖缝隙；
   作用域限定 .sticky-thead（ResponsiveTable stickyHeader 开关），只影响我们控制的表。 */
.sticky-thead .ant-table-sticky-holder {
  background: #fff;
}
.sticky-thead .ant-table-sticky-holder::before {
  content: '';
  position: absolute;
  top: -24px;
  left: -24px;
  right: -24px;
  height: 24px;
  background: #fff;
  pointer-events: none;
}

/* 侧边栏：AntD Sider 会把内容包在 .ant-layout-sider-children 里，
   必须让它撑满高度并做 flex 列，内部菜单区才能独立滚动（整页已锁 100vh 不滚） */
.ant-layout-sider-children {
  display: flex;
  flex-direction: column;
  height: 100%;
  overflow: hidden;
}

/* ============================================================
   全局控件统一（输入框 / 下拉 / 日期 / 数字框 / 按钮 / 浮窗）
   高度与圆角由 main.tsx 的 AntD 主题 token 统一控制（controlHeight 32、borderRadius 6），
   此处补充 token 无法覆盖的 wrapper 等高与浮窗结构样式，保证全系统一致。
   ============================================================ */

/* 浮窗（Modal）统一：标题栏分隔线、标题字重、内容留白、底部操作区分隔线 */
.ant-modal-header {
  border-bottom: 1px solid #f0f0f0;
  padding: 16px 24px;
  margin-bottom: 0;
}
.ant-modal-title {
  font-weight: 600;
  font-size: 16px;
  color: rgba(0, 0, 0, 0.88);
}
.ant-modal-body {
  padding: 20px 24px;
}
.ant-modal-footer {
  border-top: 1px solid #f0f0f0;
  padding: 12px 24px;
}
/* 二次确认类弹窗（Modal.confirm / Modal.warning）的图标与文字间距统一 */
.ant-modal-confirm-body .ant-modal-confirm-title {
  font-weight: 600;
}

/* 输入框 / 下拉 / 日期 / 数字框：统一圆角与边框过渡，避免各页面样式不一致 */
.ant-input,
.ant-input-number,
.ant-select-selector,
.ant-picker,
.ant-input-affix-wrapper {
  border-radius: 6px !important;
}

/* 统一所有带边框的表单控件与按钮的精确高度(32px)，
   覆盖 size=large / 带前缀后缀(affix)内边距 / 不同组件默认 padding 导致的高度不一致，
   保证同一行内 输入框/下拉/日期/数字框/按钮 完全等高对齐 */
.ant-input:not(textarea),
.ant-input-affix-wrapper,
.ant-select-selector,
.ant-picker,
.ant-input-number,
/* ★排除 .ant-btn-sm：small 按钮由下方 .ant-btn.ant-btn-sm 规则（及 token controlHeightSM）定为 24px。
   原写法带两个 :not() 权重 (0,3,0)，压过下方 (0,2,0) 的 small 规则，导致全站 small 按钮被拉成 32px。 */
.ant-btn:not(.ant-btn-link):not(.ant-btn-text):not(.ant-btn-sm) {
  height: 32px !important;
  box-sizing: border-box !important;
}
/* affix wrapper 用 flex 强制内部垂直居中，并消除上下 padding 造成的文字下沉 */
.ant-input-affix-wrapper {
  display: inline-flex !important;
  align-items: center !important;
  padding-top: 0 !important;
  padding-bottom: 0 !important;
}
.ant-input-affix-wrapper > .ant-input {
  height: 30px !important;
  line-height: 30px !important;
  padding-top: 0 !important;
  padding-bottom: 0 !important;
}
/* 下拉/日期/数字框内部输入垂直居中且不超过外层高度 */
.ant-select-selector .ant-select-selection-search-input,
.ant-picker-input > input,
.ant-input-number-input {
  height: 30px !important;
  line-height: 30px !important;
  padding-top: 0 !important;
  padding-bottom: 0 !important;
}
/* 按钮文字垂直居中（★同样排除 small：small 走下方 line-height:22px，沿用 30px 会在 24px 高的按钮里溢出） */
.ant-btn:not(.ant-btn-link):not(.ant-btn-text):not(.ant-btn-sm) {
  line-height: 30px !important;
  padding-top: 0 !important;
  padding-bottom: 0 !important;
}
/* 下拉/日期/数字框内部 flex 垂直居中 */
.ant-select-selector,
.ant-picker,
.ant-input-number {
  align-items: center !important;
}
/* ★2026-10-17 多选下拉（负责人等）占位文字垂直居中：
   antd v5 多选占位符是 .ant-select-selection-wrap 内 flex:1 的流式节点，
   统一 32px 控件高后文字下沉约 4px，仅靠 align-items 居中不可靠。
   改为绝对定位死居中：wrap 自带 position:relative 且占满内容区，top:50%+translateY 必中中心 */
/*   ★★实测（Playwright 真机量过）：wrap 是 34px、比 32px 的外框还高且下溢 2px，
   所以「在 wrap 里居中」≠「在框里居中」——这就是前两版改完仍偏下 3px 的真因。
   正解：把 wrap 的 relative 撤掉（改 static），占位符的定位基准就上移到外框
   （.ant-select-selector 自带 position:relative），top:50%+translateY 才能正中外框。 */
.ant-select-multiple .ant-select-selection-wrap {
  position: static !important;
  align-items: center !important;
}
.ant-select-multiple .ant-select-selection-placeholder {
  position: absolute !important;
  top: 50% !important;
  /* ★2026-10-19：绝对定位的基准是外框（.ant-select-selector 自带 position:relative），
     left:0 会直接贴死左边框——实测距左仅 2px，而普通输入框是 11px，看着"靠左"。
     这里补回与输入框一致的 11px 起始线（老板选方案 A）。
     注意：改 left 不影响上面的垂直居中（top:50%+translateY 独立生效），两边不再打架。 */
  left: 11px !important;
  right: 11px !important;
  transform: translateY(-50%) !important;
  text-align: left !important;
}
/* 图标/后缀/箭头/范围分隔符垂直居中 */
.ant-input-suffix,
.ant-input-prefix,
.ant-picker-suffix,
.ant-select-arrow,
.ant-picker-range-separator {
  display: flex !important;
  align-items: center !important;
}

.ant-input:hover,
.ant-input-number:hover .ant-input-number-handler-wrap,
.ant-select:not(.ant-select-disabled):not(.ant-select-customize-input):not(.ant-select-show-arrow):hover .ant-select-selector,
.ant-picker:hover {
  border-color: #1a4e88;
}
.ant-input:focus,
.ant-input-focused,
.ant-input-number-focused,
.ant-select-focused .ant-select-selector,
.ant-picker-focused {
  border-color: var(--zg-brand-700) !important;
  box-shadow: 0 0 0 2px rgba(18, 58, 107, 0.1) !important;
}

/* ★输入框聚焦只保留一层光环（10-03）：带前缀/后缀的输入框（如搜索框、带单位数字框），
   外层 wrapper 与内层 input 会各画一个光环，叠成"层层叠叠"两圈；这里让内层不再重复画。 */
.ant-input-affix-wrapper .ant-input:focus,
.ant-input-affix-wrapper .ant-input-focused {
  box-shadow: none !important;
}

/* 按钮统一：圆角由 token 控制，这里去除个别组件遗留的投影与多余过渡 */
.ant-btn {
  border-radius: 6px;
}
.ant-btn-primary,
.ant-btn-default {
  box-shadow: none;
}

/* 图纸管理：紧凑单行上传拖拽条
   （覆盖 antd Dragger 默认大号拖拽区：48px 图标 + 上下大边距，垂直占用过高导致页面空旷失衡） */
.drawing-upload-bar.ant-upload-wrapper { display: block; margin-bottom: 12px; }
.drawing-upload-bar .ant-upload-drag { height: auto !important; }
/* antd 默认在 .ant-upload.ant-upload-btn 上有 16px padding，去掉后由 container 控制紧凑高度 */
.drawing-upload-bar .ant-upload-btn {
  display: block !important;
  height: auto !important;
  padding: 0 !important;
}
.drawing-upload-bar .ant-upload-drag-container {
  display: flex !important;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 4px 10px;
  padding: 10px 16px;
}
.drawing-upload-bar p.ant-upload-drag-icon {
  margin-bottom: 0 !important;
  line-height: 1;
}
.drawing-upload-bar p.ant-upload-drag-icon .anticon { font-size: 20px !important; }
.drawing-upload-bar p.ant-upload-text {
  margin: 0 !important;
  font-size: 13px !important;
}
.drawing-upload-bar p.ant-upload-hint {
  margin: 0 !important;
  font-size: 12px !important;
}

/* ============================================================
   列表协调基线（cozy-table）
   ★2026-09-28 老板拍板（改过口径，别改回去）：
     以前 —— 长文本自动换行/折断（white-space:normal + overflow-wrap:anywhere），
             列一窄就把「图号 / 金额 / 名称」折成两行甚至一字一行，看着像被挤扁。
     现在 —— 单元格一律【不折行】，列宽按内容撑开，容器装不下就左右滑动；
             不想看的列用表格右上角的「列设置」关掉，而不是靠挤压折叠。
   全系统列表统一加此 class，保证观感一致。
   ============================================================ */
.cozy-table .ant-table-cell {
  white-space: nowrap;
  word-break: keep-all;
  overflow-wrap: normal;
  vertical-align: middle;
}
.cozy-table .ant-table-thead > tr > th {
  white-space: nowrap;
  vertical-align: middle;
}

/* 详情面板（主从布局右栏）字数多时保持可读行高 */
.cozy-detail .ant-descriptions-item-content {
  word-break: break-word;
  overflow-wrap: anywhere;
}

/* ============================================================
   图号列全局自适应（.cell-drawing）
   图号是完整编码（如 CP-XLN98000050），禁止折断换行，保证
   一眼看全、可直接框选复制；列宽随最长图号自动撑开。
   配合各列表「图号」列定义：弹性布局表去固定宽自适应，
   固定布局/弹窗表给 width 200 兜底。
   ★2026-10-21（老板拍板 A）：编码统一用等宽字体——以前只有履约助手用等宽，
     其它页是默认字体，同一份图号在不同页长得不一样。等宽下数字宽度一致，
     Rev.0 / Rev.10 这类位数差异一眼可辨，扫读更快；挂在 .cell-drawing 上
     即全站图号/单号列统一（这些列本就都是编码列）。
   ============================================================ */
.cell-drawing,
.cozy-table .ant-table-cell.cell-drawing,
.ant-modal-body .ant-table-cell.cell-drawing {
  white-space: nowrap !important;
  word-break: keep-all !important;
  overflow-wrap: normal !important;
  font-family: 'ui-monospace', 'SFMono-Regular', 'Menlo', 'Consolas', 'Liberation Mono', monospace !important;
  font-variant-numeric: tabular-nums;
}

/* ============================================================
   ★2026-11-07 路线 A：断点统一为 991 / 992（唯一真相源 hooks/useLayoutMode.ts）
   仅 ≤991px 生效（手机 + 折叠屏内屏），桌面 ≥992px 视图完全不变。
   原则：小屏把所有栅格堆叠为单列、弹窗占满屏宽、控件触控友好；
   桌面端维持原有三列/多列布局与固定弹窗宽度。
   ============================================================ */
@media (max-width: 991px) {
  /* 所有栅格在手机上堆叠为单列（不影响桌面多列布局） */
  .ant-col {
    flex: 0 0 100% !important;
    max-width: 100% !important;
    width: 100% !important;
  }

  /* 弹窗占满手机屏宽，避免溢出与错位 */
  .ant-modal {
    max-width: calc(100vw - 16px) !important;
    margin: 8px auto !important;
    top: 8px !important;
    padding-bottom: 8px !important;
  }
  .ant-modal-body { padding: 16px !important; }
  .ant-modal-header { padding: 12px 16px !important; }
  .ant-modal-footer { padding: 10px 16px !important; }

  /* ★2026-11-05 折叠屏/小屏：抽屉在手机上不许横向溢出——AntD 默认 378px、部分页面写死 420/520px，
     在 375px 外屏会顶出屏幕。统一压到 100vw 以内（导航抽屉本就 < 屏宽，不受影响）。 */
  .ant-drawer-content-wrapper,
  .ant-drawer-content { max-width: 100vw !important; }
  /* 顶栏避让刘海/灵动岛安全区 */
  .ant-layout-header { padding-left: calc(12px + env(safe-area-inset-left)) !important; padding-right: calc(12px + env(safe-area-inset-right)) !important; }
  /* 底部抽屉内容避让 Home 指示条 */
  .ant-drawer-bottom .ant-drawer-body { padding-bottom: calc(24px + env(safe-area-inset-bottom)); }

  /* ★原「.ant-drawer-body { padding: 0 }」已删除：选择器不带限定，会把全站所有内容抽屉（详情/表单抽屉）
     在手机上的内边距也清零，文字直接贴边。移动端导航抽屉（MainLayout，深蓝渐变）自带内联 padding:0，
     不受本条影响 —— 见 zg-theme.ts 的 Drawer 说明。手机端内容抽屉统一沿用规范 2.7 的 24px。 */

  /* 卡片内边距收敛 */
  .ant-card-body { padding: 14px !important; }

  /* 表单：标签与控件上下堆叠更清晰，间距收敛 */
  .ant-form-item { margin-bottom: 12px; }
  .ant-form-item-label { padding: 0 0 4px; line-height: 1.4; text-align: left; }
  .ant-form-item-label > label { height: auto; }

  /* 详情描述列表字号/内边距收敛，避免溢出 */
  .ant-descriptions-bordered .ant-descriptions-item-label,
  .ant-descriptions-bordered .ant-descriptions-item-content { padding: 8px 10px !important; }

  /* 统计数字在手机上不要过大 */
  .ant-statistic-content { font-size: 18px !important; }

  /* 表格容器允许横向滚动（已有 scroll x 的表） */
  .ant-table-wrapper { overflow-x: auto; }

  /* Tab 栏可横向滑动，避免截断（如"未来7日…"） */
  .ant-tabs .ant-tabs-nav { white-space: nowrap; }
  .ant-tabs .ant-tabs-nav-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none; }
  .ant-tabs .ant-tabs-nav-wrap::-webkit-scrollbar { display: none; }

  /* 响应式表格：手机端卡片列表间距与观感 */
  .resp-table-mobile { display: flex; flex-direction: column; gap: 10px; }
  .resp-card-wrap > .ant-card { border-radius: 8px; }
  .resp-card-wrap .ant-card-body { padding: 12px 14px; }
  /* 卡片内字段行：标签灰、值黑，紧凑两列 */
  .m-field, .rsp-field { display: flex; justify-content: space-between; gap: 12px; padding: 3px 0; font-size: 13px; line-height: 1.5; align-items: baseline; }
  /* ★2026-10-04：标签允许收缩（值优先保完整），超长时省略号，避免长金额被挤出行外 */
  .m-field > .m-label, .rsp-field-label { color: #8c8c8c; flex-shrink: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .m-field > .m-value, .rsp-field-value { color: #1f1f1f; text-align: right; word-break: break-all; min-width: 0; flex: 1; }
  /* 数字/金额/日期类值：不折行。 */
  .rsp-field-value.rsp-num, .m-field > .m-value.rsp-num { flex: 0 0 auto; white-space: nowrap; word-break: keep-all; overflow-wrap: normal; }
  /* ★金额/数字字段：手机卡片改成「标签在上、数值另起一行、独占整行」（2026-10-04 老板定：换行显示）。
     以前 → 标签和金额挤同一行，格子一窄 1,747,842.64 就从数字中间折断（1,747,842.6 / 4）；
     现在 → 金额整串独占一行，怎么也断不开，也看得清。 */
  .rsp-field-num { flex-direction: column; align-items: stretch; gap: 2px; padding: 4px 0; }
  .rsp-field-num .rsp-field-label { flex-shrink: 0; white-space: normal; overflow: visible; text-overflow: clip; }
  .rsp-field-num .rsp-field-value { flex: 0 0 auto; text-align: left; white-space: nowrap; word-break: keep-all; overflow-wrap: normal; font-variant-numeric: tabular-nums; }
  /* 数字字段在 2 列网格里独占一行，避免半列宽把长金额挤断 */
  .rsp-card-grid .rsp-field-num { grid-column: 1 / -1; }
  .m-qty-row { display: flex; gap: 10px; margin: 8px 0 4px; }
  .m-qty { flex: 1; text-align: center; border-radius: 8px; padding: 6px 0; background: #fafafa; }
  .m-qty .m-qty-num { font-size: 18px; font-weight: 700; line-height: 1.2; }
  .m-qty .m-qty-lab { font-size: 12px; color: #8c8c8c; }
  .m-card-foot { display: flex; justify-content: space-between; align-items: center; margin-top: 8px; padding-top: 8px; border-top: 1px solid #f0f0f0; }

  /* ResponsiveTable 默认生成的卡片（rsp-*） */
  /* ★2026-10-12：min-width:0 / max-width:100% —— flex 子项默认 min-width:auto，
     卡片会被内部较长的内容（如不折行的日期、按钮行）撑得比屏幕还宽，实测 320 窄屏上
     卡片宽 329px 顶出屏幕。加上这两条后卡片永远不超过父容器宽度，只影响手机端卡片，桌面零变化。 */
  .rsp-card { box-shadow: 0 1px 4px rgba(0,0,0,0.04); min-width: 0; max-width: 100%; }
  .rsp-card .ant-card-body { padding: 12px 14px; }
  .rsp-card-title { display: flex; align-items: baseline; gap: 8px; margin-bottom: 8px; padding-bottom: 8px; border-bottom: 1px dashed #f0f0f0; }
  /* ★主标题过长时省略号，不挤压右侧日期 */
  .rsp-card-title > :first-child { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
  /* ★副标题（多为日期）整行不折，避免 2026-09-30 被折成两行 */
  .rsp-card-sub { color: #8c8c8c; font-size: 12px; white-space: nowrap; flex-shrink: 0; }
  .rsp-card-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0 12px; }
  @media (max-width: 360px) { .rsp-card-grid { grid-template-columns: 1fr; } }
  /* ★2026-10-10 卡片脚部操作区：操作列以前在手机上被整列剔除（只能看不能点），
     现在渲染到卡片底部独立区域——按钮自动换行、触控目标 44px。 */
  .rsp-card-foot { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 10px; padding-top: 10px; border-top: 1px solid #f0f0f0; }
  /* ★2026-10-12 卡片脚部操作按钮：权重压过下方全局 .ant-btn(min-height:36px)，
     始终保 44px（GQQ「点详情闪一下」根因＝卡片按钮被压到 24px 点偏），工具栏/查询区降到 36 不影响它。 */
  .rsp-card-foot .ant-btn:not(.ant-btn-link):not(.ant-btn-text) { min-height: 44px !important; height: 44px !important; min-width: 44px; }
  /* ★2026-10-11 手机端卡片脚部按钮：整行放不下就换行，按钮文字绝不从中间断字
     （以前「直接收口」被挤成「直接收」就是按钮被压窄后汉字断行） */
  .rsp-card-foot .ant-btn { white-space: nowrap; flex-shrink: 0; }
  .rsp-card-foot { justify-content: flex-end; }

  /* 顶部按钮组允许换行，避免顶栏溢出 */
  .ant-layout-header .ant-space { flex-wrap: wrap; row-gap: 8px; }

  /* ★2026-10-10 手机端触控目标统一抬到 44px（WCAG 建议的最小触控尺寸）。
     ★2026-10-11 修正（GQQ「点详情闪一下」根因）：antd 运行时注入的 CSS-in-JS 自带
     `.ant-btn-sm{min-height:24px}`，与下面这条权重打平且注入在后 → 不加 !important 就输，
     实测卡片按钮停在 24px、手指按不准（点偏触到卡片其它区域＝闪一下又关）。
     故 min-height 一律 !important 压死。纯文字/链接按钮抬到 36px（同样 !important）。 */
  .ant-btn:not(.ant-btn-link):not(.ant-btn-text) { min-height: 36px !important; }
  .ant-btn-link, .ant-btn-text { min-height: 36px !important; }
  .ant-pagination-item { min-width: 44px; height: 44px !important; line-height: 42px !important; }
  .ant-checkbox-wrapper { min-height: 36px; display: inline-flex; align-items: center; }

  /* ★2026-10-11 手机端输入框与按钮等高（44px）：以前只抬按钮不抬输入框，
     搜索框矮一截、放大镜挤成窄块、与右侧按钮高低不齐（GQQ 截图圈出的失衡）。
     覆盖：单行输入/前后缀包裹/数字框/下拉/日期/普通按钮/搜索按钮块。
     搜索框复合控件内部按钮由 affix-wrapper 撑高即可，单独再抬会双重高。 */
  .ant-input:not(textarea):not(.ant-input-lg),
  .ant-input-affix-wrapper:not(.ant-input-affix-wrapper-lg),
  .ant-input-number:not(.ant-input-number-lg),
  .ant-input-number-input,
  .ant-select-single:not(.ant-select-lg) .ant-select-selector,
  .ant-picker:not(.ant-picker-lg) { min-height: 36px !important; }
  .ant-input-search > .ant-input-group > .ant-input-group-addon:last-child .ant-input-search-button:not(.ant-btn-icon-only) { height: 36px; }
  /* 输入框内文字垂直居中（单行框抬到 44 后，line-height 跟上避免贴顶） */
  .ant-input-number-input { line-height: 34px; }

  /* ★2026-10-11 移动端 UI 规范：纯说明类蓝条（Alert type=info）手机端默认隐藏，省屏幕；
     红色警告 / 报错 / 成功提示照常显示（它们不是"占空间的说明"，而是操作提醒）。
     必读说明加 .keep-on-mobile 保留。桌面端完全不变。 */
  .ant-alert-info { display: none !important; }
  .ant-alert-info.keep-on-mobile { display: flex !important; }
}

/* ============================================================
   ★2026-11-05 建，2026-11-07 路线 A 扩到 481–991px（折叠屏内屏 527 / 大折 700–880 全覆盖）
   纯 CSS 渐进增强：恢复 AntD 原生栅格（≥576 自动两列）、抽屉放宽到 92vw、弹窗放宽到 92vw。
   JS 侧已同步：useMobile()/MainLayout/DesktopOnly 均以 992 为桌面界，桌面端(≥992)完全不变。
   ============================================================ */
@media (min-width: 481px) and (max-width: 991px) {
  /* 撤销 mobile 块里强制的 .ant-col 100%，让 AntD 原生 sm(≥576) 两列自然生效 */
  .ant-col { flex: 0 1 auto !important; max-width: none !important; width: auto !important; }
  /* 抽屉放宽（不再被 mobile 的 100vw 限制，但也不到桌面那么宽） */
  .ant-drawer-content-wrapper,
  .ant-drawer-content { max-width: 92vw !important; }
  /* 弹窗放宽到 92vw（mobile 块是占满 100vw-16px，中屏稍留边更美观） */
  .ant-modal { max-width: 92vw !important; }
}

/* ============================================================
   全局 UI 一致性规范 v2
   目标：按钮/输入框高度统一、操作区小图标统一 14px、
         弹窗整体禁止左右滑动。
   ============================================================ */

/* 按钮：默认 32px（与 AntD token controlHeight 一致），small 24px，避免各页面乱用尺寸 */
.ant-btn {
  border-radius: 6px !important;
  box-shadow: none !important;
  min-height: 32px;
}
/* small 按钮由 AntD token controlHeightSM 控制为 24px，这里只做字体与内边距兜底 */
.ant-btn.ant-btn-sm {
  height: 24px !important;
  min-height: 24px !important;
  padding-block: 0 !important;
  padding-inline: 8px !important;
  line-height: 22px !important;
  font-size: 13px;
}
/* 大号按钮归到 32px（禁用明显的大号差异） */
.ant-btn.ant-btn-lg {
  height: 32px !important;
  min-height: 32px !important;
  padding-inline: 16px !important;
  font-size: 14px;
}

/* ★抽屉四向阴影换品牌蓝（对齐《UI设计基准》「阴影一律用品牌蓝」红线）。
   antd 默认四向阴影是纯黑系 rgba(0,0,0,...)；其令牌 boxShadowDrawerLeft/Right/Up/Down
   运行时有效但不在 AliasToken 类型里（zg-theme.ts 里设会 tsc 报错），故在此覆盖。
   ★必须 !important：antd 运行时 CSS 在 index.css 之后注入，同权重会被盖掉。 */
.ant-drawer-right .ant-drawer-content-wrapper { box-shadow: 6px 0 16px -8px rgba(11,37,71,0.12) !important; }
.ant-drawer-left .ant-drawer-content-wrapper { box-shadow: -6px 0 16px -8px rgba(11,37,71,0.12) !important; }
.ant-drawer-top .ant-drawer-content-wrapper { box-shadow: 0 6px 16px -8px rgba(11,37,71,0.12) !important; }
.ant-drawer-bottom .ant-drawer-content-wrapper { box-shadow: 0 -6px 16px -8px rgba(11,37,71,0.12) !important; }
/* 链接/文字按钮不强制高度 */
.ant-btn.ant-btn-link,
.ant-btn.ant-btn-text {
  height: auto;
  min-height: 0;
}

/* ★2026-11-05 折叠屏安全区：底部悬浮按钮避让 Home 指示条 */
.dw-fab { bottom: calc(78px + env(safe-area-inset-bottom)) !important; }

/* 输入框：默认 32px（与 AntD token controlHeight 一致），small 24px，圆角统一 */
.ant-input,
.ant-input-number,
.ant-input-affix-wrapper,
.ant-select-selector,
.ant-picker {
  min-height: 32px;
  border-radius: 6px !important;
}
.ant-input.ant-input-sm,
.ant-input-number.ant-input-number-sm,
.ant-select.ant-select-sm .ant-select-selector,
.ant-picker.ant-picker-small {
  min-height: 24px !important;
}
.ant-btn.ant-btn-sm {
  min-height: 24px !important;
}

/* 图标统一：操作区（按钮/表格/表单/弹窗/菜单/分页/输入框后缀）小图标 14px */
.ant-btn .anticon,
.ant-table .anticon,
.ant-form .anticon,
.ant-modal .anticon,
.ant-menu-item .anticon,
.ant-pagination .anticon,
.ant-select-selection-item .anticon,
.ant-picker-suffix .anticon,
.ant-input-affix-wrapper .anticon,
.ant-tabs-tab .anticon,
.ant-select-arrow .anticon,
.ant-select-clear .anticon,
.ant-cascader-picker-arrow .anticon,
.ant-mention-prefix .anticon,
.ant-input-number-handler-up-inner,
.ant-input-number-handler-down-inner,
.ant-tag .anticon,
.ant-badge .anticon,
.ant-steps .anticon,
.ant-collapse-header .anticon,
.ant-card-head .anticon,
.ant-notification-notice-icon .anticon,
.ant-message-custom-content .anticon,
.ant-timeline-item-head .anticon,
.ant-result-icon .anticon,
.ant-empty .anticon,
.ant-image-preview-operations .anticon,
.ant-transfer .anticon,
.ant-tree .anticon,
.ant-tree-switcher .anticon,
.ant-menu-submenu-title .anticon,
.ant-dropdown-menu-item .anticon,
.ant-segmented .anticon,
.ant-breadcrumb .anticon,
.ant-menu-item-group-title .anticon {
  font-size: 14px !important;
  vertical-align: -0.125em;
}
/* 明确保留看板/仪表盘/统计卡片上的原有大图标 */
.kanban-card .anticon,
.dashboard-card .anticon,
.ant-statistic-content-prefix .anticon,
.drawing-upload-bar .anticon {
  font-size: inherit !important;
}

/* 弹窗整体禁止左右滑动 */
.ant-modal {
  max-width: 100vw;
}
.ant-modal-wrap {
  overflow-x: hidden !important;
}
.ant-modal-body {
  overflow-x: hidden !important;
  width: 100%;
  max-width: 100%;
  box-sizing: border-box;
}
/* 弹窗内子元素禁止撑破 body */
.ant-modal-body > *,
.ant-modal-body .ant-form,
.ant-modal-body .ant-row,
.ant-modal-body .ant-descriptions,
.ant-modal-body .ant-space {
  max-width: 100%;
}
/* 弹窗内表格：★2026-09-28 同口径改为「不折叠、可横滑」
   以前用 table-layout:fixed + 折断换行，列宽被硬压、长内容必被截；
   现在改为按内容撑开（width:max-content，窄表仍撑满弹窗），装不下就在弹窗内左右拖。 */
.ant-modal-body .ant-table-wrapper {
  max-width: 100%;
  overflow-x: auto;
}
.ant-modal-body .ant-table-content > table,
.ant-modal-body .ant-table {
  width: max-content !important;
  min-width: 100% !important;
  table-layout: auto !important;
}
/* ★2026-09-29 资金日报流水弹窗：这张表自带固定高度的表体（scroll.y），
   横向滚动条常驻在表体底部、随时能左右拖（好用，保留）。
   但绝不能再让外层的 .ant-table / .ant-table-wrapper 也变成横向滚动容器——
   同一张表出现两层横向滚动时，浏览器自动聚焦滚动会让两层互相拉扯，
   表现为「表头（日期列）钉住不动、表体自己不停往右滑」。
   这里只对本弹窗关掉外层横滚，横向滚动一律交给内层表体那一个容器。 */
.funds-flow-modal .ant-table-wrapper {
  overflow-x: hidden !important;
}
.funds-flow-modal .ant-table {
  width: 100% !important;
  min-width: 0 !important;
}
/* ★2026-10-06（YDM 反馈·低库存预警弹窗列显示不全）：与上面资金日报**同款病根**——
   弹窗表格带 scroll.y（内层 .ant-table-body 是一个横滚容器），弹窗通用 CSS 又把外层
   .ant-table-wrapper 撑成第二个横滚容器，两层打架 → 表头钉住、表体右滑、列看着"缺"。
   照 09-29 资金日报的最终修法：对本弹窗关掉外层横滚，横向滚动只交给内层表体一个容器。
   （库存余额页 + 工作台两处同款弹窗共用此类名。） */
.low-stock-modal .ant-table-wrapper {
  overflow-x: hidden !important;
}
.low-stock-modal .ant-table {
  width: 100% !important;
  min-width: 0 !important;
}
/* ★2026-10-09 询价审计日志弹窗（真根因修复，老板第 4 次报溢出后用无头浏览器实证定位）：
   上面「弹窗内表格 width:max-content」的全站口径会把询价审计弹窗里的 hFit/固定列宽布局
   全部顶掉（!important）——表格被「工序: 1.毛坯(…/采纳:某厂)；2.…；3.…」这种一整行
   撑到两倍弹窗宽，字永远不折、横向冒出弹窗。单工序字短撑不超，所以表现为「只有多工序溢出」。
   本弹窗走例外（与上面资金日报 / 低库存弹窗同款先例）：表格贴满弹窗宽 + 固定布局，
   长文本由 CollapsibleText 组件折行/折叠（3 行收起、可展开），永不横向溢出。 */
.inq-audit-modal .ant-table-content > table,
.inq-audit-modal .ant-table {
  width: 100% !important;
  min-width: 0 !important;
  table-layout: fixed !important;
}
/* 弹窗内长文本自动换行 */
.ant-modal-body .ant-descriptions-item-content,
.ant-modal-body .ant-typography,
.ant-modal-body p,
.ant-modal-body span {
  word-break: break-word;
  overflow-wrap: anywhere;
}

/* 消息中心跳转落地时，履约助手订单列表里高亮目标订单（2 秒渐隐） */
.row-highlight-from-message {
  background: #fff7e6 !important;
  transition: background 1.5s ease 0.3s;
}
.ant-table-tbody > tr.row-highlight-from-message > td {
  background: #fff7e6 !important;
}

/* ★2026-10-23 履约助手：搜索命中的明细行整行标黄（老板报障「搜图号后列表没有高亮」）
   注意：antd 表格的背景色落在 td 上，必须打到 td，打 tr 看不到效果。
   hover 时给一档浅一点的黄，避免鼠标一过就把命中标记盖掉。 */
.ant-table-tbody > tr.row-hit-search > td {
  background: #fffbe6 !important;
}
.ant-table-tbody > tr.row-hit-search > td:first-child {
  box-shadow: inset 3px 0 0 #fa8c16;
}
.ant-table-tbody > tr.row-hit-search:hover > td {
  background: #fff4d6 !important;
}

/* ★2026-10-29 履约明细 BOM 表：缺料行（可用+已排产仍不足以覆盖需求）淡红底 + 左侧红条，和「仅看缺料」高亮一致 */
.ant-table-tbody > tr.row-kit-short > td {
  background: #fff1f0 !important;
}
.ant-table-tbody > tr.row-kit-short > td:first-child {
  box-shadow: inset 3px 0 0 #ff4d4f;
}
.ant-table-tbody > tr.row-kit-short:hover > td {
  background: #ffe7e3 !important;
}

/* ★2026-10-23 物流审核：待审超过 24 小时的行（兜底人才会看到的那批）——淡红底 + 左侧红条 */
.ant-table-tbody > tr.row-overdue-review > td {
  background: #fff1f0 !important;
}
.ant-table-tbody > tr.row-overdue-review > td:first-child {
  box-shadow: inset 3px 0 0 #ff4d4f;
}
.ant-table-tbody > tr.row-overdue-review:hover > td {
  background: #ffe7e5 !important;
}

/* 主页紧凑指标格 / 快捷入口 hover 反馈 */
.stat-cell {
  transition: background 0.15s ease;
}
.stat-cell:hover {
  background: #fafcff;
}

/* ★2026-11-10（方案 A）：指标卡「能点」的明证——所有格子统一一个小箭头，
   鼠标放上去标签加下划线；以前只有极淡的背景变化，看不出哪里能点。 */
.metric-arrow {
  display: inline-block;
  margin-left: 3px;
  color: #8c8c8c;
  font-size: 13px;
  line-height: 1;
  vertical-align: 1px;
}
.stat-cell:hover .stat-label {
  text-decoration: underline;
  text-underline-offset: 2px;
}
/* 灰字副行只解释口径，不折行撑歪行高；窄屏超长自动省略 */
.metric-note {
  max-width: 100%;
}

/* ============ 首页卡片：去掉常驻阴影 ============
   以前每张卡片都自带一层投影，好几张叠在一起显得「浮」。
   现在统一成 1px 浅色描边、不带阴影，鼠标移上去才轻轻抬起，
   卡片之间的层次改用间距和标题来区分，整页更平实。 */
.dash-card {
  box-shadow: none !important;
  transition: box-shadow 0.2s ease, transform 0.2s ease;
}
.dash-card:hover {
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08) !important;
  transform: translateY(-1px);
}

/* ============ 首页（手机端）操作按钮：触控目标加大 ============
   整个系统的按钮高度仍是 32px（桌面保持紧凑，不影响其它页面），
   只把首页手机端工单卡片里的按钮撑到 44px，手指不容易点错。 */
.dash-m-actions .ant-btn {
  min-height: 44px;
  padding-left: 12px;
  padding-right: 12px;
}

/* ============ 表格数字等宽（money-nums）============
   由 ResponsiveTable 自动加在表格根节点上。
   金额/数量列的小数点上下对齐，账表对账时一眼能扫出差异；
   只改字形度量，不改变任何换行与截断规则。            */
.money-nums .ant-table-cell {
  font-variant-numeric: tabular-nums;
}

/* ★2026-09-28 老板拍板 2A：金额单元格不折行。
   由 ResponsiveTable 的脚本只给「¥ 开头的纯金额」格子加 rsp-money（备注等长文本列不受影响）。
   不折行省宽度；列实在挤时省略号收起，悬停看完整金额（脚本同时写了 title）。 */
.money-nums td.rsp-money {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* 库存余额：低于安全库存的行淡红底色 */
.row-low-stock > td {
  background: #fff2f0 !important;
}
.row-low-stock:hover > td {
  background: #ffe4e0 !important;
}

/* ============ 采购明细（po-tbl）· 2026-09-20 方案 A 统一紧凑 ============
   1) 逾期整行浅红；超期 90 天以上的行左侧加一条深红竖条（点名那批压了半年钱的单）。
      必须带 !important：antd 的 hover 与单元格背景会盖住 tr 上的背景色。
   2) 单元格内边距压到 6/8（与出库管理/采购订单/销售订单同一档），配合 size="small"
      把行高从 82px 拉回 41px。
   3) 长文本列走 antd 的 ellipsis（列上 ellipsis:true，自带 title 悬浮），
      这里把优先级提上来 —— 否则被上面 .cozy-table 的 white-space:normal 盖掉、省略号失效。 */
.po-tbl .ant-table-tbody > tr.po-overdue > td {
  background: #fff7f7 !important;
}
.po-tbl .ant-table-tbody > tr.po-overdue:hover > td {
  background: #ffeceb !important;
}
.po-tbl .ant-table-tbody > tr.po-overdue-90 > td:first-child {
  box-shadow: inset 3px 0 0 #820014;
}
.po-tbl .ant-table-thead > tr > th {
  padding: 7px 8px !important;
}
.po-tbl .ant-table-tbody > tr > td {
  padding: 6px 8px !important;
}

/* ============ 销售明细（so-tbl）· 2026-09-20 与采购明细同一套紧凑度 ============
   1) 逾期整行浅红、超 90 天左侧深红竖条（必须 !important，否则被 antd 的 td 背景盖住）
   2) 单元格内边距压到 6/8（配合 size="small"），把行高从 64px 拉回 41px 一档
   3) 长文本列走 antd ellipsis（列上 ellipsis:true），这里把优先级提上来，
      否则被 .cozy-table 的 white-space:normal 盖掉、省略号失效
   4) 点整行＝查看需求，所以整行给手型光标（勾选框/按钮由 JS 排除，不会误触发） */
.so-tbl .ant-table-tbody > tr.so-overdue > td {
  background: #fff7f7 !important;
}
.so-tbl .ant-table-tbody > tr.so-overdue:hover > td {
  background: #ffeceb !important;
}
.so-tbl .ant-table-tbody > tr.so-overdue-90 > td:first-child {
  box-shadow: inset 3px 0 0 #820014;
}
.so-tbl .ant-table-thead > tr > th {
  padding: 7px 8px !important;
}
.so-tbl .ant-table-tbody > tr > td {
  padding: 6px 8px !important;
  cursor: pointer;
}

/* 图纸管理：图号（图纸名称）列定宽+省略号兜底（cozy-table 全局 nowrap 会干扰 antd ellipsis，
   提优先级确保长图号显示省略号而非撑宽整表）★2026-10-01 老板拍板 C 方案 */
.ant-table-cell.ant-table-cell-ellipsis {
  white-space: nowrap !important;
  word-break: keep-all !important;
  overflow-wrap: normal !important;
  overflow: hidden !important;
  text-overflow: ellipsis !important;
}
.cozy-table td.dw-col-name { font-weight: 600; }

/* ============================================================
   图纸管理 · 移动端（2026-09-24）
   全部用 .dw- 前缀限定在本页，不改任何全局/antd 默认值。
   背景：图纸页原来没有做移动端适配，手机上左侧「机型文件夹」固定
   210px 占了 54% 屏宽，右边的表格只剩 110px 却要横滚 642px。
   ============================================================ */
/* 手机上：桌面侧栏整体收起（改用上方胶囊条） */
.dw-hide-mobile { display: none !important; }

/* 机型筛选胶囊条（横向滑动，不占竖版空间） */
.dw-chips {
  display: flex; gap: 8px; overflow-x: auto; padding: 2px 0 10px;
  -webkit-overflow-scrolling: touch; scrollbar-width: none;
}
.dw-chips::-webkit-scrollbar { display: none; }
.dw-chip {
  flex: 0 0 auto; height: 34px; line-height: 32px; padding: 0 14px;
  border: 1px solid #e8e8e8; border-radius: 999px; background: #fff;
  font-size: 13px; color: #595959; cursor: pointer; user-select: none;
}
.dw-chip.on { background: #e3edf8; border-color: #5d8cc6; color: #123a6b; font-weight: 500; }

/* 图纸管理 V3（2026-10-01）：文件夹多行 TAB —— flex-wrap 不横滑，长名省略号 + tooltip，角标＝不含回收站数量 */
/* ★2026-10-01 文件夹由「平铺标签」改为「可折叠树」（层级按名称里的 / 展开，子文件夹不再拉成一长条） */
.dw-tree { display: flex; flex-direction: column; gap: 1px; margin-bottom: 12px; max-height: 320px; overflow-y: auto; }
.dw-tree-row {
  display: flex; align-items: center; gap: 6px; height: 30px; padding: 0 8px;
  border-radius: 6px; cursor: pointer; font-size: 13px; line-height: 30px;
  white-space: nowrap; user-select: none;
}
.dw-tree-row:hover { background: #f5f7fa; }
.dw-tree-row.on { background: #e3edf8; color: var(--zg-brand-700); font-weight: 600; }
.dw-tree-caret { width: 14px; display: inline-flex; align-items: center; justify-content: center; color: #9aa4b2; font-size: 12px; flex-shrink: 0; }
.dw-tree-caret.leaf { visibility: hidden; }
.dw-tree-lb { overflow: hidden; text-overflow: ellipsis; max-width: 260px; }
.dw-tree-cnt { font-size: 12px; color: #8c8c8c; background: #f0f2f5; border-radius: 9px; padding: 0 7px; line-height: 17px; margin-left: 2px; }
.dw-tree-row.on .dw-tree-cnt { color: var(--zg-brand-700); background: #e3edf8; }
.dw-tree-node { display: flex; flex-direction: column; gap: 1px; }
.dw-tabbar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 12px; }
.dw-tab {
  display: inline-flex; align-items: center; max-width: 260px;
  height: 30px; padding: 0 12px; border: 1px solid #d9d9d9; border-radius: 6px;
  background: #fff; font-size: 13px; color: #595959; cursor: pointer;
  user-select: none; transition: all .15s; line-height: 18px; overflow: visible;
}
.dw-tab:hover { border-color: var(--zg-brand-700); color: var(--zg-brand-700); }
.dw-tab.on { background: #e3edf8; border-color: var(--zg-brand-700); color: var(--zg-brand-700); font-weight: 600; }
.dw-tab-t { max-width: 130px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dw-tab-n {
  margin-left: 6px; font-style: normal; font-size: 12px; color: #8c8c8c;
  background: #f5f5f5; border-radius: 10px; padding: 0 6px; flex-shrink: 0;
}
.dw-tab.on .dw-tab-n { color: var(--zg-brand-700); background: #e3edf8; }
@media (max-width: 991px) {
  .dw-tab { height: 44px; line-height: 42px; max-width: 46vw; }
  .dw-tab-t { max-width: 30vw; }
}

/* 图纸墙（缩略图网格）：手机 2 列，桌面自适应铺满 */
.dw-wall { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
@media (max-width: 991px) { .dw-wall { grid-template-columns: repeat(2, 1fr); gap: 10px; } }
.dw-wall-item {
  border: 1px solid #f0f0f0; border-radius: 8px; overflow: hidden;
  background: #fff; cursor: pointer; transition: border-color .15s;
}
.dw-wall-item:active { border-color: #5d8cc6; }
.dw-wall-im { position: relative; height: 220px; background: #fafafa; display: flex; align-items: center; justify-content: center; border-bottom: 1px solid #f5f5f5; }
@media (max-width: 991px) { .dw-wall-im { height: 150px; } }
.dw-thumb-img { max-width: 100%; max-height: 100%; display: block; }
.dw-thumb-ph { font-size: 12px; color: #bfbfbf; }
.dw-sheet-ph { display: flex; flex-direction: column; align-items: center; gap: 8px; color: #389e0d; font-size: 13px; }
.dw-wall-badge {
  position: absolute; left: 6px; top: 6px; font-size: 12px; line-height: 18px;
  padding: 0 6px; border-radius: 4px; background: #fafafa; color: #8c8c8c; border: 0.5px solid #d9d9d9;
}
.dw-wall-badge.ok { background: #f6ffed; color: #389e0d; border-color: #b7eb8f; }
.dw-wall-cap { padding: 8px 10px; }
.dw-wall-name { font-size: 13px; font-weight: 500; line-height: 1.35; word-break: break-all;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.dw-wall-meta { font-size: 12px; color: #8c8c8c; margin-top: 3px; }
.dw-wall-empty { grid-column: 1 / -1; text-align: center; color: #8c8c8c; padding: 30px 0; font-size: 14px; }

/* 列表模式下的手机卡片（ResponsiveTable mobileCardRender） */
.dw-card { display: flex; gap: 10px; align-items: center; }
.dw-card-name { font-size: 14px; font-weight: 500; line-height: 1.35; word-break: break-all; }
.dw-card-meta { font-size: 12px; color: #8c8c8c; margin-top: 3px; }

/* 手机端上传：右下角悬浮按钮（手机没有拖拽动作） */
.dw-fab {
  position: fixed; right: 16px; bottom: 78px; width: 52px; height: 52px; border-radius: 26px;
  background: var(--zg-brand-700); color: #fff; font-size: 26px; line-height: 52px; text-align: center;
  box-shadow: 0 4px 14px rgba(18, 58, 107, .35); z-index: 20; cursor: pointer;
}

/* 手机端触控目标：统一抬到 44px（WCAG 建议的最小触控尺寸），小号按钮 40px。
   ★必须 !important：antd 5 的 CSS-in-JS 样式在运行时注入 <head>，通常排在 index.css 之后，
     同等特异性会盖掉这里的规则（2026-09-24 实测第一次没加 !important，高度完全没变）。 */
@media (max-width: 991px) {
  .dw-mobile .ant-btn { height: 44px !important; min-width: 44px; }
  .dw-mobile .ant-btn.ant-btn-sm { height: 40px !important; }
  .dw-mobile .ant-input-affix-wrapper,
  .dw-mobile .ant-select-single .ant-select-selector { min-height: 40px !important; }
  .dw-mobile .ant-segmented-item { min-height: 44px; display: flex; align-items: center; }
  .dw-mobile .ant-segmented-item-label { line-height: 42px !important; }
  .dw-mobile .ant-pagination-item { min-width: 44px; height: 44px !important; line-height: 42px !important; }
  /* Card 顶部页签：手机上允许横向滑动，别再被挤成「图纸…」 */
  .dw-mobile .ant-card-head .ant-tabs-nav-wrap { overflow-x: auto; }
  .dw-mobile .ant-card-head .ant-tabs-tab { padding: 10px 12px !important; }
}
/* 机型胶囊也抬到 44px */
.dw-chip { height: 44px; line-height: 42px; }

/* ============================================================
   ★2026-09-28 老板拍板（收口）：全站列表「不折叠，只横滑」
   上面 .cozy-table 与「弹窗内表格」两条已按新口径改过，这条兜住所有没挂
   cozy-table 的历史表格（直接用的 antd Table），保证全站一个样子：
     —— 单元格一律不折行，列宽按内容撑开，装不下就在表格里左右拖；
     —— 不想看的列用表格右上角的「列设置」关掉，而不是靠挤压。
   ★必须 !important：antd 5 的 CSS-in-JS 在运行时注入 <head>、排在本文件之后，
     同特异性会反盖回来（图纸页 40px 那条已踩过一次）。
   （采购/销售明细的长文本列走 ellipsis，本来就是 nowrap+省略号，不受影响。）
   ============================================================ */
.ant-table-thead > tr > th.ant-table-cell,
.ant-table-tbody > tr > td.ant-table-cell,
.ant-table-summary > tr > td.ant-table-cell {
  white-space: nowrap !important;
  word-break: keep-all !important;
  overflow-wrap: normal !important;
}
/* ★2026-10-06（老板拍板 B 方案）：消息中心「内容」列例外——允许折行。
   上面那条全站「不折行只横滑」是 !important，会盖掉列内联样式（第一版就是栽在这），
   所以例外必须同样 !important 且特异性更高（多挂一个类）。
   理由：消息内容常带一长串图号，横滑会把右侧「标记已读/删除」推出屏幕且外层裁切后连滚动条都没有。
   仅此一列例外，其它列表页不受影响。 */
.ant-table-tbody > tr > td.ant-table-cell.msg-cell-wrap {
  white-space: normal !important;
  word-break: break-all !important;
  overflow-wrap: anywhere !important;
  line-height: 1.7;
}
/* ★2026-11-07 全局复核（老板：「这个溢出要检查全局」）：
   根因不在某一页的列宽，而在下面这条「表格单元格内 Space 一律不折行」的全局规则——
   它本意是让多个状态/人名 Tag 不堆成两层，却把「操作」列的按钮组也一起锁死了。
   列宽不足时（hFit 定宽 / fixed:'right' 钉列 / 用户拖窄列宽）按钮不换行，整组顶出
   td 右边界：右固定列把「删除」推到窗口外，表体还凭空多一条横向滚动条。
   全站源码实测（2026-11-07）：61 处带按钮的操作列，其中 16 处源码写了 ≥3 个按钮
   （含按状态互斥显示的分支，是上界）、声明宽度多在 110~250px；只要某一行同时显示
   3 个以上按钮就可能不够宽。属共性缺陷，不逐页打补丁。
   现在按内容类型分流：【含按钮】的 Space/Flex 允许换行；【纯 Tag / 文本】组维持
   不折行（2026-09-28「不折行只横滑」口径不变）。
   ★用 :has(.ant-btn) 分流：ResponsiveTable 的列宽把手已用同款 :has() 选择器，
     浏览器基线一致；旧 nowrap 规则故意保留在前面，万一不支持 :has() 也只是退回
     今天的 nowrap，不会更坏。规范见《UI设计基准.md》2.4.3。 */
/* 单元格里的标签组（多个状态/人名标签）也不许自动折行：
   装不下就把列撑宽、左右拖着看，而不是堆成两层。 */
.ant-table-tbody > tr > td .ant-space-horizontal,
.ant-table-tbody > tr > td .ant-space {
  flex-wrap: nowrap !important;
}
/* ★操作列按钮组例外（特异性高于上面两条，同带 !important，稳定覆盖）：
   排得下仍是一行；排不下自动换到第二行，绝不顶出单元格。
   第三行选择器兜住「没用 antd Space/Flex、自己写 <span style="display:flex"> 包按钮」的页面：
   命中 td 的直接子容器，它是 flex 才生效，是普通行内元素则无副作用。 */
.ant-table-tbody > tr > td.ant-table-cell .ant-space:has(.ant-btn),
.ant-table-tbody > tr > td.ant-table-cell .ant-flex:has(.ant-btn),
.ant-table-tbody > tr > td.ant-table-cell > :has(.ant-btn) {
  flex-wrap: wrap !important;
  row-gap: 2px;
}
/* 裸按钮（按钮直接摆在 td 里）：解除单元格 nowrap，避免按钮旁边的文字撑死整行。
   ★局限：inline 按钮之间没有换行机会，多个裸按钮不会自动换行——这类页面应改用容器包裹。
   源码实测 25 处「无 Space/Flex 包裹」的操作列，绝大多数只有 1 个按钮、或按钮按状态互斥显示。 */
.ant-table-tbody > tr > td.ant-table-cell:has(> .ant-btn) {
  white-space: normal !important;
  word-break: normal !important;
}
/* ★2026-11-10：消息中心原页面级例外（msg-action-cell / msg-actions）已删除，改由上方全局 :has(.ant-btn) 规则统一兜底换行。
   全站不再保留任何页面级 .xxx-action-cell 折行例外类名（详见《UI设计基准.md》2.4.3）。 */

/* ★2026-09-29：横向拖到头就停住，不再「带着整页一起滑」，
   也不会在 Mac 触控板上因为继续往左滑而触发浏览器的「返回上一页」手势
   （表现就是「滑着滑着整个页面滑走了」）。 */
.ant-table-wrapper,
.ant-table-content,
.ant-table-body,
.ant-modal-body,
.ant-drawer-body {
  overscroll-behavior-x: contain;
}

/* 2026-09-27：任务详情弹窗底部操作按钮，手机端抬到 42px 更易点（弹窗是 portal，不受 .dw-mobile 包裹影响，单独指定）。
   ★必须 !important：antd 5 运行时 CSS 在 index.css 之后注入，同等特异性会盖掉（同 .dw-mobile 规则）。 */
@media (max-width: 991px) {
  .task-detail-actions { flex-wrap: wrap; row-gap: 8px; }
  .task-detail-actions .ant-btn { height: 44px !important; min-width: 44px; }

  /* ★2026-10-04：弹窗 / 抽屉底部按钮排不下就换行，不再溢出被屏幕切掉。
     数组式 footer（antd 默认）本身是 inline-block 会自动换行；这里主要盖住
     用 Space / Flex 自定义 footer 的写法——Space 默认 nowrap，按钮一多就顶出屏幕。 */
  .ant-modal-footer .ant-space,
  .ant-drawer-footer .ant-space,
  .ant-modal-footer .ant-flex,
  .ant-drawer-footer .ant-flex {
    flex-wrap: wrap;
    row-gap: 8px;
    justify-content: flex-end;
  }
}

/* ============================================================
   2026-10-13 移动端专项（老板定调：方便使用，不是功能全）
   ★以下规则全部包在 ≤768px 内，PC 端一条都不生效 → 零影响。
   ============================================================ */
@media (max-width: 991px) {
  /* ① 弹窗 / 抽屉内容限高
     全站 63 个 Modal/Drawer 里 51 个没限高，内容一多就撑出屏幕，
     底部「确定 / 取消」直接够不着（内嵌表格的 14 处最严重）。
     这里一处 CSS 全覆盖，不必逐个改文件。
     ★必须 !important：antd 5 运行时 CSS 在 index.css 之后注入（同 .task-detail-actions 的教训）。 */
  .ant-modal-body {
    max-height: 70vh !important;
    overflow-y: auto !important;
    -webkit-overflow-scrolling: touch;
  }
  .ant-drawer-body {
    max-height: 70vh !important;
    overflow-y: auto !important;
    -webkit-overflow-scrolling: touch;
  }
  /* ★2026-10-23 例外：全屏抽屉（导航菜单 / 更新日志 / 图纸审计 / 图纸回收站）
     吃了上面的 70vh 限高 → 内容/深色背景只盖住上半屏、下面一片白、滑不到底。
     挂 dw-fullheight 的抽屉解除限高、内容区撑满整屏自己滚动；其余内容抽屉维持 70vh 不动。 */
  .ant-drawer.dw-fullheight .ant-drawer-body,
  .ant-drawer-content.dw-fullheight .ant-drawer-body {
    max-height: none !important;
    height: 100% !important;
  }

  /* ② 手机卡片脚部的操作按钮组：允许换行
     （31 个页面的操作列塞了 ≥3 个按钮，Space 默认 nowrap，会挤成一排、
       字被压扁或顶出屏幕）。 */
  .rsp-card-foot,
  .rsp-card-foot .ant-space,
  .rsp-card-foot .ant-flex {
    flex-wrap: wrap !important;
    row-gap: 6px;
    column-gap: 6px;
  }

  /* ③ 卡片「展开其余 N 项」折叠入口（ResponsiveTable 默认卡片，10-13 新增） */
  .rsp-card-more {
    margin-top: 6px;
    padding: 5px 0;
    text-align: center;
    font-size: 13px;
    color: #1890ff;
    cursor: pointer;
    user-select: none;
    border-top: 1px dashed #f0f0f0;
  }
}
