/* Yard OS — shared office styles. Dense, tabular, two themes.
 *
 * Colour and type take their cue from Head-Lee's own storefront: cream
 * serif wordmark, botanical logo (rose, gold blooms, foliage). So in both
 * themes: foliage green for the things you press, gold reserved for "a
 * human is needed here", rose for accents. Display type is a serif to echo
 * the wordmark — from the SYSTEM stack, because this file is served to the
 * CSP'd add-card page (style-src 'self', no font-src) and may never fetch
 * a font, an @import, or any other external URL.
 *
 * LIGHT IS THE DEFAULT and lives in :root — warm paper rather than clinical
 * white, because a yard office is a bright room and a screen full of #fff
 * is a lamp. DARK is the storefront's black page, kept exactly as it was,
 * and arrives two ways: [data-theme="dark"] when the visitor chose it, and
 * prefers-color-scheme when they never chose. lib/html.js stamps the
 * attribute on <html> from the yardos_theme cookie, so the first paint is
 * already right — no flash, no script. A stated choice always wins.
 *
 * DESIGN TOKENS live in :root; components reference them. Class names are
 * a CONTRACT with route markup and tests — refine values freely, never
 * rename. PCI-baselined: any byte change means
 * `node scripts/page-integrity.js --baseline` at deploy. */

:root {
  /* ---- light: warm paper, ink, foliage ----
     On the ground (#f1ebdd): ink 13.7:1, muted 6.8:1, links 5.2:1,
     warn 5.5:1, good 4.8:1, rose 5.1:1. The button: brand-ink on brand
     6.1:1. Gold goes bronze here — a #e0a32e link on cream is 1.9:1 and
     unreadable, and the "a human is needed here" job matters more than
     the exact hue. */
  --bg:#f1ebdd; --panel:#fdfbf5; --panel-2:#e7dfcb; --line:#cdc2a8;
  --ink:#22201a; --muted:#584f3e;
  --brand:#2f6b45; --brand-ink:#f7fbf7; --brand-dim:#22503a;
  --accent:#875610; --accent-ink:#fffaf0;
  --rose:#a63d63;
  --warn:#b3261e; --good:#2a7440;
  /* Ink that rides ON a solid warn/good chip (6.5:1 and 5.7:1). */
  --warn-ink:#ffffff; --good-ink:#ffffff;
  /* A field lifts off the paper and is outlined at 3:1 — the border is the
     only affordance a text box has, and this one is read in sunlight. */
  --field:#fffefb; --field-line:#8f8365; --line-strong:#6f6349;
  /* Washes: a hovered row, a row that wants a human, today, money owed. */
  --hover:rgba(34,32,26,.05);
  --accent-wash:rgba(135,86,16,.10);
  --brand-wash:rgba(47,107,69,.12);
  --warn-wash:rgba(179,38,30,.08);
  --scrim:rgba(38,32,20,.45);
  --shadow:0 8px 24px rgba(74,60,32,.20);
  --halo:#fdfbf5;                    /* stroke behind SVG map labels */
  --swatch-edge:rgba(34,32,26,.30);  /* keeps a pale legend dot from dissolving */

  /* ---- map-locked: the same in BOTH themes ----
     A pin's MEANING may not change with the theme: the legend dot in the
     day bar and the dot on the map are the same class, and both read the
     tokens below. Keep --zone-* in step with ZONE_COLORS in lib/leaflet.js
     — the swatch is drawn here, the pin is drawn there.

     The stop colours are the day, in the order a dispatcher reads it:
     RED still owed, GREEN rolling, GREY spent. Gold is not in that set on
     purpose — this app spends gold on "a human is needed here", which is
     exactly what --map-flag now rings a delivery with. */
  --zone-0:#c98a12; --zone-1:#3f8f57; --zone-2:#3d78d8;
  --zone-3:#b4436c; --zone-4:#8b5fd0; --zone-5:#2f9c98;
  --stop-done:#8b8578; --stop-rolling:#2f9e5a; --stop-togo:#cf3b34;
  --stop-ink:#ffffff;
  --map-flag:#d99417; --map-flag-halo:rgba(217,148,23,.40);

  /* ---- map chrome: follows the basemap, which follows the theme ----
     A white ring is invisible on the white basemap and a black one is
     invisible on the black. These four are the only map tokens that move. */
  --map-ink:#0c0f0e; --map-ring:rgba(20,25,22,.55);
  --map-chip:rgba(255,255,255,.92); --map-shadow:rgba(0,0,0,.30);

  --radius:8px; --radius-sm:6px;
  --s1:.25rem; --s2:.5rem; --s3:.75rem; --s4:1rem; --s5:1.5rem; --s6:2.5rem;
  --fs-sm:.8rem; --fs-xs:.72rem;
  --side:14.5rem;
  --display: Georgia, "Iowan Old Style", "Times New Roman", serif;
  font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
}

/* ---- dark: the storefront's black page, unchanged ----
   THE TWO BLOCKS BELOW MUST STAY IDENTICAL. CSS has no way to name a set
   of declarations and apply it from inside a media query, so the palette
   is written twice on purpose: once for the visitor who chose dark, once
   for the visitor whose machine asked for it and who has chosen nothing.
   Edit one, edit the other. */
:root[data-theme="dark"] {
  --bg:#0c0f0e; --panel:#141917; --panel-2:#1b2220; --line:#28312d;
  --ink:#f1ece2; --muted:#93a09a;
  --brand:#3f7d54; --brand-ink:#f4fbf6; --brand-dim:#2a5a3b;
  --accent:#e0a32e; --accent-ink:#14100a;
  --rose:#c2557a;
  --warn:#e5484d; --good:#5aab6a;
  --warn-ink:#ffffff; --good-ink:#0c0f0e;
  --field:#0c0f0e; --field-line:#28312d; --line-strong:#3a463f;
  --hover:rgba(255,255,255,.03);
  --accent-wash:rgba(224,163,46,.08);
  --brand-wash:rgba(63,125,84,.10);
  --warn-wash:rgba(229,72,77,.08);
  --scrim:rgba(0,0,0,.6);
  --shadow:0 8px 24px rgba(0,0,0,.45);
  --halo:#0c0f0e;
  --swatch-edge:transparent;
  /* The map goes dark with the page; its chrome inverts with it. */
  --map-ink:#f1ece2; --map-ring:rgba(255,255,255,.85);
  --map-chip:rgba(12,15,14,.85); --map-shadow:rgba(0,0,0,.55);
}

/* The machine's preference, honoured only while the visitor has not said
   otherwise: the cookie stamps data-theme (light OR dark), and a stamped
   root never matches :not([data-theme]). */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) {
    --bg:#0c0f0e; --panel:#141917; --panel-2:#1b2220; --line:#28312d;
    --ink:#f1ece2; --muted:#93a09a;
    --brand:#3f7d54; --brand-ink:#f4fbf6; --brand-dim:#2a5a3b;
    --accent:#e0a32e; --accent-ink:#14100a;
    --rose:#c2557a;
    --warn:#e5484d; --good:#5aab6a;
    --warn-ink:#ffffff; --good-ink:#0c0f0e;
    --field:#0c0f0e; --field-line:#28312d; --line-strong:#3a463f;
    --hover:rgba(255,255,255,.03);
    --accent-wash:rgba(224,163,46,.08);
    --brand-wash:rgba(63,125,84,.10);
    --warn-wash:rgba(229,72,77,.08);
    --scrim:rgba(0,0,0,.6);
    --shadow:0 8px 24px rgba(0,0,0,.45);
    --halo:#0c0f0e;
    --swatch-edge:transparent;
    --map-ink:#f1ece2; --map-ring:rgba(255,255,255,.85);
    --map-chip:rgba(12,15,14,.85); --map-shadow:rgba(0,0,0,.55);
  }
}
/* The browser's own widgets (scrollbars, date pickers, the select popup)
   follow this, so they stop being black holes on paper. */
:root { color-scheme: light; }
:root[data-theme="dark"] { color-scheme: dark; }
@media (prefers-color-scheme: dark) { :root:not([data-theme]) { color-scheme: dark; } }

* { box-sizing: border-box; }
body { margin:0; background:var(--bg); color:var(--ink); line-height:1.45; }
a { color:var(--accent); text-decoration:none; }
a:hover { text-decoration:underline; }
:focus-visible { outline:2px solid var(--accent); outline-offset:2px; border-radius:2px; }

/* ---- the shell: nav down the left, work on the right ---- */
.shell { display:grid; grid-template-columns:var(--side) 1fr; min-height:100vh; }
.side { background:var(--panel); border-right:1px solid var(--line);
  display:flex; flex-direction:column; gap:var(--s2);
  position:sticky; top:0; height:100vh; overflow-y:auto; padding:var(--s4) 0; }
.side .brand { display:flex; align-items:center; gap:.6rem; padding:0 var(--s4) var(--s3);
  font-family:var(--display); font-size:1.25rem; font-weight:400; letter-spacing:.01em;
  color:var(--ink); line-height:1.15; }
.side .brand:hover { text-decoration:none; }
.brandmark { flex:none; width:1.6rem; height:1.6rem; border-radius:50%;
  background:radial-gradient(circle at 38% 34%, var(--rose) 0 32%, transparent 33%),
             radial-gradient(circle at 66% 42%, var(--accent) 0 24%, transparent 25%),
             radial-gradient(circle at 48% 70%, var(--brand) 0 34%, transparent 35%),
             var(--panel-2);
  border:1px solid var(--line); }
.brandsub { padding:0 var(--s4) var(--s4); color:var(--muted); font-size:var(--fs-xs);
  text-transform:uppercase; letter-spacing:.12em; }
.side nav { display:flex; flex-direction:column; }
.navgroup { color:var(--muted); font-size:var(--fs-xs); text-transform:uppercase;
  letter-spacing:.12em; padding:var(--s4) var(--s4) var(--s1); }
.side nav a { display:block; color:var(--muted); font-size:.92rem;
  padding:var(--s2) var(--s4); border-left:3px solid transparent; }
.side nav a:hover { color:var(--ink); background:var(--panel-2); text-decoration:none; }
.side nav a.on { color:var(--ink); border-left-color:var(--brand);
  background:var(--panel-2); font-weight:600; }
/* The per-yard dashboards, indented under Home: the same page through one
   gate, not five more doors. Owner rails only — see lib/html.js yardNav. */
.side nav .navyards { display:flex; flex-direction:column;
  margin:0 0 var(--s2); }
.side nav .navyards a { font-size:.84rem; padding:.15rem var(--s4) .15rem 2.1rem; }
.side nav .navyards a.on { font-weight:600; }
.side .who { margin-top:auto; padding:var(--s4); border-top:1px solid var(--line);
  color:var(--muted); font-size:var(--fs-sm); display:flex; align-items:center;
  justify-content:space-between; gap:var(--s2); flex-wrap:wrap; }
.side .who form { margin:0; }
/* Who you are, then the two things you can do about the session and the
   screen. They wrap under the name in the 14.5rem rail rather than
   squeezing it. */
.whoacts { display:flex; align-items:center; gap:var(--s2); margin-left:auto; }
/* The theme switch ships BOTH directions and CSS picks the one that is
   true, because only CSS knows the answer when the visitor has chosen
   nothing and their machine asked for dark. Exactly one is ever visible,
   so the rail never widens. Order matches the palette blocks above. */
.themeform.to-light { display:none; }
:root[data-theme="dark"] .themeform.to-dark { display:none; }
:root[data-theme="dark"] .themeform.to-light { display:block; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .themeform.to-dark { display:none; }
  :root:not([data-theme]) .themeform.to-light { display:block; }
}

main { max-width:78rem; margin:0 auto; padding:var(--s5) 1.75rem 4rem; width:100%; }

/* Signed out: no sidebar, just the mark over the form. */
.topbar { display:flex; align-items:center; gap:var(--s5); padding:var(--s3) 1.25rem;
  background:var(--panel); border-bottom:1px solid var(--line); }
.topbar.bare { justify-content:center; }
.topbar .brand { display:flex; align-items:center; gap:.5rem;
  font-family:var(--display); font-size:1.15rem; }

/* Narrow screens (a phone, a tablet held tall): the rail becomes a strip. */
@media (max-width: 60rem) {
  .shell { grid-template-columns:1fr; }
  .side { position:static; height:auto; flex-direction:row; flex-wrap:wrap;
    align-items:center; padding:var(--s2) var(--s3); gap:var(--s2);
    border-right:0; border-bottom:1px solid var(--line); }
  .side .brand { padding:0 var(--s3) 0 0; font-size:1.05rem; }
  .brandsub { display:none; }
  .navgroup { display:none; }
  .side nav { flex-direction:row; flex-wrap:wrap; gap:var(--s1); }
  .side nav a { padding:var(--s1) var(--s3); border-left:0; border-radius:999px;
    border:1px solid transparent; }
  .side nav a.on { border-color:var(--brand); background:transparent; }
  /* On a strip the yards cannot indent, so they sit in the row as pills
     with the rest — still after Home, which is where they belong. */
  .side nav .navyards { flex-direction:row; flex-wrap:wrap; gap:var(--s1);
    margin:0; }
  .side nav .navyards a { padding:var(--s1) var(--s3); font-size:.8rem; }
  .side .who { margin:0 0 0 auto; padding:0; border-top:0; }
  main { padding:var(--s4) var(--s4) 3rem; }
}

/* ---- type ---- */
h1 { font-family:var(--display); font-size:1.7rem; font-weight:400;
  margin:0 0 var(--s1); letter-spacing:-.01em; }
h2 { font-size:1.05rem; margin:0 0 var(--s3); }
/* .muted is a TONE; the margin belongs only to paragraph-level subtitles —
   div.muted inside table cells must not inherit a 1rem gap. */
.muted { color:var(--muted); margin:0; }
p.muted { margin:0 0 var(--s4); }
.warn { color:var(--warn); }
.small { font-size:.78rem; }
/* Visually hidden, still read by assistive tech — for naming otherwise
   empty <th> columns and unlabeled inline controls. */
.vh { position:absolute; width:1px; height:1px; overflow:hidden;
  clip-path:inset(50%); white-space:nowrap; }

/* ---- tables ---- */
table.grid { width:100%; border-collapse:collapse; font-size:.93rem; }
table.grid th { text-align:left; color:var(--muted); font-weight:600;
  font-size:var(--fs-sm); text-transform:uppercase; letter-spacing:.04em;
  padding:var(--s2) .6rem; border-bottom:1px solid var(--line); }
table.grid td { padding:var(--s2) .6rem; border-bottom:1px solid var(--line);
  vertical-align:top; }
table.grid tbody tr:hover td { background:var(--hover); }
table.grid td.num, table.grid th.num { text-align:right; font-variant-numeric:tabular-nums; }
table.grid tr.voided td { opacity:.5; text-decoration:line-through; }
table.grid tfoot th { text-align:left; padding:var(--s2) .6rem;
  border-top:2px solid var(--line); font-variant-numeric:tabular-nums; }
table.grid tfoot th.num { text-align:right; }
/* Some footers total a body of <td>s rather than restating headers — the
   all-yards overview's company line, for one. */
table.grid tfoot td { padding:var(--s2) .6rem; border-top:2px solid var(--line);
  border-bottom:0; font-weight:600; font-variant-numeric:tabular-nums; }

/* ---- badges & chips ---- */
.tag { display:inline-block; margin-left:.4rem; padding:.08rem .4rem; border-radius:4px;
  background:var(--line); color:var(--muted); font-size:.7rem; font-weight:600;
  letter-spacing:.03em; vertical-align:middle; }
.tag.hold { background:var(--warn); color:var(--warn-ink); }
.tag.ok { background:var(--good); color:var(--good-ink); }
.rung { display:inline-block; background:var(--bg); border:1px solid var(--line);
  border-radius:4px; padding:.1rem .4rem; margin:0 var(--s1) var(--s1) 0;
  font-size:var(--fs-sm); font-variant-numeric:tabular-nums; }

/* ---- cards & forms ---- */
.card { background:var(--panel); border:1px solid var(--line); border-radius:10px;
  padding:1.25rem; margin-top:var(--s5); max-width:26rem; }
.card label { display:block; margin:var(--s3) 0; color:var(--muted); font-size:.85rem; }
input { width:100%; margin-top:.3rem; padding:var(--s2) .6rem; background:var(--field);
  color:var(--ink); border:1px solid var(--field-line); border-radius:var(--radius-sm);
  font:inherit; }
input:hover, select:hover { border-color:var(--line-strong); }
select { width:100%; margin-top:.3rem; padding:var(--s2) .6rem; background:var(--field);
  color:var(--ink); border:1px solid var(--field-line); border-radius:var(--radius-sm);
  font:inherit; }
button { padding:var(--s2) .9rem; background:var(--brand); color:var(--brand-ink);
  border:0; border-radius:var(--radius-sm); font:inherit; font-weight:600;
  cursor:pointer; }
button:hover { filter:brightness(1.12); }
button:active { transform:translateY(1px); }
.login { margin:clamp(3rem, 14vh, 7rem) auto; }
.login button[type="submit"] { width:100%; margin-top:var(--s4); padding:var(--s3) .9rem; }
.error { color:var(--warn); font-size:.9rem; }
.card .error { border:1px solid var(--warn); border-radius:var(--radius-sm);
  padding:var(--s2) var(--s3); margin:0 0 var(--s3); }
.card .error:empty { display:none; }
/* Link dressed as the primary button, for navigational CTAs. */
.btnlink { display:inline-block; padding:var(--s2) .9rem; background:var(--brand);
  color:var(--brand-ink); border-radius:var(--radius-sm); font-weight:600; }
.btnlink:hover { text-decoration:none; filter:brightness(1.12); }
/* Shared empty-state cell for header-only tables. */
table.grid td.emptystate { color:var(--muted); text-align:center;
  padding:var(--s5) .6rem; }
.inlineform { display:inline; }
.smallbtn { padding:var(--s1) .6rem; font-size:.78rem; }
.ghostbtn { background:transparent; color:var(--muted); border:1px solid var(--line); }
.ghostbtn:hover { color:var(--ink); border-color:var(--muted); filter:none; }
.warnbtn { color:var(--warn); border-color:var(--warn); }
.chk { display:flex; gap:var(--s2); align-items:flex-start; font-size:.85rem;
  color:var(--ink) !important; }
.chk input { width:auto; margin-top:.15rem; }

/* Ticking a ticket aims a payment at it. The whole cell is the target —
   a checkbox alone is a small thing to hit on a tablet. */
.tickpick { display:flex; align-items:center; gap:var(--s2); cursor:pointer; }
.tickpick input { width:auto; margin:0; }

/* ---- recording a payment ---- */
.payform .methods { border:0; padding:0; margin:var(--s3) 0 var(--s2);
  display:flex; gap:var(--s2); flex-wrap:wrap; }
.payform .methods legend { color:var(--muted); font-size:.85rem;
  padding:0 0 var(--s2); }
/* A method is a choice among four, so it is four buttons — not a dropdown
   that hides three of them behind a tap. */
.mchip { position:relative; }
.mchip input { position:absolute; opacity:0; width:1px; height:1px; }
.mchip span { display:inline-block; padding:var(--s2) var(--s4);
  border:1px solid var(--line); border-radius:999px; cursor:pointer;
  font-size:.9rem; color:var(--muted); background:var(--panel); }
.mchip input:checked + span { border-color:var(--brand); color:var(--brand-ink);
  background:var(--brand); font-weight:600; }
.mchip input:focus-visible + span { outline:2px solid var(--accent);
  outline-offset:2px; }

/* Each method asks for the reference IT has. The chosen radio reveals its
   own field and hides the other three — no script, so this holds on a
   phone with JS off. The fields keep separate names, so the ones lying
   hidden submit blank and the server reads only the one that matches. */
.payform .ref { display:none; }
.payform:has(input[value="check"]:checked) .ref-check,
.payform:has(input[value="ach"]:checked) .ref-ach,
.payform:has(input[value="card"]:checked) .ref-card,
.payform:has(input[value="cash"]:checked) .ref-cash { display:block; }
/* Without :has() every reference shows, each clearly labelled — more form
   than needed, but never the WRONG label over a box. */
@supports not selector(:has(*)) { .payform .ref { display:block; } }

.paysum { margin:var(--s2) 0 var(--s3); }
.payfill { margin-top:var(--s2); }

/* ---- KPI strip ---- */
.kpis { display:flex; gap:1px; background:var(--line); border:1px solid var(--line);
  border-radius:var(--radius); overflow:hidden; margin:var(--s4) 0 var(--s5);
  flex-wrap:wrap; }
.kpi { flex:1 1 8rem; background:var(--panel); padding:var(--s3) var(--s4);
  display:flex; flex-direction:column; gap:.15rem; }
.kpi span { color:var(--muted); font-size:.75rem; text-transform:uppercase;
  letter-spacing:.05em; }
.kpi strong { font-size:1.35rem; font-variant-numeric:tabular-nums; }
.kpi em { font-style:normal; font-size:var(--fs-xs); color:var(--muted); }
.kpi em.down { color:var(--warn); }
.kpi em.up { color:var(--good); }

/* ---- page furniture ---- */
.section-break { margin-top:var(--s6); }
.daterange { display:flex; gap:var(--s3); align-items:flex-end; margin:0 0 1.25rem;
  flex-wrap:wrap; }
.daterange label { color:var(--muted); font-size:.75rem; text-transform:uppercase;
  letter-spacing:.05em; }
.daterange input { width:auto; }

/* Daily total bar: one hue, values live in the table's ink — the bar only
   carries magnitude. Left edge sits on the baseline, data end rounded. */
table.daily td.barcell { width:22%; min-width:8rem; }
table.daily .bar { display:block; height:8px; background:var(--brand);
  border-radius:0 4px 4px 0; }
/* Deciles as classes, not an inline width: new markup carries no
   style="" (the reports page's inline bar is the one legacy exception). */
.bar.w0 { width:2%; } .bar.w1 { width:10%; } .bar.w2 { width:20%; }
.bar.w3 { width:30%; } .bar.w4 { width:40%; } .bar.w5 { width:50%; }
.bar.w6 { width:60%; } .bar.w7 { width:70%; } .bar.w8 { width:80%; }
.bar.w9 { width:90%; } .bar.w10 { width:100%; }

/* The hosted card iframe on the office add-card page. A class, not an
   inline style: that page runs under a CSP without 'unsafe-inline'. */
.cardframe { width:100%; height:6rem; border:0; display:block; }

/* ---- deliveries + availability ---- */
.dlvtabs { display:flex; gap:var(--s1); margin:var(--s4) 0; flex-wrap:wrap; }
.dlvtab { padding:.4rem .8rem; border:1px solid var(--line);
  border-radius:var(--radius-sm); color:var(--muted); font-size:.85rem; }
.dlvtab.active { color:var(--ink); border-color:var(--brand); background:var(--panel-2); }
.dlvtab:hover { text-decoration:none; border-color:var(--brand); }
tr.review td { background:var(--accent-wash); }
tr.outrow td { opacity:.75; }
.actions form { margin-bottom:var(--s1); }
.yardpick { width:auto; margin:0 var(--s1) 0 0; padding:var(--s1) .4rem; font-size:.78rem; }
.whypick { width:7rem; margin:0 var(--s1) 0 0; padding:var(--s1) .4rem; font-size:.78rem; }
/* "When?" is a date and a TIME YOU PICK — see routes/when-fields.js. In a
   table cell the two sit side by side; in a card they share one row under
   the label. */
.timepick { width:auto; margin:0 var(--s1) 0 0; padding:var(--s1) .4rem;
  font-size:.78rem; }
.inlineform input[type="date"] { width:auto; margin:0 var(--s1) 0 0;
  padding:var(--s1) .4rem; font-size:.78rem; }
.whenrow { display:flex; gap:var(--s2); margin-top:.3rem; }
.whenrow input[type="date"] { margin-top:0; }
.whenrow .timepick { width:auto; flex:1; margin:0; padding:var(--s2) .6rem;
  font-size:inherit; }
/* Ledger entry inside a table row — a load received, a pile counted. The
   base input rule is width:100%; these are the inline exceptions. */
.qtyin { width:4.5rem; margin:0 var(--s1) 0 0; padding:var(--s1) .4rem;
  font-size:.78rem; text-align:right; font-variant-numeric:tabular-nums; }
.notein { width:8rem; margin:0 var(--s1) 0 0; padding:var(--s1) .4rem;
  font-size:.78rem; }

/* ---- home dashboard ---- */
.dashhead { display:flex; align-items:baseline; justify-content:space-between;
  gap:var(--s4); flex-wrap:wrap; }
.attnrow { display:flex; gap:var(--s2); flex-wrap:wrap; margin:var(--s4) 0 var(--s2); }
.attn { display:inline-block; background:var(--panel); border:1px solid var(--accent);
  border-radius:var(--radius); padding:var(--s2) var(--s4); color:var(--ink);
  font-size:.9rem; }
.attn:hover { text-decoration:none; background:var(--panel-2); }
.attn strong { color:var(--accent); margin-right:.25rem; }
.attn.warn2 { border-color:var(--warn); }
.attn.warn2 strong { color:var(--warn); }
.doors { display:grid; grid-template-columns:repeat(auto-fill, minmax(11rem, 1fr));
  gap:var(--s3); margin-top:var(--s5); }
.door { display:flex; flex-direction:column; gap:.2rem; background:var(--panel);
  border:1px solid var(--line); border-radius:10px; padding:var(--s4);
  color:var(--ink); }
.door:hover { text-decoration:none; border-color:var(--brand); }
.door strong { font-size:1.05rem; }
.door .muted { margin:0; }

/* Dashboard panels flow in two columns rather than sitting in grid ROWS:
   a row reserves the height of its tallest panel, so a short panel beside
   a long one left a lake of dead space underneath it. Columns let each
   panel take exactly the height it needs and the next one close up behind. */
.dash { columns:2; column-gap:var(--s5); margin-top:var(--s5); }
.dash > .panel { break-inside:avoid; margin:0 0 var(--s5); }
@media (max-width: 68rem) { .dash { columns:1; } }
.panel { background:var(--panel); border:1px solid var(--line); border-radius:10px;
  padding:var(--s4) var(--s4) var(--s2); }
.panel > h2 { display:flex; align-items:baseline; justify-content:space-between;
  gap:var(--s3); margin-bottom:var(--s3); }
.panel > h2 a { font-size:var(--fs-sm); font-weight:400; }
.panel table.grid { font-size:.88rem; }
.panel table.grid td, .panel table.grid th { padding:var(--s2) .35rem; }
.panel table.grid tr:last-child td { border-bottom:0; }

/* Delivery status rail — where every open load stands, at a glance. */
.statusrail { display:flex; gap:var(--s2); flex-wrap:wrap; margin-bottom:var(--s3); }
.statuspill { flex:1 1 5rem; background:var(--bg); border:1px solid var(--line);
  border-radius:var(--radius-sm); padding:var(--s2) var(--s3); color:var(--ink); }
.statuspill:hover { text-decoration:none; border-color:var(--brand); }
.statuspill b { display:block; font-size:1.3rem; font-variant-numeric:tabular-nums;
  line-height:1.1; }
.statuspill span { color:var(--muted); font-size:var(--fs-xs);
  text-transform:uppercase; letter-spacing:.06em; }
.statuspill.is-truck { border-color:var(--good); }
.statuspill.is-truck b { color:var(--good); }
.statuspill.is-review { border-color:var(--accent); }
.statuspill.is-review b { color:var(--accent); }

/* Month calendar. Server-rendered, no JS: dots mean something is booked. */
.cal { width:100%; border-collapse:collapse; table-layout:fixed; }
.cal th { color:var(--muted); font-size:var(--fs-xs); font-weight:600;
  text-transform:uppercase; letter-spacing:.06em; padding:var(--s1) 0;
  text-align:center; }
.cal td { border:1px solid var(--line); height:3.4rem; vertical-align:top;
  padding:.2rem .3rem; }
.cal td.pad { border-color:transparent; }
.cal .dnum { color:var(--muted); font-size:var(--fs-xs);
  font-variant-numeric:tabular-nums; }
.cal td.today { border-color:var(--brand); background:var(--brand-wash); }
.cal td.today .dnum { color:var(--ink); font-weight:700; }
.cal td.has a { display:block; color:var(--ink); }
.cal td.has a:hover { text-decoration:none; }
.cal .dots { display:flex; gap:2px; flex-wrap:wrap; margin-top:.15rem; }
.cal .dot { width:6px; height:6px; border-radius:50%; background:var(--brand); }
.cal .dot.appt { background:var(--rose); }
.cal .cnt { font-size:var(--fs-xs); color:var(--muted); }
.callegend { display:flex; gap:var(--s4); margin:var(--s3) 0 var(--s2);
  color:var(--muted); font-size:var(--fs-xs); }
.callegend i { display:inline-block; width:8px; height:8px; border-radius:50%;
  margin-right:.35rem; vertical-align:.02rem; background:var(--brand); }
.callegend i.appt { background:var(--rose); }

/* ---- zones map ---- */
.zonemap { width:100%; max-width:62rem; height:auto; background:var(--panel);
  border:1px solid var(--line); border-radius:10px; margin-top:var(--s4); }
.zonemap .yardlabel { fill:var(--ink); font-size:15px; font-weight:700;
  paint-order:stroke; stroke:var(--halo); stroke-width:4px; stroke-linejoin:round; }
.zonestats { max-width:36rem; }
/* Legend swatches keep the map's colours in both themes (see the
   map-locked tokens); on paper the pale ones get a hairline so a gold or
   teal chip does not dissolve into the page. Inset, so no geometry moves. */
.zonechip { display:inline-block; width:.85rem; height:.85rem; border-radius:3px;
  margin-right:.45rem; vertical-align:-.08rem;
  box-shadow:inset 0 0 0 1px var(--swatch-edge); }
.zc0 { background:var(--zone-0); } .zc1 { background:var(--zone-1); }
.zc2 { background:var(--zone-2); } .zc3 { background:var(--zone-3); }
.zc4 { background:var(--zone-4); } .zc5 { background:var(--zone-5); }

/* The day bar over the road map: which day, and how it is going. */
.daybar { display:flex; align-items:center; gap:var(--s3); flex-wrap:wrap;
  margin:var(--s4) 0 var(--s2); }
.daybar strong { font-family:var(--display); font-size:1.1rem; font-weight:400; }
.daycounts { display:flex; align-items:center; gap:var(--s2); margin-left:auto;
  color:var(--muted); font-size:var(--fs-sm); }
.daycounts .stopdot { margin-left:var(--s3); }

/* One legend dot, shared by the day bar, the map legend and the stop
   table, so the colour in the list is the colour on the map. */
.stopdot { display:inline-block; width:.65rem; height:.65rem; border-radius:50%;
  margin-right:.4rem; vertical-align:-.04rem; background:var(--stop-togo);
  box-shadow:inset 0 0 0 1px var(--swatch-edge); }
.stopdot.s-delivered { background:var(--stop-done); }
.stopdot.s-out_for_delivery { background:var(--stop-rolling); }
.stopdot.s-confirmed, .stopdot.s-received { background:var(--stop-togo); }
.stopdot.flagged { background:transparent;
  box-shadow:inset 0 0 0 3px var(--map-flag); }

/* Under the map: what the three colours mean, said once, in the colours
   themselves. A dispatcher should never have to be told twice. */
.maplegend { display:flex; flex-wrap:wrap; gap:var(--s4);
  margin:var(--s2) 0 var(--s3); color:var(--muted); font-size:var(--fs-sm); }
.maplegend span { display:inline-flex; align-items:center; }

/* Numbered stop pins. The fill comes from the status class so the map and
   the legend can never drift apart — public/zones.js sets no colours. */
.stoppin { white-space:nowrap; }
.stoppin-dot { display:flex; align-items:center; justify-content:center;
  width:22px; height:22px; transform:translate(-11px,-11px); border-radius:50%;
  border:2px solid var(--map-ring); color:var(--stop-ink); font-size:11px;
  font-weight:700; background:var(--stop-togo);
  box-shadow:0 1px 5px var(--map-shadow); }
.stoppin-dot.s-received, .stoppin-dot.s-confirmed { background:var(--stop-togo); }
.stoppin-dot.s-out_for_delivery { background:var(--stop-rolling); }
.stoppin-dot.s-delivered { background:var(--stop-done); }
/* Gold, not red: red is now "still to deliver", and a load needing a human
   has to be distinguishable from the twelve around it that do not. */
.stoppin-dot.flagged { border-color:var(--map-flag);
  box-shadow:0 0 0 3px var(--map-flag-halo); }
/* No street match — this dot is the middle of a ZIP, and it is not going to
   pretend otherwise by drawing as boldly as the ones that are real. */
.stoppin-dot.approx { opacity:.6; }

/* The ZIP lookup ring. Styled here, not in Leaflet options, so it inverts
   with the basemap (CSS beats SVG presentation attributes). */
.ziplookup { stroke:var(--ink); fill:var(--panel); fill-opacity:.9; }

/* The dashboard's road map: shorter than the board's, same pins. */
.homemap { height:22rem; border:1px solid var(--line); border-radius:10px;
  margin:var(--s2) 0 var(--s3); background:var(--bg); }

/* Live zones map (Leaflet) */
.zonemap-live { height:34rem; border:1px solid var(--line); border-radius:10px;
  margin-top: var(--s4); background:var(--panel); }
.yardpin { white-space:nowrap; }
.yardpin-diamond { display:inline-block; width:16px; height:16px;
  transform: translate(-8px,-8px) rotate(45deg); border:2px solid var(--map-ink);
  box-shadow:0 1px 4px var(--map-shadow); }
.yardpin-label { position:relative; top:-14px; left:4px; font-weight:700;
  font-size:13px; color:var(--map-ink); background:var(--map-chip);
  padding:1px 6px; border-radius:4px; box-shadow:0 1px 3px var(--map-shadow); }
