/* ══════════════════════════════════════════════════════════════════════════
   TALLY — theme bridge  (loads AFTER style.css)

   style.css was written dark-first for "Contento's Database" and drives ~640
   colour decisions through its own variables (--bg, --surface, --primary, …).
   Rather than rewrite 4,800 lines at once, this layer re-points those variables
   at the brand tokens in batsford-tokens.css. Everything already using a
   variable comes across for free; the ~185 hardcoded hex values and ~319 rgba()
   literals still in style.css do not, and are Phase 2.

   Light is now the default, per BRAND.md — the dark block below keeps the old
   theme working for anyone who prefers it, in brand colours rather than red.

   Structure:
     1. Legacy variable bridge (light + dark)
     2. Base — background, type
     3. Shell — icon rail, mobile header
     4. Dashboard
     5. Shared components touched by the above
   ══════════════════════════════════════════════════════════════════════════ */

/* ── 1. Legacy variable bridge ─────────────────────────────────────────── */
:root,
html[data-theme="light"] {
  --bg:               var(--bf-bg);
  --surface:          var(--bf-surface);
  --surface-solid:    var(--bf-surface);
  --surface-raised:   var(--bf-surface-alt);
  --surface-overlay:  var(--bf-surface);
  --border:           var(--bf-border);
  --border-subtle:    var(--bf-border);
  --border-red:       var(--bf-coral-tint);
  --text:             var(--bf-text);
  --text-secondary:   var(--bf-text-muted);
  --text-muted:       var(--bf-text-muted);

  --primary:          var(--bf-accent);
  --primary-hover:    #C9553E;
  --primary-glow:     rgba(224, 100, 75, 0.20);
  --primary-subtle:   var(--bf-coral-tint);

  --danger:           var(--bf-danger);
  --danger-hover:     #D95740;
  --danger-subtle:    var(--bf-danger-tint);
  --success:          var(--bf-success);
  --success-subtle:   var(--bf-success-tint);
  --warning:          var(--bf-warning);
  --warning-subtle:   var(--bf-warning-tint);

  --radius:           var(--bf-radius);
  --radius-lg:        var(--bf-radius-lg);
  --radius-sm:        var(--bf-radius-sm);
  --shadow:           var(--bf-shadow);
  --shadow-md:        var(--bf-shadow);
  --shadow-lg:        var(--bf-shadow-soft);

  --font-sans:        var(--bf-font-body);
  --font-mono:        var(--bf-font-mono);

  /* The rail is a branded ink surface in both themes — same as the brand's
     reference dashboard, and the reason the mark ships in a paper glyph. */
  --rail-bg:          var(--bf-ink);
  --rail-surface:     #24262E;
  --rail-text:        #C9C4B8;
  --rail-text-strong: var(--bf-paper);
  --rail-divider:     rgba(244, 241, 234, 0.10);
}

html[data-theme="dark"] {
  --bg:               var(--bf-bg);
  --surface:          var(--bf-surface);
  --surface-solid:    var(--bf-surface);
  --surface-raised:   var(--bf-surface-alt);
  --surface-overlay:  var(--bf-surface);
  --border:           var(--bf-border);
  --border-subtle:    var(--bf-border);
  --text:             var(--bf-text);
  --text-secondary:   var(--bf-text-muted);
  --text-muted:       var(--bf-text-muted);
  --primary:          var(--bf-accent);
  --primary-hover:    #F0836B;
  --primary-subtle:   var(--bf-coral-tint);
  --rail-bg:          var(--bf-surface-sunk);
  --rail-surface:     var(--bf-surface-alt);
}

/* ── 2. Base ───────────────────────────────────────────────────────────── */
/* The old sheet painted eight radial gradients of red "marble" onto the body.
   Nothing subtle survives on top of that, so it goes. */
body {
  background: var(--bf-bg);
  background-image: none;
  color: var(--bf-text);
  font-family: var(--bf-font-body);
}

/* Headings and anything numeric move to DM Mono — the mono + tabular figures
   are the identity, and this app is mostly weights, counts and dollars. */
h1, h2, h3,
.dashboard-header h2,
.dash-chart-card h3 {
  font-family: var(--bf-font-mono);
  font-weight: 500;
  letter-spacing: -0.3px;
}

/* ── 3. Shell: the icon rail ───────────────────────────────────────────── */
.sidebar,
.sidebar.rail {
  background: var(--rail-bg);
  border-right: 1px solid var(--rail-divider);
  box-shadow: none;
  backdrop-filter: none;
}

.rail-brand { padding: 8px 0 14px; }
.rail-mark {
  display: block;
  width: 40px;
  height: auto;
  /* BRAND.md: clear space of at least one beam-height on every side. */
  padding: 3px 2px;
}

.rail-item {
  color: var(--rail-text);
  border-radius: var(--bf-radius-sm);
  font-family: var(--bf-font-mono);
}
.rail-item:hover {
  background: var(--rail-surface);
  color: var(--rail-text-strong);
}
/* Active state is the signature square, not a red wash. */
.rail-item.rail-active {
  background: var(--rail-surface);
  color: var(--rail-text-strong);
  box-shadow: none;
}
.rail-item.rail-active::before {
  content: "";
  position: absolute;
  /* The rail is 64px and the button 42px, so this sits in the 11px gutter —
     any further left and it touches the edge. */
  left: -7px;
  top: 50%;
  transform: translateY(-50%);
  width: 9px;
  height: 9px;
  border-radius: 2px;
  background: var(--bf-accent-2);
}
.rail-div { background: var(--rail-divider); }
.rail-badge {
  background: var(--bf-accent);
  font-family: var(--bf-font-mono);
  font-weight: 500;
}
.rail-item[data-tip]::after {
  background: var(--rail-surface);
  border-color: var(--rail-divider);
  font-family: var(--bf-font-mono);
  font-size: 12px;
  font-weight: 400;
  box-shadow: var(--bf-shadow);
}
.rail-version {
  font-family: var(--bf-font-mono);
  color: var(--rail-text);
  opacity: 0.55;
}

/* Flyouts share the rail's ink surface in both themes. */
html[data-theme="light"] .rail-flyout,
.rail-flyout { background: var(--rail-bg); border-right-color: var(--rail-divider); }
.rail-flyout-head {
  font-family: var(--bf-font-mono);
  font-weight: 500;
  color: var(--rail-text-strong);
  border-bottom-color: var(--rail-divider);
}
.rail-flyout .table-list li { color: var(--rail-text); }
.rail-flyout .table-list li:hover { background: var(--rail-surface); color: var(--rail-text-strong); }
.rail-flyout .table-list li.active {
  background: var(--rail-surface);
  color: var(--rail-text-strong);
  box-shadow: inset 3px 0 0 var(--bf-accent-2);
}

/* Mobile header carries the mark too. */
.mobile-header { background: var(--rail-bg); border-bottom: 1px solid var(--rail-divider); }
.mobile-header .hamburger { color: var(--rail-text-strong); }
.rail-mark-sm { display: block; width: 30px; height: auto; }

/* ── 4. Dashboard ──────────────────────────────────────────────────────── */
.dashboard-header h2 { font-size: var(--bf-fs-display); }

/* Eyebrow above the page title — uppercase, tracked, coral (BRAND.md §3). */
.bf-eyebrow {
  font-family: var(--bf-font-mono);
  font-size: var(--bf-fs-label);
  letter-spacing: var(--bf-track-label);
  text-transform: uppercase;
  color: var(--bf-accent);
  font-weight: 500;
  margin-bottom: 4px;
}

/* KPI cards become the brand's soft tinted cards. The old sheet drove their
   accent through an inline --stat colour; that's now the tint itself. */
.dash-stat-card {
  background: var(--stat-tint, var(--bf-teal-tint));
  border: none;
  border-radius: var(--bf-radius-lg);
  padding: 18px 18px 16px;
  box-shadow: none;
}
.dash-stat-card::before,
.dash-stat-card::after { display: none; }

/* Six cards never orphan: 6 across, then 3, then 2. The old sheet hardcoded
   five columns above 1400px, which left the sixth card stranded on its own row. */
.dash-summary { grid-template-columns: repeat(2, 1fr); gap: 16px; }
@media (min-width: 700px)  { .dash-summary { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 1200px) { .dash-summary { grid-template-columns: repeat(6, 1fr); gap: 16px; } }

.dash-stat-value {
  font-family: var(--bf-font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 30px;
  font-weight: 500;
  letter-spacing: -0.5px;
  /* The old value was a red gradient clipped to the text; -webkit-text-fill-color
     beats `color`, so both have to be unwound before ink will show. */
  background: none;
  -webkit-background-clip: border-box;
  background-clip: border-box;
  -webkit-text-fill-color: currentColor;
  color: var(--bf-text);
}
@media (min-width: 1400px) { .dash-stat-value { font-size: 30px; } }
@media (min-width: 1800px) { .dash-stat-value { font-size: 34px; } }
.dash-stat-unit { font-size: 15px; color: var(--bf-text-muted); margin-left: 1px; }
.dash-stat-label {
  font-family: var(--bf-font-mono);
  font-size: var(--bf-fs-label);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--bf-text-muted);
  font-weight: 400;
}
.dash-stat-flag {
  font-family: var(--bf-font-mono);
  font-size: 11px;
  background: var(--bf-danger-tint);
  color: var(--bf-danger);
  border-radius: var(--bf-radius-pill);
  padding: 3px 9px;
  margin-left: 8px;
  vertical-align: middle;
}

/* Panels — warm white, hairline, soft shadow. */
.dash-chart-card {
  background: var(--bf-surface);
  border: 1px solid var(--bf-border);
  border-radius: var(--bf-radius-lg);
  box-shadow: var(--bf-shadow);
}
/* Section headers carry the signature square. */
.dash-chart-card h3 {
  display: flex;
  align-items: center;
  gap: 9px;
  font-size: var(--bf-fs-h3);
  color: var(--bf-text);
}
.dash-chart-card h3::before {
  content: "";
  width: 9px;
  height: 9px;
  border-radius: 2px;
  background: var(--bf-accent-2);
  flex: none;
}
.dash-pill-warn {
  font-family: var(--bf-font-mono);
  font-size: 11px;
  font-weight: 400;
  background: var(--bf-warning-tint);
  color: var(--bf-warning);
  border-radius: var(--bf-radius-pill);
  padding: 3px 10px;
}

/* Upcoming pickups list */
.dash-activity-row { border-bottom: 1px solid var(--bf-border); }
.dash-activity-row:hover { background: var(--bf-surface-alt); }
.dash-activity-source { font-family: var(--bf-font-body); font-weight: 500; color: var(--bf-text); }
.dash-activity-detail { color: var(--bf-text-muted); }
.dash-activity-date {
  font-family: var(--bf-font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--bf-text-muted);
}
.dash-date-overdue, .dash-row-overdue .dash-activity-date { color: var(--bf-danger); }

/* ── 5. Shared components in reach of the dashboard ────────────────────── */
.btn {
  font-family: var(--bf-font-mono);
  font-weight: 400;
  border-radius: var(--bf-radius-pill);
}

/* Ready to Invoice: the action columns were hand-measured against Inter. DM Mono
   is fixed-advance at 0.6em — wider per character — so the labels outgrew their
   columns and, with .btn's `white-space: nowrap`, spilled out of the pill:
   "Mark statement invoiced" lost a character off each end. Widths below are the
   measured intrinsic width of each cell plus a few px, and mono's fixed advance
   means they stay correct. Card mode (≤768px) stacks these, so it's untouched. */
@media (min-width: 769px) {
  .rti-cols, .rti-row { grid-template-columns: 1.3fr 0.55fr 0.95fr 0.8fr 0.7fr 1.9fr 1fr 160px; }
  .rti-row.rti-stmt { grid-template-columns: 1.5fr 1.6fr 0.9fr 1fr 292px; }
  .rti-cols.rti-direct, .rti-row.rti-direct { grid-template-columns: 1.6fr 1fr 0.9fr 0.8fr 1fr 184px; }
}
.btn-primary { background: var(--bf-accent); color: #fff; border-color: transparent; }
.btn-primary:hover { background: var(--bf-accent); filter: brightness(0.95); }
.btn-ghost { color: var(--bf-text-muted); }
.btn-ghost:hover { background: var(--bf-surface-alt); color: var(--bf-text); }

/* Data tables — clarity first. Warm sunk header, tabular numerics, no red. */
.data-table th {
  font-family: var(--bf-font-mono);
  font-size: var(--bf-fs-label);
  font-weight: 400;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  background: var(--bf-surface-sunk);
  color: var(--bf-text-muted);
}
html[data-theme="light"] .main .data-table th { color: var(--bf-text-muted); }
.data-table td { border-bottom-color: var(--bf-border); }

input, textarea, select {
  font-family: var(--bf-font-body);
  border-radius: var(--bf-radius-sm);
}
input:focus, textarea:focus, select:focus {
  outline: 2px solid var(--bf-focus);
  outline-offset: 1px;
  border-color: transparent;
}

/* Permit reports: a neutral sibling to .nys-review. Material that's catalogued but
   isn't hauler-permit waste is correctly excluded — showing it in warning amber
   taught people to dismiss the banner that does need acting on. */
.nys-note {
  border: 1px solid var(--bf-border); background: var(--bf-surface-alt); color: var(--bf-text-muted);
  border-radius: 10px; padding: 12px 16px; margin-bottom: 14px; font-size: 13px; line-height: 1.5;
}
.nys-note strong { color: var(--bf-text); font-weight: 500; }
.nys-note ul { margin: 6px 0 4px 18px; }

.toast { font-family: var(--bf-font-body); border-radius: var(--bf-radius); }
.spinner { border-top-color: var(--bf-accent); }

/* ══════════════════════════════════════════════════════════════════════════
   TIMELINE DISPATCH BOARD
   Unassigned pool on the left, driver columns against a clock on the right.
   ══════════════════════════════════════════════════════════════════════════ */

.dp-viewtoggle {
  display: inline-flex; background: var(--bf-surface-sunk);
  border-radius: var(--bf-radius-pill); padding: 2px; gap: 2px;
}
.dp-viewbtn {
  font-family: var(--bf-font-mono); font-size: 12px; padding: 6px 13px;
  border: 0; border-radius: var(--bf-radius-pill); background: transparent;
  color: var(--bf-text-muted); cursor: pointer; line-height: 1;
}
.dp-viewbtn.active { background: var(--bf-surface); color: var(--bf-text); box-shadow: var(--bf-shadow); }

.dp-tl { display: grid; grid-template-columns: clamp(180px, 17vw, 250px) 1fr; gap: 14px; align-items: start; }

/* ── Unassigned pool ──────────────────────────────────────────────────── */
.dp-tl-pool {
  background: var(--bf-surface); border: 1px solid var(--bf-border);
  border-radius: var(--bf-radius-lg); overflow: hidden;
  display: flex; flex-direction: column; max-height: 78vh;
}
.dp-tl-poolhead {
  display: flex; align-items: center; justify-content: space-between;
  padding: 11px 14px; background: var(--bf-surface-sunk);
  font-family: var(--bf-font-mono); font-size: var(--bf-fs-label);
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--bf-text-muted);
}
.dp-tl-poolcount {
  background: var(--bf-coral-tint); color: var(--bf-accent);
  border-radius: var(--bf-radius-pill); padding: 1px 9px; font-size: 11px;
}
.dp-tl-poolbody { padding: 9px; overflow-y: auto; flex: 1; min-height: 120px; display: grid; gap: 7px; align-content: start; }
.dp-tl-poolfoot { padding: 9px; border-top: 1px solid var(--bf-border); display: flex; gap: 6px; }
.dp-tl-poolfoot .dp-add-input { flex: 1; min-width: 0; font-size: 13px; }

/* ── Board ────────────────────────────────────────────────────────────── */
.dp-tl-boardwrap { overflow-x: auto; padding-bottom: 6px; }
.dp-tl-board { display: flex; align-items: stretch; gap: 0; min-width: min-content; }
.dp-tl-gutter { flex: none; width: 58px; position: sticky; left: 0; z-index: 3; background: var(--bf-bg); }
.dp-tl-hourlabel {
  font-family: var(--bf-font-mono); font-size: 11px; color: var(--bf-text-muted);
  text-align: right; padding-right: 7px; border-top: 1px solid var(--bf-border);
  box-sizing: border-box; padding-top: 2px;
}
.dp-tl-col {
  /* Narrow enough that four drivers fit a laptop, wide enough for a customer
     name. Below the floor the board scrolls rather than squeezing text away. */
  flex: 1 0 clamp(150px, 15vw, 215px);
  min-width: clamp(150px, 15vw, 215px);
  border-left: 1px solid var(--bf-border);
}
.dp-tl-colhead {
  display: flex; align-items: center; gap: 7px; padding: 8px 10px;
  border-bottom: 1px solid var(--bf-border); background: var(--bf-surface-sunk);
  height: 42px; box-sizing: border-box;
}
.dp-tl-gutterhead { background: transparent; border-bottom-color: transparent; }
.dp-tl-name { font-family: var(--bf-font-mono); font-size: 13px; color: var(--bf-text); }
.dp-tl-load {
  margin-left: auto; font-family: var(--bf-font-mono); font-size: 10px;
  color: var(--bf-text-muted); white-space: nowrap;
}

/* Assigned but not timed — parked above the clock so nothing goes missing. */
/* Fixed height, not min-height: a driver with more untimed work than the others
   would otherwise make their column taller and knock every clock grid out of
   alignment with its neighbours. Overflow scrolls inside the strip instead. */
.dp-tl-untimed {
  height: 58px; overflow-y: auto; padding: 5px; display: grid; gap: 5px;
  align-content: start; background: var(--bf-surface-alt);
  border-bottom: 1px dashed var(--bf-border); box-sizing: border-box;
}
.dp-tl-gutteruntimed {
  background: transparent; border-bottom-color: transparent; overflow: visible;
  font-family: var(--bf-font-mono); font-size: 9px; color: var(--bf-text-muted);
  text-align: right; padding-right: 7px; align-content: center;
}
.dp-tl-untimed-empty {
  font-family: var(--bf-font-mono); font-size: 10px; color: var(--bf-text-muted);
  opacity: .7; padding: 6px 2px; text-align: center;
}
.dp-tl-untimed.dp-over, .dp-tl-poolbody.dp-over { background: var(--bf-teal-tint); }

/* The clock grid */
.dp-tl-grid { position: relative; }
/* Height is set from JS so it always matches the pixel maths behind the cards. */
.dp-tl-slot { border-top: 1px dashed var(--bf-border); box-sizing: border-box; }
.dp-tl-slot.dp-tl-hour { border-top-style: solid; }
.dp-tl-cards { position: absolute; inset: 0; }
.dp-tl-nodrivers {
  padding: 34px 20px; color: var(--bf-text-muted); font-size: 13px;
  border-left: 1px solid var(--bf-border); flex: 1;
}

/* ── Job cards ────────────────────────────────────────────────────────── */
.dp-tl-card {
  background: var(--bf-surface); border: 1px solid var(--bf-border);
  border-left: 3px solid var(--bf-text-muted);
  border-radius: var(--bf-radius-sm); padding: 4px 7px; cursor: pointer;
  overflow: hidden; font-size: 12.5px; line-height: 1.3;
  box-shadow: 0 1px 2px rgba(31,32,36,.05);
}
.dp-tl-cards .dp-tl-card { position: absolute; }
.dp-tl-card:hover { border-color: var(--bf-accent); }
.dp-tl-card.dp-tlt-del { border-left-color: var(--bf-accent-2); }
.dp-tl-card.dp-tlt-pick { border-left-color: var(--bf-accent); }
.dp-tl-card.dp-tlt-custom { border-left-color: var(--bf-sand); }
.dp-tl-card.dp-done { opacity: .55; }
.dp-tl-card.dp-late { background: var(--bf-danger-tint); }
.dp-tl-card.dp-dragging { opacity: .4; }
.dp-tl-top { display: flex; align-items: center; gap: 5px; margin-bottom: 1px; min-width: 0; }
/* Never wrap: in a narrow overlap column "PICK UP" was breaking across lines
   and stealing the row the customer name needed. */
.dp-tl-top > * { white-space: nowrap; }
.dp-tl-type { overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.dp-tl-type {
  font-family: var(--bf-font-mono); font-size: 10px; letter-spacing: .04em;
  text-transform: uppercase; color: var(--bf-text-muted);
}
.dp-tl-time {
  margin-left: auto; font-family: var(--bf-font-mono); font-size: 11px;
  font-variant-numeric: tabular-nums; color: var(--bf-text-muted);
}
.dp-tl-late { color: var(--bf-danger); font-size: 10px; }
.dp-tl-done { color: var(--bf-success); font-size: 10px; }
.dp-tl-cust {
  font-weight: 500; color: var(--bf-text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.dp-tl-sub, .dp-tl-cont {
  color: var(--bf-text-muted); font-size: 11.5px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.dp-tl-cont { font-family: var(--bf-font-mono); }

/* Drop preview — shows the snapped time before you let go. */
.dp-tl-drop {
  position: absolute; left: 0; right: 3px; z-index: 5; pointer-events: none;
  border: 1.5px dashed var(--bf-accent); border-radius: var(--bf-radius-sm);
  background: var(--bf-coral-tint); opacity: .85;
  font-family: var(--bf-font-mono); font-size: 10px; color: var(--bf-accent);
  padding: 3px 6px;
}

@media (max-width: 900px) {
  /* The timeline is a desktop tool — HTML5 drag has no touch support, and the
     grid can't shrink usefully. Phones get the lanes board and tap-to-assign. */
  .dp-tl { grid-template-columns: 1fr; }
  .dp-tl-pool { max-height: none; }
}

/* ── Calendar: week view ──────────────────────────────────────────────── */
.cal-viewtoggle {
  display: inline-flex; background: var(--bf-surface-sunk);
  border-radius: var(--bf-radius-pill); padding: 2px; gap: 2px; margin-left: 8px;
}
/* stretch, not start: a ragged row of different-height day cards reads as broken,
   and the per-day "Dispatch \2192" action should sit on one line across the week. */
.cal-week { display: grid; grid-template-columns: repeat(7, 1fr); gap: 8px; align-items: stretch; }
.cal-wk-day {
  background: var(--bf-surface); border: 1px solid var(--bf-border);
  border-radius: var(--bf-radius); overflow: hidden; cursor: pointer;
  display: flex; flex-direction: column; min-height: 150px;
}
.cal-wk-day:hover { border-color: var(--bf-accent); }
.cal-wk-today { box-shadow: inset 0 0 0 2px var(--bf-accent-2); }
.cal-wk-sel { background: var(--bf-surface-alt); border-color: var(--bf-accent); }
.cal-wk-head {
  display: flex; align-items: baseline; gap: 6px; padding: 7px 9px;
  background: var(--bf-surface-sunk); border-bottom: 1px solid var(--bf-border);
}
.cal-wk-dow {
  font-family: var(--bf-font-mono); font-size: 10px; letter-spacing: .06em;
  text-transform: uppercase; color: var(--bf-text-muted);
}
.cal-wk-num {
  font-family: var(--bf-font-mono); font-size: 14px; font-variant-numeric: tabular-nums;
  color: var(--bf-text);
}
.cal-wk-count {
  margin-left: auto; font-family: var(--bf-font-mono); font-size: 10px;
  font-variant-numeric: tabular-nums; color: var(--bf-accent);
  background: var(--bf-coral-tint); border-radius: var(--bf-radius-pill); padding: 1px 7px;
}
.cal-wk-body { padding: 6px; display: grid; gap: 4px; align-content: start; flex: 1; }
.cal-wk-item {
  display: grid; gap: 1px; padding: 4px 6px; border-radius: var(--bf-radius-sm);
  background: var(--bf-surface-alt); font-size: 11px; line-height: 1.25;
}
.cal-wk-item:hover { background: var(--bf-teal-tint); }
.cal-wk-item.cal-done { opacity: .5; }
.cal-wk-item.cal-done .cal-wk-cust { text-decoration: line-through; }
.cal-wk-tag {
  font-family: var(--bf-font-mono); font-size: 8.5px; letter-spacing: .04em;
  text-transform: uppercase;
}
.cal-wk-tag.d { color: var(--bf-accent-2); }
.cal-wk-tag.p { color: var(--bf-accent); }
.cal-wk-tag.t { color: var(--bf-warning); }
.cal-wk-cust {
  font-weight: 500; color: var(--bf-text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cal-wk-site {
  color: var(--bf-text-muted); font-size: 10px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cal-wk-empty { color: var(--bf-text-muted); font-size: 11px; text-align: center; padding: 10px 0; opacity: .6; }
.cal-wk-go {
  border: 0; border-top: 1px solid var(--bf-border); background: transparent;
  font-family: var(--bf-font-mono); font-size: 10px; color: var(--bf-text-muted);
  padding: 6px; cursor: pointer; text-align: center;
}
.cal-wk-go:hover { background: var(--bf-coral-tint); color: var(--bf-accent); }

@media (max-width: 900px) {
  .cal-week { grid-template-columns: repeat(2, 1fr); }
}

/* Calendar type colours — the month chips, day tags and legend predate the brand
   and were blue/orange/purple. Same three meanings, brand palette. */
.cal-dot.d, .cal-chip.d, .cal-tag.d { background: var(--bf-accent-2); }
.cal-dot.p, .cal-chip.p, .cal-tag.p { background: var(--bf-accent); }
.cal-dot.t, .cal-chip.t, .cal-tag.t { background: var(--bf-warning); }
.cal-chip, .cal-tag { font-family: var(--bf-font-mono); font-weight: 400; }
.cal-cell { border-radius: var(--bf-radius-sm); }
.cal-cell.cal-today { box-shadow: inset 0 0 0 2px var(--bf-accent-2); }
/* Selection shouldn't shout — a whole tile of coral reads as an alert, and the
   selected day is usually also today, which already has the teal ring. */
.cal-cell.cal-sel { background: var(--bf-surface-alt); border-color: var(--bf-accent); }
.cal-num { font-family: var(--bf-font-mono); font-variant-numeric: tabular-nums; }
.cal-dow { font-family: var(--bf-font-mono); font-weight: 400; }
.cal-month { font-family: var(--bf-font-mono); font-weight: 500; }

/* ── Timeline: resize grip, now line, conflict flag ───────────────────── */
.dp-tl-resize {
  position: absolute; left: 0; right: 0; bottom: 0; height: 7px;
  cursor: ns-resize; touch-action: none;
}
.dp-tl-resize::after {
  content: ""; position: absolute; left: 50%; bottom: 2px;
  transform: translateX(-50%); width: 18px; height: 2px; border-radius: 1px;
  background: var(--bf-border);
}
.dp-tl-card:hover .dp-tl-resize::after { background: var(--bf-accent); }
.dp-tl-card.dp-resizing { z-index: 6; box-shadow: var(--bf-shadow); }

/* Where the day actually is. Only drawn on today. */
.dp-tl-now {
  position: absolute; left: 0; right: 0; height: 0; z-index: 4; pointer-events: none;
  border-top: 2px solid var(--bf-accent);
}
.dp-tl-now::before {
  content: ""; position: absolute; left: -3px; top: -4px;
  width: 6px; height: 6px; border-radius: 50%; background: var(--bf-accent);
}

.dp-tl-clash {
  font-family: var(--bf-font-mono); font-size: 10px;
  background: var(--bf-danger-tint); color: var(--bf-danger);
  border-radius: var(--bf-radius-pill); padding: 1px 7px;
}

/* Tap-to-assign: start + length pickers, the touch route to a scheduled time. */
.dp-assign-when { display: flex; gap: 10px; margin: 4px 0 12px; }
.dp-assign-when label {
  flex: 1; display: grid; gap: 3px;
  font-family: var(--bf-font-mono); font-size: var(--bf-fs-label);
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--bf-text-muted);
}
.dp-assign-sel {
  font-family: var(--bf-font-body); font-size: 14px;
  padding: 8px 10px; border-radius: var(--bf-radius-sm);
  border: 1px solid var(--bf-border); background: var(--bf-surface); color: var(--bf-text);
}

/* Idle gap between two jobs — a soft hatch, deliberately quieter than a card so
   it reads as absence rather than another thing to do. */
.dp-tl-gap {
  position: absolute; left: 0; right: 3px; z-index: 1; pointer-events: none;
  border-radius: var(--bf-radius-sm);
  background: repeating-linear-gradient(
    -45deg, transparent, transparent 5px,
    var(--bf-border) 5px, var(--bf-border) 6px);
  display: flex; align-items: center; justify-content: center;
}
.dp-tl-gap span {
  font-family: var(--bf-font-mono); font-size: 9.5px; color: var(--bf-text-muted);
  background: var(--bf-bg); border-radius: var(--bf-radius-pill); padding: 1px 6px;
}

/* ── Trucking templates ───────────────────────────────────────────────── */
.tt-tpl-list { display: grid; gap: 7px; }
.tt-tpl { display: flex; align-items: stretch; gap: 6px; }
.tt-tpl-use {
  flex: 1; text-align: left; display: grid; gap: 2px; cursor: pointer;
  background: var(--bf-surface); border: 1px solid var(--bf-border);
  border-radius: var(--bf-radius-sm); padding: 9px 12px;
}
.tt-tpl-use:hover { border-color: var(--bf-accent); background: var(--bf-coral-tint); }
.tt-tpl-name { font-family: var(--bf-font-mono); font-size: 13px; color: var(--bf-text); }
.tt-tpl-sub { font-size: 12px; color: var(--bf-text-muted); }
.tt-tpl-del {
  width: 34px; flex: none; border: 1px solid var(--bf-border); background: transparent;
  border-radius: var(--bf-radius-sm); color: var(--bf-text-muted); cursor: pointer; font-size: 16px;
}
.tt-tpl-del:hover { border-color: var(--bf-danger); color: var(--bf-danger); background: var(--bf-danger-tint); }

/* "Schedule these" — fills a driver's untimed jobs into the first slots that fit.
   Lives in the column header, which is a fixed height: anything conditional
   between the untimed strip and the grid would push that driver's clock out of
   line with every other column. */
.dp-tl-fill {
  border: 0; border-radius: var(--bf-radius-pill);
  background: var(--bf-teal-tint); color: var(--bf-accent-2);
  font-family: var(--bf-font-mono); font-size: 10px; padding: 2px 7px; cursor: pointer;
  white-space: nowrap;
}
.dp-tl-fill:hover { filter: brightness(0.96); }

/* Week view: who's on each job, and the day's roster at a glance. */
.cal-wk-who {
  font-family: var(--bf-font-mono); font-size: 9.5px; color: var(--bf-accent-2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cal-wk-who.cal-wk-none { color: var(--bf-text-muted); opacity: .75; }
.cal-wk-roster {
  display: flex; flex-wrap: wrap; gap: 3px; padding: 6px;
  border-top: 1px dashed var(--bf-border);
}
.cal-wk-drv {
  font-family: var(--bf-font-mono); font-size: 9.5px;
  background: var(--bf-teal-tint); color: var(--bf-accent-2);
  border-radius: var(--bf-radius-pill); padding: 1px 7px;
}
.cal-wk-orphan {
  font-family: var(--bf-font-mono); font-size: 9.5px;
  background: var(--bf-coral-tint); color: var(--bf-accent);
  border-radius: var(--bf-radius-pill); padding: 1px 7px;
}

/* ── Dispatch: lanes board (the run-order view) ───────────────────────── */
.dp-lane {
  background: var(--bf-surface); border: 1px solid var(--bf-border);
  border-radius: var(--bf-radius-lg);
}
.dp-lane-unassigned { border-style: dashed; }
.dp-lane-head {
  background: var(--bf-surface-sunk); border-bottom: 1px solid var(--bf-border);
  border-radius: var(--bf-radius-lg) var(--bf-radius-lg) 0 0;
}
.dp-lane-name { font-family: var(--bf-font-mono); font-weight: 500; font-size: 14px; }
.dp-lane-count {
  font-family: var(--bf-font-mono); font-weight: 400;
  background: var(--bf-coral-tint); color: var(--bf-accent);
}
.dp-lane-body.dp-over {
  background: var(--bf-teal-tint);
  outline: 2px dashed var(--bf-accent-2); outline-offset: -4px;
}
.dp-card {
  background: var(--bf-surface); border: 1px solid var(--bf-border);
  border-radius: var(--bf-radius); box-shadow: none;
}
.dp-card:hover { box-shadow: var(--bf-shadow); border-color: var(--bf-border); }
.dp-card.dp-del { border-left: 3px solid var(--bf-accent-2); }
.dp-card.dp-pick { border-left: 3px solid var(--bf-accent); }
.dp-card.dp-late { background: var(--bf-danger-tint); }
.dp-card-cust { font-weight: 500; }
.dp-type {
  font-family: var(--bf-font-mono); font-weight: 400; font-size: 10.5px;
  letter-spacing: 0.05em; border-radius: var(--bf-radius-pill);
}
.dp-t-del { color: var(--bf-accent-2); background: var(--bf-teal-tint); }
.dp-t-pick { color: var(--bf-accent); background: var(--bf-coral-tint); }
.dp-ds, .dp-card-cont { font-family: var(--bf-font-mono); }
.dp-mat { font-family: var(--bf-font-mono); font-size: 10.5px; border-radius: var(--bf-radius-pill); }
.dp-late-tag, .dp-done-tag {
  font-family: var(--bf-font-mono); font-weight: 400; border-radius: var(--bf-radius-pill);
}
.dp-late-tag { background: var(--bf-danger-tint); color: var(--bf-danger); }
.dp-done-tag { background: var(--bf-success-tint); color: var(--bf-success); }
.dp-sum { font-family: var(--bf-font-body); border-radius: var(--bf-radius-pill); }
.dp-sum strong { font-family: var(--bf-font-mono); }
.dp-date { font-family: var(--bf-font-mono); font-weight: 500; }
.dp-today {
  font-family: var(--bf-font-mono); font-weight: 400;
  background: var(--bf-teal-tint); color: var(--bf-accent-2);
  border-radius: var(--bf-radius-pill);
}
.dp-hint { color: var(--bf-text-muted); }

/* Every timeline card needs a route to driver + time that isn't dragging —
   drag is mouse-only, and a keyboard or touch user was otherwise stuck. */
.dp-tl-edit {
  margin-left: 2px; border: 0; background: transparent; cursor: pointer;
  color: var(--bf-text-muted); font-size: 13px; line-height: 1;
  padding: 0 3px; border-radius: var(--bf-radius-sm);
}
/* With no time chip there's nothing pushing the button right — do it here. */
.dp-tl-top:not(:has(.dp-tl-time)) .dp-tl-edit { margin-left: auto; }
.dp-tl-edit:hover { background: var(--bf-coral-tint); color: var(--bf-accent); }
.dp-tl-card:not(:hover) .dp-tl-edit { opacity: .45; }

/* ── Calendar: week × drivers ─────────────────────────────────────────── */
.cal-drivers {
  display: grid;
  grid-template-columns: 116px repeat(7, minmax(0, 1fr));
  gap: 1px; background: var(--bf-border);
  border: 1px solid var(--bf-border); border-radius: var(--bf-radius);
  overflow: hidden;
}
.cal-dv-cell { background: var(--bf-surface); padding: 6px 7px; min-height: 46px; }
.cal-dv-corner { background: var(--bf-surface-sunk); }
.cal-dv-dayhead {
  background: var(--bf-surface-sunk); display: flex; align-items: baseline; gap: 5px;
  min-height: 0; padding: 7px 9px;
}
.cal-dv-dow {
  font-family: var(--bf-font-mono); font-size: 10px; letter-spacing: .06em;
  text-transform: uppercase; color: var(--bf-text-muted);
}
.cal-dv-num { font-family: var(--bf-font-mono); font-size: 13px; font-variant-numeric: tabular-nums; }
.cal-dv-today { box-shadow: inset 0 2px 0 var(--bf-accent-2); }
.cal-dv-name-cell {
  background: var(--bf-surface-sunk); display: flex; align-items: center;
  font-family: var(--bf-font-mono); font-size: 12px; color: var(--bf-text);
  position: sticky; left: 0; z-index: 2;
}
.cal-dv-pool { color: var(--bf-text-muted); font-style: italic; }
.cal-dv-slot { display: flex; flex-direction: column; gap: 3px; position: relative; }
.cal-dv-free { background: var(--bf-surface-alt); }
.cal-dv-open {
  font-family: var(--bf-font-mono); font-size: 9.5px; color: var(--bf-text-muted);
  opacity: .55; align-self: center; margin: auto;
}
.cal-dv-count {
  position: absolute; right: 5px; bottom: 3px;
  font-family: var(--bf-font-mono); font-size: 9px; color: var(--bf-text-muted);
}
.cal-dv-chip {
  display: flex; align-items: baseline; gap: 5px; cursor: pointer;
  background: var(--bf-surface-alt); border-radius: var(--bf-radius-sm);
  padding: 3px 6px; font-size: 11px; line-height: 1.25;
}
.cal-dv-chip:hover { background: var(--bf-teal-tint); }
.cal-dv-chip.cal-done { opacity: .5; }
.cal-dv-chip.cal-done .cal-dv-name { text-decoration: line-through; }
.cal-dv-chip.cal-dv-dragging { opacity: .4; }
.cal-dv-tag { font-family: var(--bf-font-mono); font-size: 8.5px; text-transform: uppercase; flex: none; }
.cal-dv-tag.d { color: var(--bf-accent-2); }
.cal-dv-tag.p { color: var(--bf-accent); }
.cal-dv-tag.t { color: var(--bf-warning); }
.cal-dv-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Same-day drops are allowed; anything else would move the promised date. */
.cal-dv-slot.cal-dv-over { background: var(--bf-teal-tint); outline: 2px dashed var(--bf-accent-2); outline-offset: -3px; }
.cal-dv-slot.cal-dv-nope { background: var(--bf-danger-tint); cursor: not-allowed; }

@media (max-width: 900px) {
  .cal-drivers { grid-template-columns: 92px repeat(7, minmax(74px, 1fr)); overflow-x: auto; }
}

/* ── Calendar: month tiles (Outlook-style) ────────────────────────────── */
/* The old month grid was 74px cells holding count chips. These are tiles that
   list the actual jobs, so they need real height — but six rows of them is a
   lot of page, hence the per-tile cap and the "+N more" spill. */
.cal-grid { gap: 5px; }
/* Fixed, not min-height: uneven rows make a month grid look broken, and the
   "+N more" spill exists precisely so a busy day doesn't need extra room. */
.cal-cell {
  height: 132px; padding: 0; overflow: hidden;
  display: flex; flex-direction: column; gap: 0;
  background: var(--bf-surface);
}
.cal-cell.cal-out { opacity: 1; background: var(--bf-surface-alt); }
.cal-cell.cal-out .cal-num { color: var(--bf-text-muted); opacity: .6; }
.cal-mo-head {
  display: flex; align-items: baseline; justify-content: space-between;
  padding: 5px 8px 3px; flex: none;
}
/* Muted by default — every tile in a busy month carries one of these, and in
   coral they read as thirty warnings. Coral is kept for the overflow link. */
.cal-mo-count {
  font-family: var(--bf-font-mono); font-size: 9.5px; font-variant-numeric: tabular-nums;
  color: var(--bf-text-muted); background: var(--bf-surface-sunk);
  border-radius: var(--bf-radius-pill); padding: 0 6px;
}
/* Everything on the day is done — worth a quiet nod. */
.cal-mo-count.cal-mo-clear { color: var(--bf-success); background: var(--bf-success-tint); }
.cal-mo-items { display: flex; flex-direction: column; gap: 2px; padding: 0 5px 4px; overflow: hidden; }
.cal-mo-item {
  display: flex; align-items: center; gap: 5px; cursor: pointer;
  border-radius: 4px; padding: 2px 5px 2px 3px; font-size: 11px; line-height: 1.3;
  background: var(--bf-surface-alt);
}
.cal-cell.cal-out .cal-mo-item { background: transparent; }
.cal-mo-item:hover { background: var(--bf-teal-tint); }
.cal-mo-item.cal-done { opacity: .5; }
.cal-mo-item.cal-done .cal-mo-text { text-decoration: line-through; }
/* A colour bar rather than a filled pill — four of these in a tile shouldn't
   read as four alarms. */
.cal-mo-bar { width: 3px; align-self: stretch; min-height: 12px; border-radius: 2px; flex: none; }
.cal-mo-bar.d { background: var(--bf-accent-2); }
.cal-mo-bar.p { background: var(--bf-accent); }
.cal-mo-bar.t { background: var(--bf-warning); }
.cal-mo-text {
  flex: 1; min-width: 0; white-space: nowrap; overflow: hidden;
  text-overflow: ellipsis; color: var(--bf-text);
}
.cal-mo-who {
  font-family: var(--bf-font-mono); font-size: 9px; color: var(--bf-text-muted);
  flex: none; max-width: 42px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cal-mo-more {
  border: 0; background: transparent; cursor: pointer; text-align: left;
  font-family: var(--bf-font-mono); font-size: 9.5px; color: var(--bf-accent);
  padding: 0 8px 5px; margin-top: auto;
}
.cal-mo-more:hover { text-decoration: underline; }

@media (max-width: 900px) {
  .cal-cell { min-height: 92px; }
  .cal-mo-who { display: none; }
}


/* ── Timeline: card density ───────────────────────────────────────────────
   A short job has no room for four lines. Rather than clipping one halfway
   through, drop the detail and keep what identifies the job. ── */
.dp-tl-card.dp-tl-snug .dp-tl-cont { display: none; }
.dp-tl-card.dp-tl-tight .dp-tl-sub,
.dp-tl-card.dp-tl-tight .dp-tl-cont { display: none; }
.dp-tl-card.dp-tl-tight { padding: 2px 7px; line-height: 1.2; }
.dp-tl-card.dp-tl-tight .dp-tl-top { margin-bottom: 0; }
/* At the shortest, type and customer share the line. */
.dp-tl-card.dp-tl-tight .dp-tl-type { font-size: 9.5px; }
.dp-tl-card.dp-tl-tight .dp-tl-cust { font-size: 11.5px; }

/* ── Timeline: narrow screens ─────────────────────────────────────────────
   Below this the two-panel split stops earning its keep: the pool goes
   full-width above the board as a horizontal strip, and the board scrolls. ── */
@media (max-width: 1100px) {
  .dp-tl { grid-template-columns: 1fr; }
  .dp-tl-pool { max-height: none; }
  /* Stacked, the pool is a strip — it must not push the board off the screen,
     which is the whole point of the view. */
  .dp-tl-poolbody {
    grid-auto-flow: column; grid-auto-columns: minmax(190px, 220px);
    justify-content: start;
    overflow-x: auto; overflow-y: hidden;
    max-height: 96px; min-height: 0; padding: 7px;
  }
  .dp-tl-poolbody .dp-tl-untimed-empty { padding: 2px; }
  .dp-tl-poolfoot { border-top: 1px solid var(--bf-border); padding: 7px; }
  .dp-tl-poolfoot .dp-add-input { max-width: 220px; }
}

/* ── Timeline: narrow cards ───────────────────────────────────────────────
   A job overlapping another gets half a column, and at laptop width that's
   ~120px. Ellipsising the type label to "PIC…" is worse than dropping it: the
   coloured left border already says which it is, and the space is better spent
   on the customer name. Container queries, so it responds to the card's own
   width rather than the viewport's. ── */
.dp-tl-card { container-type: inline-size; }
@container (max-width: 132px) {
  .dp-tl-type { display: none; }
}
@container (max-width: 104px) {
  .dp-tl-sub, .dp-tl-cont { display: none; }
  .dp-tl-time { margin-left: 0; }
}

/* A half-hour job is ~27px of usable box, and two lines need 31 — so the
   shortest cards go single-line instead of clipping. display:contents lets the
   header's children join the card's own flex row, so the customer and the time
   share one line and the type falls away (the border already says which). */
.dp-tl-card.dp-tl-mini {
  display: flex; align-items: center; gap: 5px;
  padding: 1px 7px; line-height: 1.15;
}
.dp-tl-card.dp-tl-mini .dp-tl-top { display: contents; }
.dp-tl-card.dp-tl-mini .dp-tl-type,
.dp-tl-card.dp-tl-mini .dp-tl-sub,
.dp-tl-card.dp-tl-mini .dp-tl-cont { display: none; }
.dp-tl-card.dp-tl-mini .dp-tl-cust { order: -1; flex: 1; min-width: 0; font-size: 11.5px; }

/* Repeat controls on the schedule-pickup form. */
.ct-repeat-row { display: flex; gap: 8px; align-items: center; }
.ct-repeat-row select { flex: 1 1 auto; min-width: 0; }
.ct-repeat-row input[type="number"] { width: 86px; flex: none; }
.ct-repeat-row input[type="date"] { width: 150px; flex: none; }
@media (max-width: 560px) {
  .ct-repeat-row { flex-wrap: wrap; }
  .ct-repeat-row input[type="date"] { width: 100%; }
}

/* askChoice — a confirm() that can offer more than two answers. */
.ct-choice-list { display: grid; gap: 8px; }
.ct-choice {
  display: grid; gap: 3px; text-align: left; cursor: pointer;
  background: var(--bf-surface); border: 1px solid var(--bf-border);
  border-radius: var(--bf-radius-sm); padding: 11px 13px;
}
.ct-choice:hover { border-color: var(--bf-accent); background: var(--bf-coral-tint); }
.ct-choice-label { font-family: var(--bf-font-mono); font-size: 13px; color: var(--bf-text); }
.ct-choice-hint { font-size: 12px; color: var(--bf-text-muted); line-height: 1.4; }

/* Weekday chips on the repeat control. A multi-select, because a site is often
   worked more than one day a week — which is the whole point of the pattern. */
.ct-dow-row { display: flex; align-items: center; gap: 4px; margin: 6px 0 2px; flex-wrap: wrap; }
.ct-dow-label {
  font-family: var(--bf-font-mono); font-size: var(--bf-fs-label);
  letter-spacing: var(--bf-track-label); text-transform: uppercase;
  color: var(--bf-text-muted); margin-right: 4px;
}
.ct-dow {
  width: 30px; height: 30px; flex: none; cursor: pointer;
  border: 1px solid var(--bf-border); background: var(--bf-surface);
  border-radius: var(--bf-radius-sm); color: var(--bf-text-muted);
  font-family: var(--bf-font-mono); font-size: 12px; line-height: 1;
}
.ct-dow:hover:not(:disabled) { border-color: var(--bf-accent); }
.ct-dow.active { background: var(--bf-accent); border-color: var(--bf-accent); color: #fff; }
.ct-dow:disabled { opacity: .45; cursor: not-allowed; }

/* Projected recurring pickups — worked out from the rule, not booked yet. Muted
   and outlined rather than filled, so a month of planned service can't be
   mistaken for a month of committed work. */
.cal-mo-item.cal-planned,
.cal-wk-item.cal-planned,
.cal-dv-chip.cal-planned {
  background: transparent;
  border: 1px dashed var(--bf-border);
  color: var(--bf-text-muted);
  cursor: default;
}
.cal-mo-item.cal-planned:hover,
.cal-wk-item.cal-planned:hover,
.cal-dv-chip.cal-planned:hover { background: transparent; }
.cal-planned .cal-mo-text, .cal-planned .cal-wk-cust, .cal-planned .cal-dv-name { color: var(--bf-text-muted); }
.cal-planned .cal-mo-bar { opacity: .45; }

/* ── What's new ───────────────────────────────────────────────────────────
   Shown once after an update. Reads as a note, not an alert — nothing here
   needs acting on, it's just telling you what moved. */
.wn-modal { max-width: 520px; }
.wn-lead { margin: 0 0 16px; color: var(--bf-text-muted); font-size: 13px; }
.wn-entry { padding: 0 0 14px; margin-bottom: 14px; border-bottom: 1px solid var(--bf-border); }
.wn-entry:last-child { border-bottom: 0; margin-bottom: 0; padding-bottom: 0; }
.wn-head { display: flex; align-items: baseline; gap: 9px; margin-bottom: 7px; }
.wn-title { font-family: var(--bf-font-mono); font-size: 14px; color: var(--bf-text); }
.wn-ver {
  margin-left: auto; font-family: var(--bf-font-mono); font-size: 10px;
  font-variant-numeric: tabular-nums; color: var(--bf-text-muted);
  background: var(--bf-surface-sunk); border-radius: var(--bf-radius-pill); padding: 1px 8px;
}
.wn-items { list-style: none; display: grid; gap: 6px; }
.wn-items li {
  position: relative; padding-left: 15px; font-size: 13px; line-height: 1.5; color: var(--bf-text);
}
.wn-items li::before {
  content: ""; position: absolute; left: 0; top: 0.55em;
  width: 6px; height: 6px; border-radius: 2px; background: var(--bf-accent-2);
}
.wn-foot { font-family: var(--bf-font-mono); font-size: 11px; color: var(--bf-text-muted); }
.rail-version { cursor: pointer; }

/* ── Driver progress on the dispatch board ──────────────────────────────
 * The line that tells the dispatcher what the driver has actually done. It sits
 * on the card because the alternative — a separate screen — means finding out
 * about a locked gate at the end of the day instead of when it happened. */
.dp-card.dp-problem { background: var(--bf-danger-tint); border-left-color: var(--bf-danger); }
.dp-drv {
  display: inline-block; margin-top: 6px;
  font-family: var(--bf-font-mono); font-size: 11px; font-weight: 500;
  padding: 2px 7px; border-radius: var(--bf-radius-pill);
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dp-drv-done    { background: var(--bf-teal-tint);   color: #14685D; }
.dp-drv-onsite  { background: var(--bf-sand-tint);   color: #7A5312; }
.dp-drv-problem { background: var(--bf-danger);      color: #fff; }

/* ── Driver activity feed ───────────────────────────────────────────────── */
.dp-feed { margin-top: 18px; border-top: 1px solid var(--bf-border); padding-top: 14px; }
.dp-feed-head {
  display: flex; align-items: center; gap: 10px; margin-bottom: 10px;
  font-size: var(--bf-fs-h3); font-weight: 600;
}
.dp-feed-count {
  font-family: var(--bf-font-mono); font-size: 11px; font-weight: 500;
  background: var(--bf-surface-sunk); color: var(--bf-text-muted);
  padding: 2px 8px; border-radius: var(--bf-radius-pill);
}
.dp-feed-list { display: grid; gap: 6px; max-height: 340px; overflow-y: auto; }
.dp-feed-row {
  display: flex; align-items: baseline; gap: 10px;
  padding: 8px 10px; border-radius: var(--bf-radius-sm);
  background: var(--bf-surface); border: 1px solid var(--bf-border);
  font-size: 13px;
}
.dp-feed-row.is-problem { background: var(--bf-danger-tint); border-color: var(--bf-danger); }
.dp-feed-row.is-error   { opacity: .7; }
.dp-feed-time {
  font-family: var(--bf-font-mono); font-size: 12px; color: var(--bf-text-muted);
  flex: none; min-width: 68px;
}
.dp-feed-who { font-weight: 600; flex: none; }
.dp-feed-what { flex: 1; min-width: 0; }
.dp-feed-tag {
  font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em;
  padding: 1px 6px; border-radius: var(--bf-radius-pill); flex: none;
}
/* Offline-synced entries are marked, because otherwise an event landing hours
   after it happened looks like the driver was late rather than out of signal. */
.dp-feed-synced { background: var(--bf-info-tint); color: #2F5560; }
.dp-feed-empty { color: var(--bf-text-muted); font-size: 13px; padding: 12px 2px; }

/* ── To Weigh ───────────────────────────────────────────────────────────── */
.tw-hint {
  font-size: 13px; color: var(--bf-text-muted);
  background: var(--bf-surface-sunk); border-radius: var(--bf-radius-sm);
  padding: 10px 14px; margin-bottom: 14px;
}
.tw-count {
  font-family: var(--bf-font-mono); font-size: 12px; font-weight: 500;
  background: var(--bf-sand-tint); color: #7A5312;
  padding: 3px 10px; border-radius: var(--bf-radius-pill);
}
.tw-ref { font-family: var(--bf-font-mono); font-weight: 500; }
.tw-sub { font-size: 12px; color: var(--bf-text-muted); margin-top: 2px; }
/* Wide enough to type into comfortably with a ticket in the other hand. */
.tw-page .tw-weight { width: 92px; font-family: var(--bf-font-mono); }
.tw-page .tw-ticket { width: 110px; }

/* ── Delivery sheet: containers that have been and gone ──────────────────
 * A swap closes one slot and opens another, so a site on weekly service builds
 * up a slot a week. These are a record rather than something to act on, so they
 * fold away and stay out of the way of the can that's actually there. */
.ds-past {
  grid-column: 1 / -1;
  margin-top: 4px;
  border: 1px solid var(--bf-border);
  border-radius: var(--bf-radius);
  background: var(--bf-surface);
}
.ds-past > summary {
  cursor: pointer; padding: 12px 16px;
  font-size: 13px; font-weight: 600; color: var(--bf-text-muted);
  list-style: none;
}
.ds-past > summary::-webkit-details-marker { display: none; }
.ds-past > summary::before { content: "▸ "; display: inline-block; width: 1em; }
.ds-past[open] > summary::before { content: "▾ "; }
.ds-past-list { padding: 0 16px 8px; }
.ds-past-row {
  display: flex; align-items: center; gap: 12px;
  padding: 9px 0; border-top: 1px solid var(--bf-border);
  font-size: 13px;
}
.ds-past-serial { font-family: var(--bf-font-mono); font-weight: 500; min-width: 70px; }
.ds-past-dates { font-family: var(--bf-font-mono); color: var(--bf-text-muted); flex: 1; }
.ds-past-tag {
  font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em;
  background: var(--bf-teal-tint); color: #14685D;
  padding: 2px 7px; border-radius: var(--bf-radius-pill);
}

/* ── Annual report: which yard am I looking at ──────────────────────────
 * Loud on purpose. The dangerous mistake with a per-yard report is filing one
 * yard's figures believing they are the whole business, and the numbers alone
 * give no clue which you are looking at. */
.nys-scope {
  background: var(--bf-sand-tint);
  border: 1px solid var(--bf-warning);
  border-radius: var(--bf-radius);
  padding: 12px 16px;
  margin-bottom: 16px;
  font-size: 13px;
  line-height: 1.5;
  color: #7A5312;
}
.nys-scope strong { font-size: 14px; }
.nys-scope-note {
  margin-top: 8px; padding-top: 8px;
  border-top: 1px solid rgba(122,83,18,.25);
  font-size: 12px;
}

/* A yard with nothing free. Shown rather than omitted, and marked rather than
   left looking like any other count — "Tiona 0" is the useful reading. */
.fa-yard-chip.fa-yard-empty {
  background: var(--bf-danger-tint);
  color: #8E2E1E;
  border-color: var(--bf-danger);
}

/* ── Admin: Yards ───────────────────────────────────────────────────────── */
.yd-list { display: grid; gap: 14px; }
.yd-card {
  border: 1px solid var(--bf-border);
  border-radius: var(--bf-radius);
  background: var(--bf-surface);
  padding: 14px 16px;
}
/* Still listed when inactive — its historical records are filed under it, so
   hiding it would make those look like they belong to nothing. */
.yd-card.yd-inactive { opacity: .62; }
.yd-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 12px; }
.yd-name { font-size: var(--bf-fs-h3); font-weight: 700; }
.yd-tag {
  font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em;
  padding: 2px 8px; border-radius: var(--bf-radius-pill);
}
.yd-tag.yd-base   { background: var(--bf-teal-tint);   color: #14685D; }
.yd-tag.yd-off    { background: var(--bf-surface-sunk); color: var(--bf-text-muted); }
.yd-tag.yd-permit { background: var(--bf-sand-tint);   color: #7A5312; }
.yd-actions { margin-left: auto; display: flex; gap: 14px; align-items: center; }
.yd-toggle {
  display: flex; align-items: center; gap: 6px;
  font-size: 12px; color: var(--bf-text-muted); cursor: pointer; white-space: nowrap;
}
.yd-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px 16px; }
.yd-grid label {
  display: flex; flex-direction: column; gap: 4px;
  font-size: 11px; font-weight: 700; text-transform: uppercase;
  letter-spacing: var(--bf-track-label); color: var(--bf-text-muted);
}
.yd-grid input {
  font-family: var(--bf-font-body); font-size: 14px; font-weight: 400;
  text-transform: none; letter-spacing: normal; color: var(--bf-text);
  padding: 8px 10px;
  border: 1px solid var(--bf-border); border-radius: var(--bf-radius-sm);
  background: var(--bf-bg);
}
.yd-foot {
  display: flex; align-items: center; gap: 12px;
  margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--bf-border);
}
.yd-note { font-size: 12px; color: var(--bf-text-muted); flex: 1; }
@media (max-width: 720px) { .yd-grid { grid-template-columns: 1fr; } }

/* ── Scrap: the buying floor ────────────────────────────────────────────
 * Laid out for someone standing at a counter with a customer waiting: grades are
 * big tap targets with the price on them, the running total is always visible,
 * and the seller panel changes state in front of you rather than rejecting the
 * ticket at the end. */
.scrap-buy { display: grid; grid-template-columns: minmax(0, 1fr) 380px; gap: 20px; align-items: start; }
@media (max-width: 1000px) { .scrap-buy { grid-template-columns: 1fr; } }

.scrap-grades { display: grid; grid-template-columns: repeat(auto-fill, minmax(170px, 1fr)); gap: 8px; margin-bottom: 16px; }
.scrap-grade {
  display: flex; flex-direction: column; align-items: flex-start; gap: 3px;
  min-height: 62px; padding: 10px 12px; cursor: pointer; text-align: left;
  border: 1px solid var(--bf-border); border-radius: var(--bf-radius-sm);
  background: var(--bf-surface); color: var(--bf-text);
}
.scrap-grade:hover { border-color: var(--bf-accent); background: var(--bf-surface-alt); }
.sg-name { font-size: 13px; font-weight: 600; line-height: 1.2; }
.sg-price { font-family: var(--bf-font-mono); font-size: 15px; font-weight: 500; color: var(--bf-accent-2); }
.sg-unit { font-size: 11px; color: var(--bf-text-muted); }
/* Marks a grade that needs the seller record at any value, not just over $50. */
.sg-always {
  display: inline-block; margin-left: 5px; width: 15px; height: 15px; line-height: 15px;
  text-align: center; border-radius: 50%; font-size: 10px; font-weight: 700;
  background: var(--bf-warning); color: #2A1D06;
}

.scrap-line-table input { width: 92px; font-family: var(--bf-font-mono); padding: 5px 7px; }
.sl-unit { font-size: 11px; color: var(--bf-text-muted); }
/* The lb → GT helper sits under the weight box, quieter than it so the gross-ton
   figure stays the one being read off the row. */
.sl-conv { margin-top: 4px; font-size: 10px; color: var(--bf-text-muted); white-space: nowrap; }
.scrap-line-table .sl-conv input {
  width: 72px; padding: 3px 6px; font-size: 11px; opacity: .85;
}
.sl-amt { font-family: var(--bf-font-mono); font-weight: 500; }
.scrap-total {
  text-align: right; padding: 10px 4px; font-size: 15px;
  border-top: 2px solid var(--bf-border); margin-top: 4px;
}
.scrap-total strong { font-family: var(--bf-font-mono); font-size: 20px; margin-left: 8px; }
.scrap-empty { padding: 32px; text-align: center; color: var(--bf-text-muted); font-size: 14px; }

.scrap-seller {
  border: 1px solid var(--bf-border); border-radius: var(--bf-radius);
  background: var(--bf-surface); padding: 14px 16px;
}
.scrap-seller-head {
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  margin-bottom: 12px; padding-bottom: 10px; border-bottom: 1px solid var(--bf-border);
}
/* The panel turns amber the moment the record becomes a legal requirement. */
.scrap-seller-head.req { border-bottom-color: var(--bf-warning); }
.ss-req { font-size: 12px; font-weight: 700; color: #7A5312; background: var(--bf-sand-tint); padding: 2px 8px; border-radius: var(--bf-radius-pill); }
.ss-opt { font-size: 12px; color: var(--bf-text-muted); }
.ss-note {
  font-size: 12px; line-height: 1.45; color: #7A5312;
  background: var(--bf-sand-tint); border-radius: var(--bf-radius-sm);
  padding: 9px 11px; margin-bottom: 12px;
}

.scrap-inv-actions { display: flex; align-items: center; gap: 14px; margin-bottom: 14px; flex-wrap: wrap; }
.scrap-inv-total { margin-left: auto; font-size: 14px; }
.scrap-inv-total strong { font-family: var(--bf-font-mono); font-size: 17px; }
.sc-num { font-family: var(--bf-font-mono); text-align: right; }
.sc-mono { font-family: var(--bf-font-mono); }
.sc-noval { color: var(--bf-text-muted); font-size: 12px; font-family: var(--bf-font-body); }
.sc-reg { font-size: 11px; font-weight: 700; background: var(--bf-teal-tint); color: #14685D; padding: 2px 8px; border-radius: var(--bf-radius-pill); }
.sc-voided { opacity: .5; }
.sc-void-tag { font-size: 10px; font-weight: 700; color: var(--bf-danger); }

/* ── Admin: scrap grades & prices ───────────────────────────────────────
 * One row, two halves. Left is the grade as configuration, right is setting a
 * price from a date — a visible divider because they behave differently: one
 * edits in place, the other only ever appends. */
.as-table { width: 100%; }
.as-table th { font-size: 11px; line-height: 1.25; vertical-align: bottom; }
.as-table input[type="text"], .as-table .as-name { width: 100%; min-width: 150px; }
.as-table .as-buy, .as-table .as-sell { width: 84px; font-family: var(--bf-font-mono); }
.as-table .as-from { width: 132px; }
.as-table .as-sort { font-family: var(--bf-font-mono); }
.as-table td, .as-table th { padding: 6px 8px; }
.as-cat-head {
  margin: 20px 0 8px; font-size: var(--bf-fs-h3); font-weight: 600;
}
.as-cat-head:first-of-type { margin-top: 4px; }
/* The divider between "what this grade is" and "what it's worth today". */
.as-sep { width: 14px; border-left: 2px solid var(--bf-border); padding: 0 !important; }
.as-cur { font-family: var(--bf-font-mono); white-space: nowrap; }
.as-noprice { color: var(--bf-text-muted); font-family: var(--bf-font-body); font-size: 12px; }
.as-add { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.as-add-chk {
  display: flex; align-items: center; gap: 6px;
  font-size: 13px; color: var(--bf-text-muted); white-space: nowrap;
}

/* ── Scrap grades: drag to order ────────────────────────────────────────
 * The position used to be a number you typed, which is tedious and invites gaps
 * and duplicates. Dragging is the order, and dropping into another category's
 * table is how a grade moves between them. */
.as-grip {
  cursor: grab; user-select: none; text-align: center;
  color: var(--bf-text-muted); font-size: 13px; letter-spacing: -1px;
  width: 28px; padding: 6px 4px !important;
}
.as-grip:active { cursor: grabbing; }
.as-table tbody tr:hover .as-grip { color: var(--bf-accent); }
.as-ghost { opacity: .4; background: var(--bf-teal-tint); }
/* An empty category still shows its table, because that's the drop target. */
.as-empty-cat {
  padding: 10px 14px; margin-top: -6px;
  font-size: 12px; color: var(--bf-text-muted);
  border: 1px dashed var(--bf-border); border-radius: var(--bf-radius-sm);
}

/* A numeric column's heading has to sit over its numbers.
 * The cells are right-aligned (.sc-num) while the headings were left-aligned, so
 * every figure appeared under the *next* column's heading — a recovered tonnage
 * reading as if it had been shipped. Wrong in the most convincing way: the
 * numbers were right and the table was mislabelling them. */
/* Specificity matters here: style.css sets `.data-table th { text-align: left }`
   at (0,1,1), which a bare `.sc-numh` at (0,1,0) loses to no matter where it
   sits in the cascade. Qualifying it beats that. */
.data-table th.sc-numh,
.mc-table th.sc-numh,
th.sc-numh { text-align: right; }

/* ── Scrap: grade search ────────────────────────────────────────────────── */
.scrap-search { position: relative; margin-bottom: 10px; max-width: 340px; }
.scrap-search input {
  width: 100%; padding: 9px 32px 9px 12px; font-size: 14px;
  border: 1px solid var(--bf-border); border-radius: var(--bf-radius-sm);
  background: var(--bf-surface); color: var(--bf-text);
}
.scrap-search input:focus { outline: 2px solid var(--bf-focus); outline-offset: -1px; }
.scrap-search-clear {
  position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
  border: none; background: none; cursor: pointer;
  font-size: 18px; line-height: 1; color: var(--bf-text-muted); padding: 2px 6px;
}
.scrap-search-clear:hover { color: var(--bf-text); }
.scrap-nohit { padding: 20px 4px; color: var(--bf-text-muted); font-size: 14px; }

/* ── One scrap ticket ───────────────────────────────────────────────────── */
.sc-ticket-row { cursor: pointer; }
.sc-ticket-row:hover { background: var(--bf-surface-alt); }
.st-head { display: flex; gap: 24px; margin-bottom: 14px; }
.st-head > div { flex: 1; }
.st-lbl {
  display: block; font-size: 10px; text-transform: uppercase;
  letter-spacing: var(--bf-track-label); color: var(--bf-text-muted); margin-bottom: 3px;
}
.st-sub { font-size: 12px; color: var(--bf-text-muted); margin-top: 2px; }
.st-total {
  display: flex; align-items: baseline; gap: 10px;
  padding: 10px 12px; border-radius: var(--bf-radius-sm);
  background: var(--bf-surface-sunk); font-size: 13px;
}
.st-total strong { font-family: var(--bf-font-mono); font-size: 20px; }
.st-total .st-sub { margin-left: auto; }
/* The identity block is boxed because it's the part with a retention clock on
   it, not just another field on a form. */
.st-id {
  margin-top: 14px; padding: 10px 12px;
  border: 1px solid var(--bf-border); border-radius: var(--bf-radius-sm);
  background: var(--bf-surface);
}
.st-idgrid { display: grid; grid-template-columns: auto 1fr auto 1fr; gap: 4px 12px; align-items: baseline; }
.st-idgrid span { font-size: 10px; text-transform: uppercase; letter-spacing: var(--bf-track-label); color: var(--bf-text-muted); }
.st-void {
  margin-top: 12px; padding: 8px 12px; border-radius: var(--bf-radius-sm);
  background: var(--bf-danger-tint); color: #8E2E1E; font-weight: 600; font-size: 13px;
}

/* ── Retained photo ID ──────────────────────────────────────────────────
 * Prominent on purpose. A regulated purchase without the ID copy is a
 * compliance gap, and it is far cheaper to notice while the seller is still at
 * the counter than two years later when somebody asks for the register. */
.ss-idcap {
  display: flex; gap: 12px; align-items: center;
  border: 1px solid var(--bf-warning); background: var(--bf-sand-tint);
  border-radius: var(--bf-radius-sm); padding: 10px 12px; margin-bottom: 12px;
}
.ss-idcap.got { border-color: var(--bf-teal); background: var(--bf-teal-tint); }
.ss-idcap img { width: 74px; height: 52px; object-fit: cover; border-radius: 4px; border: 1px solid var(--bf-border); }
.ss-idcap strong { font-size: 13px; }
.ss-idcap .st-sub { margin-bottom: 6px; }

.st-idimg { margin-top: 10px; display: flex; gap: 12px; align-items: center; }
.st-idimg img {
  width: 120px; height: 80px; object-fit: cover; cursor: zoom-in;
  border: 1px solid var(--bf-border); border-radius: var(--bf-radius-sm);
}
.st-ok { color: #14685D; font-weight: 600; font-size: 13px; margin-right: 8px; }
.st-missing {
  flex: 1; padding: 9px 11px; border-radius: var(--bf-radius-sm);
  background: var(--bf-danger-tint); color: #8E2E1E; font-size: 13px; line-height: 1.45;
}
.st-missing button { margin-top: 8px; }

/* ── Scrap dashboard ────────────────────────────────────────────────────
 * Recessive chrome, thin marks, and the numbers doing the work. Category
 * colours are validated for colour-vision separation on both surfaces; every
 * segment is also directly labelled, so identity never rests on colour alone. */
.sd-range { display: flex; gap: 6px; margin-bottom: 14px; }
.sd-range-btn {
  border: 1px solid var(--bf-border); background: var(--bf-surface); color: var(--bf-text-muted);
  border-radius: var(--bf-radius-pill); padding: 5px 14px; font-size: 12px; cursor: pointer;
  font-family: var(--bf-font-body);
}
.sd-range-btn.on { background: var(--bf-accent); border-color: var(--bf-accent); color: #fff; }

/* An actionable gap, not decoration — icon plus words, never colour alone. */
.sd-alert {
  display: flex; gap: 12px; align-items: flex-start;
  border: 1px solid var(--bf-danger); background: var(--bf-danger-tint);
  border-radius: var(--bf-radius); padding: 12px 14px; margin-bottom: 16px;
}
.sd-alert-ico {
  flex: none; width: 22px; height: 22px; border-radius: 50%;
  background: var(--bf-danger); color: #fff; font-weight: 700; font-size: 13px;
  display: grid; place-items: center;
}
.sd-alert strong { color: #8E2E1E; font-size: 14px; }
.sd-alert-sub { font-size: 12px; color: #8E2E1E; opacity: .85; margin-top: 2px; }
.sd-alert-list { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-top: 8px; }
.sd-chip {
  border: 1px solid var(--bf-danger); background: var(--bf-surface); color: #8E2E1E;
  border-radius: var(--bf-radius-pill); padding: 4px 10px; font-size: 12px; cursor: pointer;
  font-family: var(--bf-font-mono);
}
.sd-chip:hover { background: var(--bf-danger); color: #fff; }

.sd-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 12px; margin-bottom: 16px; }
.sd-tile {
  border: 1px solid var(--bf-border); background: var(--bf-surface);
  border-radius: var(--bf-radius); padding: 14px 16px;
  display: flex; flex-direction: column; gap: 3px;
}
.sd-tile-label {
  font-size: 10px; text-transform: uppercase; letter-spacing: var(--bf-track-label);
  color: var(--bf-text-muted); font-weight: 700;
}
/* The hero number. Text tokens, not a series colour. */
.sd-tile-value { font-family: var(--bf-font-mono); font-size: 24px; font-weight: 500; line-height: 1.1; }
.sd-tile-sub { font-size: 12px; color: var(--bf-text-muted); }
.sd-good .sd-tile-sub { color: #14685D; font-weight: 600; }
.sd-warn .sd-tile-sub { color: #8E2E1E; font-weight: 600; }

.sd-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
.sd-card {
  border: 1px solid var(--bf-border); background: var(--bf-surface);
  border-radius: var(--bf-radius); padding: 14px 16px;
}
.sd-card.sd-wide { grid-column: 1 / -1; }
.sd-card h3 { margin: 0 0 10px; font-size: var(--bf-fs-h3); font-weight: 600; }
@media (max-width: 900px) { .sd-grid { grid-template-columns: 1fr; } }

.sd-trend { width: 100%; height: 124px; display: block; }
.sd-bar rect:first-child { transition: opacity .12s; }
.sd-bar:hover rect:first-child { opacity: .75; }
.sd-axis {
  display: flex; justify-content: space-between; margin-top: 6px;
  font-family: var(--bf-font-mono); font-size: 11px; color: var(--bf-text-muted);
}
.sd-stack { width: 100%; height: 26px; display: block; }
.sd-keys { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 10px; font-size: 12px; }
.sd-key { display: flex; align-items: center; gap: 6px; color: var(--bf-text-muted); }
.sd-key i { width: 10px; height: 10px; border-radius: 2px; flex: none; }
.sd-key b { font-family: var(--bf-font-mono); color: var(--bf-text); font-weight: 500; }

.sd-gbs { display: grid; gap: 7px; }
.sd-gb { display: grid; grid-template-columns: minmax(120px, 200px) 1fr auto auto; gap: 10px; align-items: center; font-size: 13px; }
.sd-gb-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sd-gb-track { background: var(--bf-surface-sunk); border-radius: 3px; height: 14px; overflow: hidden; }
.sd-gb-fill { display: block; height: 100%; background: var(--bf-accent-2); border-radius: 3px; }
.sd-gb-val { font-family: var(--bf-font-mono); font-weight: 500; }
.sd-gb-sub { font-family: var(--bf-font-mono); font-size: 11px; color: var(--bf-text-muted); min-width: 54px; text-align: right; }

.sd-empty { padding: 26px 4px; text-align: center; color: var(--bf-text-muted); font-size: 13px; }
.sd-note { margin-top: 10px; font-size: 12px; color: var(--bf-text-muted); line-height: 1.45; }

/* ── Purchasing report ───────────────────────────────────────────────────
 * Reuses the dashboard's tiles, cards and bar marks — it is the same data at a
 * different cadence, and giving it its own visual language would imply the two
 * screens disagree. What is added here is only what a closed period needs that
 * a rolling window doesn't: a period to page through, and changes against it. */
.sp-bar {
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap; margin-bottom: 14px;
}
.sp-bar .sd-range { margin-bottom: 0; }
.sp-nav { display: flex; align-items: center; gap: 8px; }
.sp-nav-btn {
  border: 1px solid var(--bf-border); background: var(--bf-surface); color: var(--bf-text);
  border-radius: var(--bf-radius); width: 30px; height: 30px; font-size: 17px; line-height: 1;
  cursor: pointer; display: grid; place-items: center;
}
.sp-nav-btn:hover:not(:disabled) { background: var(--bf-surface-sunk); }
.sp-nav-btn:disabled { opacity: .35; cursor: default; }
.sp-period { display: flex; flex-direction: column; min-width: 190px; text-align: center; line-height: 1.25; }
.sp-period b { font-size: 15px; font-weight: 600; }
.sp-period-sub { font-family: var(--bf-font-mono); font-size: 11px; color: var(--bf-text-muted); }
.sp-actions { margin-left: auto; display: flex; gap: 6px; }

/* The change against the period before. Signed and arrowed as well as coloured,
   so it survives being read in greyscale or by someone who can't separate the
   two hues — the same rule the buy/sell spread follows below. */
.sp-d { font-size: 12px; font-family: var(--bf-font-mono); }
.sp-d.up { color: #14685D; font-weight: 600; }
.sp-d.down { color: #8E2E1E; font-weight: 600; }
.sp-d.flat { color: var(--bf-text-muted); }
.sp-d-vs { font-family: var(--bf-font-body); color: var(--bf-text-muted); font-weight: 400; margin-left: 3px; }

.sp-partial {
  border: 1px solid var(--bf-border); border-left: 3px solid var(--bf-accent);
  background: var(--bf-surface); border-radius: var(--bf-radius);
  padding: 9px 12px; margin: 0 0 14px;
}
.sp-foot { margin-top: 14px; }

/* Day-of-week ticks under a week's seven bars. A month gets end labels instead —
   thirty-one of these would collide into a grey smear. */
.sp-dlabels { display: flex; margin-top: 5px; }
.sp-dlabels span {
  flex: 1; text-align: center; font-family: var(--bf-font-mono); font-size: 11px;
  color: var(--bf-text-muted);
}
.sp-dlabels b { color: var(--bf-text); font-weight: 600; margin-right: 3px; }

/* A category swatch is decoration beside a name that is already spelled out —
   never the only thing carrying the category. */
.sp-cat { display: inline-block; width: 8px; height: 8px; border-radius: 2px; margin-left: 6px; }
.sp-catname { font-size: 11px; color: var(--bf-text-muted); margin-left: 4px; }
.sp-hint { font-size: 11px; color: var(--bf-text-muted); }
.sp-flag {
  font-size: 10px; text-transform: uppercase; letter-spacing: .05em;
  color: #8E2E1E; font-weight: 700; margin-left: 4px;
}
.sp-mono { font-family: var(--bf-font-mono); }
.sp-click { cursor: pointer; }
.sp-click:hover td { background: var(--bf-surface-sunk); }

/* Share of spend: a bar whose only job is rank at a glance, with the figure
   printed next to it because a bar cannot be read to one decimal place. */
.sp-share {
  display: inline-block; vertical-align: middle; width: 90px; height: 8px;
  background: var(--bf-surface-sunk); border-radius: 2px; overflow: hidden;
}
.sp-share-fill { display: block; height: 100%; background: var(--bf-accent-2); border-radius: 2px; }
.sp-share-pct {
  font-family: var(--bf-font-mono); font-size: 11px; color: var(--bf-text-muted);
  margin-left: 8px; vertical-align: middle;
}
.sp-cmp td:first-child { color: var(--bf-text-muted); }
.sp-grades tfoot td, .sp-cmp tfoot td { border-top: 2px solid var(--bf-border); }
@media (max-width: 760px) {
  .sp-actions { margin-left: 0; }
  .sp-bar { gap: 10px; }
}

/* Buy, sell and the gap between them. Direction is coloured but also signed, so
   it never depends on telling green from red. */
.as-table .as-cur { text-align: right; }
.as-spread { white-space: nowrap; }
.as-spread .as-up { color: #14685D; font-weight: 500; }
.as-spread .as-down { color: #8E2E1E; font-weight: 500; }
.as-pct {
  display: inline-block; margin-left: 6px; min-width: 40px;
  font-size: 11px; color: var(--bf-text-muted);
}

/* ── An ID copy already on file ──────────────────────────────────────────
   The card appears while the seller record is being filled in, so it has to
   read as an offer rather than an error. A number match is the same person and
   gets the confident treatment; a name match is a question and stays neutral,
   because "M Smith sold here before" is not identification. */
.ss-onfile-card {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 12px; margin: 10px 0;
  border: 1px solid var(--border); border-left: 3px solid var(--text-muted);
  border-radius: 8px; background: var(--surface-raised);
}
.ss-onfile-card.strong { border-left-color: var(--success); }
.ss-of-txt { flex: 1; min-width: 0; }
.ss-onfile-card em { color: var(--text-muted); font-style: normal; }

/* Stands in for the photo thumbnail when there is no new photo to show, so the
   block keeps its shape whichever path was taken. */
.ss-reuse-mark {
  flex: 0 0 auto; display: grid; place-items: center;
  width: 64px; height: 44px; border-radius: 6px;
  border: 1px dashed var(--border);
  font-size: 11px; text-transform: uppercase; letter-spacing: .04em;
  color: var(--text-muted); background: var(--surface-raised);
}

/* Retention held open by later purchases. Informational, not a warning — the
   purge is working correctly, and the note exists so nobody concludes otherwise
   from seeing an old copy still on file. */
.sd-note.sd-held { border-left: 3px solid var(--text-muted); padding-left: 10px; }
.sd-note.sd-held .sd-alert-list { margin-top: 8px; }

/* ── The sale just taken ─────────────────────────────────────────────────
   Sits above the buying screen rather than replacing it: the counter serves the
   next person straight away, and the receipt for the last one stays one button
   away instead of behind a tab change. */
.scrap-took {
  display: flex; align-items: center; gap: 14px;
  padding: 11px 14px; margin-bottom: 14px;
  border: 1px solid var(--success); border-left: 3px solid var(--success);
  border-radius: 8px; background: var(--success-subtle);
}
.scrap-took > div:first-child { flex: 1; min-width: 0; }
.scrap-took-btns { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }

/* ── Recovered ───────────────────────────────────────────────────────────
   Two headline figures rather than a table of balances: how much came out of
   the stream, and what it would fetch today. Neither claims to describe what is
   physically in the yard. */
.sr-head { display: flex; gap: 34px; padding: 14px 2px 16px; flex-wrap: wrap; }
.sr-head strong { display: block; font-size: 24px; line-height: 1.2; margin-top: 3px; }
.sr-h3 { font-size: 13px; text-transform: uppercase; letter-spacing: .06em;
         color: var(--text-muted); margin: 20px 0 8px; font-weight: 600; }
.sr-note { color: var(--text-muted); font-size: 12px; max-width: 320px; }
/* A right-aligned figure sitting flush against the left-aligned column beside it
   reads as one value. Same crowding that made the old inventory numbers appear
   to belong to the wrong heading. */
.data-table th.sr-by, .data-table td.sr-by { padding-left: 22px; }

/* ── Storage ─────────────────────────────────────────────────────────────
   The fill colour is never the only signal: the percentage is written out, and
   the warning states carry a sentence saying what to do. Colour is the fastest
   read for someone who already knows the screen, not the carrier of meaning. */
.stg-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; flex-wrap: wrap; margin-bottom: 10px; }
.stg-big { display: block; font-size: 30px; line-height: 1.15; margin-top: 2px; }
.stg-of { color: var(--text-muted); font-size: 13px; }
.stg-ed { color: var(--text-muted); font-size: 12px; }

.stg-bar { height: 12px; border-radius: 6px; background: var(--surface-raised);
           border: 1px solid var(--border); overflow: hidden; }
.stg-fill { height: 100%; border-radius: 5px 0 0 5px; background: var(--success); transition: width .3s; }
.stg-fill.lv-warn     { background: var(--warning); }
.stg-fill.lv-critical { background: var(--danger); }
.stg-scale { display: flex; justify-content: space-between; color: var(--text-muted);
             font-size: 11px; margin: 5px 2px 14px; }

.stg-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 12px; }
.stg-tile { border: 1px solid var(--border); border-radius: 10px; padding: 12px 14px; background: var(--surface); }
.stg-tile strong { display: block; font-size: 21px; line-height: 1.2; margin: 3px 0 4px; }
.stg-tile.warn { border-color: var(--warning); }
.sd-note.stg-warn { border-left: 3px solid var(--warning); padding-left: 10px; }

.stg-spark { width: 100%; height: 84px; color: var(--primary); display: block; }
/* Cards sized to their contents: the chart is short and the table is long, and
   stretching the chart to match left a panel that looked like it had failed to
   load something. */
.sd-grid.stg-grid { align-items: start; }
/* The full table list can get long; keep the card a sane height and let the
   list scroll inside it rather than pushing everything else off the screen. */
.stg-tables { max-height: 420px; overflow-y: auto; }
/* The total pins to the bottom of the scroll area. The shadow is what makes it
   read as floating above the list rather than colliding with whichever row it
   happens to land on. */
.stg-tables tfoot td { position: sticky; bottom: 0; background: var(--surface);
                       border-top: 2px solid var(--border);
                       box-shadow: 0 -7px 11px -7px rgba(0, 0, 0, .22); }
.stg-overhead td { color: var(--text-muted); font-style: italic; }
