/* Serve REBUILD — style. Design tokens per the mode doctrine +
 * Releases .88/.91 + Patch 92: header declutter, attempted band gray
 * at top, skipped in a dark section at the bottom, window chips owed
 * = bright blue / satisfied = near-invisible silhouette, Run time
 * rail 60px, availability filled cells near-black in EVERY theme,
 * glass universal (everything glass except the words and the map). */
:root {
  --bg: #f2f3f5; --card: #ffffff; --ink: #14181d; --dim: #5a6472;
  --line: #d8dce2; --accent: #1f6feb; --chip-owed: #bfd9ff; --chip-owed-ink: #0b3d91;
  --band-att: #33373f; --band-att-ink: #e8eaee; --band-skip: #23262c; --band-skip-ink: #c9cdd4;
  --danger: #c62f3b; --ok: #1c7c43;
  /* Glass fallbacks = the CANONICAL defaults (house glass
   * standard): Opacity 63 → fill alpha .6133 on every surface;
   * Frosting 75 → blur 18px at the chrome proportion, sheets
   * ×1.22, buttons ×.55, cards/Feedback ×.67. applyGlassVars
   * rewrites all of these from the device keys on load. */
  --glass-chrome-bg: rgba(255,255,255,.613); --glass-card-bg: rgba(255,255,255,.613);
  --glass-sheet-bg: rgba(255,255,255,.613);
  --glass-filter-card: blur(12.1px); --glass-filter-chrome: blur(18px);
  --glass-filter-sheet: blur(22px); --glass-filter-nav: blur(9.9px);
  --glass-filter-soft: blur(12.1px);
}
body.theme-night {
  --bg: #0d1117; --card: #161c26; --ink: #e8ecf1; --dim: #93a0b4;
  --line: #2a3242; --accent: #4d94ff; --chip-owed: #174a8b; --chip-owed-ink: #dbeafe;
  --band-att: #04060a; --band-att-ink: #aeb6c2; --band-skip: #05070a; --band-skip-ink: #aab3c0;
  --glass-chrome-bg: rgba(22,28,38,.613); --glass-card-bg: rgba(22,28,38,.613);
  --glass-sheet-bg: rgba(22,28,38,.613);
  --glass-filter-card: blur(12.1px); --glass-filter-chrome: blur(18px);
  --glass-filter-sheet: blur(22px); --glass-filter-nav: blur(9.9px);
  --glass-filter-soft: blur(12.1px);
}
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
/* PAGE ZOOM / SHELL LOCK (p23 parity, Rob 2026-10-22 + 2026-11-01):
 * a fixed 100dvh shell — the document itself never scrolls, pans,
 * bounces or zooms; the stop list is the one scroller, contained. */
html { height: 100%; overscroll-behavior: none; }
body {
  font: 15px/1.45 -apple-system, "Segoe UI", Roboto, sans-serif;
  background: var(--bg); color: var(--ink);
  -webkit-tap-highlight-color: transparent;
  height: 100dvh; overflow: hidden; overscroll-behavior: none;
  display: flex; flex-direction: column; touch-action: pan-y;
}
/* THE APP ROOT — the shell's one child, and the presentation
 * lock's turning frame (see the PRESENTATION LOCK block further
 * down). Unturned it is exactly the old body shell: a flex
 * column filling the body, and every fixed element inside it
 * still resolves against the viewport, as before. */
#rotRoot { flex: 1 1 auto; min-height: 0; min-width: 0; position: relative;
  display: flex; flex-direction: column; }
button { font: inherit; color: inherit; background: none; border: none; cursor: pointer; }
input, select, textarea { font: inherit; font-size: 16px; color: var(--ink); background: var(--card);
  border: 1px solid var(--line); border-radius: 8px; padding: 8px 10px; width: 100%; }
label { display: block; margin: 10px 0; color: var(--dim); font-size: 13px; }
label input, label select { margin-top: 4px; color: var(--ink); }

/* pull-to-refresh grab band (trademark piece) */
/* THE BAND'S SEAT (Rob's standalone defect, 2026-11-02): the page
 * runs viewport-fit=cover under a black-translucent status bar, so
 * in the home-screen (standalone) surface the top of the page lies
 * UNDER the status bar, inside the strip the system owns — a touch
 * that starts there never reaches the page. The old 18px band sat
 * wholly inside that strip (pill centre 9px down, the strip ~47px),
 * so the pull was dead in standalone while the identical pull fired
 * in a browser tab, where the page begins below all chrome. The
 * band now seats its pill below the device's own safe-area inset:
 * env() reports the true inset on the phone (the full strip in
 * standalone, 0 in a browser tab, and 0 for the top edge in
 * landscape — which is also why the turned presentation states are
 * untouched: a turn stands only while the device is held across).
 * touch-action: none — a downward drag on the band is the pull,
 * never a pan the browser may claim (a claimed pan ends the touch
 * stream in touchcancel, which the old code never listened for). */
#grabBand { position: relative; flex: none; height: calc(50px + env(safe-area-inset-top, 0px)); display: flex; justify-content: center; align-items: flex-end; padding-bottom: 6px; z-index: 40; background: var(--bg); touch-action: none; }
/* The refresh receipt chip (refreshSync): a spinning ring and the
 * word Refreshing… centered on the grab band, over the pill — the
 * spot the pulling eye is already looking at. Shown only while a
 * user refresh is in flight (minimum 700ms, set in refreshSync). */
#pullChip { position: absolute; left: 50%; bottom: 3px; transform: translateX(-50%);
  display: flex; align-items: center; gap: 7px; padding: 5px 12px 5px 9px;
  border-radius: 999px; background: var(--accent); color: #fff;
  font-size: 13px; font-weight: 700; line-height: 1; white-space: nowrap;
  opacity: 0; visibility: hidden; transition: opacity .15s ease, visibility .15s;
  z-index: 41; pointer-events: none; }
#pullChip.on { opacity: 1; visibility: visible; }
#pullChip .pcSpin { width: 14px; height: 14px; flex: none; border-radius: 50%;
  border: 2px solid rgba(255,255,255,.35); border-top-color: #fff;
  animation: pcspin .7s linear infinite; }
@keyframes pcspin { to { transform: rotate(360deg); } }
.grabPill { width: 44px; height: 5px; border-radius: 3px; background: var(--dim); opacity: .5; will-change: transform; }
.grabPill.armed { background: var(--accent); opacity: 1; width: 58px; }
/* A fired pull holds its armed face until the refresh settles
 * (the same receipt the sync line carries in words). */
.grabPill.held { background: var(--accent); opacity: 1; width: 58px; }
.grabPill.spring { transition: transform .3s cubic-bezier(.2,.9,.25,1.15), width .15s ease; }

/* header */
#appHeader { flex: none; z-index: 39; background: var(--bg);
  padding: 4px 12px 8px; border-bottom: 1px solid var(--line); }
.hrow1 { display: flex; align-items: center; gap: 6px; }
.routeName { font-weight: 700; font-size: 17px; margin-right: auto; cursor: pointer; text-decoration: underline dotted; text-underline-offset: 3px; }
.cornerBtn { width: 36px; height: 36px; border-radius: 10px; background: var(--card);
  border: 1px solid var(--line); font-size: 16px; }
/* SORT YOUR STACK HEADER DOOR (Rob, 2026-11-04): the paper stack
 * icon sits beside the theme button, with clear space on both
 * sides. The row's ordinary gap is 6px; this button carries 16px
 * on its left and 10px on its right, so its border to border gaps
 * measure 22px from Theme and 16px toward Settings. */
.stackBtn { margin-left: 16px; margin-right: 10px; display: flex;
  align-items: center; justify-content: center; color: var(--ink); }
.stackBtn svg { width: 21px; height: 21px; display: block; }
.outboxBadge { background: var(--danger); color: #fff; border-radius: 10px;
  padding: 1px 8px; font-size: 12px; }
.summaryLine { color: var(--dim); font-size: 13px; margin: 2px 0 6px; min-height: 18px; }
#modeTabs { display: flex; gap: 6px; }
#modeTabs button { flex: 1; padding: 8px 0; border-radius: 10px; background: var(--card);
  border: 1px solid var(--line); font-weight: 600; }
#modeTabs button.on { background: var(--accent); color: #fff; border-color: var(--accent); }
#prepBar, #editBar { display: flex; gap: 6px; margin-top: 8px; align-items: center; }
#prepBar[hidden], #editBar[hidden] { display: none; }
/* THE PREP BAR OWNS TWO ROWS (defect fix, 2026-11-04): the
 * Manual|Smart pair takes the full first row; the action buttons
 * (Reoptimize, Reverse) take the second. Crowded
 * into one row, the bar's ~476px of controls in a 366px phone bar
 * made flex crush the segSwitch to 34px, and its overflow:hidden
 * hid the Smart half entirely in Manual — the organizer switch
 * looked gone and was untappable (Rob's field report, 2026-11-04).
 * The pair is the organizer door; it never shares its row. */
#prepBar { flex-wrap: wrap; }
#prepBar #planSeg { flex: 1 1 100%; }
#prepBar #planSeg button { flex: 1; white-space: nowrap; }
/* SEARCH MUST LOOK LIKE SEARCH (Rob, 2026-11-01): he found a tiny
 * field he could not identify as search and could not SEE what he
 * typed in it. The search now owns its own full-width row — a
 * magnifier and a real input wide enough to show the typed text.
 * (Filtering behavior is unchanged; this is presentation only.) */
#editBar { flex-direction: column; align-items: stretch; }
#editBar .searchRow { display: flex; align-items: center; gap: 8px; background: var(--card);
  border: 1.5px solid var(--line); border-radius: 12px; padding: 0 12px; min-height: 44px; }
#editBar .searchIco { font-size: 17px; flex: none; }
#editBar input { flex: 1; border: 0; background: transparent; min-width: 0; padding: 10px 0; }
#editBar .editBtns { display: flex; gap: 6px; flex-wrap: wrap; }
.segSwitch { display: flex; border: 1px solid var(--line); border-radius: 10px; overflow: hidden; }
.segSwitch button { padding: 7px 12px; background: var(--card); font-weight: 600; }
.segSwitch button.on { background: var(--accent); color: #fff; }
.smallBtn { padding: 7px 10px; border-radius: 10px; background: var(--card); border: 1px solid var(--line); font-weight: 600; white-space: nowrap; }
.smallBtn.primary { background: var(--accent); color: #fff; border-color: var(--accent); }
.syncLine { font-size: 12px; color: var(--dim); margin-top: 4px; min-height: 14px; }
/* A failed refresh says so on the sync line (refreshSync receipt). */
.syncLine.failed { color: var(--danger); }

/* map */
#mapWrap { position: relative; height: 46vh; z-index: 5; flex: none; }
#map { position: absolute; inset: 0; }
/* THE MAP HEADER — ONE UNIT (Rob, 2026-11-03 16:16–16:17,
 * superseding the scattered rules this replaces). The full-map
 * view's header is a single bar across the top of the map: the
 * header text (the route's name) and the Close (return) button
 * attached together in one flex row — the text takes the space
 * that is there, the Close sits at the bar's right end, and the
 * two cannot overlap because they share one container. The bar
 * lives INSIDE the full-map view only (Rob, 2026-11-01: a Close
 * rendered behind the fixed header in the normal view is
 * unreachable); outside full-map it is not displayed anywhere.
 * This is the ONLY seat either piece has: the base rule that
 * positioned Close on its own, and the landscape section's
 * competing display rules for the same button, are removed —
 * one implementation, identical in Edit, Prep and Run, in
 * portrait and landscape, on the first open and the fiftieth. */
.mapHead { display: none; }
body.fullmap .mapHead { display: flex; position: absolute; top: 10px; left: 10px; right: 10px;
  z-index: 500; align-items: flex-start; gap: 10px; pointer-events: none; }
.mapHeadText { flex: 1 1 auto; min-width: 0; font-weight: 700; color: var(--ink);
  background: var(--card); border: 1px solid var(--line); border-radius: 10px; padding: 9px 12px;
  box-shadow: 0 2px 10px rgba(0,0,0,.3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mapHeadText:empty { visibility: hidden; }
.mapClose { flex: none; pointer-events: auto;
  background: var(--card); border: 1px solid var(--line); border-radius: 10px; padding: 9px 14px; font-weight: 700;
  box-shadow: 0 2px 10px rgba(0,0,0,.3); }
#mapModeBtn { flex: .62; }
/* THE FURNITURE (identical in all modes): the glass recycle can
 * (~a foot high at phone scale) and the On hold paper stack, right
 * side, stack above the can, Feedback below — the stack's seat,
 * restored (a dead Map button sat there before; the map's door is
 * the mode bar now). */
.binBtn { position: fixed; right: 8px; bottom: 92px; z-index: 30; background: transparent;
  border: 0; padding: 2px; width: 76px; height: 94px; }
.binArt { width: 68px; height: 88px; filter: drop-shadow(0 3px 5px rgba(0,0,0,.35)); }
.binStage9 .binCanG { transform: rotate(64deg); transform-origin: 60% 88%; }
.holdBtn { position: fixed; right: 8px; bottom: 196px; z-index: 30; background: transparent;
  border: 0; padding: 2px; display: flex; flex-direction: column; align-items: center; gap: 0; }
.holdBtn[hidden] { display: none; }
.holdArt { width: 58px; height: 60px; filter: drop-shadow(0 3px 5px rgba(0,0,0,.35)); }
.holdLabel { font-size: 11.5px; font-weight: 800; color: var(--ink); background: var(--card);
  border: 1px solid var(--line); border-radius: 8px; padding: 1px 7px; margin-top: -6px; }
.stackBadge { position: absolute; top: -2px; right: -2px; background: #d81f26; color: #fff;
  border-radius: 10px; font-size: 12px; font-weight: 800; padding: 1px 6px; min-width: 20px;
  text-align: center; box-shadow: 0 1px 4px rgba(0,0,0,.35); }
.binBtn .stackBadge, .holdBtn .stackBadge { position: absolute; }
.binRow .stamp { font-size: 10.5px; font-weight: 800; letter-spacing: .4px; padding: 2px 6px;
  border-radius: 5px; border: 1.6px solid; display: inline-block; transform: rotate(-2deg); }
.stampServed { color: #15803d; border-color: #15803d; }
.stampCancelled { color: #d81f26; border-color: #d81f26; }
.stampDeleted { color: #6b7280; border-color: #6b7280; }
.binEmptyRow { margin-top: 12px; display: grid; gap: 6px; }

/* floating Feedback button (trademark piece) */
.feedbackBtn { position: fixed; right: 12px; bottom: 24px; z-index: 30;
  background: var(--accent); color: #fff; border-radius: 22px; padding: 11px 18px;
  font-weight: 700; box-shadow: 0 3px 12px rgba(0,0,0,.3); }

/* THE MOTION RULE (Rob, 2026-11-03 16:12–16:14; Run only): while
 * the vehicle is moving, Feedback, the On Hold control and the
 * trash can slide OFF the screen together — one body class, one
 * transition, the same rightward slide the full-map view uses —
 * and slide back when it stops. While they are off, they take
 * no taps: pointer-events none, so no invisible tappable area
 * is left behind. (The full-map rule below keeps its own seat:
 * in full map the bin and hold are off regardless of motion,
 * and Feedback stays — the map's standing exception. The motion
 * rule governs the normal Run view.) */
.feedbackBtn, .binBtn, .holdBtn { transition: transform .38s ease, opacity .38s ease; }
body.motion-hide .feedbackBtn, body.motion-hide .binBtn, body.motion-hide .holdBtn {
  transform: translateX(130%); opacity: 0; pointer-events: none; }

/* the list — the shell's ONE scroller */
#listWrap { flex: 1; overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch; padding: 10px 10px 120px; }
/* RUN ANCHOR (2026-11-02): in Run the list vertical drag is driven by hand (app.js) so the anchor can resist, spring and lock; the platform own scroll claim on the gesture is refused here (touch-action) and in the driver touchmove guard. Prep and Edit keep native scrolling. */
body.mode-run #listWrap { touch-action: none; }

/* RUN'S NAVIGATE BAR — one big control at the top of Run (Rob,
 * 2026-10-18 / 2026-11-01). Thumb-sized, always visible in Run. */
#runNavBar { flex: none; padding: 8px 10px; background: var(--bg); border-bottom: 1px solid var(--line); }
#runNavBar[hidden] { display: none; }
.runNavBtn { display: flex; align-items: center; justify-content: center; gap: 10px; width: 100%;
  min-height: 58px; border-radius: 14px; background: var(--accent); color: #fff;
  font-size: 20px; font-weight: 800; box-shadow: 0 3px 10px rgba(0,0,0,.25); }
.runNavBtn[disabled] { opacity: .45; box-shadow: none; }
/* NAVIGATE ALL — the quieter second door beside Navigate (Rob,
 * 2026-11-04): same thumb-sized shape, outline dress, because the
 * loud solid button stays the one-stop handover. It renders only
 * when the chosen nav program can receive a succession and a
 * succession exists (see renderRunNav) — [hidden] needs an author
 * rule because .runNavBtn's display:flex outranks the UA default. */
.runNavAllBtn { margin-top: 8px; background: var(--bg); color: var(--accent);
  border: 2px solid var(--accent); box-shadow: none; font-size: 18px; }
.runNavAllBtn[hidden] { display: none; }
#runNavAllNote[hidden] { display: none; }
.runNavArrow { font-size: 22px; }
.runNavAddr { text-align: center; color: var(--dim); font-size: 13px; font-weight: 600; margin-top: 5px; }

/* orientation cycling toggle (gear) — icon + brief state word */
.orientBtn { display: inline-flex; align-items: center; gap: 9px; padding: 8px 14px;
  border-radius: 12px; background: var(--bg); border: 1px solid var(--line); font-weight: 700; font-size: 15px; }
.orientBtn svg { flex: none; }

/* overflow menu icons — Restart's recycle arrow at least twice the
 * old size (Rob, 2026-11-01: it was too small to see). */
.menuIco { display: inline-block; width: 34px; text-align: center; font-size: 20px; vertical-align: middle; }
.menuIcoBig { font-size: 34px; line-height: 1; }
.sectionHead { font-size: 12px; letter-spacing: .08em; text-transform: uppercase;
  color: var(--dim); margin: 14px 2px 6px; }
/* INACTIVE SECTION HEADS (clone round, 2026-11-04): the Attempted
   today and Skipped heads wear an inactive treatment too — a
   section that is not active must LOOK inactive, head included.
   Before this round the Attempted head wore the plain active head
   colour in every theme, and the Skipped head's .dark colour
   (--band-skip-ink) was near-invisible in day and BRIGHTER than
   the active head at night. Each colour below is a clear step
   down from the same theme's active head colour, taken from the
   inactive cards' own text palette, and stays legible. (.dark is
   kept as an alias selector; no render uses it any longer.) */
.sectionHead.inactive, .sectionHead.dark { color: #828b97; }
body.theme-night .sectionHead.inactive, body.theme-night .sectionHead.dark { color: #767f8d; }
body.theme-glass .sectionHead.inactive, body.theme-glass .sectionHead.dark { color: #828b99; }
body.theme-glass[data-glassface="day"] .sectionHead.inactive, body.theme-glass[data-glassface="day"] .sectionHead.dark { color: #75808e; }

.card { background: var(--card); border: 1px solid var(--line); border-radius: 14px;
  margin: 0 0 6px; overflow: hidden; }
.card .cardMain { display: flex; align-items: stretch; }
.rail { width: 60px; flex: 0 0 60px; border-right: 1px solid var(--line);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px; padding: 5px 0; }
.rail .num { font-size: 17px; font-weight: 800; }
.rail .eta { font-size: 11px; color: var(--dim); text-align: center; line-height: 1.25; padding: 0 2px; }
.cardBody { flex: 1; padding: 6px 10px; min-width: 0; }
.cardName { font-weight: 700; font-size: 14px; }
.cardAddr { color: var(--dim); font-size: 13px; font-weight: 600; }
.cardMeta { color: var(--dim); font-size: 12px; margin-top: 2px; }
.badgeOverdue { color: var(--danger); font-weight: 700; }
.badgeHeld { color: #9a6a00; font-weight: 700; }
body.theme-night .badgeHeld { color: #e3b341; }
.unsynced { color: var(--danger); font-weight: 700; font-size: 12px; }
.cardBtns { display: flex; gap: 6px; padding: 0 10px 7px 70px; flex-wrap: wrap; }
.cardBtns button { padding: 5px 10px; border-radius: 9px; background: var(--bg);
  border: 1px solid var(--line); font-weight: 600; font-size: 13px; }
.cardDetail { padding: 0 12px 8px 70px; font-size: 13px; color: var(--dim); }
/* Edit stop on the front of the card (Rob, 2026-11-03): smaller
   and quieter than a standard card button — present, not loud. */
.cardBtns button.editFront { font-size: 12px; font-weight: 600; padding: 3px 9px;
  border-radius: 999px; color: var(--dim); background: transparent; }
.cardDetail .notes { color: var(--ink); white-space: pre-wrap; }

/* ATTEMPTED TODAY — ONE TREATMENT IN EVERY MODE (Rob, 2026-11-02):
   a stop attempted today wears the washed, receded grey Rob sees
   in Prep, and sits banded above the active stops in Edit, Prep,
   and Run alike. The treatment hangs on the card's own .attToday
   class (cardHTML), never on a mode's band wrapper, so no surface
   can resurrect the old accidents: the generic .card.nonactive
   near-black block that Edit showed in every theme, Prep's strip
   face painting the theme card color under ghost-white text in
   day, and the night band's #04060a black fill. Day values are
   the Run band's own (the approved recession of 2026-11-02);
   night is a DIM GREY fill with its text tuned to the same
   recession ratios, not a black block; glass carries the washed
   grey translucent in both faces. The card's controls stay
   usable; a glance slides past, a lean-in still reads. Served
   and set-aside cards keep the generic .card.nonactive seat
   further down. SKIPPED JOINS THIS TREATMENT (Rob, live verdict
   2026-11-03): the Attempted Today coloration is exactly what
   he wants for skipped at the bottom — the same coloration,
   copied value for value, Attempted Today's own colors
   untouched. Every selector below therefore names .isSkipped
   beside .attToday. */
.card.nonactive.attToday, .card.nonactive.isSkipped { background: #e3e5e9; color: #79828f; border-color: #d2d6dc; }
.card.nonactive.attToday .cardName, .card.nonactive.isSkipped .cardName { color: #69727f; }
.card.nonactive.attToday .cardAddr, .card.nonactive.attToday .cardMeta, .card.nonactive.isSkipped .cardAddr, .card.nonactive.isSkipped .cardMeta { color: #828b97; }
.card.nonactive.attToday .rail, .card.nonactive.isSkipped .rail { border-color: #d2d6dc; }
.card.nonactive.attToday .rail .num, .card.nonactive.isSkipped .rail .num { color: #79828f; }
.card.nonactive.attToday .rail .eta, .card.nonactive.isSkipped .rail .eta { color: #9aa1ab; }
.card.nonactive.attToday .cardBtns button, .card.nonactive.isSkipped .cardBtns button { background: #eceef1; color: #79828f; border-color: #d2d6dc; }
.card.nonactive.attToday .cardFace, .card.nonactive.isSkipped .cardFace { background: #e3e5e9; }
.attToday .wchip, .isSkipped .wchip { background: #ccd3dd; color: #5f6b7a; }
.attToday .wchip.sat, .isSkipped .wchip.sat { background: transparent; color: #9aa1ab; opacity: 1; box-shadow: inset 0 0 0 1px #ccd1d9; }
body.theme-night .card.nonactive.attToday, body.theme-night .card.nonactive.isSkipped { background: #232830; color: #79828f; border-color: #333a44; }
body.theme-night .card.nonactive.attToday .cardName, body.theme-night .card.nonactive.isSkipped .cardName { color: #868f9b; }
body.theme-night .card.nonactive.attToday .cardAddr, body.theme-night .card.nonactive.attToday .cardMeta, body.theme-night .card.nonactive.isSkipped .cardAddr, body.theme-night .card.nonactive.isSkipped .cardMeta { color: #767f8d; }
body.theme-night .card.nonactive.attToday .rail, body.theme-night .card.nonactive.isSkipped .rail { border-color: #333a44; }
body.theme-night .card.nonactive.attToday .rail .num, body.theme-night .card.nonactive.isSkipped .rail .num { color: #868f9b; }
body.theme-night .card.nonactive.attToday .rail .eta, body.theme-night .card.nonactive.isSkipped .rail .eta { color: #626b78; }
body.theme-night .card.nonactive.attToday .cardBtns button, body.theme-night .card.nonactive.isSkipped .cardBtns button { background: rgba(255,255,255,.05); color: #8f97a3; border-color: rgba(255,255,255,.16); }
body.theme-night .card.nonactive.attToday .cardFace, body.theme-night .card.nonactive.isSkipped .cardFace { background: #232830; }
body.theme-night .attToday .wchip, body.theme-night .isSkipped .wchip { background: #31363f; color: #98a2b3; }
body.theme-night .attToday .wchip.sat, body.theme-night .isSkipped .wchip.sat { background: transparent; color: #626b78; box-shadow: inset 0 0 0 1px #39404a; }
body.theme-glass .card.nonactive.attToday, body.theme-glass .card.nonactive.isSkipped { background: rgba(35,40,48,.55); color: #7d8694; }
body.theme-glass .card.nonactive.attToday .cardName, body.theme-glass .card.nonactive.isSkipped .cardName { color: #828b99; }
body.theme-glass .card.nonactive.attToday .cardAddr, body.theme-glass .card.nonactive.attToday .cardMeta, body.theme-glass .card.nonactive.isSkipped .cardAddr, body.theme-glass .card.nonactive.isSkipped .cardMeta { color: #767f8d; }
body.theme-glass .card.nonactive.attToday .cardBtns button, body.theme-glass .card.nonactive.isSkipped .cardBtns button { color: #828b99; }
body.theme-glass .card.nonactive.attToday .cardFace, body.theme-glass .card.nonactive.isSkipped .cardFace { background: transparent; }
body.theme-glass .attToday .wchip, body.theme-glass .isSkipped .wchip { background: rgba(49,54,63,.8); color: #98a2b3; }
body.theme-glass .attToday .wchip.sat, body.theme-glass .isSkipped .wchip.sat { background: transparent; color: #7d8694; box-shadow: inset 0 0 0 1px rgba(125,134,148,.5); }
body.theme-glass[data-glassface="day"] .card.nonactive.attToday, body.theme-glass[data-glassface="day"] .card.nonactive.isSkipped { background: rgba(226,230,236,.55); color: #6d7684; }
body.theme-glass[data-glassface="day"] .card.nonactive.attToday .cardName, body.theme-glass[data-glassface="day"] .card.nonactive.isSkipped .cardName { color: #5d6673; }
body.theme-glass[data-glassface="day"] .card.nonactive.attToday .cardAddr, body.theme-glass[data-glassface="day"] .card.nonactive.attToday .cardMeta, body.theme-glass[data-glassface="day"] .card.nonactive.isSkipped .cardAddr, body.theme-glass[data-glassface="day"] .card.nonactive.isSkipped .cardMeta { color: #75808e; }
body.theme-glass[data-glassface="day"] .card.nonactive.attToday .cardBtns button, body.theme-glass[data-glassface="day"] .card.nonactive.isSkipped .cardBtns button { background: rgba(255,255,255,.35); color: #69727f; }

/* THE NEXT ACTIVE STOP CARRIES THE PROMINENCE (Rob, 2026-11-02):
   the first active card — the one the Navigate bar names — is the
   strongest card on the screen: accent border and ring. */
.card.nextStop { border-color: var(--accent); box-shadow: 0 0 0 1.5px var(--accent), 0 4px 14px rgba(10,20,40,.18); }
body.theme-glass .card.nextStop { border-color: rgba(125,180,255,.9); box-shadow: 0 0 0 1.5px rgba(125,180,255,.75), var(--glass-bevel); }
.band-skipped .card { background: var(--band-skip); color: var(--band-skip-ink); border-color: #000; }

/* NON-ACTIVE STOPS MUCH DARKER (owner point, Rob 2026-10-30 18:54):
   attempted-today, skipped, served/done and Smart set-aside cards
   sink far below the active cards so the run stands out at a
   glance — dark, never unreadable. Active cards are unchanged. */
.card.nonactive { background: #2e323b; color: #e6e9ee; border-color: #17191d; }
.card.nonactive .cardName { color: #f2f3f6; }
.card.nonactive .cardAddr, .card.nonactive .cardMeta { color: #b4bac4; }
.card.nonactive .cardBtns button { background: rgba(255,255,255,.09); color: #e6e9ee; border-color: rgba(255,255,255,.28); }
.card.nonactive .rail { border-color: rgba(255,255,255,.18); }
.card.nonactive .rail .eta { color: #9aa1ad; }
body.theme-night .card.nonactive { background: #04060a; color: #d5d9e0; }
body.theme-glass .card.nonactive { background: rgba(6,8,12,.66); }
/* The strip face honours the generic nonactive seat (clone round,
   2026-11-04): before this round .cardFace painted var(--card)
   over it, so a served card in Edit wore near-white text on a
   white face in day, and an active-looking face at night — an
   inactive card that did not look inactive and, in day, could
   barely be read. The attToday / isSkipped faces above keep
   their own washed colours: their selectors name one more class
   than this rule and are untouched. */
.card.nonactive .cardFace { background: #2e323b; }
body.theme-night .card.nonactive .cardFace { background: #04060a; }
body.theme-glass .card.nonactive .cardFace { background: transparent; }
.band-skipped .cardAddr, .band-skipped .cardName, .band-skipped .cardMeta { color: var(--band-skip-ink); }
.band-skipped .rail { border-color: #3a3f47; }
.bracket { border: 2px dashed var(--dim); border-radius: 16px; padding: 6px 6px 0; margin-bottom: 8px; }
.bracket .card { margin-bottom: 6px; }

/* window chips + tiles: owed stands out bright, satisfied almost disappears */
.wchips { display: flex; gap: 4px; margin-top: 3px; }
.wchip { font-size: 11px; font-weight: 800; border-radius: 6px; padding: 2px 7px;
  background: var(--chip-owed); color: var(--chip-owed-ink); }
.wchip.sat { background: transparent; color: var(--dim); opacity: .38; font-weight: 600;
  box-shadow: inset 0 0 0 1px var(--line); }
.wtile { display: inline-block; min-width: 64px; text-align: center; border-radius: 10px;
  padding: 8px 6px; margin: 3px 3px 3px 0; background: var(--chip-owed); color: var(--chip-owed-ink);
  font-weight: 700; font-size: 12px; }
.wtile.sat { background: transparent; color: var(--dim); opacity: .38; font-weight: 600;
  box-shadow: inset 0 0 0 1px var(--line); }
.wtile .st { display: block; font-size: 10px; }

/* the swipe strips (owner ruling, 2026-10-30): right-slide reveals
 * Serve · Hold · Skip on the left edge; left-slide reveals Attempt
 * alone on the right edge; nothing is duplicated across the strips.
 * Invisible until the slide starts; tinted glass in glass mode. */
.card.hasStrip { position: relative; overflow: hidden; }
.cardFace { position: relative; background: var(--card); touch-action: pan-y;
  -webkit-user-select: none; user-select: none; }
.strip { position: absolute; top: 0; bottom: 0; display: flex; visibility: hidden; pointer-events: none; }
.card.sliding .strip { visibility: visible; }
.stripL { left: 0; }
/* Row-reversed so the right strip's FIRST arm-order cell (Skip;
 * on a skipped stop, Hold) sits at the far right edge of the card
 * and later cells step inward — arm order reads as screen order,
 * outside-in (owner layout, 2026-11-03). */
.stripR { right: 0; flex-direction: row-reverse; }
.stripBtn { width: 76px; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 2px; color: #fff; font-weight: 700; font-size: 13px; }
.stripBtn .st-ic { font-size: 17px; }
.st-serve { background: #15803d; }
.st-hold { background: #b45309; }
.st-skip { background: #6b7280; }
.st-attempt { background: #1d4ed8; }
.st-reopen { background: #0e7490; }
.card[data-armed="serve"] .st-serve, .card[data-armed="hold"] .st-hold,
.card[data-armed="skip"] .st-skip, .card[data-armed="attempt"] .st-attempt,
.card[data-armed="reopen"] .st-reopen {
  box-shadow: inset 0 0 0 3px #fff; filter: brightness(1.15); }
body.theme-glass .st-serve { background: rgba(21,128,61,.55); }
body.theme-glass .st-hold { background: rgba(180,83,9,.55); }
body.theme-glass .st-skip { background: rgba(107,114,128,.55); }
body.theme-glass .st-attempt { background: rgba(29,78,216,.55); }
body.theme-glass .st-reopen { background: rgba(14,116,144,.55); }
body.theme-glass .cardFace { background: transparent; }
.stripBar { position: fixed; top: 64px; left: 50%; transform: translateX(-50%); z-index: 55;
  background: #11151c; color: #fff; border-radius: 20px; padding: 9px 22px; font-weight: 800;
  box-shadow: 0 4px 18px rgba(0,0,0,.4); white-space: nowrap; }

/* sheets */
#sheetScrim { position: fixed; inset: 0; background: rgba(0,0,0,.45); z-index: 60; }
.sheet { position: fixed; left: 0; right: 0; bottom: 0; z-index: 61; background: var(--card);
  color: var(--ink); border-radius: 18px 18px 0 0; padding: 16px 16px 26px;
  max-height: 88vh; overflow-y: auto; box-shadow: 0 -4px 24px rgba(0,0,0,.3); }
.sheet.full { top: 0; border-radius: 0; max-height: none; }
/* The head (title + close X) sticks while a long sheet scrolls —
 * a close control that scrolls away is an unreachable control
 * (glass-build census, 2026-11-01: the taller glass gear sheet
 * scrolled its X off the top). */
.sheetHead { display: flex; align-items: center; justify-content: space-between;
  position: sticky; top: -16px; z-index: 3; background: inherit;
  margin: -16px -16px 0; padding: 16px 16px 8px; }
.sheetHead h3 { margin: 0; }
.sheetX { font-size: 16px; width: 34px; height: 34px; border-radius: 9px; background: var(--bg); border: 1px solid var(--line); }
.sheetSub { color: var(--dim); font-size: 13px; }
.bigBtn { display: block; width: 100%; padding: 13px; border-radius: 12px; margin-top: 10px;
  background: var(--bg); border: 1px solid var(--line); font-weight: 700; }
.bigBtn.primary { background: var(--accent); color: #fff; border-color: var(--accent); }
.bigBtn.danger { background: var(--danger); color: #fff; border-color: var(--danger); }
.wideBtn { display: block; width: 100%; text-align: left; padding: 12px; border-radius: 12px;
  background: var(--bg); border: 1px solid var(--line); margin-top: 10px; font-weight: 600; }
.wideBtn.danger { color: var(--danger); }
#runResetBtn[hidden] { display: none; } /* RUN RESET Prep-only gate (clone round, 2026-11-04 2130): .wideBtn display:block otherwise overrides [hidden], so the button painted in Edit/Run too */
.sheetHeadBtns { display: flex; gap: 6px; align-items: center; }
/* SORT YOUR STACK LIST. The original Item 26 form, restored:
 * one row per order in current route order, large type that can
 * be read at arm's length while the paper packets are in hand.
 * A stop with more than one packet wears the heavy group frame;
 * the frame alone carries that meaning. */
.stack-list { margin: 6px 0 14px; }
.stack-row { display: flex; align-items: baseline; gap: 14px;
  padding: 11px 6px; border-bottom: 2px solid var(--line); }
.stack-pos { font-size: 22px; font-weight: 800; min-width: 2ch;
  text-align: right; color: var(--dim); }
.stack-ord { font-size: 24px; font-weight: 800; letter-spacing: .01em;
  white-space: nowrap; }
.stack-name { font-size: 22px; font-weight: 600; overflow-wrap: anywhere; }
.stack-empty { font-size: 20px; padding: 18px 6px; color: var(--dim); }
.stack-group { border: 3px solid var(--ink); border-radius: 14px;
  padding: 2px 10px; margin: 10px 0; }
.stack-group .stack-row:last-child { border-bottom: none; }
.stackInfo { border: 1px solid var(--line); border-radius: 12px;
  padding: 12px 14px; margin: 8px 0 12px; font-size: 16.5px; line-height: 1.45; }
.stackInfo p { margin: 0 0 8px; }
.stackInfo p:last-child { margin-bottom: 0; }
.gearRow { display: flex; align-items: center; justify-content: space-between; color: var(--ink); font-size: 15px; }
.gearRow select { width: auto; min-width: 170px; }
/* THE LIQUID GLASS BLOCK (canonical — house glass standard): the
 * Settings unit that stands first in the gear sheet. The theme row
 * is the theme button spelled out (pressed = accent ring +
 * aria-pressed); the tint swatches are the house row — Clear, the
 * five presets in house order, then the custom picker in its
 * dashed ring. A picked swatch wears the accent ring. */
.glassBlockHead { margin: 10px 0 2px; font-size: 16px; color: var(--ink); }
#glassBlock { border-bottom: 1px solid var(--line); padding-bottom: 10px; margin-bottom: 6px; }
.themeRow { display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }
.themeRow button { padding: 7px 10px; border-radius: 999px; border: 1px solid var(--line);
  background: var(--card); color: var(--ink); font-weight: 600; font-size: 13px; }
.themeRow button.on { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent); }
.tintSwatches { display: flex; gap: 7px; align-items: center; }
.tintSwatches button { width: 30px; height: 30px; border-radius: 50%; border: 1px solid var(--line);
  background: var(--sw, transparent); color: var(--ink); font-size: 15px; padding: 0; line-height: 1; }
.tintSwatches button.on { box-shadow: 0 0 0 2px var(--accent); border-color: var(--accent); }
.tintSwatches .swClear { background: transparent; }
.tintSwatches .swCustom { width: 30px; height: 30px; border-radius: 50%; border: 2px dashed var(--dim);
  display: inline-flex; overflow: hidden; cursor: pointer; }
.tintSwatches .swCustom input { width: 100%; height: 100%; padding: 0; border: 0; background: transparent; cursor: pointer; }
.menuItem { display: block; width: 100%; text-align: left; padding: 12px 10px;
  border-bottom: 1px solid var(--line); font-weight: 600; }
.outcomeGrid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin: 10px 0; }
.outcomeGrid button { padding: 12px 8px; border-radius: 11px; background: var(--bg);
  border: 1px solid var(--line); font-weight: 600; }
.outcomeGrid button.on { background: var(--accent); color: #fff; border-color: var(--accent); }
.errText { color: var(--danger); font-size: 13px; min-height: 16px; }
.ver, .verLine { color: var(--dim); font-size: 12px; }
.wipeList { margin: 6px 0 6px 18px; padding: 0; }
.tilePick { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; margin: 10px 0; }
.tilePick button { padding: 7px 10px; border-radius: 9px; background: var(--bg); border: 1px solid var(--line); font-weight: 600; }
.tilePick button.on { background: var(--accent); color: #fff; }
textarea { width: 100%; }
.binRow { padding: 9px 4px; border-bottom: 1px solid var(--line); font-size: 14px; }
.fbListHead { color: var(--dim); font-size: 13px; margin: 12px 0 2px; }
.fbList { max-height: 38vh; overflow-y: auto; }
.fbItem { padding: 9px 4px; border-bottom: 1px solid var(--line); font-size: 14px; }
.fbItem .fbBody { white-space: pre-wrap; }
.fbItem .fbMeta { color: var(--dim); font-size: 12px; margin-top: 3px; }
.fbEmpty { color: var(--dim); font-size: 13px; padding: 8px 4px; }
.miniBtn { padding: 4px 9px; border-radius: 8px; background: var(--bg); border: 1px solid var(--line); font-weight: 600; font-size: 12px; margin-left: 8px; }
.pinIcon { background: transparent; border: none; }
.pinDot { width: 22px; height: 22px; border-radius: 50%; background: var(--accent); color: #fff;
  border: 2px solid #fff; font-size: 11px; font-weight: 800; display: flex; align-items: center; justify-content: center;
  box-shadow: 0 1px 5px rgba(0,0,0,.4); }
.pinDot.overdue { background: var(--danger); }
.pinDot.done { background: #6b7280; }

/* GLASS — universal: everything glass except the words and the map.
 * In glass the map rides behind the whole screen; every surface is a
 * frosted layer over it. */
body.theme-glass { --bg: transparent; --ink: #f2f5f9; --dim: #d6dfec; --line: rgba(255,255,255,.35);
  --card: var(--glass-card-bg); --chip-owed: rgba(23,74,139,.75); --chip-owed-ink: #eaf3ff;
  --band-att: rgba(6,8,12,.66); --band-att-ink: #ccd5e2; --band-skip: rgba(5,7,10,.72); --band-skip-ink: #c9d2e0; }
body.theme-glass #mapWrap { position: fixed; inset: 0; height: auto; z-index: -1; }
body.theme-glass #appHeader, body.theme-glass .card, body.theme-glass .cornerBtn,
body.theme-glass #modeTabs button, body.theme-glass .smallBtn, body.theme-glass .segSwitch button {
  background: var(--glass-chrome-bg); backdrop-filter: var(--glass-filter-chrome);
  -webkit-backdrop-filter: var(--glass-filter-chrome); }
body.theme-glass .card { background: var(--glass-card-bg);
  backdrop-filter: var(--glass-filter-card); -webkit-backdrop-filter: var(--glass-filter-card); }
/* Sheets carry the REFLECTION layer too (Rob, 2026-11-01 13:46):
 * a fixed sheen gradient, like the cards' and buttons' — it is a
 * separate layer from fill and blur and does NOT fade with either
 * slider, so a sheet at zero fill and zero frosting still reads
 * as a piece of glass. */
body.theme-glass .sheet { background: var(--glass-sheet-bg);
  background-image: linear-gradient(180deg, rgba(255,255,255,.20), rgba(255,255,255,.03) 40%, rgba(0,0,0,.05));
  backdrop-filter: var(--glass-filter-sheet); -webkit-backdrop-filter: var(--glass-filter-sheet); }
/* Readability over the busier map: words carry a soft dark halo in
 * glass so the raised transparency never costs legibility. */
body.theme-glass .card, body.theme-glass #appHeader, body.theme-glass #runNavBar {
  text-shadow: 0 1px 3px rgba(0,0,0,.45); }
body.theme-glass #runNavBar { background: var(--glass-chrome-bg);
  backdrop-filter: var(--glass-filter-nav); -webkit-backdrop-filter: var(--glass-filter-nav); }
body.theme-glass[data-glassface="night"] input, body.theme-glass[data-glassface="night"] select,
body.theme-glass[data-glassface="night"] textarea { background: rgba(255,255,255,.14); color: #fff; }
body.theme-glass[data-glassface="day"] input, body.theme-glass[data-glassface="day"] select,
body.theme-glass[data-glassface="day"] textarea { background: rgba(255,255,255,.14); color: #14181d; }
body.theme-glass #grabBand { background: transparent; }

/* GLASS BY DAY — THE INK FLIPS WITH THE TINT (glass-build fix,
 * 2026-11-01). The rebuild flipped the glass TINT with the sun
 * (white glass by day, dark by night) but kept NIGHT'S near-white
 * ink in both. Measured on real OSM tiles at the .26 default,
 * white text over the light day glass scored 1.07–1.44:1 — un-
 * readable, and the spec's own sentence requires the raised
 * transparency to stay "functional and readable". Production .91
 * never did this: its tinted glass rode over the day theme's dark
 * ink. So by day the words go dark and solid (doctrine: glass is
 * universal EXCEPT the words); by night nothing changes. */
/* When a strong tint flips the DAY glass face to night (the fill
 * deepened toward the dark base), the tile pane takes the house
 * glass darkening (the core's own treatment: brightness .52,
 * saturate .78) — otherwise a dark fill at mid opacity over a
 * bright street tile lands at a mid luminance where neither ink
 * can hold the §7 floors. Sun-up night never reaches this rule:
 * the night face already rides dark tiles. */
body.theme-glass[data-solar="day"][data-glassface="night"] .leaflet-tile-pane { filter: brightness(.52) saturate(.78); }
body.theme-glass[data-glassface="day"] { --ink: #14181d; --dim: #45536a; --line: rgba(20,24,29,.38); }
body.theme-glass[data-glassface="day"] .card, body.theme-glass[data-glassface="day"] #appHeader,
body.theme-glass[data-glassface="day"] #runNavBar { text-shadow: 0 1px 2px rgba(255,255,255,.55); }
body.theme-glass[data-glassface="day"] .cardAddr, body.theme-glass[data-glassface="day"] .cardName {
  text-shadow: 0 1px 0 rgba(255,255,255,.5), 0 -1px 1px rgba(0,0,0,.18); }
/* Card and sheet buttons join the glass treatment (they were base
 * --bg = transparent in glass, so "Edit stop" all but vanished
 * over a bright map). */
body.theme-glass .cardBtns button, body.theme-glass .miniBtn,
body.theme-glass .outcomeGrid button, body.theme-glass .tilePick button {
  background: var(--glass-chrome-bg); backdrop-filter: var(--glass-filter-chrome);
  -webkit-backdrop-filter: var(--glass-filter-chrome); }
/* The full-map header reads on any face: sheet-weight glass. */
body.theme-glass .mapClose, body.theme-glass .mapHeadText { background: var(--glass-sheet-bg);
  background-image: linear-gradient(180deg, rgba(255,255,255,.20), rgba(255,255,255,.03) 40%, rgba(0,0,0,.05));
  backdrop-filter: var(--glass-filter-sheet); -webkit-backdrop-filter: var(--glass-filter-sheet); }
/* The SELECTED tab/segment in glass: the glass chrome rule above
 * out-specificities the base .on rule, so the accent vanished and
 * white .on text sat on pale glass (measured: no pixel darker
 * than rgb(135,143,146) in the active tab). Accent-tinted glass
 * restores the state, in both solars. */
body.theme-glass #modeTabs button.on, body.theme-glass .segSwitch button.on {
  background: rgba(31,111,235,.55); color: #fff; }

/* LANDSCAPE — A DIFFERENT, BETTER TOOL (Rob, 2026-11-01): map and
 * stop list side by side, the map always present on the wide side,
 * the run's navigate bar across the top, cards a touch larger for
 * a driver's glance. Applied by JS only when the orientation
 * choice allows landscape (Auto / Locked Landscape); state and
 * place are untouched by rotation — layout only. */
body.land #rotRoot { display: grid; grid-template-columns: 46% 54%;
  grid-template-rows: auto auto auto 1fr;
  grid-template-areas: "grab grab" "head head" "nav nav" "map list"; }
body.land #grabBand { grid-area: grab; }
body.land #appHeader { grid-area: head; }
body.land #runNavBar { grid-area: nav; }
body.land #mapWrap { grid-area: map; height: auto; min-height: 0; }
body.land #listWrap { grid-area: list; min-height: 0; }
body.land .cardName { font-size: 17px; }
body.land .runNavBtn { min-height: 64px; font-size: 23px; }
/* (The landscape section used to carry its own display rules
 * for #mapClose — the duplicate removed 2026-11-03; the map
 * header bar above governs every orientation.) */
/* glass + landscape: the map is the grid pane, not a fixed backdrop */
body.theme-glass.land #mapWrap { position: relative; inset: auto; z-index: 5; }

/* ============ PRESENTATION LOCK (2026-11-02) ============
 * The gear's orientation lock, made real on every platform.
 * iOS web apps are given NO Screen Orientation lock API, so the
 * lock is presentation: while a lock stands and the device is
 * held the other way, the app root is sized with its width and
 * height swapped (100dvh across, 100dvw down — the presented
 * frame) and turned ±90° about the screen centre. The app
 * presents in the locked orientation at once and keeps
 * presenting in it as the device moves; the moment the device
 * reaches the locked orientation the turn drops and the app is
 * native again. The free (Auto) position carries no turn at
 * all — the app follows the device exactly as before. DOM
 * hit-testing follows the transform natively, so taps land
 * where they look like they land; app.js corrects the few
 * client-coordinate readers (Leaflet's positions, the slide /
 * drag / pull deltas). Fixed elements inside the root resolve
 * against the turned frame while it is turned — the sheets,
 * the furniture and the Feedback button present inside the
 * locked frame, as they should. */
body.rot90 #rotRoot, body.rot-90 #rotRoot { position: fixed; left: 50%; top: 50%;
  width: 100dvh; height: 100dvw; flex: none; }
body.rot90 #rotRoot { transform: translate(-50%, -50%) rotate(90deg); }
body.rot-90 #rotRoot { transform: translate(-50%, -50%) rotate(-90deg); }
/* vh-sized pieces re-measure against the PRESENTED frame while
 * turned: the map's 46% share of the frame's height, and the
 * sheets' height cap. (Land, full-map and glass already size
 * the map their own way and keep it.) */
body.rot90:not(.land):not(.fullmap):not(.theme-glass) #mapWrap,
body.rot-90:not(.land):not(.fullmap):not(.theme-glass) #mapWrap { height: 46%; }
body.rot90 .sheet:not(.full), body.rot-90 .sheet:not(.full) { max-height: 88%; }
/* Full-map inside the turned frame: the map wrap is a grid
 * item, and a fixed element whose containing block is its own
 * grid container is sized to its grid AREA, not to the inset —
 * unturned its containing block is the viewport and it fills
 * the screen as built, but turned it would stay pane-sized.
 * Standing the grid area down while full-map is up lets the
 * inset fill the presented frame; leaving full-map restores
 * the pane. */
body.rot90.fullmap #mapWrap, body.rot-90.fullmap #mapWrap { grid-area: auto; }

/* availability grid — filled paints NEAR-BLACK in every theme
 * (Patch 92); hollow cells are an INK OUTLINE on transparent
 * (Patch 92's design), never a fill. The rebuild first shipped
 * hollow = var(--bg): in the night theme that background is
 * itself near-black (#0d1117 against the filled #101216) and in
 * glass it is transparent over a dark sheet, so a cell Rob
 * emptied still looked filled and taps read as dead — his
 * 2026-11-01 report. The taps were registering all along. */
#availGridWrap { overflow: auto; max-height: 52vh; border: 1px solid var(--line); border-radius: 10px; }
#availGrid { display: grid; grid-template-columns: 44px repeat(7, 40px); gap: 2px; padding: 6px; width: max-content; }
.avHead { font-size: 11px; color: var(--dim); text-align: center; padding: 3px 0; }
.avTime { font-size: 10px; color: var(--dim); text-align: right; padding-right: 4px; line-height: 18px; }
.avCell { width: 40px; height: 18px; border-radius: 4px; background: transparent;
  box-shadow: inset 0 0 0 1.5px var(--ink); }
.avCell.on { background: rgb(16,18,22); box-shadow: none; }

/* screenshot import (2026-11-01) */
.checkline { display: inline-flex; align-items: center; gap: 6px; font-weight: 500; font-size: 14px; margin: 4px 10px 4px 0; }
.shotRow { padding: 8px 4px; border-bottom: 1px solid var(--line); font-size: 14px; }
.shotName { font-weight: 700; display: block; }
.shotState { color: var(--dim); font-size: 13px; }
.cfPhoto { width: 100%; max-height: 220px; object-fit: contain; background: var(--bg); border: 1px solid var(--line); border-radius: 10px; margin-bottom: 8px; }
.cfTiles { margin: 10px 0; border: 1px solid var(--line); border-radius: 10px; padding: 8px 10px; }
.cfTileRow { display: flex; align-items: center; gap: 8px; padding: 4px 0; flex-wrap: wrap; }
.cfTileName { font-weight: 700; min-width: 74px; }
.cfFlags { margin: 8px 0; }
.cfFlags .checkline { display: flex; }
.cfTimes { display: flex; gap: 12px; }
.cfTimes label { flex: 1; }
.gateBox { border: 1px solid var(--danger); border-radius: 10px; padding: 10px; margin: 10px 0; }
.holdBanner { display: block; width: 100%; text-align: left; padding: 12px; margin: 10px 0; border-radius: 12px;
  background: var(--card); border: 1px solid var(--danger); color: var(--ink); font-weight: 700; font-size: 14px; }
/* list sync confirm (2026-11-01) */
.lcRow { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 7px 4px; border-bottom: 1px solid var(--line); font-size: 14px; }
.lcName { font-weight: 700; }
.lcDays { color: var(--dim); font-weight: 600; }
.lcBadge { background: var(--danger); color: #fff; border-radius: 8px; padding: 1px 7px; font-size: 12px; font-weight: 700; }
.lcOk { color: var(--ok); font-weight: 600; font-size: 13px; }
.lcWarn { color: var(--danger); font-weight: 600; font-size: 13px; }
.lcDim { color: var(--dim); font-size: 13px; }
.reviewRow { display: flex; gap: 10px; padding: 10px 4px; border-bottom: 1px solid var(--line); }
.reviewPhoto { width: 74px; height: 110px; object-fit: cover; border-radius: 8px; border: 1px solid var(--line); }
.reviewBody { flex: 1; font-size: 14px; }
.rowBtns { margin-top: 6px; }
.rowBtns .miniBtn { margin-left: 0; margin-right: 8px; }

/* ============ CONSOLIDATED BUILD v6 (Rob, 2026-11-01) ============ */
/* Prep slide strip: Cancel joins the palette. */
.st-cancel { background: #991b1b; }
body.theme-glass .st-cancel { background: rgba(153,27,27,.55); }
.card[data-armed="cancel"] .st-cancel { outline: 3px solid #fff; outline-offset: -3px; filter: brightness(1.25); }

/* Prep card tools: Details + •••, small, together by the tiles. */
.chipsRow { display: flex; align-items: flex-start; gap: 8px; }
.chipsRow .wchips { flex: 1; }
.daysAge { font-size: 12px; font-weight: 700; color: var(--mut); white-space: nowrap; margin-top: 3px; line-height: 1.5; }
.cardTools { display: flex; gap: 5px; flex: none; margin-left: auto; }
.miniTool { font-size: 12px; font-weight: 700; padding: 3px 8px; border-radius: 8px;
  background: var(--card); border: 1px solid var(--line); color: var(--ink); line-height: 1.5; }
/* The drag grip: lower-left corner of the card, no word, no
 * button (Rob, 2026-11-01). The SPAN is the handle — 26x18 at
 * left 8 / bottom 6, the seat it has always had — and it paints
 * NOTHING itself. The visible corner is painted by the span's
 * ::before, bounded to the bottom 14px of the handle and shaped
 * as a corner wedge by a mask on the pseudo alone. The handle
 * is never masked or clipped, so no part of its 26x18 goes dead
 * under the finger (the 2026-11-03 clip lesson, kept: a clip
 * would shrink the tappable area and leave most of the grip
 * dead — drags never started from the grip's center).
 * THE RIDGES (Rob, 2026-11-04): real raised ridges, not the
 * flat drawn diagonal lines this replaces. Each ridge in the
 * repeating gradient is a hard-stop light face over a hard-stop
 * shadow face — white highlight, mid step, dark groove, soft
 * shadow — a directional light overlay falls across the whole
 * wedge, and inset seam shadows seat the strip in the card's
 * surface like the ridged corner of a molded case. Neutral
 * whites and blacks only, so the emboss reads on day, night,
 * glass, attempted and skipped faces alike; no new color
 * enters the palette.
 * THE TIME (Rob, 2026-11-04): the ridges can never touch the
 * printed time, by geometry, not by care. (1) The paint is
 * bounded to the handle's bottom 14px, so no painted pixel
 * rises above the card's bottom 20px. (2) A gripped card's rail
 * carries 26px of bottom padding (the :has rule below), which
 * seats the number-and-time stack in the card's upper body and
 * holds the time's bottom edge clear of the paint on the
 * shortest card the app renders. Measured in the rig on the
 * standard 79px Prep card: before this fix the paint's top sat
 * 5.6px ABOVE the time's bottom edge — the overlap in Rob's
 * 2026-11-04 screenshot; after, a clear gap stands on every
 * gripped card in every mode and state (grip-fix RECORD). */
.card { position: relative; }
.dragGrip { position: absolute; left: 8px; bottom: 6px; width: 26px; height: 18px; z-index: 6;
  cursor: grab; touch-action: none; user-select: none; background: transparent; }
.dragGrip::before { content: ""; position: absolute; left: 0; bottom: 0; width: 26px; height: 14px;
  pointer-events: none; border-radius: 3px 9px 3px 11px;
  background:
    linear-gradient(135deg, rgba(255,255,255,.50), rgba(255,255,255,0) 46%, rgba(0,0,0,.22) 88%, rgba(0,0,0,.30)),
    repeating-linear-gradient(45deg,
      rgba(255,255,255,.95) 0, rgba(255,255,255,.95) 1.5px,
      rgba(190,198,210,.55) 1.5px, rgba(190,198,210,.55) 3px,
      rgba(70,82,99,.60) 3px, rgba(70,82,99,.60) 4.5px,
      rgba(10,14,20,.42) 4.5px, rgba(10,14,20,.42) 6px);
  box-shadow: inset 0 1px 1px rgba(255,255,255,.75), inset 0 -2px 3px rgba(0,0,0,.35),
    inset 2px 0 3px rgba(0,0,0,.18), inset -1px 0 2px rgba(255,255,255,.35);
  -webkit-mask-image: linear-gradient(to top right, #000 58%, transparent 62%);
  mask-image: linear-gradient(to top right, #000 58%, transparent 62%); }
/* A gripped card reserves its corner: the rail's bottom padding
 * grows so the number-and-time stack rides up and the corner
 * shelf below it stays empty in every state and at every card
 * height. The handle's own box does not move; only the rail's
 * content re-seats. */
.card:has(.dragGrip) .rail { padding-bottom: 26px; }
.availFmtBtn { margin-left: 10px; vertical-align: middle; }

/* FULL-MAP VIEW: everything else slides off with animation; the
 * map, its own controls, the map header bar (text + Close), and
 * the Feedback button (which NEVER slides off) remain. Once the
 * slide finishes, the chrome is visibility:hidden as well — a
 * slid-away element that merely fades still leaves its box
 * across the top of the map (measured in the rig: the header's
 * sync line box sat over the Close button's corner in Run and
 * Prep), and no theme or factor may resurrect it. On exit,
 * visibility returns at once and the chrome slides back. */
#appHeader, #listWrap, #runNavBar, #grabBand { transition: transform .38s ease, opacity .38s ease, visibility 0s; }
body.fullmap #appHeader, body.fullmap #listWrap, body.fullmap #runNavBar, body.fullmap #grabBand {
  transition: transform .38s ease, opacity .38s ease, visibility 0s linear .38s; visibility: hidden; }
body.fullmap #appHeader { transform: translateY(-112%); opacity: 0; pointer-events: none; }
body.fullmap #listWrap { transform: translateY(46px); opacity: 0; pointer-events: none; }
body.fullmap #runNavBar { transform: translateY(-112%); opacity: 0; pointer-events: none; }
body.fullmap #grabBand { opacity: 0; pointer-events: none; }
body.fullmap .binBtn, body.fullmap .holdBtn { transform: translateX(130%); opacity: 0; pointer-events: none; transition: transform .38s ease, opacity .38s ease; }
body.fullmap #mapWrap { position: fixed; inset: 0; height: auto; z-index: 40; }
body.fullmap #mapModeBtn { background: var(--accent); color: #fff; }

/* GLASS, MADE BEAUTIFUL (Rob, 2026-11-01): Day/Night stay clean;
 * Glass is the beautiful mode. Buttons are 3D BEVELED glass —
 * bevel edge, top highlight, drop shadow, label printed on top.
 * Stop cards are ETCHED glass pieces floating over the map.
 * Feedback becomes a 3D piece of BLUE-tinted glass (its form and
 * placement — the trademark piece — unchanged). */
body.theme-glass .smallBtn, body.theme-glass #modeTabs button, body.theme-glass .cornerBtn,
body.theme-glass .segSwitch button, body.theme-glass .miniTool, body.theme-glass .wideBtn,
body.theme-glass .bigBtn, body.theme-glass .menuItem, body.theme-glass .runNavBtn,
body.theme-glass .cardBtns button, body.theme-glass .miniBtn,
body.theme-glass #availFmtBtn {
  background-image: linear-gradient(180deg, rgba(255,255,255,.38), rgba(255,255,255,.06) 46%, rgba(0,0,0,.10));
  border: 1px solid rgba(255,255,255,.5);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.65), inset 0 -6px 10px rgba(0,0,0,.14), 0 5px 14px rgba(0,0,0,.28);
  border-radius: 13px;
}
body.theme-glass #modeTabs button.on, body.theme-glass .segSwitch button.on {
  background-image: linear-gradient(180deg, rgba(255,255,255,.30), rgba(255,255,255,.02) 46%, rgba(0,0,0,.16));
}
body.theme-glass .card {
  background-image: linear-gradient(165deg, rgba(255,255,255,.30), rgba(255,255,255,.03) 42%, rgba(0,0,0,.08));
  border: 1px solid rgba(255,255,255,.48);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.6), inset 0 -10px 18px rgba(0,0,0,.13), 0 8px 20px rgba(0,0,0,.30);
  border-radius: 16px;
}
body.theme-glass .cardAddr, body.theme-glass .cardName {
  text-shadow: 0 1px 0 rgba(255,255,255,.28), 0 -1px 1px rgba(0,0,0,.38);
}
body.theme-glass .feedbackBtn {
  /* The house Feedback face (glass standard, "The glass look"):
   * the blue gradient rgba(37,99,235,.82) → rgba(30,64,175,.88),
   * white word, frosted, beveled — in every theme and at every
   * tint; it never takes the tint. §7: the white word holds
   * 5.1:1 against the face's lightest stop. */
  background: linear-gradient(180deg, rgba(37,99,235,.82), rgba(30,64,175,.88));
  border: 1px solid rgba(190,220,255,.65);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.75), inset 0 -8px 14px rgba(0,20,80,.30), 0 7px 18px rgba(0,20,80,.40);
  backdrop-filter: var(--glass-filter-soft); -webkit-backdrop-filter: var(--glass-filter-soft);
  color: #fff; text-shadow: 0 1px 2px rgba(0,30,90,.55);
}
/* Full-map stacking: Feedback NEVER slides off — and never hides
 * UNDER the map either; it rides above the full-map layer. */
body.fullmap .feedbackBtn { z-index: 50; }

/* Connect your Muse (step two, 2026-11-02): the Settings
 * section. Buttons ride the shared wideBtn/bigBtn shapes; the
 * fallback block box is monospace so a hand copy stays exact. */
#claudeBlock { margin-top: 16px; }
#claudeBlock a { color: var(--accent); }
#claudeStatus { color: var(--ink); }
#claudeRevokeBtn { color: var(--danger); }
#claudeBlockText { width: 100%; min-height: 132px; margin-top: 8px; padding: 10px;
  border-radius: 10px; font-family: ui-monospace, Menlo, monospace; font-size: 12px; }
#claudeRevokeBtn[hidden], #claudeBlockText[hidden] { display: none; }

/* Vehicle marker (Rob, 2026-11-02): the vehicle silhouette rides its
 * own Leaflet layer under the stop pins (zIndexOffset -1000 in app.js),
 * drawn large enough to read as a vehicle, never as another stop. The
 * double shadow is its halo: a white edge for streets and satellite,
 * a dark fall for the night basemap, so it reads on every rendering. */
.vehIcon { background: transparent; border: none; }
.vehWrap { position: relative; width: 100%; height: 100%; }
.vehSvg { width: 100%; height: 100%; display: block; overflow: visible;
  filter: drop-shadow(0 0 2px rgba(255,255,255,.95)) drop-shadow(0 1px 3px rgba(0,0,0,.6)); }
.vehBearing { position: absolute; inset: 0; pointer-events: none; }
.vehBearing span { position: absolute; top: -9px; left: 50%; margin-left: -5px; width: 0; height: 0;
  border-left: 5px solid transparent; border-right: 5px solid transparent;
  border-bottom: 9px solid #ffb300; filter: drop-shadow(0 0 1.5px rgba(0,0,0,.65)); }

/* Start / End current-location symbol (Rob, 2026-11-03): a
   little end-of-field switch — location fills, clear empties.
   The start-time clock (Rob, 2026-11-04) shares that same
   right-side control column and paint: one tap writes the
   device's current time into the field; the time input itself
   remains the manual picker. */
.seField { position: relative; display: block; }
.seField input { width: 100%; padding-right: 36px; box-sizing: border-box; }
.seLoc, .seNow { position: absolute; right: 2px; top: 50%; transform: translateY(-50%);
  border: 0; background: transparent; color: inherit; font-size: 15px;
  line-height: 1; padding: 7px; cursor: pointer; }
.seLoc:disabled { opacity: .5; cursor: default; }
/* The clock centers on the time INPUT itself: the label rule
 * gives inputs a 4px top margin inside .seField, so the wrapper's
 * own center sits 2px above the input's center. The pins keep
 * their standing seat; only the clock takes the correction. */
.seNow { top: calc(50% + 2px); }

/* LAPTOP / DESKTOP PRESENTATION (Rob, 2026-11-03: the app has to
   work on a laptop like a website, not a stranded phone strip).
   At laptop widths the app already takes the wide layout family
   (body.land: map and list side by side). What remained
   phone-shaped was the sheets: full-window panels with
   full-width fields. At 900px and up a sheet becomes a centered
   panel of website proportion, and the full sheet (the front
   door's sign-in) becomes a centered column panel — upright,
   normally proportioned, nothing rotated (the rotation gate is
   in app.js deviceRotates). Phone and tablet widths are
   untouched by this block. */
@media (min-width: 900px) {
  .sheet { left: 50%; right: auto; transform: translateX(-50%); width: 560px; max-width: 94vw;
    border-radius: 18px 18px 0 0; }
  .sheet.full { width: 480px; max-width: 94vw; top: 0; bottom: 0; border-radius: 0;
    box-shadow: 0 0 32px rgba(0,0,0,.35); }
  /* THE FRONT DOOR ON A LAPTOP (Rob, 2026-11-03 16:32, "the
   * most important part"): the LOGIN screen presents LANDSCAPE
   * on a laptop, always, as a matter of device class — it is
   * the screen before sign-in, so no account setting and no
   * per-device memory can be its mechanism; this media rule
   * (a wide, non-phone window) plus the rotation gate in app.js
   * (a laptop never turns) are the whole mechanism. The panel
   * is a centered landscape card: wide in proportion, upright,
   * clear of every edge. On a tablet the front door keeps the
   * tablet's own orientation behavior — this rule's shape
   * follows the window, and the turn still belongs to the
   * device class gate, not to this panel. */
  #loginSheet { left: 50%; top: 50%; bottom: auto; transform: translate(-50%, -50%);
    width: 720px; max-width: 94vw; max-height: 92vh; border-radius: 18px;
    box-shadow: 0 12px 44px rgba(0,0,0,.4); }
}

/* ============ SERVENAV LAYER (one engine, host switch) ============
 * Only reachable on servenav.hungrymuse.com: servenav.js builds
 * #snNav at runtime; these rules dress it. The navigation screen
 * covers the whole planner while driving (Rob's shape: everything
 * clears, full screen map, planner back on arrival). */
#snNav { position: fixed; inset: 0; z-index: 4200; background: #e9e5da; }
#snNav[hidden] { display: none; }
body.theme-night #snNav, body[data-solar="night"] #snNav { background: #191c20; }
#snMap { position: absolute; inset: 0; }
#snBanner {
  position: absolute; top: calc(10px + env(safe-area-inset-top)); left: 10px; right: 10px;
  z-index: 500; background: rgba(20, 26, 38, 0.92); color: #fff;
  border-radius: 14px; padding: 12px 14px; pointer-events: none;
  box-shadow: 0 4px 18px rgba(0,0,0,0.35);
}
#snInstr { font-size: 21px; font-weight: 700; line-height: 1.25; }
#snDist { font-size: 15px; opacity: 0.85; margin-top: 2px; }
#snSub {
  position: absolute; left: 10px; right: 10px; bottom: calc(76px + env(safe-area-inset-bottom));
  z-index: 500; background: rgba(255,255,255,0.92); color: #1c2430;
  border-radius: 10px; padding: 8px 12px; font-size: 14px; pointer-events: none;
  box-shadow: 0 2px 10px rgba(0,0,0,0.25);
}
body.theme-night #snSub, body[data-solar="night"] #snSub { background: rgba(30,34,40,0.92); color: #e8ecf0; }
#snBtns {
  position: absolute; left: 10px; right: 10px; bottom: calc(10px + env(safe-area-inset-bottom));
  z-index: 500; display: flex; gap: 10px;
}
.snBtn {
  flex: 1; padding: 14px 10px; font-size: 16px; font-weight: 700;
  border-radius: 12px; border: none; background: #2f6df6; color: #fff;
  box-shadow: 0 3px 12px rgba(0,0,0,0.3);
}
.snBtn.snEnd { background: rgba(20,26,38,0.92); }
.snVan { background: none; border: none; }
.snVan span { font-size: 27px; display: block; filter: drop-shadow(0 1px 3px rgba(0,0,0,0.5)); }
body.sn-navigating { overflow: hidden; }
