/* tech.css -- design system for the technician field app. Same token
   block (colors/shadow) as static-ops/ops.css -- this is a staff/field
   tool, not the large-text customer portal, so it inherits ops's
   density (14px base) rather than static/style.css's. Diverges from
   ops.css in layout only: no desktop nav rail (this app is phone-only,
   always run from a home-screen icon), a fixed bottom tab bar instead,
   and touch-target sizing throughout (50px+ minimum) for one-handed
   on-site use -- ops.css's own .btn is a mouse-sized 30px and isn't
   reused here for primary actions. */

:root{
  --paper:#F2F5F4;
  --card:#FFFFFF;
  --ink:#152420;
  --ink-2:#4A5B55;
  --ink-3:#7C8C86;
  --line:#DCE4E0;
  --line-2:#EDF1EF;
  --ok:#2F6B4F;
  --ok-bg:#E7F1EB;
  --warn:#B26A00;
  --warn-bg:#FBF0DE;
  --crit:#B3261E;
  --crit-bg:#FBE9E7;
  --util:#1F5B6E;
  --util-bg:#E6EFF2;
  --sh:0 1px 2px rgba(21,36,32,.05),0 4px 14px rgba(21,36,32,.05);
}
*{box-sizing:border-box;-webkit-tap-highlight-color:transparent}
html,body{margin:0;padding:0}
body{
  background:var(--paper);
  color:var(--ink);
  font-family:"Public Sans",system-ui,-apple-system,"Segoe UI",sans-serif;
  font-size:14px;
  line-height:1.45;
  -webkit-font-smoothing:antialiased;
}
button{font:inherit;color:inherit;cursor:pointer}
a{color:inherit;text-decoration:none}
input,select,textarea{font:inherit}
:focus-visible{outline:2px solid var(--util);outline-offset:2px}

/* ---------- app shell ---------- */
.app{display:flex;flex-direction:column;min-height:100vh}
main{flex:1;min-width:0;padding:14px 14px 84px;max-width:560px;margin:0 auto;width:100%}
.ph{display:flex;justify-content:space-between;align-items:baseline;gap:12px;flex-wrap:wrap;margin-bottom:14px}
h1{font-size:20px;font-weight:700;margin:0}
h2{font-size:11px;font-weight:700;letter-spacing:.07em;text-transform:uppercase;color:var(--ink-3);margin:20px 0 9px}
.ph .meta{font-size:12.5px;color:var(--ink-3)}

/* Bottom tab bar -- always on (this app is only ever run as a phone
   PWA), unlike static/style.css's .tabbar which appears only under a
   breakpoint next to a desktop rail. */
.tabbar{
  display:flex;position:fixed;left:0;right:0;bottom:0;background:var(--card);
  border-top:1px solid var(--line);padding:6px 4px calc(6px + env(safe-area-inset-bottom));
  z-index:30;max-width:560px;margin:0 auto;
}
.tabbar a{
  flex:1;border:0;background:none;display:flex;flex-direction:column;align-items:center;gap:3px;
  padding:8px 2px;font-size:11px;font-weight:600;color:var(--ink-3);text-decoration:none;min-height:50px;justify-content:center;
}
.tabbar a.current{color:var(--util)}
.tabbar svg{width:22px;height:22px;stroke-width:1.9}

.topbar{display:flex;align-items:center;gap:10px;padding:12px 14px;background:var(--card);border-bottom:1px solid var(--line);position:sticky;top:0;z-index:25}
.topbar .brand{font-size:13px;font-weight:700;display:flex;align-items:center;gap:8px}
.topbar .brand span{font-size:9.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--util);font-weight:700;display:block}

/* Offline strip -- shown whenever navigator.onLine is false or the
   last sync attempt failed, with a plain count of what's queued. See
   tech-auth.js's getQueueSummary(). */
.offline-strip{display:flex;align-items:center;gap:8px;background:var(--warn-bg);color:var(--warn);font-size:12.5px;font-weight:600;padding:9px 14px;line-height:1.4}
.offline-strip svg{flex:0 0 16px;width:16px;height:16px}
.offline-strip b{font-weight:700}

.install-note{background:var(--util-bg);color:var(--util);font-size:12px;font-weight:600;padding:8px 14px;line-height:1.5;display:flex;gap:8px;align-items:flex-start}
.install-note svg{flex:0 0 15px;width:15px;height:15px;margin-top:1px}

/* ---------- buttons ---------- */
.bigbtn{width:100%;min-height:52px;border-radius:12px;border:0;background:var(--util);color:#fff;font-size:16px;font-weight:700;padding:12px 16px;display:flex;align-items:center;justify-content:center;gap:8px;transition:transform .06s ease}
.bigbtn:active{transform:scale(.97)}
.bigbtn:disabled{opacity:.45;cursor:not-allowed}
.bigbtn.ghost{background:var(--card);color:var(--util);border:1.5px solid var(--line)}
.bigbtn.dgr{background:var(--crit)}
.bigbtn.dgr.ghost{background:var(--card);color:var(--crit);border:1.5px solid #E8BDB9}
.bigbtn.ok{background:var(--ok)}
.btnstack{display:flex;flex-direction:column;gap:9px;margin-top:14px}
.btn{border:1.5px solid var(--line);background:var(--card);border-radius:9px;padding:10px 14px;font-size:13.5px;font-weight:600;min-height:44px}
.btn:active{transform:scale(.97)}
.btn.go{background:var(--util);border-color:var(--util);color:#fff}
.btn.dgr{border-color:#E8BDB9;color:var(--crit)}
.btn:disabled{opacity:.5;cursor:not-allowed}
.linkbtn{border:0;background:none;color:var(--util);font-weight:600;font-size:13.5px;padding:10px 4px;min-height:44px}
.linkbtn.muted{color:var(--ink-3)}

/* ---------- cards / panels ---------- */
.card{background:var(--card);border:1px solid var(--line);border-radius:12px;box-shadow:var(--sh);padding:14px;margin-bottom:12px}
.panel{background:var(--card);border:1px solid var(--line);border-radius:12px;box-shadow:var(--sh);overflow:hidden;margin-bottom:14px}
.pt{font-size:11px;font-weight:700;letter-spacing:.07em;text-transform:uppercase;color:var(--ink-3);padding:11px 14px;border-bottom:1px solid var(--line-2);background:#FBFCFC}
.pb{padding:14px}
/* Shared small-caps card/section label -- same type treatment as .pt
   (panel headers) and .co-block .lab (checkout blocks), for a label
   living inside a plain .card that has no separate header chrome of
   its own (e.g. property.html's "last visit" summary). */
.lab{display:block;font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-3);margin:0 0 7px}
.hint{font-size:12.5px;color:var(--ink-3);margin:0 0 10px;line-height:1.5}
.empty{font-size:13.5px;color:var(--ink-3);padding:18px 4px;text-align:center;line-height:1.5}

/* ---------- pills / dots ---------- */
.pill{display:inline-flex;align-items:center;gap:5px;font-size:11.5px;font-weight:700;padding:3px 9px;border-radius:20px;white-space:nowrap}
.pill.ok{background:var(--ok-bg);color:var(--ok)}
.pill.w{background:var(--warn-bg);color:var(--warn)}
.pill.c{background:var(--crit-bg);color:var(--crit)}
/* A waiting reply -- on a Properties tile or the Me screen's Office panel title. Same red/icon language as the ops console's .msgchip (never the flag/pill styling used for checklist flags and alerts): a message means a person is waiting on a reply, which reads as urgent but as a different KIND of urgent than a flagged item. */
.msgbadge{display:inline-flex;align-items:center;gap:5px;font-size:12.5px;font-weight:700;background:var(--crit);color:#fff;padding:3px 9px 3px 7px;border-radius:999px;line-height:1.3}
.msgbadge svg{width:14px;height:14px;flex:0 0 auto}
.pill.u{background:var(--line-2);color:var(--ink-3)}
.pill i{width:6px;height:6px;border-radius:50%;background:currentColor;display:block}
.dot{width:9px;height:9px;border-radius:50%;flex:0 0 9px;display:block}
.dot.ok{background:var(--ok)}
.dot.w{background:var(--warn)}
.dot.c{background:var(--crit)}
.dot.u{background:var(--ink-3)}

/* ---------- visit cards (Today) ---------- */
.visit-card{background:var(--card);border:1px solid var(--line);border-radius:12px;box-shadow:var(--sh);padding:14px;margin-bottom:11px;display:block;width:100%;text-align:left;border-left:4px solid var(--line)}
.visit-card.next{border-left-color:var(--util)}
.visit-card.done{border-left-color:var(--ok);opacity:.82}
.visit-card .vtop{display:flex;justify-content:space-between;gap:10px;align-items:flex-start}
.visit-card .time{font-size:13px;font-weight:700;color:var(--ink-2);font-variant-numeric:tabular-nums}
.visit-card .addr{font-size:15.5px;font-weight:700;margin:3px 0 1px}
.visit-card .city{font-size:12.5px;color:var(--ink-3);font-weight:400}
.visit-card .drive{font-size:12px;color:var(--ink-3);margin-top:6px;display:flex;align-items:center;gap:5px}
.visit-card .drive svg{width:13px;height:13px}
.visit-card .note{margin-top:9px;padding:8px 10px;border-radius:8px;background:var(--util-bg);color:var(--util);font-size:12.5px;line-height:1.5}
.visit-card .reason{margin-top:9px;padding:8px 10px;border-radius:8px;background:var(--warn-bg);color:var(--warn);font-size:12.5px;line-height:1.5;font-weight:600}
.visit-card .prog{margin-top:9px;font-size:11.5px;color:var(--ink-3)}

/* ---------- checklist ---------- */
.crow{display:flex;gap:11px;align-items:flex-start;padding:12px 0;border-bottom:1px solid var(--line-2)}
.crow:last-child{border-bottom:0}
.cbox{flex:0 0 26px;width:26px;height:26px;border-radius:7px;border:2px solid var(--line);background:var(--card);display:grid;place-items:center;margin-top:1px}
.cbox.on{background:var(--ok);border-color:var(--ok);color:#fff}
.crow .lbl{flex:1;min-width:0;font-size:14.5px;font-weight:600;line-height:1.4;padding-top:2px}
.crow.checked .lbl{color:var(--ink-2);text-decoration:line-through;text-decoration-color:var(--line)}
.flagbtn{flex:0 0 auto;border:1.5px solid var(--line);background:var(--card);border-radius:8px;width:38px;height:38px;display:grid;place-items:center;color:var(--ink-3)}
.flagbtn.on{background:var(--warn-bg);border-color:var(--warn);color:var(--warn)}
.flag-detail{margin:0 0 0 37px;padding:10px 0 2px}
.flag-detail textarea{width:100%;border:1.5px solid var(--line);border-radius:8px;padding:9px 10px;font-size:13.5px;font-family:inherit;resize:vertical;min-height:56px}
.progressbar{height:8px;border-radius:5px;background:var(--line-2);overflow:hidden;margin-bottom:14px}
.progressbar .fill{height:100%;background:var(--ok);border-radius:5px;transition:width .2s ease}

/* ---------- photo grid ---------- */
.pgrid{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-top:10px}
.ptile{position:relative;aspect-ratio:1;border-radius:9px;overflow:hidden;background:var(--line-2);border:1px solid var(--line)}
.ptile img{width:100%;height:100%;object-fit:cover;display:block}
.ptile .badge{position:absolute;bottom:4px;right:4px;background:rgba(21,36,32,.65);color:#fff;font-size:9.5px;font-weight:700;padding:2px 6px;border-radius:5px}
.ptile.pending .badge{background:var(--warn)}
.ptile.failed .badge{background:var(--crit)}
.addtile{border:2px dashed var(--line);border-radius:9px;aspect-ratio:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;background:var(--card);color:var(--util);font-size:11px;font-weight:700;cursor:pointer}
.addtile svg{width:24px;height:24px}
.addtile input{display:none}

/* ---------- device rows / property ---------- */
.devrow{display:flex;gap:12px;align-items:flex-start;padding:13px 14px;border-bottom:1px solid var(--line-2);cursor:pointer;width:100%;text-align:left;border:0;background:none}
.devrow:last-child{border-bottom:0}
.devrow .dot{margin-top:5px}
.devrow .n{flex:1;min-width:0}
.devrow .n b{display:block;font-size:14px;font-weight:700}
/* The health word used to be the same plain, muted grey as the
   recency/zone detail after it -- the whole line read as one flat
   blob, with only the small dot carrying any real signal. It now
   matches the dot's color and reads bold, so the row's status is
   legible at a glance; the "last seen"/zone detail after it stays
   muted and unbolded, visually secondary on purpose. */
.devrow .n .devmeta{display:block;font-size:12px;color:var(--ink-3);margin-top:2px}
.devrow .n .devmeta .state{font-weight:700}
.devrow .n .devmeta .state.ok{color:var(--ok)}
.devrow .n .devmeta .state.c{color:var(--crit)}
.devrow .n .devmeta .state.u{color:var(--ink-3)}
.devrow .chev{color:var(--ink-3);flex:0 0 auto;margin-top:2px}

.contactrow{display:flex;gap:12px;align-items:center;padding:12px 14px;border-bottom:1px solid var(--line-2)}
.contactrow:last-child{border-bottom:0}
.contactrow .num{flex:0 0 22px;width:22px;height:22px;border-radius:50%;background:var(--util-bg);color:var(--util);font-size:11px;font-weight:700;display:grid;place-items:center}
/* "Urgent only" contacts (priority 99, the customer portal's own
   sentinel -- see property.html's CONTACT_URGENT_ONLY) get the warn
   palette instead of the plain call-order util palette, so a
   technician can tell a numbered rank from an urgent-only contact
   without reading the label text. */
.contactrow .num.urgent{background:var(--warn-bg);color:var(--warn)}
.contactrow .who{flex:1;min-width:0}
.contactrow .who b{display:block;font-size:14px;font-weight:700}
.contactrow .who span{display:block;font-size:12px;color:var(--ink-3);margin-top:1px}
.contactrow a.dial{flex:0 0 auto;width:38px;height:38px;border-radius:50%;background:var(--ok-bg);color:var(--ok);display:grid;place-items:center;text-decoration:none}

.plist-item{display:flex;gap:11px;align-items:center;padding:13px 14px;border-bottom:1px solid var(--line-2);width:100%;text-align:left;border-left:0;border-right:0;border-top:0;background:none;cursor:pointer}
.plist-item:last-child{border-bottom:0}
.plist-item .tx{flex:1;min-width:0}
.plist-item .tx b{display:block;font-size:15px;font-weight:700}
.plist-item .tx span{display:block;font-size:12px;color:var(--ink-3);margin-top:1px}
.plist-item .chev{color:var(--ink-3)}

/* ---------- form fields ---------- */
.fg{margin-bottom:14px}
.fg label{display:block;font-size:12px;font-weight:700;letter-spacing:.03em;text-transform:uppercase;color:var(--ink-3);margin-bottom:6px}
.fg .sub{font-size:12px;color:var(--ink-3);margin-top:5px;line-height:1.5;font-weight:400;text-transform:none;letter-spacing:0}
.fg input,.fg select,.fg textarea{width:100%;padding:12px 13px;border:1.5px solid var(--line);border-radius:9px;font-size:15px;font-family:inherit;background:#fff;color:var(--ink);min-height:46px}
.fg textarea{min-height:76px;resize:vertical}
.fg input:focus,.fg select:focus,.fg textarea:focus{border-color:var(--util);outline:none}
.protogrid{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.protocard{border:1.5px solid var(--line);border-radius:11px;padding:14px 10px;text-align:center;background:var(--card);min-height:84px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px}
.protocard b{font-size:13.5px;font-weight:700}
.protocard.sel{border-color:var(--util);background:var(--util-bg)}

/* ---------- pairing / verify ---------- */
.spinwrap{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:34px 10px;text-align:center}
.spinner{width:52px;height:52px;border-radius:50%;border:4px solid var(--line-2);border-top-color:var(--util);animation:spin 1s linear infinite;margin-bottom:16px}
@keyframes spin{to{transform:rotate(360deg)}}
.spinwrap .t{font-size:16px;font-weight:700;margin:0 0 4px}
.spinwrap .sub{font-size:13px;color:var(--ink-3);line-height:1.5;max-width:36ch}
.spinwrap .elapsed{font-size:12.5px;color:var(--ink-3);margin-top:14px;font-variant-numeric:tabular-nums}
.foundcard{background:var(--ok-bg);border:1px solid #C9E0D3;border-radius:12px;padding:18px;text-align:center;margin-bottom:14px}
.foundcard svg{width:38px;height:38px;color:var(--ok);margin-bottom:8px}
.foundcard b{display:block;font-size:16px;font-weight:700;color:var(--ok)}
.foundcard span{display:block;font-size:12.5px;color:var(--ink-2);margin-top:3px}

/* Immediate pairing-start failure (hub unreachable / Z-Wave not
   configured) -- same shape as .foundcard above, critical palette
   instead of ok, so a failure reads as visually distinct from both
   "found" and the plain informational .hint text used for genuine
   timeouts. See device.html's PairingStartError. */
.errcard{background:var(--crit-bg);border:1px solid #EFC3BD;border-radius:12px;padding:18px;text-align:center;margin-bottom:14px}
.errcard svg{width:38px;height:38px;color:var(--crit);margin-bottom:8px}
.errcard b{display:block;font-size:14.5px;font-weight:700;color:var(--crit);line-height:1.4}

/* ---------- checkout blocks ---------- */
.co-block{border-radius:12px;padding:14px;margin-bottom:12px;border:1px solid var(--line)}
.co-block .lab{font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;margin-bottom:8px;display:block}
.co-block.customer{background:var(--card)}
.co-block.customer .lab{color:var(--ink-3)}
.co-block.office{background:var(--warn-bg);border-color:#EFD9B2}
.co-block.office .lab{color:var(--warn)}
.co-block.internal{background:var(--line-2);border-color:var(--line)}
.co-block.internal .lab{color:var(--ink-2)}
.co-block p.body{margin:0;font-size:14px;line-height:1.6;white-space:pre-wrap}
.co-block .flagitem{padding:7px 0;border-top:1px dashed #EFD9B2;font-size:13.5px}
.co-block .flagitem:first-of-type{border-top:0}

/* ---------- waiting-on-you / queue ---------- */
.qrow{display:flex;justify-content:space-between;align-items:center;gap:10px;padding:10px 0;border-bottom:1px solid var(--line-2);font-size:13.5px}
.qrow:last-child{border-bottom:0}
.qrow b{font-weight:700}
.qrow .st{font-size:11.5px;font-weight:700}
.qrow .st.pending{color:var(--warn)}
.qrow .st.failed{color:var(--crit)}

.avatar{width:44px;height:44px;border-radius:50%;background:var(--util-bg);color:var(--util);display:grid;place-items:center;font-size:16px;font-weight:700}
.mecard{display:flex;gap:12px;align-items:center;margin-bottom:16px}
.mecard b{display:block;font-size:17px;font-weight:700}
.mecard span{display:block;font-size:12.5px;color:var(--ink-3)}

.kv{display:flex;justify-content:space-between;gap:12px;padding:8px 0;border-bottom:1px solid var(--line-2);font-size:13.5px}
.kv:last-child{border-bottom:0}
.kv .k{color:var(--ink-3)}
.kv .v{font-weight:600;text-align:right}

.danger{border:1.5px solid #E8BDB9;border-radius:12px;padding:14px;margin-top:18px;background:#FEFAFA}
.danger>b{display:block;font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--crit);margin-bottom:10px}

.err{font-size:13px;color:var(--crit);margin:0 0 12px;line-height:1.5}
.save-flash{display:inline-flex;align-items:center;gap:4px;font-size:12.5px;font-weight:700;color:var(--ok);animation:save-flash-fade 2s ease forwards}
@keyframes save-flash-fade{0%{opacity:0;transform:translateY(2px)}12%{opacity:1;transform:translateY(0)}75%{opacity:1}100%{opacity:0}}

/* ---------- login ---------- */
.loginwrap{min-height:100vh;display:flex;align-items:center;justify-content:center;padding:20px}
.loginbox{width:100%;max-width:360px;background:var(--card);border:1px solid var(--line);border-radius:14px;box-shadow:var(--sh);padding:26px 22px}
.loginbox .brand{display:flex;flex-direction:column;align-items:center;gap:8px;margin-bottom:22px;text-align:center}
.loginbox .brand b{font-size:16px;font-weight:700}
.loginbox .brand span{font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:var(--util);font-weight:700}


/* ---------- urgent dispatch card (Round 2, Part 3) ---------- */
.dispatch-card{background:var(--crit-bg);border:2px solid var(--crit);border-radius:14px;padding:16px;margin-bottom:16px;animation:dispatch-pulse 1.8s ease-in-out infinite}
@keyframes dispatch-pulse{0%,100%{box-shadow:0 0 0 0 rgba(179,38,30,.28)}50%{box-shadow:0 0 0 7px rgba(179,38,30,0)}}
.dispatch-card .dtitle{display:flex;align-items:center;gap:8px;font-size:12px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;color:var(--crit);margin-bottom:8px}
.dispatch-card .dtitle svg{width:16px;height:16px;flex:0 0 16px}
.dispatch-card .daddr{font-size:16.5px;font-weight:700;margin-bottom:2px}
.dispatch-card .dreason{font-size:14px;line-height:1.5;margin:8px 0;color:var(--ink)}
.dispatch-card .daccess{background:#fff;border:1px solid #EFC3BD;border-radius:9px;padding:9px 11px;font-size:13px;line-height:1.5;margin:8px 0}
.dispatch-card .daccess b{display:block;font-size:10.5px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--crit);margin-bottom:3px}
.dispatch-card .ddisplaced{font-size:12.5px;color:var(--ink-2);line-height:1.6;margin:8px 0;background:rgba(255,255,255,.5);border-radius:8px;padding:8px 10px}
.dispatch-card .ddisplaced .dtl{font-size:10.5px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--ink-3);margin-bottom:3px}
.dispatch-card .ddisplaced div.drow{padding:1px 0}
.dispatch-card .dactions{display:flex;gap:10px;margin-top:12px}
.dispatch-card .dactions .bigbtn{flex:1}
.dispatch-card textarea{width:100%;border:1.5px solid #EFC3BD;border-radius:9px;padding:9px 10px;font-size:13.5px;font-family:inherit;resize:vertical;min-height:56px;margin-top:8px;box-sizing:border-box}

/* ---------- push opt-in (Me screen) ---------- */
.push-optin{background:var(--util-bg);border-radius:12px;padding:14px;margin-bottom:14px}
.push-optin p{margin:0 0 10px;font-size:13px;line-height:1.5;color:var(--ink-2)}

/* ---------- Today/Week toggle + drive-leg divider (Round 2, Parts 1-2) ---------- */
/* Segmented Today/Week control at the top of the existing Today tab --
   NOT a new nav destination (tech-shell.js's 3-tab bar is untouched). */
.view-toggle{display:flex;gap:0;background:var(--line-2);border-radius:10px;padding:3px;margin-bottom:14px}
.view-toggle button{flex:1;border:0;background:none;padding:9px 0;border-radius:8px;font-size:13.5px;font-weight:700;color:var(--ink-3)}
.view-toggle button.sel{background:var(--card);color:var(--util);box-shadow:var(--sh)}

/* Drive-time/distance divider between two consecutive visit cards --
   Round 2 Part 2, shared by Today and an expanded week day. A dash
   (no caption) whenever the API returned null; never shown before the
   FIRST visit of a list (nothing precedes it). */
.drive-leg{display:flex;align-items:center;gap:5px;font-size:12px;color:var(--ink-3);font-weight:700;padding:0 2px 8px 11px;margin-top:-3px}
.drive-leg .dash{font-weight:400}

/* ---------- Week view (Round 2, Part 1) ---------- */
.week-nav{display:flex;align-items:center;justify-content:space-between;margin-bottom:12px;gap:10px}
.week-nav button{border:1.5px solid var(--line);background:var(--card);border-radius:9px;width:38px;height:38px;flex:0 0 38px;display:grid;place-items:center;color:var(--ink-2)}
.week-nav button:disabled{opacity:.4}
.week-nav .wr{font-size:13px;font-weight:700;color:var(--ink-2);text-align:center;flex:1}

.week-totals{display:flex;justify-content:space-around;background:var(--card);border:1px solid var(--line);border-radius:12px;padding:12px 6px;margin-bottom:14px;box-shadow:var(--sh)}
.week-totals .wt{text-align:center}
.week-totals .wt b{display:block;font-size:17px;font-weight:700;font-variant-numeric:tabular-nums}
.week-totals .wt span{display:block;font-size:10px;color:var(--ink-3);text-transform:uppercase;letter-spacing:.05em;margin-top:2px}

.week-off{background:var(--util-bg);color:var(--util);border-radius:12px;padding:20px 16px;text-align:center;font-size:15px;font-weight:700;margin-bottom:14px}

/* One day, collapsible -- non-working days get a hatched fill so
   "nothing scheduled" reads as "not a working day" at a glance,
   distinct from a working day that simply has no visits yet. */
.dayrow{background:var(--card);border:1px solid var(--line);border-radius:12px;margin-bottom:9px;overflow:hidden;box-shadow:var(--sh)}
.dayrow.nonworking{background:repeating-linear-gradient(135deg,var(--card),var(--card) 7px,var(--line-2) 7px,var(--line-2) 14px)}
.dayrow-h{display:flex;align-items:center;gap:11px;padding:13px 14px;width:100%;text-align:left;border:0;background:none}
.dayrow-h .dl{flex:0 0 44px;text-align:center}
.dayrow-h .dl b{display:block;font-size:17px;font-weight:800;line-height:1.1}
.dayrow-h .dl span{display:block;font-size:10px;color:var(--ink-3);text-transform:uppercase;font-weight:700;letter-spacing:.03em}
.dayrow-h .dsum{flex:1;min-width:0}
.dayrow-h .dsum .dtext{font-size:13px;color:var(--ink-2);font-weight:600}
.dayrow-h .dsum .dtext.off-lab{color:var(--ink-3);font-weight:400;font-style:italic}
.dayrow-h .dsum .dots{display:flex;gap:4px;margin-top:5px}
.ddot{width:7px;height:7px;border-radius:50%;flex:0 0 7px}
.ddot.normal{background:var(--ink-3)}
.ddot.extra{background:var(--warn)}
.ddot.urgent{background:var(--crit)}
.dayrow-h .chev{color:var(--ink-3);flex:0 0 auto;transition:transform .15s ease}
.dayrow-h.open .chev{transform:rotate(180deg)}
.dayrow-body{padding:2px 14px 14px}
.dayrow-body .visit-card{margin-bottom:9px}
.dayrow-body .visit-card:last-child{margin-bottom:0}

/* ---------- nav unread badge (Round 2, Part 4) ---------- */
.tabicon{position:relative;display:inline-flex}
.tab-badge{position:absolute;top:-5px;right:-7px;background:var(--crit);color:#fff;font-size:9.5px;font-weight:700;line-height:1;min-width:15px;height:15px;border-radius:999px;display:flex;align-items:center;justify-content:center;padding:0 3px;border:1.5px solid var(--card)}

/* ---------- messages (Round 2, Part 4) ---------- */
/* Plain chat-style thread -- technician's own messages right-aligned/
   filled (same var(--util) as .bigbtn's primary action), ops replies
   left-aligned/outlined, same visual grammar this app already uses
   elsewhere for "mine vs. not mine" (e.g. .visit-card.next's util
   accent vs. plain cards). */
.thread{display:flex;flex-direction:column;gap:10px;margin-bottom:18px}
.msg{max-width:82%;padding:10px 13px;border-radius:14px;font-size:14px;line-height:1.5;box-shadow:var(--sh)}
.msg .mtime{display:block;font-size:10.5px;font-weight:600;margin-top:4px}
.msg.mine{align-self:flex-end;background:var(--util);color:#fff;border-bottom-right-radius:4px}
.msg.mine .mtime{opacity:.75}
.msg.theirs{align-self:flex-start;background:var(--card);border:1px solid var(--line);border-bottom-left-radius:4px}
.msg.theirs .mtime{color:var(--ink-3)}
/* Sticky above the fixed bottom tab bar (tabbar's own ~50px min-height
   + its padding + safe-area-inset-bottom) so the compose box is always
   reachable without the tab bar covering it. */
.composer{display:flex;gap:8px;align-items:flex-end;position:sticky;bottom:calc(64px + env(safe-area-inset-bottom));background:var(--paper);padding:8px 0 2px}
.composer textarea{flex:1;border:1.5px solid var(--line);border-radius:12px;padding:11px 13px;font-size:14.5px;font-family:inherit;resize:none;min-height:46px;max-height:120px;background:#fff;box-sizing:border-box}
.composer button{flex:0 0 auto;width:46px;height:46px;border-radius:50%;background:var(--util);color:#fff;border:0;display:grid;place-items:center}
.composer button:disabled{opacity:.45}
