/* routing-app.com styles.
   REBUILT 2026-08-20 night (session 36) around RC 69/70: a page is
   ARCHITECTED for its device — the work surface (list, map, table) gets the
   screen; chrome shrinks, collapses or goes. Desktop = multi-pane workspace,
   zero page scroll (RC 57/63). Phone = its own arrangement, never one giant
   scroll (RC 70): the map is the canvas, the stop list is a slide-up sheet.
   Phone-first base styles; the desktop layer is gated on
   (min-width:1000px) + body:not(.phone).
   ⛔ `body.phone` NO LONGER EXISTS — it was the admin's simulated phone view
   (RC 53/95), removed entirely on Sep 6 (RC 535). ⭐ SO EVERY REMAINING
   `body:not(.phone)` IS ALWAYS TRUE and changes nothing; the desktop layer is
   gated on the WIDTH alone now.
   ⏳ THE 65 QUALIFIERS WERE LEFT IN DELIBERATELY, not overlooked. Stripping
   them lowers each selector's specificity by one class, which can change
   which rule wins a tie anywhere in the desk layout — a real risk of a
   visible change, for a purely cosmetic gain, in the same breath as removing
   a feature. They come out in their own mechanical sweep, with the pixel
   machine as the check. */

:root { --ink:#111; --bg:#fff; --accent:#0a58ca; --err:#b00020; --muted:#666; }

/* ============================================================ THE LOOK ====
   ⭐ CHANGE THE SITE HERE. Every page on the new shell reads these names and
   holds no colours of its own, so re-skinning the whole thing is editing this
   block. (RC 170; the operator: "a way to tweak the whole site by changing
   small universal things".)
   ⚠️ The five names ABOVE this block are the OLD look. The driver's live pages
      still use them. Do not repoint them; pages migrate one at a time. */
:root {
  /* the dark menu */
  --nav:        #0a2a5e;
  --nav-ink:    #cfd9ea;
  --nav-hover:  #ffffff14;
  --nav-active: #ffffff1e;
  --nav-mark:   #7fb2ff;
  --nav-edge:   #ffffff22;
  --nav-w:      200px;

  /* paper and ink */
  --ground:  #eef1f5;
  --card:    #ffffff;
  --line:    #dde3ea;
  --hair:    #f0f3f6;
  --hover:   #f8fafd;
  --band:    #f7f9fb;
  --text:    #16202b;
  --dim:     #66707c;
  --faint:   #8a95a1;

  /* the one accent, and the three meanings */
  --blue:      #2563c9;
  --blue-soft: #e8f0fe;
  --blue-ink:  #2456a8;
  --blue-edge: #b7cdf3;
  --green:      #157a3b;
  --green-soft: #ddf3e4;
  --green-ink:  #14672f;
  --green-edge: #93d1a8;
  --amber-ink:  #7a5a00;
  --amber-soft: #fff4dc;
  --amber-edge: #ebcb7f;
  --red:      #b00020;
  --red-soft: #fde8ec;

  /* ⭐ NAMES FOR THE COLOURS THE FILE REPEATED AS LITERALS (audit item 6,
     Sep 9). Each value below appeared eight to twenty-two times as a bare hex;
     every one of those sites reads the name now. Values unchanged — this is
     naming, not re-skinning; folding these into the palette above (one blue,
     one green, one grey ladder) is a picture round, not a quiet edit. */
  --ok:        #1a8f3a;   /* the tap slab's green, "delivered" */
  --toast-bg:  #1d2530;   /* the toast, and the press-and-hold bubble */
  --edge:      #c9d2dd;   /* a control's border on the desk */
  --edge-soft: #e6e9ee;   /* a quieter divider */
  --edge-plain:#ddd;      /* the oldest grey line, still on a few driver pages */
  --ctl-edge:  #d0d7de;   /* the standard button's border (Bootstrap's grey) */
  --ctl-bg:    #f6f8fa;   /* ...its face */
  --ctl-hover: #eef1f4;   /* ...its face under the pointer */
  --ctl-ink:   #24292f;   /* ...its text */

  /* ⭐ THE STACKING ORDER, NAMED (audit item 6). Twenty bare numbers became
     one scale; the values are the ones the site already used, so nothing
     re-stacks — but "what sits over what" is now readable in one place.
     In the page: rise < sticky < cell < drop. Over the page: sheet < menu <
     menu-hi < pane < overlay < scrim < bar < queue < modal. The kit's own
     layers and the native dialogs sit above all of it; the skip link tops
     everything. */
  --z-rise: 1;      --z-sticky: 2;    --z-cell: 4;      --z-drop: 5;
  --z-sheet: 20;    --z-menu: 30;     --z-menu-hi: 35;  --z-pane: 40;
  --z-overlay: 50;  --z-scrim: 55;    --z-bar: 60;      --z-queue: 70;
  --z-modal: 80;
  --z-kit-back: 1050; --z-kit: 1060;  --z-toast: 1070;  --z-kit-top: 1080;
  --z-dialog-back: 1200; --z-dialog: 1210;
  --z-skip: 2000;

  /* ==== THE RULER (Aug 25 night): measured design tokens, not eyeballing.
     Sources: Material Design (8px spacing grid, 48px touch targets),
     Apple HIG (44pt minimum tap size), readability practice (45-75ch
     lines), standard type ladders. Every kit pane builds ON these.
     Moved into this one block Sep 9 (audit item 6) from beside the kit's
     rules; values untouched. ⚠️ Three type ladders still coexist below —
     --fs-* (the old look), --t-* (this ruler) and --L-* (the design
     system): each page reads the one it was converted on. Reconciling them
     moves text on every screen and is a picture round (UI_SPEC 13.A1). */
  --s1: 4px;  --s2: 8px;  --s3: 16px;  --s4: 24px;  --s5: 32px; /* spacing grid */
  --row: 40px;            /* a dense desktop list row */
  --t-body: 16px; --t-small: 14px; --t-tiny: 12.5px;
  --t-h3: 20px;  --t-h2: 24px;     /* the type ladder — no sizes off it */
  --measure: 65ch;        /* longest comfortable text line */
  --pane-pad: var(--s3);
  /* the desktop GRAIN (Aug 27): 14px rows / 12.5px meta, 32px controls */
  --t-row: 14px; --t-meta: 12.5px; --ctl-h: 32px;

  /* ⭐ CORNERS MATCH — one scale, three roles (RC 161, said twice) */
  --r-card: 12px;   /* panes, cards, popups            */
  --r-ctl:  8px;    /* buttons, inputs, selects, small square controls */
  --r-pill: 999px;  /* status chips, and only those    */

  /* type */
  --fs-title: 1.2rem;
  --fs-pane:  .98rem;
  --fs-body:  .9rem;
  --fs-small: .8rem;
  --fs-label: .72rem;

  /* rhythm */
  --pad:  1.1rem;
  --gap:  .9rem;
  --tap:  44px;     /* nothing tappable on a phone is smaller */
}
/* ================================================== ⭐ THE LADDER ==========
   THE DESIGN SYSTEM'S ONE TYPE-AND-RHYTHM DIAL (UI_SPEC §13.A1, RC 438 —
   the rebuild's first foundation piece, Sep 2). ONE ladder for the whole
   site; every BLOCK reads only these names; changing the site's density is
   editing THIS BLOCK and nothing else.
   PROVENANCE: the "denser" candidate, provisional at the operator's word
   Sep 2 ("changing something is super easy... unfolds across the whole
   site") — the real ruling lands on the first fully-converted page's
   picture, where turning this dial visibly moves everything. Values
   measured from the exemplars the operator called "way better" (RC 303/305)
   and the Aug-25 ruler; the 20px root is a design constant (§13.A1).
   ⚠ THE THREE OLDER :root BLOCKS BELOW (the old look's --fs-*, the ruler's
   --t-*, the grain's --t-row/--t-meta) are what TODAY'S pages still read.
   They are NOT repointed here — each page joins the ladder in its own
   conversion sitting, with a picture (RC 109). When the last page
   converts, those blocks die and only this one remains. */
:root {
  --L-h1:    24px;  --L-h2:  20px;      /* headings                        */
  --L-body:  16px;                      /* reading text                    */
  --L-row:   14px;  --L-meta: 12.5px;   /* dense rows and their fine print */
  --L-tap:   44px;                      /* nothing a finger hits is smaller */
  --L-tapfs: 22px;                      /* the tap slab's own text          */
  --L-ctl:   32px;                      /* a desk control's height          */
  --L-row-h: 40px;                      /* a dense desktop list row         */
  --L-s1: 4px; --L-s2: 8px; --L-s3: 16px; --L-s4: 24px; --L-s5: 32px;
  --L-measure: 65ch;
}
/* Bootstrap's component variables, RE-TUNED ON THE COMPONENT CLASSES —
   a :root override is inert (Bootstrap declares them on .btn/.card
   themselves; measured, attack round 4 finding 56). app.css loads after
   Bootstrap, so equal specificity wins here. Each size class is named
   deliberately: an override on .btn alone would flatten sm/lg (same
   finding).
   ⚠ SCOPED UNDER body.ladder — the CONVERSION MARKER a surface stamps in
   its own sitting — so today's pages (a few already use .btn) do not move
   a pixel before their picture round. The marker doubles as the honest
   "which pages are converted" signal a browser can see. */
body.ladder .btn { --bs-btn-font-size: var(--L-row); --bs-btn-padding-y: .375rem; }
body.ladder .btn-sm { --bs-btn-font-size: var(--L-meta); --bs-btn-padding-y: .25rem; }
body.ladder .btn-lg { --bs-btn-font-size: var(--L-body); --bs-btn-padding-y: .5rem; }
body.ladder .card { --bs-card-spacer-y: var(--L-s3); --bs-card-spacer-x: var(--L-s3); }
/* ========================================================================= */

* { box-sizing: border-box; }
/* ⭐ ONE ROOT SIZE AT EVERY WIDTH — 17px. Ruled Sep 9 for the desk (RC 629, on
   before/after pictures: "Even the smaller ones are more than big enough.")
   and Sep 16 for the phone too (RC 700, on the one-line rule's pictures:
   "There is no need for the general font to be bigger than that."). The root
   had stayed 20px while the body text was 17px, so every rem-sized box came
   out a fifth larger than the text in it — the audit's root cause for the
   pixel literals scattered through this file; and on the phone a 20px root
   made a one-line tap slab 75px and a two-line one 110px. No root size is a
   design constant. */
html { font-size: 17px; }
body { margin:0; font-family: -apple-system, system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  color:var(--ink); background:var(--bg); line-height:1.4; }
.wrap { max-width: 34rem; margin: 0 auto; padding: 1.25rem; }
h1 { font-size: 1.8rem; margin: .5rem 0 1rem; }
h2 { font-size:1.3rem; margin:1.2rem 0 .6rem; }
a { color: var(--accent); cursor: pointer; }
.big { font-size:1.15rem; }
.huge { font-size:1.6rem; font-weight:800; }
.muted { color:var(--muted); }
.pre { white-space:pre-line; }
[hidden] { display: none !important; }

/* ---------------- forms and buttons ---------------- */
label { display:block; font-weight:600; margin: 1rem 0 .35rem; }
input[type=text], input[type=password] { width:100%; font-size:1.15rem; padding:.8rem; border:2px solid #999; border-radius:.5rem; }
input[type=number], input[type=search], input[type=date], select { font-size:1.15rem; padding:.7rem; border:2px solid #999; border-radius:.5rem; width:100%; }
input:focus { outline: 3px solid var(--accent); border-color: var(--accent); }
/* RC 309, THE INVERSION ("The whole point was to come up with a library of
   things that you always use for everything!"): the site-wide default button
   is the LIBRARY's quiet word-button — the grain's numbers copied from the
   GRAIN PASS 2 block, not invented. The driver's big blue tap-slab still
   exists, but as the NAMED tap world (body.tapui) below, which only the run
   pages stamp on themselves. For a year it was the other way around, and
   every quiet control on an office page had to out-fight the slab — the
   file below carries at least nine patched leaks from that fight. */
button { width:auto; display:inline-block; font-size:var(--t-row, 14px); font-weight:500;
  margin:.5rem 0 0; padding:.35rem .85rem; border:1px solid var(--ctl-edge); border-radius:6px;
  background:var(--ctl-bg); color:var(--ctl-ink); }
body:where(:not(.tapui)) button:hover:not(:disabled) { background:var(--ctl-hover); }
/* the color variants say COLOR only — geometry comes from whichever world
   the page is in, so one definition serves both */
button.secondary { background:#eee; color:var(--ink); }
button.plain { background:#eee; color:var(--ink); }
button.danger { background:#fde8ec; color:var(--err); }
button.add { background:#e7f6ea; color:var(--ok); }
button.okbtn { background:var(--ok); color:#fff; }
button:disabled { opacity:.4; cursor: default; }
button, .btn, .slab { transition: transform .06s ease, filter .12s ease, box-shadow .12s ease; cursor: pointer; }
/* ==== THE TAP WORLD (body.tapui — stamped by the driver's run pages):
   every button a big friendly slab, byte-for-byte the old default, now
   asked for by name. :where() keeps this rule exactly as easy to override
   as the old bare `button` rule, so every existing size-patch still wins. */
body:where(.tapui) button { width:100%; font-size:1.25rem; padding:.9rem; margin:1.25rem 0 0;
  border:0; border-radius:.6rem; background:var(--accent); color:#fff; font-weight:700; }
body:where(.tapui) button:hover:not(:disabled), .btn:hover, .slab:hover { filter: brightness(.94); box-shadow: 0 2px 6px #0002; }
body:where(.tapui) button:active:not(:disabled), .btn:active, .slab:active { transform: translateY(2px) scale(.98); filter: brightness(.88); box-shadow: none; }
button.linkish { background:none; border:0; color:#9fc0ff; text-decoration:underline; font-size:.9rem; width:auto; padding:.2rem; margin:0; }
button.linkish:hover { box-shadow:none; filter:none; color:#fff; }
.slab { display:block; width:100%; font-size:1.25rem; padding:1rem; margin:.6rem 0; border:0; border-radius:.7rem;
  font-weight:800; text-align:left; text-decoration:none; }
.slab small { font-weight:500; font-size:.95rem; }
/* ⭐⭐ RC 699 — THE ONE-LINE RULE, WRITTEN ONCE (Sep 16). Operator: *"I want it
   for everything that doesn't fit. Hovering with a mouse or a long tap with
   a finger get the full text shown. This can be a universal build."*  A thing
   named here never wraps: its words are clipped with an ellipsis, and the
   WHOLE text is one hover or one hold away — kit.js reads THIS rule's selector
   off the stylesheet (the --oneline marker), finds what really overflows and
   gives it a title, and the one bubble shows it on hover (a mouse) or on a
   half-second hold (a finger, RC 628). Until today the same three declarations
   were written in nineteen places, each its own decision; every one of those
   selectors now lives on this list and nowhere else. A NEW one-line thing
   wears the class .oneline or joins this list — never a fresh copy.
   Exceptions (a thing that may wrap after all) are written right after this
   rule, and only there. A slab marked data-big is Today's door: two lines by
   design (parts.door), so it is not on the list. EVERY tap-world button is on
   it, not only the ones called .slab — the end-of-day button ("End the day —
   17 stops go back") is a plain tap button and wrapped to 90px the first time
   the deploy's pixel machine met it. */
.oneline, .slab:not([data-big]), body.tapui button:not([data-big]),
.daybar .facts .stat,
body.phonearr .workspace.hasmap .sheetfacts .stat,
body.phonearr .workspace.hasmap .panel .nextbtn,
body.phonearr .workspace.hasmap .panel li.stop .who b,
body.phonearr .workspace.hasmap .panel li.stop .who small,
.bwho b, .ctable .bwho small, .dwho, .ctable td .chip, .pkrow a,
.caltruck b, .peek .hdate b,
body.phonearr .stops.build li.stop .who b,
body.phonearr .stops.build li.stop .who .tag,
.pmain, .prow .pc, .cuscard .sku .kind {
  --oneline: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
/* ⛔ N2 / RC 570 — `.prow .tag` CAME OFF THIS LIST. A mark clipped to "…" is
   a FACT THAT VANISHES, and the manager cannot tell "no tag" from "no room
   for the tag" (measured Sep 7: Sage Ventures' name cell was 373px wide for
   375px of contents, so its biweekly mark died while All Star ABA's lived by
   two pixels). The marks now sit in their OWN cell (.ptags, max-content,
   nowrap, never shrinking) and the NAME is the thing that clips — the name
   is the part a hover can give back, because the reader already knows who
   he is looking for. */
/* the exception, in one place: the day bar's NEED line is never cut (RC 89
   2.1) — it wraps, and the bar grows a thin row for it */
.daybar .facts .stat.needline { overflow: visible; text-overflow: clip; white-space: normal; }
.slab.ok { background:var(--ok); color:#fff; }
.slab.diff { background:#f4b400; color:var(--ink); }
.slab.no { background:var(--err); color:#fff; }
.slab.plain { background:#eee; color:var(--ink); font-weight:600; font-size:1.05rem; text-align:center; }
.slab.go { background:var(--accent); color:#fff; text-align:center; }
.error { background:#fde8ec; color:var(--err); padding:.8rem; border-radius:.5rem; font-weight:600; }
.okbox { background:#e7f6ea; color:var(--ok); font-weight:800; padding:.7rem; border-radius:.5rem; }
.warn { background:#fde8ec; color:var(--err); font-weight:800; padding:.7rem; border-radius:.5rem; }
.check { font-size:1.3rem; background:#fff7d6; padding:1rem; border-radius:.7rem; border:2px solid #f4b400; }
.field { margin:.8rem 0; }
.field label { display:block; font-weight:700; margin-bottom:.25rem; }
/* RC 115: the bottle type IS the label — tap it and the phone's rolling
   picker swaps the type; the dashed edge says "tappable" */
/* ⭐ RC 686: the BAD BOTTLES picker is THE SAME GESTURE and wears the same
   clothes. Built with its own class and no rule here, it fell back to the
   browser's full-width bordered dropdown and sat beside "Pure" looking like a
   different kind of control — he caught it in the first picture: *"why does
   it look different from the pure?"*  A new control that behaves like an old
   one and does not LOOK like it teaches the driver there are two systems. */
select.skuswap, select.badswap { font:inherit; font-weight:700; max-width:100%; background:transparent;
  border:0; border-bottom:2px dashed #0a58ca88; border-radius:0; padding:0 .1rem .1rem 0; color:inherit; }
button.plusbtn { width:auto; font-size:1.3rem; font-weight:800; padding:.35rem 1rem;
  background:#fff; color:var(--accent); border:2px solid var(--accent); }
/* RC 144: a count row — number beside its type picker */
.countrow { display:flex; gap:.5rem; align-items:center; margin:.4rem 0; }
/* RC 698: the box and its dropdown share ONE text size and ONE height — they
   were 58px beside 56px because each carried its own size (RC 334's fault:
   every piece right, the row disagreeing with itself). His word on the
   pictures: "go ahead". */
.countrow input { width:5.5rem; font-size:1.2rem; height:3rem; padding:0 .5rem; }
.countrow select { flex:1; font-size:1.2rem; height:3rem; padding:0 .5rem; }
/* RC 112/120: Today's three doors — big, labeled, unmissable */
a.slab.doorbtn { display:block; text-align:left; margin:.7rem 0; padding:1rem; font-size:1.25rem; }
/* RC 124/126: the break button rides the corner between leaving and return.
   Anchored inside .screen so the phone outline contains it (RC 102). */
.breakform { position: fixed; right: .7rem; bottom: .7rem; z-index: var(--z-bar); margin: 0; }
.screen .breakform { position: absolute; }
.breakbtn { width: auto; margin: 0; font-size: .95rem; padding: .5rem .8rem; border-radius: 2rem;
  background: #fff7d6; color: #7a5b00; border: 2px solid #f4b400; box-shadow: 0 2px 8px #0003; }
.breakbtn.on { background: #f4b400; color: #fff; }
/* RC 141: the customer's compact station rows */
.station.multi { border:2px solid var(--edge-plain); border-radius:.8rem; padding:.4rem .7rem; margin:.6rem 0; }
.station.multi.dragging { opacity:.5; border-color:var(--accent); }
/* the amounts column was `auto`, so a station that took three kinds of water
   ("6 Spring, 3 Pure, 7 Hard Pure") pushed the station's NAME into a two-line
   sliver (RC 632 picture, Sep 9). The amounts get at most three fifths of the
   the name is guaranteed seven characters' room first; the amounts take
   what is left and wrap on their own lines, right-aligned. A fixed share
   for the amounts was tried and broke the short rows' names instead. */
.station.srow { display:grid; grid-template-columns:1.6rem minmax(0,1fr) auto 1.4rem;
  align-items:center; gap:.4rem; cursor:pointer; margin:.45rem 0; }
.station.srow .qty { max-width:8.5rem; white-space:normal; text-align:right; }
.station.srow .qty .picked { display:block; font-weight:500; }
.station.srow .dot { font-size:1.2rem; text-align:right; }
.stationh { margin:.2rem 0 .4rem; }
.sgrip { cursor:grab; color:#999; touch-action:none; padding:0 .3rem; }
.num { display:flex; gap:.6rem; align-items:center; }
/* RC 698: the number box takes the circles' own 3rem height — it grew to 65px
   from its 1.6rem text beside 60px circles (the pixel machine's UNEVEN, the
   first time it opened the driver's screens). His word: "They both look the
   same, so go ahead." */
.num input[type=number] { width:5.5rem; height:3rem; font-size:1.6rem; font-weight:800; text-align:center; padding:0 .4rem; border:2px solid #999; border-radius:.5rem; }
.num button { width:3rem; height:3rem; margin:0; padding:0; border-radius:50%; border:2px solid var(--accent); background:#fff; color:var(--accent); font-size:1.6rem; font-weight:800; }
label.radio { display:block; font-size:1.15rem; padding:.6rem; border:2px solid var(--edge-plain); border-radius:.5rem; margin:.4rem 0; }
label.radio input { width:1.3rem; height:1.3rem; margin-right:.5rem; }
.toolbar { display:flex; gap:.5rem; flex-wrap:wrap; }
.toolbar form { flex:1; }
.toolbar button { margin:0; font-size:1rem; padding:.7rem; }
.ctl { display:flex; gap:.3rem; }
.ctl form { margin:0; }
/* ⭐ A REMOVE ✕ IS A GLYPH, NOT A BOX. The operator, Sep 8: *"why does the x
   have to have this gray box around it? Just make it an x like the three
   lines."*  The drag handle beside it is a bare mark on no background and the
   driver uses it all day, so the ✕ is the same: no grey slab, no rounded
   corners, just the mark — and it darkens when the pointer is on it, which is
   what says it can be pressed. It keeps a tappable HEIGHT (28px, the handle's
   own) without claiming a square of the row. */
/* ⭐ ONE RULE FOR EVERY REMOVE ✕ (RC 617): the office's is a <button>, the
   driver's is a <details><summary> (RC 267). The daytime version of this rule
   named only the button, so the driver's ✕ — the one they were looking at —
   kept its grey box, and the box overflowed its slim column and squeezed the
   name beside it to "Rocky Poin…" (caught in the camera's picture, Sep 8
   night). */
.ctl button, .ctl.xreason details summary { width:auto; min-height:28px; margin:0;
  padding:0 .35rem; border:0; border-radius:0; font-size:1.2rem; background:none;
  color:var(--ink,#16202b); }
.ctl button:hover, .ctl button:focus-visible,
.ctl.xreason details summary:hover, .ctl.xreason details summary:focus-visible { color:var(--err); }
.ctl button:disabled { opacity:.3; }
.search { display:flex; gap:.5rem; }
.search button { width:auto; margin:0; padding:.7rem 1rem; }
ul.plain { padding-left:1.2rem; }
ul.plain li { margin:.3rem 0; }
details summary { cursor:pointer; }
.card { border:2px solid var(--edge-plain); border-radius:.8rem; padding:.8rem; margin:.7rem 0; }
.row { display:flex; justify-content:space-between; gap:.6rem; flex-wrap:wrap; padding:.3rem 0; }
table.load { width:100%; border-collapse:collapse; font-size:1.2rem; }
table.load td { padding:.45rem .2rem; border-bottom:1px solid #eee; }
table.load td:last-child { text-align:right; font-weight:800; }

/* "Gave them something else?" — one expanding card per type */
button.sku-add { width: auto; display: inline-block; margin: .25rem .3rem .25rem 0; font-size: 1rem; padding: .55rem .8rem; }
button.sku-add.on { background: #dce7ff; box-shadow: inset 0 1px 4px #0003; }
.sku-card { border: 2px solid var(--edge-plain); border-radius: .7rem; margin: .35rem .35rem .35rem 0; display: inline-block; vertical-align: top; }
/* color set explicitly: the site's default button is WHITE text, and on the
   card's white ground it rendered invisible until hover (found live, RC 120) */
.sku-card > button { border: 0; background: none; margin: 0; width: auto; font-size: 1.05rem; font-weight: 700; padding: .55rem .8rem; color: var(--ink); }
.sku-card.on { background: #dce7ff; border-color: var(--accent); display: block; }
.sku-card .num { padding: 0 .7rem .6rem; }

/* frequency chips (Customers page) */
.freqs { display: flex; flex-wrap: wrap; gap: .4rem; }
label.chip { border: 2px solid var(--accent); border-radius: 2rem; padding: .3rem .8rem; font-weight: 700; color: var(--accent); cursor: pointer; }
label.chip.on { background: var(--accent); color: #fff; }
label.chip input { display: none; }

/* click-to-copy — every address, everywhere (RC 40) */
.copyable { cursor: copy; border-bottom: 1px dashed #a9c0e8; }
.copyable:hover { background: #e3f0ff; }
.copyable.copied { background: #e7f6ea; }
.copyable.copied::after { content: " ✓ copied"; color: var(--ok); font-weight: 700; }

/* a whole row/card as the click target (RC 54) */
a.rowlink { display: flex; justify-content: space-between; gap: .6rem; align-items: baseline;
  padding: .45rem .5rem; margin: .15rem -.5rem; border-radius: .5rem; color: inherit; text-decoration: none; }
a.rowlink:hover { background: #e3f0ff; }
a.rowlink .go { color: var(--accent); font-weight: 700; white-space: nowrap; }

/* ---------------- the stop lists ---------------- */
ul.stops { list-style:none; margin:0; padding:0; }
li.stop { border-bottom:1px solid var(--edge-plain); transition: transform .12s ease; }
li.stop > a, ul.build li.stop { display:grid; gap:.5rem; align-items:center; padding:.6rem .3rem; color:inherit; text-decoration:none; }
li.stop > a { grid-template-columns:1.6rem 1fr auto 1.4rem; }
/* the build list: ✕ · grip · number · who · qty */
ul.build li.stop { grid-template-columns: auto 1.6rem 1.6rem 1fr auto; }
ul.build li.stop .who a { color: inherit; text-decoration: none; border-bottom: 2px dotted #0a58ca55; }
ul.build li.stop .qty { max-width: 11rem; white-space: normal; text-align: right; }
/* THE CRUSHED-NAME ROOT CAUSE (RC 41/48): the day list has its own columns —
   grip · number · who (all the room) · qty · dot — never the builder's grid. */
ul.stops.daylist li.stop { grid-template-columns: 1.6rem 1.6rem minmax(0, 1fr) auto 1.4rem; }
/* ⭐ RC 348 — ONE ROW SHAPE PER LIST. The row is a GRID, so a row carrying
   MORE cells than the rule has columns wraps its last cell onto a second
   line: that is why every row on the driver's day was double height with
   the status dot stranded below and the bottles column crushed. The
   office's ✕ had a six-column rule (.exday); the DRIVER's ✕ (RC 267/347)
   was given a cell and never given a column. Now the list declares it
   once — .hasctl — and every row inside it, the warehouse reload
   included, emits the same cells, empty where it has no control. */
ul.stops.daylist.hasctl li.stop { grid-template-columns: 2.9rem 1.6rem 1.6rem minmax(0, 1fr) auto 1.4rem; }
/* RC 142: a future day's rows lead with the remove-x, EXACTLY the
   builder's agreed one (same .ctl style, left, grey) */
ul.stops.daylist.exday li.stop { grid-template-columns: 2.9rem 1.6rem 1.6rem minmax(0, 1fr) auto 1.4rem; }
/* the reload row is a real row of the same list: its empty cells hold the
   shape open so it can never collapse into a tall amber block again */
ul.stops.daylist li.stop.reloadrow .who { font-size: .95rem; }
ul.stops.daylist li.stop .who { min-width: 0; }
ul.stops.daylist li.stop .qty { max-width: none; }
#results li.stop { grid-template-columns: 1.6rem 1fr auto auto; }
li.stop .n { font-weight:800; color:#666; }
li.stop .qty { font-weight:700; white-space:nowrap; text-align:right; }
/* RC 118: a unit row in the builder stacks each station's own qty tap */
li.stop .qty.qstack { display:flex; flex-direction:column; gap:.15rem; align-items:flex-end; }
li.stop .qty.qstack a.qty { display:block; }
/* RC 115: a recorded row shows what actually happened, in the delivered
   green with its aura — replacing the planned text, never beside it */
li.stop .qty.actual { color:var(--ok); font-weight:800; text-shadow:0 0 8px rgba(26,143,58,.35); }
li.stop .dot { font-size:1.2rem; text-align:right; }
li.done { color:#777; }
li.done .who b { text-decoration:line-through; }
li.done .dot { color:var(--ok); }
li.done.nodeliver .dot { color:var(--err); }
li.done.nodeliver .who b { text-decoration:none; }
li.now { background:#fff7d6; border-left:5px solid #f4b400; }
li.now .dot { color:#c77d00; }
li.just { outline:3px solid var(--ok); }
li.stop.hoverpin { background: #e3f0ff; }
.tag { display:inline-block; font-size:.8rem; padding:.05rem .45rem; border-radius:.4rem; margin:.15rem .3rem 0 0; font-weight:700; }
.tag.clock { background:#e3f0ff; color:var(--accent); }
.tag.good { background:#e7f6ea; color:var(--ok); }
.tag.bad { background:#fde8ec; color:var(--err); }
/* RC 178b: a corrected or hand-entered clock time. AMBER — a person
   still has to look at it; it is not a failure (never red). */
.tag.warn { background: var(--amber-soft); color: var(--amber-ink); }
/* N2 (RC 658 item 4: "each type distinct"): the QUIET mark — a fact about
   the row that asks nothing of the reader ("by hand", "restored", "note").
   Grey ink on the site's own control face, so it reads as a label and never
   competes with the blue and amber marks that mean work. */
.tag.off { background: var(--ctl-bg); color: var(--muted); }
a.qty { color: inherit; border-bottom: 2px dotted #0a58ca55; }

/* ⭐⭐ RC 711-718 — THE VISIT: what this stop gets today, what it always gets,
   and what the driver is told. Three taps on one row, all opening one popup.
   ⚠️ THE NAME CELL MUST BE ALLOWED TO SHRINK or a long instruction pushes the
   number column off the row — the same fault RC 41/48 fixed on the day list,
   arriving here the moment this row grew a one-line thing of its own. */
ul.build li.stop .who { min-width: 0; }
/* the two lines are LINKS, each on its own line (so no <br> between them),
   wearing the dotted underline the row's number and name already wear */
ul.build li.stop a.instrline, ul.build li.stop a.visitline {
  display: block; max-width: 100%; margin-top: .25rem; color: var(--dim);
  text-decoration: none; border-bottom: 2px dotted #0a58ca55; }
ul.build li.stop a.visitline { color: var(--amber-ink); }
ul.build li.stop a.instrline b, ul.build li.stop a.visitline b { font-weight: 700; }
/* who asked for this visit's numbers, small and amber under the number —
   amber because a person still has to look at it; it is not a failure */
li.stop .qty .asked { display: block; font-size: .8rem; font-weight: 600;
  color: var(--amber-ink); }
/* the popup itself: two columns with a hairline between them */
dialog#visitpop .two { display: grid; grid-template-columns: 1fr 1fr; gap: 1.2rem; }
dialog#visitpop .two > div:last-child { border-left: 1px solid var(--edge-plain); padding-left: 1.2rem; }
dialog#visitpop h3 { font-size: 1rem; margin: .2rem 0 .3rem; }
dialog#visitpop .countrow input { width: 5.2rem; }
dialog#visitpop .field input, dialog#visitpop .field textarea { width: 100%; }
dialog#visitpop .plusbtn { margin: .2rem 0 .6rem; }
dialog#visitpop .acts { display: flex; gap: .6rem; align-items: center; margin-top: .6rem; }
dialog#visitpop .acts .muted { font-size: .85rem; }
/* ⭐ AND THE DRIVER'S OWN STYLES, written here because app.css has ONE owner
   (the templates that wear them are the stop and station screens). A number a
   PERSON asked for is boxed, warm and labelled, so he can tell it apart from
   the number the plan worked out — RC 715/716. */
.huge.asked { position: relative; display: inline-block; margin-top: .25rem;
  padding: .2rem .75rem .15rem; border: 2px solid var(--amber-ink);
  border-radius: .6rem; line-height: 1.2;
  background: linear-gradient(180deg, var(--amber-soft) 0%, #fff 70%);
  box-shadow: 0 0 0 3px var(--amber-soft), 0 4px 14px rgba(0,0,0,.18); }
.huge.asked::after { content: "requested"; position: absolute; top: -.7rem;
  right: .5rem; font-size: .7rem; font-weight: 700; letter-spacing: .03em;
  text-transform: uppercase; color: #fff; background: var(--amber-ink);
  padding: .05rem .45rem; border-radius: .5rem; }
/* the just-today card on his stop screen wears the amber edge */
.card.visit { border-color: var(--amber-ink); background: var(--amber-soft); }
.card .by { color: var(--dim); font-size: .9rem; margin-top: .3rem; }
/* and the multi-station row's small "requested" tag (.tag.warn is amber) */
.station.srow .qty .tag.warn { margin-left: .3rem; }
/* drag: a ghost follows the pointer */
.grip { cursor: grab; font-size: 1.3rem; color: #999; padding: .2rem .4rem; touch-action: none; user-select: none; }
.grip:active, body.grabbing, body.grabbing * { cursor: grabbing !important; }
li.stop.dragging { opacity: .35; background: #e3f0ff; }
li.stop.ghost { position: fixed; z-index: var(--z-bar); pointer-events: none; background: #fff;
  box-shadow: 0 8px 24px #0005; border-radius: .6rem; opacity: .95; list-style: none;
  display: grid; grid-template-columns: auto 1.6rem 1.6rem 1fr auto; gap: .5rem; align-items: center; padding: .6rem .3rem; }
/* done stops fold away into one row (RC 56) — DOM order intact for drag */
ul.stops.folded li.done { display: none; }
button.donefold { width: auto; display: inline-block; font-size: .95rem; padding: .45rem .8rem; margin: 0 0 .4rem; }

/* live search: the dropdown floats OVER the page (never stretches it) */
.searchwrap { position: relative; }
.searchwrap #results, #results.results { position: absolute; left: 0; right: 0; top: 100%;
  background: #fff; z-index: var(--z-menu); max-height: 20rem; overflow-y: auto; box-shadow: 0 10px 30px #0004;
  border: 2px solid var(--accent); border-radius: .6rem; margin-top: .3rem; }
/* wherever the search sits at a COLUMN'S BOTTOM, results open UPWARD over
   the list, never off-screen. Found live 2026-08-20 on the build page ("I
   can't add anything") and fixed only THERE — the popup repeated it on
   2026-08-27 ("the dropdown goes down off the page"), the price of a
   non-structural fix. One rule for both worlds now. */
.workspace .searchwrap #results,
.kitlayer .searchwrap #results { top: auto; bottom: 100%; margin: 0 0 .3rem; }
/* an empty day says so — a blank white box reads as broken (RC 61/77) */
.emptyday { color: #666; padding: 1.2rem .8rem; font-size: 1.02rem; }

/* the whole day-list row is the tap target (RC 90) */
ul.stops.daylist li.stop { cursor: pointer; }

/* THE TAP QUEUE (RC 96): honest words when taps are waiting for signal,
   and the in-page Right?-check that works offline */
#tapqueue { position: fixed; left: .6rem; bottom: .6rem; z-index: var(--z-queue);
  background: #f4b400; color: var(--ink); font-weight: 800; font-size: .85rem;
  padding: .4rem .8rem; border-radius: 2rem; box-shadow: 0 4px 16px #0005; }
#qcheck { position: fixed; top: 0; right: 0; bottom: 0; left: 0;
  background: #0007; z-index: var(--z-modal); display: flex; align-items: center;
  justify-content: center; padding: 1rem; }
#qcheck .check { max-width: 26rem; width: 100%; background: #fff7d6; }
#qcheck button { width: 100%; margin-top: .8rem; }
#qcheck button.plain { background: #eee; color: var(--ink); }
.tag.qwait { background: #fff7d6; color: #8a6d00; }

/* the Need line's mini popup — full per-SKU data, tap-away closes (RC 92) */
.needpop { position: absolute; bottom: 100%; left: 0; right: 0; margin: 0 0 .3rem;
  background: #fff; border: 2px solid var(--accent); border-radius: .6rem;
  box-shadow: 0 10px 30px #0004; padding: .5rem .7rem; z-index: var(--z-menu-hi); }
.needpop table.load { font-size: .95rem; }
.needpop table.load td { padding: .25rem .2rem; }

/* the background-job overlay — a corner card; the page stays usable */
.overlay { position: fixed; right: 1rem; bottom: 1rem; z-index: var(--z-overlay); }
.overlay-box { background: #fff; border-radius: 1rem; padding: 1rem 1.3rem; text-align: center; box-shadow: 0 8px 30px #0006; border: 2px solid var(--accent); }
progress#orderbar { width: 16rem; height: 1.1rem; accent-color: var(--accent); }

/* the customers table — full width, one row per customer (RC 41/48) */
table.customers { width: 100%; border-collapse: collapse; font-size: .98rem; }
table.customers th { text-align: left; font-size: .85rem; color: #666; padding: .35rem .5rem; border-bottom: 2px solid var(--edge-plain); }
table.customers td { padding: .45rem .5rem; border-bottom: 1px solid #eee; vertical-align: top; }
table.customers td.num { color: #999; font-weight: 700; }
table.customers td.qty { font-weight: 700; white-space: nowrap; }
/* narrow (or phone view — same gate, so the outline is honest, RC 89 1.4):
   the table stacks into cards */
body.phonearr table.customers, body.phonearr table.customers tbody,
body.phonearr table.customers tr, body.phonearr table.customers td { display: block; }
body.phonearr table.customers thead { display: none; }
body.phonearr table.customers tr { border-bottom: 2px solid var(--edge-plain); padding: .5rem 0; }
body.phonearr table.customers td { border: 0; padding: .1rem .2rem; }
body.phonearr table.customers td.num { display: none; }

/* ---------------- the app frame ---------------- */
.practice { background:var(--err); color:#fff; text-align:center; font-weight:800; padding:.4rem; letter-spacing:.03em; }
/* the impersonation banner */
.impbar { background: #5b2d90; color: #fff; display: flex; gap: .8rem; align-items: center; padding: .45rem 1rem; font-weight: 700; flex-wrap: wrap; }
.impbar form { margin: 0; }
.impbar a { color: #fff; }
.impbar .linkish { color: #e8d9ff; }
/* in-page navigation looks like controls, not text links (RC 64) */
.crumbs { margin: .2rem 0; }
.crumbs .slab.plain { display: inline-block; width: auto; max-width: 100%; vertical-align: top; font-size: .92rem; padding: .4rem .8rem; margin: 0 .35rem 0 0; text-decoration: none; }
/* max-width: an inline-block with width:auto sizes to its words, so the one-line
   rule above had nothing to clip against — "← Sheppard Pratt (Main Entrance -
   Admissions)" ran 16px past a 375px phone on the why / form / cooler pages
   (the pixel machine, Oct 5: SIDEWAYS on all three, the day that stop came
   first). Capped at the row, the rule clips it and the hold shows the name. */

/* ---------------- the day bar — the workspace's one slim strip (RC 69) ------
   Everything that used to be stacked cards and headings compresses into two
   thin rows; the work surface below gets the screen. */
.daybar { margin: 0 0 .45rem; }
.daybar .dbrow { display: flex; align-items: center; gap: .55rem; flex-wrap: wrap; }
/* the DATE is never cut (RC 89 2.1: "not acceptable") — its preferred size
   is its full text (max-content); a row that cannot hold it WRAPS, giving
   the date a thin row of its own, and only then does the text itself wrap */
.daybar h1 { font-size: 1.12rem; margin: 0; min-width: 0; white-space: normal;
  flex: 0 1 auto; flex-basis: max-content; }
.daybar h1 .muted { font-weight: 500; font-size: .92rem; }
.daybar .slab, .daybar button { width: auto; display: inline-block; margin: 0; font-size: .88rem; padding: .34rem .7rem; }
.daybar form { margin: 0; display: inline-block; }
.daybar .facts { font-size: .88rem; color: #333; margin-top: .2rem; display: flex; gap: 1.1rem; flex-wrap: wrap; align-items: baseline; }
.daybar .facts .okbox, .daybar .facts .warn { padding: .15rem .5rem; font-size: .85rem; font-weight: 700; }
.daybar .spacer { flex: 1; }
.daybar .stat { white-space: nowrap; }
.daybar .stat b { font-size: 1rem; }

/* the panel (stop list side) is a flex column wherever it lives */
.panel { display: flex; flex-direction: column; min-height: 0; }
.panel .panelhint { margin: 0 0 .3rem; font-size: .82rem; }
.panel .panel-list { min-height: 0; }
.panel .panelfoot-h { margin: .5rem 0 .2rem; font-size: 1rem; }
.panel .nextbtn { margin: 0 0 .4rem; text-align: center; font-size: 1.05rem; padding: .6rem .8rem; display: block; width: 100%; }
.panel button.sheet-handle { display: none; }   /* shown only by the canvas states below */
.sheetfacts { display: none; }                  /* the phone sheet's need/count line (RC 82) */
#routemap { width: 100%; height: 420px; border-radius: .8rem; box-shadow: 0 2px 10px #0002;
  position: relative; z-index: 0; }
.split-right iframe { width: 100%; height: 40vh; border: 0; border-radius: .8rem; box-shadow: 0 2px 10px #0002; margin-top: .8rem; }
.pin span { display: flex; align-items: center; justify-content: center; width: 24px; height: 24px;
  background: var(--accent); color: #fff; font-weight: 800; font-size: .8rem; border-radius: 50%; border: 2px solid #fff; box-shadow: 0 1px 4px #0006; }

/* ================= THE DESKTOP LAYER =================
   RC 52/55/57/63/69: the computer is the primary design. Dense controls,
   multi-pane, the whole viewport in use, zero page scroll on workspace
   pages. Gated on width AND not the simulated phone. */
/* ⭐ THE DESK LAYER STARTS AT 600px SINCE Sep 9 (RC 624/625): everything from
   a tablet up is the computer's page; only the MENU folds below 1100px
   (body.narrowmenu, the shell rules) and only a PHONE (below 600px,
   body.phonearr) gets the phone arrangement. This block was gated at 1000px,
   which is why a tablet used to get the phone's one-column pages. */
@media (min-width: 600px) {
  body:not(.phone) { font-size: 17px; }
  body:not(.phone) .wrap { max-width: 96rem; padding: 1.1rem 2rem; }
  /* RC 309: only the tap world needs shrinking on a computer — the library
     default is already computer-sized. The .btn half stays site-wide. */
  body:where(.tapui):not(.phone) button, body:not(.phone) .slab {
    width: auto; display: inline-block; font-size: 1.02rem; padding: .55rem 1.1rem; }
  body:not(.phone) .slab { margin: .3rem .4rem .3rem 0; }
  /* home: the two weeks as a grid of day cards */
  body:not(.phone) .daysgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(19rem, 1fr)); gap: .8rem; }
  body:not(.phone) .daysgrid .card { margin: 0; }
  /* reading/recording pages stay a comfortable column, centered — width is
     never filled for its own sake (RC 62) */
  body:not(.phone) main.wrap.focus { max-width: 46rem; }
  /* the sheet handle and the folded hamburger belong to their own states —
     these beat the generic desktop button rule above on specificity (the
     hamburger leak was RC 76's screenshot 1) */
  body:not(.phone) button.sheet-handle { display: none; }
  /* the hamburger belongs to the folded MENU (narrowmenu), which a tablet has
     even though it gets this desk layer — so the switch, not the width, hides it */
  body:not(.narrowmenu) .shellnav .shelltoggle { display: none; }
  /* the day bar stays thin on a computer: long secondary text ellipsizes
     (full text in its title / on The load page) — but the DATE and the
     NEED line are never cut (RC 89 2.1): the row wraps a thin row taller
     for them instead. The work surface still owns the height (RC 69). */
  body:not(.phone) .daybar .dbrow { flex-wrap: wrap; }
  /* the .stat clip and the NEED line's exception live on THE ONE-LINE RULE (RC 699) */
  body:not(.phone) .daybar .facts .okbox, body:not(.phone) .daybar .facts .warn { white-space: nowrap; }
  /* daybar controls stay compact and one-line — this outranks the generic
     desktop button sizing; the TITLE is what shrinks, never the buttons */
  body:not(.phone) .daybar .slab, body:not(.phone) .daybar button {
    font-size: .88rem; padding: .32rem .68rem; margin: 0; white-space: nowrap; flex: none; }
  body:not(.phone) .daybar form { flex: none; }
  body:not(.phone) .daybar .stat { flex-shrink: 1; }
  /* denser stop rows on a computer — more of the day visible at once (RC 52) */
  body:not(.phone) ul.stops li.stop > a, body:not(.phone) ul.build li.stop { padding: .38rem .3rem; }
  body:not(.phone) li.stop .who { line-height: 1.22; }
  body:not(.phone) li.stop .who small { font-size: .82rem; }
  body:not(.phone) li.stop .tag { font-size: .74rem; }
  body:not(.phone) .panel .nextbtn { padding: .45rem .8rem; font-size: 1rem; }
  body:not(.phone) .panel .panelfoot-h { margin: .4rem 0 .15rem; font-size: .92rem; }
  body:not(.phone) .panel .search input { font-size: .95rem; padding: .45rem .6rem; }

  /* THE WORKSPACE — the page is the two panes (RC 69). The whole viewport
     below the top bar belongs to the list and the map; the daybar is the
     only thing above them and it is thin. */
  /* gated on body.dayscreen — stamped by the day/build templates, never
     divined with :has() (RC 89 1.1). .screen is the base.html wrapper. */
  body:not(.phone).dayscreen { height: 100vh; overflow: hidden; display: flex; flex-direction: column; }
  body:not(.phone).dayscreen .screen { flex: 1; min-height: 0; }
  /* the one shell sits between .screen and the page now — the height has to
     pass THROUGH it or the day page loses its scrolling middle (RC 174) */
  body:not(.phone).dayscreen .shellmain { flex: 1; min-height: 0; display: flex; flex-direction: column; }
  body:not(.phone).dayscreen main.wrap { flex: 1; min-height: 0; display: flex; flex-direction: column;
    overflow: hidden; max-width: none; width: 100%; padding: .7rem 1.2rem .9rem; }
  body:not(.phone) .daypage { flex: 1; min-height: 0; display: flex; flex-direction: column; }
  /* RC 287: each calendar week's bottom bar — the Hino's week-scoped work */
  /* ⭐ RC 746 (Sep 22): ONE bar per week, the full width. The Contact chip
     that sat under Sunday (RC 406b's placement), its two-column rule under
     1100px and the spacer that filled its row are gone with it — the count
     still to call rides on the one bar. */
  .weekbar { grid-column: 1 / -1; position: relative; margin: .1rem 0 .5rem; }
  /* ⛔ THIS RULE ASSUMED A FULL-WIDTH BAR AND THEN THE CONTACT BAR REUSED IT IN
     ONE COLUMN. Spreading three pieces apart with a 12px gap each needs 142px;
     Sunday's column gives 133. So the middle piece wrapped and the box went to
     72px beside a 46px bar — the words fit (118px), the SPREADING did not.
     ⭐ THE FIX IS HERE, NOT IN AN OVERRIDE ON THE NARROW ONE. A rule written
     for the case in front of you and patched wherever it fails is this
     project's oldest disease; the same shape as a column pinned at 236px.
     A bar is ONE LINE at any width — the gap gives way before the line does. */
  .weekbar > summary { list-style: none; cursor: pointer; display: flex;
    justify-content: space-between; align-items: center; gap: 0 .6rem;
    white-space: nowrap; min-width: 0;
    background: #eef3fb; border: 1.5px solid #b9c8e4; border-radius: .55rem;
    padding: .45rem .8rem; font-size: .92rem; }
  /* ⭐ N2 — THE FINISHED WEEK'S BAR. A week that is over has no work left in
     it, so its bar must not look like the live ones: no blue, no border that
     asks to be clicked. It still opens, because what it opens is the GHOST —
     who left this week and what became of them (RC 658 item 1, "nothing is
     lost by the row moving"). */
  .weekbar.dimbar > summary { background: var(--ctl-bg); border-color: var(--edge-soft);
    color: var(--muted); font-weight: 500; }
  .weekbar > summary::-webkit-details-marker { display: none; }
  .weekbar[open] > summary { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
  .weekbar .weekpop { position: absolute; left: 0; right: 0; top: 100%;
    z-index: var(--z-pane); background: #fff; border: 1.5px solid #b9c8e4;
    border-top: none; border-radius: 0 0 .55rem .55rem;
    box-shadow: 0 8px 24px rgba(20, 40, 80, .18);
    padding: .5rem .8rem .7rem; max-height: 20rem; overflow-y: auto; }

  /* A SHORT window (the 420px laptop strip) cannot afford the locked
     viewport: the flex children shrink instead of overflowing, so "Add a
     stop" and the removed block end up buried under a body that cannot
     scroll (pixel machine, first deploy run). Below a sane height the page
     reverts to an ordinary scrolling page; the map keeps a floor. */
  @media (max-height: 559px) {
    body:not(.phone).dayscreen { height: auto; overflow: auto; display: block; }
    body:not(.phone).dayscreen .shellmain { display: block; }
    body:not(.phone).dayscreen main.wrap { overflow: visible; display: block; }
    body:not(.phone).dayscreen .workspace #routemap { min-height: 16rem; }
  }
  body:not(.phone) .daypage .workspace { flex: 1; min-height: 0; display: grid;
    grid-template-columns: minmax(26rem, 38rem) minmax(0, 1fr); gap: 1rem; align-items: stretch; }
  body:not(.phone) .workspace > * { height: 100%; min-height: 0; }
  body:not(.phone) .workspace .panel-list { flex: 1; overflow-y: auto;
    border: 2px solid var(--edge-plain); border-radius: .7rem; padding: 0 .4rem; background: #fff; }
  body:not(.phone) .workspace .split-right { position: static; display: flex; flex-direction: column; }
  body:not(.phone) .workspace #routemap { height: 100%; flex: 1; }
  body:not(.phone) .workspace .split-right p { margin: .3rem 0 0; font-size: .82rem; }
  body:not(.phone) .workspace .split-right iframe { height: 34vh; flex: none; }
}
/* a generic side-by-side split for wide non-workspace pages */
@media (min-width: 1000px) {
  body:not(.phone) .split:not(.workspace) { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); gap: 1.2rem; align-items: start; }
}

/* THE MIDDLE (RC 76, narrowed Sep 9): a narrowed desktop window is still a
   desktop — since RC 624/625 the whole desk layer above starts at 600px, so
   all that is left of this band is tighter page padding. The hamburger that
   absorbs the menu is body.narrowmenu's, below 1100px. */
@media (min-width: 600px) and (max-width: 999px) {
  body:not(.phone) .wrap { max-width: none; padding: 1rem 1.2rem; }
}

/* ================= THE PHONE ARRANGEMENT (RC 70) =================
   Not the desktop poured into one column: the map is the canvas, the stop
   list is a SHEET that slides up over it (peek -> open), the daybar stays
   compact at the top.
   ⭐ ONE BLOCK, ONE GATE (RC 89 1.4: the outline and a real phone must
   match — the old pair of hand-synced copies is exactly how they
   diverged). body.phonearr is stamped by the two-line script in base.html:
   a narrow screen (the same 999px line the desktop layer uses) OR the
   admin's phone view. body.dayscreen / body.mapday come from the day and
   build templates server-side, so nothing here leans on :has() — the
   driver's iPhone may predate it (the RC 89 1.1 root-cause suspect). */
/* 100vh first: old iOS drops the dvh line and still gets a full canvas */
body.phonearr.mapday { height: 100vh; height: 100dvh; overflow: hidden; display: flex; flex-direction: column; }
body.phonearr.mapday .screen { flex: 1; min-height: 0; display: block; }
body.phonearr.mapday .shellmain { height: 100%; min-height: 0; display: flex;
  flex-direction: column; padding-top: var(--tap); box-sizing: border-box; }
body.phonearr.mapday main.wrap { flex: 1; min-height: 0; display: flex; flex-direction: column;
  overflow: hidden; padding: .6rem .7rem; max-width: none; }
body.phonearr.mapday .daypage { flex: 1; min-height: 0; display: flex; flex-direction: column; max-width: 100%; }
body.phonearr .workspace.hasmap { flex: 1; min-height: 0; position: relative; display: block; }
/* explicit sides, not `inset` — old iOS drops `inset` silently */
body.phonearr .workspace.hasmap .split-right { position: absolute; top: 0; right: 0; bottom: 0; left: 0; }
body.phonearr .workspace.hasmap #routemap { height: 100%; border-radius: .8rem; }
body.phonearr .workspace.hasmap .split-right p, body.phonearr .workspace.hasmap .split-right iframe { display: none; }
/* ⭐⭐ NOTHING IS EVER WIDER THAN THE PHONE — RC 513 (Sep 4).
   Every phone rule for the day page is written against .workspace.HASMAP, and
   day.html only adds that class when `geo` is present — which arrives in a
   SECOND request. So on first paint a phone got the DESKTOP split: a 400px
   driver panel across a 375px screen, and the whole page scrolled sideways
   with the off-canvas menu dragged into view behind it.
   ⚠️ IT HAD PASSED THE PIXEL GATE FOR WEEKS because that panel only renders
   while a run is LIVE, and no run was live in the data the gate was reading.
   It failed the first time the database was pulled fresh on a day the truck
   was actually out. A gate is only as honest as the data behind it.
   This guard does not depend on the map arriving, which is the whole point. */
/* ⛔⛔ AND ON Sep 6 IT WAS SCOPED TO .mapday — THE ONE PAGE IT WAS FOUND ON.
   A brand-new page (the suggestion box) scrolled sideways the same way, 453px
   in a 375px window, the identical number. So did /account, which had shipped
   weeks earlier: the pixel gate had simply never measured a page of that
   shape. THE FAULT WAS NEVER THE DAY PAGE'S — it is the shell's, and fixing
   the instance in front of me left every other page carrying it.
   This is the general form. `main.wrap` is the shell's own column; it has no
   business being wider than the phone on any page at all. */
body.phonearr main.wrap { min-width: 0; max-width: 100%; }
/* ⭐ RC 518: the line that replaced the Confirm button. It must be READ, not
   noticed — quiet when all is well, and unmistakable when the phone is
   holding the tap because there is no signal. */
/* ⛔ A FILE PICKER IS AS WIDE AS THE BROWSER FEELS LIKE. On the suggestion
   box (RC 187) the "Choose File" control came out 367px starting 43px in, so
   the page scrolled sideways on a 375px phone. It has no width of its own
   worth respecting — cap it and let the long filename wrap.
   ⚠️ MEASURED, AFTER A WRONG GUESS: the page was 453px and I took that for
   RC 513's shell fault because the NUMBER matched. It was not. The number was
   a coincidence; the offender was this one input. Name the element before
   naming the cause. */
input[type=file] { max-width: 100%; box-sizing: border-box; }

.savedline { margin: .5rem 0 .2rem; }
.savedtag { font-size: .95rem; font-weight: 700; color: #1a7f37; }
.savedtag.waiting { color: #9a3412; }
/* the day bar's who-is-clocked-in line is one long sentence with a name in
   it — on a phone it ran 424px wide and pushed the page sideways on its own.
   It is prose, so it wraps. */
body.phonearr .daybar .stat, body.phonearr .daybar .muted.stat {
  white-space: normal; overflow-wrap: anywhere; max-width: 100%; }
body.phonearr .daypage,
body.phonearr .workspace,
body.phonearr .workspace > *,
body.phonearr .workspace .panel,
body.phonearr .workspace .panel > * { max-width: 100%; min-width: 0; }
body.phonearr .workspace:not(.hasmap) { display: block; }

body.phonearr .workspace.hasmap .panel { position: absolute; left: 0; right: 0; bottom: 0; height: 100%;
  transform: translateY(calc(100% - 14.6rem)); transition: transform .18s ease; z-index: var(--z-sheet);
  background: #fff; border-radius: 1.1rem 1.1rem 0 0; box-shadow: 0 -6px 24px #0005; padding: .25rem .6rem .6rem; }
body.phonearr .workspace.hasmap .panel.open { transform: translateY(0); }
/* RC 349 ("he can't easily move the tray up and down"): the handle MUST
   claim the gesture, exactly as .grip and .sgrip do. Without touch-action
   the phone reads the vertical pull as a page scroll, takes the pointer
   away mid-drag, and the tray stops following his finger. */
body.phonearr .workspace.hasmap .sheet-handle { display: block; width: 100%; background: none; color: #333;
  margin: 0; padding: .5rem 0 .45rem; font-size: .95rem; font-weight: 700;
  touch-action: none; user-select: none; -webkit-user-select: none; }
body.phonearr .workspace.hasmap .sheet-handle::before { content: ""; display: block; width: 3.6rem; height: .35rem;
  border-radius: .3rem; background: #aab; margin: .15rem auto .35rem; }
body.phonearr .workspace.hasmap .panel-list { flex: 1; overflow-y: auto; }
body.phonearr .workspace.hasmap .panelhint { display: none; }
/* the phone day chrome (RC 82): one compact top row; word-buttons become
   icons; the facts line lives in the sheet; Next is one line; rows compact
   so the peek shows real stops and the map keeps the screen.
   ⭐ THE DATE IS NEVER CUT (RC 89 2.1): the row wraps and the date takes a
   thin row of its own instead of ellipsizing; the base .daybar h1 rule
   (flex-basis: max-content) does the wrapping. */
body.phonearr.mapday .daybar .dbrow { flex-wrap: wrap; max-width: 100%; }
body.phonearr.mapday .daybar h1 { flex: 1 1 auto; flex-basis: max-content; min-width: 0; font-size: 1rem; }
body.phonearr.mapday .daybar .slab, body.phonearr.mapday .daybar button {
  white-space: nowrap; flex: none; font-size: .8rem; padding: .3rem .55rem; margin: 0; width: auto; display: inline-block; }
body.phonearr.mapday .daybar .facts.in-daybar { display: none; }
body.phonearr.mapday .daybar .bosslink,
body.phonearr.mapday .daybar .btnword { display: none; }
body.phonearr.mapday .daybar .spacer { display: none; }
body.phonearr.mapday .daybar .truckword { display: none; }
/* the sheet's need/count line — the NEED text is never cut either
   (RC 89 2.1): it wraps to more lines instead */
body.phonearr .workspace.hasmap .sheetfacts { display: flex; flex-wrap: wrap; gap: .2rem .6rem;
  align-items: baseline; font-size: .82rem; padding: 0 .2rem .25rem; position: relative; }
body.phonearr .workspace.hasmap .sheetfacts .stat { flex: 1 1 auto; }
body.phonearr .workspace.hasmap .sheetfacts a { white-space: nowrap; }
body.phonearr .workspace.hasmap .panel .nextbtn {
  font-size: 1rem; padding: .5rem .7rem; margin: 0 0 .3rem; width: 100%; display: block; }
body.phonearr .workspace.hasmap .panel { font-size: .92rem; }
body.phonearr .workspace.hasmap .panel ul.stops li.stop { padding: .35rem .2rem; }
body.phonearr .workspace.hasmap .panel .who small { font-size: .78rem; }
body.phonearr .workspace.hasmap .panel .tag { font-size: .7rem; }
/* one clipped line each for name and address — never a word-per-line
   wrap; no dotted decoration inside the sheet (RC 83, incl. search rows) */
body.phonearr .workspace.hasmap .panel ul.stops li.stop { grid-template-columns: 1.3rem 1.3rem minmax(0,1fr) auto 1.2rem; padding: .3rem .1rem; gap: .4rem; }
body.phonearr .workspace.hasmap .panel ul.stops.hasctl li.stop { grid-template-columns: 1.1rem 1.3rem 1.3rem minmax(0,1fr) auto 1.2rem; }
/* RC 142/145: on the phone the future-day remove-x is a bare quiet glyph in
   its own slim column — never a boxed button crowding the row */
body.phonearr .workspace.hasmap .panel ul.stops.exday li.stop { grid-template-columns: 1.1rem 1.3rem 1.3rem minmax(0,1fr) auto 1.2rem; }
body.phonearr .workspace.hasmap .panel ul.stops.exday .ctl button,
body.phonearr .workspace.hasmap .panel ul.stops.hasctl .ctl.xreason details summary { background: none; width: 1.1rem; height: auto; padding: 0; font-size: .95rem; color: #888; border-radius: 0; }
body.phonearr .workspace.hasmap .panel #results li.stop { grid-template-columns: 1.3rem minmax(0,1fr) auto auto; }
body.phonearr .workspace.hasmap .panel li.stop .who { min-width: 0; }
body.phonearr .workspace.hasmap .panel li.stop .who b, body.phonearr .workspace.hasmap .panel li.stop .who small {
  display: block; }
body.phonearr .workspace.hasmap .panel li.stop .who br { display: none; }
body.phonearr .workspace.hasmap .panel li.stop .who a, body.phonearr .workspace.hasmap .panel li.stop .copyable { border-bottom: 0; }
body.phonearr .workspace.hasmap .panel li.stop .tag { white-space: nowrap; }
/* a two-item quantity WRAPS inside a capped width — it must never crush
   the name's column to nothing (RC 101; the RC 41/48 family) */
body.phonearr .workspace.hasmap .panel li.stop .qty { white-space: normal; max-width: 6.5rem; text-align: right; }
body.phonearr .workspace.hasmap .sheetfacts a { background: #eee; color: var(--ink); padding: .12rem .5rem;
  border-radius: .4rem; text-decoration: none; font-weight: 600; }
/* ⛔ THE SIMULATED PHONE IS GONE (RC 535). An outline of a real iPhone
   (390x844, scaled to fit) that the admin could switch into, plus the
   .overframe strip that held the walked-into banner OUTSIDE it — all of it
   removed with the feature, along with its scaler in app.js.
   ⭐ NOTHING REPLACED IT AND NOTHING NEEDED TO: the phone arrangement is
   decided by the window's own width (999px, base.html), so narrowing the
   browser shows what the driver sees, on a real browser rather than a
   drawing of one. */

/* the walked-into banner's controls are real links (RC 87) */
.impbar .impbtn { background: #ffffff22; color: #fff; padding: .25rem .6rem;
  border-radius: .45rem; text-decoration: none; font-weight: 700; }

/* ===================================================== the manager shell ==
   Billing first (RC 153/154: the ratified drawing). The shell = dark side
   menu + slim title bar + panes that fill the screen with no page scroll.
   Pages adopt it one by one, each shown as a picture first (RC 154d). */
/* ⭐⭐ THE SIDEBAR HOLDS ITSELF UP — it is not a page's job to remember.
   Operator, Sep 3: *"you have the sidebar scroll with the customers. That
   shouldn't be possible. The fact that it is means that the sidebar isn't
   made properly."*  They were right about the CAUSE, not just the symptom:
   the menu stayed put only because each page opted into .panescreen, so one
   template forgetting a class scrolled the whole shell away. Now the nav
   pins itself whatever the page does; .panescreen still decides whether the
   WORK AREA scrolls as one piece or pane by pane.
   ⚠️ Not on the phone, where the nav is a drawer and must move with it. */
body:not(.phone) .shellnav { position: sticky; top: 0; align-self: flex-start;
  height: 100vh; height: 100dvh; }

/* ⭐ .panescreen — A FULL-HEIGHT PAGE MADE OF PANES: the page itself never
   scrolls, each pane scrolls inside itself. Billing invented this and called
   it "billingscreen"; Customers is the same KIND of page, and a second name
   for one idea is how the bar, the menu and the row all went wrong. ONE name,
   and any page that wants this shape asks for it. */
body.panescreen { height: 100vh; overflow: hidden; }
/* ⛔ THE HEIGHT CHAIN, AND WHY IT BROKE ONCE ALREADY (RC 174 -> RC 179).
   For anything inside to SCROLL, every link from <body> down to the scrolling
   box needs a DEFINITE height. .screen used to be a flex COLUMN with
   height:100% and the chain ran straight through it. The one shell made
   .screen a flex ROW, and a `min-height: 100vh` is NOT a definite height —
   children stretch to the CONTENT instead, so the deliveries list simply grew
   past the window and could not scroll. It must be `height`, not min-height. */
body.panescreen .screen { height: 100%; min-height: 0; }
body.panescreen .shellmain { min-height: 0; }
body.panescreen main.wrap { flex: 1; min-height: 0; max-width: none; width: 100%;
  margin: 0; padding: 0;
  /* ⛔ AND IT MUST BE A FLEX COLUMN. It was a plain block, so .billcols inside
     it had no flex context, ignored its own flex:1, and grew to its content —
     30,000px of deliveries in an 800px window, with nothing able to scroll.
     A definite height on the parent is not enough; the CHILD needs a parent
     that constrains it (RC 179). */
  display: flex; flex-direction: column; }
/* ============================================================ THE SHELL ==
   ⭐ RC 174, the operator: "There should be just one bar shared among all the
   pages. No?"  ONE markup (_shell.html), TWO arrangements, and NOTHING here
   is repeated on a page — a page fills the middle and draws no furniture.

   A COMPUTER  the whole shell is the navy column down the left.
   A PHONE     .shellbar is the fixed top bar; .shellitems is the drawer.
   ⛔ The old top bar and its measured overflow-fold are GONE, not hidden. */
.screen { display: flex; align-items: stretch; min-height: 100vh; }
.shellmain { flex: 1; min-width: 0; display: flex; flex-direction: column;
  background: var(--ground); }
.shellnav .shellbar { display: flex; align-items: center; flex: none; }
.shellnav .shelltoggle { display: none; }        /* a computer never needs it */
.shellnav .shellitems { display: flex; flex-direction: column; flex: 1; min-height: 0; }
/* ⭐ THE MENU SCROLLS WHEN IT OUTGROWS THE WINDOW (Sep 6). The column is
   locked to the window height (100vh, above), so every item past the fold was
   simply UNREACHABLE — and on Sep 6 adding one menu row ("Suggestions",
   RC 187b) pushed Sign out 22px off the bottom of a 1280x800 screen. The
   pixel machine caught it as BURIED on the day page, but the day page had
   nothing to do with it: it is the SHELL, so it was every manager page at
   that height, and the next menu item would have done it again.
   ⚠️ NOT A DAY-PAGE FIX AND NOT AN ITEM COUNT FIX. The condition is "the menu
   is taller than the window", which no page controls and which grows with the
   business. The phone drawer already scrolled (body.phonearr, below); this is
   the same rule for the computer, which never had it. */
body:not(.phone) .shellnav .shellitems { overflow-y: auto; }
.shellnav .swho { font-weight: 700; }
/* RC 150a/151b: the clock reads as a MENU ITEM, not a button — same words,
   same padding and hover as the links above it.
   ⛔ THE RC 76 LEAK, FIFTH APPEARANCE: retiring the old bar took its
      the old bar's own navact rule with it, and the generic filled-blue
      button rule flooded straight back in — a full-width slab in the drawer.
      THREE classes plus the element, so nothing generic can outrank it. */
.shellnav .sfoot .clockform { margin: 0; }
.shellnav .sfoot button.navact { width: auto; margin: 0 0 .35rem; background: none;
  border: 0; box-shadow: none; color: var(--nav-ink); font-size: .92rem;
  font-weight: 600; padding: .25rem 0; border-radius: 0; text-align: left;
  white-space: nowrap; cursor: pointer; }
.shellnav .sfoot button.navact:hover { color: #fff; background: none; box-shadow: none;
  filter: none; text-decoration: underline; }
.shellscrim { display: none; }

.shellnav { width: var(--nav-w); flex: none; background: var(--nav); color: var(--nav-ink);
  display: flex; flex-direction: column; }
.shellnav .sbrand { display: flex; align-items: center; gap: .5rem; padding: .85rem 1rem;
  color: #fff; font-weight: 800; font-size: 1.02rem; border-bottom: 1px solid #ffffff22;
  text-decoration: none; border-radius: 0; }
.shellnav a { display: block; padding: .5rem 1rem; color: var(--nav-ink); text-decoration: none;
  font-weight: 600; font-size: .92rem; border-left: 3px solid transparent; border-bottom: 0; }
.shellnav a:hover { background: var(--nav-hover); color: #fff; }
/* ⭐ RC 180: WHERE YOU ARE has to be obvious at a glance, not a shade
   darker. A filled bar, a bright left mark, and the heaviest weight in the
   menu — three signals, because one is easy to miss on a phone in daylight. */
.shellnav a.on { background: var(--nav-active); color: #fff; font-weight: 800;
  border-left-color: var(--nav-mark); border-left-width: 4px; }
.shellnav a.on:hover { background: var(--nav-active); }
.shellnav .ssect { margin-top: .7rem; padding: 0 1rem .2rem; font-size: .7rem;
  letter-spacing: .06em; color: #8fa3c4; text-transform: uppercase; }
.shellnav .sfoot { margin-top: auto; padding: .7rem 1rem; border-top: 1px solid var(--nav-edge); }
.shellnav .sfoot a { padding: 0; color: #fff; }
.shellnav .sfoot .linkish { background: none; border: 0; color: var(--nav-ink); padding: .2rem 0;
  font-size: .85rem; width: auto; cursor: pointer; }
.shellmain { flex: 1; min-width: 0; display: flex; flex-direction: column; background: var(--ground); }
.shellmain .pagehead { display: flex; align-items: center; gap: .9rem; padding: .6rem 1.1rem;
  background: #fff; border-bottom: 1px solid var(--line); }
.shellmain .pagehead h1 { font-size: var(--fs-title); margin: 0; }
.chip { background: #f1f4f8; border: 1px solid var(--line); border-radius: var(--r-pill);
  padding: .15rem .7rem; font-size: .84rem; font-weight: 600; white-space: nowrap; }
.chip.warn { background: var(--amber-soft); border-color: var(--amber-edge); color: var(--amber-ink); }
.billcols { flex: 1; min-height: 0; display: flex; gap: var(--gap); padding: var(--gap) var(--pad); }
.bpane { background: var(--card); border: 1px solid var(--line); border-radius: var(--r-card); min-height: 0;
  display: flex; flex-direction: column; overflow: hidden; }
.bpane h2 { font-size: var(--fs-pane); padding: .65rem 1rem .35rem; margin: 0; }
.bsub { color: var(--dim); font-size: .83rem; padding: 0 1rem .5rem; border-bottom: 1px solid #e7ebf0; }
.bscroll { overflow-y: auto; min-height: 0; }
.unbilled { flex: 1.25; }
.dayhead { position: sticky; top: 0; background: #f7f9fb; border-top: 1px solid #e7ebf0;
  border-bottom: 1px solid #e7ebf0; padding: .35rem 1rem; font-size: .85rem; font-weight: 700;
  display: flex; gap: .6rem; align-items: baseline; z-index: var(--z-rise); }
.bmuted { color: var(--dim); font-weight: 600; }
.dayhead .brt { margin-left: auto; }
.brow { display: flex; align-items: center; gap: .8rem; padding: .45rem 1rem;
  border-bottom: 1px solid var(--hair); }
.brow:hover { background: #f8fafd; }
.bwho { min-width: 0; flex: 1.2; }
.bwho b { display: block; }
.bwho small { color: var(--dim); }
.bwhat { flex: 1; font-weight: 700; color: var(--green); font-size: var(--fs-body); }
.bwhat .bextra { display: block; color: #7a5a00; font-size: .78rem; font-weight: 600; }
.bmeta { color: var(--dim); font-size: .8rem; white-space: nowrap; }
.brow form, .drow form { margin: 0; }
button.billbtn { flex: none; border: 1.5px solid var(--blue); color: var(--blue); background: var(--card);
  font-size: .84rem; font-weight: 700; border-radius: .5rem; padding: .25rem .75rem;
  width: auto; margin: 0; cursor: pointer; }
button.billbtn:hover { background: var(--blue); color: #fff; }
.bempty { color: var(--dim); padding: 1rem; }
.calpane { flex: 1; }
.bcal { padding: .55rem .85rem .1rem; }
.bcalbar { display: flex; align-items: center; justify-content: space-between; padding-bottom: .35rem; }
.bcalbar b { font-size: .95rem; }
a.calnav { border: 1px solid var(--edge); background: #fff; border-radius: .45rem;
  padding: .05rem .55rem; color: #16202b; text-decoration: none; border-bottom-width: 1px; }
table.bgrid { width: 100%; border-collapse: collapse; table-layout: fixed; }
.bgrid th { font-size: .7rem; color: #8a95a1; font-weight: 700; padding: .1rem 0; }
.bgrid td { border: 1px solid #eef1f5; height: 48px; vertical-align: top; font-size: .78rem; }
.bgrid td.off { background: #f7f9fb; }
.bgrid td .dlink { display: block; height: 100%; padding: .15rem .25rem; color: inherit;
  text-decoration: none; border-bottom: 0; }
.bgrid td .d { font-weight: 700; }
.bgrid td .n { display: inline-block; margin-top: .1rem; font-size: .7rem; font-weight: 700;
  background: #e8f0fe; color: #2456a8; border-radius: .55rem; padding: 0 .35rem; }
.bgrid td .n.amber { background: #fff1cc; color: #7a5a00; }
.bgrid td .n.pale { background: #eef1f5; color: var(--dim); }
.bgrid td.sel { outline: 2px solid #2563c9; outline-offset: -2px; }
.bgrid td.today .d { color: #2563c9; }
.callegend { padding: .3rem .85rem .45rem; color: var(--dim); font-size: .76rem;
  border-bottom: 1px solid #e7ebf0; }
.callegend .amb { color: #7a5a00; }
.dayview h3 { font-size: .93rem; padding: .5rem 1rem .05rem; margin: 0; }
.bsub2 { color: var(--dim); font-size: .8rem; padding: 0 1rem .4rem; }
.drow { display: flex; align-items: center; gap: .7rem; padding: .35rem 1rem;
  border-top: 1px solid var(--hair); font-size: .88rem; }
.dwho { flex: 1.2; font-weight: 700; }
.dwhat { flex: 1; color: #157a3b; font-weight: 700; font-size: .85rem; }
.drow.billed .dwho, .drow.billed .dwhat { color: #8a95a1; font-weight: 600; }
.drow.dead { opacity: .62; }
.drow.dead .dwhat { color: #8a95a1; font-weight: 600; }
.drow .stamp { flex: none; font-size: .76rem; color: #8a95a1; }
.drow .billbtn { font-size: .78rem; padding: .18rem .6rem; }
/* the shell on a narrow screen: the menu becomes a slim horizontal strip and
   the two panes stack — full function on a phone (RC 44), redrawn properly
   in its own round later */
body.phonearr.panescreen { overflow: auto; height: auto; }
/* ⭐ THE FOLDED MENU (RC 70/174, split out Sep 9 — RC 624/625): one fixed
   navy top bar, and the SAME menu as a drawer sliding in from the side. It is
   the same shell, not a second one. 52px bar per the kit; the drawer holds
   the items in the same order, with the person's name and Sign out at the
   bottom. ⭐ body.narrowmenu (below 1100px) is the MENU switch — a tablet gets
   this bar over the DESKTOP page; body.phonearr (below 600px) is the PAGE
   switch, further down. They used to be one 999px line. */
/* ⛔ NOT `.screen { display: block }` HERE. The phone had that, and moving it
   onto the menu switch broke the full-height chain on a tablet (measured Sep 9
   at 768px: body.panescreen's 100vh reached no scroll box, the calendar sat
   stuck on August and the page could not scroll). With the bar FIXED it is
   out of the flow, so .screen stays the flex row and .shellmain simply takes
   the whole width. The phone's own block rule is with the phone arrangement. */
body.phonearr .screen { display: block; }
/* ⛔⛔ THE MENU COLUMN IS 200px WIDE ONLY WHILE THERE IS A COLUMN. RC 663,
   Sep 10, off the operator's own reading of the card's pictures: below 1100px
   the menu is a fixed top bar with a DRAWER, so the space down the left is
   nobody's — but `--nav-w` went on saying 200px, and everything that insets
   itself by the menu's width left a dead 200px strip with the page showing
   through. MEASURED at 1060x900 before the fix: the card sat at x=200, 860
   wide, in a 1060 window whose menu was 1060x44 across the top.
   ⭐ ONE DECLARATION, THREE CONSUMERS, and all three are right afterwards:
     .shellnav (width) — already overridden to `auto` by the rule below, so
        this does not touch it;
     .kitlayer.panel.kitroom (calc(100vw - --nav-w)) — becomes the whole
        width, which is correct: there is no sidebar to stop short of;
     dialog#cuspop.pop[open] (left) — becomes 0, so the card covers the page
        the way the calendar's pane does.
   ⚠️ FIXING IT AT ANY ONE OF THOSE CALL SITES WOULD HAVE LEFT THE OTHER TWO
      WRONG, which is why it is here. */
body.narrowmenu { --nav-w: 0px; }
/* the bar is FIXED to the window */
body.narrowmenu .shellnav { position: fixed; top: 0; left: 0; right: 0; width: auto;
  height: var(--tap); z-index: var(--z-bar); flex-direction: row; align-items: center; }
body.narrowmenu .shellnav .shellbar { flex: 1; justify-content: space-between;
  padding: 0 .3rem 0 .2rem; }
body.narrowmenu .shellnav .sbrand { border-bottom: 0; padding: .5rem .7rem; }
body.narrowmenu .shellnav .shelltoggle { display: block; width: auto; margin: 0;
  padding: .25rem .7rem; font-size: 1.35rem; line-height: 1; background: none;
  border: 2px solid #ffffff44; color: #fff; border-radius: var(--r-ctl); }
/* ⭐ RC 176: THE DRAWER COMES FROM THE SIDE ITS BUTTON IS ON. RC 625 (Sep 9)
   settled the side per screen: a TABLET is "the computer view minus the
   sidebar", so its hamburger sits top-LEFT and the sidebar slides in from the
   LEFT, where a computer keeps it; a PHONE keeps the button and the drawer on
   the RIGHT ("It's super weird to have the hamburger menu button on the right
   but the actual menu on the left", 2026-08-23). It is SKINNIER than the
   sidebar — a menu is a list of words, not a panel. */
body.narrowmenu .shellnav .shelltoggle { order: -1; }              /* tablet: left */
body.narrowmenu .shellnav .shellitems { position: fixed; top: var(--tap); bottom: 0; left: 0;
  right: auto; width: 12rem; max-width: 62vw; background: var(--nav); overflow-y: auto;
  transform: translateX(-102%); transition: transform .18s ease-out;
  box-shadow: 2px 0 18px #0006; }
body.narrowmenu.phonearr .shellnav .shelltoggle { order: 0; }      /* phone: right */
body.narrowmenu.phonearr .shellnav .shellitems { right: 0; left: auto;
  transform: translateX(102%); box-shadow: -2px 0 18px #0006; }
body.narrowmenu .shellnav.open .shellitems { transform: translateX(0); }
body.narrowmenu .shellnav .shellitems a { white-space: normal; }
body.narrowmenu .shellscrim { display: block; position: fixed;
  top: var(--tap); right: 0; bottom: 0; left: 0;
  background: #0006; z-index: var(--z-scrim); }
body.narrowmenu .shellmain { padding-top: var(--tap); }

body.phonearr .billcols { flex-direction: column; padding: .6rem; }
body.phonearr .bscroll { max-height: 60vh; }

/* billing round two (RC 156): one main list with two modes; the add-card
   under the calendar replaces the dead short list */
.chip.sel { background: var(--blue-soft); border-color: var(--blue-edge); color: var(--blue-ink); }
.bmodeback { margin-left: .6rem; font-size: .8rem; padding: .12rem .55rem; }
.brow.billedrow .bwho b, .brow.billedrow .bwhat { color: #8a95a1; font-weight: 600; }
.addcard { border-top: 1px solid #e7ebf0; padding-bottom: .8rem; }
.addcard h3 { font-size: .93rem; padding: .55rem 1rem .05rem; margin: 0; }
.addcard form { padding: 0 1rem; }
.baddsearch input[type=search] { width: 100%; border: 1.5px solid var(--edge); border-radius: .55rem;
  padding: .4rem .7rem; font-size: .9rem; }
.baddsearch { position: relative; }
.baddsearch .results { position: absolute; left: 0; right: 0; top: 100%; background: #fff;
  border: 1px solid var(--edge); border-radius: .5rem; z-index: var(--z-drop); max-height: 14rem;
  overflow-y: auto; box-shadow: 0 8px 22px #0002; list-style: none; margin: .15rem 0 0; padding: 0; }
.baddsearch .results li { padding: .35rem .6rem; cursor: pointer; }
.baddsearch .results li:hover { background: #f0f5ff; }
.baddrow { display: flex; gap: .8rem; margin: .5rem 0; flex-wrap: wrap; }
.baddrow label { font-size: .84rem; color: var(--dim); font-weight: 600; display: flex;
  flex-direction: column; gap: .15rem; }
.baddrow input, .baddrow select { border: 1.5px solid var(--edge); border-radius: .5rem;
  padding: .3rem .5rem; font-size: .9rem; width: auto; }
/* ⭐ THE COMPACT QUANTITY ROW (RC 144's pattern at desk size) — ONE
   definition, Sep 2. It was written out THREE times: here, again under
   .daddpop byte-for-byte identically, and a third big-finger version at
   the top for the driver. The two desk copies are now this one list, and
   the browser-<dialog> popup joins it rather than growing a fourth. */
.addcard .countrow, .daddpop .countrow, dialog.pop .countrow {
  display: flex; gap: .5rem; margin: .3rem 0; }
.addcard .countrow input, .daddpop .countrow input, dialog.pop .countrow input {
  width: 5rem; border: 1.5px solid var(--edge); border-radius: .5rem;
  padding: .3rem .5rem; }
.addcard .countrow select, .daddpop .countrow select, dialog.pop .countrow select {
  flex: 1; border: 1.5px solid var(--edge); border-radius: .5rem;
  padding: .3rem .5rem; }
.addcard .plusbtn, .daddpop .plusbtn, dialog.pop .plusbtn {
  width: auto; background: #f1f4f8; color: #16202b; border: 1px solid var(--edge);
  border-radius: .5rem; padding: .15rem .7rem; margin: .1rem 0 .3rem; }
button.baddsave { width: 100%; background: #2563c9; color: #fff; border-radius: .55rem;
  padding: .45rem; font-weight: 700; margin-top: .3rem; }
button.baddsave:disabled { background: var(--edge); cursor: default; }

/* RC 157a: add-a-delivery as a POPUP from the + beside any date.
   RC 158f: the selector carries .dayhead so it OUTRANKS the generic
   desktop `body:not(.phone) button` sizing — that rule's width:auto +
   big padding is what stretched this into the askew pill (the RC 76
   specificity leak, third appearance). */
.dayhead button.daddplus { width: 1.5rem; height: 1.5rem; padding: 0; margin: 0 0 0 .5rem;
  line-height: 1; background: #f1f4f8; color: #2456a8; border: 1px solid var(--edge);
  border-radius: .4rem; font-size: .95rem; cursor: pointer; align-self: center;
  display: inline-flex; flex: none; align-items: center; justify-content: center; }
.dayhead button.daddplus:hover { background: #e8f0fe; }
/* RC 159: a marked row STAYS — only the button changes state */
button.billbtn.on { background: var(--green-soft); color: var(--green-ink); border-color: var(--green-edge); }
button.billbtn.on:hover { background: #cdecd8; color: #14672f; }
.daddoverlay { position: fixed; left: 0; top: 0; right: 0; bottom: 0; background: #0007;
  /* above the kit layers (1060) — a chooser opened FROM a pane must land on
     top of it, not under it (found by the RC 310b pane-remove test) */
  z-index: var(--z-kit-top); display: flex; align-items: center; justify-content: center; }
.daddpop { background: #fff; border-radius: .9rem; box-shadow: 0 18px 60px #0006;
  width: min(30rem, 92vw); max-height: 88vh; overflow-y: auto; padding: .9rem 1.1rem 1.1rem; }
.daddpop h3 { display: flex; align-items: center; justify-content: space-between;
  font-size: 1rem; margin: 0 0 .1rem; }
.daddclose { width: auto; background: none; border: 0; color: var(--dim); font-size: 1rem;
  padding: .2rem .4rem; margin: 0; cursor: pointer; }
.daddpop form { padding: 0; }

.baddnote { display: block; font-size: .84rem; color: var(--dim); font-weight: 600; margin: .4rem 0; }
.baddnote textarea { display: block; width: 100%; border: 1.5px solid var(--edge); border-radius: .5rem;
  padding: .35rem .55rem; font: inherit; margin-top: .15rem; }
.bnote { display: block; color: var(--dim); font-size: .8rem; font-weight: 500; font-style: italic; }

/* RC 168.6 Backfill a Day — a whole route is many stops, so the STOP LIST
   scrolls inside its own window and the page itself never does (RC 146/63) */
.bfhead { display: flex; gap: 1rem; flex-wrap: wrap; align-items: flex-end; margin: .2rem 0 .6rem; }
.bfhead label { font-size: .9rem; font-weight: 600; display: flex; flex-direction: column; gap: .2rem; }
.bfhead input, .bfhead select { border: 1.5px solid var(--edge); border-radius: .5rem; padding: .35rem .55rem;
  font: inherit; width: auto; }
.bfsearch { position: relative; margin-bottom: .5rem; }
.bfsearch input[type=search] { width: 100%; border: 1.5px solid var(--edge); border-radius: .5rem;
  padding: .45rem .7rem; font-size: 1rem; }
#bfstops { max-height: 46vh; overflow-y: auto; border: 1px solid #e7ebf0; border-radius: .6rem;
  padding: .3rem; background: #fbfcfe; }
#bfstops:empty { display: none; }
.bfstation { border: 1px solid var(--line); border-radius: .6rem; background: #fff; padding: .3rem .5rem;
  margin: .25rem 0; }
.bfsthead { display: flex; align-items: baseline; gap: .45rem; }
.bfsthead .bfname { font-size: .92rem; }
.bfsthead small { color: var(--dim); font-size: .78rem; }
button.bfx { width: 1.4rem; height: 1.4rem; flex: none; padding: 0; margin: 0; border-radius: .35rem;
  border: 1px solid var(--edge); background: #f1f4f8; color: var(--dim); font-size: .9rem; line-height: 1;
  align-self: center; }
/* dense: a whole route is many stops, so a stop is a few compact rows and
   nothing stretches for the sake of it (RC 52/55) */
.bfline { display: flex; gap: .4rem; align-items: center; margin: .2rem 0 .2rem 1.9rem; }
.bfline label { font-size: .72rem; color: var(--dim); font-weight: 600; display: flex;
  align-items: center; gap: .3rem; }
.bfline input { width: 3.4rem; font-size: .95rem; border: 1.5px solid var(--edge);
  border-radius: .5rem; padding: .2rem .4rem; text-align: center; }
.bfline select { flex: 0 1 13rem; font-size: .95rem; border: 1.5px solid var(--edge);
  border-radius: .5rem; padding: .2rem .4rem; }
button.bfaddline { margin: 0 0 .1rem 1.9rem; font-size: .95rem; padding: .05rem .55rem;
  border-width: 1px; }
.bfempty { color: var(--dim); }
button#bf-save { position: sticky; bottom: .4rem; margin-top: .6rem; align-self: flex-start;
  padding: .5rem 1.6rem; font-size: 1.05rem; }
/* on a computer the page owns the viewport and the stop list takes whatever
   the head and the Save button leave — zero page scroll (RC 63/146).
   From 600px up since Sep 9 (the desk layer's line, RC 624/625). */
@media (min-width: 600px) {
  body:not(.phone).bfscreen { height: 100vh; overflow: hidden; display: flex; flex-direction: column; }
  /* the column is .shellmain, NOT .screen — .screen is the sidebar+content
     ROW, and making it a column stacked the sidebar on top of the content:
     the operator's "blank" backfill page of 2026-08-24 (everything sat 664px
     below a locked viewport). Same shape as the .dayscreen rule above. */
  body:not(.phone).bfscreen .shellmain { flex: 1; min-height: 0; display: flex; flex-direction: column; }
  body:not(.phone).bfscreen main.wrap { flex: 1; min-height: 0; display: flex; flex-direction: column;
    max-width: 64rem; width: 100%; padding-bottom: .6rem; }
  /* the FORM is the column — #bfstops lives inside it, so giving the wrap
     flex alone left the form overflowing (measured, not assumed) */
  body:not(.phone).bfscreen main.wrap > form { flex: 1; min-height: 0;
    display: flex; flex-direction: column; }
  body:not(.phone).bfscreen #bfstops { flex: 1; max-height: none; min-height: 0; }
  body:not(.phone).bfscreen h1 { margin: .2rem 0 .5rem; }
}
/* ⛔ THE THIRD APPEARANCE OF THE SAME LEAK (RC 76, RC 158f, now): the generic
   `body:not(.phone) button` desktop rule outranks a plain `button.x` class, so
   these came out as fat pills. Scope beats it — and every small control added
   from here needs the same guard. */
body:not(.phone) .bfstation button.bfx { width: 1.4rem; height: 1.4rem; padding: 0;
  margin: 0; font-size: .9rem; border-radius: .35rem; }
body:not(.phone) .bfstation button.bfaddline { font-size: .9rem; padding: .05rem .6rem;
  margin: 0 0 .1rem 1.9rem; border-width: 1px; }
body:not(.phone) .bfline input { font-size: .95rem; padding: .2rem .4rem; }
body:not(.phone) .bfline select { font-size: .95rem; padding: .2rem .4rem; }
body:not(.phone) .bfhead input, body:not(.phone) .bfhead select { font-size: .95rem; padding: .3rem .5rem; }
body:not(.phone) .bfsearch input[type=search] { font-size: .95rem; padding: .35rem .6rem; }

/* ⭐ .minibtn — the SMALL PLAIN BUTTON, used for Close, copy, Save. It was
   being USED and never DEFINED, so the generic filled-blue rule dressed it as
   a full-size slab. THE RC 76 LEAK, SIXTH APPEARANCE, and the lesson is now
   unmistakable: a class that is referenced but has no rule is not neutral —
   the most generic rule on the page claims it. */
button.minibtn, .minibtn { width: auto; margin: 0; padding: .22rem .7rem;
  font-size: var(--fs-small); font-weight: 700; line-height: 1.35;
  background: var(--card); color: var(--text); border: 1px solid var(--line);
  border-radius: var(--r-ctl); box-shadow: none; cursor: pointer; }
button.minibtn:hover, .minibtn:hover { background: var(--hover); filter: none; }
body:not(.phone) button.minibtn { width: auto; padding: .22rem .7rem;
  font-size: var(--fs-small); }

/* ============================================ MY HOURS — RC 177/178/178c ==
   The clock item stays ONE word that flips between Clock in and Clock out.
   Beside it a small ROUND (!) — amber, never red: it marks "this may need
   fixing", not a failure (the kit, and RC 142b). It opens a POPUP, because he
   remembers halfway through a stop and must not lose the page he is on. */
.shellnav .sfoot .clockrow { display: flex; align-items: center; gap: .5rem; }
.shellnav .sfoot .clockrow .clockform { flex: none; }
.shellnav .sfoot button.clockfixbtn { width: 1.35rem; height: 1.35rem; flex: none;
  margin: 0 0 .35rem; padding: 0; border-radius: var(--r-pill); line-height: 1;
  font-size: .82rem; font-weight: 800; cursor: pointer;
  background: var(--amber-soft); color: var(--amber-ink);
  border: 1px solid var(--amber-edge); box-shadow: none; }
.shellnav .sfoot button.clockfixbtn:hover { filter: brightness(1.08); }

.clockover { position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: var(--z-modal);
  background: #0007; display: flex; align-items: center; justify-content: center;
  padding: 1rem; }
.clockover[hidden] { display: none; }
.clockbox { background: var(--card); border-radius: var(--r-card); width: 26rem;
  max-width: 100%; max-height: 84vh; display: flex; flex-direction: column;
  overflow: hidden; box-shadow: 0 18px 50px #0006; }
.clockhead { display: flex; align-items: center; justify-content: space-between;
  gap: .8rem; padding: .7rem var(--pad); border-bottom: 1px solid var(--line); }
.clockhead h2 { margin: 0; font-size: var(--fs-pane); }
.clockbody { padding: .3rem var(--pad) .8rem; overflow-y: auto; }
.crow { display: flex; align-items: flex-end; flex-wrap: wrap; gap: .5rem;
  padding: .6rem 0; border-bottom: 1px solid var(--hair); margin: 0; }
.crow:last-child { border-bottom: 0; }
.crow .cday { flex: 1 0 100%; font-weight: 700; font-size: var(--fs-small);
  display: flex; align-items: baseline; gap: .5rem; flex-wrap: wrap; }
.crow label { display: flex; flex-direction: column; gap: .15rem; margin: 0;
  font-size: var(--fs-label); color: var(--dim); font-weight: 600; }
/* every control in a row is the same size — a date box that dwarfs the time
   boxes beside it reads as a different kind of thing (RC 89) */
.crow input { width: 7.2rem; margin: 0; font-size: var(--fs-small);
  padding: .28rem .45rem; box-sizing: border-box; }
.crow button.go { margin-left: auto; }
.crow.addrow { background: var(--band); border-radius: var(--r-ctl);
  padding: .6rem .6rem .7rem; margin-top: .5rem; }
/* the add row: the DAY gets its own line, then start / finish / Add.
   ⛔ nowrap ran it off the right edge of a phone — a row that cannot fit must
      wrap deliberately, never be told not to (RC 89 2.1). */
.crow.addrow { flex-wrap: wrap; align-items: flex-end; }
.crow.addrow .cday { flex: 1 0 100%; }
.crow.addrow label:first-of-type { flex: 1 0 100%; }
.crow.addrow input[type=date] { width: 10rem; }
/* the three states, said to the DRIVER in his own words (RC 178b) */
.cnote { font-weight: 600; font-size: var(--fs-label); border-radius: var(--r-pill);
  padding: .05rem .5rem; border: 1px solid transparent; }
.cnote.ok { color: var(--green-ink); background: var(--green-soft); border-color: var(--green-edge); }
.cnote.fixed, .cnote.hand { color: var(--amber-ink); background: var(--amber-soft);
  border-color: var(--amber-edge); }
.cempty { color: var(--dim); font-size: var(--fs-small); padding: .6rem 0; margin: 0; }
.clockerr { margin: 0; padding: .5rem var(--pad) .7rem; color: var(--red);
  font-weight: 700; font-size: var(--fs-small); }
.clockerr[hidden] { display: none; }

/* ====================================== THE CUSTOMERS PAGE — RC 170/174 ==
   Operator, 2026-08-23, on the unstyled version: "It's currently a bit of a
   mess."  It was: the page used class names nothing had ever defined, so the
   browser's own defaults dressed it — chips as blue underlined links, the
   name welded to the address, a copy button the size of a slab.
   ⭐ EVERYTHING HERE IS THE KIT'S PIECES, and where billing already had a
      piece (.bwho, .chip, .bpane, .bscroll) this page REUSES it rather than
      growing a second name for the same thing. */

/* the page head: a search sized to its words, and the actions on the right */
.pagehead { flex-wrap: wrap; }   /* the head wraps rather than clipping its own button */
.pagehead .headsearch { margin: 0; }
.pagehead .headsearch input { width: 10rem; margin: 0; font-size: var(--fs-body);
  padding: .3rem .6rem; border-radius: var(--r-ctl); }
.pagehead .grow, .filterbar .grow { flex: 1; }
/* the kit's BUTTON piece: sized to its words on a computer, never full width */
a.btnp, button.btnp, a.btnq, button.btnq { display: inline-block; width: auto;
  margin: 0; padding: .32rem .85rem; font-size: var(--fs-body); font-weight: 700;
  border-radius: var(--r-ctl); text-decoration: none; cursor: pointer;
  white-space: nowrap; box-shadow: none; }
a.btnp, button.btnp { background: var(--blue); color: #fff; border: 1px solid var(--blue); }
a.btnq, button.btnq { background: var(--card); color: var(--text); border: 1px solid var(--line); }
a.btnp:hover, button.btnp:hover { filter: brightness(1.07); }
a.btnq:hover, button.btnq:hover { background: var(--hover); }
body:not(.phone) a.btnp, body:not(.phone) a.btnq { width: auto; padding: .32rem .85rem; }

/* the filter bar: two rows of chips, each carrying its own count */
.filterbar { padding: .45rem var(--pad); border-bottom: 1px solid var(--line);
  background: var(--band); }
.filterbar .frow { display: flex; align-items: center; gap: .35rem;
  flex-wrap: wrap; padding: .12rem 0; }
.fswitch { font-size: var(--fs-label); font-weight: 700; color: var(--dim);
  text-transform: uppercase; letter-spacing: .05em; margin-right: .3rem; }
/* a filter chip IS clickable — the only chip that is. It says so by moving,
   not by looking like a link (the kit: chips never look clickable). */
/* ⛔ WAS a.chip.pick — AN ANCHOR. When the filters stopped reloading the page
   they became BUTTONS (RC 479), every one of these rules stopped matching, and
   a picked chip looked exactly like an unpicked one. The operator, Sep 3:
   "There's no indicator that a filter is selected."  A filter you cannot see
   the state of is worse than no filter: the list is lying about why it is
   short. Named without the element so it holds for whatever the chip is made
   of next. */
.chip.pick { text-decoration: none; color: var(--text); cursor: pointer;
             font: inherit; }
.chip.pick b { font-weight: 800; color: var(--dim); margin-left: .15rem; }
.chip.pick:hover { background: var(--hover); }
.chip.pick.on { background: var(--blue-soft); border-color: var(--blue-edge);
  color: var(--blue-ink); }
.chip.pick.on b { color: var(--blue-ink); }
.chip.plainchip { background: var(--ground); }
.chip.bad { background: var(--red-soft); border-color: #f3c2cc; color: var(--red); }
.chip.good { background: var(--green-soft); border-color: var(--green-edge); color: var(--green-ink); }

/* the kit's TABLE piece */
/* ⭐ FIXED LAYOUT, OR IT WILL NEVER FIT. Seven columns of nowrap text sized
   themselves to their CONTENT and pushed Due, Stations and Status — and the
   whole peek pane — off the right edge. With table-layout:fixed the columns
   take the share they are given and long text ellipsizes inside them. */
.ctable { width: 100%; border-collapse: collapse; font-size: var(--fs-body);
  table-layout: fixed; }
.ctable th:nth-child(1), .ctable td:nth-child(1) { width: 32%; }
.ctable th:nth-child(2), .ctable td:nth-child(2) { width: 15%; }
.ctable th:nth-child(3), .ctable td:nth-child(3) { width: 16%; }
.ctable th:nth-child(4), .ctable td:nth-child(4) { width: 17%; }
.ctable th:nth-child(5), .ctable td:nth-child(5) { width: 20%; }
.ctable td { overflow: hidden; }
.ctable td .chip { max-width: 100%;
  display: inline-block; vertical-align: bottom;
  /* tighter INSIDE a table than in a page head — the row is dense by design
     and a chip that ellipsizes its own one word says nothing */
  padding: .08rem .45rem; font-size: var(--fs-label); }
.ctable thead th { position: sticky; top: 0; z-index: var(--z-sticky); background: var(--card);
  text-align: left; font-size: var(--fs-label); text-transform: uppercase;
  letter-spacing: .05em; color: var(--dim); font-weight: 700;
  padding: .45rem .55rem; border-bottom: 1px solid var(--line);
  /* the labels WRAP rather than clip — "LAST DELIVERY" cut to "LAST DELIVER'"
     names nothing (RC 89 2.1). Two short lines in a header is fine. */
  white-space: normal; line-height: 1.2; }
.ctable thead th a { color: inherit; text-decoration: none; }
.ctable thead th a:hover { color: var(--blue); }
.ctable td { padding: .38rem .55rem; border-bottom: 1px solid var(--hair);
  vertical-align: middle; }
.ctable tbody tr { cursor: pointer; }
.ctable tbody tr:hover { background: var(--hover); }
.ctable tbody tr.sel { background: var(--blue-soft); }
.ctable .bwho { max-width: none; }
.ctable .bwho small { display: block; font-size: var(--fs-small); }
.ctable .cqty { white-space: nowrap; color: var(--green-ink); font-weight: 600; }
.ctable .cwhen { white-space: nowrap; font-size: var(--fs-small); }
.ctable .cnum { text-align: right; padding-right: 1rem; }

/* ⛔ THE LIST PANE MUST BE ALLOWED TO SHRINK. Without flex:1/min-width:0 a
   flex item sizes to its CONTENT, so the table pushed the peek pane clean off
   the right edge and took DUE / STATIONS / STATUS with it. min-width:0 is the
   part everyone forgets: a flex item will not shrink below its content
   without it. */
.listpane { flex: 1; min-width: 0; }

/* the peek pane on the right */
.peek { width: 15rem; flex: none; }
.peekhead { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap;
  padding: .6rem var(--pad); border-bottom: 1px solid var(--line); }
.peekhead h2 { margin: 0 .2rem 0 0; font-size: var(--fs-pane); flex: 1 0 100%; }
.peekbody { padding: 0 var(--pad) .6rem; }
.pk { padding: .6rem 0; border-bottom: 1px solid var(--hair); }
.pk:last-child { border-bottom: 0; }
.pklab { font-size: var(--fs-label); text-transform: uppercase; letter-spacing: .05em;
  color: var(--dim); font-weight: 700; margin-bottom: .25rem; }
.pkval { display: flex; align-items: center; gap: .45rem; font-size: var(--fs-small); }
.pkval .copyable { flex: 1; min-width: 0; }
.pkrow { display: flex; align-items: baseline; gap: .5rem; padding: .15rem 0;
  font-size: var(--fs-small); }
.pkrow a { flex: 1; }
.pkrow span { color: var(--dim); white-space: nowrap; }
.pkrow.hrow .hdate { flex: 1; min-width: 0; color: var(--text); }
.pkrow.hrow .hwent { color: var(--green-ink); font-weight: 600; }
.pkrow.hrow .hemp { color: var(--dim); }
.peekfoot { margin-top: auto; display: flex; gap: .5rem; flex-wrap: wrap; padding: .6rem var(--pad);
  border-top: 1px solid var(--line); }

body.phonearr .peek { width: auto; }
body.phonearr .ctable .bwho { max-width: 11rem; }

/* ⭐ THE CUSTOMERS LIST ON A PHONE (RC 70/183): a table is a computer thing.
   Five columns inside 390px clipped every one of them — "Mont", "3 Pure",
   "n…". So the table STOPS BEING A TABLE: each customer is a block, the name
   and address on top, the facts underneath in one readable line.
   ⛔ Not a squashed desktop — the kit's rule, and the reason the old
      table.customers had exactly this treatment already. */
body.phonearr .ctable, body.phonearr .ctable tbody,
body.phonearr .ctable tr, body.phonearr .ctable td { display: block; width: auto; }
body.phonearr .ctable { table-layout: auto; }
body.phonearr .ctable thead { display: none; }
body.phonearr .ctable tr { padding: .5rem .1rem; border-bottom: 1px solid var(--hair); }
body.phonearr .ctable td { border: 0; padding: 0; overflow: visible; }
body.phonearr .ctable td:nth-child(1), body.phonearr .ctable td:nth-child(2),
body.phonearr .ctable td:nth-child(3), body.phonearr .ctable td:nth-child(4),
body.phonearr .ctable td:nth-child(5) { width: auto; }
body.phonearr .ctable .bwho { max-width: none; margin-bottom: .15rem; }
body.phonearr .ctable .bwho b { white-space: normal; }
body.phonearr .ctable .bwho small { white-space: normal; }
/* the facts run together on one line, each one labelled by its own shape:
   the rhythm in plain text, the order in green, the date muted, the state a chip */
body.phonearr .ctable td:nth-child(2), body.phonearr .ctable td:nth-child(3),
body.phonearr .ctable td:nth-child(4), body.phonearr .ctable td:nth-child(5) {
  display: inline-block; vertical-align: middle; margin-right: .5rem;
  font-size: var(--fs-small); }
body.phonearr .ctable td:nth-child(2)::after { content: " ·"; color: var(--faint); }
body.phonearr .ctable td:nth-child(3)::after { content: " ·"; color: var(--faint); }
/* the peek pane becomes the second thing on the page, not a squeezed column */
body.phonearr .peek { width: auto; margin-top: .6rem; }
body.phonearr .pagehead { padding: .5rem .7rem; gap: .4rem; }
body.phonearr .pagehead .headsearch input { width: 100%; }
body.phonearr .pagehead .headsearch { flex: 1 0 100%; }
/* RC 184: the column the list is sorted by is marked, and its arrow points
   the way the list runs. A sortable header must LOOK sortable on hover. */
.ctable thead th.sorted { color: var(--text); }
.ctable thead th .sarrow { color: var(--blue); font-size: .9em; }

/* ============================================== HOME, THE DASHBOARD — RC 181 =
   Operator: "We'll make Home a dashboard... 'X number of deliveries have yet
   to be billed', 'x number of customers are overdue for their delivery'."
   ⛔ EVERY CARD IS A LINK. A count you cannot act on is decoration. */
.dash { display: flex; flex-direction: column; gap: var(--gap); max-width: 34rem;
  margin: var(--gap) auto; }
.dashcard { display: flex; align-items: baseline; gap: .7rem; text-decoration: none;
  background: var(--card); border: 1px solid var(--line); border-left-width: 5px;
  border-radius: var(--r-card); padding: .8rem 1rem; color: var(--text); }
.dashcard:hover { background: var(--hover); }
.dashn { font-size: 1.7rem; font-weight: 800; line-height: 1; }
.dashwhat { font-size: var(--fs-body); }
.dashcard.warn { border-left-color: var(--amber-edge); }
.dashcard.warn .dashn { color: var(--amber-ink); }
.dashcard.bad { border-left-color: var(--red); }
.dashcard.bad .dashn { color: var(--red); }
.dashcard.plainchip { border-left-color: var(--blue); }
.dashcard.plainchip .dashn { color: var(--blue); }

/* the picker that now rides in the head of The days */
.pagehead .daypick { display: flex; align-items: center; gap: .4rem; margin: 0; }
.pagehead .daypick input, .pagehead .daypick select { width: auto; margin: 0;
  padding: .28rem .5rem; font-size: var(--fs-small); border-radius: var(--r-ctl); }
/* a day's row on The days */
.drow { display: flex; align-items: center; gap: .8rem; padding: .5rem 1rem;
  border-bottom: 1px solid var(--hair); }
.drow .dwho { flex: 1; min-width: 0; }
.drow .dwho a { color: inherit; text-decoration: none; }
.drow .dwho a:hover { text-decoration: underline; }
.drow .dwho b { display: block; }
.drow .dwho small { color: var(--dim); font-size: var(--fs-small); }
.drow .dacts { display: flex; gap: .4rem; flex-wrap: wrap; justify-content: flex-end; }
body.phonearr .drow { flex-wrap: wrap; }
body.phonearr .drow .dacts { flex: 1 0 100%; justify-content: flex-start; }
/* on a phone the picker takes its own line rather than clipping "Open it" */
body.phonearr .pagehead .daypick { flex: 1 0 100%; }
body.phonearr .pagehead .daypick input { flex: 1; min-width: 0; }

/* ================================== THE DAYS — THE CALENDAR (RC 181/188) ==
   Two weeks at a glance, Sunday to Saturday. Each cell holds its truck cards.
   ⛔ RC 189: EVERY CELL IS CLICKABLE, Sunday and Shabbos included. Shabbos is
      marked because it is true for BMD; it is not disabled, because it is not
      true for everyone who may use this site. */
/* RC 318: no horizontal inset — the grid's edges line up with the weekday
   bar above it ("widening the calendar so it is equal with the days of the
   week on the top"); vertical breathing stays at the bottom only */
.calwrap { padding: 0 0 var(--gap); }
.calgrid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr));
  border: 1px solid var(--line); border-radius: var(--r-card); overflow: hidden; }
.calgrid > * { border-right: 1px solid var(--line); border-bottom: 1px solid var(--line); }
.calgrid > *:nth-child(7n) { border-right: 0; }
.calwd { background: var(--band); text-align: center; padding: .3rem;
  font-size: 11px; text-transform: uppercase; letter-spacing: .05em;
  color: var(--dim); font-weight: 700; }
.calcell { background: var(--card); min-height: 7.5rem; padding: .35rem .4rem .5rem;
  display: block; text-decoration: none; color: var(--text);
  min-width: 0; overflow: hidden; }
.calcell:hover { background: var(--hover); }
.calcell.past { background: var(--ground); }
.calcell.shabbos { background: var(--band); }
.calcell.sel { outline: 2px solid var(--blue); outline-offset: -2px; }
.calhead { display: flex; align-items: baseline; gap: .3rem; }
/* the date sits on the baseline; the two little BUTTONS (fold ▾, +) do not —
   they are different type sizes, so on the baseline their tops differed by
   2px and the pixel machine called every day header "not level" at tablet
   width (162 times, Sep 9). Controls in a row share a top edge (RC 334). */
.calhead > button { align-self: center; margin-top: 0; margin-bottom: 0; }
.caldate { font-weight: 700; font-size: 12px; white-space: nowrap; }
.calcell.istoday .caldate { color: var(--blue); }
/* ⭐ 12px IS THE FLOOR AND --dim IS THE LIGHTEST GREY THAT PASSES. These were
   9-11px in --faint, which measures 3.05:1 on white — below the 4.5:1 a
   person needs — on the one screen the manager plans the week from. --dim
   measures 5.03:1, so nothing site-wide had to change (audit, Sep 8). */
.calsh { font-size: 12px; color: var(--dim); white-space: nowrap; }
.calplus { margin-left: auto; width: 1.1rem; height: 1.1rem; line-height: 1rem;
  text-align: center; border: 1px solid var(--line); border-radius: var(--r-ctl);
  color: var(--dim); font-weight: 700; font-size: .8rem; background: var(--card); }
.calcell:hover .calplus { border-color: var(--blue); color: var(--blue); }
/* ⚠️ FIXED PIXELS HERE, DELIBERATELY. This page's root font is 20px, so a
   rem-based size that looked right in the drawing comes out a quarter larger
   and "Mitsubishi" clips to "Mitsubish" in a 94px cell. A calendar cell is the
   densest thing on the site and is sized in pixels on purpose. */
.caltruck { margin-top: .3rem; padding: .22rem .3rem; border-radius: var(--r-ctl);
  border-left: 3px solid var(--line); background: var(--ground);
  font-size: 12px; line-height: 1.4; }
.caltruck b { display: block; font-size: 12px; }
.caltruck span { display: block; color: var(--dim); white-space: nowrap; }
.caltruck .calstate { font-weight: 700; }
.caltruck.good { border-left-color: var(--green); }
.caltruck.good .calstate { color: var(--green-ink); }
.caltruck.warn { border-left-color: var(--amber-edge); }
.caltruck.warn .calstate { color: var(--amber-ink); }
.caltruck.plain .calstate { color: var(--dim); }
.caltowns { margin-top: .25rem; font-size: 12px; color: var(--dim);
  line-height: 1.3; }
.peek .cnum { color: var(--dim); font-size: var(--fs-label); min-width: 1.2rem; }
.peek .hdate { flex: 1; min-width: 0; }
.peek .hdate b { display: block; }
.peek .hdate small { display: block; color: var(--dim); font-size: var(--fs-label); }
/* ⛔ THE NAME WINS. A long quantity ("3 Pure, 4 7oz cups, 2 9oz cups") took
   the whole row and the name ellipsized to NOTHING — the squashed-name fault,
   seventh appearance (RC 171c). The quantity may wrap; the name may not vanish. */
.peek .hwent { flex: 0 1 42%; white-space: normal; text-align: right; line-height: 1.25; }
.peek .hdate { flex: 1 1 50%; min-width: 7rem; }

/* a phone shows one week at a time, stacked — a 7-column grid at 390px is
   seven unreadable slivers (the kit: never a squashed desktop) */
body.phonearr .calgrid { grid-template-columns: 1fr; }
body.phonearr .calwd { display: none; }
body.phonearr .calcell { min-height: 0; }
body.phonearr .calcell:empty { display: none; }

/* RC 191: the past folded behind one bar — ON A PHONE ONLY. On a computer the
   whole fortnight fits and hiding half of it would be worse, not better. */
.calpast { display: none; }
body.phonearr .calpast { display: block; grid-column: 1 / -1; background: var(--card); }
body.phonearr .calpast > summary { padding: .5rem .7rem; font-weight: 700;
  font-size: var(--fs-small); cursor: pointer; color: var(--dim); }
body.phonearr .calpastwrap { border-top: 1px solid var(--hair); }
/* with the past folded away, the past cells in the main grid would be a second
   copy of the same days — so the grid shows only today onward */
body.phonearr .calgrid > .calcell.past { display: none; }
body.phonearr .calpast .calcell.past { display: block; }
.shellnav .sfoot .vswitch { display: block; font-weight: 600; font-size: .88rem;
  color: var(--nav-ink); margin-bottom: .3rem; }
.shellnav .sfoot .vswitch:hover { color: #fff; text-decoration: underline; }
/* RC 194: a closed day wears its name. Tinted like Shabbos — not working —
   and the reason in the same muted small type, so the eye reads "shut, and
   here is why" without a legend. */
.calcell.shut { background: var(--band); }
.calshut { margin-top: .25rem; font-size: 12px; font-weight: 700; color: var(--dim);
  line-height: 1.3; }
/* RC 201: the customer's name on billing is a quiet link — it reads as a name
   until you tap it, and it opens the delivery's popup */
.brow .bwho a.bpeek { color: inherit; text-decoration: none; }
.brow .bwho a.bpeek:hover { text-decoration: underline; color: var(--blue); }
/* RC 202: fully-billed days fold. Hidden until the pane's ▸; each shown day
   hides its customers until its own ▸. Triangles, not words — they are the
   only control on the page that says "there is more here". */
/* three classes deep — the generic desktop button rule outranks one class
   and turns the ▸ into a 60px pill (the RC 76 leak, seventh time) */
.bpane .panehead button.fold, .bpane .dayhead button.fold,
button.fold { width: 1.4rem; height: 1.4rem; padding: 0; margin: 0 0 0 .4rem;
  border: 1px solid var(--line); border-radius: var(--r-ctl); background: var(--card);
  color: var(--dim); font-size: .8rem; line-height: 1; cursor: pointer; box-shadow: none;
  vertical-align: middle; }
button.fold:hover { color: var(--blue); border-color: var(--blue); }
/* RC 215: the day's fold triangle sits at the FAR RIGHT of its bar, past the
   count — the same edge as the pane's own fold, so every triangle is in one
   place. flex order puts it last regardless of markup position. */
.dayhead { display: flex; align-items: center; }
.dayhead .brt { margin-left: auto; }
.dayhead .dayfold { order: 99; margin-left: .5rem; }
.bscroll .dgroup.done { display: none; }
.bscroll.showdone .dgroup.done { display: block; }
.dgroup.done .brow { display: none; }
.dgroup.done.open .brow { display: flex; }
/* RC 204: a fully-billed day on the calendar — the same pill, green */
.bgrid td .n.green { background: var(--green-soft); color: var(--green-ink); }
/* RC 202: the pane head holds the title and, on the right, the fold */
.panehead { display: flex; align-items: center; }
.panehead h2 { flex: 1; }
.bpane .panehead button.fold { margin-right: .8rem; width: 1.4rem; padding: 0; font-size: .8rem; }

/* ================== THE BUILD PAGE ON A PHONE — RC 209/214 ==============
   Matched to the DRIVER's phone day (the operator: "fix the phone version of
   the day planner to match the upgrades we made to the driver one"): one
   tight head, the map as the canvas, a clean sheet. The manager keeps every
   control — they fold into ONE thin strip that scrolls sideways, instead of
   a pile of pills burying the date. */
body.phonearr .buildpage .daybar { padding: .3rem .5rem .1rem; }
body.phonearr.mapday .buildpage .daybar .dbrow,
body.phonearr .buildpage .dbrow { display: flex; flex-wrap: nowrap; overflow-x: auto;
  gap: .35rem; align-items: center; padding-bottom: .25rem; scrollbar-width: none; }
body.phonearr .buildpage .dbrow::-webkit-scrollbar { display: none; }
body.phonearr .buildpage .dbrow h1 { order: -1; flex: none; font-size: 1.05rem;
  margin: 0 .2rem 0 0; white-space: nowrap; }
body.phonearr .buildpage .dbrow .slab, body.phonearr .buildpage .dbrow button {
  flex: none; width: auto; margin: 0; padding: .25rem .6rem; font-size: .78rem;
  white-space: nowrap; border-radius: var(--r-pill); }
/* the facts read as ONE quiet line; the rack warning is a short amber line,
   not a red slab (amber = someone must do something; red = failure) */
body.phonearr .buildpage .facts { font-size: .8rem; gap: .5rem; }
body.phonearr .buildpage .facts .stat { white-space: nowrap; }
body.phonearr .buildpage .facts .warn, body.phonearr .buildpage .facts .okbox {
  flex: none; white-space: nowrap; padding: .1rem .5rem; font-size: .76rem;
  background: var(--amber-soft); color: var(--amber-ink);
  border: 1px solid var(--amber-edge); border-radius: var(--r-pill); }
body.phonearr .buildpage .facts #maplink { padding: .1rem .5rem; font-size: .76rem; }
/* the rows, on the driver's grid: controls · number · name over its detail ·
   the quantity on the right with its own room */
body.phonearr .stops.build li.stop { display: flex; align-items: center; gap: .35rem;
  padding: .45rem .3rem; }
body.phonearr .stops.build li.stop .ctl { flex: none; }
/* ⭐ RC 350c: this squeezes the ✕ ITSELF, and must never reach the submit
   button inside the reason card — that is what wrapped 'Take it off'
   into a 1.6rem column hanging outside the card on his phone. */
body.phonearr .stops.build li.stop .ctl > .rmform button { width: 1.6rem; height: 1.6rem;
  padding: 0; margin: 0; font-size: .9rem; }
body.phonearr .stops.build li.stop .grip { flex: none; font-size: 1rem; padding: 0 .1rem; }
body.phonearr .stops.build li.stop .n { flex: none; font-size: .8rem; min-width: 1.2rem; }
body.phonearr .stops.build li.stop .who { flex: 1; min-width: 0; }
body.phonearr .stops.build li.stop .who b { display: block; }
body.phonearr ul.stops.build li.stop .who small.copyable { display: none; } /* the address is on the customer's page */
body.phonearr ul.stops.build li.stop .who br { display: none; }
body.phonearr .stops.build li.stop .who .tag { font-size: .68rem; padding: .02rem .35rem;
  max-width: 100%; display: inline-block; vertical-align: bottom; }
body.phonearr ul.stops.build li.stop .qty.qstack { flex: none; max-width: 34%;
  text-align: right; font-size: .82rem; white-space: normal; overflow: visible; }
body.phonearr ul.stops.build li.stop .qty.qstack a.qty { white-space: normal;
  overflow: visible; text-overflow: clip; max-width: none; }
body.phonearr .stops.build li.stop .qty.qstack a { white-space: normal; }

/* ============= RC 206/213: THE SEEDS — marks and popups ==================
   The cell's promise mark is a THIN RED EDGE that paints OVER the selection
   border on the left (the operator: "put it in front of the blue, so on the
   left edge, you just see the red"). The pinned STOP row carries a pin
   between its number and name; the truck carrying it wears a thin red
   outline. Chosen from three rendered rounds, 2026-08-23. */
.calcell { position: relative; }
.calcell.pinday::before { content: ""; position: absolute; left: -2px; top: -2px;
  bottom: -2px; width: 3px; background: #d92c2c; z-index: var(--z-sticky);
  border-radius: 2px 0 0 2px; }
.caltruck.pintruck { outline: 1.5px solid #d92c2c; outline-offset: -1.5px; }
.peekhead .chip.pintruck { border-color: #d92c2c; }
.pagehead button.pinchip { border: 1px solid #d92c2c; color: #b32222; font-weight: 800;
  cursor: pointer; background: #fff; width: auto; box-shadow: none; margin: 0;
  border-radius: var(--r-pill); padding: .15rem .7rem; font-size: .84rem;
  line-height: inherit; }
.seedmsg { background: #e8f0fe; border: 1px solid #b9d0f5; color: #123a80;
  border-radius: .5rem; padding: .4rem .7rem; margin: 0 0 .5rem; font-weight: 600; }
.pkrow .rowpin { font-size: .8rem; }
/* the popup rows share .daddpop; three-deep selectors so the generic button
   rule cannot flood in (the RC 76 leak, learned seven times) */
.daddpop.seedpop .whenrow { display: flex; gap: .4rem; margin: .45rem 0; flex-wrap: wrap; }
.daddpop.seedpop button.wchip { flex: 1; text-align: center; padding: .45rem .3rem;
  border: 1.5px solid var(--edge); border-radius: .5rem; background: #fff; color: var(--dim);
  font-weight: 700; font-size: .9rem; cursor: pointer; width: auto; margin: 0; box-shadow: none; }
.daddpop.seedpop button.wchip.on { border-color: #1156d6; color: #1156d6; background: #e8f0fe; }
.daddpop.seedpop .picked { display: flex; align-items: center; gap: .5rem; margin: .3rem 0 0; }
.daddpop.seedpop button.swap { margin-left: auto; background: none; border: 0;
  color: #1156d6; font-weight: 700; cursor: pointer; width: auto; padding: .2rem;
  box-shadow: none; }
.daddpop.seedpop .betweenrow { display: flex; gap: .5rem; align-items: center; margin: .35rem 0; }
.daddpop.seedpop .betweenrow input { flex: 1; border: 1.5px solid var(--edge);
  border-radius: .5rem; padding: .3rem .5rem; }
.daddpop.seedpop .pinfoot { display: flex; gap: .6rem; margin-top: .7rem; align-items: center; }
.daddpop.seedpop .pinfoot .go { flex: 1; }
.daddpop.seedpop .seedwhole { display: block; font-size: .85rem; color: var(--dim); margin: .25rem 0; }
.daddpop.seedpop .seedsearch { position: relative; }
.daddpop.seedpop .seedsearch .results { position: absolute; left: 0; right: 0; top: 100%;
  background: #fff; border: 1px solid var(--edge); border-radius: .5rem; z-index: var(--z-drop);
  list-style: none; margin: .2rem 0 0; padding: .2rem; max-height: 14rem; overflow-y: auto; }
.daddpop.seedpop .seedsearch .results li { padding: .35rem .5rem; cursor: pointer;
  border-radius: .35rem; }
.daddpop.seedpop .seedsearch .results li:hover { background: #f0f5ff; }
.daddpop.seedpop .pinsrow { border-bottom: 1px solid var(--hair); padding: .35rem 0; }
.daddpop.seedpop .pinsrow .pinkind { font-size: .78rem; font-weight: 700;
  padding: .1rem .5rem; border-radius: .5rem; }
.daddpop.seedpop .pinsrow .pinkind.pin { background: #fde8e8; color: #b32222; }
.daddpop.seedpop .pinsrow .pinkind.wait { background: #e8f0fe; color: #123a80; }
.daddpop.seedpop .pinsrow .pinwin { font-size: .85rem; color: var(--dim); }
.daddpop.seedpop form.pinrm { display: inline; margin-left: auto; padding: 0; }

/* ============ RC 227/228/233/223/230: the calendar's round ==============
   The weekday bar PINS and the calendar scrolls under it; the tiles FOLD
   (a master ▾ up top, one per tile); the Earlier-days bar is all tap
   target; Shabbos wears the plain closed-day look (no special tag). */
.calwd { position: sticky; top: 0; z-index: var(--z-cell); }
.calgrid .calhead button.cellfold { width: 1.1rem; height: 1.1rem; padding: 0;
  margin: 0 0 0 auto; border: 1px solid var(--line); border-radius: .3rem;
  background: var(--card); color: var(--dim); font-size: .6rem; line-height: 1;
  cursor: pointer; box-shadow: none; }
.calgrid .calhead button.cellfold + .calplus { margin-left: .25rem; }
.pagehead button.calfoldall { cursor: pointer; width: auto; box-shadow: none;
  margin: 0; background: #f1f4f8; border: 1px solid var(--line);
  border-radius: var(--r-pill); padding: .15rem .7rem; font-size: .84rem;
  font-weight: 600; line-height: inherit; }
/* folded = ONE LINE per route, the truck's name (operator: "The collapsed
   routes should still be visible, just one line: the truck name for now") */
.calgrid.allfolded .calcell:not(.unfolded) .caltruck span,
.calcell.folded .caltruck span,
.calgrid.allfolded .calcell:not(.unfolded) .caltowns,
.calcell.folded .caltowns { display: none; }
body.phonearr .calpast > summary { display: block; width: 100%; cursor: pointer;
  -webkit-tap-highlight-color: rgba(17, 86, 214, .12); }
/* RC 228 corrected: the weekday bar lives OUTSIDE the scroll area (sticky
   grid items pin to their own row track and scroll away — seen rendered) */
.calwdbar { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr));
  border: 1px solid var(--line); border-bottom: 0;
  border-radius: var(--r-card) var(--r-card) 0 0; overflow: hidden; }
.calwdbar .calwd { position: static; border-right: 1px solid var(--line); }
.calwdbar .calwd:nth-child(7n) { border-right: 0; }
.listpane .calwrap .calgrid { border-radius: 0 0 var(--r-card) var(--r-card); }
.pagehead button.calfoldall { color: #16202b; }
.listpane .calwdbar { flex: 0 0 auto; }
/* the + is a BUTTON now (a real door to the builder) — three classes deep so
   the generic button rule cannot inflate it (the RC 76 leak) */
.calgrid .calhead button.calplus { margin-left: auto; width: 1.1rem; height: 1.1rem;
  line-height: 1; padding: 0; border: 1px solid var(--line); border-radius: .3rem;
  background: var(--card); color: var(--dim); font-size: .8rem; cursor: pointer;
  box-shadow: none; }
.calgrid .calhead button.cellfold ~ button.calplus { margin-left: .25rem; }
/* >2 routes scroll INSIDE the day box, the box stays modest */
.calcell .calroutes { max-height: 10.5rem; overflow-y: auto; }
.caltruck .trktowns { color: var(--dim); font-size: 12px; }
/* the seed popup's rounded corners survive its own scrollbar: the CARD clips,
   the FORM scrolls */
.daddpop.seedpop { overflow: hidden; display: flex; flex-direction: column; }
.daddpop.seedpop form, .daddpop.seedpop #pinslist { overflow-y: auto; min-height: 0; }
/* ⭐ ON A PHONE THE DAYS PAGE FLOWS (dayscal scopes it off billing): the
   height-locked flex panes SHRANK the calendar to nothing — an explicit
   height loses to flex-shrink (MEASURED in the admin's phone-view frame:
   listpane 2px, calwrap 32px). The page flows top to bottom inside the
   frame's own scroll; the calendar keeps a deliberate window in REM,
   because vh lies inside the scaled frame. */
body.phonearr.dayscal main.wrap { display: block; overflow-y: auto; }
body.phonearr.dayscal .billcols { display: block; height: auto; }
body.phonearr.dayscal .listpane .bscroll.calwrap { height: 26rem; }

/* ------- RC 245: the buildings pane's deliver-together-with dropdown ------
   Its own lean rows — the day-page result row expects a wide list and broke
   in this narrow pane (empty control box, word-per-line wrapping; the
   operator's catch on the third draft). Mirrors the seed popup's dropdown. */
.peek .searchwrap .bondlist { position: absolute; left: 0; right: 0; top: 100%;
  background: #fff; border: 1px solid #d5dbe6; border-radius: .5rem;
  box-shadow: 0 8px 22px rgba(20,35,70,.13); z-index: var(--z-menu); margin: .2rem 0 0;
  padding: .15rem; list-style: none; max-height: 16rem; overflow-y: auto; }
.peek .searchwrap .bondlist li { padding: .35rem .5rem; cursor: pointer;
  border-radius: .35rem; }
.peek .searchwrap .bondlist li:hover { background: #f0f5ff; }
.peek .searchwrap .bondlist li small { display: block; color: #66708a; }

/* RC 246 ("The new buttons are too big and prominent"): the buildings
   page's controls sit quiet — compact footer actions, a modest new-group
   search. Scoped to this page's panes only (the RC 76 lesson). */
.peek .peekfoot form button.plain, .peek .peekfoot form button.okbtn {
  font-size: .82rem; padding: .3rem .7rem; }
.peek .pkrow form button.plain { font-size: .75rem; padding: .1rem .45rem;
  color: #66708a; }
.bhead .searchwrap.headsearch input { font-size: .85rem; padding: .3rem .6rem;
  max-width: 17rem; }

/* RC 246: the per-page TOP BAR, first worn by the buildings page ("Totally
   missing the top bar"): one slim strip across the content — title, counts
   and the page's actions in it, vertically centered, a real edge under it. */
.panescreen .bhead.pagebar { display: flex; align-items: center; gap: .7rem;
  background: #eef2f9; border: 1px solid #d5dbe6; border-bottom: 2px solid #b9c4d8;
  margin: 0 0 .8rem; padding: .55rem 1rem; border-radius: .6rem; }
.panescreen .bhead.pagebar h1 { font-size: 1.12rem; margin: 0; }
.panescreen .bhead.pagebar .chip { background: #fff; }

/* RC 245/246 round eight ("Improving, but this is bad"): the building
   pane's customer row is a real four-column grid — name, rhythm, bottles,
   the quiet action — nothing wraps, nothing floats. */
.peek .peekbody .pkrow.bldrow { display: grid;
  grid-template-columns: 1fr auto auto auto; gap: .5rem; align-items: center;
  padding: .28rem 0; }
.peek .peekbody .pkrow.bldrow form { margin: 0; }
.peek .peekbody .pkrow.bldrow form button.plain { white-space: nowrap; }

/* RC 246's phone question, first answer for this pane: on a narrow screen
   the building row wraps to two lines — name with the action beside it,
   rhythm and bottles underneath — instead of clipping off the edge. */
@media (max-width: 560px) {
  .peek .peekbody .pkrow.bldrow { grid-template-columns: 1fr auto; }
  .peek .peekbody .pkrow.bldrow > span:first-child { grid-area: 1 / 1; }
  .peek .peekbody .pkrow.bldrow > form { grid-area: 1 / 2; }
  .peek .peekbody .pkrow.bldrow > span:nth-child(2) { grid-area: 2 / 1; font-size: .85rem; }
  .peek .peekbody .pkrow.bldrow > span:nth-child(3) { grid-area: 2 / 2; font-size: .85rem; justify-self: end; }
}

/* The group popup, to the operator's spec (2026-08-24: from the right,
   top bar to page bottom, covers the list without pushing it, list still
   scrolls and clicks behind it, a little x closes it). Scoped to the
   buildings page only. */
.bldgs .billcols { position: relative; display: block; }
.bldgs .bpane.listpane { width: 100%; }
.bldgs .bpane.peek.drawer { position: absolute; top: 0; right: 0; bottom: 0;
  width: min(44rem, 62%); z-index: var(--z-pane); background: #fff;
  border-left: 1px solid #c2cbdd; border-radius: .6rem 0 0 .6rem;
  box-shadow: -14px 0 34px rgba(20,35,70,.2); }
.bldgs .drawer .peekhead { position: relative; padding-right: 2.4rem; }
.bldgs .drawer .peekhead .xbtn { position: absolute; top: .5rem; right: .6rem;
  font-size: .95rem; padding: .15rem .5rem; color: #66708a;
  text-decoration: none; border-radius: .35rem; }
.bldgs .drawer .peekhead .xbtn:hover { background: #eef2f9; color: #1c2740; }
@media (max-width: 700px) {
  .bldgs .bpane.peek.drawer { width: 94%; }
}

/* RC 249: the manager's removed-stops block on the day — quiet grey rows,
   the flag tag carrying who or why. Driver never renders it. */
/* ⭐ THE REMOVED LIST IS ONE GRID, not a stack of independent flex rows
   (operator, Sep 2: "this is weird, how this goes off the edge of the page,
   and the columns aren't straight").
   Two faults, one cause: each row laid itself out with space-between, so
   nothing could SHRINK — a long customer name pushed Restore off the right
   edge — and because every row sized itself, the tags landed at a different
   x on every line. One grid over the whole block fixes both: the columns are
   shared, so they line up, and the name column is the one that gives. */
.removedblock { display: grid; grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: center; gap: .18rem .5rem; opacity: .8; }
.removedblock > p { grid-column: 1 / -1; margin: .5rem 0 .15rem; }
/* RC 697: the rows are a real list now, and the list must disappear into the
   block's ONE grid the same way each row already does — otherwise the <ul>
   becomes a single grid item and the three columns collapse into one. */
.removedblock ul.pgroup { display: contents; }
.removedblock .pkrow.removedrow { display: contents; }
/* ⭐ THE NAME WRAPS AND THE ROW GETS TALLER (operator's own suggestion,
   Sep 2) rather than truncating with an ellipsis. A cut-off customer name
   hides the very thing the row exists to tell him; a taller row costs
   nothing. .pkrow span is nowrap by default, so this has to say otherwise. */
.removedblock .pkrow.removedrow > span:first-child {
  min-width: 0; white-space: normal; overflow-wrap: anywhere; }
.removedblock .pkrow.removedrow > .tag { white-space: normal; }

/* RC 76's flood, ninth sighting ("Who needs massive dumb buttons?!"):
   the removed-row Restore is a QUIET text control, never the site's
   full-size button. Three-class scoped. */
.removedblock .pkrow.removedrow form { margin: 0; display: inline; }
.removedblock .pkrow.removedrow form button { width: auto; display: inline-block;
  font-size: .78rem; padding: .14rem .55rem; margin: 0; color: #4a5a7a;
  background: #eef2f9; border: 1px solid #d5dbe6; border-radius: .35rem; }
.removedblock .pkrow.removedrow form button:hover { background: #e2e9f5; }

/* RC 267: the driver's take-it-off ✕ — the builder's quiet grey pattern
   (LEFT, small — ruled again 267c), opening a small reason card. The
   dropdown is the operator's exact list; "Other…" reveals the text box. */
/* its size, colour and (absent) box come from the one remove-✕ rule near the
   top of this file (RC 617); only what makes a <summary> behave stays here */
.ctl.xreason details summary { list-style: none; cursor: pointer;
  display: flex; align-items: center; justify-content: center; }
.ctl.xreason details summary::-webkit-details-marker { display: none; }
.ctl.xreason details { position: relative; }
.ctl.xreason details[open] summary { color: var(--err); }
.ctl.xreason .xrform { position: absolute; left: 0; top: 2.8rem; z-index: var(--z-menu);
  background: #fff; border: 1px solid #d5dbe6; border-radius: .5rem;
  box-shadow: 0 6px 18px rgba(20,30,50,.15); padding: .5rem;
  display: flex; flex-direction: column; gap: .4rem; min-width: 13rem; }
.ctl.xreason .xrform select, .ctl.xreason .xrform input[type=text] {
  font-size: .95rem; padding: .3rem .45rem; border: 1.5px solid var(--edge);
  border-radius: .4rem; }
/* RC 350c: width/height/padding were overridden here, but MARGIN was not —
   body:where(.tapui) button lends every button on the driver's page a
   1.25rem top margin, which walked this one out of the bottom of its card. */
.ctl.xreason .xrform button { width: 100%; height: auto; padding: .45rem .6rem;
  font-size: .95rem; font-weight: 600; border-radius: .4rem; background: #eef2f9;
  color: var(--ink); border: 1px solid #d5dbe6; margin: 0; white-space: nowrap; }

/* RC 387b (their pick: option 2): the red mark on a day tile whose truck
   carries two routes — visible while scrolling months, details in the popup */
.caldate .dblmark { color: var(--err); font-size: .85em; cursor: help; }

/* RC 386: the already-scheduled question — one quiet card, three choices.
   RC 413: the roving stop's set-the-address card (#oneoffask) is the same
   card with an input — one look, shared rules, above the kit layers. */
#schedask .askback, #oneoffask .askback { position: fixed; top:0; right:0; bottom:0; left:0;
  background: rgba(15,23,42,.35); z-index: var(--z-dialog-back); }
#schedask .askcard, #oneoffask .askcard { position: fixed; top: 30%; left: 50%; transform: translateX(-50%);
  z-index: var(--z-dialog); background: #fff; border: 1px solid #d5dbe6; border-radius: .7rem;
  box-shadow: 0 10px 30px rgba(20,30,50,.25); padding: 1rem 1.2rem;
  display: flex; flex-direction: column; gap: .5rem; width: min(26rem, 92vw); }
#schedask .askcard p, #oneoffask .askcard p { margin: 0 0 .3rem; font-size: 1rem; }
#schedask .askcard button, #oneoffask .askcard button { width: 100%; margin: 0; padding: .55rem .8rem;
  font-size: .95rem; border-radius: .5rem; }
#oneoffask .oneoffin { padding: .5rem .6rem; border: 1px solid #d5dbe6;
  border-radius: .5rem; font-size: 1rem; }

/* ==== THE KIT: popup layers + toast shelf (Aug 25 night; UI_SPEC kit) ==== */
#kitback { position: fixed; top: 0; right: 0; bottom: 0; left: 0; background: rgba(15,23,42,.35);
  opacity: 0; pointer-events: none; transition: opacity .15s; z-index: var(--z-kit-back); }
#kitback.on { opacity: 1; pointer-events: auto; }
.kitlayer { position: fixed; z-index: var(--z-kit); background: var(--pane, #fff);
  box-shadow: 0 8px 40px rgba(0,0,0,.25); display: flex; flex-direction: column; }
.kitlayer.panel { top: 0; right: 0; height: 100%;
  /* wide enough for a five-column table row — 34rem clipped the verbs
     column clean off (seen on the first grain screenshot, Aug 26) */
  width: min(56rem, 92vw); animation: kitin .12s ease-out; }
.kitlayer.sheet { left: 0; right: 0; bottom: 0; max-height: 88vh;
  border-radius: 14px 14px 0 0; animation: kitup .12s ease-out; }
@keyframes kitin { from { transform: translateX(1.5rem); opacity: .4; } }
@keyframes kitup { from { transform: translateY(2rem); opacity: .4; } }
.kithead { display: flex; align-items: center; gap: .5rem;
  padding: .7rem .9rem; border-bottom: 1px solid var(--edge-soft); }
.kittitle { font-weight: 600; flex: 0 1 auto; }   /* RC 323: no greedy
  stretch — a title-bar widget sits right beside the words */
.kitx, .kitback { border: 0; background: none; font-size: 1.1rem;
  cursor: pointer; padding: .2rem .5rem;
  /* ⛔ the base `button` rule gives every button margin:.5rem 0 0, and this one
     never cancelled it — so the ✕ sat 5px BELOW the controls beside it in
     every one of these headers, at every width. Found by measuring computed
     margins after RC 334's levelness check finally saw this bar. */
  margin: 0; }
.kitbody { overflow-y: auto; padding: .9rem; }
#kitshelf { position: fixed; left: 50%; transform: translateX(-50%);
  bottom: 1rem; z-index: var(--z-toast); display: flex; flex-direction: column;
  gap: .5rem; align-items: center; }
.kittoast { background: var(--toast-bg); color: #fff; border-radius: 8px;
  padding: .6rem .9rem; display: flex; gap: .9rem; align-items: center;
  box-shadow: 0 4px 16px rgba(0,0,0,.3); transition: opacity .3s; }
.kittoast.bye { opacity: 0; }
.kitundo { background: none; border: 0; color: #7fb4ff; font-weight: 700;
  cursor: pointer; letter-spacing: .04em; }
/* ⭐ PRESS-AND-HOLD SHOWS THE WHOLE OF A CLIPPED NAME (RC 89/622/628): the
   finger's twin of the mouse's hover tip. ONE bubble, placed by kit.js above
   the finger, dressed like the toast. A titled link loses the phone's own
   long-press menu and titled text its long-press selection — either would
   open instead of the bubble. */
.kithold { position: fixed; z-index: var(--z-toast); max-width: min(90vw, 24rem);
  background: var(--toast-bg); color: #fff; border-radius: 8px;
  padding: .5rem .8rem; font-size: var(--t-body); line-height: 1.3;
  box-shadow: 0 4px 16px rgba(0,0,0,.3); pointer-events: none; }
a[title] { -webkit-touch-callout: none; }
[title]:not(input):not(textarea):not(select) { -webkit-user-select: none; user-select: none; }

/* kit pane rows: one thin line on wide screens, a two-line card on narrow */
.kithead { width: 100%; }
.kittitle { min-width: 0; }
.prow { display: flex; justify-content: space-between; align-items: center;
  gap: .5rem; border-bottom: 1px solid #f2f4f7; padding: .25rem 0; }
.prow.dim { opacity: .55; }
.pmain { min-width: 0; }
.pacts { white-space: nowrap; }
/* ⭐ N2 — THE MARKS CELL. It never wraps and never shrinks, on any width;
   the grid below gives it a max-content track so it takes exactly the room
   its marks need and the name cell gives up the rest. */
.prow .ptags { white-space: nowrap; }
@media (max-width: 899.98px) {
  .prow { display: block; padding: .45rem 0; }
  /* on the phone the row is a card, so the marks take their own LINE under
     the name rather than fighting it for one — the same fact, one line
     lower, and still whole */
  .prow .ptags { display: block; margin-top: .15rem; }
  .pacts { display: flex; gap: .5rem; margin-top: .3rem; }
  .pacts .btn { flex: 1; }
}
.parea { background: #f0f3f7; font-weight: 600; border-radius: 6px;
  padding: .25rem .5rem; margin: .5rem 0 .15rem; }
/* ⭐ THE ROW GROUP (RC 697) — the <ul> parts.rowgroup emits. The rows were
   loose <div>s beside their heading and are real list items now, so the list
   that holds them must cost NOTHING on screen — exactly what ul.stops does
   for the driver's list. ⚠️ Bootstrap gives every <ul> a bottom margin and
   2rem of left padding; that, not the <li>, is what would have moved every
   column on these panes. A group that wants a look of its own (.plist) says
   so further down and wins on order. */
.pgroup { list-style: none; margin: 0; padding: 0; }

/* ==== THE RULER (Aug 25 night): measured design tokens, not eyeballing.
   Sources: Material Design (8px spacing grid, 48px touch targets),
   Apple HIG (44pt minimum tap size), readability practice (45-75ch
   lines), standard type ladders. Every kit pane builds ON these. ==== */
/* (the ruler's tokens live in THE LOOK block at the top of the file since
   Sep 9 — one :root, audit item 6; --tap is declared there as well) */
.kitbody { padding: var(--pane-pad); }
.kitbody p { max-width: var(--measure); }  /* the measure caps PROSE, never tables */
.kithead { padding: var(--s2) var(--s3); min-height: var(--tap); }
.kittitle { font-size: var(--t-h3); white-space: normal; overflow-wrap: normal; }
/* ⛔ RC 492 — THE TITLE WAS DRAWN ON TOP OF ITS OWN CONTROLS. kit.js lifts a
   pane's [data-kit-titlebar] widgets into this bar; the route popup lifts TWO
   dropdowns, which take nearly the whole of a 390px screen, so the heading was
   squeezed to two characters a line and spilled upward BEHIND them, reading
   "Monday, / A [Hino] [Monday, Aug 31] / 31".
   ⭐ AND EVERY GATE WAS GREEN ON IT FOR THREE DAYS: nothing scrolled sideways,
   no button was oversized, the row's controls were level. The pixel machine
   had no measurement for "two things are drawn on top of each other" — it has
   one now. A person found this by LOOKING at the deploy's own picture.
   The title takes its own line here and the controls sit beneath it.
   ⛔ AND WRAPPING ALONE WAS NOT ENOUGH — MEASURED, NOT ASSUMED. The bar is a
   flex ITEM inside .kitlayer's column and may shrink, so it stayed 48px tall
   while its contents needed 136px, and the ✕ ended up 45px BELOW its own bar
   on top of the list. Moving the overflow is not fixing it. It must not
   shrink. */
/* ⛔ AND A SECOND FAULT ON THE SAME BAR, FOUND THE MOMENT THE PIXEL MACHINE
   WAS FINALLY POINTED AT IT: the two lifted dropdowns were 32px tall beside a
   44px ✕ — a row disagreeing with itself, at EVERY size, which is exactly what
   RC 334's levelness check exists to catch. It had simply never seen this bar.
   They are things a finger hits, so they get the tap height like everything
   else. */
.kithead select[data-kit-titlebar] { min-height: var(--tap); }
/* the lifted controls, as one group — a flex row of its own so the bar
   can hand it a whole line on a phone without the browser splitting it. */
.kitctl { display: flex; gap: .5rem; align-items: center; min-width: 0; }

@media (max-width: 899.98px) {
  /* ⛔⛔ ONLY THE BARS THAT ACTUALLY CARRY LIFTED CONTROLS. kit.js builds this
     same .kithead for EVERY pane (kit.js:70), and three of the four lift
     nothing into it — floating, contact, pick. An earlier version of this
     rule was unscoped, and it put their titles on a line of their own and
     dropped the ✕ underneath, turning a tidy 48px bar into a tall one for no
     reason at all. Caught by an adversarial review, not by me: I had looked
     only at the pane I was fixing. THE HABIT: a rule written for one screen
     must name the condition that makes that screen special. */
  .kithead.hasctl { flex-wrap: wrap; flex: 0 0 auto; }
  /* ⭐ LINE ONE IS THE HEADING AND THE ✕, IN THE CORNER WHERE A PERSON LOOKS
     FOR IT. LINE TWO IS THE CONTROLS, TOGETHER. Two earlier tries got this
     wrong in opposite directions: one stranded the ✕ alone on a third line,
     the other let the browser fill line one greedily and left the ✕ in the
     MIDDLE of the header with a truck dropdown beside it. The controls only
     hold together because kit.js now puts them in one box. */
  /* ⚠️ BASIS ZERO, NOT AUTO. With `auto` the title's own text length decides
     where the line breaks, so a long one ("Friday, Sep 18 — SW 1 (catch-up)")
     claimed the whole width and pushed the ✕ onto a line by itself. At zero it
     never forces a break: it takes what is left beside the ✕ and wraps its own
     text inside that. Flex breaks lines BEFORE it shrinks anything, which is
     why min-width:0 alone could not fix this. */
  .kithead.hasctl .kittitle { flex: 1 1 0; min-width: 0; }
  .kithead.hasctl .kitx { order: 1; }
  .kithead.hasctl .kitctl { order: 2; flex: 1 0 100%; }
  /* ⛔⛔ AND THE DATE IS NEVER SHRUNK. THIS IS THE SECOND TIME. First an equal
     split cut "Monday, Aug 31" to "Monday, Aug 3" — not a truncated date, a
     DIFFERENT date. I gave the day a bigger share and called it fixed; it
     still read "Wednesday, Nov" for a longer label, with no day number at
     all. A proportional split can only ever be tuned to the label I happened
     to be looking at.
     ⭐ SO THE DAY TAKES ITS NATURAL WIDTH AND NEVER GIVES ANY BACK. If that
     pushes the ✕ onto a third line on a very narrow phone, so be it: an extra
     line is a cost, a wrong date is a fault. The truck name may shrink —
     "Mitsubishi" clipped is still obviously the Mitsubishi; "Wednesday, Nov"
     is a different day of the year. */
  .kithead select.reday { flex: 0 0 auto; }
  .kithead select.retruck { flex: 0 1 auto; min-width: 0; }
}
.kitx, .kitback { min-width: var(--tap); min-height: var(--tap);
  /* the driver-side "every button full width" phone rule was inflating the
     ✕ to 333px, starving the title to 0 wide (one word per line — the
     stubborn title-wrap defect, root-caused Aug 26 by computed-width probe) */
  width: auto; max-width: calc(var(--tap) * 1.4); flex: 0 0 auto; }
.prow { min-height: var(--row); }
@media (max-width: 899.98px) {
  .pacts .btn { min-height: var(--tap); font-size: var(--t-body); }
}
.kittoast { min-height: var(--tap); font-size: var(--t-body); }

/* ==== THE GRAIN (Aug 26 morning, RC 303/305 continued): measured from the
   exemplars the operator called "way better" — GitHub's live list answered
   14px body / 12.5px meta directly (measured by computed-style probe);
   aligned columns and quiet dividers are the shared grain of all four.
   Desktop rows read as a TABLE the eye can scan; phone keeps fat cards. ==== */
/* (--t-row / --t-meta / --ctl-h are declared in THE LOOK block at the top,
   with the rest of the ruler — one :root since Sep 9, audit item 6) */
@media (min-width: 900px) {
  .prow { display: grid; font-size: var(--t-row);
    /* RC 330b: the actions track is FIRM, never auto — each row is its own
       grid, so an assigned row with no buttons let the track collapse and
       shifted its middle cells ~100px (the Aufschnitt screenshot).
       ⭐ RC 361b: WIDENED for the third verb. "Next Week" (RC 361) did not
       fit in the two-button track, so it hung past the pane's right edge
       and dragged the whole table into a sideways scroll — the operator:
       "instead of making use of the available space, you went past the
       edge". The track now holds all three, and the name column gives up
       the room rather than the pane growing. */
    /* ⭐ RC 401b ("The unused space should be used"): the name-and-labels
       cell takes the idle width — measured: the next-visit column held
       303px for ~170px of text while two labels overflowed a 395px name
       cell and clipped to a bare "…". */
    /* ⭐⭐ N2 / RC 570 — SIX TRACKS, and the second one is the MARKS. The
       marks used to live inside the name cell and were clipped by it; they
       have a track of their own now, sized to their content (max-content),
       so they can never be eaten by a long name. The name track gives up
       the room instead, and its full text is one hover or long-press away
       (RC 699). */
    grid-template-columns: minmax(8rem, 2fr) max-content 4.5rem 8rem minmax(9rem, .75fr) 15.5rem;
    align-items: center; gap: var(--s3); padding: 0 var(--s2);
    min-height: var(--row); }
  /* ⛔ THE 11rem CAP IS GONE with the clipping rule above it: a mark is now
     three or four words at most and sits in a track that fits it. */
  .prow .tag { vertical-align: bottom; }
  /* the ghost's "what became of them" word takes the rest of the row —
     a finished week's moved-on list has no numbers and no buttons */
  .prow .gbecame { grid-column: 3 / -1; }
  .prow .btn { padding: .1rem .5rem; line-height: 1.4; }
  .prow .pc.num { text-align: right; font-variant-numeric: tabular-nums; }
  .prow .pmeta { color: #59636e; font-size: var(--t-meta); }
  .pacts { display: flex; gap: .3rem; justify-content: flex-end; }
  .pacts .btn { font-size: var(--t-meta); padding: .15rem .55rem;
    white-space: nowrap; flex: none; }
}
.prow { border-bottom: 1px solid var(--edge-soft); }
.prow:hover { background: var(--ctl-bg); }
.parea { background: none; border: 0; border-radius: 0; color: #59636e;
  font-size: var(--t-meta); font-weight: 600; text-transform: uppercase;
  letter-spacing: .05em; padding: var(--s3) var(--s2) var(--s1);
  border-bottom: 1px solid var(--edge-soft); }
.pfilter { width: 100%; border: 1px solid var(--line); border-radius: var(--r-ctl);
  padding: .35rem .6rem; font-size: var(--t-row); margin-bottom: var(--s2); }

/* ==== GRAIN PASS 2 (Aug 26, after the operator: "the first two go back to
   the old style"): the grain is EVERY element on the pane's surface, not
   the rows alone. Controls, type, spacing all move to the exemplar system:
   compact bordered buttons (words, never emoji), a quiet filter, calm type. */
.kitbody .pfilter { height: var(--ctl-h); box-sizing: border-box; padding: .25rem .6rem;
  font-size: var(--t-row); border-radius: 6px; border: 1px solid var(--ctl-edge); }
.kitbody .pfilter::placeholder { color: #8b949e; }
.kittitle { font-size: 16px; }
.kithead { min-height: 48px; }
.kitbody { padding: var(--s2) var(--s3); }
.kitbody p.muted { font-size: var(--t-meta); margin-bottom: var(--s2); }
.kitbody p.muted a { color: #0969da; text-decoration: none; }
.prow b { font-weight: 600; font-size: var(--t-row); }
.prow .btn.btn-sm.btn-outline-secondary { border: 1px solid var(--ctl-edge);
  background: var(--ctl-bg); color: var(--ctl-ink); border-radius: 6px;
  font-size: var(--t-meta); font-weight: 500; padding: .15rem .6rem; }
.prow .btn.btn-sm.btn-outline-secondary:hover { background: var(--ctl-hover); }
@media (min-width: 900px) {
  .prow { min-height: 36px; }
  .parea { padding-top: var(--s2); }
}
@media (max-width: 899.98px) {
  .prow { padding: var(--s2) 0; }
  .prow b { font-size: var(--t-body); }
  .pacts { margin-top: var(--s1); }
  .pacts .btn { min-height: 40px; }
}

/* ==== SHARED PANE PARTS (Aug 26, the operator's contract: "each thing has
   to use the same pieces, so it's all good"). Every pane row shape lives
   HERE, once. Parts declare their OWN colors — the white-on-white sheet row
   (a bare button inheriting the driver-side button color) is why. ==== */
.sheetrow { display: flex; width: 100%; justify-content: space-between;
  align-items: center; gap: var(--s2); padding: var(--s2); margin: 0;
  border: 0; border-bottom: 1px solid var(--edge-soft); border-radius: 0; background: none;
  text-align: left; cursor: pointer; color: var(--text, var(--ctl-ink));
  font-size: var(--t-row); font-weight: 400; }
/* the text half SHRINKS and wraps — otherwise a long route name shoves the
   mi-away chip off a phone's screen (caught on the RC 309 proof shots) */
.sheetrow > span:first-child { flex: 1 1 auto; min-width: 0; }
.sheetrow:hover { background: var(--ctl-bg); }
.sheetrow b { font-size: var(--t-row); font-weight: 600; color: var(--ctl-ink); }
.sheetrow small, .sheetrow .muted { color: #59636e; font-size: var(--t-meta); }
.sheetrow .go { color: #8b949e; }
.plist { border: 1px solid var(--edge-soft); border-radius: 6px;
  padding: 0 var(--s2); max-height: 40vh; overflow-y: auto; }
.plist .prow:last-child { border-bottom: 0; }
/* the day editor's facts line (RC 310b) — one quiet row of totals */
.defacts { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2);
  margin: 0 0 var(--s2); font-size: var(--t-row); }
.defacts .stat b { font-weight: 600; }
.defacts .okbox, .defacts .warn { font-size: var(--t-meta); padding: .1rem .5rem;
  border-radius: 6px; }
/* RC 310c: the popup's map — route line + floating pins wearing their fit */
.panemap { height: 40vh; min-height: 240px; border: 1px solid var(--edge-soft);
  border-radius: 6px; margin: 0 0 var(--s2); }
.fitok { color: #1a7f37; }
.fitbad { color: var(--err); }
.leaflet-popup-content button.fpin-add { width: auto; margin: .3rem 0 0;
  font-size: var(--t-meta); padding: .2rem .6rem; }
/* the FULL-WIDTH pane (ruled: "Let it cover the whole thing if it gives you
   room for the map. So long as you can ex out, and escape works") */
.kitlayer.panel.kitwide { width: 100vw; }
/* RC 331: the ROOM size — everything up to the sidebar. Defined from the
   nav's own width token, never a hand-tuned number; on phones the layer
   is a .sheet, so this rule simply never applies there. */
.kitlayer.panel.kitroom { width: calc(100vw - var(--nav-w)); }
/* RC 331: the PINHEAD part — a pane's filter/add block rides up with the
   scroll, stops at the top, and the list slides under it. Sticky against
   the pane's own scroller (.kitbody), so no height arithmetic anywhere. */
.pinhead { position: sticky; z-index: var(--z-drop); background: var(--pane, #fff);
  /* flush against the pane's true top: the scroller pins sticky children
     at its PADDING edge, so rise by exactly that token (RC 331b) */
  top: calc(-1 * var(--s2));
  padding: calc(.1rem + var(--s2)) 0 .35rem; }
/* RC 331b (operator: "The bottom one shouldn't be bigger than the top
   one"): the add row BORROWS the filter's input dress and the row-button
   dress — it has no look of its own, so it can never drift from them */
/* RC 331c (operator): the row sits HIGHER and the button is LEVEL with the
   boxes and the SAME HEIGHT — they share one control height, so they cannot
   drift apart again (the 3px gap they spotted). */
.paddrow { display: flex; align-items: center; gap: .5rem; margin-top: .25rem; }
/* an EXPLICIT shared height, not a minimum — a minimum let the box grow to
   42px while the button stayed 32px, which is the 3px-drift fault again in
   a bigger size (RC 334: things sharing a row share a height) */
.paddrow input, .paddrow button {
  height: var(--ctl-h); box-sizing: border-box; font-size: var(--t-row); }
/* margin:0 — a legacy 10px top margin on bare buttons was pushing this one
   5px below the boxes and stretching the row to 42px (RC 331c) */
.paddrow button { padding: 0 .8rem; margin: 0; }
.paddrow input { border: 1px solid var(--ctl-edge); border-radius: 6px;
  min-height: var(--ctl-h); padding: .25rem .6rem; font-size: var(--t-row); }
.paddrow input::placeholder { color: #8b949e; }
/* ⭐ RC 362: the pair used to stretch across the whole pane — flex 2 and
   flex 1 of everything available. The operator: "right now they're just
   taking up too much space. Try to bring them together by two thirds."
   They now take their own width and sit together on the left. */
.paddrow { justify-content: flex-start; }
.paddrow input[name="clname"] { flex: 0 1 19rem; min-width: 10rem; }
.paddrow input[name="reason"] { flex: 0 1 11rem; min-width: 7rem; }
.paddrow button { white-space: nowrap; }
@media (min-width: 900px) {
  /* RC 310c FINAL SHAPE, and the LAW it settled (operator: "If you're
     manually adjusting the heights, you're being stupid!!"): NO viewport
     arithmetic, NO hand-tuned heights, anywhere. The pane is a frame; the
     two columns FILL it; the map takes its share; each list takes whatever
     is left and scrolls alone. Every earlier calc() here caused a bug on
     a screen it wasn't tuned on.
     Chain: kitbody (fills the layer) > .dayedit (fills, flex row)
       > .deleft  (flex column: facts/buttons fixed, stop list flex:1,
                   add-a-stop fixed)
       > .panefloat (flex column: map takes half, floaters flex:1) */
  .kitlayer.kitwide .kitbody { overflow: hidden;
    display: flex; flex-direction: column;
    /* the missing link (operator: "Still not going to the bottom of the
       page"): the body must STRETCH to the layer, not size to content */
    flex: 1 1 0; min-height: 0; }
  .kitlayer.kitwide .kitbody > [data-kit-title] { flex: 1; min-height: 0;
    display: flex; flex-direction: column; }
  .kitlayer.kitwide .kitbody .dayedit { flex: 1; min-height: 0;
    display: flex; gap: var(--s3); align-items: stretch; }
  .kitlayer.kitwide .dayedit > .deleft { flex: 1 1 0; min-width: 0;
    display: flex; flex-direction: column; min-height: 0; }
  /* ONE scrolling middle zone: stops AND the removed block together
     (RC 312 — many removals must never squeeze the list) */
  .kitlayer.kitwide .deleft > .descroll { flex: 1 1 0; min-height: 0;
    overflow-y: auto; border: 1px solid var(--edge-soft); border-radius: 6px; }
  .kitlayer.kitwide .dayedit > .panefloat { flex: 1.1 1 0; min-width: 0;
    display: flex; flex-direction: column; min-height: 0; }
  .kitlayer.kitwide .panefloat > .panemap { flex: 1 1 50%; min-height: 0;
    height: auto; margin: 0 0 var(--s2); }
  .kitlayer.kitwide .panefloat > .floatscroll { flex: 1 1 50%; min-height: 0;
    overflow-y: auto; border-top: 1px solid var(--edge-soft); }
  /* the floater rows carry SIX cells (name·bt·mi·due+next·fit·add) — the
     shared five-column recipe wrapped their Add button onto a second line.
     The due+next cell carries the LONGEST text, so it gets the most spare
     room (it was trimming while the row had slack); tight cells size to
     content; name and due+next ellipsize with hover titles */
  /* N2: and SEVEN here — the marks cell arrived on this list too, because
     the route pane's neighbour rows now say why every person is floating,
     not only a biweekly's (the map, question 9). */
  .floatscroll .prow { grid-template-columns:
    minmax(6rem, 1fr) max-content auto auto minmax(9rem, 1.6fr) auto auto;
    gap: var(--s2); }
}
/* RC 325: the legs row — each stretch between reloads shows itself */
.legrow .stat b { color: #92400e; }
.legrow .stat.legbad, .legrow .stat.legbad b { color: var(--err); font-weight: 700; }
/* RC 325: the click-to-edit stop number */
#stoplist .n { cursor: pointer; }
#stoplist .n input.numedit { width: 3.2rem; margin: 0; padding: .1rem .3rem;
  font-size: inherit; border: 1px solid var(--accent); border-radius: 4px; }
/* RC 323: the title bar's truck dropdown — quiet, beside the title */
.kithead .kitx { margin-left: auto; }   /* the close button holds the right edge */
/* ⭐ RC 357: the DAY dropdown is the twin of the truck one and wears the
   same clothes. Without width:auto a select stretches to fill the title
   bar — which is what it did on its first outing. */
.kithead select.retruck, .kithead select.reday { width: auto; max-width: 14rem;
  margin: 0 var(--s2) 0 0;
  font-size: var(--t-row); padding: .2rem .5rem;
  border: 1px solid var(--ctl-edge); border-radius: 6px; background: var(--ctl-bg); }
/* RC 321: the mid-route reload row — the warehouse as a stop in the list */
.stop.reloadrow { background: #fff8ec; }
.stop.reloadrow .who b { color: #92400e; }
/* RC 320: the pick sheet's start-a-new-route row — one quiet line */
.newroute { display: flex; gap: var(--s2); align-items: center;
  margin-top: var(--s2); flex-wrap: wrap; }
.newroute input[type=date], .newroute select { width: auto; font-size: var(--t-row);
  padding: .3rem .5rem; border: 1px solid var(--ctl-edge); border-radius: 6px; }
/* the pane's doors row (RC 310a — the dead sidebar's three doors) */
.panefoot { display: flex; flex-wrap: wrap; gap: var(--s2); margin-top: var(--s2); }
.pfdoor { display: inline-block; width: auto; margin: 0; padding: .35rem .85rem;
  font-size: var(--t-row); font-weight: 500; border: 1px solid var(--ctl-edge);
  border-radius: 6px; background: var(--ctl-bg); color: var(--ctl-ink); text-decoration: none; }
.pfdoor:hover { background: var(--ctl-hover); }
.pfdoor.main { background: var(--blue, var(--accent)); border-color: var(--blue, var(--accent)); color: #fff; }
.pfdoor.main:hover { filter: brightness(1.07); background: var(--blue, var(--accent)); }

/* RC 339: HOW FAR a floater is, as a filled circle matching the map pin —
   green within 3 miles of the route, orange to 8, red beyond. The colours
   sit on a DISTANCE, never on lateness (which owns red on the same row). */
.fdot { display: inline-block; width: .62rem; height: .62rem; border-radius: 50%;
  margin-right: .38rem; vertical-align: baseline; }
.fdot.near { background: #1a7f37; }
.fdot.mid  { background: #d4820a; }
.fdot.far  { background: var(--err); }

/* RC 324: the DAY picker beside the truck dropdown in the popup title —
   same dress, so the two read as one pair of controls. */
.reday { font: inherit; font-size: 14px; height: var(--ctl-h); box-sizing: border-box;
  border: 1px solid var(--ctl-edge); border-radius: 6px; padding: 0 .4rem; background: #fff; }

/* RC 406: the contact list's checked buttons and row shape.
   RC 408: ckon RECOLORS ONLY — size comes from the same classes as the
   unticked button, so ticked never grows. Fully-done rows fold (billing's
   pattern); the day's "N done ▸" shows them. */
.btn.ckon { background: #e7f6ec !important; border-color: #1a7f37 !important;
  color: #14622b !important; }
.ckrow.ckdone { display: none; }
.ckday.open .ckrow.ckdone { display: grid; }
/* the billing fold class is a fixed 1.4rem square (one ▸) — this one
   carries words, so it sizes to them (the operator: "bad" on the wrap) */
button.fold.ckdayfold { width: auto; height: auto; padding: .1rem .55rem;
  margin-left: .5rem; font-size: var(--t-meta); white-space: nowrap;
  display: inline-block; vertical-align: baseline; line-height: 1.4; }
@media (min-width: 900px) {
  .prow.ckrow { grid-template-columns: minmax(10rem, 1.6fr) 10rem
    minmax(9rem, 1fr) 16rem; }
}

/* ============================ THE SHARED BLOCKS' OWN STYLES ================
   Added Sep 2 with the light-page conversions. These belong to macros in
   _parts.html, so a page never writes them and never drifts from them. Each
   one replaced an inline style="" that had already drifted across pages —
   three different top margins for the same toolbar, two different card
   headings. Sizes come off the ladder so one dial still moves everything. */
.cardtitle { font-size: var(--L-h2, 20px); margin: 0 0 .4rem; }
.card-alert { border-left: 4px solid #b91c1c; }
.toolbar-sp { margin-top: .5rem; }
.breakall { word-break: break-all; font-weight: 700; }

/* The customer page's edit form — these six were inline style="" attributes
   with three different widths for the same kind of number box. Named once
   here so the whole form moves together (RC 292 keeps its ONE-form shape). */
.fieldrow { display: flex; flex-wrap: wrap; gap: 1rem; align-items: end; }
.checkline { display: flex; gap: .4rem; align-items: center; padding-bottom: .3rem; }
.blockfield { display: block; margin-top: .8rem; }
.fullwidth { width: 100%; }
.alignright { text-align: right; }
.numbox { width: 6rem; }
/* ⭐ A GRID, NOT A FLEX ROW. There are THIRTEEN bottle types, and as flex
   items each label claimed the width of its own words — 300px for "Distilled
   (cases)" — so three fitted per row and the block stood 631px tall, taller
   than the popup it lives in. A fixed grid puts them in a couple of tidy rows
   wherever they appear, and RC 292 still holds: every one is VISIBLE, none is
   collapsed behind anything. */
.qtyrow { display: grid; gap: .5rem .7rem;
          grid-template-columns: repeat(auto-fill, minmax(6.5rem, 1fr)); }
.qtyrow label { min-width: 0; font-size: var(--L-meta, 12.5px); }
.qtybox { width: 100%; max-width: 5.2rem; }
.orderlead { margin: .9rem 0 .2rem; }
.savelead { margin-top: .9rem; }

/* ══ THE POPUP, on the browser's own <dialog> (parts.dialog + kit.js) ══════
   One definition for every local popup on the site. Panel-ish on a desk,
   a bottom sheet on a narrow screen — the same rule the fetched layers
   follow, so the two mechanisms look like one thing to a person. */
dialog.pop {
  border: 0; border-radius: 14px; padding: 0; max-width: 34rem; width: calc(100% - 2rem);
  box-shadow: 0 18px 50px rgba(0,0,0,.28); color: inherit; background: #fff;
}
dialog.pop.popwide { max-width: 52rem; }
dialog.pop::backdrop { background: rgba(15,23,42,.45); }
dialog.pop .pophead {
  display: flex; align-items: center; gap: .75rem; justify-content: space-between;
  padding: .7rem .9rem; border-bottom: 1px solid #e5e7eb; margin: 0;
}
dialog.pop .pophead h2 { margin: 0; font-size: var(--L-h2, 20px); }
dialog.pop .popbody { padding: .9rem; max-height: 70vh; overflow: auto; }
@media (max-width: 640px) {
  dialog.pop {
    width: 100%; max-width: none; margin: auto 0 0; border-radius: 14px 14px 0 0;
  }
  dialog.pop .popbody { max-height: 78vh; }
}

/* the add-a-delivery form inside the browser <dialog> — the pieces that
   used to hang off .daddpop and would otherwise fall off the new popup */
dialog.pop .baddrow { display: flex; gap: .6rem; flex-wrap: wrap; margin: .4rem 0; }
dialog.pop .baddsave { width: 100%; }
dialog.pop .searchwrap { position: relative; }
dialog.pop .results { list-style: none; margin: .25rem 0 0; padding: 0;
  max-height: 14rem; overflow: auto; }
dialog.pop .results .stop { padding: .4rem .5rem; border-radius: .45rem; cursor: pointer; }
dialog.pop .results .stop:hover { background: #eef3fb; }
dialog.pop .peekbody .pk { display: flex; justify-content: space-between;
  gap: 1rem; padding: .3rem 0; border-bottom: 1px solid #f0f2f5; }
dialog.pop .peekbody .pklab { color: var(--dim); font-weight: 600; }
dialog.pop .peekbody .pkval { text-align: right; }
dialog.pop .peekfoot { margin-top: .7rem; }
/* the panes' two lead lines — were inline style= on three fragments */
.cklead { margin: .1rem 0 .5rem; }
.flushtop { margin-top: 0; }
.dayslead { margin-top: .3rem; }

/* ══ ⭐ THE .btn COLLISION — RC 76's family, caught by the pixel machine ═════
   Sep 2. This project's OWN .btn (line ~166) means "the driver's big tap
   slab": display:block, width:100%, 1.25rem type, 1rem padding. Bootstrap's
   .btn means "a button". app.css loads AFTER Bootstrap, so ours won — and
   every shared button on a CONVERTED page came out 78px tall on a phone.
   Five pixel-machine failures on three pages, and the operator would have
   seen giant buttons on his phone.

   ⭐ THE RULE: on a converted page (body.ladder) .btn means what BOOTSTRAP
   means. The driver's pages are NOT converted, carry no .ladder, and keep
   their slabs untouched — which is the whole point of scoping the ladder.
   These sit at the END of the file so they also beat the ≥600px and ≥1000px
   media blocks, which restyle .btn at the same specificity. */
/* ⛔⛔ AND THE BORDER READS THE VARIABLE, NOT THE WORD `transparent`. RC 664,
   Sep 10: the operator, looking at the card, said the contact row's "Save" and
   "Remove" render as bare centred text instead of controls (RC 64: an in-page
   action looks like an app control, never a row of text links). The macro was
   not the fault and neither was `quiet` — parts.button(kind="quiet") asks for
   Bootstrap's `btn-outline-secondary`, which sets --bs-btn-border-color to
   #6c757d and nothing else. THIS RULE hard-coded `transparent` while copying
   Bootstrap's geometry, and at (0,2,0) it outranks Bootstrap's own
   `.btn { border: … var(--bs-btn-border-color) }` — so EVERY quiet and every
   outlined-danger button on EVERY converted page has been borderless grey
   text since Sep 2. MEASURED in the browser before the fix: computed
   border-top-color rgba(0, 0, 0, 0) on a .btn-outline-secondary.
   ⭐ `transparent` IS STILL THE DEFAULT — Bootstrap's own .btn base sets
   --bs-btn-border-color: transparent, so a plain <button class="btn"> is
   unchanged and only the variants that ask for a border get one back. */
body.ladder .btn {
  display: inline-block; width: auto; text-align: center;
  font-size: var(--L-row, 14px); font-weight: 600;
  padding: .375rem .75rem; margin: 0 .35rem .35rem 0;
  border-radius: .375rem;
  border: 1px solid var(--bs-btn-border-color, transparent);
}
/* ...and the pointer state follows the same variable, or the border would
   freeze at its resting colour while the face changed under it. */
body.ladder .btn:hover { border-color: var(--bs-btn-hover-border-color, transparent); }
body.ladder .btn-sm { font-size: var(--L-meta, 12.5px); padding: .25rem .5rem; }
body.ladder .btn-lg { font-size: var(--L-body, 16px); padding: .5rem 1rem; }
body.ladder .btn-link { padding-left: 0; padding-right: 0; border-color: transparent; }
body.ladder .btn small { font-weight: 500; font-size: .9em; }
/* ⭐ Sep 9 (audit item 5): THE SLAB HAS ITS OWN NAME NOW — .slab — so .btn
   means only what Bootstrap means and the collision above is over. A slab
   that appears on a DESK page (the confirm boxes' Yes/No, the warehouse pair)
   used to borrow this ladder geometry through the shared name; it keeps it
   here by its new name, so nothing on a desk screen moved with the rename. */
body.ladder .slab {
  display: inline-block; width: auto; text-align: center;
  font-size: var(--L-row, 14px); font-weight: 600;
  padding: .375rem .75rem; margin: 0 .35rem .35rem 0;
  border-radius: .375rem; border: 1px solid transparent;
}
body.ladder .slab small { font-weight: 500; font-size: .9em; }
/* the break tap moved OUT of the page corner and INTO the menu (Sep 2 —
   "It's too intrusive where it is"). In the menu it is an ordinary row like
   the clock, so the fixed-corner geometry must not follow it in. */
.sfoot .breakform { position: static; margin: 0; }
.sfoot .breakbtn { width: 100%; border-radius: .5rem; font-size: 1rem; }
.loadtot { margin-top: 1rem; }

/* the confirm popup — BOTH answers are real buttons, side by side and the
   same size (operator, Sep 2: "Both yes and no should be normal buttons").
   No was the popup's corner ✕ and read as an afterthought; it is the SAFE
   answer and has to be as easy to hit as Yes. */
dialog.pop.confirmpop { max-width: 22rem; }
dialog.pop.confirmpop .confirmq { margin: 0 0 1rem; font-size: var(--L-h2, 20px); }
dialog.pop.confirmpop .confirmrow { display: flex; gap: .6rem; margin: 0; }
dialog.pop.confirmpop .confirmrow > * {
  flex: 1 1 0; width: auto; margin: 0; text-align: center;
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 2.75rem; border-radius: .6rem; font-weight: 700;
}
/* the warehouse's Yes/No pair. ⚠️ The flex rule for .confirmrow is scoped to
   inside a dialog, and this pair lives ON the page — so it needs its own
   geometry. And the base .btn is text-align:left (the driver's slab), which
   left "Yes" hanging at the edge while "No" sat centred: the operator asked
   for both in the middle, so both are centred here. */
.answerrow { margin: .4rem 0 .2rem; display: block; }
.answerrow form { margin: 0 0 .4rem; }
.answerrow .slab { width: 100%; margin: 0; text-align: center; }
.answerrow .slab.on { outline: 3px solid var(--blue, var(--accent)); outline-offset: 2px; }
/* the warehouse row closes every schedule — it is a place, not a delivery */
.stop.warehouserow { background: #f3f6fa; }
.stop.warehouserow .who b { color: #1f2b3a; }

/* ⭐ THE SEED POPUP'S THREE OUTLINE ACTIONS (converted Sep 3). They were
   three inline style= attributes on .go buttons — the same shape three
   times, in amber, blue and green. Same declarations to the byte, so the
   popup looks exactly as it did; the point is only that a control's look
   now lives in the stylesheet where it can be changed once. */
.go.go-back { background:#fff; color:#b45309; border:1.5px solid #b45309; }
.go.go-pin  { background:#fff; color:#1156d6; border:1.5px solid #1156d6; }
.go.go-move { background:#fff; color:#14622b; border:1.5px solid #14622b; }

/* ═══ A DESK CONTROL IS DESK-SIZED ═════════════════════════════════════════
   ⭐⭐ THE ONE THAT COST A WHOLE AFTERNOON, Sep 3. The site's DEFAULT input
   sizing (app.css line ~132) is the DRIVER'S: 1.15rem text, .8rem padding, a
   2px grey border, width 100%. That is right for a thumb on a phone in a van
   and wrong for every manager screen — so each new desk screen started out
   finger-sized and had to be argued back to normal, control by control. The
   "minutes at the stop" box came out 291px wide; thirteen bottle boxes stood
   631px tall, taller than the popup holding them.

   Operator, Sep 3: *"I want implementing and changing things to be as easy and
   cheap as possible. That's the whole point of this."*  So the desk gets its
   sizing ONCE, here, in the ladder's own tokens — and the next desk screen
   costs nothing.

   ⚠️ THE SELECTOR IS THE WHOLE POINT: `ladder` marks a desk page, but login,
   welcome and the timeclock carry `tapui ladder` because a thumb uses them
   too. Desk = ladder AND NOT tapui. Every driver screen is untouched. */
body.ladder:not(.tapui) input[type=text],
body.ladder:not(.tapui) input[type=number],
body.ladder:not(.tapui) input[type=search],
body.ladder:not(.tapui) input[type=date],
body.ladder:not(.tapui) select,
body.ladder:not(.tapui) textarea {
  font-size: var(--L-row, 14px); padding: .3rem .55rem;
  border: 1px solid var(--line, var(--line)); border-radius: .45rem;
}
body.ladder:not(.tapui) input[type=text],
body.ladder:not(.tapui) input[type=number],
body.ladder:not(.tapui) input[type=search],
body.ladder:not(.tapui) input[type=date],
body.ladder:not(.tapui) select { height: var(--L-ctl, 32px); }
body.ladder:not(.tapui) label { font-weight: 600; margin: .5rem 0 .2rem; }
body.ladder:not(.tapui) .numbox { width: 6rem; }
body.ladder:not(.tapui) .qtybox { width: 100%; max-width: 4.6rem; }
body.ladder:not(.tapui) .fullwidth { width: 100%; }

/* ═══ THE CUSTOMER AND PLACE POPUPS ═════════════════════════════════════════
   ⛔ RC 483 — A POPUP DOES NOT SCROLL. The default .popbody above does
   (max-height 70vh, overflow auto) and every older popup depends on it, so
   these two turn it off for themselves rather than restyling the whole site:
   the content fits, and where a list genuinely cannot (a campus with 13
   places, a place with 40 deliveries) THE LIST scrolls inside its own box. */
dialog.pop .cuspop, dialog.pop .placeform { display: flex; flex-direction: column; }
/* ⭐ ON A SMALL SCREEN THE TWO COLUMNS STACK and the card is genuinely taller
   than the window — a phone cannot hold a customer's whole card at once. So
   the CONTENT scrolls inside the card; the popup itself still never does
   (RC 483). Same move as the places list, one level up. */
.cuspop { min-height: 0; flex: 1; display: flex; flex-direction: column; }
/* the CARD scrolls when a customer really is long; the popup never does */
.cusgrid { overflow: auto; min-height: 0; flex: 1; align-content: start; }
dialog#cuspop .popbody { flex: 1; min-height: 0; display: flex;
                         flex-direction: column; }
dialog#cuspop .popbody, dialog#placepop .popbody,
dialog#newpop .popbody { overflow: visible; max-height: none; }
/* the customer card carries a FIVE-COLUMN editable table beside a side
   column; at the standard 52rem the place names wrapped to four lines */
/* ⭐⭐ THE CARD FILLS THE SPACE BESIDE THE SIDEBAR, like the calendar's pane.
   Operator, Sep 3: *"the popup is covering the side bar. Just make it fill the
   space next to the side bar like on the calendar page. Also ... This is not
   functional. It's too small."*
   ⭐ THE TWO COMPLAINTS HAVE ONE FIX. A centred 68rem box left the tab panels
   so short that the standing order — thirteen boxes — became a tiny window
   with scrollbars on both edges. Filling the space gives every panel real
   room, AND makes the popup a FIXED size, which is what stops it jumping
   between tabs — so the forced panel height that caused the squeezing is not
   needed at all. */
/* ⛔ [open] IS LOAD-BEARING. A closed <dialog> is display:none by the
   browser's own rule; setting display:flex unconditionally OVERRODE that, so
   an invisible full-height card sat over the whole list and swallowed every
   click on it. Found by the picture script failing to click a row —
   "<div class=popbody> ... intercepts pointer events" — which is the thing
   itself refusing, where no string check would have noticed. */
dialog#cuspop.pop[open] {
  max-width: none; width: auto; height: auto; margin: 0; border-radius: 0;
  /* ⛔ LONGHAND, NOT `inset` — the suite bans the shorthand because older iOS
     drops it, and the driver's phone is the reason that rule exists. */
  /* ⭐⭐ THE CARD COVERS THE WHOLE PAGE, MENU INCLUDED — RC 663, Sep 10, on
     seeing it at 1170 with the menu still beside it: *"On the calendar, we
     have a popup that blocks the sidebar, and I'm totally ok with it,
     because having that space is useful, and the sidebar is just an escape
     away."*  The day popup already worked this way; the card now matches it.
     ⛔ AND THIS IS WHY IT IS `0` AND NOT `var(--nav-w)`: reading the menu's
     width made the card DEPEND on the menu still being a column, and below
     1100px it is a drawer instead — which left a dead 200px strip with the
     page showing through (RC 664 fault 1). A card that starts at the edge
     cannot have that fault at any width. --nav-w is still reset for
     narrowmenu because .kitroom reads it too. */
  position: fixed; top: 0; right: 0; bottom: 0; left: 0;
  max-height: none; display: flex; flex-direction: column;
}

.cusgrid { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
           gap: var(--L-s4, 24px); }
@media (max-width: 720px) { .cusgrid { grid-template-columns: minmax(0, 1fr); } }
.cusmain, .cusside { display: flex; flex-direction: column; min-width: 0; }
.pklab.spaced { margin-top: var(--L-s3, 16px); }

.locbox { border: 1px solid var(--line, var(--line)); border-radius: .6rem;
          margin-top: var(--L-s2, 8px); overflow: hidden; }
/* the list scrolls, the popup does not */
.locbox .scrollbox { max-height: 46vh; overflow: auto; }
.lochead { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap;
           padding: .5rem .7rem; background: #f2f6fb;
           border-bottom: 1px solid var(--line, var(--line)); font-size: var(--L-row, 14px); }
.lochead + .lochead { border-top: 1px solid var(--line, var(--line)); }
/* the agreed five columns: Place · Its own address · Every · What they take ·
   (open). The row IS the editor, so a form wears this same shape. */
.strow { display: grid;
         grid-template-columns: minmax(0, 1.6fr) 7rem 8.5rem minmax(0, 1.5fr) 4.5rem;
         gap: .6rem; align-items: center; padding: .35rem .7rem; margin: 0;
         border-bottom: 1px solid var(--hair); font-size: var(--L-row, 14px); }
.strow.head { background: #f7f9fb; border-bottom: 1px solid var(--line, var(--line)); }
.strow .qsel { width: 100%; }
.strow .skus { display: flex; gap: .35rem; align-items: center; flex-wrap: wrap; }
.strow .sku { display: inline-flex; align-items: center; gap: .25rem;
              color: #157a3b; font-weight: 600; white-space: nowrap; }
.strow .sku .qty { width: 3.2rem; }
.addrow { display: flex; gap: .5rem; align-items: center; padding: .45rem .7rem;
          border-top: 1px solid var(--hair); flex-wrap: wrap; }
.addrow input { flex: 1 1 9rem; min-width: 0; }
/* a control never wraps mid-word — "Save the / address" over two lines reads
   as two things. The boxes give up the room instead. */
.addrow .btn, .addrow a { white-space: nowrap; flex: 0 0 auto; }
.lochead.addrow { border-top: 0; }
.lochead.solo { border-radius: .5rem; border: 1px solid var(--line, var(--line));
                margin-bottom: var(--L-s3, 16px); }
.popfoot form { margin: 0; }
.strow:last-child { border-bottom: 0; }
.strow.off { background: #fbfcfd; }
.plink { text-align: left; }

.popfoot { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap;
           margin-top: var(--L-s3, 16px); }
.offnote { font-size: var(--L-meta, 12.5px); }
.offform { margin-top: var(--L-s2, 8px); padding-top: var(--L-s2, 8px);
           border-top: 1px solid var(--hair); display: flex; gap: .6rem;
           align-items: center; flex-wrap: wrap; }

/* ⭐ A LABEL THAT IS THERE FOR A SCREEN READER AND NOT FOR THE EYE. Several
   controls on the site are obvious to look at and nameless to anything that
   cannot see them — a search box, a role picker, an icon button. This gives
   them a real name without changing the picture. Clip, never display:none: a
   hidden label is not read out at all. */
.vh { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
      overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%);
      white-space: nowrap; border: 0; }
/* ⭐ A TAB STOP THAT DOES NOT SHOW ITSELF IS NOT USABLE. The rows that open a
   customer or a building are now reachable by keyboard, so they have to say
   where the keyboard IS. :focus-visible means a mouse click never draws it. */
tr[tabindex]:focus-visible, [data-pop]:focus-visible, [data-pane]:focus-visible {
  outline: 2px solid var(--brand, #2563c9); outline-offset: -2px; }
/* ⭐ A GROUP OF CHOICES IS ONE QUESTION, AND IT LOOKS EXACTLY AS IT DID. The
   radios had no group, so nothing said what six loose buttons were answering.
   A <fieldset> says it — and a browser draws one with a border and its own
   padding, so this strips all of that: the markup gains a meaning, the screen
   gains nothing. */
.plainset { border: 0; margin: 0; padding: 0; min-width: 0; }
.plainset > legend { padding: 0; }
.plainset > legend > h2 { margin: 0 0 .4rem; }

/* ⭐⭐ NOTHING A THUMB HITS IS SMALLER THAN --tap. The file has declared that
   token twice for months (:80 and :1798, "nothing tappable on a phone is
   smaller") and then shipped a 22px fold, a 27px clock-fix and a 30px + —
   including the + that BUILDS A ROUTE and the ! that fixes a clock time, which
   are two of the hardest things on the site to hit with a thumb (audit, Sep 8).
   ⭐ SCOPED TO THE PHONE ARRANGEMENT AND THE DRIVER'S PAGES, because these
   controls are deliberately dense on a desk and a mouse is not a thumb. That
   is this project's own rule about defaults: a default true for one role is
   scoped to that role the day it is written.
   ⚠️ min-width/height, never width — the box may grow, it may not shrink.
   ⛔ AND THE FIRST VERSION OF THIS RULE SET A min-width TOO, ON EVERY BUTTON —
   which turned the small ✕ beside a stop into a 44px square, squeezed the row
   and TRUNCATED THE CUSTOMER'S NAME ("Rocky Poin…"), breaking RC 89. Caught in
   the deploy's own picture. It is the same fault as the bar above: a blanket
   rule assuming room that a dense row does not have.
   ⭐ SO: HEIGHT EVERYWHERE — that is the reach a thumb needs and it costs no
   horizontal room — and a full 44×44 only on the standalone glyph controls the
   audit actually named, which sit alone and have the room. */
/* ⭐ THE FLOOR IS THE SIZE OF A CONTROL THAT ALREADY WORKS. The operator, Sep 8:
   *"Those three lines are big enough to drag the stop, so why can't the x be
   that small too?"*  The drag handle he means measures 18 × 28 and the driver
   drags stops with it all day, so 28 is not a guess about thumbs — it is this
   site's own evidence. 44 was the textbook number; this one was earned here. */
body.phonearr button, body.phonearr [role="button"],
body.tapui button, body.tapui [role="button"] { min-height: 28px; }
body.phonearr .cellfold, body.phonearr .calplus, body.phonearr .clockfixbtn,
body.phonearr .bfx, body.phonearr .daddplus, body.phonearr button.fold,
body.tapui .cellfold, body.tapui .calplus, body.tapui .clockfixbtn,
body.tapui .bfx, body.tapui .daddplus, body.tapui button.fold {
  min-width: var(--tap, 44px); }

/* ⭐ THE SKIP LINK: out of sight until the keyboard reaches it, then the first
   thing on the page. It must be reachable, not display:none. */
.skiplink { position: absolute; left: -9999px; top: 0; z-index: var(--z-skip);
            background: var(--brand, #2563c9); color: #fff; padding: .55rem .9rem;
            border-radius: 0 0 var(--r-ctl, 6px) 0; text-decoration: none; }
.skiplink:focus { left: 0; }
#wrap:focus { outline: none; }

/* ⭐ MOTION IS A PREFERENCE, NOT A DECISION WE MAKE FOR PEOPLE. The sheets and
   drawers slide; for somebody who gets motion sick, that is the difference
   between using the site and not. One block, every animation on the site. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important; animation-iteration-count: 1 !important;
    transition-duration: .001ms !important; scroll-behavior: auto !important; }
}

/* a picker and its button on one line — choosing and saving are two acts.
   ⚠️ the width and margin are needed because this project's own rules make a
   bare <button> a full-width block (the .btn collision the audit names); a
   flex row cannot beat that on its own. */
.inlinepick { display: flex; gap: .4rem; align-items: center; flex-wrap: wrap; }
.inlinepick button { width: auto; margin: 0; }
.inlinepick select { width: auto; }

/* ⭐ THE LAST INLINE style= ATTRIBUTES, NAMED (audit item 4, Sep 9). Seventeen
   templates lines carried a style="" of their own; each is a class here now,
   so the look has one home and the markup says WHAT the thing is. */
.okbox.daydone { margin: .2rem 0 .4rem; }                 /* day.html */
.slab.drivecard { display: block; text-align: center; padding: .9rem; }   /* drive.html */
.pintro { margin-top: 0; }                                /* the floaters pane's first line */
.duebad { color: var(--err); }                               /* a late due-word, plain text */
.parea .pareanote { font-weight: normal; text-transform: none; letter-spacing: 0; }
.qtyrow { display: flex; flex-wrap: wrap; gap: .7rem; }   /* plan_qty */
.qtyrow input { width: 5.2rem; }
.qtysave { margin-top: .9rem; }
.mapfilling { padding: .3rem .5rem; }                     /* the route pane's map stub */
.countrow input.wide { width: 100%; }                     /* the pin card's two boxes */
.pinfoot.stacked { flex-direction: column; align-items: stretch; }   /* the ✕ chooser */

/* ⭐ THE TABS. A tab that does not change what is on screen is a lie about the
   shape of the page — this is what makes them honest (RC 483). */
.ptabs { display: flex; gap: 2px; border-bottom: 1px solid var(--line, var(--line));
         margin-bottom: var(--L-s3, 16px); }
.ptab { border: 0; background: none; font: inherit; color: var(--dim);
        padding: 0 .8rem; height: 34px; border-bottom: 2px solid transparent;
        cursor: pointer; }
.ptab.on { color: #16202b; font-weight: 700; border-bottom-color: var(--brand, #2563c9); }

/* ⭐ THE STRIP THAT SCROLLS SIDEWAYS INSTEAD OF WRAPPING (RC 602, parts.tabstrip).
   MEASURED first, in the drawing: Sheppard Pratt's 13 station tabs wrapped to
   THREE rows — 92px of labels and nothing else — which is what sent the design
   here. One row, the wheel scrolls it (kit.js), and a fade at each end appears
   only when something really is past that edge.
   ⚠️ THE FADE COLOUR MUST MATCH WHAT IS BEHIND THE STRIP or it reads as a
   smudge. It is a variable so a host that sits its tabs on a band says so once
   rather than every strip guessing. */
.tabscroll { position: relative; --tabfade: #fff;
             border-bottom: 1px solid var(--line, var(--line)); }
.tabscroll::before, .tabscroll::after {
  content: ""; position: absolute; top: 0; bottom: 0; width: 34px; z-index: var(--z-rise);
  pointer-events: none; opacity: 0; transition: opacity .15s; }
.tabscroll::before { left: 0;
  background: linear-gradient(90deg, var(--tabfade), transparent); }
.tabscroll::after { right: 0;
  background: linear-gradient(270deg, var(--tabfade), transparent); }
.tabscroll.more-left::before, .tabscroll.more-right::after { opacity: 1; }
/* the strip inside gives up its own border — the wrapper draws it now, so the
   line runs the full width and does not scroll away with the tabs. */
.tabscroll > .ptabs { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none;
                      border-bottom: 0; margin-bottom: 0; }
.tabscroll > .ptabs::-webkit-scrollbar { display: none; }
.tabscroll .ptab { flex: none; white-space: nowrap; }
/* ⭐ THE SAME TABS ON A PAGE, not only in a popup (RC 536, My account). Only
   the spacing differs: a popup's own padding sits the strip off its edges,
   and a page has to ask. The strip, the switcher and the look are shared —
   two tab mechanisms would drift, and one of them would be the lie. */
/* ⭐ THE TIMECLOCK LIST — one line per day (RC 536). It was a card per day
   with every time printed twice, once as text and once in the box below it.
   The boxes ARE the display now, so a week is seven lines instead of a
   scroll. It wraps rather than spills on a phone: the day keeps its line and
   the times drop beneath it. */
.clockdays { border-top: 1px solid var(--hair); }
.clockday { display: flex; align-items: center; flex-wrap: wrap; gap: .45rem;
  margin: 0; padding: .4rem 0; border-bottom: 1px solid var(--hair); }
.clockday .cday { flex: 1 1 8rem; min-width: 0; font-weight: 600; }
/* ⛔ THE WIDTH NEEDS THE LADDER'S OWN SPECIFICITY TO BEAT IT. A plain
   `.clockday .ctime` loses to `body.ladder:not(.tapui) .numbox { width: 6rem }`
   above, and two 6rem boxes plus the day pushed Save onto a second line —
   which is the card fault in miniature: one day, two lines. */
body.ladder .clockday .ctime, .clockday .ctime {
  width: 4.6rem; flex: none; text-align: center; }
.clockday .cto { color: var(--dim); }
.clockday .csave { flex: none; margin: 0; }
.clockday .ctapped { color: var(--dim); font-size: var(--fs-label); }
/* ⛔ ON A PHONE THE ROW MUST BREAK IN THE RIGHT PLACE. Left to itself it put
   the day and the FIRST time on line one and started line two with the word
   "to", which reads as a fragment. The day takes the whole first line and the
   times stay together on the second — the break is chosen, not accepted.
   ⚠️ Flexbox breaks lines BEFORE it shrinks anything, so this is a basis
   change (100%), not a width one. */
body.phonearr .clockday .cday { flex: 0 0 100%; }

.acctabs { margin-top: var(--L-s3, 16px); }
.acctabs .ptabs { margin-bottom: 0; }
.acctabs .ppanel[data-on] { padding-top: var(--L-s3, 16px); }
/* ⭐ ONE HEIGHT FOR EVERY TAB. The panels are 461, 287, 112 and 101 px tall;
   without this the popup would leap about as you click between them, which is
   the kind of thing a still picture never shows and a person feels at once. */
/* ⛔ AND THE HEIGHT IS CAPPED BY THE WINDOW, NOT JUST BY THE TALLEST TAB.
   A flat 27rem held every tab steady on a big screen and pushed the whole
   popup past the edge on a phone and on a short window — so the POPUP scrolled,
   which is the forbidden thing wearing a different hat. Caught by the popup
   gate the moment it was armed (RC 488), on my own work, before it shipped.
   min() keeps the tabs steady where there is room; max-height + overflow puts
   the scrolling INSIDE the panel where it belongs. */
/* ⛔ NO FORCED PANEL HEIGHT ANY MORE. It existed to stop the popup jumping
   between tabs, and it was the thing squeezing the standing order into a box
   with scrollbars on two edges. A popup that FILLS ITS SPACE cannot jump, so
   the panel simply takes the room it needs and the card scrolls if the whole
   thing is long. The cure had become worse than the disease. */
.placeform .ppanel { min-height: 0; }
.placeform .nameflex { flex: 1 1 22rem; }
.ppanel { display: none; }
.ppanel[data-on] { display: block; }
/* the History tab still holds the SAME height as the others — its min-height:0
   used to win over the steady height above, so opening History shrank the
   popup and re-centred it under the cursor. The list scrolls inside instead. */
.ppanel.scrollpanel[data-on] { display: flex; flex-direction: column; }
/* the panel already holds the height and does the scrolling, so the box
   inside it must not fight for a second one */
.placeform .scrollbox { flex: 1 1 auto; max-height: none; }
.locbox .scrollbox { max-height: none; }
.scrollbox { overflow: auto; min-height: 0; max-height: 46vh; }
.ctable.slim th, .ctable.slim td { padding: .35rem .55rem; font-size: var(--L-row, 14px); }
.cusrow { cursor: pointer; }
.copyable.strong { font-weight: 700; }

/* the place's opening hours, one line a day, in the Getting in tab */
.hourslist { display: grid; grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
             gap: .2rem .8rem; margin-top: .4rem; font-size: var(--L-row, 14px); }

/* ⭐ WHEN THEY ARE OPEN — seven days, each with a closed box and up to TWO
   windows, because two places really do open again after lunch. */
.hourhead, .hourrow { display: grid;
  grid-template-columns: 3.5rem 4rem repeat(4, minmax(0, 7rem));
  gap: .5rem; align-items: center; padding: .25rem 0; }
.hourhead { border-bottom: 1px solid var(--line, var(--line)); padding-bottom: .35rem;
            margin-bottom: .35rem; }
.hourrow input[type=checkbox] { width: 1rem; height: 1rem; justify-self: start; }
.contactrow { display: grid;
  grid-template-columns: minmax(0, 1fr) 9rem 9rem minmax(0, 1fr) auto auto;
  gap: .5rem; align-items: center; padding: .3rem 0; margin: 0; }

/* ══════════════════ THE CUSTOMER CARD (RC 646) ══════════════════════════════
   Built Sep 10 from the drawing of record (deliverables/customer_card_mockup
   .html — the two "Their pick" cards and the two "Simple" ones) against the
   site's own tokens. THE DRAWING IS A SNAPSHOT, NEVER A SOURCE (RC 171): its
   measurements are copied, its stylesheet is not.

   ⭐ THE SHAPE (RC 601/146): the card IS the dialog's height. Its head, its
   tabs and its foot are fixed and only the boxes in the middle scroll — the
   card never scrolls, the page never scrolls, and switching a tab changes what
   is inside the card, never the card.

   ⭐ THE DENSITY IS A CARD-LOCAL DIAL, not a change to the site's ladder. The
   desk root is 17px and the site's rows are 14px; this card is drawn at 13px
   rows and 26px controls, because it is the densest screen on the site and the
   operator ruled for density on a computer (RC 52: "seeing more at once"). The
   four names below are the whole dial; nothing here carries a size of its own.
   ⚠️ NO NEW BREAKPOINT: the stylesheet is AT its ceiling of nine (engine/
   check_web_basics.py), so the one rule at the bottom reuses 640px, the width
   the drawing switches at anyway. A phone gets its own arrangement in its own
   round; this is the placeholder stack. */
.cuscard { --c-h1: 20px; --c-row: 13px; --c-meta: 12px; --c-ctl: 26px;
  display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0;
  font-size: var(--c-row); }
/* the card draws its own edges, so the popup's padding would be a second one */
dialog#cuspop .popbody { padding: 0; }
.cuscard > .cardhead, .cuscard > .tabscroll, .cuscard > .cardfoot { flex: none; }
.cuscard > .cardbody { flex: 1 1 auto; min-height: 0; display: flex;
  flex-direction: column; }
.cuscard .cardpanel[data-on] { display: flex; flex-direction: column;
  flex: 1 1 auto; min-height: 0; }
.cuscard #cardloc { display: flex; flex-direction: column; flex: 1 1 auto;
  min-height: 0; }
/* the two record tabs are lists: they scroll INSIDE their own box */
.cuscard #cardhist, .cuscard #cardbill { flex: 1 1 auto; min-height: 0;
  overflow: auto; padding: 10px 12px; }

/* ── the head ── the name never clips (RC 89): the facts beside it wrap first */
.cuscard .cardhead { display: flex; align-items: center; gap: 10px;
  padding: 6px 12px; border-bottom: 1px solid var(--line); background: var(--band); }
/* ⛔ min-width: fit-content, NOT 0. With 0 the head's flex row was free to
   squeeze the name box below the name inside it, and "Spring Grove MPRC" came
   out as "Spring Grove M" — the customer's own name, clipped, at the top of
   his card. Found in the picture round Sep 10; a string check cannot see it.
   The longest active customer's name is 32 characters (MEASURED), under the
   46ch cap below, so fit-content never wins the whole row; the facts beside
   it wrap instead, which they are already built to do. */
.cuscard .namewrap { margin: 0; min-width: fit-content; flex: 0 1 auto; }
.cuscard .nm { font-size: var(--c-h1); font-weight: 650; min-width: 14ch;
  max-width: 100%; border: 1px solid transparent; border-radius: var(--r-ctl);
  padding: 1px 7px; background: transparent; font-family: inherit; }
.cuscard .nm:hover, .cuscard .nm:focus { border-color: var(--line); background: #fff; }
@supports (field-sizing: content) {
  .cuscard .nm { field-sizing: content; max-width: 46ch; }
}
.cuscard .grow { flex: 1; }
.cuscard .hfacts { display: flex; gap: 4px 18px; align-items: center;
  flex-wrap: wrap; justify-content: flex-end; min-width: 0;
  font-size: var(--c-meta); }
.cuscard .hfacts .k { color: var(--faint); text-transform: uppercase;
  letter-spacing: .06em; font-size: 11px; }
.cuscard .hfacts b { display: block; font-size: var(--c-row); font-weight: 600;
  white-space: nowrap; }
.cuscard .cardhead .btn { margin: 0; white-space: nowrap; }

/* ── the chips (parts.statechip): the palette's soft/ink/edge triples ── */
.cuscard .chip { font-size: var(--c-meta); padding: 1px 9px; }
.cuscard .c-ok { background: var(--green-soft); color: var(--green-ink);
  border-color: var(--green-edge); }
.cuscard .c-warn { background: var(--amber-soft); color: var(--amber-ink);
  border-color: var(--amber-edge); }
.cuscard .c-info { background: var(--blue-soft); color: var(--blue-ink);
  border-color: var(--blue-edge); }
.cuscard .c-off { background: var(--band); color: var(--dim);
  border-color: var(--line); }
/* the on/off chip is a control, not a label (RC 594) */
.cuscard .onoff { cursor: pointer; font: inherit; font-size: var(--c-meta);
  font-weight: 600; }
.cuscard .onoff:hover { filter: brightness(.97); }

/* ── the tabs: the site's own .ptab, at this card's density ── */
.cuscard .ptabs { margin-bottom: 0; }
.cuscard .ptab { font-size: var(--c-row); font-weight: 600; height: 28px;
  padding: 0 11px; }
.cuscard .tabscroll { --tabfade: var(--band); background: var(--band); }
.cuscard .cnt { display: inline-block; margin-left: 6px; font-size: 11px;
  padding: 0 6px; border-radius: var(--r-pill); background: #fff;
  border: 1px solid var(--line); color: var(--dim); font-weight: 600; }
.cuscard .ptab.add { color: var(--blue-ink); font-weight: 500; }
/* the pane's own strip sits on the pane, not on the card */
.cuscard .mdetail .tabscroll { --tabfade: #fff; background: #fff;
  margin: -8px -10px 8px; padding: 0 10px; }

/* ── THE LOCATION BAR: four columns that wrap INDEPENDENTLY (RC 600) ──────
   Flex-wrap moved whole groups onto a shared new line — the pin and the
   buttons landed together. A grid gives each its own column: the address
   wraps INSIDE its column and the others never move. */
.cuscard .locbar { display: grid;
  /* ⛔ THE PIN COLUMN IS `auto`, NOT A FRACTION. A fraction gave it a share of
     the bar rather than the room its contents need, so on a one-station
     location — where the actions column also carries "＋ Add a station" — the
     "Pin" label and its box could not sit on one line and the box dropped to a
     second row, taking the whole bar with it (a label wrapping while its row
     has room: the rule this card is built on). auto asks for exactly the
     label plus the pin's 21 characters; the ADDRESS column is the one that
     gives up the room, which is what the drawing says it is for (RC 636). */
  grid-template-columns: 1fr minmax(12ch, 3.2fr) auto auto; gap: 5px 12px;
  align-items: start; padding: 6px 9px; background: var(--hover);
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }
.cuscard .lb { display: flex; gap: 6px; align-items: center; flex-wrap: wrap;
  min-width: 0; }
.cuscard .locbar input, .cuscard .stlocbar input { height: var(--c-ctl);
  border: 1px solid var(--line); border-radius: var(--r-ctl); padding: 0 7px;
  font: inherit; font-size: var(--c-row); background: #fff; }
/* ⭐⭐ EVERY BOX IS THE SIZE OF WHAT IS IN IT (RC 592), so the row fits without
   anything having to move. A browser without field-sizing keeps the flexed
   widths below, so nothing is lost. */
.cuscard .w-nm { flex: 2 1 0; min-width: 0; }
.cuscard .w-addr { flex: 3 1 0; min-width: 0; }
.cuscard .w-pin { flex: 1 1 0; min-width: 0; }
@supports (field-sizing: content) {
  .cuscard .locbar input, .cuscard .stlocbar input { field-sizing: content;
    width: auto; max-width: 30ch; }
  /* ⛔ THE NAME BOX IS THE SIZE OF THE NAME IN IT — ITS PLACEHOLDER COUNTS.
     RC 664, Sep 10: the grey name in this box was cut on the operator's own
     pictures — "Spring Grove MPR" for Spring Grove MPRC, "OPD - North A" for
     OPD - North Ave. The box holds no VALUE (the location is called after the
     customer until somebody renames it), so all there is to read is the
     placeholder, and an 11ch floor inside a 1fr grid column let the column's
     share decide the width instead of the word: MEASURED 96px at 1060 and
     149px at 1280 where the name needs 158. fit-content asks for the whole
     word — with field-sizing that is the placeholder's own width — and the
     30ch cap above still stops a long one taking the bar. Same fix, same
     reason, as the head's .namewrap. */
  .cuscard .w-nm { min-width: fit-content; }
  /* ⛔ 21ch IS THE PIN'S OWN LENGTH, not a guess: "39.312203, -76.599046" —
     six decimals each side (about four inches), a sign, a comma and a space.
     At 15ch the box was 121px where the number needs 143px, so on a
     ONE-STATION location — where the bar carries an extra "＋ Add a station"
     and the pin column is tighter — OPD's pin read "39.312203, -76.599" with
     the last digits cut off. A truncated pin is not a truncated word: it is a
     DIFFERENT PLACE, and nothing on the screen says it was cut. MEASURED with
     scratch/probe_clipped.py, before and after. */
  .cuscard .w-pin { min-width: 21ch; }
  /* ⛔ THE ADDRESS BOX FILLS ITS COLUMN, and min-width is the only lever that
     does it: with field-sizing:content the browser sizes the box to the text
     and IGNORES width and flex-grow, and it sizes it three characters short —
     the first picture showed "…Catonsville, MD 212". The drawing says this one
     box "may grow to the whole line" (RC 636); the other boxes keep the
     size-of-their-value rule. */
  .cuscard .w-addr { min-width: 100%; max-width: none; }
}
.cuscard .pinlab { font-size: var(--c-meta); color: var(--dim); }
.cuscard .g-acts { justify-content: flex-end; }
.cuscard .g-acts .btn { margin: 0; }
.cuscard .copyword { font-size: var(--c-meta); color: var(--blue-ink);
  border-bottom: 1px dashed var(--blue-edge); }
.cuscard .mini { font-size: var(--c-meta); color: var(--dim); }
.cuscard .lab { font-size: 11px; text-transform: uppercase; letter-spacing: .07em;
  color: var(--faint); font-weight: 650; margin: 0 0 5px; }

/* ── the address lookup: the matches HANG OVER the pane (RC 649) so the card
   never changes height while somebody types ── */
.cuscard .g-addr { position: relative; }
.cuscard .suggest { position: absolute; top: calc(100% + 2px); left: 0;
  min-width: 100%; z-index: var(--z-drop); }
.cuscard .sugbox { background: #fff; border: 1px solid var(--line);
  border-radius: var(--r-ctl); box-shadow: 0 10px 24px rgba(0,0,0,.18);
  overflow: hidden; white-space: nowrap; }
.cuscard .sugrow { display: flex; gap: 16px; align-items: baseline;
  justify-content: space-between; width: 100%; padding: 5px 10px;
  font: inherit; font-size: var(--c-row); text-align: left; cursor: pointer;
  background: #fff; border: 0; border-top: 1px solid var(--hair); }
.cuscard .sugrow:first-child { border-top: 0; }
.cuscard .sugrow:hover { background: var(--blue-soft); color: var(--blue-ink); }
.cuscard .sugrow .gp { color: var(--faint); font-size: var(--c-meta); }
.cuscard .sugrow.keep { color: var(--dim); font-size: var(--c-meta); }

/* ⭐ THE DOUBTFUL-ADDRESS MARK (RC 651) — no pin, so the truck cannot be
   pointed here. NEVER shown because a geocoder merely failed. */
.cuscard .qm { display: inline-flex; align-items: center; justify-content: center;
  flex: none; width: 17px; height: 17px; border-radius: 50%;
  background: var(--amber-soft); border: 1px solid var(--amber-edge);
  color: var(--amber-ink); font-size: 12px; font-weight: 700; line-height: 1;
  cursor: help; }

/* ── the middle: sidebar + pane, or (one station) pane + rail ── */
.cuscard .md { display: grid; grid-template-columns: 262px minmax(0, 1fr);
  flex: 1 1 auto; min-height: 0; overflow: hidden; }
.cuscard .md.onest { grid-template-columns: minmax(0, 1.62fr) minmax(0, 1fr); }
.cuscard .mlist { border-right: 1px solid var(--line); background: var(--band);
  overflow: auto; min-height: 0; }
.cuscard .mrow { display: grid; grid-template-columns: 1fr auto; gap: 1px 8px;
  width: 100%; padding: 5px 10px; margin: 0; text-align: left; font: inherit;
  font-size: var(--c-row); background: none; border: 0;
  border-bottom: 1px solid var(--line); cursor: pointer; align-items: center; }
.cuscard .mrow:hover { background: var(--hover); }
.cuscard .mrow b { font-size: var(--c-row); font-weight: 600; }
.cuscard .mrow .m2 { grid-column: 1 / -1; font-size: var(--c-meta);
  color: var(--dim); }
.cuscard .mrow .m2 .inh, .cuscard .ppl.inh { color: var(--faint); }
.cuscard .mrow.on { background: #fff; box-shadow: inset 3px 0 0 var(--blue); }
/* the LOCATION's own row is a header, not a sibling station (RC 642.4) */
.cuscard .mrow.lochead { background: var(--band);
  border-bottom: 2px solid var(--line); grid-template-columns: 1fr; }
.cuscard .mrow.lochead b { font-size: 11px; text-transform: uppercase;
  letter-spacing: .06em; color: var(--dim); }
.cuscard .mrow.off b, .cuscard .mrow.off .m2 { color: var(--faint); }
.cuscard .mrow .dot { width: 9px; height: 9px; border-radius: 50%;
  background: var(--green-edge); }
.cuscard .mrow.off .dot { background: var(--line); }
.cuscard .mdetail { padding: 8px 10px 6px; background: #fff; min-width: 0;
  overflow: auto; }
.cuscard .rail { border-left: 1px solid var(--line); background: var(--band);
  padding: 9px 10px; display: flex; flex-direction: column; gap: 8px;
  min-width: 0; overflow: auto; }

/* ── the pane's fields. A LABEL NEVER STACKS (RC 642): a control is the size
   of its value AND its label. ── */
.cuscard .fld { margin: 0; min-width: 0; }
.cuscard .fld span { display: block; font-size: 11px; text-transform: uppercase;
  letter-spacing: .06em; color: var(--faint); font-weight: 650;
  margin-bottom: 2px; white-space: nowrap; }
.cuscard .fld input, .cuscard .fld select, .cuscard .fld textarea {
  width: 100%; border: 1px solid var(--line); border-radius: var(--r-ctl);
  padding: 2px 6px; font: inherit; font-size: var(--c-row); background: #fff; }
.cuscard .fld input, .cuscard .fld select { height: var(--c-ctl); }
.cuscard .fld textarea { resize: vertical; height: auto; }
.cuscard .inline { display: flex; gap: 8px; align-items: flex-end;
  flex-wrap: wrap; margin-bottom: 9px; }
.cuscard .w-freq { width: 11rem; }
.cuscard .w-stname { width: 11rem; }
.cuscard .narrow input { width: 5ch; }
/* a station whose box is empty is showing the LOCATION's value (RC 637) — a
   select cannot carry a placeholder, so its first option says so in grey */
.cuscard select.inherit { color: var(--faint); }
.cuscard select.inherit option:not(:first-child) { color: var(--text); }

/* ── the standing order as chips (RC 144/592) ── */
.cuscard .skus { display: flex; gap: 6px; flex-wrap: wrap; align-items: center;
  margin: 0; }
.cuscard .sku { position: relative; display: flex; align-items: center; gap: 4px;
  border: 1px solid var(--line); border-radius: var(--r-ctl); padding: 2px 6px;
  background: #fff; max-width: 100%; }
.cuscard .sku input { width: 4ch; border: 0; border-right: 1px solid var(--hair);
  padding: 1px 3px; font: inherit; font-size: var(--c-row); text-align: right;
  background: transparent; }
/* ⛔ AND THE PADDING AGAIN, LOUDER — THE LADDER OUTRANKS THE CARD.
   `body.ladder:not(.tapui) input[type=number]` sets .3rem .55rem, which is
   three classes deep and beats `.cuscard .sku input`, so the rule above was
   never in force: MEASURED in the browser, the box was 28px wide with 9.35px
   of padding on each side, and "40 Pure" at Spring Grove came out as "4" with
   the 0 cut off. A standing order that lies about its own number is the worst
   thing this card could do, and no gate could see it — the picture did.
   field-sizing then lets a three-digit order have its third digit. */
body.ladder .cuscard .sku input { padding: 1px 3px; }
@supports (field-sizing: content) {
  .cuscard .sku input { field-sizing: content; width: auto; min-width: 2ch;
    max-width: 6ch; }
}
/* ⛔ A NUMBER BOX 4 CHARACTERS WIDE HAS NO ROOM FOR SPINNER ARROWS — with
   them the browser pushed the VALUE out of sight and every chip looked empty
   (seen in the first picture, which is the only thing that could have shown
   it). The card's numbers are typed, never stepped. */
.cuscard input[type=number] { appearance: textfield; -moz-appearance: textfield; }
.cuscard input[type=number]::-webkit-outer-spin-button,
.cuscard input[type=number]::-webkit-inner-spin-button {
  -webkit-appearance: none; margin: 0; }
.cuscard .sku .kind { border: 0; background: none; font: inherit;
  font-size: var(--c-meta); color: var(--dim); padding: 0; margin: 0;
  cursor: pointer; max-width: 22ch; text-align: left; height: auto; }
.cuscard .sku .kind:hover { color: var(--blue-ink); text-decoration: underline; }
.cuscard .sku.spare { border-style: dashed; }
.cuscard .sku.spare .kind { color: var(--blue-ink); }
/* the list of kinds, opened by the chip's own text button */
.cuscard .kindlist { position: absolute; top: calc(100% + 3px); left: 0;
  z-index: var(--z-drop); display: flex; flex-direction: column;
  min-width: 11rem; max-height: 15rem; overflow: auto; background: #fff;
  border: 1px solid var(--line); border-radius: var(--r-ctl);
  box-shadow: 0 10px 24px rgba(0,0,0,.18); }
.cuscard .kindopt { border: 0; background: none; font: inherit;
  font-size: var(--c-row); text-align: left; padding: 5px 10px; cursor: pointer; }
.cuscard .kindopt:hover { background: var(--blue-soft); color: var(--blue-ink); }

/* ── the week strip: seven chips, and the clicked day's editor under them ── */
.cuscard .week { display: flex; gap: 5px; flex-wrap: wrap; margin-bottom: 7px; }
.cuscard .day { display: flex; gap: 6px; align-items: baseline;
  border: 1px solid var(--line); border-radius: var(--r-ctl); padding: 2px 8px;
  background: #fff; font: inherit; font-size: var(--c-meta); cursor: pointer; }
.cuscard .day b { font-size: var(--c-meta); color: var(--text); }
.cuscard .day .inh { color: var(--faint); }
.cuscard .day.on { border-color: var(--blue); box-shadow: 0 0 0 1px var(--blue); }
.cuscard .dayedit { display: none; gap: 6px; align-items: center;
  flex-wrap: wrap; font-size: var(--c-meta); color: var(--dim); }
.cuscard .dayedit[data-on] { display: flex; }
/* ⛔ A TIME BOX IS NOT 8 CHARACTERS WIDE. It holds "05:15" AND the browser's
   own clock button, and at 8.5ch the first picture showed "05:" and "12:" —
   RC 89, a value cut off to fit. The clock button goes (the boxes are typed
   into, and a desk keyboard is faster than a picker) and the box holds its
   value with room to spare. */
.cuscard .dayedit input[type=time] { width: 9.5ch; height: 24px;
  border: 1px solid var(--line); border-radius: 6px; padding: 0 3px;
  font: inherit; font-size: var(--c-meta); background: #fff; }
.cuscard .dayedit input[type=time]::-webkit-calendar-picker-indicator {
  display: none; }
.cuscard .dayedit input[type=checkbox] { width: 14px; height: 14px; }
.cuscard .dayedit label { display: inline-flex; gap: 4px; align-items: center; }

/* ── a closed spell, and the summer block ── */
.cuscard .spells { margin-top: 9px; }
.cuscard .seas { display: flex; gap: 7px; align-items: center; flex-wrap: wrap;
  margin: 0 0 5px; padding: 5px 8px; border: 1px dashed var(--amber-edge);
  border-radius: var(--r-ctl); background: var(--amber-soft);
  color: var(--amber-ink); font-size: var(--c-meta); }
.cuscard .seas input { height: 24px; border: 1px solid var(--line);
  border-radius: 6px; padding: 0 5px; font: inherit; font-size: var(--c-meta);
  background: #fff; }
.cuscard .seas .btn { margin: 0; }
/* the bare ✕ (RC 617): a mark, not a grey slab */
.cuscard .bare-x { border: 0; background: none; color: var(--dim);
  font: inherit; font-size: var(--c-row); line-height: 1; padding: 0 4px;
  cursor: pointer; }
.cuscard .bare-x:hover { color: var(--red); }
.cuscard .dt { display: flex; align-items: center; gap: 5px;
  font-size: var(--c-meta); color: var(--dim); }
.cuscard .dt input { width: 9ch; height: 24px; border: 1px solid var(--line);
  border-radius: 6px; padding: 0 5px; font: inherit; font-size: var(--c-meta);
  background: #fff; color: var(--text); }

/* ── the people and the kit (the rail, and a station's People & kit tab) ── */
.cuscard .ppl { display: flex; gap: 6px; align-items: center; flex-wrap: wrap;
  margin: 0; padding: 4px 0; border-top: 1px solid var(--hair);
  font-size: var(--c-meta); }
.cuscard .ppl:first-of-type { border-top: 0; }
.cuscard .pplrow input { flex: 1 1 7rem; min-width: 0; height: var(--c-ctl);
  border: 1px solid var(--line); border-radius: var(--r-ctl); padding: 0 6px;
  font: inherit; font-size: var(--c-meta); background: #fff; }
.cuscard .ppl .btn { margin: 0; }
/* in the RAIL a person's row is only ~330px wide, so its boxes go two by two
   and its two buttons share the last line — left to flex-wrap, "Remove" ended
   up alone on a third line looking like a mistake. */
.cuscard .rail .pplrow { display: grid; grid-template-columns: 1fr 1fr;
  gap: 4px 6px; }
.cuscard .rail .pplrow input { flex: none; width: 100%; }
.cuscard .rail .pplrow .btn { width: 100%; }
/* ⛔ THE NAME AND THE EMAIL GET THE WHOLE LINE (RC 89 — nothing is shown cut).
   MEASURED at 1280 with two boxes to a line: "Office of Public Defender -
   North Ave" needs 210px in a 187px half, so a manager read "Office of Public
   Defender - North" and could not tell it from a name that really ends there.
   A name and an email are the two long ones; "what they are" and a phone
   number are short, so they still share a line and the row grows by ONE line
   in a rail that has room to spare. */
.cuscard .rail .pplrow input[name=name],
.cuscard .rail .pplrow input[name=email] { grid-column: 1 / -1; }
.cuscard .kitgrid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.cuscard .kitgrid form { margin: 0; }

/* ── the folds: a control that makes something new, out of the way until it
   is wanted. A native <details>, so it needs no script and the keyboard
   reaches it by birth. ── */
.cuscard .addfold { position: relative; }
.cuscard .addsum { list-style: none; cursor: pointer; color: var(--blue-ink);
  font-size: var(--c-meta); padding: 3px 6px; border-radius: var(--r-ctl);
  white-space: nowrap; }
.cuscard .addsum::-webkit-details-marker { display: none; }
.cuscard .addsum:hover { background: var(--hover); }
.cuscard .g-acts .addfold > .foldbody { position: absolute; right: 0;
  top: calc(100% + 3px); z-index: var(--z-drop); }
.cuscard .foldbody { display: flex; gap: 6px; align-items: center;
  flex-wrap: wrap; margin: 4px 0 0; padding: 7px 8px; background: #fff;
  border: 1px solid var(--line); border-radius: var(--r-ctl);
  box-shadow: 0 10px 24px rgba(0,0,0,.12); }
.cuscard .foldbody input { height: var(--c-ctl); border: 1px solid var(--line);
  border-radius: var(--r-ctl); padding: 0 7px; font: inherit;
  font-size: var(--c-row); background: #fff; }
.cuscard .foldbody .btn { margin: 0; }
.cuscard .addrowfold .addsum { padding: 6px 10px; }
/* the location tabs and the row their last tab opens, wrapped so each strip's
   switcher sees only its own panel (see _card_locations.html) */
.cuscard .loctabsblock { flex: none; }
.cuscard .addlocrow { padding: 7px 9px; background: var(--hover);
  border-bottom: 1px solid var(--line); }
.cuscard .addlocrow form { display: flex; gap: 6px; align-items: center;
  flex-wrap: wrap; margin: 0; }
.cuscard .addlocrow input { height: var(--c-ctl); border: 1px solid var(--line);
  border-radius: var(--r-ctl); padding: 0 7px; font: inherit;
  font-size: var(--c-row); background: #fff; }

/* ── the two record tabs' rows ── */
.cuscard .recbox { border: 1px solid var(--line); border-radius: var(--r-card);
  overflow: hidden; margin-bottom: 12px; }
.cuscard .krow { display: grid; grid-template-columns: 66px 1fr auto; gap: 10px;
  align-items: center; padding: 5px 9px; border-top: 1px solid var(--hair);
  font-size: var(--c-row); }
.cuscard .krow:first-child { border-top: 0; }
.cuscard .kd { color: var(--faint); font-size: var(--c-meta); }
/* ⭐ THE DRIVER'S NOTE (RC 579) — twenty-two of them existed and nothing on
   the site showed one. It rides on the row it belongs to, in his colour. */
.cuscard .note { display: block; margin-top: 3px; padding: 2px 7px;
  border-left: 3px solid var(--amber-edge); border-radius: 0 6px 6px 0;
  background: var(--amber-soft); color: var(--amber-ink);
  font-size: var(--c-meta); }
/* ...and the one thing the queue's copy of that note knew and the row did not:
   nobody has dealt with it yet (RC 664). It rides INSIDE the note, in the
   note's own colour, so it reads as part of the same sentence rather than as
   a second fact. */
.cuscard .note .waitmark { font-weight: 650; white-space: nowrap; }
.cuscard .bempty { padding: 8px 10px; color: var(--faint);
  font-size: var(--c-meta); }

/* ── the foot: the customer's state, as one segmented control ── */
.cuscard .cardfoot { display: flex; gap: 10px; align-items: center;
  flex-wrap: wrap; padding: 6px 12px; border-top: 1px solid var(--line);
  background: var(--band); }
.cuscard .state { display: flex; border: 1px solid var(--line);
  border-radius: var(--r-ctl); overflow: hidden; background: #fff; }
/* three forms wearing one control: only the last of them asks a question
   first, and data-confirm belongs to a form */
.cuscard .state form { display: contents; margin: 0; }
.cuscard .stbtn { border: 0; border-left: 1px solid var(--line);
  background: #fff; color: var(--dim); font: inherit; font-size: var(--c-row);
  height: 28px; padding: 0 12px; margin: 0; cursor: pointer; }
.cuscard .state form:first-child .stbtn { border-left: 0; }
.cuscard .stbtn.on { background: var(--green-soft); color: var(--green-ink);
  font-weight: 650; }
.cuscard .stbtn.gone.on { background: var(--red-soft); color: var(--red); }
.cuscard .foot-note { color: var(--faint); font-size: var(--c-meta); }

/* ⭐ ONE SWITCH, AND IT IS FOR A PHONE. Everything above it holds ONE
   arrangement and gets narrower in proportion, which is what every real site
   does. A phone is a different SHAPE, not a smaller desk, so what it really
   gets is its own round; this is the placeholder stack. */
@media (max-width: 640px) {
  .cuscard .locbar { grid-template-columns: 1fr; }
  .cuscard .md, .cuscard .md.onest { grid-template-columns: 1fr; }
  .cuscard .kitgrid { grid-template-columns: 1fr; }
}

/* ══ THE COOLERS (RC 701/703/705) ══════════════════════════════════════════
   Two surfaces, one idea: the driver photographs a cooler at the door, and the
   customer card is the list of what is standing there. The drawing of record
   is deliverables/cooler_mockup.html, states B and C.
   ⛔ NO COLOUR IS A ONE-OFF HERE. The "photo on its way" square reads the
   amber names the palette already has — amber, never red, because a picture
   still on his phone is a FACT and not a failure (RC 178's own distinction). */

/* ── the driver's photo row (state B): the small picture, its name, Retake ── */
.photorow { display: grid; grid-template-columns: 5.2rem 1fr auto;
  gap: .6rem; align-items: center; margin: .4rem 0 .8rem; }
/* nothing taken yet: the slab is the whole row, exactly as the drawing's
   state A shows it. The wrapper exists only so the offline script has a row
   to put the phone's held picture in (see parts.photorow). */
.photorow.untaken { display: block; margin: 0; }
.photorow.untaken .photopick { width: 100%; margin: .6rem 0;
  padding: 1rem; font-size: 1.05rem; }
.photorow img { width: 5.2rem; height: 5.2rem; border-radius: .6rem;
  object-fit: cover; background: var(--line); }
/* a file picker wearing the plain slab. It is a <label> round the input —
   see parts.photorow for why — so it needs the slab's own cursor, and inside
   a row it sizes to its words instead of the full width. */
.photopick { cursor: pointer; }
.photorow .photopick { width: auto; margin: 0; padding: .6rem .9rem;
  font-size: 1rem; }

/* ── the card's kit box (state C): our kit at this place ────────────────── */
.kitlist { margin: .2rem 0 .4rem; }
.kitcount { margin: .2rem 0 .6rem; }
.kitcount small { color: var(--dim); font-weight: 500; }
.kitrow { display: grid; grid-template-columns: 1fr auto auto; gap: .5rem .8rem;
  align-items: center; padding: .45rem 0; border-top: 1px solid var(--hair); }
.kitrow small { color: var(--dim); display: block; }
/* ⭐ A PICKED-UP COOLER STAYS IN THE LIST, greyed, saying where it went. The
   card is the story of our kit at this place and a cooler that left is part of
   it; the count above reads only what is standing there. */
.kitrow.gone { opacity: .6; }
/* ⚠️ Bootstrap's own .mb-3 sits on parts.field's wrapper and is declared
   !important, which this file is not allowed to answer (UI_SPEC 13.A1). So the
   row takes the spacing off the GRID instead — a negative bottom margin here
   would be the narrow override the craft file warns is an interest payment. */
.kitedit { display: grid; grid-template-columns: 1fr 1fr auto; gap: .4rem;
  align-items: end; }
.coolshot { width: 3.2rem; height: 3.2rem; border-radius: .4rem;
  display: block; background: var(--line); overflow: hidden; }
.coolshot img { width: 100%; height: 100%; object-fit: cover; display: block; }
.coolshot.away { background: var(--amber-soft);
  border: 1px solid var(--amber-edge); }
.kitwait { color: var(--amber-ink); font-size: var(--c-meta, .82rem);
  margin: .4rem 0 0; }

@media (max-width: 640px) {
  .kitrow { grid-template-columns: 1fr auto; }
  .kitedit { grid-template-columns: 1fr; }
}

/* ══ A ROW'S CHOICES — the ··· menu and its right-click twin ═══════════════
   The part is parts.rowmenu / parts.menublock in _parts.html (RC 741/736).
   It is a <details>, so it opens, closes and is reachable from the keyboard
   with no script at all; kit.js only adds the right-click, the one-open-at-a-
   time rule and — the reason the box can be POSITIONED at all — pinning it to
   the viewport. ⚠️ THAT PIN IS NOT DECORATION: every list this menu appears
   in scrolls inside its own box, and an absolutely-placed menu inside a
   scrolling box is CLIPPED by it, which would cut the bottom two choices off
   every row near the foot of the sheet. kit.js adds .placed and the real
   coordinates; without script it stays absolute and the browser's own
   scrolling still reaches it. */
.rowmenu { position: relative; display: inline-block; }
.rowmenu > summary { list-style: none; cursor: pointer; user-select: none;
  padding: 0 .3rem; border-radius: var(--r-ctl, 6px); color: var(--dim); }
.rowmenu > summary::-webkit-details-marker { display: none; }
.rowmenu > summary:hover, .rowmenu[open] > summary {
  background: var(--ctl-hover); color: var(--text); }
.rowmenubox { position: absolute; right: 0; top: 100%; z-index: var(--z-menu);
  min-width: 13rem; background: var(--card); border: 1px solid var(--line);
  border-radius: 8px; box-shadow: 0 .4rem 1.2rem rgba(0, 0, 0, .18);
  padding: .2rem 0; text-align: left; }
.rowmenubox.placed { position: fixed; right: auto; top: auto; }
/* a menu that lives on <body> but was asked for from inside a kit pane — it
   must stand OVER the pane, not behind it */
.rowmenubox.overkit { z-index: var(--z-kit-top); }
.rowmenubox .rmwho { padding: .15rem .6rem .25rem; color: var(--dim);
  border-bottom: 1px solid var(--hair); font-size: .72rem; white-space: nowrap; }
.rowmenubox .rmitem { display: block; width: 100%; text-align: left; margin: 0;
  background: none; border: 0; border-radius: 0; text-decoration: none;
  padding: .25rem .6rem; font-size: .78rem; white-space: nowrap;
  color: var(--text); }
.rowmenubox .rmitem:hover { background: var(--blue-soft); }
.rowmenubox .rmsep { border-top: 1px solid var(--hair); margin: .18rem 0; }
.rowmenubox .rmdate { padding: .25rem .6rem; }
.rowmenubox .rmsay { display: block; font-size: .78rem; }
.rowmenubox .rmpick { display: flex; gap: .3rem; margin-top: .15rem; }
.rowmenubox .rmdate input[type="date"] { flex: 1 1 auto; min-width: 0;
  font-size: .76rem; padding: .05rem .25rem; }
.rowmenubox .rmgo { margin: 0; font-size: .74rem; padding: .05rem .4rem;
  border: 1px solid var(--ctl-edge); border-radius: var(--r-ctl, 6px);
  background: var(--ctl-bg); }

/* ══ THE WEEK MANAGER (job 2, RC 743) ══════════════════════════════════════
   pane_week.html and its two fragments, built from the drawing
   (deliverables/week_questions_mockup.html). The drawing's own layout block,
   with every colour it typed as a hex read from the palette instead: its
   #b42318 is --red, its #7a5b00 is --amber-ink, its green dot is --ok.
   ⭐ THE PANE NEVER SCROLLS AS A WHOLE (RC 146). One flex chain from the
   kit's body down — head and ribbon fixed, the bottom half takes what is
   left — and the four lists inside it scroll alone.
   ⛔ NO viewport arithmetic and no hand-tuned heights (operator: "If you're
   manually adjusting the heights, you're being stupid!!"). The single
   measured number is the ribbon cell's height, which is what buys the
   customers their lines. */
.wkwrap { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0;
  font-size: .8rem; }
.wkwrap .wkspacer { flex: 1 1 auto; }
.wkwrap .over { color: var(--red); }

.wkhead { display: flex; align-items: center; gap: .6rem; flex: 0 0 auto;
  padding: 0 0 .3rem; border-bottom: 1px solid var(--hair); }
.wkhead .wkadd { display: flex; gap: .3rem; align-items: center; min-width: 0; }
/* ⭐ ONE HEIGHT FOR THE WHOLE ADD ROW (Sep 23, the pixel machine's UNEVEN
   at 375px: the two boxes 24px, the button 28px). Things sharing a row
   share a height (RC 334/698), declared once on the row, not per control. */
.wkhead .wkadd > input, .wkhead .wkadd > .wkaddbtn { min-height: 1.65rem; }
.wkhead .wkadd input { font-size: .78rem; padding: .1rem .35rem; min-width: 0;
  border: 1px solid var(--ctl-edge); border-radius: var(--r-ctl, 6px); }
.wkhead .wkadd input[name="clname"] { flex: 0 1 15rem; }
.wkhead .wkadd input[name="reason"] { flex: 0 1 9rem; }
.wkhead .wkaddbtn { margin: 0; font-size: .76rem; padding: .1rem .5rem;
  border: 1px solid var(--ctl-edge); border-radius: var(--r-ctl, 6px);
  background: var(--ctl-bg); white-space: nowrap; }

/* ── THE RIBBON — six day cells, always visible, never replaced ─────────── */
.wkribbon { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: .35rem; padding: .35rem 0; flex: 0 0 auto;
  border-bottom: 2px solid var(--line); }
/* ⭐ Sep 22, his word on the live pane ("don't waste all that space up
   top"): the day cells are SHORT — the head, the hours line, a route's name
   and about three of its customers, the rest scrolling inside the cell —
   so the open sheet below gets the room. It was 11.8rem. */
.wkday { border: 1px solid var(--line); border-radius: .4rem; min-width: 0;
  background: var(--card); display: flex; flex-direction: column;
  overflow: hidden; height: 9.4rem; }
.wkday.today { outline: 2px solid var(--blue); outline-offset: -2px; }
/* the ring that ties an open question to its day. The class is put on by
   build.js, not by a CSS selector: no selector can match one attribute's
   value against another's, and the drawing could only fake it by naming
   three dates it had been handed. */
.wkday.ringed { box-shadow: inset 0 0 0 2px var(--red); }
.wkday.drophot { border-color: var(--blue); border-style: dashed;
  background: var(--blue-soft); }
/* a day the company is shut reads as shut, and takes no drop at all */
.wkday.shut { background: var(--band); }
.wkday.shut .drop { font-style: normal; border: 0; }
.wkdhead { display: flex; align-items: baseline; gap: .3rem; flex: 0 0 auto;
  padding: .08rem .3rem; border-bottom: 1px solid var(--hair); }
/* ⚠️ THE DAY'S NAME NEVER BREAKS IN TWO. At a 1180-pixel desk today's cell
   carries two chips and "Fri 18" wrapped, so the 18 sat on its own line under
   the word Fri (seen in the walk, Sep 18). The chips wrap instead. */
.wkdhead { flex-wrap: wrap; }
.wkdhead b { font-size: .82rem; white-space: nowrap; }
.wkdhead .chip { font-size: .66rem; padding: 0 .25rem; }
.wkdmeta { flex: 0 0 auto; padding: .05rem .3rem .1rem;
  border-bottom: 1px solid var(--hair); }
.wkdbody { flex: 1 1 auto; overflow-y: auto; padding: .1rem .3rem;
  min-width: 0; min-height: 0; }
.wkday.empty .wkdbody { display: flex; align-items: center;
  justify-content: center; }
.wkday.empty .drop { font-size: .72rem; color: var(--dim); font-style: italic;
  border: 1px dashed var(--line); border-radius: .3rem; padding: .15rem .3rem;
  text-align: center; }
.wkrt { margin-bottom: .12rem; min-width: 0; }
.wkrt .rn { display: block; font-size: .76rem; font-weight: 600;
  color: var(--blue); cursor: pointer; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.wkrt .rn:hover { text-decoration: underline; }
.wkwrap .rf { display: block; font-size: .7rem; color: var(--dim);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* the two bars: the LOAD against that truck's own green and red (RC 220 — a
   Hino day is never measured by a Mitsubishi), the HOURS against that day's
   own minutes. A bar is the only thing here that says "how full" without a
   number, and those are the two facts he cannot see anywhere today. */
.wkwrap .bar { height: 3px; background: rgba(0, 0, 0, .09); border-radius: 2px;
  margin: .1rem 0; position: relative; overflow: hidden; }
.wkwrap .bar i { display: block; height: 100%; background: var(--ok); }
.wkwrap .bar i.amber { background: var(--amber-edge); }
.wkwrap .bar i.over { background: var(--red); }
.wkwrap .bar u { position: absolute; top: -1px; bottom: -1px; width: 1px;
  background: rgba(0, 0, 0, .45); }          /* the comfortable mark */
.wkstops { padding: .02rem 0 .12rem; }
/* the WAITING block "＋ route" puts on a cell (Sep 22): a truck's name and a
   dashed target for the first person — no run exists until they land. Its ✕
   is the office's bare ✕ (RC 617, the .ctl rule at the top of this file). */
.wkrt.pending { border: 1px dashed var(--line); border-radius: .3rem;
  padding: .12rem .3rem; display: grid; grid-template-columns: 1fr auto;
  align-items: center; column-gap: .2rem; }
.wkrt.pending .rn { color: var(--dim); cursor: default; }
.wkrt.pending .rn:hover { text-decoration: none; }
.wkrt.pending .rf { font-style: italic; }
.wkrt.pending .ctl { grid-row: 1 / span 2; }
.wkrt.pending .ctl button { min-height: 22px; font-size: 1rem; padding: 0 .2rem; }
.wkday.empty .wkdbody { flex-direction: column; gap: .2rem; }
.wkstops div { font-size: .74rem; line-height: 1.3; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.wkstops a { color: inherit; text-decoration: none; }
.wkstops a:hover { text-decoration: underline; }
.wkstops div.done { color: var(--dim); }
.wkstops div i { display: inline-block; width: .4rem; height: .4rem;
  border-radius: 50%; background: var(--faint); margin-right: .28rem;
  vertical-align: middle; }
.wkstops div.done i { background: var(--ok); }
.wkdverbs { flex: 0 0 auto; border-top: 1px dashed var(--line);
  padding: .05rem .3rem; font-size: .68rem; white-space: nowrap;
  display: flex; gap: .35rem; align-items: center; }
.wkdverbs .dayverb { color: var(--dim); cursor: pointer; }
.wkdverbs .dayverb:hover, .wkdverbs .rowmenu.dayverb > summary:hover {
  color: var(--text); text-decoration: underline; }
.wkdverbs .rowmenu > summary { padding: 0; font-size: .68rem; }

/* ── the bottom half: the question list, then what it opened ────────────── */
.wkbody { flex: 1 1 auto; display: flex; flex-direction: column; min-height: 0;
  padding: .35rem 0 0; gap: .35rem; }
.wkbox { border: 1px solid var(--line); border-radius: .4rem; display: flex;
  flex-direction: column; min-height: 0; overflow: hidden;
  background: var(--card); }
.wkboxhead { display: flex; align-items: baseline; gap: .45rem; flex: 0 0 auto;
  padding: .1rem .4rem; border-bottom: 1px solid var(--hair);
  background: var(--band); }
.wkboxhead b { font-size: .82rem; white-space: nowrap; }
.wkboxhead small, .wkboxhead .wknote { color: var(--dim); font-size: .72rem;
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  background: none; font-weight: 400; margin: 0; padding: 0; border-radius: 0; }
.wkboxhead .headsearch { margin: 0; }
.wkboxhead .headsearch input { font-size: .76rem; padding: .05rem .35rem;
  min-height: 0; }
.wkboxbody { flex: 1 1 auto; overflow-y: auto; min-height: 0; }
.wkwrap .wkempty { padding: .4rem; margin: 0; }

/* THE QUESTION LIST — about five questions tall and it SCROLLS in its own
   box (his word, Sep 22: "the middle section can scroll"); the count in its
   heading says how many there are. It was the whole list, at 17rem. */
.wklistbox { flex: 0 0 auto; max-height: 8.9rem; }
/* ⚠️ and the "nothing is asking" box needs the same: a flex child's default
   is to SHRINK, and without this the green notice was squeezed to nothing
   and the box rendered as a bare heading with no answer under it. */
.wkready { flex: 0 0 auto; }
.wkq { display: grid; grid-template-columns: .6rem minmax(12rem, 1.35fr)
  minmax(8rem, 1fr); gap: .4rem; align-items: center; padding: .05rem .4rem;
  border-bottom: 1px solid var(--hair); cursor: pointer; min-width: 0;
  line-height: 1.4; }
.wkq:hover { background: var(--hover); }
.wkq.on { background: var(--blue-soft); font-weight: 600; }
.wkq > * { min-width: 0; }
.qdot { width: .45rem; height: .45rem; border-radius: 50%;
  display: inline-block; background: var(--faint); }
.qdot.t-bad { background: var(--red); }
.qdot.t-warn { background: var(--amber-edge); }
.qdot.t-calm { background: var(--faint); }
.qsay { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qsay b { font-variant-numeric: tabular-nums; }
.qsub { color: var(--dim); font-size: .74rem; font-weight: 400;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* ⭐ THE ONE NUMBER IN THE SENTENCE IS PAINTED BY THE ROW'S OWN TONE, here,
   not by a class from the server: week_questions writes a PLAIN <b> (builder
   A's request R3), and the dot that already carries the tone sits before the
   sentence in the same row, so the sibling does it. The colours are the
   palette's names, so a question and the bar it is about agree.
   ⚠️ THE OLD RULES HERE (.qsay b.red / b.amb) MATCHED NOTHING — their comment
   said the server marked its own numbers and it never did. */
.qdot.t-bad ~ .qsay b { color: var(--red); }
.qdot.t-warn ~ .qsay b { color: var(--amber-ink); }

/* the open sheet and THE CALLS, side by side — the calls never leave the
   screen (RC 739), because a call belongs to a day and he is making them
   while he is looking at the week */
/* ⭐ RC 747 (Sep 22): the open sheet has the WHOLE width — the calls column
   that stood beside it (a 22rem clamp) went when the calls became one
   question like the others. */
.wksplit { flex: 1 1 auto; display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: .35rem; min-height: 0; }
.wksplit > * { min-height: 0; }
.wksheetslot { display: flex; flex-direction: column; gap: .35rem;
  min-height: 0; min-width: 0; }
.wksheetslot > .wkbox { flex: 1 1 auto; }
.wksheetslot > .wkbox.wkfinal { flex: 0 0 auto; max-height: 11rem; }

/* THE WORKING SHEET. ⛔ THE ROW IS NOT .prow, AND THAT IS DELIBERATE: app.css
   owns .prow, gives it a flex layout and a min-height, and turns it into a
   card under 900px. This sheet's whole argument is density and columns that
   line up.
   ⭐ AND EVERY COLUMN IS A FIXED WIDTH EXCEPT THE NAME, in the row AND in the
   header, from this one list. Each row is its own grid, so max-content would
   mean something different on every row: nothing would line up down the sheet
   and the header would line up with nothing at all. */
/* ⚠️ EVERY FIXED TRACK IS minmax(0, …) AND THAT IS NOT PEDANTRY. The drawing's
   widths were measured on a 16px root; the site's root is 17px, so the same
   eight tracks came to 830 pixels in 826 of sheet at a 1180-pixel desk and the
   ··· — the ONLY door to a row's choices — hung off the right edge. A max
   rather than a fixed width means the columns give the last few pixels back
   instead, and nothing moves at all on a wide screen. The ··· itself never
   shrinks, for the same reason. */
.wkrow, .sheethdr { display: grid; gap: .4rem; min-width: 0;
  grid-template-columns: .8rem minmax(6rem, 1fr) minmax(0, 8.8rem)
                         minmax(0, 6.4rem) minmax(0, 3.4rem) minmax(0, 5.4rem)
                         minmax(0, 9.6rem) 2.6rem;
  padding: .06rem .4rem; border-bottom: 1px solid var(--hair); }
.wkrow { align-items: center; font-size: .78rem; line-height: 1.4; }
.wkrow > * { min-width: 0; }
.wkrow:hover { background: var(--hover); }
.wkrow .dragh { cursor: grab; color: var(--faint); user-select: none; }
.wkrow.dragging { opacity: .45; }
.sheethdr { align-items: baseline; font-size: .66rem; letter-spacing: .03em;
  text-transform: uppercase; color: var(--dim); background: var(--band);
  position: sticky; top: 0; z-index: var(--z-sticky); }
.sheethdr span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wkwrap .nm { font-weight: 600; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; }
.wkwrap .nm a { color: inherit; text-decoration: none; }
.wkwrap .nm a:hover { text-decoration: underline; }
.wkrow .mk { white-space: nowrap; overflow: hidden; }
.wkrow .tag { font-size: .68rem; padding: 0 .22rem; }
.wkwrap .col { color: var(--dim); white-space: nowrap; overflow: hidden;
  text-overflow: ellipsis; font-variant-numeric: tabular-nums; }
.wkwrap .col.over { color: var(--red); font-weight: 600; }
.wkrow .acts, .wkdrow .acts { white-space: nowrap; text-align: right; }
/* ⚠️ body.ladder PREFIX, AND IT IS NOT DECORATION: `body.ladder .btn-sm` sets
   a .25rem padding that outranks a plain `.wkrow .btn`, and that is what gives
   a dense sheet 36-pixel buttons. Beat the live rule or lose to it. */
body.ladder .wkrow .btn, body.ladder .wkcrow .btn {
  font-size: .7rem; padding: 0 .3rem; line-height: 1.35; }

/* a day's stops, when the open question is about a day */
.wkdrow { display: grid; grid-template-columns: 1.4rem minmax(6rem, 1fr)
  minmax(0, 10rem) minmax(0, 9rem) 2.6rem;
  gap: .4rem; align-items: center; padding: .06rem .4rem;
  border-bottom: 1px solid var(--hair); font-size: .78rem; line-height: 1.4; }
.wkdrow > * { min-width: 0; }
.wkdrow.done .nm { color: var(--dim); font-weight: 400; }
.wkdrow .n { color: var(--faint); font-variant-numeric: tabular-nums; }

/* the calls: name · number · told · answered, grouped by day */
.wkcday { padding: .05rem .4rem; font-size: .7rem; letter-spacing: .03em;
  text-transform: uppercase; color: var(--dim); background: var(--band);
  border-bottom: 1px solid var(--hair); position: sticky; top: 0;
  z-index: var(--z-sticky); margin: 0; border-radius: 0; font-weight: 600; }
/* RC 749 (Sep 22): name + tag · phone · email · the two ticks. The email
   column is the one that gives room back on a narrow sheet (it wears
   .oneline, RC 699); the phone never wraps and the ticks never shrink. */
.wkcrow { display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 6.4rem) minmax(0, 14rem) max-content;
  gap: .3rem; align-items: center; padding: .06rem .4rem;
  border-bottom: 1px solid var(--hair); font-size: .76rem; line-height: 1.35; }
.wkcrow > * { min-width: 0; }
.wkcrow .ph { color: var(--dim); white-space: nowrap;
  font-variant-numeric: tabular-nums; }
.wkcrow .em { color: var(--dim); }
.wkcrow .nm .tag { margin-left: .2rem; }
.wkcrow .ph.none { color: var(--red); }
.wkcrow .ticks { display: flex; gap: .25rem; white-space: nowrap; }
.wkcrow .ticks form { display: inline; margin: 0; }

/* what was decided, and the week as it will run — the two lists that only
   exist when nothing is asking any more */
.wkdrow2 { display: grid; grid-template-columns: 1rem minmax(9rem, 1fr)
  minmax(10rem, 1.6fr) 10rem; gap: .45rem; align-items: baseline;
  padding: .12rem .4rem; border-bottom: 1px solid var(--hair);
  font-size: .78rem; }
.wkdrow2 > * { min-width: 0; }
.wkdrow2 .tick { color: var(--ok); font-weight: 700; }
.wkdrow2 .did { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wkwrap .readynote { padding: .3rem .5rem; }
.wkwrap .readynote .alert { padding: .2rem .5rem; margin: 0; font-size: .82rem; }
.finalday { display: grid; grid-template-columns: 5.5rem 1fr max-content;
  gap: .5rem; padding: .12rem .5rem; border-bottom: 1px solid var(--hair);
  font-size: .78rem; align-items: baseline; }

/* the phone and the small laptop: the ribbon stops being six across, the two
   bottom boxes stack, and the pane goes back to scrolling like any other */
@media (max-width: 899.98px) {
  .wkribbon { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .wkday { height: auto; max-height: 14rem; }
  .wksplit { display: flex; flex-direction: column; }
  .wkhead { flex-wrap: wrap; }
  .wkrow, .sheethdr { grid-template-columns: .8rem minmax(7rem, 1fr) 6rem
                      4rem 4.6rem 2.6rem; }
  .wkrow .col:nth-of-type(1), .sheethdr span:nth-child(4) { display: none; }
}

/* ⭐ RC 752-755 — MOSHE'S "DAYS DRIVEN" PAGE (web/templates/pane_days.html).
   The drawing's own rules (deliverables/driver_days_mockup.html), as drawn,
   except the frame: the drawing was a whole page (position:fixed); here the
   kit gives the pane its box and .ddwrap fills it, so the habit row stays put
   and the list scrolls in its own window (RC 146/63). It borrows the week
   manager's box and row grammar so the two read as one site. */
.ddwrap { display:flex; flex-direction:column; min-height:0; height:100%;
          overflow:hidden; font-size:.8rem; }
.ddhead { display:flex; align-items:center; gap:.6rem; padding:.35rem .7rem;
          border-bottom:1px solid var(--hair); flex:0 0 auto; flex-wrap:wrap; }
.ddhead h1 { font-size:1rem; margin:0; white-space:nowrap; }
.ddhead .spacer { flex:1 1 auto; }
.ddhabit { display:flex; gap:.4rem; flex-wrap:wrap; padding:.35rem .7rem;
           border-bottom:2px solid var(--hair); flex:0 0 auto; align-items:center; }
.ddhabit .lead { color:var(--dim); font-size:.76rem; margin-right:.2rem; }
.ddbody { flex:1 1 auto; display:flex; flex-direction:column; min-height:0;
          padding:.35rem .7rem .5rem; }
.ddbox { border:1px solid var(--hair); border-radius:.4rem; display:flex;
         flex-direction:column; min-height:0; overflow:hidden; flex:1 1 auto;
         background:var(--card,#fff); }
.ddboxhead { display:flex; align-items:baseline; gap:.45rem; flex:0 0 auto;
             padding:.1rem .4rem; border-bottom:1px solid var(--hair);
             background:rgba(0,0,0,.04); }
.ddboxhead b { font-size:.82rem; white-space:nowrap; }
.ddboxhead small { color:var(--dim); font-size:.72rem; }
.ddboxhead .spacer { flex:1 1 auto; }
.ddboxbody { flex:1 1 auto; overflow-y:auto; min-height:0; }
/* ONE grid for the header and every row, so the columns line up down the
   sheet (the week sheet's lesson). Only the note gives room back. */
.ddhdr, .ddrow { display:grid; align-items:center; gap:.4rem; padding:.08rem .4rem;
  grid-template-columns: 6.6rem minmax(7rem, .9fr) 6.2rem 6.2rem 3.6rem 3.6rem
                         5.2rem 7.4rem 8.6rem 4.6rem minmax(6rem, 1.4fr); }
.ddhdr { font-size:.66rem; letter-spacing:.03em; text-transform:uppercase;
         color:var(--dim); border-bottom:1px solid var(--hair);
         background:rgba(0,0,0,.02); position:sticky; top:0; }
.ddrow { border-bottom:1px solid var(--hair); font-size:.78rem; line-height:1.35; }
.ddrow > *, .ddhdr > * { min-width:0; }
.ddrow .d { font-weight:600; white-space:nowrap; }
.ddrow .rt { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.ddrow .rt small { color:var(--dim); }
.ddrow .t { font-variant-numeric:tabular-nums; white-space:nowrap; }
.ddrow .t small { color:var(--dim); font-size:.68rem; }
.ddrow .none { color:var(--dim); }
.ddrow .open { color:#b42318; font-weight:600; }
.ddrow .todo { color:#7a5b00; font-weight:600; }
.ddrow .ok { color:#4a8f57; }
.ddrow .note { color:var(--dim); overflow:hidden; text-overflow:ellipsis;
               white-space:nowrap; }
.ddrow.gap { background:rgba(180,35,24,.035); }
.ddrow .tag { font-size:.62rem; }
.ddfoot { flex:0 0 auto; padding:.25rem .7rem 0; color:var(--dim); font-size:.72rem; }
