:root {
  --bg: #0b1220;
  --bg-glow: #14213d;
  --surface: #111c33;
  --surface-2: #16243f;
  --border: #22314f;
  --text: #e8eefc;
  --muted: #93a4c3;
  --accent: #60a5fa;
  --accent-strong: #3b82f6;
  --danger: #fca5a5;
  --ok: #6ee7b7;
  --warn: #fcd34d;
  --radius: 14px;
  --shadow: 0 18px 40px -24px rgba(0, 0, 0, .9);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  min-height: 100vh;
  font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  color: var(--text);
  background:
    radial-gradient(1100px 520px at 15% -8%, var(--bg-glow), transparent 60%),
    var(--bg);
  -webkit-font-smoothing: antialiased;
}

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

/* ---------- top bar ---------- */

.topbar {
  display: flex;
  align-items: center;
  gap: 1.5rem;
  padding: 0 clamp(1rem, 4vw, 2.5rem);
  height: 64px;
  border-bottom: 1px solid var(--border);
  background: rgba(11, 18, 32, .75);
  backdrop-filter: blur(10px);
  position: sticky;
  top: 0;
  z-index: 10;
}

.brand {
  display: flex;
  align-items: center;
  gap: .6rem;
  font-weight: 650;
  letter-spacing: -.01em;
  color: var(--text);
}
.brand:hover { text-decoration: none; }
.brand .mark {
  width: 26px;
  height: 26px;
  border-radius: 8px;
  background: linear-gradient(135deg, var(--accent), #a78bfa);
}

.nav { display: flex; align-items: center; gap: 1.1rem; font-size: .93rem; }
.nav > [hidden] { display: none; }
.nav-extras-switch { display: flex; align-items: center; gap: .4rem; color: var(--muted); font-size: .8rem; white-space: nowrap; cursor: pointer; }
.nav-extras-switch input { width: auto; margin: 0; accent-color: var(--accent); }
.nav > a { color: var(--muted); }
.nav > a:hover, .nav > a[aria-current="page"] { color: var(--text); text-decoration: none; }

.nav-menu { position: relative; }
.nav-trigger {
  display: flex;
  align-items: center;
  gap: .35rem;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: var(--muted);
  cursor: pointer;
  /* Clicking a menu should open it, not select its label. */
  user-select: none;
}
.nav-trigger:hover, .nav-trigger.current, .nav-trigger[aria-expanded="true"] {
  color: var(--text);
}
.nav-trigger .caret {
  width: 0;
  height: 0;
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
  border-top: 5px solid currentColor;
  opacity: .8;
}

.nav-dropdown {
  position: absolute;
  top: calc(100% + .55rem);
  left: -.7rem;
  z-index: 20;
  display: flex;
  flex-direction: column;
  min-width: 190px;
  padding: .35rem;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface);
  box-shadow: 0 12px 30px rgba(0, 0, 0, .45);
}
.nav-dropdown[hidden] { display: none; }
.nav-dropdown a {
  padding: .48rem .7rem;
  border-radius: 7px;
  color: var(--muted);
  white-space: nowrap;
}
.nav-dropdown a:hover { background: var(--surface-2); color: var(--text); text-decoration: none; }
.nav-dropdown a[aria-current="page"] { color: var(--text); background: var(--surface-2); }

.user {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: .7rem;
  font-size: .9rem;
  color: var(--muted);
}
.user img, .user .avatar {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--surface-2);
  display: grid;
  place-items: center;
  font-size: .78rem;
  font-weight: 650;
  color: var(--text);
  border: 1px solid var(--border);
}
.user .signout {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: .35rem .7rem;
  color: var(--muted);
}
.user .signout:hover { color: var(--text); background: var(--surface-2); text-decoration: none; }

/* ---------- layout ---------- */

main { padding: clamp(1.5rem, 4vw, 2.75rem); max-width: 1160px; margin: 0 auto; }

.page-head {
  display: flex;
  align-items: flex-end;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 1.5rem;
}
.page-head h1 {
  font-size: 1.6rem;
  letter-spacing: -.02em;
  margin: 0 0 .3rem;
}
.page-head p { margin: 0; color: var(--muted); font-size: .93rem; }
.page-head .tools { margin-left: auto; display: flex; gap: .6rem; align-items: center; }
.view-picker { display: inline-flex; align-items: center; gap: .5rem; font-size: .88rem; color: var(--muted); }
/* A label carries `display: block` from the rule below, and any display at all
   beats the browser's own `[hidden] { display: none }`. Without this the
   picker sits in the header as an empty box before a report has been run --
   which is exactly what it did. Every element given a display here has to say
   what hidden means for it too. */
.view-picker[hidden] { display: none; }
/* Narrower than a filter: this one sits in the header beside a button, not in
   a form column, and the grain names it holds are two words at most. */
.view-picker select { min-width: 0; padding: .45rem .6rem; }

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  overflow: hidden;
}

input[type="search"], input[type="text"], input[type="url"], select {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 9px;
  color: var(--text);
  padding: .55rem .85rem;
  font: inherit;
  font-size: .9rem;
  min-width: 15rem;
}
input::placeholder { color: #6b7fa3; }
input[type="search"]:focus, input[type="text"]:focus, input[type="url"]:focus, select:focus {
  outline: none;
  border-color: var(--accent-strong);
  box-shadow: 0 0 0 3px rgba(59, 130, 246, .2);
}
select { min-width: 18rem; }

textarea {
  width: 100%;
  resize: vertical;
  min-height: 8rem;
  background: var(--surface-2);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 9px;
  padding: .8rem;
  font: .82rem ui-monospace, SFMono-Regular, Menlo, monospace;
}
textarea:focus { outline: none; border-color: var(--accent-strong); }
label { display: block; margin: 1rem 0 .45rem; font-weight: 600; }
code { overflow-wrap: anywhere; color: var(--accent); }
button, .button {
  border: 1px solid var(--accent-strong);
  border-radius: 9px;
  padding: .58rem .9rem;
  background: var(--accent-strong);
  color: white;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}
button.secondary, .button.secondary { background: transparent; border-color: var(--border); color: var(--muted); }
button#activity-census-run:disabled { opacity: .55; cursor: not-allowed; }
#activity-census-details .secondary-review > summary { padding: 1rem 1.25rem; color: var(--text); }
.button:hover { text-decoration: none; }
.button-row { display: flex; flex-wrap: wrap; gap: .65rem; margin-top: .8rem; align-items: center; }
/* The same trap `.view-picker` and `.filters` fall into: `display: flex` beats
   the browser's own `[hidden] { display: none }`. The Clear session control on
   Ad Hoc Charges is hidden until a session exists, and without this it sat on
   the page offering to clear a credential that was never pasted. */
.button-row[hidden] { display: none; }
.upload-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1rem; }
.upload-grid label { display: grid; gap: .45rem; font-weight: 650; }
/* These sit inside a `.field > label`, which uppercases and letter-spaces
   everything in it -- so "Workbooks .xlsx, several allowed" arrived as one
   shouted run-on and the qualifier stopped reading as a qualifier. */
.optional-label, .required-label {
  text-transform: none;
  letter-spacing: normal;
  font-size: .75rem;
  font-weight: 500;
}
.optional-label { color: var(--muted); }
.required-label { color: var(--accent); }
.action-card { padding: 1.4rem; }
.action-card h2 { margin-top: 0; }
.action-card > p { color: var(--muted); }
.action-form-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 1rem; }
.action-form-grid input, .action-form-grid select { width: 100%; min-width: 0; background: var(--surface-2); border: 1px solid var(--border); border-radius: 9px; color: var(--text); padding: .65rem .8rem; font: inherit; }
.confirmation-label { display: flex; align-items: flex-start; gap: .55rem; color: var(--muted); font-weight: 500; }
.confirmation-label input { margin-top: .2rem; }
.action-result { margin-top: 1rem; padding: 1rem; border: 1px solid var(--border); border-radius: 9px; background: var(--surface-2); }
.action-result h3, .action-result p { margin-top: 0; }
.upload-grid input[type="file"] { border: 1px dashed var(--border); border-radius: .65rem; padding: .8rem; background: var(--surface); }
.warning-card { border-color: #d59a35; background: #302816; color: var(--text); }
.warning-card h2 { margin-top: 0; }
.warning-card ul { margin-bottom: 0; }
/* The bill review says which bills have no charges list above its tabs. */
#unread-bills-warning { padding: 14px 18px; margin-bottom: 16px; }
#unread-bills-warning ul { margin: 8px 0 0; padding-left: 20px; }
.invoice-review-result { margin-top: 2rem; }
.review-assignment { display: flex; flex-wrap: wrap; gap: .5rem 1.25rem; align-items: baseline; }
.review-assignment span { color: var(--muted); }
.compact-summary { margin-top: .75rem; }
.status-pill { display: inline-block; border-radius: 999px; padding: .22rem .55rem; font-size: .75rem; font-weight: 700; white-space: nowrap; }
.status-pill.match, .status-good { color: #17643a; }
.status-pill.match { background: #e4f5eb; }
.status-pill.mismatch, .status-bad { color: #9f2d27; }
.status-pill.mismatch { background: #fde9e7; }
.status-pill.needs_review { color: #825500; background: #fff2cf; }
.status-pill.not_mapped { color: #52606d; background: #edf1f4; }
/* Quieter than "not mapped": these rows exist to show a charge was considered,
   so they must not draw the eye away from the ones that need acting on. */
.status-pill.not_applicable { color: #8b95a1; background: transparent; font-weight: 400; }
.comparison-mismatch td { background: #fffafa; }
.activity-tools { flex-wrap: wrap; align-items: end; }
.activity-tools label { display: grid; gap: .35rem; color: var(--muted); font-size: .82rem; }
.activity-tools input, .activity-tools select { min-width: 15rem; }
.activity-tools input[type="checkbox"] { min-width: auto; }
.activity-exception td { background: #302816; color: #f8fafc; }
.activity-exception:hover td { background: #3b3119; color: #fff; }
.activity-exception .status-pill + .status-pill { margin-left: .3rem; }
.secondary-review { margin-top: 1.5rem; }
.secondary-review > summary { cursor: pointer; font-weight: 700; color: var(--muted); padding: .75rem 0; }
.review-tabs { display: flex; gap: .5rem; margin: 1.5rem 0 .75rem; border-bottom: 1px solid var(--border); }
.review-tab { border: 0; border-bottom: 3px solid transparent; border-radius: 8px 8px 0 0; background: transparent; color: var(--muted); }
.review-tab:hover, .review-tab.active { background: var(--surface-2); color: var(--text); border-bottom-color: var(--accent); }
.rate-card-charge-view { margin-top: 1rem; }
.rate-category { margin: .8rem 0; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); overflow: hidden; }
.rate-category > summary { display: flex; justify-content: space-between; gap: 1rem; cursor: pointer; padding: 1rem 1.15rem; background: var(--surface-2); }
.rate-category > summary span, .rate-item-heading span, .empty-note { color: var(--muted); }
.rate-item { border-top: 1px solid var(--border); }
.rate-item-heading { display: flex; justify-content: space-between; gap: 1rem; padding: .85rem 1.15rem; }
.rate-item .empty-note, .rate-category > .empty-note { margin: 0; padding: 0 1.15rem .9rem; }
.unmatched-category { border-color: #8a6a28; }
.unmatched-category > summary { background: #302816; }
.rate-classifier { min-width: 22rem; }
.retrieval-actions { margin: 1.25rem 0 0; }
.section-content { padding: 1.25rem; }
.section-content h2, .table-heading h2 { margin: 0; font-size: 1.1rem; }
.section-content p, .section-content li { color: var(--muted); line-height: 1.5; }
.section-content .skill-reminder {
  color: var(--text);
  background: rgba(96, 165, 250, .08);
  border: 1px solid rgba(96, 165, 250, .25);
  border-radius: 9px;
  padding: .7rem .85rem;
}
.summary-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: .75rem; margin: 1.5rem 0; }
.summary-grid > div { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 1rem; }
.summary-grid strong, .summary-grid span { display: block; }
.summary-grid strong { font-size: 1.45rem; }
.summary-grid span, .footnote { color: var(--muted); font-size: .82rem; }
.table-heading { margin-top: 1.5rem; margin-bottom: .75rem; align-items: center; }
.state.compact { padding: 1rem 0; text-align: left; }
td.number { text-align: right; font-variant-numeric: tabular-nums; }
@media (max-width: 700px) { .summary-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 700px) { .upload-grid { grid-template-columns: 1fr; } }

/* ---------- filters ---------- */

main.wide { max-width: 1400px; }

.filters {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 1rem 1.25rem;
  padding: 1.15rem 1.25rem;
  margin-bottom: 1.25rem;
}

/* Same trap as `.view-picker` above: `display: flex` beats the browser's own
   `[hidden] { display: none }`, so a filter row that starts hidden sits on the
   page as an empty box until something reveals it. The saved-invoice picker on
   Invoice Review is hidden until the server says there are saved invoices, and
   without this it was visible the whole time. */
.filters[hidden] { display: none; }

.field { display: flex; flex-direction: column; gap: .4rem; border: 0; margin: 0; padding: 0; }
.field > label, .field legend {
  font-size: .74rem;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 600;
  padding: 0;
}
/* Buttons in an actions block sit side by side. `.field` is a column -- right
   for a label above a control, wrong here -- so a second button stacked under
   the first and stretched the filter row to three lines with an empty patch
   beside it. `.cr-save` had already worked this around locally; it belongs on
   the base rule, and that override is now only its margin. */
.field.actions {
  margin-left: auto;
  flex-direction: row;
  align-items: center;
  flex-wrap: wrap;
  gap: .6rem;
}

select, input[type="date"] {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 9px;
  color: var(--text);
  padding: .5rem .7rem;
  font: inherit;
  font-size: .9rem;
  min-width: 11rem;
}
select:focus, input[type="date"]:focus {
  outline: none;
  border-color: var(--accent-strong);
  box-shadow: 0 0 0 3px rgba(59, 130, 246, .2);
}
input[type="file"] {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 9px;
  color: var(--muted);
  padding: .38rem .5rem;
  font: inherit;
  font-size: .85rem;
  min-width: 11rem;
  max-width: 100%;
}
/* The browser's own control is a light-grey button with black text, which on
   this page reads as a piece of a different application. */
input[type="file"]::file-selector-button {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 7px;
  color: var(--text);
  font: inherit;
  font-size: .82rem;
  font-weight: 600;
  padding: .3rem .7rem;
  margin-right: .7rem;
  cursor: pointer;
}
input[type="file"]::file-selector-button:hover {
  border-color: var(--accent-strong);
  color: var(--accent);
}
input[type="file"]:focus-visible {
  outline: none;
  border-color: var(--accent-strong);
  box-shadow: 0 0 0 3px rgba(59, 130, 246, .2);
}

input[type="date"]:disabled { opacity: .55; }
input[type="date"]::-webkit-calendar-picker-indicator { filter: invert(.7); }

.group-by .checks { display: flex; gap: .5rem; }
.check {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 9px;
  padding: .5rem .7rem;
  font-size: .9rem;
  cursor: pointer;
  user-select: none;
}
.check:has(input:checked) { border-color: var(--accent-strong); background: rgba(59, 130, 246, .14); }
.check input { accent-color: var(--accent-strong); margin: 0; }

button {
  background: var(--accent-strong);
  border: 1px solid var(--accent-strong);
  border-radius: 9px;
  color: #fff;
  font: inherit;
  font-size: .9rem;
  font-weight: 600;
  padding: .55rem 1.2rem;
  cursor: pointer;
}
button:hover:not(:disabled) { background: var(--accent); border-color: var(--accent); }
button:disabled { opacity: .55; cursor: progress; }

/* Secondary action: present in the toolbar without competing with Run. */
button.ghost {
  background: transparent;
  border-color: var(--border);
  color: var(--text);
  font-weight: 500;
}
button.ghost:hover:not(:disabled) { background: var(--surface-2); border-color: var(--border); }

/* Destructive, and said so before it is clicked rather than only in the
   confirm dialog. Delete and View sat side by side looking identical, which
   makes the difference between them a matter of reading carefully. */
button.danger, .button.danger {
  background: transparent;
  border-color: rgba(252, 165, 165, .35);
  color: var(--danger);
  font-weight: 600;
}
button.danger:hover:not(:disabled), .button.danger:hover:not(:disabled) {
  background: rgba(252, 165, 165, .12);
  border-color: var(--danger);
  color: var(--danger);
}

/* Buttons in a table cell sit on one line. Left to wrap they stacked, which
   doubled every row's height and pushed the columns beside them off screen. */
.row-actions {
  display: flex;
  flex-wrap: nowrap;
  gap: .4rem;
  justify-content: flex-end;
  white-space: nowrap;
}
.row-actions button { padding: .3rem .7rem; font-size: .8rem; }

/* ---------- pager ---------- */

/* Under a table that scrolls inside its own card, so it stays put while the
   rows move. `[hidden]` is spelled out because `display: flex` beats the
   browser's own `[hidden] { display: none }` -- the same trap `.filters` and
   `.button-row` fall into. */
.pager {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: .75rem 1.25rem;
  margin: .8rem 0 1.5rem;
}
.pager[hidden] { display: none; }

.pager-count { margin: 0; color: var(--muted); font-size: .82rem; }

.pager-controls { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem; }

.pager-size {
  display: flex;
  align-items: center;
  gap: .45rem;
  color: var(--muted);
  font-size: .74rem;
  letter-spacing: .07em;
  text-transform: uppercase;
  font-weight: 600;
}
.pager-size select { min-width: auto; padding: .32rem .5rem; font-size: .82rem; }

.pager-controls button { padding: .38rem .75rem; font-size: .84rem; }
/* Kept in the layout rather than hidden at either end: controls that vanish
   move the row under the cursor and change what the next click does. */
.pager-controls button:disabled { opacity: .4; cursor: default; }

.pager-position {
  color: var(--text);
  font-size: .84rem;
  font-variant-numeric: tabular-nums;
  min-width: 7rem;
  text-align: center;
}

/* ---------- table ---------- */

/* The table scrolls inside the card rather than the page, so the sticky header
   has a scroll container to stick to. Sticking it to the viewport instead would
   need this wrapper to have no overflow, which would break wide tables. */
.table-wrap {
  overflow: auto;
  max-height: clamp(18rem, calc(100vh - 15rem), 60rem);
  overscroll-behavior: contain;
}

table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: .92rem; }

/* The saved list carried eight columns of free text and overflowed its card,
   and `.table-wrap` scrolls horizontally -- so the Name column, the one thing
   identifying a row, was the first thing scrolled out of view. Two columns
   hold nearly all the width, so they are the two that give it up: the name
   wraps instead of forcing a single line, and the notes preview is capped and
   ellipsised with the full text on hover. */
/* `tbody td` is nowrap, which is right for a table of figures and wrong for
   eight columns of names, dates, emails and free text -- together they forced
   the card to overflow by more than a column's width. These four are the ones
   with somewhere to break, so they wrap and the table fits instead of
   scrolling its own actions out of reach. */
.saved-table td:first-child { min-width: 13rem; white-space: normal; }
.saved-table td:nth-child(2),
.saved-table td:nth-child(3) { white-space: normal; }

/* An address and a note both outgrow their column, and neither is worth
   breaking mid-word to fit -- "local-development@local host" reads as two
   words. Cut with an ellipsis instead; the whole value stays on the cell for
   a hover, so the cut is a display decision rather than a loss. */
.saved-table td:nth-child(6),
.saved-table td:nth-child(7) {
  max-width: 12rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Eight columns of names, dates, emails and free text will outgrow the card on
   some screen, and `.table-wrap` scrolls -- so View and Delete were the first
   things to leave. Pinned to the right edge instead: the actions on a row stay
   reachable at any width, which giving up a column's text does not guarantee.
   The cell needs an opaque background or the rows scroll under it; the hover
   tint over `--surface` happens to compute to exactly `--surface-2`. */
.saved-table td:last-child, .saved-table th:last-child {
  position: sticky;
  right: 0;
  width: 1%;
  background: var(--surface);
  border-left: 1px solid var(--border);
}
.saved-table th:last-child { z-index: 2; background: var(--surface-2); }
.saved-table tbody tr:hover td:last-child { background: var(--surface-2); }

/* The note written against a charge is the reason somebody opened this, so it
   wraps rather than being cut like the list preview. */
.saved-charge-table td:last-child { white-space: normal; min-width: 12rem; }

thead th {
  position: sticky;
  top: 0;
  z-index: 1;
  text-align: left;
  font-size: .74rem;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 600;
  background: var(--surface-2);
  padding: .75rem 1rem;
  border-bottom: 1px solid var(--border);
  box-shadow: 0 6px 12px -10px rgba(0, 0, 0, .9);
  white-space: nowrap;
}

tbody td {
  padding: .8rem 1rem;
  border-bottom: 1px solid rgba(34, 49, 79, .6);
  white-space: nowrap;
}
tbody tr:last-child td { border-bottom: 0; }
tbody tr:hover { background: rgba(96, 165, 250, .06); }

th.num, td.num { text-align: right; font-variant-numeric: tabular-nums; }

/* A bill ShipHero has thrown away. Drawn rather than dropped -- it usually
   covers the period the bill that replaced it covers, so hiding it makes the
   gap it explains look like a period nobody ever billed -- and dimmed, so it
   does not read as one of the rows the totals are about. */
tr.discarded td { color: var(--muted); }

tfoot td {
  position: sticky;
  bottom: 0;
  background: var(--surface-2);
  border-top: 1px solid var(--border);
  padding: .7rem 1rem;
  font-weight: 650;
  font-size: .88rem;
  white-space: nowrap;
}

/* A count that can be opened to show what it counted. The underline is the
   only affordance a table cell has room for; hover and focus do the rest. */
td.openable { cursor: pointer; text-decoration: underline dotted currentColor; text-underline-offset: .25em; }
td.openable:hover { color: var(--accent, #60a5fa); }
td.openable:focus-visible { outline: 2px solid var(--accent, #60a5fa); outline-offset: -2px; }

tbody tr.opened td { background: rgba(96, 165, 250, .05); white-space: normal; }
tbody tr.opened:hover td { background: rgba(96, 165, 250, .05); }
.lpn-list { display: flex; flex-wrap: wrap; gap: .35rem .45rem; }
.lpn-list .lpn {
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
  font-size: .8rem;
  padding: .15rem .45rem;
  border: 1px solid var(--border);
  border-radius: .3rem;
  color: var(--muted);
}
/* What the LPN is, beside the barcode rather than inside it: a staging bin
   holds boxes as well as pallets, and only one of the two is billed for. */
.lpn-list .lpn em {
  font-family: inherit;
  font-style: normal;
  font-size: .72rem;
  letter-spacing: .04em;
  text-transform: uppercase;
  opacity: .65;
  margin-left: .4rem;
}

.column-notes { margin: .6rem 1rem .9rem; display: grid; gap: .3rem; }
.column-notes p { margin: 0; max-width: 78ch; }

td.name { font-weight: 550; }
td.mono { font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace; font-size: .84rem; color: var(--muted); }

.badge {
  display: inline-block;
  padding: .18rem .55rem;
  border-radius: 999px;
  font-size: .75rem;
  font-weight: 600;
  letter-spacing: .01em;
  background: var(--surface-2);
  color: var(--muted);
  border: 1px solid var(--border);
  text-transform: capitalize;
}
.badge.active { background: rgba(110, 231, 183, .12); color: var(--ok); border-color: rgba(110, 231, 183, .3); }
.badge.inactive { background: rgba(252, 165, 165, .1); color: var(--danger); border-color: rgba(252, 165, 165, .28); }

/* ---------- states ---------- */

.state { padding: 3rem 1.5rem; text-align: center; color: var(--muted); font-size: .93rem; }
.state.error { color: var(--danger); }
.state.warn {
  padding: .8rem 1.25rem;
  text-align: left;
  color: #fcd34d;
  border-bottom: 1px solid var(--border);
  background: rgba(252, 211, 77, .07);
}
.state.busy::before {
  content: "";
  display: inline-block;
  width: 13px;
  height: 13px;
  margin-right: .6rem;
  vertical-align: -1px;
  border: 2px solid var(--border);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin .8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .state.busy::before { animation: none; }
}

tr.outside-bill-period td {
  background: rgba(251, 191, 36, .16);
  border-color: rgba(251, 191, 36, .28);
}
tr.outside-bill-period:hover td { background: rgba(251, 191, 36, .23); }
.bill-period-legend { display: flex; align-items: center; gap: .45rem; }
.bill-period-legend span {
  width: .8rem;
  height: .8rem;
  border-radius: 3px;
  background: rgba(251, 191, 36, .28);
  border: 1px solid rgba(251, 191, 36, .48);
}

.skeleton-row {
  height: 14px;
  margin: 1.1rem 1rem;
  border-radius: 6px;
  background: linear-gradient(90deg, var(--surface-2), #1d2d4d, var(--surface-2));
  background-size: 200% 100%;
  animation: shimmer 1.3s ease-in-out infinite;
}
@keyframes shimmer {
  from { background-position: 200% 0; }
  to { background-position: -200% 0; }
}
@media (prefers-reduced-motion: reduce) {
  .skeleton-row { animation: none; }
}

/* ---------- centered pages (login, hero) ---------- */

.center {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 1.5rem;
}

.panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 18px;
  box-shadow: var(--shadow);
  padding: 2.5rem;
  width: min(26rem, 100%);
  text-align: center;
}
.panel .mark {
  width: 44px;
  height: 44px;
  border-radius: 13px;
  margin: 0 auto 1.1rem;
  background: linear-gradient(135deg, var(--accent), #a78bfa);
}
.panel h1 { font-size: 1.35rem; letter-spacing: -.02em; margin: 0 0 .4rem; }
.panel p { color: var(--muted); font-size: .93rem; margin: 0 0 1.6rem; line-height: 1.5; }

/* A notice is informational by default, and says so by looking like
   information. It used to be styled as danger for every use, which put "every
   day in this period was imported and ShipHero recorded nothing" -- a finished,
   good answer -- in the same alarm red as a failure. When the page shouts
   everything it says nothing, so the colours are opted into: `.warn` for
   something the reader has to act on, `.good` for a confirmation, `.alert` for
   an actual failure. Plain `.notice` is the quiet default. */
.notice {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  color: var(--text);
  border-radius: 10px;
  padding: .75rem .95rem;
  font-size: .88rem;
  text-align: left;
  margin: 0 0 1.25rem;
}
.notice strong { display: block; margin-bottom: .2rem; color: var(--text); }
.notice p { margin: .15rem 0 0; color: var(--muted); line-height: 1.5; }
.notice p:last-child { margin-bottom: 0; }

.notice.warn {
  border-left-color: var(--warn);
  background: rgba(252, 211, 77, .07);
  border-color: rgba(252, 211, 77, .22);
}
.notice.warn strong { color: var(--warn); }

.notice.good {
  border-left-color: var(--ok);
  background: rgba(110, 231, 183, .07);
  border-color: rgba(110, 231, 183, .22);
}
.notice.good strong { color: var(--ok); }

.notice.alert {
  border-left-color: var(--danger);
  background: rgba(252, 165, 165, .09);
  border-color: rgba(252, 165, 165, .3);
  color: var(--danger);
}
.notice.alert strong { color: var(--danger); }

.google-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .7rem;
  width: 100%;
  background: #fff;
  color: #1f2937;
  font-weight: 600;
  font-size: .95rem;
  padding: .8rem 1.25rem;
  border-radius: 10px;
  transition: background .15s ease, transform .15s ease;
}
.google-button:hover { background: #e9ebee; text-decoration: none; transform: translateY(-1px); }
.google-button svg { width: 18px; height: 18px; }

.panel .footnote { font-size: .8rem; color: #6b7fa3; margin: 1.4rem 0 0; }

.hero h1 { font-size: 2.1rem; margin: 0 0 .5rem; letter-spacing: -.03em; }
.hero p { margin-bottom: 0; }
.hero .actions { display: grid; gap: .6rem; margin-top: 1.6rem; }
.hero .actions a {
  border: 1px solid var(--border);
  background: var(--surface-2);
  border-radius: 10px;
  padding: .75rem 1rem;
  color: var(--text);
  font-size: .93rem;
  font-weight: 550;
}
.hero .actions a:hover { border-color: var(--accent-strong); text-decoration: none; }

/* Reconciliation: the common result format shared by every module. */
.recon-common { margin-top: .5rem; }
/* The verdict, said before any table. Colour carries no meaning on its own —
   the word is always there beside it. */
.recon-state { display: flex; align-items: baseline; gap: .8rem; flex-wrap: wrap;
  border: 1px solid var(--border); border-left-width: .35rem; border-radius: var(--radius);
  padding: .8rem 1rem; margin-bottom: 1rem; background: var(--surface); }
.recon-state strong { font-size: 1.05rem; }
.recon-state span { color: var(--muted); font-size: .88rem; }
.recon-state-reconciled { border-left-color: #17643a; }
.recon-state-reconciled strong { color: #17643a; }
.recon-state-needs_review { border-left-color: #9f2d27; }
.recon-state-needs_review strong { color: #9f2d27; }
/* Nothing to reconcile: neutral rather than green. An empty period is not a
   clean bill of health, and colouring it like one is what made a module with
   no data look like a module that had passed. */
.recon-state-nothing { border-left-color: #5b6672; }
.recon-state-nothing strong { color: #3f4854; }
.recon-state-incomplete { border-left-color: #b07d00; }
.recon-state-incomplete strong { color: #825500; }
.recon-state-not_run { border-left-color: var(--border); }
.recon-state-not_run strong { color: var(--muted); }
.inline-check { display: inline-flex; align-items: center; gap: .35rem; white-space: nowrap; }
.recon-entities, .recon-issues { margin-top: 1.5rem; }
.recon-entity-list { padding: .35rem; }
.recon-entity { border-bottom: 1px solid var(--border); }
.recon-entity:last-child { border-bottom: 0; }
.recon-entity > summary { display: flex; align-items: center; gap: 1rem; cursor: pointer; padding: .7rem 1rem; }
.recon-entity > summary strong { flex: 0 1 auto; }
.recon-entity-meta { flex: 1 1 auto; color: var(--muted); font-size: .82rem; }
.recon-entity-meta + .recon-entity-meta { flex: 0 0 auto; }
.recon-entity-variance { font-variant-numeric: tabular-nums; font-weight: 700; }
.recon-entity[open] > summary { background: var(--surface-2); }
.recon-detail-title { margin: .9rem 1rem .3rem; color: var(--muted); font-weight: 700; font-size: .82rem; }
.recon-warnings { margin: 1rem 0 0; padding-left: 1.1rem; color: var(--muted); }
.cr-coverage-notes {
  margin: .55rem 0 .85rem;
  border: 1px solid var(--border);
  border-radius: 9px;
  background: var(--surface);
  color: var(--muted);
  font-size: .82rem;
}
.cr-coverage-notes > summary {
  display: flex;
  align-items: baseline;
  gap: .45rem;
  cursor: pointer;
  padding: .45rem .7rem;
}
.cr-coverage-notes > summary strong { color: var(--text); }
.cr-coverage-notes[open] > summary { border-bottom: 1px solid var(--border); }
.cr-coverage-notes .recon-warnings { margin: .65rem .8rem .75rem; }
/* Margin on both sides. With `margin-top` alone, a collapsed panel and the
   card beneath it -- same surface, same border, no gap -- merged into one
   box with a seam through it, which read as two sections overlapping. */
.recon-audit { margin: 1.5rem 0; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); }
.recon-audit > summary { display: flex; align-items: baseline; gap: .8rem; flex-wrap: wrap; cursor: pointer; padding: .8rem 1rem; }
.recon-audit[open] > summary { border-bottom: 1px solid var(--border); }
.recon-audit > :not(summary) { padding: 0 1rem; }
.link-button { border: 0; background: none; padding: 0; color: var(--accent); font-weight: 700; text-align: left; }
.link-button:hover { text-decoration: underline; background: none; }

/* ---------- multi-read progress ----------
   The bill reconciliation starts six reads at once and they take minutes
   between them. A single spinner for all of that reads as hung, and a read
   that failed stayed invisible until the run finished. Each read says where
   it has got to instead. */
.run-progress { list-style: none; margin: 0; padding: 0; display: grid; gap: .55rem; }
.run-progress li {
  display: flex; align-items: baseline; gap: .75rem;
  font-size: .9rem; color: var(--muted);
}
.run-progress li[data-state="done"], .run-progress li[data-state="running"] { color: var(--text); }
.run-progress .dot {
  width: .55rem; height: .55rem; border-radius: 50%;
  background: var(--border); flex: none; align-self: center;
}
.run-progress li[data-state="running"] .dot { background: var(--accent); animation: pulse 1.3s ease-in-out infinite; }
.run-progress li[data-state="done"] .dot { background: #34d399; }
.run-progress li[data-state="failed"] .dot { background: var(--danger); }
.run-progress .name { flex: none; min-width: 17rem; }
.run-progress .detail { color: var(--muted); font-size: .82rem; }
.run-progress li[data-state="failed"] .detail { color: var(--danger); }

@keyframes pulse { 50% { opacity: .3; } }
@media (prefers-reduced-motion: reduce) {
  .run-progress li[data-state="running"] .dot { animation: none; }
}

@media (max-width: 700px) {
  .run-progress li { flex-wrap: wrap; }
  .run-progress .name { min-width: 0; }
}

/* ---------- Reconciliation: ad-hoc source, notes and saving ---------- */

/* Where the ad-hoc evidence comes from, said on the page that depends on it.
   It replaced a cURL panel; the point of the row is that there is nothing to
   do here any more, and a link to the place where there is. */
.cr-adhoc-source {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 1rem 1.25rem;
  margin-bottom: 1.25rem;
}
.cr-adhoc-source p { margin: .3rem 0 0; }

/* An anchor styled as a button. `.button.secondary` already exists for that,
   but only where the markup can carry two classes -- this is the one-class
   form the page-head rows use. */
.button-link {
  display: inline-block;
  border: 1px solid var(--border);
  border-radius: 9px;
  padding: .58rem .9rem;
  color: var(--muted);
  font-weight: 600;
  white-space: nowrap;
}
.button-link:hover { text-decoration: none; color: var(--text); }

.cr-adhoc-review { margin-top: 1.75rem; }
.cr-adhoc-review .recon-audit { margin-top: .75rem; }

.cr-note-button { padding: .35rem .7rem; font-size: .82rem; white-space: nowrap; }

.cr-save { display: block; margin-top: 1.75rem; }
.cr-save .field { margin-bottom: 1rem; }
.cr-save .field.actions { margin-left: 0; }
.cr-save textarea, .cr-save input[type="text"] {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 9px;
  color: var(--text);
  font: inherit;
  padding: .55rem .7rem;
  width: 100%;
}

/* The search box should take the row rather than sit at label width. */
.field.grow { flex: 1 1 24rem; }
.field.grow input[type="search"] {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 9px;
  color: var(--text);
  font: inherit;
  padding: .55rem .7rem;
  width: 100%;
}

/* A saved note is the reason somebody kept the reconciliation, so it reads as
   text rather than as another grey table cell. */
td.saved-note { color: var(--text); white-space: pre-wrap; }

#saved-rows td:last-child { display: flex; gap: .5rem; flex-wrap: wrap; }
#saved-rows td:last-child button { padding: .35rem .7rem; font-size: .82rem; }

/* Every unimported day, named. The list exists so a reader can find their own
   day in it, so it is never truncated -- a long range scrolls instead. */
.cr-missing-dates {
  display: flex;
  flex-wrap: wrap;
  gap: .3rem .55rem;
  max-height: 9rem;
  overflow-y: auto;
  margin: .5rem 0 0;
  font-variant-numeric: tabular-nums;
  font-size: .85rem;
  color: var(--muted);
}
.cr-missing-dates span {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: .12rem .4rem;
  white-space: nowrap;
}

/* A ticked activity tab with no saved read behind it.
 *
 * The checkbox says which tabs the workbook should carry; the read behind it
 * is a separate, expensive step. Ticking one and downloading before reading
 * gives a tab that says it was never read -- true, and discovered too late.
 * This marks the gap at the moment it is created instead.
 *
 * Appended as a new selector rather than folded into `.check`, which the Octup
 * review also uses and which is under sign-off. */
.check.unsaved {
  border-color: var(--warn, #b45309);
  background: rgba(180, 83, 9, .14);
}
.check.unsaved::after {
  content: "not read yet";
  font-size: .72rem;
  opacity: .8;
  margin-left: .25rem;
}

/* The ad-hoc tab is imported on another page, never read here, so the marker
   on that box has to name the thing that is actually missing. "Not read yet"
   points at a Read & save button that cannot fill it. */
.check.unsaved.imported::after { content: "not imported yet"; }

/* The third answer, and the one whose absence made this panel lie: nothing
   could be asked, so whether the tab was read is simply not known. Marked
   apart from `unsaved` and in a cooler colour, because "we could not find out"
   is not a warning about the export -- it is a fact about the read. */
.check.unknown {
  border-color: var(--muted);
  background: rgba(147, 164, 195, .12);
}
.check.unknown::after {
  content: "not known";
  font-size: .72rem;
  opacity: .8;
  margin-left: .25rem;
}

/* ---------- The ShipHero Bills workflow ---------- */

/* Numbered steps, revealed one by one. The number is the whole point of the
   treatment: this page's failure mode was never that a button was hard to
   find, it was that nothing said which button came first. */
.step > .section-content h2 { display: flex; align-items: center; gap: .6rem; }

.step-number {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.6rem;
  height: 1.6rem;
  border-radius: 50%;
  background: var(--accent);
  color: #fff;
  font-size: .82rem;
  font-weight: 700;
  flex: 0 0 auto;
}

/* A step that has not been reached yet is hidden rather than disabled, so the
   page is only ever as long as the work actually in front of somebody. */
.step[hidden] { display: none; }

/* `.primary` is deliberately not styled: a bare button already carries the
   accent, and a second rule for the same look would be one more place for the
   two to drift apart. It is written on the Go button as a statement of intent
   for whoever restyles this next. */

/* Term-and-value pairs -- what a batch holds, what ad hoc is imported. A grid
   rather than a table because there is one column of facts and no header to
   put over it, and a one-column table reads as a table with a missing half. */
.facts {
  display: grid;
  grid-template-columns: minmax(10rem, max-content) 1fr;
  gap: .4rem 1.25rem;
  margin: 0;
  padding: 0 1.25rem 1.25rem;
}
.facts[hidden] { display: none; }
.facts dt { color: var(--muted); font-size: .86rem; }
.facts dd { margin: 0; font-size: .92rem; }

/* The counted-and-selected line sits in the button row rather than under it,
   so the number moves where the eye already is when a box is ticked. */
.state.compact.inline { padding: 0; margin: 0; align-self: center; }

/* The one status colour the page was missing. `.warn` says "this will bite
   you"; this says "that is done", which on a six-step page is the more
   frequent thing to have to say. */
.state.ok {
  /* Laid out as a status line, like `.warn`, not as an empty-table
     placeholder: "Downloaded …" sat centred under three rems of padding. */
  padding: .8rem 1.25rem;
  text-align: left;
  color: var(--ok);
}

/* What a read had to say for itself, inside the step it belongs to. Not a
   `.card`: these sit *within* the step cards, and a card inside a card draws
   a box in a box. Amber and bordered because the sentences in here are the
   ones that explain a figure somebody is about to act on -- the same weight
   as `.warning-card` on the review page, without its outer chrome. */
.notes-card {
  margin: 0 1.25rem 1.25rem;
  padding: .85rem 1rem;
  border: 1px solid #d59a35;
  border-radius: 9px;
  background: #302816;
}
.notes-card[hidden] { display: none; }
.notes-card h3 { margin: 0 0 .5rem; font-size: .92rem; }
.notes-card ul { margin: 0; padding-left: 1.1rem; }
.notes-card li { color: var(--text); line-height: 1.5; font-size: .9rem; }
.notes-card li + li { margin-top: .45rem; }

/* Money and counts read right-aligned or they cannot be compared down a
   column, which is the only reason anybody scans one. */
th.numeric, td.numeric { text-align: right; font-variant-numeric: tabular-nums; }

/* A filter row that is only buttons does not need the field spacing meant for
   labelled inputs above them. */
.filters.compact { padding-top: 0; padding-bottom: .75rem; }

/* A fact and its provenance, stacked. Run together on one line -- a date and
   an email, a company and a label -- they make a column nobody can scan down,
   which on the saved-batches table is the only way it is read. */
td .sub {
  color: var(--muted);
  font-size: .8rem;
  margin-top: .15rem;
}

/* A yes/no fact as a shape rather than a sentence. These columns are read by
   running an eye down them for the row that is not like the others, and
   "not imported" beside "3 records" beside "none read" is three sentences to
   read where one badge would do. */
.pill {
  display: inline-block;
  padding: .15rem .5rem;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--surface-2);
  color: var(--muted);
  font-size: .78rem;
  white-space: nowrap;
}
.pill.ok {
  border-color: color-mix(in srgb, var(--ok) 45%, transparent);
  background: color-mix(in srgb, var(--ok) 14%, transparent);
  color: var(--ok);
}

/* The row's buttons, kept on one line and out of the way of the text. Without
   the nowrap they wrap one button per line as soon as the table is tight,
   which is exactly when the table is hardest to read. */
td.tools {
  white-space: nowrap;
  text-align: right;
}
td.tools button { margin-left: .35rem; }

/* ---------- The workbook viewer ---------- */

/* The tabs, which are the workbook's own. Scrollable rather than wrapped: a
   cut on charge type can be two dozen of them, and a strip that reflows into
   four rows moves every tab under the cursor each time one is picked. */
.tabstrip {
  display: flex;
  gap: .35rem;
  overflow-x: auto;
  padding: 0 0 .5rem;
  margin-bottom: 1rem;
  scrollbar-width: thin;
}
.tabstrip[hidden] { display: none; }

.tabstrip .tab {
  display: inline-flex;
  align-items: baseline;
  gap: .45rem;
  flex: 0 0 auto;
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--muted);
  font-size: .85rem;
  padding: .45rem .8rem;
  border-radius: 999px;
}
.tabstrip .tab:hover:not(:disabled) {
  background: var(--surface-2);
  border-color: var(--accent);
  color: var(--text);
}
.tabstrip .tab.current {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}

/* How many rows are behind a tab, on the tab. A workbook's tabs are not equal
   -- one holds fifteen thousand charges and one holds four bills -- and
   knowing which is which before opening it is most of navigating this. */
.tab-count {
  font-variant-numeric: tabular-nums;
  font-size: .75rem;
  opacity: .75;
}

/* A column heading that sorts. Styled as the heading it is rather than as a
   button: a row of buttons across the top of a table reads as a toolbar. */
th .sorter {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: inherit;
  cursor: pointer;
}
th .sorter:hover:not(:disabled) { background: none; text-decoration: underline; }
.sort-arrow { font-size: .7rem; opacity: .85; }

/* Where else the search matched. Buttons rather than links because they move
   within the page: a reader following one question across a workbook has not
   left the workbook. */
.hits { display: flex; flex-wrap: wrap; gap: .4rem; padding: 0 1.25rem 1.25rem; }
.hits .hit {
  display: inline-flex;
  align-items: baseline;
  gap: .45rem;
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text);
  font-size: .82rem;
  padding: .35rem .7rem;
  border-radius: 999px;
}
.hits .hit:hover:not(:disabled) { border-color: var(--accent); background: var(--surface-2); }

/* The matched text inside a cell. Marked rather than the whole row tinted:
   the question is which cell carried the match, and a tinted row answers
   "this one somewhere". */
mark {
  background: color-mix(in srgb, var(--accent) 40%, transparent);
  color: inherit;
  border-radius: 3px;
  padding: 0 .1rem;
}

/* The keyboard hint beside a label -- quieter than what it explains. */
.hint { color: var(--muted); font-weight: 400; font-size: .78rem; }

/* A long tab is scrolled, and a header that scrolls away with it leaves a
   grid of numbers under no columns. */
#workbook-table thead th {
  position: sticky;
  top: 0;
  z-index: 1;
}

/* The one control on the bills page that writes to ShipHero. Set apart on
   purpose: everything around it reads, and a button that raises a bill on a
   client's account should not look like the buttons that fetch one. */
.danger-zone {
  border: 1px solid color-mix(in srgb, var(--danger) 45%, transparent);
  border-radius: 10px;
  margin: 0 1.25rem 1.25rem;
  background: color-mix(in srgb, var(--danger) 7%, transparent);
}
.danger-zone h3 { margin-top: 0; color: var(--danger); }

/* ---------- The audit page ----------
   Colour carries the verdict, so the five states must stay distinguishable
   without it: each pill keeps its word, and the counts bar is labelled. A
   reader who cannot tell green from amber still reads "fail 0". */

.audit-clean { border-left: 4px solid #1a7f4b; }
.audit-failed { border-left: 4px solid #b42318; }

.audit-head-row {
  display: flex; flex-wrap: wrap; align-items: flex-start;
  justify-content: space-between; gap: 1rem;
}
.audit-head-row h2 { margin: 0 0 .2rem; }

.audit-counts { display: flex; flex-wrap: wrap; gap: .45rem; }
.audit-count {
  display: inline-flex; align-items: baseline; gap: .3rem;
  padding: .3rem .55rem; border-radius: 6px; font-size: .8rem;
  background: #f2f4f7; color: #475467; white-space: nowrap;
}
.audit-count b { font-size: 1rem; font-variant-numeric: tabular-nums; }

.audit-pill {
  display: inline-block; padding: .12rem .45rem; border-radius: 999px;
  font-size: .74rem; font-weight: 600; white-space: nowrap;
  background: #f2f4f7; color: #475467;
}
.audit-pass, .audit-count.audit-pass { background: #e7f6ed; color: #12603a; }
.audit-fail, .audit-count.audit-fail { background: #fde8e6; color: #912018; }
.audit-unavailable, .audit-count.audit-unavailable { background: #fff4e5; color: #93521a; }
.audit-skipped, .audit-count.audit-skipped { background: #f2f4f7; color: #475467; }
.audit-unaudited, .audit-count.audit-unaudited { background: #eef2ff; color: #3538cd; }

.audit-target {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: .82rem; word-break: break-all;
}
.audit-ms { text-align: right; font-variant-numeric: tabular-nums; }

.audit-changes { margin: .35rem 0 1rem; padding-left: 1.1rem; }
.audit-changes li { margin-bottom: .4rem; }
.audit-changes code {
  font-size: .82rem; word-break: break-all;
}
.audit-changes p { margin: .2rem 0 0; color: #475467; font-size: .85rem; }

/* ---------- Review page ---------- */

.review-finding {
  border-left: 3px solid #d0d5dd; padding: .1rem 0 .1rem .9rem; margin: 1rem 0;
}
.review-finding h3 {
  margin: 0 0 .35rem; font-size: .98rem; display: flex; gap: .5rem;
  align-items: baseline; flex-wrap: wrap;
}
.review-finding p { margin: .25rem 0; }
.review-finding details { margin-top: .5rem; }
.review-next { color: #475467; }

.review-high { border-left-color: #b42318; }
.review-medium { border-left-color: #b54708; }
.review-low { border-left-color: #475467; }
.review-info { border-left-color: #1a7f4b; }

.audit-pill.review-high, .audit-count.review-high { background: #fde8e6; color: #912018; }
.audit-pill.review-medium, .audit-count.review-medium { background: #fff4e5; color: #93521a; }
.audit-pill.review-low, .audit-count.review-low { background: #f2f4f7; color: #475467; }
.audit-pill.review-info, .audit-count.review-info { background: #e7f6ed; color: #12603a; }
.review-scope { margin: .3rem 0 0; text-align: right; }

.review-headline { margin: .35rem 0 0; font-weight: 600; }
.review-chips { display: flex; flex-wrap: wrap; gap: .4rem; margin: .2rem 0 .6rem; }
.review-chip {
  padding: .22rem .55rem; border-radius: 999px; font-size: .8rem;
  background: #fff4e5; color: #93521a; white-space: nowrap;
}
.review-chip-full { background: #e7f6ed; color: #12603a; }
.review-read > summary { cursor: pointer; color: #475467; font-size: .87rem; }

/* ---------- Revenue audit ---------- */

/* The queue's own row. Wider padding than a review finding and a rule down the
   left in the severity colour, because these are worked one at a time rather
   than read as a list -- a reviewer's eye has to be able to find where one
   ends and the next begins while scrolling past twenty of them. */
.finding-row {
  padding: .6rem 0 .9rem .9rem;
  border-bottom: 1px solid var(--line, #e4e7ec);
}
.finding-row:last-of-type { border-bottom: none; }

.finding-detector {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: .74rem; color: #667085; font-weight: 400;
}

/* The money, right-aligned and tabular so a column of them can be compared at
   a glance. `finding-money-none` is deliberately not styled to look like a
   number: a finding this app cannot price must not read as a small one. */
.finding-money {
  text-align: right; font-variant-numeric: tabular-nums;
  font-size: 1.25rem; font-weight: 600; white-space: nowrap;
}
.finding-money small {
  display: block; font-size: .68rem; font-weight: 400; letter-spacing: .04em;
  text-transform: uppercase; color: #667085;
}
.finding-money-none { font-size: .95rem; font-weight: 500; color: #667085; }

.finding-why { color: #475467; font-size: .9rem; }

.finding-refs {
  display: flex; flex-wrap: wrap; gap: .35rem; align-items: baseline;
  margin: .45rem 0 .2rem;
}
.finding-refs-label {
  font-size: .7rem; letter-spacing: .05em; text-transform: uppercase;
  color: #667085; margin-right: .15rem;
}
.finding-ref {
  font-size: .76rem; background: #eef2ff; color: #3538cd;
  padding: .1rem .35rem; border-radius: 4px; word-break: break-all;
}

.finding-facts { color: #475467; font-size: .88rem; margin: .3rem 0 0; }
.finding-notes { margin: .5rem 0 0; padding-left: 1.1rem; color: #475467; font-size: .88rem; }
.finding-notes li { margin: .2rem 0; }

/* The decision row. A plain form: three submit buttons, a reason and a note.
   No page script, because an accept that silently fails when a script did not
   load is the one failure this page cannot afford -- it is the only state in
   the application a re-run cannot rebuild. */
.finding-decide {
  display: flex; flex-wrap: wrap; gap: .45rem; align-items: center;
  margin-top: .6rem;
}
.finding-decide button {
  font-size: .82rem; padding: .32rem .7rem;
}
.finding-decide .finding-accept { background: #12603a; border-color: #12603a; }
.finding-decide select, .finding-decide input[type="text"] {
  font-size: .82rem; padding: .3rem .45rem; max-width: 15rem;
}
.finding-decided {
  margin: .45rem 0 0; font-size: .86rem; color: #475467;
  background: #f9fafb; border-left: 2px solid #d0d5dd; padding: .3rem .6rem;
}

.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* A figure that is not revenue reads quieter than one that is. Same size, so
   a column of them still lines up -- the point is that "billed above the
   evidence" must not be mistaken for money to invoice at a glance. */
.finding-money-other { color: #475467; }

/* The create-bills page's company picker: every company as a tickable chip,
   scrolled inside a box so sixty of them do not push the button off screen. */
.customer-picker {
  border: 0;
  margin: 0 1.25rem;
  padding: 0;
}
.customer-picker legend { font-weight: 600; padding: 0; }
.customer-picker legend span { font-weight: 400; color: var(--muted); margin-left: .4rem; }
.customer-list {
  display: flex;
  flex-wrap: wrap;
  gap: .4rem;
  max-height: 16rem;
  overflow-y: auto;
  padding: .25rem 0 .75rem;
}
#plan-box, #queue-box { margin: 0 1.25rem 1rem; }
#plan-rows td.reason { color: var(--muted); }
/* Customers: a row opens that customer's page. */
tr.clickable { cursor: pointer; }
tr.clickable:hover td { background: var(--surface-2); }
td.name a { color: inherit; text-decoration: none; }
td.name a:hover { text-decoration: underline; }
.tag-results { margin-top: 1.25rem; }
.tag-results td { vertical-align: top; }

/* ---------- Customer Review ---------- */

/* The line that says which read is on screen and when it was taken. It is the
   first thing a reviewer needs to trust the table, so it sits above the tabs. */
.review-freshness { padding: .9rem 1.25rem; margin-bottom: 0; }
.review-freshness p { margin: 0; }
.saved-period-list { display: flex; flex-wrap: wrap; gap: .4rem; margin-top: .5rem; }
.saved-period-list[hidden] { display: none; }
.saved-period { padding: .3rem .7rem; font-size: .82rem; }
.saved-period[aria-current="true"] { border-color: var(--accent); color: var(--text); }

.review-sources { margin: 0; padding: .8rem 1.25rem; color: var(--muted); font-size: .88rem; line-height: 1.5; border-bottom: 1px solid var(--border); }
.review-table tbody tr.openable-row { cursor: pointer; }
.review-table tbody tr.openable-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
/* "No purchase order" and its sibling heading are leftovers, not POs. */
.review-table tbody tr.catch-all td { color: var(--muted); font-style: italic; }
.review-table td.wrap { white-space: normal; min-width: 16rem; }
.review-table .row-actions button { padding: .3rem .7rem; font-size: .82rem; }
.review-unplaced { padding: 1rem 1.25rem; border-top: 1px solid var(--border); }
.review-unplaced h2 { font-size: 1rem; margin: 0 0 .3rem; }

.review-dialog {
  width: min(1200px, 96vw);
  max-height: 92vh;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
  box-shadow: var(--shadow);
}
.review-dialog::backdrop { background: rgba(3, 7, 18, .72); }
.review-dialog-head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 1rem;
  padding: 1rem 1.25rem;
  border-bottom: 1px solid var(--border);
  background: var(--surface-2);
}
.review-dialog-head h2 { margin: 0; font-size: 1.2rem; }
.review-dialog-head p { margin: .25rem 0 0; }
.review-dialog-body { padding: 0 1.25rem 1.25rem; overflow: auto; max-height: calc(92vh - 5rem); }
.review-dialog-body .summary-grid { grid-template-columns: repeat(6, minmax(0, 1fr)); margin: 1rem 0; }
.review-dialog-body .summary-grid strong { font-size: 1.15rem; }
.review-dialog-section { margin-top: 1.25rem; }
.review-dialog-section h3 { margin: 0 0 .25rem; font-size: 1rem; }
.review-dialog-section h4 { margin: 1rem 0 .4rem; font-size: .9rem; color: var(--muted); }
.review-dialog-section .table-wrap { max-height: 24rem; border: 1px solid var(--border); border-radius: 10px; }
.review-expected { margin: .4rem 0 0; }
@media (max-width: 900px) { .review-dialog-body .summary-grid { grid-template-columns: repeat(2, 1fr); } }
/* Fourteen columns outgrow the card on most screens, and `.table-wrap`
   scrolls -- so Details is pinned to the right edge rather than being the
   first thing scrolled out of reach, as on the saved list. */
.review-table { font-size: .88rem; }
.review-table tbody td, .review-table thead th { padding: .6rem .75rem; }
.review-table td.row-actions, #section-head th:last-child {
  position: sticky;
  right: 0;
  background: var(--surface);
  border-left: 1px solid var(--border);
}
#section-head th:last-child { z-index: 2; background: var(--surface-2); }
.review-table tbody tr:hover td.row-actions { background: var(--surface-2); }
#section-totals td:last-child { position: sticky; right: 0; background: var(--surface-2); border-left: 1px solid var(--border); }
.review-session { padding: 1rem 1.25rem; border-bottom: 1px solid var(--border); }
.review-session p { margin: 0 0 .6rem; line-height: 1.5; }
.review-session textarea { width: 100%; margin-bottom: .6rem; }
/* The rate-sheet comparison under the purchase order list on Bill Reconciliation. */
.recon-subhead { margin: 1.75rem 0 .5rem; font-size: 1rem; }

/* Delivery Intake: two stacked cards with a gap. */
.delivery-intake .card + .card { margin-top: 1.25rem; }
.delivery-intake .section-content h2 { margin-bottom: .35rem; }
.delivery-intake .section-content > p { margin: 0 0 1rem; max-width: 70ch; }
.delivery-intake .section-content > p.hint { margin: .75rem 0 0; font-size: .82rem; }
.delivery-intake .section-content > p.notice { margin: .75rem 0 0; }
.intake-run { display: flex; flex-wrap: wrap; align-items: flex-end; gap: .75rem; }

/* Delivery status: the summary card gets room and sits apart from the table;
   each subject goes under its customer and wraps, so every column fits. */
.delivery-status .status-summary {
  display: flex; flex-wrap: wrap; align-items: center; gap: .5rem;
  padding: 1rem 1.25rem; margin-bottom: 1.25rem;
}
.delivery-status .status-summary strong { margin-right: .35rem; }
.delivery-status .status-summary .hint { flex: 0 0 100%; margin: .4rem 0 0; line-height: 1.5; }
.delivery-status thead th, .delivery-status tbody td { padding-left: .7rem; padding-right: .7rem; }
.delivery-status td small { display: block; margin-top: .25rem; color: var(--muted); font-size: .8rem; }
.delivery-status td.subject { white-space: normal; min-width: 13rem; max-width: 20rem; }
.delivery-status td .button { padding: .4rem .75rem; font-size: .85rem; }
.delivery-status td.po { white-space: normal; min-width: 6rem; max-width: 9rem; }
.delivery-status td.subject small { line-height: 1.35; }
/* Deeper colours, so the choices stand out at a glance. */
.delivery-status .status-pill.match { background: #187e43; color: #fff; }
.delivery-status .status-pill.mismatch { background: #c73a32; color: #fff; }
.delivery-status .status-pill.needs_review { background: #e3a008; color: #1c1300; }
/* What each of the review step's downloads is, under the buttons. */
.download-guide { margin: .25rem 1.5rem 1.25rem; padding-left: 1.1rem; }
.download-guide li { margin: .25rem 0; }

/* The bill ShipHero is calculating right now, in the one-at-a-time queue. */
#queue-rows tr.current td { background: rgba(96, 165, 250, .08); color: var(--text); font-weight: 600; }
/* Inside the danger-zone card, whose red heading is for the warning at its
   top. Progress on bills already confirmed is not a danger. */
.danger-zone #queue-box h3 { margin: 1.25rem 0 .5rem; color: var(--text); }
/* A status line, not an empty-table placeholder. */
#queue-state { padding: .6rem 0; text-align: left; }

/* Settings: the clients whose rate card is not a tab in the workbook. */
#rate-card-unsheeted ul { margin: .4rem 0 0 1.1rem; padding: 0; }
#rate-card-unsheeted p { margin: 0; }

/* Create bills: a company, or an existing bill, that overlaps the dates
   chosen. Red-tinted and edged so it reads as "in the way" at a glance,
   without hiding the row the way `.discarded` dims a deleted bill. */
tr.conflict td { background: color-mix(in srgb, var(--danger) 12%, transparent); }
tr.conflict td:first-child { box-shadow: inset 3px 0 0 var(--danger); }
#existing-filters { margin: 0 1.25rem .5rem; align-items: flex-end; }
#existing-conflicts-field { align-self: center; }

/* A secondary button's hover kept its grey text over the solid accent fill,
   which read as disabled -- "Check what needs billing" looked switched off
   right after it was pressed. Hovered, it lifts like a ghost button instead. */
button.secondary:hover:not(:disabled), .button.secondary:hover {
  background: var(--surface-2);
  border-color: var(--accent-strong);
  color: var(--text);
}

/* Review tabs wrap rather than run off the side: twelve of them do not fit a
   phone, and a tab past the edge is a tab nobody finds. */
.review-tabs { flex-wrap: wrap; }

/* The top bar on a phone or small tablet: brand, menu and the signed-in user
   wrap onto lines of their own instead of pushing the page wider than the
   screen. The address is dropped -- the initial beside it says who it is. */
@media (max-width: 900px) {
  .topbar { height: auto; flex-wrap: wrap; row-gap: .5rem; padding-top: .6rem; padding-bottom: .6rem; }
  .topbar .nav { flex-wrap: wrap; row-gap: .5rem; order: 3; width: 100%; }
  .topbar .user { margin-left: auto; }
}
@media (max-width: 600px) {
  .topbar .user > span { display: none; }
}

/* Create bills: a reason in a plan row is read in full, not cut at the edge. */
#plan-table td.reason { white-space: normal; min-width: 16rem; }

/* Inbound pallets and pallet billing. Server-rendered forms sit inside table
   cells and month headers, so they lay out inline; a cell's notes go under
   its value, as on Delivery status. */
.inbound-pallets td small,
.inbound-pallet-billing td small { display: block; margin-top: .25rem; color: var(--muted); font-size: .8rem; }
.inbound-pallets td small.alert,
.inbound-pallet-billing td small.alert { color: var(--danger); }
.inbound-pallets ul.plain { list-style: none; margin: 0; padding: 0; }
.inbound-pallets ul.plain li + li { margin-top: .35rem; }
.inbound-pallets ul.plain small { display: inline; margin: 0; }
form.inline { display: inline-flex; flex-wrap: wrap; gap: .4rem; align-items: center; margin: 0; }
.inbound-pallet-billing td form { margin: 0 0 .3rem; }
.inbound-pallet-billing details.card > summary { cursor: pointer; line-height: 1.6; }
.inbound-pallet-billing .selection { color: var(--muted); font-size: .88rem; }
.inbound-pallet-billing tr.ignored td { opacity: .6; }
.inbound-pallet-billing tr.sent td { opacity: .8; }
.inbound-pallets > * + *,
.inbound-pallet-billing > * + * { margin-top: 1rem; }
.inbound-pallet-billing input[type="number"],
.inbound-pallet-billing input[type="month"] {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 9px;
  color: var(--text);
  padding: .5rem .7rem;
  font: inherit;
  font-size: .9rem;
  color-scheme: dark;
}
.inbound-pallet-billing input[type="number"]:focus,
.inbound-pallet-billing input[type="month"]:focus {
  outline: none;
  border-color: var(--accent-strong);
  box-shadow: 0 0 0 3px rgba(59, 130, 246, .2);
}
.inbound-pallet-billing input[type="date"] { color-scheme: dark; }
/* In a table cell the form controls stay compact, on one line where they fit. */
.inbound-pallet-billing td input[type="text"],
.inbound-pallet-billing td input[type="number"] { min-width: 0; padding: .35rem .5rem; }
.inbound-pallet-billing td form.inline { flex-wrap: nowrap; }
.inbound-pallet-billing td .ghost,
.inbound-pallet-billing td .secondary { padding: .35rem .7rem; white-space: nowrap; }
.inbound-pallets td .labels { margin-top: .6rem; display: grid; justify-items: start; gap: .3rem; white-space: nowrap; }
/* Their cards hold their own text, which `.card` alone doesn't pad: a head
   with the title (and its tools), a padded body, and tables full width
   between them. */
:is(.inbound-pallets, .inbound-pallet-billing) .card-head {
  display: flex; flex-wrap: wrap; align-items: center; gap: .5rem .75rem; padding: 1rem 1.25rem .85rem;
}
:is(.inbound-pallets, .inbound-pallet-billing) .card-head h2 { margin: 0; font-size: 1.1rem; }
:is(.inbound-pallets, .inbound-pallet-billing) .card-head .tools {
  margin-left: auto; display: flex; flex-wrap: wrap; gap: .5rem;
}
:is(.inbound-pallets, .inbound-pallet-billing) .card-body { padding: 1rem 1.25rem; margin: 0; }
:is(.inbound-pallets, .inbound-pallet-billing) .card-body > * { margin: 0; }
:is(.inbound-pallets, .inbound-pallet-billing) .card-body > * + * { margin-top: .8rem; }
:is(.inbound-pallets, .inbound-pallet-billing) .card-body .filters { padding: 0; align-items: center; gap: .5rem; }
:is(.inbound-pallets, .inbound-pallet-billing) .card-body .hint { font-size: .82rem; line-height: 1.55; max-width: 95ch; }
:is(.inbound-pallets, .inbound-pallet-billing) .card-head + .table-wrap,
:is(.inbound-pallets, .inbound-pallet-billing) .card-head + .card-body,
:is(.inbound-pallets, .inbound-pallet-billing) .table-wrap + .card-body { border-top: 1px solid var(--border); }
:is(.inbound-pallets, .inbound-pallet-billing) th.number { text-align: right; }
:is(.inbound-pallets, .inbound-pallet-billing) .page-head .tools { margin-left: auto; }
.inbound-pallets .page-head p,
.inbound-pallet-billing .page-head p { max-width: 75ch; margin-top: .35rem; }
.inbound-pallets .state-ok { color: var(--ok); }
.inbound-pallets .step-buttons { gap: .5rem; }
/* The stage counts, as tiles: a stage with nothing in it steps back. */
:is(.inbound-pallets, .inbound-pallet-billing) .tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr)); gap: .75rem; }
:is(.inbound-pallets, .inbound-pallet-billing) .tile {
  display: flex; flex-direction: column; gap: .2rem; padding: .8rem 1rem;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: 10px;
}
:is(.inbound-pallets, .inbound-pallet-billing) .tile.total { border-color: var(--accent-strong); }
:is(.inbound-pallets, .inbound-pallet-billing) .tile-number { font-size: 1.6rem; font-weight: 700; line-height: 1.1; font-variant-numeric: tabular-nums; }
:is(.inbound-pallets, .inbound-pallet-billing) .tile-label { color: var(--muted); font-size: .82rem; }
:is(.inbound-pallets, .inbound-pallet-billing) .tile.quiet .tile-number { color: var(--muted); font-weight: 500; }
.inbound-pallets .count-badge {
  min-width: 1.7rem; padding: .12rem .5rem; border-radius: 999px; text-align: center;
  background: var(--surface-2); color: var(--muted); font-size: .8rem; font-weight: 700;
}
.inbound-pallets .count-badge.alert { background: rgba(252, 211, 77, .14); color: var(--warn); }
/* A month: its summary is the header line, the figures and buttons below it. */
.inbound-pallet-billing details.card > summary {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: .25rem 1rem; padding: 1rem 1.25rem;
}
.inbound-pallet-billing details.card[open] > summary { border-bottom: 1px solid var(--border); }
/* A flex summary loses the browser's marker, so it draws its own. */
.inbound-pallet-billing details.card > summary { list-style: none; }
.inbound-pallet-billing details.card > summary::-webkit-details-marker { display: none; }
.inbound-pallet-billing details.card > summary::before { content: "▸"; color: var(--muted); }
.inbound-pallet-billing details.card[open] > summary::before { content: "▾"; }
.inbound-pallet-billing .month-tally { color: var(--muted); font-size: .9rem; }
.inbound-pallet-billing .month-tally strong { color: var(--text); }
.inbound-pallet-billing .plain-list { margin: .4rem 0 0; padding-left: 1.2rem; color: var(--muted); }
.inbound-pallet-billing .not-sent {
  padding: .75rem 1rem; border: 1px solid var(--border); border-radius: 10px; background: var(--surface-2);
  font-size: .9rem;
}
/* Nothing to charge: still listed (it explains the day), but quieter. */
.inbound-pallet-billing tr.zero td { color: var(--muted); }
.inbound-pallet-billing .late-tag { color: var(--warn); }
/* The billing rows fit the card: a delivery can have 60 pallets, so a row's
   LPN list stops at two lines (the whole list is on its hover), and the
   quantity form and cells stay compact. */
.inbound-pallet-billing small.lpns {
  max-width: 15rem; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow-wrap: anywhere; white-space: normal;
}
.inbound-pallet-billing .charges td { padding-left: .55rem; padding-right: .55rem; vertical-align: top; }
.inbound-pallet-billing .charges input[name="quantity"] { width: 4.2em; }
.inbound-pallet-billing .charges input[name="reason"] { width: 6.5em; }
.inbound-pallet-billing .charges td:last-child { min-width: 9rem; }
.inbound-pallets .setup-row { display: flex; flex-wrap: wrap; align-items: baseline; gap: .4rem .75rem; margin: 0 0 .6rem; }
.inbound-pallets .setup-row:last-child { margin-bottom: 0; }
.inbound-pallet-billing .card-body .filters :is(select, input[type="text"]) { min-width: 10rem; }

:is(.inbound-pallets, .inbound-pallet-billing) .tile.warn { border-color: var(--warn); }
:is(.inbound-pallets, .inbound-pallet-billing) .tile.warn .tile-number { color: var(--warn); }
/* Labelled controls: the label above, so a filter row says what each box is. */
:is(.inbound-pallets, .inbound-pallet-billing) label.field { display: grid; gap: .3rem; }
:is(.inbound-pallets, .inbound-pallet-billing) label.field > span { color: var(--muted); font-size: .78rem; font-weight: 600; }
:is(.inbound-pallets, .inbound-pallet-billing) .card-body .filters { align-items: flex-end; }
.inbound-pallet-billing .why-not { display: block; margin: 0; color: var(--muted); font-size: .75rem; white-space: nowrap; }
.inbound-pallets .pills { display: flex; flex-wrap: wrap; gap: .3rem; }

/* Keep delivery states and actions aligned across variable-length subjects. */
.delivery-status tbody td { vertical-align: top; }
.delivery-status td:first-child { white-space: nowrap; }
.delivery-status .delivery-choice { min-width: 9rem; }
.delivery-status .delivery-choice .status-pill { white-space: normal; }
.delivery-status .delivery-processing { min-width: 10rem; }
.delivery-status .delivery-processing > small { line-height: 1.4; }
.delivery-status .delivery-processing small small { margin-top: 0; }
.delivery-status .delivery-actions { width: 1%; white-space: nowrap; }
.delivery-status .delivery-actions .button { min-width: 7.5rem; text-align: center; white-space: nowrap; }
.delivery-details { color: var(--muted); font-size: .85rem; margin: .5rem 0 1rem; }
.delivery-details summary { cursor: pointer; }
.delivery-details p { max-width: 75ch; margin: .5rem 0; }
.delivery-status .status-summary .delivery-details { flex-basis: 100%; margin: 0; }
.delivery-intake .intake-run { margin-top: 1rem; }

/* Inbound pallets: one compact row per delivery; details stay optional. */
.inbound-pallets .page-head .tools { display: flex; gap: 1rem; align-items: center; font-size: .9rem; }
.inbound-pallets .pallet-automation { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem 1rem; color: var(--muted); font-size: .85rem; }
.inbound-pallets .pallet-all-clear { color: var(--ok); font-size: .9rem; margin: 1rem 0; }
.inbound-pallets .pallet-attention { border-color: var(--warn); }
.inbound-pallets .pallet-issue + .pallet-issue { border-top: 1px solid var(--border); padding-top: .8rem; margin-top: .8rem; }
.inbound-pallets .pallet-issue p { margin: .35rem 0; font-size: .9rem; }
.inbound-pallets .pallet-issue details { color: var(--muted); font-size: .8rem; overflow-wrap: anywhere; }
.inbound-pallets .pallet-deliveries > table > tbody > tr > td { vertical-align: top; padding: 1rem; }
.inbound-pallets .pallet-delivery { min-width: 13rem; }
.inbound-pallets .pallet-delivery strong { font-size: 1rem; }
.inbound-pallets .pallet-deliveries td small { display: block; font-size: .78rem; color: var(--muted); margin-top: .3rem; }
.inbound-pallets .pallet-labels { min-width: 10rem; }
.inbound-pallets .pallet-labels .button { padding: .4rem .7rem; font-size: .85rem; }
.inbound-pallets .pallet-labels small.alert { color: var(--warn); }
.inbound-pallets .pallet-list { min-width: 8rem; font-size: .85rem; }
.inbound-pallets summary { cursor: pointer; }
.inbound-pallets .pallet-list > summary { color: var(--accent-strong); white-space: nowrap; }
.inbound-pallets .pallet-list-scroll { max-height: 24rem; overflow: auto; margin-top: .7rem; border: 1px solid var(--border); border-radius: .5rem; }
.inbound-pallets .pallet-list-scroll th { position: sticky; top: 0; background: var(--surface); z-index: 1; }
.inbound-pallets .pallet-list-scroll td, .inbound-pallets .pallet-list-scroll th { padding: .6rem; font-size: .8rem; }
.inbound-pallets .pallet-list-scroll td:last-child { max-width: 18rem; white-space: normal; overflow-wrap: anywhere; }
.inbound-pallets .pallet-key { color: var(--muted); font-size: .85rem; margin: 1rem 0 1.5rem; }
.inbound-pallets .pallet-key p { max-width: 75ch; line-height: 1.6; }
.inbound-pallets details.pallet-admin > summary { padding: 1rem 1.25rem; font-size: .95rem; font-weight: 600; }
.inbound-pallets details.pallet-admin[open] > summary { border-bottom: 1px solid var(--border); }
.inbound-pallets .pallet-admin .card { border: 0; border-radius: 0; box-shadow: none; margin: 0; background: transparent; }
.inbound-pallets .pallet-admin .card + .card { border-top: 1px solid var(--border); }
@media (max-width: 700px) {
  .inbound-pallets .page-head .tools { margin-left: 0; }
  .inbound-pallets .tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.inbound-pallets .pallet-expanded { display: none; }
.inbound-pallets .pallet-summary:has(.pallet-list[open]) + .pallet-expanded { display: table-row; }
.inbound-pallets .pallet-expanded .pallet-list-scroll { margin: 0; }
.inbound-pallets .pallet-scope { display: flex; flex-wrap: wrap; align-items: center; gap: 1rem; }
.inbound-pallets .pallet-scope label { display: flex; align-items: center; gap: .65rem; font-weight: 600; }
