/* ===================================================================
   JOC OS — shared theme.
   Built for cheap Android phones held in one hand, at night, outdoors.
   Large targets, high contrast, no hover-dependent affordances.
   =================================================================== */
:root{
  --navy:#081426; --navy-2:#0d1e38; --navy-3:#12294a;
  --line:#1e3557; --line-soft:#20365a;
  --gold:#c8a24a; --gold-soft:#e0c88a;
  --ink:#eef3fb; --ink-dim:#9db0cc; --ink-faint:#6c83a6;
  --ok:#2ec27e; --warn:#f5a524; --bad:#e5484d; --info:#4a9de0;
  /* #e5484d with white text measures 3.91:1 and fails AA at the size PANIC uses.
     Solid fills use --bad-solid (6.15:1); --bad stays for borders and pills. */
  --bad-solid:#c2131a; --ok-solid:#1f8f5b; --warn-solid:#a86c00;
  --r:14px; --r-sm:9px;
  --shadow:0 10px 30px rgba(0,0,0,.35);
  --font:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;
}
*{box-sizing:border-box;-webkit-tap-highlight-color:transparent}
html,body{margin:0;padding:0}
body{
  background:var(--navy); color:var(--ink); font-family:var(--font);
  font-size:16px; line-height:1.45; min-height:100vh;
  -webkit-font-smoothing:antialiased;
  padding-bottom:env(safe-area-inset-bottom);
}
a{color:var(--gold);text-decoration:none}
h1,h2,h3{margin:0 0 .4em;line-height:1.2;font-weight:650;letter-spacing:-.01em}
h1{font-size:1.5rem} h2{font-size:1.15rem} h3{font-size:1rem}
small,.small{font-size:.8rem}
.dim{color:var(--ink-dim)} .faint{color:var(--ink-faint)}
.mono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-variant-numeric:tabular-nums}

/* ---------- layout ---------- */
.wrap{max-width:1180px;margin:0 auto;padding:14px}
.wrap.narrow{max-width:560px}
.row{display:flex;gap:10px;align-items:center}
.row.wrap-r{flex-wrap:wrap}
.between{justify-content:space-between}
.grow{flex:1;min-width:0}
.grid{display:grid;gap:12px}
.g2{grid-template-columns:repeat(auto-fit,minmax(230px,1fr))}
.g3{grid-template-columns:repeat(auto-fit,minmax(160px,1fr))}
.g4{grid-template-columns:repeat(auto-fit,minmax(130px,1fr))}
.stack{display:flex;flex-direction:column;gap:10px}
.hide{display:none !important}

/* ---------- top bar ---------- */
header.top{
  position:sticky;top:0;z-index:40;background:rgba(8,20,38,.94);
  backdrop-filter:blur(10px);border-bottom:1px solid var(--line);
  padding:10px 14px;display:flex;align-items:center;gap:10px;
}
.brand{display:flex;align-items:center;gap:9px;font-weight:700;letter-spacing:.02em}
.brand .mark{
  width:30px;height:30px;border-radius:8px;flex:none;
  background:linear-gradient(140deg,var(--gold),#8a6b23);
  display:grid;place-items:center;color:#0b1524;font-size:.72rem;font-weight:800
}
.who{font-size:.78rem;color:var(--ink-dim);text-align:right;line-height:1.25}

/* ---------- cards ---------- */
.card{
  background:linear-gradient(180deg,var(--navy-2),#0b1a30);
  border:1px solid var(--line);border-radius:var(--r);padding:14px;
}
.card.tight{padding:11px}
.card h3{margin-bottom:.5em}
.stat{background:var(--navy-2);border:1px solid var(--line);border-radius:var(--r);padding:12px}
.stat .n{font-size:1.7rem;font-weight:700;line-height:1;font-variant-numeric:tabular-nums}
.stat .l{font-size:.72rem;text-transform:uppercase;letter-spacing:.06em;color:var(--ink-faint);margin-top:5px}
.stat.ok .n{color:var(--ok)} .stat.warn .n{color:var(--warn)} .stat.bad .n{color:var(--bad)}

/* ---------- buttons ---------- */
button,.btn{
  font-family:inherit;font-size:1rem;font-weight:600;cursor:pointer;
  border-radius:var(--r-sm);border:1px solid var(--line);
  background:var(--navy-3);color:var(--ink);padding:12px 16px;
  display:inline-flex;align-items:center;justify-content:center;gap:8px;
  min-height:46px;transition:transform .06s ease,filter .15s ease;
}
/* A 1.5% scale is imperceptible on a wet, cracked, cheap capacitive screen —
   the officer genuinely cannot tell whether the tap registered. */
button:active,.btn:active{transform:scale(.985);filter:brightness(1.3)}
button:disabled{opacity:.45;cursor:not-allowed}
button:focus-visible,.btn:focus-visible{outline:3px solid var(--gold);outline-offset:2px}
.btn-primary{background:var(--gold);border-color:var(--gold);color:#0b1524}
.btn-danger{background:var(--bad);border-color:var(--bad);color:#fff}
.btn-ok{background:var(--ok);border-color:var(--ok);color:#04231a}
.btn-ghost{background:transparent}
.btn-sm{min-height:48px;padding:10px 16px;font-size:.9rem}
.btn-block{width:100%}
.btn-xl{min-height:78px;font-size:1.12rem;flex-direction:column;gap:3px;padding:14px}
/* The subtitle explains what the button does. At .74rem/.8 opacity on red it
   measured 3.0:1 — the least readable text on the screen, under PANIC. */
.btn-xl .sub{font-size:.85rem;font-weight:500;opacity:1;color:inherit}
.btn-lg{min-height:64px;font-size:1.05rem}

/* ---------- forms ---------- */
label{display:block;font-size:.78rem;text-transform:uppercase;letter-spacing:.05em;
  color:var(--ink-faint);margin:0 0 5px}
input,select,textarea{
  width:100%;font-family:inherit;font-size:16px;color:var(--ink);
  background:#0a1830;border:1px solid var(--line);border-radius:var(--r-sm);
  padding:12px;min-height:46px;
}
textarea{min-height:92px;resize:vertical}
/* was `input:focus,select,textarea:focus` — the middle selector had no :focus,
   so every dropdown carried a permanent gold outline and had no focus state. */
input:focus,select:focus,textarea:focus{outline:2px solid var(--gold);outline-offset:-1px}
input,select,textarea{min-height:52px}
::placeholder{color:var(--ink-faint);opacity:1}
select{appearance:none;background-image:linear-gradient(45deg,transparent 50%,var(--ink-dim) 50%),
  linear-gradient(135deg,var(--ink-dim) 50%,transparent 50%);
  background-position:calc(100% - 18px) 21px,calc(100% - 13px) 21px;
  background-size:5px 5px,5px 5px;background-repeat:no-repeat}
.field{margin-bottom:12px}
.pinpad{display:grid;grid-template-columns:repeat(3,1fr);gap:9px}
.pinpad button{min-height:58px;font-size:1.35rem;font-weight:600}
.pindots{display:flex;gap:11px;justify-content:center;margin:14px 0}
.pindots i{width:14px;height:14px;border-radius:50%;background:#16294a;border:1px solid var(--line);display:block}
.pindots i.on{background:var(--gold);border-color:var(--gold)}

/* ---------- pills & chips ---------- */
/* was .72rem (11.5px) uppercase — too small for anything an officer must read
   through a cracked screen in the rain. */
.pill{display:inline-flex;align-items:center;gap:5px;font-size:.82rem;font-weight:600;
  padding:5px 10px;border-radius:999px;border:1px solid var(--line);background:#0c1c34;
  text-transform:uppercase;letter-spacing:.04em;white-space:nowrap}
.pill.ok{color:var(--ok);border-color:rgba(46,194,126,.4);background:rgba(46,194,126,.1)}
.pill.warn{color:var(--warn);border-color:rgba(245,165,36,.4);background:rgba(245,165,36,.1)}
.pill.bad{color:var(--bad);border-color:rgba(229,72,77,.45);background:rgba(229,72,77,.12)}
.pill.info{color:var(--info);border-color:rgba(74,157,224,.4);background:rgba(74,157,224,.1)}
.pill.gold{color:var(--gold);border-color:rgba(200,162,74,.45);background:rgba(200,162,74,.1)}
.dot{width:9px;height:9px;border-radius:50%;display:inline-block;flex:none}
.dot.ok{background:var(--ok)} .dot.warn{background:var(--warn)}
.dot.bad{background:var(--bad)} .dot.off{background:#3a4d6d}
.live-dot{animation:pulse 1.8s ease-in-out infinite}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.35}}

/* ---------- tabs ---------- */
.tabs{display:flex;gap:6px;overflow-x:auto;padding:3px 0 9px;scrollbar-width:none}
.tabs::-webkit-scrollbar{display:none}
.tabs button{
  min-height:38px;padding:8px 14px;font-size:.86rem;white-space:nowrap;
  background:transparent;border:1px solid var(--line);color:var(--ink-dim)}
.tabs button.on{background:var(--gold);border-color:var(--gold);color:#0b1524}

/* ---------- lists & tables ---------- */
.list{display:flex;flex-direction:column;gap:12px}
.item{
  background:var(--navy-2);border:1px solid var(--line);border-radius:var(--r-sm);
  padding:11px 12px;display:flex;gap:10px;align-items:flex-start}
.item .grow{min-width:0}
.item .t{font-weight:600;font-size:.95rem}
.item .s{font-size:.8rem;color:var(--ink-dim);margin-top:2px;word-break:break-word}
.item.bad{border-left:3px solid var(--bad)}
.item.warn{border-left:3px solid var(--warn)}
.item.ok{border-left:3px solid var(--ok)}
table{width:100%;border-collapse:collapse;font-size:.86rem}
th{text-align:left;font-size:.7rem;text-transform:uppercase;letter-spacing:.05em;
  color:var(--ink-faint);padding:7px 8px;border-bottom:1px solid var(--line);font-weight:600}
td{padding:9px 8px;border-bottom:1px solid #142845;vertical-align:top}
tr:last-child td{border-bottom:none}
.scroll-x{overflow-x:auto;-webkit-overflow-scrolling:touch}

/* ---------- toast & banners ---------- */
#toast{position:fixed;left:50%;bottom:22px;transform:translateX(-50%);z-index:100;
  display:flex;flex-direction:column;gap:8px;width:min(92vw,460px)}
.toast{background:#12294a;border:1px solid var(--line);border-left:4px solid var(--gold);
  border-radius:var(--r-sm);padding:12px 14px;box-shadow:var(--shadow);font-size:.9rem;
  animation:slideup .22s ease}
.toast.ok{border-left-color:var(--ok)} .toast.bad{border-left-color:var(--bad)}
.toast.warn{border-left-color:var(--warn)}
@keyframes slideup{from{transform:translateY(12px);opacity:0}to{transform:none;opacity:1}}
.banner{border-radius:var(--r-sm);padding:11px 13px;font-size:.88rem;margin-bottom:12px;
  border:1px solid var(--line);background:var(--navy-3)}
.banner.bad{background:rgba(229,72,77,.13);border-color:rgba(229,72,77,.4);color:#ffc9cb}
.banner.warn{background:rgba(245,165,36,.12);border-color:rgba(245,165,36,.4);color:#ffdfa8}
.banner.ok{background:rgba(46,194,126,.12);border-color:rgba(46,194,126,.4);color:#b6f0d6}

/* ---------- offline / storage indicators ----------
   These sit in normal flow rather than fixed, so a wrapped two-line message
   cannot be overlapped by the sticky header (the old #netbar hardcoded 26px
   and a 66-character sentence wraps to ~42px on a 360px screen). */
.hardbar{text-align:center;font-size:.88rem;font-weight:600;padding:8px 12px;line-height:1.3}
.hardbar.warn{background:var(--warn);color:#2a1a00}
.hardbar.bad{background:var(--bad-solid);color:#fff}
#netbar{display:none}
body.offline #netbar{display:block}

/* ---------- modal ---------- */
.modal-bg{position:fixed;inset:0;background:rgba(3,8,17,.78);z-index:80;
  display:none;align-items:flex-end;justify-content:center;padding:0}
.modal-bg.on{display:flex}
.modal{background:var(--navy-2);border:1px solid var(--line);border-radius:16px 16px 0 0;
  width:min(100%,620px);max-height:92vh;overflow-y:auto;padding:16px;
  padding-bottom:calc(16px + env(safe-area-inset-bottom));animation:slideup .2s ease}
@media(min-width:640px){
  .modal-bg{align-items:center;padding:20px}
  .modal{border-radius:var(--r)}
}
.modal-h{display:flex;justify-content:space-between;align-items:center;margin-bottom:12px}
/* was ~24x28px — the smallest control in the app, in the least reachable corner */
.x{background:transparent;border:none;font-size:1.6rem;min-height:48px;min-width:48px;padding:0;color:var(--ink-dim)}

/* ---------- misc ---------- */
.sep{height:1px;background:var(--line);margin:14px 0}
.empty{text-align:center;padding:26px 14px;color:var(--ink-faint);font-size:.9rem}
.thumbs{display:flex;gap:7px;flex-wrap:wrap;margin-top:8px}
.thumbs img{width:62px;height:62px;object-fit:cover;border-radius:8px;border:1px solid var(--line)}
footer.legal{text-align:center;color:var(--ink-faint);font-size:.72rem;padding:22px 12px 30px}

/* ===================================================================
   v1.4.0 — the officer's screen.
   Everything below is built for one hand, in the dark, in the rain, on a
   five-year-old Android, by someone whose second language this is.
   =================================================================== */

/* ---------- duty state: readable at arm's length ---------- */
.duty-hero{border-radius:var(--r);padding:18px 16px;border:1px solid var(--line);
  border-left:8px solid var(--ink-faint);
  background:linear-gradient(180deg,var(--navy-2),#0b1a30);margin-bottom:12px}
.duty-hero.on{border-left-color:var(--ok);background:linear-gradient(180deg,rgba(46,194,126,.14),#0b1a30)}
.duty-hero.pending{border-left-color:var(--warn);border-left-style:dashed}
.duty-hero.stale{opacity:.8;border-style:dashed}
/* was 1.25rem — the single most important fact on the screen */
.duty-hero .state{font-size:2.1rem;line-height:1.05;font-weight:800;letter-spacing:.01em}
.duty-hero .sub{font-size:.95rem;color:var(--ink-dim);margin-top:4px}
/* The geofence verdict was an 11.5px pill in the top-right corner — the hardest
   place on the screen for a thumb and the easiest to lose to a cracked corner. */
.gps-line{display:flex;align-items:center;gap:9px;margin-top:12px;padding-top:12px;
  border-top:1px solid var(--line);font-size:1rem;font-weight:600}
.gps-line.ok{color:var(--ok)} .gps-line.warn{color:var(--warn)} .gps-line.bad{color:var(--bad)}

/* ---------- the one next thing ---------- */
.next-action{min-height:96px;width:100%;font-size:1.25rem;font-weight:800;letter-spacing:.02em}
.next-action .sub{font-size:.9rem;font-weight:500;opacity:1}
.btn-warn{background:var(--warn);border-color:var(--warn);color:#2a1a00}
.btn-danger{background:var(--bad-solid);border-color:var(--bad-solid);color:#fff}
.btn-ok{background:var(--ok-solid);border-color:var(--ok-solid);color:#fff}

/* ---------- patrol points ---------- */
.cp-row{display:flex;gap:11px;align-items:center;padding:12px;border:1px solid var(--line);
  border-radius:var(--r-sm);background:var(--navy-2);min-height:64px}
.cp-row.due{border-color:rgba(245,165,36,.55);border-left:4px solid var(--warn)}
.cp-row.over{border-color:rgba(229,72,77,.6);border-left:4px solid var(--bad)}
.cp-row.done{border-left:4px solid var(--ok)}
.cp-row .st{font-size:.78rem;font-weight:700;text-transform:uppercase;letter-spacing:.05em}
.cp-row.due .st{color:var(--warn)} .cp-row.over .st{color:var(--bad)} .cp-row.done .st{color:var(--ok)}
.cp-row .nm{font-size:1.05rem;font-weight:650}

/* ---------- sync state: three states, never two ---------- */
.sync{display:inline-flex;align-items:center;gap:5px;font-size:.76rem;font-weight:700;
  text-transform:uppercase;letter-spacing:.04em}
.sync.saving{color:var(--warn)} .sync.filed{color:var(--ok)} .sync.failed{color:var(--bad)}

/* ---------- chips: tapping beats typing at 02:00 ---------- */
.chips{display:flex;flex-wrap:wrap;gap:9px;margin-bottom:4px}
.chip{min-height:52px;padding:12px 16px;font-size:.95rem;font-weight:600;border-radius:999px;
  border:1px solid var(--line);background:var(--navy-3);color:var(--ink)}
.chip.on{background:var(--gold);border-color:var(--gold);color:#0b1524}

/* ---------- PANIC ----------
   Leaves the button grid entirely. Fixed to the bottom it is always in the
   thumb arc for either hand, never below the fold, never adjacent to a routine
   control, and never moves when the phone is rotated. Hold to arm: a pocket
   cannot sustain 1.2s of contiguous contact the way it can produce two taps,
   and unlike confirm() there is nothing Chrome can suppress. */
#panicBar{position:fixed;left:0;right:0;bottom:0;z-index:70;min-height:88px;
  padding-bottom:env(safe-area-inset-bottom);
  background:var(--bad-solid);color:#fff;font-size:1.3rem;font-weight:800;letter-spacing:.06em;
  border:none;border-top:4px solid var(--gold);border-radius:0;
  display:grid;place-items:center;overflow:hidden;touch-action:none;user-select:none}
#panicBar .fill{position:absolute;left:0;top:0;bottom:0;width:0;background:rgba(255,255,255,.3);
  pointer-events:none}
#panicBar .lbl{position:relative;z-index:1;text-align:center;line-height:1.15}
#panicBar .lbl small{display:block;font-size:.72rem;font-weight:600;letter-spacing:.04em;opacity:.9;
  text-transform:uppercase;margin-top:3px}
#panicBar:active{filter:none}
body.has-panicbar{padding-bottom:calc(104px + env(safe-area-inset-bottom))}
body.has-panicbar #toast{bottom:calc(104px + env(safe-area-inset-bottom))}

/* The outcome of a panic must never live in a toast that deletes itself —
   the control-room number was the only thing that reliably gets a human and it
   vanished after 20 seconds. This is a screen, and it survives a reboot. */
#panicScreen{position:fixed;inset:0;z-index:120;background:#12060a;color:#fff;
  display:none;flex-direction:column;padding:20px;padding-bottom:calc(20px + env(safe-area-inset-bottom));
  overflow-y:auto;gap:12px}
#panicScreen.on{display:flex}
#panicScreen h1{font-size:2rem;font-weight:800;margin:0;letter-spacing:.01em}
#panicScreen .status{border-radius:var(--r);padding:14px;font-weight:700;font-size:1.05rem;
  border:2px solid rgba(255,255,255,.25);background:rgba(255,255,255,.07)}
#panicScreen .status.sending{border-color:var(--warn);color:#ffd79a}
#panicScreen .status.recorded{border-color:var(--bad);color:#ffb8ba}
#panicScreen .status.acked{border-color:var(--ok);color:#a7f0cd;background:rgba(46,194,126,.14)}
#panicScreen .status.offline{border-color:var(--warn);color:#ffd79a;background:rgba(245,165,36,.14)}
#panicScreen .callbtn{min-height:68px;font-size:1.15rem;font-weight:800;background:#fff;color:#12060a;
  border:none;border-radius:var(--r)}
#panicScreen .clock{font-variant-numeric:tabular-nums;font-size:1rem;opacity:.85}
#panicScreen .standdown{margin-top:auto;min-height:60px;background:transparent;color:#fff;
  border:2px solid rgba(255,255,255,.35);position:relative;overflow:hidden;touch-action:none}
#panicScreen .standdown .fill{position:absolute;left:0;top:0;bottom:0;width:0;background:rgba(255,255,255,.25)}

/* ---------- night mode ----------
   White-on-navy measures 16.7:1 — superb at a desk and actively harmful at
   03:00. Rod-based dark adaptation takes 20-30 minutes to build and is wiped in
   seconds by blue-green light, leaving the officer functionally blind in the
   yard he is paid to watch. Long-wavelength amber spares it. #ffb067 on #0a0503
   still measures 11.1:1, so this is a night palette, not a dim one. */
body.night{
  --navy:#0a0503; --navy-2:#150b06; --navy-3:#1f1009;
  --line:#3d2110; --line-soft:#4a2814;
  --ink:#ffb067; --ink-dim:#c9884a; --ink-faint:#96603a;
  --gold:#d08a3a; --gold-soft:#e0a866;
  --ok:#cf9a3f; --ok-solid:#8a6122; --warn:#e0913a; --warn-solid:#8a5a1c;
  --info:#c08a3a;
}
/* PANIC keeps its saturated red and its unique full-bleed shape, so it stays
   identifiable when everything else has gone amber. */
body.night #panicBar{background:#8c1010;color:#fff}
body.night .duty-hero.on{background:linear-gradient(180deg,rgba(207,154,63,.12),#150b06)}

/* ---------- misc officer bits ---------- */
.tel-row{display:flex;align-items:center;gap:12px;min-height:56px;padding:10px 12px;
  border:1px solid var(--line);border-radius:var(--r-sm);background:var(--navy-2);
  font-weight:600;color:var(--ink)}
.tel-row .num{margin-left:auto;font-variant-numeric:tabular-nums;color:var(--gold)}
.thumbs .ph{position:relative}
.thumbs .ph button{position:absolute;top:-6px;right:-6px;min-height:28px;min-width:28px;
  padding:0;border-radius:50%;background:var(--bad-solid);color:#fff;border:none;font-size:.9rem}

@media(max-width:599px){
  /* auto-fit at minmax(230px) silently collapsed to one column on a 360px
     phone and snapped back to two in landscape — muscle memory for a
     life-safety control must not depend on orientation. */
  .g2{grid-template-columns:1fr}
}
@media(prefers-reduced-motion:reduce){
  *{animation:none !important;transition:none !important}
  /* the hold-progress wipe is functional feedback, not decoration */
  #panicBar .fill,#panicScreen .standdown .fill{transition:none}
}

/* ---------- print ---------- */
@media print{
  :root{--ink:#111;--ink-dim:#444;--ink-faint:#666;--line:#ccc}
  body{background:#fff;color:#111}
  header.top,.tabs,.no-print,button{display:none !important}
  .card,.item,.stat{background:#fff;border:1px solid #ccc;break-inside:avoid}
  .wrap{max-width:100%;padding:0}
  a{color:#111}
}
