/* 组件样式 · 依赖 tokens.css（页面先引 tokens.css 再引 app.css）
   类与 data-* 钩子契约见 public/css/README.md；交互全部在 public/js/app.js，无内联脚本 */
/* ===== 分层令牌（z-index 体系：抽屉遮罩 < 抽屉 < 弹层 < 下拉 < toast；弹层内：遮罩 0 < 白卡 1）===== */
:root{--white:#fff;--z-nav:50;--z-mask:45;--z-modal:60;--z-modal-mask:0;--z-modal-box:1;--z-dropdown:70;--z-toast:80}   /* --white：卡片/弹层/表单底色令牌（避免样式里硬编码 #fff） */
*,*::before,*::after{box-sizing:border-box}
html,body{margin:0;padding:0}
body{font-family:var(--font);font-size:var(--fs14);color:var(--g900);background:var(--bg);-webkit-font-smoothing:antialiased}
a{color:var(--brand);text-decoration:none}
h1,h2,h3{margin:0}
img{max-width:100%}
.mono{font-family:var(--mono);letter-spacing:.02em}
.muted{color:var(--g500);font-size:var(--fs13)}
.right{text-align:right}
.mb2{margin-bottom:var(--s2)}.mb3{margin-bottom:var(--s3)}.mb4{margin-bottom:var(--s4)}
.mt3{margin-top:var(--s3)}.mt4{margin-top:var(--s4)}
.hide,[hidden]{display:none!important}
.row-flex{display:flex;align-items:center;gap:var(--s2)}
.gap4{display:flex;gap:var(--s4);flex-wrap:wrap}
/* ===== 布局：侧栏 + 顶栏 + 内容区 ===== */
.layout{display:flex;min-height:100vh}
.mask{position:fixed;inset:0;background:rgba(17,24,39,.45);z-index:var(--z-mask);display:none}
.side{width:216px;flex:0 0 216px;background:var(--side);color:var(--side-fg);display:flex;flex-direction:column}
.side .logo{height:56px;display:flex;align-items:center;gap:var(--s2);padding:0 18px;color:#fff;font-weight:700;font-size:var(--fs16);border-bottom:1px solid var(--side2);flex:0 0 56px}
/* 品牌标（统一）：渐变底 + 白色「光」字；侧栏 <i> 为空时由 :empty::after 补字（view/** 不改） */
.side .logo i,.login-brand .brand i{background:linear-gradient(135deg,#2563eb,#1d4ed8);color:#fff;font-style:normal;font-weight:700;display:flex;align-items:center;justify-content:center;box-shadow:0 2px 8px rgba(37,99,235,.35)}
.side .logo i{width:22px;height:22px;border-radius:6px;flex:0 0 22px;font-size:12px}
.side .logo i:empty::after{content:"光";line-height:1}
.side nav{padding:var(--s2);overflow:auto}
.side nav a{display:flex;align-items:center;gap:var(--s2);padding:9px var(--s3);border-radius:var(--r-ctl);color:var(--side-fg);font-size:13.5px;margin-bottom:2px;white-space:nowrap}
.side nav a:hover{background:var(--side2);color:#fff}
.side nav a.on{background:var(--brand);color:#fff}
.side .grp{color:var(--side-mut);font-size:11px;padding:var(--s3) var(--s3) 6px;letter-spacing:.08em}
.main{flex:1;min-width:0;display:flex;flex-direction:column}
.topbar{height:56px;flex:0 0 56px;background:#fff;border-bottom:1px solid var(--bd);display:flex;align-items:center;justify-content:space-between;gap:var(--s3);padding:0 20px}
.crumb{font-size:var(--fs13);color:var(--g500)}.crumb b{color:var(--g900)}.crumb a{color:var(--g500)}.crumb a:hover{color:var(--g900)}   /* T124：面包屑根节点不再继承全局 a 的品牌蓝 */
.who{display:flex;align-items:center;gap:var(--s2);font-size:var(--fs13);color:var(--g700)}
/* V5 3.1①：标头（顶栏）文字**一律单行不换行** —— 面包屑过长走省略号，姓名与按钮不许折成两行；窄屏收窄内边距/间距。 */
.crumb,.crumb b{white-space:nowrap}
.crumb{overflow:hidden;text-overflow:ellipsis;min-width:0}
.who{white-space:nowrap;flex:0 0 auto}
.who>span{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.who>form{margin:0;flex:0 0 auto}
.topbar .btn,.topbar .navtoggle{white-space:nowrap;flex:0 0 auto}
@media(max-width:480px){
  .topbar{padding:0 10px;gap:var(--s2)}
  .crumb{font-size:var(--fs12)}
  .who{gap:6px}
  .who>span{max-width:76px}
}
.avatar{width:30px;height:30px;border-radius:50%;background:var(--brand50);color:var(--brand);display:flex;align-items:center;justify-content:center;font-weight:700;font-size:var(--fs12)}
.content{flex:1;padding:18px 20px;min-width:0}
.pagetitle{font-size:var(--fs20);font-weight:700;margin:0 0 14px}
.pagehead{display:flex;align-items:center;justify-content:space-between;gap:var(--s3);flex-wrap:wrap;margin-bottom:var(--s3)}
.grid{display:flex;gap:var(--s4);flex-wrap:wrap}
.col{flex:1;min-width:260px}.col2{flex:2;min-width:320px}
/* 同一行内「单卡列」等高：卡片撑满列高、正文吸收差值（修仪表盘两列卡片底部参差，平台方「显示不正常」） */
.grid>.col{display:flex;flex-direction:column}
.grid>.col>.card:only-of-type{flex:1;display:flex;flex-direction:column}
.grid>.col>.card:only-of-type>.bd{flex:1}
.grid>.col>.card:only-of-type>.bd>.btn:last-child{margin-top:auto}
.navtoggle{display:none;border:1px solid var(--bd);background:#fff;border-radius:var(--r-ctl);width:36px;height:36px;font-size:16px;color:var(--g700)}
/* ≤1199px：侧栏改为 off-canvas 抽屉（顶栏 ☰ 展开 / 点遮罩收起）。
   原「992~1199px 压成 64px 图标栏」的规则已删除——导航项只有文字没有图标，图标栏只会空白（平台方反馈）。 */
@media(max-width:1199px){
  .navtoggle{display:inline-flex;align-items:center;justify-content:center}
  .side{position:fixed;z-index:var(--z-nav);inset:0 auto 0 0;transform:translateX(-100%);transition:transform .2s;width:216px;flex-basis:216px}
  .side.open{transform:none;box-shadow:var(--sh-pop)}
  .side .logo span,.side .grp{display:block}
  .side nav a{justify-content:flex-start;padding:9px var(--s3);font-size:13.5px}
  .nav-open .mask{display:block}
}
/* ===== 登录页（view/page/auth/login.php：左深色品牌区 + 右表单，效果稿 PDF 第 4/18 页 · lyear v5 提示体系）===== */
.login,.login *{box-sizing:border-box}
body:has(.login form[action="/login"])>.content{display:flex;flex-direction:column;justify-content:center;align-items:stretch;min-height:100vh;padding:var(--s4);background:linear-gradient(135deg,var(--brand50),var(--g50))}
.login-card{display:flex;width:960px;max-width:100%;min-height:540px;margin:0 auto;background:#fff;border-radius:16px;box-shadow:0 20px 60px rgba(16,24,40,.12);overflow:hidden}
.login-brand{flex:1 1 auto;min-width:0;background:var(--side);color:#fff;padding:40px;display:flex;flex-direction:column}
.login-brand .brand{display:flex;align-items:center;gap:10px;font-weight:700;font-size:18px}
.login-brand .brand i{width:26px;height:26px;border-radius:8px;flex:0 0 26px;font-size:14px}
.login-brand .login-title{font-size:var(--fs20);font-weight:700;margin:26px 0 0;color:#fff;letter-spacing:.02em}
.login-brand .tagline{margin:12px 0 0;font-size:15px;color:var(--side-fg);line-height:1.9}
.login-brand .foot{margin:auto 0 0;padding-top:var(--s6);font-size:12.5px;color:#94a3b8}
.login-form{width:480px;flex:0 0 480px;padding:44px;display:flex;flex-direction:column;justify-content:center}
.login-title{font-size:var(--fs20);font-weight:700;margin:0 0 6px}
.login-form .subtitle{margin:0 0 var(--s4)}
.login-row{display:flex;align-items:center;justify-content:space-between;gap:var(--s2);margin:var(--s3) 0}
.login-sms{margin-top:var(--s2)}
.login-sms .hint{margin:6px 0 0;text-align:center}
.login-foot{margin:var(--s4) 0 0;text-align:center;font-size:var(--fs12);color:var(--g500)}
/* 首页选择入口（T113，view/page/home.php 的 .entry 钩子）：未登录整屏水平垂直居中 */
body:has(.entry)>.content{display:flex;align-items:center;justify-content:center;min-height:100vh}
/* 已登录（带侧栏外壳）访问首页：入口区在**可用内容区内**居中——不设 100vh，不与侧栏/顶栏冲突；
   用列向 flex + 子项 margin:auto，避免把同级 .pagehead（页头）一起参与居中而整体右移（T118） */
body:has(.entry)>.layout>.main>.content{display:flex;flex-direction:column}
body:has(.entry)>.layout>.main>.content>.entry{margin:auto}
.entry{width:100%;max-width:760px}
.entry .grid{margin:0}
@media(max-width:860px){
  body:has(.login form[action="/login"])>.content{padding:0}
  .login-card{flex-direction:column;border-radius:0;box-shadow:none;min-height:100vh}
  .login-brand{padding:var(--s6) var(--s4)}
  .login-brand .tagline{margin-top:var(--s3);font-size:var(--fs14)}
  .login-brand .foot{margin-top:var(--s4);padding-top:0}
  .login-form{width:100%;flex:1 1 auto;padding:var(--s6) var(--s4) var(--s4);justify-content:center}
}
/* ===== 提示条（lyear 风格：浅底 + 状态色左边框 + 图标 + 一行文案）===== */
.alert{display:flex;align-items:flex-start;gap:var(--s2);padding:10px var(--s3);margin-bottom:var(--s3);border-left:3px solid var(--info);border-radius:var(--r-ctl);background:var(--info-bg);color:var(--info-fg);font-size:var(--fs13);word-break:break-word;overflow-wrap:anywhere}
.alert .ic{font-weight:700;line-height:1.4;flex:0 0 auto}
.alert>span{min-width:0}
.alert-danger{border-left-color:var(--danger);background:var(--danger-bg);color:var(--danger-fg)}
.alert-ok{border-left-color:var(--success);background:var(--success-bg);color:var(--success-fg)}
.alert-warn{border-left-color:var(--warning);background:var(--warning-bg);color:var(--warning-fg)}
.link{color:var(--brand);font-size:var(--fs12)}
/* ===== 卡片 ===== */
.card{background:#fff;border:1px solid var(--bd);border-radius:var(--r-card);box-shadow:var(--sh-card);margin-bottom:var(--s4);overflow:hidden}
.card>.hd,.card>form>.hd{display:flex;align-items:center;justify-content:space-between;gap:var(--s2);padding:var(--s3) var(--s4);border-bottom:1px solid var(--bd);font-weight:600;font-size:var(--fs14)}
.card>.bd,.card>form>.bd{padding:14px var(--s4)}
.card>.ft,.card>form>.ft{display:flex;align-items:center;justify-content:flex-end;gap:var(--s2);padding:var(--s3) var(--s4);border-top:1px solid var(--bd);background:var(--g50)}
.hero{background:linear-gradient(135deg,#0f172a,#1e3a8a);color:#fff;border-radius:var(--r-card);padding:var(--s4) var(--s4)}
.hero .t{font-size:15px;font-weight:700}.hero .s{font-size:12.5px;color:#c7d2fe;margin-top:6px}
/* ===== 表格 ===== */
.tbl-wrap{overflow:auto}
table{width:100%;border-collapse:collapse;font-size:var(--fs13)}
th{background:var(--g50);color:var(--g700);text-align:left;font-weight:600;padding:10px var(--s3);border-bottom:1px solid var(--bd);white-space:nowrap}
td{padding:10px var(--s3);border-bottom:1px solid var(--line);color:var(--g700);white-space:nowrap}
tbody tr:nth-child(even) td{background:#fcfcfd}
tbody tr:hover td{background:var(--g50)}
td.nums,th.nums{text-align:right;font-family:var(--mono);letter-spacing:.02em}
/* 单元格内行内表单（编辑/启停/核销/查看）不换行：否则 <form> 独占一行把行高撑成两行（款号管理观感异常主因） */
td form,td form.row-flex{display:inline-flex;align-items:center;gap:var(--s2);vertical-align:middle;margin:0}
td .btn{vertical-align:middle}
.diffrow{display:flex;align-items:center;justify-content:space-between;padding:10px var(--s3);border-bottom:1px solid var(--line);font-size:var(--fs13)}
/* ===== 徽章 / 标签 ===== */
.badge{display:inline-flex;align-items:center;gap:5px;padding:2px 9px;border-radius:var(--r-pill);font-size:var(--fs12);font-weight:600;line-height:20px;white-space:nowrap}
.b-brand{background:var(--brand-bg);color:var(--brand-fg)}
.b-ok{background:var(--success-bg);color:var(--success-fg)}
.b-warn{background:var(--warning-bg);color:var(--warning-fg)}
.b-danger{background:var(--danger-bg);color:var(--danger-fg)}
.b-info{background:var(--info-bg);color:var(--info-fg)}
.b-purple{background:var(--purple-bg);color:var(--purple-fg)}
.b-gray{background:var(--gray-bg);color:var(--gray-fg)}
/* T271（V5-16）：平台方语义四色 —— 未处理灰 / 生产过程中蓝 / 完成紫 / 出货绿（+ 异常红，沿用 .b-danger）。
   .b-blue / .b-green 是语义色类；.b-brand/.b-ok/.b-warn/.b-info 仍供能力标记（需称重/必填/第 N 道等）使用。 */
.b-blue{background:var(--info-bg);color:var(--info-fg)}
.b-green{background:var(--success-bg);color:var(--success-fg)}
/* V5 4.2：袋状态「一状态一色」调色板 —— **全局唯一映射源**（状态码 → st-* 见 core/fmt.php::st_slug()；颜色只在这里定义）。
   42 个互不相同的色相：18 个已知生命周期状态码各占一个，动态工序名（织造/定型/包装/… 用户自建工艺）按名字哈希落到 h0..h23。
   `.badge.st-*` 比 `.b-*` 更具体 ⇒ 覆盖既有色调；未登记/非状态徽章（「第 N 道」等）继续用 .b-* 七色调，不受影响。
   三端一致：平台端 pp_badge() / 工厂端 fx_badge() / 手机端都走 core/fmt.php::status_badge() 的同一套类名。 */
.badge.st-placed{--st-bg:var(--gray-bg);--st-fg:var(--gray-fg)}
.badge.st-confirmed{--st-bg:var(--info-bg);--st-fg:var(--info-fg)}
.badge.st-scheduled{--st-bg:var(--info-bg);--st-fg:var(--info-fg)}
.badge.st-in_production{--st-bg:var(--info-bg);--st-fg:var(--info-fg)}
.badge.st-completed{--st-bg:var(--purple-bg);--st-fg:var(--purple-fg)}
.badge.st-shipped{--st-bg:var(--success-bg);--st-fg:var(--success-fg)}
.badge.st-received{--st-bg:var(--success-bg);--st-fg:var(--success-fg)}
.badge.st-closed{--st-bg:var(--success-bg);--st-fg:var(--success-fg)}
.badge.st-cancelled{--st-bg:var(--gray-bg);--st-fg:var(--gray-fg)}
.badge.st-diff_hold{--st-bg:var(--danger-bg);--st-fg:var(--danger-fg)}
.badge.st-pending{--st-bg:var(--gray-bg);--st-fg:var(--gray-fg)}
.badge.st-in_progress{--st-bg:var(--info-bg);--st-fg:var(--info-fg)}
.badge.st-diff{--st-bg:var(--danger-bg);--st-fg:var(--danger-fg)}
.badge.st-voided{--st-bg:var(--gray-bg);--st-fg:var(--gray-fg)}
.badge.st-draft{--st-bg:var(--gray-bg);--st-fg:var(--gray-fg)}
.badge.st-sealed{--st-bg:var(--info-bg);--st-fg:var(--info-fg)}
.badge.st-active{--st-bg:var(--success-bg);--st-fg:var(--success-fg)}
.badge.st-disabled{--st-bg:var(--gray-bg);--st-fg:var(--gray-fg)}
.badge.st-h0{--st-bg:var(--info-bg);--st-fg:var(--info-fg)}
.badge.st-h1{--st-bg:var(--info-bg);--st-fg:var(--info-fg)}
.badge.st-h2{--st-bg:var(--info-bg);--st-fg:var(--info-fg)}
.badge.st-h3{--st-bg:var(--info-bg);--st-fg:var(--info-fg)}
.badge.st-h4{--st-bg:var(--info-bg);--st-fg:var(--info-fg)}
.badge.st-h5{--st-bg:var(--info-bg);--st-fg:var(--info-fg)}
.badge.st-h6{--st-bg:var(--info-bg);--st-fg:var(--info-fg)}
.badge.st-h7{--st-bg:var(--info-bg);--st-fg:var(--info-fg)}
.badge.st-h8{--st-bg:var(--info-bg);--st-fg:var(--info-fg)}
.badge.st-h9{--st-bg:var(--info-bg);--st-fg:var(--info-fg)}
.badge.st-h10{--st-bg:var(--info-bg);--st-fg:var(--info-fg)}
.badge.st-h11{--st-bg:var(--info-bg);--st-fg:var(--info-fg)}
.badge.st-h12{--st-bg:var(--info-bg);--st-fg:var(--info-fg)}
.badge.st-h13{--st-bg:var(--info-bg);--st-fg:var(--info-fg)}
.badge.st-h14{--st-bg:var(--info-bg);--st-fg:var(--info-fg)}
.badge.st-h15{--st-bg:var(--info-bg);--st-fg:var(--info-fg)}
.badge.st-h16{--st-bg:var(--info-bg);--st-fg:var(--info-fg)}
.badge.st-h17{--st-bg:var(--info-bg);--st-fg:var(--info-fg)}
.badge.st-h18{--st-bg:var(--info-bg);--st-fg:var(--info-fg)}
.badge.st-h19{--st-bg:var(--info-bg);--st-fg:var(--info-fg)}
.badge.st-h20{--st-bg:var(--info-bg);--st-fg:var(--info-fg)}
.badge.st-h21{--st-bg:var(--info-bg);--st-fg:var(--info-fg)}
.badge.st-h22{--st-bg:var(--info-bg);--st-fg:var(--info-fg)}
.badge.st-h23{--st-bg:var(--info-bg);--st-fg:var(--info-fg)}

.tag{display:inline-block;background:var(--g100);color:var(--g700);border-radius:6px;padding:2px var(--s2);font-size:var(--fs12);margin-right:6px}
/* 可点击的「添加」入口（款号编辑·工艺候选）：白底 + 品牌色描边/文字，hover 品牌浅底；与只读灰 .tag 明确区分 */
.tag-add{display:inline-block;background:var(--white);color:var(--brand);border:1px solid var(--brand);border-radius:var(--r-pill);padding:3px 10px;font-size:var(--fs12);font-weight:600;font-family:inherit;line-height:1.5;margin:0 6px 6px 0;cursor:pointer}
.tag-add:hover,.tag-add:focus-visible{background:var(--brand50)}
.tag-add:focus-visible{outline:2px solid var(--brand);outline-offset:1px}
.tag-add:disabled{opacity:.5;cursor:not-allowed}
.pill{display:inline-flex;align-items:center;padding:3px 10px;border-radius:var(--r-pill);font-size:var(--fs12);background:var(--g100);color:var(--g700)}
/* ===== 按钮 ===== */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;border-radius:var(--r-ctl);padding:var(--s2) 14px;font-size:var(--fs13);font-weight:600;border:1px solid transparent;background:#fff;color:var(--g700);cursor:pointer;line-height:1.4}
.btn:hover{filter:brightness(.97)}
.btn[disabled],.btn.dis{opacity:.55;pointer-events:none}
.btn-p{background:var(--brand);color:#fff}.btn-p:hover{background:var(--brand5)}
.btn-s{border-color:var(--g300);color:var(--g700)}
.btn-d{background:var(--danger);color:#fff}
.btn-t{background:transparent;color:var(--brand);padding:6px 8px}
.btn-sm{padding:5px 10px;font-size:var(--fs12)}
.btn-block{width:100%}
.btn-grp{display:inline-flex;gap:6px}
/* ===== 表单 ===== */
.f{display:flex;flex-direction:column;gap:6px;margin-bottom:var(--s3)}
.f>label{font-size:12.5px;color:var(--g700);font-weight:600}
.f>label i{color:var(--danger);font-style:normal;margin-left:3px}
.inp,select.inp,textarea.inp{border:1px solid var(--g300);border-radius:var(--r-ctl);padding:var(--s2) 10px;font-size:var(--fs13);line-height:1.4;color:var(--g700);background:#fff;font-family:inherit;width:100%}
.inp:focus{outline:0;border-color:var(--brand);box-shadow:0 0 0 3px var(--brand50)}
.inp::placeholder{color:#9ca3af}
.inp.err{border-color:var(--danger)}
.err-msg{font-size:var(--fs12);color:var(--danger)}
.hint{font-size:11.5px;color:var(--g500)}
.f-inline{display:flex;gap:var(--s3);flex-wrap:wrap}.f-inline>.f{flex:1;min-width:180px}
.chk{display:flex;align-items:center;gap:6px;font-size:var(--fs13);color:var(--g700)}
.desc{font-size:var(--fs12);color:var(--g500)}
/* ===== 筛选栏 / 分页 ===== */
.filters{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-bottom:14px}
.filters .inp{padding:7px 10px;width:auto;min-width:150px}
.pager{display:flex;align-items:center;justify-content:flex-end;gap:var(--s2);padding:var(--s3) 0 0}
.pager a,.pager span{min-width:32px;height:32px;padding:0 var(--s2);display:inline-flex;align-items:center;justify-content:center;border:1px solid var(--bd);border-radius:var(--r-ctl);background:#fff;font-size:var(--fs13);color:var(--g700)}
.pager .on{background:var(--brand);border-color:var(--brand);color:#fff}
.pager .dis{opacity:.5;pointer-events:none}
.pager .info{border:0;background:none;color:var(--g500)}
/* T266（V5 4.1）：页码输入跳转（平台端 pp_pager 与工厂端 fx_pager 同一套 HTML） */
.pager .pg-jump{display:inline-flex;align-items:center;gap:var(--s2);margin-left:var(--s2)}
.pager .pg-jump .pg-lbl{padding:0;border:0;background:none;color:var(--g500)}
.pager .pg-jump .pg-inp{width:64px;height:32px;text-align:center}
/* ===== 状态 tabs（选中项用其状态色下划线）===== */
.tabs{display:flex;align-items:center;gap:22px;border-bottom:1px solid var(--bd);padding:0 var(--s1);background:#fff;overflow-x:auto;overflow-y:hidden}   /* T124：overflow-x:auto 会把 overflow-y 计算成 auto → 出现竖滚动条，显式 hidden */
.tab{--c:var(--brand);position:relative;padding:var(--s3) 2px;font-size:13.5px;color:var(--g500);cursor:pointer;white-space:nowrap}
.tab[data-color=brand]{--c:var(--brand)}.tab[data-color=success]{--c:var(--success)}
.tab[data-color=warning]{--c:var(--warning)}.tab[data-color=danger]{--c:var(--danger)}
.tab[data-color=info]{--c:var(--info)}.tab[data-color=purple]{--c:var(--purple)}
.tab[data-color=muted]{--c:var(--g400)}   /* T271：语义「未处理」的页签下划线（灰） */
.tab:hover{color:var(--g900)}
.tab.on{color:var(--c);font-weight:700}
.tab.on::after{content:"";position:absolute;left:0;right:0;bottom:-1px;height:2px;background:var(--c);border-radius:2px}
.tab .cnt{color:var(--g500);font-weight:400}.tab.on .cnt{color:var(--c)}
/* ===== SKU 列表（填一行自动新增一行）===== */
.sku{border:1px solid var(--bd);border-radius:var(--r-ctl);overflow:hidden}
.sku .row{display:flex;align-items:center;border-top:1px solid var(--line)}
.sku .hdrow{display:flex;background:var(--g50);font-size:12.5px;color:var(--g700);font-weight:600}
.sku .c{padding:8px var(--s3);flex:1;min-width:0;overflow:hidden}
.sku .c.w1{flex:1.4}.sku .c.act{flex:0 0 72px;text-align:right}
.sku .row input,.sku .row select{border:0;background:transparent;width:100%;font-size:var(--fs13);font-family:inherit;color:var(--g900);padding:6px 0;outline:0}
.sku .row input:focus{background:var(--brand50);border-radius:4px;padding:6px 4px}
.sku .add{padding:10px var(--s3);border-top:1px dashed var(--g300);color:var(--brand);font-size:12.5px}
/* SKU 图片列（T113/T165 平台方 1.2②；T240/V4 1.1）：**无图** = 虚线方框 + 「＋」（点框选图）；
   **有图** = 框内缩略图（点图 = 放大查看）+ 悬停底部「更换」入口（选图/换图）+ 右上角圆形 ×（移除） */
.skupic{display:flex;flex-wrap:wrap;align-items:center;gap:6px var(--s2);min-width:0}
.skupic .chk{flex:0 0 auto;white-space:nowrap}
.skupic .shot{position:relative;width:64px;height:64px;flex:0 0 64px;border:1.5px dashed var(--g300);border-radius:var(--r-ctl);background:var(--g50);color:var(--g500);display:flex;align-items:center;justify-content:center;overflow:hidden}
.skupic .shot.has{border-style:solid;border-color:var(--bd);background:#fff}
.skupic .shot .ph{font-size:24px;line-height:1}
.skupic .shot img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;cursor:pointer}   /* V6-19/t311：点图片=直接选新图（放大只保留款号详情弹窗） */
/* T240（V4 1.1②）：file 输入只铺在「选图 / 更换」入口上（不再盖住整张图）—— 有图时点图 = 放大，点底部「更换」才选图 */
.skupic .shot-add{position:absolute;inset:0;z-index:1;display:flex;align-items:center;justify-content:center;border-radius:var(--r-ctl);cursor:pointer}
.skupic .shot-add input[type=file]{position:absolute;inset:0;width:100%;height:100%;opacity:0;cursor:pointer}
.skupic .shot.has .shot-add{inset:0;height:auto;background:none;color:transparent;font-size:0;opacity:1}   /* V6-19/t311：无「更换」文字与可见层，input 盖住整格 ⇒ 点图片直接选新图 */
.skupic .shot.has .shot-add .ph{display:none}
.skupic .shot.has .shot-add::after{content:none}   /* V6-19/t311：去掉「更换」文字 */
.skupic .shot.has:hover .shot-add{opacity:1}   /* 覆盖层恒显（透明），悬停语义由 .rm 承担 */
/* T240（V4 1.1①）：「移除」= 圆形 ×（默认隐藏，鼠标移到图片上才显示；触控端常显；勾选待删时常显并转红） */
.skupic .shotwrap{position:relative;display:inline-block}
.skupic .rm{position:absolute;top:-8px;right:-8px;z-index:2;width:20px;height:20px;border-radius:50%;background:rgba(17,24,39,.72);color:#fff;display:flex;align-items:center;justify-content:center;font-size:14px;line-height:1;cursor:pointer;opacity:0;transition:opacity .15s}
.skupic .rm[hidden]{display:none}
.skupic .rm input{position:absolute;inset:0;margin:0;opacity:0;cursor:pointer}
.skupic .shotwrap:hover .rm,.skupic .rm:has(input:checked){opacity:1}
.skupic .rm:has(input:checked){background:var(--danger)}
@media(hover:none){.skupic .rm,.skupic .shot.has .shot-add{opacity:1}}
/* ≤768px：SKU 行换行（每格约半行宽），图片列整行到行首，勾选与缩略图同行不溢出 */
@media(max-width:768px){
  .sku .row,.sku .hdrow{flex-wrap:wrap}
  .sku .c,.sku .c.w1{flex:1 1 45%}
  .sku .c.act{flex:1 1 100%;text-align:left}
  .sku .c:has(.skupic),.sku .hdrow>.c:nth-child(5){flex:1 1 100%}
}
/* ===== 单图上传 ===== */
.upload{position:relative;width:220px;height:150px;border:1.5px dashed var(--g300);border-radius:var(--r-card);background:var(--g50);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;color:var(--g500);font-size:12.5px;cursor:pointer;overflow:hidden;text-align:center;padding:var(--s2)}
.upload.has{border-style:solid;border-color:var(--bd);background:#fff}
.upload img.thumb{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.upload .del{position:absolute;top:6px;right:6px;z-index:2;background:rgba(17,24,39,.6);color:#fff;border:0;border-radius:var(--r-pill);padding:2px 8px;font-size:11px;cursor:pointer}
.upload .ph{font-size:20px;line-height:1}
/* ===== 数量输入（− / 数字 / +，默认值可带入）===== */
.qty{display:flex;align-items:center;gap:var(--s3);border:1.5px solid var(--brand);border-radius:var(--r-card);padding:10px var(--s4);background:#fff}
.qty .lab{font-size:12.5px;color:var(--g700);font-weight:600}
.qty .hint{font-size:11.5px;color:var(--g500);margin-top:4px}
.qty .step{margin-left:auto;display:flex;align-items:center;gap:var(--s2)}
.qty .step button{width:40px;height:40px;border-radius:var(--r-ctl);border:1px solid var(--g300);background:#fff;font-size:var(--fs20);line-height:1;color:var(--g700);cursor:pointer}
.qty .step button:hover{border-color:var(--brand);color:var(--brand)}
.qty input{width:96px;height:40px;text-align:center;font-size:var(--fs18);font-weight:700;color:var(--brand);border:1px solid var(--bd);border-radius:var(--r-ctl);font-family:var(--mono)}
.qty .val{font-size:26px;font-weight:700;color:var(--brand)}
.qty .unit{font-size:var(--fs13);color:var(--g500)}
/* ===== 弹层 / Toast / 空态 ===== */
.modal{position:fixed;inset:0;z-index:var(--z-modal);display:none;overflow:auto}
.modal.open{display:block}
/* 弹层内遮罩用层内 0，白卡 1：修「遮罩压住白卡 → 弹层发灰」（平台方反馈） */
.modal .mask{position:absolute;inset:0;z-index:var(--z-modal-mask);display:block}
.modal .box{position:relative;z-index:var(--z-modal-box);width:min(560px,92vw);max-height:84vh;overflow:auto;margin:8vh auto var(--s4);box-shadow:var(--sh-pop)}
.modal .box .hd{position:sticky;top:0;z-index:1;background:#fff}
/* T124/T143：弹层右上角关闭按钮（T122 注入 .hd 内的 .x）——无边框、与标题同行右对齐、hover 变色。
   必须用**后代**选择器：真实页面里 .hd 常位于 <form> 内（.modal > .card.box > form > .hd），
   直接子选择器 >.hd 会漏匹配（T143 线上实测 .x 仍是默认按钮 23.8×21 / cursor:default / border:outset）。
   只命中「.hd 内」的 .x，不误伤弹层其它区域。 */
.modal .card.box .hd .x,.modal .box .hd .x{border:0;background:transparent;color:var(--g500);flex:0 0 auto;margin-left:auto;width:28px;height:28px;padding:0;border-radius:var(--r-ctl);cursor:pointer;font-family:inherit;font-size:18px;line-height:1;display:inline-flex;align-items:center;justify-content:center}
.modal .card.box .hd .x:hover,.modal .box .hd .x:hover,.modal .card.box .hd .x:focus-visible{background:var(--g100);color:var(--g900)}
.modal .box .ft,.modal .box>.ft{position:sticky;bottom:0;z-index:1;background:#fff}
.modal .f-inline{display:block}          /* 弹层内字段统一单列 */
.modal .bd>.f:last-child{margin-bottom:0}
.modal .row-flex{flex-wrap:wrap}
/* T140：订单创建「添加产品」弹窗（#mdlAddSku）——6 列结果行需要更宽口径；列宽舒展 + 名称列省略号 */
#mdlAddSku .box{width:min(880px,94vw)}
#mdlAddSku .sku .c{min-width:88px}
#mdlAddSku .sku .c.w1{flex:1.8;min-width:200px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
#mdlAddSku .sku .act{flex:0 0 64px}
/* T165（1.2③④⑤⑥）：勾选列移到最左（无数量列）、检索按钮蓝色且与输入框同一行 */
#mdlAddSku .row-flex>.inp{flex:1 1 180px;width:auto}
#mdlAddSku .sku .c.pick{flex:0 0 52px;text-align:center}
@media(max-width:768px){
  #mdlAddSku .sku .c,#mdlAddSku .sku .c.w1{flex:1 1 45%;min-width:0}   /* 窄屏回到 2 列一行、可折行，不撑破 */
  #mdlAddSku .sku .c.act{flex:1 1 100%}
  #mdlAddSku .sku .c.pick{flex:0 0 52px}
}
/* T240（V4 1.2①）：弹窗结果行「图片」列 —— 有图显示缩略图（点图放大），无图留空 */
#mdlAddSku .sku .c.thumb{flex:0 0 40px;min-width:40px;padding-left:0;padding-right:0;text-align:center}
#mdlAddSku .sku .c.thumb img{width:32px;height:32px;object-fit:cover;border-radius:4px;cursor:zoom-in;vertical-align:middle}
/* T240（V4 1.1② / 1.2①）：**通用图片放大层**（原生实现，无第三方库）—— 点任意 [data-zoom] 元素全屏预览，点遮罩 / Esc 关闭；
   三个位置（款号编辑 SKU 图、下单弹窗 SKU 图、其它缩略图）共用这一处，风格一致；触控同样可用（点图放大、点遮罩关闭） */

/* T263（V5 3.2①③④）：发货扫码页 —— 开单态状态行 / 已加入袋列表（按袋移除）/ 未开单门控视觉。
   列表自身滚动（max-height:30vh）⇒ 页面不滚，保住手机端「一屏」口径；数量/时间用 tabular-nums 对齐。 */
.m-ship-state{margin:8px 0 0;padding:8px 10px;border-radius:var(--r-ctl);background:var(--success-bg);color:var(--success-fg);font-size:13px;line-height:1.35}
.m-ship-state.is-locked{background:var(--warning-bg);color:var(--warning-fg)}
.m-ship-bags{max-height:26vh;overflow:auto;-webkit-overflow-scrolling:touch}   /* 26vh：360×640 也一屏（列表自身滚，页面不滚） */
.m-ship-row{display:flex;align-items:center;gap:8px;padding:7px 0;border-bottom:1px solid var(--bd)}
.m-ship-row:last-child{border-bottom:0}
.m-ship-row .c1{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:600;font-variant-numeric:tabular-nums}
.m-ship-row .c2{flex:0 0 auto;color:var(--g700);font-size:13px;font-variant-numeric:tabular-nums}
.m-ship-row .c3{flex:0 0 auto;color:var(--g500);font-size:12px;font-variant-numeric:tabular-nums}
.m-ship-rm{flex:0 0 auto}
.m-ship-empty{margin:6px 0;color:var(--g500);font-size:13px}
.m-ship-empty[hidden]{display:none}
.m-ship-sum{margin:8px 0 0;font-weight:600;font-size:13px;color:var(--g900);font-variant-numeric:tabular-nums}
.m-ship-sum[hidden]{display:none}
[data-ship-locked="1"] .m-grp,[data-ship-locked="1"] .m-btn-scan{opacity:.55}
/* T267（V5 1.4③/2.2③/2.3①）：袋详情弹窗内的订单节点横向时间轴容器 —— 与主内容分段；空容器自动收起（服务端没渲染又没行数据时不占位）。 */
[data-bag-timeline]{border-top:1px solid var(--bd);padding-top:var(--s2)}
[data-bag-timeline]:empty{display:none}
/* T263：发货页不显示共享的「结果卡」（提示一律走居中弹层 + 语音；三个表单也带 data-m-nodetail）——
   结果卡一出现就多 ~70-114px，会把 360×640 的「一屏不滚动」顶破。 */
[data-ship]+[data-m-result]{display:none}
.imgzoom{position:fixed;inset:0;z-index:calc(var(--z-toast) + 10);display:none;align-items:center;justify-content:center;background:rgba(17,24,39,.72)}
.imgzoom.open{display:flex}
.imgzoom .mask{position:absolute;inset:0;display:block;background:transparent}
.imgzoom img{position:relative;max-width:92vw;max-height:88vh;border-radius:var(--r-card);background:var(--white);box-shadow:var(--sh-card)}
/* ===== 可搜索下拉（T165 平台方 1.2⑥，自研 select2 风格，不引第三方库）===== */
/* T221（V3 1.5①）：可搜索下拉所在卡片不得裁切 —— .card 的 overflow:hidden 会把绝对定位的候选列表切掉/压到下一张卡下面。
   排除弹层卡片（.card.box）：弹层靠 .modal .box 的 overflow:auto 做 max-height:84vh 滚动，改 visible 会让长内容无法滚动 */
.card:not(.box):has(.pick){overflow:visible}
.pick{position:relative}
.pick .pick-list{position:absolute;left:0;right:0;top:calc(100% + 4px);z-index:var(--z-dropdown);max-height:240px;overflow:auto;background:#fff;border:1px solid var(--bd);border-radius:var(--r-ctl);box-shadow:var(--sh-pop)}
.pick .pick-item{display:block;width:100%;text-align:left;border:0;background:#fff;padding:8px 10px;font:inherit;font-size:var(--fs13);color:var(--g900);cursor:pointer}
.pick .pick-item:hover,.pick .pick-item:focus-visible{background:var(--brand50);outline:0}
/* T175：工厂可搜索下拉 —— 候选必须真正能被隐藏（作者样式的 display:block 会盖掉 UA 的 [hidden]），
   并补「已选中 (.on)」与「键盘高亮 (.hl)」两态；展开时展示全部候选由 app.js 的 render(null) 负责。 */
.pick .pick-item[hidden]{display:none}
/* T221（V3 1.5⑤）：下单页明细数量输入框收窄（默认留空，由用户在行内录入；统计行复用既有 .hint/.right） */
td .qty-in{width:96px;text-align:right}
.pick .pick-item.on{background:var(--brand50);font-weight:600}
.pick .pick-item.hl{background:var(--g100)}
.pick .pick-item.hl.on{background:var(--brand50)}
/* T181：取景失败 / 引导类多行文案保留换行 + 诊断串（T189 起只写在**扫码层内** [data-m-layer-help]，页面内联提示区保持静态） */
.cam-help{white-space:pre-line}
.cam-help .btn{margin-top:6px}
/* T196/T222/T224：报工页「工序步骤条」= 效果稿 docs/mockups/m-scan-v2-f-full.html 的 .stepper/.st/.dot/.nm
   （类名加 m- 前缀避开既有类）：**圆点 + 序号 + 连接线**，工序名在圆点下方；横向滚动不换行。
   done 绿底白 ✓ / current 蓝底白序号 + 放大到 38px（1.19×）+ 蓝色加粗工序名 / todo 灰边白底灰序号；连接线 2px（done 段绿色）。
   T222：工序区无卡头、无「当前报工：xxx」大字行（T224：也不再出「当前报工」小标签 —— 效果稿没有）。
   T223（V3 3.1②）：工序条**左右居中** —— 外层仍是横向滚动容器，内层 inline-flex 在放得下时居中（text-align:center），
   放不下时按正常流从左起排、可横向滑动（不会出现「居中导致开头被裁掉、又滑不回去」的老问题）。 */
.m-stepper{overflow-x:auto;padding:5px 0;-webkit-overflow-scrolling:touch;text-align:center}   /* V5 3.1③：上下内边距相等（原 4/6 不对称，红框上下空白不一致） */
.m-stepper-in{display:inline-flex;vertical-align:top}
.m-st{flex:0 0 auto;min-width:76px;display:flex;flex-direction:column;align-items:center;gap:4px;position:relative}
.m-st-dot{width:32px;height:32px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:13px;font-weight:700;font-style:normal;background:#fff;border:1px solid var(--g300);color:var(--g500)}
.m-st-nm{font-size:13px;line-height:1;color:var(--g500);white-space:nowrap}   /* V5 3.1③：行高收到 1，文字上下留白对称、便于断言上下空白相等 */
.m-st.done .m-st-dot{background:var(--success);border-color:var(--success);color:#fff}
.m-st.done .m-st-nm{color:var(--g700)}
.m-st.todo .m-st-dot{background:#fff;border-color:var(--g300);color:var(--g500)}
.m-st.cur .m-st-dot{width:38px;height:38px;background:var(--brand);border-color:var(--brand);color:#fff;font-size:15px;box-shadow:0 0 0 4px var(--brand50)}
.m-st.cur .m-st-nm{font-size:14px;font-weight:700;color:var(--brand)}
.m-st:not(:last-child)::after{content:"";position:absolute;top:15px;left:calc(50% + 20px);width:calc(100% - 40px);height:2px;background:var(--g300)}
.m-st.done:not(:last-child)::after{background:var(--success)}
.m-st.cur:not(:last-child)::after{top:18px;left:calc(50% + 23px);width:calc(100% - 46px)}
/* T223（V3 3.1① ③）：报工页三态 —— [data-m-idle] 初始态只出「大圆扫码入口」（圆内两个字，无任何说明文字），
   扫码成功 / 手输袋码后切 [data-m-busy] 报工态，提交成功由 mStage(false) 回到初始态。
   圆 184px（单手可点、车间一眼可见），白字「扫码」30px；不占卡片（页面上只有这个圆）。
   T233（平台方 2026-09-25 追加「左右居中，上下居中」）：初始态那个圆改为在**可视内容区**（顶栏之下、视口底部之上）正中 ——
   用 flex 把「顶栏之下的剩余高度」撑满再居中，**不写死任何像素偏移**，故 375×700 / 375×812 / 390×844 / 375×600 等
   视口都稳（更矮的屏幕由 min-height 兜底，不会把圆挤出去）。这些规则只在**初始态可见时**生效
   （body:has(.m-idle:not([hidden]))），报工态与 /m/ship 的版式一字不动。
   .m-idle[hidden] 必须显式 display:none —— 作者样式的 display 会盖掉 UA 的 [hidden]（同 .m-fr[hidden] 口径）。 */
body:has(.m-idle:not([hidden])) .main{display:flex;flex-direction:column;min-height:100vh}
body:has(.m-idle:not([hidden])) .main>.content{display:flex;flex-direction:column;flex:1 1 auto;min-height:0}
body:has(.m-idle:not([hidden])) .m-compact{display:flex;flex-direction:column;flex:1 1 auto;min-height:0}
body:has(.m-idle:not([hidden])) [data-m-scan]{display:flex;flex-direction:column;flex:1 1 auto;min-height:0}   /* 大圆的直接父层也要进这条 flex 链 */
.m-idle{position:relative;flex:1 1 auto;display:flex;align-items:center;justify-content:center;min-height:184px}
/* T277（V6-1）：手机端「语音自检」入口 —— 绝对定位（不参与上面那条 flex 居中、不新增高度）：
   入口在初始态右下角，结果行浮在它上方；两者都 out-of-flow，故大圆仍居中、页面高度/一屏口径完全不变。 */
.m-vcheck{position:absolute;right:0;bottom:0}
.m-vcheck-out{position:absolute;right:0;bottom:30px;max-width:64vw;text-align:right;font-size:12px;line-height:1.35;color:var(--g700);background:var(--white);border:1px solid var(--bd);border-radius:var(--r-ctl);padding:5px 8px;box-shadow:0 2px 6px rgba(17,24,39,.08)}
.m-vcheck-out[hidden]{display:none}
.m-idle[hidden]{display:none}
.m-bigscan{width:184px;height:184px;border-radius:50%;background:var(--brand);color:#fff;border:0;cursor:pointer;font-family:inherit;font-size:30px;font-weight:700;letter-spacing:2px;display:inline-flex;align-items:center;justify-content:center;box-shadow:0 8px 24px rgba(37,99,235,.35)}
.m-bigscan:active{transform:scale(.98)}
/* T222（方案己）：② 产品信息 3×2 六项（无卡头、row-gap 8px）；③ 报工区「灰标签 + 白输入框」44px 输入组
   （未需要的必填项整行不渲染）；提交 48px。类名对应效果稿的 .info/.fr/.grp/.gtag/.gbody/.ginp/.unit/.btn-scan/.btn-lg，
   加 m- 前缀以避免与既有 .side .grp（侧栏分组）/ .pager .info（分页信息）相撞。 */
.m-info{display:grid;grid-template-columns:1fr 1fr;column-gap:10px;row-gap:8px}
.m-info .kv{min-width:0;height:32px;display:flex;flex-direction:column;justify-content:center;padding:0;border-bottom:0}
.m-info .kv .k{width:auto;flex:0 0 auto;font-size:var(--fs13);color:var(--g500);line-height:15px}
.m-info .kv .v{flex:1;font-size:var(--fs14);font-weight:600;color:var(--g900);line-height:15px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.m-fr,.scan-inline{display:flex;align-items:stretch;gap:8px}
.m-fr>.btn,.scan-inline>.btn{flex:0 0 76px;height:44px;padding:0;font-size:15px}
.m-grp{flex:1;min-width:0;display:flex;align-items:stretch;height:44px;border:1px solid var(--g300);border-radius:var(--r-ctl);overflow:hidden;background:#fff}
.m-gtag{flex:0 0 auto;min-width:76px;display:flex;align-items:center;justify-content:center;gap:2px;padding:0 10px;background:#f5f6f8;border-right:1px solid var(--g300);font-size:var(--fs13);color:var(--g700);white-space:nowrap}
.m-gtag i{color:var(--danger);font-style:normal}
.m-gbody{position:relative;flex:1;min-width:0;display:flex}
.m-ginp{flex:1;min-width:0;border:0;outline:0;padding:0 10px;font-size:var(--fs14);background:#fff;color:var(--g900);font-family:inherit}
.m-ginp.mono{font-family:var(--mono);letter-spacing:.02em}
.m-gbody .unit{position:absolute;right:9px;top:0;height:100%;display:flex;align-items:center;font-size:var(--fs13);color:var(--g500);pointer-events:none}
.m-gbody.has-unit .m-ginp{padding-right:34px}
.m-ginp::placeholder{color:#9ca3af}
.m-btn-scan{flex:0 0 76px;height:44px;padding:0;background:var(--brand);color:#fff;font-weight:600}
.m-btn-lg{height:48px;margin-top:6px}
.m-rescan{display:flex;flex-direction:column;gap:4px}
.m-fr[hidden],.m-rescan[hidden]{display:none}   /* 显式 [hidden]：上面的 display 会盖掉 UA 样式（同 .pick .pick-item[hidden] 的口径） */
/* T222：报工页要在一屏（375×700）内放下三块 —— 只对 /m/scan 生效（m_shell 的 $compact 包出 .m-compact，
   同时不渲染与顶栏重复的页头 h1）：卡片内边距 / 卡片间距 / 表单底距压到效果稿口径。 */
.m-compact .card{margin-bottom:8px}
.m-compact .card>.bd{padding:10px 12px}
.m-compact .f{margin-bottom:0}
/* T182：移动端专用全屏扫码层（docs/23 §11 分层：弹层 60 < 扫码层 70 < toast 80）
   T192：层内常显只有一句短提示 + 关闭 + 手输；取景框放大到车间可读；诊断行/引导默认 hidden */
.scanlayer{position:fixed;inset:0;z-index:70;background:#0b1220;display:flex;flex-direction:column;color:#fff}
.scanlayer[hidden]{display:none}                       /* 必须显式：上面的 display:flex 会盖掉 UA 的 [hidden] */
.scanlayer-stage{position:relative;flex:1;overflow:hidden}
.scanlayer-video{position:absolute;inset:0}
.scanlayer-video video{width:100%;height:100%;object-fit:cover;display:block}
/* T192：取景框尽量占满（车间对照）+ 短提示放大到一眼可读 */
.scanlayer-frame{position:absolute;left:50%;top:50%;width:min(84vw,360px);height:min(84vw,360px);transform:translate(-50%,-50%);border:3px solid rgba(255,255,255,.9);border-radius:16px;box-shadow:0 0 0 9999px rgba(0,0,0,.28);pointer-events:none}
.scanlayer-top{display:flex;align-items:center;justify-content:space-between;gap:var(--s2);flex-wrap:wrap;padding:12px 14px;background:rgba(0,0,0,.55)}   /* T192：诊断行（调试模式）换行独占一行，不挤压短提示 */
.scanlayer-tip{font-size:18px;font-weight:600;line-height:1.45;letter-spacing:.5px;min-width:0;flex:1;word-break:break-word}
.scanlayer-live{order:3;flex:1 1 100%;min-width:0;font-size:11.5px;line-height:1.5;color:#93c5fd;word-break:break-all}   /* T192：**仅调试模式**（?mdebug=1 / localStorage.m_debug=1）显示 */
.scanlayer-bottom{padding:14px;background:rgba(0,0,0,.55);display:flex;flex-direction:column;gap:8px}
.scanlayer-help{margin:0;font-size:14px;line-height:1.6;color:#e2e8f0;white-space:pre-line}
.scanlayer-help .btn{margin-top:6px}
.scanlayer-close{min-width:44px;min-height:44px}        /* 触控目标 ≥44px */
.scanlayer-bottom .btn{min-height:44px}
.toasts{position:fixed;top:var(--s4);right:var(--s4);z-index:var(--z-toast);display:flex;flex-direction:column;gap:var(--s2)}
.toast{min-width:200px;max-width:360px;padding:10px var(--s3);border-radius:var(--r-ctl);background:#fff;box-shadow:var(--sh-pop);font-size:var(--fs13);color:var(--g900);border-left:3px solid var(--brand);transition:opacity .25s,transform .25s}
.toast.ok{border-left-color:var(--success)}.toast.err{border-left-color:var(--danger)}
.toast.warn{border-left-color:var(--warning)}.toast.info{border-left-color:var(--info)}
.toast.out{opacity:0;transform:translateX(12px)}
/* T163：**页面正中、自动关闭**的提示弹层 —— app.js 的 toast() 与服务端 flash（[data-flash]）共用；
   信息/成功 1.8s、错误 2.6s 自动关闭，错误带手动关闭按钮（.x）。 */
.flash{position:fixed;inset:0;z-index:var(--z-toast);display:flex;align-items:center;justify-content:center;pointer-events:none;transition:opacity .2s}
.flash .flash-box{pointer-events:auto;display:flex;align-items:center;gap:var(--s2);max-width:min(440px,90vw);background:#fff;border:1px solid var(--bd);border-radius:var(--r-card);box-shadow:var(--sh-pop);padding:14px var(--s4);font-size:var(--fs14);color:var(--g900)}
.flash .flash-ic{flex:0 0 auto;width:22px;height:22px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;color:#fff;font-size:13px;font-weight:700;background:var(--success)}
.flash-danger .flash-ic{background:var(--danger)}.flash-warning .flash-ic{background:var(--warning)}
.flash .flash-msg{min-width:0;word-break:break-word}
.flash .x{flex:0 0 auto;border:0;background:transparent;color:var(--g500);width:24px;height:24px;padding:0;border-radius:var(--r-ctl);cursor:pointer;font-size:18px;line-height:1;display:inline-flex;align-items:center;justify-content:center;font-family:inherit}
.flash .x:hover{background:var(--g100);color:var(--g900)}
.flash.out{opacity:0}
/* T163-3.5 / T222：移动端「扫码」按钮与袋码输入组同一行（同一行 = 输入组 44px 与按钮 44px 等高，规则与 .m-fr 共用） */
.empty{padding:40px var(--s4);text-align:center;color:var(--g500);font-size:var(--fs13)}
.empty .ic{font-size:26px;opacity:.6;margin-bottom:var(--s2)}
/* ===== 指标卡 / 步骤条 / 时间线 / KV / 进度 ===== */
.stats{display:flex;gap:var(--s4);flex-wrap:wrap;margin-bottom:var(--s4)}   /* T124：KPI 行与后续卡片之间补间距，与 .grid 行间距一致 */
.stat{background:#fff;border:1px solid var(--bd);border-radius:var(--r-card);padding:14px var(--s4);flex:1;min-width:160px}
.stat .k{font-size:12.5px;color:var(--g500);margin-bottom:6px}
.stat .v{font-size:26px;font-weight:700;letter-spacing:.5px}
.stat .s{font-size:var(--fs12);color:var(--g500);margin-top:4px}
.steps{display:flex;align-items:flex-start;gap:var(--s2);margin:0 0 var(--s4);flex-wrap:nowrap;overflow-x:auto;overflow-y:visible;-webkit-overflow-scrolling:touch;padding-bottom:2px}
/* T278（V6-2，平台方 2026-09-28 晚）：轴一律**单行不换行** —— flex-wrap:nowrap + 节点/连接线 flex:0 0 auto（放不下就整条横向滚动，绝不折行）。
   .steps 原本 flex-wrap:wrap 会让第 5 个「发货」节点掉到第二行（平台方截图红框），这里统一改掉；订单轴与工序轴共用这一处。 */
.steps>.st,.steps>.bar{flex:0 0 auto}
/* T278：**工序轴**（data-timeline=route）节点 = <i>序号</i> + 工序名 + 一行小字（状态 · 数量 · 时间）——节点内两行，节点本身仍在同一行。 */
.steps .st .n{display:inline-flex;flex-direction:column;line-height:1.25;text-align:left}
.steps .st .s{font-size:11px;font-style:normal;font-weight:400;color:var(--g500)}
.steps .st.done .s{color:var(--success)}
.steps .st{display:flex;align-items:center;gap:6px;font-size:12.5px;color:var(--g500);white-space:nowrap}
.steps .st i{width:22px;height:22px;border-radius:50%;background:var(--g100);color:var(--g500);display:flex;align-items:center;justify-content:center;font-style:normal;font-size:var(--fs12);font-weight:700}
.steps .st.on{color:var(--brand);font-weight:700}.steps .st.on i{background:var(--brand);color:#fff}
.steps .st.done{color:var(--success)}.steps .st.done i{background:var(--success);color:#fff}
.steps .bar{align-self:center;flex:0 0 auto;width:22px;height:2px;background:var(--g100);min-width:22px}
/* T294（V6-7，平台方 2026-09-28 晚）：`.tl` **恢复为纵向列表** —— 平台仪表盘「最近动作」、订单详情「审计时间线」
   等都用它渲染，t278 把全局 .tl 改成 flex/nowrap 是把它们误伤成一整行（带横向滚动条）。
   t278 想要的「单行不换行」收窄为**专用修饰类 `.tl-hx`**（工厂袋详情工序轴，服务端渲染处加类）
   与**袋详情弹窗作用域** `#bag-detail .tl`（两端弹窗里由 app.js 注入的工序轴，平台侧 _ui 不在本任务范围，用作用域等效覆盖）。 */
.tl{list-style:none;margin:0;padding:0}
.tl li{position:relative;padding:0 0 14px 18px;border-left:2px solid var(--g100);font-size:12.5px;color:var(--g700)}
.tl li::before{content:"";position:absolute;left:-6px;top:3px;width:10px;height:10px;border-radius:50%;background:var(--brand)}
.tl li .t{color:var(--g500);font-size:11.5px}
/* T294/T278：专用修饰类（横向单行）—— flex-wrap:nowrap + 节点 flex:0 0 auto + 整条 overflow-x:auto ⇒ 绝不折行，放不下就横向滚动。 */
.tl-hx, .tl-hx .tl, #bag-detail .tl{display:flex;flex-wrap:nowrap;align-items:flex-start;gap:14px;overflow-x:auto;overflow-y:visible;-webkit-overflow-scrolling:touch;padding-bottom:2px}
.tl-hx>li, .tl-hx .tl>li, #bag-detail .tl>li{flex:0 0 auto;display:flex;flex-direction:column;gap:2px;padding:0 0 0 12px;border-left:0;white-space:nowrap}
.tl-hx>li::before, .tl-hx .tl>li::before, #bag-detail .tl>li::before{left:0;top:4px;width:8px;height:8px}
.tl-hx>li+li::after, .tl-hx .tl>li+li::after, #bag-detail .tl>li+li::after{content:"";position:absolute;left:-14px;top:7px;width:12px;height:2px;background:var(--g100)}
.tl-hx .s, .tl-hx .tl .s, #bag-detail .tl .s{display:inline-flex;align-items:center;gap:4px;font-size:11.5px;color:var(--g500)}
/* T224：袋流转详情弹窗的工序时间线（完成=品牌点、当前=蓝点放大、未报工=灰点）与送货单两块的段标题 */
.tl li.done::before{background:var(--brand)}
.tl li.cur::before{background:var(--brand);box-shadow:0 0 0 3px var(--brand50)}
.tl li.todo::before{background:var(--g300)}
.ship-sec{font-weight:600;font-size:var(--fs13);color:var(--g700);margin:0 0 6px}
.kv{display:flex;font-size:var(--fs13);padding:6px 0;border-bottom:1px dashed var(--g100)}
.kv:last-child{border-bottom:0}
.kv .k{width:110px;flex:0 0 110px;color:var(--g500)}.kv .v{flex:1;color:var(--g900);min-width:0}
.bar-mini{height:6px;border-radius:99px;background:var(--g100);overflow:hidden;width:120px}
.bar-mini i{display:block;height:100%;background:var(--brand)}
/* 进度条宽度：数据属性 → 类（页面零内联样式，F-12） */
.bar-mini[data-pct="0"] i{width:0}.bar-mini[data-pct="10"] i{width:10%}.bar-mini[data-pct="20"] i{width:20%}
.bar-mini[data-pct="30"] i{width:30%}.bar-mini[data-pct="40"] i{width:40%}.bar-mini[data-pct="50"] i{width:50%}
.bar-mini[data-pct="60"] i{width:60%}.bar-mini[data-pct="70"] i{width:70%}.bar-mini[data-pct="80"] i{width:80%}
.bar-mini[data-pct="90"] i{width:90%}.bar-mini[data-pct="100"] i{width:100%}
.crumbbar{border-bottom:1px solid var(--bd);margin:-4px 0 var(--s3)}
/* ===== 手机框（小程序稿 / 工厂端手机预览）===== */
.phones{display:flex;gap:28px;flex-wrap:wrap;padding:var(--s1) 0 0 var(--s1)}
.phone{width:390px;height:844px;background:#fff;border:9px solid var(--side);border-radius:34px;overflow:hidden;flex:0 0 390px;position:relative}
.phone .ph-top{height:44px;border-bottom:1px solid var(--g100);display:flex;align-items:center;justify-content:center;font-size:var(--fs14);font-weight:600}
.phone .ph-body{padding:var(--s3);height:calc(844px - 44px - 76px - 18px);overflow:auto}
.phone .ph-tab{height:76px;border-top:1px solid var(--g100);display:flex;align-items:center;justify-content:space-around;font-size:11px;color:var(--g500)}
.phone .ph-tab .it{display:flex;flex-direction:column;align-items:center;gap:4px}
.phone .ph-tab .it.on{color:var(--brand);font-weight:700}
.pcard{background:#fff;border:1px solid var(--bd);border-radius:12px;padding:var(--s3);margin-bottom:10px}
.ph-label{font-size:var(--fs12);color:var(--g500);margin:0 0 6px 4px}
.big{font-size:22px;font-weight:700}
/* ===== 打印 ===== */
/* T170：只读展示的缩略图（款号「查看」弹窗）不显示可点击光标；.chk 勾选列窄列。
   T202：勾选列此前被 .chk 的 display:flex 覆盖 —— 单元格不再是 table-cell，浏览器给它套匿名单元格，
   于是列宽虚胖（34px 里塞 13px 复选框，左右各留白）且与下一列之间浮出一条白色间隙（实测 7px）。
   这里显式还原 table-cell 并让列宽贴合复选框（width:1% + 右内边距归零），表头与数据行同规则对齐。 */
#product-detail .shot{cursor:zoom-in}   /* V5 2.4：款号详情弹窗的 SKU 图可点开放大（复用 #imgZoom；见 app.js 的 [data-zoom] 委托） */
.tbl-wrap th.chk,.tbl-wrap td.chk{display:table-cell;width:1%;padding-right:0;text-align:left;white-space:nowrap}
.tbl-wrap th.chk>input,.tbl-wrap td.chk>input{margin:0;vertical-align:middle}
@media print{
  .side,.topbar,.filters,.pager,.btn,.toasts,.no-print{display:none!important}
  body{background:#fff}.content{padding:0}.card{box-shadow:none;border:0}
}
/* ===== T241（V4 2.1①②/2.2③/2.4①）：看板折线 hover 详情 · 样式化二次确认框 ===== */
/* 图例：工序名 + 数值（替代旧的灰色说明文字；圆点颜色与折线一致，页面零内联样式） */
.lg{display:flex;flex-wrap:wrap;gap:6px 16px;margin:0 0 var(--s2);font-size:var(--fs13);color:var(--g900)}
.lg .lg-i{display:inline-flex;align-items:center;gap:6px}
.lg .lg-n{color:var(--g700);font-size:var(--fs12)}
.lg-dot{width:9px;height:9px;border-radius:50%;background:var(--g300);display:inline-block;flex:0 0 9px}
.lg-dot[data-c="0"]{background:#2563eb}
.lg-dot[data-c="1"]{background:#16a34a}
.lg-dot[data-c="2"]{background:#d97706}
.lg-dot[data-c="3"]{background:#7c3aed}
/* 折线 hover 详情：竖线 + 圆点（SVG 内，随 viewBox 缩放）+ 数值气泡（HTML，绝对定位） */
.chart{position:relative}
.chart svg{display:block;cursor:crosshair}
/* 二次确认框（自研；危险操作「确定」为红底 .btn-d，取消在左） */
.modal.cfm{z-index:calc(var(--z-modal) + 5)}
.modal.cfm .box{width:min(420px,92vw);margin-top:16vh}
.modal.cfm .hd{gap:var(--s2);justify-content:flex-start}
.modal.cfm .cfm-ic{width:22px;height:22px;border-radius:50%;background:var(--danger-bg);color:var(--danger-fg);display:inline-flex;align-items:center;justify-content:center;font-weight:700;flex:0 0 22px;font-size:var(--fs13)}
.modal.cfm .cfm-msg{margin:0;font-size:var(--fs14);color:var(--g900);line-height:1.6}
.modal.cfm .ft{background:var(--white)}


/* V6-13（t304，平台方 19:4x）：袋级合并轴节点 = **两行居中的块**（第一行 圆圈+名称+数量 / 第二行 时间），两行水平中心对齐；
   容器给横向滚动条留底部内边距（不压第二行文字）；行距/间距收紧，同屏更多节点；仍单行不换行（容器横向滚动）。 */
.steps[data-timeline=bag]{padding-bottom:8px;gap:var(--s1,8px)}
.steps[data-timeline=bag] .st{flex-direction:column;align-items:center;gap:0;line-height:1.15}
.steps[data-timeline=bag] .n1{display:inline-flex;align-items:center;gap:5px}
.steps[data-timeline=bag] .n1 b,.steps[data-timeline=bag] .nm{font-weight:700;font-style:normal}
.steps[data-timeline=bag] .q{font-style:normal;font-weight:400;color:var(--g700)}
.steps[data-timeline=bag] .n2{font-size:11px;color:var(--g500);margin-top:1px;white-space:nowrap}
.steps[data-timeline=bag] .bar{align-self:flex-start;margin-top:9px}

/* ===== V6-70（t360）安装向导：引导式交互样式 =====
   复用既有令牌（--success/--danger/--warning/--g*/--bd/--r-card/--s*/--fs*），不引框架、不写内联 style。
   结构：.wiz-steps 顶部进度条 · .wiz-card 状态卡 · .wiz-bar 固定底部操作条（大号主按钮 + 就近原因）。 */
.install{background:var(--g50);padding:var(--s4) var(--s3) 112px}   /* 底部留白给固定操作条：小屏不遮挡内容 */
.install-box{max-width:760px;margin:0 auto}
.install-h1{font-size:var(--fs20);margin:0 0 6px}
.install .card{margin-bottom:var(--s3)}
.install .bd{padding:var(--s4)}
.install .bd h2{font-size:var(--fs16);margin:0 0 8px}
.install .bd h3{font-size:var(--fs13);margin:14px 0 6px}
/* 顶部步骤进度条 */
.wiz-steps{list-style:none;display:flex;gap:6px;padding:0;margin:12px 0 6px;flex-wrap:wrap}
.wiz-steps li{flex:1;min-width:120px;display:flex;align-items:center;gap:8px;padding:8px 10px;border:1px solid var(--bd);border-radius:var(--r-ctl);background:var(--white);color:var(--g500);font-size:var(--fs13)}
.wiz-steps li b{width:22px;height:22px;flex:0 0 22px;display:flex;align-items:center;justify-content:center;border-radius:50%;background:var(--g50);color:var(--g700);font-size:12px}
.wiz-steps li[data-nav-state=on]{border-color:var(--g900);color:var(--g900);font-weight:600;box-shadow:0 1px 0 var(--bd)}
.wiz-steps li[data-nav-state=on] b{background:var(--g900);color:var(--white)}
.wiz-steps li[data-nav-state=done]{color:var(--success-fg);border-color:var(--success)}
.wiz-steps li[data-nav-state=done] b{background:var(--success-bg);color:var(--success-fg)}
.wiz-steps li[data-nav-state=done][data-step-nav]{cursor:pointer}
.wiz-now{margin:2px 0 0;font-size:var(--fs13);color:var(--g500)}
/* 状态卡（自检项一行一卡：✓绿 ✘红 未知灰；怎么修默认折叠） */
.wiz-cards{display:flex;flex-direction:column;gap:8px;padding:0;margin:0;list-style:none}
.wiz-card{display:flex;gap:10px;align-items:flex-start;border:1px solid var(--bd);border-left-width:4px;border-radius:var(--r-ctl);background:var(--white);padding:10px 12px;font-size:var(--fs13)}
.wiz-card[data-state=ok]{border-left-color:var(--success)}
.wiz-card[data-state=fail]{border-left-color:var(--danger)}
.wiz-card[data-state=pending],.wiz-card[data-state=unknown]{border-left-color:var(--warning)}
.wiz-badge{flex:0 0 18px;line-height:1.4;font-weight:700}
.wiz-badge.ok{color:var(--success-fg)}
.wiz-badge.no{color:var(--danger-fg)}
.wiz-lines{flex:1;display:flex;flex-direction:column;gap:2px}
.wiz-fix{margin-top:2px}
.wiz-fix summary{cursor:pointer;color:var(--g700);font-size:var(--fs13)}
.wiz-fix p{margin:6px 0 0;color:var(--g700)}
.wiz-fix code{background:var(--g50);padding:1px 4px;border-radius:4px}
/* 固定底部操作条 */
.wiz-bar{position:sticky;bottom:0;z-index:5;display:flex;align-items:center;gap:var(--s2);flex-wrap:wrap;margin:var(--s3) calc(-1 * var(--s4)) calc(-1 * var(--s4));padding:10px var(--s4);background:var(--white);border-top:1px solid var(--bd);border-radius:0 0 var(--r-card) var(--r-card)}
.wiz-bar .grow{flex:1}
.wiz-why{flex:0 0 100%;color:var(--danger-fg);background:var(--danger-bg);border-radius:var(--r-ctl);padding:6px 10px;font-size:var(--fs13)}
.wiz-main{padding:12px 22px;font-size:var(--fs16);font-weight:700;min-width:210px;justify-content:center}
.wiz-main[disabled]{opacity:.55}
/* 表单 / 说明 / 结果 */
.install-grid{display:grid;grid-template-columns:150px 1fr;gap:8px 12px;align-items:center;margin:8px 0 4px}
.install-grid label{font-size:var(--fs13);color:var(--g700)}
.install-grid input{width:100%;padding:9px 10px;border:1px solid var(--bd);border-radius:var(--r-ctl);font-size:var(--fs14);box-sizing:border-box;background:var(--white)}
.install-env{list-style:none;padding:0;margin:8px 0}
.install-actions{display:flex;gap:var(--s2);flex-wrap:wrap;margin:10px 0 0}
.install-pre{background:var(--g50);border:1px solid var(--bd);border-radius:var(--r-ctl);padding:10px 12px;font-size:12px;line-height:1.55;white-space:pre-wrap;word-break:break-all;margin:6px 0}
.install-warn{color:var(--danger-fg);background:var(--danger-bg);border-radius:var(--r-ctl);padding:8px 10px;font-size:var(--fs13);margin:8px 0 0}
.install-note{color:var(--warning-fg);background:var(--warning-bg);border-radius:var(--r-ctl);padding:8px 10px;font-size:var(--fs13);margin:8px 0 0}
.install-note.ok{color:var(--success-fg);background:var(--success-bg)}
.install-note.no{color:var(--danger-fg);background:var(--danger-bg)}
.install-steps{list-style:none;padding:0;margin:8px 0 0}
.install-steps li{padding:6px 0;font-size:var(--fs13);border-bottom:1px dashed var(--bd)}
.install-steps li.ok{color:var(--success-fg)}
.install-steps li.no{color:var(--danger-fg)}
.install-steps li.ing{color:var(--g700)}
.install-fixlist{border:1px solid var(--warning);border-radius:var(--r-ctl);padding:10px 12px;background:var(--warning-bg);margin:8px 0 12px}
@media (max-width:560px){
  .install{padding:var(--s3) 10px 132px}
  .wiz-steps li{min-width:calc(50% - 6px);font-size:12px}
  .install-grid{grid-template-columns:1fr}
  .wiz-bar{padding:8px 10px;gap:8px}
  .wiz-bar .btn,.wiz-main{width:100%;min-width:0;flex:1 1 100%}
  .wiz-bar .grow{display:none}
}

