/* Goodness Gardens receiving app.
 *
 * 🗣️ Dael, 2026-08-24: "this is not the same structure the floor scan has."
 * It is now. Brand, header and vocabulary are the floor scan app's
 * (gg-floor-app), because the same phones run both and two GG apps that look
 * like two different products is how an internal tool starts looking
 * unofficial. Cream #FFFCC7 on brown #582D1C, sampled from the logo artwork.
 *
 * This REPLACES an earlier decision to dress the app as a NetSuite page. That
 * reasoning - "a receiver handed a link on a domain nobody recognises wonders
 * whether it is a real system" - is answered better by looking like the GG app
 * the floor already trusts than by looking like NetSuite.
 *
 * Dock ergonomics are the floor app's too: 60px targets for gloves, heavy
 * weights, high contrast, nothing hairline-thin.
 *
 * 🛑 The six grade colours are UNCHANGED and are never restyled to match a
 * theme. They are vocabulary, not decoration.
 */
:root {
  /* brand, from gg-floor-app/public/styles.css */
  --brown:#582D1C; --brown-deep:#3F1F13; --cream:#FFFCC7;
  --paper:#F6F2E8; --card:#FFFFFF; --line:#E4DBCB; --line-soft:#EFE8DA;
  --ink:#2A1710; --dim:#7A6455; --row-alt:#FBF8F1;
  --ok:#2E6F4E; --need:#B5761B; --bad:#A32B1E;
  /* 🗣️ Yaki, 2026-09-03, receiving Eshkol at Miami: "me ayudarías aumentando la
     intensidad del color cuando falta algún dato por poner, sale un color
     anaranjado claro, necesito un tono un poquito más oscurito para darnos
     cuenta." The pale amber below (#FBF0DC on #B5761B) is legible on a desk and
     invisible on a dock: a phone in daylight, at arm's length, held by someone
     already carrying a box. A field that is WAITING FOR HER has to win that
     fight, so the two states she acts on - a tile with no value and a chip with
     no photo or reason - get their own darker pair. The amber token itself is
     unchanged: it is vocabulary elsewhere (ORGANIC, warnings, review rows) and
     darkening those would say something this change does not mean. */
  --need-strong:#8A5209; --need-fill:#F6DFAE;
  --tap:60px;                    /* generous: gloves */
  /* the grade scale - unchanged, and never restyled to match a theme */
  --A:#3fb96b; --B:#5aa9e6; --C:#e6a23c; --D:#e5534b; --L:#9b7fe6; --T:#6b7a72;
}
* { box-sizing:border-box; -webkit-tap-highlight-color:transparent; }
/* A class selector beats the UA sheet's [hidden] rule, so .stack{display:flex}
   silently defeats element.hidden = true. Found on the sign-in screen, where
   the operator list stayed visible behind the PIN pad. */
[hidden] { display:none !important; }
html,body { margin:0; height:100%; }
body {
  background:var(--paper); color:var(--ink);
  font:400 16px/1.45 -apple-system,BlinkMacSystemFont,system-ui,"Segoe UI",sans-serif;
  -webkit-font-smoothing:antialiased;
  padding-bottom:env(safe-area-inset-bottom);
}
code { font:13px/1 ui-monospace,SFMono-Regular,Menlo,monospace; color:var(--dim); }

/* --- the floor app's nav bar: logo left (and it IS the home button), the
       dock and the site right ------------------------------------------- */
#bar {
  position:sticky; top:0; z-index:5; display:flex; justify-content:space-between; align-items:center;
  gap:12px; padding:calc(10px + env(safe-area-inset-top)) 16px 10px;
  background:var(--brown);
  box-shadow:0 1px 0 rgba(0,0,0,.18), 0 6px 20px -12px rgba(0,0,0,.5);
}
/* The logo doubles as Home, exactly as it does on the floor app. A receiver who
   has learnt "tap the logo to get back" on one app must not have to learn
   something else on the other. */
#btn-home {
  display:flex; align-items:center; gap:10px;
  background:none; border:0; padding:6px 8px 6px 0; margin:-6px 0;
}
#btn-home:active { opacity:.65; }
#hdr-logo { height:26px; width:auto; display:block; }
#home-hint {
  color:rgba(255,252,199,.55); font-size:11px; font-weight:800;
  letter-spacing:.08em; text-transform:uppercase;
}
/* 🛑 The account badge. Amber on the brown bar, the one thing up here that is
   not the house palette - because it must not read as decoration. `margin-right:auto`
   keeps it beside Home and leaves the identity block on the right where it was.
   Hidden entirely on production. */
#envtag {
  margin-right:auto; padding:4px 9px; border-radius:6px;
  background:#F0A22E; color:#2B1C0A;
  font-size:11px; font-weight:900; letter-spacing:.1em; text-transform:uppercase;
  white-space:nowrap;
}
/* 🗣️ Dael, 2026-09-08: "put up there the last deploy we did ... very small just
   for reference." Deliberately the QUIETEST thing on the bar - dimmer than the
   HOME hint beside it - because it is evidence to go looking for, not something
   to read all day. It carries `margin-right:auto` so it takes the dead space
   after Home and leaves the identity block on the right exactly where it was.
   🛑 IT MUST YIELD, NEVER PUSH. #bar is a nowrap flex row, so without min-width:0
   and an ellipsis a long stamp shoves "Francesville receiving" off a 390px
   screen - the identity is load-bearing and this is not.
   🔑 Tabular figures so the clock does not jitter between polls. */
#buildtag {
  margin-right:auto; min-width:0; flex:0 1 auto;
  /* 🔑 TWO LINES, and they cost the bar NO height - the identity opposite is
     already two lines, so the row is as tall as it always was. Stacked because
     inline the version and the timestamp need ~130px against the ~110px this
     slot has, and the timestamp was the half that kept losing. */
  display:flex; flex-direction:column; align-items:flex-start;
  line-height:1.25;
  color:rgba(255,252,199,.38); font-size:10px; font-weight:600;
  letter-spacing:.02em; font-variant-numeric:tabular-nums;
}
#buildtag span { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; max-width:100%; }
/* 🛑 THE BADGE WINS. Three things do not fit on a 390px bar, and the order is
   settled: the SITE IDENTITY first (a receiver must always know which dock she
   is on), the ACCOUNT BADGE second (it exists to stop someone working a
   plausible-looking sandbox board), and this stamp last. Ellipsising it to
   "8 Se…" would keep the width and lose the meaning, so on a badged instance it
   goes entirely - the stamp is still on /healthz and in the title. */
#envtag:not([hidden]) ~ #buildtag { display:none; }

/* The way back out of a closed line. Quiet - it sits beside the "closed" pill
   and must not compete with the work - but unmistakably a button. */
.reopen {
  margin-left:8px; padding:4px 12px; border-radius:999px;
  background:var(--card); border:1.5px solid var(--line);
  color:var(--brown); font:800 13px -apple-system,system-ui,sans-serif;
}
.reopen:active { background:#F1EADC; }

/* 🛑 THE BAR MUST NOT GROW. It is sticky, so every pixel it gains is taken from
   every screen underneath it for ever. With the SANDBOX badge and the build
   stamp both up here, "New Hampton, NY" wrapped to two lines and the bar went
   53px → 80px. The identity is two fixed lines that ellipsise; nothing in this
   block may wrap. */
/* 🛑 NEVER SHRINKS. The dock's identity is the one thing on this bar a receiver
   must always be able to read in full; the build stamp beside it is a
   reference. Left shrinkable, the browser ellipsised "Francesville receiving"
   to make room for a timestamp. */
#bar-right {
  display:flex; flex-direction:column; align-items:flex-end; line-height:1.15;
  min-width:0; flex:0 0 auto;
}
#who, #site { max-width:100%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
#who  { color:var(--cream); font-size:14px; font-weight:700; }
#site { color:rgba(255,252,199,.62); font-size:12px; font-weight:600; }

/* The screen title. The floor app gives each screen a big h1 rather than a
   sticky NetSuite title strip, so this is type on paper, not a bar. */
#pagetitle { padding:16px 16px 0; max-width:640px; margin:0 auto; }
#title { display:block; font-size:26px; line-height:1.15; font-weight:800; letter-spacing:-.02em; }
/* 🗣️ Dael, 2026-10-19: "way too big ... not discreet, nicely modern".
   One slim segmented control - a single rounded track, the chosen segment
   lifted - used by the home Show row and the Received kinds. */
.seg { display:flex; gap:2px; padding:3px; margin:0 0 10px; border-radius:10px;
  background:rgba(120,110,90,.12); overflow-x:auto; scrollbar-width:none; }
.seg::-webkit-scrollbar { display:none; }
.seg button { flex:1 1 auto; min-height:34px; padding:0 10px; border:0; border-radius:8px;
  background:transparent; color:var(--dim); font:600 14px -apple-system,system-ui,sans-serif; white-space:nowrap; }
.seg button.on { background:var(--card); color:var(--ink); box-shadow:0 1px 3px rgba(0,0,0,.14); }
.rv-bar { margin:0 0 10px; }
.rv-q { width:100%; box-sizing:border-box; min-height:42px; padding:0 14px; border-radius:10px;
  border:1px solid var(--line); background:var(--card); font-size:16px; }
.rv-when { display:flex; flex-wrap:wrap; align-items:center; gap:8px; margin:0 0 10px; }
.rv-win { min-height:36px; padding:0 10px; border-radius:8px; border:1px solid var(--line);
  background:var(--card); font:600 14px -apple-system,system-ui,sans-serif; color:var(--ink); }
.rv-pick { display:inline-flex; align-items:center; gap:6px; flex-wrap:wrap; }
.rv-pick input[type=date] { min-height:36px; padding:0 8px; border-radius:8px; border:1px solid var(--line);
  background:var(--card); font-size:14px; }
.rv-dash { color:var(--dim); }
.rv-go { min-height:36px; padding:0 14px; border-radius:8px; border:0; background:var(--ink, #1f2a44);
  color:#fff; font:700 14px -apple-system,system-ui,sans-serif; }
/* 🗣️ 2026-10-19: one Bin transfer card per lot, with where it sits. */
.mv-where { display:flex; flex-wrap:wrap; gap:6px; margin-top:4px; }
.mv-part { font-size:13px; padding:2px 8px; border:1px solid var(--line); border-radius:8px; background:var(--card); }
.mv-from { padding:8px 4px; display:flex; flex-direction:column; gap:6px; }
.mv-fromb { text-align:left; font-size:16px; }
.mv-fromb.on { border-color:var(--brown, #6b4f2a); border-width:2px; }
#title-row { display:flex; align-items:center; justify-content:space-between; gap:12px; }
#title-row #title { flex:1 1 auto; }
/* 🗣️ 2026-10-19: a small square, easy to find, next to "Before unloading". */
.skip-top { flex:0 0 auto; min-width:88px; min-height:56px; padding:8px 10px; border-radius:10px;
  border:2px solid var(--brown, #6b4f2a); background:var(--card); color:var(--brown, #6b4f2a);
  font:800 15px -apple-system,system-ui,sans-serif; line-height:1.1; }
.skip-top:active { background:#F1EADC; }
#back {
  display:inline-flex; align-items:center; margin:0 0 10px; padding:10px 16px 10px 12px;
  background:var(--card); border:1.5px solid var(--line); border-radius:999px;
  color:var(--brown); font:800 15px -apple-system,system-ui,sans-serif;
}
#back:active { background:#F1EADC; }

.screen { padding:12px; max-width:640px; margin:0 auto; }
h1 { font-size:15px; font-weight:700; margin:2px 0 10px; color:var(--ink); }
.stack { display:flex; flex-direction:column; gap:0; }
.sub { color:var(--dim); font-size:12px; }
.muted { color:var(--dim); }
.note { color:var(--dim); font-size:12px; text-align:center; margin-top:10px; }
.prompt { margin:14px 0 8px; font-size:14px; font-weight:600; }

/* --- portlet-style list rows, striped like a NetSuite sublist ------------- */
.card {
  background:var(--card); border:1px solid var(--line); border-top:0;
  border-radius:0; padding:11px 12px; text-align:left; color:inherit; font:inherit; width:100%;
  min-height:52px;
}
.stack > .card:first-child { border-top:1px solid var(--line); border-radius:14px 14px 0 0; }
.stack > .card:last-child  { border-radius:0 0 14px 14px; }
.stack > .card:nth-child(even) { background:var(--row-alt); }
.card.done { opacity:.7; }
button.tap:active, .chip:active { background:#F1EADC; }
.row { display:flex; align-items:center; gap:8px; justify-content:space-between; }
.row.nums { justify-content:flex-start; gap:8px; margin-top:10px; flex-wrap:wrap; }
.pill {
  background:var(--line-soft); color:var(--ink); border:1px solid var(--line);
  border-radius:3px; padding:1px 7px; font-size:11px; font-weight:600;
}
.pill.ok { background:#E6F0E9; border-color:#A9CBB7; color:var(--ok); }

.chip {
  background:var(--card); border:1px solid var(--line); color:var(--ink);
  border-radius:12px; padding:10px 12px; font:inherit; min-height:48px; flex:1;
}
/* Same reason as .tile.need: this chip is the photo or the reason a claim line
   is still missing, and it has to read as WAITING, not as decoration. */
.chip.need { border-color:var(--need-strong); color:var(--need-strong); background:var(--need-fill); }
/* A vendor-derived default is not the same as a value someone looked at. It is
   filled in and legible, but visibly not yet confirmed. */
.chip.suggest { border-color:var(--brown); background:#F1EADC; }
.chip.wide-chip { flex:1 1 100%; text-align:left; }
/* The chosen window on Received. COLOUR only - the label is the same size and
   weight selected or not, so the row does not reflow when the choice changes. */
.chip.on { background:var(--line-soft); border-color:var(--brown); }

/* The Receive By window. Two native date inputs - the OS picker is better than
   anything worth building here, and it is already the keyboard a phone shows. */
#window { display:flex; gap:8px; margin:10px 0 6px; }
#window label {
  flex:1; display:flex; flex-direction:column; gap:4px;
  font-size:12px; color:var(--dim);
}
#window input {
  border:1px solid var(--line); border-radius:12px; background:var(--card);
  color:var(--ink); font:inherit; padding:10px; min-height:44px;
}
#window-summary { text-align:left; margin:0 0 10px; }
.s-bad { color:var(--bad); font-weight:700; }
.s-warn { color:var(--need); font-weight:700; }

/* LATE / TODAY / INCOMING - the Pending Receive Dashboard's own vocabulary and
   its red / amber / grey, so the dock and the office are calling the same truck
   the same thing. */
.pill.h-bad  { background:#F7E7E4; border-color:#E3B5AE; color:var(--bad);  font-weight:700; }
.pill.h-warn { background:#FBF0DC; border-color:#E8CB93; color:var(--need); font-weight:700; }
.pill.h-open { background:var(--line-soft); border-color:var(--line); color:var(--dim); }

/* The board card. Weight is the number a receiver reads first - it is what
   says pallet or box - so it gets the size. */
.card.load .v { font-weight:600; color:var(--ink); }
.card.load .qty { font-size:15px; color:var(--ink); margin-top:2px; }
.card.load .qty b { font-size:17px; }
.card.load .memo { font-style:italic; }
.flags { display:flex; gap:6px; flex-wrap:wrap; margin-top:6px; }
.flag {
  font-size:11px; font-weight:700; letter-spacing:.02em; border-radius:3px;
  padding:3px 7px; background:#FBF0DC; border:1px solid #E8CB93; color:var(--need);
}
.flag.open { background:#F1EADC; border-color:#C9BBA6; color:var(--brown); }
/* Receipts posted against this order TODAY — rare, and it means somebody is on
   this truck right now. 🛑 Deliberately NOT the old "Partially Received" flag,
   which sat on six Miami cards in eight and was noise. */
.flag.part { background:#FDF3E3; border-color:#E0BE7A; color:#8A5B12; }

/* What an earlier receipt already made from this line. Muted, because it is
   context rather than a thing to act on - until the lot number clashes, which
   is not muted at all. */
.priors { margin-top:8px; border-top:1px dashed var(--line); padding-top:8px; }
.priors-h {
  font-size:11px; font-weight:700; letter-spacing:.03em; text-transform:uppercase;
  color:var(--dim); margin-bottom:4px;
}
.prior {
  display:flex; align-items:baseline; gap:6px; flex-wrap:wrap;
  font-size:13px; color:var(--ink); padding:3px 0;
}
.prior i { font-style:normal; font-size:11px; color:var(--dim); }
.prior.clash { color:var(--bad); font-weight:600; }
.clash-note {
  margin-top:6px; padding:8px 10px; border-radius:12px; font-size:12px; line-height:1.4;
  background:#F7E7E4; border:1px solid #E3B5AE; color:var(--bad);
}


/* Country of origin, as a sheet over the line list rather than a screen: it is
   one tap from a card and one tap back, and it must not lose the list. */
#coo {
  position:fixed; inset:0; background:rgba(15,23,42,.55);
  display:flex; align-items:flex-end; z-index:40;
}
#coo-panel {
  background:var(--paper); width:100%; max-height:85vh; overflow-y:auto;
  border-radius:10px 10px 0 0; padding:16px;
}
#coo-panel h1 { margin:0 0 4px; }
.card.coo { min-height:52px; font:inherit; text-align:left; }
.card.coo.on { border-color:var(--brown); background:#F1EADC; font-weight:700; }
.x { background:none; border:0; color:var(--brown); font-size:12px; text-decoration:underline; }

/* status colours double as the grade vocabulary */
.g { border-radius:3px; padding:2px 8px; font-weight:700; font-size:12px; color:#0b1620; }
.gA{background:var(--A)} .gB{background:var(--B)} .gC{background:var(--C)}
.gD{background:var(--D);color:#fff} .gL{background:var(--L)} .gT{background:var(--T);color:#fff}

.variance, .equiv { text-align:center; font-size:13px; min-height:20px; color:var(--dim); }
.ok{color:var(--ok)} .need{color:var(--need)} .short{color:var(--need)} .over{color:var(--bad)}
.warn{color:var(--need);font-weight:600}

/* --- receive & grade: the rows ARE the count -----------------------------
   🗣️ Dael, 2026-08-25: "received qty 30 lbs - grade C - pics / I can add a
   line". One row per grade, and the row is the whole interaction: a quantity,
   the unit it was counted in, the grade, and its evidence. Everything is a
   native control, so the OS supplies the keypad and the picker.

   🛑 Every control is >=44px tall. This is used with cold hands, on a dock,
   sometimes through gloves - the reason the old keypad buttons were 58px. */
.variance-bar {
  margin:10px 0 12px; padding:9px 12px; border-radius:12px; text-align:center;
  font-size:14px; background:var(--line-soft); border:1px solid var(--line);
  font-variant-numeric:tabular-nums;
}
.card.grow { padding:10px 12px; }
/* A row the receiver has not finished is visibly a DRAFT: nothing is sent to
   the server until it has both a quantity and a grade, and the card should say
   so rather than looking committed. */
.card.grow.draft { border-style:dashed; background:var(--line-soft); }
/* 🛑 The row has to hold five controls at 390px and NOTHING may clip - a grade
   reading "T — 100% gar" is the app failing to say which grade it is. So the
   widths are budgeted rather than left to the browser: the quantity is fixed
   (it holds 2-5 digits), the unit takes only what its longest name needs, and
   the GRADE gets everything left over, because its label is the longest and the
   most load-bearing. */
.grow-in { display:flex; align-items:center; gap:5px; }
.grow-in .qty {
  flex:0 0 74px; min-width:0; min-height:46px; font:inherit; font-size:19px; font-weight:700;
  text-align:right; padding:6px 7px; border:1px solid var(--line); border-radius:10px;
  background:var(--card); color:var(--ink); font-variant-numeric:tabular-nums;
}
/* 🗣️ Dael, 2026-08-26: "put a drop down on the unit of measure." A <select>,
   for the same reason the grade and the country are: it looks like a control,
   it gets the OS wheel, and it does not cover the row being worked on. */
.grow-in .unit {
  flex:0 0 auto; min-height:46px; padding:6px 2px 6px 6px; font:inherit; font-size:12px;
  border:1px solid var(--line); border-radius:10px; background:var(--card); color:var(--ink);
  white-space:nowrap; max-width:72px;
}
/* A line whose vendor unit IS the stock unit has nothing to toggle, so it gets
   a label rather than a button that cannot do anything. */
.grow-in .unit.flat {
  border-color:transparent; background:none; color:var(--dim);
  line-height:46px; padding:0 2px;
}
.grow-in .grade {
  flex:1 1 auto; min-width:0; min-height:46px; font:inherit; font-size:14px; font-weight:700;
  padding:6px 2px 6px 6px; border:1px solid var(--line); border-radius:10px;
  background:var(--card); color:var(--ink);
}
/* The grade colours the control it was chosen in, so a row's grade is readable
   at arm's length without reading the letter. */
.grow-in .grade.gA{background:var(--A)} .grow-in .grade.gB{background:var(--B)}
.grow-in .grade.gC{background:var(--C)} .grow-in .grade.gD{background:var(--D);color:#fff}
.grow-in .grade.gL{background:var(--L)} .grow-in .grade.gT{background:var(--T);color:#fff}
.grow-in .chip { flex:0 0 auto; min-height:46px; padding:6px 8px; }
.grow-in .chip:disabled { opacity:.35; }
.grow-in .x { flex:0 0 auto; font-size:20px; text-decoration:none; padding:0 2px; min-height:46px; }
.grow-sub {
  margin-top:6px; font-size:12px; color:var(--dim); font-variant-numeric:tabular-nums;
}
.grow-sub code { font-size:12px; }

/* 🗣️ Dael, 2026-08-26: "I still need to put the reason for the grade C."
   Why the grade, on its own full-width row under the quantity - the main row
   already holds four controls at 390px and a fifth would clip the grade again.
   Shown on C, D and T (refreshGrade toggles it).
   🗣️ Dael, 2026-09-09: "the grade C reason I wanna leave that in black."
   🛑 AMBER MEANS "THIS IS HOLDING THE CLOSE", and since C stopped being gated
   on a reason it is no longer true there - amber on an optional field tells a
   receiver she is blocked when she is not, which is the whole complaint. So the
   fill is driven by `.need`, which refreshGrade now only sets for D and T; C
   renders in plain ink like any other optional control. */
.card.grow .reason {
  width:100%; margin-top:6px; min-height:46px; font:inherit; font-size:14px;
  padding:6px 2px 6px 6px; border:1px solid var(--line); border-radius:10px;
  background:var(--card); color:var(--ink);
}
.card.grow .reason.need { border-color:var(--need-strong); border-width:2px; background:var(--need-fill); }

/* "Did not arrive" reads as a statement, not an action - quiet, and amber
   because it is a claim against the vendor. */
.miss {
  background:none; border:0; color:var(--need); font:600 13px -apple-system,system-ui,sans-serif;
  min-height:44px; text-decoration:underline; margin-bottom:2px;
}
/* 🗣️ Yaki, via Dael, 2026-09-09: receive the cilantro now, leave the rest.
   🛑 DELIBERATELY NOT AMBER. It sits directly under "This line did not arrive"
   and the two say opposite things about the PO - one ends the order line, this
   one keeps it. Matching the amber would make them read as a pair of equal
   options at arm's length on a dock. Neutral ink, same quiet weight, same 44px
   target. */
.later {
  background:none; border:0; color:var(--brown); font:600 13px -apple-system,system-ui,sans-serif;
  min-height:44px; text-decoration:underline; margin-bottom:2px;
}
/* 🗣️ "(I can add a line)" - the whole flow in one button. */
.add {
  background:var(--card); border:1px dashed var(--line); color:var(--brown);
  border-radius:12px; font:inherit; font-weight:600; min-height:48px; margin-bottom:4px;
}

/* NetSuite's action buttons are small and flat; this one has to be hit with a
   glove, so it keeps the height and takes the colour. */
.primary {
  background:var(--brown); color:#fff; border:1px solid var(--brown-deep); border-radius:12px;
  padding:14px; font-size:15px; font-weight:600; min-height:52px;
}
.primary:disabled { background:var(--line-soft); color:var(--dim); border-color:var(--line); }
.wide { width:100%; margin-top:14px; }

/* 🗣️ Dael, 2026-09-12: "I don't like this bs little line bottom. I like real
   buttons." The count's stock-moving actions were styled as underlined text
   (.x) — footnotes, not controls. This is the real face for them: card
   background, border, full glove-height tap target. Deliberately quieter
   than .primary so the armed confirm still outranks the offer. */
.btn2 {
  background:var(--card); color:var(--brown); border:1px solid var(--line);
  border-radius:12px; padding:12px; font-size:14px; font-weight:600;
  min-height:48px; text-decoration:none;
}
.btn2:active { background:#F1EADC; }
.btn2:disabled { color:var(--dim); border-color:var(--line-soft); }

/* When an assembly was last built, on its count row. Amber past 30 days —
   at that age the on-hand is stranded stock, not build-ahead. */
.cc-built { color:var(--dim); }
.cc-built.old { color:var(--need); font-weight:600; }

/* The batch-disassemble checkbox — glove-sized, riding beside the 🧩. */
.cc-asmpick { width:24px; height:24px; margin:0 2px; vertical-align:middle; accent-color:var(--brown); }

/* The bulk runs' heartbeat — visible from across a dock while thirty NetSuite
   saves grind through. Fill = edits finished (posted or skipped). */
.cc-progress {
  position:relative; height:30px; margin:10px 0;
  border:1px solid var(--line); border-radius:8px;
  background:var(--line-soft); overflow:hidden;
}
.cc-progress i {
  display:block; height:100%; width:0%;
  background:#A9CBB7; transition:width .3s ease;
}
.cc-progress-txt {
  position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
  font-size:12px; font-weight:700; color:var(--ink);
}
/* 🗣️ Dael, 2026-09-10: "clear the whole information we entered." It is a
   DESTRUCTIVE control on the same screen as Submit, so it has to be reachable
   with a glove and impossible to mistake for the primary action: outlined in
   the bad colour rather than filled with it, so the eye lands on Submit first
   and on this one only when it is being looked for. Armed, it fills, because at
   that point the next tap really does throw the count away. */
.danger {
  background:var(--card); color:var(--bad); border:1px solid var(--bad);
  border-radius:12px; padding:14px; font-size:15px; font-weight:600; min-height:52px;
}
.danger[data-armed] { background:var(--bad); color:#fff; }

.error { color:var(--bad); text-align:center; margin:10px 0; font-size:13px; line-height:1.45; }
/* A refusal that needs a sentence to explain itself reads left-aligned - the
   bin messages are instructions, not a one-word status. */
.error b { display:block; margin-bottom:2px; font-size:14px; }

/* camera overlay — full bleed, above everything */
#cam { position:fixed; inset:0; z-index:20; background:#000; }
#cam-video { width:100%; height:100%; object-fit:cover; }
/* The reticle must stay aligned with scanner.js's CROP_FRACTION (0.7) or the
   operator aims at a box we do not actually read. Change one, change both. */
#reticle {
  position:absolute; top:50%; left:50%; transform:translate(-50%,-50%);
  width:70vmin; height:70vmin; border:3px solid rgba(255,255,255,.85); border-radius:8px;
  box-shadow:0 0 0 100vmax rgba(0,0,0,.45); pointer-events:none;
}
#cam-hint {
  position:absolute; left:0; right:0; bottom:120px; margin:0; text-align:center;
  color:#fff; font-size:15px; text-shadow:0 1px 3px #000;
}
#cam-actions {
  position:absolute; left:0; right:0; bottom:calc(28px + env(safe-area-inset-bottom));
  display:flex; gap:12px; justify-content:center;
}
#cam-actions button {
  background:rgba(255,255,255,.14); color:#fff; border:1px solid rgba(255,255,255,.35);
  border-radius:999px; padding:14px 26px; font:inherit; min-height:52px;
}
#cam-actions button.on { background:#f5d90a; color:#111; border-color:#f5d90a; }

/* A lot field and its camera. The button is deliberately a WORD, not a glyph:
   the same phones run the floor app, where every action is spelled out, and a
   camera icon on a cooler aisle in gloves is a guess. min-width keeps it a
   thumb-sized target while the field keeps everything else. */
.scanrow { display:flex; gap:8px; align-items:stretch; margin-top:4px; }
.scanrow input { margin-top:0; flex:1 1 auto; min-width:0; }
.scanrow .cam {
  flex:0 0 auto; min-width:84px; min-height:48px; padding:0 16px;
  font:inherit; font-weight:600; border-radius:12px;
  border:1px solid var(--line); background:var(--card); color:var(--ink);
}
/* Hidden where the camera cannot possibly work rather than left to fail: an
   insecure origin has no getUserMedia at all, and a button that always says
   "the camera is not available" is worse than no button. app.js sets this. */
.scanrow.nocam:not(.hw) .cam { display:none; }
/* A camera failure is an instruction ("type it below"), not a status word, and
   instructions read left-aligned - the same reason .error b exists. */
.scan-err { text-align:left; }
/* The handheld trigger's "Ready" line: an instruction, not a failure. */
.scan-hint { color:var(--dim); margin:8px 0; font-size:13px; line-height:1.45; }
.scan-hint:empty { display:none; }

/* The bin break on a whole-site count sheet. Sticky because a counter halfway
   down a 40-lot bin must still be able to see WHICH bin they are standing in -
   that is the one fact the rows below no longer repeat. */
.cc-binhead {
  position:sticky; top:0; z-index:2;
  display:flex; justify-content:space-between; align-items:baseline; gap:10px;
  margin:14px 0 4px; padding:8px 12px; border-radius:10px;
  /* --brown IS the nav bar in both palettes (the NS skin remaps it to #25364D),
     so the bin break reads as a bar rather than a card in either skin. */
  background:var(--brown); color:#fff; font-size:13px;
}
.cc-binhead b { font-size:14px; letter-spacing:.02em; }
.cc-binhead span { opacity:.75; }
/* A finished aisle, readable at arm's length - and with a ✓ in the markup, not
   a new colour: this is the same bar in both skins and gains no palette. */
.cc-binhead .cc-bindone { opacity:1; font-weight:600; }
/* Inventory adjustments. A reversed document is struck through rather than
   hidden: it is still a real entry on the record, and a receiver who cannot
   find yesterday's document assumes the list is broken. */
#adj-window { display:flex; gap:10px; margin-bottom:8px; }
#adj-window label { flex:1 1 0; font-size:12px; color:var(--dim); }
#adj-window input { width:100%; }
#adj-filters { flex-wrap:wrap; gap:6px; margin:6px 0 10px; }
#adj-filters .chip { min-height:44px; padding:8px 14px; border:1px solid var(--line);
  border-radius:10px; background:var(--card); color:var(--ink); font:inherit; }
#adj-filters .chip.on { background:var(--brown); color:var(--cream); border-color:var(--brown); }
.adj-reversed b { text-decoration:line-through; }
#adj-back { margin:0 0 8px; min-height:var(--tap); text-align:left; }
#adj-lines .cc-entry b { font-size:15px; }

/* 🗣️ Dael, 2026-08-27: "the organics should be separated and by alphabet order
   just don't make any dropdown on that part."
   🛑 A plain heading, never a drawer. Organic vs conventional is the one split
   on this sheet where being wrong is a certification failure, so it may not be
   a tap away - a collapsed drawer is a bin nobody counted. Typographic only:
   weight, spacing and a rule. No new colour, in either skin. */
.cc-orghead {
  margin:10px 0 2px; padding:2px 2px 4px;
  border-bottom:2px solid var(--line);
  font-size:12px; font-weight:800; letter-spacing:.10em; color:var(--dim);
}
.cc-orghead.org { border-bottom-color:var(--brown); color:var(--ink); }

/* 🗣️ Dael, 2026-09-04: "we need to separate shakers, peppers, shroomz" on the
   walking count sheet too. Reuses the picker's .mv-plhead/.pl-* colour rules
   (styles.css:861+) as a STATIC header - display only, no button, no chevron:
   this sheet's job is "count what you SEE" and a closed tray here is a shelf
   left uncounted. */
.cc-plhead { display:flex; align-items:center; gap:6px;
  border-top:2px solid var(--line); margin-top:10px; padding:6px 2px 4px; }
.cc-plhead .mv-gname { flex:1 1 auto; font-size:13px; font-weight:800;
  text-transform:uppercase; letter-spacing:.08em; }
.cc-plhead .mv-gsum { flex:0 0 auto; font-size:12px; color:var(--dim); }

/* 🗣️ Dael, 2026-08-27: "we need a way to go up and down on the screen fast."
   🛑 LEFT edge. Every row's entry box is on the right, and a floating control
   over a count box is a control that eats counts. */
#cc-jump {
  position:fixed; left:8px; bottom:14px; z-index:6;
  display:flex; flex-direction:column; gap:6px;
}
/* 🛑 The floating control needs headroom: without this it sits ON the left
   edge of the full-width Review button and on the DUMPED / NOT FOUND chips of
   the last rows - a jump control that can eat the Post tap is worse than no
   jump control. The pad scrolls the real controls clear of it. */
#cc-sheet { padding-bottom:120px; }
#cc-jump button {
  min-height:46px; padding:6px 10px; border-radius:12px;
  border:1px solid var(--line); background:var(--card); color:var(--ink);
  font:inherit; font-size:12px; font-weight:700; white-space:nowrap;
  box-shadow:0 2px 8px rgba(0,0,0,.18); opacity:.94;
}

/* 🗣️ Dael, 2026-08-27: "Add a bottom here to move everything to cooler bin."
   It lives IN the bar, because the bin it empties is the bin the bar names.
   🛑 A real tap target on a sticky bar the thumb is already scrolling past -
   44px floor, and it opens a confirm panel, it never moves stock on one tap. */
.cc-binhead .cc-sweepbtn {
  flex:0 0 auto; min-height:44px; padding:6px 12px; border-radius:9px;
  border:1px solid rgba(255,255,255,.55); background:transparent;
  color:#fff; font:inherit; font-weight:600; white-space:nowrap;
}
/* 🗣️ Dael, 2026-09-07: "we just need another one next to it that says
   -> assemblies". Same shape as the sweep beside it - but NOT the same kind of
   button, so it does not wear the same weight. "-> cooler" is one Bin Transfer
   and can be moved back; this REWRITES POSTED ASSEMBLY BUILDS. Quieter border,
   same 44px target. 🛑 Never restyle these two to look identical. */
.cc-binhead .cc-asmbtn { border-color:rgba(255,255,255,.32); margin-left:6px; }

/* The confirm panel. It names the bin, the lot count, the pounds and the
   destination before anything posts - a sweep is one Bin Transfer over a whole
   bin, which is the largest single thing this app writes. */
#cc-sweep { margin-bottom:10px; border-left:4px solid var(--brown); }
#cc-sweep .cc-l1 { font-size:14px; line-height:1.35; }

.photos { justify-content:flex-start; margin-top:10px; }
.photos .chip { flex:0 0 auto; min-width:150px; }

/* Enrolment. The token is typed once, on a phone, often outdoors: a big field,
   monospace so O and 0 are distinguishable, and no autocorrect. */
#enroll-token {
  width:100%; box-sizing:border-box; margin:12px 0;
  background:var(--card); border:1px solid var(--line); border-radius:12px;
  color:var(--ink); font:600 19px/1.2 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing:2px; text-align:center; padding:13px;
}
#enroll-token::placeholder { color:var(--dim); letter-spacing:normal; font-weight:400; }
#enroll .note { text-align:left; margin-top:0; }

/* A field group, the way a NetSuite subtab frames one. */
.screen > h1 {
  background:var(--line-soft); border:1px solid var(--line); border-bottom:0;
  border-radius:14px 14px 0 0; padding:7px 10px; margin:0 0 0;
  font-size:12px; letter-spacing:.04em; text-transform:uppercase; color:var(--ink);
}
.screen > h1 + .stack > .card:first-child { border-radius:0; }
.screen > h1 + .stack { margin-bottom:12px; }

/* Grading is QC's job and happens on its own clock, so an ungraded line is a
   normal state, not a fault. The chip is neutral until it is done. */
.chip.done-chip { border-color:#A9CBB7; background:#E6F0E9; color:var(--ok); font-weight:600; }

/* ---------- the pre-check ---------- */
/* Yes/No PAIRS, not toggles. A toggle has a default and a default is exactly
   what makes an unanswered check look answered - which is the Sysco finding
   this screen exists to close. An untouched row is deliberately colourless:
   it must not read as either answer. */
.checkpair {
  display:flex; align-items:center; gap:8px; justify-content:space-between;
  background:var(--card); border:1px solid var(--line);
  padding:10px 12px; min-height:56px; border-radius:14px; margin:10px 0;
}
.ck-l { font-size:14px; line-height:1.25; }
.ck-l .hint { display:block; color:var(--dim); font-size:11px; font-style:italic; }
.ck-b { display:flex; gap:6px; flex-shrink:0; }
.ck {
  min-width:56px; min-height:44px; border:1px solid var(--line); background:var(--card);
  border-radius:12px; font:inherit; font-weight:600; color:var(--dim);
}
.ck.yes.on { border-color:var(--ok); background:#E6F0E9; color:var(--ok); }
.ck.no.on  { border-color:var(--bad); background:#F7E7E4; color:var(--bad); }

/* A field the server named as blank. Marking the field beats a message that
   says "something is missing" and leaves the operator to hunt for it.
   🛑 Qualified with .fld on purpose. A bare `select.need` ties on specificity
   with the `.fld select` rule below it and LOSES to it on source order, so the
   highlight silently did nothing on exactly the two fields most likely to be
   blank - the QC name and the temperature. Caught in the browser; both tests and
   the walkthrough are blind to it. */
.checkpair.need, .fld.need,
.fld select.need, .fld input.need, .fld textarea.need {
  border-color:var(--bad); background:#FBF1EF;
}

/* The read-only PO view. Nothing on a .ro card is tappable, and it must not
   invite a tap: no shadow, no chevron, flat against the page. */
.card.ro { box-shadow:none; }

.fld { display:block; margin:10px 0; font-size:12px; color:var(--dim); }
.fld input, .fld textarea, .fld select {
  display:block; width:100%; margin-top:4px; padding:12px; font:inherit;
  min-height:48px; border:1px solid var(--line); border-radius:12px;
  background:var(--card); color:var(--ink);
}
.fld .hint { color:var(--dim); font-size:11px; font-style:italic; }

/* \u{1F6D1} A TICK THE FLOOR HAS TO READ AND HIT. `.fld input` is display:block,
   width:100%, so reusing .fld for a checkbox rendered a full-width square
   floating above its own sentence - a declaration nobody could parse and a
   target nobody could aim at. A declaration is a ROW: the box, then the words
   it is agreeing to, in a card big enough for a thumb in a cold room. */
.gh-regrow {
  display:flex; align-items:flex-start; gap:10px;
  margin:10px 0; padding:12px; font-size:14px; color:var(--ink);
  border:1px solid var(--line); border-radius:12px; background:var(--card);
}
.gh-regrow input[type=checkbox] {
  display:inline-block; width:24px; height:24px; min-height:24px;
  margin:2px 0 0; flex:none; accent-color:var(--brown);
}
.temps { display:flex; gap:8px; }
.temps .fld { flex:1; }

/* The verdict is not a colour alone: "still to answer" and "fails" are
   different states and a receiver must never confuse the two. */
.verdict { margin:12px 0; padding:10px 12px; border-radius:12px; font-size:13px; border:1px solid var(--line); }
.verdict.ok   { background:#E6F0E9; border-color:#A9CBB7; color:var(--ok); font-weight:600; }
.verdict.todo { background:var(--line-soft); color:var(--dim); }
.verdict.bad  { background:#F7E7E4; border-color:#E3B5AE; color:var(--bad); }
.verdict ul { margin:4px 0 0; padding-left:18px; }
.verdict .v-h { font-weight:700; }
.verdict .v-note { margin-top:8px; font-style:italic; color:var(--ink); }

.precheck-chip { margin-top:4px; cursor:pointer; }
.precheck-chip u { color:var(--brown); }

/* The verdict lines the pre-check renders one at a time. */
.v-bad { color:var(--bad); font-weight:600; }
.v-ok  { color:var(--ok); font-weight:600; }

/* ---------- home ---------- */

/* A module row is a card like every other row in this app. The ready ones are
   buttons; the unbuilt ones are NOT - see renderHome. */
.mod { display:flex; align-items:center; justify-content:space-between; gap:10px;
       width:100%; min-height:64px; font:inherit; text-align:left; color:var(--ink); }
.mod .mod-l { display:flex; flex-direction:column; gap:2px; }
.mod .mod-name { font-weight:600; font-size:15px; }
.mod .mod-sub { font-size:12px; color:var(--dim); }
.mod .go { color:var(--brown); font-size:20px; line-height:1; }

/* Not grey-on-grey: "soon" is information, not a disabled state. */
.mod.soon { color:var(--dim); }
.mod.soon .mod-name { font-weight:500; }
.chip.soon { border-color:var(--line); color:var(--dim); background:var(--line-soft);
             text-transform:uppercase; letter-spacing:.06em; font-size:10px; }

/* The sign-out footer. Set well below the modules and in quiet type: it is the
   least-used control on the screen and must never compete with the thing the
   receiver actually opened the app for. */
#home-foot { margin-top:28px; padding-top:16px; border-top:1px solid var(--line-soft);
             text-align:center; }
#signed-in { font-size:12px; color:var(--dim); line-height:1.5; }
#signed-in b { color:var(--ink); }
#switch-site { margin-top:10px; }
#switch-warn { text-align:center; margin-top:10px; }

/* ---------- the line-card field grid ---------- */
/* 🗣️ Dael, 2026-08-24, off the live screen: "This is a poor design job. We
   shouldn't be making this terrible sizes boxes like that." The old layout put
   a two-row count chip beside a one-row grade chip and a full-width dropdown
   under both, so every card was a patchwork of box sizes - and the two-unit
   value ("23 1LB · 360 1OZ (EA)") wrapped mid-unit and made it worse.

   ONE tile system now. Every field on a line - expected, received, origin,
   product temp, grade - is a .tile in a 2-column grid: same height, same
   border, same radius, same label treatment. State (needed / vendor-default /
   graded) changes a tile's colour, never its shape. A value never wraps
   mid-unit: the stock figure is the value line, the vendor figure is the
   subline. */
/* 🗣️ Dael, second pass the same night: "This part needs to be a bit more
   compact. Those are big boxes." Same grid, tighter numbers - and the empty
   subline is no longer rendered at all. Equal heights inside each row come
   free from the GRID (cells stretch to their row), so nothing needs padding
   out with reserved blank lines. */
.fields {
  display:grid; grid-template-columns:1fr 1fr; gap:6px; margin-top:8px;
}
.tile {
  display:flex; flex-direction:column; justify-content:center; gap:0;
  min-height:54px; padding:6px 10px; text-align:left;
  background:var(--card); border:1px solid var(--line); border-radius:10px;
  font:inherit; color:var(--ink);
}
.tile.wide { grid-column:1 / -1; min-height:42px; }
.tile.act:active { background:#F1EADC; }
.tile .f-l {
  font-size:10px; font-weight:800; letter-spacing:.07em;
  text-transform:uppercase; color:var(--dim);
}
.tile .f-v { font-size:14px; line-height:1.25; font-variant-numeric:tabular-nums; }
.tile .f-v b { font-size:17px; }
.tile .f-s { font-size:12px; line-height:1.2; color:var(--dim); font-variant-numeric:tabular-nums; }
.tile .f-chips { display:flex; gap:5px; flex-wrap:wrap; align-items:center; margin-top:2px; }
/* read-only tiles sit on the paper, not on white - the eye goes to the ones
   that take input */
.tile.ro { background:var(--row-alt); }
/* 🗣️ Yaki: darker, so a missing datum is visible at arm's length on a dock.
   Two pixels of border and a saturated fill, not a new colour family. */
.tile.need { border-color:var(--need-strong); border-width:2px; background:var(--need-fill); }
.tile.suggest { border-color:var(--brown); background:#F1EADC; }
.tile.ok-tile { border-color:#A9CBB7; background:#F3F8F4; }
/* native controls inside a tile: the tile is the border, the control is bare.
   The OS wheel is a better picker than anything worth building here. */
/* 🛑 font-size stays at 16px: iOS zooms the whole page on focusing any input
   smaller than that, and an unrequested zoom on a dock is a lost receiver. */
.tile select, .tile input {
  width:100%; min-height:28px; font:inherit; font-size:16px; font-weight:600;
  color:var(--ink); background:transparent; border:0; padding:0;
}
.tile input::placeholder { color:var(--dim); font-weight:400; }

/* A line the PO has already received in full. Present so the phone shows the
   same lines the Purchase Order page does, but visibly not work: no chips, no
   tap targets, just the PO's own figures. */
.card.complete { background:var(--line-soft); opacity:.85; }
.card.complete .done-fig { font-size:13px; color:var(--dim); margin-right:14px; }
.card.complete .done-fig b { color:var(--ink); font-size:15px; }

/* The expected day beside the health pill. The pill says LATE/TODAY/INCOMING,
   which is the office's word for it; the date says which day. */
.when { display:flex; align-items:center; gap:8px; flex-shrink:0; }
.when { font-size:13px; font-weight:700; color:var(--dim); font-variant-numeric:tabular-nums; }
.card.load .total { font-weight:700; color:var(--ink); font-variant-numeric:tabular-nums; margin-top:2px; }

/* 🗣️ Description first, item code under it - the order the Purchase Order page
   uses and the order a receiver reads: the thing in the box, then the SKU. */
.card .desc { font-size:15px; line-height:1.25; }
.card .sku { font-family:ui-monospace,SFMono-Regular,Menlo,monospace; font-size:12px;
             letter-spacing:.02em; color:var(--dim); }
.card .po-fig { margin-top:4px; font-variant-numeric:tabular-nums; }
.card .po-fig b { color:var(--ink); }

/* Move bins - same tile family as the board. */
.mv-group { margin:14px 2px 4px; font-size:13px; font-weight:700; color:var(--dim); text-transform:uppercase; }
/* A commodity drawer. Same tap height as every other card - a header that is
   also a control must be as easy to hit as the rows it hides. */
.mv-ghead { display:flex; align-items:center; gap:10px; width:100%; min-height:var(--tap);
  text-align:left; font:inherit; }
.mv-ghead .mv-gname { flex:1 1 auto; font-size:14px; font-weight:700; text-transform:uppercase; }
.mv-ghead .mv-gsum { flex:0 0 auto; font-size:13px; color:var(--dim); white-space:nowrap; }
.mv-ghead .mv-chev { flex:0 0 auto; font-size:15px; color:var(--dim); }
.mv-ghead.open .mv-gname { color:var(--brown); }
/* Batch mode. Ticks are oversized on purpose - they are the tap target's own
   label, and the whole card is the target (never just the glyph). */
.mv-tick, .mv-gtick { flex:0 0 auto; font-size:22px; line-height:1; color:var(--brown); }
.mv-gtick { padding:8px 4px 8px 0; }
.mv-row.sel { outline:2px solid var(--brown); outline-offset:-2px; background:var(--row-alt); }
/* 🗣️ Dael, 2026-09-10: "I wanna a better square nice buttom." This control
   switches the screen into a different MODE - every tap becomes a tick - so it
   cannot go on wearing `.x`, the 12px underlined class used for quiet
   secondary links. Square-shouldered, full width, glove height, and it FILLS
   when the mode is on: the button's own look is the only thing on the screen
   that says which mode you are in. */
.mv-batchbtn {
  display:block; width:100%; margin:0 0 10px; min-height:48px;
  font:inherit; font-size:15px; font-weight:700;
  background:var(--card); color:var(--brown);
  border:1.5px solid var(--brown); border-radius:8px; padding:12px;
}
.mv-batchbtn.on { background:var(--brown); color:#fff; border-color:var(--brown-deep); }

/* One ticked row's quantity, under the row because the row is a <button>.
   The number is the wide half: a receiver types into it, and reads the cap off
   the "all" button beside it. */
.mv-bqty {
  display:flex; align-items:center; gap:8px;
  margin:-4px 0 8px; padding:8px 10px;
  border:1px solid var(--brown); border-top:0; border-radius:0 0 10px 10px;
  background:var(--row-alt);
}
.mv-bqty.bad { border-color:var(--bad); background:var(--need-fill); }
.mv-bqlab { flex:0 0 auto; font-size:13px; color:var(--dim); }
.mv-bqin {
  flex:1 1 auto; min-width:0; min-height:44px; font:inherit; font-size:18px; font-weight:700;
  text-align:right; padding:6px 8px; border:1px solid var(--line); border-radius:8px;
  background:var(--card); color:var(--ink);
}
.mv-bqty.bad .mv-bqin { border-color:var(--bad); border-width:2px; }
.mv-bqall {
  flex:0 0 auto; min-height:44px; padding:6px 12px; font:inherit; font-size:13px; font-weight:600;
  background:var(--card); color:var(--brown); border:1px solid var(--brown); border-radius:8px;
  white-space:nowrap;
}
#mv-batchbar { position:sticky; bottom:0; margin-top:12px; padding:12px;
  padding-bottom:calc(12px + env(safe-area-inset-bottom));
  background:var(--card); border:1px solid var(--line); border-radius:14px;
  box-shadow:0 -4px 16px rgba(63,31,19,.12); }
#mvb-sum { font-weight:700; margin-bottom:8px; }
#mv-batchbar select { width:100%; min-height:46px; font:inherit; font-size:14px; padding:6px;
  border:1px solid var(--line); border-radius:10px; background:var(--card); color:var(--ink); }
.mv-row { display:flex; justify-content:space-between; align-items:center; width:100%; text-align:left; font:inherit; }
.mv-row .sub { display:block; font-size:12px; color:var(--dim); }
.mv-row .mv-r { display:flex; align-items:center; gap:8px; }
.mv-panel .mv-f { display:flex; align-items:center; gap:10px; margin-bottom:8px; }
.mv-panel .mv-f > span:first-child { flex:0 0 44px; color:var(--dim); font-size:13px; }
.mv-panel .qty { flex:0 0 96px; min-height:46px; font:inherit; font-size:19px; font-weight:700; text-align:right; padding:6px 7px; border:1px solid var(--line); border-radius:10px; background:var(--card); color:var(--ink); }
.mv-panel select { flex:1 1 auto; min-height:46px; font:inherit; font-size:14px; padding:6px; border:1px solid var(--line); border-radius:10px; background:var(--card); color:var(--ink); }

/* An open line is visibly unfinished - the amber edge the pre-check chip uses.
   A line that still needs work must never look like a line that is done. */
.card.open { border-left:4px solid var(--need); }

/* 🗣️ Dael 2026-09-15: the chives Tatiana added to a Mister Specialties PO two
   minutes after Jackie received it. The server re-reads the PO on every open
   now, so the line is there - this is how a receiver FINDS it in a list they
   have already read once. The header banner says the truck grew; this says
   where.
   🛑 A tag, not a colour swap. The left edge already carries open-vs-done and
   that distinction is the one that decides whether somebody has work to do;
   overloading it would trade a fact for a novelty. */
.card.just-added { position:relative; }
.card.just-added::after {
  content:"NEW"; position:absolute; top:6px; right:6px;
  font-size:10px; font-weight:800; letter-spacing:.06em;
  padding:1px 5px; border-radius:3px;
  background:var(--need-fill); color:var(--need-strong);
  border:1px solid var(--need-strong);
}

/* Cycle count. An entered row wears the done edge; a zero asks its question
   inline; the reveal colours the delta, never the type. */
/* 🗣️ Dael 2026-08-26, third pass: "The quantity box is too fucking big and it
   doesn't display how I want it should be" — with an exact spec:
       RECBASLB - (grade) / (Lot) - (received date) / (Country)
   Three tight lines on the left, entry box on the right. The row is now as tall
   as its information and no taller.
   🛑 The box is 56px — BELOW --tap (60) and deliberately so: Dael asked three
   times and runs the docks, so this is his call, not a skin quietly shrinking
   ergonomics. It stays at/above the 44px platform floor, and it is a TEXT FIELD
   (mis-taps focus the wrong box, they do not post anything). Every real
   ACTION on this screen — the chips, Review, Post — is still full --tap. */
/* 🗣️ Dael, 2026-08-27: "We have box count on left side and then right side.
   And I like the ones you did on the right side."
   🛑 GRID, not flex-wrap. Under flex-wrap a line is broken on each item's
   CONTENT width BEFORE any shrinking happens, so min-width:0 on the body never
   saved it: a long row (RECBASLB · C · COOLER : LOWER LEVEL · on hand 10)
   pushed the entry box onto its own line, where it landed on the LEFT. Two
   fixed tracks cannot break, so the box is on the right in every row. */
.cc-row { padding:6px 10px; display:grid; grid-template-columns:minmax(0,1fr) auto;
  align-items:center; column-gap:8px; }
.cc-row .cc-body { min-width:0; }
.cc-row .cc-l1 { font-size:14px; line-height:1.25; }
.cc-row .cc-l2, .cc-row .cc-l3 { font-size:12px; line-height:1.25; color:var(--dim); }
/* 🗣️ Fourth pass, and he is right: it was still a form control pretending to
   be a keypad. 48x34, hairline, no chrome - a field you type a number into,
   sized like the number. The unit sits UNDER it rather than beside it, which
   is where the row's width was actually going. */
.cc-row .cc-entry { display:flex; flex-direction:column; align-items:flex-end; gap:2px; }
.cc-row .cc-qty { flex:0 0 auto; width:52px; min-height:34px; height:34px;
  font-size:16px; font-weight:700; text-align:right; padding:2px 5px; border-radius:7px; }
.cc-row .cc-unit { width:64px; min-height:26px; height:26px; font-size:11px; padding:0 2px; border-radius:6px; }
.cc-row .cc-entry .sub { font-size:11px; }
.cc-row.done { border-left:4px solid var(--ok); }
/* The lot also lives in another bin. A warning the counter reads and decides
   on - never the red of an error, because nothing here is wrong. */
.cc-also { display:block; margin-top:3px; color:var(--need); }
.cc-neg { color:var(--bad); }
.cc-pos { color:var(--ok); }
.cc-rev { color:var(--need); }
/* 🗣️ Dael, 2026-08-27: "I can't go back without clicking review and posting."
   The way out sits at the TOP of the sheet: a counter 180 rows into a cooler
   should not have to scroll past the whole walk to leave the scope. */
#cc-back { margin:0 0 8px; min-height:var(--tap); text-align:left; }
/* The live count, on the picker. Marked like a row in progress, because that
   is what it is - the count is still open behind the menu. */
.cc-live { border-left:4px solid var(--brown); }
.cc-live .cc-l1 { font-size:14px; line-height:1.3; }
/* The on-hand figure on a sighted sheet: legible but quiet, and never beside
   the entry box - a number touching the box gets copied into it.
   🗣️ Dael, 2026-08-27: "I also don't like that you put 'sys' wtf is 'sys'. It
   should be 'on hand'." The floor's word, everywhere, class included. */
.cc-onhand { color:var(--dim); font-weight:700; }
/* The found-stock warning: this screen is about to offer an edit to a posted
   build, so it must not look like an ordinary row. */
.cc-missing { border-left:4px solid var(--need); }
.cc-missing .sub { margin-top:4px; }
.cc-done { border-left:4px solid var(--ok); padding:10px; background:var(--card); border-radius:10px; }

/* Settings — 🗣️ Dael 2026-08-27. A switched-off module renders GREYED with the
   reason, never hidden: a control that vanishes reads as a broken app. */
.mod.capoff { opacity:.55; }
.set-cap { display:grid; grid-template-columns:minmax(0,1fr) auto; align-items:center; column-gap:8px; padding:8px 10px; }
.set-cap.off { opacity:.6; }
.set-cap .chip { min-height:44px; min-width:64px; padding:8px 14px; border:1px solid var(--line);
  border-radius:10px; background:var(--card); color:var(--ink); font:inherit; font-weight:700; }
.set-cap .chip.on { background:var(--brown); color:var(--cream); border-color:var(--brown); }
.set-logrow { font-size:12px; opacity:.8; margin:2px 0; }
/* Greenhouse. 🛑 ORGANIC is VOCABULARY, like the grade colours — never struck
   through, because a struck-out ORGANIC reads as "not organic" on the one
   screen where that is a certification failure. */
.gh-org { color:var(--need); font-weight:800; letter-spacing:.02em; }
/* Finished work, collapsed. Visible because "where's the one I did?" is a real
   question — just not the first one. */
.gh-done { text-align:center; color:var(--dim); }
.gh-builtrow { opacity:.65; }
/* The item picker: rows, not a native <select>. It rendered blank on Dael's
   phone, and a picker that shows nothing is a dead end on a dock. */
.gh-pick { margin:8px 0; }
.gh-pickrow.on { background:#F1EADC; border-color:var(--brown); border-width:2px; }
/* The ORGANIC / CONVENTIONAL section header. It rendered as an unstyled div
   next to rows whose titles are <b> — so the split that is a certification
   control was LESS prominent than the rows it separates. Mirrors .cc-orghead
   above (same split, same screen family, same "the organics should be
   separated" ask): typographic-only, no new colour. No position:sticky — this
   list is not its own scroll container, unlike the cycle-count bin list. */
.cc-sec {
  margin:10px 0 2px; padding:2px 2px 4px;
  border-bottom:2px solid var(--brown);
  font-size:12px; font-weight:800; letter-spacing:.10em; text-transform:uppercase;
  color:var(--ink);
}

/* The confirm before a switch moves, and the receipt after it. 🗣️ "We need a
   save button to submit" — the real complaint was "I couldn't tell if it
   saved", so this is the acknowledgement, on the row, above the fold. */
.set-ask { margin:8px 0; border:2px solid var(--brown); padding:12px; }
/* 🛑 A GRID, not flex-wrap. A row that must never break on content width is a
   grid — the entry-box-on-the-left bug, and the reason that rule exists. */
.set-askrow { display:grid; grid-template-columns:1fr 1fr; gap:8px; margin-top:12px; }
.set-askrow .chip, .set-askrow .primary { min-height:var(--tap); font-weight:700; }
.set-saved { display:flex; align-items:center; justify-content:space-between;
  gap:8px; grid-column:1 / -1; margin-top:6px; font-size:12px; }
.set-undo { min-height:36px; padding:4px 12px; font-size:12px; }

/* NOT SAVED. Loud on purpose: it means the work on screen exists in one process
   and nowhere else, so the receiver has to finish and submit rather than walk
   away. It rides every poll, so it cannot be missed the way a toast can. */
.nosave{background:var(--need,#b00020);color:#fff;font-weight:700;padding:8px 10px;
  border-radius:6px;margin-top:6px;line-height:1.35}

/* 🗣️ Dael, 2026-09-03: "a nice square little print icon up there, and so the
   option to take a picture of the invoice." Two whole-load actions, at the top
   where the driver is still standing, not at the foot of a screen that scrolls
   for forty lines.
   🛑 Square and --tap tall: this is a dock phone in gloves, so "little" is
   about the FOOTPRINT, never about the target. */
.ltools { display:flex; align-items:center; gap:8px; margin:6px 0 2px; }
.ltool {
  flex:0 0 auto; width:var(--tap); height:var(--tap); min-height:var(--tap);
  display:flex; align-items:center; justify-content:center;
  font-size:22px; line-height:1; text-decoration:none;
  background:var(--card,#fff); border:1px solid var(--line,#d5d5d5); border-radius:8px;
}
.ltool:disabled { opacity:.5; }
/* The row's own answer line: what just happened to the photo, in words, beside
   the button that did it. An icon cannot say "it did not attach". */
.ltools-note { flex:1 1 auto; font-size:12px; color:var(--dim); line-height:1.25; }
.ltools-note.bad { color:var(--bad); font-weight:700; }

/* The greenhouse picker's commodity accordion. One row per herb; variants
   unfold in place, indented and rule-marked so the eye keeps the group. */
.gh-grouphead{display:flex;align-items:center;gap:8px}
/* 🛑 .chip is `flex:1; min-height:48px` - it is built to BE a tap target, so
   reusing it for the variant count stretched the number into something that
   read as a text input. The count is a label inside the row, not a control;
   the row itself is the tap target and keeps its own height. */
.gh-count{margin-left:auto;flex:0 0 auto;min-height:0;padding:2px 10px;border-radius:10px}
.gh-caret{opacity:.6}
.gh-variant{margin-left:14px;border-left:3px solid var(--line,#d5d5d5)}

/* 🗣️ Dael, 2026-09-02: "I could just select the RECBASLB, add the pounds and
   click receive - that's what I want." The pounds list is flat and the entry
   opens ON the selected row, so the selection is never off-screen from the box
   that acts on it. Selected row mirrors .gh-pickrow.on - same screen, same
   meaning, one visual language. */
.gh-row.on { background:#F1EADC; border-color:var(--brown); border-width:2px; }
/* The box hangs off the row above it: same indent rule as .gh-variant, so the
   eye reads "this belongs to that". */
.gh-entry { margin:0 0 10px 14px; padding:10px 12px 12px;
  border-left:3px solid var(--brown); background:#FAF7F0; }
.gh-entry .fld { margin-top:0; }
/* Pots and plants are the other 71 items and a different job (each, not
   pounds). One quiet line at the foot of the list, never a peer of the herbs. */
.gh-toggle { margin-top:10px; text-align:center; color:var(--dim); font-weight:600; }

/* The count's account picker - on the sheet AND on the reveal.
   🛑 BOTH ROWS WERE UNSTYLED. They carry `class="row mv-f"`, and every mv-f
   rule is scoped `.mv-panel .mv-f` - outside a move panel they matched NOTHING,
   so the control rendered as a bare 19px browser select on a screen where the
   floor's smallest deliberate target is 28px and every ACTION is --tap (60).
   The reveal's picker has been that size since the day it shipped; naming both
   here rather than only the new one.
   🛑 min-width:0 on the select: an account name is long ("502705 Rotation /
   Spoiled / Over Stock") and a flex child is sized on its CONTENT first, so
   without this the row pushes the page wider than the phone. */
#cc-sacct-row, #cc-acct-row { display:flex; align-items:center; gap:10px; margin:0 0 10px; }
#cc-sacct-row > span:first-child,
#cc-acct-row > span:first-child { flex:0 0 auto; color:var(--dim); font-size:13px; }
#cc-sacct, #cc-acct { flex:1 1 auto; min-width:0; min-height:var(--tap); font:inherit;
  font-size:15px; font-weight:700; padding:6px 8px; border:1px solid var(--line);
  border-radius:10px; background:var(--card); color:var(--ink); }
/* Held back by a negative sibling elsewhere at the site - not a count error and
   it must not wear the error colour. The same amber edge the screen already
   uses for "this needs a person". */
.cc-plan-row .cc-frozen { color:var(--need); }


/* A product-line tray sits UNDER every herb drawer: a rule above it and a
   tinted name, so the eye reads "different kind of thing" before it reads the
   word. Not a new control - the same drawer, one level out. */
.mv-plhead { border-top:2px solid var(--line); margin-top:8px; }
.mv-plhead .mv-gname { color:var(--brown); letter-spacing:.4px; }

/* Stated, not implied: in a merged drawer an unlabelled row would read as
   "nobody filled this in" rather than "conventional". Quiet next to the green
   ORGANIC, because conventional is the common case. */
.mv-conv { color:var(--dim); font-weight:700; font-size:12px; letter-spacing:.02em; }

/* A phone parked off home/loads/enroll never learns a new build exists - so it
   is TOLD, at the bottom where a thumb already rests, and it updates only when
   tapped. Never auto-reload mid-count. */
.updatebar { position:fixed; left:12px; right:12px; bottom:12px; z-index:60;
  min-height:var(--tap); border:0; border-radius:10px; padding:12px 16px;
  background:var(--brown); color:#fff; font-weight:800; font-size:15px;
  box-shadow:0 6px 20px rgba(0,0,0,.28); }

/* The thing he asked for is the button; a partial correction hides behind a
   disclosure so the common case is one tap and no typing.
   The panel hangs off the row it edits, so it repeats NOTHING from it - the
   product, code, lot and bin are one line above. Tighter chrome, full-size
   tap targets: --tap is untouched. */
.adj-linepanel { margin-top:0; border-top:0; border-radius:0 0 10px 10px; padding:10px 12px 12px; }
.adj-linepanel .wide { margin-top:8px; }
.adj-linepanel .note { margin-top:6px; font-size:11px; }
.adj-linepanel .fld { margin:8px 0 0; }
.adj-part { margin-top:8px; }
.adj-part > summary { min-height:var(--tap); display:flex; align-items:center;
  color:var(--brown); font-size:13px; cursor:pointer; }

/* One colour and one mark per product line, so a thumb scrolling sixty drawers
   finds the mushrooms before it reads a word. Colour is carried on the RULE and
   the NAME only - type size and weight are untouched, and the label always
   stays, so a tray is still readable if the glyph does not render. */
.mv-plmark { flex:0 0 auto; font-size:17px; line-height:1; margin-right:2px; }
/* 🗣️ Dael, 2026-09-06: "I also don't want this to show on the picking bin the
   peppers and shit." DriedZ standing in picking leaves the list and lands on
   THIS line - the only way to act on it from here, so it must read as an
   action, not as a disabled leftover. Amber rather than a tray colour: it is
   not a fifth product line, it is an exception asking to be cleared.
   🛑 Same 60px --tap floor as every other row. Smaller by deleting repetition,
   never by shrinking a target. */
/* 🗣️ Dael, 2026-09-06: "force the lots to get inject into the assemblies that
   they were used". The trace sits UNDER the shrink row it belongs to, so the
   choice reads as "or do this instead" rather than as a second unrelated
   control. 🛑 --tap untouched: this is on a glove surface. */
.cc-trace  { margin-top:8px; }
/* 🗣️ "We need a faster way of doing this." The whole-bin plan, above the sheet
   and shaped like the bin sweep beside it. Square and flush like every card. */
#cc-bulk { border-top:1px solid var(--line); }
#cc-bulk-go { margin-top:8px; min-height:var(--tap); }
#cc-bulk-plan { margin-top:8px; }
.cc-bulkrow { padding:4px 0; border-top:1px solid var(--line-soft); font-size:14px; }
.cc-bulkrow .sub { color:#5B5044; }
/* 🗣️ Dael, 2026-09-07: "I want this button to be very smaller compared to the
   item... just a button on the same item just like that small dot there maybe we
   add this butt 🔄".
   🔑 It lives INSIDE .cc-body's second line, so it is not a grid item of .cc-row
   and cannot redefine that column - which is exactly what the previous
   full-width band did, crushing every row's text to one word per line.
   ⚠️ DELIBERATE --tap EXCEPTION, and the only one on this surface. Dael asked for
   it small twice. The GLYPH is small; the HIT AREA is not - padding carries the
   target to ~44px without the ink growing. Never shrink the padding to make it
   look tidier: the visual size is his, the tap size is the gloves'. */
.cc-rowtrace, .cc-rowundo {
  display:inline-block; vertical-align:middle;
  background:none; border:0; border-radius:0; text-decoration:none;
  font-size:15px; line-height:1;
  padding:12px 10px; margin:-12px 0 -12px 4px;   /* ~44px target, zero extra height */
  cursor:pointer; opacity:.75;
}
.cc-rowtrace:active, .cc-rowundo:active { opacity:1; }
.cc-rowtrace.open, .cc-rowundo.open     { opacity:1; }
/* The disassemble panel. Amber, not the trace's purple: the trace only READS,
   this one reverses a whole build in both directions. Different colour for a
   different weight of action. */
.cc-undo { grid-column:1 / -1; margin-top:8px; border-left:3px solid #B57B1F; padding-left:8px; }
.cc-undo .cc-back { font-size:13px; color:#5B5044; margin:4px 0 0; }
.cc-undo .cc-back b { color:#2C2620; }
.cc-undo .cc-no { color:#9B2C1F; font-weight:600; }
/* The refusal a bare glyph has no room to say. Transient, on the row. */
.cc-rowsay { grid-column:1 / -1; margin-top:4px; font-size:13px; color:#5B5044; }
/* 🛑 SPANS BOTH COLUMNS. This panel opens from two different cards - the review
   row (block) and the SHEET row (a 2-col grid). Without this it renders inside
   column one on the sheet, squeezed against the quantity box. Harmless on the
   review row, which is not a grid. */
.cc-builds { grid-column:1 / -1; margin-top:8px; border-left:3px solid #7B4FB5; padding-left:8px; }
/* The waterfall rides the cc-builds rail so recovery reads as one family. */
.cc-wstep  { margin-top:10px; padding-top:6px; border-top:1px solid #E3E7EE; }
.cc-wstep .sub { display:block; margin-top:3px; line-height:1.35; }
.cc-wstep button { margin-top:6px; }
.cc-wgive  { display:flex; gap:8px; margin-top:6px; align-items:center; }
.cc-wgive input { flex:1; min-width:0; min-height:var(--tap); font-size:16px; padding:4px 8px; }
.cc-wmemo  { width:100%; margin-top:6px; font-size:16px; padding:6px 8px; box-sizing:border-box; }
.cc-build  { margin-top:6px; }
.cc-build .mv-l .sub { line-height:1.35; }

.mv-dried { border-top:2px solid #C77D0A; margin-top:8px; min-height:var(--tap); }
.mv-dried .mv-gname { color:#8A5A07; }
.mv-dried .mv-gsum  { color:#8A5A07; opacity:.9; }

.pl-shroomz { border-top-color:#8B5E3C; }
.pl-shroomz .mv-gname { color:#7A4E2E; }
.pl-pepperz { border-top-color:#C0392B; }
.pl-pepperz .mv-gname { color:#A62F23; }
.pl-shakerz { border-top-color:#2E7D5B; }
.pl-shakerz .mv-gname { color:#256849; }
.pl-blastz  { border-top-color:#7B4FB5; }
.pl-blastz  .mv-gname { color:#67409B; }
.pl-puree   { border-top-color:#C77D0A; }
.pl-puree   .mv-gname { color:#A66708; }
/* The open state must not repaint the name back to the shared brown. */
.mv-ghead.mv-plhead.open .mv-gname { color:inherit; }
.pl-shroomz.open .mv-gname { color:#7A4E2E; }
.pl-pepperz.open .mv-gname { color:#A62F23; }
.pl-shakerz.open .mv-gname { color:#256849; }
.pl-blastz.open  .mv-gname { color:#67409B; }
.pl-puree.open   .mv-gname { color:#A66708; }

/* The adjustment's related records: what posted it, then a line per edit.
   Small and columnar - the time is the thing the eye scans, so it gets its own
   fixed column and the rest wraps under itself rather than around it. */
.adj-hist { margin-top:8px; border-top:1px solid var(--line-soft); padding-top:6px; }
.adj-histrow { display:flex; gap:8px; align-items:baseline; font-size:11px;
  color:var(--dim); line-height:1.45; padding:1px 0; }
.adj-histwhen { flex:0 0 auto; min-width:74px; font-variant-numeric:tabular-nums; }
.adj-histwhat { flex:1 1 auto; min-width:0; }

/* 🗣️ "I need one section where it shows me the inventory." Read-only, so it is
   type and numbers — no control affordances anywhere on it except the one that
   expands a bucket's lots. */
.stock-b { margin-bottom: 10px; }
/* 🗣️ "the right side to be like one column Cooler qty, next column shows if the
   same lot is also in picking, some column should be the age."
   🛑 The product column FLEXES and the three number columns are FIXED, so a
   long description shortens itself instead of pushing Age off a 390px screen.
   Numbers are tabular so the columns line up down the page - that alignment is
   the whole reason this is a table and not a list. */
.stock-t { margin-bottom: 12px; }
.stock-grid {
  display:grid; grid-template-columns: minmax(0,1fr) 58px 58px 40px;
  gap: 8px; align-items: start;
}
.stock-head-row {
  margin-top:8px; padding-bottom:6px; border-bottom:1.5px solid var(--line);
  font-size:11px; font-weight:800; letter-spacing:.06em; text-transform:uppercase; color:var(--dim);
}
.stock-head-row div:not(:first-child) { text-align:right; }
.stock-row { padding:8px 0; border-bottom:1px solid var(--line-soft); }
.stock-row:last-child { border-bottom:0; }
.stock-p { min-width:0; }
.stock-n { text-align:right; font-variant-numeric:tabular-nums; font-weight:700; }
/* Anywhere that is neither cooler nor picking, named on the row so the two
   columns never silently swallow a third place. */
.stock-else { color:#7A5B12; }
.stock-h { display:flex; justify-content:space-between; align-items:baseline; gap:12px; font-size:17px; }
.stock-h span { font-variant-numeric:tabular-nums; font-weight:800; }
/* Itemised bins: the line that turns "Other bins 19,404" into something a
   person can walk to. Indented so it reads as detail under the bucket. */
.stock-bin {
  margin-top:4px; padding-left:10px; border-left:2px solid var(--line);
  font-size:13px; color:#5B5044;
}
.stock-bin b { color:#2C2620; font-variant-numeric:tabular-nums; }
.stock-lots { margin-top:8px; border-top:1px solid var(--line); }
.cc-row-lite { padding:8px 0; border-bottom:1px solid var(--line-soft); }
.cc-row-lite:last-child { border-bottom:0; }
/* The disagreement — a grade-B pallet in the trash, grade T in picking. It is
   the reason to open this screen, so it is the one coloured thing on it. */
.cc-why { color:#9B2C1F; font-weight:600; }
.dim { color:var(--dim); }

/* ---- Mock recall: the timeline, layout A (spec 2026-09-11) ----
   🔑 A FIXED TIME GUTTER. The clock is what the eye scans down when someone is
   asking "when did it leave?", so it is a column, not a prefix buried in the
   sentence. Times are tabular so 09:05 and 16:07 line up.
   🛑 The quantity sits right-aligned on the same line as what happened, because
   on this screen those two are ONE fact - "moved" and "−64" read as a lie
   apart and as the truth together. */
.rc-day {
  margin:12px 0 4px; padding-bottom:5px; border-bottom:1.5px solid var(--line);
  font-size:11px; font-weight:800; letter-spacing:.06em; text-transform:uppercase;
  color:var(--dim);
}
.rc-day:first-child { margin-top:0; }
.rc-ev {
  display:grid; grid-template-columns: 46px minmax(0,1fr); gap:10px;
  padding:8px 0; border-bottom:1px solid var(--line-soft);
}
.rc-ev:last-child { border-bottom:0; }
.rc-when { font-variant-numeric:tabular-nums; font-size:13px; font-weight:700; color:var(--dim); }
.rc-what { min-width:0; }
.rc-what .cc-l1 { display:flex; justify-content:space-between; align-items:baseline; gap:10px; font-size:14px; }
/* 🛑 The total belongs on the RIGHT EDGE, not jammed against the heading. It
   read "WHERE IT IS NOW0" in the first browser pass - a heading and a figure
   with nothing between them, which parses as one word and hides the number
   that answers the question. Caught on glass, not by a test. */
.rc-sum { display:flex; justify-content:space-between; align-items:baseline; gap:10px; }
.rc-what .cc-l2 { font-size:12px; line-height:1.3; color:var(--dim); }
.rc-q2 { font-variant-numeric:tabular-nums; font-weight:800; white-space:nowrap; }

/* 🗣️ Dael, 2026-10-13: "receive a PO from another location". Framed and
   headed so another site's POs can never be read as this dock's own board,
   which sits right below it. Controls at tap height - gloves. */
.xpo-panel { border:2px solid var(--need); border-radius:14px; background:var(--need-fill);
  padding:10px; margin:8px 0 14px; }
.xpo-head { display:flex; flex-direction:column; gap:2px; margin-bottom:8px; color:var(--brown-deep); }
.xpo-panel .row.xpo { display:flex; gap:8px; align-items:center; margin:6px 0; }
.xpo-panel select, .xpo-panel input { flex:1 1 auto; min-height:var(--tap); font-size:17px;
  border:1.5px solid var(--line); border-radius:12px; background:var(--card); padding:0 12px; }
.xpo-panel .x { min-width:var(--tap); min-height:var(--tap); font-size:24px; text-decoration:none;
  border:1.5px solid var(--line); border-radius:12px; background:var(--card); color:var(--ink); }
.xpo-panel #xpo-go { min-height:var(--tap); }
.xpo-panel .stack { margin-top:6px; }
#xpo-open { margin:8px 0 12px; min-height:var(--tap); }

/* 🗣️ Dael, 2026-10-13: a transfer's grade on arrival, one row per lot. */
.to-grade { border:1.5px dashed var(--line); border-radius:12px; padding:8px; margin:6px 0; }
.to-grade .to-grade-row { display:flex; flex-wrap:wrap; gap:6px; align-items:center; margin:4px 0; }
.to-grade select, .to-grade input { min-height:var(--tap); font-size:17px; border:1.5px solid var(--line);
  border-radius:10px; background:var(--card); padding:0 8px; }
.to-grade input { width:90px; }
.to-grade select, .to-grade input { box-sizing:border-box; max-width:100%; }
.to-grade [data-tggrade] { flex:1 1 120px; }
.to-grade [data-tgreason] { flex:1 1 100%; }

/* 🗣 Dael, 2026-10-19: the note box "is too small and doesn't let me select high
   medium or low like NetSuite". A real text area, full width, and the three
   NetSuite sticky-note colours. */
.fld.wide:has(.note-box) { display:flex; flex-direction:column; align-items:stretch; gap:8px; }
.note-box { width:100%; box-sizing:border-box; min-height:120px; font-size:17px; line-height:1.4;
  padding:10px 12px; border:1.5px solid var(--line); border-radius:12px; background:var(--card);
  color:inherit; resize:vertical; font-family:inherit; }
.note-pri { display:flex; gap:8px; }
.note-pri .pri { flex:1 1 0; min-height:var(--tap); font-size:16px; font-weight:600; border-radius:10px;
  border:2px solid transparent; color:#fff; opacity:.55; cursor:pointer; }
.note-pri .pri.on { opacity:1; border-color:var(--ink, #111); box-shadow:0 0 0 2px var(--card) inset; }
.pri-high { --pri:#c94a6d; } .pri-medium { --pri:#e0a246; } .pri-low { --pri:#3d8fd6; }
.note-pri .pri { background:var(--pri); }
.note-add { min-height:var(--tap); font-size:17px; font-weight:600; border-radius:10px; }
.note-card { border-left:6px solid var(--pri, #3d8fd6); }

/* 🗣️ Dael, 2026-10-19: "smaller, just enough to click." The Bin transfer kinds
   are a compact strip above the scan row, not three tall tiles beside it. */
#mv-kinds { display:flex; gap:6px; margin:4px 0 6px; }
#mv-kinds .tile { flex:1 1 0; min-height:40px; padding:6px 4px; text-align:center; }
#mv-kinds .tile .f-s { display:none; }

/* The scan card: lot, what it is, and one row per place it sits. The big
   button is the whole point - In (to the cooler) and Out (to picking) read
   apart at a glance, by colour as well as by word. */
.mv-quick { padding:12px; }
.mvq-head { display:flex; flex-wrap:wrap; align-items:baseline; gap:4px 8px; }
.mvq-head .sub { flex-basis:100%; color:var(--dim); font-size:13px; }
.mvq-lot { font-size:20px; letter-spacing:.02em; }
.mvq-row { border-top:1px solid var(--line-soft); margin-top:10px; padding-top:10px; }
.mvq-where { display:flex; align-items:center; gap:8px; font-size:14px; }
.mvq-have { margin-left:auto; color:var(--dim); }
.mvq-act { display:flex; gap:8px; align-items:stretch; margin-top:8px; }
.mvq-qty { width:5.5em; flex:0 0 auto; min-height:52px; font-size:18px; text-align:center; }
.mvq-go { flex:1 1 auto; min-height:52px; font-size:16px; }
/* Scoped under .mv-quick so the NetSuite skin's .primary colour cannot paint
   In and Out the same. */
.mv-quick .mvq-go.in  { background:#2B5F8A; border-color:#1E4766; color:#fff; }
.mv-quick .mvq-go.out { background:var(--ok); border-color:#1F4D36; color:#fff; }
.mv-quick .mvq-go:disabled { background:var(--line-soft); color:var(--dim); border-color:var(--line); }
.mvq-none { flex:1 1 auto; align-self:center; color:var(--dim); font-size:13px; }
/* ⋯ opens the full panel INSIDE the card: it loses the card chrome, and its
   quantity keeps the panel's own width instead of the field-wide default. */
.mv-quick .mv-panel { margin:10px 0 0; padding:10px 0 0; border:0; border-top:1px solid var(--line-soft); box-shadow:none; }
.mv-quick .mv-panel .mv-f .qty { width:96px; min-width:0; }
.mv-quick .mv-panel .mv-f .sub { min-width:0; }
/* The home screen's invisible trigger target (#home-scan): focusable, never seen. */
.scan-catch { position:absolute; left:-9999px; top:0; width:1px; height:1px; opacity:0; border:0; padding:0; }
.mvq-more {
  flex:0 0 auto; min-width:52px; min-height:52px; font-size:22px; font-weight:700;
  border:1px solid var(--line); border-radius:12px; background:var(--card); color:var(--ink);
}


/* ==================================================================
   NETSUITE SKIN  (body.ns)  — Dael, 2026-08-26: "make this whole thing look
   like NetSuite."

   🔑 COPIED FROM gg-floor-app, not invented here. The same phones run both
   apps; two GG tools that look like two different products is how an internal
   system starts looking unofficial. Every value in the palette block below is
   the floor app's, byte for byte.

   🛑 THE SKIN CHANGES COLOUR, NOT LEGIBILITY.
   The floor app's first cut of this took NetSuite's quiet desktop typography
   along with its palette - h1 28/800 down to 21/600, the row headline 700 to
   600, the case count 800 to 700 - and Dael sent it back: "we don't wanna
   affect the operational part, we just want the nice design." That is every
   piece of text a receiver reads at arm's length, under bright light, with a
   pallet in front of them. So NOTHING below changes a font-size or a
   font-weight on operational data. What it changes: grey canvas, navy bar,
   square corners, hairline rows, blue links and actions, bordered fields.

   🛑 --tap is NOT overridden. NetSuite is a desktop product and its target
   sizes do not transfer to a dock in gloves. Enterprise look, floor ergonomics.

   🛑 The six grade colours are NOT touched. They are vocabulary, not
   decoration, and a receiver who learns "blue means B" must not have to relearn
   it because the chrome changed.
   ================================================================== */

body.ns {
  --brown:      #25364D;   /* global nav bar */
  --brown-deep: #1A2839;
  --cream:      #FFFFFF;   /* text ON the nav bar */
  --paper:      #F1F3F6;   /* canvas grey */
  --card:       #FFFFFF;
  --ink:        #2C3E50;
  --dim:        #64748B;   /* the floor app calls this --ink-soft */
  --line:       #D5DBE3;
  --line-soft:  #EDF0F4;
  --row-alt:    #F7F9FB;
  --ok:         #3C8C4E;   /* the green on the Suitelet action buttons */
  --need:       #B77A18;
  --bad:        #C0392B;
  --link:       #2A6EBB;   /* NetSuite link blue */
  --radius:     4px;       /* enterprise square, not app-rounded */
}

/* Flat chrome: NetSuite's bars sit on the page, they do not float above it. */
body.ns #bar { box-shadow:none; border-bottom:3px solid #3C6E9F; }
body.ns #site { color:rgba(255,255,255,.66); }
/* Weight stays on #who: it is the label saying WHICH DOCK and WHICH SITE this
   phone is bound to, and a phone showing another site's board under the right
   site's header is a bug this app has already had (FL509922, 8/25). */

/* Headings keep their size and weight; only the spacing is normalised. */
body.ns h1 { letter-spacing:-.01em; }

/* Rows read as table rows: hairline, square, no lift. Weight untouched. */
body.ns .stack > .card:first-child { border-radius:var(--radius) var(--radius) 0 0; }
body.ns .stack > .card:last-child  { border-radius:0 0 var(--radius) var(--radius); }
body.ns button.tap:active, body.ns .chip:active,
body.ns .tile.act:active, body.ns .reopen:active { background:#EEF3FA; }

/* Fields and tiles: a field in NetSuite looks like a field - bordered, square,
   white. Sizes and label weights are untouched. */
body.ns .tile { border-radius:var(--radius); }
body.ns .tile.suggest { border-color:var(--link); background:#EEF3FA; }
body.ns .tile.ok-tile { border-color:#A9CBB7; background:#F0F7F2; }
body.ns .tile.need    { border-color:var(--need); background:#FCF3E2; }
body.ns input, body.ns select, body.ns textarea {
  border-radius:var(--radius); border:1px solid #B9C2CE; background:#fff;
}
body.ns input:focus, body.ns select:focus, body.ns textarea:focus {
  outline:2px solid rgba(42,110,187,.35); outline-offset:1px; border-color:var(--link);
}
/* A control INSIDE a tile is still bare - the tile is the border. Re-stating it
   here because the rule above would otherwise draw a box inside a box. */
body.ns .tile select, body.ns .tile input {
  border:0; outline:0; background:transparent; border-radius:0;
}

/* Actions: solid enterprise blue, square, no glow. Height and weight untouched -
   .primary is Submit and Close line, the two things a receiver hits with a
   glove. */
body.ns .primary {
  background:var(--link); border-color:#1B4E88; border-radius:var(--radius);
  box-shadow:none;
}
body.ns .primary:active { filter:brightness(.94); }
body.ns .primary:disabled { background:var(--line-soft); color:var(--dim); border-color:var(--line); }
body.ns .add { border-radius:var(--radius); color:var(--link); border-color:#B9C2CE; }
body.ns .reopen { border-radius:var(--radius); border-color:#B9C2CE; color:var(--link); }
body.ns .mod .go { color:var(--link); }
body.ns .x { color:var(--link); }
body.ns .btn2 { color:var(--link); }

/* Status chips: NetSuite's are square and quiet. */
body.ns .pill, body.ns .chip { border-radius:3px; }
/* The pill-shaped Back button is the last round corner on screen under the
   skin, and one rounded control among square ones reads as a mistake. */
body.ns #back { border-radius:var(--radius); }
body.ns .pill.ok        { background:#F0F7F2; border-color:#A9CBB7; color:var(--ok); }
body.ns .pill.h-open    { background:#E7EBF1; border-color:var(--line); color:#566274; }
body.ns .chip.suggest   { border-color:var(--link); background:#EEF3FA; color:var(--link); }
body.ns .chip.done-chip { background:#F0F7F2; border-color:#A9CBB7; color:var(--ok); }

/* The grading rows: square controls, blue selection ring. The grade <select>
   keeps its own colour - that is the grade scale, not chrome. */
body.ns .grow-in .qty, body.ns .grow-in .unit, body.ns .grow-in .grade {
  border-radius:var(--radius);
}
body.ns .variance-bar { border-radius:var(--radius); background:#E7EBF1; }

/* 🛑 The SANDBOX badge stays amber and loud. It is a safety indicator, not
   decoration: it is the one thing on screen that says these counts are not
   landing on real records. Squared to match the chrome, and nothing else. */
body.ns #envtag { border-radius:3px; }

/* 🗣️ Kiara, 2026-09-12: "see what she received ... on the PO pending to
   receive." What a part-received line already took in, beside what was ordered.
   Coloured like the other "this is already settled" figures on the line, not
   like a warning: it is context for the count, not a problem with it. */
.sku-got { color:#2F6B4F; }
.sku-got b { font-variant-numeric:tabular-nums; }
