/* ERP 界面 Design Tokens v1.0 —— 两个 ERP 共用
 *
 * 权威文档: docs/UI_STANDARD_v1.md
 * 机检:     node scripts/ui_audit.js
 *
 * 规矩只有一条: 页面 CSS 里不许再出现硬编码的色值 / 字号 / 圆角 / 间距,
 * 一律引这里的变量。改这里 = 改规范, 要先改文档。
 *
 * 站内差异(侧栏宽/内容左边缘)在文件末尾按站点覆盖 —— 这两个值两站不同是
 * 认可的(改动风险大于收益), 但**站内必须只有一个值**。
 */
:root {
  /* ── 颜色: 只承载语义, 不做装饰 ───────────────────────── */
  --c-primary:        #22c55e;   /* 主按钮底 / 选中 / 链接 */
  --c-primary-hover:  #16a34a;
  --c-primary-deep:   #15803d;
  --c-primary-bg:     #dcfce7;   /* 选中行 / 选中菜单底 */
  --c-primary-bg-weak:#f0fdf4;

  --c-danger:         #dc2626;
  --c-danger-bg:      #fef2f2;
  --c-warn:           #d97706;
  --c-warn-bg:        #fffbeb;
  --c-info:           #2563eb;

  --c-text:           #262626;
  --c-text-2:         #6b7280;
  --c-text-3:         #9ca3af;
  --c-line:           #e5e7eb;
  --c-bg:             #f9fafb;
  --c-surface:        #ffffff;
  /* 列表行 hover 底色 —— 实测哈他现状是 #f0fdf4, 全年是 #f6fdf8(全年版覆盖)。
     单拎出来是因为 T-17 的冻结列要跟它对齐, 不然横拉时鼠标划过, 冻结的
     那几格不跟着变色, 一行看着像断成两截。 */
  --c-row-hover:      #f0fdf4;

  /* ── 字号: 只有 6 档, 多一档都不行 ──────────────────────
     实测收敛前: 哈他还有 15/16/20/22/26/28, 全年还有 10/16/20/25 */
  --fs-micro: 11px;   /* 徽章 / 单位后缀 */
  --fs-sm:    12px;   /* 辅助说明 */
  --fs-base:  13px;   /* 正文 —— 全站主力 */
  --fs-md:    14px;   /* 小标题 / fieldset 标题 */
  --fs-lg:    18px;   /* 页面标题 */
  --fs-xl:    24px;   /* 看板 KPI 数值 */

  /* ── 圆角: 只有 2 档 ────────────────────────────────────
     实测收敛前: 哈他 3/4/6/8/10/12, 全年 2/3/4/5/6/8 —— 先收成 4/8/full,
     09-11 吴凡看小满的供应商调整单列表:「两站的各种表格框都用直角框显示,
     不用椭圆角」, 于是矩形那两档直接归零。

     ★ 为什么归零而不是删 token: 全站 40+ 处写的是 var(--r-sm),
       token 留着、值改成 0, 一处都不用动; 哪天要改回去也只改这两行。
     ★★ --r-full **不能一起归零**: 头像、状态灯、编辑开关那颗 pill
       靠它成形。归零的话头像变方块、开关变成一根方条 ——
       小满那张截图右上角的头像也还是圆的。 */
  --r-sm:   0;        /* 输入框 / 按钮 / 徽章 / 表格 —— 直角 */
  --r-md:   0;        /* 卡片 / 弹窗 / 看板块 —— 直角 */
  --r-full: 9999px;   /* 头像 / 状态灯 / 开关 —— 这一档保留 */

  /* ── 间距: 4 的倍数, 只有 6 档 ──────────────────────── */
  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 20px;
  --sp-6: 24px;

  /* ── 尺寸 ──────────────────────────────────────────────
     控件与按钮同高, 表单里就不会出现参差不齐的一排。
     实测收敛前: 控件高 哈他 29/37 · 全年 32/38
                 行高   哈他 31/34/42/37 · 全年 42/50/47
                 按钮高 哈他 34/36/38 · 全年 15/18/30/35/36/37 */
  --h-control: 36px;
  --h-btn:     36px;
  --h-btn-sm:  32px;   /* 表格行内操作 */
  --h-row:     36px;   /* 列表页表格行高, 与控件同高 */
  --h-thead:   40px;
  --h-topbar:  48px;

  /* ── 布局: 09-09 两站已统一 ────────────────────────────
     原先哈他 160/205、全年 200/288。统一取 200/208 (内容左 = 侧栏 + 8):
     哈他加宽的 40px 全部来自它和内容区之间那条 45px 死空隙, 内容只少 3px;
     全年内容左边缘收 80px 直接让给表格(它有 11 个列表页超 12 列)。
     ★ 这两个值由各站自己的主题 CSS 落地(哈他 foodmax-theme 的侧栏宽 + padding 链,
       全年 theme.js 的 sidebarWidth + 下面 .container.mx-auto 撑满), 这里只作记录。 */
  --sidebar-w:    200px;
  --content-left: 208px;

  --dur: .15s;         /* 过渡时长, 全站一个值 */
}

/* ── 基础落地: 只写规范里明确要求"全站一个值"的那几条 ────────
   不覆盖任何 🔴 锁定项(侧栏宽/active 色/一二级字号/内容左 205/
   +新建按钮/filter 列数/inline 列宽), 那些由各站原有 CSS 负责。 */

/* ══ 全年供应链 qn.sslx.me 专属 ══════════════════════════════
   这一段由 scripts/deploy_ui_tokens.sh 在生成全年版时插入 ——
   **不要直接改 static/jg/ui-tokens.css**, 下次部署会被覆盖。 */

/* 行 hover 底色: 全年 theme.css 现状是 #f6fdf8, 哈他是 #f0fdf4。
   T-17 的冻结列靠这个 token 跟行 hover 对齐, 值不对就会横拉时看到
   一行断成两截(冻结的几格不变色)。这里按各站现状对齐, 不强行统一 ——
   统一配色是另一件事, 要改先改 theme.css, 别在收敛层偷偷改。 */
:root { --c-row-hover: #f6fdf8; }

/* ── 编辑态: 4 列之后标签列也要收窄 (**只给全年**) ────────────────────
   一行从 3 个变成 4 个之后每列只剩 304px, 而标签列还是 unfold 写死的
   14rem(224px) —— 输入框只剩 60px, 「新土豆（黄心）原料」显示成「新土豆（黄…」。
   收到 6.5rem(104px), 输入框拿回 ~180px。
   ★★ 为什么只给全年: 哈他的 .field-line 是**竖排**(标签在上、值在下),
      `flex: 0 0 6.5rem` 在竖排里变成"标签块**高** 104px", 把值顶到 150px 以下。
      09-11 我先写成两站通用, 当场把哈他采购单详情页打坏, 回量才发现。
      **同一条 CSS 对两套 DOM 未必都成立。**
   ★ min-width 一起清掉: unfold 写的是 `lg:min-w-56 lg:w-56` 两条,
     只改 width 不改 min-width, 量出来一点没变。 */
#content-main .field-line { align-items: flex-start !important; }
#content-main .field-line > div:first-child {
  flex: 0 0 6.5rem !important;
  width: 6.5rem !important;
  min-width: 0 !important;
}

/* 内容左边缘 208: 侧栏 200 之外还被 .container.mx-auto 的居中留白吃掉 80px,
   表格白白窄了 176px。撑满。(哈他那边由 foodmax-theme 负责) */
#content.container.mx-auto,
div#content.container.mx-auto,
.container.mx-auto {
  max-width: 100% !important; margin-left: 0 !important; margin-right: 0 !important;
  padding-left: 0 !important; padding-right: 0 !important;
}

/* 侧栏一级分类的图标 (nav_icons.js 注入) —— 与哈他 .nav-icon 同一套观感:
   20px 图标 + 中性灰, hover 变主色, 不参与 flex 伸缩。
   09-09 吴凡: 顶层单项有图标、可折叠分组没有, 文字左边缘两种起点。 */
#nav-sidebar .jg-nav-icon {
  font-size: 20px !important;
  color: var(--c-text-2) !important;
  flex: 0 0 auto !important;
  transition: color var(--dur);
}
#nav-sidebar h2:hover .jg-nav-icon,
#nav-sidebar h3:hover .jg-nav-icon { color: var(--c-primary-hover) !important; }
#nav-sidebar h2, #nav-sidebar h3 { display: flex; align-items: center; }

/* 二级分类不要图标 (吴凡 09-09: 对齐哈他 —— 一级有图标, 二级没有)。
   `.jg-has-title` 由 nav_icons.js 打在**有一级标题的分组**上, 所以顶部那三个
   单项(经营看板/填报规则/报个问题)本身是一级, 图标不受影响。
   去掉图标后子项文字要缩进到与一级文字同一条线上:
   一级文字起点 = px-3(12) + 图标 20 + 间距 8 = 40px。 */
#nav-sidebar .jg-has-title ol li a .material-symbols-outlined,
#nav-sidebar .jg-has-title ul li a .material-symbols-outlined {
  display: none !important;
}
/* 三层对齐 (吴凡 09-09:「一个二级对齐的是文字, 一个二级对齐的是图标, 都对齐文字」)。
   实测改前三层三个起点: 顶层单项 图标24/文字54 · 一级 图标36/文字64 · 二级 文字40。
   统一成 **图标 24 / 文字 54** —— 与顶层单项看齐, 也与哈他的「二级对齐一级文字」一致。
   一级图标左移 12(抵消 H2 的 margin-left:12), 间距 10 让文字正好落在 54。 */
#nav-sidebar .jg-nav-icon { margin-left: -12px; margin-right: 10px; }
#nav-sidebar .jg-has-title ol li a,
#nav-sidebar .jg-has-title ul li a { padding-left: 54px !important; }

/* L-13 组标题的说明位: 第二行小字, 不跟标题挤一行。
   挤一行的下场是顶栏被撑长、侧栏截成「系统与历史数据　只读，出」。 */
#nav-sidebar h2 { flex-wrap: wrap; }
#nav-sidebar .jg-nav-note {
  flex: 0 0 100%;
  padding-left: 30px;
  font-size: var(--fs-micro);
  font-weight: 400;
  color: var(--c-text-3);
  line-height: 1.4;
}

/* L-06 新建页顶栏溢出 23px —— 全站 80 个 /add/ 页在 1280 窄屏都横滚。
   实测: `.jg-top-nav` 里 12 个菜单 tab + 用户区正好占满 1280, 而新建页比列表页
   **多一个 38px 的 `jg-addslot`**(列表页它宽 0), 把用户区顶到 right=1303。
   09-09 那次全站扫在 1920 下跑, 看不见 —— 又是"窄屏才暴露"(T-15 那条)。
   ★ 不给顶栏加 overflow-x:auto —— 用户下拉是 position:absolute 往下开,
     一旦父级 overflow 不是 visible 就会被裁掉, 菜单点开只剩一条缝。
   ★ 改成窄屏收 tab 的左右内边距: 16→11px, 12 个 tab 省 120px, 缺的 23px 绰绰有余,
     文字一个字都不截。1440 以上保持原样。 */
@media (max-width: 1440px) {
  .jg-top-nav a.tab { padding-left: 11px !important; padding-right: 11px !important; }
}

/* T-02 行高: 付款/回款状态列堆成三行, 把行撑到 57px(规范 36px)。
   实测 采购单 field-pay_col = 状态 + 日期 + 付款按钮 **三个块级元素竖着排**,
   内容 52px; 销售单 field-receipt_col 两行 38px。
   ★ **不动 payment.py** —— 那边注释写得很清楚, 每行都占两行高是**故意的**
     (「才不会出现两档行高」), 而且 apps/masterdata/ 今天是另一个窗口在改。
     这里只在显示层把「日期」和「付款」并到同一行: 信息一个不丢, 从三行变两行。
   ★ 治本的做法是把「付款/收款」挪进操作列中格(规范 T-11 第二步), 那时这段可以删。 */
#result_list td.field-pay_col > div,
#result_list td.field-receipt_col > div,
#result_list td.field-cost_review_col > div {
  display: inline-block;
  line-height: 14px;
  vertical-align: middle;
}
#result_list td.field-pay_col,
#result_list td.field-receipt_col,
#result_list td.field-cost_review_col { line-height: 16px; }

/* 多选筛选(销售渠道)收成浮层 —— 吴凡 09-09:「一直下拉应该是个bug」。
   它整块 253px 高, 而同排别的筛选只有 38px, 一个人把筛选条撑成三行。
   收起后只剩「标题 + 搜索框」, 与别的筛选等高; 点搜索框才展开选项列表。
   ★ 只改外壳。里面的搜索过滤 /「全部」语义 / sessionStorage 记忆
     是刘晓缤和张丽博 09-03 提过两轮才做成的, 一行没动。 */
.jg-multi-filter .jg-multi-list { display: none; }
.jg-multi-filter.jg-mf-open { position: relative; z-index: 30; }
.jg-multi-filter.jg-mf-open .jg-multi-list {
  display: block;
  position: absolute;
  left: 0; right: 0; top: 100%;
  margin-top: var(--sp-1);
  max-height: 260px;
  overflow-y: auto;
  background: var(--c-surface);
  border: 1px solid var(--c-line);
  border-radius: var(--r-sm);
  box-shadow: 0 6px 16px rgba(0, 0, 0, .12);
}
/* 「已选 N」—— 收起来之后唯一能看出筛选还生效的地方 */
.jg-multi-filter .jg-mf-count:not(:empty) {
  margin-left: var(--sp-2);
  padding: 1px var(--sp-2);
  border-radius: var(--r-sm);
  background: var(--c-primary-bg);
  color: var(--c-primary-hover);
  font-size: var(--fs-micro);
}

/* ── 全年的 `jg-*` 那套也要直角 (**只给全年**) ────────────────────
   09-11 全站改直角。哈他那边 token 归零 + 压平 unfold 的 `rounded-*`
   就干净了, 全年不行 —— 它有**自己一整套** `jg-*` 组件
   (`static/jg/theme.css` 里 30+ 处硬编码 4px/8px), 既不走 token,
   也不带 Tailwind 的 `rounded-` 类, 两条规则一条都够不着。
   实测漏网的正是吴凡截图圈的那几个: `.jg-filter-card`(筛选卡 8px)、
   `.jg-searchbtn` `.jg-exportbtn` `.jg-addbtn`(查询/导出/新增 4px)。

   ★ `theme.css` 不在本窗口的归属表里, 所以压在收敛层, 不去改它。
   ★★ 三样**必须留圆**, 排除条件别删:
      · `.jg-bell-dot` 是铃铛上的**红点**       → `:not([class*="-dot"])`
      · `.avatar` 是圆形头像                    → `:not([class*="avatar"])`
      · `.ui-switch` / `.ui-switch-knob` 那颗 pill(「可以编辑/锁定编辑」开关)
        不带 `jg-` 前缀, 天然不在范围内 —— 但别顺手把选择器放宽到通配符,
        一放宽这颗开关就变成方条了。
   ★ 用前缀匹配而不是一个个列: 这套组件还在长, 列举法下次加个
     `.jg-xxxbtn` 又是圆的。 */
/* ── 加工产出表里那列「这单还有哪几种损耗」(**只给全年**) ────────────
   吴凡 2026-09-11 拿哈他那张产出表对比:「直接改成这样的」。
   那一列是一组 checkbox(`.jg-people-pick`), 挤在表格单元格里,
   每个标签的文字被**逐字断行竖排**下去、还被容器裁断 ——
   「腐损」「自然失重」在页面上是一列单字, 根本读不出来, 表也被撑变形。
   ★ 功能一个字没动(聂广镇 09-03 要的「一次勾几种损耗」还在),
     只是让它横着排、不换行、超出就滚。 */
#outputs-group .jg-people-pick { max-height: 96px; overflow-y: auto; }
#outputs-group .jg-people-pick label {
  display: flex; align-items: center; gap: 4px;
  white-space: nowrap; font-size: var(--fs-sm); line-height: 20px;
}
#outputs-group .jg-people-pick input[type="checkbox"] { flex: 0 0 auto; }

/* ══════════════════════════════════════════════════════════════
   全站直角。

   ★★★ 吴凡 2026-10-10 拍板：「全部直角为准」。
      起因：这条规则原来**只压 `jg-` 开头的元素**，而标准列表页的
      卡片是 Unfold 的 `rounded-default`（圆角）—— 于是
      「照着列表页做」的自建页，一挂 jg- 类圆角就被抹平，
      做出来反而跟列表页不一样。两套规范在同一个页面上打架。

   ★★ 口径统一成**全站直角**之后，`docs/UI_STANDARD_v1.md` 的 C-06
      （圆角三档 4/8/9999）也跟着改了 —— 只剩「直角 + 正圆」两种。

   ★ 唯二例外：**头像**和**状态圆点**。它们的「圆」是形状本身，
     不是装饰 —— 压成方的就不是那个东西了。
     例外写在后面：同为 !important 时后者胜。
   ══════════════════════════════════════════════════════════════ */
*, *::before, *::after {
  border-radius: 0 !important;
}
[class*="rounded-full"], [class*="avatar"], [class*="-dot"],
[class*="rounded-full"]::before, [class*="rounded-full"]::after {
  border-radius: 9999px !important;
}

body { font-size: var(--fs-base); color: var(--c-text); }

/* 输入控件: 全站一种高度、一种圆角。
   ★ 用 !important 是因为本文件排在 foodmax-theme.css 之后, 而那边同类规则
     也带 !important —— 不带就压不住, 页面上看不出任何变化。
   ★ 排除 checkbox/radio(它们本来就该是小方块)和 file(浏览器原生控件)。 */
input:not([type=checkbox]):not([type=radio]):not([type=hidden]):not([type=file]),
select,
.select2-container .select2-selection--single {
  height: var(--h-control) !important;
  min-height: var(--h-control) !important;
  border-radius: var(--r-sm) !important;
  font-size: var(--fs-base) !important;
}
textarea {
  min-height: calc(var(--h-control) * 2);
  border-radius: var(--r-sm) !important;
  font-size: var(--fs-base) !important;
}

/* F-04 控件不许压到隔壁格子。
   日期框在 Unfold 里带 min-w-56(224px), 一旦格子只有 118px 就溢出 106px
   压住右边的标签 —— 哈他全站 28 处都是这一个根因。
   实测链路: input.min-w-52(208px) 装在 div.min-w-56(224px) 里, 而格子只有 130px。
   光给 input 加 max-width 没用 —— 撑开格子的是**外面那层 wrapper**, 所以
   min-width 必须连 wrapper 一起清掉。 */
/* ★★ 必须排除 checkbox / radio —— 09-11 吴凡:「是否次月销售两个地方都填不了,
   这应该是没人填写的原因」。查下来**是这条规则干的**:
   Unfold 把布尔字段渲染成**开关就是 input 自己**
   (`appearance-none ... w-8 min-w-8 rounded-full`), 靠 Tailwind 的 `min-w-8` 撑住 32px。
   这里的 `min-width:0 !important` 把它抹了, 而父级是 flex 行 ——
   没有最小宽度, 它就被压成 **0×20**: 渲染了、可见、没禁用, **但点不着**。
   全年 66,265 张采购单一张都没勾过这个字段, 模型注释里当成"业务上没人填",
   其实是**填不了**。
   ★ 同文件上面那条控件高度规则本来就排除了 checkbox/radio, 这条漏了 ——
     同一个文件里两条规则对同一类控件给了两种待遇, 就是这么出事的。 */
.field-line input:not([type="checkbox"]):not([type="radio"]),
.field-line select,
.field-line textarea,
.field-line .select2-container,
.field-line [class*="min-w-"]:not([type="checkbox"]):not([type="radio"]) {
  max-width: 100% !important;
  min-width: 0 !important;
  box-sizing: border-box;
}
/* F-02/F-03 一行三列, 等宽。
   ★ 真正的根因在**行**, 不在控件: 那一行的三个格子本来就不等宽 ——
     「下单日期」的格子只有 130px, 而右边的格子有 530px。所以给控件设
     min-width 它就溢出压住邻居, 清掉 min-width 它就缩到 106px 把
     "2026/09/09" 截成 "2026/09/0"。两头都不对, 因为改的是果不是因。
     行改成等宽三列栅格, 控件填满格子, 溢出和截断就同时没有了,
     而且全站输入框左边缘自动落在同一条线上(F-03)。
   独占一行的字段(备注/地址)仍然铺满整行, 保持原有观感。

   ★★ 选择器必须写 `div.form-row`, 不能写 `.form-row`。
      inline 明细表的每一行**也叫 form-row**, 但它是 `<tr>`; 主表单的行是 `<div>`。
      写成 `.form-row` 会把 <tr> 一起压成 grid 3 列 —— 表头 9~14 列、行体挤成 3 列,
      就是王悦报的「采购明细显示异常」和全年加工单投入原料错乱。
      按**元素类型**分(div=主表单 / tr=明细行)比按 class 白名单(.tabular-table)可靠:
      全年的 inline <tr> 父级是 TBODY.form-group, 根本没有 .tabular-table, 白名单漏了它。 */
fieldset div.form-row {
  display: grid !important;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--sp-3) var(--sp-5);
  align-items: start;
}
fieldset div.form-row > .field-line:only-child { grid-column: 1 / -1; }

/* 09-09 王悦「新建采购单 采购明细显示异常」修复:
   上面 `fieldset .form-row` 的三列栅格(!important)本意只给主表单用, 但 tabular inline
   的行也叫 `.form-row`(class="form-row dynamic-items") 且同样落在 <fieldset> 内, 于是被
   压成 grid 3 列 → 采购明细/付款计划整行的输入框错位堆叠(表头 14 列 vs 行体挤成 3 列)。
   桌面(≥1024, 对齐 Unfold 的 lg:table-cell)下把 tabular inline 的行显式复原成表格行,
   td 自然回到 Unfold 的 lg:table-cell; 移动端保留 Unfold 的响应式堆叠(带字段标签)不动。
   ★ 上面的栅格规则改成 `div.form-row` 之后, 这条已经不再需要 —— 留着当第二道保险,
     万一以后有人把选择器改回 `.form-row`, 至少 tabular inline 还能兜住。 */
@media (min-width: 1024px) {
  fieldset .tabular-table tr.form-row:not(.empty-form) {
    display: table-row !important;
    grid-template-columns: none !important;
  }
}
/* 🔴 王悦 B26090903「第二行显示错乱且不可删除」根因:
   `.empty-form`(id="items-empty") 是"添加另一个"用的**克隆模板行**, Django 本应
   `display:none` 藏起来, 但上面 `fieldset .form-row {display:X !important}` 把它顶成
   可见 → 变成一整行错乱、又没有删除钮的"幽灵第二行"。这是全站所有 tabular inline
   的通病(采购明细/付款计划/销售明细/加工…)。这里把克隆模板行显式藏回去(所有宽度都藏),
   克隆生成的真实行会丢掉 empty-form 类, 不受影响。 */
fieldset .tabular-table tr.empty-form,
.tabular-table tr.empty-form { display: none !important; }

/* F-14 幽灵空行 (用户 09-09 截图: 库位分布「1」是空的, 数据在「2」)。
   Unfold 的 tabular inline 在每个表体前放了一行**移动端字段标签行** ——
   它靠 `lg:bg-transparent lg:border-0` 在桌面端变透明, 但行本身没隐藏,
   于是桌面上白占 30px 一行, 还把行序号顶到 2, 看着像有两条记录。
   桌面端(≥1024, 与 Unfold 自己的 lg: 断点一致)藏掉; 移动端原样保留 —— 那儿要靠它显示字段名。
   实测这行的结构: `<td>1</td><td class="original" colspan="6">PUR-260909-03</td>`
   —— 一共 7 格, 而表头只有 6 列, 列数对不上, 所以它渲染成一条错位的空白带,
   里面那个批次号根本落不到任何一列上, 人只看到"空行"。
   藏掉之后序号要重排: 服务端按 <tr> 顺序编号, 藏了第一行, 唯一的数据行仍写着 "2"。
   第一格就是个纯数字, 用 CSS 计数器盖掉即可(计数器在 table 上重置 —— Unfold
   每条记录一个 <tbody>, 挂 tbody 上会每行都从 1 开始)。 */
@media (min-width: 1024px) {
  table.tabular-table tbody > tr[class*="lg:bg-transparent"]:not(.form-row):not(.add-row) {
    display: none !important;
  }
  table.tabular-table { counter-reset: ui-inline-row; }
  table.tabular-table tbody > tr.form-row:not(.empty-form) { counter-increment: ui-inline-row; }
  table.tabular-table tbody > tr.form-row:not(.empty-form) > td:first-child {
    font-size: 0 !important;
  }
  table.tabular-table tbody > tr.form-row:not(.empty-form) > td:first-child::before {
    content: counter(ui-inline-row);
    font-size: var(--fs-base);
    color: var(--c-text-2);
  }
}

/* F-15 inline 明细的文本列不设上限就会把空间吃光:
   库位分布实测「库位」716px、「备注」736px, 而「数量」只有 80px。
   这两个字段不在 2026-08-08 锁定的列宽清单里, 给个合理上限。
   ★ 表格是 table-layout:auto, 只给 max-width 浏览器会当建议无视掉 ——
     必须 min/max/width 三个一起给(锁定的那批列宽规则也都是这么写的)。 */
table.tabular-table td[class*="field-location_code"],
table.tabular-table th[class*="field-location_code"] {
  min-width: 140px !important; max-width: 220px !important; width: 200px !important;
}
table.tabular-table td[class*="field-remark"],
table.tabular-table th[class*="field-remark"] {
  min-width: 160px !important; max-width: 320px !important; width: 280px !important;
}

/* ★ 表格列宽规则泄漏到表单里 —— 「下单日期」和「状态」在表单上被压成 106px/60px,
   根因是 foodmax-theme.css 里 `.field-status { max-width: 60px }`、
   `.field-order_date { ... }` 这批**本来是给 inline 表格列宽用的**规则写成了
   裸类选择器, 于是把表单里同名的 .field-line 一起压了。
   🔒 那批数值是 2026-08-08 锁定项, 这里**一个数都不改**, 只在表单上下文里
   解除它们的作用 —— td/th 上的列宽分毫不动。 */
fieldset div.form-row > .field-line[class*="field-"] {
  max-width: none !important;
  min-width: 0 !important;
  width: auto !important;
}

/* ★★ 上面那条特异性不够, 压不住带 :not() 链的那批。
   实例: `.field-unit:not(.field-unit_cost_cny):not(.field-unit_cost_display)
          :not(.field-unit_price_cny):not(.field-unit_price_foreign){max-width:70px}`
   —— 5 个类选择器 = (0,5,0)，而上面那条只有 (0,3,2)，同为 !important 时它赢，
   于是库存批次编辑页的「单位」下拉被压成 46px 的小方块(用户 09-09 截图报回来)。
   foodmax-theme 里这类**本该只作用于表格列宽、却写成裸 `.field-XXX`** 的规则共 20 条、
   涉及 135 个字段类, 全是 2026-08-08 锁定值 —— 一个数都不改, 改用 **ID 选择器**
   把它们在表单里解除: `#content-main` 带来 (1,x,x), 稳压任何纯类组合。
   inline 明细不受影响: 它的格子是 `td.field-XXX`, 且父级是 `tr.form-row` 不是 div。 */
#content-main div.form-row div.field-line,
#content-main div.form-row div.field-line > div,
#content-main div.form-row div.field-line .grow {
  max-width: none !important;
  min-width: 0 !important;
  width: auto !important;
}

/* 控件填满格子 —— checkbox/radio 除外, 它们本来就是小方块 */
.field-line input:not([type=checkbox]):not([type=radio]):not([type=hidden]),
.field-line select,
.field-line textarea,
.field-line .select2-container,
/* 控件外面那层 wrapper 也要撑满 —— 只给 input 设 100% 没用, 它撑满的是
   一个已经缩到 106px 的父盒子。 */
.field-line [class*="min-w-"],
.field-line .grow > div { width: 100% !important; }

/* 数字输入去 spinner (锁定项 #6, 这里只是集中化, 值不变) */
input[type=number] { -moz-appearance: textfield; appearance: textfield; }
input[type=number]::-webkit-outer-spin-button,
input[type=number]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

/* 列表页表格: 行高统一, 单元格不折行。
   ★ 不折行必须配可横滚的容器, 否则列多的页面会把内容直接切掉 ——
     所以下面这两条永远成对出现, 不许只加一条。 */
#result_list tbody td,
#result_list tbody th {
  height: var(--h-row) !important;
  padding-top: 0 !important; padding-bottom: 0 !important;
  white-space: nowrap;
}
#result_list thead th { height: var(--h-thead) !important; }
#result_list tbody td > * { max-width: 100%; }

/* T-01/T-04: 列多的表格在自己的容器里横滚, 页面本身不横滚。
   哈他产品列表 18 列撑到 2058px, 比容器宽 204px 且容器 overflow 是 visible ——
   右边 204px 的内容直接被切掉, 谁也看不见。 */
#result_list_wrapper,
.results,
#changelist-form > .overflow-x-auto,
#changelist .results { overflow-x: auto !important; max-width: 100%; }

/* ══ 列表页「操作」列: 文字链接 + 冻结在最右 ══════════════════
   吴凡 09-09 看了叮咚小满的采购明细/供应商调整单两个列表页, 要这个:
   横拉到第 15 列, 右边的「详情 编辑 历史」还在原地点得到。

   🔴 上一版「冻结左边两列」把编号列压塌成一条只显示 "-" 的窄缝还盖住旁边内容,
      根因是**冻结列没有自带宽度和不透明背景**。所以这一版三样一起给:
        ① 固定宽度(min=max=width, 表格是 auto 布局, 只给一个不管用)
        ② 不透明背景(默认白 + hover 变色, 否则底下的内容会透上来)
        ③ z-index 分层(表头 4 > 行体 3 > 普通单元格)
      加完必须跑 T-15/T-16 横滚检查, 见 scripts/ui_audit.js。 */
/* 09-10 吴凡:「字体不加粗, 字体颜色为绿色(类似 收支表 这样)」。
   实测他指的那个绿 = 侧栏链接 hover 色 rgb(22,163,74) = --c-primary-hover,
   而侧栏非选中态是 font-weight:400 —— 两项照抄, 行内操作从此和侧栏链接同一套观感。
   ★ 改前是 --c-info(#2563eb 蓝) + 600 加粗: 蓝色在这套全绿的界面里是唯一一处冷色,
     加粗又让它比同一行的正文还重, 一列 15 行全是加粗蓝字, 比数据本身还抢眼。
   ★ font-weight 必须显式写 400: Unfold 给表格里的 <a> 是 600, 不写就压不住。 */
/* ★ 两站的主题都给表格里的 <a> 上了色和字重, 而且强度不一样, 分两步才压住:
     全年 theme.css      `#result_list tbody td a { font-weight: 600 }`      —— 特异性 (1,0,2)
     哈他 foodmax-theme  `#result_list tbody td a, table tbody td a
                          { font-weight: 500 !important; color: …!important }`
   对全年: 我这条加个 #result_list 变成 (1,1,1) 就赢了, 不需要 !important。
   对哈他: 对方是 !important, 特异性再高也没用, **只能同样 !important**。
   ★ !important 只加在这一条、只作用于 `.ui-row-actions` 里的链接 ——
     不去动 foodmax-theme 那条(它管的是全站表格链接, 是锁定文件),
     也不把 !important 撒到别处。掏 !important 是因为对面先掏了, 不是图省事。
   第一版两个作用域都没加, 结果颜色进去了、加粗纹丝不动。 */
.ui-row-actions { white-space: nowrap; display: inline-flex; gap: var(--sp-2); }
.ui-row-actions a,
#result_list .ui-row-actions a {
  color: var(--c-primary-hover) !important;
  font-size: var(--fs-base);
  font-weight: 400 !important;
  text-decoration: none;
}
.ui-row-actions a:hover,
#result_list .ui-row-actions a:hover {
  color: var(--c-primary-deep) !important;
  text-decoration: underline;
}

#result_list th.column-action_column,
#result_list td.field-action_column {
  position: sticky;
  right: 0;
  z-index: 3;
  min-width: 156px !important;
  max-width: 156px !important;
  width: 156px !important;
  background: var(--c-surface);
  text-align: center;
  /* 左侧一道极浅的投影, 让人看出它是浮在上面的, 不是内容的一部分 */
  box-shadow: -6px 0 6px -6px rgba(0, 0, 0, .18);
}
/* 溢出还不到操作列自己那么宽 → sticky_columns.js 打上这个类, 不冻了。
   冻结在这种表上拿不到好处(拉到底也才二三十像素), 却会在 scrollLeft=0 时
   压住左邻的列 —— 09-10 换启用开关时, 供应商列表的开关右半截就是这么没的。 */
html.ui-no-freeze-action #result_list th.column-action_column,
html.ui-no-freeze-action #result_list td.field-action_column {
  position: static !important;
  box-shadow: none !important;
}

#result_list thead th.column-action_column {
  z-index: 4;
  background: var(--c-primary-bg-weak);
}
/* 09-10: 上面那两处 background 现在只是**兜底**(JS 没跑起来时也别透底)。
   真正生效的底色由 sticky_columns.js 现读现写 —— 写死的话, 哈他斑马行
   (偶数行 #f9fafb)在操作列这一段又是一道白接缝, 跟左边冻结列犯的是同一个错。
   hover 与左冻结列共用 --c-row-hover, 两边不许各写各的。 */
#result_list tbody tr:hover td.field-action_column {
  background: var(--c-row-hover) !important;
}

/* 09-10 修一个查 T-16 时顺手挖出来的既有 bug: 每一行的主标识列都顶着表头灰。
   根因是 foodmax-theme.css 里这条 —— 选择器意图是"表头", 却漏了 thead 限定:
       #result_list thead th, …, th.column-code, th.field-code,
       th[class*="field-"] { background: var(--foodmax-header-bg) !important; }
   Django 把 list_display_links 那一列渲染成 **tbody 行里的 `<th class="field-xxx">`**,
   于是每行的编号/单号列都被涂成表头灰 #fafafa。跟斑马色 #f9fafb 只差一两个色阶,
   所以谁也没看出来, 但斑马纹确实在这一列断着 —— 是这次做冻结列查底色才露出来的。
   ★ 在收敛层修, 不动 foodmax-theme.css: 那是锁定文件, 而且这条规则的表头部分
     仍然要留着。要是哪天发现有页面靠这抹灰区分主标识列, 删掉这一段即可复原。 */
#result_list tbody th[class*="field-"] { background: transparent !important; }

/* 🔴 2026-09-10 已撤销(当天上线当天撤): 「冻结左侧标识列」的 .ui-frozen 那一段。
   吴凡拿全年「费用录入」的截图回来: 金额列只剩 `.00` `.46` `8.62`。
   实测(费用录入, 溢出 428px): 拉到底时「金额」宽 98px 被**完整盖掉**,
   内容是 ¥1,674.00。

   撤的理由不是没调好, 是**左右结构上不对称**, 调参解决不了 ——
     ① 人横拉是为了看右边, 所以拉到底是终态。拉到底时右侧操作列正好归位、
        谁也不盖; 左侧冻结区却停在表格中段, 盖住中间那几列。
        实测哈他产品档案拉到底: 右操作列盖 0 列, 左冻结盖住「计量单位」「包装规格」。
     ② 金额右对齐, 左冻结吃的是**头**: ¥1,674.00 剩下 `.00`, 还像个合法的数;
        右冻结吃尾, ¥1,6 一眼看得出被截断。**半截可读比完全看不见更危险。**

   规范 T-17 已改成「只准冻结最右的操作列」, 机检会盯着。
   要恢复左冻结, 先回答上面两条 —— 不是把这段 CSS 贴回来就行。 */
/* border-collapse 下 sticky 单元格的右边框会被吃掉, 用 inset 阴影补一条;
   外侧再给一道浅投影, 让人看出它是浮在上面的, 不是内容的一部分。 */
#result_list .ui-frozen-last {
  box-shadow: inset -1px 0 0 var(--c-line), 6px 0 6px -6px rgba(0, 0, 0, .18);
}

/* ══ 新建/详情页顶部标题 ═══════════════════════════════════
   吴凡 09-10:「加工单新建页面也丑」——最扎眼的是顶上那条
   **1344px 通栏实心绿横幅**(`.foodmax-addbtn-bar`)。

   ★ 这个类是 🔴 锁定项 #5「+新建按钮位置」, 而且模板注释写着
     「08-07 v4: 列表页 = 可点击 <a> / change 页 = bar 装饰」——
     是张宏娟 08-07 定的、冯海艳 09-09 又改成按「新建/查看」分情况显示。
     **不是我看不顺眼就能改的**, 09-10 吴凡明确点头之后才改。
   ★ 只覆盖 change/add 页那条 bar, **列表页那颗 `.foodmax-addbtn` 绿按钮一个像素没动** ——
     锁定的是"按钮", 这条 bar 在新建页上根本不是按钮, 是页面标题。
   ★ 也不去改 foodmax-theme.css(锁定文件), 在收敛层覆盖, 要复原删掉这段就行。

   改成规范 L-05 说的样子: 页面标题在主区左上角, 18px 深色, 不占一条通栏。 */
.foodmax-addbtn-bar {
  background: transparent !important;
  min-height: 0 !important;
  justify-content: flex-start !important;
  border-radius: 0 !important;
  margin: 0 0 var(--sp-4) 8px !important;
  padding: var(--sp-2) 0 0 0 !important;
}
.foodmax-addbtn-label {
  color: var(--c-text) !important;
  font-size: var(--fs-lg) !important;
  font-weight: 600 !important;
}

/* 折叠区标题后面那个「(显示)」—— Django 的 Show/Hide 切换链接被中文化之后,
   紧贴在标题后面, 读起来像标题的一部分:
     ▶ 实际日期覆盖 (少用 · 延期时展开) (显示)
   前面已经有 ▶ 指示能展开了, 这个词只是噪音。收成小灰字, 别跟标题抢。 */
fieldset.collapse > h2 a,
fieldset.collapse > .collapse-toggle a,
.collapse-toggle > a {
  font-size: var(--fs-micro) !important;
  font-weight: 400 !important;
  color: var(--c-text-3) !important;
  margin-left: var(--sp-2);
}

/* ══ 列表页的系统字段列: 表头在、数据格却被藏了 ══════════
   吴凡 09-10 让查的。实测售后单列表: 表头「创建时间」有 156px, 而 tbody 的
   对应 td 是 `display:none` —— **一个有标题、永远空着的列**。

   根因在 foodmax-theme.css(那条规则的注释写着"系统字段…详情页隐藏"):
       .form-row.field-created_at, .form-row.field-updated_at, …   ← 前 5 个有 .form-row 限定, 对
       .field-created_at, .field-updated_at,                        ← 后 5 个是**裸类选择器**
       .field-created_by, .field-updated_by, .field-is_deleted { display: none !important }
   注释写的是"详情页隐藏", 但后半截漏了作用域, 泄漏到列表页的 td 上。
   ★ 跟 09-10 早上修的 `th[class*="field-"]`(本该只管表头, 误伤了 tbody 里的主标识列)
     是**同一类错**: 本该限定在表单/表头的规则写成裸类选择器, 泄漏到列表页。

   波及 7 个列表页, 最要命的是**审计日志的「创建时间」** ——
   审计日志最核心的就是时间戳, 而它看不见。

   ★ 只在 `#result_list` 里恢复, **详情页和 inline 明细表继续隐藏** —— 那才是原注释的本意。
   ★ display 要跟 Unfold 自己的响应式一致: 窄屏 flex / ≥1024 table-cell。
   ★ **必须同时写 td 和 th** —— Django 把 list_display_links 那一列渲染成
     **tbody 里的 `<th>`**。审计日志和通知消息正是把「创建时间」当主标识列,
     第一版只写了 td, 这两页照旧不显示。今天早上那个 `th[class*="field-"]`
     表头灰泄漏到行内, 踩的是同一个坑 —— **Django 的 tbody 里有 th**。 */
#result_list tbody td[class*="field-created_at"],
#result_list tbody td[class*="field-updated_at"],
#result_list tbody td[class*="field-created_by"],
#result_list tbody td[class*="field-updated_by"],
#result_list tbody td[class*="field-is_deleted"],
#result_list tbody th[class*="field-created_at"],
#result_list tbody th[class*="field-updated_at"],
#result_list tbody th[class*="field-created_by"],
#result_list tbody th[class*="field-updated_by"],
#result_list tbody th[class*="field-is_deleted"] { display: flex !important; }
@media (min-width: 1024px) {
  #result_list tbody td[class*="field-created_at"],
  #result_list tbody td[class*="field-updated_at"],
  #result_list tbody td[class*="field-created_by"],
  #result_list tbody td[class*="field-updated_by"],
  #result_list tbody td[class*="field-is_deleted"],
  #result_list tbody th[class*="field-created_at"],
  #result_list tbody th[class*="field-updated_at"],
  #result_list tbody th[class*="field-created_by"],
  #result_list tbody th[class*="field-updated_by"],
  #result_list tbody th[class*="field-is_deleted"] { display: table-cell !important; }
}

/* ══ 详情页: 只读字段不要长成输入框 ══════════════════════
   吴凡 09-10 看审批流详情页:「页面整个结构不好看, 重新布局」。
   实测这一页 **19 个只读字段、0 个可编辑控件、页面 2027px 长** ——
   每个只读值都套着 `bg:#fafafa + 1px 边框 + 6px 圆角 + 8/12 内边距` 的外壳,
   长得跟输入框一模一样, 但一个都改不了。**看着能填, 点了没反应**, 比难看更糟。
   这是全站详情页的通病, 不是这一页的事: 审批流、库存批次、任何锁着的编辑页都这样。

   改成"单据详情"的样子: 去掉外壳, 只留文字, 用一条浅下划线分隔 ——
   密度立刻上来, 也一眼看得出"这是结果不是输入"。
   ★ 附件/表格类只读值(settlement_info 那种自己带 <table> 的)不受影响,
     它们的外观在自己的 HTML 里。 */
/* ★ 必须带 #content-main 作用域: foodmax-theme 有条
   `.fm-readonly-form .readonly { background:#fafafa !important }` 特异性 (0,2,0),
   裸 `.readonly` (0,1,0) 就算带 !important 也输。第一版边框圆角都去掉了、
   底色纹丝不动 —— 又是"改完必须回量"。 */
#content-main .readonly,
.readonly {
  background: transparent !important;
  border: 0 !important;
  border-bottom: 1px solid var(--c-line) !important;
  border-radius: 0 !important;
  padding: var(--sp-1) 0 var(--sp-2) 0 !important;
  min-height: 0 !important;
  color: var(--c-text);
}
/* 里面自带表格/卡片的只读值, 连下划线也不要 —— 它自己有边界 */
.readonly:has(table),
.readonly:has(.ui-badge-auto) { border-bottom: 0 !important; }

/* ══ 启用/禁用开关 ══════════════════════════════════════
   吴凡 09-10:「两个网站的禁用和启用都改成这个样式, 换掉对号叉号的模式」,
   对着的是叮咚小满的商品列表 —— 一个带「启用/禁用」文字的胶囊开关。

   改之前两站一共**三种画法**:
     哈他 部分页  `.fm-toggle` 36×20 无文字开关(能点即改, 08-06 张宏娟做的)
     哈他 部分页  Unfold 只读小圆点 4×4px
     全年 全部    Unfold 只读圆圈 ✓/✗ (就是吴凡截图里那个)
   三种画法混在两个站里, 同一个字段三个样子。

   ★ 样式从**内联搬进 CSS 类** —— 原来 toggle_html 把 36px/颜色/位移全写死在
     inline style 里, JS 切换时还要再写一遍同样的数值(两处写死, 改一处必漏一处)。
     现在只切 `is-on` / `is-off` 类, 数值只有这里一份。
   ★ `is-static` = 没有 change 权限或只读表: **长得一样但不可点**(S-04 权限即界面)。 */
.ui-switch {
  position: relative;
  display: inline-block;
  width: 64px;
  height: 24px;
  border-radius: var(--r-full);
  cursor: pointer;
  user-select: none;
  vertical-align: middle;
  transition: background-color var(--dur);
}
.ui-switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.ui-switch .ui-switch-knob {
  position: absolute; top: 3px;
  width: 18px; height: 18px;
  border-radius: var(--r-full);
  background: #fff;
  box-shadow: 0 1px 2px rgba(0, 0, 0, .2);
  transition: left var(--dur);
}
.ui-switch .ui-switch-text {
  position: absolute; top: 0;
  line-height: 24px;
  font-size: var(--fs-sm);
  color: #fff;
  transition: left var(--dur);
}
.ui-switch.is-on  { background: var(--c-primary-hover); }
.ui-switch.is-on  .ui-switch-knob { left: 43px; }
.ui-switch.is-on  .ui-switch-text { left: 10px; }
/* ★ 「关」的底色走 token, 不写死。原来是 #cbd5e1(slate-300) —— 不在调色板里,
   机检 C-01 会报「表外色值」。规范第一条就是"页面 CSS 里不许再出现硬编码的色值"。
   ★ 换 --c-text-3(#9ca3af) 而不是更浅的 --c-line(#e5e7eb): 这个底上要压
     **白色的文字和圆钮**, 太浅就看不清"禁用"两个字了。 */
.ui-switch.is-off { background: var(--c-text-3); }
.ui-switch.is-off .ui-switch-knob { left: 3px; }
.ui-switch.is-off .ui-switch-text { left: 25px; }
/* 不可点的那种: 同一个样子, 但不给"能点"的暗示 */
.ui-switch.is-static { cursor: default; opacity: .85; }

/* ══ 列表页可直接改的单元格 (list_editable) ═══════════════
   吴凡 09-10 看产品品类:「这块内容不一样, 像有膏药一样」。
   实测: 「排序」列里那个填 0 的框 **36px 高 × 256px 宽, 白底 + 1px 边框**,
   而行本身只有 37px —— 白框压满整行, 在斑马行上就是一块贴上去的膏药。
   一个数字 0 占 256px, 也让整列白白撑宽。

   改成 ERP 里可编辑单元格的常规做法: **静止时看着就是普通单元格内容,
   鼠标划过才现出边框, 点进去才是完整输入框**。
   ★ 不是把边框一删了事 —— 那样人就不知道这一格能改了。三态各司其职:
       静止  透明底 + 透明边框(占位不跳)  → 一行读下来是连贯的
       hover 浅边框 + 白底                → "这一格能改"
       focus 主色边框 + 白底              → "正在改这一格"
   ★ 高度收到 28px: 原来 36px 等于行高, 框和行一样高就没有"框在格子里"的层次,
     只剩一块白。28px 在 36px 行里上下各留 4px, 行高也不会被撑。
   ★ 数字右对齐(T-06) —— 原来 start 对齐, 一列价格参差不齐。 */
#result_list tbody td input[type="text"],
#result_list tbody td input[type="number"],
#result_list tbody td select,
#result_list tbody th input[type="text"],
#result_list tbody th input[type="number"] {
  /* ★ min-height 也必须带 !important —— 本文件上面那条全站控件高
     `input:not(…){ min-height: var(--h-control) !important }` 是 36px,
     只把 height 降到 28 而 min-height 还是 36, 元素照样 36px。
     第一版就漏了这个: 底色边框都进去了、高度纹丝不动。 */
  height: 28px !important;
  min-height: 28px !important;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  padding: 0 var(--sp-2);
  font-size: var(--fs-base);
  color: var(--c-text);
  box-shadow: none;
  transition: border-color var(--dur), background-color var(--dur);
}
#result_list tbody tr:hover td input[type="text"],
#result_list tbody tr:hover td input[type="number"],
#result_list tbody tr:hover td select {
  background: var(--c-surface);
  border-color: var(--c-line);
}
#result_list tbody td input:focus,
#result_list tbody td select:focus {
  background: var(--c-surface) !important;
  border-color: var(--c-primary) !important;
  outline: none;
}
#result_list tbody td input[type="number"] {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* T-06 数字列右对齐 —— 类名由 sticky_columns.js **按内容**判定后打上。
   下面那批 `[class*="amount"]` 是旧的按列名猜的写法, 留着当兜底(JS 没跑起来时
   常见字段仍然对齐), 但**真正管用的是 .ui-num-col** —— 按名字猜迟早漏,
   09-10 吴凡就是拿「收支表金额右对齐、费用录入金额左对齐」问回来的。
   tabular-nums 是关键: 不等宽的数字, 小数点对不成一条线, 右对齐了照样参差。 */
#result_list th.ui-num-col,
#result_list td.ui-num-col,
#result_list th.ui-num-col *,
#result_list td.ui-num-col * {
  text-align: left !important;
  font-variant-numeric: tabular-nums;
}

/* 数字列右对齐(按 admin 的 field-xxx 类名匹配常见数字字段) */
#result_list td[class*="amount"],
#result_list td[class*="price"],
#result_list td[class*="cost"],
#result_list td[class*="qty"],
#result_list td[class*="quantity"],
#result_list td[class*="weight"],
#result_list td[class*="rate"],
#result_list td[class*="total"] { text-align: left; font-variant-numeric: tabular-nums; }

/* ══ 状态徽章: 实心饱和色块 → 浅底深字 ═══════════════════
   吴凡 09-10 连提两次:「颜色太复杂」「页面颜色太复杂」。
   看的是销售订单(状态列一列 16 行全是实心绿/红/灰块)和通知消息
   (「信息」是实心青块)。一列彩色实心块比数据本身还抢眼 ——
   **状态是给人扫的, 不是给人看的**, 扫的时候只需要"不一样"就够, 不需要"响亮"。
   也违反 C-01: 那批色是 Bootstrap 的 #28a745/#dc3545/#ffc107/#17a2b8, 全在调色板外。

   ★ 不重新配一遍色 —— 用 color-mix 从**原色**现算:
       字 = 原色混 28% 近黑(保证 #ffc107 这种浅色也读得清)
       底 = 字色 12%(currentColor 会先解析成上面那个字色)
     好处: 各状态原来的色义(绿=完成 红=取消 灰=待办)一个不丢, 只是不吵了;
     以后业务改配色, 这里不用跟着改。
   ★ 只有一个 {} 占位符, 所以 11 处 format_html 的**参数个数不变**,
     替换一个模板字符串就够, 不用动任何一个 colors 字典。 */
.ui-badge-auto {
  display: inline-block;
  padding: 2px var(--sp-2);
  border-radius: var(--r-sm);
  font-size: var(--fs-micro);
  line-height: 18px;
  white-space: nowrap;
  color: color-mix(in srgb, var(--tone, var(--c-text-2)) 72%, #262626);
  background: color-mix(in srgb, currentColor 12%, transparent);
}

/* 表格里的行内动作链接(销售单「一键出库」这种)。
   和操作列的三个链接同一套观感 —— 它本来就是同一类东西, 只是还没挪进操作列
   (挪进去是 T-11 第二步)。 */
#result_list .ui-row-action-link,
.ui-row-action-link {
  color: var(--c-primary-hover) !important;
  font-size: var(--fs-base);
  font-weight: 400 !important;
  text-decoration: none;
  white-space: nowrap;
}
#result_list .ui-row-action-link:hover,
.ui-row-action-link:hover {
  color: var(--c-primary-deep) !important;
  text-decoration: underline;
}

/* 状态徽章 */
.ui-badge {
  display: inline-block; padding: 2px var(--sp-2);
  border-radius: var(--r-sm); font-size: var(--fs-micro); line-height: 18px;
}
.ui-badge--ok   { background: var(--c-primary-bg); color: var(--c-primary-hover); }
.ui-badge--warn { background: var(--c-warn-bg);    color: var(--c-warn); }
.ui-badge--err  { background: var(--c-danger-bg);  color: var(--c-danger); }
.ui-badge--mute { background: #f3f4f6;             color: var(--c-text-2); }

/* 宽内容在自己的容器里滚。
   ★ 这里**故意不写** html,body{overflow-x:hidden} —— 那是把溢出藏起来,
     不是把溢出修掉; 藏起来之后被挤出屏幕的表格连横滚都没有了, 用户看到
     的仍然是一片空白, 而机器人还会判成"没有横向滚动"。 */
.ui-scroll-x { overflow-x: auto; max-width: 100%; }

/* ══ 图标字体守卫 (I-03) ══════════════════════════════════════
   吴凡 09-09 截图: 侧栏顶栏一片英文 —— dashboard经营看板 / search 搜销售单号 /
   expand_more / arrow_circle_up cancel …
   不是字体挂了(实测 woff2 是 200、最终 loaded), 是 **FOUT**: Material Symbols
   拿 ligature 当图标, 字体下载完成之前浏览器把 <span>dashboard</span> 里的
   **文字本体**照原样画出来, 于是整屏英文; 字体一到位才变回图标。
   字体就绪前藏起来但**保留占位**(visibility 不是 display, 布局不跳),
   就绪后由 icon_font_guard.js 给 <html> 加 .ui-icons-ready 放出来(3 秒兜底)。 */
html:not(.ui-icons-ready) .material-symbols-outlined,
html:not(.ui-icons-ready) .material-icons {
  visibility: hidden !important;
}

/* 图标字号不参与排版字号档位, 但也不许乱设 */
.material-symbols-outlined, .material-icons { line-height: 1; }

/* B-02 按钮高度只允许 36 / 32 两档。
   ★ 故意不动 .foodmax-addbtn —— 「+新建」按钮的位置和样式属于 2026-08-08
     UI 锁定清单第 5 条, 改它要红字二次确认, 不在本次收敛范围内。 */
.submit-row input[type=submit],
.submit-row .button,
.object-tools a,
button.default,
input[type=submit]:not(.foodmax-addbtn),
a.button:not(.foodmax-addbtn) {
  height: var(--h-btn);
  border-radius: var(--r-sm);
  font-size: var(--fs-base);
}
#result_list .button, #result_list a.button { height: var(--h-btn-sm); }

/* 分页的 ‹ › 箭头(哈他是 ._fm_pag_ctl)是 16px 越界字号, 收到 14px */
.paginator a, .pagination a, ._fm_pag_ctl a { font-size: var(--fs-md) !important; }

/* 列表页里用 <small> 写次要信息(创建人/时间), 默认 10px 太小且不在档位上 */
small, .text-2xs { font-size: var(--fs-micro) !important; }

/* inline 明细的汇总条金额 <b>(如「¥0.00」)是 16px, 不在档位上。
   来自框架层(项目代码里 grep 不到), 所以在这儿收 —— 只限 inline 表单集内,
   不碰正文里的加粗(那些应该跟着 13px 正文走)。 */
.inline-group b, .js-inline-admin-formset b,
/* 全年采购单编辑页的金额 <b>(¥22,572.64) 是 15px, 同一类"金额强调" */
.jg-has-supconfirm b { font-size: var(--fs-md) !important; }

/* 字段说明的「?」按钮(全年 .jg-tip)默认 10px, 收到 11px 档位 */
.jg-tip, .foodmax-tip, button[data-tip] { font-size: var(--fs-micro) !important; }

/* 自定义报表页(利润表/台账)的 wrapper 也要落在同一条左边缘上,
   否则从列表切到利润表, 内容会往右跳 17px。 */
.pl-card, .pl-wrap, .rp-wrap, .rp-container {
  padding-left: 0 !important; padding-right: 0 !important;
  max-width: 100% !important; margin-left: 0 !important; margin-right: 0 !important;
}

/* 🔴 2026-09-09 已撤销: 「列多的表格横拉时冻结前两列」。
   加过一版 `position:sticky` 冻结 #result_list 前两列, 用户在产品档案页横拉后
   截图报回来: 编号列被压成一条只显示 "-" 的窄缝, 还盖住后面的内容。
   根因是 sticky 列必须自带固定宽度 + 不透明背景 + z-index 分层, 而哈他这些列的
   宽度由 2026-08-08 锁定的 `td.field-XXX` 规则决定, 我这条规则跟它们打架。

   ★ 更要紧的教训: **机器人漏了它**, 因为机器人在"没横滚"的状态下测量,
     而这个问题只有横滚之后才出现。列表页的检查要补一条"横拉到底再量一次"。

   现在只保留可横滚的容器(见上面 T-01/T-04), 不冻结列。
   要冻结列得先给这几列显式宽度, 那要动锁定项, 得单独提。 */

/* ── 文件/图片字段的「清除」勾选框 ────────────────────────────────
   吴凡 2026-09-11 看原料商品档案:「那么多框, 看信息都不舒服」。
   其中一处是**画出来就是坏的**: 图片字段下面那行「清除」被压成两行 ——
       清
       除
   Django 的 ClearableFileInput 渲染成 <label for="xxx-clear_id">, unfold 给它
   `flex items-center`; 它是 flex 项, 没写 flex-none 就会被压缩, 复选框占掉
   16px 之后只剩 21px 给文字, 两个汉字正好一行一个。
   ★ 不是这一页的事 —— 两站**每个**图片/附件字段都是这个渲染, 所以收在 tokens 里。
   ★ 选择器钉 `-clear_id`: 那是 Django 自己拼的后缀(`name + "-clear_id"`),
     比 unfold 的 class 稳 —— class 会随 unfold 版本变, 这个后缀十年没动过。 */
label[for$="-clear_id"] {
  flex: none !important;
  white-space: nowrap !important;
  gap: 6px;
}

/* ── 详情页「🔒 编辑」开关 + 只读态观感 ──────────────────────────────
   吴凡 2026-09-11:「详情 点击去是不是该是阅读, 编辑点进去才是可以更改,
   现在点进去都一样」。哈他 2026-08-15 就有这套(apps/sys/edit_lock.py),
   全年这次移植过去; 样式收在这里, 两站同一套。
   ★ 哈他的按钮用的是 foodmax-theme.css 里的 .fm-btn 体系, 那份全年没有,
     所以另起 .ui-edit-toggle —— 不去动哈他已经在跑的类名。
   ★ 只读态**不是**把字段涂灰了事: 服务端已经全字段 readonly, 这里只是
     给一点"现在改不了"的观感, 免得人对着不能改的页面反复点。 */
.ui-edit-toggle {
  display: inline-flex; align-items: center; gap: 4px;
  margin-left: 12px; padding: 4px 12px;
  font-size: var(--fs-sm); line-height: 1.6; white-space: nowrap;
  border: 1px solid var(--c-line); border-radius: var(--r-sm);
  background: #fff; color: var(--c-text); text-decoration: none;
  vertical-align: middle; cursor: pointer;
}
.ui-edit-toggle:hover { border-color: var(--c-primary); color: var(--c-primary); }
.ui-edit-toggle.is-on {
  background: var(--c-primary); border-color: var(--c-primary); color: #fff;
}
.ui-edit-toggle.is-on:hover { color: #fff; opacity: .9; }

/* 只读态: 输入框外框淡一档。★ 不动字色 —— 详情页是拿来**读**的,
   把值读淡了正好把这页最该看的东西弄糊, 方向反了。 */
.ui-readonly-form input:not([type="checkbox"]):not([type="radio"]),
.ui-readonly-form select,
.ui-readonly-form textarea { background: #fafafa; }

/* ── 只读态的行高: 别按输入框的尺寸留位置 ────────────────────────────
   吴凡 2026-09-11 看全年代办人详情页:「这个页面布局也看着很复杂,
   重新排下格式」。

   09-10 已经把只读值的**外壳**去掉了(见上面那段), 但**行容器**没动 ——
   unfold 给每个 `.field-line` 写死 `min-h-[59px] + py-2.5`,
   那是按"里面要塞一个 37px 高的输入框"算的。
   只读页里一个输入框都没有, 32px 的文字撑在 59px 的格子里, 白占 27px。
   实测代办人详情页 19 行 × 27px ≈ 500px, 占整页三分之一。

   ★★ 只给全年(.ui-readonly-form)。哈他的详情页 09-10 已经单独排过版,
     结构和全年不一样 —— 09-11 我把这几条按"两站通用"写上去, 当场把哈他的
     采购单详情打成"标签在上、值在 160px 以下"。**同一条 CSS 未必对两套 DOM
     都成立, 共享层要改必须两站都回量。**
   ★ 只在只读态收窄(.ui-readonly-form),
     编辑态一行仍是 59px —— 那里真有输入框, 收窄了控件会挤。
   ★ 这不是"把字变小", 字号一个都没动。挤掉的全是**为控件预留、
     而这一页根本没有控件**的空白。 */
.ui-readonly-form .field-line {
  min-height: 0 !important;
  padding-top: var(--sp-2) !important;
  padding-bottom: var(--sp-2) !important;
}
/* 只读态的值已经有下划线当分隔, 行本身那条虚线就重了 —— 去掉一层 */
.ui-readonly-form .field-line { border-bottom-color: transparent !important; }

/* ── 只读详情页排版: 对齐叮咚小满的「查看商品」 ───────────────────────
   吴凡 2026-09-11 发了小满那张截图:「我感觉这样的商品详情页面很简单,
   很好看, 就是排版如果不够整齐」。

   对着小满逐条比, 差的是这几样(全在**只读态**, 编辑态一律不动):
     ① 标签列 224px 宽还左对齐 → 标签和值之间空一大截, 值被挤到只剩 180px,
        公司名一律折行。小满是**标签右对齐、值紧跟其后**。
     ② 每格之间一条竖虚线 + 值下面一条下划线 —— 小满一条都没有。
        详情页是读的, 线越多越碎。
     ③ 区块标题是个灰底横条 —— 小满是一行加粗文字, 干净得多。
     ④ 整块外面还套着圆角边框 + 阴影, 一层套一层。

   ★ 只读态才这么排。编辑态那 224px 的标签列和分隔线是**对齐输入框**用的,
     动了控件会歪 —— 两种形态本来就该有两套密度。 */

/* ★★ 09-11 二改: 吴凡「只读状态也和编辑模式排版一样, 只是是只读的样式」。
   原来只读态**另排一套**: 标签右对齐、收窄到 8rem、还降一档字号 ——
   于是同一页在「看」和「改」之间来回切时, 每个标签都从左边跳到右边、
   宽度也变, 人得重新找一遍字段在哪。
   现在: **位置、宽度、对齐跟编辑态一模一样**, 唯一的差别是
   值不长成输入框(下面 ② 那几条)。「只读」是渲染方式, 不该是另一套排版。
   ★ 标签宽度用和编辑态同一个值(6.5rem, 见全年专属段), 这里不再另写。 */
.ui-readonly-form .field-line > div:first-child {
  text-align: left !important;
  justify-content: flex-start !important;
  padding-right: var(--sp-3);
}

/* ② 竖虚线、下划线全去掉 */
.ui-readonly-form .field-line { border-left: 0 !important; }
/* ★ 必须带 #content-main: 上面那条「只读值不要长成输入框」写的是
   `#content-main .readonly`(1,1,0), 裸的 `.ui-readonly-form .readonly`(0,2,0)
   带 !important 也输 —— ID 比 class 值钱。这条坑 09-10 那段注释里就写过一次,
   我又踩了一遍。 */
/* ★★ 09-12 下午改口: 只读的值**长成禁用的输入框**(灰底 + 细框 + 直角)。
   上午吴凡说「把很多框去掉」, 我把框全撤了;
   下午他指着哈他的采购订单详情说「两站所有的商品档案、详情页, 都改成这个样式」——
   而哈他的只读值是**带框**的。以他指的那一版为准。
   ★ 留着这段历史是因为这两条要求看起来矛盾, 其实不是:
     他要的一直是「和哈他一样」, 中间那一版是我照字面理解做偏了。 */
#content-main .ui-readonly-form .readonly {
  border: 1px solid var(--c-line) !important;
  background: #fafafa !important;
  padding: 7px 12px !important;
  min-height: 36px !important;
  border-radius: 0 !important;
  max-width: 100% !important;
}
/* ③ 区块标题: 灰底横条 —— 哈他就是灰底, 09-12 上午改成透明的那版已撤 */
.ui-readonly-form > h2 {
  background: #f3f4f6 !important;
  padding: 12px 16px !important;
  /* ★ fs-md(14px) 不是 fs-lg(18px) —— 规范写得很直白:
     「--fs-md 14px 小标题 / fieldset 标题」「--fs-lg 18px 页面标题」。
     09-12 我随手写了 fs-lg, 吴凡一眼看出来「这字体太大了」。
     实测哈他的区块标题就是 14px/600, 两边现在一致。
     ★ 「启用」那个字段被 status_to_header.js 搬进了这一行, 它会继承这里的
       字号 —— 所以这一个数写错, 连带旁边那个字段的值也一起变大。 */
  font-size: var(--fs-md) !important;
}

/* ── 字段标签/值的字号: 压掉 unfold 0.104 挂的 text-sm ────────────────
   吴凡 2026-09-12:「你不是设置了字体大小吗? 这字体太大了, 统一检查一遍」。

   ★★ 实测两站主力字号: **哈他 13px×1053, 全年 14px×33** —— 全年整体大一档。
     根源还是 unfold 版本: 0.104 给 label 挂了 Tailwind `text-sm`(14px),
     压过了 `body { font-size: 13px }`; 0.59 没挂, 老老实实继承 body。
     所以"设过 --fs-base 13px"并不等于"页面上就是 13px",
     ★ **token 定了值, 不代表没人在更具体的地方覆盖它。**

   ★ 写成两站通用是安全的: 哈他本来就是 13px, 这条对它是幂等的;
     而且限定了 #content-main, 不碰侧栏和页头。 */
#content-main .field-line label,
#content-main .ui-readonly-form .readonly {
  font-size: var(--fs-base) !important;
}
/* 内联表的表头: 全年 14px / 哈他 13px —— 表头是表格内容的一部分,
   按正文那一档走。规范里 14px 那一档写的是「小标题 / fieldset 标题」,
   表头不属于小标题。 */
#content-main table thead th {
  font-size: var(--fs-base) !important;
}
/* ④ 区块外层: 哈他是一圈细边框, 不是无边框 */
.ui-readonly-form .form-rows {
  border: 1px solid var(--c-line) !important;
  box-shadow: none !important;
  border-radius: 0 !important;
}

/* 🔴 09-12 上午那段「只读态三改(标签和值同一条线)」**已撤销**。
   它把标签和值拉到同一行、同一基线, 是为**并排**布局算的;
   当天下午吴凡指定照哈他的样式来, 而哈他是**竖排**(标签在上、值在下),
   竖排里 `align-items: baseline` 和"标签容器放开高度"都不再适用。
   布局方向那几条现在收在 scripts/deploy_ui_tokens.sh 的全年专属段
   (只进全年版 —— 哈他本来就是竖排, 再叠一层只会打架)。
   要翻旧版 → git show 71fa6b2 */

/* ── 表单一行 4 列 (只读态和编辑态都是) ──────────────────────────────
   吴凡 2026-09-11:「这里一行只有三个, 也太空了, 可以一行最少要排 4 个信息,
   编辑模式也可以最少排 4 个信息」。

   ★★ 这个数字在**三处**出现, 改一处等于没改:
        · docs/UI_STANDARD_v1.md 的 F-02
        · scripts/ui_audit.js 的 ROW_MAX
        · 平台层装箱 (哈他 _ROW_MAX / 全年 ROW_MAX)
      这里是第四处 —— unfold 给 .form-row 写死 `lg:grid-cols-3`,
      后端装成 4 个、前端还按 3 列排, 第 4 个会掉到下一行。
   ★ 1216px / 4 = 304px 一列。标签收到 6rem, 值还剩 ~230px ——
     比原来 3 列时的 200px 反而宽, 因为标签从 168px 收到了 96px。 */
#content-main .form-row.grid,
#content-main .field-row.grid {
  grid-template-columns: repeat(4, minmax(0, 1fr)) !important;
}
@media (max-width: 1280px) {
  #content-main .form-row.grid,
  #content-main .field-row.grid {
    grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
  }
}
@media (max-width: 860px) {
  #content-main .form-row.grid,
  #content-main .field-row.grid { grid-template-columns: 1fr !important; }
}

/* 4 列之后标签要收窄, 否则 "标准出成率(份/kg)" 这种长标签会折成两行,
   而值还挂在第一行 —— 09-11 截图里就是这么歪的。
   ★ 顶部对齐 + 允许折行: 标签折两行时, 值跟着标签的**第一行**走,
     而不是掉到中间。 */
/* 8rem = 128px: 全站最长的标签是「标准出成率(份/kg)」——9 个字 + 一个「?」按钮,
   12px 下实测 6rem 折行、7rem 还折, 8rem 才放得下。**按最长那个量出来的, 不是估的**。
   4 列每列 304px, 标签 128 + 间距 12, 值还剩 164px。 */
/* 🔴 09-12 撤销: 标签列固定 8rem 是**并排**布局才需要的。
   改成竖排(标签在上、值在下)之后, 固定宽度会让长标签折行, 见全年专属段。 */
/* (align-items 也挪到全年专属段, 同上: 哈他是竖排) */
#content-main .field-line > div:first-child { line-height: 1.6; }

/* 🔴 只读态的字号层级(标签 12px / 值 13px)已撤销 —— 见上面「只读和编辑同一套排版」。
   09-11 吴凡看完说「只读状态也和编辑模式排版一样, 只是是只读的样式」。 */


/* 🔴 编辑态标签列收窄那段 **移到全年专属段**(scripts/deploy_ui_tokens.sh 里)。
   09-11: 写成两站通用之后, 哈他的详情页当场被打坏 —— 它的 .field-line 是
   **竖排**(标签在上、值在下), `flex: 0 0 6.5rem` 在竖排里变成"标签块高 104px",
   把值顶到 150px 以下。同一条 CSS 对两套 DOM 未必都成立, 共享层改完两站都要回量。 */


/* ── 搬到区块标题行右上角的字段(目前是「启用」) ───────────────────────
   吴凡 2026-09-11:「启用按钮放到右上角」。DOM 由 status_to_header.js 搬,
   这里只管它到了标题行之后长什么样。 */
/* ★ 09-11 吴凡:「商品是否启用的标签换回原来的样式」——
   第一版给它加了 gap/字号/次要色, 搬到标题行之后长得跟原来那个不一样了。
   搬位置就只搬位置: 除了"顶到最右"和"去掉原来那格的框线宽度",
   一个样式都不加, 标签和开关照它在行里的原样渲染。 */
.ui-header-field {
  margin-left: auto;                 /* 标题行是 flex, 这一句把它顶到最右 */
  display: inline-flex; align-items: center; gap: var(--sp-2);
  font-weight: 400;
}
/* 搬进来之后不要再带原来那格的框线和宽度 */
.ui-header-field > div { flex: none !important; width: auto !important;
  min-width: 0 !important; padding: 0 !important; border: 0 !important; }

/* ── 商品主图: 尺寸固定, 不再随原图大小忽大忽小 ──────────────────────
   吴凡 2026-09-11:「商品主图的大小, 位置应该固定」。
   原来直接吐原图, 一张竖图能把整行撑到 400px 高, 一张小图又缩成一条 ——
   同一个页面在不同商品之间跳来跳去。
   ★ 固定 160×160 + object-fit: cover: 不变形、不留白、每个商品一样高。
   ★ 「清除」挪到图片右边同一行, 不再自己占一整条。 */
/* ★ 按内容认(和上面那段同一条口径): 只读态那一格没有 field-photo 类,
   钉 class 的话只读态的图还是原图大小 —— 09-11 实测 192×128, 编辑态 160×160,
   同一张图在"看"和"改"之间变大小。 */
#content-main .ui-photo-panel img,
#content-main [class*="field-photo"] img,
#content-main [class*="field-image"] img {
  width: 160px !important; height: 160px !important;
  object-fit: cover; border-radius: var(--r-sm);
  border: 1px solid var(--c-line); background: #fff;
}
#content-main .field-photo .grow > div,
#content-main [class*="field-photo"] .grow > div {
  display: flex; align-items: flex-start; gap: var(--sp-3); flex-wrap: wrap;
}

/* ── 商品主图: 钉在区块右上角, 其余字段在左边铺开 ─────────────────────
   吴凡 2026-09-11:「图固定在右侧」「商品基本信息标签重排, 不要被图的新位置
   相互遮挡」「只读状态也和编辑模式排版一样」。

   原来主图是**字段流里的一格**: 它一来就把那一整行撑高, 后面的字段被推下去,
   右边又空一大片。图是"一块面板", 不是"一个字段", 按字段排它就一直别扭。

   ★★ 钉的是**那一格(.field-line)**, 不是那一行(.form-row)。
      09-11 第一版钉的是整行 —— 而 photo 早先被我从"独占整行"名单里拿掉了
      (为了修 F-02 的"短控件独占一行"), 于是它和「计量单位/采购分类/启用」
      挤在同一行。整行一绝对定位, 那三个字段被一起拖到右边压在图上。
      **一个改动的副作用落在另一个改动上, 只有回量才看得见。**

   ★★ 认哪一格由 JS 打标记(status_to_header.js 的 tagPhoto), 这里只认标记:
      编辑态那一格有 `field-photo` 类, **只读态一个类都没有**;
      而按"这一格里有文件输入框"认又太宽 —— 代办人页有身份证人像面/国徽面
      两个文件框, 09-11 实测**两张都被钉到同一个位置叠在一起**,
      还把新建页撑出横向滚动(文档 1991px > 视口 1920px, 机检 L-06 报的就是它)。

   ★★ 还踩过三个坑, 每一条都是回量才发现的:
      · `grid-row: 1 / -1` 跨行 → 高度被摊到每一行, **左边空出 450px**
      · unfold 给 .form-rows 写了 `overflow:hidden` → 图比字段高就被**切掉**
      · `:has()` **不能嵌套** —— 写成 `.form-rows:has(.form-row:has(img))`
        整条规则不被采纳, 右侧留白和 min-height 一点没生效
   ★ 窄屏(<1280px)退回原位: 200px 的图 + 4 列字段挤在 900px 里谁也读不了。 */
/* ★ min-width:0 三条缺一不可: flex/grid 的项默认 `min-width:auto`,
   也就是"不许缩到比内容还窄"。右边让出 220px 之后, 4 列的最小宽度加起来
   超过了剩下的空间, 于是 #main 被撑宽 60px, 整页横向滚动(机检 L-06)。
   ★ 这不是把字挤没 —— .readonly 上有 break-words, 缩的是"不肯换行"的那部分。 */
#content-main .ui-has-photo-panel .form-row,
#content-main .ui-has-photo-panel .field-line,
#content-main .ui-has-photo-panel .field-line > div { min-width: 0 !important; }
#content-main .ui-has-photo-panel {
  position: relative;
  padding-right: 220px;
  /* ★ 纵向 visible(图比字段高, 不能被切) + 横向 clip(不产生横滚)。
     `visible` 和 `clip` 是规范里**唯一允许的混搭**, 换成 hidden 会
     连纵向一起变成滚动容器, 图当场被切掉一半。
     ★ 为什么需要横向 clip: 面板是绝对定位的, 实测它自己没越界
       (1920 下右边缘 1892), 但文档 scrollWidth 就是 1980 —— 逐条对照
       (padding / min-height / 栅格 / 绝对定位)才定位到是绝对定位带出来的,
       而 clip 一加就回 1920。**量不到元凶时, 用对照法定位到规则本身。** */
  overflow: visible !important;
  overflow-x: clip !important;
  min-height: 420px;              /* 面板实测 380px, 不给下限图会探出容器 */
}
/* ★ 选择器写这么长是**被逼的**: 本文件里早有一条
   `#content-main div.form-row div.field-line { width:auto !important }`,
   特异性 (1,2,2) 压过 `#content-main .ui-photo-panel` 的 (1,1,0) ——
   同一条规则里 position 生效了、width 没生效, 面板于是缩成图的宽度(160px)
   或撑到内容宽(377px)。09-11 是把"命中这个元素并设了 width 的规则"全列出来
   才看见的。**`!important` 不是终点, 特异性照样分高下。** */
#content-main div.form-row div.field-line.ui-photo-panel,
#content-main .ui-photo-panel {
  position: absolute; top: var(--sp-3); right: var(--sp-3);
  /* ★ 必须 !important: 编辑态那一格被别处的宽度规则压着, 只写 width 量出来是
     377px —— 图就从预留的 220px 带里溢出来, 盖住「类型」和「上市季」。
     只读态没这条冲突, 所以**只看只读态会以为已经好了**。 */
  width: 200px !important; max-width: 200px !important;
  margin: 0 !important;
  display: block !important;
  border: 0 !important; padding: 0 !important;
}
#content-main .ui-photo-panel > div {
  flex: none !important; width: 100% !important; max-width: 100% !important;
  text-align: left !important;
}
#content-main .ui-photo-panel > div:first-child {
  white-space: nowrap; margin-bottom: var(--sp-2);
}
@media (max-width: 1280px) {
  #content-main .ui-has-photo-panel { padding-right: 0; min-height: 0; }
  #content-main .ui-photo-panel { position: static; width: auto !important; }
}

/* ── 加工单「本单汇总」那一条 ─────────────────────────────────────────
   吴凡 2026-09-11:「全年有一条总体费用的情况, 显示的满清楚的, 希望哈他也加出来」。
   区块由 ProcOrderAdmin 打 `proc-summary` 标记(见 fieldsets)。
   ★ 两件事: ① 标签是空的, Django 照样渲染一个孤零零的「:」——藏掉;
             ② 它是一整条汇总, 不是一个字段, 要占满整行而不是 4 列里的 1 列。 */
#content-main .proc-summary .field-line > div:first-child { display: none !important; }
#content-main .proc-summary .field-line,
#content-main .proc-summary .form-row > .field-line {
  grid-column: 1 / -1 !important;
  width: 100% !important; max-width: 100% !important;
  padding-left: 0 !important;
}
#content-main .proc-summary .form-row { grid-template-columns: 1fr !important; }
/* ★ unfold 给只读值套了 `max-w-2xl`(672px) —— 一句话的字段合适, 一条 8 格的
   汇总就被卡在半页宽, 后 4 格挤到第二行。这一块解除上限。 */
#content-main .proc-summary .readonly,
#content-main .proc-summary .grow { max-width: 100% !important; width: 100% !important; }
/* ★ 09-11 吴凡:「本单汇总字体显示不全, 边上有遮挡」。
   上面那条 `padding-left: 0` 是为了让汇总条跟区块标题左对齐(标签列藏了,
   不清 padding 会整条右移一个标签位) —— 但它把**左右内边距一起清成 0**,
   于是「投入原料」的「投」字和「1,000.00」的「1」直接贴在框线上,
   看着就像被框切掉了半个字。
   ★ 量出来的结论: 页面上并没有 overflow 裁切(各层 left 都是 9, scrollW=clientW)
     —— **是贴边, 不是溢出**。这两种在截图上长得几乎一样, 差别只有量得出来:
     溢出要去找哪一层 overflow:hidden, 贴边只要把内边距加回来。
   把内边距加在里层(汇总条自己), 不动外层的对齐。 */
#content-main .proc-summary .readonly > div { padding: 10px 14px !important; }

/* ── 详情页的「编辑 / 锁定」开关 ───────────────────────────────────────
   吴凡 2026-09-11:「编辑 锁定 状态 放右上角, 状态也改成类似这种(启用那个开关),
   可以编辑 / 锁定编辑」。

   原来是个按钮, 而且**挂在页面最底下** —— 它找的锚点是页签栏, 加工单页没有
   内联页签, 就退回到了 `#content-main` 的末尾(实测 y=1426, 要滚到底才看得见)。
   现在: 有页签栏挂页签栏, 没有就自己在右上角开一格(见 edit_lock.js)。

   ★ 样式复用「启用」那个胶囊(.ui-switch), 只是更宽 —— 四个汉字放不进 64px。
     宽了之后旋钮和文字的位置要跟着算, 不能只改宽度: 09-11 第一版只改 width,
     旋钮还停在 43px, 卡在胶囊中间。 */
.ui-switch--wide { width: 92px; }
.ui-switch--wide.is-on  .ui-switch-knob { left: 71px; }
.ui-switch--wide.is-on  .ui-switch-text { left: 10px; }
.ui-switch--wide.is-off .ui-switch-knob { left: 3px; }
.ui-switch--wide.is-off .ui-switch-text { left: 25px; }

/* 没有页签栏的页面, 在内容区右上角开一格放它 */
.ui-page-actions {
  display: flex; justify-content: flex-end; align-items: center;
  gap: var(--sp-2); margin: 0 0 var(--sp-2);
}

/* ══ 全站直角 ═══════════════════════════════════════════════
   吴凡 2026-09-11 对着小满的「供应商调整单」列表:
   「两站的各种表格框都用直角框显示, 不用椭圆角。两站统一改」。

   token 那两档(`--r-sm` `--r-md`)已经归零, 走 token 的 40+ 处一次就平了。
   下面这几处是**硬编码、不走 token 的漏网**, 实测逐个量出来的
   (探针: 遍历全页 getComputedStyle().borderRadius, 只留非 0 且可见的):

     6px  侧栏顶上的绿色图标块      foodmax-theme.css:70  (带 !important)
     6px  侧栏搜索框               unfold 自带
     6px  顶栏 brand-logo          foodmax-theme.css:196
     4px  工作台页签 .tab.active   foodmax-theme.css:229
     4px  退出按钮 .logout-btn     foodmax-theme.css:261
     4px  .badge-*  五个徽章       foodmax-theme.css:122-126
     4px  iframe 页签 #fm-tabs     workspace.html 的**行内 style**
     2px  页签上的 ✕              同上

   ★ 为什么不去各自的文件里改: `foodmax-theme.css` 和 `workspace.html`
     都不在本窗口的归属表里(见 docs/UI_COLLAB.md)。收敛层压平是这个文件
     本来的职责 —— 它存在的理由就是「各处硬编码收口到一处」。
   ★ `#fm-tabs` 那两条是**行内 style**, 特异性最高 —— 但带 `!important`
     的作者样式表规则**优先于不带 !important 的行内样式**, 所以压得住。
     (顺带: 所以这里的 !important 不是偷懒, 是唯一能赢的写法。)

   ★★ 不归零的两样, 改之前先想清楚:
     · `.avatar` 是 50% 的**圆形头像** —— 小满那张截图右上角的头像也还是圆的。
     · `.ui-switch` 那颗 pill 靠 `--r-full` 成形, 归零会变成一根方条。
     圆形 ≠ 圆角。这条规则只管「方框的角」, 不碰本来就该是圆的东西。 */
/* ★ 这一条的写法是**量出来的**, 别手贱简化成 `#nav-sidebar-inner a.bg-primary-600`:
   foodmax-theme.css:70 那条是
     `#nav-sidebar-inner > div:first-of-type a[href="/admin/"].bg-primary-600`
   = 特异性 (1,3,1) 且带 !important。两边都 !important 时**特异性高的赢**,
   简写的 (1,1,1) 压不住 —— 我先写了简写, 部署完回量, 那个块还是 6px。
   这里堆到 (1,4,1) 才赢过它。 */
#nav-sidebar-inner a.bg-primary-600.flex.items-center[href],
#nav-sidebar-inner .bg-white.border,
.foodmax-top-nav .brand-logo,
.foodmax-top-nav a.tab,
.foodmax-top-nav a.tab.active,
.foodmax-top-nav .logout-btn,
.badge-success, .badge-danger, .badge-warning, .badge-info, .badge-neutral,
#fm-tabs > div,
#fm-tabs > div > span {
  border-radius: 0 !important;
}

/* ── unfold 自带的 `rounded-*` 类 ─────────────────────────────
   上面那一串治的是我们自己写的硬编码。unfold 模板里还满是
   `rounded-default` / `rounded-b-default`(按钮、下拉、空结果框、用户菜单…),
   它的值来自各站编译进去的 `--radius-default` ——
   **实测哈他是 0px、全年是 6px**, 两站长得不一样的根就在这。
   (`rounded-default` 单页就有 83 处, 一个个列没有意义。)

   ★ `:not([class*="rounded-full"])` 不能省: unfold 的圆形头像、圆点状态灯
     用的就是 rounded-full。这条规则只压方框的角。
   ★ 我们自己的 `.avatar` / `.ui-switch` 不带 `rounded-` 类, 天然不受影响。 */
[class*="rounded-"]:not([class*="rounded-full"]) {
  border-radius: 0 !important;
}

/* ★ Tailwind 的**子元素变体** `*:rounded-default`: 圆角加在"所有直接子元素"上,
   而子元素自己的 class 里根本没有 `rounded-` —— 上面那条按 class 匹配的规则
   一个都够不着。
   实测漏网: 新建页顶上的页签栏 `#tabs-items` 自己是 0 了, 里面的
   「基本 / 渠道映射」两个页签还是 6px。
   **父元素平了不代表子元素平了** —— 量的时候要连子元素一起量。 */
[class*="*:rounded-"] > *:not([class*="rounded-full"]) {
  border-radius: 0 !important;
}

/* ── 还有两类自带圆角、既不走 token 也不带 `rounded-` 类的 ──────────
   ★ 这两类**只在新建/编辑页出现**, 09-11 第一轮扫的是列表页和详情页,
     一个都没扫到 —— 「扫过了」要看扫的是哪种页面。
     (同一条教训 09-09 记过一次: 机器人只扫列表+新建, 漏了整类编辑页。) */
/* ── 模板里**写死在 style= 上**的圆角 ───────────────────────────
   收敛层够不着 CSS 文件之外的东西, 但够得着行内样式:
   **带 `!important` 的样式表规则优先于不带 `!important` 的行内 style**。
   (同一条今天用过两次: #fm-tabs 的页签、这里。)

   实测漏网的例子: 全年利润表页那条「2026-09 的利润表还没跟上最新单据」
   提示条 —— 它是 `profit_stale_banner.html` 里一个**没有类名**的 div,
   `<div style="…border-radius:8px…">`。没有类名就没法点名压,
   只能按"带内联圆角"这个特征收。

   ★ 限定在内容区里面。不要写成全局 `[style*="border-radius"]` ——
     页面外壳(头像、状态灯)将来有人用内联写圆形, 会被一起压扁。 */
#content-main [style*="border-radius"],
#changelist [style*="border-radius"],
#content [style*="border-radius"] {
  border-radius: 0 !important;
}

.select2-container,                     /* autocomplete 下拉的外壳 */
.select2-container .select2-selection,  /* 里面那个真正带边框的框 */
.select2-dropdown,                      /* 展开的候选列表 */
tr.add-row td a,                        /* 「添加另一个 …」 */
.readonly > span, .readonly > div {     /* 只读值容器里的那一层 */
  border-radius: 0 !important;
}

/* ★★ 开关胶囊是**椭圆**, 不受全站直角约束 ────────────────────────────
   吴凡 2026-09-12:「这个开启禁用的胶囊改成椭圆的」「这类启用禁用类胶囊
   都可以是椭圆的」。

   ★ 直角是给**方框**定的(输入框/卡片/页签), 开关是个滑轨 ——
     两端不是圆的, 里面那个圆钮滑到头就会露出直角的墙, 一眼就不对。

   ★★ 上面那条「我们自己的 .ui-switch 不带 rounded- 类, 天然不受影响」
     的注释**当时对、后来错了**: 它说的是按 class 匹配那一条。
     实际有两条规则各从一个方向命中它:
       ① `[class*="*:rounded-"] > *`  —— 胶囊是 .readonly 的直接子元素,
          而 .readonly 带着 Tailwind 的 `*:rounded-default`
       ② `.readonly > span`           —— 胶囊就是个 <span>
     ★ **"这个元素不带那个类"不等于"够不着它"** —— 按父元素、按标签名
       匹配的规则照样能盖到。写豁免要顺着**它待在哪**想, 不只是它自己长什么样。

   ★ 特异性要压过 `.readonly > span`(0,1,1), 所以写成 `.readonly > .ui-switch`(0,2,0);
     位置也必须在所有直角规则**之后**。 */
.ui-switch,
.readonly > .ui-switch,
.ui-switch .ui-switch-knob {
  border-radius: var(--r-full) !important;
}

/* ══ 去掉阴影 / 假的"输入框感" ═══════════════════════════════
   吴凡 2026-09-11:「小红框内感觉有背景/阴影效果, 请去除,
   全站有的都去除这种效果, 做到简洁纯净」。

   他圈的是详情页上的**只读值**(加工车间/原料出库仓/原料批次/现有库存量)。
   量出来它们是 `.readonly`, 带两样东西:
     · box-shadow: rgba(0,0,0,.05) 0 1px 2px   ← 那点"阴影感"
     · border-bottom: 1px                       ← 那条横线
   两样加起来, **只读的值被画成了一个输入框的样子** ——
   看着能点, 点了没反应。这比"不好看"更糟: 它在骗人。
   只读就该长得像文字。

   ★★ `.ui-switch-knob` 的阴影**留着**, 别顺手一起清:
      那点立体感是"这颗东西可以拨"的唯一视觉线索,
      去掉之后开关就是一块平色, 没人知道能点。
      **阴影不都是装饰, 有的是在说"我是个控件"。**
   ★ 浮层(select2 下拉、弹窗、tooltip)的阴影也留着 ——
     它们靠阴影跟底下的内容分层, 去掉会糊成一片。
     所以下面全部限定在 `#content-main` / 具名组件里, 不写全局通配。 */
#content-main .readonly {
  box-shadow: none !important;
  border-bottom: 0 !important;
}
.foodmax-top-nav,
.foodmax-addbtn-bar,
#nav-sidebar-inner .bg-white.border,   /* 侧栏搜索框: 不在 #content-main 里 */
#content-main .border,
#content-main [class*="shadow-"] {
  box-shadow: none !important;
}

/* ── 筛选条上的「关键词」框: 只留一层边框 ──────────────────────
   吴凡 2026-09-11:「这个搜索框内多了一层东西, 也做的和其他框一样,
   去掉多的内容」。

   实测: 那一格是 **两层边框套着**
     input                          1px solid #e8e8e8   ← 自己的
     div.bg-white.border.border-…   1px solid #e5e7eb   ← unfold 搜索框的外壳
   而旁边「类型」「适用仓库」是 `select.foodmax-filter-select` 直接放在
   `.foodmax-filter-item` 里, 外层不带边框 —— **一层**。
   所以关键词那格看着比别人"深一圈"。

   ★ 去掉的是**外壳**那层, 不是 input 自己的 —— 反过来去的话,
     框会比旁边的下拉矮一点、内边距也对不上(外壳的 padding 和 input 的不同)。 */
.foodmax-filter-item .bg-white.border,
.fm-search-cell .bg-white.border {
  border: 0 !important;
  background: transparent !important;
  box-shadow: none !important;
}
/* 09-15 #190 冯海艳: 原生日期筛选 <input type=date>(vCustomDateField, 如「下单日期/实际送达日」的起止框)
   自身就挂着 bg-white+border 两个类、外层又没有壳, 会被上面「去外壳边框」那条连自己的边框一起抹掉 →
   看着没有灰框。给它单独补回 1px 灰边(和其它筛选框、文本框一致)。选择器多带一个类以压过上面那条。 */
.foodmax-filter-item input[type="date"].bg-white.border,
.fm-search-cell input[type="date"].bg-white.border,
.foodmax-filter-item input.vCustomDateField.border,
.fm-search-cell input.vCustomDateField.border {
  border: 1px solid var(--c-line) !important;
  background: #fff !important;
}
