/* Design-handoff token scale (main spec §8) layered onto the original app tokens: --accent /
   --accentD / --accentSoft / --mut keep their historical names (used everywhere); the ink/bg
   scales and state colours below are the handoff's names so new screens can use them 1:1. */
:root{--accent:#0d9488;--accentD:#0f766e;--accentSoft:#e9f6f3;--bg:#f8fafb;--ink:#16202b;--mut:#5b6770;--line:#e4e8ec;
  --accent-bg:#e8f5f2;--accent-border:#cfe8e2;
  --ink-2:#313c47;--ink-3:#7b8794;--ink-4:#97a1ac;--ink-5:#b9c1c9;
  --surface:#fff;--bg-2:#fbfcfd;--bg-3:#eef1f4;--chip:#f1f4f7;--line-2:#eef1f4;--border-input:#dbe0e5;
  --danger:#b3261e;--danger-bg:#fdecec;--ok:#197a48;--warn:#a5680c;--warn-bg:#fdf1dd;
  --shadow-menu:0 12px 32px rgba(16,24,40,.16);--shadow-modal:0 24px 60px rgba(16,24,40,.28)}
*{box-sizing:border-box}
html,body{margin:0;height:100%}
body{font-family:'IBM Plex Sans',system-ui,-apple-system,sans-serif;background:var(--bg);color:var(--ink);-webkit-font-smoothing:antialiased}
input,button,select{font-family:inherit}
button{cursor:pointer}
.mono{font-family:'IBM Plex Mono',monospace}
@keyframes spin{to{transform:rotate(360deg)}}
@keyframes toastIn{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}
@keyframes drawerIn{from{transform:translateX(28px);opacity:.55}to{transform:none;opacity:1}}
@keyframes modalIn{from{transform:translateY(14px) scale(.99);opacity:0}to{transform:none;opacity:1}}
::-webkit-scrollbar{width:11px;height:11px}
::-webkit-scrollbar-thumb{background:#d2d7dc;border-radius:7px;border:3px solid var(--bg)}

.app{height:100vh;display:flex;flex-direction:column;overflow:hidden}
/* 58px per the shell spec §8. position:relative + NO overflow rule — the nav/account menus are
   absolutely positioned inside it and must escape fully (spec §2.3: overflow:hidden here once hid
   every dropdown). */
header.top{height:58px;flex:none;display:flex;align-items:center;gap:16px;padding:0 18px;background:#fff;border-bottom:1px solid var(--line);position:relative;z-index:30}
.brand{display:flex;align-items:center;gap:9px}
.logo{width:26px;height:26px;border-radius:6px;background:var(--accent);display:flex;align-items:center;justify-content:center;color:#fff;font-weight:700;font-size:14px}
.brand .name{font-weight:600;font-size:14.5px}
.search{display:flex;align-items:center;gap:7px;flex:1;max-width:420px;background:#f3f5f6;border:1px solid var(--line);border-radius:8px;padding:0 10px;height:34px}
.search input{border:none;background:transparent;outline:none;font-size:13px;width:100%;color:var(--ink)}
.headspacer{flex:1}
/* Header utility cluster (shell spec §2.3): sync status → gear → account chip, right-aligned as
   one unit (the cluster owns margin-left:auto; .sync no longer does). */
.hutil{margin-left:auto;display:flex;align-items:center;gap:12px;flex:none}
.sync{display:flex;align-items:center;gap:8px;font-size:11.5px;color:var(--mut);white-space:nowrap}
.dot{width:7px;height:7px;border-radius:50%;background:#22a06b;display:inline-block}
.admingrp{position:relative;flex:none}
.gearbtn{height:34px;min-width:34px;padding:0 8px;border:none;background:transparent;border-radius:8px;display:flex;align-items:center;justify-content:center;gap:6px;color:var(--mut);flex:none;cursor:pointer}
.gearbtn:hover{background:var(--bg-3);color:var(--ink)}
.gearbtn.on{background:var(--accentSoft);color:var(--accentD)}
.gearbtn .gearlabel{font-size:12.5px;font-weight:600}
.gearbtn.unlocked{color:var(--ink-2)}
.gearbtn.unlocked.on{color:var(--accentD)}
/* Admin menu — right-aligned under the gear; three labelled sections + Lock footer (spec §3.3) */
.adminmenu{right:0;left:auto;min-width:210px}
.menusect{font-size:9.5px;font-weight:700;letter-spacing:.6px;color:var(--ink-4);padding:7px 10px 3px}
.menufoot{border-top:1px solid var(--line-2);margin-top:6px;padding-top:6px}
.lockbtn{width:100%;text-align:left;border:none;background:transparent;font-size:12.5px;font-weight:500;color:#b5344d;padding:8px 10px;border-radius:7px;cursor:pointer}
.lockbtn:hover{background:#fdecef}
/* Admin unlock dialog delta (reuses the .confirm modal look) */
.adminUnlock .lockglyph{width:38px;height:38px;border-radius:50%;background:var(--accent-bg);color:var(--accentD);display:flex;align-items:center;justify-content:center;margin-bottom:8px}
.fielderr.hide{display:none}
/* Role-mode header (design canvas role screens — base.html's role_title branch): the two-line
   role title/sub next to the logo, and the right-aligned station-chip cluster (chips reuse
   .stchip / .stchip.count). On phones the informational bits yield to the count chip. */
.roletitle{line-height:1.15}
.roletitle h1{font-size:16px;letter-spacing:-.3px}
.roletitle .rsub{font-size:11.5px;color:var(--mut);margin-top:2px}
.rolechips{display:flex;align-items:center;gap:8px;flex:none}
/* FV2-003's identity+logout userbox never had a layout rule — its block-level logout <form>
   dropped to a second line and overflowed the 54px bar on EVERY page. One inline flex row. */
/* Account chip + menu (design shell spec §5): avatar-initial chip toggling a right-aligned
   menu — header block (name + role line), Keyboard shortcuts, Change password, Sign out. */
.userbox{position:relative;display:flex;align-items:center;flex:none}
.userchip{display:flex;align-items:center;gap:7px;border:none;background:transparent;height:38px;padding:3px 8px 3px 4px;border-radius:20px;cursor:pointer;color:var(--mut)}
.userchip:hover{background:var(--bg-3)}
.avatar{width:27px;height:27px;border-radius:50%;background:var(--accent-bg);color:var(--accentD);display:flex;align-items:center;justify-content:center;font-size:12.5px;font-weight:700;flex:none}
.userbox .uname{font-size:12px;font-weight:500;color:#4b5763;white-space:nowrap;max-width:160px;overflow:hidden;text-overflow:ellipsis}
.usermenu{right:0;left:auto;min-width:238px}
.umhead{padding:8px 10px 9px;border-bottom:1px solid var(--line-2);margin-bottom:5px}
.umname{font-size:13px;font-weight:600;color:var(--ink)}
.umrole{font-size:11px;font-weight:600;color:var(--ink-3);margin-top:2px;letter-spacing:.3px}
.umitem{display:flex;align-items:center;gap:8px;width:100%;text-align:left;border:none;background:transparent;padding:8px 10px;border-radius:7px;font-size:12.5px;font-weight:500;color:var(--ink);cursor:pointer}
.umitem:hover{background:var(--bg-3)}
.umitem.danger{color:#b5344d}
.umitem.danger:hover{background:#fdecef}
.umitem .keycap{margin-left:auto}
.usermenu form{margin:0;border-top:1px solid var(--line-2);margin-top:5px;padding-top:5px}
/* Keycap glyph (shell spec §8): mono, raised bottom edge — used in menus, hint strips, the guide */
.keycap{font-family:'IBM Plex Mono',monospace;font-size:11.5px;font-weight:700;line-height:1;color:var(--ink-2);background:var(--chip);border:1px solid #e0e5ea;border-bottom-width:2px;border-radius:5px;padding:3px 6px;display:inline-block}
/* `?` shortcuts guide (shell spec §7.3) — modal cheat sheet of the active screen's keys */
.kbguide{min-width:400px;max-width:520px}
.kbgroups{margin-top:4px;max-height:56vh;overflow-y:auto}
.kbgtitle{font-size:10px;font-weight:700;letter-spacing:.6px;text-transform:uppercase;color:var(--ink-4);margin:12px 0 5px}
.kbgroup:first-child .kbgtitle{margin-top:2px}
.kbrow{display:flex;align-items:center;gap:14px;padding:5px 0}
.kbcaps{flex:none;min-width:96px;display:flex;align-items:center;gap:4px}
.kbjoin{color:var(--ink-5);font-size:11px;margin:0 1px}
.kblabel{font-size:12.5px;color:var(--ink-2)}
.kbfoot{font-size:11.5px;color:var(--ink-3);margin-top:10px;padding-top:9px;border-top:1px solid var(--line-2)}
/* Inline KEYS hint strip (shell spec §7.3.3) — roomy screens print their top keys in place */
.keystrip{display:flex;align-items:center;gap:9px;flex-wrap:wrap;font-size:11.5px;color:var(--ink-3)}
.keystrip .klabel{font-size:10px;font-weight:700;letter-spacing:.5px;color:var(--ink-4)}
.keystrip a{color:var(--accentD);text-decoration:none;font-weight:600;cursor:pointer}
@media (max-width:640px){
  .roletitle .rsub{display:none}
  .rolechips .stchip:not(.count){display:none}
}

/* OFS-132 / ADR-023 D-6 — default-on catch-all content container. Wraps `{% block content %}`
   inside the fixed-height, overflow-hidden `.app` shell (below the 54px header). Opinion-free:
   zero color/border/padding/background/gap, so every page's own top-level element keeps its exact
   margins. THE CONTRACT: pages with their own scroll root (`.tablewrap`/`.formpage`/`.adminstack`/
   `.stpage`/`.pickscroll`/`#root`/`.board`/`#pickSection`/`#matchBoard`, each `flex:1;min-height:0`
   + its own `overflow:auto`) bound inside this column and scroll INTERNALLY — per the flexbox spec a
   scroll container's `min-height:auto` resolves to 0, so `.content` sees no residual overflow and,
   because its own `overflow-y` is demand-driven `auto`, its bar never appears (no double scrollbar;
   sticky `<thead>`s keep the inner root as nearest scroll ancestor). Pages WITHOUT a scroll root are
   caught here and scroll instead of clipping against `.app{overflow:hidden}` — killing the recurring
   clipped-content bug class (OFS-028/OFS-060/BUG-124). `overflow-y` (not `overflow`) keeps the x axis
   visible so the wide `.orders` tables' own horizontal scroll inside `.tablewrap` is untouched. */
.content{flex:1;min-height:0;display:flex;flex-direction:column;overflow-y:auto}

.toolbar{flex:none;padding:15px 20px 0}
.toolrow{display:flex;align-items:flex-end;justify-content:space-between;gap:14px;flex-wrap:wrap;margin-bottom:13px}
h1{margin:0;font-size:18px;font-weight:600}
.sub{font-size:12px;color:var(--mut);margin-top:2px}
.btn{display:flex;align-items:center;gap:7px;border:1px solid #d6dce0;background:#fff;color:var(--ink);height:36px;padding:0 13px;border-radius:9px;font-size:12.5px;font-weight:500}
.btn.primary{background:var(--accentD);color:#fff;border:none;font-weight:600;box-shadow:0 1px 2px rgba(15,118,110,.25)}
.btn:disabled{opacity:.5;cursor:not-allowed}
.filters{display:flex;align-items:center;gap:14px;margin-bottom:12px;flex-wrap:wrap}
.fgroup{display:flex;align-items:center;gap:8px}
.flabel{font-size:10px;font-weight:700;letter-spacing:.5px;text-transform:uppercase;color:var(--mut)}
.fspacer{flex:1}
/* OFS-035 / DES-OFS-035 §2 — per-field inline validation message (no existing precedent;
   every other validation failure toasts, but an AC-mandated *inline* rejection needs a
   persistent, in-place message a 3.4s toast can't provide). Reuses the app's one error red. */
.fielderr{font-size:11px;color:#b42318;margin-top:3px;display:block}
.seg{display:flex;background:#fff;border:1px solid var(--line);border-radius:9px;padding:3px;gap:2px}
.seg button{border:none;background:transparent;color:#5b656d;font-size:12px;font-weight:500;padding:6px 12px;border-radius:6px;display:flex;align-items:center;gap:6px}
.seg button.on{background:var(--accentSoft);color:var(--accentD)}
.seg.chan button.on{background:var(--accent);color:#fff}
.seg .cnt{font-size:10.5px;font-weight:700;color:var(--mut)}
.seg button.on .cnt{color:var(--accentD)}
.daterange{display:flex;align-items:center;gap:6px;background:#fff;border:1px solid var(--line);border-radius:9px;height:34px;padding:0 10px}
.daterange.active{background:var(--accentSoft);border-color:#bfe6df}
.daterange input{border:none;outline:none;font-size:12px;color:var(--ink);background:transparent;font-family:inherit;width:120px}

.bulk{flex:none;margin:0 20px 12px;background:#0f2a27;color:#fff;border-radius:10px;display:flex;align-items:center;gap:12px;padding:10px 14px;flex-wrap:wrap}
.bulk b{font-size:12.5px}
.bulk .vline{width:1px;height:18px;background:rgba(255,255,255,.18)}
.bulk button{border:none;background:rgba(255,255,255,.14);color:#fff;font-size:12px;font-weight:600;padding:7px 12px;border-radius:7px}
.bulk button:disabled{background:rgba(255,255,255,.05);color:rgba(255,255,255,.35)}
.bulk .clear{margin-left:auto;background:transparent;color:rgba(255,255,255,.65)}

/* background-job progress bar (create shipment / sync all missing) */
/* The job bar is a STACK — one .jobcard per concurrent background job (push /
   parse / cancel / sync can run at once), so parallel jobs no longer clobber a
   single shared element. The container holds the cards; the card carries the look. */
.jobbar{flex:none;margin:0 20px 12px;display:flex;flex-direction:column;gap:9px}
.jobcard{background:#fff;border:1px solid var(--line);border-radius:10px;padding:11px 14px;box-shadow:0 1px 2px rgba(20,24,27,.04);animation:jobIn .2s ease}
@keyframes jobIn{from{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:none}}
.jobhead{display:flex;align-items:center;gap:10px;margin-bottom:9px}
.joblabel{font-size:12.5px;font-weight:600;color:var(--ink)}
.jobchips{display:flex;gap:6px;flex-wrap:wrap}
.jobchip{font-size:11px;font-weight:600;padding:2px 9px;border-radius:20px;background:#f0f2f4;color:#5b656d}
.jobchip.ok{background:var(--accentSoft);color:var(--accentD)}
.jobchip.ship{background:#eaf2ff;color:#256362}
.jobchip.err{background:#fdecec;color:#b42318}
.jobx{margin-left:auto;border:none;background:transparent;color:var(--mut);font-size:13px;line-height:1;padding:2px 4px;border-radius:5px}
.progress{height:8px;border-radius:6px;background:#eef0f2;overflow:hidden}
.progressfill{height:100%;width:0;border-radius:6px;background:linear-gradient(90deg,var(--accent),var(--accentD));transition:width .4s ease}
.progressfill.indet{width:35%;animation:indet 1.1s ease-in-out infinite}
@keyframes indet{0%{margin-left:-35%}100%{margin-left:100%}}
.results{margin-top:11px;border-top:1px solid var(--line);padding-top:10px;max-height:220px;overflow:auto}
.results .rrow{display:flex;gap:9px;font-size:12px;padding:5px 0;border-bottom:1px solid #f1f3f5}
.results .rrow:last-child{border-bottom:none}
.results .rid{font-family:'IBM Plex Mono',monospace;font-weight:600;color:var(--ink);flex:none}
.results .rmsg{color:#b42318}
.results .rempty{font-size:12px;color:var(--accentD)}
.pill.err{background:#fdecec;color:#b42318}

.tablewrap{flex:1;overflow:auto;margin:0 20px;background:#fff;border:1px solid var(--line);border-radius:10px 10px 0 0}
/* Single-column form/utility pages (e.g. Fulfil) that have no .tablewrap: the content
   column itself must own the vertical scroll. It is a direct child of the fixed-height,
   overflow-hidden .app, so without flex:1 + overflow-y:auto + min-height:0 a tall CSV
   preview / progress stack overflows and gets clipped with no way to scroll to it. */
.formpage{flex:1;min-height:0;overflow-y:auto;padding:0 24px 32px}
.formpage>.card{max-width:100vw}
/* List-admin pages that stack MULTIPLE toolbar+tablewrap+tablefoot sections (Stations, which now
   carries Stations + Operators [OFS-085] + Trays [OFS-087] = 3 sections). The shared shell fixes
   .app to height:100vh/overflow:hidden and every .tablewrap is flex:1 — fine for one or two, but
   three flex:1 boxes competing for one fixed budget each collapse to ~50px (barely their sticky
   <thead>), making row action buttons unreachable by a real mouse click and turning an empty
   section into an axe scrollable-region-focusable violation (BUG-124). Fix, scoped to this page
   only (other pages keep the shell contract): opt the content column OUT of "no page-scroll" — the
   .adminstack column itself scrolls, and each section is naturally sized (flex:none) with a bounded
   max-height so a long section keeps its own scroll + sticky header instead of squeezing siblings. */
.adminstack{flex:1;min-height:0;overflow-y:auto}
.adminstack .tablewrap{flex:none;max-height:52vh}
/* base table style — the default look for EVERY table (see CLAUDE.md "Table style").
   A bare <table> already renders as the standard data grid; `.orders` only adds the
   wide-grid min-width. Components that are deliberately not data grids (`.kv`,
   `.prev table`) override below. */
table{width:100%;border-collapse:collapse;font-size:12.5px}
table thead th{position:sticky;top:0;z-index:1;background:#fafbfc;text-align:left;padding:10px 12px;font-weight:600;color:#6b747b;font-size:11px;letter-spacing:.3px;box-shadow:inset 0 -1px 0 var(--line);white-space:nowrap}
table thead th.sortable{cursor:pointer;user-select:none}
table thead th.sortable:hover{color:var(--accentD);background:#f3f7f6}
table thead th.sortable.on{color:var(--accentD)}
.sarrow{display:inline-block;margin-left:4px;font-size:9px;width:8px}
table td{padding:9px 12px;border-bottom:1px solid #f0f2f4;vertical-align:middle}
table tbody tr.row{cursor:pointer;transition:background .1s}
table tbody tr.row:hover td{background:#fafdfc}
table tr.row.sel td{background:#f3fbf9}
table tbody tr:last-child td{border-bottom:none}
table.orders{min-width:1040px}

/* .kv — the sanctioned "two-column key/value summary" exception named above. Promoted
   VERBATIM from health.css (byte-identical values — do not diverge) so pages that load
   app.css but not health.css (e.g. order.html, ADR-001 D-2) can use the same pattern;
   health.css keeps its own copy unchanged for the dashboard (OFS-059). */
.kv{width:100%;border-collapse:collapse;font-size:13px}
.kv td{padding:11px 18px;border-bottom:1px solid #f0f2f4}
.kv td:last-child{text-align:right;font-weight:700}
.kv tr:last-child td{border-bottom:none}

/* table footer: count · pager · per-page */
.tablefoot{flex:none;margin:0 20px 16px;padding:9px 14px;background:#fff;border:1px solid var(--line);border-top:none;border-radius:0 0 10px 10px;display:flex;align-items:center;gap:14px;flex-wrap:wrap}
.showing{font-size:12px;color:var(--mut)}
.showing b{color:var(--ink);font-weight:600}
.pager{display:flex;align-items:center;gap:4px;margin:0 auto;flex-wrap:wrap}
.pg{min-width:30px;height:30px;padding:0 8px;border:1px solid var(--line);background:#fff;color:#5b656d;border-radius:8px;font-size:12.5px;font-weight:500;display:inline-flex;align-items:center;justify-content:center}
.pg.edge{font-size:15px;color:#6b747b}
.pg:hover:not(:disabled):not(.on){background:#f3f5f6;border-color:#d6dce0}
.pg.on{background:var(--accent);border-color:var(--accent);color:#fff;font-weight:600}
.pg:disabled{opacity:.4;cursor:not-allowed}
.pg.gap{border:none;background:transparent;color:#a3abb2;min-width:18px;pointer-events:none}
.goto{display:inline-flex;align-items:center;gap:6px;margin-left:8px;font-size:12px;color:var(--mut)}
.goto input{width:56px;height:30px;border:1px solid var(--line);border-radius:8px;padding:0 9px;font-size:12.5px;font-family:inherit;outline:none;color:var(--ink)}
.goto input:focus{border-color:var(--accent)}
.perpage-wrap{display:flex;align-items:center;gap:8px}
.perpage{display:flex;background:#f3f5f6;border:1px solid var(--line);border-radius:8px;padding:2px;gap:2px}
.perpage button{border:none;background:transparent;color:#6b747b;font-size:12px;font-weight:500;padding:5px 11px;border-radius:6px}
.perpage button.on{background:#fff;color:var(--accentD);font-weight:600;box-shadow:0 1px 2px rgba(0,0,0,.08)}
.chip{font-size:10.5px;font-weight:600;padding:1px 7px;border-radius:5px;display:inline-block}
.chip.mode{font-weight:500;background:#eef1f3;color:#6b747b}
/* OFS-012 (FR-8): the drawer "Details ↗" is a text-link affordance, NOT a metadata chip —
   no pill background/padding, teal link colour, underline on hover. Sits by .chip on purpose. */
.detaillink{font-size:10.5px;font-weight:600;color:var(--accentD);text-decoration:none}
.detaillink:hover{text-decoration:underline}
.pill{display:inline-flex;align-items:center;gap:5px;font-size:11px;font-weight:600;padding:3px 9px;border-radius:20px}
.pill .pd{width:6px;height:6px;border-radius:50%}
.iconbtn{flex:none;width:30px;height:30px;display:inline-flex;align-items:center;justify-content:center;border:1px solid #e0e4e7;background:#fff;color:#5b656d;border-radius:7px}
.iconbtn:disabled{opacity:.4;cursor:not-allowed}
.nextbtn{border:1px solid var(--accent);background:var(--accentSoft);color:var(--accentD);font-size:11.5px;font-weight:500;padding:6px 11px;border-radius:7px;white-space:nowrap}
.nextbtn.done{border-color:#eef0f2;background:#f6f8f8;color:var(--mut);cursor:default}
.actions{display:flex;align-items:center;justify-content:flex-end;gap:6px}
.muted{color:var(--mut)}
.empty{padding:54px;text-align:center;color:var(--mut);font-size:13px}
/* Shared table-area lifecycle states (OFS-009, ADR-002/DES §2). Loading + error render
   inside the existing .tablewrap shell; empty reuses .empty. Reuses @keyframes spin. */
.tstate{padding:54px;text-align:center;color:var(--mut);font-size:13px;display:flex;flex-direction:column;align-items:center;gap:10px}
.tspin{width:22px;height:22px;border:2.5px solid #e6e9ec;border-top-color:var(--accent);border-radius:50%;animation:spin .7s linear infinite}
.tstate.error{color:#b42318}
.tstate .tretry{margin-top:2px}
/* OFS-011: Process board (#board) is a flex row (.board{display:flex}); a lone .tstate/
   .empty state node would otherwise shrink to its own content width instead of filling
   the row like it does inside the block-level .tablewrap. Minimal position-only delta. */
.board > .tstate, .board > .empty{width:100%}
@media (prefers-reduced-motion:reduce){.tspin{animation:none}}
/* OFS-085 (DES-OFS-085 §7): a worklist row fades out after a confirmed advance/produce, then the
   table re-fetches. Matches .toast's exact .18s ease timing; reduced-motion resolves instantly. */
.rowdone{opacity:0;transition:opacity .18s ease}
@media (prefers-reduced-motion:reduce){.rowdone{transition:none}}
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}

.scrim{position:fixed;inset:0;background:rgba(15,25,30,.34);z-index:40;display:flex;justify-content:flex-end}
.lightbox{position:fixed;inset:0;background:rgba(15,25,30,.72);z-index:60;display:flex;align-items:center;justify-content:center;padding:32px;cursor:zoom-out}
.drawer{width:520px;max-width:94vw;height:100%;background:#f7f8f9;display:flex;flex-direction:column;animation:drawerIn .22s ease;box-shadow:-8px 0 30px rgba(0,0,0,.12)}
/* OPT-IN wider drawer. 520px leaves 448px of usable body, and an editor whose rows are a
   sentence of controls does not fit: the SKU sub-part row (name · colour · qty · custom ·
   coverage · route · open · remove) measured 487px of content in 448px, and the build-preview
   grid beside it had to wrap every part name over three lines. Opt-in rather than a new
   default so the drawers that are plain forms — Orders, Process — keep the narrower panel
   that suits them. */
.drawer.wide{width:min(880px,94vw)}
.drawer .dh{flex:none;padding:15px 20px;background:#fff;border-bottom:1px solid var(--line);display:flex;align-items:flex-start;justify-content:space-between}
.drawer .body{flex:1;overflow:auto;padding:16px 20px;display:flex;flex-direction:column;gap:13px}
.card{background:#fff;border:1px solid var(--line);border-radius:11px;padding:13px 15px}
.card h3{margin:0 0 8px;font-size:10.5px;font-weight:700;letter-spacing:.4px;color:var(--mut)}
/* OFS-066: picking-hub operation-screen launcher row (DES-OFS-066 §7 — the one new class; gap
   reused from the "small control gaps 5-9px" spacing scale). */
.launcherRow{display:flex;gap:9px;flex-wrap:wrap}
/* Work-Stations landing (.wsland scopes the /work-stations toolbar). The tile flex basis lives
   here (not inline on each anchor) so the common breakpoints can reflow it: ≥641px keeps the
   original auto-wrap 200px basis; ≤640px goes two-up with 44px touch-height tiles; ≤480px goes
   one-up full-width. Card side margins track the tighter toolbar padding on phones. */
.wsland .card{margin:0 20px 16px}
.launcherRow .btn{flex:1 1 200px;justify-content:center}
/* OFS-147 (DES-OFS-147) — Work-Stations landing workload tiles. .wstile reuses .card's exact
   geometry as a NEW class rather than nesting .card in .card (would double the border — same
   reasoning as DES-OFS-140 §3.2's .insight-card). .progress/.progressfill/.pill/.pd/.sync/.dot/
   .tretry are reused byte-identical; only the grid shell + this small type scale are new. This
   page STOPS using .launcherRow (introduces .wstiles instead) so settings.html/
   operation_screen.html's plain button-row layout is never put at risk by a delta here. */
.wstiles{display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));gap:9px}
.wstile{background:#fff;border:1px solid var(--line);border-radius:11px;padding:13px 15px;
  display:flex;flex-direction:column;gap:8px}
.wstile-top{display:flex;align-items:flex-start;justify-content:space-between;gap:8px;flex-wrap:wrap}
.wstile-label{margin:0;font-size:13.5px;font-weight:600;color:var(--ink)}
.wstile-nums{display:flex;gap:16px}
.wstile-nums .n{flex:1;min-width:0}
.wstile-nums .n b{display:block;font-family:'IBM Plex Mono',monospace;font-size:19px;font-weight:700;color:var(--ink);line-height:1.15}
.wstile-nums .n span{display:block;font-size:10px;font-weight:600;letter-spacing:.3px;text-transform:uppercase;color:var(--mut)}
.wstile-progress .pct{display:block;margin-top:4px;font-size:11px;color:var(--mut)}
.wstile-progress.idle .pct{font-weight:500}
.wstile-note{font-size:10.5px;color:var(--mut);margin:6px 0 0}
.wstile-detail{font-size:11.5px}
.wstile-detail summary{cursor:pointer;color:var(--accentD);font-weight:600;list-style:none;
  display:inline-flex;align-items:center;gap:4px;min-height:28px}
.wstile-detail summary::-webkit-details-marker{display:none}
.wstile-detail summary::before{content:'▸';font-size:9px;transition:transform .15s ease}
.wstile-detail[open] summary::before{transform:rotate(90deg)}
.wstile-detail summary:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:5px}
.wstile-benches{list-style:none;margin:6px 0 0;padding:0;display:flex;flex-direction:column;gap:5px;color:var(--ink-2)}
.wstile-benches li{display:flex;align-items:center;gap:6px;flex-wrap:wrap}
.wsland .wstile .btn{margin-top:auto;width:100%;justify-content:center}
.dot.stale{background:#92600a}
.dot.off{background:var(--mut)}
@media (prefers-reduced-motion:reduce){.wstile-detail summary::before{transition:none}}
@media (max-width:640px){
  .wsland{padding:12px 14px 0}
  .wsland .card{margin:0 14px 12px}
  .launcherRow{gap:8px}
  .launcherRow .btn{flex-basis:calc(50% - 4px);height:44px;font-size:13.5px}
  .wstiles{grid-template-columns:1fr 1fr;gap:8px}
  .wstile{padding:12px 13px}
  .wsland .wstile .btn{height:44px;font-size:13.5px}
}
@media (max-width:480px){
  .launcherRow .btn{flex-basis:100%;height:46px;font-size:14px}
  .wstiles{grid-template-columns:1fr}
  .wsland .wstile .btn{height:46px;font-size:14px}
}
/* OFS-089 tray scan-first shell (DES-OFS-087 §7) — NO new colours: every hex is cited from the
   existing palette (the .card border/radius, and the urgency/status chip pairs from worklist_ui.js
   / process.js). New selectors only recombine them. */
.scanbar{margin:0 20px 16px;background:#fff;border:1px solid var(--line);border-radius:10px;padding:15px}
.scanbar form{display:flex;gap:9px;align-items:flex-end;flex-wrap:wrap}
.scanbar #trayScan{flex:1 1 260px;font-size:15px}
.scanbar[aria-busy="true"] :is(input,button){opacity:.7;pointer-events:none}
.scanfb{display:none;margin-top:10px;padding:9px 12px;border-radius:8px;font-size:12.5px;font-weight:500}
.scanfb.show{display:block}
.scanfb.ok{background:#eef6f4;color:#0f766e}
.scanfb.warn,.scanfb.hold{background:#fef3d7;color:#92600a}
.scanfb.err{background:#fdecec;color:#b42318}
.chip.tray{background:#eef0fb;color:#3b46a8}
.x{border:none;background:#f0f2f4;width:30px;height:30px;border-radius:8px;color:#5b656d;font-size:16px;display:flex;align-items:center;justify-content:center}
.timeline{display:flex;align-items:center;justify-content:space-between}
.step{display:flex;flex-direction:column;align-items:center;flex:1;position:relative}
.step .mk{width:26px;height:26px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:700;z-index:1}
.step .ln{position:absolute;top:13px;left:50%;width:100%;height:2px;z-index:0}
.dfoot{flex:none;padding:13px 20px;background:#fff;border-top:1px solid var(--line);display:flex;gap:9px;align-items:center}

.modalScrim{position:fixed;inset:0;background:rgba(15,25,30,.38);z-index:50;display:flex;align-items:center;justify-content:center;padding:24px}
.modal{width:680px;max-width:96vw;max-height:90vh;background:#fff;border-radius:14px;display:flex;flex-direction:column;overflow:hidden;animation:modalIn .2s ease;box-shadow:0 20px 60px rgba(0,0,0,.25)}
.modal .mh{flex:none;padding:16px 20px;border-bottom:1px solid #eef0f2;display:flex;align-items:center;justify-content:space-between}
.drop{border:1.5px dashed #cdd3d8;border-radius:12px;background:repeating-linear-gradient(135deg,#fbfcfc,#fbfcfc 11px,#f4f6f7 11px,#f4f6f7 22px);padding:24px;text-align:center}
.prev{margin-top:16px;border:1px solid var(--line);border-radius:11px;overflow:hidden}
.prev table{width:100%;border-collapse:collapse;font-size:12px}
.prev th{background:#fafbfc;text-align:left;padding:7px 12px;font-weight:600;color:#6b747b;font-size:10.5px;border-bottom:1px solid #eef0f2}
.prev td{padding:7px 12px;border-top:1px solid #f0f2f4;border-bottom:none;vertical-align:top}
.mfoot{flex:none;display:flex;gap:9px;padding:13px 20px;border-top:1px solid #eef0f2}

/* Toast layer (shell spec §6): top-center pinned under the 58px header, stacking downward,
   newest on top. The CONTAINER is click-through (pointer-events:none) so the layer never
   blocks a control underneath — it used to sit bottom-right, on top of the Process action
   bar and the sticky bottom bars of Picking/QC/Boxing. Individual toasts stay interactive
   (hover-to-hold + click-to-dismiss). ui.js caps the stack at 4, dropping the oldest. */
.toasts{position:fixed;top:70px;left:50%;transform:translateX(-50%);z-index:80;display:flex;flex-direction:column;gap:9px;align-items:center;pointer-events:none}
.toasts .toast{pointer-events:auto}
.toast{display:flex;align-items:center;gap:10px;background:#15211f;color:#fff;padding:11px 15px;border-radius:10px;font-size:12.5px;box-shadow:0 8px 24px rgba(0,0,0,.22);animation:toastIn .22s ease;min-width:230px;max-width:360px;cursor:pointer;transition:opacity .18s ease,transform .18s ease}
.toast.out{opacity:0;transform:translateY(8px)}
.toast .ti{width:18px;height:18px;flex:none;border-radius:50%;display:flex;align-items:center;justify-content:center;color:#15211f;font-size:11px;font-weight:700}
.toast .tmsg{flex:1}
.toast .tx{color:rgba(255,255,255,.4);font-size:11px;line-height:1;flex:none}
.toast:hover .tx{color:rgba(255,255,255,.8)}

/* confirm dialog — styled replacement for window.confirm(), reuses .modalScrim */
.confirmScrim{z-index:70}
.confirmScrim.out{opacity:0;transition:opacity .15s ease}
.confirm{width:420px;max-width:94vw;background:#fff;border-radius:14px;padding:20px 22px 18px;box-shadow:0 20px 60px rgba(0,0,0,.28);animation:modalIn .18s ease}
.confirm-h{font-size:15px;font-weight:600;color:var(--ink)}
.confirm-b{font-size:13px;color:#5b656d;line-height:1.55;margin-top:8px}
.confirm-f{display:flex;justify-content:flex-end;gap:9px;margin-top:18px}
.confirm-f .btn{height:36px}
/* Start-processing filters — inline on the Shipments upload card (.filters deltas) */
.procfilters{margin:12px 0 12px;gap:8px 18px;align-items:flex-end}
.procgroup{display:flex;flex-direction:column;gap:5px}
.procgroup .seg{align-self:flex-start}
.procnop{display:flex;align-items:center;gap:7px;font-size:12.5px;color:#5b656d;cursor:pointer;padding-bottom:6px}
.pfdate{height:32px;max-width:150px;width:auto;margin-top:4px}
.shipactions .fsstate{margin-left:4px}
.btn.danger{background:#dc2626;color:#fff;border:none;font-weight:600;box-shadow:0 1px 2px rgba(220,38,38,.25)}
.btn:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.switch{display:flex;align-items:center;gap:7px;font-size:12px;color:#5b656d;background:#fff;border:1px solid #d6dce0;border-radius:9px;height:34px;padding:0 12px}
.switch input{accent-color:var(--accent)}
.hide{display:none}
/* Picking hub (OFS-034): the two section-filter containers carry an inline `display:flex`
   (pick sheet) which would out-specify the `.hide` class, so their `setSection` toggle would
   otherwise no-op. Scope `!important` to just these two ids so the inactive section's filters
   are truly hidden — the shell contract's "toggle .hide in lockstep with #hubSeg" (no app-wide
   effect; every other `.hide` user has no inline display to fight). */
#sectionFilters.hide,#workFilters.hide{display:none !important}
/* Picking hub + operation screens (/picking, /picking/<op>): the tablewrap/tablefoot shell is
   wrapped in a <section> (so the hub can swap Pick sheet <-> Worklists), but a plain-block
   section sizes to its content inside the overflow-hidden 100vh .app — long tables and the
   .tablefoot pager clip below the fold with no way to scroll. Relay the column flex through
   the section so .tablewrap's flex:1/overflow:auto contract works as on Orders/SKUs. */
#pickSection,#worklistSection,#opSection{flex:1;min-height:0;display:flex;flex-direction:column}
/* The id rules above out-specify `.hide` (same fight as #sectionFilters) — keep the hub's
   setSection toggle actually hiding the inactive section. */
#pickSection.hide,#worklistSection.hide{display:none !important}

/* top nav (Orders / SKUs / Parts) */
/* Top navigation — three audience groups + dropdowns (shell spec §2). The group button stays
   lit (.on) whenever the active screen belongs to it; menus escape the header (z above content). */
.topnav{display:flex;align-items:center;gap:2px;margin-left:4px}
.navgrp{position:relative}
.navbtn{display:flex;align-items:center;gap:5px;border:none;background:transparent;font-size:12.5px;font-weight:500;color:#5b656d;text-decoration:none;padding:7px 11px;border-radius:7px;white-space:nowrap}
.navbtn:hover{background:#f3f5f6;color:var(--ink)}
.navbtn.on{background:var(--accentSoft);color:var(--accentD);font-weight:600}
.navbtn svg{opacity:.55}
.navmenu{position:absolute;top:calc(100% + 7px);left:0;background:#fff;border:1px solid var(--line);border-radius:11px;box-shadow:var(--shadow-menu);padding:6px;min-width:238px;z-index:45;display:flex;flex-direction:column}
.navmenu.hide{display:none}   /* .hide alone loses to the display:flex above (same-specificity, later rule) */
.navmenu a{display:flex;align-items:center;gap:10px;padding:8px 10px;border-radius:7px;font-size:12.5px;font-weight:500;color:var(--ink);text-decoration:none}
.navmenu a:hover{background:var(--bg-3)}
.navmenu a[aria-current]{background:var(--accent-bg);color:var(--accentD);font-weight:600}
.navbadge{margin-left:auto;font-size:10.5px;font-weight:600;line-height:1;background:var(--accent-bg);color:var(--accentD);border:1px solid var(--accent-border);border-radius:20px;padding:3px 7px}

/* drawer form fields */
.field{display:flex;flex-direction:column;gap:5px;margin-bottom:11px}
.field:last-child{margin-bottom:0}
.field label{font-size:10.5px;font-weight:700;letter-spacing:.4px;color:var(--mut);text-transform:uppercase}
.fld{border:1px solid #d6dce0;border-radius:8px;height:34px;padding:0 10px;font-size:13px;color:var(--ink);background:#fff;outline:none;width:100%}
textarea.fld{height:auto;padding:8px 10px;resize:vertical;min-height:56px}
.fld:focus{border-color:var(--accent)}
.frow{display:flex;gap:9px}
.frow .field{flex:1}

/* parts editor (SKU drawer) */
.peditor{display:flex;flex-direction:column;gap:7px}
/* The sub-part editor row. `flex:2` is `flex:2 1 0%` — a basis of ZERO — so the name and
   colour inputs only ever got LEFTOVER space, while the chips beside them are flex:none and
   refuse to give any back. A long route name ("Eyemask Line · by rule") therefore starved both
   inputs down to unusable stubs. A flex row never overflows, it shrinks its children, so this
   never showed up as a clipped row — only as a form you cannot type in.
   Fixed three ways: the inputs get a real basis AND a min-width so they can never collapse;
   the row wraps, so on a narrow panel the chips drop to a second line instead of eating the
   fields; and the chips themselves truncate rather than push. */
.prow{display:flex;gap:6px;align-items:center;flex-wrap:wrap}
.prow .pn{flex:2 1 190px;min-width:150px}
.prow .pc{flex:1.4 1 130px;min-width:110px}
.prow .pq{flex:0 0 58px;width:58px}
.prow .cfc{display:flex;align-items:center;gap:4px;font-size:11px;color:#6b747b;white-space:nowrap}
.prow .cfc input{margin:0}
/* semantic tag editor (shared by Parts + SKU drawers) and read-only row chips */
.tagedit{display:flex;flex-wrap:wrap;gap:5px;align-items:center;border:1px solid var(--line);border-radius:8px;padding:6px;min-height:34px;cursor:text}
.tagedit .taginput{border:none;outline:none;flex:1;min-width:90px;font-size:12.5px;background:transparent}
.tag{display:inline-flex;align-items:center;gap:4px;font-size:11px;font-weight:500;background:#eef6f4;color:#0f766e;padding:2px 4px 2px 8px;border-radius:5px}
.tag [data-del]{border:none;background:transparent;font:inherit;cursor:pointer;font-weight:600;color:#0f766e;opacity:.6;padding:0 2px}
.tag [data-del]:hover{opacity:1}
.tag.ro{padding:1px 7px}
.tag.ro[data-tag]{cursor:pointer}
.tag.ro[data-tag]:hover{background:#dcefe9}
.tags-cell{display:flex;flex-wrap:wrap;gap:4px}
.prow .fld{height:32px;font-size:12.5px}
.prow .pjump{flex:none;text-decoration:none;color:var(--accentD);font-weight:700;font-size:13px;opacity:.65;padding:0 2px}
.prow .pjump:hover{opacity:1}
/* deep-link focus: briefly highlight the row a ?focus= link landed on */
@keyframes focusFlash{0%,55%{background:#fff7cc}100%{background:transparent}}
table tr.row.focusflash td{animation:focusFlash 2s ease}
.addrow{align-self:flex-start;border:1px dashed #cdd3d8;background:#fbfcfc;color:#5b656d;font-size:12px;font-weight:500;padding:6px 11px;border-radius:7px}
.delrow{border:none;background:#f0f2f4;color:#8a939b;width:30px;height:32px;border-radius:7px;flex:none;font-size:15px}

/* stock badge */
.stock{font-weight:600;font-variant-numeric:tabular-nums}
.stock.low{color:#c2410c}
.stock.zero{color:#b91c1c}
.thumb{width:34px;height:34px;border-radius:7px;object-fit:cover;border:1px solid var(--line);background:#fff}
.thumbbig{width:100%;max-height:220px;object-fit:contain;border-radius:9px;border:1px solid var(--line);background:#fff}
.ledger{font-size:12px;width:100%;border-collapse:collapse}
.ledger th{text-align:left;color:var(--mut);font-size:10px;font-weight:700;text-transform:uppercase;padding:5px 6px;border-bottom:1px solid var(--line)}
.ledger td{padding:5px 6px;border-bottom:1px solid #f0f2f4}
.delta-pos{color:#15803d;font-weight:600}.delta-neg{color:#b91c1c;font-weight:600}

/* (Tag-Matching v1/v2 board styles removed — the page is rebuilt to the design handoff in
   docs/manual-readme/tag-matching.md; see the "Tag-Matching v3" section below.) */
.dfoot .btn.primary{height:44px;font-size:15px}
.tstate.success{color:#0f766e}
.tstate.done{color:var(--accentD);font-size:15px}

/* ---- Station screens v2 (AICA OFS design canvas: Matching · Quality Check · Tag Creation) ----
   Shared primitives for the three floor-station surfaces. One vocabulary: a horizontal queue
   strip (.stq*), a focused white work panel (.fpanel with head/body/foot), per-part cards
   carrying component tiles (.comp item/tag/charm), shared progress bars (.pbar) and the QC
   bottom sheet (.sheet). Colors echo the design canvas; chrome tokens stay on :root vars. */
.stpage{flex:1;min-height:0;display:flex;flex-direction:column;gap:12px;padding:14px 20px 16px}
/* The section/panel classes below (.fpanel, .qcscanpanel, .stempty, .tbrow, …) all set their own
   display AFTER the global `.hide` rule, which would out-specify it (the same fight as
   #pickSection/#matchBoard above) — so every .hide toggle inside a station page wins here. */
.stpage .hide{display:none !important}
.stpage .pagehead{flex:none;display:flex;align-items:flex-end;justify-content:space-between;gap:16px;flex-wrap:wrap}
.stchip{display:inline-flex;align-items:center;gap:7px;font-size:12px;color:#4b5763;background:#fff;border:1px solid var(--line);border-radius:9px;padding:7px 12px}
.stchip .dot{background:var(--accent)}
.stchip.count{font-weight:600;color:var(--accentD);background:#e8f5f2;border-color:#cfe8e2}
.swdot{width:11px;height:11px;flex:none;border-radius:50%;box-shadow:inset 0 0 0 1px rgba(0,0,0,.14);display:inline-block}
.pbar{height:8px;background:#eef1f4;border-radius:20px;overflow:hidden}
.pbar>i{display:block;height:100%;border-radius:20px;background:var(--accent);transition:width .25s ease}
.prpill{font-size:11px;font-weight:600;background:#eef1f4;color:#57626e;padding:3px 9px;border-radius:20px}
.prpill.express{background:#fdecef;color:#b5344d}
.chanpill{font-size:11px;font-weight:600;background:#fdefdd;color:#8a5408;padding:3px 9px;border-radius:6px}
.idchip{display:inline-flex;align-items:center;gap:6px;font-family:'IBM Plex Mono',monospace;font-size:12px;font-weight:600;background:#eef1f4;color:#3a444e;padding:2px 9px;border-radius:6px}
.idchip .idk{font-size:8.5px;font-weight:700;letter-spacing:.5px;color:#5a6570;font-family:'IBM Plex Sans',system-ui,sans-serif}
.idchip.teal{background:#e8f5f2;color:var(--accentD);border:1px solid #cfe8e2}
.idchip.teal .idk{color:#2f6e63}
.colpill{display:inline-flex;align-items:center;gap:6px;font-size:11px;font-weight:600;background:#f3f5f7;color:#3a444e;padding:2px 9px 2px 7px;border-radius:20px}
/* OFS-111: mixed-colour order chip — up to 3 swatch dots (tight gap) + "+N" overflow, same pill */
.colpill.multi{gap:3px;padding:3px 9px}
.colpill .colmore{margin-left:2px;color:#5a6570}

/* queue strip (Matching top row) */
.stqueue{flex:none;display:flex;gap:11px;overflow-x:auto;padding:2px 2px 8px}
.stqcard{flex:none;width:246px;text-align:left;background:#fff;border:1.5px solid var(--line);box-shadow:0 1px 2px rgba(16,24,40,.04);border-radius:12px;padding:11px 13px;cursor:pointer;display:flex;flex-direction:column;gap:8px}
.stqcard.on{border-color:var(--accent);background:#f4fbfa;box-shadow:0 0 0 3px rgba(13,148,136,.13)}
.stqcard .qtop{display:flex;align-items:center;justify-content:space-between;gap:8px}
.stqcard .qeyebrow{font-size:9.5px;font-weight:700;letter-spacing:.6px;color:#5f6b77}
.stqcard.on .qeyebrow{color:var(--accentD)}
.stqcard .qid{font-family:'IBM Plex Mono',monospace;font-size:12px;font-weight:600;color:var(--ink)}
.stqcard .qtitle{font-size:12px;color:#4b5763;margin-top:3px;line-height:1.35;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.stqcard .qmeta{display:flex;align-items:center;gap:8px;font-size:11px;color:#626e7a}
.stqcard .qfoot{display:flex;align-items:center;justify-content:space-between;font-size:11px}
.stqcard .qtray{font-weight:600;color:#5f6b77}
.stqcard .qtray.has{color:var(--accentD)}
.stqcard .qnum{font-family:'IBM Plex Mono',monospace;font-weight:600;color:#57626e}

/* focused work panel */
.fpanel{flex:1;min-height:0;background:#fff;border:1px solid var(--line);border-radius:14px;box-shadow:0 1px 3px rgba(16,24,40,.05);display:flex;flex-direction:column;overflow:hidden;position:relative}
.fpanel .fhead{flex:none;padding:13px 20px;border-bottom:1px solid #eef1f4;display:flex;align-items:center;gap:14px;flex-wrap:wrap}
.fpanel .fhead .fsw{width:34px;height:34px;flex:none;border-radius:9px;box-shadow:inset 0 0 0 1px rgba(0,0,0,.14)}
.fpanel .fhead .fmain{min-width:0;flex:1}
.fpanel .fhead .frow{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.fpanel .fhead .ftitle{font-size:12.5px;color:#4b5763;margin-top:5px;line-height:1.4;display:-webkit-box;-webkit-line-clamp:1;-webkit-box-orient:vertical;overflow:hidden}
.fpanel .fbody{flex:1;min-height:0;overflow:auto;padding:13px 20px}
.fpanel .ffoot{flex:none;padding:11px 20px;border-top:1px solid #eef1f4;background:#fbfcfd;display:flex;align-items:center;gap:16px;flex-wrap:wrap}
.fpanel .ffoot .fprog{flex:1;min-width:200px}
.fpanel .ffoot .fprog .pbar{margin-top:6px;max-width:420px}
.sectionlabel{font-size:11px;font-weight:700;letter-spacing:.5px;color:#5d6875}
.stempty{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;background:#fff;border:1px dashed #cfd6dc;border-radius:14px;color:#68727c;font-size:13px}
.stempty .bigtick{width:52px;height:52px;border-radius:50%;background:#e8f5f2;color:var(--accentD);display:flex;align-items:center;justify-content:center;font-size:24px}
.stempty .bigmsg{font-size:15px;font-weight:600;color:var(--ink)}

/* Quality Check */
.qcscanpanel{flex:1;min-height:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:22px;background:#fff;border:1px solid var(--line);border-radius:14px}
.qcscanpanel .qhero{text-align:center}
.qcscanpanel .qheroicon{display:inline-flex;align-items:center;justify-content:center;width:60px;height:60px;border-radius:14px;background:#e8f5f2;color:var(--accentD);font-size:26px}
.qcscanpanel .qherotitle{font-size:16px;font-weight:600;color:var(--ink);margin-top:12px}
.qcscanpanel .qherosub{font-size:12.5px;color:#68727c;margin-top:3px}
.qcscanrow{display:flex;align-items:center;gap:9px;width:100%;max-width:460px;padding:0 16px}
.qcscanrow .scanfld{display:flex;align-items:center;gap:8px;flex:1;background:#fff;border:1.5px solid #cfd6dc;border-radius:10px;padding:0 13px;height:46px}
.qcscanrow .scanfld input{border:none;outline:none;flex:1;font-size:14px;color:var(--ink);background:transparent;font-family:'IBM Plex Mono',monospace}
.qcscanrow .btn.primary{height:46px;font-size:14px;border-radius:10px}
.qctraypick{width:100%;max-width:760px;padding:0 16px 6px}
.qctraypick .qtp-label{font-size:10.5px;font-weight:700;letter-spacing:.5px;color:#6b7681;margin-bottom:9px;text-align:center}
.qctraypick .qtp-row{display:flex;gap:10px;flex-wrap:wrap;justify-content:center}
.qtraychip{display:flex;align-items:center;gap:10px;text-align:left;background:#fff;border:1.5px solid var(--line);border-radius:11px;padding:10px 13px;cursor:pointer}
.qtraychip .qsw{width:26px;height:26px;flex:none;border-radius:7px;box-shadow:inset 0 0 0 1px rgba(0,0,0,.14)}
.qtraychip .qcode{display:block;font-family:'IBM Plex Mono',monospace;font-size:13px;font-weight:600;color:var(--accentD)}
.qtraychip .qsub{display:block;font-size:11px;color:#626e7a}
/* Verify toolbar + part rows (quality-checking.md §C2) — a part row shares the Tag-Matching
   .mrow anatomy (stacked pname/ptag/pcharm lines) plus a status eyebrow and a right 24×24
   keyboard-number badge that flips to ✓. .qccard/.ok are the pinned e2e contract classes. */
.qctoolbar{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:12px}
.btn.qcallbtn{color:var(--accentD);background:#e8f5f2;border:1px solid #cfe8e2;font-weight:600;font-size:12.5px;border-radius:8px;padding:6px 12px}
.qccard{display:flex;align-items:flex-start;gap:10px;width:100%;min-width:0;text-align:left;border:none;background:transparent;border-bottom:1px solid #f0f2f5;border-radius:0;padding:6px 2px 10px;cursor:pointer}
.qccard .qcmain{flex:1;min-width:0}
.qccard .pname{display:block;font-size:19px;font-weight:600;letter-spacing:-.3px;color:#16202b;line-height:1.25}
.qccard .ptag{display:block;font-size:17px;font-weight:700;line-height:1.35;overflow-wrap:anywhere}
.qccard .pcharm{display:block;font-size:15px;font-weight:600;color:#2f5bd4}
.qccard .pxtra{display:block;font-size:12px;color:#626e7a;margin-top:2px}
/* ---- The detail popover, shared by every workstation card (QC verify tiles, tag matching,
   tray assign, boxing, rework desk, sticker bench). The chosen-option lines, the ordered font
   and the full parsed one-liner are CONTEXT, not the thing being checked, and on the multi-up
   card grids they repeat near-identically on every card of an order, and the mono piece
   code + colour (the old .csub line, now .qcdrow.qcdcode) is read off the part, not the screen
   — five grey lines burying the two that ARE checked (the engraved text and the charm). So
   they come off the card FACE and ride this hover/focus reveal, the same contract as
   .taskerr .errpop.
   A card owning one carries .qcdhost (the positioning context) and wears a .qcdcue ⓘ on the
   face — without that cue the popover is undiscoverable. left:0/right:0 pins the popover to the
   card's own width, so it can never widen a grid column and pull a horizontal scrollbar out of
   the scroll container; it sits INSIDE the card, so hovering the popover keeps the card hovered
   (no flicker) and a stray click lands on the card it belongs to. */
.qcdhost{position:relative}
.qcdcue{margin-left:6px;font-weight:400;letter-spacing:0;color:#8a939b}
.qcdet{display:none;position:absolute;left:0;right:0;top:calc(100% - 5px);z-index:60;background:#fff;border:1px solid #e4e8ec;border-radius:10px;box-shadow:0 8px 24px rgba(16,24,40,.16);padding:8px 11px;text-align:left;cursor:default;white-space:normal}
.qcdhost:hover .qcdet,.qcdhost:focus-visible .qcdet,.qcdhost:focus-within .qcdet{display:block}
.qcdrow{display:block;font-size:11.5px;line-height:1.45;color:#4b5763;padding:3px 0;overflow-wrap:anywhere}
.qcdrow+.qcdrow{border-top:1px solid #f0f2f5}
.qcdrow.qcdcode{font-family:'IBM Plex Mono',monospace;font-size:11px;color:#68727c}
.qcdrow.qcdsum{color:#7b8794}
/* design's TAP TO VERIFY #c3cad1 fails AA on white — the repo's eyebrow tone instead */
.qccard .qcstatus{display:block;font-size:10px;font-weight:700;letter-spacing:.5px;color:#5f6b77;margin-top:4px}
.qccard.ok .qcstatus{color:var(--accentD)}
.qccard .qcbadge{flex:none;width:24px;height:24px;border-radius:7px;background:#f3f5f7;color:#68727c;display:flex;align-items:center;justify-content:center;font-family:'IBM Plex Mono',monospace;font-size:12px;font-weight:700}
.qccard.ok .qcbadge{background:var(--accent);color:#fff}
.qccard:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:6px}
/* All-parts-together gate status (#qcWait) — the amber "order incomplete" strip shown while
   parts of the loaded order are still converging on this station (QC passes the whole order
   as one set; Pass stays inert). Tones = the .scanfb.warn/hold pair (#92600a on #fef3d7). */
.qcwait{display:flex;gap:10px;align-items:flex-start;margin:0 0 12px;padding:10px 14px;border:1px solid #f0d9a8;background:#fef3d7;border-radius:10px;font-size:13px;color:#92600a}
.qcwait.hide{display:none}
.qcwait .qwicon{font-size:16px;line-height:1.3}
.qcwait .qwmain{flex:1;min-width:0}
.qcwait .qwlist{display:flex;flex-wrap:wrap;gap:4px 16px;margin-top:6px}
.qcwait .qwpart{display:inline-flex;align-items:center;gap:6px;font-weight:600;white-space:nowrap}
/* the rail / queue-chip "x/y here" badge for an incomplete order */
.qmiss{color:#92600a;font-weight:700}
/* QC tray-rail card delta: the scan handle is the TRAY code, in teal mono (rail spec row 1);
   row 2's order id ellipsizes so the parts count never wraps against it */
.qcard .qoid.tray{color:var(--accentD);font-size:11.5px}
.qcard.traycard .qr2 span{white-space:nowrap}
.qcard.traycard .qnum{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis}
/* Shipment QC workstation theme — the amber first-shipment-check identity, so the three QC
   workstations are unmistakable at a glance (workqc and Recheck + Label keep the design's teal;
   the pass / reject actions stay semantic green/red everywhere). Tones echo .chanpill
   (#8a5408 on amber tint), which already clears axe AA elsewhere. */
.tmpage.qcop-shipqc .railcount{color:#8a5408;background:#fdf3e0;border-color:#f0d9a8}
.tmpage.qcop-shipqc .qheroicon{color:#a5680c;background:#fdf3e0}
.tmpage.qcop-shipqc .qtraychip .qcode{color:#a5680c}
.tmpage.qcop-shipqc .qcard .qoid.tray{color:#a5680c}
.tmpage.qcop-shipqc .qcard.on{border-color:#c9942a;background:#fdfaf2;box-shadow:0 0 0 3px rgba(201,148,42,.16)}
.tmpage.qcop-shipqc .qcard.on .qeyebrow{color:#a5680c}
.tmpage.qcop-shipqc .idchip.teal{color:#8a5408;background:#fdf3e0;border-color:#f0d9a8}
.tmpage.qcop-shipqc .idchip.teal .idk{color:#a5680c}
.tmpage.qcop-shipqc .btn.qcallbtn{color:#8a5408;background:#fdf3e0;border-color:#f0d9a8}
.tmpage.qcop-shipqc .railtabs .seg button.on{background:#fdf3e0;color:#8a5408}
.tmpage.qcop-shipqc .railtabs .seg button.on .cnt{color:#8a5408}
.qcactions{display:flex;align-items:center;gap:10px}
.btn.reject{display:flex;align-items:center;gap:8px;background:#fff;color:#b5344d;border:1.5px solid #f0c9d1;height:44px;padding:0 20px;border-radius:10px;font-size:14px;font-weight:600}
.btn.pass{display:flex;align-items:center;gap:8px;background:#e6f4ec;color:#256b47;border:none;height:44px;padding:0 24px;border-radius:10px;font-size:14px;font-weight:600}
.btn.pass.ready{background:#12855b;color:#fff}
.btn .kb{font-size:11px;font-weight:500}

/* QC reject bottom sheet */
/* not-yet-armed primary action (e.g. Complete & release before all parts are in): solid soft
   colors, NOT opacity — a faded teal fails AA contrast on the white label. */
.btn.primary.soft{background:#e8f5f2;color:#0d5c53;box-shadow:none}

/* z-index 90: the modal sheet must layer ABOVE the fixed .toasts stack (z 80) — a transient
   toast in the bottom corner otherwise intercepts taps on the sheet's bottom-anchored controls. */
.sheetscrim{position:absolute;inset:0;background:rgba(16,24,40,.34);display:flex;align-items:flex-end;justify-content:center;z-index:90}
.sheetscrim.hide{display:none}
.sheet{width:100%;max-width:560px;background:#fff;border-radius:16px 16px 0 0;box-shadow:0 -8px 30px rgba(16,24,40,.2);padding:20px 22px 24px;animation:modalIn .18s ease}
.sheet .shead{display:flex;align-items:center;justify-content:space-between;margin-bottom:4px}
.sheet .stitle{font-size:16px;font-weight:600;color:var(--ink)}
.sheet .sclose{border:none;background:#f1f3f5;color:#57626e;width:30px;height:30px;border-radius:8px;font-size:16px}
.sheet .ssub{font-size:12.5px;color:#68727c;margin-bottom:14px}
.sheet .slabel{font-size:10.5px;font-weight:700;letter-spacing:.5px;color:#6b7681;margin-bottom:8px}
.sheet .slabel .shint{font-weight:500;color:#8b98a4;letter-spacing:0}
.sheet .pchips{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:18px}
.pchip{display:flex;align-items:center;gap:8px;border:1.5px solid var(--line);background:#fff;color:#4b5763;font-size:13px;font-weight:600;padding:8px 12px;border-radius:9px;cursor:pointer}
.pchip .tick,.rbtn .tick{width:18px;height:18px;flex:none;border-radius:5px;border:2px solid #cbd2d8;background:#fff;display:flex;align-items:center;justify-content:center;color:#fff;font-size:11px;font-weight:700}
.pchip.on{border-color:#d24a63;background:#fdeef1;color:#8a2d43}
.pchip.on .tick{border-color:#d24a63;background:#d24a63}
.sheet .targets{display:flex;flex-direction:column;gap:14px;max-height:300px;overflow:auto}
.sheet .target{border:1px solid #eef1f4;border-radius:12px;padding:12px 13px;background:#fbfcfd}
.sheet .target .tname{display:flex;align-items:center;gap:8px;margin-bottom:10px;font-size:13.5px;font-weight:700;color:var(--ink)}
.sheet .reasons{display:grid;grid-template-columns:1fr 1fr;gap:8px}
.rbtn{display:flex;align-items:center;gap:9px;text-align:left;border:1.5px solid #f0c9d1;background:#fff;color:#b5344d;font-size:12.5px;font-weight:600;padding:9px 11px;border-radius:9px;cursor:pointer}
.rbtn .tick{border-color:#e0aeb8}
.rbtn.on{border-color:#d24a63;background:#fdeef1;color:#8a2d43}
.rbtn.on .tick{border-color:#d24a63;background:#d24a63}
.sheet .sconfirm{margin-top:16px;width:100%;height:46px;border:none;background:#f3d7dd;color:#96455a;font-size:14px;font-weight:600;border-radius:11px;display:flex;align-items:center;justify-content:center;gap:8px}
.sheet .sconfirm.ready{background:#c2334c;color:#fff}

/* Print-label sheet (FINAL_QUALITY_CHECK.md) — centered modal over the inspect panel; the
   left column picks the label, the right shows the #labelPrint preview the fallback print
   isolates. Primary uses --accentD like .btn.primary (white on --accent misses AA). */
.sheetscrim.center{align-items:center;padding:16px}
.sheet.lblsheet{max-width:min(880px,94vw);max-height:92vh;overflow:auto;border-radius:16px;box-shadow:0 20px 50px rgba(16,24,40,.25);padding:20px 22px 22px}
.lblcols{display:flex;gap:22px;flex-wrap:wrap;margin-top:6px}
.lblleft{flex:1 1 320px;min-width:0;display:flex;flex-direction:column;gap:8px}
.lblright{flex:0 1 auto;display:flex;flex-direction:column;gap:8px}
.lblopt{display:flex;align-items:flex-start;gap:10px;width:100%;text-align:left;border:1.5px solid #e4e8ec;background:#fff;border-radius:11px;padding:11px 13px;cursor:pointer;margin-bottom:8px}
.lblopt.on{border-color:var(--accent);background:#f4fbfa}
.lblopt .lradio{width:18px;height:18px;flex:none;border-radius:50%;border:2px solid #cbd2d8;background:#fff;margin-top:1px}
.lblopt.on .lradio{border-color:var(--accent);background:var(--accent);box-shadow:inset 0 0 0 2px #fff}
.lblopt .lname{display:block;font-size:13.5px;font-weight:600;color:#16202b}
.lblopt .lmeta{display:block;font-size:11.5px;color:#68727c;margin-top:1px}
.lblactions{display:flex;gap:10px;margin-top:16px}
.btn.lblprint{flex:1;height:44px;background:var(--accentD);color:#fff;border:none;border-radius:10px;font-size:14px;font-weight:600}
/* on-screen thumbnail at A6 ratio (105:148), sized down so the sheet never overflows */
#labelPrint{width:228px;height:322px;max-width:100%;background:#fff;border:1px solid #cfd6dc;border-radius:6px;padding:14px;display:flex;flex-direction:column;gap:9px;color:#000}
/* real-PDF mode: the iframe thumbnail fills the label frame edge to edge */
#labelPrint.pdf{padding:0;overflow:hidden}
#labelPrint.pdf iframe{width:100%;height:100%;border:0;display:block}
#labelPrint .lp-carrier{display:flex;align-items:baseline;justify-content:space-between;gap:8px;border-bottom:2px solid #000;padding-bottom:6px}
#labelPrint .lp-cname{font-size:12px;font-weight:700;letter-spacing:.4px}
#labelPrint .lp-svc{font-size:10px;font-weight:600}
#labelPrint .lp-eyebrow{font-size:8px;font-weight:700;letter-spacing:.6px;color:#555}
#labelPrint .lp-name{font-size:12.5px;font-weight:700}
#labelPrint .lp-addr{font-size:10px;line-height:1.35;color:#333;overflow:hidden}
#labelPrint .lp-mono{font-family:'IBM Plex Mono',monospace;font-size:10px;overflow-wrap:anywhere}
#labelPrint .lp-div{border-top:1px dashed #999}
#labelPrint .lp-bottom{margin-top:auto}
#labelPrint .lp-barcode{height:56px;background:repeating-linear-gradient(90deg,#000 0 2px,#fff 2px 4px,#000 4px 5px,#fff 5px 8px,#000 8px 11px,#fff 11px 13px)}
#labelPrint .lp-awb{font-family:'IBM Plex Mono',monospace;font-size:11px;letter-spacing:1.5px;margin-top:6px;text-align:center}
@media print{
  body.lblprinting{height:auto;background:#fff}
  body.lblprinting *{visibility:hidden}
  body.lblprinting #labelPrint,body.lblprinting #labelPrint *{visibility:visible}
  /* labels print on A6 stock (qc.js injects the matching @page{size:A6}); invoice slips on A5 */
  body.lblprinting #labelPrint{position:fixed;left:0;top:0;width:105mm;height:148mm;border:none;border-radius:0;box-shadow:none}
  body.lblprinting.lbla5 #labelPrint{width:148mm;height:210mm}
}

/* Tag Creation (waves) */
.wavecols{flex:1;min-height:0;display:flex;gap:14px}
.wavelist{flex:none;width:230px;display:flex;flex-direction:column;gap:9px;overflow:auto}
.wavelist .sectionlabel{padding:2px 2px 0;font-size:10.5px}
.wavecard{text-align:left;background:#fff;border:1.5px solid var(--line);border-radius:11px;padding:11px 13px;cursor:pointer;display:flex;flex-direction:column;gap:6px}
.wavecard.on{border-color:var(--accent);background:#f4fbfa}
.wavecard .wtop{display:flex;align-items:center;justify-content:space-between;gap:8px}
.wavecard .wno{font-family:'IBM Plex Mono',monospace;font-size:14px;font-weight:600;color:var(--ink)}
.wavecard .wsub{font-size:11px;color:#68727c}
.wstatus{font-size:10px;font-weight:600;background:#e8f5f2;color:var(--accentD);padding:2px 8px;border-radius:20px}
.wstatus.pending{background:#fdefdd;color:#8a5408}
/* OFS-148 (DES-OFS-148 §12) — search match-count pill. Styled after .stchip.count's teal-on-tint
   pair, NOT .wstatus (whose accent/amber pair already means production done/pending — reusing it
   would make "3 match" misread as a status). One component, three surfaces: Tag Creation's
   per-wave badge (§6.3), the ADR-028 mine/all cross-slice line (§4.7.2/§5.7) and the Laser/Print
   other-waves line (§4.7.1). Rendered only while a search is active; never shown at rest. */
.wmatch{font-size:10px;font-weight:600;padding:2px 8px;border-radius:20px;background:#e8f5f2;color:var(--accentD)}
.wmatch.zero{background:var(--chip);color:var(--mut)}
/* ADR-036 carryover — the same amber the bench's AGE badge and setup-bar count use, so one
   idea reads as one colour wherever the screen states it */
.wmatch.carry{background:#fdefdd;color:#a5680c}
.wavepanel{flex:1;min-width:0;background:#fff;border:1px solid var(--line);border-radius:14px;box-shadow:0 1px 3px rgba(16,24,40,.05);display:flex;flex-direction:column;overflow:hidden}
.wavepanel .whead{flex:none;padding:14px 20px;border-bottom:1px solid #eef1f4;display:flex;align-items:center;gap:14px;flex-wrap:wrap}
.wavepanel .whead .wno{font-family:'IBM Plex Mono',monospace;font-size:18px;font-weight:600;color:var(--ink)}
.wavepanel .whead .wmeta{font-size:12px;color:#68727c}
.wavepanel .whead .wactions{margin-left:auto;display:flex;align-items:center;gap:10px}
.wavepanel .wstatus{font-size:11.5px;padding:4px 11px}
.wavepanel .wbody{flex:1;min-height:0;overflow:auto}
.wavepanel .wcols{display:flex;align-items:center;gap:12px;padding:10px 20px;border-bottom:1px solid #f1f3f5;font-size:10.5px;font-weight:700;letter-spacing:.5px;color:#6b7681;position:sticky;top:0;background:#fff;z-index:1}
.tagrow{display:flex;align-items:center;gap:12px;padding:11px 20px;border-bottom:1px solid #f4f6f8;background:#fff}
.tagrow.pending{background:#fffdf7}
.tagrow .tickwrap{width:26px;flex:none;display:flex;justify-content:flex-start;border:none;background:transparent;cursor:pointer;padding:0}
/* stack number — this tag's place in the engraved pile handed to Tag Matching, which prints
   the same digit on the same tag (worklist.wave_tag_positions) */
.tagrow .tseq{width:38px;flex:none;font-family:'IBM Plex Mono',monospace;font-size:12.5px;font-weight:700;color:#6b7681}
.tagrow .tseq.none{color:#97a1ac;font-weight:500}
.tickbox{width:22px;height:22px;border-radius:6px;border:2px solid #cbd2d8;background:#fff;display:flex;align-items:center;justify-content:center;color:#fff;font-size:12px;font-weight:700}
.tickbox.done{border-color:var(--accent);background:var(--accent)}
.tagrow .tmain{flex:1;min-width:0;display:flex;align-items:baseline;gap:10px}
.tagrow .ttext{font-size:20px;font-weight:700;color:var(--ink)}
.tagrow .tstatuslbl{font-size:11px;font-weight:600;color:var(--accentD)}
.tagrow.pending .tstatuslbl{color:#8a5408}
.tagrow .tstyle{width:120px;flex:none}
.stylepill{font-size:11.5px;font-weight:600;background:#ecefff;color:#3a5bd0;padding:3px 10px;border-radius:6px}
.stylepill.alt{background:#f1ecfd;color:#6d3fc0}
.tagrow .tprod{width:200px;flex:none}
.tagrow .tprod .tp1{display:block;font-size:12.5px;font-weight:500;color:#313c47}
.tagrow .tprod .tp2{display:block;font-family:'IBM Plex Mono',monospace;font-size:11px;color:#6b7681}

/* Picking (wave-based inventory pick) — phone-width column; the page owns its own scroll
   (.pickscroll) inside the fixed-height .app shell, with room for the fixed .pickbar. */
.pickscroll{flex:1;min-height:0;overflow-y:auto;padding:14px 16px 110px}
.pickscroll .hide{display:none !important}
.pickwrap{max-width:560px;margin:0 auto;width:100%}
.pickwrap .stqueue .wavecard{flex:none;min-width:150px}
.pickhead{background:#fff;border:1px solid var(--line);border-radius:14px;box-shadow:0 1px 3px rgba(16,24,40,.05);padding:14px 16px;margin-bottom:12px}
.pickhead .ph-row{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.pickhead .ph-tote{display:flex;align-items:center;gap:10px;margin-top:12px;flex-wrap:wrap}
.totechip{display:inline-flex;align-items:center;gap:8px;font-size:12.5px;font-weight:600;color:var(--accentD);background:#e8f5f2;border:1px solid #cfe8e2;border-radius:9px;padding:8px 13px}
.binhead{display:flex;align-items:center;gap:9px;margin:6px 2px 5px;font-family:'IBM Plex Mono',monospace;font-size:13px;font-weight:700;letter-spacing:.5px;color:#4b5763}
.binhead .binrule{flex:1;height:1px;background:#eef1f4}
.pline{border:1.5px solid #dfe4e8;background:#fff;border-radius:13px;padding:12px 13px;display:flex;align-items:center;gap:13px;cursor:pointer}
.pline.picked{border-color:var(--accent);background:#f4fbf7}
.pline .pcheck{flex:none;width:30px;height:30px;border-radius:50%;border:2px solid #cfd6dc;background:#fff;color:#fff;font-size:15px;font-weight:700;display:flex;align-items:center;justify-content:center;padding:0}
.pline.picked .pcheck{border-color:var(--accent);background:var(--accent)}
.pline .pmain{flex:1;min-width:0}
.pline .pname{font-size:15px;font-weight:700;color:var(--ink)}
.pline .pqty{font-family:'IBM Plex Mono',monospace;font-size:12px;font-weight:700;color:var(--accentD);background:#e8f5f2;border:1px solid #cfe8e2;border-radius:7px;padding:2px 8px}
.pline .psub{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-top:6px;font-family:'IBM Plex Mono',monospace;font-size:11px;color:#68727c}
/* --accentD, not --accent: white 13px text on the brighter teal misses AA (same reason .btn.primary uses it) */
.pline .pbtn{flex:none;align-self:stretch;min-width:74px;border:1.5px solid var(--accentD);background:var(--accentD);color:#fff;font-size:13px;font-weight:600;border-radius:10px;padding:0 12px}
.pline.picked .pbtn{background:#e8f5f2;color:var(--accentD);border-color:#cfe8e2}
.pickbar{position:fixed;left:0;right:0;bottom:0;background:#fff;border-top:1px solid var(--line);padding:11px 16px;box-shadow:0 -2px 10px rgba(16,24,40,.05);z-index:20}

/* Picking — Main worklist ⇄ Batches tabs (station screens v2). Visibility is CSS-driven off
   body[data-pktab] (no attribute = Main worklist, so there is no first-paint flash); pickwave.js
   keeps toggling .hide inside the Batches panel untouched. The grid reuses the base table/
   tablewrap/tablefoot shell — .pkwl only drops the side margins the .pickscroll padding already
   provides and caps the wrap height (adminstack precedent) so the sticky header keeps working. */
body:not([data-pktab="batches"]) #pkBatchesPanel{display:none}
body:not([data-pktab="batches"]) #pwFooter{display:none}
body[data-pktab="batches"] #pkWorklistPanel{display:none}
.pkwl .tablewrap{margin:0;max-height:62vh}
.pkwl .tablefoot{margin:0}
/* grid thumbnail — a real <button> (keyboard-operable) framing the SKU sample; the shared
   lightbox (#lightboxScrim) expands it on click */
.wlthumb{width:38px;height:38px;border-radius:8px;border:1px solid #eef0f2;background:#fafbfc;cursor:zoom-in;padding:0;appearance:none;overflow:hidden;display:block}
.wlthumb img{width:100%;height:100%;object-fit:cover;display:block}

/* Main worklist — the phone rendering ("like it was earlier"): the SAME paged rows painted as
   the wave-walk's .pline packet cards in the earlier phone-width centered column. CSS swaps
   grid ⇄ cards at the house 1024px breakpoint; the data/state is one TableView (renderExtra),
   so the two representations can never disagree. */
.wlcards{display:none;flex-direction:column;gap:9px;max-width:560px;margin:0 auto}
.wlpline{cursor:default}
.wlpline .wlthumb{width:44px;height:44px;flex:none}
.wlpline .wlthumb.noimg{border-style:dashed;border-color:#cfd6dc;background:repeating-linear-gradient(135deg,#f4f6f8 0 5px,#eceff2 5px 10px);cursor:default}

/* Grouped view (default) — the Batches screen's bin-walk look over the whole filtered
   worklist: .binhead per bin + ×N .pline lines, centered like the wave column (a little wider
   on desktop). While .pkwl.grouped is on, the Units grid/cards, sort row and pager hide —
   Grouped walks bins A→Z, so per-column sorting and pagination don't apply. */
.wlgroups{display:none;flex-direction:column;gap:9px;max-width:640px;margin:0 auto}
.pkwl.grouped .wlgroups{display:flex}
.pkwl.grouped .tablewrap{display:none}
.pkwl.grouped #pkCards{display:none}
.pkwl.grouped .pksort{display:none}
.pkwl.grouped .tablefoot{display:none}
/* the aggregate headline ("To pick: N packets · M part lines · K bins") */
.pkagg{max-width:640px;margin:0 auto 10px;font-size:12.5px;color:#4b5763}
.pkagg b{font-family:'IBM Plex Mono',monospace}
@media (max-width:1023px){
  .pkwl .tablewrap{display:none}
  .wlcards{display:flex}
  /* the earlier screen's centered phone column for the controls + footer too */
  .pkwl .filters,.pkwl .pksort,.pkwl .tablefoot{max-width:560px;margin-left:auto;margin-right:auto}
  .pkwl .filters .fgroup .fld{max-width:150px}
  .pkwl #pkSearch{width:150px}
}
/* multi-level sort: the chip readout row + the header's stacked-level badge (sortHeader prio) */
.pksort{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin:0 0 10px}
.pksort #pkSortChips{display:inline-flex;align-items:center;gap:6px;flex-wrap:wrap}
.pksort .pksort-hint{font-size:11px;color:var(--mut)}
.pksort .chip button{border:none;background:transparent;cursor:pointer;font:inherit;font-weight:700;padding:0 0 0 5px;color:inherit}
.sprio{font-size:9px;font-weight:700;vertical-align:super;margin-left:1px}
th.sortable{user-select:none}  /* Shift+click stacks a sort level — never select header text */

/* Thumbnail → lightbox affordances (station screens): the QC/matching SKU-card sample
   (.skusnap), the Laser/Printing batch-bar icon (button.cwicon), the sticker bench reference
   photo (.skrefbtn) and the Boxing piece media all expand via the shared #lightboxScrim
   (custom.js CustomView.wire). The buttons are style-transparent wrappers — the existing
   .skumedia img / .skref img rules keep styling the image inside. */
.skumedia .skusnap{display:block;padding:0;border:none;background:none;appearance:none;cursor:zoom-in}
.cwbar button.cwicon{padding:0;border:1px solid #e4e8ec;background:#fff;cursor:zoom-in;overflow:hidden}
.cwbar button.cwicon img{width:100%;height:100%;object-fit:cover;display:block}
.skref .skrefbtn{display:block;width:100%;padding:0;border:none;background:none;appearance:none;cursor:zoom-in}
.bxmedia img{cursor:zoom-in}  /* the piece card is one big <button> — the img is the expand hotspot */

@media (max-width:1023px){
  .wavelist{width:180px}
  .tagrow .tprod{width:150px}
  .tagrow .tseq{width:30px}
}

/* OFS-119 — Analytics page. Section headers + the simple throughput bar (health.js bar pattern:
   a fixed-width track with an inline width:% fill; base table look is inherited, not restyled). */
.asection{margin-top:18px}
.asection:first-of-type{margin-top:4px}
.ahead{font-size:14px;font-weight:600;margin:0}
/* OFS-141 / DES-OFS-140 — Insights panel (§3): .card geometry + a 4px severity left
   border; severity hexes are the existing red/amber/teal status pairs, no new colours. */
.insight-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:12px}
.insight-card{background:#fff;border:1px solid var(--line);border-left:4px solid var(--line);
  border-radius:11px;padding:13px 15px;display:flex;flex-direction:column;gap:6px;
  text-decoration:none;color:inherit}
.insight-card.sev-red{border-left-color:#b42318}
.insight-card.sev-amber{border-left-color:#92600a}
.insight-card.sev-teal{border-left-color:#0f766e}
.insight-card:hover{box-shadow:0 1px 3px rgba(20,24,27,.08)}
.insight-card:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.insight-card .ititle{font-size:13.5px;font-weight:600;color:var(--ink);line-height:1.35}
.insight-card .idetail{font-size:12px;color:var(--mut);line-height:1.4}
.insight-allclear{padding:20px 15px;border:1px dashed var(--line);border-radius:11px;background:#fff;
  display:flex;align-items:center;gap:10px}
.asection.focusflash{animation:focusFlash 2s ease}
/* Sparkline (§8) — inline SVG wrapped in a labelled span. */
.spark{display:inline-block;vertical-align:middle;line-height:0}
.spark svg{display:block}
/* Demand movers 2-up (§4.2). The .hide guard must out-specify the display:grid above —
   the bare .hide rule sits earlier in this file and would lose the same-specificity tie. */
.demand-movers{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:12px}
.demand-movers.hide{display:none}
@media (max-width:480px){.demand-movers{grid-template-columns:1fr}}
.tbar{display:inline-flex;align-items:center;gap:8px}
.tbar .tb{width:120px;height:7px;border-radius:5px;background:#edf0f1;overflow:hidden}
.tbar .tb i{display:block;height:100%;border-radius:5px;background:var(--accent)}
.tbar b{font-family:'IBM Plex Mono',monospace;font-weight:600}

/* --- Matching: "Elsewhere on the line" strip (mixed combos) --- the order's pieces that
   never visit the match station (engrave-only pen/bottle of a diary combo), read-only so a
   3-piece order doesn't read as 1-piece. Spans the whole piece grid. */
.elsewhere{grid-column:1/-1;border:1.5px dashed #dfe4e8;border-radius:12px;padding:10px 13px;background:#fafbfc;display:flex;flex-direction:column;gap:6px}
.elsewhere .elhead{font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:#5b6773}
.elsewhere .elrow{display:flex;align-items:center;gap:8px;font-size:13px;color:var(--ink)}
.elsewhere .elname{font-weight:700}
.elsewhere .elsub{color:#5b6773;font-size:12.5px}
.elsewhere .elstation{margin-left:auto;color:#5b6773;font-weight:600;white-space:nowrap}
.elsewhere .elnote{font-size:12px;color:#8a94a0}

/* ==== Tag-Matching v3 (docs/manual-readme/tag-matching.md) — queue rail + focused panel ====
   Values are the handoff's final tokens (colors/sizes/radii quoted from its Design Tokens
   section), EXCEPT the faint text greys (#97a1ac/#a3abb2/#7b8794): those fail the app's hard
   axe AA gate on small bold text, so they are darkened to the nearest existing AA-passing
   greys (#6b7681/#5f6b77/#57626e/#626e7a/#68727c) — same hierarchy, compliant contrast.
   The prototype's page-level scroll is re-expressed as the app-shell contract
   (OFS-132): the grid fills .content, the rail's queue list and the panel's .fbody scroll
   internally, so the progress footer stays on screen (ofs-120/ofs-132 QA pins). */
.tmpage{flex:1;min-height:0;display:grid;grid-template-columns:196px minmax(0,1fr);gap:12px;padding:10px 14px 12px}
.tmpage.railclosed{grid-template-columns:40px minmax(0,1fr)}
.tmpage .hide{display:none !important}
/* header scan slot (the design's top-bar search): TRAY-### / tag lookup box */
.tmscan{display:flex;align-items:center;gap:6px;background:#f3f5f6;border:1px solid var(--line);border-radius:8px;height:34px;padding:0 4px 0 10px;flex:0 1 300px;min-width:150px}
.tmscan input{border:none;background:transparent;outline:none;font-size:12.5px;font-family:'IBM Plex Mono',monospace;flex:1;min-width:0;color:var(--ink)}
.tmscan button{border:none;background:transparent;color:#667180;font-size:15px;width:28px;height:28px;border-radius:6px;display:flex;align-items:center;justify-content:center;flex:none}
.tmscan button:hover{background:#e8ecee}
@media (max-width:820px){.tmscan{display:none}}
/* the scan slot + userbox + station chips can't all fit a mid-width bar — the informational
   station chip yields first (count chip + scan box stay), only on pages carrying .tmscan */
@media (max-width:1500px){header.top:has(.tmscan) .rolechips .stchip:not(.count){display:none}}

/* A. queue rail */
.tmrail{min-height:0;display:flex;flex-direction:column;gap:9px}
.railhead{flex:none;display:flex;align-items:center;gap:6px}
.railtoggle{width:28px;height:28px;flex:none;border:1px solid #e4e8ec;background:#fff;border-radius:8px;color:#667180;font-size:14px;display:flex;align-items:center;justify-content:center;padding:0}
.raillabel{font-size:9.5px;font-weight:700;letter-spacing:.6px;color:#5f6b77}
.railcount{font-size:10.5px;font-weight:600;color:#0f766e;background:#e8f5f2;border:1px solid #cfe8e2;border-radius:20px;padding:1px 8px}
.railbody{flex:1;min-height:0;display:flex;flex-direction:column}
.raillist{flex:1;min-height:0;overflow-y:auto;display:flex;flex-direction:column;gap:7px;padding:2px}
.railfoot{flex:none;margin-top:auto;border-top:1px solid #eef1f4;padding-top:8px;display:flex;flex-direction:column;gap:5px}
.railbtn{display:block;width:100%;text-align:left;border:1px solid #e4e8ec;background:#fff;color:#4b5763;font-size:11.5px;font-weight:500;border-radius:8px;padding:7px 9px;text-decoration:none}
.railbtn:hover{background:#fafbfc}
.railbtn.signout{border-color:#f2dcdc;color:#b5344d;font-weight:600}
.tmpage.railclosed :is(.raillabel,.railcount,.railbody,.railfoot,.railwaves,.railtabs){display:none}

/* Shipment-QC ready/pending rail tabs — the queue splits on the all-parts-together gate: a
   card whose order is still short of parts can never pass here, so it waits under Pending
   instead of padding the actionable Ready list. Same rail-scoped .seg deltas as .railwaves. */
.railtabs{flex:none;display:flex;flex-direction:column;gap:5px;padding:2px 2px 6px;border-bottom:1px solid #eef1f4}
.railtabs .seg{display:flex;width:100%}
.railtabs .seg button{flex:1;justify-content:center;font-size:10.5px;padding:5px 4px}

/* ADR-028 userwise queues — rail controls + wave sections + being-processed pills */
.railwaves{flex:none;display:flex;flex-direction:column;gap:5px;padding:2px 2px 6px;border-bottom:1px solid #eef1f4}
.railwaves .seg{display:flex;width:100%}
.railwaves .seg button{flex:1;font-size:10.5px;padding:5px 4px}
.railbtn.take{text-align:center;font-weight:600;color:#0f766e;border-color:#cfe8e2;background:#f4fbfa}
.railbtn.take:hover{background:#e8f5f2}
.wavehead{display:flex;align-items:center;gap:6px;margin:4px 0 -2px;padding:3px 4px 2px;font-size:10px}
.wavehead-label{font-weight:700;letter-spacing:.5px;color:#3d4854;font-size:10.5px}
.wavehead-owner{color:#7b8794;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
.wavehead-owner.own{color:#0f766e;font-weight:600}
.wavehead-btn{margin-left:auto;flex:none;border:1px solid #cfe8e2;background:#fff;color:#0f766e;font-size:10px;font-weight:600;border-radius:12px;padding:1px 8px;cursor:pointer}
.wavehead-btn:hover{background:#e8f5f2}
.wavehead-n{flex:none;font-size:9.5px;font-weight:600;color:#626e7a;background:#f1f3f6;border-radius:10px;padding:0 6px}
.wavehead-btn+.wavehead-n,.wavehead-owner+.wavehead-n{margin-left:0}
.wavehead:first-child{margin-top:0}
.cwwho{display:inline-flex;align-items:center;font-size:9px;font-weight:700;letter-spacing:.4px;color:#8a5a12;background:#fdf3e0;border:1px solid #f2e2c2;border-radius:10px;padding:0 6px;white-space:nowrap}
.cwwho.own{color:#0f766e;background:#e8f5f2;border-color:#cfe8e2}
.cwprocbtn{border-color:#f2e2c2;color:#8a5a12;background:#fffaf0}
.cwprocbtn:hover{background:#fdf3e0}

/* queue card */
.qcard{width:100%;text-align:left;background:#fff;border:1.5px solid #e4e8ec;border-radius:11px;box-shadow:0 1px 2px rgba(16,24,40,.04);padding:8px 10px;display:flex;flex-direction:column;gap:5px;cursor:pointer}
.qcard.on{border-color:#0d9488;background:#f4fbfa;box-shadow:0 0 0 3px rgba(13,148,136,.13)}
.qcard .qr1{display:flex;align-items:center;gap:6px;min-width:0}
.qcard .qdot{width:10px;height:10px;flex:none;border-radius:50%;box-shadow:inset 0 0 0 1px rgba(0,0,0,.12)}
.qcard .qoid{font-family:'IBM Plex Mono',monospace;font-size:11px;font-weight:600;color:#16202b;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0}
.qcard .prpill{margin-left:auto;flex:none;font-size:9px;padding:1px 6px}
.qcard .qr2{display:flex;align-items:center;gap:6px;font-size:10px;color:#626e7a;min-width:0}
.qcard .qeyebrow{font-size:8.5px;font-weight:700;letter-spacing:.5px;color:#5f6b77;flex:none}
.qcard.on .qeyebrow{color:#0f766e}
.qcard .qclaim{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#8a5408}
.qcard .qnum{margin-left:auto;flex:none;font-family:'IBM Plex Mono',monospace;font-weight:600;color:#57626e}
.qcard .qnum.has{color:#0f766e}

/* B. focused panel deltas (base .fpanel look is shared with the other station screens) */
.tmmain{min-height:0;display:flex;flex-direction:column}
.tmpage .fpanel .fhead{padding:10px 16px;gap:12px}
.tmpage .fpanel .fbody{padding:12px 16px}
.tmpage .fpanel .ffoot{padding:9px 16px}
.tmpage .unitcode{font-family:'IBM Plex Mono',monospace;font-size:14px;font-weight:600;color:#16202b}
.tmpage .foid{font-family:'IBM Plex Mono',monospace;font-size:11.5px;color:#6b7681}
/* anchor variant (qc.js): Amazon orders link the id out to Seller Central */
.tmpage a.foid{text-decoration:none;display:inline-flex;align-items:center;gap:3px}
.tmpage a.foid svg{flex:none;opacity:.75}
.tmpage a.foid:hover,.tmpage a.foid:focus-visible{color:#0f766e;text-decoration:underline}

/* tray control (in the panel header, right-aligned) */
.traybox{margin-left:auto;flex:none;display:flex;align-items:center}
.traybox .tbrow{display:flex;align-items:center;gap:10px}
.traybox .tbicon.ok{width:34px;height:34px;flex:none;border-radius:9px;background:#e8f5f2;color:#0f766e;display:flex;align-items:center;justify-content:center;font-size:16px}
.traybox .tblabel{font-size:9.5px;font-weight:700;letter-spacing:.5px;color:#6b7681}
.traybox .tbcode{font-family:'IBM Plex Mono',monospace;font-size:14px;font-weight:600;color:#0f766e;margin-top:1px}
.traybox .tbscan{display:flex;align-items:center;gap:9px}
.traybox .tbfld{display:flex;align-items:center;gap:8px;height:38px;border:1px solid #cfd6dc;border-radius:9px;background:#fff;padding:0 12px}
.traybox .tbglyph{color:#97a1ac;font-size:15px;flex:none}
.traybox .tbfld input{border:none;outline:none;background:transparent;width:230px;max-width:38vw;font-size:13px;font-family:'IBM Plex Mono',monospace;color:var(--ink)}

/* parts area — one SKU card per SKU, minimal part rows */
.skustack{display:flex;flex-direction:column;gap:12px}
.skucard{border:1px solid #e4e8ec;background:#fff;border-radius:13px;padding:14px;display:flex;gap:18px;align-items:flex-start}
.skumedia{flex:none;width:92px;display:flex;flex-direction:column;gap:6px;align-items:center}
.skumedia img{width:92px;height:92px;object-fit:contain;border:1px solid #e4e8ec;border-radius:10px;background:#fff}
.skumedia .noimg{width:92px;height:92px;border:1px dashed #cfd6dc;border-radius:10px;background:repeating-linear-gradient(135deg,#f4f6f8 0 5px,#eceff2 5px 10px)}
.skumedia .skucap{font-family:'IBM Plex Mono',monospace;font-size:11.5px;font-weight:600;color:#4b5763;text-align:center;overflow-wrap:anywhere}
/* per-unit customer artwork on the workstation cards (photo products): small lightbox
   thumbs — QC/matching SKU media columns, the rework desk part cards. The worker
   compares the piece to the buyer's actual photo. */
.qcartcap{font-size:8.5px;font-weight:700;letter-spacing:.5px;color:#8a939b;margin-top:2px}
.qcartwork{display:none;flex-wrap:wrap;gap:6px 10px;justify-content:center;margin-top:4px}
.qcartwork .qcartthumb{width:44px;height:44px;flex:none;border-radius:8px;border:1px solid #e4e8ec;background:#fff;cursor:zoom-in;padding:0;overflow:hidden}
.qcartwork img{width:100%;height:100%;object-fit:cover;border:none;border-radius:0}
.qcartwork .qcartcell{display:flex;flex-direction:column;align-items:center;gap:2px}
.qcartwork .qcartdl{font-size:9px;font-weight:700;color:#0f766e;text-decoration:none}
.qcartlbl{font-size:8.5px;font-weight:700;letter-spacing:.5px;color:#8a939b}
.qcartwork .rwart{display:flex;align-items:center;gap:6px;min-width:0}
.rwleft .qcartwork{justify-content:flex-start}   /* rework desk: align with the card text */
.qcartwork .rwartdl{font-size:10.5px;font-weight:600;color:#0f766e;text-decoration:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* ordered-font pill on the tag-creation rows + sticker bench text line */
.tfont{font-size:10.5px;font-weight:600;color:#6a6fc0;margin-left:6px}
/* buyer-rotated-photo pill on the artwork strips — a naive print gets rotation wrong */
.cwartrot{display:inline-block;font-size:9.5px;font-weight:700;color:#a5680c;background:#fdefdd;border-radius:20px;padding:1px 7px;margin-left:4px}
.skuparts{flex:1;min-width:0;display:grid;gap:10px 26px;grid-template-columns:repeat(auto-fill,minmax(240px,1fr))}
.mrow{display:block;position:relative;width:100%;min-width:0;text-align:left;border:none;background:transparent;border-bottom:1px solid #f0f2f5;border-radius:0;padding:6px 34px 10px 2px;cursor:pointer}
.mrow.added{opacity:.42}
/* keyboard badge (shell §7: badge N = key N) — the .qcbadge/.bxbadge 24×24 formula */
.mrow .mnum{position:absolute;top:6px;right:2px;width:24px;height:24px;border-radius:7px;background:#f3f5f7;color:#68727c;display:flex;align-items:center;justify-content:center;font-family:'IBM Plex Mono',monospace;font-size:12px;font-weight:700}
.mrow.added .mnum{background:var(--accent);color:#fff}
.mrow .pname{display:block;font-size:19px;font-weight:600;letter-spacing:-.3px;color:#16202b;line-height:1.25}
.mrow .ptag{display:block;font-size:17px;font-weight:700;line-height:1.35;overflow-wrap:anywhere}
/* the tag's stack number — neutral, so it reads as an index and never as part of the engraved
   name it sits beside (which renders in the piece's own colour) */
.mrow .ptag .tstack{font-family:'IBM Plex Mono',monospace;font-size:11.5px;font-weight:600;color:#68727c;background:#f3f5f7;border-radius:6px;padding:1px 6px;margin-left:8px;vertical-align:2px}
.mrow .pcharm{display:block;font-size:15px;font-weight:600;color:#2f5bd4}
.mrow .pxtra{display:block;font-size:12px;color:#626e7a;margin-top:2px}
.mrow.focusflash{animation:focusFlash 2s ease}
.mrow:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:6px}

@media (max-width:1279px){
  .skuparts{grid-template-columns:1fr}
}

/* ---- Boxing & Collecting (docs/manual-readme/Boxing+Collecting.md) — deltas on the shared
   station-screen shell (.tmpage/.tmrail/.fpanel/.qcscanpanel are reused as-is). The body is the
   station-group "integration view": one tinted group card per upstream station, piece cards
   inside. Design's faint text tones are AA-darkened like the sibling screens (#c3cad1→#5f6b77,
   #a5680c→#8a5408); .inbox/.transit opacities follow the .mrow.added precedent. */
.bxstatus{font-size:11.5px;font-weight:600}
.bxstatus.allhere{color:#12855b}
.bxstatus.transit{color:#8a5408}
.bxnav{margin-left:auto;flex:none;display:flex;align-items:center;gap:8px}
.btn.bxnavbtn{width:32px;height:32px;padding:0;font-size:15px;display:flex;align-items:center;justify-content:center}
.bxnavpos{font-size:12px;font-weight:600;color:#667180;font-family:'IBM Plex Mono',monospace}
.bxtools{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.bxsizes{display:flex;gap:6px}
.bxsize{border:1.5px solid #e4e8ec;background:#fff;color:#4b5763;font-family:'IBM Plex Mono',monospace;font-size:11.5px;font-weight:600;padding:5px 9px;border-radius:8px;cursor:pointer}
.bxsize.on{border-color:var(--accent);background:#f4fbfa;color:var(--accentD)}
.bxsuggest{font-size:11.5px;color:#68727c}
.bxkeys{display:flex;align-items:center;justify-content:center;gap:14px;font-size:11.5px;color:#68727c}
.bxkeys.inline{gap:10px;margin-right:6px}
.bxkeys kbd{font-family:'IBM Plex Mono',monospace;font-size:10.5px;background:#f3f5f7;border:1px solid #e4e8ec;border-bottom-width:2px;border-radius:5px;padding:1px 5px;color:#4b5763}
@media (max-width:1100px){.bxkeys.inline{display:none}}
.btn.park{display:flex;align-items:center;gap:8px;background:#fff;color:#8a5408;border:1.5px solid #f3e2c4;height:44px;padding:0 20px;border-radius:10px;font-size:14px;font-weight:600}

/* station groups — 2-col grid; the biggest group spans both columns (the handoff's
   Tag-Matching rule: it carries most pieces) */
.bxgroups{display:grid;grid-template-columns:1fr 1fr;gap:10px}
/* NO overflow rule: the group tint is flat (its header carries no background of its own,
   and the inset .bxpiece cards never reach the 11px corner), so clipping bought nothing
   here and cut the bottom row's .qcdet popover off at the group's edge. */
.bxgroup{border:1.5px solid #cfe8e2;background:#f4fbfa;border-radius:11px}
.bxgroup.span{grid-column:1 / -1}
.bxgroup.pending{border-color:#f3e2c4;background:#fff8ec}
.bxghead{display:flex;align-items:center;gap:8px;padding:7px 11px}
.bxgdot{width:8px;height:8px;flex:none;border-radius:50%;background:var(--accent)}
.bxgroup.pending .bxgdot{background:#d99a2b}
.bxgname{font-size:13px;font-weight:700;color:var(--accentD)}
.bxgroup.pending .bxgname{color:#8a5408}
.bxgeyebrow{font-size:8.5px;font-weight:700;letter-spacing:.5px;color:#2f6e63}
.bxgroup.pending .bxgeyebrow{color:#8a5408}
.bxgcount{margin-left:auto;font-family:'IBM Plex Mono',monospace;font-size:11px;font-weight:600;color:#2f6e63}
.bxgroup.pending .bxgcount{color:#8a5408}
.bxpieces{display:grid;grid-template-columns:repeat(auto-fill,minmax(185px,1fr));gap:8px 14px;padding:2px 10px 10px}

/* piece card — compact thumbnail column + stacked lines + right index badge */
.bxpiece{display:flex;align-items:flex-start;gap:8px;min-width:0;text-align:left;background:#fff;border:1px solid #e4e8ec;border-radius:9px;padding:8px 9px;cursor:pointer}
.bxpiece.inbox{opacity:.45}
.bxpiece.transit{opacity:.62;cursor:default}
.bxpiece:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.bxmedia{flex:none;width:40px;display:flex;flex-direction:column;gap:3px;align-items:center}
.bxmedia img{width:40px;height:40px;object-fit:contain;border:1px solid #e4e8ec;border-radius:7px;background:#fff}
.bxmedia .noimg{width:40px;height:40px;border:1px dashed #cfd6dc;border-radius:7px;background:repeating-linear-gradient(135deg,#f4f6f8 0 5px,#eceff2 5px 10px)}
.bxmedia .bxsku{font-family:'IBM Plex Mono',monospace;font-size:9.5px;font-weight:600;color:#68727c;text-align:center;overflow-wrap:anywhere}
.bxmain{flex:1;min-width:0}
.bxmain .pname{display:block;font-size:16px;font-weight:600;letter-spacing:-.2px;color:#16202b;line-height:1.25}
.bxmain .ptag{display:block;font-size:15px;font-weight:700;line-height:1.3;overflow-wrap:anywhere}
.bxmain .pcharm{display:block;font-size:13px;font-weight:600;color:#2f5bd4}
.bxmain .pxtra{display:block;font-size:11.5px;color:#626e7a;margin-top:1px}
.bxpstatus{display:block;font-size:9.5px;font-weight:700;letter-spacing:.5px;color:#5f6b77;margin-top:3px}
.bxpiece.inbox .bxpstatus{color:var(--accentD)}
.bxpiece.transit .bxpstatus{color:#8a5408}
.bxbadge{flex:none;width:24px;height:24px;border-radius:7px;background:#f3f5f7;color:#68727c;display:flex;align-items:center;justify-content:center;font-family:'IBM Plex Mono',monospace;font-size:12px;font-weight:700}
.bxpiece.inbox .bxbadge{background:var(--accent);color:#fff}
.bxpiece.transit .bxbadge{background:#fdefdd;color:#8a5408}
/* rail eyebrow states (trays-to-box cards) */
.qcard .qeyebrow.allhere{color:#12855b}
.qcard .qeyebrow.transit{color:#8a5408}
@media (max-width:1100px){.bxgroups{grid-template-columns:1fr}}

/* ============================= SHIPMENT OPERATOR DESK ============================= */
/* docs/manual-readme/Shipment-fulfillment-operator.md — single scrolling column of cards.
   Tokens per the handoff (shared with tag-matching.md). */
.shippage{padding:14px 16px 18px;max-width:1460px;margin:0 auto;width:100%;display:flex;flex-direction:column;gap:12px;overflow-y:auto}
.shiphead{display:flex;align-items:center;gap:12px}
.shiphead .roletitle h1{font-size:16px;letter-spacing:-.3px}
.shipcard{background:#fff;border:1px solid #e4e8ec;border-radius:14px;box-shadow:0 1px 3px rgba(16,24,40,.05);padding:14px 16px 16px;display:flex;flex-direction:column;gap:11px}
.shiprow1{display:grid;grid-template-columns:minmax(0,1.15fr) minmax(0,1fr);gap:12px}
@media (max-width:980px){.shiprow1{grid-template-columns:1fr}}
.shipcardhead{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.shipeyebrow{font-size:9.5px;font-weight:700;letter-spacing:.6px;color:#97a1ac}
.shiplink{font-size:12px;font-weight:600;color:#2f5bd4;text-decoration:none;border:1px solid #e4e8ec;border-radius:8px;height:30px;display:inline-flex;align-items:center;padding:0 10px;background:#fff}
.shiplink:hover{background:#f7f9fc}
/* header sync strip (right of the nav, divider on the left; chips never grow) */
.syncstrip{display:flex;align-items:center;gap:7px;flex:none;border-left:1px solid var(--line);margin-left:12px;padding-left:12px}
.synceyebrow{font-size:9.5px;font-weight:700;letter-spacing:.6px;color:#97a1ac}
.syncchip{display:inline-flex;align-items:center;gap:6px;height:26px;padding:0 9px;border-radius:7px;font-size:11.5px;font-weight:600;color:#4b5763;background:#fff;border:1px solid #e4e8ec;cursor:pointer}
.syncchip .dot{width:7px;height:7px}
.syncchip:disabled{opacity:.55;cursor:not-allowed}
.syncchip.running{background:#f4fbfa;border-color:#cfe8e2;color:#0f766e}
/* upload */
.shipmode{align-self:flex-start}
.shipdrop{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;border:1.5px dashed #cfd6dc;border-radius:12px;background:#fbfcfd;padding:22px 14px;cursor:pointer;text-align:center}
.shipdrop.drag{border-color:#0d9488;background:#f4fbfa}
.shipdrop .dropicon{width:44px;height:44px;border-radius:12px;background:#e8f2ff;color:#2f5bd4;display:flex;align-items:center;justify-content:center;font-size:20px}
.shipdrop .drophead{font-size:13.5px;font-weight:600;color:#16202b}
.shipdrop .dropsub{font-size:11.5px;color:#8a939b}
.filechip{display:inline-flex;align-items:center;gap:8px;font-size:12px;font-weight:600;color:#0f766e;background:#e8f5f2;border:1px solid #cfe8e2;border-radius:8px;padding:5px 10px;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.shipactions{display:flex;align-items:center;gap:10px}
.shipactions .btn{height:40px}
.shipactions .btn.ready{border-color:#cfe8e2;color:#0f766e;font-weight:600}
/* file stats */
.fsfile{font-size:12px;font-weight:600;color:#16202b;max-width:220px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.fsstate{font-size:11.5px;font-weight:600;color:#8a939b}
.fsstate.busy{color:#2f5bd4}
.fsstate.ok{color:#12855b}
.fsstate.stop{color:#7b8794}
.fstiles{display:grid;grid-template-columns:repeat(auto-fit,minmax(112px,1fr));gap:8px}
/* unknown-SKU callout under the tiles (red pills, capped list, jump to SKUs) */
.fsmiss{display:flex;align-items:center;gap:6px;flex-wrap:wrap}
.fsmiss .fsmisslab{font-size:9px;font-weight:700;letter-spacing:.5px;color:#b42318;flex:none}
.fsmiss .fsmissmore{font-size:11px;font-weight:600;color:#8a939b}
.fsmiss .fsmissgo{height:24px;font-size:11px;margin-left:auto}
.fstile{border:1px solid #eef1f4;background:#fbfcfd;border-radius:10px;padding:9px 10px}
.fstile .tl{font-size:9px;font-weight:700;letter-spacing:.5px;color:#97a1ac}
.fstile .tv{font-family:'IBM Plex Mono',monospace;font-size:20px;font-weight:600;margin-top:3px}
/* batch waves */
.wavebar{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.wavelab{font-size:9px;font-weight:700;letter-spacing:.5px;color:#97a1ac}
.wavechips{display:flex;gap:5px}
.wavechips button{height:28px;padding:0 11px;border-radius:8px;border:1px solid #e4e8ec;background:#fff;font-size:11.5px;font-weight:600;color:#4b5763;cursor:pointer}
.wavechips button.on{border-color:#0d9488;background:#f4fbfa;color:#0f766e}
.waveexp{display:inline-flex;align-items:center;gap:7px;font-size:12px;font-weight:500;color:#4b5763;cursor:pointer}
.waveexp input{width:16px;height:16px;border-radius:5px;accent-color:#0d9488}
.wavebar .btn.primary{height:32px;font-size:12.5px}
.waverows{display:flex;flex-direction:column;gap:7px}
.waverow{display:flex;align-items:center;gap:12px;border:1px solid #e4e8ec;border-radius:9px;padding:8px 12px;font-size:12px;color:#4b5763;flex-wrap:wrap}
.waverow .wid{font-family:'IBM Plex Mono',monospace;font-weight:600;color:#0f766e}
.wavepill{font-size:9px;font-weight:700;letter-spacing:.4px;border-radius:20px;padding:2px 8px}
.wavepill.released{background:#e6f4ec;color:#12855b}
.waveempty{border:1px dashed #cfd6dc;border-radius:10px;padding:16px;text-align:center;font-size:12.5px;color:#8a939b}
/* accordion card (pending orders): the whole head row toggles its body; the head keeps
   working controls (count + wave button), so the toggle is a real button INSIDE the head */
.acchead{cursor:pointer;user-select:none}
.accbtn{background:none;border:0;padding:0;cursor:pointer;display:inline-flex;align-items:center;gap:7px}
.accbtn:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:6px}
.accarrow{width:18px;height:18px;flex:none;border-radius:5px;background:#eef1f4;color:#667180;display:inline-flex;align-items:center;justify-content:center;font-size:9px}
.accbody{display:flex;flex-direction:column;gap:11px}
/* shared .tablewrap/.tablefoot INSIDE a card: drop the page-level side margins and cap the
   height so a long list scrolls under its sticky header instead of stretching the card */
.shipcard .tablewrap{margin:0;flex:none;max-height:52vh}
.shipcard .tablefoot{margin:0}
/* background tasks */
.tasklist{display:flex;flex-direction:column;gap:7px}
.taskrow{display:flex;align-items:center;gap:10px;border:1px solid #e4e8ec;border-radius:9px;padding:7px 10px;font-size:12px;cursor:pointer}
.taskrow.sel{border-color:#0d9488;background:#f8fdfc}
.taskrow .tdot{width:8px;height:8px;border-radius:50%;flex:none}
.taskrow .tlabel{font-weight:600;color:#16202b;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:300px}
/* who started it — the task list is shared across logins, so a row names its operator */
.taskrow .tby{font-size:11px;color:#8a939b;flex:none;white-space:nowrap;max-width:140px;overflow:hidden;text-overflow:ellipsis}
.taskpill{font-size:9px;font-weight:700;letter-spacing:.4px;border-radius:20px;padding:2px 8px;flex:none}
.taskpill.running{background:#e8f2ff;color:#2f5bd4}
.taskpill.done{background:#e6f4ec;color:#12855b}
.taskpill.cancelled{background:#f3f5f7;color:#7b8794}
.taskpill.error{background:#fdecef;color:#b5344d}
.taskbar{flex:1;min-width:80px;height:5px;background:#eef1f4;border-radius:20px;overflow:hidden}
.taskbar>i{display:block;height:100%;border-radius:20px;background:#2f5bd4;transition:width .3s ease}
.taskrow.done .taskbar>i{background:#12855b}
.taskrow.cancelled .taskbar>i{background:#c2c8cd}
.taskrow.error .taskbar>i{background:#b5344d}
.taskrow .tpct{font-family:'IBM Plex Mono',monospace;font-size:11.5px;font-weight:600;color:#4b5763;width:38px;text-align:right;flex:none}
.taskrow .trows{font-family:'IBM Plex Mono',monospace;font-size:11px;color:#7b8794;flex:none}
.taskrow .teta{font-size:11px;color:#8a939b;flex:none;white-space:nowrap}
.taskrow .tact{height:26px;padding:0 10px;border-radius:7px;border:1px solid #e4e8ec;background:#fff;font-size:11px;font-weight:600;color:#4b5763;cursor:pointer;flex:none}
.taskrow .tact.cancel{color:#b5344d;border-color:#f2dcdc}
.taskempty{border:1px dashed #cfd6dc;border-radius:10px;padding:14px;text-align:center;font-size:12.5px;color:#8a939b}
@media (max-width:1250px){.syncstrip .synceyebrow{display:none}}
/* .hide guard — these shipops rules are appended AFTER the base .hide (line ~284), so an equal-
   specificity display rule here would win over it; scope-force like .tmpage/.stpage do. */
.shippage .hide,.syncstrip .hide{display:none !important}
/* partial-failure state + hover error popover on task rows */
.taskpill.partial{background:#fdf1dd;color:#a5680c}
.taskerr{position:relative;display:inline-flex;align-items:center;gap:4px;height:22px;padding:0 8px;border-radius:20px;background:#fdf1dd;color:#a5680c;border:1px solid #f5e3c2;font-size:10.5px;font-weight:700;cursor:help;flex:none}
.taskerr:focus-visible{outline:2px solid #a5680c;outline-offset:1px}
.taskerr .errpop{display:none;position:absolute;right:0;bottom:calc(100% + 7px);z-index:60;background:#fff;border:1px solid #e4e8ec;border-radius:10px;box-shadow:0 8px 24px rgba(16,24,40,.16);padding:9px 11px;min-width:280px;max-width:420px;max-height:220px;overflow-y:auto;font-weight:500;cursor:default;text-align:left}
.taskerr:hover .errpop,.taskerr:focus .errpop,.taskerr:focus-within .errpop{display:block}
.errpop .erow{display:flex;gap:10px;align-items:baseline;padding:4px 0;border-bottom:1px solid #f0f2f5;font-size:11.5px;color:#4b5763}
.errpop .erow:last-child{border-bottom:none}
.errpop .erow .mono{flex:none;font-size:11px;font-weight:600;color:#16202b}
.errpop .erow.emore{color:#8a939b;font-size:11px}
.errpop .erow span:last-child{overflow-wrap:anywhere;min-width:0}

/* ============== Laser / Printing Customization station pages (.cwpage) ==============
   docs/manual-readme/Laser-customization.md / Printing-Customization.md — the Tag Matching
   shell (.tmpage/.tmrail/.fpanel) is reused as-is; these are the aggregate-batch deltas only:
   the two-row chip filter bar, and ONE CSS-grid table whose header, group bars and rows share
   a column template (group bars are full-width flex rows in the same visual rhythm, which a
   <table> can't do — the sanctioned non-<table> exception, like the tag-creation rows). */
.cwpage .fpanel .fhead{padding:10px 16px;gap:12px}
.cwpage .fpanel .fbody{padding:12px 16px}
.cwpage .fpanel .ffoot{padding:9px 16px}
.cwheadprog{margin-left:auto;display:flex;align-items:center;gap:10px;min-width:170px}
.cwheadprog .mono{font-size:12px;font-weight:600;color:#667180}
.cwheadprog .pbar{flex:1;min-width:90px}
/* filter bar — two 28px-chip rows on the subtle surface */
.cwfilters{flex:none;display:flex;flex-direction:column;gap:7px;padding:9px 16px;border-bottom:1px solid #eef1f4;background:#fbfcfd}
.cwfrow{display:flex;align-items:center;gap:9px}
.cwflabel{flex:none;width:64px;font-size:9px;font-weight:700;letter-spacing:.4px;color:#8a939b}
.cwfchips{display:flex;align-items:center;gap:6px;flex-wrap:wrap}
/* WAVES strip — the shared .stqueue + .wavecard component (Tag Creation's wave list, the pick
   screen's queue strip), sized for this bench's filter bar. Deltas only; the card is untouched. */
.cwwaves .sectionlabel{padding:0 2px 5px;font-size:10.5px}
.cwwaves .stqueue{padding:0 2px 2px}
.cwwaves .wavecard{flex:none;min-width:158px;padding:8px 11px;gap:4px}
.cwwaves .wavecard .wno{font-size:13px}
.cwchip{display:inline-flex;align-items:center;gap:6px;height:28px;padding:0 10px;border:1px solid #e4e8ec;background:#fff;border-radius:20px;font-size:11.5px;font-weight:600;color:#4b5763;cursor:pointer}
.cwchip.on{border-color:var(--accent);background:#e8f5f2;color:var(--accentD)}
.cwchip .cwcount{font-family:'IBM Plex Mono',monospace;font-size:10px;font-weight:600;color:#8a939b;background:#f3f5f7;border-radius:20px;padding:1px 6px}
.cwchip .cwcount.full{background:var(--accent);color:#fff}
.cwsw{width:10px;height:10px;flex:none;border-radius:50%;box-shadow:inset 0 0 0 1px rgba(0,0,0,.12)}
/* the batch table — container + one shared column template per mode */
.cwtable{border:1px solid #e4e8ec;border-radius:10px;overflow:hidden}
.cwrow{display:grid;align-items:center;gap:0 10px;width:100%;padding:5px 11px;border:none;text-align:left;background:#fff;font:inherit}
/* …ORDER, WAVE (66px — the W{n} badge / the "No wave" pool), AGE (54px — ADR-036's waiting
   days), STATUS, tick */
.cwrow.laser{grid-template-columns:30px minmax(0,1.5fr) 72px minmax(0,1fr) minmax(0,1fr) minmax(0,1.3fr) 66px 54px 118px 34px}
.cwrow.printing{grid-template-columns:30px minmax(0,1.4fr) minmax(0,1.1fr) minmax(0,1fr) minmax(0,1fr) minmax(0,1.3fr) 66px 54px 118px 34px}
.cwrow>span{min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.cwrow.cwhead{background:#f3f6f7;font-size:9px;font-weight:700;letter-spacing:.4px;color:#8a939b;position:sticky;top:0;z-index:2;padding:7px 11px}
button.cwrow{cursor:pointer}
button.cwrow.odd{background:#fbfcfd}
button.cwrow.done{background:#f7fbf9;opacity:.5}
button.cwrow:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
.cwrow .cwn{font-size:10px;color:#a3abb2}
.cwrow .cwname{font-size:14px;font-weight:700;color:#16202b}
.cwrow.done .cwname{color:#7b8794}
.cwrow .cwfont{font-size:10.5px;font-weight:600;color:#6a6fc0}
.cwrow .cwmono{font-size:10.5px;color:#4b5763}
.cwrow .cwmono.dim{color:#7b8794}
.cwrow .cwstatus{font-size:9px;font-weight:700;letter-spacing:.4px;color:#c3cad1}
.cwrow .cwstatus.done{color:var(--accentD)}
/* the piece's planner wave — a badge, not plain text, so the eye can group a mixed setup by
   wave at a glance; .none is the unwaved pool ("No wave"), muted rather than absent */
.cwrow .cwwv{justify-self:start;font-family:'IBM Plex Mono',monospace;font-size:10px;font-weight:600;color:var(--accentD);background:#e8f5f2;border-radius:20px;padding:2px 7px}
.cwrow .cwwv.none{color:#8a939b;background:#f3f5f7}
/* how long the piece has been waiting (ADR-036) — quiet for today's own work, an amber badge
   for a carryover, so an old piece inside a fresh setup is visible at a glance instead of
   reading as one more row */
.cwrow .cwage{justify-self:start;font-family:'IBM Plex Mono',monospace;font-size:10px;font-weight:600;color:#a3abb2}
.cwrow .cwage.old{color:#a5680c;background:#fdefdd;border-radius:20px;padding:2px 7px}
.cwtick{width:18px;height:18px;border-radius:5px;border:1px solid #cfd6dc;background:#fff;display:flex;align-items:center;justify-content:center;font-size:12px;color:#fff}
.cwtick.done{background:var(--accent);border-color:var(--accent)}
/* group bar — one per machine-setup batch, inside the table */
.cwbar{display:flex;align-items:center;gap:9px;padding:7px 11px;border-top:1px solid #e4e8ec;background:#fbfcfd;flex-wrap:wrap}
.cwbar.done{background:#f4fbfa}
/* icon slot: SKU sample photo, or the product-initials tile when the SKU has no image —
   both 48px so mixed bars keep one height */
.cwbar .cwicon{width:48px;height:48px;flex:none;border-radius:9px;background:#eef1f4;color:#667180;display:flex;align-items:center;justify-content:center;font-size:13px;font-weight:700}
.cwbar img.cwicon{object-fit:cover;border:1px solid #e4e8ec;background:#fff}
.cwbar .cwkey{width:20px;height:20px;flex:none;border-radius:6px;background:#eef1f4;color:#667180;display:flex;align-items:center;justify-content:center;font-family:'IBM Plex Mono',monospace;font-size:11px;font-weight:700}
.cwbar .cwtitle{font-size:14.5px;font-weight:600;color:#16202b;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cwbar .cwsub{font-size:11px;color:#68727c;flex:none}
/* which waves this machine setup draws from — a setup spans waves by design (ADR-028), so the
   mix is stated on the bar in the same mono the row badges use */
.cwbar .cwwvmix{font-family:'IBM Plex Mono',monospace;font-size:10.5px;font-weight:600;color:var(--accentD);background:#e8f5f2;border-radius:20px;padding:2px 8px}
/* how much of this machine setup is carried over from an earlier day — same amber as the row
   badge, since a pooled load can mix the shift's work with a carryover (ADR-036) */
.cwbar .cwcarry{font-size:10.5px;font-weight:600;color:#a5680c;background:#fdefdd;border-radius:20px;padding:2px 8px}
.cwpill{flex:none;font-size:9px;font-weight:700;letter-spacing:.4px;background:#fdefdd;color:#a5680c;border-radius:20px;padding:3px 9px}
.cwpill.on{background:#e8f2ff;color:#2f5bd4}
.cwpill.done{background:#e6f4ec;color:#12855b}
.cwright{margin-left:auto;display:flex;align-items:center;gap:9px}
.cwbarprog{width:64px;height:6px;background:#eef1f4;border-radius:20px;overflow:hidden}
.cwbarprog>i{display:block;height:100%;background:var(--accent);border-radius:20px}
/* per-unit buyer artwork strip under a batch bar (photo products, e.g. photo lamps):
   lightbox thumb per unit + the original-file download for the print software */
.cwartstrip{display:flex;flex-wrap:wrap;gap:8px 14px;padding:7px 11px;background:#f8fafb;border-top:1px dashed #e4e8ec}
.cwartstrip .cwart{display:flex;align-items:center;gap:7px;min-width:0}
.cwartstrip button.cwartthumb{width:40px;height:40px;flex:none;border-radius:8px;border:1px solid #e4e8ec;background:#fff;cursor:zoom-in;padding:0;overflow:hidden}
.cwartstrip button.cwartthumb img{width:100%;height:100%;object-fit:cover;display:block}
.cwartstrip .cwartmeta{display:flex;flex-direction:column;min-width:0}
.cwartstrip .cwartrow{font-size:10px;color:#4b5763;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cwartstrip .cwartdl{font-size:10.5px;font-weight:600;color:#0f766e;text-decoration:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* the PER-ROW file line — a piece's own preview/artwork/print layout, sitting under its row
   (the strip above a setup keeps only what the whole setup shares). It is a sibling of the row
   button, so it repeats the row's stripe to read as part of it. */
.cwartstrip.cwrowart{padding:2px 11px 7px 51px;border-top:none;background:#fff;gap:6px 14px}
.cwartstrip.cwrowart.odd{background:#fbfcfd}
.cwartstrip.cwrowart.done{background:#f7fbf9;opacity:.5}
.cwartstrip.cwrowart button.cwartthumb{width:34px;height:34px}
.cwbar .btn{height:26px;padding:0 10px;font-size:11px}
/* the batch toggle's reverse state ("Undo batch") — outline teal, same slot as the primary */
.cwbar .btn.cwundobtn{color:var(--accentD);background:#fff;border-color:#cfe8e2;font-weight:600}
/* footer extras */
.cwkeys{font-size:10.5px;color:#8a939b;flex:none}
.cwkeys b{font-weight:700;color:#667180;font-family:'IBM Plex Mono',monospace}
#cwAllBtn.muted{background:#e6f4ec;color:#98c4ac;box-shadow:none}
@media (max-width:1000px){.cwbarprog{display:none}}

/* ---- Patch Sticking bench — the Sticker Customization page (docs/manual-readme/
   Sticker-customization.md) — deltas on the shared station-screen shell
   (.tmpage/.tmrail/.fpanel/.stempty/.qcard/.cwchip/.cwsw are reused as-is). The panel is a
   focused ORDER: type-filter bar on top, then the order header with its ‹ n/total › stepper,
   one guidance card per piece (reference photo left, work details + theme-patch panel
   right), pinned progress footer. */
.skpage .skfilters{flex:none;display:flex;align-items:center;gap:10px;padding:9px 16px;border-bottom:1px solid #eef1f4;background:#fbfcfd;flex-wrap:wrap}
.skflabel{flex:none;font-size:9px;font-weight:700;letter-spacing:.4px;color:#8a939b}
.skfchips{display:flex;align-items:center;gap:6px;flex-wrap:wrap}
.skorderwrap{flex:1;min-height:0;display:flex;flex-direction:column}
.skstepper{margin-left:auto;display:flex;align-items:center;gap:8px}
.skstepper .mono{font-size:12px;font-weight:600;color:#667180}
.skstepbtn{width:28px;height:28px;border:1px solid #e4e8ec;background:#fff;border-radius:8px;color:#667180;font-size:15px;cursor:pointer;display:flex;align-items:center;justify-content:center}
.skstepbtn:hover{background:#fafbfc}
.skeyebrow{font-size:9.5px;font-weight:700;letter-spacing:.6px;color:#97a1ac;margin-bottom:10px}
/* rail order cards — .qcard base + the bench's two info rows */
.skcard .skrow1,.skcard .skrow2{display:flex;align-items:center;gap:7px;min-width:0}
.skcard .skoid{font-size:11px;font-weight:600;color:#16202b;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.skcard .skrow1 .prpill{margin-left:auto;flex:none;font-size:9px;padding:1px 6px}
.skcard .skrow2{font-size:10px;color:#7b8794}
.skcard .skeye{font-size:8.5px;font-weight:700;letter-spacing:.5px;color:#a3abb2}
.skcard .skeye.inhand{color:#0f766e}
.skcard .skeye.ready{color:#12855b}
.skcard .skunit{margin-left:auto;flex:none;font-size:10px;color:#a3abb2}
.skcard.on{border-color:#0d9488;background:#f4fbfa;box-shadow:0 0 0 3px rgba(13,148,136,.13)}
/* job cards — one per physical piece, HALF-width (two per row; single column on narrow) */
.skjobs{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;align-items:start}
.skjob{display:flex;gap:18px;align-items:flex-start;border:1px solid #e4e8ec;background:#fff;border-radius:13px;padding:14px;cursor:pointer}
.skjob.applied{opacity:.55;border-color:#cfe8e2;background:#f4fbfa}
.skjob:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.skmedia{flex:none;width:190px;display:flex;flex-direction:column;gap:6px}
/* reference photo — the SKU's catalogue sample, the finish the operator matches against */
.skref img{display:block;width:190px;height:190px;object-fit:contain;background:#fff;border:1px solid #e4e8ec;border-radius:11px}
.skcap{display:flex;align-items:center;justify-content:space-between;gap:8px;font-size:8.5px;font-weight:700;letter-spacing:.5px;color:#97a1ac}
.skcap .mono{font-size:10px;text-align:right}
.skinfo{flex:1;min-width:0;display:flex;flex-direction:column;gap:8px}
.skinforow{display:flex;align-items:center;gap:9px;min-width:0}
.sktypeicon{width:30px;height:30px;flex:none;border-radius:8px;background:#f3f5f7;display:flex;align-items:center;justify-content:center;font-size:16px}
.skinforow .cwkey{width:20px;height:20px;flex:none;border-radius:6px;background:#eef1f4;color:#667180;display:flex;align-items:center;justify-content:center;font-family:'IBM Plex Mono',monospace;font-size:11px;font-weight:700}
.skitem{font-size:19px;font-weight:600;letter-spacing:-.3px;color:#16202b;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.skchips{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.skcol{display:inline-flex;align-items:center;gap:6px;font-size:11px;font-weight:600;color:#3a444e;background:#f3f5f7;border-radius:20px;padding:3px 9px}
.sksku{display:inline-flex;align-items:center;gap:5px;font-family:'IBM Plex Mono',monospace;font-size:12px;font-weight:600;color:#0f766e;background:#e8f5f2;border:1px solid #cfe8e2;border-radius:6px;padding:2px 8px}
.sksku .skk{font-size:8.5px;font-weight:700;color:#5aa79c;font-family:'IBM Plex Sans',system-ui,sans-serif}
.skqty{font-size:11px;color:#667180}
.skname{font-family:'IBM Plex Mono',monospace;font-size:22px;font-weight:600;color:#0f766e;overflow-wrap:anywhere;line-height:1.2}
.skplace{font-size:13px;color:#4b5763;line-height:1.45}
/* theme patches — the acceptance-critical pick (a wrong patch is a common defect), so LARGE
   and unmissable: its own amber panel with each patch as a bold chip */
.skpatches{display:flex;align-items:center;gap:9px;flex-wrap:wrap;background:#fdf7ec;border:1px solid #f3e2c4;border-radius:11px;padding:10px 13px}
.skpatches .skpeyebrow{flex:none;font-size:9.5px;font-weight:700;letter-spacing:.6px;color:#a5680c}
/* ---- The theme-patch chip (ui.js patchChipHTML) — ONE look for every screen that shows a
   patch: tag matching, tray assignment, QC, boxing, the rework desk and the Patch Sticking
   bench's own amber panel. The picture leads and the motif names it, because a motif name is
   not a check an operator can make against the physical piece (the live corpus carries
   "Rainbow Could" and "Astronanut" as ordered). Without artwork the chip is text alone —
   .haspic carries every image rule, so a missing thumb can never leave an empty box.
   36px is the in-card size; the Patch Sticking panel scales it to the 44px .qcartthumb the
   rest of that screen's artwork uses, that bench being the one that STICKS the thing. */
.opatch{display:inline-flex;align-items:center;gap:8px;margin-top:4px;max-width:100%;font-size:13px;font-weight:600;color:#8a5408;background:#fff;border:1px solid #f3e2c4;border-radius:8px;padding:4px 10px;vertical-align:middle}
.opatch.haspic{padding:4px 10px 4px 4px}
/* a <button> where zoom is opt-in, a plain <span> inside the <button> cards (ui.js explains
   why) — the box rules must hold for both, so nothing here assumes an element. */
.opatch .opthumb{width:36px;height:36px;flex:none;border-radius:6px;border:1px solid #f3e2c4;background:#fff;padding:0;overflow:hidden;display:block}
button.opthumb{cursor:zoom-in}
.opatch .opthumb img{width:100%;height:100%;object-fit:contain;display:block}
/* break-word, NOT anywhere: `anywhere` also shrinks the flex item's min-content size, so
   in boxing's 185px card the motif shredded into "Foot / ball". This breaks a word only
   when it genuinely cannot fit. */
.opatch .opname{min-width:0;overflow-wrap:break-word}
.opatch .opdl{flex:none;font-size:12px;font-weight:600;color:#a5680c;text-decoration:none}
/* Boxing's cards are by far the narrowest that carry one: 185px minus the 40px media column
   and the 24px badge leaves .bxmain about 90px, so a 36px thumb beside the motif shredded it
   ("Foot / ball"). Only here the chip STACKS — thumb over name — which hands the motif the
   card's full text width. Same chip, same tones; the axis is what the width forces. */
.bxmain .opatch{display:flex;flex-direction:column;align-items:flex-start;gap:4px;width:100%;font-size:12px;padding:5px 7px}
.bxmain .opatch.haspic{padding:5px 7px}
.bxmain .opthumb{width:40px;height:40px}
/* the Patch Sticking panel's own scale — same chip, the bench that sticks it gets it bigger */
.skpatches .opatch{font-size:15px;font-weight:700;gap:9px;margin-top:0;padding:5px 12px}
.skpatches .opatch.haspic{padding:5px 12px 5px 5px}
.skpatches .opthumb{width:44px;height:44px;border-radius:7px}
.sknote{background:#fdf7ec;border:1px solid #f3e2c4;color:#a5680c;font-size:12px;line-height:1.5;border-radius:9px;padding:8px 11px}
.skactions{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.skstatus{flex:none;font-size:9px;font-weight:700;letter-spacing:.5px;background:#fdf3e0;color:#a5680c;border-radius:20px;padding:3px 9px}
.skstatus.done{background:#e6f4ec;color:#0f766e}
.skjob .skmarkbtn{height:30px;padding:0 12px;font-size:12px}
/* undo state — outline teal, the cwundobtn convention */
.skjob.applied .skmarkbtn{color:var(--accentD);background:#fff;border-color:#cfe8e2;font-weight:600}
.skfileicon{width:52px;height:52px;border-radius:12px;background:#e8f5f2;color:#0f766e;display:flex;align-items:center;justify-content:center;font-size:24px}
@media (max-width:1500px){
  .skmedia{width:150px}
  .skref img{width:150px;height:150px}
}
@media (max-width:1100px){
  .skjobs{grid-template-columns:minmax(0,1fr)}
  .skname{font-size:19px}
}

/* ---- QC Rejects & Rework desk (docs/manual-readme/QC Rejects & Rework.md) — deltas on the
   shared station-screen shell (.tmpage/.tmrail/.fpanel/.stempty/.qcard/.cwchip/.cwsw reused
   as-is, the sk* convention). The rail is a PROBLEM queue, so its count pill and the order
   header's REJECT chip are the two deliberately red elements; everything else stays in the
   shell's neutral palette. */
.rwpage .railcount.rwred{color:#b5344d;background:#fdecef;border-color:#f0c9d1}
.rwpage .rwfilters{flex:none;display:flex;align-items:center;gap:10px;padding:9px 16px;border-bottom:1px solid #eef1f4;background:#fbfcfd;flex-wrap:wrap}
.rwflabel{flex:none;font-size:9px;font-weight:700;letter-spacing:.4px;color:#8a939b}
.rwfchips{display:flex;align-items:center;gap:6px;flex-wrap:wrap}
.rworderwrap{flex:1;min-height:0;display:flex;flex-direction:column}
.rwstepper{margin-left:auto;display:flex;align-items:center;gap:8px}
.rwstepper .mono{font-size:12px;font-weight:600;color:#667180}
.rwstepbtn{width:28px;height:28px;border:1px solid #e4e8ec;background:#fff;border-radius:8px;color:#667180;font-size:15px;cursor:pointer;display:flex;align-items:center;justify-content:center}
.rwstepbtn:hover{background:#fafbfc}
/* the one deliberately alarming element — the REJECT id chip */
.idchip.rwreject{background:#fdecef;color:#b5344d;border:1px solid #f2dcdc}
.idchip.rwreject .idk{color:#c26478}
.rwshipby{font-size:12px;font-weight:700;color:#b5344d}
/* rail reject cards — .qcard base + the desk's rows */
.rwcard .rwrow1,.rwcard .rwrow2{display:flex;align-items:center;gap:7px;min-width:0}
.rwcard .rwoid{font-size:11px;font-weight:600;color:#16202b;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rwcard .rwrow1 .prpill{margin-left:auto;flex:none;font-size:9px;padding:1px 6px}
.rwcard .rwrow2{font-size:10px;color:#7b8794}
.rwcard .rweye{font-size:8.5px;font-weight:700;letter-spacing:.5px}
.rwcard .rweye.open{color:#0f766e}
.rwcard .rweye.awaiting{color:#b5344d}
.rwcard .rweye.rework{color:#2f5bd4}
.rwcard .rweye.ready{color:#12855b}
.rwcard .rwshipmini{margin-left:auto;flex:none;color:#b5344d;font-weight:600}
.rwcard .rwby{font-size:9.5px;color:#a3abb2;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rwcard.on{border-color:#0d9488;background:#f4fbfa;box-shadow:0 0 0 3px rgba(13,148,136,.13)}
/* accountability, deliberately low-key: one grey line, no avatar, no badge, no colour */
.rwprov{font-size:11.5px;color:#8a939b;margin-bottom:10px;cursor:default}
/* part cards — one per flagged part; card tint tracks state */
.rwparts{display:flex;flex-direction:column;gap:12px}
.rwjob{display:flex;gap:18px;align-items:flex-start;border:1px solid #f0c9d1;background:#fffafb;border-radius:13px;padding:14px}
.rwjob.rework{border-color:#cfe0f7;background:#f7faff}
.rwjob.done{border-color:#cfe8e2;background:#f4fbfa;opacity:.6}
.rwleft{flex:1;min-width:0;display:flex;flex-direction:column;gap:8px}
.rwinforow{display:flex;align-items:center;gap:9px;min-width:0}
.rwinforow .cwkey{width:20px;height:20px;flex:none;border-radius:6px;background:#eef1f4;color:#667180;display:flex;align-items:center;justify-content:center;font-family:'IBM Plex Mono',monospace;font-size:11px;font-weight:700}
.rwitem{font-size:19px;font-weight:600;letter-spacing:-.3px;color:#16202b;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rwchips{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.rwcol{display:inline-flex;align-items:center;gap:6px;font-size:11px;font-weight:600;color:#3a444e;background:#f3f5f7;border-radius:20px;padding:3px 9px}
.rwpcode{font-size:11px;font-weight:600;color:#57626e;background:#eef1f4;border-radius:6px;padding:2px 8px}
.rwpill{font-size:9px;font-weight:700;letter-spacing:.5px;border-radius:20px;padding:3px 9px}
.rwpill.awaiting{background:#fdecef;color:#b5344d}
.rwpill.rework{background:#e8f2ff;color:#2f5bd4}
.rwpill.done{background:#e6f4ec;color:#12855b}
/* what was customized on it — same treatment the customization stations use */
.rwcustom{display:flex;align-items:baseline;gap:12px;flex-wrap:wrap}
.rwtag{font-size:17px;font-weight:700;font-family:'IBM Plex Mono',monospace;overflow-wrap:anywhere}
.rwcharm{font-size:14px;font-weight:600;color:#2f5bd4}
.rweyebrow{flex:none;font-size:9px;font-weight:700;letter-spacing:.5px;color:#97a1ac}
.rwreasons{display:flex;align-items:center;gap:7px;flex-wrap:wrap}
.rwreason{font-size:12px;font-weight:600;color:#8a2d43;background:#fdeef1;border:1px solid #f0c9d1;border-radius:8px;padding:4px 10px}
/* right column — routing, replacement stock, the advance action */
.rwright{flex:none;width:260px;display:flex;flex-direction:column;align-items:flex-start;gap:8px}
.rwroutes{display:flex;gap:6px;flex-wrap:wrap}
.rwroutes .rwroute{height:26px;font-size:11px;padding:0 9px}
.rwstock{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.rwstockok{font-size:11.5px;font-weight:700;color:#12855b}
.rwstockout{font-size:11.5px;font-weight:700;color:#b5344d}
.rwreservebtn{height:28px;padding:0 11px;font-size:11.5px}
.rwreserved{font-size:11.5px;font-weight:700;color:#0f766e;background:#e8f5f2;border:1px solid #cfe8e2;border-radius:20px;padding:4px 11px}
.rwadvbtn{height:30px;padding:0 12px;font-size:12px;margin-top:auto}
.rwjob.done .rwadvbtn{color:var(--accentD);background:#fff;border-color:#cfe8e2;font-weight:600}
/* footer actions: Escalate = the QC reject outline-red look at footer height; Return to QC
   muted (still clickable — it explains itself) until the derived order state is ready */
.rwescbtn{height:40px;font-size:13.5px;padding:0 16px}
#rwReleaseBtn.muted{background:#e6f4ec;color:#98c4ac;box-shadow:none}
@media (max-width:1100px){
  .rwjob{flex-direction:column}
  .rwright{width:100%}
  .rwadvbtn{margin-top:0}
}


/* The opt-in second line on an empty table (TableKit PageState `emptyHint`): the first line
   reports that there is nothing, this one says what would put something there. */
.empty-hint { margin-top:6px; font-size:12px; line-height:1.5; color:#8a939b; font-weight:400; }


/* Settings hub: the launcher tiles are LINKS, so they need the affordance a .wstile (a plain
   div on the Work-Stations landing) never needed. Layout is inherited untouched. */
a.wstile{text-decoration:none;color:inherit;display:block;transition:border-color .12s,box-shadow .12s}
a.wstile:hover{border-color:var(--accent);box-shadow:0 2px 8px rgba(15,118,110,.10)}
a.wstile:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
a.wstile .wstile-label{color:var(--accentD)}
