/* ===== Renhe ERP — module styles (extends styles.css design system) ===== */

/* nav */
.nav-badge{margin-left:auto;background:rgba(255,255,255,.16);border-radius:10px;padding:1px 7px;font-size:11px}

/* data table */
.view{padding:18px 24px 40px;width:100%;max-width:1440px;margin:0 auto}
.card{background:var(--panel);border:1px solid var(--line);border-radius:var(--r);box-shadow:var(--shadow-sm);overflow:hidden}
table.grid{width:100%;border-collapse:collapse;font-size:13.5px}
table.grid th{background:#f7f9fc;color:var(--muted);font-weight:600;text-align:left;padding:11px 12px;border-bottom:1px solid var(--line);white-space:nowrap;position:sticky;top:0;z-index:2}
table.grid td{padding:10px 12px;border-bottom:1px solid var(--line2);white-space:nowrap}
table.grid tr:hover td{background:#f9fbfe}
table.grid tr:last-child td{border-bottom:0}
table.grid td.num,table.grid th.num{text-align:right;font-variant-numeric:tabular-nums}
/* 首列钉左：窄屏横滑后行标识（编码/名称）不跟着滑走 */
table.grid td:first-child{position:sticky;left:0;z-index:1;background:var(--panel)}
table.grid tr:hover td:first-child{background:#f9fbfe}
table.grid th:first-child{left:0;z-index:3}
.row-link{cursor:pointer}
.row-link:hover td:first-child{color:var(--brand)}
/* 可排序表头（app.js 渲染 th[data-sortcol] + role=button + .sort-ind）此前样式表里 0 条规则：
   光标是 auto、hover 无变化、未排序时指示器是空串 —— 用户根本看不出表头能点。
   常驻 ⇅ 提供静态可供性，排序后 app.js 把 .sort-ind 填成 ▲/▼，:empty 自然让位。 */
table.grid th[data-sortcol]{cursor:pointer;user-select:none}
table.grid th[data-sortcol]:hover{background:#eef3fb;color:var(--brand-ink)}
.sort-ind{margin-left:5px;font-size:10px}
.sort-ind:empty::before{content:"⇅"}
/* 行选择复选框：默认 13×13 低于 WCAG 2.2 AA 2.5.8 的 24×24，也比同屏任何控件都小；
   与抽屉里 .fld.chk input 的 18px 统一，移动端再放大到 24px。 */
th.selcol input,td.selcol input{width:18px;height:18px;margin:0;accent-color:var(--brand);cursor:pointer}
.grid-wrap{overflow-x:auto}
/* 列表卡片：表格贴满卡片（覆盖 styles.css .card 的 16px padding），
   并让卡片自己成为纵向滚动容器 —— 否则 th 的 position:sticky 对 window 滚动无效，表头会滚没。 */
.card.grid-wrap{padding:0;overflow:auto;max-height:calc(100vh - 210px);max-height:calc(100dvh - 210px)}
.empty{padding:60px 20px;text-align:center;color:var(--muted)}
.view>.empty,#rptBody>.empty{background:var(--panel);border:1px solid var(--line);border-radius:var(--r);box-shadow:var(--shadow-sm);font-size:15px}
.view>.empty::before,#rptBody>.empty::before{content:"🗂";display:block;font-size:38px;line-height:1;margin-bottom:12px;opacity:.5}
/* 少数模块（assemble/recon/transfers）把空态渲染成表格里的 <td class="empty">，
   会继承 table.grid td 的 10px 内距 / nowrap / 首列 sticky，与 .view>.empty 的卡片空态完全两副样子。
   这里把 td 形态拉回同一套视觉（同内距·同字号·同 🗂 图标），外层 .card.grid-wrap 已提供白底卡片。 */
table.grid td.empty{position:static;white-space:normal;padding:60px 20px;font-size:15px;background:var(--panel)}
table.grid td.empty::before{content:"🗂";display:block;font-size:38px;line-height:1;margin-bottom:12px;opacity:.5}
table.grid tr:hover td.empty{background:var(--panel)}

/* pagination */
.pager{display:flex;align-items:center;gap:10px;justify-content:flex-end;padding:12px 6px;color:var(--muted);font-size:13px}
.pager button{background:var(--panel);border:1px solid var(--line);padding:6px 12px;border-radius:8px;min-height:36px;white-space:nowrap}
.pager select{background:var(--panel);border:1px solid var(--line);padding:0 8px;border-radius:8px;min-height:36px;font-size:13px;color:var(--ink)}
.pager button:disabled{opacity:.55;cursor:not-allowed}

/* status badges — 语义分五档：中性/终态灰 · 进行中蓝 · 需关注橙 · 成功绿 · 失败或超期红 */
.badge{display:inline-block;padding:2px 9px;border-radius:20px;font-size:12px;font-weight:600;line-height:1.6;background:#eef1f6;color:var(--muted)}
/* 进行中 */
.b-issued,.b-processing,.b-running,.b-doing,.b-reviewed,.b-open{background:#e8f0ff;color:var(--brand)}
/* 成功终态 */
.b-active,.b-paid,.b-confirmed,.b-sent,.b-delivered,.b-approved,.b-done,.b-closed,.b-posted{background:var(--ok-bg);color:var(--ok)}
/* 失败 / 超期 */
.b-unpaid,.b-overdue,.b-rejected,.b-returned,.b-low{background:var(--danger-bg);color:var(--danger)}
/* 需关注 / 待办 */
.b-in_transit,.b-booked,.b-reserved,.b-pending,.b-sending,.b-occupied,.b-submitted,.b-partial{background:#fff5e6;color:var(--warn)}
/* 中性 / 未开始 / 已撤销 */
.b-draft,.b-none,.b-cancelled{background:#eef1f6;color:var(--muted)}

/* KPI cards */
.kpis{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(230px,100%),1fr));gap:14px;margin-bottom:18px}
.kpi{background:var(--panel);border:1px solid var(--line);border-radius:var(--r);padding:16px 18px;box-shadow:var(--shadow-sm)}
.kpi .klabel{color:var(--muted);font-size:13px;margin-bottom:6px}
.kpi .kval{font-size:26px;font-weight:800;letter-spacing:-.5px}
.kpi .kval.neg{color:var(--danger)}.kpi .kval.pos{color:var(--ok)}
.kpi .ksub{color:var(--faint);font-size:12px;margin-top:3px}
.panel-card{background:var(--panel);border:1px solid var(--line);border-radius:var(--r);padding:18px;box-shadow:var(--shadow-sm);margin-bottom:16px}
.panel-card h3{margin:0 0 12px;font-size:15px}

/* drawer form */
.drawer-panel{display:flex;flex-direction:column}
.drawer-body{flex:1;overflow-y:auto;padding:18px 22px}
/* ---------- 操作条口径（.drawer-foot / .qf-actions / .de-actions 共用）----------
   历史：一排按钮的 flex 容器如果既不换行也不横滚，放不下时 flex 只能去**收缩按钮本身**，
   中文按钮被压到 min-content = 每行一个字（实测 375 下 7 个按钮全部 h=101px），
   再叠加 justify-content:flex-end，溢出的部分往左跑出容器 —— 「删除」实测落在 x=-56。
   上一版改成「nowrap + overflow-x:auto」，竖排是没了，但换来更隐蔽的病：横滚条在 Linux/
   Android 的 overlay 模式下只有 1px 且不常驻，用户看到的永远是 scrollLeft=0 那一屏 ——
   375 下出入库草稿单实测 scrollWidth=669 / clientWidth=364，「审核 / 保存数量 /
   过账（登记库存）」三个按钮（含唯一主操作）整个落在视口外，屏幕上没有任何「还有更多」的线索。
   ⚠ 根因收口：**操作条一律换行（flex-wrap:wrap），不再横滚**。
   换行 + flex:0 0 auto 的组合给出的是布局模型层面的机械保证：
   每个子项要么排在当前行、要么另起一行，只要子项自身宽度 ≤ 容器内容宽（由 max-width:100% 保证），
   就**不可能**有任何按钮落在容器之外 —— 无论今后加到几个按钮、文案多长、语言换成哪种。
   于是「首屏看不到主操作」这件事在结构上不存在，也就不需要任何滚动提示。
   右对齐改回 justify-content:flex-end（原来那条 :first-child{margin-inline-start:auto}
   是为横滚场景写的，换行后它会把 DOM 首项单独顶到行尾、把其余行拆成左对齐）；
   flex-end 之所以在这里安全，正是因为已经不会溢出 —— 溢出往左跑的老毛病没有了土壤。 */
.drawer-foot{padding:14px 22px;border-top:1px solid var(--line);display:flex;gap:10px;
  justify-content:flex-end;flex-wrap:wrap}
/* max-width:100% 是上面那条机械保证的前提：任何单个按钮都不许比容器还宽（否则 flex-end 下
   它会往左溢出到够不着的地方）。css_regression §24 按几何逐项验证，越界当场红。 */
.drawer-foot>*{flex:0 0 auto;white-space:nowrap;max-width:100%}
/* 表单栅格用 flex-wrap 而不是 2 列 grid：
   根因是 grid 的固定列轨道 —— 一个整宽字段（备注/名称/分组标题）只能另起一行，它前面那个
   半宽字段的右半格就永远空着（实测产品表单 3 处内部空洞 + 1 处结尾空洞）。grid 里没有任何
   选择器能表达「我落在第几列」，所以只能靠 :has() 猜，猜错就会把本来配对好的字段拆散。
   换成「每项 basis = 半宽、flex-grow:1」后，落单的一项自动长满整行 —— 空洞在布局模型层面
   就不可能出现，而且 DOM 顺序原样保留（不像 grid-auto-flow:dense 会让视觉顺序与 Tab 顺序错开）。
   calc(50% - 8px) 里的 8px = 列间距 16px 的一半，两项 + 间距刚好 100%。 */
.form-grid{display:flex;flex-wrap:wrap;gap:14px 16px}
.form-grid>*{flex:1 1 calc(50% - 8px);min-width:0}
.form-grid .full{flex-basis:100%}
.fld{display:flex;flex-direction:column;gap:5px}
.fld label{font-size:12.5px;color:var(--muted);font-weight:600}
/* 必填字段的标签原来与非必填完全同色（都是 --muted），连那个 * 都是灰的，扫一眼分不出哪些必须填。
   锚点用 a11yPass() 已经统一打好的 aria-required —— 它是按 label 文本里的「*」推出来的，
   所以所有渲染入口（fieldHTML / 单据表头 / 各个手写抽屉）一次全覆盖，不用逐处枚举。 */
.fld:has([aria-required="true"])>label{color:var(--ink);font-weight:800}
/* 待 app.js 把星号包成 <i class="req">*</i> 后自动生效（见 needs_frontend） */
.fld label .req{color:var(--danger);font-weight:800}
.fld input,.fld select,.fld textarea{padding:9px 11px;border:1.5px solid var(--line);border-radius:9px;background:#fff}
.fld input:focus,.fld select:focus,.fld textarea:focus{outline:0;border-color:var(--brand);box-shadow:0 0 0 3px rgba(31,95,209,.1)}
/* 布尔字段：清掉 styles.css .chk（顶部工具条筛选药丸）串过来的边框/白底/内距，别长得像文本框 */
.fld.chk{flex-direction:row;align-items:center;gap:9px;border:0;background:transparent;padding:0;
  height:auto;min-height:34px;border-radius:0;color:var(--ink);font-size:inherit}
.fld.chk label{font-size:13.5px;color:var(--ink);font-weight:500;cursor:pointer}
.fld.chk input{width:18px;height:18px;flex:none;accent-color:var(--brand);cursor:pointer;margin:0}
.kv{display:grid;grid-template-columns:120px 1fr;gap:8px 14px;font-size:14px}
.kv dt{color:var(--muted)}.kv dd{margin:0;font-weight:500}

/* buttons */
/* 次级按钮此前没有 color 声明，实测落到浏览器默认 rgb(0,0,0)，与全站正文 --ink 是两种黑 */
.btn{padding:9px 16px;border-radius:9px;font-weight:600;font-size:13.5px;border:1px solid var(--line);background:var(--panel);color:var(--ink)}
.btn:hover{background:#f4f7fc}
.btn.primary{background:var(--brand);color:#fff;border-color:var(--brand)}.btn.primary:hover{background:var(--brand-d)}
/* 变体的 background 与上一行 .btn:hover 同为 (0,2,0) 特异度且写在后面，会把默认 hover 底色
   整条盖掉 —— .ok/.warn 曾因此完全没有 hover 反馈（提交/审批/完工入库这些改状态的主操作）。
   每个实色变体都必须自带一条 :hover，css_regression 里有一条动态守卫按类名签名遍历全站按钮，
   任何新变体漏写 hover 都会当场红。 */
.btn.ok{background:var(--ok);color:#fff;border-color:var(--ok)}.btn.ok:hover{background:var(--ok-d);border-color:var(--ok-d)}
.btn.warn{background:var(--warn);color:#fff;border-color:var(--warn)}.btn.warn:hover{background:var(--warn-d);border-color:var(--warn-d)}
.btn.danger{color:var(--danger);border-color:#f3c2c7}.btn.danger:hover{background:var(--danger-bg)}
.btn.sm{padding:6px 11px;font-size:12.5px}
/* ---------- 交互三态：hover(上面已有) / active 按下 / disabled·busy 置灰 ----------
   原来全站 0 条 :active 规则、:disabled 规则只覆盖分页器：withBusy() 把按钮 disabled 掉之后
   外观（满色蓝 + pointer 光标）完全不变，慢网下用户看不出还能不能点。 */
:where(button,.btn,.nav-item,.picker-item,.al-chip,.flow):not(:disabled):not([aria-busy="true"]):active{
  transform:translateY(1px);filter:brightness(.94);transition-duration:.02s}
button:disabled,.btn:disabled,input:disabled,select:disabled,textarea:disabled,
button[aria-busy="true"],.btn[aria-busy="true"]{opacity:.55;cursor:not-allowed;filter:grayscale(.25);transform:none}
/* 校验失败高亮：app.js markErr() 用内联 style 写死 #d93026（不在色板里的第三个红），
   内联样式只能用 !important 拉回 --danger；markErr 同时会打上 aria-invalid，正好做锚点。 */
[aria-invalid="true"]{outline:2px solid var(--danger)!important;outline-offset:1px!important}

/* document editor overlay */
.doc-editor{position:fixed;inset:0;background:var(--bg);z-index:60;display:flex;flex-direction:column;overflow:hidden}
/* 顶栏与 .de-section 共用同一条内容栅格线：de-body 左右各 22px + 内容列最大 1200px 居中 */
.de-head{display:flex;align-items:center;gap:14px;background:var(--panel);border-bottom:1px solid var(--line);box-shadow:var(--shadow-sm);
  padding:14px calc(22px + max(0px,(100% - 1244px)/2))}
/* 返回按钮：覆盖 styles.css 的 .icon-btn 方形(36x36)约束，否则「‹ 返回」会竖排换行 */
#deBack{width:auto;height:auto;white-space:nowrap;flex:none;display:inline-flex;align-items:center;gap:5px;padding:8px 15px;font-size:13.5px;font-weight:600}
.de-title{font-size:17px;font-weight:800;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* 同一条操作条不变量：换行 + 按钮永不收缩 + 单项不宽于容器（见上方 .drawer-foot 的长注释）。
   .de-actions 原来只在 ≤720 的移动块里写了 flex:0 0 auto，桌面上一旦标题挤到 0 宽仍会
   把按钮压成逐字竖排（就是 .drawer-foot 在 375 上发生过的事）。这里提到无条件层。 */
.de-actions{margin-left:auto;display:flex;gap:8px;flex-wrap:wrap}
.de-actions>*{flex:0 0 auto;white-space:nowrap;max-width:100%}
.de-body{flex:1;overflow-y:auto;padding:20px 22px 60px}
.de-section{max-width:1200px;margin:0 auto 18px}

/* item grid (editable) */
/* overflow 必须是 clip 而不是 hidden：hidden 会让 table 自己变成 scrollport，
   下面 td:first-child 的横向 sticky 就会钉在不滚动的 table 上而完全失效。clip 一样能裁圆角。 */
.items-tbl{width:100%;border-collapse:collapse;font-size:13px;background:var(--panel);border:1px solid var(--line);border-radius:var(--r);overflow:clip}
.items-tbl th{background:#f7f9fc;color:var(--muted);font-weight:600;padding:9px 8px;text-align:left;border-bottom:1px solid var(--line);font-size:12px;white-space:nowrap;position:sticky;top:0;z-index:1}
.items-tbl td{padding:5px 6px;border-bottom:1px solid var(--line2);vertical-align:middle}
.items-tbl td.num,.items-tbl th.num{text-align:right}
/* 首列（产品名）钉左：明细表总宽 1011px vs 375 手机可视 355px，横滑到「金额/仓库」列后
   原来完全看不出在改哪一行。与 table.grid 的写法一致。 */
.items-tbl td:first-child{position:sticky;left:0;z-index:2;background:var(--panel)}
.items-tbl th:first-child{position:sticky;left:0;z-index:3;background:#f7f9fc}
/* 数字列给足最小宽度：窄屏时让 .grid-wrap 真正横滚，而不是把「数量」压成 28px 看不见数值 */
/* 可供性：可编辑格必须看起来像输入框（淡边框 + 面板底），只读的「金额」反过来去掉边框底色。
   原来正好倒过来 —— 4 个可改的数值是纯文本，唯一像输入框的是不能改的金额格。 */
.items-tbl input,.items-tbl select{width:100%;min-width:68px;padding:6px 7px;border:1px solid var(--line);border-radius:6px;background:var(--panel);font-size:13px}
.items-tbl input.num{min-width:84px}
.items-tbl input:focus{border-color:var(--brand);background:#fff;box-shadow:0 0 0 2px rgba(31,95,209,.08)}
.items-tbl input.ro{color:var(--muted);background:transparent;border-color:transparent}
.items-tbl input.num{text-align:right;font-variant-numeric:tabular-nums}
.it-prod{min-width:190px;font-weight:600;cursor:pointer;color:var(--ink)}
.it-del{color:var(--danger);cursor:pointer;text-align:center;font-weight:700;min-width:36px}
.it-add{margin-top:10px}
/* 合计条：可换行，并作为一条独立的白底汇总带（别裸浮在页面背景上） */
.de-tot{display:flex;flex-wrap:wrap;gap:8px 26px;justify-content:flex-end;padding:12px 16px;font-size:14px;
  background:var(--panel);border:1px solid var(--line);border-radius:var(--r);margin-top:10px}
.de-tot>*{white-space:nowrap}
.de-tot b{font-size:18px;color:var(--brand-ink)}
@media(max-width:620px){
  .de-tot{display:grid;grid-template-columns:1fr 1fr;gap:6px 12px;padding:12px}
  .de-tot>*{display:flex;justify-content:space-between;gap:8px;min-width:0}
  .de-tot b{font-size:15px}
}

/* picker：列表留在弹窗卡片内部滚动（styles.css 的旧 absolute 下拉已删），卡片本身限高不越出视口 */
.picker-search{width:100%;padding:11px 13px;border:1.5px solid var(--line);border-radius:10px;margin-bottom:10px}
.picker-list{flex:1 1 auto;min-height:0;max-height:none;overflow-y:auto;display:flex;flex-direction:column;gap:4px}
.picker-item{padding:10px 12px;min-height:44px;border:1px solid var(--line);border-radius:9px;cursor:pointer;display:flex;gap:10px;align-items:center}
.picker-item:hover{border-color:var(--brand);background:#f4f7fc}
.picker-item b{font-size:13px}.picker-item small{color:var(--muted)}
.qf-card{width:min(560px,94vw)}
#picker{padding:16px;overflow:auto}
#picker .qf-card{display:flex;flex-direction:column;max-height:88vh;max-height:88dvh}
@media(max-width:480px){
  #picker{padding:0;place-items:end center}
  #picker .qf-card{width:100vw;max-width:100vw;border-radius:16px 16px 0 0;
    padding-bottom:calc(22px + env(safe-area-inset-bottom))}
}

/* dashboard extras */
.kpi .kicon{font-size:20px;margin-bottom:4px}
.alerts{display:flex;gap:10px;flex-wrap:wrap;margin-bottom:16px}
.al-chip{background:var(--panel);border:1px solid var(--line);border-radius:20px;padding:8px 14px;font-size:13px;font-weight:600;cursor:pointer;box-shadow:var(--shadow-sm)}
.al-chip.warn{color:var(--warn);border-color:#f0d9b5}
.al-chip.danger{color:var(--danger);border-color:#f3c2c7}
.al-chip:hover{filter:brightness(.97)}
/* align-items:start —— 默认 stretch 会让左边的图表卡被极长的右栏（快捷操作+智能工具+10 个流程图）
   拉到同高，实测 1440 下图表下方留 1178px 白块（卡片 82% 是空的）。 */
.dash-2col{display:grid;grid-template-columns:1.5fr 1fr;gap:16px;align-items:start}
.quick-acts{display:flex;flex-wrap:wrap;gap:8px}
.legend{display:flex;align-items:center;gap:16px;margin-top:8px;font-size:12px;color:var(--muted)}
.legend i{display:inline-block;width:10px;height:10px;border-radius:2px;margin-right:5px;vertical-align:middle}
/* CSS bar chart (crisp, responsive — replaces distorted SVG) */
.bars{display:flex;align-items:flex-end;gap:12px;height:180px;padding:6px 2px 0;border-bottom:1px solid var(--line)}
.bar-col{flex:1;min-width:0;height:100%;display:flex;flex-direction:column}
.bar-pair{flex:1;display:flex;align-items:flex-end;justify-content:center;gap:4px;min-height:0}
.bar{width:30%;max-width:22px;min-height:3px;border-radius:4px 4px 0 0;transition:.2s}
.bar.s{background:#1f5fd1}.bar.p{background:#bcd0ee}
.bar-pair:hover .bar.s{background:var(--brand-d)}
.bar-x{text-align:center;font-size:11px;color:var(--faint);padding-top:6px}

/* nav search + collapsible groups */
.nav-search{width:calc(100% - 24px);margin:6px 12px 4px;padding:8px 12px;border-radius:9px;border:1px solid rgba(255,255,255,.15);background:rgba(255,255,255,.08);color:#fff;font-size:13px}
.nav-search::placeholder{color:rgba(255,255,255,.5)}
.nav-search:focus{outline:0;border-color:var(--brand2);background:rgba(255,255,255,.14)}
/* min-height 30px：分组标题是 role=button 的可交互控件，原来实测只有 22.5px 高、
   且与下一个菜单项 0 间隙，拿不到 WCAG 2.2 AA 2.5.8 的间距例外 → 直接把目标撑到 ≥24px。 */
.nav-title{cursor:pointer;display:flex;align-items:center;justify-content:space-between;user-select:none;min-height:30px}
.nav-caret{font-size:9px;opacity:.55;transition:.15s}
.nav-group.collapsed .nav-item{display:none!important}
.nav-group.collapsed .nav-caret{transform:rotate(-90deg)}

/* dashboard built-in flows */
.flows{display:flex;flex-direction:column;gap:12px}
.flow{border:1px solid var(--line);border-radius:10px;padding:10px 12px;background:#fbfcff}
.flow-h{display:flex;align-items:center;gap:7px;font-size:13.5px;color:var(--ink);margin-bottom:7px}
.flow-h b{font-weight:700}
.flow-steps{display:flex;flex-wrap:wrap;align-items:center;gap:5px}
.fstep{background:#eef3fb;color:var(--brand-ink,#1f5fd1);border:1px solid #dbe6f7;border-radius:6px;padding:2px 8px;font-size:11.5px;white-space:nowrap}
.farr{color:var(--faint);font-style:normal;font-size:11px}

/* entity lookup field */
.lookup-wrap{display:flex;gap:6px}
.lookup-wrap input{flex:1}
.lookup-wrap .btn{white-space:nowrap}

/* ---------- focus visibility (WCAG 2.4.7 / 1.4.11：焦点环需 ≥3:1)
   原来各处 outline:0 + 10% 淡蓝 box-shadow 实测只有 1.12–1.19:1，等于没有焦点指示。 */
/* button{transition:.15s} / .nav-item{transition:.14s} 都是「all」，会把 outline 一起做 150ms 淡入：
   键盘落焦瞬间实测环色只有 rgb(230,237,250)（对比度 1.06:1），要等动画走完才到 5.21:1。
   焦点指示必须即时可见，这里把进入焦点态时的过渡属性收窄，把 outline 排除掉。 */
:focus-visible{transition-property:background-color,border-color,color,box-shadow,transform,filter}
:where(button,a[href],input,select,textarea,summary,[tabindex]:not([tabindex="-1"])):focus-visible{
  outline:2px solid var(--brand);outline-offset:2px}
.fld input:focus-visible,.fld select:focus-visible,.fld textarea:focus-visible,
.login-card input:focus-visible,.qf-fields input:focus-visible,.qf-fields select:focus-visible,
.search:focus-visible,.picker-search:focus-visible,#rF:focus-visible,#rT:focus-visible,
.daterange input:focus-visible{outline:2px solid var(--brand);outline-offset:2px}
.items-tbl input:focus-visible,.items-tbl select:focus-visible{outline:2px solid var(--brand);outline-offset:-1px}
/* 深色侧栏上蓝色环只有 3.1:1，改用白色环 */
.nav-item:focus-visible,.nav-title:focus-visible,.nav-search:focus-visible,
.side-foot .out:focus-visible{outline:2px solid #fff;outline-offset:-2px}
/* 容器 overflow:hidden 会裁掉外扩的环，行/条目一律内缩 */
table.grid tbody tr:focus-visible,.picker-item:focus-visible,
.al-chip:focus-visible,.flow:focus-visible{outline:2px solid var(--brand);outline-offset:-2px}

/* 断点按「内容区」算而不是视口：>860px 时侧栏还占着 236px，
   861–956px 的视口内容区实际只有 625–720px，双栏会被挤成 318+243。 */
@media(max-width:720px),(min-width:861px) and (max-width:956px){
  .dash-2col{grid-template-columns:1fr}
}

@media(max-width:720px){
  .form-grid>*{flex-basis:100%}
  .view{padding:14px 12px 40px}
  /* 底部固定操作栏换行后最多 3 行（3×44 + 2×8 + 上下内距 ≈ 164px），
     de-body 的下内距必须盖得住，否则最后一段内容被操作栏永久遮住。
     css_regression §24 会实测「de-body 末元素不被固定操作栏压住」。 */
  .de-body{padding:14px 10px 176px}
  /* 单据编辑器：顶栏压到一行 56px，5 个操作按钮改成底部固定操作栏（原来头部吃掉 262px / 32% 屏高） */
  .de-head{flex-wrap:nowrap;gap:8px;padding:8px 12px;min-height:56px}
  #deBack{padding:8px 10px}
  .de-title{font-size:15px;flex:1 1 auto}
  .de-actions{position:fixed;left:0;right:0;bottom:0;margin:0;z-index:2;background:var(--panel);
    border-top:1px solid var(--line);box-shadow:0 -2px 10px rgba(20,27,45,.08);
    padding:8px 12px calc(8px + env(safe-area-inset-bottom));gap:8px;flex-wrap:wrap}
  .de-actions>*{min-height:44px}
  /* 抽屉页脚与 .de-actions 同一套移动端口径：触摸目标 ≥44px + 安全区内距 */
  .drawer-foot{padding:8px 12px calc(8px + env(safe-area-inset-bottom));gap:8px}
  .drawer-foot>*{min-height:44px}
  /* ---------- 操作优先级（只在移动端重排）----------
     窄屏换行后按钮会占满 2–3 行，DOM 顺序决定「先读到什么」：出入库草稿单的 DOM 顺序是
     删除 / 关闭 / 电子单 / 品检合格 / 审核 / 保存数量 / 过账，最破坏性的「删除」排在最前、
     唯一的主操作「过账（登记库存）」排在最后。这里按**语义 class**（而不是逐个 id）重排：
     .primary = 该抽屉的主操作 → 提到第一位；.danger = 破坏性动作 → 压到最后一位。
     用 class 而不是 id 意味着以后新加的任何主操作/删除按钮自动落位，不需要回来补规则。
     桌面不重排：宽屏一行放得下，右对齐 + 主操作在最右是既有的桌面惯例。
     margin-inline-start:auto 让破坏性按钮吃掉所在行的全部剩余空间、与相邻按钮拉开距离（防误触）；
     auto 外距在空间不足时自动归零，**永远不会**把行撑爆或多挤出一行，所以它与上面那条
     「不可能溢出」的机械保证不冲突。 */
  .drawer-foot>.primary,.de-actions>.primary{order:-1}
  .drawer-foot>.danger,.de-actions>.danger{order:1;margin-inline-start:auto}
  .card.grid-wrap{max-height:calc(100vh - 260px);max-height:calc(100dvh - 260px)}
}
/* iOS Safari 会在聚焦 <16px 输入框时强制放大页面；明细行的 13px 定义在本文件，需在此覆盖 */
@media(max-width:860px){
  .items-tbl input,.items-tbl select{font-size:16px}
  .items-tbl input,.items-tbl select{min-width:76px}
  .items-tbl input.num{min-width:96px}
  .pager button,.pager select{min-height:44px}
  th.selcol input,td.selcol input{width:24px;height:24px}
}
@media(max-width:620px){
  /* KPI 手机端两列：原来 minmax(190px) 在 347px 可用宽下只能排 1 列，6 张卡撑到 3290px 高 */
  .kpis{grid-template-columns:repeat(2,1fr);gap:10px}
  .kpi{padding:12px 12px 10px}
  .kpi .kval{font-size:20px}
  .kpi .kicon{font-size:16px;margin-bottom:2px}
}

/* ---------- 空态：0 条时别再渲染「共 0 条 · 第 1/1 页」分页器与 0.00 合计带 ----------
   分页器由 app.js 无条件输出；空态容器恰好是它的前一个兄弟，用相邻选择器纯 CSS 收掉。 */
.empty+.pager,#rptBody>.empty+.de-tot{display:none}
/* 相邻兄弟选择器太脆：JS 只要在空态与分页器之间插入任何节点（例如「合并所选草稿」工具条）
   分页器就会重新露出来；而 <td class="empty"> 形态的空态压根不是 .pager 的兄弟。
   改用容器级 :has() 判空，与两者之间隔了什么节点无关。 */
#view:has(.empty)>.pager,#rptBody:has(>.empty)>.de-tot{display:none}

/* ---------- 报表合计：贴在表格卡片底部成为一条页脚带，并与数字列同侧（右）对齐 ----------
   合计仍由 app.js 以 .de-tot 输出（带 justify-content:flex-start 内联样式），
   若后续改成 <tfoot> 渲染，本段自然失效、不影响。 */
#rptBody>.card.grid-wrap{margin-bottom:0;border-bottom-left-radius:0;border-bottom-right-radius:0}
#rptBody>.card.grid-wrap+.de-tot{margin-top:-1px;border-top:2px solid var(--brand);
  border-radius:0 0 var(--r) var(--r);font-weight:700;justify-content:flex-end!important}
table.grid tfoot td,table.grid tfoot th{border-top:2px solid var(--brand);font-weight:800;background:#f4f7ff}

/* ---------- print ----------
   必须留在最后一张样式表：同特异度下后加载的规则胜出，放在 styles.css 里时
   .view/.card/.de-head 等 app.css 常规规则会把打印覆盖顶掉（实测卡片打印仍带阴影）。
   目标：Ctrl+P 直接出可归档的 A4 —— 去掉导航/操作条，保留模块标题作页眉，
   表头跨页重复，行不劈开，彩色徽章转黑白描边。 */
@page{size:A4;margin:14mm 12mm}
@media print{
  html,body{background:#fff;color:#000}
  .sidebar,.scrim,.menu-btn,.tools,#toast,.pager,.de-actions,.drawer-foot,.drawer-bg,
  .icon-btn,.btn,.al-chip,.quick-acts,.skip-link,.nav-badge,.kpi::after{display:none!important}
  #app{display:block;min-height:0}
  .main{display:block}
  /* 页眉：模块标题留下来，否则打出来的清单不知道是哪张表 */
  .appbar{display:block;position:static;background:none;backdrop-filter:none;
    padding:0 0 6px;margin:0 0 10px;border-bottom:2px solid #000}
  .appbar h1{font-size:16pt;color:#000;overflow:visible;white-space:normal}
  .view{padding:0;max-width:none;margin:0;width:auto}
  .card,.panel-card,.kpi{box-shadow:none;border:1px solid #999;break-inside:avoid;page-break-inside:avoid}
  .card.grid-wrap{padding:0;max-height:none;overflow:visible}
  .grid-wrap{max-height:none;overflow:visible}
  .doc-editor{position:static;overflow:visible;height:auto;background:#fff}
  .de-head{padding:0 0 8px;box-shadow:none;border-bottom:2px solid #000;background:none}
  .de-title{font-size:14pt}
  .de-body{overflow:visible;padding:0}
  .de-tot{border:1px solid #999;background:none}
  .drawer{position:static}
  .drawer-panel{position:static;width:auto;box-shadow:none;animation:none;background:#fff;overflow:visible}
  .drawer-head{position:static;background:none;border-bottom:2px solid #000}
  thead{display:table-header-group}
  tfoot{display:table-footer-group}
  tr{break-inside:avoid;page-break-inside:avoid}
  table.grid{font-size:9.5pt}
  .items-tbl{font-size:9pt}
  table.grid th,.items-tbl th{position:static;background:#f0f0f0;color:#000;
    -webkit-print-color-adjust:exact;print-color-adjust:exact}
  table.grid td,table.grid th,.items-tbl td,.items-tbl th{border:1px solid #999}
  table.grid td:first-child{position:static;background:none}
  .items-tbl input,.items-tbl select{border:0;background:none;color:#000;
    -webkit-appearance:none;appearance:none}
  /* 黑白打印机上 6 档彩色底色会糊成同一片灰，改描边 + 纯黑字 */
  .badge,.fstep{background:none!important;color:#000!important;border:1px solid #666}
  a[href]::after{content:""}
}
