:root {
  --bg: #FDFCFB;
  --ink: #1A1A1A;
  /* #7A7A72 measured 4.22:1 on --bg — under the 4.5:1 AA floor, while carrying
     addresses, doc numbers and every form label across all three apps. */
  --muted: #63635B;
  --faint: #C8C6BF;
  --green: #2D5016;
  --green-soft: #E8F0DF;
  --line: #E8E6DF;
  --chip: #F5F3EE;
  --wa-text: #25D366;
  --wa-bg: #E8F5E3;
  --r-card: 16px;
  --r-tile: 12px;
}

* { box-sizing: border-box; }

/* `hidden` must win over component display rules (e.g. .login-step { display:flex }). */
[hidden] { display: none !important; }

html, body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: Inter, "Noto Sans Thai", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  -webkit-font-smoothing: antialiased;
  line-height: 1.5; /* comfortable reading (matches sample); was browser "normal" (~1.2) */
}

::selection { background: var(--green-soft); }

.mono {
  font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, monospace;
}

/* ---------- Page shell ---------- */
.app {
  max-width: 28rem;
  margin: 0 auto;
  min-height: 100vh;
  padding: 0 1.5rem 8rem;
  position: relative;
}

.center-card {
  max-width: 28rem;
  margin: 0 auto;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 2rem;
  text-align: center;
}
.lang-screen { align-items: stretch; justify-content: flex-start; text-align: left; padding-top: 3.5rem; }
.lang-intro { margin-bottom: 2.5rem; }
.app-icon {
  display: inline-grid; place-items: center; width: 4.5rem; height: 4.5rem;
  border-radius: 1.25rem; background: #fff; border: 1px solid var(--line);
  box-shadow: 0 8px 20px -10px rgba(26, 26, 26, 0.25); margin-bottom: 2rem;
}
.app-icon img { width: 3.25rem; height: 3.25rem; }
.lang-intro .welcome-title { font-size: 2.5rem; font-weight: 600; letter-spacing: -0.025em; line-height: 1.1; margin: 0 0 0.75rem; color: var(--ink); }
.lang-intro .welcome-sub { font-size: 1.0625rem; color: var(--muted); margin: 0; }
.lang-version { margin: 2rem 0 0; font-size: 0.75rem; text-align: center; }

/* ---------- Sticky header ---------- */
.topbar {
  position: sticky;
  top: 0;
  z-index: 10;
  margin: 0 -1.5rem;
  padding: 1.25rem 1.5rem 1rem;
  background: rgba(253, 252, 251, 0.8);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--line);
}
.topbar-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 1rem;
}
.topbar-actions {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-left: auto; /* always right-align, even when the back button is hidden */
}
.topbar-title {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
}
.topbar-title h1 {
  font-size: 1.25rem;
  font-weight: 600;
  margin: 0;
  line-height: 1.2;
}
.step-counter {
  font-size: 0.625rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--muted);
  white-space: nowrap;
}

.icon-btn {
  background: none;
  border: none;
  font-size: 1.35rem;
  line-height: 1;
  cursor: pointer;
  color: var(--muted);
  padding: 0.25rem;
  border-radius: 999px;
}
.icon-btn:hover { color: var(--ink); }
.icon-btn svg { display: block; width: 1.5rem; height: 1.5rem; }
#back-btn { display: inline-flex; align-items: center; justify-content: center; color: var(--ink); }
#back-btn[hidden] { display: inline-flex; visibility: hidden; }

.wa-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  max-width: 15rem;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  background: var(--wa-bg);
  color: var(--wa-text);
  font-size: 0.625rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 0.25rem 0.6rem;
  border-radius: 999px;
}
.wa-badge .wa-dot { flex-shrink: 0; }
/* Long-pressing the badge signs out (static/logout.js). Suppress the text
   selection + iOS callout a hold would otherwise trigger, and dim while held
   so the gesture has feedback before the confirm appears. */
.wa-badge {
  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
  touch-action: manipulation;
  transition: opacity .15s, transform .15s;
}
.wa-badge.pressing { opacity: 0.6; transform: scale(0.96); }
.wa-dot {
  width: 0.375rem;
  height: 0.375rem;
  background: var(--wa-text);
  border-radius: 999px;
  animation: pulse 1.6s ease-in-out infinite;
}
@keyframes pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.35; }
}

/* ---------- Screen content ---------- */
/* Both apps' content wells share the same breathing room under the sticky
   topbar — the receiver used to butt its first element against the divider. */
#screen, #rcv-main { padding-top: 1.5rem; }

.orders-toolbar { display: flex; justify-content: flex-end; margin: -0.75rem 0 0.5rem; }

h1 { font-size: 1.5rem; margin: 0.5rem 0; font-weight: 600; }
.muted { color: var(--muted); }
.empty-msg { padding: 0.5rem 0.25rem 1rem; font-size: 0.875rem; }

.section {
  font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, monospace;
  font-size: 0.625rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.15em;
  color: var(--muted);
  margin: 1.5rem 0.25rem 0.75rem;
}

.intro { margin-bottom: 1.5rem; }
.intro h1 { font-weight: 300; letter-spacing: -0.01em; }
.intro h1 strong { font-weight: 600; }

/* ---------- Cards ---------- */
.card {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--r-card);
}
.pad { padding: 1.5rem; }
.big-emoji { font-size: 3rem; }

/* ---------- Selectable grids (lang / date / branch) ---------- */
.lang-grid, .date-grid, .branch-grid {
  display: grid;
  gap: 0.75rem;
  margin-top: 1rem;
}
.date-grid { grid-template-columns: 1fr 1fr; }

.lang-btn, .date-card, .branch-card {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  padding: 1.5rem;
  font-size: 1rem;
  cursor: pointer;
  text-align: left;
  color: var(--ink);
  transition: border-color .15s, background .15s, box-shadow .15s, transform .1s;
  position: relative;
  overflow: hidden;
  width: 100%;
}
.lang-btn:hover, .date-card:hover, .branch-card:hover { border-color: var(--green); }
.lang-btn:active, .date-card:active, .branch-card:active { transform: scale(0.98); }

.date-card.selected, .branch-card.selected {
  background: var(--green);
  border-color: var(--green);
  color: #fff;
  box-shadow: 0 10px 20px -8px rgba(45, 80, 22, 0.4);
}

.lang-btn {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.lang-btn > span:first-child { display: flex; flex-direction: column; }
.lang-btn .lang-label { font-weight: 500; font-size: 1.125rem; }
.lang-btn .lang-native { font-size: 0.875rem; color: var(--muted); }
.lang-btn .chev { color: var(--faint); font-size: 1.25rem; transition: color .15s; }
.lang-btn:hover .chev { color: var(--green); }

.date-card .wd { color: var(--muted); font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.1em; margin-bottom: 0.25rem; }
.date-card .day { font-size: 1.75rem; font-weight: 600; line-height: 1; }
.date-card .mon { font-size: 0.875rem; color: var(--muted); margin-top: 0.25rem; }
.date-card.selected .wd { color: rgba(255,255,255,0.6); }
.date-card.selected .mon { color: rgba(255,255,255,0.8); }

.date-card .corner-badge {
  position: absolute;
  top: 0;
  right: 0;
  padding: 0.2rem 0.55rem;
  font-size: 0.6875rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  border-bottom-left-radius: 0.5rem;
  background: var(--green-soft);
  color: var(--green);
}
.date-card.selected .corner-badge { background: rgba(255,255,255,0.2); color: #fff; }

.branch-card .name { font-weight: 600; }
.branch-card .hint { color: var(--muted); font-size: 0.8125rem; margin-top: 0.25rem; }
.branch-card.selected .hint { color: rgba(255,255,255,0.8); }

/* ---------- Product rows ---------- */
#regs, #search-sec { display: block; }
.item-wrap {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  padding: 0.75rem 0.875rem;
  margin-bottom: 0.5rem;
  transition: border-color .15s, box-shadow .15s;
}
.item-wrap.active {
  border-color: var(--green);
  box-shadow: 0 0 0 1px var(--green);
}
.item { display: flex; align-items: center; gap: 0.75rem; }
.item .emoji {
  width: 44px;
  height: 44px;
  flex-shrink: 0;
  display: grid;
  place-items: center;
  background: var(--chip);
  border-radius: var(--r-tile);
  font-size: 1.375rem;
}
.item .info { flex: 1; min-width: 0; }
.item .name {
  font-weight: 600;
  font-size: 0.9375rem;
  line-height: 1.3;
  overflow-wrap: anywhere; /* full name, no truncation — pack-size matters to the picker */
}
.item .hint { color: var(--muted); font-size: 0.75rem; line-height: 1.3; margin-top: 0.15rem; }
.star { color: var(--faint); font-size: 0.7rem; }

.remark {
  width: 100%;
  margin-top: 0.5rem;
  border: none;
  border-top: 1px solid var(--line);
  padding: 0.45rem 0.125rem 0;
  font-size: 0.8125rem;
  background: transparent;
  color: var(--ink);
  outline: none;
}
.remark::placeholder { color: var(--muted); }

/* ---------- Stepper ---------- */
.stepper { display: flex; align-items: center; gap: 0.375rem; flex-shrink: 0; }
.stepper button {
  width: 2.625rem;
  height: 2.625rem;
  border-radius: 999px;
  border: none;
  cursor: pointer;
  font-size: 1.375rem;
  line-height: 1;
  display: grid;
  place-items: center;
  transition: background .15s, color .15s, transform .1s;
}
.stepper button:active { transform: scale(0.95); }
/* Hold-to-repeat: long-press must not trigger the iOS callout / text selection. */
.stepper button {
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
}
.stepper .minus { background: var(--chip); color: var(--muted); }
.stepper .minus:hover { background: var(--line); }
.stepper .plus { background: var(--green-soft); color: var(--green); }
.stepper .plus:hover { background: var(--green); color: #fff; }

.qty-wrap { display: flex; flex-direction: column; align-items: center; gap: 0.2rem; }
.stepper input.qty {
  width: 3.5rem;
  height: 2.5rem;
  text-align: center;
  border: 1px solid transparent;
  border-radius: var(--r-tile);
  background: var(--chip);
  color: var(--ink);
  font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, monospace;
  font-weight: 700;
  font-size: 1rem;
  outline: none;
  -moz-appearance: textfield;
  appearance: textfield;
  transition: background .15s, border-color .15s, color .15s;
}
.stepper input.qty::-webkit-outer-spin-button,
.stepper input.qty::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.stepper input.qty:focus { background: #fff; border-color: var(--green); color: var(--green); }

/* Read-only quantity + unit, where the +/- stepper used to sit in the item
   rows. Editing moved into the scan match card (ScanUI.matchCard) — tapping
   the row opens it — which hands the item name back the width the stepper's
   two 44px buttons and its input were taking. Bilingual names in this
   catalogue ("PEEL TAPIOCA (VACUUM) 去皮木薯 - (KG)") wrapped to five lines
   in the old column.

   The .stepper rules above are NOT dead: the match card still renders one. */
.qty-read {
  flex-shrink: 0;
  min-width: 3.5rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.2rem;
}
/* The readout is the RECEIVED figure and has to say so: with the ordered hint
   now hidden while the two agree, an unlabelled number beside an item name is
   as easily read as the ordered one. */
.qty-read .qty-read-cap {
  font-size: 0.625rem; font-weight: 600; letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--muted); line-height: 1;
  margin-bottom: 0.1875rem;
}
.qty-read .qty-read-n {
  font-size: 1.0625rem;
  font-weight: 700;
  line-height: 1.1;
  color: var(--ink);
}
/* Same green the struck-through "Ordered:" hint uses, so a changed line reads
   the same whether you scan the qty column or the hint line. */
.qty-read.changed .qty-read-n { color: var(--green); }

/* A not-yet-received receiver line must not show a bold quantity that reads
   like a receipt: submit() only serialises ticked lines, so an unticked row's
   readout is provisional, not a fact, and a 20-line PO with 3 lines delivered
   otherwise shows 17 bold "receipts" that will never be submitted. Replaces
   the disabled/greyed input+select the pre-revamp row used for the same line.
   Scoped to .rcv-row (a receiver-only modifier class on the row — see
   lineRow, receiver-app.js) rather than `.item-wrap:not(.picked)` globally:
   the picker's own not-yet-picked rows must keep their full-strength
   readout, since an ordered quantity there is still meaningful before the
   tick — it just isn't "received" the way this rule cares about. This rule
   deliberately overrides .qty-read.changed for an unticked line, so if the
   picker edits the quantity and then unticks it, the row renders muted grey
   rather than green — correct, since the edited quantity won't be submitted. */
.rcv-row:not(.picked) .qty-read-n { font-weight: 400; color: var(--muted); }

/* .is-tappable marks a row whose body opens the edit sheet on click; the
   tick button (<button class="pick-check">) inside it is a separate tap
   target with its own click handler. No role/tabindex here on purpose —
   the hardware barcode scanner types its terminating Enter as a real
   keystroke, and a focusable row would risk opening the sheet on a stray
   scan terminator. Not applied to plain .item-wrap so non-tappable rows
   elsewhere don't get a misleading pointer cursor / press animation. */
.item-wrap.is-tappable { cursor: pointer; }
.item-wrap.is-tappable:active { transform: scale(0.995); }

/* Unit shown as a clear label under the qty (was a tiny corner sticker) — the
   picker must not confuse 2.5 KG with 2.5 PACK. */
.uom-badge {
  font-size: 0.625rem;
  font-weight: 700;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  line-height: 1;
}

/* ---------- Search ---------- */
.search-box {
  width: 100%;
  padding: 1rem 1.25rem;
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  font-size: 1rem;
  background: #fff;
  color: var(--ink);
  outline: none;
  transition: border-color .15s;
  font-family: inherit;
}
.search-box:focus { border-color: var(--green); }
.search-box::placeholder { color: var(--muted); }

.same-btn {
  width: 100%;
  margin: 0.25rem 0 1rem;
  padding: 0.875rem;
  border: 1px dashed var(--faint);
  color: var(--muted);
  background: transparent;
  border-radius: var(--r-card);
  font-weight: 500;
  font-size: 0.9375rem;
  cursor: pointer;
  transition: border-color .15s, color .15s;
  font-family: inherit;
}
.same-btn:hover { border-color: var(--green); color: var(--green); }

/* ---------- Sticky bottom bar ---------- */
.sticky {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 20;
  /* Every app's primary action lives in here, and at a flat 1.5rem it sat
     inside the iOS home-indicator / Android gesture strip. */
  padding: 1.5rem 1.5rem calc(1.5rem + env(safe-area-inset-bottom));
  max-width: 28rem;
  margin: 0 auto;
  background: linear-gradient(to top, var(--bg) 55%, rgba(253,252,251,0));
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
/* Scan + Preview live side by side so Scan never scrolls out of reach. */
.sticky-row { display: flex; gap: 0.625rem; align-items: stretch; }
.sticky-row .btn-primary { flex: 1; }
.scan-fab {
  flex-shrink: 0; width: 3.5rem; border: none; border-radius: var(--r-tile);
  background: var(--green-soft); color: var(--green); cursor: pointer;
  display: grid; place-items: center; font-family: inherit;
  transition: background .15s, color .15s, transform .1s;
}
.scan-fab:hover { background: var(--green); color: #fff; }
.scan-fab:active { transform: scale(0.96); }
.sticky:empty { display: none; }

.btn {
  width: 100%;
  padding: 1.25rem;
  border: none;
  border-radius: var(--r-card);
  font-size: 1rem;
  font-weight: 600;
  color: #fff;
  background: var(--green);
  cursor: pointer;
  font-family: inherit;
  box-shadow: 0 12px 24px -10px rgba(45, 80, 22, 0.5);
  transition: opacity .15s, transform .1s;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.75rem;
}
.btn:active { transform: scale(0.98); }
.btn[disabled] { opacity: 0.3; cursor: not-allowed; box-shadow: none; }
.btn.is-loading { opacity: 1; cursor: wait; }
.btn.is-loading .spinner {
  width: 1.15rem; height: 1.15rem;
  border-color: rgba(255, 255, 255, 0.4); border-top-color: #fff;
}

.btn-review { justify-content: space-between; }
.btn-review-left { display: flex; align-items: center; gap: 0.75rem; }
.btn-chevron { font-size: 1.1rem; }
.btn-count {
  background: rgba(255,255,255,0.2);
  padding: 0.125rem 0.6rem;
  border-radius: 0.375rem;
  font-size: 0.875rem;
  font-weight: 700;
}

.btn-ghost {
  background: none;
  color: var(--muted);
  font-weight: 500;
  font-size: 0.875rem;
  padding: 0.75rem;
  box-shadow: none;
}
.btn-ghost:hover { color: var(--ink); }

/* ---------- Summary ---------- */
.summary-card {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  display: flex;
  gap: 1rem;
  align-items: flex-start;
  padding: 1.25rem;
  margin-bottom: 1rem;
}
.summary-tile {
  width: 2.5rem;
  height: 2.5rem;
  flex-shrink: 0;
  background: var(--green-soft);
  border-radius: var(--r-tile);
  display: grid;
  place-items: center;
  font-size: 1.25rem;
}
.summary-card .sum-label {
  font-size: 0.625rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.15em;
  color: var(--muted);
  margin-bottom: 0.25rem;
  font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, monospace;
}
.summary-card .sum-value { font-weight: 500; }

.items-card {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  overflow: hidden;
  margin-bottom: 1rem;
}
.items-card .items-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  padding: 0.75rem 1.25rem;
  border-bottom: 1px solid var(--line);
  background: var(--bg);
}
.items-card .items-head .section { margin: 0; }
.items-card .items-head .items-total {
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--muted);
}
.summary-line {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
  padding: 1rem 1.25rem;
  border-bottom: 1px solid var(--line);
}
.summary-line:last-child { border-bottom: none; }
.summary-line .li-main { display: flex; align-items: center; gap: 1rem; min-width: 0; }
.summary-line .li-emoji { font-size: 1.25rem; flex-shrink: 0; }
.summary-line .li-name { font-weight: 500; font-size: 0.875rem; }
.summary-line .li-remark { color: var(--muted); font-size: 0.75rem; margin-top: 0.125rem; }
.summary-line .li-qty {
  font-weight: 600;
  font-size: 0.875rem;
  white-space: nowrap;
  flex-shrink: 0;
}

.box {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  padding: 1.25rem;
  margin-bottom: 1rem;
}
.box.box-billed { background: var(--chip); }
.box .section { margin: 0 0 0.75rem; }
.box strong { font-size: 0.9375rem; }
.box .hint { color: var(--muted); font-size: 0.8125rem; margin-top: 0.25rem; }

/* ---------- Success screen ---------- */
.success {
  position: fixed;
  inset: 0;
  background: var(--green);
  color: #fff;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 2rem;
  z-index: 50;
}
.success-check {
  width: 6rem;
  height: 6rem;
  background: #fff;
  border: 4px solid rgba(255,255,255,0.2);
  border-radius: 999px;
  display: grid;
  place-items: center;
  font-size: 3rem;
  margin-bottom: 2rem;
}
.success h1 { font-size: 2rem; font-weight: 300; letter-spacing: -0.01em; margin-bottom: 1rem; }
.success-sub { color: rgba(255,255,255,0.6); margin-bottom: 2.5rem; max-width: 18rem; }
.success-ref {
  width: 100%;
  max-width: 22rem;
  padding: 1.5rem;
  background: rgba(255,255,255,0.1);
  border: 1px solid rgba(255,255,255,0.2);
  border-radius: 1.5rem;
  margin-bottom: 2.5rem;
}
.success-ref .ref-label {
  font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, monospace;
  font-size: 0.625rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.15em;
  color: rgba(255,255,255,0.4);
  margin-bottom: 0.5rem;
}
.success-ref .ref-id {
  font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, monospace;
  font-size: 1.5rem;
  font-weight: 700;
  letter-spacing: -0.01em;
}
.success-footer {
  color: rgba(255,255,255,0.4);
  font-size: 0.625rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  text-decoration: none;
}
.success-footer a { color: rgba(255,255,255,0.7); text-decoration: underline; text-underline-offset: 2px; }
.success-footer a:hover { color: #fff; }

/* Summary "Powered by FlowGo" footer (cream bg) */
.powered {
  text-align: center;
  margin-top: 0.5rem;
  font-size: 0.625rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--muted);
}
.powered a { color: var(--green); text-decoration: underline; text-underline-offset: 2px; }

/* ---------- Seller logo ---------- */
.seller-logo {
  height: 4rem;
  width: auto;
  max-width: 70%;
  object-fit: contain;
  border-radius: 0.5rem;
  margin: 0.5rem 0 1.5rem;
  display: block;
}
.other-date {
  width: 100%;
  margin-top: 1rem;
  padding: 1.25rem;
  border: 1px dashed var(--faint);
  border-radius: var(--r-card);
  color: var(--muted);
  background: none;
  font: inherit;
  font-weight: 500;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  position: relative;
}
.other-date:hover { border-color: var(--green); color: var(--green); }
.other-date.selected {
  background: var(--green); border-color: var(--green); color: #fff;
  box-shadow: 0 10px 20px -8px rgba(45, 80, 22, 0.4);
}
.other-date input[type="date"] {
  position: absolute; inset: 0; opacity: 0; width: 100%; height: 100%;
  border: none; cursor: pointer;
}
.success-check img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  border-radius: 999px;
}

/* ---------- Animations ---------- */
@keyframes screenEnter {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}
.screen-enter { animation: screenEnter 0.28s cubic-bezier(0.22, 1, 0.36, 1); }

/* Rapid taps (e.g. +/- stepper) must not fire iOS Safari's double-tap smart
   zoom. Universal because a fat-finger second tap often lands on the card
   padding next to the button, and touch-action does not inherit. `manipulation`
   keeps pan + pinch-zoom; only double-tap-to-zoom is dropped. */
* {
  touch-action: manipulation;
}

/* Press feedback (active:scale like the reference) */
.btn, .same-btn, .other-date, .stepper button, .icon-btn, .lang-btn, .date-card, .branch-card {
  transition: transform 0.1s ease, border-color 0.15s, background 0.15s, box-shadow 0.15s, color 0.15s;
}
.btn:active:not([disabled]), .same-btn:active, .other-date:active,
.stepper button:active, .icon-btn:active { transform: scale(0.97); }

/* Success check pops in */
@keyframes pop {
  0% { transform: scale(0); }
  70% { transform: scale(1.08); }
  100% { transform: scale(1); }
}
.success-check { animation: pop 0.45s cubic-bezier(0.34, 1.56, 0.64, 1); }

/* Greeting subtitle to text-sm (reference) */
.intro .muted { font-size: 0.875rem; }

/* Language screen logo (reference: h-16, mb-8, above Welcome, left-aligned) */
.lang-logo { margin: 0 0 2rem; align-self: flex-start; }

/* ---------- Custom calendar modal ---------- */
.cal-overlay {
  position: fixed; inset: 0; z-index: 100;
  background: rgba(0,0,0,0.4);
  -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
  display: flex; align-items: center; justify-content: center;
  padding: 1rem;
  animation: fadeIn 0.2s ease;
}
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
.cal-modal {
  background: #fff; border-radius: 1.5rem; padding: 1.5rem;
  width: 100%; max-width: 22rem;
  box-shadow: 0 25px 50px -12px rgba(0,0,0,0.35);
  animation: popIn 0.22s cubic-bezier(0.34, 1.56, 0.64, 1);
}
@keyframes popIn { from { opacity: 0; transform: scale(0.95) translateY(10px); } to { opacity: 1; transform: none; } }
.cal-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 1.5rem; }
.cal-title { font-size: 1.125rem; font-weight: 600; }
.cal-x { background: none; border: none; font-size: 1.1rem; color: var(--muted); cursor: pointer; padding: 0.4rem; border-radius: 999px; line-height: 1; }
.cal-x:hover { background: var(--chip); color: var(--ink); }
.cal-nav { display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.75rem; }
.cal-month { font-size: 1.125rem; font-weight: 700; }
.cal-arrows { display: flex; gap: 0.25rem; }
.cal-arrows button { background: none; border: none; color: var(--green); font-size: 1.5rem; line-height: 1; cursor: pointer; padding: 0.1rem 0.4rem; border-radius: 0.5rem; }
.cal-arrows button:hover { background: var(--green-soft); }
.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 0.15rem; }
.cal-wd { text-align: center; font-size: 0.8125rem; color: var(--muted); padding: 0.4rem 0; }
.cal-cell { aspect-ratio: 1; display: flex; align-items: center; justify-content: center; border: none; background: none; font: inherit; font-size: 0.95rem; color: var(--ink); border-radius: 12px; cursor: pointer; }
.cal-cell.day:hover:not(.selected) { background: var(--green-soft); }
.cal-cell.disabled { color: var(--faint); cursor: not-allowed; }
.cal-cell.today { color: var(--green); font-weight: 700; }
.cal-cell.selected { background: var(--green); color: #fff; font-weight: 700; }
.cal-cell.day.disabled:hover { background: none; }
.other-date .cal-ico { margin-right: 0.25rem; }

/* ---------- Accessibility polish ---------- */
/* Unified keyboard focus ring (mouse clicks stay clean via :focus-visible). */
.lang-btn:focus-visible, .date-card:focus-visible, .branch-card:focus-visible,
.other-date:focus-visible, .btn:focus-visible, .btn-ghost:focus-visible,
.icon-btn:focus-visible, .same-btn:focus-visible, .stepper button:focus-visible,
.cal-cell.day:focus-visible, .cal-arrows button:focus-visible, .cal-x:focus-visible,
.lang-btn:focus-visible { outline: 2px solid var(--green); outline-offset: 2px; }
.search-box:focus-visible, .stepper input.qty:focus-visible, .remark:focus-visible {
  outline: 2px solid var(--green); outline-offset: 1px;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* Regulars loading (background fetch after branch switch) */
.regs-loading { display: flex; justify-content: center; padding: 2.5rem 0; }
/* Regulars fetch failed — retry affordance */
.regs-error { display: flex; flex-direction: column; align-items: center; gap: 0.75rem; padding: 2rem 0; text-align: center; }
.btn-retry {
  padding: 0.6rem 1.5rem; border-radius: 999px; border: 1px solid var(--green);
  background: var(--green-soft); color: var(--green); font-weight: 600; cursor: pointer;
}
.spinner {
  width: 1.75rem; height: 1.75rem; border-radius: 999px;
  border: 2px solid var(--green-soft); border-top-color: var(--green);
  animation: spin 0.7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* Sticky search bar (stays pinned below topbar while items scroll) */
.search-bar {
  position: sticky;
  z-index: 9;
  background: var(--bg);
  padding: 0.75rem 0 0.5rem;
}


/* Success "Start New Order" button (on green bg) */
.success-btn {
  width: 100%;
  max-width: 22rem;
  padding: 1rem;
  margin-bottom: 1.5rem;
  border: 1px solid rgba(255,255,255,0.2);
  background: rgba(255,255,255,0.1);
  color: #fff;
  font: inherit;
  font-weight: 500;
  border-radius: var(--r-card);
  cursor: pointer;
  transition: background .15s, transform .1s;
}
.success-btn:hover { background: rgba(255,255,255,0.18); }
.success-btn:active { transform: scale(0.98); }

/* "Select Other Date" as a grid card */
.other-card {
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  text-align: center; gap: 0.35rem; color: var(--muted);
}
.other-card .oc-icon { font-size: 1.5rem; line-height: 1; }
.other-card .oc-label { font-size: 0.8125rem; font-weight: 600; line-height: 1.2; }
.other-card .oc-day { font-size: 1.75rem; font-weight: 600; line-height: 1; color: var(--ink); }
.other-card.selected, .other-card.selected .oc-day, .other-card.selected .mon { color: #fff; }

/* ---------- Placer name gate ---------- */
.name-input {
  width: 100%;
  box-sizing: border-box;
  padding: 14px 16px;
  font-size: 16px;
  border: 1px solid var(--line);
  border-radius: 12px;
  margin-top: 8px;
}
.link-btn {
  background: none;
  border: 0;
  padding: 0;
  color: var(--green);
  font-size: 13px;
  text-decoration: underline;
  cursor: pointer;
}

/* ---------- picker additions (Task 8) ---------- */

/* Bare .card is used throughout the picker screens (order/item rows, preview
   lines) with no separate .pad modifier — give it its own padding so content
   doesn't sit flush against the border (base .card above is a plain container). */
.card { padding: 1rem 1.25rem; }

/* Not in the design spec's CSS block, but referenced by login.ts/no-access.ts/
   app.js — the picker's own primary/secondary/link button vocabulary
   (web-order's equivalent is the unrelated .btn class). */
.btn-primary, .btn-secondary, .btn-link { font-family: inherit; cursor: pointer; text-decoration: none; }
.btn-primary {
  width: 100%;
  padding: 1.25rem;
  border: none;
  border-radius: var(--r-card);
  font-size: 1rem;
  font-weight: 600;
  color: #fff;
  background: var(--green);
  box-shadow: 0 6px 16px -8px rgba(45, 80, 22, 0.45);
  transition: filter .15s, box-shadow .15s, transform .1s;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.75rem;
}
.btn-primary:hover { filter: brightness(1.08); box-shadow: 0 8px 20px -8px rgba(45, 80, 22, 0.5); }
.btn-primary:active { transform: scale(0.98); }
.btn-primary[disabled] { opacity: 0.3; cursor: not-allowed; box-shadow: none; }
/* Looks blocked, still receives the tap — the handler in receiver-app.js is
   what tells the receiver WHY and scrolls them to it. A real [disabled] here
   would swallow that tap and leave the button reading as broken. */
.btn-primary.is-blocked { opacity: 0.3; box-shadow: none; }
.btn-secondary {
  width: 100%;
  padding: 1.1rem;
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  font-size: 1rem;
  font-weight: 600;
  color: var(--ink);
  background: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
}
.btn-secondary:hover { border-color: var(--green); color: var(--green); }
.btn-secondary:active { transform: scale(0.98); }
.btn-link {
  background: none;
  border: none;
  padding: 0.5rem;
  color: var(--green);
  font-size: 0.875rem;
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 2px;
  display: inline-block;
}
.btn-link:hover { opacity: 0.8; }

/* App shell topbar user name (referenced by picker-app.ts, no rule shipped for it yet). */
.user-badge {
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--muted);
  max-width: 9rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.order-card { cursor: pointer; }
.order-card.disabled { opacity: .55; cursor: default; }
.order-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
/* PO number subtitle under the items-screen title (referenced by app.js, no shipped rule). */
.order-sub { margin-bottom: 1rem; font-size: 0.8125rem; }
.badge { font-size: 12px; padding: 2px 8px; border-radius: 999px; font-weight: 600; }
.badge-green { background: var(--green-soft); color: var(--green); }
.badge-grey { background: var(--chip); color: var(--muted); }
/* Receiver PO-list "Partial" tag — same amber as the prior-GRN banner and the
   changed-line marker, so "needs a look" reads the same everywhere. */
.badge-amber { background: #fef3c7; color: #92400e; }
.item-row { display: flex; justify-content: space-between; align-items: center; gap: 12px; margin-bottom: 0.5rem; }
.item-row.changed { border-left: 4px solid #f59e0b; }
.item-row.zero { border-left: 4px solid #dc2626; }
.item-info { min-width: 0; }
.item-name { font-weight: 600; overflow-wrap: anywhere; }
/* Scoped under .stepper with two class selectors so this beats the pre-existing
   ".stepper button" web-order rule (0,1,1) which would otherwise win on specificity
   and shrink these to its 30px round +/- buttons. */
.stepper .step-btn {
  width: 44px; height: 44px; font-size: 22px; border-radius: 10px;
  border: 1px solid var(--line); background: #fff; color: var(--ink); cursor: pointer;
}
.stepper .step-btn:hover { border-color: var(--green); color: var(--green); }
.stepper .step-btn:active { transform: scale(0.95); }
.qty-input {
  width: 64px; height: 44px; text-align: center; font-size: 18px;
  border: 1px solid var(--line); border-radius: 10px; font-family: inherit;
}
.qty-input:focus { outline: 2px solid var(--green); outline-offset: 1px; }

.offline-banner { background: #fef3c7; color: #92400e; text-align: center; padding: 0.5rem; font-size: 0.875rem; }
.done-box { text-align: center; padding: 3rem 1rem; }
.done-icon { font-size: 3.5rem; }

.login-main { padding: 2rem 1.25rem; display: flex; flex-direction: column; max-width: 24rem; margin: 0 auto; }
.login-logo { margin: 0 0 3rem 0; height: 3rem; width: auto; max-width: 12rem; align-self: flex-start; }
.login-title { font-size: 1.5rem; font-weight: 650; letter-spacing: -0.015em; line-height: 1.15; margin: 0 0 1.75rem; }
.login-step { display: flex; flex-direction: column; gap: 1rem; }
.login-sub { color: var(--muted); font-size: 0.9375rem; line-height: 1.45; margin: 0 0 0.25rem; }
.field { display: block; }
.field-label { display: block; font-size: 0.8125rem; font-weight: 500; color: var(--muted); margin-bottom: 0.5rem; }
.login-main input {
  width: 100%; box-sizing: border-box; height: 3.25rem; font-size: 1.0625rem;
  padding: 0 0.875rem; border: 1px solid var(--line); border-radius: 0.75rem;
  background: #fff; color: var(--ink); transition: border-color .15s, box-shadow .15s;
}
.login-main input::placeholder { color: var(--muted); }
.login-main input:focus { outline: none; border-color: var(--green); box-shadow: 0 0 0 3px var(--green-soft); }
.otp-input {
  text-align: center; letter-spacing: 0.4em; padding-left: 0.4em;
  font-size: 1.5rem; font-weight: 700; height: 3.5rem;
}
/* "or" divider between the email path (primary) and Google (secondary). */
.or-divider { display: flex; align-items: center; gap: 0.75rem; color: var(--muted); font-size: 0.8125rem; margin: 0.125rem 0; }
.or-divider::before, .or-divider::after { content: ''; flex: 1; height: 1px; background: var(--line); }
.btn-google { gap: 0.625rem; }
.g-icon { flex-shrink: 0; }
.login-error {
  margin: 0.5rem 0 0; font-size: 0.875rem; color: #a3341f;
  background: #fbeae7; border-radius: 0.625rem; padding: 0.625rem 0.875rem;
}

.error-box, .error { color: #dc2626; text-align: center; font-size: 0.875rem; }
.error-box { padding: 1.5rem 0; }
.empty { text-align: center; color: var(--muted); padding: 2.5rem 1rem; }
/* .spinner and @keyframes spin already defined above (web-order "regulars loading"
   block) with the same shape this needs — reused as-is, not redeclared. */

/* ---------- team/company selection ---------- */
.sel-prompt { color: var(--muted); margin: 0 0 0.75rem; font-size: 0.9375rem; }
.sel-card { cursor: pointer; }
.sel-card strong { display: block; font-size: 1.0625rem; }
.sel-context { margin: 0 0 0.75rem; font-size: 0.8125rem; }
/* Orders screen: company · reload on one line (reload moved off the search row). */
.orders-context { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; margin: -0.5rem 0 0.25rem; }
.orders-context .sel-context { margin: 0; min-width: 0; }

/* ---------- order search ---------- */
.search-input {
  width: 100%; height: 2.75rem; padding: 0 0.875rem; margin: 0 0 0.75rem;
  font-size: 1rem; border: 1px solid var(--border, #d1d5db); border-radius: 0.625rem;
  background: var(--card-bg, #fff); color: inherit; box-sizing: border-box;
}

/* ---------- login: fill exactly one viewport, no scroll ----------
   The shared .app rule adds an 8rem bottom pad (for the picking-flow sticky
   footer) and uses 100vh; on iOS Safari that overflows and the login scrolls.
   Login has no footer, so zero that pad, use dvh, and flex-center the content. */
.login-app {
  min-height: 100vh;  /* fallback for pre-dvh browsers (Chrome <108, old cheap Androids) */
  min-height: 100dvh;
  padding-bottom: 1.5rem;
  display: flex;
  flex-direction: column;
}
.login-app .login-main { flex: 1; justify-content: center; }

/* ---------- order list ---------- */
.search-bar { display: flex; gap: 0.5rem; align-items: center; }
.search-bar .search-box { flex: 1; min-width: 0; }
#orders-list { gap: 0.5rem; }
/* Row: debtor + meta on the left, total-qty chip (the picker's key number) on the right. */
.order-card {
  width: 100%; display: flex; align-items: center; gap: 0.875rem; text-align: left;
  padding: 0.8125rem 0.9375rem; background: #fff; color: var(--ink);
  border: 1px solid var(--line); border-radius: 0.875rem; cursor: pointer;
  transition: border-color .15s, box-shadow .15s, transform .08s;
}
.order-card:hover { border-color: var(--green); box-shadow: 0 4px 14px -10px rgba(45, 80, 22, 0.4); }
.order-card:active { transform: scale(0.99); }
.order-card.disabled { opacity: 0.62; cursor: default; }
.order-card.disabled:hover { border-color: var(--line); box-shadow: none; }
.order-card.disabled:active { transform: none; }
.order-main { flex: 1; min-width: 0; }
.order-name {
  font-weight: 700; font-size: 1.0625rem; line-height: 1.2; letter-spacing: -0.01em;
  color: var(--ink); overflow-wrap: anywhere;
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.375rem;
}
.order-meta { margin-top: 0.3125rem; font-size: 0.8125rem; color: var(--muted); }
.order-qty {
  flex-shrink: 0; display: flex; flex-direction: column; align-items: center;
  min-width: 3.25rem; padding: 0.375rem 0.5rem; line-height: 1.05;
  background: var(--chip); border-radius: 0.625rem;
}
.qty-num { font-weight: 700; font-size: 1.0625rem; color: var(--ink); }
.qty-lbl { font-size: 0.5625rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.07em; margin-top: 0.15rem; }
.item-wrap.zero { border-left: 3px solid #dc2626; }

/* Calendar grid: force 7 columns to shrink (aspect-ratio cells otherwise blow out 1fr). */
.cal-grid { grid-template-columns: repeat(7, minmax(0, 1fr)); }
.cal-cell { min-width: 0; }

/* ---------- per-line "mark as picked" toggle ---------- */
.pick-check {
  flex-shrink: 0; width: 2.25rem; height: 2.25rem; border-radius: 50%;
  border: 2px solid var(--line); background: #fff; color: transparent;
  display: flex; align-items: center; justify-content: center; cursor: pointer;
  transition: background .12s, border-color .12s, color .12s;
}
.pick-check.on { background: var(--green); border-color: var(--green); color: #fff; }
.item .info { min-width: 0; }
.item-wrap.picked { border-color: var(--green); background: var(--green-soft); }

/* ---------- language-select brand logo ---------- */
.brand-logo { display: block; margin: 0 auto 0.75rem; width: auto; max-width: 160px; height: auto; }

/* ---------- preview / review summary ---------- */
.review-summary { padding: 1rem 1.125rem; border: 1px solid var(--line); border-radius: 0.875rem; background: #fff; margin-bottom: 1.25rem; }
.review-debtor { font-weight: 700; font-size: 1.0625rem; line-height: 1.2; letter-spacing: -0.01em; margin-bottom: 0.625rem; }
.review-stats { display: flex; flex-wrap: wrap; gap: 0.375rem 1rem; }
.rv-stat { font-size: 0.8125rem; color: var(--muted); }
.rv-stat strong { color: var(--ink); font-weight: 700; }
.review-h { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.07em; color: var(--muted); margin: 0 0 0.5rem; font-weight: 600; }
.review-line { display: flex; justify-content: space-between; align-items: center; gap: 0.75rem; padding: 0.625rem 0; border-bottom: 1px solid var(--line); }
.review-line:last-of-type { border-bottom: none; }
.rv-name { min-width: 0; overflow-wrap: anywhere; font-size: 0.9375rem; }
.rv-delta { flex-shrink: 0; color: var(--muted); font-variant-numeric: tabular-nums; }
.rv-delta strong { color: var(--ink); }
.review-nochange { margin: 0.25rem 0 0; }
.picked-by { margin-top: 1.5rem; font-size: 0.8125rem; }

/* ---------- item screen: PO + mark-all ---------- */
.items-head { display: flex; justify-content: space-between; align-items: center; gap: 0.75rem; margin-bottom: 0.875rem; }
.items-po { margin: 0; font-size: 0.8125rem; }
/* The delivery date takes its own line rather than flowing after the doc date:
   at a handheld's width the two dates wrap mid-label ("Delivery" / "date:"),
   and a shared delivery date is a heading for the whole PO, not a suffix. */
.items-po .po-delivery { display: block; }
.mark-all {
  flex-shrink: 0; background: var(--green-soft); color: var(--green); border: none;
  border-radius: 0.5rem; padding: 0.45rem 0.8rem; font-size: 0.8125rem; font-weight: 600;
  cursor: pointer; font-family: inherit; transition: background .12s, transform .08s;
}
.mark-all:hover { background: #dbe8cd; }
.mark-all:active { transform: scale(0.97); }

/* ---------- barcode scan ---------- */
.items-actions { display: flex; align-items: center; gap: 0.5rem; flex-shrink: 0; }
.scan-btn {
  display: inline-flex; align-items: center; gap: 0.35rem; background: var(--ink); color: #fff;
  border: none; border-radius: 0.5rem; padding: 0.45rem 0.75rem; font-size: 0.8125rem; font-weight: 600;
  cursor: pointer; font-family: inherit; transition: opacity .12s, transform .08s;
}
.scan-btn:hover { opacity: 0.9; }
.scan-btn:active { transform: scale(0.97); }

.scan-overlay {
  position: fixed; inset: 0; z-index: 100; background: #0b0b0c;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
}
.scan-stage { position: relative; width: 100%; max-width: 30rem; aspect-ratio: 3 / 4; overflow: hidden; }
.scan-video { width: 100%; height: 100%; object-fit: cover; display: block; }
.scan-preview { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
.scan-preview[hidden] { display: none; }
/* iOS frame source behind the canvas preview (scan-ui.js startPreview). Kept
   laid out and at full size rather than display:none, so WebKit keeps
   decoding frames into it. */
.scan-video.scan-video-src { opacity: 0; }
.scan-reticle {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 72%; height: 34%; border: 2px solid rgba(255,255,255,0.9); border-radius: 0.75rem;
  box-shadow: 0 0 0 100vmax rgba(0,0,0,0.45);
}
/* Sits above .scan-close's bar (both are absolute against the fixed overlay,
   not the stage), so this bottom offset must clear the bar's height plus its
   own gap — change one and check the other. */
.scan-status {
  position: absolute; left: 0; right: 0; bottom: calc(5.75rem + env(safe-area-inset-bottom, 0px)); text-align: center;
  color: #fff; font-size: 1rem; font-weight: 600; padding: 0 1.5rem; line-height: 1.35;
  text-shadow: 0 1px 4px rgba(0,0,0,0.6);
}
.scan-status.ok { color: #8fdc7a; }
.scan-status.warn { color: #ffc978; }
/* The dismiss control lives at the BOTTOM, not in a corner. The overlay is
   position:fixed inset:0, so the old top-right ✕ pinned to the far corner of
   the screen — on a 6.7" handheld that is the worst thumb reach on the
   device, and this is the control someone reaches for when a scan is going
   wrong. It is also the only VISIBLE way out (hardware back closes the
   camera too, but a phone on gesture nav gives no hint of that), which is why
   it is labelled rather than a glyph. The background is opaque enough to stay
   legible in native-engine mode, where the overlay itself goes transparent
   and ML Kit renders the camera behind the WebView. */
.scan-close {
  position: absolute; left: 1rem; right: 1rem;
  bottom: calc(1.25rem + env(safe-area-inset-bottom, 0px));
  height: 3.25rem; border-radius: 0.875rem;
  background: rgba(255,255,255,0.16); color: #fff;
  border: 1px solid rgba(255,255,255,0.3);
  font-family: inherit; font-size: 1.0625rem; font-weight: 600; cursor: pointer;
  display: grid; place-items: center;
}
.scan-close:active { background: rgba(255,255,255,0.28); }

/* ML Kit barcode scanning renders the camera behind the WebView, so the page
   itself must become invisible for the duration — anything opaque covers the
   preview. Only the scan overlay's own chrome (reticle, status, close) is
   re-shown. Scoped to a body class that ScanUI adds and removes around a
   native scan; the zxing path never sets it, so the picker is unaffected. */
/* html AND body: the root element's background propagates to the canvas and is
   painted even when the element is visibility:hidden, so clearing body alone
   left `html, body { background: var(--bg) }`'s near-white showing through and
   the camera behind the WebView was never visible — the scan overlay appeared
   over a white page. */
html.barcode-scanner-active { background: transparent !important; }
body.barcode-scanner-active { visibility: hidden; background: transparent !important; }
body.barcode-scanner-active .scan-overlay { visibility: visible; background: transparent; }
body.barcode-scanner-active .scan-overlay .scan-video { display: none; }

/* Backdrop that hosts the match card when there is no camera overlay — a
   hardware-wedge scan. .scan-match is position:absolute, so it anchors to this
   exactly as it does to .scan-overlay. */
.scan-sheet { position: fixed; inset: 0; z-index: 100; background: rgba(0, 0, 0, 0.45); }

/* Match card — slides up over the camera on a scan hit. Picker confirms
   how many to grab (adjustable for short/out-of-stock), then scanning resumes. */
.scan-match {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 10;
  background: #fff; color: var(--ink);
  border-radius: 1rem 1rem 0 0; padding: 1.15rem 1.25rem calc(1.25rem + env(safe-area-inset-bottom));
  box-shadow: 0 -8px 24px rgba(0,0,0,0.35);
  animation: sm-up .18s cubic-bezier(0.16, 1, 0.3, 1);
}
@keyframes sm-up { from { transform: translateY(100%); } to { transform: translateY(0); } }
.scan-match .sm-name { font-size: 1.0625rem; font-weight: 700; line-height: 1.3; }
.scan-match .sm-code { font-size: 0.8125rem; margin-top: 0.15rem; }
/* Weight decoded from a scale-printed label — the pack just scanned. */
.scan-match .sm-pack {
  display: inline-block; margin-top: 0.5rem; padding: 0.2rem 0.6rem; border-radius: 999px;
  background: var(--green-soft); color: var(--green); font-weight: 700; font-size: 0.9375rem;
  font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, monospace;
}
.scan-match .sm-pick { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; margin-top: 1rem; }
.scan-match .sm-label { font-size: 0.9375rem; font-weight: 600; }
.scan-match .sm-confirm {
  width: 100%; margin-top: 1.15rem; padding: 0.9rem; border-radius: 0.7rem; border: none;
  background: var(--green); color: #fff; font-size: 1rem; font-weight: 700; cursor: pointer; font-family: inherit;
}
.scan-match .sm-confirm:active { transform: scale(0.99); }
/* Slots the caller fills: header lines, the unit control beside the qty input,
   and an optional block below the pick row (the receiver's "Other…" unit+note).
   .sm-head and .sm-unit are display:contents so their children stay DIRECT
   children of their layout parents — .qty-wrap is a column flex container
   (styles.css:334) and .uom-select's own width/margin-top assume it is a flex
   item, which it would stop being inside a real inline wrapper. .sm-extra is a
   genuine block below the pick row, so it only needs to collapse when empty. */
.scan-match .sm-head, .scan-match .sm-unit { display: contents; }
.scan-match .sm-extra:empty { display: none; }
/* Dismiss control — rendered only when the caller passes onDismiss (the
   receiver's card and the picker's wedge sheet; the picker's camera card has
   none — its only exit is the scanner's own ✕, unchanged by this). Reuses
   .scan-close's glyph + aria-label pattern but recolored for a white card
   instead of a dark camera overlay, sized to match .scan-close (2.75rem) for
   the same reason — a real touch target on a gloved warehouse user's escape
   hatch. The gutter is reserved on the header text alone, not the card
   (.sm-name / .sm-code below), because a card-wide padding-right would also
   push in the full-width Confirm button and the space-between .sm-pick row,
   which need no clearance and would end up off-centre. */
.scan-match.has-dismiss .sm-name, .scan-match.has-dismiss .sm-code { padding-right: 2.75rem; }
.scan-match .sm-dismiss {
  position: absolute; top: 0.75rem; right: 0.75rem; width: 2.75rem; height: 2.75rem;
  border-radius: 999px; background: var(--chip); color: var(--muted); border: none;
  font-size: 0.875rem; line-height: 1; cursor: pointer; display: grid; place-items: center;
}
.scan-match .sm-dismiss:active { transform: scale(0.94); }
/* .uom-select now renders only inside the scan match card (the item-row
   revamp removed the list row's inline copy — see .uom-select below for the
   base rule this overrides). Width covers the <=4-char UOM codes AutoCount
   actually uses (PCS/PACK/CTN/PKT/BDL/BTL) without truncating, at the real
   touch-target size this control deserves as the card's primary correction
   point. */
.scan-match .uom-select {
  width: 4.75rem; min-height: 2.25rem; font-size: 0.9375rem;
  padding: 0.3rem 1.5rem 0.3rem 0.5rem; margin-top: 0;
}
/* Print actions on the items screen — same pill vocabulary as .mark-all */
.print-btn {
  flex-shrink: 0; background: #fff; color: var(--ink); border: 1px solid var(--line);
  border-radius: 0.5rem; padding: 0.45rem 0.7rem; font-size: 0.8125rem; font-weight: 600;
  cursor: pointer; font-family: inherit; transition: border-color .15s, color .15s, transform .08s;
}
.print-btn:hover { border-color: var(--green); color: var(--green); }
.print-btn:active { transform: scale(0.97); }

/* ---- receiver ---- */

/* Shell header: logo + app name. Simpler than the picker's sticky .topbar —
   the receiver has no back/settings chrome, just one static header row. */
.app-head { display: flex; align-items: center; gap: 0.625rem; padding: 1.25rem 0 0.25rem; }
.app-head .head-logo { width: 2rem; height: 2rem; flex-shrink: 0; }
.app-head h1 { font-size: 1.25rem; font-weight: 600; margin: 0; }

/* PO-number / supplier-name search fields (same visual weight as login's
   .login-main input, scoped locally since the receiver shell has no
   .login-main ancestor). */
.rcv-field { margin-bottom: 1rem; }
.rcv-field label { display: block; font-size: 0.8125rem; font-weight: 500; color: var(--muted); margin-bottom: 0.5rem; }
.rcv-field input {
  width: 100%; box-sizing: border-box; height: 3.25rem; font-size: 1.0625rem;
  padding: 0 0.875rem; border: 1px solid var(--line); border-radius: 0.75rem;
  background: #fff; color: var(--ink); font-family: inherit;
  transition: border-color .15s, box-shadow .15s;
}
.rcv-field input::placeholder { color: var(--muted); }
.rcv-field input:focus { outline: none; border-color: var(--green); box-shadow: 0 0 0 3px var(--green-soft); }
/* PO field + its camera button. The button is a fixed square so the input
   keeps the full remaining width; both are 3.25rem tall so they sit on one
   baseline with the field's existing height. */
.rcv-field-row { display: flex; align-items: center; gap: 0.5rem; }
.rcv-field-row input { flex: 1; min-width: 0; }
.rcv-ocr-btn {
  flex-shrink: 0; width: 3.25rem; height: 3.25rem; display: flex;
  align-items: center; justify-content: center; padding: 0;
  background: var(--chip); color: var(--ink);
  border: 1px solid var(--line); border-radius: 0.75rem; cursor: pointer;
  transition: border-color .15s, background .15s;
}
.rcv-ocr-btn:active { background: var(--green-soft); border-color: var(--green); }
/* Candidate list in the multi-PO picker — one full-width button per number. */
.rcv-po-choices { display: flex; flex-direction: column; gap: 0.5rem; margin-bottom: 1rem; }
.rcv-po-choices .btn-secondary { width: 100%; }
.rcv-loading { display: flex; justify-content: center; padding: 2rem 0; }
.rcv-results { margin-top: 1rem; }

/* Prior-GRN warning banner — amber, same palette as .offline-banner. */
.rcv-banner {
  background: #fef3c7; color: #92400e; border-radius: 0.75rem;
  padding: 0.75rem 1rem; font-size: 0.875rem; line-height: 1.4; margin-bottom: 1rem;
}

.rcv-po-head { margin-bottom: 0.25rem; }
.rcv-po-head .rcv-creditor { font-weight: 700; font-size: 1.0625rem; line-height: 1.2; letter-spacing: -0.01em; }
.rcv-po-head .rcv-meta { color: var(--muted); font-size: 0.8125rem; margin-top: 0.25rem; }

/* Receiving line rows reuse the picker's .item-wrap/.item/.qty-read markup for
   an identical look (the stepper itself is gone from the row — see .rcv-row
   above — so there is no disabled/greyed stepper rule to keep here any more). */
/* "add a DO photo to submit" hint — sits above the sticky footer so the
   disabled Submit reads as blocked-on-a-step, not broken. */
.rcv-submit-hint { text-align: center; font-size: 0.8125rem; margin: -0.5rem 0 0.625rem; }
/* The footer normally fades to transparent at its top; with the hint in it,
   that top is text, so it gets a solid ground (and a hairline) instead. */
.sticky.has-hint { background: var(--bg); border-top: 1px solid var(--line); padding-top: 1rem; }
.rcv-footer-room { height: 2.25rem; }

/* Scroll-into-view + flash on a scan match (closeScanner reveals the row
   again since the overlay is full-screen while scanning). */
@keyframes rcv-flash-kf { 0% { box-shadow: 0 0 0 3px var(--green); } 100% { box-shadow: 0 0 0 0 rgba(45, 80, 22, 0); } }
.rcv-flash { animation: rcv-flash-kf 1.1s ease-out; }

/* Photo thumbnail strip — horizontal scroll, same tile size for add + shots. */
.rcv-photo-strip {
  display: flex; gap: 0.625rem; overflow-x: auto; padding: 0.25rem 0.125rem 0.5rem;
  margin-bottom: 0.5rem; -webkit-overflow-scrolling: touch;
}
/* Separates the photo section from the item list above it — the two ran
   together, so the heading read as part of the last line item. */
.rcv-photos-h { margin-top: 1.5rem; }
/* Sits between the section heading and the strip, so it is read before the
   first capture rather than after. */
.rcv-kind-hint { margin: -0.25rem 0 0.375rem; font-size: 0.75rem; }
.rcv-thumb {
  /* 6rem, not 5: the bottom strip is a two-segment control, and at 5rem each
     half was under 40px — narrow enough that "Invoice" was clipped mid-word. */
  position: relative; flex-shrink: 0; width: 6rem; height: 6rem;
  border-radius: 0.625rem; overflow: hidden; border: 1px solid var(--line); background: var(--chip);
}
.rcv-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.rcv-thumb-remove {
  position: absolute; top: 0.25rem; right: 0.25rem; width: 1.5rem; height: 1.5rem;
  border-radius: 999px; border: none; background: rgba(0,0,0,0.55); color: #fff;
  font-size: 0.75rem; line-height: 1; cursor: pointer; display: grid; place-items: center;
}
/* Two-segment page-type switch pinned across the bottom of each thumbnail.
   Both options are always visible and the active one is filled, so the control
   reads as a switch at a glance — a single badge showing only the current value
   was mistaken for a caption and went untapped. Each half is ~40x26px, which is
   the practical floor for a gloved finger on the handhelds this runs on. */
.rcv-thumb-kind {
  position: absolute; left: 0; right: 0; bottom: 0;
  display: flex; overflow: hidden;
  background: rgba(0, 0, 0, .55);
}
/* nowrap + ellipsis rather than silent clipping: if a translation ever
   outgrows the half again, it must be visibly truncated so it gets noticed,
   not sliced off mid-glyph against the overflow:hidden parent. */
.rcv-kind-opt {
  flex: 1 1 50%; min-width: 0; height: 1.625rem;
  border: 0; padding: 0 0.125rem; margin: 0;
  font-family: inherit; font-size: 0.6875rem; font-weight: 700; line-height: 1;
  letter-spacing: 0.02em; cursor: pointer;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  background: transparent; color: rgba(255, 255, 255, .88);
}
.rcv-kind-opt + .rcv-kind-opt { border-left: 1px solid rgba(255, 255, 255, .25); }
.rcv-kind-opt.is-on { background: var(--green, #2D5016); color: #fff; }
/* Ambiguous page (both a DO and an invoice header phrase found): the switch
   itself gets an amber top edge, and a small "?" badge sits over the
   thumbnail so the marker is noticeable without shrinking the DO/Invoice
   labels — a third segment in the already-tight two-button bar would have
   clipped one of them again. */
.rcv-thumb-kind.is-ambig { box-shadow: inset 0 2px 0 0 #f59e0b; }
.rcv-thumb-ambig {
  position: absolute; top: 0.25rem; left: 0.25rem; width: 1.125rem; height: 1.125rem;
  border-radius: 999px; background: #f59e0b; color: #1a1a1a;
  font-size: 0.6875rem; font-weight: 800; line-height: 1;
  display: grid; place-items: center;
}
.rcv-ambig-hint { color: #b45309; }

/* Off-PO ("extra") receiving lines — see receiver-app.js addExtra. */
.rcv-add-extra { width: 100%; margin: 0.25rem 0 0.5rem; }
/* "+ Add PO to this delivery" sits directly under "+ Item not on PO" — the two
   share the secondary-button look; only the bottom gap before the photo
   section belongs to whichever of them renders last. */
.rcv-add-po { width: 100%; margin: 0 0 1.25rem; }
.rcv-po-limit { margin: 0 0 1.25rem; font-size: 0.8125rem; }
/* Per-PO subheading in the receiving list — rendered ONLY when the session
   holds more than one PO, so a single-PO receipt is untouched. Quiet enough
   to read as a divider rather than compete with the item rows it groups. */
.rcv-po-sub {
  display: flex; align-items: baseline; justify-content: space-between; gap: 0.5rem;
  margin: 0.875rem 0 0.375rem; padding: 0.3rem 0.125rem;
  border-bottom: 1px solid var(--line);
}
.rcv-po-sub:first-of-type { margin-top: 0.25rem; }
.rcv-po-sub-no { font-size: 0.8125rem; font-weight: 700; letter-spacing: 0.01em; }
.rcv-po-sub-meta { font-size: 0.75rem; color: var(--muted); text-align: right; }
.rcv-extra-tag { color: #b45309; font-weight: 600; }
.rcv-row.is-extra .pick-check { opacity: 0.55; pointer-events: none; }
.rcv-extra-remove {
  flex: 0 0 auto; margin-left: 0.5rem; width: 2rem; height: 2rem; border-radius: 50%;
  /* --border isn't actually defined in :root (see the same fallback at line ~1068) —
     without it, var(--border) is invalid and the whole shorthand drops to no border. */
  border: 1px solid var(--border, #d1d5db); background: var(--bg); color: var(--muted); font-size: 0.875rem; line-height: 1;
}
.rcv-extra-remove:active { transform: scale(0.95); }

/* Item-not-on-PO search sheet — see receiver-app.js openExtraPicker. */
.rcv-extra-sheet {
  position: absolute; left: 0; right: 0; bottom: 0; max-height: 85vh; display: flex; flex-direction: column;
  background: var(--bg); border-radius: 1rem 1rem 0 0; padding: 1rem 1rem calc(1rem + env(safe-area-inset-bottom));
  gap: 0.75rem;
}
.rcv-extra-q { width: 100%; font-size: 1rem; padding: 0.75rem 0.875rem; border: 1px solid var(--border, #d1d5db); border-radius: 0.75rem; }
/* flex-shrink:0 on everything but the results: in this column flexbox a long
   result list otherwise squeezed the scope switch down to a thin strip. */
.rcv-extra-q, .rcv-scope, .rcv-extra-close { flex-shrink: 0; }
.rcv-scope { display: flex; border: 1px solid var(--border, #d1d5db); border-radius: 0.75rem; overflow: hidden; }
.rcv-scope button { flex: 1; padding: 0.5rem; background: var(--bg); color: var(--muted); border: 0; font-size: 0.875rem; }
.rcv-scope button.on { background: var(--green-soft); color: var(--green); font-weight: 600; }
.rcv-extra-results { flex: 1 1 auto; min-height: 6rem; overflow-y: auto; display: flex; flex-direction: column; gap: 0.375rem; }
.rcv-extra-hit { text-align: left; width: 100%; padding: 0.625rem 0.75rem; border: 1px solid var(--border, #d1d5db); border-radius: 0.75rem; background: var(--bg); }
.rcv-extra-hit .name { font-weight: 600; }
.rcv-extra-empty { text-align: center; padding: 1rem 0; }
.rcv-photo-add {
  /* matches .rcv-thumb so the strip's tiles stay aligned */
  flex-shrink: 0; width: 6rem; height: 6rem; border-radius: 0.625rem;
  border: 1px dashed var(--faint); background: transparent; color: var(--muted);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  /* 0.625rem (10px) was unreadable at arm's length in daylight — these tiles
     are the only label on the POD screen telling a driver where photos go. */
  gap: 0.25rem; font-size: 0.75rem; text-align: center; cursor: pointer; font-family: inherit;
  transition: border-color .15s, color .15s;
}
.rcv-photo-add:hover { border-color: var(--green); color: var(--green); }
.rcv-photo-plus { font-size: 1.25rem; line-height: 1; }
.rcv-photo-ico { display: grid; place-items: center; }
.rcv-photo-ico svg { display: block; }
/* Replaces .rcv-photo-add as the strip's trailing flex item once the
   combined DO+invoice cap is hit — same footprint so the strip doesn't
   reflow, but no border/cursor since it isn't interactive. */
.rcv-photo-limit {
  flex-shrink: 0; width: 5rem; display: flex; align-items: center;
  font-size: 0.6875rem; line-height: 1.3; text-align: center; margin: 0;
}

/* Received-unit control. Base/default sizing only — the item-row revamp made
   the scan match card its sole caller, and `.scan-match .uom-select` above
   overrides width/min-height/font-size/padding to the size that actually
   shows on screen; this rule just supplies everything that override doesn't
   touch (caret, colors, border). Kept narrow rather than `width: 100%`
   because a select sizes itself to its longest option ("Other…") — the wrap
   is content-sized, so a percentage would resolve circularly and the browser
   would fall back to intrinsic width anyway. Long unit names ellipsize
   instead of widening. */
.uom-select {
  /* Native arrow dropped for a 9px caret: the platform control reserves ~20px,
     which at this width truncated real units ("PACK" -> "PA..."). */
  -webkit-appearance: none; appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='9' height='6' viewBox='0 0 9 6'%3E%3Cpath d='M1 1.5 4.5 5 8 1.5' fill='none' stroke='%237A7A72' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 0.25rem center;
  margin-top: 0.2rem; width: 3.5rem; min-width: 0;
  padding: 0.1rem 0.85rem 0.1rem 0.3rem;
  text-overflow: ellipsis;
  font-family: inherit; font-size: 0.625rem; font-weight: 600;
  letter-spacing: 0.04em; text-transform: uppercase; text-align: center;
  color: var(--muted);
  /* background-color, not the `background` shorthand — the shorthand resets
     every longhand it doesn't mention, including the background-image/
     -repeat/-position set above, which is exactly why the custom caret never
     rendered before this fix. */
  background-color: var(--chip);
  border: 1px solid transparent; border-radius: 0.375rem; cursor: pointer;
}
.uom-select.changed { border-color: var(--green); color: var(--green); background-color: var(--green-soft); }
.rcv-uom-other { margin-top: 0.625rem; padding-top: 0.625rem; border-top: 1px solid var(--line); }
.rcv-uom-other-lbl { display: block; font-size: 0.75rem; color: var(--muted); margin-bottom: 0.25rem; }
.rcv-uom-other-row { display: flex; gap: 0.375rem; }
.rcv-uom-input { width: 5.5rem; flex-shrink: 0; }
.rcv-note-input { flex: 1; min-width: 0; }
.rcv-uom-other input {
  height: 2.5rem; padding: 0 0.625rem; font-family: inherit; font-size: 0.875rem;
  color: var(--ink); background: #fff;
  border: 1px solid var(--line); border-radius: 0.5rem;
}
.rcv-uom-other input:focus { outline: 2px solid var(--green); outline-offset: 1px; }
.rcv-uom-hint { font-size: 0.75rem; margin-top: 0.25rem; }

/* Product-photo capture + expiry on a receiving line (lineRow, not the scan
   match card). Sits below .item inside .item-wrap's own padding — same
   separator treatment as .rcv-uom-other above — and is empty (no border/gap)
   once a line has neither a photo action to offer nor an expiry to show. */
.rcv-line-extra:empty { display: none; }
/* Row, not a column: the photo button and the expiry field are two small
   controls that together still fit one handheld line, and stacking them cost
   a whole extra row on EVERY line of the PO — on an 8-line order that was
   half a screen of scrolling between the receiver and the Submit button. */
.rcv-line-extra {
  margin-top: 0.625rem; padding-top: 0.625rem; border-top: 1px solid var(--line);
  display: flex; flex-direction: row; flex-wrap: wrap; align-items: flex-start;
  gap: 0.5rem 0.75rem;
}
/* Photo controls are one column (button above its own thumbnails), so a
   thumbnail can never push the expiry field around, and a tall expiry (label
   + input + OCR hint) can never strand the button in the middle of the card. */
.rcv-line-photo {
  flex: 0 1 auto; min-width: 0; max-width: 55%;
  display: flex; flex-direction: column; align-items: flex-start; gap: 0.5rem;
}
/* Product photos run ACROSS, like the DO/invoice strip — they are siblings of
   one line's capture, not a stack. Without this rule the thumbs inherit
   .rcv-line-extra's column direction and pile up vertically, pushing the next
   line off screen after two shots. Scrolls horizontally past the third. */
.rcv-product-photo-strip {
  display: flex; flex-direction: row; flex-wrap: nowrap; gap: 0.5rem;
  overflow-x: auto; -webkit-overflow-scrolling: touch;
  align-self: stretch; max-width: 100%; padding-bottom: 0.125rem;
}
.rcv-product-photo-strip .rcv-thumb { flex: 0 0 auto; }
.rcv-product-photo-btn {
  display: inline-flex; align-items: center; gap: 0.375rem;
  border: 1px dashed var(--faint); background: transparent; color: var(--muted);
  border-radius: 999px; padding: 0.3rem 0.75rem; font-family: inherit;
  font-size: 0.75rem; font-weight: 600; cursor: pointer;
  transition: border-color .15s, color .15s;
}
.rcv-product-photo-btn:hover { border-color: var(--green); color: var(--green); }
.rcv-product-photo-btn svg { display: block; flex-shrink: 0; }
.rcv-product-photo-count { font-size: 0.75rem; }
/* Takes the row's remaining width beside the photo button, so the input's
   right edge lines up with the card's — a fixed 9rem box left a ragged gap. */
.rcv-expiry-field { flex: 1 1 8rem; min-width: 0; }
.rcv-expiry-field label { display: block; font-size: 0.75rem; color: var(--muted); margin-bottom: 0.25rem; }
.rcv-expiry-input {
  width: 100%; height: 2.25rem; padding: 0 0.625rem; font-family: inherit; font-size: 0.8125rem;
  color: var(--ink); background: #fff; border: 1px solid var(--line); border-radius: 0.5rem;
}
.rcv-expiry-input:focus { outline: 2px solid var(--green); outline-offset: 1px; }
/* The field is a button opening the in-app calendar (not a text input), so it
   left-aligns its value like a filled input and shows a pointer. Empty renders
   the placeholder muted. */
.rcv-expiry-btn { display: flex; align-items: center; text-align: left; cursor: pointer; }
.rcv-expiry-btn.is-empty { color: var(--muted); }
.rcv-expiry-wrap { position: relative; }
.rcv-expiry-wrap.has-value .rcv-expiry-btn { padding-right: 2rem; }
.rcv-expiry-clear {
  position: absolute; top: 0; right: 0; width: 2.25rem; height: 2.25rem;
  display: flex; align-items: center; justify-content: center;
  background: none; border: none; color: var(--muted); font-size: 0.875rem; cursor: pointer;
}
.rcv-expiry-clear:hover { color: var(--ink); }
.rcv-expiry-clear:focus-visible { outline: 2px solid var(--green); outline-offset: -2px; border-radius: 0.5rem; }
/* OCR-filled expiry marker — subtle tint (same amber-soft as the offline
   banner / prior-GRN warning) plus a short hint, not an alarm. Cleared the
   instant the receiver edits the field (see receiver-app.js's input
   handler) — a human-entered value is never a suggestion, so it carries no
   marker at all. */
.rcv-expiry-field.is-ocr .rcv-expiry-input { background: #fef3c7; border-color: #f5d68a; }
/* Ambiguous (both dd/mm and mm/dd readings were valid and differed — the
   country default was a guess, not a deduction) gets the same amber border
   treatment as the DO/invoice page-kind marker (.rcv-thumb-kind.is-ambig),
   so the two "we had to guess" markers in this app read as one language. */
.rcv-expiry-field.is-ambig .rcv-expiry-input { border-color: #f59e0b; box-shadow: inset 0 0 0 1px #f59e0b; }
.rcv-expiry-hint { font-size: 0.6875rem; margin: 0.3rem 0 0; line-height: 1.3; max-width: 11rem; }
/* Below the whole item list once MAX_PRODUCT_PHOTOS is hit (see
   renderReceiving) — plain block text, unlike .rcv-photo-limit above which
   is sized as a flex child of the horizontal-scroll photo strip. */
.rcv-product-photo-limit { font-size: 0.8125rem; margin: 0 0 0.75rem; }

/* ---- DO document scanner (receiver) ---- */
.dscn-overlay {
  position: fixed; inset: 0; z-index: 60; background: #000;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 0.75rem;
}
.dscn-stage { position: relative; width: 100%; max-width: 30rem; flex: 1; min-height: 0; overflow: hidden; }
.dscn-video { width: 100%; height: 100%; object-fit: cover; display: block; }
.dscn-svg { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
.dscn-quad { fill: #ffc978; stroke: #ffc978; stroke-width: 2; stroke-linejoin: round; }
.dscn-quad.good { fill: #8fdc7a; }
/* fill-opacity + stroke-width are driven inline by scanner.js's rAF loop:
   they ramp with the stability count so the tint visibly "charges up" as the
   auto shutter arms. */
.dscn-quad.good { stroke: #8fdc7a; }
.dscn-bracket {
  position: absolute; width: 1.5rem; height: 1.5rem; border: 3px solid rgba(255, 255, 255, 0.5);
  pointer-events: none;
}
.dscn-bracket.tl { top: 8%; left: 8%; border-right: 0; border-bottom: 0; }
.dscn-bracket.tr { top: 8%; right: 8%; border-left: 0; border-bottom: 0; }
.dscn-bracket.bl { bottom: 12%; left: 8%; border-right: 0; border-top: 0; }
.dscn-bracket.br { bottom: 12%; right: 8%; border-left: 0; border-top: 0; }
.dscn-flash { position: absolute; inset: 0; background: #fff; opacity: 0; pointer-events: none; transition: opacity 0.15s; }
.dscn-flash.on { opacity: 0.8; }
.dscn-hint { color: #fff; font-size: 0.95rem; min-height: 1.4em; }
.dscn-hint.good { color: #8fdc7a; }
.dscn-controls {
  /* 1fr|auto|1fr with explicit columns keeps the shutter dead-center even
     while the count badge is [hidden] (display:none removes it from flow). */
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center;
  width: 100%; padding: 0 1rem 1.25rem;
}
.dscn-shutter {
  grid-column: 2;
  width: 4rem; height: 4rem; border-radius: 50%; border: 4px solid #fff;
  background: rgba(255, 255, 255, 0.25);
}
.dscn-shutter:active { background: #fff; }
.dscn-thumbwrap { grid-column: 1; justify-self: center; position: relative; }
/* Captured-page thumbnail beside the shutter (Drive-scan pattern): shows WHAT
   was captured; the count rides its corner as a badge. */
.dscn-thumb {
  display: block; width: 2.75rem; height: 3.5rem; object-fit: cover;
  border-radius: 0.5rem; border: 2px solid #fff; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.45);
}
.dscn-thumb.pop { animation: dscn-pop 0.35s ease-out; }
@keyframes dscn-pop {
  from { transform: scale(1.5); opacity: 0.3; }
  to { transform: scale(1); opacity: 1; }
}
.dscn-count {
  position: absolute; top: -0.5rem; right: -0.6rem;
  min-width: 1.5rem; height: 1.5rem; border-radius: 0.75rem; background: var(--green, #2d5016);
  color: #fff; display: grid; place-items: center; font-weight: 600; font-size: 0.8125rem; padding: 0 0.3rem;
}
.dscn-done {
  grid-column: 3; justify-self: center;
  min-height: 2.75rem; padding: 0 1.1rem; border: 0; border-radius: 1.375rem;
  background: var(--green, #2d5016); color: #fff; font-weight: 600; font-size: 1rem;
}
.dscn-done:active { opacity: 0.85; }
.dscn-close {
  position: absolute; top: max(0.75rem, env(safe-area-inset-top)); right: 0.75rem;
  width: 2.5rem; height: 2.5rem; border-radius: 50%;
  background: rgba(0, 0, 0, 0.5); color: #fff; border: 0; font-size: 1.1rem;
}
.dscn-adjust { justify-content: flex-start; padding-top: max(0.75rem, env(safe-area-inset-top)); }
.dscn-adjust-head { color: #fff; font-weight: 600; padding: 0.5rem 0; }
.dscn-adjust-stage { position: relative; width: 100%; max-width: 30rem; flex: 1; min-height: 0; }
.dscn-adjust-img { width: 100%; height: 100%; object-fit: contain; display: block; }
.dscn-handle {
  position: absolute; width: 2.25rem; height: 2.25rem; margin: -1.125rem 0 0 -1.125rem;
  border-radius: 50%; border: 2px solid #8fdc7a; background: rgba(143, 220, 122, 0.25);
  touch-action: none;
}
.dscn-enhance-row { color: #fff; display: flex; align-items: center; gap: 0.5rem; padding: 0.5rem 0; }
.dscn-adjust-actions { display: flex; gap: 0.75rem; padding-bottom: max(1rem, env(safe-area-inset-bottom)); }
.dscn-btn {
  padding: 0.6rem 1.1rem; border-radius: 0.5rem; border: 1px solid rgba(255, 255, 255, 0.4);
  background: transparent; color: #fff;
}
.dscn-btn.primary { background: var(--green, #2d5016); border-color: var(--green, #2d5016); }
.dscn-btn:disabled { opacity: 0.4; }

/* ---- driver ---- */

/* --- deliveries list: one row per stop ---
   The DO number leads because it is the token printed on the hardcopy the
   driver is holding, and the only thing separating two stops at the same
   customer. The old card led with the debtor and buried the DO mid-sentence
   in the meta line next to a three-line address. */
/* min-width:0 — the card is a grid item (min-width defaults to auto), so
   without it the clipped-address line's min-content width widens the whole
   column and the page scrolls sideways. */
.drv-stop { align-items: flex-start; gap: 0.75rem; padding: 0.875rem 0.9375rem; min-width: 0; }
.drv-stop-n {
  flex-shrink: 0; width: 1.75rem; height: 1.75rem; border-radius: 0.5rem;
  background: var(--green-soft); color: var(--green);
  display: grid; place-items: center; font-size: 0.8125rem; font-weight: 700;
}
.drv-stop .order-main { display: block; min-width: 0; }
.drv-stop-do { display: block; font-size: 1.0625rem; font-weight: 700; letter-spacing: -0.01em; color: var(--ink); }
.drv-stop-debtor { display: block; margin-top: 0.125rem; font-size: 0.875rem; font-weight: 500; color: var(--ink); }
/* One clipped line, not three wrapped ones: the full address belongs to the
   detail screen, this is only here to tell two stops apart at a glance. */
.drv-stop-addr {
  display: block; margin-top: 0.1875rem; font-size: 0.8125rem; color: var(--muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.drv-chev { flex-shrink: 0; align-self: center; color: var(--faint); display: grid; place-items: center; }
.order-card:hover .drv-chev { color: var(--green); }

/* Loading placeholders shaped like the rows they become, so the list settles
   instead of jumping from a centred spinner to left-aligned cards. */
.drv-skel {
  height: 5.25rem; border-radius: 0.875rem; border: 1px solid var(--line); background: #fff;
  position: relative; overflow: hidden;
}
.drv-skel::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, var(--chip), transparent);
  transform: translateX(-100%); animation: drvShimmer 1.4s infinite;
}
@keyframes drvShimmer { to { transform: translateX(100%); } }

/* Empty route — the old version was one grey sentence pinned top-left above
   1400px of nothing, with no hint that the answer was the button at the very
   bottom of the screen. */
.drv-empty { text-align: center; padding: 3.5rem 1rem 1rem; color: var(--muted); }
.drv-empty-ico { color: var(--faint); }
.drv-empty-title { margin: 1rem 0 0.375rem; font-size: 1.0625rem; font-weight: 600; color: var(--ink); }
.drv-empty-sub { margin: 0 auto; max-width: 17rem; font-size: 0.875rem; line-height: 1.5; }

/* --- add DO --- */
/* Scanning leads the screen instead of hiding in a 3.5rem icon-only footer
   button; typing is the labelled fallback under it. */
.drv-scan-big {
  width: 100%; display: flex; align-items: center; justify-content: center; gap: 0.625rem;
  padding: 1.125rem; margin-bottom: 1.25rem;
  border: 1px solid var(--green); border-radius: var(--r-card);
  background: var(--green-soft); color: var(--green);
  font-family: inherit; font-size: 1rem; font-weight: 600; cursor: pointer;
  transition: background .15s, color .15s, transform .1s;
}
.drv-scan-big:hover { background: var(--green); color: #fff; }
.drv-scan-big:active { transform: scale(0.99); }
.drv-or {
  display: flex; align-items: center; gap: 0.75rem;
  margin-bottom: 1.25rem; color: var(--muted); font-size: 0.8125rem;
}
.drv-or::before, .drv-or::after { content: ''; flex: 1; height: 1px; background: var(--line); }
/* Errors belong to the field they describe — this one used to render centred
   inside a .error-box roughly 140px below the input. */
.drv-field-error { margin: 0.5rem 0 0; color: #dc2626; font-size: 0.8125rem; }
.rcv-field input.is-error { border-color: #dc2626; }
.rcv-field input.is-error:focus { border-color: #dc2626; box-shadow: 0 0 0 3px rgba(220, 38, 38, 0.12); }

/* --- delivery detail: the arrival card --- */
.drv-arrival { margin-bottom: 1.25rem; }
.drv-arrival-debtor { font-size: 1.125rem; font-weight: 700; line-height: 1.25; letter-spacing: -0.01em; }
.drv-arrival-addr { margin-top: 0.5rem; font-size: 0.9375rem; line-height: 1.45; color: var(--muted); }
.drv-arrival-contact { margin-top: 0.5rem; font-size: 0.875rem; font-weight: 500; color: var(--ink); }
/* Order remark — arrival context (loading bay, floor, ATTN person), so it sits
   with the address and above the tools that act on it. Clamped to 3 lines; the
   More control is added by driver-app.js only when the text actually overflows,
   so the ~95% short case has no tap target at all. */
.drv-remark {
  display: flex; gap: 0.5rem; margin: 0.875rem 0 1.25rem; padding: 0.625rem 0.75rem;
  border-radius: 10px; border-left: 3px solid #2D5016;
  background: rgba(45, 80, 22, 0.06); color: var(--ink);
}
.drv-remark-ico { flex: none; margin-top: 0.0625rem; color: #2D5016; }
.drv-remark-body { min-width: 0; }
.drv-remark-text {
  font-size: 0.9375rem; line-height: 1.45; white-space: pre-wrap; overflow-wrap: anywhere;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; overflow: hidden;
}
.drv-remark-text.is-open { -webkit-line-clamp: unset; overflow: visible; }
.drv-remark-more {
  margin-top: 0.25rem; padding: 0; border: none; background: none;
  font-family: inherit; font-size: 0.8125rem; font-weight: 600;
  color: #2D5016; text-decoration: underline; cursor: pointer;
}
/* Navigate is the first thing that happens on this screen, so it takes primary
   weight here; the footer is reserved for finishing the stop. */
.drv-nav-btn { display: flex; align-items: center; justify-content: center; gap: 0.5rem; margin-bottom: 0.75rem; }
.drv-side-actions { display: flex; gap: 0.625rem; }
.drv-side-actions .btn-secondary { flex: 1; }
/* Can't-deliver is the exception path, not a peer of Complete delivery.
   .btn-ghost assumes .btn's reset alongside it; used bare it keeps the UA
   button border, so the reset is repeated here. */
.drv-fail-link {
  width: 100%; border: none; background: none; box-shadow: none;
  padding: 0.75rem; font-family: inherit; font-size: 0.875rem; font-weight: 500;
  color: var(--muted); cursor: pointer;
}
.drv-fail-link:hover { color: #dc2626; }

/* Can't-deliver reason picker — same selectable-card grid as .branch-grid
   (team/company pick), just two columns since there are exactly four reasons. */
.drv-reason-grid { grid-template-columns: 1fr 1fr; margin-bottom: 1rem; }
/* Outline + tint rather than .branch-card's solid green fill: a chosen reason
   is a radio answer, and filled it outranked the actual Report button. */
.drv-reason-grid .branch-card.selected {
  background: var(--green-soft); color: var(--ink);
  border-color: var(--green); box-shadow: inset 0 0 0 1px var(--green);
}

/* Note field on the can't-deliver screen — .rcv-field styles the label, this
   is the one control shape .rcv-field's `input` rule doesn't cover. */
.drv-note-field label { display: flex; align-items: baseline; justify-content: space-between; gap: 0.75rem; }
.drv-counter { font-size: 0.75rem; font-weight: 400; color: var(--faint); }
.drv-textarea {
  /* resize:vertical left a drag-handle artefact in the corner of the field on
     a touch screen that cannot drag it. */
  width: 100%; box-sizing: border-box; min-height: 5rem; resize: none;
  font-family: inherit; font-size: 0.9375rem; padding: 0.625rem 0.875rem;
  border: 1px solid var(--line); border-radius: 0.75rem; background: #fff; color: var(--ink);
}
.drv-textarea::placeholder { color: var(--muted); }
.drv-textarea:focus { outline: none; border-color: var(--green); box-shadow: 0 0 0 3px var(--green-soft); }

/* --- gates ---
   A disabled primary button must always be accompanied by the reason it is
   disabled. Sits in the .sticky column directly above the button it explains. */
.drv-gate-hint { margin: 0; text-align: center; font-size: 0.8125rem; color: var(--muted); }
/* Per-section requirement chip on the POD headings — "1 required" until met,
   then a tick, so the two near-identical photo strips read differently. */
.drv-req { margin-left: 0.5rem; font-weight: 500; letter-spacing: 0; text-transform: none; color: var(--faint); }
.drv-req.is-done { color: var(--green); font-weight: 700; }

/* Content used to start flush against the topbar's hairline — the company
   context line in particular read as part of the header. Driver-scoped so the
   picker and receiver shells keep their current rhythm. */
#drv-main { display: block; padding-top: 0.875rem; }

/* Done screen (POD submitted) — mirrors the receiver's .done-box. */
.done-ref { margin: 0.75rem 0 0.25rem; font-size: 1.125rem; font-weight: 700; }
.done-sub { margin-top: 0.75rem; }

/* Company search, shown only once the list outgrows a screenful. */
.drv-company-search { margin-bottom: 0.875rem; }

/* Bottom snackbar for one-off confirmations (takeover-declined errors, "Reported",
   "Delivered") shared by all three apps — driver's original use, plus picker's
   and receiver's wedge-scan feedback — that don't warrant a full screen or the
   persistent .error-box. */
.app-toast {
  position: fixed; left: 50%; bottom: 1.5rem; transform: translateX(-50%) translateY(0.5rem);
  max-width: calc(100% - 3rem); background: var(--ink); color: #fff;
  padding: 0.75rem 1.125rem; border-radius: 0.75rem; font-size: 0.875rem; text-align: center;
  box-shadow: 0 10px 24px -10px rgba(0, 0, 0, 0.5); z-index: 90;
  opacity: 0; transition: opacity .15s, transform .15s;
}
.app-toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }

/* Blocking modals — GPS consent, forced-update gate, one-time phone prompt,
   and (all three apps, .app-modal) the long-press sign-out confirm.
   Centered white card over a dim backdrop, reusing the app's card/button
   vocabulary (.field/.btn-primary/.btn-secondary) for their contents. */
/* Print format sheet (shell only, static/print-open.js): two equal buttons. */
.print-fmt-actions button { flex: 1; }
/* 110: above the camera overlay and scan sheet (100) — a confirm raised while
   scanning ("Already delivered — add anyway?") must never hide behind them. */
.drv-modal, .app-modal {
  position: fixed; inset: 0; z-index: 110; background: rgba(26, 26, 26, 0.55);
  display: flex; align-items: center; justify-content: center; padding: 1.5rem;
}
.drv-modal-card, .app-modal-card {
  width: 100%; max-width: 24rem; background: #fff; border-radius: var(--r-card);
  padding: 1.5rem; box-shadow: 0 20px 40px -12px rgba(0, 0, 0, 0.35);
  max-height: calc(100dvh - 3rem);
  overflow-y: auto;
}
.drv-modal-card h2, .app-modal-card h2 { margin: 0 0 0.5rem; font-size: 1.125rem; }
.drv-modal-card p, .app-modal-card p { margin: 0 0 1rem; line-height: 1.5; }
.drv-modal-card .field { margin-bottom: 1rem; }
.drv-modal-card input {
  width: 100%; box-sizing: border-box; height: 3rem; font-size: 1rem; font-family: inherit;
  padding: 0 0.875rem; border: 1px solid var(--line); border-radius: 0.75rem;
  background: #fff; color: var(--ink);
}
.drv-modal-card input:focus { outline: none; border-color: var(--green); box-shadow: 0 0 0 3px var(--green-soft); }
.drv-modal-actions, .app-modal-actions { display: flex; gap: 0.625rem; }
.drv-modal-actions .btn-primary, .drv-modal-actions .btn-secondary,
.app-modal-actions .btn-primary, .app-modal-actions .btn-secondary { width: auto; flex: 1; padding: 0.875rem; }

/* Driver detail: attempt history chip + read-only load list */
.drv-attempt-chip { margin: 0 0 12px; padding: 8px 12px; border-radius: 10px; font-size: 13px;
  background: #fff7e6; color: #8a5a00; }
.drv-items { margin: 4px 0 90px; }
.drv-items-head { font-size: 12px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
  color: #8a8f86; margin-bottom: 6px; }
.drv-line { display: flex; justify-content: space-between; gap: 12px; padding: 10px 2px;
  border-bottom: 1px solid rgba(45, 80, 22, 0.1); font-size: 15px; }
.drv-line-name { min-width: 0; }
.drv-line-qty { white-space: nowrap; font-weight: 600; color: #2D5016; }

/* Detail utility row: Navigate/Call sit side by side, outlined, icon inline. */
.drv-nav-btn { display: inline-flex; align-items: center; justify-content: center; gap: 8px; }

/* Navigate-with sheet (iOS/browser — Android uses the system geo: chooser) */
.drv-nav-sheet { position: absolute; left: 0; right: 0; bottom: 0; background: #FDFCFB;
  border-radius: 16px 16px 0 0; padding: 16px 16px calc(16px + env(safe-area-inset-bottom));
  display: flex; flex-direction: column; gap: 10px; }
.drv-nav-sheet-title { font-size: 13px; font-weight: 700; letter-spacing: 0.05em;
  text-transform: uppercase; color: #8a8f86; margin-bottom: 2px; }

/* Driver permission gate — blocks adding work until tracking can survive.
   Amber banner reuses .rcv-banner; the denied banner reuses .error-box. */
.drv-perm-card { border: 1px solid var(--line); border-radius: 0.75rem; background: #fff;
  padding: 0.875rem 1rem 0.25rem; margin: 0 0 1rem; }
.drv-perm-title { font-weight: 700; font-size: 0.9375rem; margin: 0 0 0.25rem; }
.drv-perm-row { display: flex; align-items: center; gap: 0.625rem; min-height: 48px;
  padding: 0.25rem 0; }
.drv-perm-row + .drv-perm-row { border-top: 1px solid var(--line); }
.drv-perm-label { flex: 1; font-size: 0.9375rem; }
.drv-perm-ok { color: var(--green); font-weight: 700; font-size: 1.125rem; }
.drv-perm-fix { min-height: 40px; padding: 0.375rem 1rem; }
.drv-gps-banner { margin-bottom: 1rem; }
.drv-gps-banner .btn-secondary { margin-top: 0.5rem; min-height: 40px; }

/* Photo-strip placeholder while a capture decodes/encodes (seconds on a
   low-end WebView) — instant feedback that the shot was received. */
.rcv-thumb-pending { display: flex; align-items: center; justify-content: center; }
.rcv-thumb-pending::after { content: ''; width: 22px; height: 22px; border: 3px solid var(--line);
  border-top-color: var(--green); border-radius: 50%; animation: drv-spin 0.8s linear infinite; }
@keyframes drv-spin { to { transform: rotate(360deg); } }

/* Full-screen photo viewer (receiver openLightbox) — tap anywhere closes. */
.rcv-lightbox { z-index: 101; display: flex; align-items: center; justify-content: center; background: rgba(0, 0, 0, 0.92); }
.rcv-lightbox img { max-width: 100vw; max-height: 100vh; width: auto; height: auto; object-fit: contain; display: block; }
.rcv-lightbox-close {
  position: absolute; top: calc(0.75rem + env(safe-area-inset-top)); right: 0.75rem;
  width: 2.5rem; height: 2.5rem; border-radius: 999px; border: none;
  background: rgba(255,255,255,0.18); color: #fff; font-size: 1.125rem; line-height: 1;
  cursor: pointer; display: grid; place-items: center;
}

/* Scanner shutter while the warp/enhance runs — pressed + inert. */
.dscn-shutter.busy { opacity: 0.45; transform: scale(0.9); pointer-events: none; }

/* ---------- consolidated picking (spec 2026-09-22) ---------- */
.cs-divider { border: 0; border-top: 1px solid var(--line); margin: 1.5rem 0 1rem; }
.cs-heading { font-size: 0.8125rem; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: 0.07em; margin: 0 0 0.625rem; }
.cs-entrance-btn { min-height: 3rem; }
/* Chip — the .mark-all vocabulary with an active (filled) state. */
.cs-chip {
  flex-shrink: 0; background: var(--green-soft); color: var(--green); border: none;
  border-radius: 0.5rem; padding: 0.55rem 0.8rem; min-height: 2.75rem; font-size: 0.8125rem; font-weight: 600;
  cursor: pointer; font-family: inherit; transition: background .12s, color .12s, transform .08s;
}
.cs-chip.on { background: var(--green); color: #fff; }
.cs-chip:active { transform: scale(0.97); }
.cs-status-row { display: flex; gap: 0.5rem; padding: 0 0 0.75rem; flex-wrap: wrap; }
.cs-status-row .cs-select-all { margin-left: auto; background: var(--chip); color: var(--ink); }
/* Select mode: the whole card is the target; the circle only shows state. */
.order-card .pick-check { pointer-events: none; }
.order-card.picked { border-color: var(--green); background: var(--green-soft); }
.cs-select-footer { display: flex; flex-direction: column; gap: 0.5rem; }
.cs-select-note { font-size: 0.8125rem; min-height: 1.2em; text-align: center; }
.cs-select-footer .btn-secondary { flex: 0 0 auto; width: auto; padding-left: 1.25rem; padding-right: 1.25rem; }
/* Consol screen */
.cs-orders-btn { padding-left: 0; font-size: 0.9375rem; }
.cs-head-left { display: flex; align-items: center; gap: 0.125rem; flex-shrink: 0; }
.cs-head-left .cs-orders-btn { white-space: nowrap; }
/* Reload the batch's orders: a quiet round icon next to the order count,
   44px target, spins while chunks are still arriving. */
.cs-refresh {
  display: inline-flex; align-items: center; justify-content: center;
  width: 2.5rem; height: 2.5rem; flex-shrink: 0; padding: 0;
  background: none; border: none; border-radius: 999px; color: var(--muted); cursor: pointer;
}
.cs-refresh:active { background: rgba(0, 0, 0, 0.06); }
.cs-refresh svg { width: 1.125rem; height: 1.125rem; }
.cs-refresh[disabled] { cursor: default; }
.cs-refresh.spinning svg { animation: cs-spin 0.9s linear infinite; }
@keyframes cs-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .cs-refresh.spinning svg { animation: none; opacity: 0.5; } }
.cs-loading { margin: 0 0 0.75rem; font-size: 0.8125rem; }
.cs-progress { height: 4px; background: var(--chip); border-radius: 2px; margin-top: 0.375rem; overflow: hidden; }
.cs-progress-bar { height: 100%; background: var(--green); transition: width .2s; }
.cs-banner {
  background: var(--chip); color: var(--ink); border-radius: var(--r-tile); padding: 0.625rem 0.875rem;
  font-size: 0.8125rem; margin: 0 0 0.625rem; display: flex; justify-content: space-between; align-items: center; gap: 0.5rem;
}
.cs-banner-warn { background: #fef3c7; color: #92400e; }
.cs-hide-row { display: flex; justify-content: flex-end; margin: 0 0 0.625rem; }
.cs-parts-toggle {
  background: none; border: none; padding: 0.25rem 0; margin-top: 0.15rem; color: var(--green);
  font: inherit; font-size: 0.75rem; font-weight: 600; cursor: pointer; text-align: left;
}
.cs-parts { border-top: 1px solid var(--line); margin-top: 0.625rem; padding-top: 0.375rem; }
.cs-part { display: flex; align-items: center; gap: 0.5rem; padding: 0.35rem 0; font-size: 0.8125rem; }
.cs-part-who { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.cs-part-ref { flex-shrink: 0; }
.cs-part-qty { flex-shrink: 0; min-width: 2.5rem; text-align: right; font-weight: 600; }
/* Line notes: placed qty (muted) and the order line's remark (amber, it is
   an instruction the picker must act on). */
.cs-placed { font-size: 0.8125rem; margin-top: 0.25rem; }
/* Running weight from scale labels on the consolidated list (row not yet complete). */
.cs-weighed { font-size: 0.8125rem; margin-top: 0.25rem; font-weight: 600; color: var(--green); }
.cs-remark {
  margin-top: 0.375rem; padding: 0.3rem 0.5rem; border-radius: 0.375rem;
  background: #fff7e6; color: #7a4b00; font-size: 0.8125rem; line-height: 1.35; overflow-wrap: anywhere;
}
.cs-remark-who { font-weight: 600; }
.cs-part-note { font-size: 0.75rem; margin: -0.2rem 0 0.25rem; text-align: right; }
.cs-clear { text-align: center; padding: 1rem 0 5.5rem; }
.cs-empty { display: flex; flex-direction: column; gap: 0.75rem; padding-top: 1rem; }
.cs-empty .intro { text-align: center; }
.cs-empty .btn-secondary { min-height: 3.25rem; }
/* Orders sheet (bottom sheet in the .scan-sheet backdrop) */
.scan-sheet .cs-sheet {
  position: absolute; left: 0; right: 0; bottom: 0; max-width: 28rem; margin: 0 auto;
  border-radius: var(--r-card) var(--r-card) 0 0; max-height: 85vh; display: flex; flex-direction: column;
  padding-bottom: calc(1rem + env(safe-area-inset-bottom));
}
.cs-sheet-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.75rem; }
.cs-sheet-head h2 { margin: 0; font-size: 1.0625rem; }
.cs-doc-label { display: block; font-size: 0.75rem; font-weight: 600; color: var(--muted); margin-bottom: 0.25rem; }
.cs-doc-row { display: flex; gap: 0.5rem; margin-bottom: 0.75rem; }
.cs-doc-row .search-box { flex: 1; min-width: 0; }
.cs-doc-row .btn-primary { width: auto; padding: 0 1.125rem; }
.cs-sheet-list { overflow-y: auto; -webkit-overflow-scrolling: touch; }
.cs-sheet-row { display: flex; align-items: center; gap: 0.5rem; padding: 0.625rem 0; border-top: 1px solid var(--line); }
.cs-sheet-main { flex: 1; min-width: 0; }
.cs-sheet-who { font-weight: 600; overflow-wrap: anywhere; }
.cs-sheet-ref, .cs-sheet-skip { font-size: 0.75rem; }
.cs-sheet-remove { flex-shrink: 0; }

/* ---------- consolidated picking: dogfood pass (2026-09-24) ---------- */
/* Scan is the primary action; Done sits beside it at its natural width. */
.cs-bar .cs-scan-btn { flex: 1; }
.cs-scan-btn svg, .cs-sheet-scan svg { flex-shrink: 0; }
.cs-bar .cs-done-btn { flex: 0 0 auto; width: auto; padding-left: 1.5rem; padding-right: 1.5rem; }
.cs-chip[disabled] { opacity: 0.45; cursor: default; }
/* Code · debtor on one line; the "n orders" toggle is inline text. */
.cs-row-meta { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 0.25rem; }
.cs-row-meta .cs-parts-toggle { margin-top: 0; padding: 0.35rem 0; }
/* Orders sheet: scan + choose-from-list above the typed field. */
.cs-sheet-actions { display: flex; flex-direction: column; gap: 0.5rem; margin-bottom: 0.875rem; }
.cs-sheet-actions .btn-primary, .cs-sheet-actions .btn-secondary { min-height: 3rem; padding-top: 0.75rem; padding-bottom: 0.75rem; }
/* Head-row buttons share one height: the chip (no border) and the bordered
   print button used to differ by their padding and min-height. */
.items-head .cs-hide, .items-head .cs-print {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.3rem;
  box-sizing: border-box; height: 2.5rem; min-height: 0; padding: 0 0.75rem; line-height: 1;
}
.items-head .items-actions { flex-wrap: wrap; justify-content: flex-end; }
