/* DoorFlow design system — tokens lifted directly from crm-mockup.html */
@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@500;600;700&family=IBM+Plex+Mono:wght@400;500&display=swap');

:root {
  --navy: #0F1C2E;
  --navy-soft: #16273D;
  --navy-line: #26364C;
  --navy-text: #9AA7BD;
  --canvas: #F5F6F3;
  --ink: #1A2333;
  --slate: #666D79;        /* was #6B7280: 4.46:1 on the canvas */
  /* ⚠️ Was #98A2B3, which is 2.58:1 on white — well under AA, on the 12px
     labels and table headings used across every screen. A browser contrast
     audit run over the new billing panel turned it up app-wide. */
  --slate-light: #67707F;
  --border: #E3E5E1;
  --blue: #2B6CB0;
  --blue-light: #EAF2FB;
  --orange: #C2410C;       /* was #E8590C: 3.58:1 on white */
  --orange-light: #FDEEE5;
  --green: #177A41;        /* was #1F9D55: 3.49:1 on white, and it is
                              what every dollar figure is printed in */
  --green-deep: #177A41;   /* readable on --green-light */
  --green-light: #E8F6ED;
  --amber: #9A5B00;
  --amber-light: #FEF3E2;
  --red: #B3122F;
  --red-light: #FDECEE;

  --display: 'Space Grotesk', system-ui, sans-serif;
  --mono: 'IBM Plex Mono', ui-monospace, Menlo, monospace;
  --body: system-ui, -apple-system, 'Segoe UI', sans-serif;

  --sidebar: 176px;
  --radius: 10px;
  --radius-sm: 6px;
  --shadow: 0 1px 2px rgba(15, 28, 46, .06);
  --shadow-pop: 0 8px 28px rgba(15, 28, 46, .16);
}

* { box-sizing: border-box; }

html, body { height: 100%; }

body {
  margin: 0;
  background: var(--canvas);
  color: var(--ink);
  font-family: var(--body);
  font-size: 14px;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 { font-family: var(--display); font-weight: 600; margin: 0; }
a { color: var(--blue); text-decoration: none; }
button { font-family: inherit; }

.display { font-family: var(--display); font-weight: 600; }
.mono { font-family: var(--mono); font-variant-numeric: tabular-nums; }
.micro {
  font-size: 12px; color: var(--slate); text-transform: uppercase;
  letter-spacing: .04em; margin: 0;
}
.muted { color: var(--slate); }
.dim { color: var(--slate-light); }
.right { text-align: right; }
.grow { flex: 1; }
.row { display: flex; align-items: center; gap: 10px; }
.wrap { flex-wrap: wrap; }
.hide { display: none !important; }

/* ------------------------------------------------------------------ shell */
#app { display: flex; flex-direction: column; min-height: 100vh; }

.topbar {
  display: flex; align-items: center; gap: 16px;
  padding: 11px 20px; background: #fff;
  border-bottom: 1px solid var(--border);
  position: sticky; top: 0; z-index: 40;
}

.brand { display: flex; align-items: center; gap: 9px; }
.brand-mark {
  width: 26px; height: 26px; border-radius: var(--radius-sm);
  background: var(--navy); color: #fff;
  display: flex; align-items: center; justify-content: center;
}
.brand-name { font-family: var(--display); font-weight: 700; font-size: 15px; white-space: nowrap; }
.brand-co {
  font-size: 12px; color: var(--slate); padding-left: 10px; margin-left: 2px;
  border-left: 1px solid var(--border);
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.searchbox {
  display: flex; align-items: center; gap: 7px; margin-left: 12px;
  background: var(--canvas); border: 1px solid transparent;
  border-radius: var(--radius-sm); padding: 6px 10px; width: 260px;
}
.searchbox:focus-within { border-color: var(--blue); background: #fff; }
.searchbox input {
  border: 0; background: transparent; outline: none; width: 100%;
  font-size: 13px; color: var(--ink); font-family: var(--body);
}

.topbar-right { margin-left: auto; display: flex; align-items: center; gap: 14px; }

.avatar {
  width: 28px; height: 28px; border-radius: 50%;
  background: var(--blue-light); color: var(--blue);
  display: flex; align-items: center; justify-content: center;
  font-size: 11px; font-weight: 600; flex: 0 0 auto;
}
.avatar.lg { width: 36px; height: 36px; font-size: 13px; }

.shell { display: flex; flex: 1; min-height: 0; }

.sidebar {
  width: var(--sidebar); flex: 0 0 var(--sidebar);
  background: var(--navy); padding: 16px 10px;
  display: flex; flex-direction: column;
  /* stays put on long job and AR pages, under the sticky top bar */
  position: sticky; top: 49px; align-self: flex-start;
  height: calc(100vh - 49px); overflow-y: auto;
}
.navlink {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 10px; border-radius: var(--radius-sm); margin-bottom: 2px;
  color: var(--navy-text); font-size: 13px; cursor: pointer;
  border: 0; background: transparent; width: 100%; text-align: left;
}
.navlink:hover { color: #fff; background: rgba(255, 255, 255, .05); }
/* the Knock entry is an anchor out to the field app, not a route button */
a.navlink { text-decoration: none; box-sizing: border-box; }
.navlink.on { background: var(--navy-soft); color: #fff; }
.navlink svg { flex: 0 0 auto; }
.nav-foot { margin-top: auto; border-top: 1px solid var(--navy-line); padding-top: 12px; }
.nav-badge {
  margin-left: auto; font-family: var(--mono); font-size: 11px;
  color: var(--navy-text);
}

.main { flex: 1; min-width: 0; padding: 20px 24px 48px; }
.page-head { margin-bottom: 18px; }
.page-head p { font-size: 13px; color: var(--slate); margin: 0 0 2px; }
.page-head h1 { font-size: 22px; }

/* ------------------------------------------------------------------ cards */
.card {
  background: #fff; border: 1px solid var(--border);
  border-radius: var(--radius); padding: 14px 16px; box-shadow: var(--shadow);
}
.card + .card { margin-top: 16px; }
/* ⚠️ That stacking rule leaks into grids: every card after the first sagged
   16px (the dashboard KPI stagger on the punch list — measured, card 2 sat
   top+16 with 16px less height at every width). Grids space with gap. */
.grid > .card + .card { margin-top: 0; }
.card-head {
  display: flex; align-items: center; gap: 8px; margin-bottom: 10px;
}
.card-head h3 { font-size: 14px; }

.grid { display: grid; gap: 12px; }
.grid-4 { grid-template-columns: repeat(4, 1fr); }
.grid-2 { grid-template-columns: 1fr 1fr; }
.grid-14 { grid-template-columns: 1.4fr 1fr; }
.gap-16 { gap: 16px; }
.mt-22 { margin-top: 22px; }
.mb-12 { margin-bottom: 12px; }

.stat p:first-child { margin: 0; }
.stat .value {
  font-family: var(--display); font-size: 24px; font-weight: 600;
  margin: 4px 0 0; font-variant-numeric: tabular-nums;
}
.stat .sub { font-size: 12px; color: var(--slate-light); margin: 2px 0 0; }

/* --------------------------------------------------------------- pipeline */
.board { display: grid; gap: 10px; position: relative; align-items: start; }
.board-col { position: relative; min-width: 0; }
.board-col.drop { outline: 2px dashed var(--blue); outline-offset: 4px; border-radius: 8px; }
.col-head { display: flex; align-items: center; gap: 6px; margin-bottom: 10px; }
.col-dot { width: 8px; height: 8px; border-radius: 2px; flex: 0 0 auto; }
.col-head p { font-size: 12px; font-weight: 600; margin: 0; }
.col-count { font-family: var(--mono); font-size: 11px; color: var(--slate-light); margin-left: auto; }
.col-total {
  font-family: var(--mono); font-size: 11px; color: var(--green);
  margin-bottom: 8px;
}

.lead-card {
  background: #fff; border: 1px solid var(--border); border-radius: 8px;
  padding: 10px 12px; margin-bottom: 8px; cursor: pointer;
  box-shadow: var(--shadow);
}
.lead-card:hover { border-color: var(--blue); }
.lead-card.dragging { opacity: .4; }
.lead-card .name { font-size: 13.5px; font-weight: 600; margin: 0; }
.lead-card .addr {
  font-family: var(--mono); font-size: 11.5px; color: var(--slate);
  margin: 2px 0 0; display: flex; align-items: center; gap: 4px;
}
.lead-card .val { font-family: var(--mono); font-size: 12.5px; color: var(--green); font-weight: 500; }
.lead-foot { display: flex; justify-content: space-between; align-items: center; margin-top: 8px; }
.chip {
  font-size: 11px; color: var(--slate); background: var(--canvas);
  border-radius: 4px; padding: 2px 6px; white-space: nowrap;
}
.col-empty {
  border: 1px dashed var(--border); border-radius: 8px; padding: 14px 10px;
  text-align: center; font-size: 11.5px; color: var(--slate-light);
}
.col-compact {
  background: #fff; border: 1px solid var(--border); border-radius: 8px;
  padding: 16px 10px; text-align: center;
}
.col-compact .n { font-family: var(--display); font-size: 22px; font-weight: 600; margin: 0; }
.col-compact .l { font-size: 11px; color: var(--slate-light); margin: 2px 0 0; }

.badge {
  font-family: var(--mono); font-size: 11px; border-radius: 4px;
  padding: 2px 6px; border: 1px solid; white-space: nowrap;
}
.badge.slate { color: var(--slate); border-color: #6B728055; }
.badge.blue { color: var(--blue); border-color: #2B6CB055; }
.badge.amber { color: var(--amber); border-color: #B36B0055; }
.badge.green { color: var(--green); border-color: #1F9D5555; }
.badge.red { color: var(--red); border-color: #B3122F55; }

/* ---------------------------------------------------------------- tables */
table.data { width: 100%; font-size: 12.5px; border-collapse: collapse; }
table.data th {
  text-align: left; font-weight: 500; color: var(--slate-light);
  padding-bottom: 6px; font-size: 12px; white-space: nowrap;
}
table.data td { padding: 8px 0; border-top: 1px solid var(--border); }
table.data tbody tr.clickable { cursor: pointer; }
table.data tbody tr.clickable:hover td { background: var(--canvas); }
table.data td.num { font-family: var(--mono); font-variant-numeric: tabular-nums; }

.list-row { padding: 8px 0; border-top: 1px solid var(--border); }
.list-row:first-child { border-top: 0; }
.list-row .t { font-size: 12.5px; font-weight: 500; margin: 0; }
.list-row .d { font-size: 11.5px; color: var(--slate); margin: 2px 0 0; }

/* --------------------------------------------------------------- controls */
.btn {
  border: 1px solid var(--border); background: #fff; color: var(--ink);
  border-radius: var(--radius-sm); padding: 7px 13px; font-size: 13px;
  cursor: pointer; display: inline-flex; align-items: center; gap: 6px;
}
.btn:hover { border-color: var(--slate-light); }
.btn.primary { background: var(--blue); border-color: var(--blue); color: #fff; }
.btn.primary:hover { background: #245C96; }
.btn.ghost { border-color: transparent; background: transparent; color: var(--slate); }
.btn.sm { padding: 4px 9px; font-size: 12px; }
.btn.danger { color: var(--red); border-color: #B3122F44; }
.btn:disabled { opacity: .5; cursor: not-allowed; }

.seg { display: flex; background: var(--canvas); border-radius: 8px; padding: 3px; }
.seg button {
  border: 0; cursor: pointer; font-size: 12.5px; font-weight: 500;
  padding: 6px 14px; border-radius: var(--radius-sm);
  background: transparent; color: var(--slate);
}
.seg button.on { background: #fff; color: var(--ink); box-shadow: 0 1px 2px rgba(0,0,0,.08); }

.field { display: block; margin-bottom: 12px; }
.field > span {
  display: block; font-size: 12px; color: var(--slate);
  text-transform: uppercase; letter-spacing: .04em; margin-bottom: 5px;
}
input, select, textarea {
  width: 100%; font-family: var(--body); font-size: 13.5px; color: var(--ink);
  background: #fff; border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: 8px 10px; outline: none;
}
input:focus, select:focus, textarea:focus {
  border-color: var(--blue); box-shadow: 0 0 0 3px var(--blue-light);
}
textarea { resize: vertical; min-height: 70px; }
.field-row { display: flex; gap: 12px; }
.field-row > * { flex: 1; }

/* ----------------------------------------------------------------- modal */
.scrim {
  position: fixed; inset: 0; background: rgba(15, 28, 46, .45);
  display: flex; align-items: flex-start; justify-content: center;
  padding: 60px 20px; z-index: 100; overflow: auto;
}
.modal {
  background: #fff; border-radius: 12px; width: 100%; max-width: 520px;
  box-shadow: var(--shadow-pop); padding: 20px 22px;
}
.modal.wide { max-width: 760px; }
.modal-head { display: flex; align-items: flex-start; margin-bottom: 16px; }
.modal-head h2 { font-size: 17px; }
.modal-foot {
  display: flex; justify-content: flex-end; gap: 8px; margin-top: 18px;
  padding-top: 14px; border-top: 1px solid var(--border);
}

/* ----------------------------------------------------------------- charts */
.bars {
  display: flex; align-items: flex-end; justify-content: center;
  gap: 14px; height: 170px; padding-top: 8px;
}
.bar-col {
  flex: 1 1 0; max-width: 108px; display: flex; flex-direction: column;
  justify-content: flex-end; height: 100%;
}
.bar {
  border-radius: 3px 3px 0 0; min-height: 2px; width: 100%;
  transition: height .3s ease;
}
.bar-label { font-size: 11px; color: var(--slate); text-align: center; margin-top: 6px; }
.bar-value {
  font-family: var(--mono); font-size: 10.5px; color: var(--slate);
  text-align: center; margin-bottom: 4px;
}
.chart-grid { border-top: 1px solid var(--border); }

.funnel { display: grid; gap: 8px; position: relative; }
.funnel-step { text-align: center; position: relative; }
.funnel-dot {
  width: 10px; height: 10px; border-radius: 50%; margin: 12px auto 10px;
  background: var(--blue);
}
.funnel-step .n { font-family: var(--display); font-size: 20px; font-weight: 600; margin: 0; }
.funnel-step .s { font-size: 11.5px; color: var(--slate); margin: 2px 0 0; }
.funnel-step .c { font-family: var(--mono); font-size: 10.5px; color: var(--slate-light); margin: 3px 0 0; }
.funnel-rule {
  position: absolute; top: 17px; left: 8%; right: 8%; height: 1px;
  border-top: 1px dashed var(--border);
}

/* ------------------------------------------------------------- job detail */
.job-head { display: flex; align-items: flex-start; gap: 16px; flex-wrap: wrap; }
/* Eight tabs do not fit in 375px, and the honest answer is to let the strip
   scroll rather than let it drag the whole page sideways with it. */
.tabs {
  display: flex; gap: 2px; border-bottom: 1px solid var(--border);
  margin: 18px 0 16px; overflow-x: auto; scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.tabs::-webkit-scrollbar { display: none; }
.tab {
  border: 0; background: transparent; cursor: pointer; font-size: 13px;
  color: var(--slate); padding: 9px 14px; border-bottom: 2px solid transparent;
  margin-bottom: -1px; white-space: nowrap; flex: 0 0 auto;
}
.tab.on { color: var(--ink); border-bottom-color: var(--blue); font-weight: 500; }

.timeline { position: relative; }
.tl-item { display: flex; gap: 10px; padding: 10px 0; border-top: 1px solid var(--border); }
.tl-item:first-child { border-top: 0; }
.tl-icon {
  width: 26px; height: 26px; border-radius: 50%; flex: 0 0 auto;
  display: flex; align-items: center; justify-content: center;
  background: var(--canvas); color: var(--slate);
}
.tl-body { flex: 1; min-width: 0; }
.tl-body .b { font-size: 13px; margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; }
.tl-body .m { font-size: 11.5px; color: var(--slate-light); margin: 3px 0 0; }

.stage-rail { display: flex; gap: 4px; flex-wrap: wrap; }
.stage-pip {
  font-size: 11.5px; padding: 5px 10px; border-radius: 4px; cursor: pointer;
  border: 1px solid var(--border); background: #fff; color: var(--slate);
}
.stage-pip.on { background: var(--navy); border-color: var(--navy); color: #fff; }
.stage-pip.done { background: var(--canvas); color: var(--slate); }

/* ---- narrow screens --------------------------------------------------- */
/* The rule everything below serves: WIDE CONTENT SCROLLS INSIDE ITS OWN BOX,
   and the page itself never scrolls sideways. A page that slides horizontally
   under your thumb while you are trying to scroll down is the difference
   between an app a rep uses on a roof and one they give up on.
   Ten of thirteen pages did exactly that; the culprit was always a data table
   sitting in a card that could not clip it. */
/* ⚠️ The scroll goes on a BOX AROUND the table, never on the table itself.
   Making `table.data` a block and its thead/tbody `display:table` did make the
   overflow behave — and silently broke every data table in the app, at every
   width: two independent tables each size their own columns, so the headers
   stopped lining up with the numbers underneath. On the coaching report that
   put revenue under "Close rate". The mobile audit never caught it because it
   measured horizontal overflow, which was genuinely fixed, and nothing was
   measuring alignment. app.js puts the box in place so no render site has to
   remember. */
.table-scroll {
  display: block; overflow-x: auto; -webkit-overflow-scrolling: touch;
}

@media (max-width: 760px) {
  /* Below this, a table stops trying to squeeze and starts scrolling. The
     minimum keeps columns legible rather than crushing six of them into
     375px and making all six unreadable. */
  .table-scroll > table.data { min-width: 460px; }

  /* Cards and the shell get their padding back. */
  .main { padding: 14px 12px 40px; }
  .card { padding: 14px; }
  .page-head h1 { font-size: 22px; }

  /* A job's header stacks: the money should not be squeezed against the name. */
  .job-head { flex-wrap: wrap; }
  .job-head .right { width: 100%; display: flex; align-items: center;
                     gap: 10px; margin-top: 6px; }
  .job-head .right .display { margin: 0 !important; }

  /* Two-up grids become one-up. */
  .grid-2, .grid-14, .grid-3 { grid-template-columns: 1fr !important; }

  /* A grid or flex item defaults to min-width:auto, so it refuses to shrink
     below its contents — one wide table inside a card pushed the card past
     its own track and took the page with it. This is the line that actually
     lets the scroll containers above do their job. */
  .grid > *, .row > * { min-width: 0; }

  /* Filters and toolbars wrap instead of pushing the page wide. A card header
     with a title, a spacer and two buttons is 400px of content in a 375px
     screen, so the buttons drop to their own line rather than hang off it. */
  .row, .searchbox, .seg, .card-head { flex-wrap: wrap; }
  .card-head .grow { flex-basis: 100%; height: 0; }

  /* The pipeline board is columns side by side — that IS its shape, so it
     scrolls as a board rather than pretending to be a list. The template is
     set inline per stage count, so it has to be overridden outright, or five
     columns just crush to 60px each. */
  .board {
    overflow-x: auto; -webkit-overflow-scrolling: touch;
    grid-template-columns: none !important;
    grid-auto-flow: column; grid-auto-columns: 78vw;
    padding-bottom: 6px;
  }
  .board-col { min-width: 0; }

  /* Modals were sized for a desktop dialog. */
  .modal { width: calc(100vw - 20px); max-width: none; padding: 16px; }
  .modal-foot { flex-wrap: wrap; gap: 8px; }
  .modal-foot .btn { flex: 1 1 auto; justify-content: center; }
  .field-row { flex-direction: column; gap: 0; }
}

/* -------------------------------------------------------------- postcards */
.pc-grid {
  display: grid; gap: 10px;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
}
.pc-card {
  display: flex; flex-direction: column; gap: 5px; text-align: left;
  background: #fff; border: 1px solid var(--border); border-radius: 8px;
  padding: 12px; cursor: pointer; font: inherit; color: inherit;
}
.pc-card:hover { border-color: var(--blue); }
.pc-card.on { border-color: var(--blue); border-width: 2px; padding: 11px; background: var(--blue-light); }
.pc-name {
  font-size: 11px; text-transform: uppercase; letter-spacing: .04em;
  color: var(--slate);
}
.pc-headline { font-family: var(--display); font-weight: 600; font-size: 14px; }
.pc-body { font-size: 12px; color: var(--slate); line-height: 1.45; }
.pc-cta { font-size: 11.5px; color: var(--blue); font-weight: 500; margin-top: 2px; }

.pc-row {
  display: flex; align-items: center; gap: 10px; padding: 9px 0;
  border-top: 1px solid var(--border); font-size: 13px; cursor: pointer;
}
.pc-row:first-of-type { border-top: 0; }
.pc-row.blocked { opacity: .55; cursor: not-allowed; }
.pc-row input { width: auto; }

/* --------------------------------------------------------------- activity */
.act-scale {
  position: relative; height: 18px; margin: 0 0 6px 190px;
  border-bottom: 1px solid var(--border);
}
.act-tick {
  position: absolute; transform: translateX(-50%); font-size: 10.5px;
  color: var(--slate-light); font-family: var(--mono);
}
.act-row {
  display: flex; gap: 14px; align-items: center;
  padding: 10px 0; border-top: 1px solid var(--border);
}
.act-who { display: flex; align-items: center; gap: 9px; width: 176px; flex: 0 0 176px; }
.act-track { flex: 1; min-width: 0; }
.tl-bar {
  position: relative; height: 20px; border-radius: 4px;
  background: var(--canvas); overflow: hidden;
}
.tl-stint { position: absolute; top: 3px; height: 14px; border-radius: 3px; background: var(--blue); }
.tl-gap { position: absolute; top: 3px; height: 14px; border-radius: 3px; background: #E7E9E4; }
.tl-gap.long { background: var(--orange-light); border: 1px solid var(--orange); }
.act-meta {
  display: flex; gap: 14px; flex-wrap: wrap; margin-top: 5px; font-size: 11.5px;
}
.act-legend {
  display: flex; gap: 18px; flex-wrap: wrap; margin-top: 14px;
  padding-top: 12px; border-top: 1px solid var(--border);
  font-size: 11.5px; color: var(--slate);
}
.act-legend i { display: inline-block; width: 14px; height: 10px; border-radius: 2px; margin-right: 5px; }
.sw-stint { background: var(--blue); }
.sw-gap { background: #E7E9E4; }
.sw-long { background: var(--orange-light); border: 1px solid var(--orange); }

@media (max-width: 760px) {
  .act-row { flex-direction: column; align-items: stretch; gap: 6px; }
  .act-who { width: auto; flex: none; }
  .act-scale { margin-left: 0; }
}

/* ------------------------------------------------------------------ toast */
#toast {
  position: fixed; bottom: 22px; left: 50%; transform: translateX(-50%);
  background: var(--navy); color: #fff; padding: 10px 16px;
  border-radius: 8px; font-size: 13px; box-shadow: var(--shadow-pop);
  z-index: 200; max-width: 80vw;
}
#toast.err { border-left: 4px solid #FF8B8B; padding-left: 14px; }
#toast.ok { border-left: 4px solid #6EE7A8; padding-left: 14px; }
/* Something happened, but not the whole thing — e.g. the estimate sent
   while the email sat in an outbox because no provider is configured. */
#toast.warn { border-left: 4px solid #F0B357; padding-left: 14px; }

/* ------------------------------------------------------------------ login */
.auth-wrap {
  min-height: 100vh; display: flex; align-items: center; justify-content: center;
  padding: 24px; background: var(--navy);
}
.auth-card {
  background: #fff; border-radius: 14px; padding: 28px; width: 100%;
  max-width: 420px; box-shadow: var(--shadow-pop);
}
.auth-card h1 { font-size: 20px; margin-bottom: 4px; }
.auth-card .sub { font-size: 13px; color: var(--slate); margin: 0 0 20px; }
.auth-switch { font-size: 12.5px; color: var(--slate); margin-top: 14px; text-align: center; }

.empty {
  text-align: center; padding: 40px 20px; color: var(--slate-light);
  font-size: 13px;
}

/* --------------------------------------------------------------- responsive */
@media (max-width: 1000px) {
  .grid-4 { grid-template-columns: repeat(2, 1fr); }
  .grid-2, .grid-14 { grid-template-columns: 1fr; }
  .searchbox { width: 160px; }
}
@media (max-width: 760px) {
  /* ⚠️ The two mobile defects that lived here (MOBILE-PUNCHLIST.md):
     `.searchbox { display:none }` deleted search from phones entirely, and
     `.navlink span { display:none }` turned 18 features into mystery
     glyphs. Search now collapses to a tap-to-expand icon — never removed —
     and the rail keeps its labels, small but named. */
  :root { --sidebar: 64px; }
  .navlink {
    flex-direction: column; gap: 3px; padding: 8px 2px;
    justify-content: center; text-align: center;
  }
  .navlink span {
    display: block; font-size: 9px; line-height: 1.1; width: 100%;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .main { padding: 16px 14px 40px; }
  .board { overflow-x: auto; }
  .searchbox {
    display: none;                    /* until the icon opens it */
    position: absolute; left: 8px; right: 8px; top: 100%; margin: 6px 0 0;
    width: auto; background: #fff; border-color: var(--border);
    box-shadow: var(--shadow-pop); z-index: 60;
  }
  .topbar.search-open .searchbox { display: flex; }
  #searchToggle { display: flex; }
  .brand-co { display: none; }
  /* KPI cards: same height per row, whatever their sub-lines do */
  .grid-4 { grid-template-columns: repeat(2, 1fr); grid-auto-rows: 1fr; }
  .stat .value { font-size: 20px; }
}

/* The search icon lives in the topbar always, shown only at phone width.
   ⚠️ The media override must FOLLOW this base rule — same specificity, so
   source order decides, and a base display:none written later silently
   wins at every width. That exact bug shipped for five minutes. */
#searchToggle {
  display: none; align-items: center; justify-content: center;
  width: 32px; height: 32px; border: 1px solid var(--border);
  border-radius: var(--radius-sm); background: #fff; cursor: pointer;
  color: var(--slate); flex: 0 0 auto;
}
@media (max-width: 760px) {
  #searchToggle { display: flex; }
}

/* --------------------------------------------------------------------- AI */
/* A draft should never look like a saved record. */
.ai-card { border-left: 3px solid var(--blue); background: #FCFDFF; }
.ai-headline { font-family: var(--display); font-weight: 600; font-size: 15px; margin: 0 0 6px; }
.ai-status { font-size: 13.5px; margin: 0 0 12px; color: var(--ink); }
.ai-list { margin: 4px 0 12px; padding-left: 18px; font-size: 13px; color: var(--slate); }
.ai-list li { margin-bottom: 3px; }
.ai-list li b { color: var(--ink); font-weight: 500; }
.ai-concern {
  display: flex; align-items: flex-start; gap: 6px; font-size: 13px;
  color: var(--amber); background: var(--amber-light);
  border-radius: 6px; padding: 8px 10px; margin: 0 0 10px;
}
.ai-error { font-size: 12.5px; color: var(--red); margin: 10px 0 0; }
.ai-prov {
  font-family: var(--mono); font-size: 11px; color: var(--slate-light);
  margin: 10px 0 0; border-top: 1px solid var(--border); padding-top: 8px;
}

/* ------------------------------------------------------------- checklists */
.cl-bar {
  height: 8px; border-radius: 4px; background: var(--canvas);
  overflow: hidden; border: 1px solid var(--border);
}
.cl-bar.sm { height: 6px; }
.cl-bar span { display: block; height: 100%; transition: width .25s ease; }

.cl-item {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 9px 0; border-top: 1px solid var(--border);
}
.cl-item:first-of-type { border-top: 0; }
.cl-item.done .cl-label { color: var(--slate); text-decoration: line-through; }
.cl-item.na .cl-label { color: var(--slate-light); }
.cl-item.blocked { background: var(--amber-light); border-radius: 6px; padding-left: 8px; padding-right: 8px; }
.cl-label { font-size: 13.5px; margin: 0; }
.cl-meta { font-size: 11.5px; color: var(--slate-light); margin: 2px 0 0; }
.cl-blocked {
  display: flex; align-items: center; gap: 5px;
  font-size: 12px; color: var(--amber); margin: 3px 0 0;
}
.cl-tick {
  flex: 0 0 auto; width: 24px; height: 24px; border-radius: 6px;
  border: 1px solid var(--border); background: #fff; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  color: var(--green); padding: 0;
}
.cl-tick:hover { border-color: var(--green); }
.cl-item.done .cl-tick { background: var(--green-light); border-color: var(--green); }
.cl-empty { width: 10px; height: 10px; }
.cl-actions { display: flex; gap: 2px; flex: 0 0 auto; opacity: 0; }
.cl-item:hover .cl-actions, .cl-item.blocked .cl-actions, .cl-item.na .cl-actions { opacity: 1; }

/* A key or URL you need to read in full and copy. Not an <input>: a long
   value in a one-line field is exactly what made this painful. */
.keybox {
  font-family: var(--mono); font-size: 12.5px; line-height: 1.5;
  background: var(--canvas); border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: 10px 12px;
  word-break: break-all; user-select: all; -webkit-user-select: all;
  color: var(--ink);
}

/* A line the AI scoped but nobody has measured. Zero is not a price. */
input.needs-qty {
  border-color: var(--amber); background: var(--amber-light);
}

/* Same warning in a read-only table — a supplement item with no quantity
   cannot be sent, so it should be impossible to miss on the way past. */
td.needs-qty {
  color: var(--amber); font-weight: 600;
}

/* The "and this is what it all adds up to" row on a carrier scope. */
table.data tfoot td {
  border-top: 2px solid var(--border); padding-top: 10px;
}

/* Compose, on the Communications tab. Sized for a thumb: the textarea is big
   enough to see what you wrote and the button is full width on a phone. */
.compose { display: flex; flex-direction: column; gap: 10px; }
.kind-row { display: flex; flex-wrap: wrap; gap: 6px; }
.compose textarea { width: 100%; font-size: 15px; }
@media (max-width: 620px) {
  .compose .btn { width: 100%; justify-content: center; }
}

/* ---- rep diagnosis ---------------------------------------------------- */
/* The sentence leads. The table exists to back it up, not to be stared at. */
.diagnosis {
  border-left: 3px solid var(--blue); background: var(--blue-light);
  padding: 11px 14px; border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  margin: 0 0 14px;
}
.diagnosis.muted-diag {
  border-left-color: var(--slate-light); background: var(--canvas);
}
.diag-head {
  margin: 0; font-weight: 600; font-size: 14px; display: flex;
  align-items: center; gap: 7px; color: var(--ink);
}
.diag-detail { margin: 5px 0 0; font-size: 13px; color: var(--slate); }
.diag-fix {
  margin: 7px 0 0; font-size: 12.5px; color: var(--slate);
  display: flex; align-items: baseline; gap: 6px;
}

/* Pitch sections, worst first — the shape of the bar is the message. */
.sections { display: flex; flex-direction: column; gap: 5px; }
.sec-row { display: flex; align-items: center; gap: 10px; font-size: 12.5px; }
.sec-label { width: 150px; flex: 0 0 auto; color: var(--slate); }
.sec-track {
  flex: 1; height: 7px; background: var(--canvas);
  border-radius: 4px; overflow: hidden;
}
.sec-fill { display: block; height: 100%; border-radius: 4px; }
.sec-val { width: 28px; text-align: right; flex: 0 0 auto; }

/* ---- photos and documents -------------------------------------------- */
/* Capture. Designed for someone standing on a roof holding a phone in one
   hand, so the targets are big and there are only two of them. */
.capture {
  border: 2px dashed var(--border); border-radius: var(--radius);
  padding: 16px; background: var(--canvas);
  transition: border-color .12s, background .12s;
}
.capture.over { border-color: var(--blue); background: var(--blue-light); }

.phase-row { display: flex; flex-wrap: wrap; gap: 6px; margin: 6px 0 14px; }
.phase {
  border: 1px solid var(--border); background: #fff; color: var(--slate);
  border-radius: 999px; padding: 7px 14px; font-size: 13px; cursor: pointer;
  font-weight: 500;
}
.phase.on {
  background: var(--ink); color: #fff; border-color: var(--ink);
}
.capture-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.btn.big { padding: 12px 18px; font-size: 14.5px; }

/* Batch progress. A bar plus a count, because "uploading…" for four minutes
   with no number is indistinguishable from a hang. */
.upload-status { margin-top: 12px; }
.up-track {
  height: 8px; background: var(--canvas); border: 1px solid var(--border);
  border-radius: 5px; overflow: hidden;
}
.up-fill {
  display: block; height: 100%; background: var(--blue);
  transition: width .2s ease;
}

@media (max-width: 620px) {
  .capture-actions { flex-direction: column; }
  .capture-actions .btn { width: 100%; justify-content: center; }
}

/* Photo reading. The picture sits next to what was said about it, because a
   description you cannot check against the image is worth nothing. */
.triage-grid {
  display: flex; flex-direction: column; gap: 10px; margin-top: 12px;
  max-height: 420px; overflow-y: auto;
}
.triage-row { display: flex; gap: 11px; align-items: flex-start; }
.triage-row img {
  width: 74px; height: 56px; object-fit: cover; border-radius: 5px;
  flex: 0 0 auto; background: var(--canvas);
}
.triage-row .b { margin: 0; font-size: 13px; line-height: 1.4; }
.triage-row .micro { margin: 3px 0 0; }
.cond-dmg { color: var(--orange); font-weight: 600; }
.cond-unclear { color: var(--slate-light); }

.file-grid {
  display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
}
.file-tile {
  margin: 0; cursor: pointer; border: 1px solid var(--border);
  border-radius: var(--radius-sm); overflow: hidden; background: #fff;
  transition: border-color .12s, transform .12s;
}
.file-tile:hover { border-color: var(--blue); transform: translateY(-1px); }
.file-tile img {
  width: 100%; height: 118px; object-fit: cover; display: block;
  background: var(--canvas);
}
.file-tile figcaption {
  padding: 8px 9px; display: flex; flex-direction: column; gap: 3px;
  font-size: 12px; line-height: 1.35;
}
.file-tile figcaption b {
  font-weight: 500; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap;
}
.doc-tile {
  height: 118px; display: flex; flex-direction: column; gap: 6px;
  align-items: center; justify-content: center; background: var(--canvas);
  color: var(--slate); font-family: var(--mono); font-size: 11px;
}

/* Lightbox. The image gets the room; the chrome stays out of the way. */
.lb-stage {
  position: relative; display: flex; align-items: center;
  justify-content: center; background: var(--ink);
  border-radius: var(--radius-sm); min-height: 260px; margin: 10px 0;
}
.lb-stage img { max-width: 100%; max-height: 62vh; display: block; }
.lb-doc {
  color: #fff; text-align: center; padding: 40px 20px;
  display: flex; flex-direction: column; gap: 10px; align-items: center;
}
.lb-nav {
  position: absolute; left: 6px; top: 50%; transform: translateY(-50%);
  width: 38px; height: 38px; border-radius: 50%; border: 0; cursor: pointer;
  background: #0009; color: #fff; font-size: 22px; line-height: 1;
}
.lb-nav.right { left: auto; right: 6px; }
.lb-nav:hover { background: #000c; }

/* Evidence attached to a supplement item — small, inline, and clearly
   different from the item's own text. */
.evidence-row { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.evidence-chip {
  display: inline-flex; align-items: center; gap: 5px; font-size: 11.5px;
  border: 1px solid var(--border); border-radius: 999px; padding: 2px 4px 2px 2px;
  background: #fff;
}
.evidence-chip img {
  width: 22px; height: 22px; object-fit: cover; border-radius: 50%;
}
.evidence-chip button {
  border: 0; background: none; cursor: pointer; color: var(--slate);
  font-size: 13px; line-height: 1; padding: 0 3px;
}
.evidence-chip button:hover { color: var(--red); }

/* The letter to the carrier: plain text, but long, so it scrolls in place
   rather than stretching the card. */
pre.keybox {
  max-height: 380px; overflow: auto; word-break: normal;
  user-select: text; -webkit-user-select: text;
}

/* ------------------------------------------------------------ plan cards */
.plans { display: grid; gap: 12px; grid-template-columns: repeat(3, 1fr); }
.plan {
  border: 1px solid var(--border); border-radius: var(--radius);
  padding: 12px 14px; background: #fff;
}
.plan.on { border-color: var(--blue); box-shadow: 0 0 0 1px var(--blue); }
.pill {
  font-size: 12px; padding: 3px 9px; border-radius: 999px;
  background: var(--canvas); color: var(--slate); white-space: nowrap;
}
.pill.ok { background: var(--green-light); color: var(--green-deep); }
.pill.warn { background: var(--amber-light); color: var(--amber); }
.pill.bad { background: var(--red-light); color: var(--red); }
@media (max-width: 760px) { .plans { grid-template-columns: 1fr; } }

/* ------------------------------------------------- the follow-up worklist
   ⚠️ THIS LIST IS WORKED FROM A PHONE, standing on a driveway between two
   calls — which is exactly where the rest of the app's tables get away with
   scrolling sideways inside their card and this one does not. Every row here
   ends in a decision (ring them, send it, push it back), and a button you
   have to scroll horizontally to reach is a button nobody presses.
   So on a narrow screen each row stops being a table row and becomes a card:
   date and customer on one line, the task, then the actions full width. */
@media (max-width: 760px) {
  .worklist, .worklist tbody, .worklist tr, .worklist td { display: block; }
  .worklist { min-width: 0 !important; table-layout: auto !important; }
  .worklist colgroup { display: none; }
  .worklist tr {
    padding: 12px 0; border-bottom: 1px solid var(--border);
  }
  .worklist tr:last-child { border-bottom: 0; }
  table.data.worklist td { border-top: 0; padding: 0 2px; }
  table.data.worklist td + td { margin-top: 6px; }
  table.data.worklist td.num {
    text-align: left; white-space: normal; margin-top: 10px;
    font-family: inherit;
  }
  .worklist td.num .btn { margin: 0 6px 6px 0; }
}

/* ---- the field mode (knock view) ---------------------------------- */
.knock-view { display: flex; flex-direction: column; gap: 10px;
  height: calc(100vh - 49px - 48px); }
.knock-top { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.knock-top .page-kicker { margin: 0; }
.knock-stats { display: flex; gap: 14px; margin-left: auto; font-size: 13px;
  color: var(--slate); }
.knock-stats b { color: var(--ink); font-family: var(--display); }
.knock-gps { align-self: flex-start; display: inline-flex; align-items: center;
  gap: 6px; font-size: 12px; color: var(--slate); background: #fff;
  border: 1px solid var(--border); border-radius: 999px; padding: 3px 10px; }
.knock-gps.ok { color: var(--green); border-color: var(--green-light); }
.knock-map { flex: 1; min-height: 320px; border-radius: var(--radius);
  border: 1px solid var(--border); overflow: hidden; background: #E9ECE6;
  z-index: 0; }
.knock-sheet[hidden] { display: none; }
.knock-sheet { position: absolute; right: 24px; bottom: 24px; width: 340px;
  max-width: calc(100vw - 48px); background: #fff; border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow-pop); padding: 14px;
  display: flex; flex-direction: column; gap: 10px; z-index: 1000; }
.knock-sheet-head { display: flex; align-items: center; justify-content: space-between; }
.knock-sheet input, .knock-sheet textarea { width: 100%; box-sizing: border-box;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 8px 10px; font: inherit; }
.dispo-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; }
.dispo-chip { border: 1px solid var(--border); background: #fff; color: var(--ink);
  border-radius: var(--radius-sm); padding: 8px 6px; font-size: 12.5px;
  cursor: pointer; border-left: 3px solid var(--chip); }
.dispo-chip.on { background: var(--chip); border-color: var(--chip); color: #fff; }
#apptFields { display: flex; flex-direction: column; gap: 6px; }
#apptFields[hidden] { display: none; }
.pin-pop p { margin: 2px 0; font-size: 12.5px; }
.knock-view .main, .knock-view { position: relative; }
@media (max-width: 760px) {
  .knock-view { height: calc(100vh - 49px - 24px); }
  .knock-sheet { position: fixed; left: 12px; right: 12px; width: auto;
    bottom: 12px; }
}

/* ------------------------------------------------------------------ mail */
.note { background: var(--canvas); border-radius: var(--radius-sm);
  padding: 10px 14px; font-size: 13px; margin-top: 10px; }
.mailcard { position: relative; width: 100%; overflow: hidden;
  border: 1px solid var(--border); border-radius: 6px; background: #fff;
  box-shadow: 0 1px 3px rgba(0,0,0,.08); }
.mailtile-grid { display: grid; gap: 18px;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); }
.mailtile { min-width: 0; }
.scheme-chip { width: 26px; height: 26px; border-radius: 50%;
  border: 2px solid var(--border); cursor: pointer; padding: 0; }
.scheme-chip.on { border-color: var(--ink); box-shadow: 0 0 0 2px #fff inset; }
.designer { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
.designer-preview { min-width: 0; }
.designer-form { min-width: 0; }
@media (max-width: 760px) {
  .designer { grid-template-columns: 1fr; }
}
