/* Landsat Ledger — design tokens shared with fleet-manager
   (fleet-manager/src/public/styles.css), on a conventional page shell.
   Fleet is a map with a rail over it; Ledger is tables, forms and boards,
   so the scaffold differs while the palette stays identical. */

:root {
  --bg: #121314;
  --surface: #191a1c;
  --surface-2: #202225;
  --chip: #26282c;
  --btn-strong: #2d3034;
  --border: #2c2e32;
  --text: #d7d6d1;
  --text-dim: #8e8d85;
  --text-on-strong: #f0efe9;
  --chip-text: #cbb586;
  --accent: #4dc3b2;
  --accent-soft: rgba(77, 195, 178, .16);
  --ok: #7fa66a;
  --warn: #d29a4b;
  --warn-soft: rgba(210, 154, 75, .14);
  --danger: #c96555;
  --danger-soft: rgba(201, 101, 85, .16);
  /* Shift-timeline category colors: one hue per work kind plus break. Mid-
     luminance on purpose — dark text stays readable on them here, and they
     survive a light surface if the palette ever grows one. */
  --cat-build: #6b9fd8;
  --cat-maintenance: #d2a054;
  --cat-install: #a98fd0;
  --cat-drive: #55b8a6;
  --cat-training: #93b573;
  --cat-other: #b0a89b;
  --cat-break: #71747a;
  --sans: system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  --mono: ui-monospace, 'SF Mono', 'Cascadia Mono', Menlo, Consolas, monospace;
  --radius: 6px;
  --shadow: 0 8px 24px rgba(0, 0, 0, .45);
}

* { margin: 0; padding: 0; box-sizing: border-box; }

body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--sans);
  line-height: 1.55;
  min-height: 100vh;
}

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 3px; }

h1 { font-size: 1.4rem; font-weight: 640; }
h2 { font-size: 1.05rem; font-weight: 640; }
h3 { font-size: .9rem; font-weight: 640; }

/* ---------- shell ---------- */
.topbar {
  align-items: center;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  display: flex;
  gap: 4px;
  padding: 0 18px;
  position: sticky;
  top: 0;
  z-index: 40;
}
.topbar .brand {
  align-items: center;
  color: var(--text);
  display: flex;
  font-weight: 700;
  gap: 9px;
  letter-spacing: .04em;
  margin-right: 18px;
  padding: 12px 0;
}
.topbar .brand .dot {
  background: var(--accent);
  border-radius: 2px;
  height: 10px;
  width: 10px;
}
.topbar nav { display: flex; flex-wrap: wrap; gap: 2px; }
.topbar nav a {
  border-bottom: 2px solid transparent;
  color: var(--text-dim);
  font-size: .86rem;
  padding: 13px 12px 11px;
}
.topbar nav a:hover { color: var(--text); text-decoration: none; }
.topbar nav a.active { border-bottom-color: var(--accent); color: var(--text); }
.topbar .spacer { flex: 1; }
.topbar .who { color: var(--text-dim); font-size: .78rem; padding-right: 12px; }

.page { margin: 0 auto; max-width: 1280px; padding: 22px 18px 80px; }
.page-head {
  align-items: baseline;
  border-bottom: 1px solid var(--border);
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-bottom: 20px;
  padding-bottom: 14px;
}
.page-head .sub { color: var(--text-dim); font-size: .82rem; }
.page-head .grow { flex: 1; }

.flash {
  background: var(--accent-soft);
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  color: var(--text);
  font-size: .86rem;
  margin-bottom: 16px;
  padding: 10px 13px;
}
.flash.bad { background: var(--danger-soft); border-color: var(--danger); }

/* A work order's parts and supplies: the first thing on the job page and
   the tech's card, so nobody drives out without them. */
.bring {
  background: var(--warn-soft);
  border: 1px solid var(--warn);
  border-radius: var(--radius);
  margin: 0 0 16px;
  padding: 10px 13px;
}
.bring .bring-head { color: var(--warn); font-size: .76rem; font-weight: 600; letter-spacing: .05em; text-transform: uppercase; }
.bring ul { margin: 6px 0 0; padding-left: 20px; }
.bring li { font-size: .95rem; font-weight: 600; line-height: 1.45; }

/* ---------- cards & stats ---------- */
.cards { display: grid; gap: 12px; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); margin-bottom: 22px; }
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 13px 15px;
}
.card .label {
  color: var(--text-dim);
  font-size: .66rem;
  font-weight: 600;
  letter-spacing: .13em;
  text-transform: uppercase;
}
.card .value { font-size: 1.7rem; font-weight: 650; font-variant-numeric: tabular-nums; line-height: 1.2; }
.card .value.bad { color: var(--danger); }
.card .value.warn { color: var(--warn); }
.card .value.good { color: var(--ok); }
.card .foot { color: var(--text-dim); font-size: .74rem; }

.panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  margin-bottom: 20px;
  overflow: hidden;
}
.panel > header {
  align-items: center;
  background: var(--surface-2);
  border-bottom: 1px solid var(--border);
  display: flex;
  gap: 10px;
  padding: 9px 14px;
}
.panel > header h2 { font-size: .74rem; letter-spacing: .12em; text-transform: uppercase; color: var(--text-dim); }
.panel > header .grow { flex: 1; }
.panel .body { padding: 14px; }
.panel .empty { color: var(--text-dim); font-size: .85rem; padding: 16px 14px; }

/* ---------- tables ---------- */
.tablewrap { overflow-x: auto; }
table { border-collapse: collapse; font-size: .85rem; width: 100%; }
th {
  border-bottom: 1px solid var(--border);
  color: var(--text-dim);
  font-size: .64rem;
  font-weight: 600;
  letter-spacing: .11em;
  padding: 9px 12px;
  text-align: left;
  text-transform: uppercase;
  white-space: nowrap;
}
td {
  border-bottom: 1px solid var(--border);
  font-variant-numeric: tabular-nums;
  padding: 9px 12px;
  vertical-align: middle;
}
tbody tr:hover td { background: var(--surface-2); }
td.dim, .dim { color: var(--text-dim); }
td.num { text-align: right; }
.mono { font-family: var(--mono); }
.uid { font-family: var(--mono); font-weight: 600; }

/* ---------- chips ---------- */
.chip {
  background: var(--chip);
  border: 1px solid var(--border);
  border-radius: 999px;
  color: var(--text-dim);
  display: inline-block;
  font-size: .68rem;
  font-weight: 500;
  padding: 1px 8px;
  white-space: nowrap;
}
.chip.ok { border-color: rgba(127, 166, 106, .5); color: var(--ok); }
.chip.warn { border-color: rgba(210, 154, 75, .5); color: var(--warn); }
.chip.bad { border-color: rgba(201, 101, 85, .55); color: var(--danger); }
.chip.accent { border-color: rgba(77, 195, 178, .5); color: var(--accent); }
.chip.solid-bad { background: var(--danger); border-color: var(--danger); color: #fff; }

.sev { border-radius: 3px; font-family: var(--mono); font-size: .68rem; font-weight: 600; padding: 2px 6px; }
.sev.cov-none { background: var(--danger); color: #fff; }
.sev.cov-partial { background: var(--warn-soft); border: 1px solid var(--warn); color: var(--warn); }
/* Covered means something is broken but nobody is losing eyes on the site.
   It should read as a backlog item, not an alarm. */
.sev.cov-full { background: var(--chip); border: 1px solid var(--border); color: var(--text-dim); }
.sev.draft { background: var(--accent-soft); border: 1px solid var(--accent); color: var(--accent); }

/* ---------- SLO burn ---------- */
.burn { background: var(--chip); border-radius: 999px; height: 5px; min-width: 90px; overflow: hidden; position: relative; }
.burn > i { background: var(--ok); display: block; height: 100%; }
.burn.warn > i { background: var(--warn); }
.burn.bad > i { background: var(--danger); }
.burnrow { align-items: center; display: flex; gap: 9px; }
.burnrow .t { color: var(--text-dim); font-size: .72rem; font-variant-numeric: tabular-nums; white-space: nowrap; }

/* ---------- forms ---------- */
label { color: var(--text-dim); display: block; font-size: .76rem; font-weight: 600; margin-bottom: 4px; }
input[type="text"], input[type="email"], input[type="number"], input[type="date"], input[type="tel"], input[type="search"], input[type="time"],
select, textarea {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--text);
  font: inherit;
  font-size: .87rem;
  padding: .42rem .55rem;
  width: 100%;
}
textarea { min-height: 84px; resize: vertical; }
input:focus, select:focus, textarea:focus { border-color: var(--accent); outline: none; }
.field { margin-bottom: 12px; }
.row { display: grid; gap: 12px; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); }
/* A hidden row stays hidden: the attribute must beat the grid display above. */
[hidden] { display: none !important; }
.inline { align-items: flex-end; display: flex; flex-wrap: wrap; gap: 10px; }

button, .btn {
  background: var(--btn-strong);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--text-on-strong);
  cursor: pointer;
  display: inline-block;
  font: inherit;
  font-size: .84rem;
  font-weight: 600;
  padding: .42rem .8rem;
}
button:hover, .btn:hover { border-color: var(--accent); text-decoration: none; }
button.primary, .btn.primary { background: var(--accent); border-color: var(--accent); color: #10201d; }
button.quiet, .btn.quiet { background: none; color: var(--text-dim); font-weight: 500; }
button.quiet:hover { color: var(--text); }
button.danger { border-color: var(--danger); color: var(--danger); }
button:disabled { cursor: not-allowed; opacity: .45; }
/* A form on its way to the server (busy.js): the clicked button spins, the
   rest stand down until the next page. */
form[data-busy] button { cursor: progress; opacity: .55; pointer-events: none; }
form[data-busy] button.busy { opacity: 1; }
button.busy::before {
  animation: spin .8s linear infinite;
  border: 2px solid currentColor;
  border-radius: 50%;
  border-right-color: transparent;
  content: '';
  display: inline-block;
  height: .75em;
  margin-right: .45em;
  vertical-align: -.08em;
  width: .75em;
}
@keyframes spin { to { transform: rotate(360deg); } }

.hint { color: var(--text-dim); font-size: .76rem; margin-top: 4px; }
details.form summary { color: var(--accent); cursor: pointer; font-size: .84rem; padding: 4px 0; }
details.form[open] summary { margin-bottom: 10px; }

/* ---------- login ---------- */
.login {
  align-items: center;
  display: flex;
  justify-content: center;
  min-height: 100vh;
  padding: 24px;
}
.login .box {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: var(--shadow);
  max-width: 380px;
  padding: 30px;
  width: 100%;
}
.login h1 { align-items: center; display: flex; gap: 10px; margin-bottom: 6px; }
.login .lead { color: var(--text-dim); font-size: .85rem; margin-bottom: 20px; }
.login .devlink {
  background: var(--surface-2);
  border: 1px dashed var(--border);
  border-radius: var(--radius);
  font-family: var(--mono);
  font-size: .72rem;
  margin-top: 16px;
  overflow-wrap: anywhere;
  padding: 10px;
}

/* ---------- misc ---------- */
.kv { display: grid; gap: 6px 14px; grid-template-columns: max-content 1fr; font-size: .85rem; }
.kv dt { color: var(--text-dim); }
.kv dd { font-variant-numeric: tabular-nums; }
.timeline { display: grid; gap: 0; }
.timeline .ev { border-bottom: 1px solid var(--border); display: grid; gap: 2px 12px; grid-template-columns: 150px 1fr; padding: 8px 0; }
.timeline .ev:last-child { border-bottom: 0; }
.timeline .ev .when { color: var(--text-dim); font-family: var(--mono); font-size: .72rem; }
.timeline .ev .what { font-size: .84rem; }
.timeline .ev .note { color: var(--text-dim); font-size: .8rem; grid-column: 2; }
pre.report {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font-family: var(--mono);
  font-size: .78rem;
  line-height: 1.6;
  overflow-x: auto;
  padding: 14px;
  white-space: pre-wrap;
}
.filters { align-items: center; display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }
.filters select, .filters input { width: auto; min-width: 130px; }
.toolbar { display: flex; flex-wrap: wrap; gap: 8px; }
@media (prefers-reduced-motion: reduce) { * { transition: none !important; animation: none !important; } }

/* ---------- worker surfaces ----------
   The phone view. One decision per screen, targets big enough to hit in a
   parking lot with gloves on. */
.page.narrow { max-width: 640px; }

.clockcard {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  margin-bottom: 20px;
  padding: 26px 22px;
  text-align: center;
}
.clockcard.live { border-color: var(--accent); }
.clockcard .label {
  color: var(--text-dim);
  font-size: .68rem;
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
}
.clockcard .big {
  font-family: var(--mono);
  font-size: 1.15rem;
  font-variant-numeric: tabular-nums;
  margin: 6px 0 2px;
}
.clockcard .running {
  color: var(--accent);
  font-size: 2rem;
  font-variant-numeric: tabular-nums;
  font-weight: 650;
  margin-bottom: 18px;
}
.clockbtn {
  border-radius: 10px;
  font-size: 1.05rem;
  font-weight: 700;
  letter-spacing: .02em;
  padding: 16px;
  width: 100%;
}
.clockbtn.in { background: var(--accent); border-color: var(--accent); color: #10201d; }
.clockbtn.out { background: var(--btn-strong); border-color: var(--border); color: var(--text-on-strong); }

.splitrow {
  align-items: center;
  display: grid;
  gap: 8px;
  grid-template-columns: minmax(0, 1fr) 92px 34px;
  margin-bottom: 8px;
}
.splitrow input[type="number"] { font-variant-numeric: tabular-nums; text-align: right; }
.splitrow button { padding: .42rem 0; }

/* The live clock's category buttons: big targets for a phone in one hand.
   Start uses them to answer "what are you working on?", the live panel uses
   the same grid to switch mid-shift. */
.catgrid {
  display: grid;
  gap: 8px;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  text-align: left;
}
.catgrid button {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 10px;
  font-size: .92rem;
  font-weight: 600;
  line-height: 1.3;
  min-height: 60px;
  padding: 10px 12px;
  text-align: left;
  white-space: normal;
}
.catgrid button:hover { border-color: var(--accent); }

/* Local-day headers the history script inserts between shift rows. */
.dayhead {
  color: var(--text-dim);
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .12em;
  margin: 16px 0 8px;
  text-transform: uppercase;
}

/* The forgotten-clock banner: loud on purpose, never an auto-cutoff. */
.longshift {
  background: var(--danger-soft);
  border: 1px solid var(--danger);
  border-radius: 6px;
  font-size: .9rem;
  margin-bottom: 16px;
  padding: 12px 14px;
}

/* ---------- shift timeline (portal-timeline.js) ----------
   The day as one bar: blocks colored by category, draggable dividers between
   them, the outer edges being the shift span itself. Category fills are
   shared utility classes so the editor blocks, legend dots and the hours
   page's mini bars all read from one vocabulary. */
.cat-build { background-color: var(--cat-build); }
.cat-maintenance { background-color: var(--cat-maintenance); }
.cat-install { background-color: var(--cat-install); }
.cat-drive { background-color: var(--cat-drive); }
.cat-training { background-color: var(--cat-training); }
.cat-other { background-color: var(--cat-other); }
/* Break is unpaid: dim and striped so it never reads as work. */
.cat-break {
  background: repeating-linear-gradient(135deg, var(--cat-break) 0 5px, transparent 5px 10px);
  background-color: var(--surface-2);
}

.tlwrap { padding: 8px 0 4px; position: relative; touch-action: pan-y; }
.tlbar {
  border: 1px solid var(--border);
  border-radius: 8px;
  display: flex;
  height: 58px;
  overflow: hidden;
  width: 100%;
}
.tlblock {
  align-items: center;
  color: #14161a;
  cursor: pointer;
  display: flex;
  flex: 0 0 auto;
  flex-direction: column;
  justify-content: center;
  min-width: 0;
  overflow: hidden;
}
.tlblock.cat-break { color: var(--text-dim); }
.tlblock.selected { box-shadow: inset 0 0 0 2px var(--text-on-strong); }
.tlblock .tlb-label {
  font-size: .72rem;
  font-weight: 700;
  line-height: 1.25;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tlblock .tlb-dur { font-size: .68rem; font-weight: 600; opacity: .85; }

/* Dividers: a 28px touch target centered on a slim visible line. The outer
   two are the shift's own start and end, marked with a knob. */
.tlhandle {
  bottom: 0;
  cursor: col-resize;
  margin-left: -14px;
  position: absolute;
  top: 0;
  touch-action: none;
  width: 28px;
  z-index: 5;
}
.tlhandle::before {
  background: var(--bg);
  border-radius: 2px;
  bottom: 10px;
  content: '';
  left: 50%;
  position: absolute;
  top: 10px;
  transform: translateX(-50%);
  width: 3px;
}
.tlhandle.edge::before { background: var(--text-dim); }
.tlhandle.selected::before { background: var(--accent); width: 5px; }
.tlhandle.edge i {
  background: var(--btn-strong);
  border: 1px solid var(--text-dim);
  border-radius: 50%;
  height: 14px;
  left: 50%;
  position: absolute;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 14px;
}
.tlhandle.edge.selected i { border-color: var(--accent); }

.tlends {
  color: var(--text-dim);
  display: flex;
  font-family: var(--mono);
  font-size: .72rem;
  justify-content: space-between;
  margin-top: 4px;
}

/* The selected thing's controls: nudge buttons for a divider, category /
   split / delete for a block. Phone-sized targets. */
.tlctl {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 6px;
  font-size: .82rem;
  margin-top: 10px;
  padding: 8px 10px;
}
.tlctl .head { font-family: var(--mono); font-size: .78rem; font-variant-numeric: tabular-nums; }
.tlctl .nudges { display: flex; gap: 6px; margin-top: 8px; }
.tlctl .nudges button { flex: 1; font-variant-numeric: tabular-nums; padding: .55rem 0; }
.tlctl .acts { align-items: center; display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
.tlctl .acts select { flex: 1; min-width: 130px; width: auto; }

.tlleg-row {
  align-items: center;
  cursor: pointer;
  display: flex;
  font-size: .8rem;
  gap: 8px;
  padding: 3px 0;
}
.tlleg-row .dot { border: 1px solid var(--border); border-radius: 3px; flex: 0 0 auto; height: 10px; width: 10px; }
.tlleg-row .n { flex: 1; }
.tlleg-row .t { color: var(--text-dim); font-family: var(--mono); font-size: .74rem; font-variant-numeric: tabular-nums; }
.tllegend { margin-top: 10px; }

/* Sticky save bar — appears only once something changed. */
.tlsavebar {
  background: var(--surface);
  border-top: 1px solid var(--border);
  bottom: 0;
  box-shadow: 0 -6px 18px rgba(0, 0, 0, .35);
  left: 0;
  position: fixed;
  right: 0;
  z-index: 60;
}
.tlsavebar-inner {
  align-items: center;
  display: flex;
  gap: 10px;
  margin: 0 auto;
  max-width: 640px;
  padding: 10px 18px;
}
.tlsavebar .msg { color: var(--text-dim); flex: 1; font-size: .8rem; }
.tlsavebar .msg.bad { color: var(--danger); }

/* Read-only mini bar on history rows: same category vocabulary, no editing. */
.tlmini {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 4px;
  height: 10px;
  margin-top: 8px;
  overflow: hidden;
  position: relative;
}
.tlmini i { bottom: 0; position: absolute; top: 0; }

/* ---------- Map (the same flat map fleet-manager draws) ---------- */

.leaflet-control-attribution { background: rgba(10, 15, 22, .7) !important; color: var(--text-dim) !important; }
.custom-leaflet-marker { align-items: center; display: flex; justify-content: center; }
/* Dark-map trick for the DEFAULT (light) basemap only. A configured
   MAP_TILES_URL provider owns its own theme (the map gets .custom-tiles). */
.flat-map:not(.custom-tiles) .leaflet-tile { filter: invert(1) hue-rotate(180deg) brightness(.82) saturate(.14); }
.flat-map.leaflet-container { background: var(--bg); font-family: var(--sans); }
.flat-map .leaflet-popup-content-wrapper, .flat-map .leaflet-popup-tip { background: var(--surface); box-shadow: var(--shadow); color: var(--text); }
.flat-map .leaflet-popup-content { font-size: .82rem; line-height: 1.45; margin: 10px 14px; }
.flat-map .leaflet-popup-content a { color: var(--accent); }
.flat-map .leaflet-popup-close-button { color: var(--text-dim) !important; }
.flat-map .leaflet-bar a { background: var(--surface); border-bottom-color: var(--border); color: var(--text); }
.flat-map .leaflet-bar a:hover { background: var(--surface-2); }

/* ---------- phone layer ----------
   Everything above is the desk layout. Under 720px the same pages get one
   rule set: a two-row top bar (brand + sign out, then the doors on a row
   that scrolls sideways instead of wrapping), tighter shell and cards,
   tables that keep their first column pinned while the rest scrolls, forms
   in one column at a font size iOS will not zoom on, and two helper classes
   the dense tables use to fold low-value columns into the row: .sm-hide
   drops a column, .sm-only shows a stacked line in its place. */
.sm-only { display: none; }

@media (max-width: 720px) {
  .sm-hide { display: none !important; }
  .sm-only { display: block; }

  .topbar { flex-wrap: wrap; gap: 0; padding: 0 12px; }
  .topbar .brand { margin-right: auto; padding: 10px 0; }
  .topbar .spacer, .topbar .who { display: none; }
  .topbar > a.btn.quiet { font-size: .78rem; padding: .3rem .55rem; }
  .topbar nav {
    flex-basis: 100%;
    flex-wrap: nowrap;
    gap: 0;
    margin: 0 -12px;
    order: 5;
    overflow-x: auto;
    padding: 0 6px;
    scrollbar-width: none;
  }
  .topbar nav::-webkit-scrollbar { display: none; }
  .topbar nav a { font-size: .84rem; padding: 8px 10px 9px; white-space: nowrap; }
  .subnav { flex-wrap: nowrap !important; overflow-x: auto; padding: 0 6px !important; scrollbar-width: none; }
  .subnav::-webkit-scrollbar { display: none; }
  .subnav a { white-space: nowrap; }
  /* A tab row that scrolls fades at its right edge, so a cut-off tab reads
     as "more this way" rather than a layout bug. */
  .topbar nav, .subnav {
    -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 28px), transparent);
    mask-image: linear-gradient(90deg, #000 calc(100% - 28px), transparent);
  }

  .page { padding: 12px 12px 60px; }
  /* Title, its one-line description, then the count — stacked, so the
     description is not squeezed into a column beside the count. */
  .page-head { flex-direction: column; gap: 2px; margin-bottom: 14px; padding-bottom: 10px; }
  .page-head .grow { flex: none; }
  h1 { font-size: 1.25rem; }
  .flash { font-size: .82rem; margin-bottom: 12px; padding: 9px 11px; }

  .cards { gap: 8px; grid-template-columns: repeat(2, minmax(0, 1fr)); margin-bottom: 14px; }
  .card { padding: 10px 12px; }
  .card .value { font-size: 1.4rem; }
  .panel { margin-bottom: 14px; }
  .panel > header { padding: 8px 12px; }
  .panel .body { padding: 12px; }

  table { font-size: .8rem; }
  th { font-size: .6rem; letter-spacing: .06em; padding: 7px 8px; }
  td { padding: 7px 8px; }
  /* The first column is the row's identity (unit, job, who). It stays put
     while the rest of the row scrolls under it. */
  .tablewrap th:first-child, .tablewrap td:first-child {
    background: var(--surface);
    box-shadow: 1px 0 0 var(--border);
    left: 0;
    position: sticky;
    z-index: 1;
  }
  .tablewrap tbody tr:hover td:first-child { background: var(--surface-2); }
  td[style*="max-width"] { max-width: 180px !important; }

  .row { gap: 8px; grid-template-columns: 1fr; }
  .filters { gap: 6px; }
  .filters select, .filters input { min-width: 0; width: 100%; }
  input[type="text"], input[type="email"], input[type="number"], input[type="date"], input[type="tel"],
  select, textarea { font-size: 16px; }
  button, .btn { padding: .5rem .85rem; }
  .toolbar { gap: 6px; }
  .toolbar form { width: 100%; }
  .toolbar form input { min-width: 0 !important; width: 100%; }

  .kv { gap: 0; grid-template-columns: 1fr; }
  .kv dt { margin-top: 6px; }
  .timeline .ev { gap: 0; grid-template-columns: 1fr; }
  .timeline .ev .note { grid-column: 1; }
}

/* ---------- legal documents (portal signing) ----------
   The canonical text, formatted for reading by utils/legal.js
   renderLegalHtml: headings, lists and paragraphs from the file's own lines,
   never reflowed or reworded. In flow, not in a scroll box — the reader
   scrolls the page, and the signature form waits at the end. */
.legal { font-size: .88rem; line-height: 1.6; }
.legal .doc-title { font-size: 1.05rem; font-weight: 650; }
.legal .doc-meta { color: var(--text-dim); font-size: .78rem; margin-bottom: 14px; }
.legal h3 { font-size: .92rem; font-weight: 650; margin: 18px 0 6px; }
.legal h4 { font-size: .86rem; font-weight: 650; margin: 12px 0 4px; }
.legal p { margin: 0 0 8px; overflow-wrap: anywhere; }
.legal p.sub { padding-left: 18px; }
.legal p.caps { font-weight: 600; }
.legal ol, .legal ul { margin: 0 0 10px 22px; }
.legal li { margin-bottom: 3px; overflow-wrap: anywhere; }
.signblock {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  margin-top: 16px;
  padding: 14px;
}


/* ---------- Timesheets: a day at a glance ----------
   The contractor's own hours-page shape, in a table cell: the mini bar, the
   split as colored dots, the closing note, and a fold with every stretch.
   Accepted rows carry the same cell, so inspection never stops at
   acceptance. Category colors are the timeline's own (.cat-*). */
table.glance-rows td { vertical-align: top; }
td.glance-cell { font-size: .78rem; max-width: 560px; min-width: 300px; }
.glance-bar { margin: 0 0 6px; }
.glance-split { align-items: center; display: flex; flex-wrap: wrap; gap: 4px 10px; }
.glance-alloc { white-space: nowrap; }
.dot { border-radius: 50%; display: inline-block; height: 8px; margin-right: 5px; width: 8px; }
.glance-note { margin-top: 5px; white-space: pre-wrap; }
.glance-returned { color: var(--warn); font-size: .76rem; margin-top: 4px; }
details.glance { margin-top: 6px; }
details.glance summary { color: var(--text-dim); cursor: pointer; font-size: .72rem; }
details.glance summary:hover { color: var(--accent); }
.glance-body { border-left: 2px solid var(--border); margin-top: 6px; padding-left: 10px; }
.glance-table { width: auto; }
.glance-table td { border: 0; font-size: .74rem; padding: 2px 12px 2px 0; }
.glance-table tbody tr:hover td { background: transparent; }
.glance-table tr.brk td { color: var(--text-dim); font-style: italic; }
.glance-facts { display: grid; gap: 3px 12px; grid-template-columns: max-content 1fr; margin: 8px 0 0; }
.glance-facts dt { color: var(--text-dim); font-size: .66rem; letter-spacing: .08em; padding-top: 2px; text-transform: uppercase; }
.glance-facts dd { font-size: .74rem; margin: 0; }
.glance-facts dd.pre { white-space: pre-wrap; }
@media (max-width: 720px) {
  td.glance-cell { max-width: 320px !important; min-width: 240px; }
  /* The pinned first column is the outer row's identity; the nested stretch
     table's first cell is just a time. */
  .tablewrap .glance-table td:first-child { background: transparent; box-shadow: none; position: static; }
}

/* The reviewer's two moves on a timesheet row, stacked and the same width:
   Accept on top, the send-back reason and its button as one row below.
   Input and button share font size and padding, so the row is one height. */
.review { display: flex; flex-direction: column; gap: 6px; width: 300px; }
.review form { display: flex; gap: 6px; margin: 0; }
.review form:first-child button { width: 100%; }
.review input[type="text"] { flex: 1; font-size: .84rem; min-width: 0; padding: .42rem .55rem; }
.review button { flex: 0 0 auto; white-space: nowrap; }
@media (max-width: 720px) { .review { min-width: 220px; width: 100%; } }


/* ---------- the schedule (/schedule) ----------
   The week: days across, people down. Each cell stacks the plan (booking
   chips) over the fact (shift lines). The table scrolls inside .tablewrap
   on a phone, where the day stack (.weekstack) takes over. */
.week { border-collapse: separate; border-spacing: 0; width: 100%; min-width: 900px; table-layout: fixed; }
.week th, .week td { vertical-align: top; padding: 6px 6px; border-bottom: 1px solid var(--border); }
.week th.person { width: 130px; text-align: left; font-weight: 600; font-size: .8rem; white-space: normal; overflow-wrap: anywhere; letter-spacing: 0; text-transform: none; }
.week .lane a, .week .bk { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
.weeknav { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
@media (max-width: 720px) {
  .weeknav { gap: 6px; margin-top: 8px; }
  .weeknav .btn { font-size: .8rem; padding: .4rem .65rem; }
}
.weekstack .panel.today > header h2 { color: var(--accent); }
/* Three weeks on a phone: a heading per week, a full card for a day with
   something on it (and for today), one quiet line for an empty day. */
.weekstack .weekhead { color: var(--text-dim); font-size: .68rem; font-weight: 600; letter-spacing: .11em; margin: 16px 0 6px; text-transform: uppercase; }
.weekstack .weekhead:first-child { margin-top: 0; }
.weekstack .dayline { align-items: center; border-bottom: 1px solid var(--border); color: var(--text-dim); display: flex; font-size: .8rem; gap: 10px; padding: 7px 4px; }
.weekstack .dayline .d { flex: 1; }
.weekstack .dayline a { font-size: .78rem; }
.weekstack .panel { margin-bottom: 8px; }
/* Desktop: one grid per week, the week named on its panel. */
.weekpanel > header h2 { letter-spacing: .08em; }
.weekpanel > header .dim { font-size: .74rem; }
/* Phones: the day stack (schedule.ejs .weekstack) replaces the grid. */
.weekstack .panel.today { border-color: var(--accent); }
.weekstack .body { display: grid; gap: 6px; }
.weekstack .laneitem { display: block; font-size: .8rem; padding: 6px 8px; border-radius: 6px; background: var(--surface-2); border-left: 3px solid var(--text-dim); color: inherit; text-decoration: none; }
.weekstack .laneitem.warn { border-left-color: var(--warn); }
.weekstack .laneitem.bad { border-left-color: var(--danger); }
.weekstack .laneitem.accent { border-left-color: var(--accent); }
.weekstack .bk { display: block; border-radius: 6px; padding: 6px 8px; font-size: .84rem; line-height: 1.3; background: var(--surface-2); color: inherit; text-decoration: none; border-left: 3px solid var(--text-dim); }
.weekstack .bk.confirmed { border-left-color: var(--ok); }
.weekstack .bk.proposed, .weekstack .bk.unanswered { border-left-color: var(--warn); }
.weekstack .bk.late, .weekstack .bk.declined { border-left-color: var(--danger); }
.weekstack .bk.done { opacity: .6; }
.weekstack .bk.onsite { border-left-color: var(--ok); box-shadow: inset 0 0 0 1px var(--ok); }
.weekstack .sh { display: block; font-size: .74rem; color: var(--text-dim); font-family: var(--mono, monospace); padding-left: 8px; }
.week th.day { font-size: .78rem; font-weight: 600; text-align: left; color: var(--text-dim); }
.week th.day.today, .week td.today { background: color-mix(in srgb, var(--accent) 8%, transparent); }
.week th.day.today { color: var(--text); }
.week .bk { display: block; border-radius: 6px; padding: 4px 6px; margin: 0 0 4px; font-size: .74rem; line-height: 1.25; background: var(--surface-2); color: inherit; text-decoration: none; border-left: 3px solid var(--text-dim); }
.week .bk.confirmed { border-left-color: var(--ok); }
.week .bk.proposed, .week .bk.unanswered { border-left-color: var(--warn); }
.week .bk.late, .week .bk.declined { border-left-color: var(--danger); }
.week .bk.done { opacity: .6; }
.week .bk.onsite { border-left-color: var(--ok); box-shadow: inset 0 0 0 1px var(--ok); }
.week .bk .t { font-weight: 600; }
.week .sh { display: block; font-size: .7rem; color: var(--text-dim); font-family: var(--mono, monospace); margin: 0 0 3px; }
.week .lane a { display: block; font-size: .72rem; margin: 0 0 3px; text-decoration: none; padding: 2px 6px; border-radius: 4px; background: var(--surface-2); border-left: 3px solid var(--text-dim); color: inherit; }
.week .lane a.warn { border-left-color: var(--warn); }
.week .lane a.bad { border-left-color: var(--danger); }
.week .lane a.accent { border-left-color: var(--accent); }

/* ---------- the search box (contractors) ----------
   Sits in the table's header: one quiet input, the count beside it, Enter
   to search. No native search chrome, no big button. */
.search { display: flex; align-items: center; gap: 8px; }
.search input[type="search"] { width: 240px; max-width: 60vw; padding: .34rem .6rem; font-size: .84rem; -webkit-appearance: none; appearance: none; }
.search input[type="search"]::-webkit-search-decoration, .search input[type="search"]::-webkit-search-cancel-button { -webkit-appearance: none; }
.search button { font-size: .78rem; padding: .34rem .7rem; }
.search a { font-size: .78rem; }
@media (max-width: 720px) {
  .panel > header { flex-wrap: wrap; }
  .search { flex: 1 1 100%; }
  .search input[type="search"] { flex: 1 1 auto; width: auto; max-width: none; min-width: 0; }
}


/* ---------- Payables board ----------
   Rows read top-aligned, the Work column keeps a readable width, and the
   contractor's own words open in a fold row under the row (full width, a
   reading column) instead of wrapping inside a narrow cell. The actions
   stack in one column so the table stays a table. */
table.payables td { vertical-align: top; }
table.payables td.nowrap { white-space: nowrap; }
table.payables td.who { max-width: 200px; min-width: 120px; }
table.payables td.who .name { white-space: nowrap; }
table.payables td.work { font-size: .8rem; max-width: 440px; min-width: 230px; }
table.payables td.lands { font-size: .78rem; max-width: 230px; min-width: 165px; }
table.payables td.lands select { font-size: .76rem; padding: .28rem .4rem; width: 100%; }
table.payables .toolbar { align-items: stretch; flex-direction: column; gap: 6px; min-width: 200px; }
table.payables .toolbar form { display: flex; gap: 4px; margin: 0; width: 100%; }
table.payables .toolbar form > select, table.payables .toolbar form > input[type="text"] { flex: 1; font-size: .78rem; min-width: 0; }
table.payables .toolbar form > button { flex: 0 0 auto; white-space: nowrap; }
table.payables .toolbar form > button:only-child { width: 100%; }
table.payables .toolbar .note { color: var(--text-dim); font-size: .74rem; }
button.linkish { background: none; border: 0; color: var(--text-dim); cursor: pointer; font: inherit; font-size: .72rem; padding: 0; }
button.linkish:hover { color: var(--accent); }
tr.fold td, tbody tr.fold:hover td { background: var(--surface-2); padding: 10px 14px 12px; }
.foldbody { max-width: 78ch; }
.foldbody .glance-facts { margin: 0; }
.foldbody .glance-facts dd { font-size: .8rem; line-height: 1.5; }
.foldbody .move { align-items: center; display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.foldbody .move select { font-size: .76rem; max-width: 420px; }
@media (max-width: 720px) {
  .tablewrap tr.fold td { box-shadow: none; position: static; }
  /* The fold stays in view while the row scrolls sideways under it. */
  .foldbody { left: 0; max-width: calc(100vw - 48px); position: sticky; }
  table.payables td.work { max-width: 280px; min-width: 220px; }
  table.payables .toolbar { min-width: 210px; }
}

/* Payables (revamped 2026-09-24): a list you click into, a page per payable. */
.filters .count { background: rgba(255, 255, 255, .12); border-radius: 999px; font-size: .7rem; margin-left: 4px; padding: 1px 7px; }
table.paylist td { vertical-align: top; }
table.paylist .small, .small { font-size: .74rem; }
table.paylist tr.rowlink { cursor: pointer; }
table.paylist tr.rowlink:hover td { background: var(--surface-2); }
table.paylist td.who .name { white-space: nowrap; }
table.paylist td.what { max-width: 520px; }
table.paylist td.what a.title { color: var(--text); font-weight: 600; text-decoration: none; }
table.paylist tr.rowlink:hover td.what a.title { color: var(--accent); }
table.paylist td.status { max-width: 280px; }
table.paylist td.status .chip { margin: 0 4px 3px 0; }
.crumbs { font-size: .8rem; margin-bottom: 6px; }
.crumbs a { color: var(--text-dim); }
.detail-grid { align-items: start; display: grid; gap: 18px; grid-template-columns: minmax(0, 1.7fr) minmax(280px, 1fr); }
.detail-grid > div > .panel { margin-bottom: 18px; }
@media (max-width: 900px) { .detail-grid { grid-template-columns: 1fr; } }
.panel.dupwarn { border-color: var(--danger); margin-bottom: 18px; }
.panel.dupwarn > header h2 { color: var(--danger); }
.said { margin-top: 14px; }
.said .label { color: var(--text-dim); font-size: .66rem; letter-spacing: .1em; margin-bottom: 4px; text-transform: uppercase; }
.said p { font-size: .9rem; line-height: 1.5; margin: 0; }
.pre { white-space: pre-wrap; }
.photos { display: grid; gap: 10px; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); }
.photos a { display: block; }
.photos img { background: var(--surface-2); border: 1px solid var(--border); border-radius: 8px; display: block; max-height: 420px; object-fit: contain; width: 100%; }
.panel.actions .stack { display: grid; gap: 8px; margin-bottom: 18px; }
.panel.actions .stack:last-child { margin-bottom: 0; }
.panel.actions .stack label { font-size: .82rem; }
.panel.actions .stack textarea { width: 100%; }
.panel.actions .stack.reject { border-top: 1px solid var(--border); padding-top: 14px; }
.btnrow { align-items: center; display: flex; flex-wrap: wrap; gap: 8px; }
label.check { align-items: flex-start; display: flex; font-size: .82rem; gap: 8px; }
label.check input { margin-top: 3px; }
/* The portal's "what was the work on?" choice: two big tap targets. */
.choice { display: grid; gap: 8px; }
.choice label { align-items: center; border: 1px solid var(--border); border-radius: 8px; cursor: pointer; display: flex; font-weight: 500; gap: 10px; padding: 10px 12px; }
.choice label:has(input:checked) { border-color: var(--accent); }
/* The portal's pay schedule: always visible above the invoice form. */
.payinfo { background: var(--surface-2); border: 1px solid var(--border); border-left: 3px solid var(--accent); border-radius: 6px; font-size: .88rem; line-height: 1.5; margin-bottom: 18px; padding: 10px 12px; }
/* The portal's submissions, stacked for a phone: status and payout first. */
.sublist .sub { border-top: 1px solid var(--border); padding: 12px 14px; }
.sublist .sub:first-child { border-top: 0; }
.sublist .top { align-items: flex-start; display: flex; gap: 10px; justify-content: space-between; }
.sublist .amt { font-variant-numeric: tabular-nums; font-weight: 650; white-space: nowrap; }
.sublist .note { color: var(--text-dim); font-size: .8rem; }
.sublist .note.warn { color: var(--warn); }
.sublist .note.bad { color: var(--danger); }
.sublist .note.ok { color: var(--ok); }
.sublist .what { font-size: .86rem; margin-top: 6px; }
.sublist .meta { color: var(--text-dim); font-size: .74rem; margin-top: 4px; }

/* ---------- quote configurator (public/quotePlacer.js) ---------- */
.placer { margin: 2px 0 14px; }
.placetotal { font-size: .95rem; margin: 4px 0 2px; }
.placesite { margin-top: 10px; }
.placehead { align-items: center; display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 8px; }
.placehead .placetitle { font-size: .86rem; }
.placehead .placecount { font-size: .95rem; }
.placehead .placeadd { border-color: var(--accent); color: var(--accent); font-weight: 600; }
.placehead .placenote { font-size: .76rem; margin-left: auto; }
.placehead .placegoto { width: 260px; }
.placehead .placeimg { font-size: .78rem; padding: 3px 9px; }
.placemap.plan { background: #fff; }
.placemap { border: 1px solid var(--border); border-radius: var(--radius); height: 400px; }
.placehint { margin-top: 6px; }
.placehint b { color: var(--text); font-weight: 600; }
/* The frame: what the quote's site map shows. Outside it is dimmed; the label names it. */
.placeframe { border: 2px solid #fff; border-radius: 2px; box-shadow: 0 0 0 4000px rgba(10, 12, 16, .55); pointer-events: none; position: absolute; z-index: 650; }
.placeframe span { background: #fff; border-radius: 3px 3px 0 0; color: #33407A; font: 600 11px/1 var(--sans); padding: 4px 8px; position: absolute; right: -2px; top: -21px; white-space: nowrap; }
.placewarn { color: var(--warn); font-size: .8rem; margin-top: 5px; }
.placewarn:empty { display: none; }
.unitpin { background: none; border: 0; pointer-events: none; }
.unitpin i {
  background: #111;
  border: 2px solid #fff;
  border-radius: 50%;
  box-shadow: 0 0 4px rgba(0, 0, 0, .7);
  box-sizing: border-box;
  cursor: grab;
  height: 14px;
  left: 5px;
  pointer-events: auto;
  position: absolute;
  top: 29px;
  width: 14px;
}
.unitpin span {
  background: #33407A;
  border: 2px solid #fff;
  border-radius: 5px;
  box-shadow: 0 1px 5px rgba(0, 0, 0, .65);
  box-sizing: border-box;
  color: #fff;
  cursor: grab;
  font: 600 13px/20px var(--sans);
  height: 24px;
  left: 21px;
  min-width: 24px;
  padding: 0 5px;
  pointer-events: auto;
  position: absolute;
  text-align: center;
  top: 4px;
}

/* A fold that sits inline in a line of text (the work order's "start a
   trip" link): no marker, the summary reads as a link. */
details.inline-fold { display: inline-block; }
details.inline-fold > summary { list-style: none; cursor: pointer; }
details.inline-fold > summary::-webkit-details-marker { display: none; }
details.inline-fold > summary.linkish { color: var(--text-dim); }
details.inline-fold > summary.linkish:hover { color: var(--accent); }

/* Crew pay at a glance (Timesheets) and the crew-hours owed list (Payables).
   The day strip is one bar per day, full height at 12 hours; a hollow bar
   still has hours not accepted. Single series, so one hue and no legend. */
.nowrap { white-space: nowrap; }
table.crewtable td { vertical-align: middle; }
table.crewtable th.num, table.crewtable td.num { font-variant-numeric: tabular-nums; }
table.crewtable tfoot td { border-top: 1px solid var(--border); color: var(--text-dim); font-weight: 600; }
table.crewtable td .small { margin-top: 2px; }
.daystrip { align-items: flex-end; display: flex; gap: 2px; height: 26px; min-width: 180px; }
.daystrip i { align-items: flex-end; border-bottom: 1px solid var(--border); display: flex; flex: 1 1 0; height: 100%; min-width: 2px; }
.daystrip i.future { border-bottom-style: dotted; opacity: .5; }
.daystrip b { background: var(--accent); border-radius: 2px 2px 0 0; display: block; width: 100%; }
.daystrip b.open { background: var(--accent-soft); box-shadow: inset 0 0 0 1px var(--accent); }
table.crewtable th.num { text-align: right; }
