/* PackTrack — kraft & ink.
   Palette drawn from the packing table itself: kraft cardboard background,
   white label surfaces, ink text, packing-tape amber as the single accent.
   No webfonts or CDN assets: the tool must work on a warehouse machine with
   no internet. */

:root {
  --bg: #ece7dc;            /* kraft paper */
  --surface: #ffffff;       /* label stock */
  --ink: #1f2228;
  --muted: #6b6f76;
  --line: #d7d0c0;
  --accent: #a35d12;        /* packing tape */
  --accent-soft: #f4e8d6;
  --ok: #1e6b3a;   --ok-bg: #e7f3ec;
  --err: #a61b1b;  --err-bg: #fbeaea;
  --warn: #8a5200; --warn-bg: #fff3d6;
  --mono: ui-monospace, "SF Mono", "Cascadia Mono", Consolas, "Courier New", monospace;
  --sans: -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  font: 15px/1.5 var(--sans);
  background: var(--bg);
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
}

/* ------------------------------------------------ shell & side rail */
/* The sections used to live in a topbar, which meant every page cost a look
   up and back down, and on a 200-row boxes table the nav scrolled away
   entirely. They are a rail now: always on screen, always in the same order,
   so the bench learns one set of positions and stops reading the labels.
   Flex, not grid, so the sign-in screen (no rail) still fills the window. */
.shell { display: flex; min-height: 100vh; align-items: stretch; }

.side {
  flex: 0 0 var(--side-w);
  background: var(--ink); color: #e6e2d8;
  padding: 18px 0 18px 18px;
  display: flex; flex-direction: column; gap: 20px;
  position: sticky; top: 0; align-self: flex-start;
  height: 100vh; overflow-y: auto;
  /* The tape stripe was a seam under the old topbar. Stood on end it is the
     rail's edge -- the one piece of decoration in the tool, and it is the
     same tape that is holding the actual boxes shut. */
  border-right: 8px solid transparent;
  border-image: repeating-linear-gradient(
    -45deg, var(--accent) 0 14px, #c07a25 14px 28px) 8;
}

.brand {
  color: #fff; text-decoration: none; font-weight: 800;
  font-size: 18px; letter-spacing: .04em; padding-right: 18px;
}
.brand-mark { color: #e9b872; margin-right: 2px; }

.side-nav { display: flex; flex-direction: column; }
.side-link {
  color: #e6e2d8; text-decoration: none; font-size: 15px; font-weight: 600;
  padding: 10px 18px 10px 14px; border-radius: 6px 0 0 6px;
  border-left: 4px solid transparent;
}
.side-link:hover { background: #2b2f37; color: #fff; }
/* Amber, not a colour of its own: the accent already means "this one" every
   other place it appears in the tool. */
.side-link.is-here {
  background: #2b2f37; color: #fff; border-left-color: var(--accent);
}

/* Every template already fills {% block nav %} with these two, so the rail
   inherits all thirteen of them unedited. What was a horizontal strip of
   links becomes a stack under the section it belongs to. */
.nav-context {
  display: block; color: #a9a496; font-size: 12px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .07em;
  padding: 0 18px 0 14px; margin-bottom: 6px;
  border-top: 1px solid #3a3e46; padding-top: 16px;
  /* Session names are long ("Packing - Receiving 11000 + 23000"). Two lines
     of it beats an ellipsis: the number at the end is the half that tells
     two sessions apart. */
  overflow-wrap: anywhere; line-height: 1.35;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
  overflow: hidden;
}
.nav-links {
  margin-left: 0; display: flex; flex-direction: column;
  align-items: stretch; gap: 2px;
}
.nav-links a {
  color: #d8d3c6; text-decoration: none; font-size: 14px;
  padding: 7px 18px 7px 14px; border-radius: 6px 0 0 6px;
}
.nav-links a:hover { background: #2b2f37; color: #fff; text-decoration: none; }
/* Some nav blocks post a form (Finish session, Pack received books). Inline
   is right in a strip and wrong in a stack. */
.nav-links form.inline, .nav-links form { display: block; margin: 6px 14px 0 14px; }
.nav-links form .btn { width: 100%; text-align: center; }

.signout {
  margin-top: auto; padding: 14px 18px 0 14px;
  border-top: 1px solid #3a3e46;
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
}
.signout .who {
  font-size: 12px; color: #a9a496; text-transform: uppercase;
  letter-spacing: .06em; font-weight: 700;
}
.signout .btn { background: transparent; color: #e6e2d8; border-color: #5a5e66; }
.signout .btn:hover { background: #2b2f37; color: #fff; }

/* Deliberately outside the kraft palette. Every colour in this tool means
   something on a label or a badge; this one means "you are not where you
   think you are", so it must not resemble any of them. */
.env-banner {
  position: sticky; top: 0; z-index: 80;
  background: #5b21b6; color: #fff;
  padding: 7px 18px; font-size: 13px;
  display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap;
  border-bottom: 3px solid #ede9fe;
}
.env-banner strong {
  font-size: 12px; font-weight: 800;
  text-transform: uppercase; letter-spacing: .14em;
}
.env-banner span { color: #ddd6fe; }
/* The rail sticks to the top of the window; with a banner above it, it has
   to stick below the banner instead or the two overlap on scroll. */
@media (min-width: 901px) {
  /* Only where the rail actually is a rail: unscoped, these three classes
     outrank the plain .side in the narrow-screen block and the top strip
     inherits a 100vh height that pushes the page off screen. */
  .env-banner ~ .shell .side { top: 34px; height: calc(100vh - 34px); }
}

.skip-link {
  position: absolute; left: -9999px; top: 8px;
  background: #fff; color: var(--ink); padding: 8px 14px;
  border-radius: 6px; z-index: 100;
}
.skip-link:focus { left: 8px; }

/* ------------------------------------------------ layout & panels */
/* 1180px was a reading width, and the boxes table is not reading -- eight
   columns and a couple of hundred rows were squeezed into the middle of the
   screen with the monitor empty either side. The content column takes the
   whole window now. Forms stay narrow on their own (.field caps at 440px),
   so only the tables actually spend the room.
   min-width:0 matters: without it a wide table refuses to shrink and pushes
   the rail off the left edge instead of scrolling inside its own panel. */
:root { --side-w: 210px; }
.wrap {
  flex: 1; min-width: 0;
  margin: 0; padding: 22px 26px 60px;
}
.panel {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 20px 22px;
  margin-bottom: 18px;
  box-shadow: 0 1px 2px rgba(31, 34, 40, .05);
  /* A nine-column table on a laptop used to scroll the whole page sideways,
     which took the rail and the panel headings off screen with it. Let the
     panel scroll its own overflow instead, so what scrolls is the table and
     nothing else moves. */
  overflow-x: auto;
}
h1 { font-size: 22px; margin: 0 0 8px; letter-spacing: .01em; }
h2 { font-size: 17px; margin: 0 0 10px; }
h3 { font-size: 15px; margin: 0 0 8px; }
p { margin: 6px 0 12px; }
.muted { color: var(--muted); }
.small { font-size: 13px; }
.mono { font-family: var(--mono); }
.inline { display: inline; }

/* ------------------------------------------------ flashes, badges, toast */
.flashes { margin-bottom: 14px; }
.flash {
  border-radius: 6px; padding: 10px 14px; margin-bottom: 8px;
  border: 1px solid; font-size: 14px;
}
.flash-ok    { background: var(--ok-bg);   border-color: #bcdcc8; color: var(--ok); }
.flash-error { background: var(--err-bg);  border-color: #e7b9b9; color: var(--err); }
.flash-warn  { background: var(--warn-bg); border-color: #ecd39a; color: var(--warn); }

.badge {
  display: inline-block; font-size: 11px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .06em;
  padding: 2px 8px; border-radius: 999px;
}
.badge-mapping  { background: var(--accent-soft); color: var(--accent); }
.badge-packing  { background: var(--ok-bg); color: var(--ok); }
.badge-finished { background: #e8e8ee; color: #4a4d64; }
.badge-warn     { background: var(--warn-bg); color: var(--warn); }

.count-pill {
  background: var(--accent-soft); color: var(--accent);
  font-weight: 700; font-size: 13px; border-radius: 999px; padding: 3px 12px;
}

.toast {
  position: fixed; left: 50%; bottom: 26px; transform: translateX(-50%);
  background: var(--ink); color: #fff; padding: 10px 18px;
  border-radius: 6px; font-size: 14px; z-index: 60;
  box-shadow: 0 4px 14px rgba(0,0,0,.25);
}

/* ------------------------------------------------ forms & buttons */
.field { display: block; margin-bottom: 14px; max-width: 440px; }
.field span { display: block; font-weight: 600; font-size: 13px; margin-bottom: 5px; }
input[type="text"], input[type="file"] {
  width: 100%; padding: 9px 11px; font: inherit;
  border: 1px solid var(--line); border-radius: 6px; background: #fff;
}
input[type="text"]:focus, input[type="file"]:focus,
button:focus-visible, a.btn:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 1px;
}

.btn {
  display: inline-block; font: 600 14px var(--sans); cursor: pointer;
  padding: 9px 16px; border-radius: 6px; text-decoration: none;
  border: 1px solid var(--ink); background: #fff; color: var(--ink);
}
.btn:hover { background: #f3f1ea; }
.btn-primary { background: var(--ink); color: #fff; }
.btn-primary:hover { background: #33373f; }
.btn-danger { border-color: var(--err); color: var(--err); }
.btn-danger:hover { background: var(--err-bg); }
.btn-small { padding: 5px 10px; font-size: 13px; }
.btn-big { padding: 12px 22px; font-size: 15px; }
.form-actions { display: flex; gap: 10px; margin-top: 16px; align-items: center; }
.upload-form { margin-top: 10px; }
/* A session name is a few words. Without a cap it now stretches the whole
   window, and a 1400px box for "US Export Receiving" reads as a mistake. */
.upload-form input[type="text"], .upload-form input[type="file"] { max-width: 440px; }

.link-danger {
  border: 0; background: none; color: var(--err); font-size: 17px;
  cursor: pointer; line-height: 1; padding: 2px 6px; border-radius: 4px;
}
.link-danger:hover { background: var(--err-bg); }

/* ------------------------------------------------ tables */
.table { width: 100%; border-collapse: collapse; font-size: 14px; }
.table th {
  text-align: left; font-size: 12px; text-transform: uppercase;
  letter-spacing: .06em; color: var(--muted);
  border-bottom: 2px solid var(--ink); padding: 8px 10px;
}
.table td { border-bottom: 1px solid var(--line); padding: 8px 10px; vertical-align: top; }
.table tr:last-child td { border-bottom: 0; }
.table .center { text-align: center; }
.table .seq { width: 44px; color: var(--muted); }
.actions { white-space: nowrap; }
.actions .btn, .actions form { margin-right: 6px; }

/* ------------------------------------------------ mapping page */
.map-grid { display: grid; grid-template-columns: minmax(320px, 1fr) 1.2fr; gap: 22px; }
.map-table input[type="radio"], .map-table input[type="checkbox"] {
  width: 18px; height: 18px; accent-color: var(--accent);
}
.order-input { width: 58px; padding: 5px 6px; font: inherit;
  border: 1px solid var(--line); border-radius: 6px; text-align: center; }
.preview-scroll { overflow-x: auto; border: 1px solid var(--line); border-radius: 6px; }
.preview-scroll .table td, .preview-scroll .table th { white-space: nowrap; }

/* ------------------------------------------------ progress band */
.progress-band { padding: 14px 22px; }
.progress-numbers {
  display: flex; gap: 12px; flex-wrap: wrap; font-size: 14px; margin-bottom: 8px;
}
.progress-numbers strong { font-size: 16px; }
.progress-track {
  height: 10px; background: var(--bg); border-radius: 999px;
  border: 1px solid var(--line); overflow: hidden;
}
.progress-fill {
  height: 100%; background: var(--accent); border-radius: 999px;
  transition: width .25s ease;
}

/* ------------------------------------------------ packing screen */
.pack-grid {
  display: grid; grid-template-columns: 380px 1fr; gap: 18px; align-items: start;
}
.scan-column { display: flex; flex-direction: column; gap: 18px; }
.scan-panel { border-top: 4px solid var(--accent); }
.scan-label { display: block; font-size: 14px; margin-bottom: 8px; }
.scan-input {
  width: 100%; font-size: 22px; padding: 14px 14px; letter-spacing: .05em;
  border: 2px dashed var(--accent); border-radius: 8px; background: #fffdf8;
}
.scan-input:focus { border-style: solid; outline: none; }

/* The stamp: big at-a-glance verification of the last scan. */
.stamp { border-width: 2px; text-align: left; transition: border-color .15s ease; }
.stamp-status {
  display: inline-block; font: 800 12px var(--sans); letter-spacing: .14em;
  text-transform: uppercase; padding: 3px 10px; border-radius: 4px;
  margin-bottom: 10px;
}
.stamp-title { font-size: 19px; font-weight: 700; min-height: 1.4em; overflow-wrap: anywhere; }
.stamp-code { color: var(--muted); font-size: 14px; margin-top: 4px; }
.stamp-idle    { border-color: var(--line); }
.stamp-idle .stamp-status  { background: var(--bg); color: var(--muted); }
.stamp-ok      { border-color: var(--ok); }
.stamp-ok .stamp-status    { background: var(--ok-bg); color: var(--ok); }
.stamp-err     { border-color: var(--err); }
.stamp-err .stamp-status   { background: var(--err-bg); color: var(--err); }
.stamp-warn    { border-color: var(--warn); }
.stamp-warn .stamp-status  { background: var(--warn-bg); color: var(--warn); }

@keyframes stamp-pop { 0% { transform: scale(.985); } 100% { transform: scale(1); } }
.stamp.pop { animation: stamp-pop .18s ease; }
@media (prefers-reduced-motion: reduce) {
  .stamp.pop { animation: none; }
  .progress-fill { transition: none; }
}

.actions-panel { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.sound-toggle { font-size: 13px; color: var(--muted); margin-left: auto; }

.box-head { display: flex; align-items: center; gap: 12px; margin-bottom: 6px; }
.box-head h2 { margin: 0; }
.empty-note { padding: 18px 4px; }
#box-table tr.new-row td { background: var(--ok-bg); }
.row-remove { width: 34px; text-align: right; }

/* ------------------------------------------------ modal */
.modal-overlay {
  position: fixed; inset: 0; background: rgba(31,34,40,.55);
  display: flex; align-items: center; justify-content: center; z-index: 50;
  padding: 20px;
}
/* The [hidden] attribute must beat the display:flex above,
   otherwise the overlay can never close. */
.modal-overlay[hidden] { display: none; }
.modal { max-width: 460px; width: 100%; margin: 0; position: relative; }
.modal-close {
  position: absolute; top: 8px; right: 8px;
  border: 0; background: none; font-size: 22px; line-height: 1;
  color: var(--muted); cursor: pointer; padding: 4px 9px; border-radius: 4px;
}
.modal-close:hover { background: var(--bg); color: var(--ink); }
.modal h3 { font-size: 17px; }
.modal-buttons { display: flex; flex-direction: column; gap: 8px; margin-top: 16px; }
.modal-buttons .btn { width: 100%; text-align: center; }
.modal .mono-big {
  font-family: var(--mono); font-size: 18px; background: var(--bg);
  padding: 6px 10px; border-radius: 6px; display: inline-block; margin: 4px 0;
}

/* ------------------------------------------------ summary stats */
.stat-row { display: flex; gap: 14px; flex-wrap: wrap; margin: 14px 0 18px; }
.stat {
  border: 1px solid var(--line); border-radius: 8px; padding: 12px 18px;
  min-width: 110px; background: #fffdf8;
}
.stat-num { font-size: 24px; font-weight: 700; }
.stat-label { font-size: 12px; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); }
.download-row { display: flex; gap: 10px; flex-wrap: wrap; margin: 8px 0 12px; }

.rename-form { display: flex; gap: 6px; margin-top: 6px; }
.rename-form input { max-width: 220px; padding: 5px 8px; font-size: 13px; }

/* ------------------------------------------------ access code */
.login-panel { max-width: 420px; margin: 64px auto; }

/* ------------------------------------------------ responsive */
@media (max-width: 900px) {
  .pack-grid { grid-template-columns: 1fr; }
  .map-grid { grid-template-columns: 1fr; }
  /* A rail costs half a phone. Back to a strip along the top, same order. */
  .shell { display: block; }
  .side {
    position: static; height: auto; flex-direction: row; flex-wrap: wrap;
    align-items: center; gap: 10px; padding: 10px 14px;
    border-right: 0; border-bottom: 8px solid transparent;
    border-image: repeating-linear-gradient(
      -45deg, var(--accent) 0 14px, #c07a25 14px 28px) 8;
  }
  .side-nav { flex-direction: row; gap: 4px; }
  .side-link, .nav-links a { padding: 7px 12px; border-radius: 6px; border-left: 0; }
  .side-link.is-here { border-left: 0; box-shadow: inset 0 -3px 0 var(--accent); }
  .nav-context {
    border-top: 0; padding-top: 0; margin-bottom: 0;
    padding-left: 0; width: 100%;
  }
  .nav-links { flex-direction: row; flex-wrap: wrap; width: 100%; }
  .nav-links form.inline, .nav-links form { display: inline-block; margin: 0; }
  .nav-links form .btn { width: auto; }
  .signout { margin-top: 0; margin-left: auto; border-top: 0; padding: 0; }
  .printer-pin {
    flex-direction: row; align-items: center; gap: 6px;
    margin: 0; padding-top: 0; border-top: 0;
  }
  .printer-pin select { width: auto; }
  .wrap { padding: 16px 14px 48px; }
}

/* The scan-into-it filter box, shared by the receiving lists. The same rules
   are still inlined in receive_relabel / receive_suppliers /
   receive_supplier_detail; those copies are identical and can go whenever
   someone is in those files anyway. */
.det-filter { padding:7px 10px; border:1px solid var(--ink); border-radius:5px;
              min-width:280px; font-size:14px; }
.det-filter.armed { background:#f2f8f3; }
.filter-wrap { position:relative; display:inline-block; }
.filter-clear { position:absolute; right:6px; top:50%; transform:translateY(-50%);
                border:0; background:none; font-size:18px; line-height:1;
                color:var(--muted); cursor:pointer; padding:2px 6px; }
.filter-clear:hover { color:var(--ink); }
.filter-note { margin:-4px 0 10px; min-height:17px; }
.counts { margin-top:8px; }
.warn-text { color:#9a6700; font-weight:600; }
tr.hidden-row { display:none; }

/* Which printer this laptop is paired to. Unpaired is worth seeing from
   across the room: the print buttons refuse until it is set. */
/* In the rail it is a stacked field, not an inline chip: the laptop pairs
   to a printer once and then this is the thing that says which bench you are
   standing at, so it reads as a label over its control. */
.printer-pin {
  display:flex; flex-direction:column; align-items:stretch; gap:5px;
  font-size:12px; margin:0 18px 0 14px; padding-top:14px;
  border-top:1px solid #3a3e46;
}
.printer-pin label {
  color:#a9a496; text-transform:uppercase; letter-spacing:.07em; font-weight:700;
}
.printer-pin select { padding:6px 8px; font-size:13px; width:100%; }
.printer-pin.unpaired select { border:2px solid #cf222e; }
.printer-pin.unpaired label { color:#cf222e; font-weight:700; }


/* The bench notice. Raised by writing data/notice.txt, cleared by deleting
   it -- see NOTICE_FILE in app.py. Deliberately loud: it is shown when
   somebody has to stop what they are doing. */
dialog.notice {
  border: 0; border-top: 8px solid #b91c1c; border-radius: 10px;
  padding: 26px 30px 22px; max-width: 34rem;
  box-shadow: 0 24px 60px rgba(0,0,0,.35);
}
dialog.notice::backdrop { background: rgba(15, 23, 42, .72); }
dialog.notice h2 {
  margin: 0 0 10px; font-size: 21px; line-height: 1.25; color: #b91c1c;
}
dialog.notice p { margin: 0 0 18px; font-size: 15px; line-height: 1.5; }
dialog.notice form { margin: 0; text-align: right; }
