:root{
  /* Home screen install on iOS puts content under the notch and the home
     bar, so every fixed edge respects the safe area. */
  --safe-t: env(safe-area-inset-top, 0px);
  --safe-b: env(safe-area-inset-bottom, 0px);
  --navy:#0b2545; --navy-2:#123a68; --gold:#ffb703; --gold-d:#e0a000;
  --ink:#12151c; --muted:#6b7280; --line:#e3e7ee; --bg:#f5f7fa;
  --ok:#0f9d58; --warn:#e8a33d; --bad:#c0392b;
  --sheet-h: 52vh;
}
*{box-sizing:border-box; -webkit-tap-highlight-color:transparent}
html,body{height:100%}
body{
  margin:0; background:var(--bg); color:var(--ink);
  font:15px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
  overscroll-behavior:none;
}
.hidden{display:none !important}
.muted{color:var(--muted)}
.pad{padding:16px; padding-bottom:80px; max-width:1100px; margin:0 auto}

/* ---------- brand ---------- */
.brandMark{font-size:30px; font-weight:800; letter-spacing:-.5px; color:#fff}
.brandMark span{color:var(--gold)}
.brandMark.small{font-size:19px}
.login .brandMark{color:var(--navy)}

/* ---------- login ---------- */
.login{
  position:fixed; inset:0; display:grid; place-items:center; padding:20px;
  background:linear-gradient(160deg,var(--navy),var(--navy-2));
}
.loginCard{
  background:#fff; border-radius:18px; padding:28px 24px; width:100%; max-width:390px;
  box-shadow:0 24px 60px rgba(0,0,0,.3);
}
.loginSub{margin:2px 0 20px; color:var(--muted); font-size:14px}
.loginCard label{display:block; font-size:12px; font-weight:700; color:var(--muted);
  text-transform:uppercase; letter-spacing:.5px; margin-bottom:12px}
.loginCard input{
  display:block; width:100%; margin-top:5px; padding:12px 13px; font-size:16px;
  border:1.5px solid var(--line); border-radius:10px; background:#fbfcfe;
  text-transform:none; letter-spacing:0; color:var(--ink); font-weight:400;
}
.loginCard input:focus{outline:none; border-color:var(--navy)}
.loginError{color:var(--bad); font-size:13px; margin-top:10px; min-height:18px; text-align:center}
.loginHint{margin-top:16px; padding-top:14px; border-top:1px solid var(--line);
  font-size:12px; color:var(--muted); line-height:1.7}
.loginHint strong{display:block; color:var(--ink); margin-bottom:4px}

/* ---------- buttons ---------- */
.btn{
  border:none; border-radius:11px; padding:12px 18px; font-size:15px; font-weight:700;
  cursor:pointer; font-family:inherit; transition:transform .06s, filter .15s;
}
.btn:active{transform:scale(.975)}
.btnPrimary{background:var(--navy); color:#fff}
.btnAccent{background:var(--gold); color:#111}
.btnGhost{background:#fff; color:var(--navy); border:1.5px solid var(--line)}
.btnDanger{background:#fff; color:var(--bad); border:1.5px solid #f3c9c4}
.btnBlock{display:block; width:100%}
.btnSm{padding:8px 13px; font-size:13px; border-radius:9px}
.btn:disabled{opacity:.5; cursor:not-allowed}

/* ---------- app shell ---------- */
.app{height:100%; display:flex; flex-direction:column}
.topbar{
  height:calc(54px + var(--safe-t)); flex:0 0 calc(54px + var(--safe-t));
  padding-top:var(--safe-t); background:var(--navy); color:#fff;
  display:flex; align-items:center; gap:10px; padding:0 10px; z-index:900;
  box-shadow:0 2px 10px rgba(0,0,0,.16);
}
.topbarRight{margin-left:auto; display:flex; gap:8px; align-items:center}
.iconBtn{background:none; border:none; color:inherit; font-size:24px; line-height:1;
  padding:8px; cursor:pointer; border-radius:8px}
.chip{background:rgba(255,255,255,.15); border-radius:20px; padding:5px 11px;
  font-size:12px; font-weight:600; max-width:44vw; overflow:hidden;
  text-overflow:ellipsis; white-space:nowrap}

/* ---------- drawer ---------- */
.drawer{
  position:fixed; top:0; left:0; bottom:0; width:278px;
  padding-top:var(--safe-t); background:var(--navy); color:#fff;
  transform:translateX(-100%); transition:transform .22s ease; z-index:1000;
  display:flex; flex-direction:column; padding-bottom:16px;
}
.drawer.open{transform:none}
.drawerHead{display:flex; align-items:center; gap:11px; padding:18px 16px 14px;
  border-bottom:1px solid rgba(255,255,255,.14)}
.avatar{width:42px; height:42px; border-radius:50%; background:var(--gold); color:#111;
  display:grid; place-items:center; font-weight:800; font-size:16px; flex:0 0 42px}
.navName{font-weight:700; font-size:15px}
.navRole{font-size:12px; color:rgba(255,255,255,.65); text-transform:capitalize}
.drawerClose{margin-left:auto}
.navGroup{font-size:11px; letter-spacing:1.2px; text-transform:uppercase;
  color:rgba(255,255,255,.5); padding:16px 16px 6px; font-weight:700}
.navItem{display:block; padding:11px 16px; font-size:15px; color:#fff; text-decoration:none;
  cursor:pointer; border-left:3px solid transparent}
.navItem.active{background:rgba(255,255,255,.1); border-left-color:var(--gold); font-weight:700}
.navItem.danger{color:#ffb4a8}
.navSpacer{flex:1}
.scrim{position:fixed; inset:0; background:rgba(0,0,0,.45); z-index:999; display:none}
.scrim.open{display:block}

/* ---------- views ---------- */
.view{flex:1; display:none; position:relative; overflow-y:auto; -webkit-overflow-scrolling:touch}
.view.active{display:block}
#view-map{overflow:hidden}
#map{position:absolute; inset:0}

/* ---------- map chrome ---------- */
/* The top-of-map stack. Everything inside flows in document order, so
   adding a banner can never land on top of the search box again. The stack
   does not capture pointer events itself, only its children, so the map is
   still draggable in the gaps between cards. */
.mapTop{
  position:absolute; top:calc(10px + var(--safe-t)); left:10px; right:10px; z-index:700;
  display:flex; flex-direction:column; gap:8px; align-items:stretch;
  max-width:520px; pointer-events:none;
}
.mapTop > *{pointer-events:auto}

.mapChips{display:flex; flex-direction:column; gap:7px; align-items:flex-start}
.segmented{display:flex; background:#fff; border-radius:22px; padding:3px;
  box-shadow:0 2px 10px rgba(0,0,0,.2); gap:2px; flex-wrap:nowrap; overflow:auto}
.segmented button{
  border:none; background:none; padding:7px 13px; border-radius:20px; font-size:13px;
  font-weight:600; cursor:pointer; white-space:nowrap; color:var(--muted); font-family:inherit;
}
.segmented button.on{background:var(--navy); color:#fff}

/* Centred, so it must stay clear of BOTH side columns or it lands on the
   location button. Inset by more than the widest side control rather than
   nearly the full width. */
.mapStatus{position:absolute; left:50%; transform:translateX(-50%);
  bottom:calc(78px + var(--safe-b));
  z-index:650; background:#fff; border-radius:22px; padding:9px 16px; font-size:13px;
  font-weight:700; box-shadow:0 4px 16px rgba(0,0,0,.3); max-width:calc(100% - 136px);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; display:none}
.mapStatus.show{display:block}

.pinBar{position:absolute; left:0; right:0; bottom:0; z-index:600;
  padding:12px 14px calc(18px + var(--safe-b));
  display:flex; align-items:center; gap:12px;
  background:linear-gradient(to top,rgba(0,0,0,.5),transparent); pointer-events:none}
.pinBar > *{pointer-events:auto}
.pinCount{color:#fff; font-weight:700; font-size:14px; text-shadow:0 1px 4px rgba(0,0,0,.6)}
.btnPin{margin-left:auto; background:var(--gold); color:#111; padding:14px 26px;
  border-radius:30px; font-size:16px; box-shadow:0 5px 18px rgba(0,0,0,.35)}
.btnPin.on{background:var(--bad); color:#fff}

/* ---------- sheet ---------- */
.sheet{
  position:absolute; left:0; right:0; bottom:0; z-index:700; background:#fff;
  border-radius:18px 18px 0 0; box-shadow:0 -6px 30px rgba(0,0,0,.28);
  transform:translateY(102%); transition:transform .25s cubic-bezier(.2,.8,.3,1);
  max-height:88vh; display:flex; flex-direction:column;
}
.sheet.open{transform:none}
.sheetGrip{width:40px; height:4px; background:#d3d8e2; border-radius:3px;
  margin:9px auto 3px; flex:0 0 auto}
.sheetBody{overflow-y:auto; padding:8px 16px calc(26px + var(--safe-b))}

/* ---------- cards, tables ---------- */
.card{background:#fff; border-radius:14px; padding:16px; margin-bottom:14px;
  box-shadow:0 1px 3px rgba(16,24,40,.07); border:1px solid var(--line)}
.card h3{margin:0 0 10px; font-size:16px}
.cardTitle{display:flex; align-items:center; justify-content:space-between; gap:10px; margin-bottom:12px}
.cardTitle h3{margin:0}

h2.viewTitle{font-size:22px; margin:4px 0 14px}

.kv{display:grid; grid-template-columns:1fr auto; gap:5px 14px; font-size:14px}
.kv .k{color:var(--muted)}
.kv .v{font-weight:700; text-align:right}

.statGrid{display:grid; grid-template-columns:repeat(auto-fit,minmax(128px,1fr)); gap:10px}
.stat{background:#fff; border:1px solid var(--line); border-radius:12px; padding:12px}
.stat .k{font-size:10.5px; text-transform:uppercase; letter-spacing:.7px; color:var(--muted); font-weight:700}
.stat .v{font-size:23px; font-weight:800; margin-top:3px; letter-spacing:-.5px}
.stat .s{font-size:11.5px; color:var(--muted)}

table.data{width:100%; border-collapse:collapse; font-size:13.5px}
table.data th{text-align:left; font-size:10.5px; text-transform:uppercase; letter-spacing:.6px;
  color:var(--muted); padding:8px 9px; border-bottom:2px solid var(--line); white-space:nowrap}
table.data td{padding:9px; border-bottom:1px solid var(--line)}
table.data td.n, table.data th.n{text-align:right; white-space:nowrap}
.tableWrap{overflow-x:auto; -webkit-overflow-scrolling:touch}

.pill{display:inline-block; padding:3px 9px; border-radius:20px; font-size:11px; font-weight:700;
  text-transform:uppercase; letter-spacing:.4px}
.pill.pinned{background:#eef1f6; color:#5b6270}
.pill.measured{background:#e7f0fd; color:#1b4f9c}
.pill.quoted{background:#e6f6ec; color:#0f7a3d}
.pill.queued{background:#fff3d6; color:#8a6100}
.pill.batched{background:#ede7fb; color:#5a37b8}
.pill.mailed{background:#dff3fb; color:#0d6a86}
.pill.responded{background:#ffe9d6; color:#a5490a}
.pill.sold{background:var(--gold); color:#111}
.pill.rejected{background:#fdeceb; color:#a5271c}

.rowFlex{display:flex; gap:10px; align-items:center; flex-wrap:wrap}
.grow{flex:1}

label.field{display:block; margin-bottom:12px; font-size:12px; font-weight:700;
  color:var(--muted); text-transform:uppercase; letter-spacing:.5px}
label.field input, label.field select, label.field textarea{
  display:block; width:100%; margin-top:5px; padding:10px 12px; font-size:15px;
  border:1.5px solid var(--line); border-radius:9px; font-family:inherit;
  text-transform:none; letter-spacing:0; color:var(--ink); font-weight:500; background:#fff;
}
label.field input:focus,label.field select:focus{outline:none; border-color:var(--navy)}
.fieldRow{display:grid; grid-template-columns:1fr 1fr; gap:0 12px}

.bandRow{display:grid; grid-template-columns:56px 12px 56px 1fr 110px; gap:8px;
  align-items:center; margin-bottom:9px}
.bandRow input{padding:8px 9px; font-size:14px; border:1.5px solid var(--line);
  border-radius:8px; width:100%; font-family:inherit}
.bandRow .dash{text-align:center; color:var(--muted)}
.bandRow .lbl{font-size:12.5px; color:var(--muted); overflow:hidden; text-overflow:ellipsis; white-space:nowrap}

.banner{border-radius:11px; padding:11px 13px; font-size:13.5px; margin-bottom:12px; line-height:1.5}
.banner.warn{background:#fff6e5; border:1px solid #f0d9a8; color:#7a5300}
.banner.bad{background:#fdeceb; border:1px solid #f5c6c2; color:#8a231a}
.banner.info{background:#eaf2fd; border:1px solid #c3d8f5; color:#14417f}
.banner.ok{background:#e8f6ed; border:1px solid #b6e0c6; color:#0d6b34}

.bigPrice{font-size:38px; font-weight:800; letter-spacing:-1.5px; line-height:1}
.subPrice{font-size:13px; color:var(--muted)}

.leaderRow{display:flex; align-items:center; gap:12px; background:#fff; border:1px solid var(--line);
  border-radius:12px; padding:12px 14px; margin-bottom:9px}
.rank{width:30px; height:30px; border-radius:50%; background:var(--bg); display:grid;
  place-items:center; font-weight:800; font-size:14px; flex:0 0 30px}
.rank.r1{background:var(--gold); color:#111}
.rank.r2{background:#d6dae2}
.rank.r3{background:#e8c9a0}
.leaderName{font-weight:700; font-size:15px}
.leaderSub{font-size:12px; color:var(--muted)}
.leaderNums{margin-left:auto; text-align:right}
.leaderBig{font-weight:800; font-size:17px}

.toastWrap{position:fixed; left:0; right:0; bottom:calc(88px + var(--safe-b)); z-index:2000; display:flex;
  flex-direction:column; align-items:center; gap:8px; pointer-events:none; padding:0 16px}
.toast{background:#12151c; color:#fff; padding:11px 17px; border-radius:11px; font-size:14px;
  font-weight:600; box-shadow:0 6px 22px rgba(0,0,0,.3); max-width:520px}
.toast.bad{background:var(--bad)}
.toast.ok{background:var(--ok)}

.spinner{width:17px; height:17px; border:2.5px solid rgba(255,255,255,.35);
  border-top-color:#fff; border-radius:50%; animation:spin .7s linear infinite; display:inline-block;
  vertical-align:-3px; margin-right:7px}
@keyframes spin{to{transform:rotate(360deg)}}

.pinDot{border-radius:50%; border:2.5px solid #fff; box-shadow:0 1px 5px rgba(0,0,0,.5)}

@media(min-width:760px){
  .fieldRow{grid-template-columns:1fr 1fr 1fr}
  .sheet{left:auto; right:16px; bottom:16px; width:440px; border-radius:16px; max-height:calc(100% - 32px)}
  .sheet{transform:translateY(calc(100% + 32px))}
  .sheet.open{transform:none}
}

/* Shown when the map library or tile host is unreachable. */
.mapFail{position:absolute; inset:0; display:flex; flex-direction:column; gap:6px;
  align-items:center; justify-content:center; text-align:center; padding:24px;
  background:var(--bg); color:var(--muted)}
.mapFail strong{color:var(--ink); font-size:17px}
.mapFail span{font-size:13.5px; max-width:300px}

/* Rapid pinning feedback */
.pinPending{animation:pulse 1s ease-in-out infinite}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.4}}
.pendingBadge{background:var(--gold); color:#111; border-radius:12px; padding:2px 8px;
  font-size:11px; font-weight:800; margin-left:6px; text-shadow:none}
.warnDot{display:inline-block; width:8px; height:8px; border-radius:50%;
  background:var(--bad); margin-right:6px; vertical-align:1px}

/* Manual trace toolbar */
.traceBar{position:absolute; left:10px; right:10px; bottom:calc(12px + var(--safe-b)); z-index:800;
  background:#fff; border-radius:14px; padding:12px 14px;
  box-shadow:0 6px 26px rgba(0,0,0,.3)}
.traceTop{display:flex; justify-content:space-between; align-items:baseline; margin-bottom:3px}
.traceHint{font-size:12.5px; color:var(--muted); margin-bottom:10px}
.traceRow{display:flex; gap:8px; align-items:flex-end; margin-bottom:9px}
.traceRow:last-child{margin-bottom:0}
.traceField{flex:1; font-size:11px; font-weight:700; color:var(--muted);
  text-transform:uppercase; letter-spacing:.5px}
.traceField select{display:block; width:100%; margin-top:4px; padding:9px 10px;
  font-size:15px; border:1.5px solid var(--line); border-radius:9px;
  font-family:inherit; text-transform:none; letter-spacing:0; color:var(--ink)}

/* The sheet and the pin bar share the bottom of a phone screen, so the
   pin bar steps aside whenever the sheet is up. */
#view-map.sheetOpen .pinBar,
#view-map.sheetOpen .mapStatus{display:none}
.sheetClose{position:absolute; top:6px; right:8px; z-index:2; background:#f1f3f7;
  border:none; width:32px; height:32px; border-radius:50%; font-size:22px;
  line-height:1; color:var(--muted); cursor:pointer}
@media(min-width:760px){ #view-map.sheetOpen .pinBar{display:flex} }

/* ---------- owner and contacts ---------- */
.ownerBlock{margin:14px 0 0}
.contactRow{border:1px solid var(--line); border-left-width:4px; border-radius:10px;
  padding:10px 11px; margin-bottom:9px; background:#fff}
.contactRow.ok{border-left-color:var(--ok)}
.contactRow.warn{border-left-color:var(--warn)}
.contactRow.bad{border-left-color:var(--bad); background:#fffafa}
.contactVal{font-weight:700; font-size:15px; letter-spacing:.2px}
.light{font-size:10px; font-weight:800; letter-spacing:.5px; padding:4px 8px;
  border-radius:20px; white-space:nowrap; flex:0 0 auto}
.light.ok{background:#e6f6ec; color:#0f7a3d}
.light.warn{background:#fff3d6; color:#8a6100}
.light.bad{background:#fdeceb; color:#a5271c}
.chips{margin-top:4px; display:flex; gap:5px; flex-wrap:wrap}
.flagChip{font-size:10px; font-weight:700; padding:2px 7px; border-radius:20px;
  background:#eef1f6; color:#5b6270; text-transform:uppercase; letter-spacing:.3px}
.flagChip.bad{background:#fdeceb; color:#a5271c}
.flagChip.warn{background:#fff3d6; color:#8a6100}
.flagChip.ok{background:#e6f6ec; color:#0f7a3d}
ul.why{margin:6px 0 0; padding-left:16px; font-size:12px; color:var(--bad); line-height:1.5}
ul.why.duty{color:var(--muted)}
ul.why li.hard{color:var(--bad); font-weight:700}

/* Keep the status pill clear of the sheet's close button */
.sheetBody > .rowFlex:first-child{padding-right:34px}

/* ---------- pinned houses list ---------- */
.searchBox{width:100%; padding:11px 13px; font-size:16px; border:1.5px solid var(--line);
  border-radius:10px; font-family:inherit}
.searchBox:focus{outline:none; border-color:var(--navy)}
table.listTable th[data-sort]{cursor:pointer; user-select:none}
table.listTable th[data-sort]:hover{color:var(--navy)}
table.listTable tbody tr{cursor:pointer}
table.listTable tbody tr:hover{background:#f7f9fc}

/* ---------- settings check rows ---------- */
.checkRow{display:flex; gap:11px; align-items:flex-start; padding:9px 11px;
  border:1px solid var(--line); border-left-width:4px; border-radius:9px; margin-bottom:7px}
.checkRow.ok{border-left-color:var(--ok)}
.checkRow.warn{border-left-color:var(--warn)}
.checkRow.bad{border-left-color:var(--bad)}
.checkRow strong{font-size:10px; letter-spacing:.5px; padding:3px 7px; border-radius:20px;
  flex:0 0 auto; margin-top:1px}
.checkRow.ok strong{background:#e6f6ec; color:#0f7a3d}
.checkRow.warn strong{background:#fff3d6; color:#8a6100}
.checkRow.bad strong{background:#fdeceb; color:#a5271c}
details summary::-webkit-details-marker{color:var(--navy)}

/* The pinned-houses table folds down on a phone: Address and Quote stay,
   everything else moves into the address cell so nothing is clipped. */
.rowMeta{display:none; gap:8px; align-items:center; flex-wrap:wrap; margin-top:5px; font-size:11.5px}
@media(max-width:700px){
  table.listTable .opt{display:none}
  .rowMeta{display:flex}
  table.listTable td{padding:10px 8px}
}

/* Undo snackbar. Deleting is one gesture; undoing is one tap for six seconds. */
.undoBar{position:fixed; left:14px; right:14px; bottom:calc(94px + var(--safe-b));
  z-index:3000; background:#12151c; color:#fff; border-radius:12px;
  padding:13px 15px; display:flex; align-items:center; gap:12px;
  font-size:14px; font-weight:600; box-shadow:0 8px 28px rgba(0,0,0,.4);
  animation:undoIn .18s ease}
@keyframes undoIn{from{transform:translateY(12px);opacity:0}to{transform:none;opacity:1}}
.undoBtn{background:none; border:none; color:var(--gold); font-weight:800;
  font-size:14px; padding:4px 6px; cursor:pointer; font-family:inherit; flex:0 0 auto}
@media(min-width:760px){ .undoBar{left:auto; right:16px; width:380px} }

/* ---------- import ---------- */
.dropZone{border:2px dashed #c3cbd9; border-radius:12px; padding:24px 16px; text-align:center;
  cursor:pointer; background:#f9fbfd; display:flex; flex-direction:column; gap:4px}
.dropZone.over{border-color:var(--gold); background:#fffaf0}
.dropZone strong{font-size:15px}
.dropZone span{font-size:13px; color:var(--muted)}
label.field textarea{font-family:ui-monospace,SFMono-Regular,Menlo,monospace; font-size:13px;
  line-height:1.6; resize:vertical}
.progressOuter{height:10px; background:#eef1f6; border-radius:6px; overflow:hidden}
.progressInner{height:100%; background:var(--navy); border-radius:6px; transition:width .3s}

/* ---------- mailer preview ---------- */
.previewShell{position:relative; border:1px solid var(--line); border-radius:9px;
  overflow:hidden; background:#f4f6fa; min-height:150px; display:flex;
  align-items:center; justify-content:center; cursor:pointer}
.previewShell.ready{background:#fff}
.previewImg{width:100%; display:block}
.previewSkeleton{font-size:13px; color:var(--muted); padding:34px 16px; text-align:center}
.previewShell.ready:after{content:"Tap to see all 5 pages"; position:absolute;
  left:0; right:0; bottom:0; padding:7px; text-align:center; font-size:11.5px;
  font-weight:700; color:#fff; background:linear-gradient(transparent,rgba(0,0,0,.72))}

/* Checkbox rows in settings forms */
label.field.chk{display:flex; align-items:center; gap:10px; text-transform:none;
  letter-spacing:0; font-size:14px; font-weight:500; color:var(--ink)}
label.field.chk.top{align-items:flex-start}
label.field.chk input{width:auto; margin:0; flex:0 0 auto}
label.field.chk.top input{margin-top:3px}

/* ---------- address search ---------- */
.mapSearch{position:relative}
.searchRow{display:flex; align-items:center; gap:8px; background:#fff; border-radius:12px;
  padding:0 12px; box-shadow:0 3px 14px rgba(0,0,0,.25); height:44px}
.searchIcon{color:var(--muted); flex:0 0 auto}
.searchRow input{flex:1; border:none; outline:none; font-size:16px; font-family:inherit;
  background:none; color:var(--ink); min-width:0}
.searchRow input::-webkit-search-cancel-button{display:none}
.searchClear{background:#eef1f6; border:none; width:24px; height:24px; border-radius:50%;
  font-size:17px; line-height:1; color:var(--muted); cursor:pointer; flex:0 0 auto}
.suggestList{margin-top:6px; background:#fff; border-radius:12px; overflow:hidden;
  box-shadow:0 6px 22px rgba(0,0,0,.28); max-height:46vh; overflow-y:auto}
.sugItem{padding:11px 14px; cursor:pointer; border-bottom:1px solid var(--line)}
.sugItem:last-child{border-bottom:none}
.sugItem.on,.sugItem:hover{background:#f2f6fd}
.sugMain{font-size:14.5px; font-weight:600; line-height:1.25}
.sugSub{font-size:12.5px; color:var(--muted); margin-top:1px}
.sugMsg{padding:13px 14px; font-size:13.5px; color:var(--muted)}
.sugNote{padding:8px 14px; font-size:11.5px; color:var(--muted); background:#f7f9fc;
  border-top:1px solid var(--line)}
/* The chips sit below the search box now. */
#view-map.sheetOpen .mapSearch{display:none}

/* Zoom lives at the BOTTOM right, out of the top stack for good.
   It previously sat at a hardcoded offset below the search bar, which broke
   whenever the stack changed height: it covered the search clear button, and
   the suggestion list covered it. The stack's height is variable by design,
   so no fixed top offset can be correct. Bottom right has nothing above the
   pin bar, and it is closer to a thumb on a phone. */
/* Zoom sits ABOVE the My Location button, which is the more used control and
   so gets the spot closest to the thumb. These two overlapped when the button
   was added; a headless click test caught the zoom control swallowing the
   press. The offsets below are: pin bar, then locBtn (46px), then zoom. */
.leaflet-top.leaflet-right{top:auto; bottom:calc(130px + var(--safe-b)); right:10px}
.leaflet-top.leaflet-right .leaflet-control{margin-bottom:0}
/* Map controls and chips get out of the way while suggestions are open. */
#view-map.searching .leaflet-control-container,
#view-map.searching .mapChips{display:none}

/* ---------- signup ---------- */
.loginAlt{margin-top:14px; text-align:center; font-size:13.5px; color:var(--muted)}
.loginAlt a{color:var(--navy); font-weight:700; cursor:pointer; text-decoration:underline}

/* ---------- setup checklist ---------- */
.setupCard{background:#fff;
  border-radius:13px; padding:13px 14px; box-shadow:0 5px 20px rgba(0,0,0,.25)}
.setupHead{display:flex; align-items:baseline; gap:9px; margin-bottom:8px}
.setupHead strong{font-size:15px}
.setupHead span{font-size:12px; color:var(--muted)}
.setupHide{margin-left:auto; background:none; border:none; font-size:20px; line-height:1;
  color:var(--muted); cursor:pointer; padding:0 2px}
.setupBar{height:5px; background:#eef1f6; border-radius:3px; overflow:hidden; margin-bottom:10px}
.setupBar div{height:100%; background:var(--gold); border-radius:3px; transition:width .3s}
.setupStep{display:flex; align-items:center; gap:10px; padding:9px 0; cursor:pointer;
  border-top:1px solid var(--line)}
.setupLabel{font-size:14px; font-weight:600}
.setupDetail{font-size:12px; color:var(--muted); margin-top:1px}
.setupCrit{background:var(--gold); color:#111; font-size:9.5px; font-weight:800; padding:1px 6px;
  border-radius:10px; text-transform:uppercase; letter-spacing:.4px; vertical-align:1px}
.setupArrow{margin-left:auto; color:var(--muted); font-size:22px; line-height:1}
#view-map.searching .setupCard, #view-map.sheetOpen .setupCard{display:none}

/* With the result sheet up, the whole top stack and the zoom control are
   behind it or clipped by it. Hide them rather than leave half a banner
   peeking out from under the sheet. */
#view-map.sheetOpen .mapTop{display:none}
#view-map.sheetOpen .leaflet-control-container{display:none}

/* ---------- accounts back office ---------- */
.acctCard{background:#fff; border:1px solid var(--line); border-radius:13px; padding:14px;
  margin-bottom:12px}
.acctName{font-weight:700; font-size:16px}
.acctSub{font-size:12.5px; color:var(--muted); margin-top:1px}
.acctNums{display:grid; grid-template-columns:repeat(auto-fit,minmax(72px,1fr)); gap:8px;
  margin:10px 0; padding:10px 0; border-top:1px solid var(--line); border-bottom:1px solid var(--line)}
.acctNums div{font-size:10.5px; color:var(--muted); text-transform:uppercase; letter-spacing:.4px}
.acctNums div span{display:block; font-size:16px; font-weight:800; color:var(--ink);
  text-transform:none; letter-spacing:-.3px}
.acctActions{display:flex; gap:8px; align-items:flex-end; flex-wrap:wrap}
.acctField{flex:1; min-width:88px; font-size:10.5px; font-weight:700; color:var(--muted);
  text-transform:uppercase; letter-spacing:.4px}
.acctField select,.acctField input{display:block; width:100%; margin-top:4px; padding:8px 9px;
  font-size:14px; border:1.5px solid var(--line); border-radius:8px; font-family:inherit;
  text-transform:none; letter-spacing:0; color:var(--ink); font-weight:500}
.acctNote{width:100%; margin-top:9px; padding:9px 11px; font-size:13.5px; font-family:inherit;
  border:1.5px solid var(--line); border-radius:8px}

/* The ACH authorization text has to be readable and obviously a document. */
.agreeBox{background:#f7f9fc; border:1px solid var(--line); border-radius:9px;
  padding:12px 13px; margin:0 0 12px; max-height:230px; overflow-y:auto;
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace; font-size:11.5px;
  line-height:1.65; white-space:pre-wrap; color:var(--ink)}

/* ================================================================== *
 * The pricing lock
 *
 * Prices and the mailer PDF are withheld until billing is set up. The lock
 * has to read as a deliberate state with a way out, not as a broken screen,
 * or every locked company files a bug instead of adding a bank account.
 * ================================================================== */

.accessBanner{
  display:flex; gap:10px; align-items:center;
  background:#fff; border-radius:12px; padding:10px 12px;
  box-shadow:0 6px 22px rgba(11,37,69,.18);
  border-left:4px solid var(--warn);
}
.accessBanner.locked{border-left-color:var(--bad)}
.accessBanner .abIcon{
  width:30px; height:30px; flex:0 0 30px; border-radius:9px; display:grid; place-items:center;
  background:#fdeceb; color:var(--bad); font-weight:800;
}
.accessBanner.warn .abIcon{background:#fff3d6; color:#8a6100}
.accessBanner .abBody{flex:1; min-width:0; display:flex; flex-direction:column; line-height:1.25}
.accessBanner .abBody strong{font-size:13px}
.accessBanner .abBody span{font-size:11.5px; color:var(--muted)}
.accessBanner .abHint{font-size:11.5px; color:var(--muted); white-space:nowrap}

.lockPanel{
  border:1px dashed var(--line); border-radius:12px; padding:18px 16px;
  text-align:center; background:linear-gradient(180deg,#fbfcfe,#f5f7fa); margin:6px 0 14px;
}
.lockPanel .lockIcon{
  width:38px; height:38px; margin:0 auto 9px; border-radius:11px; display:grid; place-items:center;
  background:var(--navy); color:var(--gold);
}
.lockPanel .lockTitle{font-weight:800; font-size:15.5px}
.lockPanel .lockWhy{font-size:13px; color:var(--muted); margin:5px 0 9px}
.lockPanel .lockKeep{
  font-size:12px; color:#0f7a3d; background:#e6f6ec; border-radius:9px;
  padding:8px 10px; margin:0 0 11px; text-align:left;
}
.lockPanel .lockHint{font-size:12px; color:var(--muted)}
.mailerPreview.locked .lockPanel{margin-bottom:0}
.cellLock{opacity:.5; font-size:12px}

/* ================================================================== *
 * Print operations (platform owner only)
 * ================================================================== */

.viewSub{margin:-8px 0 14px; color:var(--muted); font-size:13.5px; max-width:640px}
h3.secTitle,h4.secTitle{margin:20px 0 10px; font-size:15px}

.opsNums{display:flex; gap:14px; text-align:right}
.opsNums div{display:flex; flex-direction:column; font-size:11px; color:var(--muted)}
.opsNums span{font-size:16px; font-weight:800; color:var(--ink); line-height:1.1}
.opsNums span.ok{color:var(--ok)}
.opsNums span.warn{color:#8a6100}

.trustPill{
  display:inline-block; padding:2px 8px; border-radius:20px;
  font-size:10.5px; font-weight:800; letter-spacing:.02em;
}
.trustPill.yes{background:#e6f6ec; color:#0f7a3d}
.trustPill.no{background:#fff3d6; color:#8a6100}

.holdList{
  margin-top:9px; border-top:1px solid var(--line); padding-top:8px;
  display:flex; flex-direction:column; gap:3px;
}
.holdList div{font-size:12px; color:var(--muted); display:flex; gap:8px; align-items:baseline}
.holdList .holdN{
  min-width:24px; text-align:center; font-weight:800; color:var(--ink);
  background:#eef1f6; border-radius:6px; font-size:11px; padding:1px 4px;
}
.opsDetail{margin-top:10px; border-top:1px solid var(--line); padding-top:10px}

.pkgRow{
  display:flex; gap:12px; align-items:center; background:#fff;
  border:1px solid var(--line); border-radius:11px; padding:11px 12px; margin-bottom:8px;
}
.mono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace; word-break:break-all}

/* ---------- modal ---------- */
.modalWrap{
  position:fixed; inset:0; z-index:900; display:none;
  background:rgba(11,37,69,.55); padding:24px 14px;
  overflow:auto; -webkit-overflow-scrolling:touch;
}
.modalWrap.open{display:block}
.modalCard{
  position:relative; background:#fff; border-radius:16px; padding:20px;
  max-width:900px; margin:0 auto; box-shadow:0 24px 60px rgba(0,0,0,.3);
  padding-bottom:calc(20px + var(--safe-b));
}
.modalClose{
  position:absolute; top:8px; right:10px; border:0; background:transparent;
  font-size:26px; line-height:1; color:var(--muted); cursor:pointer; padding:4px 8px;
}

/* A disabled action must not look like the primary call to action. */
.btn[disabled]{opacity:.5; cursor:default; pointer-events:none}

/* ---------- My location ----------
   Bottom right, above the pin bar. Sized for a thumb in a moving truck, which
   is the only place this button ever gets pressed. */
.locBtn{
  position:absolute; right:10px; bottom:calc(74px + var(--safe-b)); z-index:660;
  width:46px; height:46px; border-radius:50%; border:none; cursor:pointer;
  background:#fff; color:var(--navy); display:grid; place-items:center;
  box-shadow:0 3px 14px rgba(0,0,0,.3);
}
.locBtn:active{transform:scale(.94)}
/* Following: the button reads as engaged, so the state is visible without
   looking at the pin bar. */
.locBtn.following{background:var(--navy); color:#fff}
/* Located but the rep has panned away, so pressing it does something. */
.locBtn.stale{color:var(--muted)}
.locBtn.busy svg{animation:locSpin 1s linear infinite}
@keyframes locSpin{to{transform:rotate(360deg)}}
#view-map.sheetOpen .locBtn{display:none}

/* The rep's own position. A heading cone is drawn only when the device
   reports one, because a wrong heading is worse than none. */
.meWrap{pointer-events:none}
.meCone{opacity:.35}

/* ---------- Order lifecycle ----------
   Always visible on a pin, so "why is there no price" is answered before it is
   asked. Three steps, because that is the whole journey: ordered, paying, paid. */
.orderStrip{
  border:1px solid var(--line); border-radius:12px; padding:10px 12px;
  margin:0 0 12px; background:#fff;
}
.osHead{display:flex; align-items:center; gap:8px; font-size:12px}
.osTrack{display:flex; gap:6px; margin-top:9px}
.osStep{flex:1; display:flex; flex-direction:column; gap:5px; font-size:10.5px;
  color:var(--muted); text-transform:uppercase; letter-spacing:.4px}
.osStep i{display:block; height:4px; border-radius:2px; background:#eef1f6}
.osStep.done i{background:var(--gold)}
.osStep.done span{color:var(--ink); font-weight:700}

/* One-stat grid, for when the pitch is the only number a customer may see. */
.statGrid.one{grid-template-columns:1fr}

/* "edit" next to the address. Small, but always present: a wrong house number
   is the one error that costs a whole piece and annoys a stranger. */
.addrEdit{
  border:1px solid var(--line); background:#fff; color:var(--navy);
  font:inherit; font-size:11px; font-weight:700; text-transform:uppercase;
  letter-spacing:.4px; border-radius:6px; padding:2px 7px; margin-left:7px;
  cursor:pointer; vertical-align:middle;
}
.addrEdit:active{background:#eef1f6}

/* Draggable corner handles. Bigger hit area than they look, because this is
   done with a thumb on a phone in a truck. */
.traceHandle{cursor:grab}
.traceHandle:active{cursor:grabbing}
path.traceHandle{stroke-width:2}

/* While drawing, the map is the canvas: no controls competing with the
   tall trace bar for the same corner. */
#view-map.tracing .leaflet-control-container,
#view-map.tracing .locBtn,
#view-map.tracing .mapTop,
/* The pin bar too: you are drawing one roof, not pinning houses, and the
   trace bar sits exactly where it lives. */
#view-map.tracing .pinBar{display:none}

/* ---------- Dev mode banner ----------
   Fixed, undismissable, and ugly on purpose. The failure this prevents is
   demoing to a prospect with the paywall off. */
.modeBanner{
  position:fixed; left:0; right:0; top:0; z-index:1200;
  background:repeating-linear-gradient(45deg,#7c2d12,#7c2d12 12px,#9a3412 12px,#9a3412 24px);
  color:#fff; font-size:11.5px; line-height:1.3; padding:5px 12px;
  padding-top:calc(5px + var(--safe-t)); text-align:center; letter-spacing:.2px;
}
.modeBanner strong{letter-spacing:.08em; margin-right:6px}
.modeBanner .mbGo{
  margin-left:8px; background:#fff; color:#7c2d12; border:none; border-radius:5px;
  font:inherit; font-weight:700; padding:1px 7px; cursor:pointer;
}
/* Push everything down by the banner's measured height, set from JS. A
   hardcoded offset was wrong as soon as the banner wrapped to two lines on a
   narrow phone, and it covered the nav bar. */
:root{--devbar-h:0px}
/* .app is height:100% and lays its views out with flex, so PADDING would push
   the bottom of the app past the viewport and hide the pin bar. It needs a
   shorter box instead: shift it down and take the same amount off its height. */
body.devMode .app{margin-top:var(--devbar-h); height:calc(100% - var(--devbar-h))}
body.devMode .login .loginCard{margin-top:calc(var(--devbar-h) + 8px)}

/* Two explicit choices, not a checkbox: "on"/"off" is ambiguous for a switch
   that changes what customers can see. */
.modeChoices{display:grid; gap:9px; grid-template-columns:1fr 1fr}
@media(max-width:520px){.modeChoices{grid-template-columns:1fr}}
.modeChoice{
  text-align:left; background:#fff; border:2px solid var(--line); border-radius:12px;
  padding:12px; cursor:pointer; font:inherit; display:flex; flex-direction:column; gap:4px;
}
.modeChoice strong{font-size:15px}
.modeChoice span{font-size:12px; color:var(--muted); line-height:1.4}
.modeChoice.on{border-color:var(--navy); background:#f7f9fc}
.modeChoice.dev.on{border-color:#9a3412; background:#fdf3ef}
.modeChoice[disabled]{opacity:.5; cursor:default}

/* Viewing as a customer uses the same bar as dev mode, in a different colour
   so the two states are never confused at a glance. */
.modeBanner.asUser{
  background:repeating-linear-gradient(45deg,#134e4a,#134e4a 12px,#115e59 12px,#115e59 24px);
}
.modeBanner.asUser .mbGo{color:#134e4a}

/* ================================================================== *
 * Map dashboard: stage filters, legend, layers, full screen
 * ================================================================== */

/* Stage chips. Horizontally scrollable, because six filters plus counts do
   not fit across a phone and wrapping them eats the map. */
.stageBar{
  display:flex; gap:6px; overflow-x:auto; overflow-y:hidden;
  padding:3px; margin:-3px; scrollbar-width:none;
}
.stageBar::-webkit-scrollbar{display:none}
.stageChip{
  flex:0 0 auto; display:flex; align-items:center; gap:6px;
  background:#fff; border:none; border-radius:20px; padding:7px 12px;
  font:inherit; font-size:12.5px; font-weight:600; color:var(--muted);
  cursor:pointer; box-shadow:0 2px 10px rgba(0,0,0,.2); white-space:nowrap;
}
.stageChip i{width:9px; height:9px; border-radius:50%; flex:0 0 9px}
.stageChip b{font-size:12px; color:var(--ink); font-weight:800}
.stageChip.on{background:var(--navy); color:#fff}
.stageChip.on b{color:var(--gold)}

/* Legend, bottom left, above the pin bar. Tapping a row filters, so it is a
   control as well as a key. */
/* Sits above the status bar rather than under it. The status bar is centred
   and full width, so a legend at the same height is guaranteed to be covered
   the moment anything is announced. */
.mapLegend{
  position:absolute; left:10px; bottom:calc(122px + var(--safe-b)); z-index:640;
  background:rgba(255,255,255,.94); border-radius:10px; padding:7px 9px;
  box-shadow:0 3px 14px rgba(0,0,0,.28); display:flex; flex-direction:column; gap:3px;
  max-width:44%;
}
.lgRow{
  display:flex; align-items:center; gap:7px; font-size:11.5px; font-weight:600;
  color:var(--ink); cursor:pointer; line-height:1.5; white-space:nowrap;
}
.lgRow i{width:9px; height:9px; border-radius:50%; flex:0 0 9px}
.lgRow.dim{opacity:.35}

/* Satellite / Map, top right under the search stack. */
.mapLayers{
  position:absolute; right:10px; top:calc(10px + var(--safe-t)); z-index:660;
  display:flex; background:#fff; border-radius:9px; overflow:hidden;
  box-shadow:0 3px 14px rgba(0,0,0,.28);
}
.mlBtn{
  border:none; background:#fff; font:inherit; font-size:11.5px; font-weight:700;
  color:var(--muted); padding:7px 11px; cursor:pointer;
}
.mlBtn.on{background:var(--navy); color:#fff}

/* Full screen lives inside the layer group rather than floating on its own,
   which is how it ended up sitting on the search box. */
.mlBtn.fsBtn{padding:0 10px; display:grid; place-items:center; border-left:1px solid var(--line)}
.mlBtn.fsBtn.on{background:#fff; color:var(--navy)}

/* Cluster bubbles. The count sits inside the circle rather than beside it. */
.clusterDot{cursor:pointer}
.clusterLabel{
  background:none; border:none; box-shadow:none; padding:0;
  color:#fff; font-weight:800; font-size:12px; text-shadow:0 1px 2px rgba(0,0,0,.45);
}
.clusterLabel::before{display:none}

/* The map stack is busy enough while drawing or with the sheet up. */
#view-map.tracing .mapLegend, #view-map.sheetOpen .mapLegend,
#view-map.tracing .mapLayers, #view-map.sheetOpen .mapLayers,
#view-map.searching .mapLegend{display:none}

/* The layer toggle sits top right, in the same band as the first row of the
   top stack. Only the STAGE BAR collides with it, so only the stage bar gives
   up the room: squeezing the whole stack would leave the search box a third
   narrower for no reason. */
@media(max-width:700px){
  .stageBar{margin-right:164px}
}


/* ---- show/hide password ----
 * The eye lives INSIDE the field so it works in every password input the
 * app has now or grows later, without each screen laying it out again. */
.pwWrap{position:relative; display:block}
.pwWrap input{width:100%; padding-right:44px}
.pwEye{position:absolute; right:6px; top:50%; transform:translateY(-50%);
  border:0; background:none; cursor:pointer; padding:6px; line-height:0;
  color:#8b93a3; border-radius:6px}
.pwEye:hover{color:#0b2545}
.pwEye:active{background:#eef1f5}

/* "Open that pin" chip shown with a duplicate rejection: the blocking pin
 * holds the data the rep was after, so give it a named door. */
.dupeChip{position:fixed; left:50%; transform:translateX(-50%);
  bottom:118px; z-index:1200; background:#0b2545; color:#fff; border:0;
  border-radius:22px; padding:10px 18px; font-size:13.5px; font-weight:700;
  box-shadow:0 4px 14px rgba(0,0,0,.35); cursor:pointer}
.dupeChip:active{transform:translateX(-50%) scale(.97)}

/* Per-account operations editor on the admin Accounts screen. */
.opsBlock{margin-top:8px; border-top:1px dashed #dde1e8; padding-top:8px}
.opsBlock summary{cursor:pointer; font-size:12.5px; font-weight:700; color:#0b2545}
.opsBlock .acctField.chk{display:flex; align-items:center; gap:5px; font-size:12px}
.opsBlock input[type="number"]{width:74px}

/* Clickable account numbers: a subtle affordance, obvious on hover. */
.acctNums .drill:hover span{text-decoration:underline; color:#0b2545}

/* ---- hot leads ---- */
.navBadge{display:inline-block; min-width:18px; text-align:center; background:#ff6d00;
  color:#fff; border-radius:10px; font-size:11px; font-weight:800; padding:1px 5px;
  margin-left:6px; vertical-align:middle}
.navBadge.hidden{display:none}
.leadCard.heat-blazing{border-left:4px solid #ff3d00}
.leadCard.heat-hot{border-left:4px solid #ff6d00}
.leadCard.heat-warm{border-left:4px solid #ffb703}
@keyframes hotPulse{0%{stroke-opacity:1}50%{stroke-opacity:.35}100%{stroke-opacity:1}}
.hotPin{animation:hotPulse 1.6s ease-in-out infinite}

/* ================================================================== *
 * Dark mode
 *
 * For the rep driving at dusk: a white screen in a dark cab is hostile.
 * Variable-driven where the light theme used variables, explicit where it
 * hardcoded white. The satellite map is already dark, so the map screen
 * needs almost nothing.
 * ================================================================== */
body.dark{
  --ink:#e8ebf1; --muted:#9aa3b2; --line:#2a3242; --bg:#12161f;
  background:var(--bg); color:var(--ink);
}
body.dark .card, body.dark .stat, body.dark .acctCard, body.dark .loginCard,
body.dark .orderStrip, body.dark .lockPanel, body.dark .contactRow,
body.dark .setupCard, body.dark .previewShell{
  background:#1a2029; border-color:var(--line); color:var(--ink);
}
body.dark .drawer, body.dark .sheet{background:#161b24; color:var(--ink)}
body.dark table.data td{border-color:var(--line)}
body.dark input, body.dark select, body.dark textarea{
  background:#12161f; color:var(--ink); border-color:var(--line);
}
body.dark .searchBox{background:#1a2029; color:var(--ink)}
body.dark .btn.btnGhost{background:#232a36; color:var(--ink); border-color:var(--line)}
body.dark .banner.info{background:#1c2d45; color:#cfe0f5}
body.dark .banner.ok{background:#173226; color:#bfe8d0}
body.dark .banner.warn{background:#3a2f14; color:#f0dba6}
body.dark .banner.bad{background:#3a1a17; color:#f3c1ba}
body.dark .muted{color:var(--muted)}
body.dark .leadRow{border-color:var(--line) !important}

/* ================================================================== *
 * Canvass mode: driving, gloves, one hand
 * ================================================================== */
body.canvass .mlBtn, body.canvass .locBtn, body.canvass #pinToggle{
  transform:scale(1.28); transform-origin:bottom right;
}
body.canvass #pinToggle{font-size:17px; padding:14px 22px}
body.canvass .stageBar{display:none}      /* fewer distractions at 25 mph */
body.canvass .leaflet-control-zoom{display:none}  /* pinch works; buttons distract */

/* A just-dropped pin pulses until its measurement lands, so the rep sees
 * the machine working without reading anything. */
@keyframes pinWorking{0%{r:8}50%{r:12}100%{r:8}}
.pinWorking{animation:pinWorking 1s ease-in-out infinite}

/* Small print under a form field. */
.fieldHint{display:block; margin-top:4px; font-size:11.5px; font-weight:400;
  color:var(--muted); line-height:1.5}
