/* css/12-cameras.css — LIVE CAMERAS view.
   Split from the index.html <style> block (REFACTOR-PLAN Phase 2). Load order = cascade order; do not reorder. */
/* ---------- CAMERAS (revamp 6) --------------------------------------------
   Two panes. LEFT: the vehicle rendered in 3D from above (carview.js) under an
   SVG overlay of the four camera fields of view — the wedge IS the control.
   RIGHT: the four feeds, at rest, as STILLS; one tap promotes one of them to a
   60 s stream and it stops itself again.
   The four <video> elements never stop decoding (camsPlay: they are the
   perception sensor, see drawCues/captureEvidence) — what the tap owns is the
   picture, not the decoder. */
#view-cams{grid-column:1 / -1}
/* minmax(0,1fr), not 1fr: a grid track's automatic min-content floor let the
   status line's own text widen the right pane past the viewport at 640 */
#view-cams .camwrap{grid-column:1 / -1;display:grid;grid-template-columns:38% minmax(0,1fr);
  gap:12px;min-height:0;position:relative;pointer-events:auto}

/* ---------- left: the car pane -------------------------------------------- */
.carpane{position:relative;display:flex;flex-direction:column;min-height:0;
  padding:10px 12px 10px;border-radius:var(--r);overflow:hidden;
  background:var(--glass);border:1px solid var(--hair);
  -webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);
  box-shadow:0 10px 30px -20px rgba(0,0,0,.9)}
/* the wedges deliberately run past the car and are CLIPPED by the pane — a
   field of view does not stop at a panel edge, and the gradient has faded to
   near nothing by the time it reaches one. */
/* the HUD floor the car stands on: one soft pool of light, nothing else */
.carpane::before{content:"";position:absolute;inset:0;pointer-events:none;border-radius:inherit;
  background:radial-gradient(120% 78% at 50% 46%, rgba(34,200,130,.07), rgba(34,200,130,0) 62%),
             radial-gradient(140% 100% at 50% 120%, rgba(0,0,0,.45), rgba(0,0,0,0) 70%)}
.carbox{position:relative;flex:1;min-height:0}
.carbox canvas{position:absolute;inset:0;display:block;width:100%;height:100%}
/* the SVG fallback silhouette carview.js draws when WebGL or the glb fails */
.carbox .carsil{position:absolute;inset:0;width:100%;height:100%}
.carbox .carsil .hull{fill:#242C31;stroke:rgba(255,255,255,.14);stroke-width:1}
.carbox .carsil .glass{fill:#101A20}
.carbox .carsil .roof{fill:#2A3339}
/* the two sensor bars and the four camera pods that carry the wedges */
.carbox .carsil .bar{fill:#2F383E}
.carbox .carsil .pod{fill:#161D22;stroke:rgba(255,255,255,.20);stroke-width:1}
.carbox .carsil .wheel{fill:#0E1214}
.carlegend{position:relative;flex:0 0 auto;margin:6px auto 0;padding:5px 12px;border-radius:999px;
  font-size:var(--fz-cap);letter-spacing:.12em;text-transform:uppercase;color:var(--tx3);font-weight:600;
  background:rgba(6,11,12,.62);border:1px solid var(--hair)}

/* ---------- the FOV wedges ------------------------------------------------ */
/* only the wedge groups take pointer events, so a tap on bare pane background
   still reaches the canvas (carview.js maps it to the nearest camera) */
.carfov{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;overflow:visible}
.carfov stop{stop-color:var(--acc)}
.carfov .fov{pointer-events:auto;cursor:pointer}
.carfov .fov .wedge{opacity:.12;transition:opacity .3s var(--ease)}
.carfov .fov .edge{fill:none;stroke:var(--acc);stroke-width:1;opacity:.30;
  transition:opacity .3s var(--ease),stroke-width .3s var(--ease)}
/* the dot is a RETICLE around the pod, not a lid over it: its fill stays
   translucent so the camera pod underneath still reads (it goes solid accent
   only when that camera is the selection) */
.carfov .fov .dot{fill:rgba(6,11,12,.18);stroke:var(--acc);stroke-width:1.3;opacity:.82;
  transition:fill .3s var(--ease),opacity .3s var(--ease)}
.carfov .fov .num{font-family:var(--mono);font-size:10px;font-weight:700;fill:var(--tx2);
  text-anchor:middle;pointer-events:none}
.carfov .fov .lbl{font-size:10px;font-weight:700;letter-spacing:.11em;text-transform:uppercase;
  fill:var(--tx3);pointer-events:none;transition:fill .3s var(--ease)}
.carfov .fov:hover .wedge{opacity:.30}
/* a focused SVG <g> takes the UA's own blue ring, which is neither the
   cockpit's accent nor confined to the wedge: suppress it and let the wedge's
   own outline carry focus instead */
.carfov .fov:focus,.carfov .fov:focus-visible{outline:none}
.carfov .fov:focus-visible .edge{opacity:1;stroke-width:2.2}
.carfov .fov:focus-visible .wedge{opacity:.40}
/* SELECTED / STREAMING — the one accent glow on this screen */
.carfov .fov.on .wedge{opacity:.50}
.carfov .fov.on .edge{opacity:.92;stroke-width:1.5}
.carfov .fov.on .dot{fill:var(--acc);opacity:1}
.carfov .fov.on .num{fill:var(--on-acc)}
.carfov .fov.on .lbl{fill:var(--acc);font-weight:800}
.carfov .fov.live .edge{stroke-dasharray:16 11;animation:fovsweep 1.2s linear infinite}
@keyframes fovsweep{to{stroke-dashoffset:-54}}
/* ...and everything that is NOT the selection recedes, so the pane carries
   exactly one highlight while a camera is chosen (revamp 7) */
.carfov .fov.dim .wedge{opacity:.06}
.carfov .fov.dim .edge{opacity:.25}
.carfov .fov.dim .dot{opacity:.38}
.carfov .fov.dim .num,.carfov .fov.dim .lbl{fill:var(--tx3)}
.carfov .fov.dim:hover .wedge{opacity:.22}
@media (prefers-reduced-motion: reduce){
  .carfov .fov.live .edge{animation:none;stroke-dasharray:none}
}

/* ---------- right: the wall ----------------------------------------------- */
.campane{position:relative;display:flex;flex-direction:column;min-height:0;min-width:0}
/* revamp 7: the pane has no header. #camStatus is still in the DOM (JS writes
   the live readout into it) but it is [hidden] and carries no styling of its
   own, so `hidden`'s display:none is the last word. */
.cams{flex:1;display:grid;grid-template-columns:1fr 1fr;grid-template-rows:1fr 1fr;gap:8px;min-height:0}
#view-cams .cams{gap:12px}
/* one tile streaming: it IS the pane. The other three are not a strip along
   the bottom any more (client, revamp 7) — the stream alone fills the pane and
   the ✕ is the only way back to the 2x2 wall of stills. */
#camhost .cams.streaming .tile.streaming{grid-area:1 / 1 / -1 / -1}
#camhost .cams.streaming .tile:not(.streaming){display:none}

.tile{position:relative;border-radius:var(--r-sm);overflow:hidden;background:#0B1012;border:1px solid var(--hair);min-height:0;
  box-shadow:0 8px 22px -14px rgba(0,0,0,.8);transition:border-color .25s var(--ease)}
.tile video,.tile canvas,.tile .still{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
/* A RESTING tile shows its poster. The decoder underneath keeps running — it
   is the perception sensor — it just is not painted, which is what actually
   costs data on a real MDVR link. */
.tile:not(.streaming) video{visibility:hidden}
.tile.streaming .still{visibility:hidden}
.tile.streaming{border-color:rgba(34,200,130,.55)}
.tile .lab{position:absolute;top:8px;left:8px;z-index:3;display:flex;align-items:center;gap:7px;font-size:var(--fz-body);font-weight:700;background:var(--glass-solid);padding:5px 9px;border-radius:999px;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);border:1px solid var(--hair)}
/* the one place an infinite blink survives: a LIVE camera feed indicator, and
   Cameras is not the Drive view (wave3-motion §1). A still is not live, so its
   dot is a dim grey disc instead. */
.tile .lab i{width:7px;height:7px;border-radius:50%;background:var(--crit);animation:blink 1.2s infinite}
.tile:not(.streaming) .lab i{background:var(--tx3);animation:none}
/* static sensor reticle per tile (wave3-visual §3) */
.tile::after{content:"";position:absolute;inset:12%;border:1px solid rgba(34,200,130,.24);border-radius:6px;pointer-events:none}
.tile:not(.streaming)::after{opacity:.45}
/* the AI overlays belong to a picture that is actually on screen */
.tile:not(.streaming) .det,.tile:not(.streaming) .clslab{display:none!important}
/* cue boxes are positioned with translate/scale, never left/top/width/height */
.tile .det{position:absolute;left:0;top:0;transform-origin:0 0;border:2px solid var(--acc);border-radius:4px;transition:transform .2s var(--ease),translate 4s linear,opacity .4s var(--ease);z-index:3}
/* wave14: the overlay is a 4 s LIFETIME on ONE tile, then it fades. `.out` is
   the fade-out state. Placement stays on `transform` (px, re-written on every
   relayout); the slight tracking DRIFT rides the separate `translate` property
   over the box's four seconds, so the two never fight over one transition. */
.tile .det.out,.tile .clslab.out{opacity:0}
.tile .clslab{transition:opacity .4s var(--ease)}
/* the owning tile's LIVE dot pops once when its camera reports a detection */
@keyframes dotpop{0%{transform:scale(1)}40%{transform:scale(1.4)}100%{transform:scale(1)}}
.tile.detpulse .lab i{animation:dotpop .5s var(--ease) 1,blink 1.2s infinite}
.tile .det span{position:absolute;left:-2px;bottom:100%;margin-bottom:6px;background:var(--acc);color:var(--on-acc);font-size:var(--fz-body);font-weight:700;padding:3px 8px;border-radius:6px;white-space:nowrap}
.tile .det.below span{bottom:auto;top:100%;margin:6px 0 0}
/* a box near the frame's right edge anchors its label to its own right edge, so
   the label never runs off the tile */
.tile .det.rightish span{left:auto;right:-2px}
.tile .clslab{position:absolute;left:8px;bottom:7px;z-index:3;max-width:calc(100% - 16px);
  font-size:var(--fz-meta);font-weight:700;color:var(--on-acc);background:var(--acc);padding:3px 8px;border-radius:6px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.tile.crit .clslab{background:var(--crit);color:#1A0608}
/* one label per tile: the box carries its own at this size */
#camhost .tile .clslab{display:none!important}
.tile.crit .det{border-color:var(--crit)}
.tile.crit .det span{background:var(--crit);color:#1A0608}
.tile .tap{position:absolute;inset:0;z-index:5;background:none;border:0}

/* ---------- still affordances -------------------------------------------- */
.tile .playg{position:absolute;left:50%;top:50%;z-index:4;transform:translate(-50%,-50%);
  width:46px;height:46px;border-radius:50%;display:grid;place-items:center;color:var(--tx);
  background:rgba(6,11,12,.60);border:1px solid var(--hair2);
  -webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);
  transition:transform .2s var(--ease),background .2s var(--ease),color .2s var(--ease)}
.tile .playg .i{width:18px;height:18px;margin-left:2px}
.tile .stillhint{position:absolute;right:8px;bottom:7px;z-index:4;font-family:var(--mono);
  font-size:var(--fz-cap);letter-spacing:.06em;color:var(--tx3);
  background:var(--glass-solid);border:1px solid var(--hair);padding:4px 7px;border-radius:6px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:calc(100% - 16px)}
.tile:hover .playg{background:var(--acc);color:var(--on-acc);transform:translate(-50%,-50%) scale(1.06)}
.tile.streaming .playg,.tile.streaming .stillhint{display:none}
.tile .standin{position:absolute;left:8px;bottom:7px;z-index:4;right:52%;font-size:var(--fz-cap);font-weight:600;color:var(--tx3);background:var(--glass-solid);padding:4px 7px;border-radius:6px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* ---------- 60 s countdown + stop ---------------------------------------- */
.tile .camcd{position:absolute;top:8px;right:60px;z-index:6;width:44px;height:44px;
  display:grid;place-items:center}
.tile .camcd svg{position:absolute;inset:0;width:100%;height:100%;transform:rotate(-90deg)}
.tile .camcd .trk{fill:rgba(6,11,12,.74);stroke:var(--hair2);stroke-width:2}
.tile .camcd .prg{fill:none;stroke:var(--acc);stroke-width:2.4;stroke-linecap:round;
  transition:stroke-dashoffset 1s linear}
.tile .camcd .num{position:relative;font-family:var(--mono);font-variant-numeric:tabular-nums;
  font-size:var(--fz-body);font-weight:700;color:var(--tx)}
.tile .camx{position:absolute;top:8px;right:8px;z-index:6;width:44px;height:44px;
  border-radius:11px;display:none;align-items:center;justify-content:center;
  background:var(--glass-solid);border:1px solid var(--hair2);color:var(--tx);
  -webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px)}
.tile .camx svg{width:18px;height:18px;stroke-width:2.4}
.cams .tile.streaming .camx{display:flex}

/* pass 23: the Cameras module is not a map screen. #map is hidden off Drive
   (css/02) and the wall sits on its own opaque token surface, so nothing of the
   basemap can read through the gaps between the tiles. */
#view-cams .camwrap{background:var(--bg)}
