/* ops.css -- design system for the ops console. Same palette as the
   customer portal's style.css (so it reads as the same company) but a
   deliberately different density: 14px base, tables instead of cards,
   device IDs and model numbers visible. The customer app's large-text,
   plain-language treatment is a constraint for a 70-something snowbird
   reading a phone screen -- it is not a constraint for staff running
   the fleet, so none of it carries over here. Kept as its own file
   (not shared with static/style.css) for exactly that reason: the two
   are allowed to diverge, and sharing a stylesheet would fight that. */

: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}
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}
:focus-visible{outline:2px solid var(--util);outline-offset:2px}

.shell{display:flex;min-height:100vh}
#nav-root{display:contents}

.rail{
  width:212px;flex:0 0 212px;
  background:var(--card);
  border-right:1px solid var(--line);
  padding:18px 12px;
  display:flex;flex-direction:column;
  position:sticky;top:0;height:100vh;
}
.brand{display:flex;gap:9px;align-items:center;padding:0 6px 14px;border-bottom:1px solid var(--line-2);margin-bottom:14px}
.brand b{font-size:13px;font-weight:700;line-height:1.2;display:block}
.brand span{display:block;font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:var(--util);font-weight:700;margin-top:2px}

nav{display:flex;flex-direction:column;gap:1px}
.ni{display:flex;align-items:center;gap:9px;width:100%;border:0;background:none;padding:8px 10px;border-radius:7px;font-size:14px;font-weight:600;color:var(--ink-2);text-align:left;text-decoration:none}
.ni:hover{background:var(--line-2)}
.ni.current{background:var(--util-bg);color:var(--util)}
.ni svg{flex:0 0 16px;stroke-width:1.9}
.ni .ct{margin-left:auto;font-size:11px;font-weight:700;background:var(--crit);color:#fff;min-width:18px;height:18px;border-radius:9px;display:grid;place-items:center;padding:0 5px}
.ni .ct.w{background:var(--warn)}

.railfoot{margin-top:auto;border-top:1px solid var(--line-2);padding-top:12px;font-size:12px;color:var(--ink-3)}
.railfoot b{display:block;font-size:13px;color:var(--ink);font-weight:600}
.railfoot a{border:0;background:none;color:var(--util);font-weight:600;font-size:13px;padding:6px 0 0;display:inline-block;cursor:pointer;text-decoration:none}
.railfoot a.acct{display:flex;align-items:center;gap:9px;padding:0;text-decoration:none;color:inherit}
.railfoot a.acct:hover .acctname b{text-decoration:underline}
.railfoot .avatar{flex:0 0 auto;width:30px;height:30px;border-radius:50%;background:var(--util);color:#fff;display:grid;place-items:center;font-size:12px;font-weight:700;letter-spacing:.02em}
.railfoot .acctname{min-width:0}
/* Which Postgres this console is actually talking to -- always visible,
   not something that takes a wrong query and a lucky env-var read to
   find out again. See ops.py's GET /api/ops/system-info and
   hub/CLAUDE.md's "PG_HOST: one variable, one place" section for the
   2026-08-30 incident this exists to shorten next time. */
.railfoot .dbinfo{margin-top:10px;padding-top:10px;border-top:1px solid var(--line-2);font-size:10.5px;color:var(--ink-3);font-family:ui-monospace,monospace;line-height:1.5;word-break:break-all}

main{flex:1;min-width:0;padding:20px 24px 60px}
.ph{display:flex;justify-content:space-between;align-items:baseline;gap:16px;flex-wrap:wrap;margin-bottom:16px}
h1{font-size:21px;font-weight:700;margin:0}
.ph .meta{font-size:13px;color:var(--ink-3)}

.stats{display:grid;grid-template-columns:repeat(5,1fr);gap:10px;margin-bottom:18px}
.st{background:var(--card);border:1px solid var(--line);border-radius:10px;padding:12px 14px;box-shadow:var(--sh)}
.st .k{font-size:11px;font-weight:700;letter-spacing:.07em;text-transform:uppercase;color:var(--ink-3)}
.st .v{font-size:25px;font-weight:700;margin-top:2px;line-height:1.1}
.st .v.c{color:var(--crit)}.st .v.w{color:var(--warn)}.st .v.o{color:var(--ok)}.st .v.u{color:var(--ink-3)}

table{width:100%;border-collapse:collapse;background:var(--card);border:1px solid var(--line);border-radius:10px;overflow:hidden;box-shadow:var(--sh)}
th{text-align:left;font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-3);padding:9px 12px;border-bottom:1px solid var(--line);background:#FBFCFC;white-space:nowrap}
td{padding:10px 12px;border-bottom:1px solid var(--line-2);vertical-align:top}
tr:last-child td{border-bottom:0}
tbody tr{cursor:pointer}
tbody tr:hover{background:#FAFCFB}
.addr{font-weight:600;font-size:14px}
.addr span{display:block;font-size:12px;color:var(--ink-3);font-weight:400}
.pill{display:inline-flex;align-items:center;gap:5px;font-size:12px;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)}
.pill.u{background:var(--line-2);color:var(--ink-3)}
.pill i{width:6px;height:6px;border-radius:50%;background:currentColor;display:block}
.sub{font-size:12px;color:var(--ink-2);margin-top:3px;max-width:34ch}
.hb{font-size:13px;font-variant-numeric:tabular-nums}
.hb.bad{color:var(--crit);font-weight:700}
.hb.old{color:var(--warn);font-weight:600}
.hb.unknown{color:var(--ink-3)}
.tiny{font-size:12px;color:var(--ink-3)}
.chip{display:inline-block;font-size:11px;font-weight:700;background:var(--util-bg);color:var(--util);padding:2px 7px;border-radius:5px}
/* A technician message waiting on a reply -- distinct from .chip (plain text, used for open_requests/roles) by carrying its own icon (a chat bubble, never the alert triangle -- an alert means the house has a problem, this means a person is waiting for an answer). Red/var(--crit) and sized up past a normal chip on purpose (Alex: "make the flag red and a little bigger") -- someone waiting on a reply should read as at least as urgent as an alert, not quieter than one. Clickable -- goes straight to the thread, not just the property/employee page -- so it gets cursor:pointer and a hover state of its own. */
.msgchip{display:inline-flex;align-items:center;gap:5px;font-size:13px;font-weight:700;background:var(--crit);color:#fff;padding:4px 10px 4px 8px;border-radius:6px;cursor:pointer;border:0;font-family:inherit;line-height:1.3}
.msgchip:hover{background:#8F1F19}
.msgchip svg{width:15px;height:15px;flex:0 0 auto}

.back{border:0;background:none;color:var(--util);font-weight:600;font-size:13px;padding:0 0 10px;display:flex;gap:6px;align-items:center;text-decoration:none}
.grid2{display:grid;grid-template-columns:1.6fr 1fr;gap:14px}
.panel{background:var(--card);border:1px solid var(--line);border-radius:10px;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}
.kv{display:flex;justify-content:space-between;gap:12px;padding:7px 0;border-bottom:1px solid var(--line-2);font-size:13px}
.kv:last-child{border-bottom:0}
.kv .k{color:var(--ink-3)}
.kv .v{font-weight:600;text-align:right}
.dev{display:flex;justify-content:space-between;gap:10px;padding:8px 0;border-bottom:1px solid var(--line-2);font-size:13px;align-items:baseline}
.dev:last-child{border-bottom:0}
.dev .n{font-weight:600}
.dev .n span{display:block;font-size:11px;color:var(--ink-3);font-weight:400;font-family:ui-monospace,monospace}
.dev .s{font-size:12px;color:var(--ink-2);text-align:right;white-space:nowrap}
.btn{border:1.5px solid var(--line);background:var(--card);border-radius:7px;padding:7px 12px;font-size:13px;font-weight:600;transition:transform .06s ease}
.btn:hover{background:var(--line-2)}
.btn:active{transform:scale(.96)}
.btn.go{background:var(--util);border-color:var(--util);color:#fff}
.btn.dgr{border-color:#E8BDB9;color:var(--crit)}
/* No disabled treatment existed anywhere before -- added here (home.html's
   valve lock is the reason: "the single moment it should be hardest to
   press" needs to visibly read as locked, not just silently ignore clicks). */
.btn:disabled,.btn[disabled]{opacity:.5;cursor:not-allowed}
.btn:disabled:hover,.btn[disabled]:hover{background:var(--card)}
.btn.go:disabled:hover,.btn.go[disabled]:hover{background:var(--util)}
.btnrow{display:flex;gap:7px;flex-wrap:wrap;padding:12px 14px;border-top:1px solid var(--line-2);background:#FBFCFC;align-items:center}
/* Classic "it worked" confirmation for a silent save/add action (Alex:
   pressing Save/Save availability gave no feedback at all). Shared
   across ops pages -- any panel with a busy/success flow can drop this
   span into its .btnrow next to the button. Fades in, holds, fades out;
   the element unmounts (setTimeout in the component) rather than
   relying on CSS alone so it can re-fire on a second save. */
.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}}

.rq{display:flex;gap:12px;padding:13px 14px;border-bottom:1px solid var(--line-2);align-items:flex-start}
.rq:last-child{border-bottom:0}
.rq .who{flex:0 0 190px}
.rq .who b{display:block;font-size:13px;font-weight:700}
.rq .who span{font-size:12px;color:var(--ink-3)}
.rq .bod{flex:1;min-width:0;font-size:13px}
.rq .bod b{display:block;margin-bottom:2px}
.rq .bod p{margin:0;color:var(--ink-2)}
.rq .act{display:flex;gap:6px;align-items:flex-start;flex-wrap:wrap}

/* Request thread (requests.html): a small chat-style list of
   request_messages rows inside .rq .bod, plus a cancel affordance
   that's deliberately a plain link, not a peer button next to
   Send/Send & close -- see requests.html's ThreadRow comment. */
.reqmsg{margin:6px 0 0;padding:6px 9px;border-radius:7px;background:var(--line-2)}
.reqmsg b{display:block;font-size:11px;font-weight:700;color:var(--ink-3);text-transform:uppercase;letter-spacing:.02em;margin-bottom:2px}
.reqmsg p{margin:0;color:var(--ink-2);white-space:pre-wrap}
.reqmsg.internal{background:var(--warn-bg);border:1px dashed var(--warn)}
.reqmsg.internal b{color:var(--warn)}
.rq .outcome{margin:8px 0 0;font-size:13px;color:var(--ink-2)}
.rq .outcome b{color:var(--ink)}
.cancel-link{color:var(--ink-3);text-decoration:underline;cursor:pointer;font-size:12px;align-self:center;background:none;border:0;padding:0}

/* Alerts row (alerts.html): the live status.py detail line under the
   rule label, and a small overflow menu so a row used rarely (Snooze,
   Add a note, Schedule a visit, Update the customer) doesn't need four
   buttons showing at once -- see AlertRow's own comment on the
   primary/secondary split. */
.rq .alert-detail{margin:2px 0 0;font-size:12.5px;color:var(--ink-3)}
.rq .alert-linked-visit{margin:6px 0 0;font-size:12px;color:var(--util);font-weight:600}
.rq .alert-prefs{margin:0 0 6px;font-size:12.5px;color:var(--ink-2);background:var(--util-bg);border-radius:6px;padding:6px 9px}
.overflow-wrap{position:relative}
.overflow-menu{position:absolute;right:0;top:calc(100% + 4px);background:var(--card);border:1px solid var(--line);border-radius:8px;box-shadow:var(--sh);z-index:30;min-width:180px;padding:4px}
.overflow-menu button{display:block;width:100%;text-align:left;padding:8px 10px;border:0;background:none;border-radius:6px;font-size:13px;font-weight:600;color:var(--ink-2);cursor:pointer}
.overflow-menu button:hover{background:var(--line-2)}
.reqmsg.system{background:var(--ok-bg)}
.reqmsg.system b{color:var(--ok)}

.lrow{display:flex;gap:12px;justify-content:space-between;align-items:flex-start;padding:11px 0;border-bottom:1px solid var(--line-2)}
.lrow:last-child{border-bottom:0}
.lrow b{font-size:14px;font-weight:700;display:block}
.lrow p{margin:2px 0 0;font-size:12px;color:var(--ink-2)}
.lrow code{background:var(--line-2);padding:1px 5px;border-radius:3px;font-size:11px}
.seg{display:flex;background:var(--line-2);border-radius:8px;padding:3px;gap:3px}
.seg button{border:0;background:none;padding:6px 13px;border-radius:6px;font-size:13px;font-weight:600;color:var(--ink-2)}
.seg button.on{background:var(--card);color:var(--ink);box-shadow:0 1px 2px rgba(21,36,32,.16)}

.fg{margin-bottom:12px}
.fg label{display:block;font-size:12px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--ink-3);margin-bottom:5px}
.fg input,.fg select,.fg textarea{width:100%;padding:9px 11px;border:1.5px solid var(--line);border-radius:7px;font-size:14px;font-family:inherit;background:#fff;color:var(--ink)}
.fg input:focus,.fg select:focus,.fg textarea:focus{border-color:var(--util);outline:none}
/* A populated <input type="date"> renders its own text via the
   browser's internal datetime-edit fields, which default to a muted
   grey regardless of `color` on the input itself -- a filled-in start
   date otherwise reads as an unset placeholder next to plain-text
   fields like Name/Phone right beside it. Force full-strength ink so a
   real value actually looks like one. */
.fg input[type="date"]::-webkit-datetime-edit,
.fg input[type="date"]::-webkit-datetime-edit-fields-wrapper,
.fg input[type="date"]::-webkit-datetime-edit-text,
.fg input[type="date"]::-webkit-datetime-edit-month-field,
.fg input[type="date"]::-webkit-datetime-edit-day-field,
.fg input[type="date"]::-webkit-datetime-edit-year-field{color:var(--ink);opacity:1}
.two{display:grid;grid-template-columns:1fr 1fr;gap:12px}
.three{display:grid;grid-template-columns:2fr 1fr 1fr;gap:12px}
.sep{font-size:11px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-3);border-top:1px solid var(--line-2);padding-top:14px;margin:6px 0 12px}
.hint{font-size:12px;color:var(--ink-3);margin:0 0 13px;line-height:1.5}
.ck{display:flex;gap:9px;align-items:center;font-size:14px;padding:6px 0;cursor:pointer}

.danger{border:1.5px solid #E8BDB9;border-radius:10px;padding:14px;margin-top:20px;background:#FEFAFA}
.danger>b{display:block;font-size:12px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--crit);margin-bottom:11px}
.drow{display:flex;gap:14px;align-items:flex-start;justify-content:space-between;padding:10px 0;border-top:1px solid #F2DEDC}
.drow:first-of-type{border-top:0;padding-top:0}
.drow b{font-size:14px;font-weight:700;display:block}
.drow p{margin:3px 0 0;font-size:12px;color:var(--ink-2);max-width:44ch;line-height:1.5}
.drow .btn{flex:0 0 auto}

.ov{position:fixed;inset:0;background:rgba(21,36,32,.42);z-index:60;padding:28px 16px;overflow-y:auto}
.md{max-width:640px;margin:0 auto;background:var(--card);border-radius:14px;box-shadow:0 18px 50px rgba(21,36,32,.28);overflow:hidden}
.mh{display:flex;justify-content:space-between;align-items:center;padding:15px 18px;border-bottom:1px solid var(--line-2);background:#FBFCFC}
.mh b{font-size:16px;font-weight:700}
.x{border:0;background:none;font-size:17px;color:var(--ink-3);padding:2px 6px}
.mb{padding:18px}
.mf{display:flex;justify-content:space-between;align-items:center;gap:12px;padding:13px 18px;border-top:1px solid var(--line-2);background:#FBFCFC;flex-wrap:wrap}

.note{font-size:12px;color:var(--ink-3);border-top:1px solid var(--line);margin-top:26px;padding-top:12px}

.cal{display:grid;grid-template-columns:repeat(5,1fr);gap:10px}
.day{background:var(--card);border:1px solid var(--line);border-radius:10px;box-shadow:var(--sh);min-height:230px}
.dh{padding:9px 11px;border-bottom:1px solid var(--line-2);background:#FBFCFC}
.dh b{font-size:13px;font-weight:700}
.dh span{display:block;font-size:11px;color:var(--ink-3)}
.appt{margin:7px;padding:8px 9px;border-radius:7px;border-left:3px solid var(--util);background:var(--util-bg);cursor:pointer}
.appt b{display:block;font-size:12px;font-weight:700}
.appt span{display:block;font-size:11px;color:var(--ink-2);margin-top:1px}
.appt.extra{border-left-color:var(--warn);background:var(--warn-bg)}
.appt.dana{border-left-color:#7A4E8C;background:#F1EAF5}

.wk{background:var(--card);border:1px solid var(--line);border-radius:10px;box-shadow:var(--sh);overflow:hidden}
.wkhead{display:grid;grid-template-columns:54px repeat(5,1fr);border-bottom:1px solid var(--line);background:#FBFCFC}
.wkhead .dcol{padding:9px 10px;border-left:1px solid var(--line-2)}
.wkhead .dcol b{font-size:13px;font-weight:700;display:block}
.wkhead .dcol span{font-size:11px;color:var(--ink-3)}
.wkbody{display:grid;grid-template-columns:54px repeat(5,1fr);position:relative}
.wkbody .gut .hr{height:46px;border-bottom:1px solid var(--line-2);position:relative}
.wkbody .gut .hr span{position:absolute;top:-7px;right:7px;font-size:11px;color:var(--ink-3);background:var(--card);padding:0 2px}
.dtrack{position:relative;border-left:1px solid var(--line-2);background:repeating-linear-gradient(to bottom,transparent 0,transparent 45px,var(--line-2) 45px,var(--line-2) 46px);cursor:pointer}
.dtrack:hover{background-color:#FBFDFC}
.ev{position:absolute;left:3px;right:3px;border-radius:6px;border-left:3px solid var(--util);background:var(--util-bg);padding:6px 8px;overflow:hidden;cursor:pointer}
.ev b{display:block;font-size:12px;font-weight:700;line-height:1.25}
.ev span{display:block;font-size:11px;color:var(--ink-2);margin-top:1px}
.ev.dana{border-left-color:#7A4E8C;background:#F1EAF5}
.ev.extra{border-left-color:var(--warn);background:var(--warn-bg)}
.tech{display:flex;align-items:center;gap:8px;font-size:13px;font-weight:600;padding:5px 0;cursor:pointer}
.tech i{width:10px;height:10px;border-radius:3px;display:block}

.mapwrap{background:var(--card);border:1px solid var(--line);border-radius:10px;box-shadow:var(--sh);padding:16px}
.legend{display:flex;gap:16px;font-size:12px;color:var(--ink-2);margin-top:10px;flex-wrap:wrap}
.legend i{width:9px;height:9px;border-radius:50%;display:inline-block;margin-right:5px;vertical-align:0}

@media(max-width:900px){.stats{grid-template-columns:repeat(2,1fr)}.grid2{grid-template-columns:1fr}.cal{grid-template-columns:1fr}.rail{display:none}}

/* ---------- map.html ---------- */
.radarnote{font-size:11px;font-weight:600;color:var(--warn);background:var(--warn-bg);padding:4px 10px;border-radius:6px;display:inline-block;margin-top:8px}
.btn.sm{padding:5px 9px;font-size:12px}
.layout{display:grid;grid-template-columns:286px 1fr;gap:13px;align-items:start}
@media(max-width:1020px){.layout{grid-template-columns:1fr}}

.search{position:relative;margin-bottom:11px}
.search input{width:100%;padding:10px 12px 10px 34px;border:1.5px solid var(--line);border-radius:9px;font-size:14px;background:var(--card);color:var(--ink)}
.search input:focus{border-color:var(--util);outline:none}
.search svg{position:absolute;left:11px;top:11px;color:var(--ink-3)}

.techsearch{position:relative}
.techsearch input{width:100%;padding:9px 12px;border:1.5px solid var(--line);border-radius:9px;font-size:14px;background:var(--card);color:var(--ink)}
.techsearch input:focus{border-color:var(--util);outline:none}
.techresults{position:absolute;top:calc(100% + 4px);left:0;right:0;max-height:220px;overflow-y:auto;background:var(--card);border:1.5px solid var(--line);border-radius:9px;box-shadow:0 6px 18px rgba(0,0,0,.1);z-index:20}
.techresults button{display:block;width:100%;text-align:left;padding:8px 12px;font-size:13px;border:0;background:none;color:var(--ink)}
.techresults button:hover,.techresults button.hi{background:var(--line-2)}
.techresults .none{padding:8px 12px;font-size:13px;color:var(--ink-3)}

.plist{max-height:330px;overflow-y:auto}
.pitem{display:flex;gap:9px;align-items:flex-start;padding:9px 12px;border-bottom:1px solid var(--line-2);cursor:pointer;width:100%;border:0;background:none;text-align:left}
.pitem:last-child{border-bottom:0}
.pitem:hover{background:#FAFCFB}
.pitem.sel{background:var(--util-bg)}
.pitem i{flex:0 0 9px;width:9px;height:9px;border-radius:50%;margin-top:5px}
.pitem .tx{flex:1;min-width:0}
.pitem b{display:block;font-size:13px;font-weight:600;line-height:1.3;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pitem span{display:block;font-size:11px;color:var(--ink-3);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pitem em{font-style:normal;font-size:11px;font-weight:700;color:var(--crit)}

.alertbox{background:var(--crit-bg);border:1px solid #E8BDB9;border-radius:10px;padding:11px 12px;margin-bottom:11px}
.alertbox b{display:block;font-size:13px;font-weight:700;color:var(--crit)}
.alertbox p{margin:4px 0 0;font-size:12px;color:var(--ink-2);line-height:1.5}
.alertbox.none{background:var(--ok-bg);border-color:#C9E0D3}
.alertbox.none b{color:var(--ok)}

.tot{display:flex;justify-content:space-between;font-size:13px;padding:8px 12px;border-top:1px solid var(--line-2);background:#FBFCFC;font-weight:600}
.tot span{color:var(--ink-3);font-weight:400}

.stop{display:flex;gap:9px;align-items:flex-start;padding:7px 0;border-bottom:1px solid var(--line-2)}
.stop:last-child{border-bottom:0}
.num{flex:0 0 20px;width:20px;height:20px;border-radius:50%;background:var(--util);color:#fff;font-size:11px;font-weight:700;display:grid;place-items:center;margin-top:1px}
.stop b{display:block;font-size:13px;font-weight:600;line-height:1.3}
.stop span{display:block;font-size:11px;color:var(--ink-3)}
.rleg{font-size:11px;color:var(--ink-3);padding:2px 0 2px 29px}

.mapcard{background:var(--card);border:1px solid var(--line);border-radius:10px;box-shadow:var(--sh);overflow:hidden}
.maptop{display:flex;gap:8px;align-items:center;padding:8px 11px;border-bottom:1px solid var(--line-2);background:#FBFCFC;flex-wrap:wrap}
.tog{display:flex;align-items:center;gap:6px;font-size:12px;font-weight:600;color:var(--ink-2);cursor:pointer;padding:4px 8px;border-radius:6px}
.tog:hover{background:var(--line-2)}
.tog i{width:10px;height:10px;border-radius:3px;display:block}
.spacer{flex:1}
.mapstage{position:relative;background:#E4EDE9}
#leafmap{width:100%;height:560px}
/* Radar frames are swapped in as a brand-new tile layer that fades
   in once loaded (see map-layers.js's applyRadarStep) rather than
   L.tileLayer.setUrl()'s instant swap-in-place -- without a real CSS
   transition, that fade-in still happens in a single paint, which
   reads as a hard cut between two different-looking frames every
   ~700ms during Play (Alex: "still shakey"). This gives opacity
   changes on every Leaflet tile an actual duration so the radar swap
   cross-dissolves instead of cutting. Applied to all tiles (Leaflet
   has no built-in way to scope this to one layer's tiles specifically)
   rather than just the radar pane -- harmless everywhere else, since
   street/satellite tiles already fade in similarly as you pan. */
.leaflet-tile{transition:opacity 220ms linear}
.mapnote{position:absolute;left:11px;bottom:9px;font-size:11px;background:rgba(255,255,255,.9);color:var(--ink-2);padding:4px 9px;border-radius:6px;z-index:1000}
/* Stacks the date badge above the property-focus badge, both pinned
   to the map's top-left corner -- Alex wanted the date "in the
   viewpoint" of the map itself rather than up in the page header, and
   a plain absolute position on each independently would have made
   them overlap the moment a property is also focused. */
.datebadge{background:var(--card);border-radius:8px;box-shadow:0 1px 4px rgba(21,36,32,.12);padding:6px 12px;font-size:14px;font-weight:800;color:var(--ink);margin-right:4px;border:1.5px solid var(--line-2)}
.datebadge.not-today{background:var(--warn-bg);color:var(--warn);border-color:transparent}
.selbadge{position:absolute;left:11px;top:11px;z-index:1000;max-width:240px;background:var(--card);border-radius:8px;box-shadow:0 2px 8px rgba(21,36,32,.18);padding:8px 12px;font-size:12px;display:none}
.selbadge.on{display:block}
.selbadge b{display:block;font-size:13px;font-weight:700}
.selbadge span{color:var(--ink-3)}

.tl{padding:11px 14px;border-top:1px solid var(--line-2);background:#FBFCFC}
.tlrow{display:flex;gap:12px;align-items:center}
.play{width:36px;height:36px;border-radius:50%;border:1.5px solid var(--line);background:var(--card);display:grid;place-items:center;flex:0 0 36px}
.play:hover{background:var(--line-2)}
.tlwrap{flex:1;min-width:0}
.tlwrap input[type=range]{width:100%;accent-color:var(--util)}
.tlticks{display:flex;justify-content:space-between;font-size:10px;color:var(--ink-3);margin-top:2px}
.tlnow{flex:0 0 auto;text-align:right;min-width:150px}
.tlnow b{display:block;font-size:14px;font-weight:700;font-variant-numeric:tabular-nums}
.tlnow span{font-size:11px;color:var(--ink-3)}
.rainkey{display:flex;gap:12px;margin-top:9px;font-size:11px;color:var(--ink-3);align-items:center;flex-wrap:wrap}
.rainkey i{width:22px;height:9px;border-radius:2px;display:inline-block;vertical-align:-1px;margin-right:5px}

.pin-div{border-radius:50%;border:2.5px solid #fff;box-shadow:0 1px 4px rgba(21,36,32,.35)}

/* ---------- storm tab: season / outlook / readiness / last event ---------- */
.cols{display:grid;grid-template-columns:1.5fr 1fr;gap:14px;align-items:start}
@media(max-width:960px){.cols{grid-template-columns:1fr}}

.season .big{font-size:17px;font-weight:700;display:block;margin-bottom:3px}
.season p.sub{margin:0 0 15px;font-size:13px;color:var(--ink-2)}
.track{position:relative;height:9px;border-radius:5px;background:var(--line-2);margin:20px 0 7px}
.track .fill{position:absolute;left:0;top:0;bottom:0;border-radius:5px;background:linear-gradient(90deg,#8FB4A0,#B26A00)}
.track .peak{position:absolute;top:-5px;bottom:-5px;width:2px;background:var(--crit);opacity:.5}
.track .now{position:absolute;top:-7px;width:15px;height:23px;border-radius:8px;background:var(--card);border:2.5px solid var(--util);transform:translateX(-50%)}
.tlabels{display:flex;justify-content:space-between;font-size:11px;color:var(--ink-3)}
.mstat{display:flex;gap:26px;margin-top:17px;flex-wrap:wrap}
.mstat div b{display:block;font-size:23px;font-weight:700;line-height:1.1}
.mstat div span{font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-3)}

.trk{display:flex;gap:12px;padding:12px 14px;border-bottom:1px solid var(--line-2);align-items:flex-start}
.trk:last-child{border-bottom:0}
.prob{flex:0 0 52px;text-align:center}
.prob b{display:block;font-size:19px;font-weight:700;line-height:1.1}
.prob span{font-size:10px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--ink-3)}
.prob.hi b{color:var(--crit)}
.prob.md b{color:var(--warn)}
.prob.lo b{color:var(--ink-3)}
.trk .tx{flex:1;min-width:0}
.trk .tx b{display:block;font-size:14px;font-weight:700}
.trk .tx p{margin:2px 0 0;font-size:13px;color:var(--ink-2);line-height:1.5}
.trk .tx em{font-style:normal;font-size:11px;color:var(--ink-3);display:block;margin-top:4px}

.tick{color:var(--ok);font-weight:700}
.cross{color:var(--crit);font-weight:700}
.dashv{color:var(--ink-3)}
.foot{padding:11px 14px;border-top:1px solid var(--line-2);background:#FBFCFC;font-size:12px;color:var(--ink-2);line-height:1.5}
.foot .nr{display:block;font-size:11px;color:var(--ink-3);margin-top:4px}

.ev .t{font-size:15px;font-weight:700;display:block}
.ev .d{font-size:12px;color:var(--ink-3)}
.ev p{margin:11px 0 0;font-size:13px;color:var(--ink-2);line-height:1.55}
.kv{display:flex;justify-content:space-between;padding:7px 0;border-bottom:1px solid var(--line-2);font-size:13px}
.kv:last-child{border-bottom:0}
.kv .k{color:var(--ink-3)}
.kv .v{font-weight:600}

.hero{background:var(--crit-bg);border:1px solid #E8BDB9;border-left:5px solid var(--crit);border-radius:10px;padding:16px 18px;margin-bottom:14px}
.hero b.t{display:block;font-size:18px;font-weight:700;color:var(--crit)}
.hero p{margin:6px 0 0;font-size:13px;color:var(--ink-2);line-height:1.55}
.hero .meta{display:flex;gap:22px;margin-top:13px;flex-wrap:wrap}
.hero .meta div b{font-size:15px;color:var(--ink);display:block}
.hero .meta div span{font-size:11px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--ink-3)}
.pill.armed{background:var(--ok-bg);color:var(--ok)}
.pill.off{background:var(--line-2);color:var(--ink-3)}
.pill.res{background:var(--util-bg);color:var(--util)}

/* Shared modal shell -- originally schedule.html's own page-scoped
   style (its New/Edit visit modal), promoted here so ops-log-call.js's
   LogCallModal (requests.html, customer.html, home.html) can reuse the
   exact same look without a second implementation. schedule.html's own
   copy of these four rules was removed in the same round -- one
   definition, not two. */
.modal-overlay { position: fixed; inset: 0; background: rgba(21,36,32,.35); display: flex; align-items: flex-start; justify-content: center; padding: 40px 16px; overflow-y: auto; z-index: 50; }
.modal-panel { width: 100%; max-width: 520px; background: var(--card); border-radius: 12px; box-shadow: var(--sh); }
.modal-panel .pt { display: flex; justify-content: space-between; align-items: center; }
.modal-panel .pt button { border: 0; background: none; font-size: 18px; color: var(--ink-3); cursor: pointer; line-height: 1; }

/* ---------- technician messages (Round 2, Part 4) ---------- */
/* Reused on employee.html's MessagesPanel -- a mini inbox list
   (threads, one per property) that opens into a plain chat-style
   thread, same visual grammar as .ni .ct's existing unread-count
   badge above. */
.msg-threads{display:flex;flex-direction:column}
.msg-thread-row{display:flex;gap:10px;align-items:center;padding:11px 0;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}
.msg-thread-row:last-child{border-bottom:0}
.msg-thread-row.sel{background:var(--util-bg);margin:0 -14px;padding:11px 14px;border-radius:8px}
.msg-thread-row .tx{flex:1;min-width:0}
.msg-thread-row .tx b{display:block;font-size:13.5px;font-weight:700}
.msg-thread-row .tx span{display:block;font-size:12px;color:var(--ink-3);margin-top:1px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.msg-thread-row .ct{flex:0 0 auto;font-size:11px;font-weight:700;background:var(--crit);color:#fff;min-width:18px;height:18px;border-radius:9px;display:grid;place-items:center;padding:0 5px}

.msg-thread{display:flex;flex-direction:column;gap:9px;margin-bottom:14px;max-height:360px;overflow-y:auto;padding-right:2px}
.msg-bubble{max-width:80%;padding:9px 12px;border-radius:12px;font-size:13.5px;line-height:1.5}
.msg-bubble .mtime{display:block;font-size:10.5px;font-weight:600;margin-top:4px}
.msg-bubble.mine{align-self:flex-end;background:var(--util);color:#fff;border-bottom-right-radius:3px}
.msg-bubble.mine .mtime{opacity:.75}
.msg-bubble.theirs{align-self:flex-start;background:var(--paper);border:1px solid var(--line);border-bottom-left-radius:3px}
.msg-bubble.theirs .mtime{color:var(--ink-3)}
.msg-compose{display:flex;gap:8px;align-items:flex-end}
.msg-compose textarea{flex:1;border:1.5px solid var(--line);border-radius:9px;padding:9px 11px;font-size:13.5px;font-family:inherit;resize:vertical;min-height:44px;box-sizing:border-box}
