/* SBU Order Portal — visual system ported from the previous Campus Supply app
   (sbunew.html), so the portal reads as familiar to people who used that one:
   sand background, deep-green header, rounded white cards with a soft shadow,
   coral primary action, uppercase micro-labels, sticky table headers. */

:root{
  color-scheme:light;
  --ink:#10201e;--muted:#64748b;--line:#dce3e1;--paper:#fff;--bg:#f4f3ed;
  --green:#153f3a;--green-2:#0f766e;--mint:#dff5ef;--coral:#e46f4c;
  --amber:#f2b56b;--danger:#be123c;
  --radius:18px;--shadow:0 18px 55px rgba(15,35,32,.09);
}

*{box-sizing:border-box}
html{background:var(--bg);-webkit-text-size-adjust:100%}
body{margin:0;min-width:320px;background:var(--bg);color:var(--ink);
  font:15px/1.45 Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}
button,input,select,textarea{font:inherit}
button{cursor:pointer}
button:disabled{cursor:not-allowed;opacity:.55}
input,select{font-size:16px}
:focus-visible{outline:3px solid rgba(15,118,110,.25);outline-offset:2px}
h1,h2,h3,p{margin-top:0}
h2{margin-bottom:6px;font-size:22px;letter-spacing:-.01em}
h3{margin-bottom:6px;font-size:17px}

.hidden{display:none!important}
.muted{margin-bottom:0;color:var(--muted);font-size:13px}
.error{min-height:18px;margin:0;color:var(--danger);font-size:12px;font-weight:800}
.eyebrow{margin:0 0 4px;color:var(--green-2);font-size:11px;font-weight:900;
  letter-spacing:.18em;text-transform:uppercase}

/* ---------- buttons ---------- */
.btn{min-height:44px;border:0;border-radius:12px;padding:10px 16px;background:#edf2f1;
  color:var(--ink);font-weight:800;transition:background .15s,transform .15s,box-shadow .15s}
.btn:hover{background:#e1e9e7}
.btn:active{transform:translateY(1px)}
.btn-primary{background:var(--coral);color:#fff;box-shadow:0 9px 20px rgba(228,111,76,.24)}
.btn-primary:hover{background:#d75f3d}
.btn-dark{background:var(--green);color:#fff}
.btn-dark:hover{background:#0d312d}
.btn-outline{border:1px solid var(--green-2);background:#fff;color:#115e59}
.btn-outline:hover{background:#effcf8}
.btn-ghost{background:transparent;color:var(--muted)}
.btn-ghost:hover{background:#edf2f1}
.btn-block{width:100%}
.btn-small{min-height:34px;padding:6px 10px;border-radius:9px;font-size:12px}

/* ---------- form controls ---------- */
.field{display:block;min-width:0;margin-bottom:14px}
.label{display:block;margin-bottom:7px;color:#334155;font-size:11px;font-weight:900;
  letter-spacing:.07em;text-transform:uppercase}
.control{width:100%;min-height:46px;border:1px solid #cbd5e1;border-radius:12px;background:#fff;
  padding:10px 12px;color:#0f172a;transition:border-color .15s,box-shadow .15s}
.control:focus{border-color:var(--green-2);outline:0;box-shadow:0 0 0 4px rgba(13,148,136,.1)}
.control:disabled{background:#f1f5f9;color:#94a3b8}

/* ---------- sign-in ---------- */
.login-wrap{min-height:100dvh;display:grid;place-items:center;padding:16px}
.login-card{width:min(420px,100%);padding:30px;border:1px solid rgba(203,213,225,.75);
  border-radius:24px;background:var(--paper);box-shadow:var(--shadow)}
.login-card h1{margin-bottom:4px;font-size:26px;letter-spacing:-.02em}

/* ---------- app chrome ---------- */
.app-header{display:flex;align-items:center;justify-content:space-between;gap:16px;
  min-height:66px;padding:12px 24px;background:var(--green);color:#fff;
  border-bottom:1px solid rgba(255,255,255,.1)}
.brand{font-size:15px;font-weight:900;letter-spacing:.08em}
.header-right{display:flex;align-items:center;gap:10px}
.header-right .btn-ghost{color:rgba(255,255,255,.82)}
.header-right .btn-ghost:hover{background:rgba(255,255,255,.12);color:#fff}
.pill{padding:5px 11px;border-radius:999px;background:rgba(255,255,255,.1);
  color:#d6fff1;font-size:11px;font-weight:900}

/* Wraps rather than scrolling horizontally, at EVERY width. `overflow-x:auto` hid
   tabs off the right edge with nothing to indicate there were more: three of five
   store tabs at 360px (including "My orders", so a store could not reach its own
   order history on a phone), and even at 1280px the manufacturer's ninth tab,
   "Stores", was clipped by 28px. A wrapped strip is always fully visible. */
.tabs{position:sticky;z-index:3;top:0;display:flex;flex-wrap:wrap;gap:8px;
  padding:13px 24px 10px;background:#eef1ed;border-bottom:1px solid var(--line)}
.tab{min-width:max-content;min-height:42px;border:1px solid var(--line);border-radius:11px;
  padding:9px 17px;background:#fff;color:#64748b;font-weight:900}
.tab:hover{background:#f8faf9}
.tab.active{border-color:var(--green);background:var(--green);color:#fff}

.shell{width:min(1240px,100%);margin:auto;padding:22px 24px 34px}

/* ---------- cards ---------- */
.card,.admin-card{padding:22px;border:1px solid var(--line);border-radius:20px;
  background:var(--paper);margin-bottom:16px}
.card h2,.admin-card h2{margin-top:0}

/* A dispatch-board variant with nothing outstanding. De-emphasised so the ones
   needing attention stand out, but never hidden and never disabled — every variant
   stays dispatchable, because over-dispatch is deliberate here. Text contrast is
   left alone; only the card's own background and border recede. */
.card-quiet{background:#fbfcfc;border-style:dashed}

/* ---------- filter rows ---------- */
.orders-filters{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:12px}
.orders-filters .field{margin-bottom:0}

/* ---------- size grids (order + dispatch) ---------- */
.size-grid{overflow-x:auto;-webkit-overflow-scrolling:touch;padding-bottom:4px}
.size-grid table{border-collapse:collapse;min-width:100%}
.size-grid th,.size-grid td{padding:7px 6px;border-bottom:1px solid #edf1f0;text-align:center;
  white-space:nowrap}
.size-grid th{background:#f1f5f9;color:#64748b;font-size:10px;font-weight:900;
  letter-spacing:.05em;text-transform:uppercase}
.size-grid th:first-child,.size-grid td:first-child{position:sticky;left:0;z-index:1;
  text-align:left;background:var(--paper);font-weight:800}
.size-grid th:first-child{background:#f1f5f9;z-index:2}
.size-input{width:66px;min-height:40px;text-align:center;border:1px solid #cbd5e1;
  border-radius:9px;font-weight:900;padding:6px}
.size-input:focus{border-color:var(--green-2);outline:0;box-shadow:0 0 0 3px rgba(13,148,136,.12)}
/* Over-dispatch is allowed, never blocked — only flagged, so surplus is deliberate. */
.size-input.over-dispatch{border-color:var(--amber);background:#fffbeb}
/* A balance cell that has gone negative: more was sent than was ordered. Flagged and
   captioned ("over-dispatched by 7") so a surplus is distinguishable from a mistake.
   Never clamped — the figure itself stays negative. */
td.over-dispatched{background:#fffbeb;color:#92400e;font-weight:900}
.ctx{display:block;margin-top:3px;color:var(--muted);font-size:10px;font-weight:700}

/* ---------- data tables ---------- */
table.data{width:100%;border-collapse:collapse;text-align:left;font-size:13px}
table.data th{position:sticky;top:0;z-index:1;background:#f1f5f9;color:#64748b;
  font-size:10px;font-weight:900;letter-spacing:.05em;text-transform:uppercase}
table.data th,table.data td{padding:9px 10px;border-bottom:1px solid #edf1f0}
table.data tbody tr:hover{background:#f8faf9}
table.data tbody tr[data-jump]{cursor:pointer}

/* ---------- dated ledgers ---------- */
.day-head{display:flex;align-items:baseline;justify-content:space-between;gap:12px;
  margin-bottom:10px;padding-bottom:8px;border-bottom:1px solid var(--line)}
.day-head span{color:var(--muted);font-size:12px;font-weight:800}

/* ---------- production sheet ---------- */
.sheet-scroll{overflow:auto;max-height:70vh}
table.sheet{border-collapse:collapse;font-size:12px;white-space:nowrap}
table.sheet td{padding:5px 9px;border-bottom:1px solid #edf1f0}
table.sheet tr.row-summary td{background:#f8faf9;font-weight:800}
table.sheet tr.row-balance td{background:var(--mint);font-weight:900}
table.sheet td:first-child{position:sticky;left:0;background:var(--paper);font-weight:700}
table.sheet tr.row-summary td:first-child{background:#f8faf9}
table.sheet tr.row-balance td:first-child{background:var(--mint)}

/* ---------- status + emphasis ---------- */
.tag{display:inline-block;padding:4px 8px;border-radius:999px;background:#e2e8f0;
  color:#64748b;font-size:10px;font-weight:900}
.tag.active{background:#d1fae5;color:#047857}
/* Order status on the store's "My orders" screen. Derived from the quantities in
   js/calc.js (orderProgress), never stored — an append-only table must not carry a
   status column that can disagree with its own rows. */
.tag.tag-done{background:#d1fae5;color:#047857}
.tag.tag-part{background:#fffbeb;color:#92400e}
.tag.tag-wait{background:#e2e8f0;color:#475569}
.trend-down{color:var(--danger);font-weight:900}
.trend-up{color:#047857;font-weight:900}

/* ---------- stock levels (Record sale, Inventory, Retailer inventory) ---------- */
.stock-ok{font-weight:800}
.stock-amber{background:#fffbeb;color:#92400e;font-weight:900}
.stock-red{background:#fff1f2;color:var(--danger);font-weight:900}
/* A size the store has never carried: no dispatch and no sale, ever. Not "out of
   stock" — not applicable. Muted so the red on the sizes that ARE stocked still
   reads as a signal rather than as the background colour of the whole screen. */
.stock-none{color:#94a3b8;font-weight:400}

/* ---------- editable stock figures + the change log ---------- */
/* The Counted row on Inventory / Retailer inventory. Green like every other row in
   this app that the user types into (the Orders grid's Dispatch-now row uses the same
   colour on its label), so it reads as "this one is yours to fill in" rather than as
   another read-only figure. */
.adj-row-input td:first-child{color:var(--green-2)}
/* The Counted row with no single store to write to. The boxes STAY -- greyed, dead,
   but on screen -- because the previous version removed them altogether and explained
   their absence in a paragraph above the grids, and nobody ever found the control.
   What is missing is never discoverable; what is visibly switched off is. */
.adj-row-locked td:first-child{color:var(--muted)}
.adj-row-locked .size-input{background:#f1f5f9;border-style:dashed;color:#94a3b8;
  cursor:not-allowed}
/* The line under the boxes, inside the same grid, saying what the row is for -- or,
   when it is locked, how to switch it on. It sits with the control rather than in the
   intro paragraph, which is the whole point. Left-aligned and wrapping, unlike every
   other cell in a size grid, because it is a sentence and not a figure. */
.adj-row-hint td{border-bottom:0;padding-top:0}
td.adj-hint{text-align:left;white-space:normal;color:var(--muted);font-size:12px;
  font-weight:700}
/* The cap that stops a hint from widening the grid it lives in. A table cell in auto
   layout is sized to its content, so a long sentence in a colspan cell drags the whole
   size grid wider and pushes the figures off the right of a phone -- the grid scrolls,
   but you would have to scroll it to read the numbers. An inline-block child's
   max-width clamps the cell's max-content width, so the sentence wraps instead. */
td.adj-hint span{display:inline-block;max-width:52ch}
.adj-row-locked+.adj-row-hint td.adj-hint{color:#92400e}
/* "4 -> 6". The pair, never a delta -- the arrow is what makes it a sentence. */
.log-change{white-space:nowrap;font-weight:900}
/* Free text somebody typed. It wraps rather than stretching the table off-screen, and
   it is the one column here that is allowed to be long. */
.log-note{white-space:normal;min-width:180px;color:var(--muted);font-weight:600}
/* The log's eight columns do not fit a phone. They scroll inside this box rather than
   pushing the whole page -- and the tab strip with it -- off to the right. */
.log-scroll{overflow-x:auto;-webkit-overflow-scrolling:touch}

/* ---------- special-orders board ---------- */
.production-insights{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));
  gap:14px;margin-bottom:16px}
.production-insight{min-width:0}
.production-insight h3{margin:0 0 12px;font-size:16px}
.special-row{min-width:0;padding:11px 0;border-bottom:1px solid #edf1f0}
.special-row:last-child{border-bottom:0}
.special-row strong{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.special-row small{display:block;margin-top:2px;color:var(--muted);font-size:11px}
.special-row select{margin-top:7px}
.special-row .btn-small{margin-top:7px;margin-right:4px}

/* ---------- toast ---------- */
.toast{position:fixed;z-index:80;left:50%;bottom:max(18px,env(safe-area-inset-bottom));
  width:min(420px,calc(100% - 28px));transform:translateX(-50%);padding:13px 15px;
  border-radius:14px;background:#0f172a;color:#fff;font-size:13px;font-weight:800;
  box-shadow:0 18px 45px rgba(15,23,42,.28)}
.toast.success{background:#065f46}
.toast.error{background:var(--danger)}

/* ---------- responsive ---------- */
@media(max-width:700px){
  .app-header{padding:10px 14px}
  /* Wrapping is in the base rule now. What is narrow-only is dropping sticky: nine
     manufacturer tabs wrap to four rows here (261px measured at 360px), and pinning
     a third of a phone screen to the top costs more than the scroll it saves. */
  .tabs{position:static;padding:10px 14px 8px}
  .shell{padding:14px}
  .card,.admin-card{padding:17px;border-radius:16px}
  .orders-filters{grid-template-columns:1fr}
  .production-insights{grid-template-columns:1fr}
  .login-card{padding:22px}
}
@media(prefers-reduced-motion:reduce){*{transition:none!important;scroll-behavior:auto!important}}

/* ---------- combined Orders grid (manufacturer landing screen) ---------- */
/* Modelled on the Retailer inventory cards above -- same card-per-variant shape and
   the same .size-grid table -- with three read-only rows (Ordered / Dispatched /
   Pending) and one editable Dispatch-now row per variant. The extra classes here are
   all about LABELLING the numbers: a bare grid of digits is a support call. */
.og-head{display:flex;align-items:center;justify-content:space-between;gap:12px;
  margin-bottom:2px}
.og-head strong{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* The plain-language line under each variant name: "40 ordered · 35 dispatched ·
   5 pending". It is the sentence the three rows below it add up to. */
.og-counts{margin:0 0 10px;color:var(--muted);font-size:12px;font-weight:700}
.og-counts strong{color:#92400e}
.og-headline{margin:0 0 6px;font-size:15px;font-weight:800}
.og-headline strong{color:#92400e}
.og-group{margin:22px 0 4px;padding-top:4px;font-size:15px;letter-spacing:.04em;
  text-transform:uppercase}
.og-group-counts{margin:0 0 10px;font-size:12px;font-weight:700}
.og-note{margin-top:12px}
/* A size still owed. Amber like everything else in this app that means "outstanding",
   deliberately NOT red -- red here is the store's out-of-stock signal, and an order
   still working its way through production is not an error. */
td.og-owed{background:#fffbeb;color:#92400e;font-weight:900}
/* Ordered and fully dispatched: settled. Kept as a digit rather than a tick so the
   row still adds up to the Total column beside it. */
td.og-clear{color:#047857;font-weight:800}
.og-row-pending td:first-child{color:#92400e}
.og-row-input td:first-child{color:var(--green-2)}
/* All stores: a dispatch has no single recipient, so the input row is replaced by
   the reason rather than silently writing to whichever store happened to be first. */
td.og-locked{color:var(--muted);font-size:12px;font-weight:700;text-align:left;
  white-space:normal}
.og-foot{position:sticky;bottom:0;z-index:2;box-shadow:var(--shadow)}
.og-foot p{margin-top:10px}
@media(max-width:700px){
  /* A sticky save bar costs a third of a phone screen for the whole scroll; the
     tab strip already gives up sticky at this width for the same reason. */
  .og-foot{position:static;box-shadow:none}
  .og-head{flex-wrap:wrap}
}

/* ---------- stores, their people, and the three ways to remove one ---------- */
.su-store .su-people{margin-top:14px}
.su-person{padding:14px;border:1px solid var(--line);border-radius:14px;margin-bottom:12px}
/* A login that has been disabled still has to be readable -- it is how you find the
   person to re-enable -- so it is dimmed, never hidden. */
.su-person-off{background:#fbfcfc;opacity:.72}
.su-perms{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));
  gap:6px;margin-bottom:12px}
.su-perm{display:flex;align-items:center;gap:8px;font-size:13px;font-weight:700}
.su-perm input{width:18px;height:18px}
.su-reset{margin-top:12px;margin-bottom:8px;max-width:280px}
.su-remove{margin-top:18px;padding-top:14px;border-top:1px solid var(--line)}
.su-remove h3{margin:0 0 6px;font-size:15px}
.su-remove .field{max-width:340px}

/* THE ONLY IRREVERSIBLE ACTION IN THIS APPLICATION.
   Two similar buttons side by side is how a mis-click happens, so this one does not
   look like the Remove button above it: its own box, its own colour, its own heading,
   folded shut until it is deliberately opened, and a button that cannot be pressed at
   all until the phrase has been typed. */
.su-danger{margin-top:16px;padding:16px;border:1px solid #fecdd3;border-radius:14px;
  background:#fff5f6}
.su-danger h3{margin:0 0 8px;color:var(--danger);font-size:15px}
.su-danger .field{max-width:340px}
.su-danger code{padding:2px 6px;border-radius:6px;background:#fff;
  border:1px solid #fecdd3;color:var(--danger);font-weight:900}
/* Shut, it is a quiet link -- nothing about it should invite a stray click. */
.su-danger-shut{padding:0;border:0;background:none;margin-top:10px}
.su-impact{margin:0 0 10px;color:var(--danger);font-size:13px;font-weight:700}
.btn-danger{background:var(--danger);color:#fff}
.btn-danger:hover{background:#9f1239}
.btn-danger:disabled{background:#fecdd3;color:#9f1239;cursor:not-allowed;box-shadow:none}

/* ---------- returns ---------- */
/* The line that appears the moment a counter return is saved: "BOYS PANT BLUE 32X42
   +1, BOYS PANT BLUE 34X42 −1". It sits directly above the Save button, where the
   person who just pressed it is already looking, and it states what the SERVER
   recorded rather than what the form said. Loud enough to be read across a counter,
   quiet enough not to look like an error. */
.returns-result{margin:14px 0 12px;padding:12px 14px;border-radius:12px;
  border:1px solid #bbf7d0;background:#f0fdf4;color:#14532d;font-size:14px;
  font-weight:700;line-height:1.5}
.returns-result strong{font-weight:900}
