:root{
  --bg:#11151c; --panel:#1a212b; --panel2:#222b38; --line:#2f3b4c;
  --text:#e7edf5; --dim:#93a2b6; --gold:#d9a441; --ok:#3fb27f;
  --bad:#e2585f; --warn:#e0a63c;
  /* Derived surfaces. Were hard-coded hexes until the light theme landed -
     anything not expressed as a token here will not flip with the theme. */
  /* Surfaces, darkest to lightest: page --bg, then --row for data rows, then
     --hover / --sel, then --group for the group-header band. The band is the
     LIGHTEST of them on purpose - taking it below --row squeezes it between
     the rows and the page, where it reads as either one or the other. Every
     one of these must stay clear of both neighbours. */
  --row:#232d3a; --group:#3d4d69; --group-hover:#47597a;
  /* Grouping levels. The BANDS stay in the theme's own slate family, one
     step apart, so nothing fights the gold buttons or the data rows; what
     names a level is its 5px stripe and the accent its label is painted in
     (gold, teal, blue, violet). Saturated bands were tried first and read
     as four unrelated colours pasted onto the grid.
     All four sit ABOVE --row (#232d3a) in lightness on purpose: a band that
     dips below it lands between the rows and the page and stops reading as
     a header at all. The ladder is a constant 1.19:1 step so no two levels
     touch, and the deepest still clears --row by 1.23:1. */
  --lvl-0:#465c80; --lvl-0-hover:#516a93; --lvl-0-line:#d9a441;
  --lvl-1:#3d5170; --lvl-1-hover:#475e81; --lvl-1-line:#3fb2a8;
  --lvl-2:#354660; --lvl-2-hover:#3e5271; --lvl-2-line:#7fa9e0;
  --lvl-3:#2d3b50; --lvl-3-hover:#354661; --lvl-3-line:#9d8ae4;
  --hover:#2c3849; --sel:#334158; --info:#7fa9e0;
  --on-gold:#1a1206;
  --shadow-menu:rgba(0,0,0,.5); --shadow-modal:rgba(0,0,0,.6);
  --backdrop:rgba(6,9,13,.72);
  color-scheme:dark;
}
/* Light theme. Set by the inline script in base.html before first paint,
   toggled by the sun/moon button in the sidebar, persisted in localStorage
   under "weigh-theme". Only the tokens change - no rule below is duplicated. */
:root[data-theme="light"]{
  --bg:#f4f6f9; --panel:#ffffff; --panel2:#eef1f6; --line:#d3dae4;
  --text:#1a212b; --dim:#5d6b7f; --gold:#a97614; --ok:#1f8f5f;
  --bad:#c93840; --warn:#b57d10;
  --row:#ffffff; --group:#c9d5e6; --group-hover:#bbcadf;
  --lvl-0:#bccbe3; --lvl-0-hover:#adbfdb; --lvl-0-line:#a97614;
  --lvl-1:#ccd8ec; --lvl-1-hover:#bdcce5; --lvl-1-line:#17827a;
  --lvl-2:#dbe3f1; --lvl-2-hover:#ccd7ea; --lvl-2-line:#2c62a8;
  --lvl-3:#e9eef7; --lvl-3-hover:#dae2f1; --lvl-3-line:#6b3fa0;
  --hover:#eef2f7; --sel:#dce7f6; --info:#2c62a8;
  --on-gold:#ffffff;
  --shadow-menu:rgba(20,30,45,.16); --shadow-modal:rgba(20,30,45,.24);
  --backdrop:rgba(30,40,55,.42);
  color-scheme:light;
}
*{box-sizing:border-box}
/* Must come before any rule that sets display. A plain `display:flex` on
   .modal-back or `display:inline-block` on button beats the [hidden]
   attribute's UA default, which left the weigh modal permanently open and
   made its close button a no-op. !important keeps that from happening
   again for any element hidden this way. */
[hidden]{display:none !important}
body{margin:0;background:var(--bg);color:var(--text);
  font:14px/1.45 "Segoe UI",system-ui,sans-serif}
a{color:var(--gold);text-decoration:none}

/* ---- Left sidebar -------------------------------------------------------
   The nav is a fixed 56px icon rail. It widens to --nav-open-w on hover or
   when anything inside it takes focus, drawing OVER the page so nothing
   reflows. Pinning (the << / >> button, kept in localStorage under
   "weigh-nav") holds it open and reserves the width instead, which is why
   --nav-w is re-declared on the pinned root: every layout rule below and the
   nested-table widths further down read that one token. */
:root{--nav-w:56px;--nav-open-w:224px;--pad:18px}
:root.nav-pinned{--nav-w:var(--nav-open-w)}

.sidenav{position:fixed;top:0;left:0;bottom:0;z-index:60;width:56px;
  display:flex;flex-direction:column;overflow:hidden;
  background:var(--panel);border-right:1px solid var(--line);
  transition:width .16s ease}
:root.nav-pinned .sidenav,
.sidenav:hover,.sidenav:focus-within{width:var(--nav-open-w);
  box-shadow:6px 0 24px var(--shadow-menu)}
:root.nav-pinned .sidenav:not(:hover):not(:focus-within){box-shadow:none}

/* Every row in the rail is icon + label on one 56px-tall line. The label is
   clipped, not hidden, so it slides out with the width instead of popping. */
.sidenav .nav-brand,.sidenav nav a,.sidenav .nav-foot > *{
  display:flex;align-items:center;gap:12px;height:40px;padding:0 18px 0 17px;
  white-space:nowrap}
.sidenav .lbl{opacity:0;transition:opacity .12s ease .02s;
  overflow:hidden;text-overflow:ellipsis}
:root.nav-pinned .sidenav .lbl,
.sidenav:hover .lbl,.sidenav:focus-within .lbl{opacity:1}
.sidenav .ico,.sidenav .gly{flex:0 0 20px;width:20px;text-align:center;
  font-size:15px;line-height:1}

.sidenav .nav-brand{height:52px;border-bottom:1px solid var(--line);
  color:var(--gold);font-weight:700;letter-spacing:.5px}
.sidenav .nav-brand .logo{flex:0 0 20px;width:20px;text-align:center;
  font-size:14px}

.sidenav nav{display:flex;flex-direction:column;gap:2px;padding:8px 6px;
  flex:1;overflow-y:auto;overflow-x:hidden}
.sidenav nav a{color:var(--text);border-radius:8px;padding:0 11px}
.sidenav nav a:hover{background:var(--panel2)}
.sidenav nav a.on{background:var(--panel2);color:var(--gold)}
/* The rail is only 56px wide when collapsed, so the active page has to be
   readable from the icon alone. */
.sidenav nav a.on{box-shadow:inset 3px 0 0 var(--gold)}

.sidenav .nav-foot{border-top:1px solid var(--line);padding:6px;
  display:flex;flex-direction:column;gap:2px}
.sidenav .nav-foot button{background:none;border:none;border-radius:8px;
  color:var(--dim);cursor:pointer;font:inherit;text-align:left;padding:0 11px;
  width:100%}
.sidenav .nav-foot button:hover{background:var(--panel2);color:var(--text)}
.sidenav .who{color:var(--dim);font-size:12px;padding:0 11px;height:36px}
.sidenav .who .gly{color:var(--ok);font-size:9px}
.sidenav .who .logout{margin-left:6px}

/* Slim bar for phones/tablets, where the rail is off-canvas. Hidden above
   the breakpoint - see the media blocks at the end. */
.mobilebar{display:none;align-items:center;gap:12px;padding:8px 12px;
  background:var(--panel);border-bottom:1px solid var(--line);
  position:sticky;top:0;z-index:55}
.mobilebar .brand{font-weight:700;color:var(--gold);margin-right:auto}
.nav-scrim{display:none;position:fixed;inset:0;background:var(--backdrop);
  z-index:59}

/* Full-bleed content. There is no max-width: the grids are the point of the
   app and every one of them is happier with the extra column room. */
main{padding:16px var(--pad);max-width:none;margin:0}
main.with-nav{margin-left:var(--nav-w);transition:margin-left .16s ease}
h1{font-size:20px;margin:0}
h2{font-size:15px;margin:0}
h3.sec{font-size:13px;text-transform:uppercase;letter-spacing:.5px;
  color:var(--dim);margin:18px 0 8px;font-weight:600}
.dim{color:var(--dim)}

.page-head{display:flex;align-items:center;gap:12px;margin-bottom:12px;flex-wrap:wrap}
.page-head h1{margin-right:auto}

/* Filters and the column menu share ONE row and do not wrap: the search box
   is the only elastic thing in it, so every control keeps its natural width
   and the row shrinks by shrinking the search box. Below the tablet
   breakpoint it wraps again - see the media blocks at the end. */
.filterbar{display:flex;align-items:center;gap:8px;flex-wrap:nowrap;
  margin-bottom:12px}
.filters{display:flex;gap:8px;flex:1;min-width:0;flex-wrap:nowrap;
  align-items:center}
.filters .q-in{flex:1 1 auto;min-width:110px}
.filters select{max-width:190px}
.filters > *{flex:0 0 auto}

input,select,textarea{background:var(--panel2);color:var(--text);
  border:1px solid var(--line);border-radius:6px;padding:7px 9px;font:inherit}
input:focus,select:focus,textarea:focus{outline:2px solid var(--gold);outline-offset:-1px}
input[disabled],textarea[disabled]{opacity:.55}
textarea{width:100%;resize:vertical}
input[type=checkbox]{accent-color:var(--gold);width:15px;height:15px}

button,.btn{background:var(--panel2);color:var(--text);border:1px solid var(--line);
  border-radius:6px;padding:7px 14px;font:inherit;cursor:pointer;display:inline-block}
button:hover,.btn:hover{border-color:var(--gold)}
button.primary,.btn.primary{background:var(--gold);color:var(--on-gold);border-color:var(--gold);font-weight:600}
button.warn{background:var(--warn);color:var(--on-gold);border-color:var(--warn);font-weight:600}
button.danger{background:transparent;color:var(--bad);border-color:var(--bad)}
button[disabled]{opacity:.5;cursor:default}
.btn.small,button.small{padding:4px 10px;font-size:12px}
button.link{background:none;border:none;color:var(--gold);padding:0;font-size:12px}

/* 3-dot menu */
.menu-wrap{position:relative}
button.dots{padding:6px 10px;font-size:17px;line-height:1}
.menu{position:absolute;right:0;top:110%;z-index:40;width:250px;
  background:var(--panel);border:1px solid var(--line);border-radius:10px;
  box-shadow:0 12px 32px var(--shadow-menu);padding:6px;max-height:70vh;overflow:auto}
.menu-sec{padding:6px 4px}
.menu-sec + .menu-sec{border-top:1px solid var(--line);margin-top:4px}
.menu-title{display:flex;justify-content:space-between;align-items:center;
  font-size:11px;text-transform:uppercase;letter-spacing:.5px;color:var(--dim);
  padding:2px 6px 6px}
.menu-hint{font-size:11px;color:var(--dim);margin:0 6px 6px;line-height:1.35}
.menu-item{display:flex;align-items:center;gap:8px;padding:4px 6px;border-radius:5px;cursor:pointer}
.menu-item:hover{background:var(--panel2)}
.order-badge{margin-left:auto;color:var(--gold);font-size:11px;font-weight:700}

.card{background:var(--panel);border:1px solid var(--line);border-radius:10px;
  padding:16px;margin-bottom:16px}
.card.auth{max-width:380px;margin:60px auto}
.card.auth h1{margin-bottom:4px}
.card.auth .sub{color:var(--dim);margin:0 0 18px}
.card.auth label{display:block;margin-bottom:12px;color:var(--dim);font-size:13px}
.card.auth input{width:100%;margin-top:4px}
.card.auth button{width:100%;margin-top:6px}
label.block{display:block;color:var(--dim);font-size:13px;margin-bottom:10px}
label.block input,label.block textarea{margin-top:4px}
.inline-form{display:flex;gap:8px;align-items:center;flex-wrap:wrap}
.chk{display:flex;align-items:center;gap:5px;color:var(--dim)}

table.grid{width:100%;border-collapse:collapse;background:var(--row);
  border:1px solid var(--line);border-radius:10px;overflow:hidden}
table.grid th{background:var(--group);text-align:left;padding:9px 10px;
  font-size:12px;text-transform:uppercase;letter-spacing:.4px;color:var(--dim);
  border-bottom:1px solid var(--line);white-space:nowrap}
table.grid td{padding:7px 10px;border-bottom:1px solid var(--line)}
table.grid.tight td,table.grid.tight th{padding:5px 8px;font-size:13px}
table.grid tbody tr:hover{background:var(--hover)}
table.grid tr.sel{background:var(--sel)}
table.grid .num{text-align:right}
table.grid .right{text-align:right;color:var(--dim)}
table.grid .strong{font-weight:700}
table.grid tfoot td{background:var(--group);border-top:2px solid var(--line);padding:9px}
.chk-col{width:34px;text-align:center}
.mono{font-family:Consolas,monospace}
.empty{text-align:center;color:var(--dim);padding:24px}

tr.group-row{background:var(--group);cursor:pointer}
tr.group-row:hover{background:var(--group-hover)}
tr.group-row td{border-bottom:1px solid var(--line)}
tr.group-row .glabel{color:var(--dim);font-size:12px;text-transform:uppercase;
  letter-spacing:.4px;margin-right:4px}
tr.group-row .caret{color:var(--gold);margin-right:6px}
tr.group-row.d1 td:nth-child(2){padding-left:26px}
tr.group-row.d2 td:nth-child(2){padding-left:46px}
tr.group-row.d3 td:nth-child(2){padding-left:66px}

/* Level colouring. orders.js puts d<depth> on every group row; depth 4 and
   deeper reuse the last step rather than fading into the data rows. The
   stripe is on the first cell because a border on the tr itself is dropped
   by border-collapse. */
tr.group-row.d0{background:var(--lvl-0)}
tr.group-row.d0:hover{background:var(--lvl-0-hover)}
tr.group-row.d0 td:first-child{box-shadow:inset 5px 0 0 var(--lvl-0-line)}
tr.group-row.d1{background:var(--lvl-1)}
tr.group-row.d1:hover{background:var(--lvl-1-hover)}
tr.group-row.d1 td:first-child{box-shadow:inset 5px 0 0 var(--lvl-1-line)}
tr.group-row.d2{background:var(--lvl-2)}
tr.group-row.d2:hover{background:var(--lvl-2-hover)}
tr.group-row.d2 td:first-child{box-shadow:inset 5px 0 0 var(--lvl-2-line)}
tr.group-row.d3,tr.group-row.d4,tr.group-row.d5{background:var(--lvl-3)}
tr.group-row.d3:hover,tr.group-row.d4:hover,tr.group-row.d5:hover{
  background:var(--lvl-3-hover)}
tr.group-row.d3 td:first-child,tr.group-row.d4 td:first-child,
tr.group-row.d5 td:first-child{box-shadow:inset 5px 0 0 var(--lvl-3-line)}
/* One type size per level, stepping down with depth, so the outermost
   group reads first even where two bands sit next to each other. The whole
   row scales - value, count and totals - not just the value. */
tr.group-row.d0 td{font-size:16px}
tr.group-row.d0 b{font-size:17px;font-weight:700}
tr.group-row.d1 td{font-size:14px}
tr.group-row.d1 b{font-size:15px;font-weight:700}
tr.group-row.d2 td{font-size:13px}
tr.group-row.d2 b{font-size:13px;font-weight:700}
tr.group-row.d3 td,tr.group-row.d4 td,tr.group-row.d5 td{font-size:12px}
tr.group-row.d3 b,tr.group-row.d4 b,tr.group-row.d5 b{font-size:12px;
  font-weight:600}
/* The label chip shrinks with the row but never below legible. */
tr.group-row.d0 .glabel{font-size:12px}
tr.group-row.d1 .glabel{font-size:11px}
tr.group-row.d2 .glabel,tr.group-row.d3 .glabel,
tr.group-row.d4 .glabel,tr.group-row.d5 .glabel{font-size:10px}
/* The level's accent names it: caret, label and karat markers all take it.
   The band itself stays slate, so this is what tells two nested groups
   apart at a glance. */
tr.group-row.d0 .caret,tr.group-row.d0 .glabel,
tr.group-row.d0 .kchip i{color:var(--lvl-0-line)}
tr.group-row.d1 .caret,tr.group-row.d1 .glabel,
tr.group-row.d1 .kchip i{color:var(--lvl-1-line)}
tr.group-row.d2 .caret,tr.group-row.d2 .glabel,
tr.group-row.d2 .kchip i{color:var(--lvl-2-line)}
tr.group-row.d3 .caret,tr.group-row.d3 .glabel,tr.group-row.d3 .kchip i,
tr.group-row.d4 .caret,tr.group-row.d4 .glabel,tr.group-row.d4 .kchip i,
tr.group-row.d5 .caret,tr.group-row.d5 .glabel,
tr.group-row.d5 .kchip i{color:var(--lvl-3-line)}
/* Totals and counts sit on a band, not on --panel, so --dim reads too weak
   there. The label is NOT in here - it carries the level's accent colour,
   and an opacity on top of that would mute exactly what names the level. */
tr.group-row .gtot,tr.group-row .kchip,
tr.group-row .dim{color:var(--text);opacity:.8}

.pill{display:inline-block;padding:2px 9px;border-radius:20px;font-size:11px;
  border:1px solid var(--line);white-space:nowrap}
.pill.pending{color:var(--dim)}
.pill.checked{color:var(--ok);border-color:var(--ok)}
.pill.onexit{color:var(--warn);border-color:var(--warn)}
.pill.exited{color:var(--gold);border-color:var(--gold)}
.pill.invoiced{color:var(--info);border-color:var(--info)}
.pill.notinvoiced{color:var(--dim)}
.pill.external{color:var(--warn);border-color:var(--warn)}
.pill.finished{color:var(--info);border-color:var(--info)}
.pill.cancelled{color:var(--bad);border-color:var(--bad)}

.banner{background:var(--panel2);border:1px solid var(--line);border-left:3px solid var(--ok);
  padding:9px 14px;border-radius:6px;margin-bottom:12px}
.banner.locked{border-left-color:var(--gold)}

.msg{color:var(--dim)}
.msg.bad{color:var(--bad)}
.msg.ok{color:var(--ok)}
.bad{color:var(--bad)}
.warnval{color:var(--warn)}

.flashes{margin-bottom:14px}
.flash{padding:9px 14px;border-radius:6px;margin-bottom:8px;border:1px solid var(--line)}
.flash.error{border-color:var(--bad);color:var(--bad)}
.flash.ok{border-color:var(--ok);color:var(--ok)}

/* Weigh modal */
.modal-back{position:fixed;inset:0;background:var(--backdrop);z-index:100;
  display:flex;align-items:center;justify-content:center;padding:20px}
.modal{background:var(--bg);border:1px solid var(--line);border-radius:12px;
  width:min(1800px,96vw);max-height:92vh;display:flex;flex-direction:column;
  box-shadow:0 24px 64px var(--shadow-modal)}
.modal-head{display:flex;align-items:center;gap:12px;padding:14px 18px;
  border-bottom:1px solid var(--line)}
.modal-head h2{margin-right:auto}
button.x{background:none;border:none;font-size:22px;line-height:1;color:var(--dim);padding:0 6px}
button.x:hover{color:var(--text)}
.modal-body{padding:16px 18px;overflow:auto;flex:1}
.modal-foot{display:flex;align-items:center;gap:10px;padding:12px 18px;
  border-top:1px solid var(--line);background:var(--panel)}
.modal-foot .spacer{flex:1}

.two-col{display:grid;grid-template-columns:1fr 360px;gap:22px;align-items:start}
@media (max-width:1100px){.two-col{grid-template-columns:1fr}}
#pkg-table input.num-in{width:100%;text-align:right;font-family:Consolas,monospace}

.calc{background:var(--panel);border:1px solid var(--line);border-radius:10px;
  padding:14px;margin-top:18px}
.calc .big-input{width:100%;font-size:20px;text-align:right;
  font-family:Consolas,monospace;padding:8px 10px}
.calc-row{display:flex;justify-content:space-between;align-items:baseline;
  padding:5px 0;border-bottom:1px solid var(--line);font-family:Consolas,monospace}
.calc-row span{font-family:"Segoe UI",sans-serif;color:var(--dim);font-size:12px}
.calc-row.result{border-bottom:none;margin-top:4px;padding-top:8px;
  border-top:2px solid var(--line)}
.calc-row.result b{font-size:22px;color:var(--gold)}
/* The exit line's gross is typed, not just read: it looks like the result
   figure until it is focused, so the row still reads as a total. */
.calc-row.result input.result-in{font-size:22px;color:var(--gold);
  font-family:Consolas,monospace;font-weight:700;text-align:right;
  background:none;border:1px solid transparent;border-radius:6px;
  padding:0 6px;width:11ch}
.calc-row.result input.result-in:hover{border-color:var(--line)}
.calc-row.result input.result-in:focus{background:var(--panel2);
  border-color:var(--gold);outline:none}
.calc-row.result b.bad{color:var(--bad)}
.calc-row.dim b{color:var(--dim)}
.calc-note{min-height:16px;font-size:12px;margin:6px 0 10px}

details.ref{margin-top:18px}
details.ref summary{cursor:pointer;color:var(--dim);font-size:13px;padding:6px 0}
details.ref summary:hover{color:var(--text)}

/* Theme toggle button. Lives in the sidebar (and the phone bar) when signed
   in and floats in the top-right corner on the login/setup screens, which
   have neither. */
button.theme-tgl{background:none;border:1px solid var(--line);border-radius:6px;
  padding:5px 9px;font-size:14px;line-height:1;cursor:pointer;color:var(--text)}
button.theme-tgl:hover{border-color:var(--gold)}
button.theme-tgl.floating{position:fixed;top:14px;right:16px;z-index:50;
  background:var(--panel)}

/* Group-by bar. Lives on the orders page itself - the 3-dot menu now only
   holds column show/hide. */
.groupbar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;
  margin:0 0 12px;padding:8px 12px;background:var(--panel);
  border:1px solid var(--line);border-radius:10px}
.groupbar .gb-label{font-size:12px;text-transform:uppercase;letter-spacing:.5px;
  color:var(--dim);font-weight:600}
.gb-chips{display:flex;align-items:center;gap:6px;flex-wrap:wrap;flex:1}
.gb-chip{display:inline-flex;align-items:center;gap:6px;padding:3px 6px 3px 4px;
  border:1px solid var(--line);border-radius:20px;background:var(--panel2);font-size:12px}
.gb-chip i{display:inline-flex;align-items:center;justify-content:center;
  width:16px;height:16px;border-radius:50%;background:var(--gold);
  color:var(--on-gold);font-style:normal;font-size:10px;font-weight:700}
button.gb-x{background:none;border:none;color:var(--dim);padding:0 2px;
  font-size:14px;line-height:1;cursor:pointer}
button.gb-x:hover{color:var(--bad)}

/* Running total of the ticked rows, on the group bar. Ordered gross only -
   a weighed figure does not exist until the parcel has been on the scale. */
.sel-summary{display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  margin-left:auto;padding:3px 10px;border:1px solid var(--gold);
  border-radius:20px;color:var(--dim);font-size:12px}
.sel-summary b{color:var(--text);font-family:Consolas,monospace}
.sel-summary .sel-tot b{color:var(--gold)}
.sel-summary .kchip{font-family:Consolas,monospace;white-space:nowrap}
.sel-summary .kchip i{font-style:normal;color:var(--gold);font-weight:700;
  margin-right:3px}

/* Group header totals */
tr.group-row .gtot{margin-left:14px;color:var(--dim);font-size:.86em}
tr.group-row .gtot b{color:var(--text);font-family:Consolas,monospace}
tr.group-row .kchip{margin-left:10px;font-size:.8em;color:var(--dim);
  font-family:Consolas,monospace;white-space:nowrap}
tr.group-row .kchip i{font-style:normal;color:var(--gold);font-weight:700;margin-right:3px}

/* Packaging weight is typed by hand to 4 decimals - a text input, so no
   spinner arrows. This also strips them from any number input left in the
   weighing modal. */
#pkg-table input.num-in{width:100%;text-align:right;font-family:Consolas,monospace}
#pkg-table input[type=number]::-webkit-outer-spin-button,
#pkg-table input[type=number]::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}
#pkg-table input[type=number]{-moz-appearance:textfield}

/* Exit tab: the waiting pool reads as one pending batch, so it gets a
   panel of its own rather than being a second grid on a flat page. */
.exit-pool{border:1px solid var(--warn);border-radius:8px;padding:12px 14px;
  margin-bottom:22px;background:var(--panel2)}
.exit-pool .pool-head{display:flex;align-items:center;gap:12px;margin-bottom:10px}
.exit-pool .pool-head h2{margin:0;font-size:15px}
.exit-pool .pool-tot{color:var(--dim);font-size:12px}
.exit-pool .pool-tot b{color:var(--text)}
.exit-pool .spacer{flex:1}
.karat-cell{color:var(--dim);font-size:12px;white-space:nowrap}
.exit-sec{margin:0 0 10px;font-size:15px}

/* Inline packaging editor on the Exit tab's waiting pool. */
.pkg-editor{padding:8px 4px 4px}
.pkg-editor .pkg-rows{max-width:520px}
.pkg-editor .block{display:block;margin:8px 0;max-width:520px}
.pkg-editor textarea{width:100%}
.pkg-foot{display:flex;align-items:center;gap:14px;margin-top:6px}
.pkg-foot .dim{font-size:12px}

/* Production Type filter. The shared .menu is a fixed 250px, which wraps
   values like "SAMPLE - FULL WORK" onto three lines; this one sizes itself to
   its longest entry instead, up to a cap, and never breaks a value. */
#prod-menu{width:max-content;min-width:250px;max-width:min(560px,90vw)}
#prod-menu .menu-item{white-space:nowrap}
#prod-menu .menu-hint{white-space:normal}

/* Layouts page: the column and user tick lists in the editor modals. They
   reuse .menu-item, which is built for a dropdown, so they only need the
   column shape and a frame. */
.col-picker{border:1px solid var(--line);border-radius:8px;padding:6px;
  max-height:46vh;overflow:auto;background:var(--panel)}
.col-picker .menu-item{padding:5px 6px}
tr.default-row{background:var(--panel2)}

/* "Until hh:mm" cut-off on the orders filters. A typed text box, not an
   <input type=time>: the native widget is a fixed-width segmented control
   that cannot be narrowed and forces its own keying order. The server takes
   10, 1030, 10:30 or 10:30 pm - see app._until_datetime. */
.until-wrap{display:inline-flex;align-items:center;gap:6px;
  border:1px solid var(--line);border-radius:6px;padding:0 8px;
  background:var(--panel2)}
.until-wrap .until-lbl{color:var(--dim);font-size:12px;text-transform:uppercase;
  letter-spacing:.4px}
/* Two digit fields with the colon as markup between them. Fixed 2ch each -
   the box must not grow, and a wider field invites more than two digits. */
.until-wrap .tseg{width:2.4ch;border:none;background:none;padding:6px 0;
  font-family:Consolas,monospace;text-align:center;color:var(--text)}
.until-wrap .tseg:focus{outline:none;color:var(--gold)}
.until-wrap .tseg::placeholder{color:var(--dim);opacity:.7}
.until-wrap .tcolon{color:var(--dim);font-family:Consolas,monospace}
.until-wrap .ampm{background:none;border:1px solid var(--line);border-radius:4px;
  padding:2px 6px;font-size:11px;font-weight:700;color:var(--gold);
  line-height:1.4;min-height:0;cursor:pointer}
.until-wrap .ampm:hover{border-color:var(--gold)}
.until-clear{color:var(--dim);font-size:16px;line-height:1;padding:0 2px}
.until-clear:hover{color:var(--bad)}

/* Users page: the full-name cell is an inline form, so it has to stay on
   one line and not stretch the column. */
.name-form{flex-wrap:nowrap;gap:6px}
.name-form input[name=full_name]{min-width:0}
.name-form .chk{font-size:11px;white-space:nowrap}

/* Access page: a permission matrix. The user columns are narrow ticks, so
   they get a fixed width and the permission name takes the rest. */
#access-table th.perm-user{text-align:center;white-space:nowrap;
  writing-mode:horizontal-tb;min-width:88px}
#access-table td.chk-col{text-align:center;width:88px}
#access-table tbody tr:hover{background:var(--hover)}

/* Locally created orders (the New / Edit order page) */
.wide-hint{max-width:760px;margin:0 0 16px}
.form-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));
  gap:0 18px}
.form-grid label.block input{width:100%}
#lo-table input{width:100%;padding:5px 7px}
#lo-table input.num-in{text-align:right;font-family:Consolas,monospace}
#lo-table .karat-col{width:130px}
#lo-table td.act{width:36px;text-align:center}
.page-foot{display:flex;align-items:center;gap:10px;margin-top:16px}


/* ==================================================================
   Mobile / responsive layer (added 2026-09-01)
   Desktop layout above is untouched; everything here is either a
   wrapper class used at every width (.table-wrap, .navtgl) or lives
   inside a max-width media query. Breakpoints: 900px = tablet and
   below, 640px = phone.
   ================================================================== */

/* Every grid is wrapped in this in the templates. Wide tables scroll
   inside their own box instead of pushing the page sideways - the body
   must never scroll horizontally or the toolbar drifts off. */
.table-wrap{max-width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}
/* Default: never squeeze a wide grid - scroll it. ui.js measures each grid
   after every render, column show/hide and resize, and adds .fits when the
   natural width already clears the wrapper; then the table stretches to fill
   the page instead of leaving dead space to the right of the last column. */
.table-wrap > table.grid{min-width:max-content}
.table-wrap.fits > table.grid{min-width:0;width:100%}

/* "Prepared exit papers" summary rows (2026-09-02). The customer list used
   to sit in this table and was the one cell long enough to need wrapping;
   it now lives only in the Lines panel, so every remaining cell is short
   enough to hold on one line. Nothing here wraps: a summary row split over
   two lines is harder to read than one that runs a little wide, and the
   .table-wrap box scrolls on its own if it ever does. */
#papers-table > thead th,
#papers-table > tbody > tr > td{white-space:nowrap}
/* Trimmed from the browser default so the row keeps its width down; the
   full value is still there to scroll through when it is longer. */
#papers-table .rcv-in{width:140px;box-sizing:border-box}
/* Tighter than the 10px default - nine narrow columns gain more from the
   space than they lose in separation. */
#papers-table > thead th,
#papers-table > tbody > tr > td{padding-left:8px;padding-right:8px}
/* Three buttons on one line rather than stacked, so the row stays one
   line tall. */
#papers-table td .btn.small,
#papers-table td button.small{margin-bottom:0}
/* The expanded Lines panel (2026-09-02). It must fit the page: nothing in
   it is worth dragging the whole summary table sideways for.

   Its width is taken from the VIEWPORT, not from the cell it sits in. The
   outer table is sized min-width:max-content, so that cell's width is
   decided BY this panel's content: a percentage here would be circular and
   the row would simply grow until the orders list fit on one line. A
   viewport length is definite, so it both caps the panel's max-content
   contribution (the outer table stops growing) and lets it fill the page.

   The numbers are main's own box: the viewport less the sidebar rail
   (--nav-w, which grows when the rail is pinned) and main's own padding
   (--pad a side). The extra 32px covers the vertical scrollbar - which
   100vw counts and the content column does not - plus the wrapper's own
   border; without it the panel overhangs by a scrollbar's width and puts
   the sideways scroll straight back.

   table-layout:fixed then holds the columns to the shares below instead of
   letting the orders list decide them, and the wrapping rules underneath
   let the long cells run onto a second line inside their column. */
#papers-table > tbody > tr.paper-detail > td{white-space:normal}
#papers-table .paper-detail table.grid{
  width:calc(100vw - var(--nav-w) - var(--pad) * 2 - 32px);table-layout:fixed}
#papers-table .paper-detail table.grid td{overflow-wrap:anywhere}
/* Both were held on one line elsewhere; in here they wrap instead. */
#papers-table .paper-detail .mono,
#papers-table .paper-detail .karat-cell{white-space:normal}
/* Customer, Orders, Karat split, then the four weights, Notes, buttons.
   The weights are the only cells with a known short width, so they are
   kept narrow and the space goes to the three that hold text. */
#papers-table .paper-detail table.grid th:nth-child(1),
#papers-table .paper-detail table.grid td:nth-child(1){width:16%}
#papers-table .paper-detail table.grid th:nth-child(2),
#papers-table .paper-detail table.grid td:nth-child(2){width:22%}
#papers-table .paper-detail table.grid th:nth-child(3),
#papers-table .paper-detail table.grid td:nth-child(3){width:12%}
#papers-table .paper-detail table.grid th:nth-child(4),
#papers-table .paper-detail table.grid td:nth-child(4),
#papers-table .paper-detail table.grid th:nth-child(5),
#papers-table .paper-detail table.grid td:nth-child(5),
#papers-table .paper-detail table.grid th:nth-child(6),
#papers-table .paper-detail table.grid td:nth-child(6),
#papers-table .paper-detail table.grid th:nth-child(7),
#papers-table .paper-detail table.grid td:nth-child(7){width:7%}
#papers-table .paper-detail table.grid th:nth-child(8),
#papers-table .paper-detail table.grid td:nth-child(8){width:12%}
#papers-table .paper-detail table.grid th:nth-child(9),
#papers-table .paper-detail table.grid td:nth-child(9){width:10%}
/* Two buttons in a 10% column - let them stack rather than widen it. */
#papers-table .paper-detail td .btn.small,
#papers-table .paper-detail td button.small{margin-bottom:2px}
/* The "no lines left" row spans the table and is a sentence, not a cell. */
#papers-table .paper-detail table.grid td.empty{width:auto}

/* ---- Report scorecards --------------------------------------------------
   One card per production number. The card is a summary the eye scans down:
   number and customer, then one gross figure, then the karat split. The
   order lines behind it are two summed tabs, hidden until the head is
   clicked.

   Type scale, largest to smallest: the gross figure (21px) is what the page
   is for; the production number (15px) names it; the customer (12px) tells
   them apart; chips and meta (11px) are detail. Nothing else on the card
   competes with those four steps - an earlier pass had a 26px figure and a
   16px number and the cards read as a wall of headings.

   The grid is auto-fill rather than a fixed column count, so the same markup
   gives four cards on a desktop and one on a phone. A finished card takes a
   green left edge; that stripe, not the pill alone, is what makes a finished
   production findable at a glance. */
/* The filter row. On a desktop it reads as the toolbar it replaces - the
   controls keep their own widths at the left of the row rather than being
   stretched across it. */
.report-head{margin:0 0 12px}
.report-head .filters{flex:0 1 auto}
.report-head .filters input{width:280px}
.report-head .filters input[type=date]{width:auto}

.sc-summary{display:flex;align-items:baseline;gap:16px;flex-wrap:wrap;
  color:var(--dim);font-size:12px;margin:0 0 14px}
.sc-summary b{color:var(--text);font-size:15px}
.sc-summary .sc-sum-gross{margin-left:auto;color:var(--gold)}
.sc-summary .sc-sum-gross b{color:var(--gold);font-family:Consolas,monospace}

.scorecards{display:grid;gap:12px;
  /* min(280px,100%), not a bare 280px: on a screen narrower than the track
     a bare minimum is still honoured and the grid overflows. This lets the
     single remaining column collapse to the container instead. */
  grid-template-columns:repeat(auto-fill,minmax(min(280px,100%),1fr));
  align-items:start;min-width:0}
/* An open card keeps its column width - it grows downwards, not sideways,
   so the grid around it does not reflow and the card the user clicked stays
   exactly where they clicked it. Its two tables are narrow as a result and
   scroll inside their own .table-wrap when they have to. */

/* min-width:0 is not decoration. A grid item's default min-width is auto,
   which means it refuses to shrink below its own content's min-content
   width - one long model name or customer, and the card pushes the whole
   grid wider than the screen. The page then scrolls sideways, and a sticky
   bar spans the viewport rather than that wider scroll area, so it stops
   short of the edge the moment the user zooms out to see the rest. */
.scorecard{background:var(--panel);border:1px solid var(--line);
  border-left:3px solid var(--line);border-radius:9px;padding:11px 13px;
  min-width:0;max-width:100%;overflow:hidden}
.scorecard.done{border-left-color:var(--ok)}
.scorecard:hover{border-color:var(--gold)}
.scorecard.done:hover{border-left-color:var(--ok)}

/* The head is a real button so a card opens from the keyboard too. */
.sc-head{display:flex;align-items:center;gap:7px;width:100%;
  background:none;border:none;padding:0;margin:0;cursor:pointer;
  color:var(--text);text-align:left;font:inherit}
.sc-caret{color:var(--gold);font-size:12px;transition:transform .12s ease;
  flex:none}
.scorecard.open .sc-caret{transform:rotate(90deg)}
/* Number and customer share one column so the name can ellipsis on its own
   without shortening the number, which must always be readable in full. */
.sc-title{min-width:0;flex:1}
.sc-id{display:block;font-size:15px;font-weight:700;
  font-family:Consolas,monospace;line-height:1.2;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.sc-name{display:block;font-size:12px;color:var(--dim);line-height:1.3;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.sc-head .pill{margin-left:auto;flex:none;align-self:flex-start}

/* The number the page exists for. Tabular figures so a column of cards
   lines up decimal point under decimal point. */
.sc-gross{margin:9px 0 7px;font-family:Consolas,monospace;
  font-variant-numeric:tabular-nums}
.sc-gross b{font-size:21px;font-weight:700;color:var(--gold);line-height:1}
.sc-gross .unit{color:var(--dim);font-size:11px;margin-left:5px;
  font-family:"Segoe UI",system-ui,sans-serif}

.sc-karats{display:flex;flex-wrap:wrap;gap:5px}
.sc-karats .kchip{font-family:Consolas,monospace;font-size:11px;
  white-space:nowrap;color:var(--text);background:var(--panel2);
  border:1px solid var(--line);border-radius:20px;padding:1px 8px}
.sc-karats .kchip i{font-style:normal;color:var(--gold);font-weight:700;
  margin-right:4px}

.sc-meta{font-size:11px;margin-top:7px}

/* The karat split inside the "By date" tab: the card face's chip, a size
   down. It sits between the date and the day's gross and must stay quieter
   than both - the figure being read down that tab is the gross, and chips
   at full size fought it for the eye. */
.date-karats{display:flex;flex-wrap:wrap;gap:4px}
.date-karats .kchip{font-family:Consolas,monospace;font-size:10px;
  white-space:nowrap;color:var(--dim);background:var(--panel2);
  border:1px solid var(--line);border-radius:20px;padding:0 6px}
.date-karats .kchip i{font-style:normal;color:var(--gold);font-weight:700;
  margin-right:3px}

/* ---- Expanded card: By product / By date -------------------------------- */
.sc-detail{margin-top:11px;border-top:1px solid var(--line);padding-top:9px}
.sc-tabs{display:flex;gap:4px;margin-bottom:9px}
.sc-tab{background:none;border:1px solid transparent;border-radius:6px;
  padding:4px 11px;font:inherit;font-size:12px;color:var(--dim);
  cursor:pointer}
.sc-tab:hover{color:var(--text);border-color:var(--line)}
.sc-tab.on{background:var(--panel2);border-color:var(--line);
  color:var(--gold);font-weight:700}
/* These tables are a breakdown inside a card, not a page-level grid, so
   they run a size down from the portal's usual .tight. */
.sc-pane table.grid td,.sc-pane table.grid th{font-size:12px;padding:4px 6px}
.sc-pane table.grid tfoot td{font-size:12px;padding:5px 6px}
/* These two tables never scroll sideways. They live inside a card that is
   only as wide as its grid column, and a breakdown you have to drag left and
   right to read is worse than one that wraps.
   table-layout:fixed is what makes that hold: the columns take the widths
   set below instead of their content's, so a long model name wraps in the
   first column rather than widening the table. The portal-wide default is
   the opposite (min-width:max-content on .table-wrap > table.grid, then
   ui.js measures and stretches) - that rule is for full-page grids with a
   whole window to scroll in, and is overridden here on purpose. */
.sc-pane .table-wrap{overflow-x:hidden}
.sc-pane .table-wrap > table.grid,
.sc-pane .table-wrap.fits > table.grid{min-width:0;width:100%;
  table-layout:fixed}
/* Column one takes what is left and wraps; the rest are numbers and short
   labels, sized to their longest realistic value and held on one line. */
.sc-pane table.grid td:first-child,
.sc-pane table.grid th:first-child{overflow-wrap:anywhere}
.sc-pane table.grid th:not(:first-child),
.sc-pane table.grid td:not(:first-child){white-space:nowrap}
.sc-pane[data-pane="products"] table.grid th:nth-child(2){width:52px}
.sc-pane[data-pane="products"] table.grid th:nth-child(3){width:86px}
.sc-pane[data-pane="systems"] table.grid th:nth-child(2){width:96px}
.sc-pane[data-pane="dates"] table.grid th:nth-child(2){width:96px}
.sc-pane[data-pane="countries"] table.grid th:nth-child(2),
.sc-pane[data-pane="orders"] table.grid th:nth-child(2){width:86px}
.sc-chiprow{margin-top:3px}
/* Daily report: country rows open to list that country's products. */
tr.dr-country{cursor:pointer}
tr.dr-country:hover td{background:var(--panel2)}
tr.dr-country .sc-caret{display:inline-block;margin-right:3px;font-size:10px}
tr.dr-country[aria-expanded="true"] .sc-caret{transform:rotate(90deg)}
tr.dr-product td{background:var(--panel2);font-size:11px}
tr.dr-product td:first-child{padding-left:20px}
.dr-karat{font-family:Consolas,monospace;font-size:10px;font-style:normal;
  margin-left:4px;padding:0 5px;border:1px solid var(--line);border-radius:20px}
.dr-karat i{font-style:normal;color:var(--gold);font-weight:700}
/* Three tabs no longer fit one line in a 280px card at every font size. */
.sc-tabs{flex-wrap:wrap}
.sc-pane .empty{padding:14px;font-size:12px}

/* ---- Per-user permissions panel (Users page) -----------------------------
   The Access page is a matrix - every user across, every permission down -
   which is the right shape for comparing people and the wrong one for
   setting a single account up. This panel is the same keys for one user,
   opened from that user's row, so the groups can be read as groups. */
tr.perm-row > td{background:var(--panel2);padding:14px 16px}
.perm-groups{display:grid;gap:16px;
  grid-template-columns:repeat(auto-fit,minmax(230px,1fr));
  align-items:start}
.perm-group h4{margin:0 0 8px;font-size:12px;text-transform:uppercase;
  letter-spacing:.5px;color:var(--gold)}
/* The whole line is the label, so the click target is the wording and not
   just the 13px box. */
.perm-group .chk{align-items:flex-start;gap:7px;padding:3px 0;
  color:var(--text);cursor:pointer;line-height:1.35}
.perm-group .chk input{margin-top:2px;flex:none}
.perm-group .chk .pill{margin-left:4px}
.perm-foot{display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  margin-top:14px;padding-top:12px;border-top:1px solid var(--line)}
.perm-foot .dim{font-size:12px;flex:1;min-width:200px}
/* The summary button in the Tabs cell. */
button.perm-tgl{white-space:nowrap}
button.perm-tgl .caret{color:var(--gold);margin-right:4px}

/* Hamburger. Hidden above the breakpoint, where the rail is always on
   screen and hovering it is enough. */
button.navtgl{display:none;background:none;border:1px solid var(--line);
  border-radius:6px;padding:5px 10px;font-size:16px;line-height:1;
  color:var(--text);cursor:pointer}
button.navtgl:hover{border-color:var(--gold)}

@media (max-width:900px){
  /* main is padded 12px a side down here, not 20px. */
  #papers-table .paper-detail table.grid{width:calc(100vw - 40px)}

  :root{--nav-w:0px;--pad:12px}
  :root.nav-pinned{--nav-w:0px}
  /* Backstop against a sideways scroll. clip, not hidden: hidden would make
     main a scroll container and break every position:sticky inside it,
     which is exactly what the report's frozen filter strip relies on. */
  main{padding:14px var(--pad);overflow-x:clip}
  main.with-nav{margin-left:0}
  .mobilebar{display:flex}
  button.navtgl{display:inline-block}
  /* Off-canvas at this width: toggleNav() flips .nav-open on <html> and the
     rail slides in over a scrim at its full width. Hover/pin do nothing. */
  .sidenav{width:var(--nav-open-w);transform:translateX(-100%);
    transition:transform .18s ease}
  :root.nav-open .sidenav{transform:none;box-shadow:6px 0 24px var(--shadow-menu)}
  :root.nav-open .nav-scrim{display:block}
  .sidenav .lbl{opacity:1}
  .sidenav .nav-foot .nav-pin{display:none}

  .page-head{gap:8px}
  .page-head h1{width:100%;margin-right:0}
  /* One row is a desktop luxury - below this width the controls wrap. */
  .filterbar{flex-wrap:wrap}
  .filters{flex-wrap:wrap;width:100%}
  .filters .q-in{min-width:0;flex:1 1 200px}
  .filters select{max-width:none}
  .filters select,.filters .menu-wrap,.filters .menu-wrap > button{flex:1 1 160px}

  /* The menu is anchored to a button that can now sit anywhere on a
     wrapped row, so cap it to the viewport instead of a fixed 250px. */
  .menu{width:min(300px,calc(100vw - 24px))}
  /* #prod-menu's own rule is an ID selector and outranks .menu at every
     width, so it has to be restated here. */
  #prod-menu{min-width:0;width:min(300px,calc(100vw - 24px));max-width:none}

  /* Full-screen modals: a centred sheet with 20px of backdrop wastes the
     little height a phone has, and the weighing form is tall. */
  .modal-back{padding:0;align-items:stretch}
  .modal{width:100%;max-height:100vh;height:100%;border-radius:0;border:none}
  .modal-head,.modal-foot{padding:12px}
  .modal-body{padding:12px}
  .modal-foot{flex-wrap:wrap}
  .modal-foot .spacer{flex-basis:100%;height:0}

  .exit-pool .pool-head{flex-wrap:wrap}
  .exit-pool .pool-head .spacer{display:none}
  .exit-pool .pool-head input{flex:1 1 180px}
}

@media (max-width:640px){
  /* Through the token, not a literal, so anything that has to pull out to
     the screen edges can undo main's gutter with a calc on --pad instead of
     hard-coding a number that then drifts. */
  :root{--pad:10px}
  main{padding:12px var(--pad)}
  h1{font-size:18px}
  .mobilebar{padding:7px 10px}
  .mobilebar .brand{font-size:15px}

  /* 16px keeps iOS Safari from zooming the page on focus, and 40px is the
     smallest comfortable tap target. */
  input,select,textarea{font-size:16px;padding:9px 10px}
  button,.btn{min-height:40px;padding:9px 14px}
  .btn.small,button.small{min-height:34px;padding:6px 10px}
  button.link{min-height:0;padding:0}
  button.x{min-height:0}
  input[type=checkbox]{width:18px;height:18px}

  /* Toolbar buttons on the orders page go full width, one per row: at this
     width they wrap into ragged half-rows otherwise. */
  .page-head .filters{order:1}
  .page-head > button,.page-head > a.btn{flex:1 1 100%}
  .page-head .menu-wrap{margin-left:auto}
  .filters input{flex:1 1 100%}
  .filters select,.filters .menu-wrap,.filters .menu-wrap > button,
  .filters button[type=submit]{flex:1 1 100%}
  .filters .menu-wrap > button{width:100%}

  .groupbar{padding:8px 10px}
  .groupbar .gb-label{width:100%}
  .sel-summary{margin-left:0;width:100%;border-radius:10px;padding:6px 10px}

  /* Anchored right of a full-width button, a 300px menu still overflows a
     360px screen. It becomes a bottom sheet instead - pinned to the bottom
     of the viewport, so it cannot land off-screen whatever the page scroll
     or where its button ended up on a wrapped toolbar row. */
  .menu,#prod-menu{position:fixed;left:10px;right:10px;bottom:10px;top:auto;
    width:auto;min-width:0;max-width:none;max-height:65vh;z-index:60;
    box-shadow:0 -8px 40px var(--shadow-modal)}
  #prod-menu .menu-item{white-space:normal}

  table.grid td,table.grid th{padding:7px 8px}
  table.grid.tight td,table.grid.tight th{padding:5px 6px;font-size:12px}

  /* The two-column weighing form is already stacked by the 1100px rule;
     these keep the calculator readable once stacked. */
  .calc{padding:12px}
  .calc-row.result b{font-size:20px}
  .calc .big-input{font-size:18px}

  .card{padding:12px}
  .card.auth{margin:28px auto;max-width:none}
  .inline-form{width:100%}
  .inline-form input{flex:1 1 100%}
  .form-grid{grid-template-columns:1fr}

  button.theme-tgl{min-height:34px}
  button.theme-tgl.floating{top:10px;right:10px}
}

/* ------------------------------------------------------------------
   Phone card grids (<=640px). A 12-column order grid cannot be read by
   scrolling sideways on a 390px screen, so every table.grid stops being a
   table and becomes one card per row: a fixed label column on the left and
   the value beside it, from the data-label that static/mobile.js copies off
   the header.
   Group headers stay full-width bands so grouping still reads.
   ------------------------------------------------------------------ */
@media (max-width:640px){
  /* The scroll box is what the card layout replaces - leaving it on would
     re-clip the cards and bring the sideways scrollbar back. */
  .table-wrap{overflow-x:visible}
  .table-wrap > table.grid{min-width:0}

  table.grid{display:block;background:none;border:none;border-radius:0;
    overflow:visible}
  table.grid thead{display:none}
  table.grid tbody,table.grid tfoot{display:block}

  table.grid tbody > tr,table.grid tfoot > tr{display:block;
    background:var(--row);border:1px solid var(--line);border-radius:10px;
    margin-bottom:8px;overflow:hidden}
  table.grid tbody > tr:hover{background:var(--row)}
  table.grid tbody > tr.sel{background:var(--sel);border-color:var(--gold)}
  table.grid tfoot > tr{background:var(--group)}

  /* Two fixed columns, not a space-between flex row: with space-between
     every value hugged the right edge while unlabelled and wrapped ones sat
     left, so a card read as two ragged edges. A grid puts every value on the
     same left margin whatever its length or type. */
  table.grid td{display:grid;grid-template-columns:40% 1fr;gap:10px;
    align-items:baseline;padding:7px 10px;text-align:left;
    border-bottom:1px solid var(--line);min-height:0}
  table.grid tr > td:last-child{border-bottom:none}
  table.grid.tight td{padding:6px 10px;font-size:13px}
  /* Only labelled cells get the label column - an empty ::before on an
     actions cell would still claim its 40% and push the buttons over. */
  table.grid td[data-label]::before{content:attr(data-label);color:var(--dim);
    font-size:11px;text-transform:uppercase;letter-spacing:.4px;
    line-height:1.35;word-break:break-word}
  /* The value side takes what the label leaves and may wrap. */
  table.grid td[data-label]{word-break:break-word}
  /* Weights stay monospaced so digits still line up down a card list. */
  table.grid td.num{font-family:Consolas,monospace}
  /* Numeric columns are right-aligned in the desktop table; in a card the
     value column is the same width for every row, so right-aligning only
     some of them is what made the alignment look random. Everything is
     left-aligned here - the label column already separates them. */
  table.grid .num,table.grid .right,table.grid td.num,table.grid td.right{
    text-align:left}
  /* Actions and checkbox cells have no header text: one full-width column. */
  table.grid td:not([data-label]){display:flex;align-items:center;
    justify-content:flex-start;flex-wrap:wrap;gap:8px}
  table.grid td:not([data-label]):empty{display:none}
  table.grid td.chk-col{width:auto;justify-content:flex-start;gap:10px;
    background:var(--panel2)}
  table.grid td.chk-col::before{content:"Select";flex:0 0 auto;
    color:var(--dim);font-size:11px;text-transform:uppercase;
    letter-spacing:.4px;order:2}
  table.grid td.empty{justify-content:center;text-align:center;padding:18px}
  table.grid tfoot td.right{color:var(--text);font-weight:600}
  /* Inputs inside a card cell (packaging lines, the exit pool editor). */
  /* A labelled cell is a grid, so the control just fills its value column;
     an unlabelled one is a flex row and the control grows in it. */
  table.grid td[data-label] input,table.grid td[data-label] select{
    width:100%;min-width:0}
  table.grid td:not([data-label]) input,table.grid td:not([data-label]) select{
    flex:1 1 auto;min-width:0}
  /* num-in is right-aligned in the desktop table; left here, like the rest
     of the value column. */
  /* #pkg-table / #ex-pkg-table / #lo-table right-align num-in with an ID
     selector, which outranks any class rule - restate them here. */
  table.grid td input.num-in,#pkg-table td input.num-in,
  #ex-pkg-table td input.num-in,#lo-table td input.num-in{text-align:left}
  table.grid td button,table.grid td .btn{flex:0 0 auto}

  /* Group headers stay one band, not a card of labelled rows. */
  /* No background here: the d0..d3 level colours above must win, and a
     repaint at this specificity would flatten every level to one slab. The
     stripe moves to the row itself, which is a flex box on a phone. */
  table.grid tr.group-row{display:flex;align-items:flex-start;gap:6px;
    border:none;border-radius:8px;padding:6px 8px;margin-bottom:8px}
  table.grid tr.group-row td:first-child{box-shadow:none}
  table.grid tr.group-row.d0{border-left:5px solid var(--lvl-0-line)}
  table.grid tr.group-row.d1{border-left:5px solid var(--lvl-1-line)}
  table.grid tr.group-row.d2{border-left:5px solid var(--lvl-2-line)}
  table.grid tr.group-row.d3,table.grid tr.group-row.d4,
  table.grid tr.group-row.d5{border-left:5px solid var(--lvl-3-line)}
  table.grid tr.group-row td{display:block;border:none;padding:0;
    background:none}
  table.grid tr.group-row td::before{content:none}
  table.grid tr.group-row td.chk-col{flex:0 0 auto;background:none}
  table.grid tr.group-row td:nth-child(2){flex:1;min-width:0}
  tr.group-row .gtot,tr.group-row .kchip{display:inline-block;margin-left:0;
    margin-right:10px}
  tr.group-row.d1 td:nth-child(2){padding-left:12px}
  tr.group-row.d2 td:nth-child(2){padding-left:24px}
  tr.group-row.d3 td:nth-child(2){padding-left:36px}

  /* Nested tables (the Exit tab's inline packaging editor) are already
     inside a card cell - a second card frame around them reads as noise. */
  table.grid td table.grid tbody > tr{border:none;margin-bottom:4px}
}

/* ------------------------------------------------------------------
   Phone popups (<=640px): the dropdown menus and the two big modals.
   ------------------------------------------------------------------ */
@media (max-width:640px){
  /* Bottom sheets: full width, thumb-sized rows, clear of the home bar. */
  .menu,#prod-menu{left:0;right:0;bottom:0;border-radius:14px 14px 0 0;
    padding:8px 10px calc(10px + env(safe-area-inset-bottom));
    max-height:70vh}
  .menu .menu-item{padding:10px 8px;min-height:44px;font-size:14px}
  .menu .menu-title{font-size:12px;padding:4px 6px 8px;position:sticky;top:0;
    background:var(--panel);z-index:1}
  .menu .menu-title .link{font-size:13px;padding:4px}
  .menu-hint{font-size:12px}

  /* Modals fill the screen; head and foot stay put while the body scrolls,
     so Save is always reachable without scrolling to the end of a long
     weighing form. */
  .modal{height:100dvh;max-height:100dvh}
  .modal-head{position:sticky;top:0;background:var(--bg);z-index:2}
  .modal-head h2{font-size:15px}
  .modal-body{padding:10px;-webkit-overflow-scrolling:touch}
  .modal-foot{padding:10px 10px calc(10px + env(safe-area-inset-bottom));
    gap:8px}
  .modal-foot button,.modal-foot .btn{flex:1 1 45%}
  .modal-foot #modal-msg,.modal-foot #ex-msg{flex-basis:100%}
  .two-col{gap:14px}
  h3.sec{margin:14px 0 6px}
  details.ref{margin-top:12px}
}

/* Phone layout turns every table into stacked label/value blocks, where a
   column width and a nowrap are meaningless at best and clip cells at
   worst. Both sets of rules for the exit-papers table are undone here so
   that layout is left exactly as it was. */
@media (max-width: 640px){
  #papers-table > thead th,
  #papers-table > tbody > tr > td{white-space:normal}
  #papers-table .rcv-in{width:100%}
  #papers-table .paper-detail table.grid{width:auto;table-layout:auto}
  #papers-table .paper-detail table.grid th,
  #papers-table .paper-detail table.grid td{width:auto}
}

@media (max-width:640px){
  .scorecards{grid-template-columns:1fr}
  .sc-gross b{font-size:19px}
  .sc-summary .sc-sum-gross{margin-left:0;width:100%}

  /* The three filter controls stack and freeze under the mobile bar, so the
     search box, the finished picker and the button stay reachable however
     far down the card list the user has scrolled.

     --mbar-h is the mobile bar's measured height, written by report.js on
     load and on resize; the literal is only a fallback for the frame before
     that runs. It was a hard-coded 55px at first, which left a hairline of
     scrolling cards showing between the two bars whenever the real height
     differed by a pixel.

     The strip must also cover the full width or cards slide past at its
     edges, so the negative margin undoes main's gutter and the padding puts
     it back inside - both from --pad, so they cannot drift apart. z-index
     30 keeps it over the cards and under the mobile bar's 55. */
  .report-head{position:sticky;top:calc(var(--mbar-h, 54px) - 1px);z-index:30;
    background:var(--bg);
    margin:0 calc(-1 * var(--pad)) 12px;padding:8px var(--pad);
    border-bottom:1px solid var(--line);
    /* Cards passing beneath read as passing beneath, rather than as the
       strip having gone transparent. */
    box-shadow:0 6px 12px -6px var(--shadow-menu)}
  .report-head .filters{flex-direction:column;align-items:stretch;gap:8px}
  .report-head .filters > *{width:100%;flex:0 0 auto;max-width:none}
  /* The strap line is a one-off explanation, not something worth the height
     it costs on a phone. */
  .page-head .sc-sub{display:none}

  /* An expanded card keeps real tables here. Everything else on a phone is
     turned into one card per row by the block above, which is right for a
     twelve-column order grid and wrong for these two: they are three narrow
     columns that already fit, and stacking them turned six numbers into
     eighteen labelled lines. */
  .sc-pane .table-wrap{overflow-x:hidden}
  .sc-pane table.grid{display:table;width:100%;table-layout:fixed;
    background:var(--row);border:1px solid var(--line);border-radius:8px;
    overflow:hidden}
  .sc-pane table.grid thead{display:table-header-group}
  .sc-pane table.grid tbody{display:table-row-group}
  .sc-pane table.grid tfoot{display:table-footer-group}
  .sc-pane table.grid tr{display:table-row;background:none;border:none;
    margin:0;border-radius:0}
  .sc-pane table.grid th,
  .sc-pane table.grid td{display:table-cell;padding:5px 7px;font-size:12px;
    border-bottom:1px solid var(--line);min-height:0}
  /* The card layout prints the column name in front of every value; with
     the header row back, that is the same word twice. */
  .sc-pane table.grid td::before{content:none !important}
  .sc-pane table.grid td.num{text-align:right;font-family:Consolas,monospace}
  .sc-pane table.grid tfoot td{background:var(--group)}
  .sc-pane[data-pane="products"] table.grid th:nth-child(2){width:46px}
  .sc-pane[data-pane="products"] table.grid th:nth-child(3){width:78px}
  .sc-pane[data-pane="systems"] table.grid th:nth-child(2){width:88px}
  .sc-pane[data-pane="dates"] table.grid th:nth-child(2){width:88px}
  .sc-tab{padding:4px 8px}
}
