:root{--bg:#0b0d10;--panel:#15191e;--line:#262c33;--txt:#d7dde3;--mut:#8a939c;--accent:#37b6ff;--good:#46d369;--bad:#ff5468;--warn:#ffb454}
*{box-sizing:border-box}html,body{margin:0;height:100%}
body{background:var(--bg);color:var(--txt);font:14px/1.4 -apple-system,Segoe UI,Roboto,sans-serif;-webkit-user-select:none;user-select:none;display:flex;flex-direction:column;overflow-x:clip;overflow-y:hidden}
/* WRAP the bar when it can't fit (instead of overflowing -> body pannable -> the view-clip bug, or collapsing the
   tabs). Tabs keep natural width and stay fully visible; the camera chips are the only thing that shrinks+scrolls. */
.topbar{display:flex;align-items:center;gap:14px;padding:10px 16px;background:var(--panel);border-bottom:1px solid var(--line);flex:0 0 auto;z-index:20;flex-wrap:wrap;row-gap:8px}
.brand{font-weight:700;font-size:18px;letter-spacing:.5px;flex:0 0 auto}.brand .sub{font-size:11px;color:var(--mut);font-weight:400;margin-left:6px}
.tabs{display:flex;gap:6px;flex:0 0 auto;flex-wrap:wrap;row-gap:6px}
.tab{background:transparent;color:var(--mut);border:1px solid transparent;padding:7px 14px;border-radius:8px;cursor:pointer;font-size:14px;flex:0 0 auto;min-height:40px;display:inline-flex;align-items:center;white-space:nowrap}
.tab.active{background:#1d2530;color:var(--txt);border-color:var(--line)}.tab:hover{color:var(--txt)}
.topstatus{margin-left:auto;display:flex;gap:14px;font-size:12px;color:var(--mut);flex:0 1 auto;min-width:0;overflow-x:auto;scrollbar-width:none}.topstatus::-webkit-scrollbar{display:none}
.topstatus .ts{display:flex;align-items:center;gap:5px;flex:0 0 auto;white-space:nowrap}
.topstatus .ts.cam-jump{cursor:pointer;border-radius:6px;padding:2px 6px;margin:-2px -2px;transition:color .12s,background .12s}
.topstatus .ts.cam-jump:hover{color:var(--txt);background:rgba(255,255,255,.08)}
.view{display:none;padding:14px}.view.active{display:block;flex:1 1 auto;min-height:0;overflow:auto}
/* Timeline: lay its sections (clipbar / scrubber / video stage / recordings) out as a FLEX COLUMN so they physically
   cannot overlap. In plain block flow at narrow widths the video stage rode UP over the scrubber (the absolutely-
   positioned timeline track then painted across the camera). flex:0 0 auto keeps each section at natural height; the
   view's own overflow:auto still scrolls. */
#view-clips.active{display:flex;flex-direction:column}
#view-clips.active>*{flex:0 0 auto}
.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(380px,1fr));gap:14px;max-width:1700px;margin:0 auto}
.cam{background:var(--panel);border:1px solid var(--line);border-radius:12px;overflow:hidden;display:flex;flex-direction:column}
.cam-head{display:flex;justify-content:space-between;align-items:center;padding:8px 12px}
.cam-name{font-weight:600}.cam-stat{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--mut)}
.dot{width:9px;height:9px;border-radius:50%;background:var(--mut);display:inline-block}
.cam-stat.live .dot{background:var(--good);box-shadow:0 0 8px var(--good)}
.cam-stat.reconnecting .dot,.cam-stat.connecting .dot{background:var(--warn)}.cam-stat.error .dot{background:var(--bad)}
.canvas-wrap{position:relative;background:#000;aspect-ratio:8/9;display:flex;align-items:center;justify-content:center;cursor:grab;touch-action:none}
/* LIVE grid fills the viewport: rows share the height, each tile's video flexes to fit so the control bar is
   always visible with no wasted space below (canvas object-fit:contain letterboxes as needed). */
/* live area fills below the topbar (flex body); grid rows share the height so each tile's video flexes to fit
   and the control bar is always visible — at any window size or camera count. */
#view-live .grid{height:100%;grid-auto-rows:1fr;align-items:stretch}
#view-live .cam{min-height:0}
#view-live .canvas-wrap{aspect-ratio:auto;flex:1 1 auto;min-height:0}
/* ============================================================================
   LIVE (multi-view) — fMP4/H.264 over /live.mp4 rendered into a STANDARD <video>
   by MSE/ManagedMediaSource (static/live.js). Replaces the old MJPEG <img> +
   CSS-transform-crop hack (the 0x0-collapse / firehose source).

   ★ ANTI-COLLAPSE CSS CHAIN (the core guarantee): sizing flows TOP-DOWN from the
   viewport; the video NEVER determines a container size; there is NO transform
   anywhere. html,body height:100% + flex column (set above) -> #view-grid claims
   the non-zero remainder -> .stage is sized by it -> .livevideo is 100% of that
   non-zero box. object-fit is the ONLY fit mechanism (no scale/translate). A
   <video> occupies its box even with no media (unlike a src-less <img>), so it
   cannot collapse to 0x0 on resize.
   ============================================================================ */
#view-grid{padding:0}                                          /* live video fills edge-to-edge (no .view padding inset) */
#view-grid.active{flex:1 1 auto;min-height:0;display:flex;overflow:hidden}      /* claims the non-zero remainder below the topbar */
.stage{position:relative;flex:1 1 auto;min-height:0;width:100%;background:#000;overflow:hidden}  /* size is INPUT to the video, never output */
.livevideo{width:100%;height:100%;display:block;object-fit:contain;background:#000}  /* 100% of the non-zero parent; object-fit ONLY (no transform) */
/* Dual-sensor crop of the SHARP single-cam native stream (1280x1440 = sensor0 top
   / sensor1 bottom). object-position crops to the chosen half — NOT an upscale,
   NOT a transform. Applied (by live.js) only after /api/select re-targets the
   backend to that single camera. */
.livevideo.sensor-top{object-fit:cover;object-position:center top}
.livevideo.sensor-bot{object-fit:cover;object-position:center bottom}
/* NxN click-overlay: sized by .stage (inset:0), NOT by the video. grid-template-columns/rows set by live.js
   bind() from window.GRID_DIM so it auto-sizes to 2x2 / 3x3 / 4x4 without CSS changes. */
.quadgrid{position:absolute;inset:0;display:grid}
.quad{background:transparent;border:0;cursor:pointer;padding:0;touch-action:manipulation}
/* Hover affordance = a faint fill ONLY (no accent BORDER). The composite is shown object-fit:contain, so a portrait
   stream (1280x1440 single-cam / 1280x2160 grid) letterboxes to a centered column with black bars either side; a
   per-quad accent BORDER framed that dead space as a mis-sized "blue half-width box". The faint fill still signals
   the clickable quadrant without drawing a frame around the letterbox. */
.quad:hover{background:rgba(55,182,255,.08)}
/* zoomed (single quad) state: hide the quad overlay so the cropped video shows clean; show the back button. */
#view-grid.zoomed .quadgrid{display:none}
#view-grid.zoomed .livevideo{cursor:pointer}   /* click a sensor (top=Fixed / bottom=Movable) to zoom; click again = Both */
.backbtn{position:absolute;top:10px;left:12px;z-index:5;display:none;background:rgba(13,16,20,.78);border:1px solid var(--line);color:var(--txt);border-radius:8px;padding:8px 14px;font-size:13px;cursor:pointer;min-height:44px;touch-action:manipulation}
.backbtn:hover{border-color:var(--accent)}
#view-grid.zoomed .backbtn{display:block}
/* manual lens toggle (Both/Fixed/Movable) — only meaningful on a single-camera zoom, so hidden until zoomed. */
#sensorbtn{display:none}
#view-grid.zoomed #sensorbtn{display:inline-flex;align-items:center}
.stage-overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;color:var(--mut);font-size:13px;background:rgba(0,0,0,.45);pointer-events:none}
.stage-overlay.hide{display:none}
.stage-ctrls{position:absolute;bottom:12px;right:12px;z-index:5;display:flex;gap:8px}
.stage-ctrls button{background:rgba(13,16,20,.78);border:1px solid var(--line);color:var(--txt);border-radius:8px;padding:8px 12px;font-size:16px;cursor:pointer;min-width:44px;min-height:44px;touch-action:manipulation}
.stage-ctrls button:hover{border-color:var(--accent)}
/* ---- mobile (Step 6): ALL rules inside @media so there is NO desktop regression. Feature-detected
   fullscreen / orientation-lock live in static/live.js behind if(feature)+catch. ---- */
@media(max-width:760px){
  .topbar{gap:10px;padding:8px 10px}
  .brand .sub{display:none}
  .tabs{gap:3px}
  .tab{padding:7px 9px;font-size:13px}   /* tabs tighten to icon-ish on narrow screens */
  .userchip .uemail{max-width:90px}
  .quad{min-width:44px;min-height:44px}  /* each quarter is already a big target; keep a floor */
}
@media(orientation:landscape) and (pointer:coarse){
  .topbar{padding:4px 10px}              /* landscape phone: thin topbar to maximise the video area */
  .topstatus{display:none}
}
.canvas-wrap.grabbing{cursor:grabbing}
canvas{position:absolute;inset:0;width:100%;height:100%;display:block;object-fit:contain}
.hidden-src{position:absolute;width:1px;height:1px;opacity:0;pointer-events:none}
.overlay-msg{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;color:var(--mut);font-size:13px;background:rgba(0,0,0,.45);pointer-events:none}
.overlay-msg.hide{display:none}
.canvas-wrap.inactive{cursor:pointer;filter:brightness(.5) grayscale(.25)}
.canvas-wrap.inactive .overlay-msg{font-size:20px;font-weight:700;color:#fff;background:rgba(0,0,0,.25);letter-spacing:.5px}
.canvas-wrap.inactive:hover .overlay-msg{background:rgba(55,182,255,.18)}
.cam.active-cam{border-color:var(--accent);box-shadow:0 0 0 1px var(--accent)}
.cam.active-cam .cam-name::before{content:"● ";color:var(--good)}
.ptz-hint{position:absolute;bottom:6px;left:8px;font-size:10px;color:rgba(255,255,255,.4);pointer-events:none;transition:opacity .4s}
.canvas-wrap:not(:hover) .ptz-hint{opacity:0}
.cam-ctrls{display:flex;flex-wrap:wrap;align-items:center;gap:8px;padding:9px 12px;border-top:1px solid var(--line)}
.cam:not(.active-cam) .cam-ctrls{opacity:.4}   /* non-active controls look disabled; a click just activates the cam */
.cam:not(.active-cam) .cam-ctrls button{cursor:pointer}
.seg{display:flex;gap:2px;background:#10141a;border:1px solid var(--line);border-radius:8px;padding:2px}
.ptzseg{display:none}                 /* zoom/pan/tilt: shown only when the Movable lens is selected */
.cam.movable .ptzseg{display:flex}
.seg button{background:transparent;color:var(--txt);border:0;padding:6px 9px;border-radius:6px;cursor:pointer;font-size:13px;min-width:30px}
.seg button:hover{background:#222b35}.seg button.on{background:var(--accent);color:#04222f}
/* F: PTZ presets + guard-tour control (operator-only; unhidden by app.js) */
.ptz-presets[hidden]{display:none}
.ptz-preset-sel{background:#10141a;color:var(--txt);border:1px solid var(--line);border-radius:6px;padding:4px 6px;font-size:12px;cursor:pointer;max-width:120px}
.fps{margin-left:auto;font-size:11px;color:var(--mut);font-variant-numeric:tabular-nums}
.cam.fs{position:fixed;inset:0;z-index:50;border-radius:0;max-width:none;height:100%;overflow:hidden}
.cam.fs .cam-head{flex:0 0 auto}
.cam.fs .canvas-wrap{aspect-ratio:auto;flex:1 1 auto;min-height:0;max-height:none}
.cam.fs .cam-ctrls{flex:0 0 auto}
/* recordings */
.clipbar{display:flex;align-items:center;gap:10px;flex-wrap:wrap;max-width:1700px;margin:0 auto 12px}
.clipbar select,.clipbar button{background:var(--panel);color:var(--txt);border:1px solid var(--line);border-radius:8px;padding:7px 10px;cursor:pointer}
.auditflt input[type=text],.auditflt input[type=date]{background:#10141a;color:var(--txt);border:1px solid var(--line);border-radius:8px;padding:6px 9px;font-size:13px;width:auto;display:inline-block;margin-bottom:0}
.auditflt input[type=date]{cursor:pointer}.auditflt .chk{cursor:default}
.chk{display:flex;align-items:center;gap:5px;color:var(--mut);font-size:13px}.clipcount{color:var(--mut);font-size:12px}
/* The full-res clip player (right column) is hidden until you click ⤢ on a row — otherwise it's a permanent empty
   'Select a recording' viewer. Default = recordings list full-width; .showplayer reveals the player alongside it. */
.clipmain{display:grid;grid-template-columns:1fr;gap:14px;max-width:1700px;margin:0 auto}
.clipmain.showplayer{grid-template-columns:340px 1fr}
.clipplayer{display:none}.clipmain.showplayer .clipplayer{display:block}
.cliplist{max-height:78vh;overflow:auto;display:flex;flex-direction:column;gap:6px}
.clipitem{display:flex;gap:9px;padding:6px;background:var(--panel);border:1px solid var(--line);border-radius:9px;cursor:pointer;align-items:center}
.clipitem:hover{border-color:var(--accent)}.clipitem.sel{border-color:var(--accent);background:#16222c}
.clipitem img{width:86px;height:48px;object-fit:cover;border-radius:5px;background:#000;flex:none}
.clipitem .ci-t{font-size:13px;font-weight:600}.clipitem .ci-s{font-size:11px;color:var(--mut)}
.clipitem > div{flex:1 1 auto;min-width:0}
.ci-dl{flex:none;background:rgba(13,16,20,.6);border:1px solid var(--line);color:var(--txt);border-radius:7px;width:40px;height:40px;font-size:16px;cursor:pointer;line-height:1;touch-action:manipulation}
.ci-dl:hover{border-color:var(--accent);color:var(--accent)}.ci-dl[data-busy]{opacity:.6;cursor:wait}
.ci-fullres,.ci-share{flex:none;background:rgba(13,16,20,.6);border:1px solid var(--line);color:var(--txt);border-radius:7px;width:40px;height:40px;font-size:15px;cursor:pointer;line-height:1;touch-action:manipulation}
.ci-fullres:hover,.ci-share:hover{border-color:var(--accent);color:var(--accent)}
.tag{display:inline-block;font-size:10px;padding:1px 6px;border-radius:6px;background:#23303b;color:var(--accent);margin-right:4px}
.tag.person{background:#2a2140;color:#b89cff}.tag.vehicle{background:#1f3326;color:#7fe0a0}.tag.pet{background:#3a2a1e;color:#ffc07a}
.clipplayer{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:12px;min-height:300px;position:relative}
.clip-close{position:absolute;top:8px;right:8px;z-index:2;background:rgba(13,16,20,.85);color:var(--txt);border:1px solid var(--line);border-radius:6px;padding:5px 10px;font-size:12px;cursor:pointer;min-height:34px}
.clip-close:hover{border-color:var(--accent)}
.clipplayer video{width:100%;border-radius:8px;background:#000;max-height:70vh}
.clipmeta{color:var(--mut);font-size:13px;margin-top:10px}
.dlbtn{display:inline-block;margin-top:10px;background:var(--accent);color:#04222f;padding:8px 14px;border-radius:8px;text-decoration:none;font-weight:600}
.hidden{display:none}
@media(max-width:760px){.clipmain{grid-template-columns:1fr}.cliplist{max-height:30vh;flex-direction:row;overflow-x:auto}}
/* wide multi-column tables scroll horizontally within their card on narrow screens (else the right columns —
   e.g. the Users "Actions" — clip off-screen with body overflow-x:clip and are unreachable). */
@media(max-width:760px){.idcard{overflow-x:auto}#admin-users,#admin-sessions,#admin-audit,#admin-connections,.systab.camtab{min-width:560px}}

/* ---------- identity page ---------- */
.idwrap{max-width:680px;margin:24px auto;padding:0 16px;display:flex;flex-direction:column;gap:16px}
.idcard{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:16px 18px}
.idcard h3{margin:0 0 4px}.idsub{font-weight:400;color:var(--mut);font-size:13px}
.idhint{margin:0 0 12px;color:var(--mut);font-size:13px;line-height:1.4}
.idstat{margin-bottom:12px;font-size:14px}
.idstat code{background:#10141a;border:1px solid var(--line);border-radius:6px;padding:2px 7px;font-size:13px}
.idcard input{display:block;width:100%;box-sizing:border-box;margin-bottom:9px;background:#10141a;color:var(--txt);
  border:1px solid var(--line);border-radius:8px;padding:10px 12px;font-size:14px}
.idbtn{background:var(--accent);color:#04222f;border:0;border-radius:8px;padding:10px 16px;font-size:14px;
  font-weight:600;cursor:pointer;min-height:44px;touch-action:manipulation}
.idbtn.warn{background:var(--warn)}.idbtn:hover{filter:brightness(1.08)}
.idmsg{min-height:20px;font-size:13px;color:var(--mut);text-align:center}.idmsg.bad{color:var(--bad)}
.ok{color:var(--good)}.bad{color:var(--bad)}.mut{color:var(--mut)}
.systab{width:100%;border-collapse:collapse;font-size:14px}
.systab td,.systab th{text-align:left;padding:6px 8px;border-bottom:1px solid var(--line);vertical-align:top}
.systab th{color:var(--mut);font-weight:600;font-size:12px;text-transform:uppercase;letter-spacing:.4px}
.systab .sk{color:var(--mut);width:130px;white-space:nowrap}.systab .sv{word-break:break-all}
.systab tr.conn-you td{background:rgba(55,182,255,.10)}.systab tr.conn-you td:first-child{box-shadow:inset 3px 0 0 var(--accent)}
.systab code{background:#10141a;border:1px solid var(--line);border-radius:5px;padding:1px 6px;font-size:12px;-webkit-user-select:text;user-select:text;cursor:text;word-break:break-all;overflow-wrap:anywhere}
/* collapsed audit/events table: show header + first 5 data rows; toggle removes .collapsed to reveal all */
.systab.collapsed tr:nth-child(n+7){display:none}
.camtab td:first-child{font-weight:600}
.camtab tr[data-ch],#st-cams tr[data-ch]{cursor:pointer}.camtab tr[data-ch]:hover td,#st-cams tr[data-ch]:hover td{background:rgba(55,182,255,.07)}

/* ---------- auth: user chip + admin users/activity tab ---------- */
.userchip{display:flex;align-items:center;gap:8px;font-size:12px;color:var(--mut);flex:0 0 auto}
.userchip .uemail{max-width:190px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.userchip .urole{background:#1d2530;border:1px solid var(--line);border-radius:6px;padding:1px 7px;color:var(--accent);text-transform:uppercase;font-size:10px;letter-spacing:.4px}
.userchip .ulogout{color:var(--mut);text-decoration:none;border:1px solid var(--line);border-radius:6px;padding:3px 9px}
.userchip .ulogout:hover{color:var(--txt);border-color:var(--accent)}
#admin-users select,#admin-users button,#admin-audit button,#admin-sessions button,#admin-connections button,#conn-toggle{background:#10141a;color:var(--txt);border:1px solid var(--line);border-radius:6px;padding:7px 12px;cursor:pointer;font-size:12px;min-height:40px;touch-action:manipulation}
#admin-users button:hover,#admin-sessions button:hover,#admin-connections button:hover,#conn-toggle:hover{border-color:var(--accent)}
.pill{font-size:10px;padding:1px 7px;border-radius:6px;text-transform:uppercase;letter-spacing:.3px}
.pill.pending{background:#3a2a1e;color:#ffc07a}.pill.active{background:#1f3326;color:#7fe0a0}.pill.disabled{background:#2a1e22;color:#ff8a9b}
.toast{position:fixed;bottom:22px;left:50%;transform:translateX(-50%);background:#1d2530;border:1px solid var(--line);color:var(--txt);padding:10px 16px;border-radius:8px;font-size:13px;z-index:200;box-shadow:0 6px 24px rgba(0,0,0,.5)}

/* Recordings date-range inputs */
.dateinp { background:#0d1320; color:var(--fg,#dfe7f3); border:1px solid #243049; border-radius:6px; padding:4px 8px; font-size:13px; color-scheme:dark; }

/* ---------- Events tab (AI detections) — C.1/C.2 ---------- */
.ev-live{font-size:12px;color:var(--mut)}
.ev-live.on{color:var(--good)}.ev-live.off{color:#ffc07a}
/* Events toolbar shares .clipbar (1700px) but the list is 900px — pin the toolbar to 900px too so both centre on
   the same column (was the ~250px left inset that read as "clipped on the left"). */
#view-events .clipbar{max-width:900px}
.evlist{display:flex;flex-direction:column;gap:8px;padding:12px;max-width:900px;margin:0 auto;overflow:auto}
.evrow{display:flex;align-items:center;gap:12px;background:#0f141b;border:1px solid var(--line);border-radius:10px;padding:8px 12px;transition:border-color .12s}
.evrow.ev-actionable{cursor:pointer}                 /* only rows with a recording look/act clickable */
.evrow.ev-actionable:hover{border-color:var(--accent)}
.ev-thumb{width:104px;height:58px;object-fit:cover;border-radius:6px;background:#10141a;flex:0 0 auto}
.evrow.ev-actionable .ev-thumb{cursor:pointer}
.ev-nothumb{display:flex;align-items:center;justify-content:center;font-size:24px;color:var(--mut)}
.ev-meta{flex:1 1 auto;min-width:0}
.ev-line1{display:flex;align-items:center;gap:10px;min-width:0}
.ev-line2{color:var(--mut);font-size:12px;margin-top:3px;min-width:0}
.ev-abs{opacity:.7}
.ev-cam{font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ev-badge{font-size:11px;padding:2px 9px;border-radius:6px;text-transform:uppercase;letter-spacing:.3px;background:#1d2530;color:var(--txt)}
.ev-badge.ev-person{background:#2a1f3a;color:#c79bff}
.ev-badge.ev-vehicle{background:#1e2b3a;color:#7abaff}
.ev-badge.ev-pet{background:#1f3326;color:#7fe0a0}
.ev-badge.ev-face{background:#3a2f1e;color:#ffcf7a}
.ev-badge.ev-motion{background:#2a2230;color:#bfa8d0}
.ev-play{background:#10141a;color:var(--txt);border:1px solid var(--line);border-radius:8px;padding:6px 12px;cursor:pointer;flex:0 0 auto;min-height:44px;display:inline-flex;align-items:center;justify-content:center;touch-action:manipulation}
.ev-play:hover{border-color:var(--accent)}
.ev-acts{display:flex;gap:6px;flex:0 0 auto;align-items:center}
.ev-dl,.ev-share{background:#10141a;color:var(--txt);border:1px solid var(--line);border-radius:8px;padding:6px 10px;cursor:pointer;flex:0 0 auto;font-size:14px;min-height:44px;min-width:44px;display:inline-flex;align-items:center;justify-content:center;touch-action:manipulation}
.ev-dl:hover,.ev-share:hover{border-color:var(--accent)}
.ev-empty{padding:36px;text-align:center}

/* H.3 feed-liveness / tamper warning overlay + E.3 quality selector */
.tamper-warn{position:absolute;top:12px;left:50%;transform:translateX(-50%);z-index:40;background:rgba(120,20,24,.92);color:#fff;padding:7px 14px;border-radius:8px;font-size:13px;box-shadow:0 4px 16px rgba(0,0,0,.5)}
.tamper-warn.hide{display:none}
.qualsel{background:#10141a;color:var(--txt);border:1px solid var(--line);border-radius:6px;padding:4px 6px;font-size:12px;cursor:pointer}
.qualsel.hidden{display:none}

/* B: DVR timeline scrubber */
.scrubber-wrap{padding:8px 14px 4px;border-bottom:1px solid var(--line)}
.scrubber-head{display:flex;align-items:center;gap:16px;font-size:12px;color:var(--mut);margin-bottom:6px;flex-wrap:wrap}
.scrubber-day{font-weight:600;color:var(--txt);display:inline-flex;align-items:center;gap:10px;flex-wrap:wrap}
.scrubber-tz{font-weight:500;color:#7abaff;background:#12233a;border:1px solid #244a70;border-radius:5px;padding:1px 7px;font-size:11px;cursor:help}
.scrubber-avail{font-weight:500;color:var(--mut);font-size:12px}
.scrubber-legend{display:flex;align-items:center;gap:6px}
.scrubber-legend .lg{width:11px;height:11px;border-radius:3px;display:inline-block;margin:0 3px 0 10px}
.scrubber-legend .cov{background:#2b6cff}.scrubber-legend .gap{background:#1a2230}.scrubber-legend .ev{background:#c79bff;border-radius:50%}
.scrubber-speed{margin-left:auto;display:flex;gap:4px}
.scrubber-speed button{background:#10141a;color:var(--txt);border:1px solid var(--line);border-radius:6px;padding:2px 10px;cursor:pointer;font-size:12px;min-height:40px;min-width:40px;display:inline-flex;align-items:center;justify-content:center;touch-action:manipulation}
#dvr-ctl button,#dvr-ctl select,#dvr-banner button{min-height:40px;touch-action:manipulation}
.scrubber-speed button.on{border-color:var(--accent);color:var(--accent)}
.scrubber{position:relative;height:44px;margin-top:14px}
.scrub-hours{position:absolute;top:-14px;left:0;right:0;height:12px}
.scrub-hour{position:absolute;transform:translateX(-50%);font-size:10px;color:var(--mut)}
/* the track background reads as "no footage"; coverage blocks are brighter so available footage is obvious at a glance */
.scrub-track{position:absolute;left:0;right:0;top:0;height:28px;background:repeating-linear-gradient(45deg,#0e131b,#0e131b 6px,#121821 6px,#121821 12px);border:1px solid var(--line);border-radius:6px;overflow:hidden;cursor:crosshair}
.scrub-block{position:absolute;top:3px;height:22px;background:#2b6cff;opacity:.9;border-radius:2px;box-shadow:0 0 0 1px rgba(122,186,255,.35) inset}
.scrub-block.evt{background:#c79bff;opacity:.92}
.scrub-pip{position:absolute;top:-3px;width:7px;height:7px;border-radius:50%;background:#c79bff;transform:translateX(-50%);box-shadow:0 0 3px rgba(0,0,0,.6)}
.scrub-pip.ev-vehicle{background:#7abaff}.scrub-pip.ev-pet{background:#7fe0a0}.scrub-pip.ev-face{background:#ffcf7a}.scrub-pip.ev-motion{background:#8a939c}
.scrub-cursor{position:absolute;top:-2px;bottom:-2px;width:2px;background:#ffcf7a;transform:translateX(-50%);pointer-events:none;z-index:3;box-shadow:0 0 4px rgba(255,207,122,.7)}
/* "live edge" on TODAY: a NOW line at the current time (recorded up to here) + a dimmed un-recorded future so it's
   clear you can't play past the present. */
.scrub-now{position:absolute;top:-4px;bottom:-4px;width:3px;background:var(--good);pointer-events:none;z-index:5;box-shadow:0 0 9px 1px var(--good)}
.scrub-now::after{content:"● NOW";position:absolute;top:-17px;left:50%;transform:translateX(-50%);font-size:10px;font-weight:800;color:#04120a;background:var(--good);padding:1px 6px;border-radius:4px;letter-spacing:.5px;white-space:nowrap;box-shadow:0 1px 4px rgba(0,0,0,.55)}
/* the un-recorded future: a clear grey hatched "no footage yet" wash — deliberately obvious, not a faint dim. It
   intercepts the pointer (not-allowed cursor) so it reads as un-seekable; its handler blocks the seek. */
.scrub-future{position:absolute;top:0;bottom:0;pointer-events:auto;cursor:not-allowed;z-index:2;background:repeating-linear-gradient(45deg,rgba(74,82,96,.5),rgba(74,82,96,.5) 5px,rgba(10,12,16,.78) 5px,rgba(10,12,16,.78) 10px)}
.scrub-hover{position:absolute;top:-17px;transform:translateX(-50%);font-size:10px;background:rgba(0,0,0,.8);color:#fff;padding:1px 5px;border-radius:3px;pointer-events:none;white-space:nowrap;z-index:4}
/* Timeline inline DVR playback stage (second /live.mp4 feeder; plays while the scrubber above stays clickable) */
.dvr-stage{position:relative;background:#000;border-bottom:1px solid var(--line);max-height:52vh;display:flex;align-items:center;justify-content:center;min-height:180px;overflow:hidden}
.dvr-stage video{width:100%;max-height:52vh;object-fit:contain;background:#000;display:block}
/* DVR "Both": two REAL side-by-side <video>s (a canvas over an occluded/hidden feeder throttles its decode + stalls) */
#dvr-video-b{display:none}
.dvr-stage.dvrboth{flex-direction:row;gap:2px}
.dvr-stage.dvrboth #dvr-video,.dvr-stage.dvrboth #dvr-video-b{width:50%}
.dvr-stage.dvrboth #dvr-video-b{display:block}
/* Stacked Both (narrow): let the STAGE grow to fit both full-width videos (page scrolls) rather than capping it at
   52vh — otherwise the two videos overflow the stage and the top one spills UP over the scrubber (the timeline track
   then paints across the camera). Full-width videos keep them large; the base video max-height:52vh still caps each. */
@media (max-width:760px){.dvr-stage.dvrboth{flex-direction:column;max-height:none}.dvr-stage.dvrboth #dvr-video,.dvr-stage.dvrboth #dvr-video-b{width:100%}}
.dvr-stage .overlay-msg{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);color:var(--mut);font-size:13px;pointer-events:none;text-align:center;padding:0 16px}

/* A.3 share-link dialog (with an explicit Copy-to-clipboard button) */
.modal-ov{position:fixed;inset:0;background:rgba(0,0,0,.55);z-index:10000;display:flex;align-items:center;justify-content:center;padding:16px}
.modal{background:#0f141b;border:1px solid var(--line);border-radius:12px;padding:18px;max-width:560px;width:100%;box-shadow:0 12px 40px rgba(0,0,0,.5)}
.modal-h{font-weight:600;font-size:15px;margin-bottom:12px;color:var(--txt)}
.modal-foot{display:flex;justify-content:flex-end;gap:8px;margin-top:14px}
.share-row{display:flex;gap:8px}
.share-row input{flex:1;background:#0b0f15;color:var(--txt);border:1px solid var(--line);border-radius:8px;padding:8px 10px;font-size:13px;font-family:ui-monospace,monospace}
.btn{background:#10141a;color:var(--txt);border:1px solid var(--line);border-radius:8px;padding:8px 14px;cursor:pointer;font-size:13px;text-decoration:none;display:inline-flex;align-items:center}
.btn:hover{border-color:var(--accent)}
.btn-accent{background:var(--accent);color:#04222f;border:0;border-radius:8px;padding:8px 14px;cursor:pointer;font-size:13px;font-weight:600;white-space:nowrap}
.btn-accent:hover{filter:brightness(1.08)}
/* Eufy-session-expired alert (shown when a live view is stuck because the NVR token expired) */
#auth-expired-bar,#engine-warn-bar{position:fixed;left:50%;bottom:22px;transform:translateX(-50%);z-index:9000;max-width:calc(100vw - 24px);display:flex;align-items:center;gap:12px;flex-wrap:wrap;justify-content:center;background:#3a2a0c;border:1px solid var(--warn);color:var(--txt);padding:11px 16px;border-radius:10px;font-size:13px;box-shadow:0 8px 30px rgba(0,0,0,.6)}
#auth-expired-bar .ae-txt,#engine-warn-bar .ae-txt{max-width:520px}
#auth-expired-bar strong,#engine-warn-bar strong{color:var(--warn)}
#auth-expired-bar .ae-sub,#engine-warn-bar .ae-sub{color:var(--mut)}
#auth-expired-bar button,#engine-warn-bar button{background:var(--warn);color:#161a12;border:0;border-radius:7px;padding:8px 16px;font-size:13px;font-weight:600;cursor:pointer;min-height:38px;touch-action:manipulation}
#auth-expired-bar button:hover,#engine-warn-bar button:hover{filter:brightness(1.08)}
