From ecc8bbba6f9ed30d7f67cc6b49d9e28a61e4a22a Mon Sep 17 00:00:00 2001 From: Suriyakumarvijayanayagam Date: Wed, 30 Sep 2026 16:33:32 +0530 Subject: [PATCH] The app on a laptop reported an engine that was never meant to be there Signing in on a second Mac showed "engine not reachable at http://127.0.0.1:8010" and 0 of 0 cameras, on an account whose shops were running and recognising people the whole time. Nothing was broken: Live() and Cameras() read only the engine on loopback, so the app answered as though the person had never signed in - and camera sync goes through the engine, which is why the count was zero rather than stale. Having no engine is a normal state. A shop PC watches cameras; an owner's laptop, a manager's machine and a second till being set up do not, and all three are signed in to the same estate. Both methods now fall back to head office when loopback fails and somebody is signed in. Loopback is still tried first: a real shop PC must never be shown a minute-old summary when the engine two milliseconds away has the live one. Decisions worth keeping: - Viewing is on the snapshot, not inferred per screen. Three surfaces read it, and a screen that computed it separately is how the shops screen once came out labelled Working, in green, above "2 of 3 cameras not connecting". - fraction_below_gate takes the WORST shop, never an average. 0.10 against 0.73 averages to 0.42 and hides the only shop anyone needs to visit. - A remote camera is flagged, and Edit, Remove and Check placement are withheld. They talk to a camera on a LAN this computer cannot reach, and a button that cannot work is worse than one that is absent. - connected is three states. null is "no shop computer has reported yet" and reads as waiting; false is "Not connecting". A bare false sends somebody to check cabling on a camera nobody has tried to reach. - Snapshots are fetched in Go as data: URIs and cached by snapshot_at. A webview resolves a relative src against wails:// and cannot send the bearer - the problem VisitorImage already solved - and this screen polls every 8 seconds at ~90 KB a camera. - With no engine AND no session, the engine error is still the answer. The person is most likely setting this PC up. The picture is the last snapshot and the banner says so: there is no live video from here, because the engine's MJPEG stream is on the shop PC's loopback behind a router with no inbound route. The LiveHub relay head office uses is the answer to that and is a further step for this client. Verified against production: five arrivals and two cameras parsed from the real API. viewing_test.go covers the fallback, the worst-shop rule, the withheld credentials and that an unchanged snapshot is fetched once across two polls. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01KGcjxF1cNLcuwc3DAPcnfj --- CLAUDE.md | 52 ++++++ desktop/app.go | 112 +++++++++++- .../{index-Be_Iv2Nz.js => index-vAtlvw9l.js} | 18 +- ...{index-lhDNZRcC.css => index-xWw5ie4A.css} | 2 +- desktop/frontend/dist/index.html | 4 +- desktop/frontend/src/styles.css | 14 ++ desktop/frontend/src/views/Cameras.jsx | 66 +++++-- desktop/frontend/src/views/Live.jsx | 36 +++- desktop/internal/cloud/client.go | 117 ++++++++++++ desktop/viewing_test.go | 166 ++++++++++++++++++ 10 files changed, 548 insertions(+), 39 deletions(-) rename desktop/frontend/dist/assets/{index-Be_Iv2Nz.js => index-vAtlvw9l.js} (57%) rename desktop/frontend/dist/assets/{index-lhDNZRcC.css => index-xWw5ie4A.css} (98%) create mode 100644 desktop/viewing_test.go diff --git a/CLAUDE.md b/CLAUDE.md index 759b523..0abd90a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -3307,3 +3307,55 @@ Against real Postgres, on the demo tenant: - HTML, PDF, GIF and empty bodies are all refused as face images: the check is on the magic bytes, never the `Content-Type` header, because this endpoint stores what it is handed and serves it back to a browser. + +## Viewer mode: the app on a computer that is not watching anything + +Signing in on a second Mac showed `engine not reachable at +http://127.0.0.1:8010` and **0 of 0 cameras**, on an account whose shops were +running and recognising people the whole time. Nothing was broken. `App.Live()` +and `App.Cameras()` read **only** `a.local`, so the app answered as though the +person had never signed in — and camera sync goes *through* the engine, which +is why the count was zero rather than merely stale. + +That is the wrong model of what this application is. A shop PC watches +cameras; an owner's laptop, a manager's machine, a second till being set up do +not, and all three are signed in to the same estate. **Having no engine is a +normal state, not a failure**, and the app now says what it can see from where +it is standing instead of reporting the absence of something it does not need. + +Both methods try loopback first and fall back to head office when it fails and +somebody is signed in. The order matters: a real shop PC must never be shown +head office's minute-old summary when the engine two milliseconds away has the +live one. + +- **`Viewing` is on the snapshot, not inferred in the browser.** Three surfaces + read it — the banner, the camera tally, the getting-started panel — and a + screen that computed it separately is how the shops screen once came out + labelled **Working**, in green, directly above *"2 of 3 cameras not + connecting"*. One fact, one place, the same rule as the tray being a client + of `EngineStatus()`. +- **`fraction_below_gate` is the WORST shop, never an average.** 0.10 against + 0.73 averages to 0.42 and hides the only shop anyone needs to visit. Same + rule the heartbeat already follows with `worst_site`. +- **A remote camera is flagged `remote: true`, and the screen withholds Edit, + Remove and Check placement.** Those talk to a camera on a LAN this computer + cannot reach, and an Edit button that cannot work is worse than one that is + absent. The tenant response structurally cannot carry `host`, `username` or + `has_password`, so nothing here can invent them either — a test asserts that. +- **`connected` is three states.** `null` is "no shop computer has reported on + this yet" and reads as *waiting*; `false` is *"Not connecting"*. A bare false + sends somebody to check cabling on a camera nobody has tried to reach. +- **The picture is the last snapshot, and it says so.** There is no live video + here: the engine's MJPEG stream is on the shop PC's loopback behind a router + with no inbound route. Head office's `LiveHub` relay is the answer to that + and is a further step for this client; the banner does not imply otherwise. +- **Snapshots are fetched in Go and passed as `data:` URIs, cached by + `snapshot_at`.** A webview `` resolves a relative src against `wails://` + and cannot send the session's bearer — the same problem `VisitorImage` + already solved — and this screen polls every 8 seconds at ~90 KB a camera, so + re-fetching an unchanged frame is megabytes an hour to redraw the same + picture. Keyed on the server's `snapshot_at`, because a new timestamp is the + only thing that means a new photograph. +- **With no engine AND nobody signed in, the engine error is still the answer.** + There is nothing else to show and the person is most likely setting this PC + up; naming head office there points them at a step they have not reached. diff --git a/desktop/app.go b/desktop/app.go index 8cfa1d8..9238d3d 100644 --- a/desktop/app.go +++ b/desktop/app.go @@ -616,10 +616,43 @@ func (a *App) StopEngine() EngineStatus { // ---------------------------------------------------------------- cameras -- +// Cameras lists this PC's cameras, or the company's if this PC has none of +// its own. +// +// The distinction is load-bearing and the UI is told which it got. A camera +// from the local engine is one THIS machine can reach, edit and stream. One +// from head office is a camera at a shop somewhere else: it has a snapshot +// and a connection state, and it cannot be edited from here because the shop +// PC on that LAN is the only thing that can reach it. Offering an Edit button +// that could not work would be worse than not showing the camera at all. func (a *App) Cameras() ([]map[string]any, error) { ctx, cancel := context.WithTimeout(a.ctx, 15*time.Second) defer cancel() - return a.local.Cameras(ctx) + + cams, err := a.local.Cameras(ctx) + if err == nil { + return cams, nil + } + if !a.cloud.LoggedIn() { + return nil, err + } + remote, rerr := a.cloud.RemoteCameras(ctx) + if rerr != nil { + return nil, err // the local failure is the one worth reporting + } + out := make([]map[string]any, 0, len(remote)) + for _, c := range remote { + out = append(out, map[string]any{ + "id": c.ID, "camera_id": c.CameraID, "label": c.Label, + "site": c.Site, "enabled": c.Enabled, + "connected": c.Connected, "last_seen_at": c.LastSeenAt, + "snapshot": c.Snapshot, "snapshot_at": c.SnapshotAt, + // What the screen keys off to hide Edit, Test and Check: this + // camera is on a network this PC cannot reach. + "remote": true, + }) + } + return out, nil } // DiscoverCameras lists the cameras on this PC's network, so the add-camera @@ -691,20 +724,93 @@ func (a *App) StreamURL(cameraID string) string { type LiveSnapshot struct { Stats map[string]any `json:"stats"` Events []map[string]any `json:"events"` + // Viewing is true when none of this came from an engine on THIS PC. The + // screen must say so: the numbers are the company's, not this machine's, + // and a laptop in a hotel showing "2 cameras live" without that word + // would be claiming to be watching a shop it cannot see. + Viewing bool `json:"viewing"` } +// Live is what the shop PC sees, and falls back to what HEAD OFFICE sees. +// +// A PC with no engine is not necessarily broken - it is somebody signed in on +// a laptop away from the shop, which is the ordinary way an owner looks at +// their estate. Until now that produced "engine not reachable at +// 127.0.0.1:8010", an accurate sentence and a useless one when the reader was +// never expecting an engine on that machine. +// +// The local engine always wins when it is there: it is this shop's own +// ground truth and it is live rather than a heartbeat old. func (a *App) Live() (LiveSnapshot, error) { ctx, cancel := context.WithTimeout(a.ctx, 15*time.Second) defer cancel() + stats, err := a.local.Stats(ctx) + if err == nil { + events, eerr := a.local.Events(ctx, 40) + if eerr == nil { + return LiveSnapshot{Stats: stats, Events: events}, nil + } + } + // No engine here. If nobody is signed in either, the honest answer is + // still the local error - there is nothing else to show and the person + // is most likely setting this PC up. + if !a.cloud.LoggedIn() { + return LiveSnapshot{}, err + } + return a.liveFromCloud(ctx) +} + +// liveFromCloud builds the same shape the Live screen already renders, out of +// the estate's own feed, so the view needs no second code path. +func (a *App) liveFromCloud(ctx context.Context) (LiveSnapshot, error) { + sites, err := a.cloud.Sites(ctx) if err != nil { return LiveSnapshot{}, err } - events, err := a.local.Events(ctx, 40) + arrivals, err := a.cloud.Arrivals(ctx, 40) if err != nil { return LiveSnapshot{}, err } - return LiveSnapshot{Stats: stats, Events: events}, nil + + // The counters are summed across the estate, and fraction_below_gate + // takes the WORST site rather than an average - one badly placed camera + // is a hole in the numbers, and averaging it against three good ones + // hides the only site anyone needs to visit. Same rule the heartbeat + // already follows. + var up, total int + worst := 0.0 + people := map[string]struct{}{} + for _, s := range sites { + up, total = up+s.CamerasUp, total+s.CamerasTotal + if s.FractionBelowGate > worst { + worst = s.FractionBelowGate + } + } + events := make([]map[string]any, 0, len(arrivals)) + for _, v := range arrivals { + if v.VisitorID != "" { + people[v.VisitorID] = struct{}{} + } + events = append(events, map[string]any{ + "type": map[bool]string{true: "person.new", false: "person.seen"}[v.IsNew], + "ts": v.OccurredAt, "camera_id": v.CameraID, + "data": map[string]any{ + "label": v.Label, "ref": v.Ref, "site": v.Site, + "similarity": v.Similarity, "attributes": v.Attributes, + }, + }) + } + return LiveSnapshot{ + Viewing: true, + Events: events, + Stats: map[string]any{ + "cameras": []map[string]any{}, + "gallery": map[string]any{"identities": len(people), "sightings": len(arrivals)}, + "cameras_up": up, "cameras_total": total, + "fraction_below_gate": worst, + }, + }, nil } // ---------------------------------------------------------------- reports -- diff --git a/desktop/frontend/dist/assets/index-Be_Iv2Nz.js b/desktop/frontend/dist/assets/index-vAtlvw9l.js similarity index 57% rename from desktop/frontend/dist/assets/index-Be_Iv2Nz.js rename to desktop/frontend/dist/assets/index-vAtlvw9l.js index 1bb0f97..13aafed 100644 --- a/desktop/frontend/dist/assets/index-Be_Iv2Nz.js +++ b/desktop/frontend/dist/assets/index-vAtlvw9l.js @@ -1,4 +1,4 @@ -(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const l of document.querySelectorAll('link[rel="modulepreload"]'))r(l);new MutationObserver(l=>{for(const s of l)if(s.type==="childList")for(const o of s.addedNodes)o.tagName==="LINK"&&o.rel==="modulepreload"&&r(o)}).observe(document,{childList:!0,subtree:!0});function n(l){const s={};return l.integrity&&(s.integrity=l.integrity),l.referrerPolicy&&(s.referrerPolicy=l.referrerPolicy),l.crossOrigin==="use-credentials"?s.credentials="include":l.crossOrigin==="anonymous"?s.credentials="omit":s.credentials="same-origin",s}function r(l){if(l.ep)return;l.ep=!0;const s=n(l);fetch(l.href,s)}})();function Lc(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var aa={exports:{}},dl={},ua={exports:{}},D={};/** +(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const l of document.querySelectorAll('link[rel="modulepreload"]'))r(l);new MutationObserver(l=>{for(const s of l)if(s.type==="childList")for(const o of s.addedNodes)o.tagName==="LINK"&&o.rel==="modulepreload"&&r(o)}).observe(document,{childList:!0,subtree:!0});function n(l){const s={};return l.integrity&&(s.integrity=l.integrity),l.referrerPolicy&&(s.referrerPolicy=l.referrerPolicy),l.crossOrigin==="use-credentials"?s.credentials="include":l.crossOrigin==="anonymous"?s.credentials="omit":s.credentials="same-origin",s}function r(l){if(l.ep)return;l.ep=!0;const s=n(l);fetch(l.href,s)}})();function Tc(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var ua={exports:{}},dl={},ca={exports:{}},D={};/** * @license React * react.production.min.js * @@ -6,7 +6,7 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - */var nr=Symbol.for("react.element"),Tc=Symbol.for("react.portal"),Mc=Symbol.for("react.fragment"),Rc=Symbol.for("react.strict_mode"),Dc=Symbol.for("react.profiler"),Oc=Symbol.for("react.provider"),Fc=Symbol.for("react.context"),Ic=Symbol.for("react.forward_ref"),$c=Symbol.for("react.suspense"),Uc=Symbol.for("react.memo"),Ac=Symbol.for("react.lazy"),Yi=Symbol.iterator;function Bc(e){return e===null||typeof e!="object"?null:(e=Yi&&e[Yi]||e["@@iterator"],typeof e=="function"?e:null)}var ca={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},da=Object.assign,fa={};function pn(e,t,n){this.props=e,this.context=t,this.refs=fa,this.updater=n||ca}pn.prototype.isReactComponent={};pn.prototype.setState=function(e,t){if(typeof e!="object"&&typeof e!="function"&&e!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,e,t,"setState")};pn.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")};function pa(){}pa.prototype=pn.prototype;function Js(e,t,n){this.props=e,this.context=t,this.refs=fa,this.updater=n||ca}var qs=Js.prototype=new pa;qs.constructor=Js;da(qs,pn.prototype);qs.isPureReactComponent=!0;var Gi=Array.isArray,ha=Object.prototype.hasOwnProperty,bs={current:null},ma={key:!0,ref:!0,__self:!0,__source:!0};function va(e,t,n){var r,l={},s=null,o=null;if(t!=null)for(r in t.ref!==void 0&&(o=t.ref),t.key!==void 0&&(s=""+t.key),t)ha.call(t,r)&&!ma.hasOwnProperty(r)&&(l[r]=t[r]);var a=arguments.length-2;if(a===1)l.children=n;else if(1>>1,ee=_[G];if(0>>1;Gl(zl,R))Ntl(ar,zl)?(_[G]=ar,_[Nt]=R,G=Nt):(_[G]=zl,_[St]=R,G=St);else if(Ntl(ar,R))_[G]=ar,_[Nt]=R,G=Nt;else break e}}return M}function l(_,M){var R=_.sortIndex-M.sortIndex;return R!==0?R:_.id-M.id}if(typeof performance=="object"&&typeof performance.now=="function"){var s=performance;e.unstable_now=function(){return s.now()}}else{var o=Date,a=o.now();e.unstable_now=function(){return o.now()-a}}var u=[],c=[],p=1,v=null,h=3,g=!1,y=!1,w=!1,T=typeof setTimeout=="function"?setTimeout:null,f=typeof clearTimeout=="function"?clearTimeout:null,d=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function m(_){for(var M=n(c);M!==null;){if(M.callback===null)r(c);else if(M.startTime<=_)r(c),M.sortIndex=M.expirationTime,t(u,M);else break;M=n(c)}}function x(_){if(w=!1,m(_),!y)if(n(u)!==null)y=!0,_l(S);else{var M=n(c);M!==null&&Pl(x,M.startTime-_)}}function S(_,M){y=!1,w&&(w=!1,f(E),E=-1),g=!0;var R=h;try{for(m(M),v=n(u);v!==null&&(!(v.expirationTime>M)||_&&!V());){var G=v.callback;if(typeof G=="function"){v.callback=null,h=v.priorityLevel;var ee=G(v.expirationTime<=M);M=e.unstable_now(),typeof ee=="function"?v.callback=ee:v===n(u)&&r(u),m(M)}else r(u);v=n(u)}if(v!==null)var or=!0;else{var St=n(c);St!==null&&Pl(x,St.startTime-M),or=!1}return or}finally{v=null,h=R,g=!1}}var C=!1,k=null,E=-1,z=5,L=-1;function V(){return!(e.unstable_now()-L_||125<_?console.error("forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported"):z=0<_?Math.floor(1e3/_):5},e.unstable_getCurrentPriorityLevel=function(){return h},e.unstable_getFirstCallbackNode=function(){return n(u)},e.unstable_next=function(_){switch(h){case 1:case 2:case 3:var M=3;break;default:M=h}var R=h;h=M;try{return _()}finally{h=R}},e.unstable_pauseExecution=function(){},e.unstable_requestPaint=function(){},e.unstable_runWithPriority=function(_,M){switch(_){case 1:case 2:case 3:case 4:case 5:break;default:_=3}var R=h;h=_;try{return M()}finally{h=R}},e.unstable_scheduleCallback=function(_,M,R){var G=e.unstable_now();switch(typeof R=="object"&&R!==null?(R=R.delay,R=typeof R=="number"&&0G?(_.sortIndex=R,t(c,_),n(u)===null&&_===n(c)&&(w?(f(E),E=-1):w=!0,Pl(x,R-G))):(_.sortIndex=ee,t(u,_),y||g||(y=!0,_l(S))),_},e.unstable_shouldYield=V,e.unstable_wrapCallback=function(_){var M=h;return function(){var R=h;h=M;try{return _.apply(this,arguments)}finally{h=R}}}})(ja);wa.exports=ja;var bc=wa.exports;/** + */(function(e){function t(_,M){var R=_.length;_.push(M);e:for(;0>>1,ee=_[G];if(0>>1;Gl(zl,R))Ntl(ar,zl)?(_[G]=ar,_[Nt]=R,G=Nt):(_[G]=zl,_[St]=R,G=St);else if(Ntl(ar,R))_[G]=ar,_[Nt]=R,G=Nt;else break e}}return M}function l(_,M){var R=_.sortIndex-M.sortIndex;return R!==0?R:_.id-M.id}if(typeof performance=="object"&&typeof performance.now=="function"){var s=performance;e.unstable_now=function(){return s.now()}}else{var o=Date,a=o.now();e.unstable_now=function(){return o.now()-a}}var u=[],c=[],p=1,h=null,v=3,g=!1,x=!1,w=!1,L=typeof setTimeout=="function"?setTimeout:null,f=typeof clearTimeout=="function"?clearTimeout:null,d=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function m(_){for(var M=n(c);M!==null;){if(M.callback===null)r(c);else if(M.startTime<=_)r(c),M.sortIndex=M.expirationTime,t(u,M);else break;M=n(c)}}function y(_){if(w=!1,m(_),!x)if(n(u)!==null)x=!0,_l(S);else{var M=n(c);M!==null&&Pl(y,M.startTime-_)}}function S(_,M){x=!1,w&&(w=!1,f(E),E=-1),g=!0;var R=v;try{for(m(M),h=n(u);h!==null&&(!(h.expirationTime>M)||_&&!V());){var G=h.callback;if(typeof G=="function"){h.callback=null,v=h.priorityLevel;var ee=G(h.expirationTime<=M);M=e.unstable_now(),typeof ee=="function"?h.callback=ee:h===n(u)&&r(u),m(M)}else r(u);h=n(u)}if(h!==null)var or=!0;else{var St=n(c);St!==null&&Pl(y,St.startTime-M),or=!1}return or}finally{h=null,v=R,g=!1}}var C=!1,k=null,E=-1,z=5,T=-1;function V(){return!(e.unstable_now()-T_||125<_?console.error("forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported"):z=0<_?Math.floor(1e3/_):5},e.unstable_getCurrentPriorityLevel=function(){return v},e.unstable_getFirstCallbackNode=function(){return n(u)},e.unstable_next=function(_){switch(v){case 1:case 2:case 3:var M=3;break;default:M=v}var R=v;v=M;try{return _()}finally{v=R}},e.unstable_pauseExecution=function(){},e.unstable_requestPaint=function(){},e.unstable_runWithPriority=function(_,M){switch(_){case 1:case 2:case 3:case 4:case 5:break;default:_=3}var R=v;v=_;try{return M()}finally{v=R}},e.unstable_scheduleCallback=function(_,M,R){var G=e.unstable_now();switch(typeof R=="object"&&R!==null?(R=R.delay,R=typeof R=="number"&&0G?(_.sortIndex=R,t(c,_),n(u)===null&&_===n(c)&&(w?(f(E),E=-1):w=!0,Pl(y,R-G))):(_.sortIndex=ee,t(u,_),x||g||(x=!0,_l(S))),_},e.unstable_shouldYield=V,e.unstable_wrapCallback=function(_){var M=v;return function(){var R=v;v=M;try{return _.apply(this,arguments)}finally{v=R}}}})(ka);ja.exports=ka;var bc=ja.exports;/** * @license React * react-dom.production.min.js * @@ -30,11 +30,11 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - */var ed=P,Ne=bc;function j(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),rs=Object.prototype.hasOwnProperty,td=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,Zi={},Ji={};function nd(e){return rs.call(Ji,e)?!0:rs.call(Zi,e)?!1:td.test(e)?Ji[e]=!0:(Zi[e]=!0,!1)}function rd(e,t,n,r){if(n!==null&&n.type===0)return!1;switch(typeof t){case"function":case"symbol":return!0;case"boolean":return r?!1:n!==null?!n.acceptsBooleans:(e=e.toLowerCase().slice(0,5),e!=="data-"&&e!=="aria-");default:return!1}}function ld(e,t,n,r){if(t===null||typeof t>"u"||rd(e,t,n,r))return!0;if(r)return!1;if(n!==null)switch(n.type){case 3:return!t;case 4:return t===!1;case 5:return isNaN(t);case 6:return isNaN(t)||1>t}return!1}function he(e,t,n,r,l,s,o){this.acceptsBooleans=t===2||t===3||t===4,this.attributeName=r,this.attributeNamespace=l,this.mustUseProperty=n,this.propertyName=e,this.type=t,this.sanitizeURL=s,this.removeEmptyString=o}var se={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){se[e]=new he(e,0,!1,e,null,!1,!1)});[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0];se[t]=new he(t,1,!1,e[1],null,!1,!1)});["contentEditable","draggable","spellCheck","value"].forEach(function(e){se[e]=new he(e,2,!1,e.toLowerCase(),null,!1,!1)});["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){se[e]=new he(e,2,!1,e,null,!1,!1)});"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(e){se[e]=new he(e,3,!1,e.toLowerCase(),null,!1,!1)});["checked","multiple","muted","selected"].forEach(function(e){se[e]=new he(e,3,!0,e,null,!1,!1)});["capture","download"].forEach(function(e){se[e]=new he(e,4,!1,e,null,!1,!1)});["cols","rows","size","span"].forEach(function(e){se[e]=new he(e,6,!1,e,null,!1,!1)});["rowSpan","start"].forEach(function(e){se[e]=new he(e,5,!1,e.toLowerCase(),null,!1,!1)});var ti=/[\-:]([a-z])/g;function ni(e){return e[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(e){var t=e.replace(ti,ni);se[t]=new he(t,1,!1,e,null,!1,!1)});"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(e){var t=e.replace(ti,ni);se[t]=new he(t,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)});["xml:base","xml:lang","xml:space"].forEach(function(e){var t=e.replace(ti,ni);se[t]=new he(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)});["tabIndex","crossOrigin"].forEach(function(e){se[e]=new he(e,1,!1,e.toLowerCase(),null,!1,!1)});se.xlinkHref=new he("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1);["src","href","action","formAction"].forEach(function(e){se[e]=new he(e,1,!1,e.toLowerCase(),null,!0,!0)});function ri(e,t,n,r){var l=se.hasOwnProperty(t)?se[t]:null;(l!==null?l.type!==0:r||!(2"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),rs=Object.prototype.hasOwnProperty,td=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,Ji={},qi={};function nd(e){return rs.call(qi,e)?!0:rs.call(Ji,e)?!1:td.test(e)?qi[e]=!0:(Ji[e]=!0,!1)}function rd(e,t,n,r){if(n!==null&&n.type===0)return!1;switch(typeof t){case"function":case"symbol":return!0;case"boolean":return r?!1:n!==null?!n.acceptsBooleans:(e=e.toLowerCase().slice(0,5),e!=="data-"&&e!=="aria-");default:return!1}}function ld(e,t,n,r){if(t===null||typeof t>"u"||rd(e,t,n,r))return!0;if(r)return!1;if(n!==null)switch(n.type){case 3:return!t;case 4:return t===!1;case 5:return isNaN(t);case 6:return isNaN(t)||1>t}return!1}function he(e,t,n,r,l,s,o){this.acceptsBooleans=t===2||t===3||t===4,this.attributeName=r,this.attributeNamespace=l,this.mustUseProperty=n,this.propertyName=e,this.type=t,this.sanitizeURL=s,this.removeEmptyString=o}var se={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){se[e]=new he(e,0,!1,e,null,!1,!1)});[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0];se[t]=new he(t,1,!1,e[1],null,!1,!1)});["contentEditable","draggable","spellCheck","value"].forEach(function(e){se[e]=new he(e,2,!1,e.toLowerCase(),null,!1,!1)});["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){se[e]=new he(e,2,!1,e,null,!1,!1)});"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(e){se[e]=new he(e,3,!1,e.toLowerCase(),null,!1,!1)});["checked","multiple","muted","selected"].forEach(function(e){se[e]=new he(e,3,!0,e,null,!1,!1)});["capture","download"].forEach(function(e){se[e]=new he(e,4,!1,e,null,!1,!1)});["cols","rows","size","span"].forEach(function(e){se[e]=new he(e,6,!1,e,null,!1,!1)});["rowSpan","start"].forEach(function(e){se[e]=new he(e,5,!1,e.toLowerCase(),null,!1,!1)});var ti=/[\-:]([a-z])/g;function ni(e){return e[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(e){var t=e.replace(ti,ni);se[t]=new he(t,1,!1,e,null,!1,!1)});"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(e){var t=e.replace(ti,ni);se[t]=new he(t,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)});["xml:base","xml:lang","xml:space"].forEach(function(e){var t=e.replace(ti,ni);se[t]=new he(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)});["tabIndex","crossOrigin"].forEach(function(e){se[e]=new he(e,1,!1,e.toLowerCase(),null,!1,!1)});se.xlinkHref=new he("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1);["src","href","action","formAction"].forEach(function(e){se[e]=new he(e,1,!1,e.toLowerCase(),null,!0,!0)});function ri(e,t,n,r){var l=se.hasOwnProperty(t)?se[t]:null;(l!==null?l.type!==0:r||!(2a||l[o]!==s[a]){var u=` -`+l[o].replace(" at new "," at ");return e.displayName&&u.includes("")&&(u=u.replace("",e.displayName)),u}while(1<=o&&0<=a);break}}}finally{Ml=!1,Error.prepareStackTrace=n}return(e=e?e.displayName||e.name:"")?En(e):""}function sd(e){switch(e.tag){case 5:return En(e.type);case 16:return En("Lazy");case 13:return En("Suspense");case 19:return En("SuspenseList");case 0:case 2:case 15:return e=Rl(e.type,!1),e;case 11:return e=Rl(e.type.render,!1),e;case 1:return e=Rl(e.type,!0),e;default:return""}}function os(e){if(e==null)return null;if(typeof e=="function")return e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case Bt:return"Fragment";case At:return"Portal";case ls:return"Profiler";case li:return"StrictMode";case ss:return"Suspense";case is:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case Na:return(e.displayName||"Context")+".Consumer";case Sa:return(e._context.displayName||"Context")+".Provider";case si:var t=e.render;return e=e.displayName,e||(e=t.displayName||t.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case ii:return t=e.displayName||null,t!==null?t:os(e.type)||"Memo";case rt:t=e._payload,e=e._init;try{return os(e(t))}catch{}}return null}function id(e){var t=e.type;switch(e.tag){case 24:return"Cache";case 9:return(t.displayName||"Context")+".Consumer";case 10:return(t._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return e=t.render,e=e.displayName||e.name||"",t.displayName||(e!==""?"ForwardRef("+e+")":"ForwardRef");case 7:return"Fragment";case 5:return t;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return os(t);case 8:return t===li?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof t=="function")return t.displayName||t.name||null;if(typeof t=="string")return t}return null}function yt(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function Ea(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function od(e){var t=Ea(e)?"checked":"value",n=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),r=""+e[t];if(!e.hasOwnProperty(t)&&typeof n<"u"&&typeof n.get=="function"&&typeof n.set=="function"){var l=n.get,s=n.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return l.call(this)},set:function(o){r=""+o,s.call(this,o)}}),Object.defineProperty(e,t,{enumerable:n.enumerable}),{getValue:function(){return r},setValue:function(o){r=""+o},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function dr(e){e._valueTracker||(e._valueTracker=od(e))}function _a(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var n=t.getValue(),r="";return e&&(r=Ea(e)?e.checked?"true":"false":e.value),e=r,e!==n?(t.setValue(e),!0):!1}function Br(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}function as(e,t){var n=t.checked;return K({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:n??e._wrapperState.initialChecked})}function bi(e,t){var n=t.defaultValue==null?"":t.defaultValue,r=t.checked!=null?t.checked:t.defaultChecked;n=yt(t.value!=null?t.value:n),e._wrapperState={initialChecked:r,initialValue:n,controlled:t.type==="checkbox"||t.type==="radio"?t.checked!=null:t.value!=null}}function Pa(e,t){t=t.checked,t!=null&&ri(e,"checked",t,!1)}function us(e,t){Pa(e,t);var n=yt(t.value),r=t.type;if(n!=null)r==="number"?(n===0&&e.value===""||e.value!=n)&&(e.value=""+n):e.value!==""+n&&(e.value=""+n);else if(r==="submit"||r==="reset"){e.removeAttribute("value");return}t.hasOwnProperty("value")?cs(e,t.type,n):t.hasOwnProperty("defaultValue")&&cs(e,t.type,yt(t.defaultValue)),t.checked==null&&t.defaultChecked!=null&&(e.defaultChecked=!!t.defaultChecked)}function eo(e,t,n){if(t.hasOwnProperty("value")||t.hasOwnProperty("defaultValue")){var r=t.type;if(!(r!=="submit"&&r!=="reset"||t.value!==void 0&&t.value!==null))return;t=""+e._wrapperState.initialValue,n||t===e.value||(e.value=t),e.defaultValue=t}n=e.name,n!==""&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,n!==""&&(e.name=n)}function cs(e,t,n){(t!=="number"||Br(e.ownerDocument)!==e)&&(n==null?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+n&&(e.defaultValue=""+n))}var _n=Array.isArray;function qt(e,t,n,r){if(e=e.options,t){t={};for(var l=0;l"+t.valueOf().toString()+"",t=fr.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}});function An(e,t){if(t){var n=e.firstChild;if(n&&n===e.lastChild&&n.nodeType===3){n.nodeValue=t;return}}e.textContent=t}var Ln={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},ad=["Webkit","ms","Moz","O"];Object.keys(Ln).forEach(function(e){ad.forEach(function(t){t=t+e.charAt(0).toUpperCase()+e.substring(1),Ln[t]=Ln[e]})});function Ma(e,t,n){return t==null||typeof t=="boolean"||t===""?"":n||typeof t!="number"||t===0||Ln.hasOwnProperty(e)&&Ln[e]?(""+t).trim():t+"px"}function Ra(e,t){e=e.style;for(var n in t)if(t.hasOwnProperty(n)){var r=n.indexOf("--")===0,l=Ma(n,t[n],r);n==="float"&&(n="cssFloat"),r?e.setProperty(n,l):e[n]=l}}var ud=K({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function ps(e,t){if(t){if(ud[e]&&(t.children!=null||t.dangerouslySetInnerHTML!=null))throw Error(j(137,e));if(t.dangerouslySetInnerHTML!=null){if(t.children!=null)throw Error(j(60));if(typeof t.dangerouslySetInnerHTML!="object"||!("__html"in t.dangerouslySetInnerHTML))throw Error(j(61))}if(t.style!=null&&typeof t.style!="object")throw Error(j(62))}}function hs(e,t){if(e.indexOf("-")===-1)return typeof t.is=="string";switch(e){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var ms=null;function oi(e){return e=e.target||e.srcElement||window,e.correspondingUseElement&&(e=e.correspondingUseElement),e.nodeType===3?e.parentNode:e}var vs=null,bt=null,en=null;function ro(e){if(e=sr(e)){if(typeof vs!="function")throw Error(j(280));var t=e.stateNode;t&&(t=vl(t),vs(e.stateNode,e.type,t))}}function Da(e){bt?en?en.push(e):en=[e]:bt=e}function Oa(){if(bt){var e=bt,t=en;if(en=bt=null,ro(e),t)for(e=0;e>>=0,e===0?32:31-(wd(e)/jd|0)|0}var pr=64,hr=4194304;function Pn(e){switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return e&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return e}}function Qr(e,t){var n=e.pendingLanes;if(n===0)return 0;var r=0,l=e.suspendedLanes,s=e.pingedLanes,o=n&268435455;if(o!==0){var a=o&~l;a!==0?r=Pn(a):(s&=o,s!==0&&(r=Pn(s)))}else o=n&~l,o!==0?r=Pn(o):s!==0&&(r=Pn(s));if(r===0)return 0;if(t!==0&&t!==r&&!(t&l)&&(l=r&-r,s=t&-t,l>=s||l===16&&(s&4194240)!==0))return t;if(r&4&&(r|=n&16),t=e.entangledLanes,t!==0)for(e=e.entanglements,t&=r;0n;n++)t.push(e);return t}function rr(e,t,n){e.pendingLanes|=t,t!==536870912&&(e.suspendedLanes=0,e.pingedLanes=0),e=e.eventTimes,t=31-Ie(t),e[t]=n}function Cd(e,t){var n=e.pendingLanes&~t;e.pendingLanes=t,e.suspendedLanes=0,e.pingedLanes=0,e.expiredLanes&=t,e.mutableReadLanes&=t,e.entangledLanes&=t,t=e.entanglements;var r=e.eventTimes;for(e=e.expirationTimes;0=Mn),po=" ",ho=!1;function tu(e,t){switch(e){case"keyup":return bd.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function nu(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var Vt=!1;function tf(e,t){switch(e){case"compositionend":return nu(t);case"keypress":return t.which!==32?null:(ho=!0,po);case"textInput":return e=t.data,e===po&&ho?null:e;default:return null}}function nf(e,t){if(Vt)return e==="compositionend"||!mi&&tu(e,t)?(e=ba(),Mr=fi=ot=null,Vt=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:n,offset:t-e};e=r}e:{for(;n;){if(n.nextSibling){n=n.nextSibling;break e}n=n.parentNode}n=void 0}n=yo(n)}}function iu(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?iu(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function ou(){for(var e=window,t=Br();t instanceof e.HTMLIFrameElement;){try{var n=typeof t.contentWindow.location.href=="string"}catch{n=!1}if(n)e=t.contentWindow;else break;t=Br(e.document)}return t}function vi(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}function ff(e){var t=ou(),n=e.focusedElem,r=e.selectionRange;if(t!==n&&n&&n.ownerDocument&&iu(n.ownerDocument.documentElement,n)){if(r!==null&&vi(n)){if(t=r.start,e=r.end,e===void 0&&(e=t),"selectionStart"in n)n.selectionStart=t,n.selectionEnd=Math.min(e,n.value.length);else if(e=(t=n.ownerDocument||document)&&t.defaultView||window,e.getSelection){e=e.getSelection();var l=n.textContent.length,s=Math.min(r.start,l);r=r.end===void 0?s:Math.min(r.end,l),!e.extend&&s>r&&(l=r,r=s,s=l),l=xo(n,s);var o=xo(n,r);l&&o&&(e.rangeCount!==1||e.anchorNode!==l.node||e.anchorOffset!==l.offset||e.focusNode!==o.node||e.focusOffset!==o.offset)&&(t=t.createRange(),t.setStart(l.node,l.offset),e.removeAllRanges(),s>r?(e.addRange(t),e.extend(o.node,o.offset)):(t.setEnd(o.node,o.offset),e.addRange(t)))}}for(t=[],e=n;e=e.parentNode;)e.nodeType===1&&t.push({element:e,left:e.scrollLeft,top:e.scrollTop});for(typeof n.focus=="function"&&n.focus(),n=0;n=document.documentMode,Ht=null,ks=null,Dn=null,Ss=!1;function wo(e,t,n){var r=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;Ss||Ht==null||Ht!==Br(r)||(r=Ht,"selectionStart"in r&&vi(r)?r={start:r.selectionStart,end:r.selectionEnd}:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection(),r={anchorNode:r.anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset}),Dn&&Kn(Dn,r)||(Dn=r,r=Gr(ks,"onSelect"),0Kt||(e.current=zs[Kt],zs[Kt]=null,Kt--)}function U(e,t){Kt++,zs[Kt]=e.current,e.current=t}var xt={},ue=jt(xt),ge=jt(!1),Mt=xt;function on(e,t){var n=e.type.contextTypes;if(!n)return xt;var r=e.stateNode;if(r&&r.__reactInternalMemoizedUnmaskedChildContext===t)return r.__reactInternalMemoizedMaskedChildContext;var l={},s;for(s in n)l[s]=t[s];return r&&(e=e.stateNode,e.__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=l),l}function ye(e){return e=e.childContextTypes,e!=null}function Zr(){B(ge),B(ue)}function _o(e,t,n){if(ue.current!==xt)throw Error(j(168));U(ue,t),U(ge,n)}function vu(e,t,n){var r=e.stateNode;if(t=t.childContextTypes,typeof r.getChildContext!="function")return n;r=r.getChildContext();for(var l in r)if(!(l in t))throw Error(j(108,id(e)||"Unknown",l));return K({},n,r)}function Jr(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||xt,Mt=ue.current,U(ue,e),U(ge,ge.current),!0}function Po(e,t,n){var r=e.stateNode;if(!r)throw Error(j(169));n?(e=vu(e,t,Mt),r.__reactInternalMemoizedMergedChildContext=e,B(ge),B(ue),U(ue,e)):B(ge),U(ge,n)}var Ke=null,gl=!1,Yl=!1;function gu(e){Ke===null?Ke=[e]:Ke.push(e)}function Nf(e){gl=!0,gu(e)}function kt(){if(!Yl&&Ke!==null){Yl=!0;var e=0,t=I;try{var n=Ke;for(I=1;e>=o,l-=o,Ye=1<<32-Ie(t)+l|n<E?(z=k,k=null):z=k.sibling;var L=h(f,k,m[E],x);if(L===null){k===null&&(k=z);break}e&&k&&L.alternate===null&&t(f,k),d=s(L,d,E),C===null?S=L:C.sibling=L,C=L,k=z}if(E===m.length)return n(f,k),H&&Ct(f,E),S;if(k===null){for(;EE?(z=k,k=null):z=k.sibling;var V=h(f,k,L.value,x);if(V===null){k===null&&(k=z);break}e&&k&&V.alternate===null&&t(f,k),d=s(V,d,E),C===null?S=V:C.sibling=V,C=V,k=z}if(L.done)return n(f,k),H&&Ct(f,E),S;if(k===null){for(;!L.done;E++,L=m.next())L=v(f,L.value,x),L!==null&&(d=s(L,d,E),C===null?S=L:C.sibling=L,C=L);return H&&Ct(f,E),S}for(k=r(f,k);!L.done;E++,L=m.next())L=g(k,f,E,L.value,x),L!==null&&(e&&L.alternate!==null&&k.delete(L.key===null?E:L.key),d=s(L,d,E),C===null?S=L:C.sibling=L,C=L);return e&&k.forEach(function(vn){return t(f,vn)}),H&&Ct(f,E),S}function T(f,d,m,x){if(typeof m=="object"&&m!==null&&m.type===Bt&&m.key===null&&(m=m.props.children),typeof m=="object"&&m!==null){switch(m.$$typeof){case cr:e:{for(var S=m.key,C=d;C!==null;){if(C.key===S){if(S=m.type,S===Bt){if(C.tag===7){n(f,C.sibling),d=l(C,m.props.children),d.return=f,f=d;break e}}else if(C.elementType===S||typeof S=="object"&&S!==null&&S.$$typeof===rt&&To(S)===C.type){n(f,C.sibling),d=l(C,m.props),d.ref=Sn(f,C,m),d.return=f,f=d;break e}n(f,C);break}else t(f,C);C=C.sibling}m.type===Bt?(d=Tt(m.props.children,f.mode,x,m.key),d.return=f,f=d):(x=Ar(m.type,m.key,m.props,null,f.mode,x),x.ref=Sn(f,d,m),x.return=f,f=x)}return o(f);case At:e:{for(C=m.key;d!==null;){if(d.key===C)if(d.tag===4&&d.stateNode.containerInfo===m.containerInfo&&d.stateNode.implementation===m.implementation){n(f,d.sibling),d=l(d,m.children||[]),d.return=f,f=d;break e}else{n(f,d);break}else t(f,d);d=d.sibling}d=ts(m,f.mode,x),d.return=f,f=d}return o(f);case rt:return C=m._init,T(f,d,C(m._payload),x)}if(_n(m))return y(f,d,m,x);if(yn(m))return w(f,d,m,x);jr(f,m)}return typeof m=="string"&&m!==""||typeof m=="number"?(m=""+m,d!==null&&d.tag===6?(n(f,d.sibling),d=l(d,m),d.return=f,f=d):(n(f,d),d=es(m,f.mode,x),d.return=f,f=d),o(f)):n(f,d)}return T}var un=ju(!0),ku=ju(!1),el=jt(null),tl=null,Xt=null,wi=null;function ji(){wi=Xt=tl=null}function ki(e){var t=el.current;B(el),e._currentValue=t}function Ms(e,t,n){for(;e!==null;){var r=e.alternate;if((e.childLanes&t)!==t?(e.childLanes|=t,r!==null&&(r.childLanes|=t)):r!==null&&(r.childLanes&t)!==t&&(r.childLanes|=t),e===n)break;e=e.return}}function nn(e,t){tl=e,wi=Xt=null,e=e.dependencies,e!==null&&e.firstContext!==null&&(e.lanes&t&&(ve=!0),e.firstContext=null)}function Te(e){var t=e._currentValue;if(wi!==e)if(e={context:e,memoizedValue:t,next:null},Xt===null){if(tl===null)throw Error(j(308));Xt=e,tl.dependencies={lanes:0,firstContext:e}}else Xt=Xt.next=e;return t}var Pt=null;function Si(e){Pt===null?Pt=[e]:Pt.push(e)}function Su(e,t,n,r){var l=t.interleaved;return l===null?(n.next=n,Si(t)):(n.next=l.next,l.next=n),t.interleaved=n,qe(e,r)}function qe(e,t){e.lanes|=t;var n=e.alternate;for(n!==null&&(n.lanes|=t),n=e,e=e.return;e!==null;)e.childLanes|=t,n=e.alternate,n!==null&&(n.childLanes|=t),n=e,e=e.return;return n.tag===3?n.stateNode:null}var lt=!1;function Ni(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function Nu(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,effects:e.effects})}function Xe(e,t){return{eventTime:e,lane:t,tag:0,payload:null,callback:null,next:null}}function pt(e,t,n){var r=e.updateQueue;if(r===null)return null;if(r=r.shared,O&2){var l=r.pending;return l===null?t.next=t:(t.next=l.next,l.next=t),r.pending=t,qe(e,n)}return l=r.interleaved,l===null?(t.next=t,Si(r)):(t.next=l.next,l.next=t),r.interleaved=t,qe(e,n)}function Dr(e,t,n){if(t=t.updateQueue,t!==null&&(t=t.shared,(n&4194240)!==0)){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,ui(e,n)}}function Mo(e,t){var n=e.updateQueue,r=e.alternate;if(r!==null&&(r=r.updateQueue,n===r)){var l=null,s=null;if(n=n.firstBaseUpdate,n!==null){do{var o={eventTime:n.eventTime,lane:n.lane,tag:n.tag,payload:n.payload,callback:n.callback,next:null};s===null?l=s=o:s=s.next=o,n=n.next}while(n!==null);s===null?l=s=t:s=s.next=t}else l=s=t;n={baseState:r.baseState,firstBaseUpdate:l,lastBaseUpdate:s,shared:r.shared,effects:r.effects},e.updateQueue=n;return}e=n.lastBaseUpdate,e===null?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}function nl(e,t,n,r){var l=e.updateQueue;lt=!1;var s=l.firstBaseUpdate,o=l.lastBaseUpdate,a=l.shared.pending;if(a!==null){l.shared.pending=null;var u=a,c=u.next;u.next=null,o===null?s=c:o.next=c,o=u;var p=e.alternate;p!==null&&(p=p.updateQueue,a=p.lastBaseUpdate,a!==o&&(a===null?p.firstBaseUpdate=c:a.next=c,p.lastBaseUpdate=u))}if(s!==null){var v=l.baseState;o=0,p=c=u=null,a=s;do{var h=a.lane,g=a.eventTime;if((r&h)===h){p!==null&&(p=p.next={eventTime:g,lane:0,tag:a.tag,payload:a.payload,callback:a.callback,next:null});e:{var y=e,w=a;switch(h=t,g=n,w.tag){case 1:if(y=w.payload,typeof y=="function"){v=y.call(g,v,h);break e}v=y;break e;case 3:y.flags=y.flags&-65537|128;case 0:if(y=w.payload,h=typeof y=="function"?y.call(g,v,h):y,h==null)break e;v=K({},v,h);break e;case 2:lt=!0}}a.callback!==null&&a.lane!==0&&(e.flags|=64,h=l.effects,h===null?l.effects=[a]:h.push(a))}else g={eventTime:g,lane:h,tag:a.tag,payload:a.payload,callback:a.callback,next:null},p===null?(c=p=g,u=v):p=p.next=g,o|=h;if(a=a.next,a===null){if(a=l.shared.pending,a===null)break;h=a,a=h.next,h.next=null,l.lastBaseUpdate=h,l.shared.pending=null}}while(!0);if(p===null&&(u=v),l.baseState=u,l.firstBaseUpdate=c,l.lastBaseUpdate=p,t=l.shared.interleaved,t!==null){l=t;do o|=l.lane,l=l.next;while(l!==t)}else s===null&&(l.shared.lanes=0);Ot|=o,e.lanes=o,e.memoizedState=v}}function Ro(e,t,n){if(e=t.effects,t.effects=null,e!==null)for(t=0;tn?n:4,e(!0);var r=Xl.transition;Xl.transition={};try{e(!1),t()}finally{I=n,Xl.transition=r}}function Bu(){return Me().memoizedState}function Pf(e,t,n){var r=mt(e);if(n={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null},Vu(e))Hu(t,n);else if(n=Su(e,t,n,r),n!==null){var l=de();$e(n,e,r,l),Wu(n,t,r)}}function zf(e,t,n){var r=mt(e),l={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null};if(Vu(e))Hu(t,l);else{var s=e.alternate;if(e.lanes===0&&(s===null||s.lanes===0)&&(s=t.lastRenderedReducer,s!==null))try{var o=t.lastRenderedState,a=s(o,n);if(l.hasEagerState=!0,l.eagerState=a,Ue(a,o)){var u=t.interleaved;u===null?(l.next=l,Si(t)):(l.next=u.next,u.next=l),t.interleaved=l;return}}catch{}finally{}n=Su(e,t,l,r),n!==null&&(l=de(),$e(n,e,r,l),Wu(n,t,r))}}function Vu(e){var t=e.alternate;return e===Q||t!==null&&t===Q}function Hu(e,t){On=ll=!0;var n=e.pending;n===null?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function Wu(e,t,n){if(n&4194240){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,ui(e,n)}}var sl={readContext:Te,useCallback:ie,useContext:ie,useEffect:ie,useImperativeHandle:ie,useInsertionEffect:ie,useLayoutEffect:ie,useMemo:ie,useReducer:ie,useRef:ie,useState:ie,useDebugValue:ie,useDeferredValue:ie,useTransition:ie,useMutableSource:ie,useSyncExternalStore:ie,useId:ie,unstable_isNewReconciler:!1},Lf={readContext:Te,useCallback:function(e,t){return Be().memoizedState=[e,t===void 0?null:t],e},useContext:Te,useEffect:Oo,useImperativeHandle:function(e,t,n){return n=n!=null?n.concat([e]):null,Fr(4194308,4,Fu.bind(null,t,e),n)},useLayoutEffect:function(e,t){return Fr(4194308,4,e,t)},useInsertionEffect:function(e,t){return Fr(4,2,e,t)},useMemo:function(e,t){var n=Be();return t=t===void 0?null:t,e=e(),n.memoizedState=[e,t],e},useReducer:function(e,t,n){var r=Be();return t=n!==void 0?n(t):t,r.memoizedState=r.baseState=t,e={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:t},r.queue=e,e=e.dispatch=Pf.bind(null,Q,e),[r.memoizedState,e]},useRef:function(e){var t=Be();return e={current:e},t.memoizedState=e},useState:Do,useDebugValue:Mi,useDeferredValue:function(e){return Be().memoizedState=e},useTransition:function(){var e=Do(!1),t=e[0];return e=_f.bind(null,e[1]),Be().memoizedState=e,[t,e]},useMutableSource:function(){},useSyncExternalStore:function(e,t,n){var r=Q,l=Be();if(H){if(n===void 0)throw Error(j(407));n=n()}else{if(n=t(),ne===null)throw Error(j(349));Dt&30||Pu(r,t,n)}l.memoizedState=n;var s={value:n,getSnapshot:t};return l.queue=s,Oo(Lu.bind(null,r,s,e),[e]),r.flags|=2048,er(9,zu.bind(null,r,s,n,t),void 0,null),n},useId:function(){var e=Be(),t=ne.identifierPrefix;if(H){var n=Ge,r=Ye;n=(r&~(1<<32-Ie(r)-1)).toString(32)+n,t=":"+t+"R"+n,n=qn++,0")&&(u=u.replace("",e.displayName)),u}while(1<=o&&0<=a);break}}}finally{Ml=!1,Error.prepareStackTrace=n}return(e=e?e.displayName||e.name:"")?En(e):""}function sd(e){switch(e.tag){case 5:return En(e.type);case 16:return En("Lazy");case 13:return En("Suspense");case 19:return En("SuspenseList");case 0:case 2:case 15:return e=Rl(e.type,!1),e;case 11:return e=Rl(e.type.render,!1),e;case 1:return e=Rl(e.type,!0),e;default:return""}}function os(e){if(e==null)return null;if(typeof e=="function")return e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case Bt:return"Fragment";case At:return"Portal";case ls:return"Profiler";case li:return"StrictMode";case ss:return"Suspense";case is:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case Ca:return(e.displayName||"Context")+".Consumer";case Na:return(e._context.displayName||"Context")+".Provider";case si:var t=e.render;return e=e.displayName,e||(e=t.displayName||t.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case ii:return t=e.displayName||null,t!==null?t:os(e.type)||"Memo";case rt:t=e._payload,e=e._init;try{return os(e(t))}catch{}}return null}function id(e){var t=e.type;switch(e.tag){case 24:return"Cache";case 9:return(t.displayName||"Context")+".Consumer";case 10:return(t._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return e=t.render,e=e.displayName||e.name||"",t.displayName||(e!==""?"ForwardRef("+e+")":"ForwardRef");case 7:return"Fragment";case 5:return t;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return os(t);case 8:return t===li?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof t=="function")return t.displayName||t.name||null;if(typeof t=="string")return t}return null}function yt(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function _a(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function od(e){var t=_a(e)?"checked":"value",n=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),r=""+e[t];if(!e.hasOwnProperty(t)&&typeof n<"u"&&typeof n.get=="function"&&typeof n.set=="function"){var l=n.get,s=n.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return l.call(this)},set:function(o){r=""+o,s.call(this,o)}}),Object.defineProperty(e,t,{enumerable:n.enumerable}),{getValue:function(){return r},setValue:function(o){r=""+o},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function dr(e){e._valueTracker||(e._valueTracker=od(e))}function Pa(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var n=t.getValue(),r="";return e&&(r=_a(e)?e.checked?"true":"false":e.value),e=r,e!==n?(t.setValue(e),!0):!1}function Br(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}function as(e,t){var n=t.checked;return K({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:n??e._wrapperState.initialChecked})}function eo(e,t){var n=t.defaultValue==null?"":t.defaultValue,r=t.checked!=null?t.checked:t.defaultChecked;n=yt(t.value!=null?t.value:n),e._wrapperState={initialChecked:r,initialValue:n,controlled:t.type==="checkbox"||t.type==="radio"?t.checked!=null:t.value!=null}}function za(e,t){t=t.checked,t!=null&&ri(e,"checked",t,!1)}function us(e,t){za(e,t);var n=yt(t.value),r=t.type;if(n!=null)r==="number"?(n===0&&e.value===""||e.value!=n)&&(e.value=""+n):e.value!==""+n&&(e.value=""+n);else if(r==="submit"||r==="reset"){e.removeAttribute("value");return}t.hasOwnProperty("value")?cs(e,t.type,n):t.hasOwnProperty("defaultValue")&&cs(e,t.type,yt(t.defaultValue)),t.checked==null&&t.defaultChecked!=null&&(e.defaultChecked=!!t.defaultChecked)}function to(e,t,n){if(t.hasOwnProperty("value")||t.hasOwnProperty("defaultValue")){var r=t.type;if(!(r!=="submit"&&r!=="reset"||t.value!==void 0&&t.value!==null))return;t=""+e._wrapperState.initialValue,n||t===e.value||(e.value=t),e.defaultValue=t}n=e.name,n!==""&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,n!==""&&(e.name=n)}function cs(e,t,n){(t!=="number"||Br(e.ownerDocument)!==e)&&(n==null?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+n&&(e.defaultValue=""+n))}var _n=Array.isArray;function qt(e,t,n,r){if(e=e.options,t){t={};for(var l=0;l"+t.valueOf().toString()+"",t=fr.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}});function An(e,t){if(t){var n=e.firstChild;if(n&&n===e.lastChild&&n.nodeType===3){n.nodeValue=t;return}}e.textContent=t}var Tn={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},ad=["Webkit","ms","Moz","O"];Object.keys(Tn).forEach(function(e){ad.forEach(function(t){t=t+e.charAt(0).toUpperCase()+e.substring(1),Tn[t]=Tn[e]})});function Ra(e,t,n){return t==null||typeof t=="boolean"||t===""?"":n||typeof t!="number"||t===0||Tn.hasOwnProperty(e)&&Tn[e]?(""+t).trim():t+"px"}function Da(e,t){e=e.style;for(var n in t)if(t.hasOwnProperty(n)){var r=n.indexOf("--")===0,l=Ra(n,t[n],r);n==="float"&&(n="cssFloat"),r?e.setProperty(n,l):e[n]=l}}var ud=K({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function ps(e,t){if(t){if(ud[e]&&(t.children!=null||t.dangerouslySetInnerHTML!=null))throw Error(j(137,e));if(t.dangerouslySetInnerHTML!=null){if(t.children!=null)throw Error(j(60));if(typeof t.dangerouslySetInnerHTML!="object"||!("__html"in t.dangerouslySetInnerHTML))throw Error(j(61))}if(t.style!=null&&typeof t.style!="object")throw Error(j(62))}}function hs(e,t){if(e.indexOf("-")===-1)return typeof t.is=="string";switch(e){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var ms=null;function oi(e){return e=e.target||e.srcElement||window,e.correspondingUseElement&&(e=e.correspondingUseElement),e.nodeType===3?e.parentNode:e}var vs=null,bt=null,en=null;function lo(e){if(e=sr(e)){if(typeof vs!="function")throw Error(j(280));var t=e.stateNode;t&&(t=vl(t),vs(e.stateNode,e.type,t))}}function Oa(e){bt?en?en.push(e):en=[e]:bt=e}function Fa(){if(bt){var e=bt,t=en;if(en=bt=null,lo(e),t)for(e=0;e>>=0,e===0?32:31-(wd(e)/jd|0)|0}var pr=64,hr=4194304;function Pn(e){switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return e&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return e}}function Qr(e,t){var n=e.pendingLanes;if(n===0)return 0;var r=0,l=e.suspendedLanes,s=e.pingedLanes,o=n&268435455;if(o!==0){var a=o&~l;a!==0?r=Pn(a):(s&=o,s!==0&&(r=Pn(s)))}else o=n&~l,o!==0?r=Pn(o):s!==0&&(r=Pn(s));if(r===0)return 0;if(t!==0&&t!==r&&!(t&l)&&(l=r&-r,s=t&-t,l>=s||l===16&&(s&4194240)!==0))return t;if(r&4&&(r|=n&16),t=e.entangledLanes,t!==0)for(e=e.entanglements,t&=r;0n;n++)t.push(e);return t}function rr(e,t,n){e.pendingLanes|=t,t!==536870912&&(e.suspendedLanes=0,e.pingedLanes=0),e=e.eventTimes,t=31-Ie(t),e[t]=n}function Cd(e,t){var n=e.pendingLanes&~t;e.pendingLanes=t,e.suspendedLanes=0,e.pingedLanes=0,e.expiredLanes&=t,e.mutableReadLanes&=t,e.entangledLanes&=t,t=e.entanglements;var r=e.eventTimes;for(e=e.expirationTimes;0=Mn),ho=" ",mo=!1;function nu(e,t){switch(e){case"keyup":return bd.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function ru(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var Vt=!1;function tf(e,t){switch(e){case"compositionend":return ru(t);case"keypress":return t.which!==32?null:(mo=!0,ho);case"textInput":return e=t.data,e===ho&&mo?null:e;default:return null}}function nf(e,t){if(Vt)return e==="compositionend"||!mi&&nu(e,t)?(e=eu(),Mr=fi=ot=null,Vt=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:n,offset:t-e};e=r}e:{for(;n;){if(n.nextSibling){n=n.nextSibling;break e}n=n.parentNode}n=void 0}n=xo(n)}}function ou(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?ou(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function au(){for(var e=window,t=Br();t instanceof e.HTMLIFrameElement;){try{var n=typeof t.contentWindow.location.href=="string"}catch{n=!1}if(n)e=t.contentWindow;else break;t=Br(e.document)}return t}function vi(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}function ff(e){var t=au(),n=e.focusedElem,r=e.selectionRange;if(t!==n&&n&&n.ownerDocument&&ou(n.ownerDocument.documentElement,n)){if(r!==null&&vi(n)){if(t=r.start,e=r.end,e===void 0&&(e=t),"selectionStart"in n)n.selectionStart=t,n.selectionEnd=Math.min(e,n.value.length);else if(e=(t=n.ownerDocument||document)&&t.defaultView||window,e.getSelection){e=e.getSelection();var l=n.textContent.length,s=Math.min(r.start,l);r=r.end===void 0?s:Math.min(r.end,l),!e.extend&&s>r&&(l=r,r=s,s=l),l=wo(n,s);var o=wo(n,r);l&&o&&(e.rangeCount!==1||e.anchorNode!==l.node||e.anchorOffset!==l.offset||e.focusNode!==o.node||e.focusOffset!==o.offset)&&(t=t.createRange(),t.setStart(l.node,l.offset),e.removeAllRanges(),s>r?(e.addRange(t),e.extend(o.node,o.offset)):(t.setEnd(o.node,o.offset),e.addRange(t)))}}for(t=[],e=n;e=e.parentNode;)e.nodeType===1&&t.push({element:e,left:e.scrollLeft,top:e.scrollTop});for(typeof n.focus=="function"&&n.focus(),n=0;n=document.documentMode,Ht=null,ks=null,Dn=null,Ss=!1;function jo(e,t,n){var r=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;Ss||Ht==null||Ht!==Br(r)||(r=Ht,"selectionStart"in r&&vi(r)?r={start:r.selectionStart,end:r.selectionEnd}:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection(),r={anchorNode:r.anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset}),Dn&&Kn(Dn,r)||(Dn=r,r=Gr(ks,"onSelect"),0Kt||(e.current=zs[Kt],zs[Kt]=null,Kt--)}function U(e,t){Kt++,zs[Kt]=e.current,e.current=t}var xt={},ue=jt(xt),ge=jt(!1),Mt=xt;function on(e,t){var n=e.type.contextTypes;if(!n)return xt;var r=e.stateNode;if(r&&r.__reactInternalMemoizedUnmaskedChildContext===t)return r.__reactInternalMemoizedMaskedChildContext;var l={},s;for(s in n)l[s]=t[s];return r&&(e=e.stateNode,e.__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=l),l}function ye(e){return e=e.childContextTypes,e!=null}function Zr(){B(ge),B(ue)}function Po(e,t,n){if(ue.current!==xt)throw Error(j(168));U(ue,t),U(ge,n)}function gu(e,t,n){var r=e.stateNode;if(t=t.childContextTypes,typeof r.getChildContext!="function")return n;r=r.getChildContext();for(var l in r)if(!(l in t))throw Error(j(108,id(e)||"Unknown",l));return K({},n,r)}function Jr(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||xt,Mt=ue.current,U(ue,e),U(ge,ge.current),!0}function zo(e,t,n){var r=e.stateNode;if(!r)throw Error(j(169));n?(e=gu(e,t,Mt),r.__reactInternalMemoizedMergedChildContext=e,B(ge),B(ue),U(ue,e)):B(ge),U(ge,n)}var Ke=null,gl=!1,Yl=!1;function yu(e){Ke===null?Ke=[e]:Ke.push(e)}function Nf(e){gl=!0,yu(e)}function kt(){if(!Yl&&Ke!==null){Yl=!0;var e=0,t=I;try{var n=Ke;for(I=1;e>=o,l-=o,Ye=1<<32-Ie(t)+l|n<E?(z=k,k=null):z=k.sibling;var T=v(f,k,m[E],y);if(T===null){k===null&&(k=z);break}e&&k&&T.alternate===null&&t(f,k),d=s(T,d,E),C===null?S=T:C.sibling=T,C=T,k=z}if(E===m.length)return n(f,k),H&&Ct(f,E),S;if(k===null){for(;EE?(z=k,k=null):z=k.sibling;var V=v(f,k,T.value,y);if(V===null){k===null&&(k=z);break}e&&k&&V.alternate===null&&t(f,k),d=s(V,d,E),C===null?S=V:C.sibling=V,C=V,k=z}if(T.done)return n(f,k),H&&Ct(f,E),S;if(k===null){for(;!T.done;E++,T=m.next())T=h(f,T.value,y),T!==null&&(d=s(T,d,E),C===null?S=T:C.sibling=T,C=T);return H&&Ct(f,E),S}for(k=r(f,k);!T.done;E++,T=m.next())T=g(k,f,E,T.value,y),T!==null&&(e&&T.alternate!==null&&k.delete(T.key===null?E:T.key),d=s(T,d,E),C===null?S=T:C.sibling=T,C=T);return e&&k.forEach(function(vn){return t(f,vn)}),H&&Ct(f,E),S}function L(f,d,m,y){if(typeof m=="object"&&m!==null&&m.type===Bt&&m.key===null&&(m=m.props.children),typeof m=="object"&&m!==null){switch(m.$$typeof){case cr:e:{for(var S=m.key,C=d;C!==null;){if(C.key===S){if(S=m.type,S===Bt){if(C.tag===7){n(f,C.sibling),d=l(C,m.props.children),d.return=f,f=d;break e}}else if(C.elementType===S||typeof S=="object"&&S!==null&&S.$$typeof===rt&&Mo(S)===C.type){n(f,C.sibling),d=l(C,m.props),d.ref=Sn(f,C,m),d.return=f,f=d;break e}n(f,C);break}else t(f,C);C=C.sibling}m.type===Bt?(d=Lt(m.props.children,f.mode,y,m.key),d.return=f,f=d):(y=Ar(m.type,m.key,m.props,null,f.mode,y),y.ref=Sn(f,d,m),y.return=f,f=y)}return o(f);case At:e:{for(C=m.key;d!==null;){if(d.key===C)if(d.tag===4&&d.stateNode.containerInfo===m.containerInfo&&d.stateNode.implementation===m.implementation){n(f,d.sibling),d=l(d,m.children||[]),d.return=f,f=d;break e}else{n(f,d);break}else t(f,d);d=d.sibling}d=ts(m,f.mode,y),d.return=f,f=d}return o(f);case rt:return C=m._init,L(f,d,C(m._payload),y)}if(_n(m))return x(f,d,m,y);if(yn(m))return w(f,d,m,y);jr(f,m)}return typeof m=="string"&&m!==""||typeof m=="number"?(m=""+m,d!==null&&d.tag===6?(n(f,d.sibling),d=l(d,m),d.return=f,f=d):(n(f,d),d=es(m,f.mode,y),d.return=f,f=d),o(f)):n(f,d)}return L}var un=ku(!0),Su=ku(!1),el=jt(null),tl=null,Xt=null,wi=null;function ji(){wi=Xt=tl=null}function ki(e){var t=el.current;B(el),e._currentValue=t}function Ms(e,t,n){for(;e!==null;){var r=e.alternate;if((e.childLanes&t)!==t?(e.childLanes|=t,r!==null&&(r.childLanes|=t)):r!==null&&(r.childLanes&t)!==t&&(r.childLanes|=t),e===n)break;e=e.return}}function nn(e,t){tl=e,wi=Xt=null,e=e.dependencies,e!==null&&e.firstContext!==null&&(e.lanes&t&&(ve=!0),e.firstContext=null)}function Le(e){var t=e._currentValue;if(wi!==e)if(e={context:e,memoizedValue:t,next:null},Xt===null){if(tl===null)throw Error(j(308));Xt=e,tl.dependencies={lanes:0,firstContext:e}}else Xt=Xt.next=e;return t}var Pt=null;function Si(e){Pt===null?Pt=[e]:Pt.push(e)}function Nu(e,t,n,r){var l=t.interleaved;return l===null?(n.next=n,Si(t)):(n.next=l.next,l.next=n),t.interleaved=n,qe(e,r)}function qe(e,t){e.lanes|=t;var n=e.alternate;for(n!==null&&(n.lanes|=t),n=e,e=e.return;e!==null;)e.childLanes|=t,n=e.alternate,n!==null&&(n.childLanes|=t),n=e,e=e.return;return n.tag===3?n.stateNode:null}var lt=!1;function Ni(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function Cu(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,effects:e.effects})}function Xe(e,t){return{eventTime:e,lane:t,tag:0,payload:null,callback:null,next:null}}function pt(e,t,n){var r=e.updateQueue;if(r===null)return null;if(r=r.shared,O&2){var l=r.pending;return l===null?t.next=t:(t.next=l.next,l.next=t),r.pending=t,qe(e,n)}return l=r.interleaved,l===null?(t.next=t,Si(r)):(t.next=l.next,l.next=t),r.interleaved=t,qe(e,n)}function Dr(e,t,n){if(t=t.updateQueue,t!==null&&(t=t.shared,(n&4194240)!==0)){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,ui(e,n)}}function Ro(e,t){var n=e.updateQueue,r=e.alternate;if(r!==null&&(r=r.updateQueue,n===r)){var l=null,s=null;if(n=n.firstBaseUpdate,n!==null){do{var o={eventTime:n.eventTime,lane:n.lane,tag:n.tag,payload:n.payload,callback:n.callback,next:null};s===null?l=s=o:s=s.next=o,n=n.next}while(n!==null);s===null?l=s=t:s=s.next=t}else l=s=t;n={baseState:r.baseState,firstBaseUpdate:l,lastBaseUpdate:s,shared:r.shared,effects:r.effects},e.updateQueue=n;return}e=n.lastBaseUpdate,e===null?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}function nl(e,t,n,r){var l=e.updateQueue;lt=!1;var s=l.firstBaseUpdate,o=l.lastBaseUpdate,a=l.shared.pending;if(a!==null){l.shared.pending=null;var u=a,c=u.next;u.next=null,o===null?s=c:o.next=c,o=u;var p=e.alternate;p!==null&&(p=p.updateQueue,a=p.lastBaseUpdate,a!==o&&(a===null?p.firstBaseUpdate=c:a.next=c,p.lastBaseUpdate=u))}if(s!==null){var h=l.baseState;o=0,p=c=u=null,a=s;do{var v=a.lane,g=a.eventTime;if((r&v)===v){p!==null&&(p=p.next={eventTime:g,lane:0,tag:a.tag,payload:a.payload,callback:a.callback,next:null});e:{var x=e,w=a;switch(v=t,g=n,w.tag){case 1:if(x=w.payload,typeof x=="function"){h=x.call(g,h,v);break e}h=x;break e;case 3:x.flags=x.flags&-65537|128;case 0:if(x=w.payload,v=typeof x=="function"?x.call(g,h,v):x,v==null)break e;h=K({},h,v);break e;case 2:lt=!0}}a.callback!==null&&a.lane!==0&&(e.flags|=64,v=l.effects,v===null?l.effects=[a]:v.push(a))}else g={eventTime:g,lane:v,tag:a.tag,payload:a.payload,callback:a.callback,next:null},p===null?(c=p=g,u=h):p=p.next=g,o|=v;if(a=a.next,a===null){if(a=l.shared.pending,a===null)break;v=a,a=v.next,v.next=null,l.lastBaseUpdate=v,l.shared.pending=null}}while(!0);if(p===null&&(u=h),l.baseState=u,l.firstBaseUpdate=c,l.lastBaseUpdate=p,t=l.shared.interleaved,t!==null){l=t;do o|=l.lane,l=l.next;while(l!==t)}else s===null&&(l.shared.lanes=0);Ot|=o,e.lanes=o,e.memoizedState=h}}function Do(e,t,n){if(e=t.effects,t.effects=null,e!==null)for(t=0;tn?n:4,e(!0);var r=Xl.transition;Xl.transition={};try{e(!1),t()}finally{I=n,Xl.transition=r}}function Vu(){return Me().memoizedState}function Pf(e,t,n){var r=mt(e);if(n={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null},Hu(e))Wu(t,n);else if(n=Nu(e,t,n,r),n!==null){var l=de();$e(n,e,r,l),Qu(n,t,r)}}function zf(e,t,n){var r=mt(e),l={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null};if(Hu(e))Wu(t,l);else{var s=e.alternate;if(e.lanes===0&&(s===null||s.lanes===0)&&(s=t.lastRenderedReducer,s!==null))try{var o=t.lastRenderedState,a=s(o,n);if(l.hasEagerState=!0,l.eagerState=a,Ue(a,o)){var u=t.interleaved;u===null?(l.next=l,Si(t)):(l.next=u.next,u.next=l),t.interleaved=l;return}}catch{}finally{}n=Nu(e,t,l,r),n!==null&&(l=de(),$e(n,e,r,l),Qu(n,t,r))}}function Hu(e){var t=e.alternate;return e===Q||t!==null&&t===Q}function Wu(e,t){On=ll=!0;var n=e.pending;n===null?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function Qu(e,t,n){if(n&4194240){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,ui(e,n)}}var sl={readContext:Le,useCallback:ie,useContext:ie,useEffect:ie,useImperativeHandle:ie,useInsertionEffect:ie,useLayoutEffect:ie,useMemo:ie,useReducer:ie,useRef:ie,useState:ie,useDebugValue:ie,useDeferredValue:ie,useTransition:ie,useMutableSource:ie,useSyncExternalStore:ie,useId:ie,unstable_isNewReconciler:!1},Tf={readContext:Le,useCallback:function(e,t){return Be().memoizedState=[e,t===void 0?null:t],e},useContext:Le,useEffect:Fo,useImperativeHandle:function(e,t,n){return n=n!=null?n.concat([e]):null,Fr(4194308,4,Iu.bind(null,t,e),n)},useLayoutEffect:function(e,t){return Fr(4194308,4,e,t)},useInsertionEffect:function(e,t){return Fr(4,2,e,t)},useMemo:function(e,t){var n=Be();return t=t===void 0?null:t,e=e(),n.memoizedState=[e,t],e},useReducer:function(e,t,n){var r=Be();return t=n!==void 0?n(t):t,r.memoizedState=r.baseState=t,e={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:t},r.queue=e,e=e.dispatch=Pf.bind(null,Q,e),[r.memoizedState,e]},useRef:function(e){var t=Be();return e={current:e},t.memoizedState=e},useState:Oo,useDebugValue:Mi,useDeferredValue:function(e){return Be().memoizedState=e},useTransition:function(){var e=Oo(!1),t=e[0];return e=_f.bind(null,e[1]),Be().memoizedState=e,[t,e]},useMutableSource:function(){},useSyncExternalStore:function(e,t,n){var r=Q,l=Be();if(H){if(n===void 0)throw Error(j(407));n=n()}else{if(n=t(),ne===null)throw Error(j(349));Dt&30||zu(r,t,n)}l.memoizedState=n;var s={value:n,getSnapshot:t};return l.queue=s,Fo(Lu.bind(null,r,s,e),[e]),r.flags|=2048,er(9,Tu.bind(null,r,s,n,t),void 0,null),n},useId:function(){var e=Be(),t=ne.identifierPrefix;if(H){var n=Ge,r=Ye;n=(r&~(1<<32-Ie(r)-1)).toString(32)+n,t=":"+t+"R"+n,n=qn++,0<\/script>",e=e.removeChild(e.firstChild)):typeof r.is=="string"?e=o.createElement(n,{is:r.is}):(e=o.createElement(n),n==="select"&&(o=e,r.multiple?o.multiple=!0:r.size&&(o.size=r.size))):e=o.createElementNS(e,n),e[Ve]=t,e[Xn]=r,ec(e,t,!1,!1),t.stateNode=e;e:{switch(o=hs(n,r),n){case"dialog":A("cancel",e),A("close",e),l=r;break;case"iframe":case"object":case"embed":A("load",e),l=r;break;case"video":case"audio":for(l=0;lfn&&(t.flags|=128,r=!0,Nn(s,!1),t.lanes=4194304)}else{if(!r)if(e=rl(o),e!==null){if(t.flags|=128,r=!0,n=e.updateQueue,n!==null&&(t.updateQueue=n,t.flags|=4),Nn(s,!0),s.tail===null&&s.tailMode==="hidden"&&!o.alternate&&!H)return oe(t),null}else 2*X()-s.renderingStartTime>fn&&n!==1073741824&&(t.flags|=128,r=!0,Nn(s,!1),t.lanes=4194304);s.isBackwards?(o.sibling=t.child,t.child=o):(n=s.last,n!==null?n.sibling=o:t.child=o,s.last=o)}return s.tail!==null?(t=s.tail,s.rendering=t,s.tail=t.sibling,s.renderingStartTime=X(),t.sibling=null,n=W.current,U(W,r?n&1|2:n&1),t):(oe(t),null);case 22:case 23:return $i(),r=t.memoizedState!==null,e!==null&&e.memoizedState!==null!==r&&(t.flags|=8192),r&&t.mode&1?we&1073741824&&(oe(t),t.subtreeFlags&6&&(t.flags|=8192)):oe(t),null;case 24:return null;case 25:return null}throw Error(j(156,t.tag))}function $f(e,t){switch(yi(t),t.tag){case 1:return ye(t.type)&&Zr(),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return cn(),B(ge),B(ue),_i(),e=t.flags,e&65536&&!(e&128)?(t.flags=e&-65537|128,t):null;case 5:return Ei(t),null;case 13:if(B(W),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(j(340));an()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return B(W),null;case 4:return cn(),null;case 10:return ki(t.type._context),null;case 22:case 23:return $i(),null;case 24:return null;default:return null}}var Sr=!1,ae=!1,Uf=typeof WeakSet=="function"?WeakSet:Set,N=null;function Zt(e,t){var n=e.ref;if(n!==null)if(typeof n=="function")try{n(null)}catch(r){Y(e,t,r)}else n.current=null}function Bs(e,t,n){try{n()}catch(r){Y(e,t,r)}}var Ko=!1;function Af(e,t){if(Ns=Kr,e=ou(),vi(e)){if("selectionStart"in e)var n={start:e.selectionStart,end:e.selectionEnd};else e:{n=(n=e.ownerDocument)&&n.defaultView||window;var r=n.getSelection&&n.getSelection();if(r&&r.rangeCount!==0){n=r.anchorNode;var l=r.anchorOffset,s=r.focusNode;r=r.focusOffset;try{n.nodeType,s.nodeType}catch{n=null;break e}var o=0,a=-1,u=-1,c=0,p=0,v=e,h=null;t:for(;;){for(var g;v!==n||l!==0&&v.nodeType!==3||(a=o+l),v!==s||r!==0&&v.nodeType!==3||(u=o+r),v.nodeType===3&&(o+=v.nodeValue.length),(g=v.firstChild)!==null;)h=v,v=g;for(;;){if(v===e)break t;if(h===n&&++c===l&&(a=o),h===s&&++p===r&&(u=o),(g=v.nextSibling)!==null)break;v=h,h=v.parentNode}v=g}n=a===-1||u===-1?null:{start:a,end:u}}else n=null}n=n||{start:0,end:0}}else n=null;for(Cs={focusedElem:e,selectionRange:n},Kr=!1,N=t;N!==null;)if(t=N,e=t.child,(t.subtreeFlags&1028)!==0&&e!==null)e.return=t,N=e;else for(;N!==null;){t=N;try{var y=t.alternate;if(t.flags&1024)switch(t.tag){case 0:case 11:case 15:break;case 1:if(y!==null){var w=y.memoizedProps,T=y.memoizedState,f=t.stateNode,d=f.getSnapshotBeforeUpdate(t.elementType===t.type?w:De(t.type,w),T);f.__reactInternalSnapshotBeforeUpdate=d}break;case 3:var m=t.stateNode.containerInfo;m.nodeType===1?m.textContent="":m.nodeType===9&&m.documentElement&&m.removeChild(m.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(j(163))}}catch(x){Y(t,t.return,x)}if(e=t.sibling,e!==null){e.return=t.return,N=e;break}N=t.return}return y=Ko,Ko=!1,y}function Fn(e,t,n){var r=t.updateQueue;if(r=r!==null?r.lastEffect:null,r!==null){var l=r=r.next;do{if((l.tag&e)===e){var s=l.destroy;l.destroy=void 0,s!==void 0&&Bs(t,n,s)}l=l.next}while(l!==r)}}function wl(e,t){if(t=t.updateQueue,t=t!==null?t.lastEffect:null,t!==null){var n=t=t.next;do{if((n.tag&e)===e){var r=n.create;n.destroy=r()}n=n.next}while(n!==t)}}function Vs(e){var t=e.ref;if(t!==null){var n=e.stateNode;switch(e.tag){case 5:e=n;break;default:e=n}typeof t=="function"?t(e):t.current=e}}function rc(e){var t=e.alternate;t!==null&&(e.alternate=null,rc(t)),e.child=null,e.deletions=null,e.sibling=null,e.tag===5&&(t=e.stateNode,t!==null&&(delete t[Ve],delete t[Xn],delete t[Ps],delete t[kf],delete t[Sf])),e.stateNode=null,e.return=null,e.dependencies=null,e.memoizedProps=null,e.memoizedState=null,e.pendingProps=null,e.stateNode=null,e.updateQueue=null}function lc(e){return e.tag===5||e.tag===3||e.tag===4}function Yo(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||lc(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.flags&2||e.child===null||e.tag===4)continue e;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function Hs(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.nodeType===8?n.parentNode.insertBefore(e,t):n.insertBefore(e,t):(n.nodeType===8?(t=n.parentNode,t.insertBefore(e,n)):(t=n,t.appendChild(e)),n=n._reactRootContainer,n!=null||t.onclick!==null||(t.onclick=Xr));else if(r!==4&&(e=e.child,e!==null))for(Hs(e,t,n),e=e.sibling;e!==null;)Hs(e,t,n),e=e.sibling}function Ws(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(r!==4&&(e=e.child,e!==null))for(Ws(e,t,n),e=e.sibling;e!==null;)Ws(e,t,n),e=e.sibling}var re=null,Oe=!1;function nt(e,t,n){for(n=n.child;n!==null;)sc(e,t,n),n=n.sibling}function sc(e,t,n){if(He&&typeof He.onCommitFiberUnmount=="function")try{He.onCommitFiberUnmount(fl,n)}catch{}switch(n.tag){case 5:ae||Zt(n,t);case 6:var r=re,l=Oe;re=null,nt(e,t,n),re=r,Oe=l,re!==null&&(Oe?(e=re,n=n.stateNode,e.nodeType===8?e.parentNode.removeChild(n):e.removeChild(n)):re.removeChild(n.stateNode));break;case 18:re!==null&&(Oe?(e=re,n=n.stateNode,e.nodeType===8?Kl(e.parentNode,n):e.nodeType===1&&Kl(e,n),Wn(e)):Kl(re,n.stateNode));break;case 4:r=re,l=Oe,re=n.stateNode.containerInfo,Oe=!0,nt(e,t,n),re=r,Oe=l;break;case 0:case 11:case 14:case 15:if(!ae&&(r=n.updateQueue,r!==null&&(r=r.lastEffect,r!==null))){l=r=r.next;do{var s=l,o=s.destroy;s=s.tag,o!==void 0&&(s&2||s&4)&&Bs(n,t,o),l=l.next}while(l!==r)}nt(e,t,n);break;case 1:if(!ae&&(Zt(n,t),r=n.stateNode,typeof r.componentWillUnmount=="function"))try{r.props=n.memoizedProps,r.state=n.memoizedState,r.componentWillUnmount()}catch(a){Y(n,t,a)}nt(e,t,n);break;case 21:nt(e,t,n);break;case 22:n.mode&1?(ae=(r=ae)||n.memoizedState!==null,nt(e,t,n),ae=r):nt(e,t,n);break;default:nt(e,t,n)}}function Go(e){var t=e.updateQueue;if(t!==null){e.updateQueue=null;var n=e.stateNode;n===null&&(n=e.stateNode=new Uf),t.forEach(function(r){var l=Xf.bind(null,e,r);n.has(r)||(n.add(r),r.then(l,l))})}}function Re(e,t){var n=t.deletions;if(n!==null)for(var r=0;rl&&(l=o),r&=~s}if(r=l,r=X()-r,r=(120>r?120:480>r?480:1080>r?1080:1920>r?1920:3e3>r?3e3:4320>r?4320:1960*Vf(r/1960))-r,10e?16:e,at===null)var r=!1;else{if(e=at,at=null,al=0,O&6)throw Error(j(331));var l=O;for(O|=4,N=e.current;N!==null;){var s=N,o=s.child;if(N.flags&16){var a=s.deletions;if(a!==null){for(var u=0;uX()-Fi?Lt(e,0):Oi|=n),xe(e,t)}function pc(e,t){t===0&&(e.mode&1?(t=hr,hr<<=1,!(hr&130023424)&&(hr=4194304)):t=1);var n=de();e=qe(e,t),e!==null&&(rr(e,t,n),xe(e,n))}function Gf(e){var t=e.memoizedState,n=0;t!==null&&(n=t.retryLane),pc(e,n)}function Xf(e,t){var n=0;switch(e.tag){case 13:var r=e.stateNode,l=e.memoizedState;l!==null&&(n=l.retryLane);break;case 19:r=e.stateNode;break;default:throw Error(j(314))}r!==null&&r.delete(t),pc(e,n)}var hc;hc=function(e,t,n){if(e!==null)if(e.memoizedProps!==t.pendingProps||ge.current)ve=!0;else{if(!(e.lanes&n)&&!(t.flags&128))return ve=!1,Ff(e,t,n);ve=!!(e.flags&131072)}else ve=!1,H&&t.flags&1048576&&yu(t,br,t.index);switch(t.lanes=0,t.tag){case 2:var r=t.type;Ir(e,t),e=t.pendingProps;var l=on(t,ue.current);nn(t,n),l=zi(null,t,r,e,l,n);var s=Li();return t.flags|=1,typeof l=="object"&&l!==null&&typeof l.render=="function"&&l.$$typeof===void 0?(t.tag=1,t.memoizedState=null,t.updateQueue=null,ye(r)?(s=!0,Jr(t)):s=!1,t.memoizedState=l.state!==null&&l.state!==void 0?l.state:null,Ni(t),l.updater=xl,t.stateNode=l,l._reactInternals=t,Ds(t,r,e,n),t=Is(null,t,r,!0,s,n)):(t.tag=0,H&&s&&gi(t),ce(null,t,l,n),t=t.child),t;case 16:r=t.elementType;e:{switch(Ir(e,t),e=t.pendingProps,l=r._init,r=l(r._payload),t.type=r,l=t.tag=Jf(r),e=De(r,e),l){case 0:t=Fs(null,t,r,e,n);break e;case 1:t=Ho(null,t,r,e,n);break e;case 11:t=Bo(null,t,r,e,n);break e;case 14:t=Vo(null,t,r,De(r.type,e),n);break e}throw Error(j(306,r,""))}return t;case 0:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:De(r,l),Fs(e,t,r,l,n);case 1:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:De(r,l),Ho(e,t,r,l,n);case 3:e:{if(Ju(t),e===null)throw Error(j(387));r=t.pendingProps,s=t.memoizedState,l=s.element,Nu(e,t),nl(t,r,null,n);var o=t.memoizedState;if(r=o.element,s.isDehydrated)if(s={element:r,isDehydrated:!1,cache:o.cache,pendingSuspenseBoundaries:o.pendingSuspenseBoundaries,transitions:o.transitions},t.updateQueue.baseState=s,t.memoizedState=s,t.flags&256){l=dn(Error(j(423)),t),t=Wo(e,t,r,n,l);break e}else if(r!==l){l=dn(Error(j(424)),t),t=Wo(e,t,r,n,l);break e}else for(je=ft(t.stateNode.containerInfo.firstChild),ke=t,H=!0,Fe=null,n=ku(t,null,r,n),t.child=n;n;)n.flags=n.flags&-3|4096,n=n.sibling;else{if(an(),r===l){t=be(e,t,n);break e}ce(e,t,r,n)}t=t.child}return t;case 5:return Cu(t),e===null&&Ts(t),r=t.type,l=t.pendingProps,s=e!==null?e.memoizedProps:null,o=l.children,Es(r,l)?o=null:s!==null&&Es(r,s)&&(t.flags|=32),Zu(e,t),ce(e,t,o,n),t.child;case 6:return e===null&&Ts(t),null;case 13:return qu(e,t,n);case 4:return Ci(t,t.stateNode.containerInfo),r=t.pendingProps,e===null?t.child=un(t,null,r,n):ce(e,t,r,n),t.child;case 11:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:De(r,l),Bo(e,t,r,l,n);case 7:return ce(e,t,t.pendingProps,n),t.child;case 8:return ce(e,t,t.pendingProps.children,n),t.child;case 12:return ce(e,t,t.pendingProps.children,n),t.child;case 10:e:{if(r=t.type._context,l=t.pendingProps,s=t.memoizedProps,o=l.value,U(el,r._currentValue),r._currentValue=o,s!==null)if(Ue(s.value,o)){if(s.children===l.children&&!ge.current){t=be(e,t,n);break e}}else for(s=t.child,s!==null&&(s.return=t);s!==null;){var a=s.dependencies;if(a!==null){o=s.child;for(var u=a.firstContext;u!==null;){if(u.context===r){if(s.tag===1){u=Xe(-1,n&-n),u.tag=2;var c=s.updateQueue;if(c!==null){c=c.shared;var p=c.pending;p===null?u.next=u:(u.next=p.next,p.next=u),c.pending=u}}s.lanes|=n,u=s.alternate,u!==null&&(u.lanes|=n),Ms(s.return,n,t),a.lanes|=n;break}u=u.next}}else if(s.tag===10)o=s.type===t.type?null:s.child;else if(s.tag===18){if(o=s.return,o===null)throw Error(j(341));o.lanes|=n,a=o.alternate,a!==null&&(a.lanes|=n),Ms(o,n,t),o=s.sibling}else o=s.child;if(o!==null)o.return=s;else for(o=s;o!==null;){if(o===t){o=null;break}if(s=o.sibling,s!==null){s.return=o.return,o=s;break}o=o.return}s=o}ce(e,t,l.children,n),t=t.child}return t;case 9:return l=t.type,r=t.pendingProps.children,nn(t,n),l=Te(l),r=r(l),t.flags|=1,ce(e,t,r,n),t.child;case 14:return r=t.type,l=De(r,t.pendingProps),l=De(r.type,l),Vo(e,t,r,l,n);case 15:return Gu(e,t,t.type,t.pendingProps,n);case 17:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:De(r,l),Ir(e,t),t.tag=1,ye(r)?(e=!0,Jr(t)):e=!1,nn(t,n),Qu(t,r,l),Ds(t,r,l,n),Is(null,t,r,!0,e,n);case 19:return bu(e,t,n);case 22:return Xu(e,t,n)}throw Error(j(156,t.tag))};function mc(e,t){return Va(e,t)}function Zf(e,t,n,r){this.tag=e,this.key=n,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=t,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=r,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function ze(e,t,n,r){return new Zf(e,t,n,r)}function Ai(e){return e=e.prototype,!(!e||!e.isReactComponent)}function Jf(e){if(typeof e=="function")return Ai(e)?1:0;if(e!=null){if(e=e.$$typeof,e===si)return 11;if(e===ii)return 14}return 2}function vt(e,t){var n=e.alternate;return n===null?(n=ze(e.tag,t,e.key,e.mode),n.elementType=e.elementType,n.type=e.type,n.stateNode=e.stateNode,n.alternate=e,e.alternate=n):(n.pendingProps=t,n.type=e.type,n.flags=0,n.subtreeFlags=0,n.deletions=null),n.flags=e.flags&14680064,n.childLanes=e.childLanes,n.lanes=e.lanes,n.child=e.child,n.memoizedProps=e.memoizedProps,n.memoizedState=e.memoizedState,n.updateQueue=e.updateQueue,t=e.dependencies,n.dependencies=t===null?null:{lanes:t.lanes,firstContext:t.firstContext},n.sibling=e.sibling,n.index=e.index,n.ref=e.ref,n}function Ar(e,t,n,r,l,s){var o=2;if(r=e,typeof e=="function")Ai(e)&&(o=1);else if(typeof e=="string")o=5;else e:switch(e){case Bt:return Tt(n.children,l,s,t);case li:o=8,l|=8;break;case ls:return e=ze(12,n,t,l|2),e.elementType=ls,e.lanes=s,e;case ss:return e=ze(13,n,t,l),e.elementType=ss,e.lanes=s,e;case is:return e=ze(19,n,t,l),e.elementType=is,e.lanes=s,e;case Ca:return kl(n,l,s,t);default:if(typeof e=="object"&&e!==null)switch(e.$$typeof){case Sa:o=10;break e;case Na:o=9;break e;case si:o=11;break e;case ii:o=14;break e;case rt:o=16,r=null;break e}throw Error(j(130,e==null?e:typeof e,""))}return t=ze(o,n,t,l),t.elementType=e,t.type=r,t.lanes=s,t}function Tt(e,t,n,r){return e=ze(7,e,r,t),e.lanes=n,e}function kl(e,t,n,r){return e=ze(22,e,r,t),e.elementType=Ca,e.lanes=n,e.stateNode={isHidden:!1},e}function es(e,t,n){return e=ze(6,e,null,t),e.lanes=n,e}function ts(e,t,n){return t=ze(4,e.children!==null?e.children:[],e.key,t),t.lanes=n,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function qf(e,t,n,r,l){this.tag=t,this.containerInfo=e,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=Ol(0),this.expirationTimes=Ol(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=Ol(0),this.identifierPrefix=r,this.onRecoverableError=l,this.mutableSourceEagerHydrationData=null}function Bi(e,t,n,r,l,s,o,a,u){return e=new qf(e,t,n,a,u),t===1?(t=1,s===!0&&(t|=8)):t=0,s=ze(3,null,null,t),e.current=s,s.stateNode=e,s.memoizedState={element:r,isDehydrated:n,cache:null,transitions:null,pendingSuspenseBoundaries:null},Ni(s),e}function bf(e,t,n){var r=3"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(xc)}catch(e){console.error(e)}}xc(),xa.exports=Ce;var lp=xa.exports,wc,na=lp;wc=na.createRoot,na.hydrateRoot;const jc=()=>{var e,t;return(t=(e=window==null?void 0:window.go)==null?void 0:e.main)==null?void 0:t.App},sp=()=>!!jc();async function F(e,...t){const n=jc();if(!n||typeof n[e]!="function")throw new Error(`${e} is unavailable — run this inside the Behavision app`);return n[e](...t)}const $={session:()=>F("Session"),login:(e,t)=>F("Login",e,t),logout:()=>F("Logout"),claim:e=>F("Claim",e),runStandalone:()=>F("RunStandalone"),engineStatus:()=>F("EngineStatus"),startEngine:()=>F("StartEngine"),stopEngine:()=>F("StopEngine"),cameras:()=>F("Cameras"),testCamera:e=>F("TestCamera",e),discoverCameras:()=>F("DiscoverCameras"),saveCamera:(e,t)=>F("SaveCamera",e,t),deleteCamera:e=>F("DeleteCamera",e),startPlacement:(e,t)=>F("StartPlacementCheck",e,t),placementResult:e=>F("PlacementResult",e),streamURL:e=>F("StreamURL",e),live:()=>F("Live"),pipelineStatus:()=>F("PipelineStatus"),localIdentities:e=>F("LocalIdentities",e),localSightings:e=>F("LocalSightings",e),footfall:(e,t,n)=>F("Footfall",e,t,n),sites:()=>F("Sites"),visitorHistory:(e,t)=>F("VisitorHistory",e,t),visitorPhoto:e=>F("VisitorPhoto",e),forgetCustomer:e=>F("ForgetCustomer",e),sales:(e,t)=>F("Sales",e,t),customers:(e,t)=>F("Customers",e,t),saveProfile:e=>F("SaveProfile",e),ask:e=>F("Ask",e),recordPurchase:(e,t,n,r)=>F("RecordPurchase",e,t,n,r)};function fe(e){return e?typeof e=="string"?e:e.message||String(e):"Something went wrong."}function et(e,t,n=[]){const[r,l]=P.useState(null),[s,o]=P.useState(null),[a,u]=P.useState(!0),c=P.useRef(!0),p=P.useRef(!1),v=P.useCallback(async()=>{if(!p.current){p.current=!0;try{const h=await e();if(!c.current)return;l(h),o(null)}catch(h){c.current&&o(fe(h))}finally{p.current=!1,c.current&&u(!1)}}},n);return P.useEffect(()=>{if(c.current=!0,v(),!t)return()=>{c.current=!1};const h=setInterval(v,t);return()=>{c.current=!1,clearInterval(h)}},[v,t]),{data:r,error:s,loading:a,reload:v}}function ip(e){if(!e)return"—";const t=typeof e=="number"?new Date(e*1e3):new Date(e);return isNaN(t)?"—":t.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}function ra(e){if(!e)return"—";const t=typeof e=="number"?new Date(e*1e3):new Date(e);return isNaN(t)?"—":t.toLocaleDateString([],{day:"numeric",month:"short"})}const ns={width:18,height:18,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:1.6,strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",focusable:"false"};function J({size:e,children:t,...n}){return i.jsx("svg",{...ns,...n,width:e??ns.width,height:e??ns.height,children:t})}const op=e=>i.jsxs(J,{...e,children:[i.jsx("circle",{cx:"12",cy:"12",r:"3.2"}),i.jsx("path",{d:"M5.6 5.6a9 9 0 0 0 0 12.8M18.4 18.4a9 9 0 0 0 0-12.8"})]}),ap=e=>i.jsxs(J,{...e,children:[i.jsx("circle",{cx:"9",cy:"8.5",r:"3.2"}),i.jsx("path",{d:"M2.8 19.5a6.4 6.4 0 0 1 12.4 0"}),i.jsx("path",{d:"M16.5 6.2a3.2 3.2 0 0 1 0 6.1M18 19.5a6 6 0 0 0-1.6-4"})]}),up=e=>i.jsxs(J,{...e,children:[i.jsx("path",{d:"M3 8.5h3.4L8 6h8l1.6 2.5H21v10.2H3z"}),i.jsx("circle",{cx:"12",cy:"13.2",r:"3.1"})]}),kc=e=>i.jsxs(J,{...e,children:[i.jsx("circle",{cx:"11",cy:"11",r:"6.4"}),i.jsx("path",{d:"M15.8 15.8 20.5 20.5"})]}),la=e=>i.jsx(J,{...e,children:i.jsx("path",{d:"M12 5.5v13M5.5 12h13"})}),Qi=e=>i.jsx(J,{...e,children:i.jsx("path",{d:"M6.5 6.5l11 11M17.5 6.5l-11 11"})}),Xs=e=>i.jsx(J,{...e,children:i.jsx("path",{d:"M5 12.8l4.4 4.2L19 7"})}),Sc=e=>i.jsx(J,{...e,children:i.jsx("path",{d:"M8 5.6v12.8L18.5 12z"})}),cp=e=>i.jsx(J,{...e,children:i.jsx("rect",{x:"7",y:"7",width:"10",height:"10",rx:"1.6"})}),Se=e=>i.jsxs(J,{...e,children:[i.jsx("path",{d:"M12 4.6 21 19.4H3z"}),i.jsx("path",{d:"M12 10v4.1"}),i.jsx("path",{d:"M12 17.1v.01"})]}),sa=e=>i.jsx(J,{...e,children:i.jsx("path",{d:"M5 19.4v-4.2M10.3 19.4v-7.6M15.7 19.4v-11M21 19.4V4.6"})}),Nc=e=>i.jsx(J,{...e,children:i.jsx("path",{d:"M7.2 18.4a4.2 4.2 0 0 1-.6-8.35A6.2 6.2 0 0 1 18.4 9a4.2 4.2 0 0 1 .3 9.4z"})}),Cc=e=>i.jsxs(J,{...e,children:[i.jsx("path",{d:"M7.2 18.4a4.2 4.2 0 0 1-.6-8.35 6.2 6.2 0 0 1 2-3.2M10.6 5.2A6.2 6.2 0 0 1 18.4 9a4.2 4.2 0 0 1 1.9 7.6"}),i.jsx("path",{d:"M3.6 3.6l16.8 16.8"})]}),dp=e=>i.jsx(J,{...e,children:i.jsx("path",{d:"M12 3.8 19.4 6.6v5.2c0 4.2-3 7.4-7.4 8.4-4.4-1-7.4-4.2-7.4-8.4V6.6z"})}),fp=e=>i.jsxs(J,{...e,children:[i.jsx("path",{d:"M10.2 13.8a3.6 3.6 0 0 0 5.2 0l2.8-2.8a3.7 3.7 0 0 0-5.2-5.2l-1.3 1.3"}),i.jsx("path",{d:"M13.8 10.2a3.6 3.6 0 0 0-5.2 0l-2.8 2.8a3.7 3.7 0 0 0 5.2 5.2l1.3-1.3"})]}),pp=e=>i.jsxs(J,{...e,children:[i.jsx("path",{d:"M14.4 7.6V5.4H4.6v13.2h9.8v-2.2"}),i.jsx("path",{d:"M10 12h9.4M16.4 8.8 19.8 12l-3.4 3.2"})]}),ia=e=>i.jsx(J,{...e,children:i.jsx("path",{d:"M14.6 5.6 8 12l6.6 6.4"})}),hp=e=>i.jsx(J,{...e,children:i.jsx("path",{d:"M9.4 5.6 16 12l-6.6 6.4"})}),Ec=e=>i.jsxs(J,{...e,strokeWidth:"1.2",children:[i.jsx("path",{d:"M3 8.5h3.4L8 6h8l1.6 2.5H21v10.2H3z"}),i.jsx("circle",{cx:"12",cy:"13.2",r:"3.1"}),i.jsx("path",{d:"M3.6 3.6l16.8 16.8"})]}),_c=e=>i.jsxs(J,{...e,strokeWidth:"1.2",children:[i.jsx("circle",{cx:"12",cy:"9",r:"3.4"}),i.jsx("path",{d:"M5.4 20a6.8 6.8 0 0 1 13.2 0"})]}),gt=""+new URL("loyaly-mark-Ch-P3VSF.png",import.meta.url).href;function mp({onDone:e}){const[t,n]=P.useState(""),[r,l]=P.useState(""),[s,o]=P.useState(!1),[a,u]=P.useState(null);async function c(p){p.preventDefault(),o(!0),u(null);try{e(await $.login(t.trim(),r))}catch(v){u(fe(v))}finally{o(!1)}}return i.jsx("div",{className:"login",children:i.jsxs("div",{className:"box",children:[i.jsx("span",{className:"mark",children:i.jsx("img",{src:gt,alt:""})}),i.jsx("h1",{children:"Behavision"}),i.jsx("p",{className:"lead",children:"Sign in to connect this PC to your store."}),i.jsxs("form",{onSubmit:c,children:[a&&i.jsxs("div",{className:"err",children:[i.jsx(Se,{size:15}),a]}),i.jsxs("label",{className:"field",children:[i.jsx("span",{children:"Email"}),i.jsx("input",{type:"email",value:t,autoComplete:"username",required:!0,autoFocus:!0,onChange:p=>n(p.target.value)})]}),i.jsxs("label",{className:"field",children:[i.jsx("span",{children:"Password"}),i.jsx("input",{type:"password",value:r,autoComplete:"current-password",required:!0,onChange:p=>l(p.target.value)})]}),i.jsx("button",{className:"btn primary",disabled:s||!t||!r,children:s?"Signing in…":"Sign in"})]}),i.jsx("p",{className:"foot",children:"Signing in downloads this store's recognition models and connects it to your account. Nothing is sent until a camera is set up."})]})})}function oa({onDone:e,onCancel:t}){const[n,r]=P.useState(""),[l,s]=P.useState(null),[o,a]=P.useState(null);async function u(h){h.preventDefault(),s("claim"),a(null);try{e(await $.claim(n))}catch(g){a(fe(g))}finally{s(null)}}async function c(){s("alone"),a(null);try{e(await $.runStandalone())}catch(h){a(fe(h))}finally{s(null)}}const[p,v]=P.useState(t?"code":null);return p===null?i.jsx("div",{className:"login",children:i.jsxs("div",{className:"box wide",children:[i.jsx("span",{className:"mark",children:i.jsx("img",{src:gt,alt:""})}),i.jsx("h1",{children:"Welcome to Behavision"}),i.jsx("p",{className:"lead",children:"This PC will watch your shop’s cameras and recognise returning customers. First, one question: is this shop managed from a head office?"}),i.jsxs("div",{className:"choices",children:[i.jsxs("button",{type:"button",className:"choice",onClick:()=>v("code"),children:[i.jsx(Nc,{size:22}),i.jsx("b",{children:"Yes — I have an installation code"}),i.jsxs("span",{children:["Head office gave you a code like ",i.jsx("em",{children:"ABCDEF-123456-…"}),". This PC joins that shop and gets its cameras from there."]})]}),i.jsxs("button",{type:"button",className:"choice",onClick:()=>v("alone"),children:[i.jsx(dp,{size:22}),i.jsx("b",{children:"No — set up on this PC only"}),i.jsx("span",{children:"Cameras, customers and recognition stay on this PC. Nothing is sent anywhere. You can link to a head office later."})]})]})]})}):p==="alone"?i.jsx("div",{className:"login",children:i.jsxs("div",{className:"box",children:[i.jsx("span",{className:"mark",children:i.jsx("img",{src:gt,alt:""})}),i.jsx("h1",{children:"On this PC only"}),i.jsx("p",{className:"lead",children:"Behavision will run entirely here. Next you’ll add your camera — it can find it on the network for you — and walk past it once so it can prove it works."}),o&&i.jsxs("div",{className:"err",children:[i.jsx(Se,{size:15}),o]}),i.jsx("button",{type:"button",className:"btn primary",disabled:!!l,onClick:c,children:l==="alone"?"Setting up…":"Continue"}),i.jsx("div",{className:"alt",children:i.jsxs("button",{type:"button",className:"linkbtn",onClick:()=>v(null),children:[i.jsx(ia,{size:14})," Back"]})})]})}):i.jsx("div",{className:"login",children:i.jsxs("div",{className:"box",children:[i.jsx("span",{className:"mark",children:i.jsx("img",{src:gt,alt:""})}),i.jsx("h1",{children:t?"Link to head office":"Join your shop"}),i.jsx("p",{className:"lead",children:"Type the installation code head office gave you. It works once, and this PC becomes that shop."}),i.jsxs("form",{onSubmit:u,children:[o&&i.jsxs("div",{className:"err",children:[i.jsx(Se,{size:15}),o]}),i.jsxs("label",{className:"field",children:[i.jsx("span",{children:"Installation code"}),i.jsx("input",{value:n,autoFocus:!0,required:!0,placeholder:"ABCDEF-123456-GHIJKL-789012",autoComplete:"off",spellCheck:"false",style:{textTransform:"uppercase",letterSpacing:".06em"},onChange:h=>r(h.target.value)})]}),i.jsx("button",{className:"btn primary",disabled:!!l||n.trim().length<6,children:l==="claim"?"Linking…":"Link this PC"})]}),i.jsxs("p",{className:"foot",children:["Don’t have one? Whoever runs head office creates it under the shop: ",i.jsx("em",{children:"Shops → the shop → Set up a shop PC"}),"."]}),i.jsx("div",{className:"alt",children:t?i.jsx("button",{type:"button",className:"linkbtn",onClick:t,children:"Not now — go back"}):i.jsxs("button",{type:"button",className:"linkbtn",onClick:()=>v(null),children:[i.jsx(ia,{size:14})," Back"]})})]})})}function Pc({onNavigate:e}){var v,h;const{data:t,error:n}=et(()=>$.live(),3e3),{data:r}=et(()=>$.pipelineStatus(),5e3),l=((v=t==null?void 0:t.stats)==null?void 0:v.cameras)??[],s=((h=t==null?void 0:t.stats)==null?void 0:h.gallery)??{},o=(t==null?void 0:t.events)??[],a=l.reduce((g,y)=>{var T,f;const w=(f=(T=y==null?void 0:y.pipeline)==null?void 0:T.best_quality)==null?void 0:f.fraction_below_gate;return typeof w=="number"&&w>g?w:g},0),u=l.filter(g=>g.connected).length,c=o.filter(g=>g.type==="person.new"||g.type==="person.seen"),p=wp(c[0]);return i.jsxs("div",{className:"page",children:[i.jsxs("header",{children:[i.jsx("h2",{children:"Live"}),i.jsx("p",{children:"Who is in the shop, and whether it is reaching head office."})]}),n&&i.jsxs("div",{className:"err",children:[i.jsx(Se,{size:15}),n]}),i.jsx(xp,{pipe:r,cameras:l,up:u}),i.jsx(vp,{cameras:l,arrivals:c,onNavigate:e}),i.jsxs("div",{className:"panel arrivals-panel",children:[i.jsxs("div",{className:"panelhead",children:[i.jsx("h3",{children:"Who just walked in"}),c.length>0&&i.jsxs("span",{className:"note",children:[c.length," today"]})]}),i.jsx("div",{className:"panelbody flush",children:c.length===0?i.jsxs("div",{className:"empty",children:[i.jsx(_c,{size:34}),i.jsx("b",{children:"Nobody yet"}),i.jsx("p",{children:"Customers appear here the moment a camera recognises a face."})]}):i.jsx("div",{className:"arrivals",children:c.slice(0,30).map((g,y)=>i.jsx(gp,{e:g,fresh:y===0&&p},`${g.ts}-${y}`))})})]}),i.jsxs("div",{className:"metrics",style:{marginTop:"var(--sp-4)"},children:[i.jsx(Er,{k:"People known",v:s.identities??"—"}),i.jsx(Er,{k:"Sightings",v:s.sightings??"—"}),i.jsx(Er,{k:"Cameras live",v:l.length?`${u}/${l.length}`:"—",tone:l.length?u===0?"bad":u.5?"bad":a>.2?"warn":"ok",s:a>.5?"Most visitors are being missed — move the camera":"Share of faces below the enrolment gate"})]})]})}function vp({cameras:e,arrivals:t,onNavigate:n}){const{data:r}=et(()=>$.engineStatus(),4e3),[l,s]=P.useState(()=>{try{return localStorage.getItem("bv.gettingStarted")==="done"}catch{return!1}}),o=e.length>0,a=e.some(y=>y.connected),{data:u}=et(async()=>{const y={};for(const w of e)try{y[w.camera_id]=await $.placementResult(w.camera_id)}catch{}return y},1e4,[e.map(y=>y.camera_id).join("|")]),c=Object.values(u??{}).some(y=>y&&!y.running&&y.verdict==="good"),p=t.length>0;if(l||p)return null;const v=!!(r!=null&&r.reachable&&(r!=null&&r.recognition_model)),h=r==null?void 0:r.progress,g=[{done:v,now:!v,title:v?`Recognition ready (${r.recognition_model})`:h?`Downloading ${h.what}… ${h.percent}%`:"Starting recognition…",text:v?null:"First start downloads about 275 MB of recognition models. A few minutes on a normal connection; nothing to do meanwhile."},{done:o&&a,now:v&&!(o&&a),title:o?a?"Camera connected":"Camera added — not connecting yet":"Add your camera",text:o?a?null:"Check its password and stream path under Cameras → Edit.":"Behavision can find it on the network; you type only its password.",action:o?null:{label:"Add camera",go:"cameras"}},{done:c,now:o&&a&&!c,title:c?"Camera proven — it can recognise faces":"Walk past the camera",text:c?null:"Run Check placement and walk past like a customer for 25 seconds. Only a “good” verdict means it will recognise people.",action:o&&a&&!c?{label:"Check placement",go:"cameras"}:null}];return i.jsxs("section",{className:"panel starter",children:[i.jsxs("div",{className:"panelhead",children:[i.jsx("h3",{children:"Getting started"}),i.jsx("button",{className:"btn sm ghost",onClick:()=>{try{localStorage.setItem("bv.gettingStarted","done")}catch{}s(!0)},children:"Hide"})]}),i.jsx("ol",{className:"steps compact",children:g.map((y,w)=>i.jsxs("li",{className:y.done?"done":y.now?"now":"",children:[i.jsx("b",{children:y.title}),y.text&&i.jsx("span",{children:y.text}),y.action&&i.jsx("button",{className:"btn sm primary",onClick:()=>n==null?void 0:n(y.action.go),children:y.action.label})]},w))}),i.jsx("p",{className:"note",children:"The moment a customer is recognised, this list goes away."})]})}function gp({e,fresh:t}){var o,a,u,c,p,v;const n=e.type==="person.new",r=((o=e.data)==null?void 0:o.label)||"Unrecognised",l=[(a=e.data)==null?void 0:a.gender,((u=e.data)==null?void 0:u.age)??((c=e.data)==null?void 0:c.age_range),(p=e.data)==null?void 0:p.emotion].filter(Boolean),s=typeof((v=e.data)==null?void 0:v.similarity)=="number"?e.data.similarity:null;return i.jsxs("article",{className:`arrival ${n?"is-new":"is-seen"} ${t?"fresh":""}`,children:[i.jsx("div",{className:"avatar",children:yp(r)}),i.jsxs("div",{className:"who",children:[i.jsx("div",{className:"name",children:r}),i.jsxs("div",{className:"meta",children:[e.camera_id,l.length>0&&i.jsxs(i.Fragment,{children:[" · ",l.join(", ")]}),s!==null&&!n&&i.jsxs(i.Fragment,{children:[" · match ",s.toFixed(2)]})]})]}),i.jsxs("div",{className:"right",children:[i.jsx("span",{className:`tag ${n?"new":"seen"}`,children:n?"new":"returning"}),i.jsx("span",{className:"when",children:ip(e.ts)})]})]})}function yp(e){var r;const t=/^Visitor (\d+)$/.exec(String(e).trim());if(t)return t[1];const n=String(e).trim().split(/\s+/).filter(Boolean);return n.length?(n[0][0]+(((r=n[1])==null?void 0:r[0])??"")).toUpperCase():"?"}function xp({pipe:e,cameras:t,up:n}){if(!e)return null;if(e.standalone)return i.jsxs("div",{className:"statusbar",children:[i.jsxs("span",{className:"item",children:[i.jsx("i",{className:"dot ok"}),i.jsx("b",{children:"Running on this PC only"})]}),i.jsx("span",{className:"sep"}),i.jsx("span",{className:"item note",children:"Recognition and customers stay here."}),i.jsx("span",{className:"grow"}),i.jsxs("span",{className:"item note",children:[i.jsx(sa,{size:14}),n," of ",t.length," cameras"]})]});const r=e.claimed&&!e.broker_up,l=e.claimed?e.broker_up?"ok":"bad":"idle",s=e.claimed?e.broker_up?"Sending to head office":"Offline — saving locally":"Not linked to head office";return i.jsxs("div",{className:"statusbar",children:[i.jsxs("span",{className:"item",children:[e.broker_up?i.jsx(Nc,{size:15}):i.jsx(Cc,{size:15}),i.jsx("i",{className:`dot ${l}`}),i.jsx("b",{children:s})]}),i.jsx("span",{className:"sep"}),i.jsxs("span",{className:"item note",children:[e.accepted," recorded today"]}),e.queued>0&&i.jsxs("span",{className:`item note ${r?"warn":""}`,children:[e.queued," waiting to send"]}),e.dropped>0&&i.jsxs("span",{className:"item note bad",children:[i.jsx(Se,{size:14}),e.dropped," lost — this PC was offline too long"]}),i.jsx("span",{className:"grow"}),i.jsxs("span",{className:"item note",children:[i.jsx(sa,{size:14}),n," of ",t.length," cameras"]})]})}function Er({k:e,v:t,s:n,tone:r}){return i.jsxs("div",{className:`metric ${r??""}`,children:[i.jsx("div",{className:"metric-k",children:e}),i.jsx("div",{className:"metric-v",children:t}),n&&i.jsx("div",{className:"metric-s",children:n})]})}function wp(e){const[t,n]=P.useState(!1),r=P.useRef(null);return P.useEffect(()=>{if(!e||e.ts===r.current)return;const l=r.current===null;if(r.current=e.ts,l)return;n(!0);const s=setTimeout(()=>n(!1),1200);return()=>clearTimeout(s)},[e==null?void 0:e.ts]),t}function jp(e){const[t,n]=P.useState(null);return P.useEffect(()=>{let r=!0;return n(null),$.visitorPhoto(e).then(l=>{r&&n(l)}).catch(()=>{r&&n({available:!1,reason:""})}),()=>{r=!1}},[e]),t}function kp({photo:e,name:t,customerRef:n,onBroken:r}){return e!=null&&e.available?i.jsx("img",{className:"avatar",src:e.url,alt:`Photo of ${t}`,onError:r}):i.jsx("div",{className:"avatar none",role:"img","aria-label":`No photo of ${t}`,children:i.jsx("span",{children:Sp(t,n)})})}function Sp(e,t){const n=/^Visitor (\d+)$/.exec(String(e||"").trim());if(n)return n[1];const r=/^V-(\d+)$/.exec(String(t||""));return r?r[1]:String(e||"").split(/\s+/).filter(Boolean).slice(0,2).map(l=>l[0].toUpperCase()).join("")||"?"}function Np({customer:e}){const{data:t,error:n,loading:r}=et(()=>$.visitorHistory(e.id,50),0,[e.id]);if(r)return i.jsx("p",{className:"note",children:"Loading visits…"});if(n)return i.jsxs("p",{className:"note",children:["Could not load visits: ",n]});const l=t??[];return l.length===0?i.jsx("p",{className:"note",children:"No recorded visits yet."}):i.jsx("ul",{className:"timeline",children:l.map(s=>i.jsxs("li",{children:[i.jsx("span",{className:"when",children:Cp(s.occurred_at)}),i.jsxs("span",{className:"where",children:[s.site||"this store",s.camera_id?i.jsxs("span",{className:"note",children:[" · ",s.camera_id]}):null]}),s.is_new_visitor&&i.jsx("span",{className:"tag new",children:"first visit"})]},s.id))})}function Cp(e){const t=new Date(e);return isNaN(t)?"—":t.toLocaleString([],{day:"numeric",month:"short",hour:"2-digit",minute:"2-digit"})}function Ep({customer:e,onCancel:t,onDone:n}){const r=e.full_name||e.label,[l,s]=P.useState(""),[o,a]=P.useState(!1),[u,c]=P.useState(null),p=l.trim().toLowerCase()===r.trim().toLowerCase();async function v(){a(!0),c(null);try{await $.forgetCustomer(e.id),n()}catch(h){c(fe(h)),a(!1)}}return i.jsxs("div",{className:"confirm",children:[i.jsxs("h4",{children:["Erase ",r,"?"]}),i.jsxs("div",{className:"cols",children:[i.jsxs("div",{children:[i.jsx("p",{className:"lbl bad",children:"Deleted for good"}),i.jsxs("ul",{children:[i.jsx("li",{children:"Their face data — they will not be recognised again"}),i.jsx("li",{children:"Their photo"}),i.jsx("li",{children:"Their name, phone, email and notes"})]})]}),i.jsxs("div",{children:[i.jsx("p",{className:"lbl",children:"Kept"}),i.jsxs("ul",{children:[i.jsx("li",{children:"Past visits, with their name removed — your footfall figures do not change"}),i.jsx("li",{children:"The consent record, marked withdrawn, as proof of what was agreed"})]})]})]}),i.jsx("p",{className:"note",children:"This cannot be undone. If they come back they will be recorded as a new customer."}),u&&i.jsx("div",{className:"err",children:u}),i.jsxs("label",{className:"field",children:[i.jsxs("span",{children:["Type ",i.jsx("b",{children:r})," to confirm"]}),i.jsx("input",{value:l,onChange:h=>s(h.target.value),autoFocus:!0,autoComplete:"off",spellCheck:"false","aria-label":`Type ${r} to confirm erasure`})]}),i.jsxs("div",{className:"row",children:[i.jsx("button",{type:"button",className:"btn danger",disabled:!p||o,onClick:v,children:o?"Erasing…":"Erase permanently"}),i.jsx("button",{type:"button",className:"btn",onClick:t,disabled:o,children:"Cancel"})]})]})}function _p({customer:e,session:t,onClose:n,onSaved:r}){var C;const[l,s]=P.useState({full_name:e.full_name??"",phone:e.phone??"",email:e.email??"",gender:"",date_of_birth:"",notes:"",consent:e.has_consent??!1}),[o,a]=P.useState({amount:"",items:"",notes:""}),[u,c]=P.useState(!1),[p,v]=P.useState(null),[h,g]=P.useState(!1),[y,w]=P.useState(null),T=jp(e.id),f=y??T,d=(C=t==null?void 0:t.user)==null?void 0:C.role,m=["admin","owner","manager"].includes(d),x=k=>E=>s({...l,[k]:E.target.value});async function S(k){k.preventDefault(),c(!0),v(null);try{await $.saveProfile({visitor_id:e.id,...l});const E=parseFloat(o.amount);if(!isNaN(E)&&E>0){const z=o.items.split(",").map(L=>L.trim()).filter(Boolean);await $.recordPurchase(e.id,E,z,o.notes)}r()}catch(E){v(fe(E))}finally{c(!1)}}return i.jsx("div",{className:"drawer",onMouseDown:k=>k.target===k.currentTarget&&n(),children:i.jsxs("div",{className:"sheet",children:[i.jsxs("div",{className:"sheethead who",children:[i.jsx(kp,{photo:f,name:e.full_name||e.label,customerRef:e.ref,onBroken:()=>w({available:!1,reason:"The photo could not be loaded."})}),i.jsxs("div",{className:"grow",children:[i.jsx("h2",{children:e.full_name||e.label}),i.jsxs("p",{className:"note",children:[e.visit_count," visit",e.visit_count===1?"":"s",e.last_seen_at&&` · last seen ${new Date(e.last_seen_at).toLocaleDateString()}`,f&&!f.available&&f.reason&&` · ${f.reason}`]})]}),i.jsx("button",{type:"button",className:"close",onClick:n,"aria-label":"Close",children:"✕"})]}),i.jsxs("form",{className:"sheetbody",onSubmit:S,children:[p&&i.jsx("div",{className:"err",children:p}),i.jsxs("section",{className:"formsection",children:[i.jsx("h4",{children:"Customer details"}),i.jsxs("label",{className:"field",children:[i.jsx("span",{children:"Full name"}),i.jsx("input",{value:l.full_name,onChange:x("full_name"),autoFocus:!0})]}),i.jsxs("div",{className:"fieldrow",children:[i.jsxs("label",{className:"field",children:[i.jsx("span",{children:"Phone"}),i.jsx("input",{value:l.phone,onChange:x("phone"),inputMode:"tel"})]}),i.jsxs("label",{className:"field",children:[i.jsx("span",{children:"Email"}),i.jsx("input",{value:l.email,onChange:x("email"),type:"email"})]})]}),i.jsxs("div",{className:"fieldrow",children:[i.jsxs("label",{className:"field",children:[i.jsx("span",{children:"Gender"}),i.jsxs("select",{value:l.gender,onChange:x("gender"),children:[i.jsx("option",{value:"",children:"Not recorded"}),i.jsx("option",{children:"Female"}),i.jsx("option",{children:"Male"}),i.jsx("option",{children:"Other"})]})]}),i.jsxs("label",{className:"field",children:[i.jsx("span",{children:"Date of birth"}),i.jsx("input",{type:"date",value:l.date_of_birth,onChange:x("date_of_birth")})]})]}),i.jsxs("label",{className:"field",children:[i.jsx("span",{children:"Notes"}),i.jsx("textarea",{value:l.notes,onChange:x("notes"),placeholder:"Preferences, sizes, anything worth remembering"})]})]}),i.jsxs("section",{className:"formsection",children:[i.jsx("h4",{children:"Purchase (optional)"}),i.jsxs("div",{className:"fieldrow",children:[i.jsxs("label",{className:"field",children:[i.jsx("span",{children:"Amount"}),i.jsx("input",{value:o.amount,inputMode:"decimal",placeholder:"0.00",onChange:k=>a({...o,amount:k.target.value})})]}),i.jsxs("label",{className:"field",children:[i.jsx("span",{children:"Items"}),i.jsx("input",{value:o.items,placeholder:"shirt, belt",onChange:k=>a({...o,items:k.target.value})})]})]}),i.jsx("p",{className:"note",children:"Leave the amount blank if they did not buy anything — a visit without a sale is still worth recording."})]}),i.jsxs("section",{className:"formsection",children:[i.jsx("h4",{children:"Consent"}),i.jsxs("label",{style:{display:"flex",gap:10,alignItems:"flex-start",fontSize:13,cursor:"pointer"},children:[i.jsx("input",{type:"checkbox",checked:l.consent,style:{marginTop:3},onChange:k=>s({...l,consent:k.target.checked})}),i.jsx("span",{children:"This customer agreed to us keeping their details and recognising them on future visits."})]}),i.jsx("p",{className:"note",style:{marginTop:10},children:"Recorded with the date and who collected it. They can withdraw it at any time, which erases their face data."})]}),i.jsxs("section",{className:"formsection",children:[i.jsx("h4",{children:"Visits"}),i.jsx(Np,{customer:e})]}),i.jsxs("div",{className:"sheetactions",children:[i.jsx("button",{type:"button",className:"btn",onClick:n,children:"Cancel"}),i.jsx("button",{className:"btn primary",disabled:u,children:u?"Saving…":"Save"})]}),m&&i.jsxs("section",{className:"formsection dangerzone",children:[i.jsx("h4",{children:"At the customer's request"}),h?i.jsx(Ep,{customer:e,onCancel:()=>g(!1),onDone:r}):i.jsxs(i.Fragment,{children:[i.jsx("p",{className:"note",children:"Erase this person's face data, photo and details. Their past visits stay in your footfall figures, without their name."}),i.jsx("button",{type:"button",className:"btn danger",onClick:()=>g(!0),children:"Erase this customer…"})]})]})]})]})})}function Pp({session:e}){const[t,n]=P.useState(""),[r,l]=P.useState(null),{data:s,error:o,reload:a}=et(()=>$.customers(t,200),3e4,[t]),u=s??[],c=u.filter(p=>p.has_profile).length;return i.jsxs("div",{className:"page",children:[i.jsxs("header",{className:"pagehead",children:[i.jsxs("div",{children:[i.jsx("h2",{children:"Customers"}),i.jsx("p",{children:"Everyone this business has recognised. Fill in details once and they are known at every store."})]}),i.jsxs("label",{className:"search",children:[i.jsx(kc,{size:15}),i.jsx("input",{placeholder:"Search by name or phone",value:t,onChange:p=>n(p.target.value)})]})]}),o&&i.jsxs("div",{className:"err",children:[i.jsx(Se,{size:15}),o]}),i.jsxs("div",{className:"metrics",style:{marginBottom:"var(--sp-4)"},children:[i.jsx(_r,{k:"Known people",v:u.length||"—"}),i.jsx(_r,{k:"With details",v:c||"—",s:u.length?`${Math.round(100*c/u.length)}% captured`:null}),i.jsx(_r,{k:"Returning",v:u.filter(p=>p.visit_count>1).length||"—"}),i.jsx(_r,{k:"With consent",v:u.filter(p=>p.has_consent).length||"—"})]}),i.jsx("div",{className:"panel",children:u.length===0?i.jsxs("div",{className:"empty tall",children:[i.jsx(_c,{size:34}),i.jsx("b",{children:"No customers yet"}),i.jsx("p",{children:"They appear here the first time a camera recognises them. Click one to add a name and phone number."})]}):i.jsx("div",{className:"tablewrap",children:i.jsxs("table",{children:[i.jsx("thead",{children:i.jsxs("tr",{children:[i.jsx("th",{children:"Customer"}),i.jsx("th",{children:"Phone"}),i.jsx("th",{className:"num",children:"Visits"}),i.jsx("th",{children:"First seen"}),i.jsx("th",{children:"Last seen"}),i.jsx("th",{children:"Details"})]})}),i.jsx("tbody",{children:u.map(p=>i.jsxs("tr",{className:"click",onClick:()=>l(p),children:[i.jsxs("td",{children:[p.full_name||i.jsx("span",{className:"note",children:p.label}),p.full_name&&p.ref&&i.jsxs("span",{className:"note",children:[" · ",p.ref]})]}),i.jsx("td",{className:"mono",children:p.phone||"—"}),i.jsx("td",{className:"num",children:p.visit_count}),i.jsx("td",{children:ra(p.first_seen_at)}),i.jsx("td",{children:ra(p.last_seen_at)}),i.jsx("td",{children:p.has_profile?i.jsxs("span",{className:"pill ok",children:[i.jsx("i",{className:"dot ok"}),"captured"]}):i.jsxs("span",{className:"pill warn",children:[i.jsx("i",{className:"dot warn"}),"needed"]})})]},p.id))})]})})}),r&&i.jsx(_p,{customer:r,session:e,onClose:()=>l(null),onSaved:()=>{l(null),a()}})]})}function _r({k:e,v:t,s:n}){return i.jsxs("div",{className:"metric",children:[i.jsx("div",{className:"metric-k",children:e}),i.jsx("div",{className:"metric-v",children:t}),n&&i.jsx("div",{className:"metric-s",children:n})]})}const ln=[{id:"hikvision",label:"Hikvision",path:"/Streaming/Channels/101",note:"Channel 1, main stream. Use /Streaming/Channels/102 for the lower-quality sub stream."},{id:"dahua",label:"Dahua",path:"/cam/realmonitor?channel=1&subtype=0",note:"Channel 1, main stream. subtype=1 is the sub stream."},{id:"cpplus",label:"CP Plus",path:"/cam/realmonitor?channel=1&subtype=0",note:"CP Plus cameras use the Dahua stream path."},{id:"uniview",label:"Uniview",path:"/media/video1",note:"Some older Uniview models use /video1 instead."},{id:"tplink",label:"TP-Link / Tapo",path:"/stream1",note:"Tapo cameras need a separate camera account created in the Tapo app — your Tapo login will not work."},{id:"reolink",label:"Reolink",path:"/h264Preview_01_main",note:"Use /h264Preview_01_sub for the lower-quality stream."},{id:"amcrest",label:"Amcrest",path:"/cam/realmonitor?channel=1&subtype=0",note:"Amcrest cameras use the Dahua stream path."},{id:"axis",label:"Axis",path:"/axis-media/media.amp",note:""},{id:"onvif",label:"Other (ONVIF)",path:"/onvif1",note:"Many generic cameras answer here. If it does not work, look for “RTSP” in the camera’s own app."},{id:"manual",label:"I know the path",path:"",note:""}],Pr=e=>ln.find(t=>t.id===e)||ln[ln.length-1],Zs={id:"",host:"",port:554,path:"",username:"",password:"",max_width:1280};function zp(){const{data:e,error:t,reload:n}=et(()=>$.cameras(),8e3),[r,l]=P.useState(null),[s,o]=P.useState(null),a=e??[],u=Tp(a);async function c(p){if(confirm(`Remove ${p.id}? Recognition from it stops immediately.`))try{await $.deleteCamera(p.id),n()}catch(v){alert(fe(v))}}return i.jsxs("div",{className:"page",children:[i.jsxs("header",{className:"pagehead",children:[i.jsxs("div",{children:[i.jsx("h2",{children:"Cameras"}),i.jsx("p",{children:"Add a camera, then prove it can see faces with a walk-past. Only then is it working."})]}),i.jsxs("button",{className:"btn primary",onClick:()=>l({...Zs}),children:[i.jsx(la,{size:15}),"Add camera"]})]}),t&&i.jsxs("div",{className:"err",children:[i.jsx(Se,{size:15}),t]}),a.length===0?i.jsx("div",{className:"panel",children:i.jsxs("div",{className:"empty tall",children:[i.jsx(Ec,{size:40}),i.jsx("b",{children:"No cameras yet"}),i.jsx("p",{children:"Add the camera by its address. Most cameras print it on a label underneath, or show it in their own app."}),i.jsxs("button",{className:"btn primary",onClick:()=>l({...Zs}),children:[i.jsx(la,{size:15}),"Add your first camera"]})]})}):i.jsx("div",{className:"camgrid",children:a.map(p=>i.jsx(Lp,{cam:p,stream:u[p.id],onEdit:()=>l(p),onCheck:()=>o(p.id),onRemove:()=>c(p)},p.id))}),r&&i.jsx(Mp,{cam:r,onClose:()=>l(null),onSaved:()=>{l(null),n()}}),s&&i.jsx(Rp,{id:s,onClose:()=>o(null)})]})}function Lp({cam:e,stream:t,onEdit:n,onCheck:r,onRemove:l}){const s=e.connected===void 0?{tone:"idle",label:"Engine stopped"}:e.connected?{tone:"ok",label:"Connected"}:{tone:"bad",label:"Not connecting"},{data:o}=et(()=>$.placementResult(e.id),15e3,[e.id]),a=!o||o.running||!o.verdict||o.verdict==="starting"?{tone:"miss",label:"Not yet proven",text:"Walk past it once and Behavision will tell you if the placement works."}:o.verdict==="good"?{tone:"seen",label:"Proven",text:o.headline||"Faces recognised on a walk-past."}:{tone:"miss",label:"Not proven",text:o.headline||"Move the camera and check again."};return i.jsxs("article",{className:"camcard",children:[i.jsxs("div",{className:"camview",children:[t?i.jsx("img",{src:t,alt:e.id}):i.jsx("div",{className:"placeholder",children:i.jsx(Ec,{size:34})}),i.jsxs("span",{className:`pill ${s.tone==="idle"?"":s.tone} over`,children:[i.jsx("i",{className:`dot ${s.tone}`}),s.label]})]}),i.jsxs("div",{className:"cambody",children:[i.jsxs("div",{className:"camtitle",children:[i.jsxs("div",{children:[i.jsx("h3",{children:e.id}),i.jsxs("span",{className:"mono note",children:[e.host||e.url,e.path?` · ${e.path}`:""]})]}),i.jsxs("div",{className:"camactions",children:[i.jsx("button",{className:"btn sm",onClick:n,children:"Edit"}),i.jsx("button",{className:"btn sm danger",onClick:l,children:"Remove"})]})]}),i.jsxs("div",{className:"camproof",children:[i.jsx("span",{className:`tag ${a.tone}`,children:a.label}),i.jsx("span",{className:"note",children:a.text}),i.jsxs("button",{className:`btn sm ${a.tone==="seen"?"":"primary"}`,onClick:r,children:[i.jsx(Sc,{size:13}),a.tone==="seen"?"Check again":"Check placement"]})]})]})]})}function Tp(e){const[t,n]=P.useState({});return P.useEffect(()=>{let r=!0;const l=e.filter(s=>!(s.id in t));if(l.length)return(async()=>{const s={};for(const o of l)try{s[o.id]=await $.streamURL(o.id)}catch{s[o.id]=""}r&&n(o=>({...o,...s}))})(),()=>{r=!1}},[e.map(r=>r.id).join("|")]),t}function Mp({cam:e,onClose:t,onSaved:n}){var E;const r=!e.id,[l,s]=P.useState({...Zs,...e,password:"",path:e.path||(r?ln[0].path:"")}),[o,a]=P.useState(r?ln[0].id:"manual"),[u,c]=P.useState(null),[p,v]=P.useState(null),[h,g]=P.useState(null),y=z=>L=>s({...l,[z]:L.target.value});function w(z){const L=Pr(z.target.value);a(L.id),s(V=>({...V,path:L.path||V.path}))}function T(){const z={};for(const[L,V]of Object.entries(l))V===""||V===null||V===void 0||(z[L]=L==="port"||L==="max_width"?Number(V):V);return z}async function f(){v("test"),g(null),c(null);try{c(await $.testCamera(T()))}catch(z){g(fe(z))}finally{v(null)}}async function d(z){z.preventDefault(),v("save"),g(null);try{await $.saveCamera(r?"":e.id,T()),n()}catch(L){g(fe(L))}finally{v(null)}}const m=Pr(o),[x,S]=P.useState(null);async function C(){S("busy");try{S(await $.discoverCameras())}catch(z){S({error:fe(z)})}}function k(z){const L=z.make?Pr(z.make):null;s(V=>({...V,host:z.host,path:(L==null?void 0:L.path)||V.path,id:V.id||""})),L&&a(L.id),c(null)}return i.jsx("div",{className:"drawer",onMouseDown:z=>z.target===z.currentTarget&&t(),children:i.jsxs("div",{className:"sheet",children:[i.jsxs("div",{className:"sheethead",children:[i.jsx("h2",{children:r?"Add a camera":`Edit ${e.id}`}),i.jsx("button",{className:"close",onClick:t,"aria-label":"Close",children:i.jsx(Qi,{size:16})})]}),i.jsxs("form",{className:"sheetbody",onSubmit:d,autoComplete:"off",children:[i.jsx("p",{className:"lead",children:"Three things from the camera: its address, its make, and its password. Test before you save — a wrong address is the most common mistake."}),h&&i.jsxs("div",{className:"err",children:[i.jsx(Se,{size:15}),h]}),r&&i.jsxs("section",{className:"formsection",children:[i.jsx("h4",{children:"Find it"}),x===null&&i.jsxs("div",{className:"finder",children:[i.jsx("p",{children:"Behavision can look for cameras on this shop’s network."}),i.jsxs("button",{type:"button",className:"btn primary",onClick:C,children:[i.jsx(kc,{size:14}),"Find cameras on this network"]})]}),x==="busy"&&i.jsxs("div",{className:"finder busy",children:[i.jsx("span",{className:"spinner"}),"Looking on the network… a few seconds."]}),(x==null?void 0:x.error)&&i.jsxs("div",{className:"err",children:[i.jsx(Se,{size:15}),x.error]}),(x==null?void 0:x.cameras)&&(x.cameras.length===0?i.jsxs("div",{className:"finder",children:[i.jsxs("p",{children:["Nothing answered on ",((E=x.networks)==null?void 0:E.join(", "))||"this network",". The camera may be on a different network, switched off, or not yet connected — check its cable and power, then try again. You can still type its address below."]}),i.jsx("button",{type:"button",className:"btn",onClick:C,children:"Try again"})]}):i.jsxs("ul",{className:"foundlist",children:[x.cameras.map(z=>i.jsx("li",{className:l.host===z.host?"picked":"",children:i.jsxs("button",{type:"button",onClick:()=>k(z),children:[i.jsx("span",{className:"mono",children:z.host}),i.jsx("span",{className:"what",children:z.name||(z.rtsp?"Streams video (RTSP)":"Answers ONVIF")}),z.make&&i.jsx("span",{className:"tag seen",children:Pr(z.make).label}),l.host===z.host&&i.jsx(Xs,{size:16})]})},z.host)),i.jsx("li",{className:"rescan",children:i.jsx("button",{type:"button",className:"btn sm",onClick:C,children:"Scan again"})})]}))]}),i.jsxs("section",{className:"formsection",children:[i.jsx("h4",{children:"The camera"}),r&&i.jsxs("label",{className:"field",children:[i.jsx("span",{children:"Name"}),i.jsx("input",{value:l.id,onChange:y("id"),placeholder:"entrance",required:!0,autoFocus:!0}),i.jsx("em",{className:"hint",children:"Short, no spaces. It names this camera everywhere and cannot be changed later."})]}),i.jsxs("div",{className:"fieldrow",children:[i.jsxs("label",{className:"field",children:[i.jsx("span",{children:"Address"}),i.jsx("input",{value:l.host,onChange:y("host"),placeholder:"192.168.1.20",inputMode:"decimal"}),i.jsx("em",{className:"hint",children:"On a label on the camera, or in its own app under “network”."})]}),i.jsxs("label",{className:"field narrow",children:[i.jsx("span",{children:"Port"}),i.jsx("input",{value:l.port,onChange:y("port"),inputMode:"numeric"})]})]})]}),i.jsxs("section",{className:"formsection",children:[i.jsx("h4",{children:"The stream"}),i.jsxs("label",{className:"field",children:[i.jsx("span",{children:"Make of camera"}),i.jsx("select",{value:o,onChange:w,children:ln.map(z=>i.jsx("option",{value:z.id,children:z.label},z.id))}),m.note&&i.jsx("em",{className:"hint",children:m.note})]}),i.jsxs("label",{className:"field",children:[i.jsx("span",{children:"Stream path"}),i.jsx("input",{className:"mono",value:l.path,onChange:y("path"),placeholder:"/Streaming/Channels/101"}),i.jsx("em",{className:"hint",children:"Filled in from the make. Change it only if the camera’s own app says something else."})]})]}),i.jsxs("section",{className:"formsection",children:[i.jsx("h4",{children:"Sign-in to the camera"}),i.jsxs("div",{className:"fieldrow",children:[i.jsxs("label",{className:"field",children:[i.jsx("span",{children:"Username"}),i.jsx("input",{name:"rtsp-account",autoComplete:"off",value:l.username,onChange:y("username"),placeholder:"admin"})]}),i.jsxs("label",{className:"field",children:[i.jsx("span",{children:"Password"}),i.jsx("input",{type:"password",name:"rtsp-secret",autoComplete:"new-password",value:l.password,onChange:y("password"),placeholder:e.has_password?"(unchanged)":""})]})]})]}),u&&(u.ok?i.jsxs("div",{className:"testresult ok",children:[i.jsx(Xs,{size:16}),i.jsxs("div",{children:[i.jsxs("b",{children:["Connected — ",u.width,"×",u.height,u.codec?` · ${String(u.codec).toUpperCase()}`:""]}),u.snapshot&&i.jsx("img",{alt:"Camera preview",src:`data:image/jpeg;base64,${u.snapshot}`})]})]}):i.jsxs("div",{className:"testresult bad",children:[i.jsx(Se,{size:16}),i.jsxs("div",{children:[i.jsx("b",{children:"Could not connect"}),i.jsx("span",{children:u.error})]})]})),i.jsxs("div",{className:"sheetactions",children:[i.jsx("button",{type:"button",className:"btn",onClick:f,disabled:!!p||!l.host,children:p==="test"?"Connecting…":"Test connection"}),i.jsx("button",{className:"btn primary",disabled:!!p||!l.id||!l.host,children:p==="save"?"Saving…":r?"Add camera":"Save changes"})]})]})]})})}function Rp({id:e,onClose:t}){var p,v;const[n,r]=P.useState({verdict:"starting",advice:[]}),[l,s]=P.useState(null),o=P.useRef(null);P.useEffect(()=>{let h=!0;return(async()=>{try{r(await $.startPlacement(e,25)),o.current=setInterval(async()=>{try{const g=await $.placementResult(e);if(!h)return;r(g),g.running||clearInterval(o.current)}catch(g){h&&s(fe(g))}},1e3)}catch(g){h&&s(fe(g))}})(),()=>{h=!1,clearInterval(o.current)}},[e]);const a={good:"ok",marginal:"warn",poor:"bad",artifact:"bad",no_faces:"warn",inconclusive:"warn",no_completed_passes:"warn"}[n.verdict],u=n.seconds?Math.min(100,n.elapsed/n.seconds*100):0,c=n.running||n.verdict==="starting";return i.jsx("div",{className:"drawer",onMouseDown:h=>h.target===h.currentTarget&&t(),children:i.jsxs("div",{className:"sheet",children:[i.jsxs("div",{className:"sheethead",children:[i.jsxs("h2",{children:["Check placement · ",e]}),i.jsx("button",{className:"close",onClick:t,"aria-label":"Close",children:i.jsx(Qi,{size:16})})]}),i.jsxs("div",{className:"sheetbody",children:[l&&i.jsxs("div",{className:"err",children:[i.jsx(Se,{size:15}),l]}),i.jsxs("ol",{className:"steps",children:[i.jsxs("li",{className:c?"now":"done",children:[i.jsx("b",{children:"Walk past the camera"}),i.jsx("span",{children:"The way a customer would — in through the door, not looking at the lens. Two or three times, for about 25 seconds."}),c&&i.jsx("div",{className:"progress",children:i.jsx("div",{style:{width:`${u}%`}})})]}),i.jsxs("li",{className:c?"":"now",children:[i.jsx("b",{children:"Behavision judges what it saw"}),i.jsx("span",{children:"Not “were the frames sharp”, but “did a person walking past produce a face worth recognising”."})]})]}),i.jsxs("div",{className:`verdict ${a??""}`,children:[i.jsxs("div",{className:"verdict-head",children:[c?i.jsx("span",{className:"spinner"}):a==="ok"?i.jsx(Xs,{size:18}):i.jsx(Se,{size:18}),i.jsx("b",{children:n.headline||"Watching…"})]}),((p=n.advice)==null?void 0:p.length)>0&&i.jsx("ul",{children:n.advice.map((h,g)=>i.jsx("li",{children:h},g))}),((v=n.quality)==null?void 0:v.n)>0&&i.jsxs("p",{className:"note",children:[n.quality.n," face",n.quality.n===1?"":"s"," seen · median quality ",n.quality.p50," ·"," ",Math.round((n.quality.fraction_below_gate??0)*100),"% too poor to use"]})]}),!c&&i.jsxs("div",{className:"sheetactions",children:[i.jsx("button",{className:"btn",onClick:t,children:"Close"}),i.jsx("button",{className:"btn primary",onClick:()=>{r({verdict:"starting",advice:[]}),$.startPlacement(e,25).then(r).catch(h=>s(fe(h)))},children:"Run again"})]})]})]})})}const Dp=["Is my shop working right now?","How do I add my camera?","Why has nobody been recognised today?","Who came in this morning?"];function Op({session:e,onClose:t}){const[n,r]=P.useState([]),[l,s]=P.useState(""),[o,a]=P.useState(!1),[u,c]=P.useState(null),p=P.useRef(null),v=e==null?void 0:e.standalone;P.useEffect(()=>{var g;(g=p.current)==null||g.scrollTo({top:1e9,behavior:"smooth"})},[n,o]);async function h(g){const y=(g??l).trim();if(!y||o)return;const w=[...n,{role:"user",text:y}];r(w),s(""),a(!0),c(null);try{const T=await $.ask(w.map(f=>({role:f.role,text:f.text})));r([...w,{role:"assistant",text:T.text,used:T.used??[]}])}catch(T){const f=fe(T);c(/session expired|unauthori[sz]ed/i.test(f)?"You’re signed out of head office, so I can’t look anything up. Sign in again and ask me once more.":f),r(n),s(y)}finally{a(!1)}}return i.jsxs("aside",{className:"helper",role:"dialog","aria-label":"Loya",children:[i.jsxs("header",{className:"helperhead",children:[i.jsx("img",{src:gt,alt:""}),i.jsxs("div",{children:[i.jsx("b",{children:"Loya"}),i.jsx("span",{children:"Your Behavision buddy — knows your cameras, your customers and your numbers."})]}),i.jsx("button",{className:"close",onClick:t,"aria-label":"Close",children:i.jsx(Qi,{size:16})})]}),i.jsxs("div",{className:"helperbody",ref:p,children:[v&&i.jsxs("div",{className:"helpernote",children:[i.jsx(Cc,{size:16}),i.jsxs("div",{children:[i.jsx("b",{children:"This PC runs on its own"}),i.jsx("span",{children:"Loya lives at head office. Link this PC to a shop to talk to her; the basics are below."})]})]}),n.length===0&&i.jsxs("div",{className:"helperintro",children:[i.jsx("div",{className:"turn assistant",children:i.jsxs("div",{className:"bubble",children:[Fp(e)," Ask me anything about ",(e==null?void 0:e.site_name)||"this shop"," — what’s happening now, or how to set something up."]})}),i.jsx("div",{className:"chips",children:Dp.map(g=>i.jsx("button",{className:"chip",onClick:()=>h(g),disabled:o||v,children:g},g))}),v&&i.jsx(Ip,{})]}),n.map((g,y)=>{var w;return i.jsxs("div",{className:`turn ${g.role}`,children:[i.jsx("div",{className:"bubble",children:g.text}),((w=g.used)==null?void 0:w.length)>0&&i.jsxs("span",{className:"used",children:["Looked at: ",g.used.join(", ")]})]},y)}),o&&i.jsx("div",{className:"turn assistant",children:i.jsxs("div",{className:"bubble thinking",children:[i.jsx("span",{}),i.jsx("span",{}),i.jsx("span",{})]})}),u&&i.jsxs("div",{className:"err",children:[i.jsx(Se,{size:15}),u]})]}),i.jsxs("form",{className:"helperask",onSubmit:g=>{g.preventDefault(),h()},children:[i.jsx("input",{value:l,onChange:g=>s(g.target.value),disabled:o||v,placeholder:v?"Link this PC to head office to talk to Loya":"Ask Loya…"}),i.jsx("button",{className:"btn primary",disabled:o||v||!l.trim(),"aria-label":"Ask",children:i.jsx(hp,{size:16})})]})]})}function Fp(e){var l,s;const t=new Date().getHours(),n=t<12?"Morning":t<17?"Afternoon":"Evening",r=(s=(l=e==null?void 0:e.user)==null?void 0:l.full_name)==null?void 0:s.split(" ")[0];return r?`${n}, ${r}.`:`${n}.`}function Ip(){return i.jsxs("dl",{className:"quickhelp",children:[i.jsx("dt",{children:"Adding a camera"}),i.jsx("dd",{children:"Cameras → Add camera. The address is on a label on the camera; pick the make and the stream path fills itself in. Test, then save."}),i.jsx("dt",{children:"Proving it works"}),i.jsx("dd",{children:"Press Check placement and walk past the camera like a customer for 25 seconds. Only “good” means it can recognise faces — otherwise move it to head height, facing the way people approach."}),i.jsx("dt",{children:"Nothing showing on Live"}),i.jsx("dd",{children:"Check the camera is Connected and Proven. A camera aimed from above or the side streams fine and recognises nobody."}),i.jsx("dt",{children:"Linking to head office later"}),i.jsx("dd",{children:"Use the link button at the bottom of the sidebar and type an installation code from head office. Nothing recorded here is lost."})]})}const $p=[{id:"live",label:"Live",Glyph:op,View:Pc},{id:"customers",label:"Customers",Glyph:ap,View:Pp,cloud:!0},{id:"cameras",label:"Cameras",Glyph:up,View:zp}];function Up(){var h,g,y;const[e,t]=P.useState(null),[n,r]=P.useState("live"),[l,s]=P.useState(!0),[o,a]=P.useState(!1),[u,c]=P.useState(!1);if(P.useEffect(()=>{let w=!0;const T=async()=>{try{const d=await $.session();w&&t(m=>JSON.stringify(m)===JSON.stringify(d)?m:d)}catch{w&&t(null)}w&&s(!1)};T();const f=setInterval(T,8e3);return()=>{w=!1,clearInterval(f)}},[]),!sp())return i.jsx("div",{className:"login",children:i.jsxs("div",{className:"box",children:[i.jsx("span",{className:"mark",children:i.jsx("img",{src:gt,alt:""})}),i.jsx("h1",{children:"Behavision"}),i.jsx("p",{className:"lead",children:"This is the Behavision window running outside the app, so it has no connection to the recognition engine. Launch the Behavision application instead."})]})});if(l)return i.jsx("div",{className:"login",children:i.jsx("p",{className:"note",children:"Starting…"})});if(!(e!=null&&e.claimed)&&!(e!=null&&e.standalone))return i.jsx(oa,{onDone:t});if(!(e!=null&&e.standalone)&&!(e!=null&&e.logged_in))return i.jsx(mp,{onDone:t});const p=$p.filter(w=>!w.cloud||!e.standalone),v=((h=p.find(w=>w.id===n))==null?void 0:h.View)??Pc;return o?i.jsx(oa,{onDone:w=>{a(!1),t(w)},onCancel:()=>a(!1)}):i.jsxs("div",{className:"shell",children:[i.jsxs("aside",{className:"side",children:[i.jsxs("div",{className:"brand",children:[i.jsx("span",{className:"mark",children:i.jsx("img",{src:gt,alt:""})}),i.jsxs("div",{className:"id",children:[i.jsx("h1",{children:"Behavision"}),i.jsx("p",{children:e.site_name||((g=e.user)==null?void 0:g.client_name)||"This shop"})]})]}),i.jsx("nav",{className:"nav",children:p.map(({id:w,label:T,Glyph:f})=>i.jsxs("button",{onClick:()=>r(w),"aria-current":w===n?"page":void 0,children:[i.jsx(f,{size:17}),T]},w))}),i.jsx(Ap,{}),i.jsx("div",{className:"who",children:e.standalone?i.jsxs(i.Fragment,{children:[i.jsxs("div",{className:"id",children:[i.jsx("b",{children:"On its own"}),i.jsx("span",{children:"No head office"})]}),i.jsx("button",{className:"btn sm icon",title:"Link to head office",onClick:()=>a(!0),children:i.jsx(fp,{size:15})})]}):i.jsxs(i.Fragment,{children:[i.jsxs("div",{className:"id",children:[i.jsx("b",{children:"Signed in"}),i.jsx("span",{children:(y=e.user)==null?void 0:y.email})]}),i.jsx("button",{className:"btn sm icon",title:"Sign out",onClick:async()=>t(await $.logout()),children:i.jsx(pp,{size:15})})]})})]}),i.jsxs("main",{className:"main",children:[i.jsx(v,{session:e,onNavigate:r}),!u&&i.jsxs("button",{className:"loya-fab",onClick:()=>c(!0),"aria-label":"Ask Loya",title:"Ask Loya",children:[i.jsx("img",{src:gt,alt:""}),i.jsx("span",{children:"Loya"})]})]}),u&&i.jsx(Op,{session:e,onClose:()=>c(!1)})]})}function Ap(){const{data:e,reload:t}=et(()=>$.engineStatus(),5e3),[n,r]=P.useState(!1),l=e??{state:"stopped"},s=P.useCallback(async v=>{r(!0);try{await v()}catch(h){alert(fe(h))}finally{r(!1),t()}},[t]),o=l.state==="running",a=Object.values(l.cameras??{}),u=a.filter(Boolean).length;let c="idle",p="Stopped";return!o&&l.reachable?(c="warn",p="Running outside the app"):l.state==="failed"||l.state==="backoff"?(c="bad",p="Not running"):o&&!l.reachable&&l.progress?(c="warn",p=`Downloading ${l.progress.what}… ${l.progress.percent}%`):o&&!l.reachable?(c="warn",p="Starting…"):o&&a.length===0?(c="warn",p="No cameras"):o&&u===0?(c="bad",p="No camera connected"):o&&us($.startEngine),children:[i.jsx(Sc,{size:13}),"Start"]}),i.jsxs("button",{className:"btn sm",disabled:n||!o,onClick:()=>s($.stopEngine),children:[i.jsx(cp,{size:13}),"Stop"]})]})]})}wc(document.getElementById("root")).render(i.jsx(Kc.StrictMode,{children:i.jsx(Up,{})})); +`+s.stack}return{value:e,source:t,stack:l,digest:null}}function ql(e,t,n){return{value:e,source:null,stack:n??null,digest:t??null}}function Os(e,t){try{console.error(t.value)}catch(n){setTimeout(function(){throw n})}}var Rf=typeof WeakMap=="function"?WeakMap:Map;function Yu(e,t,n){n=Xe(-1,n),n.tag=3,n.payload={element:null};var r=t.value;return n.callback=function(){ol||(ol=!0,Qs=r),Os(e,t)},n}function Gu(e,t,n){n=Xe(-1,n),n.tag=3;var r=e.type.getDerivedStateFromError;if(typeof r=="function"){var l=t.value;n.payload=function(){return r(l)},n.callback=function(){Os(e,t)}}var s=e.stateNode;return s!==null&&typeof s.componentDidCatch=="function"&&(n.callback=function(){Os(e,t),typeof r!="function"&&(ht===null?ht=new Set([this]):ht.add(this));var o=t.stack;this.componentDidCatch(t.value,{componentStack:o!==null?o:""})}),n}function Uo(e,t,n){var r=e.pingCache;if(r===null){r=e.pingCache=new Rf;var l=new Set;r.set(t,l)}else l=r.get(t),l===void 0&&(l=new Set,r.set(t,l));l.has(n)||(l.add(n),e=Yf.bind(null,e,t,n),t.then(e,e))}function Ao(e){do{var t;if((t=e.tag===13)&&(t=e.memoizedState,t=t!==null?t.dehydrated!==null:!0),t)return e;e=e.return}while(e!==null);return null}function Bo(e,t,n,r,l){return e.mode&1?(e.flags|=65536,e.lanes=l,e):(e===t?e.flags|=65536:(e.flags|=128,n.flags|=131072,n.flags&=-52805,n.tag===1&&(n.alternate===null?n.tag=17:(t=Xe(-1,1),t.tag=2,pt(n,t,1))),n.lanes|=1),e)}var Df=tt.ReactCurrentOwner,ve=!1;function ce(e,t,n,r){t.child=e===null?Su(t,null,n,r):un(t,e.child,n,r)}function Vo(e,t,n,r,l){n=n.render;var s=t.ref;return nn(t,l),r=zi(e,t,n,r,s,l),n=Ti(),e!==null&&!ve?(t.updateQueue=e.updateQueue,t.flags&=-2053,e.lanes&=~l,be(e,t,l)):(H&&n&&gi(t),t.flags|=1,ce(e,t,r,l),t.child)}function Ho(e,t,n,r,l){if(e===null){var s=n.type;return typeof s=="function"&&!Ai(s)&&s.defaultProps===void 0&&n.compare===null&&n.defaultProps===void 0?(t.tag=15,t.type=s,Xu(e,t,s,r,l)):(e=Ar(n.type,null,r,t,t.mode,l),e.ref=t.ref,e.return=t,t.child=e)}if(s=e.child,!(e.lanes&l)){var o=s.memoizedProps;if(n=n.compare,n=n!==null?n:Kn,n(o,r)&&e.ref===t.ref)return be(e,t,l)}return t.flags|=1,e=vt(s,r),e.ref=t.ref,e.return=t,t.child=e}function Xu(e,t,n,r,l){if(e!==null){var s=e.memoizedProps;if(Kn(s,r)&&e.ref===t.ref)if(ve=!1,t.pendingProps=r=s,(e.lanes&l)!==0)e.flags&131072&&(ve=!0);else return t.lanes=e.lanes,be(e,t,l)}return Fs(e,t,n,r,l)}function Zu(e,t,n){var r=t.pendingProps,l=r.children,s=e!==null?e.memoizedState:null;if(r.mode==="hidden")if(!(t.mode&1))t.memoizedState={baseLanes:0,cachePool:null,transitions:null},U(Jt,we),we|=n;else{if(!(n&1073741824))return e=s!==null?s.baseLanes|n:n,t.lanes=t.childLanes=1073741824,t.memoizedState={baseLanes:e,cachePool:null,transitions:null},t.updateQueue=null,U(Jt,we),we|=e,null;t.memoizedState={baseLanes:0,cachePool:null,transitions:null},r=s!==null?s.baseLanes:n,U(Jt,we),we|=r}else s!==null?(r=s.baseLanes|n,t.memoizedState=null):r=n,U(Jt,we),we|=r;return ce(e,t,l,n),t.child}function Ju(e,t){var n=t.ref;(e===null&&n!==null||e!==null&&e.ref!==n)&&(t.flags|=512,t.flags|=2097152)}function Fs(e,t,n,r,l){var s=ye(n)?Mt:ue.current;return s=on(t,s),nn(t,l),n=zi(e,t,n,r,s,l),r=Ti(),e!==null&&!ve?(t.updateQueue=e.updateQueue,t.flags&=-2053,e.lanes&=~l,be(e,t,l)):(H&&r&&gi(t),t.flags|=1,ce(e,t,n,l),t.child)}function Wo(e,t,n,r,l){if(ye(n)){var s=!0;Jr(t)}else s=!1;if(nn(t,l),t.stateNode===null)Ir(e,t),Ku(t,n,r),Ds(t,n,r,l),r=!0;else if(e===null){var o=t.stateNode,a=t.memoizedProps;o.props=a;var u=o.context,c=n.contextType;typeof c=="object"&&c!==null?c=Le(c):(c=ye(n)?Mt:ue.current,c=on(t,c));var p=n.getDerivedStateFromProps,h=typeof p=="function"||typeof o.getSnapshotBeforeUpdate=="function";h||typeof o.UNSAFE_componentWillReceiveProps!="function"&&typeof o.componentWillReceiveProps!="function"||(a!==r||u!==c)&&$o(t,o,r,c),lt=!1;var v=t.memoizedState;o.state=v,nl(t,r,o,l),u=t.memoizedState,a!==r||v!==u||ge.current||lt?(typeof p=="function"&&(Rs(t,n,p,r),u=t.memoizedState),(a=lt||Io(t,n,a,r,v,u,c))?(h||typeof o.UNSAFE_componentWillMount!="function"&&typeof o.componentWillMount!="function"||(typeof o.componentWillMount=="function"&&o.componentWillMount(),typeof o.UNSAFE_componentWillMount=="function"&&o.UNSAFE_componentWillMount()),typeof o.componentDidMount=="function"&&(t.flags|=4194308)):(typeof o.componentDidMount=="function"&&(t.flags|=4194308),t.memoizedProps=r,t.memoizedState=u),o.props=r,o.state=u,o.context=c,r=a):(typeof o.componentDidMount=="function"&&(t.flags|=4194308),r=!1)}else{o=t.stateNode,Cu(e,t),a=t.memoizedProps,c=t.type===t.elementType?a:De(t.type,a),o.props=c,h=t.pendingProps,v=o.context,u=n.contextType,typeof u=="object"&&u!==null?u=Le(u):(u=ye(n)?Mt:ue.current,u=on(t,u));var g=n.getDerivedStateFromProps;(p=typeof g=="function"||typeof o.getSnapshotBeforeUpdate=="function")||typeof o.UNSAFE_componentWillReceiveProps!="function"&&typeof o.componentWillReceiveProps!="function"||(a!==h||v!==u)&&$o(t,o,r,u),lt=!1,v=t.memoizedState,o.state=v,nl(t,r,o,l);var x=t.memoizedState;a!==h||v!==x||ge.current||lt?(typeof g=="function"&&(Rs(t,n,g,r),x=t.memoizedState),(c=lt||Io(t,n,c,r,v,x,u)||!1)?(p||typeof o.UNSAFE_componentWillUpdate!="function"&&typeof o.componentWillUpdate!="function"||(typeof o.componentWillUpdate=="function"&&o.componentWillUpdate(r,x,u),typeof o.UNSAFE_componentWillUpdate=="function"&&o.UNSAFE_componentWillUpdate(r,x,u)),typeof o.componentDidUpdate=="function"&&(t.flags|=4),typeof o.getSnapshotBeforeUpdate=="function"&&(t.flags|=1024)):(typeof o.componentDidUpdate!="function"||a===e.memoizedProps&&v===e.memoizedState||(t.flags|=4),typeof o.getSnapshotBeforeUpdate!="function"||a===e.memoizedProps&&v===e.memoizedState||(t.flags|=1024),t.memoizedProps=r,t.memoizedState=x),o.props=r,o.state=x,o.context=u,r=c):(typeof o.componentDidUpdate!="function"||a===e.memoizedProps&&v===e.memoizedState||(t.flags|=4),typeof o.getSnapshotBeforeUpdate!="function"||a===e.memoizedProps&&v===e.memoizedState||(t.flags|=1024),r=!1)}return Is(e,t,n,r,s,l)}function Is(e,t,n,r,l,s){Ju(e,t);var o=(t.flags&128)!==0;if(!r&&!o)return l&&zo(t,n,!1),be(e,t,s);r=t.stateNode,Df.current=t;var a=o&&typeof n.getDerivedStateFromError!="function"?null:r.render();return t.flags|=1,e!==null&&o?(t.child=un(t,e.child,null,s),t.child=un(t,null,a,s)):ce(e,t,a,s),t.memoizedState=r.state,l&&zo(t,n,!0),t.child}function qu(e){var t=e.stateNode;t.pendingContext?Po(e,t.pendingContext,t.pendingContext!==t.context):t.context&&Po(e,t.context,!1),Ci(e,t.containerInfo)}function Qo(e,t,n,r,l){return an(),xi(l),t.flags|=256,ce(e,t,n,r),t.child}var $s={dehydrated:null,treeContext:null,retryLane:0};function Us(e){return{baseLanes:e,cachePool:null,transitions:null}}function bu(e,t,n){var r=t.pendingProps,l=W.current,s=!1,o=(t.flags&128)!==0,a;if((a=o)||(a=e!==null&&e.memoizedState===null?!1:(l&2)!==0),a?(s=!0,t.flags&=-129):(e===null||e.memoizedState!==null)&&(l|=1),U(W,l&1),e===null)return Ls(t),e=t.memoizedState,e!==null&&(e=e.dehydrated,e!==null)?(t.mode&1?e.data==="$!"?t.lanes=8:t.lanes=1073741824:t.lanes=1,null):(o=r.children,e=r.fallback,s?(r=t.mode,s=t.child,o={mode:"hidden",children:o},!(r&1)&&s!==null?(s.childLanes=0,s.pendingProps=o):s=kl(o,r,0,null),e=Lt(e,r,n,null),s.return=t,e.return=t,s.sibling=e,t.child=s,t.child.memoizedState=Us(n),t.memoizedState=$s,e):Ri(t,o));if(l=e.memoizedState,l!==null&&(a=l.dehydrated,a!==null))return Of(e,t,o,r,a,l,n);if(s){s=r.fallback,o=t.mode,l=e.child,a=l.sibling;var u={mode:"hidden",children:r.children};return!(o&1)&&t.child!==l?(r=t.child,r.childLanes=0,r.pendingProps=u,t.deletions=null):(r=vt(l,u),r.subtreeFlags=l.subtreeFlags&14680064),a!==null?s=vt(a,s):(s=Lt(s,o,n,null),s.flags|=2),s.return=t,r.return=t,r.sibling=s,t.child=r,r=s,s=t.child,o=e.child.memoizedState,o=o===null?Us(n):{baseLanes:o.baseLanes|n,cachePool:null,transitions:o.transitions},s.memoizedState=o,s.childLanes=e.childLanes&~n,t.memoizedState=$s,r}return s=e.child,e=s.sibling,r=vt(s,{mode:"visible",children:r.children}),!(t.mode&1)&&(r.lanes=n),r.return=t,r.sibling=null,e!==null&&(n=t.deletions,n===null?(t.deletions=[e],t.flags|=16):n.push(e)),t.child=r,t.memoizedState=null,r}function Ri(e,t){return t=kl({mode:"visible",children:t},e.mode,0,null),t.return=e,e.child=t}function kr(e,t,n,r){return r!==null&&xi(r),un(t,e.child,null,n),e=Ri(t,t.pendingProps.children),e.flags|=2,t.memoizedState=null,e}function Of(e,t,n,r,l,s,o){if(n)return t.flags&256?(t.flags&=-257,r=ql(Error(j(422))),kr(e,t,o,r)):t.memoizedState!==null?(t.child=e.child,t.flags|=128,null):(s=r.fallback,l=t.mode,r=kl({mode:"visible",children:r.children},l,0,null),s=Lt(s,l,o,null),s.flags|=2,r.return=t,s.return=t,r.sibling=s,t.child=r,t.mode&1&&un(t,e.child,null,o),t.child.memoizedState=Us(o),t.memoizedState=$s,s);if(!(t.mode&1))return kr(e,t,o,null);if(l.data==="$!"){if(r=l.nextSibling&&l.nextSibling.dataset,r)var a=r.dgst;return r=a,s=Error(j(419)),r=ql(s,r,void 0),kr(e,t,o,r)}if(a=(o&e.childLanes)!==0,ve||a){if(r=ne,r!==null){switch(o&-o){case 4:l=2;break;case 16:l=8;break;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:l=32;break;case 536870912:l=268435456;break;default:l=0}l=l&(r.suspendedLanes|o)?0:l,l!==0&&l!==s.retryLane&&(s.retryLane=l,qe(e,l),$e(r,e,l,-1))}return Ui(),r=ql(Error(j(421))),kr(e,t,o,r)}return l.data==="$?"?(t.flags|=128,t.child=e.child,t=Gf.bind(null,e),l._reactRetry=t,null):(e=s.treeContext,je=ft(l.nextSibling),ke=t,H=!0,Fe=null,e!==null&&(_e[Pe++]=Ye,_e[Pe++]=Ge,_e[Pe++]=Rt,Ye=e.id,Ge=e.overflow,Rt=t),t=Ri(t,r.children),t.flags|=4096,t)}function Ko(e,t,n){e.lanes|=t;var r=e.alternate;r!==null&&(r.lanes|=t),Ms(e.return,t,n)}function bl(e,t,n,r,l){var s=e.memoizedState;s===null?e.memoizedState={isBackwards:t,rendering:null,renderingStartTime:0,last:r,tail:n,tailMode:l}:(s.isBackwards=t,s.rendering=null,s.renderingStartTime=0,s.last=r,s.tail=n,s.tailMode=l)}function ec(e,t,n){var r=t.pendingProps,l=r.revealOrder,s=r.tail;if(ce(e,t,r.children,n),r=W.current,r&2)r=r&1|2,t.flags|=128;else{if(e!==null&&e.flags&128)e:for(e=t.child;e!==null;){if(e.tag===13)e.memoizedState!==null&&Ko(e,n,t);else if(e.tag===19)Ko(e,n,t);else if(e.child!==null){e.child.return=e,e=e.child;continue}if(e===t)break e;for(;e.sibling===null;){if(e.return===null||e.return===t)break e;e=e.return}e.sibling.return=e.return,e=e.sibling}r&=1}if(U(W,r),!(t.mode&1))t.memoizedState=null;else switch(l){case"forwards":for(n=t.child,l=null;n!==null;)e=n.alternate,e!==null&&rl(e)===null&&(l=n),n=n.sibling;n=l,n===null?(l=t.child,t.child=null):(l=n.sibling,n.sibling=null),bl(t,!1,l,n,s);break;case"backwards":for(n=null,l=t.child,t.child=null;l!==null;){if(e=l.alternate,e!==null&&rl(e)===null){t.child=l;break}e=l.sibling,l.sibling=n,n=l,l=e}bl(t,!0,n,null,s);break;case"together":bl(t,!1,null,null,void 0);break;default:t.memoizedState=null}return t.child}function Ir(e,t){!(t.mode&1)&&e!==null&&(e.alternate=null,t.alternate=null,t.flags|=2)}function be(e,t,n){if(e!==null&&(t.dependencies=e.dependencies),Ot|=t.lanes,!(n&t.childLanes))return null;if(e!==null&&t.child!==e.child)throw Error(j(153));if(t.child!==null){for(e=t.child,n=vt(e,e.pendingProps),t.child=n,n.return=t;e.sibling!==null;)e=e.sibling,n=n.sibling=vt(e,e.pendingProps),n.return=t;n.sibling=null}return t.child}function Ff(e,t,n){switch(t.tag){case 3:qu(t),an();break;case 5:Eu(t);break;case 1:ye(t.type)&&Jr(t);break;case 4:Ci(t,t.stateNode.containerInfo);break;case 10:var r=t.type._context,l=t.memoizedProps.value;U(el,r._currentValue),r._currentValue=l;break;case 13:if(r=t.memoizedState,r!==null)return r.dehydrated!==null?(U(W,W.current&1),t.flags|=128,null):n&t.child.childLanes?bu(e,t,n):(U(W,W.current&1),e=be(e,t,n),e!==null?e.sibling:null);U(W,W.current&1);break;case 19:if(r=(n&t.childLanes)!==0,e.flags&128){if(r)return ec(e,t,n);t.flags|=128}if(l=t.memoizedState,l!==null&&(l.rendering=null,l.tail=null,l.lastEffect=null),U(W,W.current),r)break;return null;case 22:case 23:return t.lanes=0,Zu(e,t,n)}return be(e,t,n)}var tc,As,nc,rc;tc=function(e,t){for(var n=t.child;n!==null;){if(n.tag===5||n.tag===6)e.appendChild(n.stateNode);else if(n.tag!==4&&n.child!==null){n.child.return=n,n=n.child;continue}if(n===t)break;for(;n.sibling===null;){if(n.return===null||n.return===t)return;n=n.return}n.sibling.return=n.return,n=n.sibling}};As=function(){};nc=function(e,t,n,r){var l=e.memoizedProps;if(l!==r){e=t.stateNode,zt(We.current);var s=null;switch(n){case"input":l=as(e,l),r=as(e,r),s=[];break;case"select":l=K({},l,{value:void 0}),r=K({},r,{value:void 0}),s=[];break;case"textarea":l=ds(e,l),r=ds(e,r),s=[];break;default:typeof l.onClick!="function"&&typeof r.onClick=="function"&&(e.onclick=Xr)}ps(n,r);var o;n=null;for(c in l)if(!r.hasOwnProperty(c)&&l.hasOwnProperty(c)&&l[c]!=null)if(c==="style"){var a=l[c];for(o in a)a.hasOwnProperty(o)&&(n||(n={}),n[o]="")}else c!=="dangerouslySetInnerHTML"&&c!=="children"&&c!=="suppressContentEditableWarning"&&c!=="suppressHydrationWarning"&&c!=="autoFocus"&&(Un.hasOwnProperty(c)?s||(s=[]):(s=s||[]).push(c,null));for(c in r){var u=r[c];if(a=l!=null?l[c]:void 0,r.hasOwnProperty(c)&&u!==a&&(u!=null||a!=null))if(c==="style")if(a){for(o in a)!a.hasOwnProperty(o)||u&&u.hasOwnProperty(o)||(n||(n={}),n[o]="");for(o in u)u.hasOwnProperty(o)&&a[o]!==u[o]&&(n||(n={}),n[o]=u[o])}else n||(s||(s=[]),s.push(c,n)),n=u;else c==="dangerouslySetInnerHTML"?(u=u?u.__html:void 0,a=a?a.__html:void 0,u!=null&&a!==u&&(s=s||[]).push(c,u)):c==="children"?typeof u!="string"&&typeof u!="number"||(s=s||[]).push(c,""+u):c!=="suppressContentEditableWarning"&&c!=="suppressHydrationWarning"&&(Un.hasOwnProperty(c)?(u!=null&&c==="onScroll"&&A("scroll",e),s||a===u||(s=[])):(s=s||[]).push(c,u))}n&&(s=s||[]).push("style",n);var c=s;(t.updateQueue=c)&&(t.flags|=4)}};rc=function(e,t,n,r){n!==r&&(t.flags|=4)};function Nn(e,t){if(!H)switch(e.tailMode){case"hidden":t=e.tail;for(var n=null;t!==null;)t.alternate!==null&&(n=t),t=t.sibling;n===null?e.tail=null:n.sibling=null;break;case"collapsed":n=e.tail;for(var r=null;n!==null;)n.alternate!==null&&(r=n),n=n.sibling;r===null?t||e.tail===null?e.tail=null:e.tail.sibling=null:r.sibling=null}}function oe(e){var t=e.alternate!==null&&e.alternate.child===e.child,n=0,r=0;if(t)for(var l=e.child;l!==null;)n|=l.lanes|l.childLanes,r|=l.subtreeFlags&14680064,r|=l.flags&14680064,l.return=e,l=l.sibling;else for(l=e.child;l!==null;)n|=l.lanes|l.childLanes,r|=l.subtreeFlags,r|=l.flags,l.return=e,l=l.sibling;return e.subtreeFlags|=r,e.childLanes=n,t}function If(e,t,n){var r=t.pendingProps;switch(yi(t),t.tag){case 2:case 16:case 15:case 0:case 11:case 7:case 8:case 12:case 9:case 14:return oe(t),null;case 1:return ye(t.type)&&Zr(),oe(t),null;case 3:return r=t.stateNode,cn(),B(ge),B(ue),_i(),r.pendingContext&&(r.context=r.pendingContext,r.pendingContext=null),(e===null||e.child===null)&&(wr(t)?t.flags|=4:e===null||e.memoizedState.isDehydrated&&!(t.flags&256)||(t.flags|=1024,Fe!==null&&(Gs(Fe),Fe=null))),As(e,t),oe(t),null;case 5:Ei(t);var l=zt(Jn.current);if(n=t.type,e!==null&&t.stateNode!=null)nc(e,t,n,r,l),e.ref!==t.ref&&(t.flags|=512,t.flags|=2097152);else{if(!r){if(t.stateNode===null)throw Error(j(166));return oe(t),null}if(e=zt(We.current),wr(t)){r=t.stateNode,n=t.type;var s=t.memoizedProps;switch(r[Ve]=t,r[Xn]=s,e=(t.mode&1)!==0,n){case"dialog":A("cancel",r),A("close",r);break;case"iframe":case"object":case"embed":A("load",r);break;case"video":case"audio":for(l=0;l<\/script>",e=e.removeChild(e.firstChild)):typeof r.is=="string"?e=o.createElement(n,{is:r.is}):(e=o.createElement(n),n==="select"&&(o=e,r.multiple?o.multiple=!0:r.size&&(o.size=r.size))):e=o.createElementNS(e,n),e[Ve]=t,e[Xn]=r,tc(e,t,!1,!1),t.stateNode=e;e:{switch(o=hs(n,r),n){case"dialog":A("cancel",e),A("close",e),l=r;break;case"iframe":case"object":case"embed":A("load",e),l=r;break;case"video":case"audio":for(l=0;lfn&&(t.flags|=128,r=!0,Nn(s,!1),t.lanes=4194304)}else{if(!r)if(e=rl(o),e!==null){if(t.flags|=128,r=!0,n=e.updateQueue,n!==null&&(t.updateQueue=n,t.flags|=4),Nn(s,!0),s.tail===null&&s.tailMode==="hidden"&&!o.alternate&&!H)return oe(t),null}else 2*X()-s.renderingStartTime>fn&&n!==1073741824&&(t.flags|=128,r=!0,Nn(s,!1),t.lanes=4194304);s.isBackwards?(o.sibling=t.child,t.child=o):(n=s.last,n!==null?n.sibling=o:t.child=o,s.last=o)}return s.tail!==null?(t=s.tail,s.rendering=t,s.tail=t.sibling,s.renderingStartTime=X(),t.sibling=null,n=W.current,U(W,r?n&1|2:n&1),t):(oe(t),null);case 22:case 23:return $i(),r=t.memoizedState!==null,e!==null&&e.memoizedState!==null!==r&&(t.flags|=8192),r&&t.mode&1?we&1073741824&&(oe(t),t.subtreeFlags&6&&(t.flags|=8192)):oe(t),null;case 24:return null;case 25:return null}throw Error(j(156,t.tag))}function $f(e,t){switch(yi(t),t.tag){case 1:return ye(t.type)&&Zr(),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return cn(),B(ge),B(ue),_i(),e=t.flags,e&65536&&!(e&128)?(t.flags=e&-65537|128,t):null;case 5:return Ei(t),null;case 13:if(B(W),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(j(340));an()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return B(W),null;case 4:return cn(),null;case 10:return ki(t.type._context),null;case 22:case 23:return $i(),null;case 24:return null;default:return null}}var Sr=!1,ae=!1,Uf=typeof WeakSet=="function"?WeakSet:Set,N=null;function Zt(e,t){var n=e.ref;if(n!==null)if(typeof n=="function")try{n(null)}catch(r){Y(e,t,r)}else n.current=null}function Bs(e,t,n){try{n()}catch(r){Y(e,t,r)}}var Yo=!1;function Af(e,t){if(Ns=Kr,e=au(),vi(e)){if("selectionStart"in e)var n={start:e.selectionStart,end:e.selectionEnd};else e:{n=(n=e.ownerDocument)&&n.defaultView||window;var r=n.getSelection&&n.getSelection();if(r&&r.rangeCount!==0){n=r.anchorNode;var l=r.anchorOffset,s=r.focusNode;r=r.focusOffset;try{n.nodeType,s.nodeType}catch{n=null;break e}var o=0,a=-1,u=-1,c=0,p=0,h=e,v=null;t:for(;;){for(var g;h!==n||l!==0&&h.nodeType!==3||(a=o+l),h!==s||r!==0&&h.nodeType!==3||(u=o+r),h.nodeType===3&&(o+=h.nodeValue.length),(g=h.firstChild)!==null;)v=h,h=g;for(;;){if(h===e)break t;if(v===n&&++c===l&&(a=o),v===s&&++p===r&&(u=o),(g=h.nextSibling)!==null)break;h=v,v=h.parentNode}h=g}n=a===-1||u===-1?null:{start:a,end:u}}else n=null}n=n||{start:0,end:0}}else n=null;for(Cs={focusedElem:e,selectionRange:n},Kr=!1,N=t;N!==null;)if(t=N,e=t.child,(t.subtreeFlags&1028)!==0&&e!==null)e.return=t,N=e;else for(;N!==null;){t=N;try{var x=t.alternate;if(t.flags&1024)switch(t.tag){case 0:case 11:case 15:break;case 1:if(x!==null){var w=x.memoizedProps,L=x.memoizedState,f=t.stateNode,d=f.getSnapshotBeforeUpdate(t.elementType===t.type?w:De(t.type,w),L);f.__reactInternalSnapshotBeforeUpdate=d}break;case 3:var m=t.stateNode.containerInfo;m.nodeType===1?m.textContent="":m.nodeType===9&&m.documentElement&&m.removeChild(m.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(j(163))}}catch(y){Y(t,t.return,y)}if(e=t.sibling,e!==null){e.return=t.return,N=e;break}N=t.return}return x=Yo,Yo=!1,x}function Fn(e,t,n){var r=t.updateQueue;if(r=r!==null?r.lastEffect:null,r!==null){var l=r=r.next;do{if((l.tag&e)===e){var s=l.destroy;l.destroy=void 0,s!==void 0&&Bs(t,n,s)}l=l.next}while(l!==r)}}function wl(e,t){if(t=t.updateQueue,t=t!==null?t.lastEffect:null,t!==null){var n=t=t.next;do{if((n.tag&e)===e){var r=n.create;n.destroy=r()}n=n.next}while(n!==t)}}function Vs(e){var t=e.ref;if(t!==null){var n=e.stateNode;switch(e.tag){case 5:e=n;break;default:e=n}typeof t=="function"?t(e):t.current=e}}function lc(e){var t=e.alternate;t!==null&&(e.alternate=null,lc(t)),e.child=null,e.deletions=null,e.sibling=null,e.tag===5&&(t=e.stateNode,t!==null&&(delete t[Ve],delete t[Xn],delete t[Ps],delete t[kf],delete t[Sf])),e.stateNode=null,e.return=null,e.dependencies=null,e.memoizedProps=null,e.memoizedState=null,e.pendingProps=null,e.stateNode=null,e.updateQueue=null}function sc(e){return e.tag===5||e.tag===3||e.tag===4}function Go(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||sc(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.flags&2||e.child===null||e.tag===4)continue e;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function Hs(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.nodeType===8?n.parentNode.insertBefore(e,t):n.insertBefore(e,t):(n.nodeType===8?(t=n.parentNode,t.insertBefore(e,n)):(t=n,t.appendChild(e)),n=n._reactRootContainer,n!=null||t.onclick!==null||(t.onclick=Xr));else if(r!==4&&(e=e.child,e!==null))for(Hs(e,t,n),e=e.sibling;e!==null;)Hs(e,t,n),e=e.sibling}function Ws(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(r!==4&&(e=e.child,e!==null))for(Ws(e,t,n),e=e.sibling;e!==null;)Ws(e,t,n),e=e.sibling}var re=null,Oe=!1;function nt(e,t,n){for(n=n.child;n!==null;)ic(e,t,n),n=n.sibling}function ic(e,t,n){if(He&&typeof He.onCommitFiberUnmount=="function")try{He.onCommitFiberUnmount(fl,n)}catch{}switch(n.tag){case 5:ae||Zt(n,t);case 6:var r=re,l=Oe;re=null,nt(e,t,n),re=r,Oe=l,re!==null&&(Oe?(e=re,n=n.stateNode,e.nodeType===8?e.parentNode.removeChild(n):e.removeChild(n)):re.removeChild(n.stateNode));break;case 18:re!==null&&(Oe?(e=re,n=n.stateNode,e.nodeType===8?Kl(e.parentNode,n):e.nodeType===1&&Kl(e,n),Wn(e)):Kl(re,n.stateNode));break;case 4:r=re,l=Oe,re=n.stateNode.containerInfo,Oe=!0,nt(e,t,n),re=r,Oe=l;break;case 0:case 11:case 14:case 15:if(!ae&&(r=n.updateQueue,r!==null&&(r=r.lastEffect,r!==null))){l=r=r.next;do{var s=l,o=s.destroy;s=s.tag,o!==void 0&&(s&2||s&4)&&Bs(n,t,o),l=l.next}while(l!==r)}nt(e,t,n);break;case 1:if(!ae&&(Zt(n,t),r=n.stateNode,typeof r.componentWillUnmount=="function"))try{r.props=n.memoizedProps,r.state=n.memoizedState,r.componentWillUnmount()}catch(a){Y(n,t,a)}nt(e,t,n);break;case 21:nt(e,t,n);break;case 22:n.mode&1?(ae=(r=ae)||n.memoizedState!==null,nt(e,t,n),ae=r):nt(e,t,n);break;default:nt(e,t,n)}}function Xo(e){var t=e.updateQueue;if(t!==null){e.updateQueue=null;var n=e.stateNode;n===null&&(n=e.stateNode=new Uf),t.forEach(function(r){var l=Xf.bind(null,e,r);n.has(r)||(n.add(r),r.then(l,l))})}}function Re(e,t){var n=t.deletions;if(n!==null)for(var r=0;rl&&(l=o),r&=~s}if(r=l,r=X()-r,r=(120>r?120:480>r?480:1080>r?1080:1920>r?1920:3e3>r?3e3:4320>r?4320:1960*Vf(r/1960))-r,10e?16:e,at===null)var r=!1;else{if(e=at,at=null,al=0,O&6)throw Error(j(331));var l=O;for(O|=4,N=e.current;N!==null;){var s=N,o=s.child;if(N.flags&16){var a=s.deletions;if(a!==null){for(var u=0;uX()-Fi?Tt(e,0):Oi|=n),xe(e,t)}function hc(e,t){t===0&&(e.mode&1?(t=hr,hr<<=1,!(hr&130023424)&&(hr=4194304)):t=1);var n=de();e=qe(e,t),e!==null&&(rr(e,t,n),xe(e,n))}function Gf(e){var t=e.memoizedState,n=0;t!==null&&(n=t.retryLane),hc(e,n)}function Xf(e,t){var n=0;switch(e.tag){case 13:var r=e.stateNode,l=e.memoizedState;l!==null&&(n=l.retryLane);break;case 19:r=e.stateNode;break;default:throw Error(j(314))}r!==null&&r.delete(t),hc(e,n)}var mc;mc=function(e,t,n){if(e!==null)if(e.memoizedProps!==t.pendingProps||ge.current)ve=!0;else{if(!(e.lanes&n)&&!(t.flags&128))return ve=!1,Ff(e,t,n);ve=!!(e.flags&131072)}else ve=!1,H&&t.flags&1048576&&xu(t,br,t.index);switch(t.lanes=0,t.tag){case 2:var r=t.type;Ir(e,t),e=t.pendingProps;var l=on(t,ue.current);nn(t,n),l=zi(null,t,r,e,l,n);var s=Ti();return t.flags|=1,typeof l=="object"&&l!==null&&typeof l.render=="function"&&l.$$typeof===void 0?(t.tag=1,t.memoizedState=null,t.updateQueue=null,ye(r)?(s=!0,Jr(t)):s=!1,t.memoizedState=l.state!==null&&l.state!==void 0?l.state:null,Ni(t),l.updater=xl,t.stateNode=l,l._reactInternals=t,Ds(t,r,e,n),t=Is(null,t,r,!0,s,n)):(t.tag=0,H&&s&&gi(t),ce(null,t,l,n),t=t.child),t;case 16:r=t.elementType;e:{switch(Ir(e,t),e=t.pendingProps,l=r._init,r=l(r._payload),t.type=r,l=t.tag=Jf(r),e=De(r,e),l){case 0:t=Fs(null,t,r,e,n);break e;case 1:t=Wo(null,t,r,e,n);break e;case 11:t=Vo(null,t,r,e,n);break e;case 14:t=Ho(null,t,r,De(r.type,e),n);break e}throw Error(j(306,r,""))}return t;case 0:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:De(r,l),Fs(e,t,r,l,n);case 1:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:De(r,l),Wo(e,t,r,l,n);case 3:e:{if(qu(t),e===null)throw Error(j(387));r=t.pendingProps,s=t.memoizedState,l=s.element,Cu(e,t),nl(t,r,null,n);var o=t.memoizedState;if(r=o.element,s.isDehydrated)if(s={element:r,isDehydrated:!1,cache:o.cache,pendingSuspenseBoundaries:o.pendingSuspenseBoundaries,transitions:o.transitions},t.updateQueue.baseState=s,t.memoizedState=s,t.flags&256){l=dn(Error(j(423)),t),t=Qo(e,t,r,n,l);break e}else if(r!==l){l=dn(Error(j(424)),t),t=Qo(e,t,r,n,l);break e}else for(je=ft(t.stateNode.containerInfo.firstChild),ke=t,H=!0,Fe=null,n=Su(t,null,r,n),t.child=n;n;)n.flags=n.flags&-3|4096,n=n.sibling;else{if(an(),r===l){t=be(e,t,n);break e}ce(e,t,r,n)}t=t.child}return t;case 5:return Eu(t),e===null&&Ls(t),r=t.type,l=t.pendingProps,s=e!==null?e.memoizedProps:null,o=l.children,Es(r,l)?o=null:s!==null&&Es(r,s)&&(t.flags|=32),Ju(e,t),ce(e,t,o,n),t.child;case 6:return e===null&&Ls(t),null;case 13:return bu(e,t,n);case 4:return Ci(t,t.stateNode.containerInfo),r=t.pendingProps,e===null?t.child=un(t,null,r,n):ce(e,t,r,n),t.child;case 11:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:De(r,l),Vo(e,t,r,l,n);case 7:return ce(e,t,t.pendingProps,n),t.child;case 8:return ce(e,t,t.pendingProps.children,n),t.child;case 12:return ce(e,t,t.pendingProps.children,n),t.child;case 10:e:{if(r=t.type._context,l=t.pendingProps,s=t.memoizedProps,o=l.value,U(el,r._currentValue),r._currentValue=o,s!==null)if(Ue(s.value,o)){if(s.children===l.children&&!ge.current){t=be(e,t,n);break e}}else for(s=t.child,s!==null&&(s.return=t);s!==null;){var a=s.dependencies;if(a!==null){o=s.child;for(var u=a.firstContext;u!==null;){if(u.context===r){if(s.tag===1){u=Xe(-1,n&-n),u.tag=2;var c=s.updateQueue;if(c!==null){c=c.shared;var p=c.pending;p===null?u.next=u:(u.next=p.next,p.next=u),c.pending=u}}s.lanes|=n,u=s.alternate,u!==null&&(u.lanes|=n),Ms(s.return,n,t),a.lanes|=n;break}u=u.next}}else if(s.tag===10)o=s.type===t.type?null:s.child;else if(s.tag===18){if(o=s.return,o===null)throw Error(j(341));o.lanes|=n,a=o.alternate,a!==null&&(a.lanes|=n),Ms(o,n,t),o=s.sibling}else o=s.child;if(o!==null)o.return=s;else for(o=s;o!==null;){if(o===t){o=null;break}if(s=o.sibling,s!==null){s.return=o.return,o=s;break}o=o.return}s=o}ce(e,t,l.children,n),t=t.child}return t;case 9:return l=t.type,r=t.pendingProps.children,nn(t,n),l=Le(l),r=r(l),t.flags|=1,ce(e,t,r,n),t.child;case 14:return r=t.type,l=De(r,t.pendingProps),l=De(r.type,l),Ho(e,t,r,l,n);case 15:return Xu(e,t,t.type,t.pendingProps,n);case 17:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:De(r,l),Ir(e,t),t.tag=1,ye(r)?(e=!0,Jr(t)):e=!1,nn(t,n),Ku(t,r,l),Ds(t,r,l,n),Is(null,t,r,!0,e,n);case 19:return ec(e,t,n);case 22:return Zu(e,t,n)}throw Error(j(156,t.tag))};function vc(e,t){return Ha(e,t)}function Zf(e,t,n,r){this.tag=e,this.key=n,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=t,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=r,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function ze(e,t,n,r){return new Zf(e,t,n,r)}function Ai(e){return e=e.prototype,!(!e||!e.isReactComponent)}function Jf(e){if(typeof e=="function")return Ai(e)?1:0;if(e!=null){if(e=e.$$typeof,e===si)return 11;if(e===ii)return 14}return 2}function vt(e,t){var n=e.alternate;return n===null?(n=ze(e.tag,t,e.key,e.mode),n.elementType=e.elementType,n.type=e.type,n.stateNode=e.stateNode,n.alternate=e,e.alternate=n):(n.pendingProps=t,n.type=e.type,n.flags=0,n.subtreeFlags=0,n.deletions=null),n.flags=e.flags&14680064,n.childLanes=e.childLanes,n.lanes=e.lanes,n.child=e.child,n.memoizedProps=e.memoizedProps,n.memoizedState=e.memoizedState,n.updateQueue=e.updateQueue,t=e.dependencies,n.dependencies=t===null?null:{lanes:t.lanes,firstContext:t.firstContext},n.sibling=e.sibling,n.index=e.index,n.ref=e.ref,n}function Ar(e,t,n,r,l,s){var o=2;if(r=e,typeof e=="function")Ai(e)&&(o=1);else if(typeof e=="string")o=5;else e:switch(e){case Bt:return Lt(n.children,l,s,t);case li:o=8,l|=8;break;case ls:return e=ze(12,n,t,l|2),e.elementType=ls,e.lanes=s,e;case ss:return e=ze(13,n,t,l),e.elementType=ss,e.lanes=s,e;case is:return e=ze(19,n,t,l),e.elementType=is,e.lanes=s,e;case Ea:return kl(n,l,s,t);default:if(typeof e=="object"&&e!==null)switch(e.$$typeof){case Na:o=10;break e;case Ca:o=9;break e;case si:o=11;break e;case ii:o=14;break e;case rt:o=16,r=null;break e}throw Error(j(130,e==null?e:typeof e,""))}return t=ze(o,n,t,l),t.elementType=e,t.type=r,t.lanes=s,t}function Lt(e,t,n,r){return e=ze(7,e,r,t),e.lanes=n,e}function kl(e,t,n,r){return e=ze(22,e,r,t),e.elementType=Ea,e.lanes=n,e.stateNode={isHidden:!1},e}function es(e,t,n){return e=ze(6,e,null,t),e.lanes=n,e}function ts(e,t,n){return t=ze(4,e.children!==null?e.children:[],e.key,t),t.lanes=n,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function qf(e,t,n,r,l){this.tag=t,this.containerInfo=e,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=Ol(0),this.expirationTimes=Ol(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=Ol(0),this.identifierPrefix=r,this.onRecoverableError=l,this.mutableSourceEagerHydrationData=null}function Bi(e,t,n,r,l,s,o,a,u){return e=new qf(e,t,n,a,u),t===1?(t=1,s===!0&&(t|=8)):t=0,s=ze(3,null,null,t),e.current=s,s.stateNode=e,s.memoizedState={element:r,isDehydrated:n,cache:null,transitions:null,pendingSuspenseBoundaries:null},Ni(s),e}function bf(e,t,n){var r=3"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(wc)}catch(e){console.error(e)}}wc(),wa.exports=Ce;var lp=wa.exports,jc,ra=lp;jc=ra.createRoot,ra.hydrateRoot;const kc=()=>{var e,t;return(t=(e=window==null?void 0:window.go)==null?void 0:e.main)==null?void 0:t.App},sp=()=>!!kc();async function F(e,...t){const n=kc();if(!n||typeof n[e]!="function")throw new Error(`${e} is unavailable — run this inside the Behavision app`);return n[e](...t)}const $={session:()=>F("Session"),login:(e,t)=>F("Login",e,t),logout:()=>F("Logout"),claim:e=>F("Claim",e),runStandalone:()=>F("RunStandalone"),engineStatus:()=>F("EngineStatus"),startEngine:()=>F("StartEngine"),stopEngine:()=>F("StopEngine"),cameras:()=>F("Cameras"),testCamera:e=>F("TestCamera",e),discoverCameras:()=>F("DiscoverCameras"),saveCamera:(e,t)=>F("SaveCamera",e,t),deleteCamera:e=>F("DeleteCamera",e),startPlacement:(e,t)=>F("StartPlacementCheck",e,t),placementResult:e=>F("PlacementResult",e),streamURL:e=>F("StreamURL",e),live:()=>F("Live"),pipelineStatus:()=>F("PipelineStatus"),localIdentities:e=>F("LocalIdentities",e),localSightings:e=>F("LocalSightings",e),footfall:(e,t,n)=>F("Footfall",e,t,n),sites:()=>F("Sites"),visitorHistory:(e,t)=>F("VisitorHistory",e,t),visitorPhoto:e=>F("VisitorPhoto",e),forgetCustomer:e=>F("ForgetCustomer",e),sales:(e,t)=>F("Sales",e,t),customers:(e,t)=>F("Customers",e,t),saveProfile:e=>F("SaveProfile",e),ask:e=>F("Ask",e),recordPurchase:(e,t,n,r)=>F("RecordPurchase",e,t,n,r)};function fe(e){return e?typeof e=="string"?e:e.message||String(e):"Something went wrong."}function et(e,t,n=[]){const[r,l]=P.useState(null),[s,o]=P.useState(null),[a,u]=P.useState(!0),c=P.useRef(!0),p=P.useRef(!1),h=P.useCallback(async()=>{if(!p.current){p.current=!0;try{const v=await e();if(!c.current)return;l(v),o(null)}catch(v){c.current&&o(fe(v))}finally{p.current=!1,c.current&&u(!1)}}},n);return P.useEffect(()=>{if(c.current=!0,h(),!t)return()=>{c.current=!1};const v=setInterval(h,t);return()=>{c.current=!1,clearInterval(v)}},[h,t]),{data:r,error:s,loading:a,reload:h}}function ip(e){if(!e)return"—";const t=typeof e=="number"?new Date(e*1e3):new Date(e);return isNaN(t)?"—":t.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}function la(e){if(!e)return"—";const t=typeof e=="number"?new Date(e*1e3):new Date(e);return isNaN(t)?"—":t.toLocaleDateString([],{day:"numeric",month:"short"})}const ns={width:18,height:18,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:1.6,strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",focusable:"false"};function J({size:e,children:t,...n}){return i.jsx("svg",{...ns,...n,width:e??ns.width,height:e??ns.height,children:t})}const op=e=>i.jsxs(J,{...e,children:[i.jsx("circle",{cx:"12",cy:"12",r:"3.2"}),i.jsx("path",{d:"M5.6 5.6a9 9 0 0 0 0 12.8M18.4 18.4a9 9 0 0 0 0-12.8"})]}),ap=e=>i.jsxs(J,{...e,children:[i.jsx("circle",{cx:"9",cy:"8.5",r:"3.2"}),i.jsx("path",{d:"M2.8 19.5a6.4 6.4 0 0 1 12.4 0"}),i.jsx("path",{d:"M16.5 6.2a3.2 3.2 0 0 1 0 6.1M18 19.5a6 6 0 0 0-1.6-4"})]}),up=e=>i.jsxs(J,{...e,children:[i.jsx("path",{d:"M3 8.5h3.4L8 6h8l1.6 2.5H21v10.2H3z"}),i.jsx("circle",{cx:"12",cy:"13.2",r:"3.1"})]}),Sc=e=>i.jsxs(J,{...e,children:[i.jsx("circle",{cx:"11",cy:"11",r:"6.4"}),i.jsx("path",{d:"M15.8 15.8 20.5 20.5"})]}),sa=e=>i.jsx(J,{...e,children:i.jsx("path",{d:"M12 5.5v13M5.5 12h13"})}),Qi=e=>i.jsx(J,{...e,children:i.jsx("path",{d:"M6.5 6.5l11 11M17.5 6.5l-11 11"})}),Xs=e=>i.jsx(J,{...e,children:i.jsx("path",{d:"M5 12.8l4.4 4.2L19 7"})}),Nc=e=>i.jsx(J,{...e,children:i.jsx("path",{d:"M8 5.6v12.8L18.5 12z"})}),cp=e=>i.jsx(J,{...e,children:i.jsx("rect",{x:"7",y:"7",width:"10",height:"10",rx:"1.6"})}),Se=e=>i.jsxs(J,{...e,children:[i.jsx("path",{d:"M12 4.6 21 19.4H3z"}),i.jsx("path",{d:"M12 10v4.1"}),i.jsx("path",{d:"M12 17.1v.01"})]}),ia=e=>i.jsx(J,{...e,children:i.jsx("path",{d:"M5 19.4v-4.2M10.3 19.4v-7.6M15.7 19.4v-11M21 19.4V4.6"})}),Ki=e=>i.jsx(J,{...e,children:i.jsx("path",{d:"M7.2 18.4a4.2 4.2 0 0 1-.6-8.35A6.2 6.2 0 0 1 18.4 9a4.2 4.2 0 0 1 .3 9.4z"})}),Cc=e=>i.jsxs(J,{...e,children:[i.jsx("path",{d:"M7.2 18.4a4.2 4.2 0 0 1-.6-8.35 6.2 6.2 0 0 1 2-3.2M10.6 5.2A6.2 6.2 0 0 1 18.4 9a4.2 4.2 0 0 1 1.9 7.6"}),i.jsx("path",{d:"M3.6 3.6l16.8 16.8"})]}),dp=e=>i.jsx(J,{...e,children:i.jsx("path",{d:"M12 3.8 19.4 6.6v5.2c0 4.2-3 7.4-7.4 8.4-4.4-1-7.4-4.2-7.4-8.4V6.6z"})}),fp=e=>i.jsxs(J,{...e,children:[i.jsx("path",{d:"M10.2 13.8a3.6 3.6 0 0 0 5.2 0l2.8-2.8a3.7 3.7 0 0 0-5.2-5.2l-1.3 1.3"}),i.jsx("path",{d:"M13.8 10.2a3.6 3.6 0 0 0-5.2 0l-2.8 2.8a3.7 3.7 0 0 0 5.2 5.2l1.3-1.3"})]}),pp=e=>i.jsxs(J,{...e,children:[i.jsx("path",{d:"M14.4 7.6V5.4H4.6v13.2h9.8v-2.2"}),i.jsx("path",{d:"M10 12h9.4M16.4 8.8 19.8 12l-3.4 3.2"})]}),oa=e=>i.jsx(J,{...e,children:i.jsx("path",{d:"M14.6 5.6 8 12l6.6 6.4"})}),hp=e=>i.jsx(J,{...e,children:i.jsx("path",{d:"M9.4 5.6 16 12l-6.6 6.4"})}),Ec=e=>i.jsxs(J,{...e,strokeWidth:"1.2",children:[i.jsx("path",{d:"M3 8.5h3.4L8 6h8l1.6 2.5H21v10.2H3z"}),i.jsx("circle",{cx:"12",cy:"13.2",r:"3.1"}),i.jsx("path",{d:"M3.6 3.6l16.8 16.8"})]}),_c=e=>i.jsxs(J,{...e,strokeWidth:"1.2",children:[i.jsx("circle",{cx:"12",cy:"9",r:"3.4"}),i.jsx("path",{d:"M5.4 20a6.8 6.8 0 0 1 13.2 0"})]}),gt=""+new URL("loyaly-mark-Ch-P3VSF.png",import.meta.url).href;function mp({onDone:e}){const[t,n]=P.useState(""),[r,l]=P.useState(""),[s,o]=P.useState(!1),[a,u]=P.useState(null);async function c(p){p.preventDefault(),o(!0),u(null);try{e(await $.login(t.trim(),r))}catch(h){u(fe(h))}finally{o(!1)}}return i.jsx("div",{className:"login",children:i.jsxs("div",{className:"box",children:[i.jsx("span",{className:"mark",children:i.jsx("img",{src:gt,alt:""})}),i.jsx("h1",{children:"Behavision"}),i.jsx("p",{className:"lead",children:"Sign in to connect this PC to your store."}),i.jsxs("form",{onSubmit:c,children:[a&&i.jsxs("div",{className:"err",children:[i.jsx(Se,{size:15}),a]}),i.jsxs("label",{className:"field",children:[i.jsx("span",{children:"Email"}),i.jsx("input",{type:"email",value:t,autoComplete:"username",required:!0,autoFocus:!0,onChange:p=>n(p.target.value)})]}),i.jsxs("label",{className:"field",children:[i.jsx("span",{children:"Password"}),i.jsx("input",{type:"password",value:r,autoComplete:"current-password",required:!0,onChange:p=>l(p.target.value)})]}),i.jsx("button",{className:"btn primary",disabled:s||!t||!r,children:s?"Signing in…":"Sign in"})]}),i.jsx("p",{className:"foot",children:"Signing in downloads this store's recognition models and connects it to your account. Nothing is sent until a camera is set up."})]})})}function aa({onDone:e,onCancel:t}){const[n,r]=P.useState(""),[l,s]=P.useState(null),[o,a]=P.useState(null);async function u(v){v.preventDefault(),s("claim"),a(null);try{e(await $.claim(n))}catch(g){a(fe(g))}finally{s(null)}}async function c(){s("alone"),a(null);try{e(await $.runStandalone())}catch(v){a(fe(v))}finally{s(null)}}const[p,h]=P.useState(t?"code":null);return p===null?i.jsx("div",{className:"login",children:i.jsxs("div",{className:"box wide",children:[i.jsx("span",{className:"mark",children:i.jsx("img",{src:gt,alt:""})}),i.jsx("h1",{children:"Welcome to Behavision"}),i.jsx("p",{className:"lead",children:"This PC will watch your shop’s cameras and recognise returning customers. First, one question: is this shop managed from a head office?"}),i.jsxs("div",{className:"choices",children:[i.jsxs("button",{type:"button",className:"choice",onClick:()=>h("code"),children:[i.jsx(Ki,{size:22}),i.jsx("b",{children:"Yes — I have an installation code"}),i.jsxs("span",{children:["Head office gave you a code like ",i.jsx("em",{children:"ABCDEF-123456-…"}),". This PC joins that shop and gets its cameras from there."]})]}),i.jsxs("button",{type:"button",className:"choice",onClick:()=>h("alone"),children:[i.jsx(dp,{size:22}),i.jsx("b",{children:"No — set up on this PC only"}),i.jsx("span",{children:"Cameras, customers and recognition stay on this PC. Nothing is sent anywhere. You can link to a head office later."})]})]})]})}):p==="alone"?i.jsx("div",{className:"login",children:i.jsxs("div",{className:"box",children:[i.jsx("span",{className:"mark",children:i.jsx("img",{src:gt,alt:""})}),i.jsx("h1",{children:"On this PC only"}),i.jsx("p",{className:"lead",children:"Behavision will run entirely here. Next you’ll add your camera — it can find it on the network for you — and walk past it once so it can prove it works."}),o&&i.jsxs("div",{className:"err",children:[i.jsx(Se,{size:15}),o]}),i.jsx("button",{type:"button",className:"btn primary",disabled:!!l,onClick:c,children:l==="alone"?"Setting up…":"Continue"}),i.jsx("div",{className:"alt",children:i.jsxs("button",{type:"button",className:"linkbtn",onClick:()=>h(null),children:[i.jsx(oa,{size:14})," Back"]})})]})}):i.jsx("div",{className:"login",children:i.jsxs("div",{className:"box",children:[i.jsx("span",{className:"mark",children:i.jsx("img",{src:gt,alt:""})}),i.jsx("h1",{children:t?"Link to head office":"Join your shop"}),i.jsx("p",{className:"lead",children:"Type the installation code head office gave you. It works once, and this PC becomes that shop."}),i.jsxs("form",{onSubmit:u,children:[o&&i.jsxs("div",{className:"err",children:[i.jsx(Se,{size:15}),o]}),i.jsxs("label",{className:"field",children:[i.jsx("span",{children:"Installation code"}),i.jsx("input",{value:n,autoFocus:!0,required:!0,placeholder:"ABCDEF-123456-GHIJKL-789012",autoComplete:"off",spellCheck:"false",style:{textTransform:"uppercase",letterSpacing:".06em"},onChange:v=>r(v.target.value)})]}),i.jsx("button",{className:"btn primary",disabled:!!l||n.trim().length<6,children:l==="claim"?"Linking…":"Link this PC"})]}),i.jsxs("p",{className:"foot",children:["Don’t have one? Whoever runs head office creates it under the shop: ",i.jsx("em",{children:"Shops → the shop → Set up a shop PC"}),"."]}),i.jsx("div",{className:"alt",children:t?i.jsx("button",{type:"button",className:"linkbtn",onClick:t,children:"Not now — go back"}):i.jsxs("button",{type:"button",className:"linkbtn",onClick:()=>h(null),children:[i.jsx(oa,{size:14})," Back"]})})]})})}function Pc({onNavigate:e}){var v,g,x,w;const{data:t,error:n}=et(()=>$.live(),3e3),{data:r}=et(()=>$.pipelineStatus(),5e3),l=((v=t==null?void 0:t.stats)==null?void 0:v.cameras)??[],s=((g=t==null?void 0:t.stats)==null?void 0:g.gallery)??{},o=(t==null?void 0:t.events)??[],a=(t==null?void 0:t.viewing)===!0,u=a?((x=t==null?void 0:t.stats)==null?void 0:x.fraction_below_gate)??0:l.reduce((L,f)=>{var m,y;const d=(y=(m=f==null?void 0:f.pipeline)==null?void 0:m.best_quality)==null?void 0:y.fraction_below_gate;return typeof d=="number"&&d>L?d:L},0),c=a?((w=t==null?void 0:t.stats)==null?void 0:w.cameras_up)??0:l.filter(L=>L.connected).length,p=o.filter(L=>L.type==="person.new"||L.type==="person.seen"),h=wp(p[0]);return i.jsxs("div",{className:"page",children:[i.jsxs("header",{children:[i.jsx("h2",{children:"Live"}),i.jsx("p",{children:a?"Your shops, as head office sees them.":"Who is in the shop, and whether it is reaching head office."})]}),n&&i.jsxs("div",{className:"err",children:[i.jsx(Se,{size:15}),n]}),a&&i.jsxs("div",{className:"viewing",children:[i.jsx(Ki,{size:15}),i.jsxs("span",{children:[i.jsx("b",{children:"Viewing your shops from here."})," This computer is not watching any cameras itself — everything below is what your shop PCs reported. To recognise people on this machine, it has to be on the same network as a camera."]})]}),i.jsx(xp,{pipe:r,cameras:l,up:c}),!a&&i.jsx(vp,{cameras:l,arrivals:p,onNavigate:e}),i.jsxs("div",{className:"panel arrivals-panel",children:[i.jsxs("div",{className:"panelhead",children:[i.jsx("h3",{children:"Who just walked in"}),p.length>0&&i.jsxs("span",{className:"note",children:[p.length," today"]})]}),i.jsx("div",{className:"panelbody flush",children:p.length===0?i.jsxs("div",{className:"empty",children:[i.jsx(_c,{size:34}),i.jsx("b",{children:"Nobody yet"}),i.jsx("p",{children:"Customers appear here the moment a camera recognises a face."})]}):i.jsx("div",{className:"arrivals",children:p.slice(0,30).map((L,f)=>i.jsx(gp,{e:L,fresh:f===0&&h},`${L.ts}-${f}`))})})]}),i.jsxs("div",{className:"metrics",style:{marginTop:"var(--sp-4)"},children:[i.jsx(Er,{k:"People known",v:s.identities??"—"}),i.jsx(Er,{k:"Sightings",v:s.sightings??"—"}),i.jsx(Er,{k:"Cameras live",v:l.length?`${c}/${l.length}`:"—",tone:l.length?c===0?"bad":c.5?"bad":u>.2?"warn":"ok",s:u>.5?"Most visitors are being missed — move the camera":"Share of faces below the enrolment gate"})]})]})}function vp({cameras:e,arrivals:t,onNavigate:n}){const{data:r}=et(()=>$.engineStatus(),4e3),[l,s]=P.useState(()=>{try{return localStorage.getItem("bv.gettingStarted")==="done"}catch{return!1}}),o=e.length>0,a=e.some(x=>x.connected),{data:u}=et(async()=>{const x={};for(const w of e)try{x[w.camera_id]=await $.placementResult(w.camera_id)}catch{}return x},1e4,[e.map(x=>x.camera_id).join("|")]),c=Object.values(u??{}).some(x=>x&&!x.running&&x.verdict==="good"),p=t.length>0;if(l||p)return null;const h=!!(r!=null&&r.reachable&&(r!=null&&r.recognition_model)),v=r==null?void 0:r.progress,g=[{done:h,now:!h,title:h?`Recognition ready (${r.recognition_model})`:v?`Downloading ${v.what}… ${v.percent}%`:"Starting recognition…",text:h?null:"First start downloads about 275 MB of recognition models. A few minutes on a normal connection; nothing to do meanwhile."},{done:o&&a,now:h&&!(o&&a),title:o?a?"Camera connected":"Camera added — not connecting yet":"Add your camera",text:o?a?null:"Check its password and stream path under Cameras → Edit.":"Behavision can find it on the network; you type only its password.",action:o?null:{label:"Add camera",go:"cameras"}},{done:c,now:o&&a&&!c,title:c?"Camera proven — it can recognise faces":"Walk past the camera",text:c?null:"Run Check placement and walk past like a customer for 25 seconds. Only a “good” verdict means it will recognise people.",action:o&&a&&!c?{label:"Check placement",go:"cameras"}:null}];return i.jsxs("section",{className:"panel starter",children:[i.jsxs("div",{className:"panelhead",children:[i.jsx("h3",{children:"Getting started"}),i.jsx("button",{className:"btn sm ghost",onClick:()=>{try{localStorage.setItem("bv.gettingStarted","done")}catch{}s(!0)},children:"Hide"})]}),i.jsx("ol",{className:"steps compact",children:g.map((x,w)=>i.jsxs("li",{className:x.done?"done":x.now?"now":"",children:[i.jsx("b",{children:x.title}),x.text&&i.jsx("span",{children:x.text}),x.action&&i.jsx("button",{className:"btn sm primary",onClick:()=>n==null?void 0:n(x.action.go),children:x.action.label})]},w))}),i.jsx("p",{className:"note",children:"The moment a customer is recognised, this list goes away."})]})}function gp({e,fresh:t}){var o,a,u,c,p,h;const n=e.type==="person.new",r=((o=e.data)==null?void 0:o.label)||"Unrecognised",l=[(a=e.data)==null?void 0:a.gender,((u=e.data)==null?void 0:u.age)??((c=e.data)==null?void 0:c.age_range),(p=e.data)==null?void 0:p.emotion].filter(Boolean),s=typeof((h=e.data)==null?void 0:h.similarity)=="number"?e.data.similarity:null;return i.jsxs("article",{className:`arrival ${n?"is-new":"is-seen"} ${t?"fresh":""}`,children:[i.jsx("div",{className:"avatar",children:yp(r)}),i.jsxs("div",{className:"who",children:[i.jsx("div",{className:"name",children:r}),i.jsxs("div",{className:"meta",children:[e.camera_id,l.length>0&&i.jsxs(i.Fragment,{children:[" · ",l.join(", ")]}),s!==null&&!n&&i.jsxs(i.Fragment,{children:[" · match ",s.toFixed(2)]})]})]}),i.jsxs("div",{className:"right",children:[i.jsx("span",{className:`tag ${n?"new":"seen"}`,children:n?"new":"returning"}),i.jsx("span",{className:"when",children:ip(e.ts)})]})]})}function yp(e){var r;const t=/^Visitor (\d+)$/.exec(String(e).trim());if(t)return t[1];const n=String(e).trim().split(/\s+/).filter(Boolean);return n.length?(n[0][0]+(((r=n[1])==null?void 0:r[0])??"")).toUpperCase():"?"}function xp({pipe:e,cameras:t,up:n}){if(!e)return null;if(e.standalone)return i.jsxs("div",{className:"statusbar",children:[i.jsxs("span",{className:"item",children:[i.jsx("i",{className:"dot ok"}),i.jsx("b",{children:"Running on this PC only"})]}),i.jsx("span",{className:"sep"}),i.jsx("span",{className:"item note",children:"Recognition and customers stay here."}),i.jsx("span",{className:"grow"}),i.jsxs("span",{className:"item note",children:[i.jsx(ia,{size:14}),n," of ",t.length," cameras"]})]});const r=e.claimed&&!e.broker_up,l=e.claimed?e.broker_up?"ok":"bad":"idle",s=e.claimed?e.broker_up?"Sending to head office":"Offline — saving locally":"Not linked to head office";return i.jsxs("div",{className:"statusbar",children:[i.jsxs("span",{className:"item",children:[e.broker_up?i.jsx(Ki,{size:15}):i.jsx(Cc,{size:15}),i.jsx("i",{className:`dot ${l}`}),i.jsx("b",{children:s})]}),i.jsx("span",{className:"sep"}),i.jsxs("span",{className:"item note",children:[e.accepted," recorded today"]}),e.queued>0&&i.jsxs("span",{className:`item note ${r?"warn":""}`,children:[e.queued," waiting to send"]}),e.dropped>0&&i.jsxs("span",{className:"item note bad",children:[i.jsx(Se,{size:14}),e.dropped," lost — this PC was offline too long"]}),i.jsx("span",{className:"grow"}),i.jsxs("span",{className:"item note",children:[i.jsx(ia,{size:14}),n," of ",t.length," cameras"]})]})}function Er({k:e,v:t,s:n,tone:r}){return i.jsxs("div",{className:`metric ${r??""}`,children:[i.jsx("div",{className:"metric-k",children:e}),i.jsx("div",{className:"metric-v",children:t}),n&&i.jsx("div",{className:"metric-s",children:n})]})}function wp(e){const[t,n]=P.useState(!1),r=P.useRef(null);return P.useEffect(()=>{if(!e||e.ts===r.current)return;const l=r.current===null;if(r.current=e.ts,l)return;n(!0);const s=setTimeout(()=>n(!1),1200);return()=>clearTimeout(s)},[e==null?void 0:e.ts]),t}function jp(e){const[t,n]=P.useState(null);return P.useEffect(()=>{let r=!0;return n(null),$.visitorPhoto(e).then(l=>{r&&n(l)}).catch(()=>{r&&n({available:!1,reason:""})}),()=>{r=!1}},[e]),t}function kp({photo:e,name:t,customerRef:n,onBroken:r}){return e!=null&&e.available?i.jsx("img",{className:"avatar",src:e.url,alt:`Photo of ${t}`,onError:r}):i.jsx("div",{className:"avatar none",role:"img","aria-label":`No photo of ${t}`,children:i.jsx("span",{children:Sp(t,n)})})}function Sp(e,t){const n=/^Visitor (\d+)$/.exec(String(e||"").trim());if(n)return n[1];const r=/^V-(\d+)$/.exec(String(t||""));return r?r[1]:String(e||"").split(/\s+/).filter(Boolean).slice(0,2).map(l=>l[0].toUpperCase()).join("")||"?"}function Np({customer:e}){const{data:t,error:n,loading:r}=et(()=>$.visitorHistory(e.id,50),0,[e.id]);if(r)return i.jsx("p",{className:"note",children:"Loading visits…"});if(n)return i.jsxs("p",{className:"note",children:["Could not load visits: ",n]});const l=t??[];return l.length===0?i.jsx("p",{className:"note",children:"No recorded visits yet."}):i.jsx("ul",{className:"timeline",children:l.map(s=>i.jsxs("li",{children:[i.jsx("span",{className:"when",children:Cp(s.occurred_at)}),i.jsxs("span",{className:"where",children:[s.site||"this store",s.camera_id?i.jsxs("span",{className:"note",children:[" · ",s.camera_id]}):null]}),s.is_new_visitor&&i.jsx("span",{className:"tag new",children:"first visit"})]},s.id))})}function Cp(e){const t=new Date(e);return isNaN(t)?"—":t.toLocaleString([],{day:"numeric",month:"short",hour:"2-digit",minute:"2-digit"})}function Ep({customer:e,onCancel:t,onDone:n}){const r=e.full_name||e.label,[l,s]=P.useState(""),[o,a]=P.useState(!1),[u,c]=P.useState(null),p=l.trim().toLowerCase()===r.trim().toLowerCase();async function h(){a(!0),c(null);try{await $.forgetCustomer(e.id),n()}catch(v){c(fe(v)),a(!1)}}return i.jsxs("div",{className:"confirm",children:[i.jsxs("h4",{children:["Erase ",r,"?"]}),i.jsxs("div",{className:"cols",children:[i.jsxs("div",{children:[i.jsx("p",{className:"lbl bad",children:"Deleted for good"}),i.jsxs("ul",{children:[i.jsx("li",{children:"Their face data — they will not be recognised again"}),i.jsx("li",{children:"Their photo"}),i.jsx("li",{children:"Their name, phone, email and notes"})]})]}),i.jsxs("div",{children:[i.jsx("p",{className:"lbl",children:"Kept"}),i.jsxs("ul",{children:[i.jsx("li",{children:"Past visits, with their name removed — your footfall figures do not change"}),i.jsx("li",{children:"The consent record, marked withdrawn, as proof of what was agreed"})]})]})]}),i.jsx("p",{className:"note",children:"This cannot be undone. If they come back they will be recorded as a new customer."}),u&&i.jsx("div",{className:"err",children:u}),i.jsxs("label",{className:"field",children:[i.jsxs("span",{children:["Type ",i.jsx("b",{children:r})," to confirm"]}),i.jsx("input",{value:l,onChange:v=>s(v.target.value),autoFocus:!0,autoComplete:"off",spellCheck:"false","aria-label":`Type ${r} to confirm erasure`})]}),i.jsxs("div",{className:"row",children:[i.jsx("button",{type:"button",className:"btn danger",disabled:!p||o,onClick:h,children:o?"Erasing…":"Erase permanently"}),i.jsx("button",{type:"button",className:"btn",onClick:t,disabled:o,children:"Cancel"})]})]})}function _p({customer:e,session:t,onClose:n,onSaved:r}){var C;const[l,s]=P.useState({full_name:e.full_name??"",phone:e.phone??"",email:e.email??"",gender:"",date_of_birth:"",notes:"",consent:e.has_consent??!1}),[o,a]=P.useState({amount:"",items:"",notes:""}),[u,c]=P.useState(!1),[p,h]=P.useState(null),[v,g]=P.useState(!1),[x,w]=P.useState(null),L=jp(e.id),f=x??L,d=(C=t==null?void 0:t.user)==null?void 0:C.role,m=["admin","owner","manager"].includes(d),y=k=>E=>s({...l,[k]:E.target.value});async function S(k){k.preventDefault(),c(!0),h(null);try{await $.saveProfile({visitor_id:e.id,...l});const E=parseFloat(o.amount);if(!isNaN(E)&&E>0){const z=o.items.split(",").map(T=>T.trim()).filter(Boolean);await $.recordPurchase(e.id,E,z,o.notes)}r()}catch(E){h(fe(E))}finally{c(!1)}}return i.jsx("div",{className:"drawer",onMouseDown:k=>k.target===k.currentTarget&&n(),children:i.jsxs("div",{className:"sheet",children:[i.jsxs("div",{className:"sheethead who",children:[i.jsx(kp,{photo:f,name:e.full_name||e.label,customerRef:e.ref,onBroken:()=>w({available:!1,reason:"The photo could not be loaded."})}),i.jsxs("div",{className:"grow",children:[i.jsx("h2",{children:e.full_name||e.label}),i.jsxs("p",{className:"note",children:[e.visit_count," visit",e.visit_count===1?"":"s",e.last_seen_at&&` · last seen ${new Date(e.last_seen_at).toLocaleDateString()}`,f&&!f.available&&f.reason&&` · ${f.reason}`]})]}),i.jsx("button",{type:"button",className:"close",onClick:n,"aria-label":"Close",children:"✕"})]}),i.jsxs("form",{className:"sheetbody",onSubmit:S,children:[p&&i.jsx("div",{className:"err",children:p}),i.jsxs("section",{className:"formsection",children:[i.jsx("h4",{children:"Customer details"}),i.jsxs("label",{className:"field",children:[i.jsx("span",{children:"Full name"}),i.jsx("input",{value:l.full_name,onChange:y("full_name"),autoFocus:!0})]}),i.jsxs("div",{className:"fieldrow",children:[i.jsxs("label",{className:"field",children:[i.jsx("span",{children:"Phone"}),i.jsx("input",{value:l.phone,onChange:y("phone"),inputMode:"tel"})]}),i.jsxs("label",{className:"field",children:[i.jsx("span",{children:"Email"}),i.jsx("input",{value:l.email,onChange:y("email"),type:"email"})]})]}),i.jsxs("div",{className:"fieldrow",children:[i.jsxs("label",{className:"field",children:[i.jsx("span",{children:"Gender"}),i.jsxs("select",{value:l.gender,onChange:y("gender"),children:[i.jsx("option",{value:"",children:"Not recorded"}),i.jsx("option",{children:"Female"}),i.jsx("option",{children:"Male"}),i.jsx("option",{children:"Other"})]})]}),i.jsxs("label",{className:"field",children:[i.jsx("span",{children:"Date of birth"}),i.jsx("input",{type:"date",value:l.date_of_birth,onChange:y("date_of_birth")})]})]}),i.jsxs("label",{className:"field",children:[i.jsx("span",{children:"Notes"}),i.jsx("textarea",{value:l.notes,onChange:y("notes"),placeholder:"Preferences, sizes, anything worth remembering"})]})]}),i.jsxs("section",{className:"formsection",children:[i.jsx("h4",{children:"Purchase (optional)"}),i.jsxs("div",{className:"fieldrow",children:[i.jsxs("label",{className:"field",children:[i.jsx("span",{children:"Amount"}),i.jsx("input",{value:o.amount,inputMode:"decimal",placeholder:"0.00",onChange:k=>a({...o,amount:k.target.value})})]}),i.jsxs("label",{className:"field",children:[i.jsx("span",{children:"Items"}),i.jsx("input",{value:o.items,placeholder:"shirt, belt",onChange:k=>a({...o,items:k.target.value})})]})]}),i.jsx("p",{className:"note",children:"Leave the amount blank if they did not buy anything — a visit without a sale is still worth recording."})]}),i.jsxs("section",{className:"formsection",children:[i.jsx("h4",{children:"Consent"}),i.jsxs("label",{style:{display:"flex",gap:10,alignItems:"flex-start",fontSize:13,cursor:"pointer"},children:[i.jsx("input",{type:"checkbox",checked:l.consent,style:{marginTop:3},onChange:k=>s({...l,consent:k.target.checked})}),i.jsx("span",{children:"This customer agreed to us keeping their details and recognising them on future visits."})]}),i.jsx("p",{className:"note",style:{marginTop:10},children:"Recorded with the date and who collected it. They can withdraw it at any time, which erases their face data."})]}),i.jsxs("section",{className:"formsection",children:[i.jsx("h4",{children:"Visits"}),i.jsx(Np,{customer:e})]}),i.jsxs("div",{className:"sheetactions",children:[i.jsx("button",{type:"button",className:"btn",onClick:n,children:"Cancel"}),i.jsx("button",{className:"btn primary",disabled:u,children:u?"Saving…":"Save"})]}),m&&i.jsxs("section",{className:"formsection dangerzone",children:[i.jsx("h4",{children:"At the customer's request"}),v?i.jsx(Ep,{customer:e,onCancel:()=>g(!1),onDone:r}):i.jsxs(i.Fragment,{children:[i.jsx("p",{className:"note",children:"Erase this person's face data, photo and details. Their past visits stay in your footfall figures, without their name."}),i.jsx("button",{type:"button",className:"btn danger",onClick:()=>g(!0),children:"Erase this customer…"})]})]})]})]})})}function Pp({session:e}){const[t,n]=P.useState(""),[r,l]=P.useState(null),{data:s,error:o,reload:a}=et(()=>$.customers(t,200),3e4,[t]),u=s??[],c=u.filter(p=>p.has_profile).length;return i.jsxs("div",{className:"page",children:[i.jsxs("header",{className:"pagehead",children:[i.jsxs("div",{children:[i.jsx("h2",{children:"Customers"}),i.jsx("p",{children:"Everyone this business has recognised. Fill in details once and they are known at every store."})]}),i.jsxs("label",{className:"search",children:[i.jsx(Sc,{size:15}),i.jsx("input",{placeholder:"Search by name or phone",value:t,onChange:p=>n(p.target.value)})]})]}),o&&i.jsxs("div",{className:"err",children:[i.jsx(Se,{size:15}),o]}),i.jsxs("div",{className:"metrics",style:{marginBottom:"var(--sp-4)"},children:[i.jsx(_r,{k:"Known people",v:u.length||"—"}),i.jsx(_r,{k:"With details",v:c||"—",s:u.length?`${Math.round(100*c/u.length)}% captured`:null}),i.jsx(_r,{k:"Returning",v:u.filter(p=>p.visit_count>1).length||"—"}),i.jsx(_r,{k:"With consent",v:u.filter(p=>p.has_consent).length||"—"})]}),i.jsx("div",{className:"panel",children:u.length===0?i.jsxs("div",{className:"empty tall",children:[i.jsx(_c,{size:34}),i.jsx("b",{children:"No customers yet"}),i.jsx("p",{children:"They appear here the first time a camera recognises them. Click one to add a name and phone number."})]}):i.jsx("div",{className:"tablewrap",children:i.jsxs("table",{children:[i.jsx("thead",{children:i.jsxs("tr",{children:[i.jsx("th",{children:"Customer"}),i.jsx("th",{children:"Phone"}),i.jsx("th",{className:"num",children:"Visits"}),i.jsx("th",{children:"First seen"}),i.jsx("th",{children:"Last seen"}),i.jsx("th",{children:"Details"})]})}),i.jsx("tbody",{children:u.map(p=>i.jsxs("tr",{className:"click",onClick:()=>l(p),children:[i.jsxs("td",{children:[p.full_name||i.jsx("span",{className:"note",children:p.label}),p.full_name&&p.ref&&i.jsxs("span",{className:"note",children:[" · ",p.ref]})]}),i.jsx("td",{className:"mono",children:p.phone||"—"}),i.jsx("td",{className:"num",children:p.visit_count}),i.jsx("td",{children:la(p.first_seen_at)}),i.jsx("td",{children:la(p.last_seen_at)}),i.jsx("td",{children:p.has_profile?i.jsxs("span",{className:"pill ok",children:[i.jsx("i",{className:"dot ok"}),"captured"]}):i.jsxs("span",{className:"pill warn",children:[i.jsx("i",{className:"dot warn"}),"needed"]})})]},p.id))})]})})}),r&&i.jsx(_p,{customer:r,session:e,onClose:()=>l(null),onSaved:()=>{l(null),a()}})]})}function _r({k:e,v:t,s:n}){return i.jsxs("div",{className:"metric",children:[i.jsx("div",{className:"metric-k",children:e}),i.jsx("div",{className:"metric-v",children:t}),n&&i.jsx("div",{className:"metric-s",children:n})]})}const ln=[{id:"hikvision",label:"Hikvision",path:"/Streaming/Channels/101",note:"Channel 1, main stream. Use /Streaming/Channels/102 for the lower-quality sub stream."},{id:"dahua",label:"Dahua",path:"/cam/realmonitor?channel=1&subtype=0",note:"Channel 1, main stream. subtype=1 is the sub stream."},{id:"cpplus",label:"CP Plus",path:"/cam/realmonitor?channel=1&subtype=0",note:"CP Plus cameras use the Dahua stream path."},{id:"uniview",label:"Uniview",path:"/media/video1",note:"Some older Uniview models use /video1 instead."},{id:"tplink",label:"TP-Link / Tapo",path:"/stream1",note:"Tapo cameras need a separate camera account created in the Tapo app — your Tapo login will not work."},{id:"reolink",label:"Reolink",path:"/h264Preview_01_main",note:"Use /h264Preview_01_sub for the lower-quality stream."},{id:"amcrest",label:"Amcrest",path:"/cam/realmonitor?channel=1&subtype=0",note:"Amcrest cameras use the Dahua stream path."},{id:"axis",label:"Axis",path:"/axis-media/media.amp",note:""},{id:"onvif",label:"Other (ONVIF)",path:"/onvif1",note:"Many generic cameras answer here. If it does not work, look for “RTSP” in the camera’s own app."},{id:"manual",label:"I know the path",path:"",note:""}],Pr=e=>ln.find(t=>t.id===e)||ln[ln.length-1],Zs={id:"",host:"",port:554,path:"",username:"",password:"",max_width:1280};function zp(){const{data:e,error:t,reload:n}=et(()=>$.cameras(),8e3),[r,l]=P.useState(null),[s,o]=P.useState(null),a=e??[],u=a.some(h=>h.remote),c=Lp(u?[]:a);async function p(h){if(confirm(`Remove ${h.id}? Recognition from it stops immediately.`))try{await $.deleteCamera(h.id),n()}catch(v){alert(fe(v))}}return i.jsxs("div",{className:"page",children:[i.jsxs("header",{className:"pagehead",children:[i.jsxs("div",{children:[i.jsx("h2",{children:"Cameras"}),i.jsx("p",{children:u?"The cameras across your shops, as the shop computers last reported them.":"Add a camera, then prove it can see faces with a walk-past. Only then is it working."})]}),!u&&i.jsxs("button",{className:"btn primary",onClick:()=>l({...Zs}),children:[i.jsx(sa,{size:15}),"Add camera"]})]}),u&&i.jsxs("div",{className:"viewing",children:[i.jsx("b",{children:"Viewing your shops from here."})," These cameras are wired to the shop computers, so they are set up and checked there. The picture is each camera's most recent frame, not live video."]}),t&&i.jsxs("div",{className:"err",children:[i.jsx(Se,{size:15}),t]}),a.length===0?i.jsx("div",{className:"panel",children:i.jsxs("div",{className:"empty tall",children:[i.jsx(Ec,{size:40}),i.jsx("b",{children:"No cameras yet"}),i.jsx("p",{children:"Add the camera by its address. Most cameras print it on a label underneath, or show it in their own app."}),i.jsxs("button",{className:"btn primary",onClick:()=>l({...Zs}),children:[i.jsx(sa,{size:15}),"Add your first camera"]})]})}):i.jsx("div",{className:"camgrid",children:a.map(h=>i.jsx(Tp,{cam:h,stream:c[h.id],onEdit:()=>l(h),onCheck:()=>o(h.id),onRemove:()=>p(h)},h.id))}),r&&i.jsx(Mp,{cam:r,onClose:()=>l(null),onSaved:()=>{l(null),n()}}),s&&i.jsx(Rp,{id:s,onClose:()=>o(null)})]})}function Tp({cam:e,stream:t,onEdit:n,onCheck:r,onRemove:l}){var c,p,h;const s=e.connected===void 0||e.connected===null?e.remote?{tone:"idle",label:"Waiting for the shop computer"}:{tone:"idle",label:"Engine stopped"}:e.connected?{tone:"ok",label:"Connected"}:{tone:"bad",label:"Not connecting"},{data:o}=et(()=>e.remote?Promise.resolve(null):$.placementResult(e.id),15e3,[e.id]),a=!o||o.running||!o.verdict||o.verdict==="starting"?{tone:"miss",label:"Not yet proven",text:"Walk past it once and Behavision will tell you if the placement works."}:o.verdict==="good"?{tone:"seen",label:"Proven",text:o.headline||"Faces recognised on a walk-past."}:{tone:"miss",label:"Not proven",text:o.headline||"Move the camera and check again."},u=(c=e.snapshot)!=null&&c.available?e.snapshot.url:"";return i.jsxs("article",{className:"camcard",children:[i.jsxs("div",{className:"camview",children:[t||u?i.jsx("img",{src:t||u,alt:e.id}):i.jsx("div",{className:"placeholder",children:i.jsx(Ec,{size:34})}),i.jsxs("span",{className:`pill ${s.tone==="idle"?"":s.tone} over`,children:[i.jsx("i",{className:`dot ${s.tone}`}),s.label]})]}),i.jsxs("div",{className:"cambody",children:[i.jsxs("div",{className:"camtitle",children:[i.jsxs("div",{children:[i.jsx("h3",{children:e.label||e.camera_id||e.id}),i.jsx("span",{className:"mono note",children:e.remote?e.site||e.site_slug||"":`${e.host||e.url}${e.path?` · ${e.path}`:""}`})]}),!e.remote&&i.jsxs("div",{className:"camactions",children:[i.jsx("button",{className:"btn sm",onClick:n,children:"Edit"}),i.jsx("button",{className:"btn sm danger",onClick:l,children:"Remove"})]})]}),e.remote?i.jsx("div",{className:"camproof",children:i.jsx("span",{className:"note",children:(p=e.snapshot)!=null&&p.available?"Last picture from the shop computer.":((h=e.snapshot)==null?void 0:h.reason)||"No picture yet from the shop computer."})}):i.jsxs("div",{className:"camproof",children:[i.jsx("span",{className:`tag ${a.tone}`,children:a.label}),i.jsx("span",{className:"note",children:a.text}),i.jsxs("button",{className:`btn sm ${a.tone==="seen"?"":"primary"}`,onClick:r,children:[i.jsx(Nc,{size:13}),a.tone==="seen"?"Check again":"Check placement"]})]})]})]})}function Lp(e){const[t,n]=P.useState({});return P.useEffect(()=>{let r=!0;const l=e.filter(s=>!(s.id in t));if(l.length)return(async()=>{const s={};for(const o of l)try{s[o.id]=await $.streamURL(o.id)}catch{s[o.id]=""}r&&n(o=>({...o,...s}))})(),()=>{r=!1}},[e.map(r=>r.id).join("|")]),t}function Mp({cam:e,onClose:t,onSaved:n}){var E;const r=!e.id,[l,s]=P.useState({...Zs,...e,password:"",path:e.path||(r?ln[0].path:"")}),[o,a]=P.useState(r?ln[0].id:"manual"),[u,c]=P.useState(null),[p,h]=P.useState(null),[v,g]=P.useState(null),x=z=>T=>s({...l,[z]:T.target.value});function w(z){const T=Pr(z.target.value);a(T.id),s(V=>({...V,path:T.path||V.path}))}function L(){const z={};for(const[T,V]of Object.entries(l))V===""||V===null||V===void 0||(z[T]=T==="port"||T==="max_width"?Number(V):V);return z}async function f(){h("test"),g(null),c(null);try{c(await $.testCamera(L()))}catch(z){g(fe(z))}finally{h(null)}}async function d(z){z.preventDefault(),h("save"),g(null);try{await $.saveCamera(r?"":e.id,L()),n()}catch(T){g(fe(T))}finally{h(null)}}const m=Pr(o),[y,S]=P.useState(null);async function C(){S("busy");try{S(await $.discoverCameras())}catch(z){S({error:fe(z)})}}function k(z){const T=z.make?Pr(z.make):null;s(V=>({...V,host:z.host,path:(T==null?void 0:T.path)||V.path,id:V.id||""})),T&&a(T.id),c(null)}return i.jsx("div",{className:"drawer",onMouseDown:z=>z.target===z.currentTarget&&t(),children:i.jsxs("div",{className:"sheet",children:[i.jsxs("div",{className:"sheethead",children:[i.jsx("h2",{children:r?"Add a camera":`Edit ${e.id}`}),i.jsx("button",{className:"close",onClick:t,"aria-label":"Close",children:i.jsx(Qi,{size:16})})]}),i.jsxs("form",{className:"sheetbody",onSubmit:d,autoComplete:"off",children:[i.jsx("p",{className:"lead",children:"Three things from the camera: its address, its make, and its password. Test before you save — a wrong address is the most common mistake."}),v&&i.jsxs("div",{className:"err",children:[i.jsx(Se,{size:15}),v]}),r&&i.jsxs("section",{className:"formsection",children:[i.jsx("h4",{children:"Find it"}),y===null&&i.jsxs("div",{className:"finder",children:[i.jsx("p",{children:"Behavision can look for cameras on this shop’s network."}),i.jsxs("button",{type:"button",className:"btn primary",onClick:C,children:[i.jsx(Sc,{size:14}),"Find cameras on this network"]})]}),y==="busy"&&i.jsxs("div",{className:"finder busy",children:[i.jsx("span",{className:"spinner"}),"Looking on the network… a few seconds."]}),(y==null?void 0:y.error)&&i.jsxs("div",{className:"err",children:[i.jsx(Se,{size:15}),y.error]}),(y==null?void 0:y.cameras)&&(y.cameras.length===0?i.jsxs("div",{className:"finder",children:[i.jsxs("p",{children:["Nothing answered on ",((E=y.networks)==null?void 0:E.join(", "))||"this network",". The camera may be on a different network, switched off, or not yet connected — check its cable and power, then try again. You can still type its address below."]}),i.jsx("button",{type:"button",className:"btn",onClick:C,children:"Try again"})]}):i.jsxs("ul",{className:"foundlist",children:[y.cameras.map(z=>i.jsx("li",{className:l.host===z.host?"picked":"",children:i.jsxs("button",{type:"button",onClick:()=>k(z),children:[i.jsx("span",{className:"mono",children:z.host}),i.jsx("span",{className:"what",children:z.name||(z.rtsp?"Streams video (RTSP)":"Answers ONVIF")}),z.make&&i.jsx("span",{className:"tag seen",children:Pr(z.make).label}),l.host===z.host&&i.jsx(Xs,{size:16})]})},z.host)),i.jsx("li",{className:"rescan",children:i.jsx("button",{type:"button",className:"btn sm",onClick:C,children:"Scan again"})})]}))]}),i.jsxs("section",{className:"formsection",children:[i.jsx("h4",{children:"The camera"}),r&&i.jsxs("label",{className:"field",children:[i.jsx("span",{children:"Name"}),i.jsx("input",{value:l.id,onChange:x("id"),placeholder:"entrance",required:!0,autoFocus:!0}),i.jsx("em",{className:"hint",children:"Short, no spaces. It names this camera everywhere and cannot be changed later."})]}),i.jsxs("div",{className:"fieldrow",children:[i.jsxs("label",{className:"field",children:[i.jsx("span",{children:"Address"}),i.jsx("input",{value:l.host,onChange:x("host"),placeholder:"192.168.1.20",inputMode:"decimal"}),i.jsx("em",{className:"hint",children:"On a label on the camera, or in its own app under “network”."})]}),i.jsxs("label",{className:"field narrow",children:[i.jsx("span",{children:"Port"}),i.jsx("input",{value:l.port,onChange:x("port"),inputMode:"numeric"})]})]})]}),i.jsxs("section",{className:"formsection",children:[i.jsx("h4",{children:"The stream"}),i.jsxs("label",{className:"field",children:[i.jsx("span",{children:"Make of camera"}),i.jsx("select",{value:o,onChange:w,children:ln.map(z=>i.jsx("option",{value:z.id,children:z.label},z.id))}),m.note&&i.jsx("em",{className:"hint",children:m.note})]}),i.jsxs("label",{className:"field",children:[i.jsx("span",{children:"Stream path"}),i.jsx("input",{className:"mono",value:l.path,onChange:x("path"),placeholder:"/Streaming/Channels/101"}),i.jsx("em",{className:"hint",children:"Filled in from the make. Change it only if the camera’s own app says something else."})]})]}),i.jsxs("section",{className:"formsection",children:[i.jsx("h4",{children:"Sign-in to the camera"}),i.jsxs("div",{className:"fieldrow",children:[i.jsxs("label",{className:"field",children:[i.jsx("span",{children:"Username"}),i.jsx("input",{name:"rtsp-account",autoComplete:"off",value:l.username,onChange:x("username"),placeholder:"admin"})]}),i.jsxs("label",{className:"field",children:[i.jsx("span",{children:"Password"}),i.jsx("input",{type:"password",name:"rtsp-secret",autoComplete:"new-password",value:l.password,onChange:x("password"),placeholder:e.has_password?"(unchanged)":""})]})]})]}),u&&(u.ok?i.jsxs("div",{className:"testresult ok",children:[i.jsx(Xs,{size:16}),i.jsxs("div",{children:[i.jsxs("b",{children:["Connected — ",u.width,"×",u.height,u.codec?` · ${String(u.codec).toUpperCase()}`:""]}),u.snapshot&&i.jsx("img",{alt:"Camera preview",src:`data:image/jpeg;base64,${u.snapshot}`})]})]}):i.jsxs("div",{className:"testresult bad",children:[i.jsx(Se,{size:16}),i.jsxs("div",{children:[i.jsx("b",{children:"Could not connect"}),i.jsx("span",{children:u.error})]})]})),i.jsxs("div",{className:"sheetactions",children:[i.jsx("button",{type:"button",className:"btn",onClick:f,disabled:!!p||!l.host,children:p==="test"?"Connecting…":"Test connection"}),i.jsx("button",{className:"btn primary",disabled:!!p||!l.id||!l.host,children:p==="save"?"Saving…":r?"Add camera":"Save changes"})]})]})]})})}function Rp({id:e,onClose:t}){var p,h;const[n,r]=P.useState({verdict:"starting",advice:[]}),[l,s]=P.useState(null),o=P.useRef(null);P.useEffect(()=>{let v=!0;return(async()=>{try{r(await $.startPlacement(e,25)),o.current=setInterval(async()=>{try{const g=await $.placementResult(e);if(!v)return;r(g),g.running||clearInterval(o.current)}catch(g){v&&s(fe(g))}},1e3)}catch(g){v&&s(fe(g))}})(),()=>{v=!1,clearInterval(o.current)}},[e]);const a={good:"ok",marginal:"warn",poor:"bad",artifact:"bad",no_faces:"warn",inconclusive:"warn",no_completed_passes:"warn"}[n.verdict],u=n.seconds?Math.min(100,n.elapsed/n.seconds*100):0,c=n.running||n.verdict==="starting";return i.jsx("div",{className:"drawer",onMouseDown:v=>v.target===v.currentTarget&&t(),children:i.jsxs("div",{className:"sheet",children:[i.jsxs("div",{className:"sheethead",children:[i.jsxs("h2",{children:["Check placement · ",e]}),i.jsx("button",{className:"close",onClick:t,"aria-label":"Close",children:i.jsx(Qi,{size:16})})]}),i.jsxs("div",{className:"sheetbody",children:[l&&i.jsxs("div",{className:"err",children:[i.jsx(Se,{size:15}),l]}),i.jsxs("ol",{className:"steps",children:[i.jsxs("li",{className:c?"now":"done",children:[i.jsx("b",{children:"Walk past the camera"}),i.jsx("span",{children:"The way a customer would — in through the door, not looking at the lens. Two or three times, for about 25 seconds."}),c&&i.jsx("div",{className:"progress",children:i.jsx("div",{style:{width:`${u}%`}})})]}),i.jsxs("li",{className:c?"":"now",children:[i.jsx("b",{children:"Behavision judges what it saw"}),i.jsx("span",{children:"Not “were the frames sharp”, but “did a person walking past produce a face worth recognising”."})]})]}),i.jsxs("div",{className:`verdict ${a??""}`,children:[i.jsxs("div",{className:"verdict-head",children:[c?i.jsx("span",{className:"spinner"}):a==="ok"?i.jsx(Xs,{size:18}):i.jsx(Se,{size:18}),i.jsx("b",{children:n.headline||"Watching…"})]}),((p=n.advice)==null?void 0:p.length)>0&&i.jsx("ul",{children:n.advice.map((v,g)=>i.jsx("li",{children:v},g))}),((h=n.quality)==null?void 0:h.n)>0&&i.jsxs("p",{className:"note",children:[n.quality.n," face",n.quality.n===1?"":"s"," seen · median quality ",n.quality.p50," ·"," ",Math.round((n.quality.fraction_below_gate??0)*100),"% too poor to use"]})]}),!c&&i.jsxs("div",{className:"sheetactions",children:[i.jsx("button",{className:"btn",onClick:t,children:"Close"}),i.jsx("button",{className:"btn primary",onClick:()=>{r({verdict:"starting",advice:[]}),$.startPlacement(e,25).then(r).catch(v=>s(fe(v)))},children:"Run again"})]})]})]})})}const Dp=["Is my shop working right now?","How do I add my camera?","Why has nobody been recognised today?","Who came in this morning?"];function Op({session:e,onClose:t}){const[n,r]=P.useState([]),[l,s]=P.useState(""),[o,a]=P.useState(!1),[u,c]=P.useState(null),p=P.useRef(null),h=e==null?void 0:e.standalone;P.useEffect(()=>{var g;(g=p.current)==null||g.scrollTo({top:1e9,behavior:"smooth"})},[n,o]);async function v(g){const x=(g??l).trim();if(!x||o)return;const w=[...n,{role:"user",text:x}];r(w),s(""),a(!0),c(null);try{const L=await $.ask(w.map(f=>({role:f.role,text:f.text})));r([...w,{role:"assistant",text:L.text,used:L.used??[]}])}catch(L){const f=fe(L);c(/session expired|unauthori[sz]ed/i.test(f)?"You’re signed out of head office, so I can’t look anything up. Sign in again and ask me once more.":f),r(n),s(x)}finally{a(!1)}}return i.jsxs("aside",{className:"helper",role:"dialog","aria-label":"Loya",children:[i.jsxs("header",{className:"helperhead",children:[i.jsx("img",{src:gt,alt:""}),i.jsxs("div",{children:[i.jsx("b",{children:"Loya"}),i.jsx("span",{children:"Your Behavision buddy — knows your cameras, your customers and your numbers."})]}),i.jsx("button",{className:"close",onClick:t,"aria-label":"Close",children:i.jsx(Qi,{size:16})})]}),i.jsxs("div",{className:"helperbody",ref:p,children:[h&&i.jsxs("div",{className:"helpernote",children:[i.jsx(Cc,{size:16}),i.jsxs("div",{children:[i.jsx("b",{children:"This PC runs on its own"}),i.jsx("span",{children:"Loya lives at head office. Link this PC to a shop to talk to her; the basics are below."})]})]}),n.length===0&&i.jsxs("div",{className:"helperintro",children:[i.jsx("div",{className:"turn assistant",children:i.jsxs("div",{className:"bubble",children:[Fp(e)," Ask me anything about ",(e==null?void 0:e.site_name)||"this shop"," — what’s happening now, or how to set something up."]})}),i.jsx("div",{className:"chips",children:Dp.map(g=>i.jsx("button",{className:"chip",onClick:()=>v(g),disabled:o||h,children:g},g))}),h&&i.jsx(Ip,{})]}),n.map((g,x)=>{var w;return i.jsxs("div",{className:`turn ${g.role}`,children:[i.jsx("div",{className:"bubble",children:g.text}),((w=g.used)==null?void 0:w.length)>0&&i.jsxs("span",{className:"used",children:["Looked at: ",g.used.join(", ")]})]},x)}),o&&i.jsx("div",{className:"turn assistant",children:i.jsxs("div",{className:"bubble thinking",children:[i.jsx("span",{}),i.jsx("span",{}),i.jsx("span",{})]})}),u&&i.jsxs("div",{className:"err",children:[i.jsx(Se,{size:15}),u]})]}),i.jsxs("form",{className:"helperask",onSubmit:g=>{g.preventDefault(),v()},children:[i.jsx("input",{value:l,onChange:g=>s(g.target.value),disabled:o||h,placeholder:h?"Link this PC to head office to talk to Loya":"Ask Loya…"}),i.jsx("button",{className:"btn primary",disabled:o||h||!l.trim(),"aria-label":"Ask",children:i.jsx(hp,{size:16})})]})]})}function Fp(e){var l,s;const t=new Date().getHours(),n=t<12?"Morning":t<17?"Afternoon":"Evening",r=(s=(l=e==null?void 0:e.user)==null?void 0:l.full_name)==null?void 0:s.split(" ")[0];return r?`${n}, ${r}.`:`${n}.`}function Ip(){return i.jsxs("dl",{className:"quickhelp",children:[i.jsx("dt",{children:"Adding a camera"}),i.jsx("dd",{children:"Cameras → Add camera. The address is on a label on the camera; pick the make and the stream path fills itself in. Test, then save."}),i.jsx("dt",{children:"Proving it works"}),i.jsx("dd",{children:"Press Check placement and walk past the camera like a customer for 25 seconds. Only “good” means it can recognise faces — otherwise move it to head height, facing the way people approach."}),i.jsx("dt",{children:"Nothing showing on Live"}),i.jsx("dd",{children:"Check the camera is Connected and Proven. A camera aimed from above or the side streams fine and recognises nobody."}),i.jsx("dt",{children:"Linking to head office later"}),i.jsx("dd",{children:"Use the link button at the bottom of the sidebar and type an installation code from head office. Nothing recorded here is lost."})]})}const $p=[{id:"live",label:"Live",Glyph:op,View:Pc},{id:"customers",label:"Customers",Glyph:ap,View:Pp,cloud:!0},{id:"cameras",label:"Cameras",Glyph:up,View:zp}];function Up(){var v,g,x;const[e,t]=P.useState(null),[n,r]=P.useState("live"),[l,s]=P.useState(!0),[o,a]=P.useState(!1),[u,c]=P.useState(!1);if(P.useEffect(()=>{let w=!0;const L=async()=>{try{const d=await $.session();w&&t(m=>JSON.stringify(m)===JSON.stringify(d)?m:d)}catch{w&&t(null)}w&&s(!1)};L();const f=setInterval(L,8e3);return()=>{w=!1,clearInterval(f)}},[]),!sp())return i.jsx("div",{className:"login",children:i.jsxs("div",{className:"box",children:[i.jsx("span",{className:"mark",children:i.jsx("img",{src:gt,alt:""})}),i.jsx("h1",{children:"Behavision"}),i.jsx("p",{className:"lead",children:"This is the Behavision window running outside the app, so it has no connection to the recognition engine. Launch the Behavision application instead."})]})});if(l)return i.jsx("div",{className:"login",children:i.jsx("p",{className:"note",children:"Starting…"})});if(!(e!=null&&e.claimed)&&!(e!=null&&e.standalone))return i.jsx(aa,{onDone:t});if(!(e!=null&&e.standalone)&&!(e!=null&&e.logged_in))return i.jsx(mp,{onDone:t});const p=$p.filter(w=>!w.cloud||!e.standalone),h=((v=p.find(w=>w.id===n))==null?void 0:v.View)??Pc;return o?i.jsx(aa,{onDone:w=>{a(!1),t(w)},onCancel:()=>a(!1)}):i.jsxs("div",{className:"shell",children:[i.jsxs("aside",{className:"side",children:[i.jsxs("div",{className:"brand",children:[i.jsx("span",{className:"mark",children:i.jsx("img",{src:gt,alt:""})}),i.jsxs("div",{className:"id",children:[i.jsx("h1",{children:"Behavision"}),i.jsx("p",{children:e.site_name||((g=e.user)==null?void 0:g.client_name)||"This shop"})]})]}),i.jsx("nav",{className:"nav",children:p.map(({id:w,label:L,Glyph:f})=>i.jsxs("button",{onClick:()=>r(w),"aria-current":w===n?"page":void 0,children:[i.jsx(f,{size:17}),L]},w))}),i.jsx(Ap,{}),i.jsx("div",{className:"who",children:e.standalone?i.jsxs(i.Fragment,{children:[i.jsxs("div",{className:"id",children:[i.jsx("b",{children:"On its own"}),i.jsx("span",{children:"No head office"})]}),i.jsx("button",{className:"btn sm icon",title:"Link to head office",onClick:()=>a(!0),children:i.jsx(fp,{size:15})})]}):i.jsxs(i.Fragment,{children:[i.jsxs("div",{className:"id",children:[i.jsx("b",{children:"Signed in"}),i.jsx("span",{children:(x=e.user)==null?void 0:x.email})]}),i.jsx("button",{className:"btn sm icon",title:"Sign out",onClick:async()=>t(await $.logout()),children:i.jsx(pp,{size:15})})]})})]}),i.jsxs("main",{className:"main",children:[i.jsx(h,{session:e,onNavigate:r}),!u&&i.jsxs("button",{className:"loya-fab",onClick:()=>c(!0),"aria-label":"Ask Loya",title:"Ask Loya",children:[i.jsx("img",{src:gt,alt:""}),i.jsx("span",{children:"Loya"})]})]}),u&&i.jsx(Op,{session:e,onClose:()=>c(!1)})]})}function Ap(){const{data:e,reload:t}=et(()=>$.engineStatus(),5e3),[n,r]=P.useState(!1),l=e??{state:"stopped"},s=P.useCallback(async h=>{r(!0);try{await h()}catch(v){alert(fe(v))}finally{r(!1),t()}},[t]),o=l.state==="running",a=Object.values(l.cameras??{}),u=a.filter(Boolean).length;let c="idle",p="Stopped";return!o&&l.reachable?(c="warn",p="Running outside the app"):l.state==="failed"||l.state==="backoff"?(c="bad",p="Not running"):o&&!l.reachable&&l.progress?(c="warn",p=`Downloading ${l.progress.what}… ${l.progress.percent}%`):o&&!l.reachable?(c="warn",p="Starting…"):o&&a.length===0?(c="warn",p="No cameras"):o&&u===0?(c="bad",p="No camera connected"):o&&us($.startEngine),children:[i.jsx(Nc,{size:13}),"Start"]}),i.jsxs("button",{className:"btn sm",disabled:n||!o,onClick:()=>s($.stopEngine),children:[i.jsx(cp,{size:13}),"Stop"]})]})]})}jc(document.getElementById("root")).render(i.jsx(Kc.StrictMode,{children:i.jsx(Up,{})})); diff --git a/desktop/frontend/dist/assets/index-lhDNZRcC.css b/desktop/frontend/dist/assets/index-xWw5ie4A.css similarity index 98% rename from desktop/frontend/dist/assets/index-lhDNZRcC.css rename to desktop/frontend/dist/assets/index-xWw5ie4A.css index 357efea..4da2cc8 100644 --- a/desktop/frontend/dist/assets/index-lhDNZRcC.css +++ b/desktop/frontend/dist/assets/index-xWw5ie4A.css @@ -1 +1 @@ -:root{--bg: #0A0F13;--s1: #111A20;--s2: #17232B;--s3: #1E2D37;--line: #223038;--line-2: #1A252C;--ink: #ECF3F7;--ink-2: #A3B6C2;--ink-3: #6C808D;--accent: #40C4DC;--accent-2: #0F3B47;--accent-3: #0B2A33;--ok: #48C78E;--ok-2: #102E22;--warn: #EAAA3D;--warn-2: #31260F;--bad: #EC6A5C;--bad-2: #331815;--r-sm: 6px;--r: 10px;--r-lg: 14px;--sp-1: 4px;--sp-2: 8px;--sp-3: 12px;--sp-4: 16px;--sp-5: 20px;--sp-6: 24px;--sp-7: 32px;--sp-8: 40px;--shadow: 0 1px 2px rgb(0 0 0 / .4), 0 8px 24px -12px rgb(0 0 0 / .6);--shadow-lg: 0 2px 4px rgb(0 0 0 / .4), 0 24px 48px -16px rgb(0 0 0 / .7);--font: "Segoe UI Variable Text", "Segoe UI", Inter, -apple-system, BlinkMacSystemFont, system-ui, "Helvetica Neue", Arial, sans-serif;--font-display: "Segoe UI Variable Display", var(--font);--mono: "Cascadia Mono", "SFMono-Regular", ui-monospace, Menlo, Consolas, monospace;--ground: var(--bg);--surface: var(--s1);--surface-2: var(--s2);--line-soft: var(--line-2);--muted: var(--ink-3);--radius: var(--r);--accent-dim: var(--accent-3)}*{box-sizing:border-box;margin:0}html,body,#root{height:100%}body{background:var(--bg);color:var(--ink);font-family:var(--font);font-size:14px;line-height:1.5;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;overflow:hidden;-webkit-user-select:none;user-select:none}button,input,select,textarea{font:inherit;color:inherit}input,textarea{-webkit-user-select:text;user-select:text}:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:3px}::selection{background:var(--accent-2);color:var(--ink)}.num,.value,.metric-v,.when,.mono,.code,td{font-variant-numeric:tabular-nums}.mono,.code{font-family:var(--mono)}*{scrollbar-width:thin;scrollbar-color:var(--s3) transparent}*::-webkit-scrollbar{width:10px;height:10px}*::-webkit-scrollbar-track{background:transparent}*::-webkit-scrollbar-thumb{background:var(--s3);border-radius:99px;border:3px solid var(--bg)}*::-webkit-scrollbar-thumb:hover{background:#2a3d49}.shell{display:grid;grid-template-columns:232px 1fr auto;height:100%}.side{background:var(--s1);border-right:1px solid var(--line);display:flex;flex-direction:column;min-height:0}.side .brand{display:flex;align-items:center;gap:var(--sp-3);padding:var(--sp-5) var(--sp-5) var(--sp-4)}.side .brand .mark{width:30px;height:30px;flex:none;display:grid;place-items:center}.side .brand .mark img,.login .mark img{width:100%;height:100%;object-fit:contain;display:block}.side .brand .id{min-width:0}.side .brand h1{font-family:var(--font-display);font-size:15px;font-weight:600;letter-spacing:-.012em;line-height:1.2}.side .brand p{font-size:11.5px;color:var(--ink-3);margin-top:1px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.nav{padding:var(--sp-2) var(--sp-3);display:flex;flex-direction:column;gap:2px;flex:1}.nav button{position:relative;display:flex;align-items:center;gap:var(--sp-3);width:100%;background:none;border:0;border-radius:var(--r-sm);padding:9px var(--sp-3);color:var(--ink-2);cursor:pointer;text-align:left;font-size:13.5px;font-weight:450;transition:background .12s ease,color .12s ease}.nav button svg{flex:none;opacity:.9}.nav button:hover{background:var(--s2);color:var(--ink)}.nav button[aria-current=page]{background:var(--accent-3);color:var(--accent);font-weight:550}.nav button[aria-current=page]:before{content:"";position:absolute;left:-12px;top:7px;bottom:7px;width:2.5px;border-radius:0 2px 2px 0;background:var(--accent)}.enginebox{margin:var(--sp-3);padding:var(--sp-3) var(--sp-4) var(--sp-4);border:1px solid var(--line);border-radius:var(--r);background:var(--s2)}.enginebox .row{display:flex;align-items:center;gap:var(--sp-2)}.enginebox .state{font-size:12.5px;font-weight:600;letter-spacing:-.005em}.enginebox .label{display:block;color:var(--ink-3);font-size:11px;line-height:1.45;margin-top:3px;font-variant-numeric:tabular-nums}.enginebox .actions,.enginebox .controls{display:flex;gap:var(--sp-2);margin-top:var(--sp-3)}.enginebox .actions .btn,.enginebox .controls .btn{flex:1;justify-content:center;padding:6px 8px;font-size:12px}.side .who{padding:var(--sp-3) var(--sp-5) var(--sp-5);border-top:1px solid var(--line-2);display:flex;align-items:center;gap:var(--sp-3)}.side .who .id{min-width:0;flex:1}.side .who .id b{display:block;font-size:12.5px;font-weight:550}.side .who .id span{display:block;font-size:11px;color:var(--ink-3);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.main{min-width:0;min-height:0;overflow:auto;position:relative}.page{padding:var(--sp-6) var(--sp-7) var(--sp-8);max-width:1500px}.page>header{margin-bottom:var(--sp-5)}.page>header h2,.page h2{font-family:var(--font-display);font-size:22px;font-weight:600;letter-spacing:-.02em;line-height:1.2}.page>header p,.page header p{color:var(--ink-3);font-size:13px;margin-top:3px}.pagehead{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--sp-4);margin-bottom:var(--sp-5);flex-wrap:wrap}h3{font-size:11px;font-weight:600;letter-spacing:.085em;text-transform:uppercase;color:var(--ink-3)}.card{background:var(--s1);border:1px solid var(--line);border-radius:var(--r);padding:var(--sp-4)}.card>h3{margin-bottom:var(--sp-3)}.card.flush{padding:0;overflow:hidden}.panel{background:var(--s1);border:1px solid var(--line);border-radius:var(--r-lg);overflow:hidden;display:flex;flex-direction:column;min-height:0}.panel>.panelhead{display:flex;align-items:center;justify-content:space-between;gap:var(--sp-3);padding:var(--sp-3) var(--sp-4);border-bottom:1px solid var(--line-2);background:linear-gradient(var(--s2),var(--s1));flex:none}.panel>.panelhead h3{margin:0}.panel>.panelbody{padding:var(--sp-4);min-height:0;overflow:auto}.panel>.panelbody.flush{padding:0}.grid{display:grid;gap:var(--sp-4)}.cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}@media (max-width: 1180px){.cols-4{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (max-width: 980px){.cols-2,.cols-3{grid-template-columns:minmax(0,1fr)}}.metrics{display:grid;grid-template-columns:repeat(auto-fit,minmax(152px,1fr));gap:1px;background:var(--line);border:1px solid var(--line);border-radius:var(--r);overflow:hidden}.metric{background:var(--s1);padding:var(--sp-3) var(--sp-4) var(--sp-4)}.metric .metric-k{font-size:10.5px;font-weight:600;letter-spacing:.085em;text-transform:uppercase;color:var(--ink-3)}.metric .metric-v{font-family:var(--font-display);font-size:26px;font-weight:600;letter-spacing:-.025em;line-height:1.1;margin-top:5px}.metric .metric-s{font-size:11.5px;color:var(--ink-3);margin-top:3px;line-height:1.4}.metric.ok .metric-v{color:var(--ok)}.metric.warn .metric-v{color:var(--warn)}.metric.bad .metric-v{color:var(--bad)}.stat h3{margin-bottom:var(--sp-2)}.stat .value{font-family:var(--font-display);font-size:26px;font-weight:600;letter-spacing:-.025em;line-height:1.1}.stat .unit{font-size:15px;color:var(--ink-3);margin-left:3px}.stat .sub{font-size:11.5px;color:var(--ink-3);margin-top:4px;line-height:1.4}.dot{width:7px;height:7px;border-radius:99px;flex:none;background:var(--ink-3)}.dot.ok{background:var(--ok);box-shadow:0 0 0 3px color-mix(in srgb,var(--ok) 18%,transparent)}.dot.warn{background:var(--warn);box-shadow:0 0 0 3px color-mix(in srgb,var(--warn) 18%,transparent)}.dot.bad{background:var(--bad);box-shadow:0 0 0 3px color-mix(in srgb,var(--bad) 18%,transparent)}.dot.idle{background:var(--ink-3)}.dot.live{background:var(--ok);animation:pulse 2.4s ease-in-out infinite}@keyframes pulse{0%,to{box-shadow:0 0 color-mix(in srgb,var(--ok) 55%,transparent)}70%{box-shadow:0 0 0 6px color-mix(in srgb,var(--ok) 0%,transparent)}}.pill{display:inline-flex;align-items:center;gap:6px;padding:3px 9px 3px 7px;border-radius:99px;font-size:11px;font-weight:600;letter-spacing:.02em;background:var(--s3);color:var(--ink-2);border:1px solid var(--line);white-space:nowrap}.pill.ok{background:var(--ok-2);color:var(--ok);border-color:color-mix(in srgb,var(--ok) 28%,transparent)}.pill.warn{background:var(--warn-2);color:var(--warn);border-color:color-mix(in srgb,var(--warn) 28%,transparent)}.pill.bad{background:var(--bad-2);color:var(--bad);border-color:color-mix(in srgb,var(--bad) 28%,transparent)}.pill.accent{background:var(--accent-3);color:var(--accent);border-color:color-mix(in srgb,var(--accent) 30%,transparent)}.tag{display:inline-flex;align-items:center;padding:2px 7px;border-radius:var(--r-sm);font-size:10.5px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;background:var(--s3);color:var(--ink-2)}.tag.new{background:var(--accent-3);color:var(--accent)}.tag.seen{background:var(--ok-2);color:var(--ok)}.tag.miss{background:var(--warn-2);color:var(--warn)}.statusbar{display:flex;align-items:center;gap:var(--sp-5);flex-wrap:wrap;padding:var(--sp-3) var(--sp-4);background:var(--s1);border:1px solid var(--line);border-radius:var(--r);margin-bottom:var(--sp-4)}.statusbar .item{display:flex;align-items:center;gap:var(--sp-2);font-size:12.5px}.statusbar .item b{font-weight:600;letter-spacing:-.005em}.statusbar .item svg{color:var(--ink-3)}.statusbar .sep{width:1px;align-self:stretch;background:var(--line)}.statusbar .grow{flex:1}.arrivals{display:flex;flex-direction:column;gap:var(--sp-2);padding:var(--sp-3)}.arrival{display:grid;grid-template-columns:46px 1fr auto;gap:var(--sp-3);align-items:center;padding:var(--sp-3);border-radius:var(--r);background:var(--s2);border:1px solid var(--line-2);position:relative;overflow:hidden}.arrival:before{content:"";position:absolute;left:0;top:0;bottom:0;width:2.5px;background:var(--ink-3)}.arrival.is-new:before{background:var(--accent)}.arrival.is-seen:before{background:var(--ok)}.arrival.is-miss:before{background:var(--warn)}.arrival.fresh{animation:slidein .28s cubic-bezier(.2,.8,.3,1)}@keyframes slidein{0%{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:none}}.arrival .avatar{width:46px;height:46px;border-radius:var(--r-sm);display:grid;place-items:center;overflow:hidden;background:var(--s3);border:1px solid var(--line);font-family:var(--font-display);font-size:15px;font-weight:600;color:var(--ink-2);letter-spacing:-.01em;font-variant-numeric:tabular-nums}.arrival .avatar img{width:100%;height:100%;object-fit:cover}.arrival.is-new .avatar{background:var(--accent-3);color:var(--accent);border-color:color-mix(in srgb,var(--accent) 25%,transparent)}.arrival .who{min-width:0}.arrival .who .name{font-size:14.5px;font-weight:600;letter-spacing:-.01em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.arrival .who .meta{font-size:11.5px;color:var(--ink-3);margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.arrival .right{text-align:right;display:flex;flex-direction:column;align-items:flex-end;gap:5px}.arrival .right .when{font-size:11.5px;color:var(--ink-3)}.feeds{display:grid;gap:var(--sp-3);grid-template-columns:repeat(auto-fit,minmax(300px,1fr));padding:var(--sp-3)}.feed{position:relative;border-radius:var(--r);overflow:hidden;background:#05090c;border:1px solid var(--line);aspect-ratio:16 / 9}.feed img{width:100%;height:100%;object-fit:cover;display:block}.feed .placeholder{width:100%;height:100%;display:grid;place-items:center;color:var(--ink-3)}.feed .cap{position:absolute;left:0;right:0;bottom:0;display:flex;align-items:center;justify-content:space-between;gap:var(--sp-2);padding:var(--sp-5) var(--sp-3) var(--sp-3);background:linear-gradient(transparent,#000c);font-size:12.5px;font-weight:600;letter-spacing:-.005em}.events,.timeline{list-style:none;padding:0;display:flex;flex-direction:column}.events li{display:flex;align-items:center;gap:var(--sp-3);padding:9px var(--sp-4);border-bottom:1px solid var(--line-2);font-size:13px}.timeline li{display:flex;align-items:center;gap:var(--sp-3);padding:9px 0;border-bottom:1px solid var(--line-2);font-size:13px}.events li:last-child,.timeline li:last-child{border-bottom:0}.events .when,.timeline .when{font-size:11.5px;color:var(--ink-3);width:46px;flex:none}.empty{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--sp-3);padding:var(--sp-8) var(--sp-5);color:var(--ink-3);text-align:center;font-size:12.5px}.empty svg{opacity:.35}.empty b{display:block;color:var(--ink-2);font-size:13.5px;font-weight:550}.empty p{max-width:34ch;line-height:1.5}.tablewrap{overflow:auto}table{border-collapse:collapse;width:100%;font-size:13px}th{text-align:left;padding:9px var(--sp-4);font-size:10.5px;font-weight:600;letter-spacing:.085em;text-transform:uppercase;color:var(--ink-3);background:var(--s2);border-bottom:1px solid var(--line);position:sticky;top:0;z-index:1}td{padding:10px var(--sp-4);border-bottom:1px solid var(--line-2)}tbody tr:last-child td{border-bottom:0}tbody tr[role=button],tbody tr.clickable{cursor:pointer}tbody tr[role=button]:hover,tbody tr.clickable:hover{background:var(--s2)}.btn{display:inline-flex;align-items:center;gap:7px;padding:8px 14px;border-radius:var(--r-sm);background:var(--s3);color:var(--ink);border:1px solid var(--line);cursor:pointer;font-size:13px;font-weight:550;letter-spacing:-.005em;white-space:nowrap;transition:background .12s ease,border-color .12s ease,transform .06s ease}.btn:hover:not(:disabled){background:#253643;border-color:#2e414e}.btn:active:not(:disabled){transform:translateY(.5px)}.btn:disabled{opacity:.45;cursor:not-allowed}.btn svg{flex:none}.btn.primary{background:var(--accent);color:#04171c;border-color:transparent;font-weight:600}.btn.primary:hover:not(:disabled){background:#55d0e6}.btn.danger{background:var(--bad-2);color:var(--bad);border-color:color-mix(in srgb,var(--bad) 32%,transparent)}.btn.danger:hover:not(:disabled){background:#43201c}.btn.ghost{background:transparent}.btn.ghost:hover:not(:disabled){background:var(--s2)}.btn.sm{padding:5px 10px;font-size:12px}.btn.icon{padding:7px}.linkbtn{background:none;border:0;color:var(--accent);cursor:pointer;font-size:12.5px;padding:2px 0;text-align:left}.linkbtn:hover{text-decoration:underline}.seg{display:inline-flex;background:var(--s2);border:1px solid var(--line);border-radius:var(--r-sm);padding:2px;gap:2px}.seg button{background:none;border:0;border-radius:4px;padding:5px 11px;color:var(--ink-3);cursor:pointer;font-size:12.5px;font-weight:500}.seg button[aria-pressed=true]{background:var(--s3);color:var(--ink)}.field{display:block;margin-bottom:var(--sp-4)}.field>span{display:block;font-size:11.5px;font-weight:550;color:var(--ink-2);margin-bottom:6px}.field input,.field select,.field textarea{width:100%;padding:9px 11px;background:var(--s2);color:var(--ink);border:1px solid var(--line);border-radius:var(--r-sm);transition:border-color .12s ease,background .12s ease,box-shadow .12s ease}.field input::placeholder{color:var(--ink-3)}.field input:hover,.field select:hover,.field textarea:hover{border-color:#2c3d49}.field input:focus,.field select:focus,.field textarea:focus{outline:none;border-color:var(--accent);background:var(--s1);box-shadow:0 0 0 3px var(--accent-3)}.field .hint{display:block;font-size:11.5px;color:var(--ink-3);margin-top:5px;line-height:1.45}.fieldrow{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--sp-3)}.fieldrow.three{grid-template-columns:repeat(3,minmax(0,1fr))}.err{display:flex;align-items:flex-start;gap:var(--sp-2);background:var(--bad-2);color:var(--bad);border:1px solid color-mix(in srgb,var(--bad) 30%,transparent);border-radius:var(--r-sm);padding:9px 11px;font-size:12.5px;line-height:1.45;margin-bottom:var(--sp-4)}.err svg{flex:none;margin-top:1px}.note{color:var(--ink-3);font-size:12px;line-height:1.5}.note.warn{color:var(--warn)}.note.bad{color:var(--bad)}.lead{color:var(--ink-2);font-size:13.5px;line-height:1.55}.sm{font-size:12px}.lbl,.key{color:var(--ink-3);font-size:11.5px}.grow{flex:1}.row{display:flex;align-items:center;gap:var(--sp-3)}.login{height:100%;display:grid;place-items:center;padding:var(--sp-6);overflow:auto;background:radial-gradient(900px 480px at 50% -10%,#10303A 0%,transparent 62%),var(--bg)}.login .box{width:100%;max-width:396px;background:var(--s1);border:1px solid var(--line);border-radius:var(--r-lg);padding:var(--sp-7);box-shadow:var(--shadow-lg)}.login .mark{width:44px;height:44px;margin-bottom:var(--sp-4);display:grid;place-items:center}.login h1{font-family:var(--font-display);font-size:21px;font-weight:600;letter-spacing:-.022em}.login .lead{margin:6px 0 var(--sp-5)}.login .btn{width:100%;justify-content:center;margin-top:var(--sp-1)}.login .foot{font-size:11.5px;color:var(--ink-3);line-height:1.55;margin-top:var(--sp-5);padding-top:var(--sp-4);border-top:1px solid var(--line-2)}.login .alt{margin-top:var(--sp-4);display:flex;flex-direction:column;gap:var(--sp-3)}.login .alt .btn{margin-top:0}.login .note{margin:0}.drawer{position:fixed;top:0;right:0;bottom:0;left:0;z-index:40;background:#04080b99;display:flex;justify-content:flex-end;animation:fade .16s ease}@keyframes fade{0%{opacity:0}to{opacity:1}}.drawer .sheet{width:min(540px,100%);height:100%;overflow:auto;background:var(--s1);border-left:1px solid var(--line);box-shadow:var(--shadow-lg);animation:slidein-r .2s cubic-bezier(.2,.8,.3,1)}@keyframes slidein-r{0%{transform:translate(16px);opacity:.6}to{transform:none;opacity:1}}.drawer .sheethead{position:sticky;top:0;z-index:1;display:flex;align-items:center;justify-content:space-between;gap:var(--sp-3);padding:var(--sp-4) var(--sp-5);background:var(--s1);border-bottom:1px solid var(--line)}.drawer .sheethead h2{font-family:var(--font-display);font-size:17px;font-weight:600;letter-spacing:-.015em}.drawer .sheetbody{padding:var(--sp-5)}.drawer .close{background:none;border:0;color:var(--ink-3);cursor:pointer;padding:6px;border-radius:var(--r-sm);display:grid;place-items:center}.drawer .close:hover{background:var(--s2);color:var(--ink)}.avatar{width:44px;height:44px;border-radius:var(--r-sm);flex:none;display:grid;place-items:center;overflow:hidden;background:var(--s3);border:1px solid var(--line);font-weight:600;color:var(--ink-2);font-variant-numeric:tabular-nums}.avatar img{width:100%;height:100%;object-fit:cover}@media (prefers-reduced-motion: reduce){*,*:before,*:after{animation:none!important;transition:none!important}}.live-split{display:grid;gap:var(--sp-4);grid-template-columns:minmax(0,1.35fr) minmax(0,1fr);align-items:stretch;min-height:420px}.live-split>.panel{max-height:62vh}@media (max-width: 1100px){.live-split{grid-template-columns:minmax(0,1fr)}.live-split>.panel{max-height:none}}.arrivals-panel{max-height:64vh;margin-bottom:var(--sp-4)}.arrivals-panel .arrivals{display:grid;grid-template-columns:repeat(auto-fill,minmax(340px,1fr));gap:var(--sp-2)}.pagehead{display:flex;justify-content:space-between;align-items:flex-end;gap:var(--sp-4)}.page>header.pagehead{margin-bottom:var(--sp-5)}.camgrid{display:grid;gap:var(--sp-4);grid-template-columns:repeat(auto-fill,minmax(440px,1fr))}.camcard{background:var(--s1);border:1px solid var(--line);border-radius:var(--r-lg);overflow:hidden;display:flex;flex-direction:column}.camview{position:relative;aspect-ratio:16 / 9;background:#05090c}.camview img{width:100%;height:100%;object-fit:cover;display:block}.camview .placeholder{width:100%;height:100%;display:grid;place-items:center;color:var(--ink-3)}.camview .pill.over{position:absolute;top:10px;right:10px;-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px)}.cambody{padding:var(--sp-4);display:flex;flex-direction:column;gap:var(--sp-3)}.camtitle{display:flex;justify-content:space-between;align-items:flex-start;gap:var(--sp-3)}.camtitle h3{font-family:var(--font-display);font-size:16px;font-weight:600;letter-spacing:-.012em;text-transform:none;color:var(--ink);margin:0 0 2px}.camtitle .note{font-size:12px}.camactions{display:flex;gap:6px;flex:none}.camproof{display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:var(--sp-3);padding:var(--sp-3);border-radius:var(--r);background:var(--s2);border:1px solid var(--line-2)}.camproof .note{font-size:12px;line-height:1.45}@media (max-width: 640px){.camproof{grid-template-columns:1fr}}.empty.tall{padding:var(--sp-8) var(--sp-6)}.empty.tall .btn{margin-top:var(--sp-3)}.drawer .sheet{width:min(600px,100%)}.sheetbody .lead{color:var(--ink-2);font-size:13.5px;line-height:1.55;margin-bottom:var(--sp-5)}.formsection{margin-bottom:var(--sp-5)}.formsection h4{font-size:11px;font-weight:600;letter-spacing:.09em;text-transform:uppercase;color:var(--ink-3);margin:0 0 var(--sp-3);padding-bottom:6px;border-bottom:1px solid var(--line-2)}.field .hint{font-style:normal}.fieldrow{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}.fieldrow .field.narrow{max-width:140px}.fieldrow:has(.field.narrow){grid-template-columns:minmax(0,1fr) 140px}.field input.mono{font-family:var(--font-mono);font-size:12.5px}.sheetactions{display:flex;gap:var(--sp-2);justify-content:flex-end;padding-top:var(--sp-4);border-top:1px solid var(--line-2);margin-top:var(--sp-2)}.testresult{display:flex;gap:var(--sp-3);align-items:flex-start;padding:var(--sp-3) var(--sp-4);border-radius:var(--r);margin-bottom:var(--sp-4);border:1px solid;font-size:13px}.testresult.ok{background:var(--ok-2);border-color:color-mix(in srgb,var(--ok) 30%,transparent);color:var(--ok)}.testresult.bad{background:var(--bad-2);border-color:color-mix(in srgb,var(--bad) 30%,transparent);color:var(--bad)}.testresult b{display:block}.testresult span{display:block;color:var(--ink-2);margin-top:2px}.testresult img{width:100%;margin-top:var(--sp-3);border-radius:var(--r-sm);border:1px solid var(--line);display:block}.steps{list-style:none;counter-reset:step;margin:0 0 var(--sp-5);padding:0;display:grid;gap:var(--sp-3)}.steps li{counter-increment:step;position:relative;padding:var(--sp-3) var(--sp-4) var(--sp-3) 52px;border-radius:var(--r);border:1px solid var(--line-2);background:var(--s2);opacity:.55}.steps li.now,.steps li.done{opacity:1}.steps li:before{content:counter(step);position:absolute;left:16px;top:14px;width:24px;height:24px;border-radius:50%;display:grid;place-items:center;font-size:12px;font-weight:600;background:var(--s3);color:var(--ink-2);border:1px solid var(--line)}.steps li.now:before{background:var(--accent);color:#041014;border-color:transparent}.steps li.done:before{content:"✓";background:var(--ok-2);color:var(--ok)}.steps b{display:block;font-size:14px}.steps span{display:block;font-size:12.5px;color:var(--ink-2);line-height:1.5;margin-top:2px}.progress{height:4px;background:var(--s3);border-radius:2px;overflow:hidden;margin-top:var(--sp-3)}.progress>div{height:100%;background:var(--accent);transition:width .4s linear}.verdict{padding:var(--sp-4);border-radius:var(--r);border:1px solid var(--line);background:var(--s2)}.verdict.ok{border-color:color-mix(in srgb,var(--ok) 35%,transparent);background:var(--ok-2)}.verdict.warn{border-color:color-mix(in srgb,var(--warn) 35%,transparent);background:var(--warn-2)}.verdict.bad{border-color:color-mix(in srgb,var(--bad) 35%,transparent);background:var(--bad-2)}.verdict-head{display:flex;align-items:center;gap:var(--sp-2);font-size:15px}.verdict.ok .verdict-head{color:var(--ok)}.verdict.warn .verdict-head{color:var(--warn)}.verdict.bad .verdict-head{color:var(--bad)}.verdict ul{margin:var(--sp-3) 0 0 18px;font-size:13px;color:var(--ink);line-height:1.5}.verdict ul li{margin-bottom:5px}.verdict .note{margin-top:var(--sp-3)}.spinner{width:16px;height:16px;border-radius:50%;border:2px solid var(--line);border-top-color:var(--accent);animation:spin .8s linear infinite;display:inline-block}@keyframes spin{to{transform:rotate(360deg)}}.helpbtn{display:flex;align-items:center;gap:9px;margin:0 var(--sp-3) var(--sp-3);padding:9px 10px;border-radius:var(--r);border:1px solid var(--line);background:var(--s2);color:var(--ink);cursor:pointer;font-size:12.5px;font-weight:550;text-align:left}.helpbtn img{width:18px;height:18px;object-fit:contain}.helpbtn span{flex:1}.helpbtn:hover,.helpbtn[aria-pressed=true]{border-color:color-mix(in srgb,var(--accent) 45%,transparent);background:var(--accent-3)}.helper{width:380px;height:100%;display:flex;flex-direction:column;min-height:0;background:var(--s1);border-left:1px solid var(--line);animation:slidein-r .2s cubic-bezier(.2,.8,.3,1)}.helperhead{display:flex;align-items:center;gap:var(--sp-3);padding:var(--sp-4);border-bottom:1px solid var(--line)}.helperhead img{width:26px;height:26px;object-fit:contain}.helperhead div{flex:1;min-width:0}.helperhead b{display:block;font-family:var(--font-display);font-size:14.5px}.helperhead span{display:block;font-size:11.5px;color:var(--ink-3);margin-top:1px}.helperhead .close{background:none;border:0;color:var(--ink-3);cursor:pointer;padding:6px;border-radius:var(--r-sm);display:grid;place-items:center}.helperhead .close:hover{background:var(--s2);color:var(--ink)}.helperbody{flex:1;overflow:auto;padding:var(--sp-4);display:flex;flex-direction:column;gap:var(--sp-3)}.helperintro p{color:var(--ink-2);font-size:13px;line-height:1.55;margin-bottom:var(--sp-3)}.chips{display:flex;flex-wrap:wrap;gap:6px}.chip{border:1px solid var(--line);background:var(--s2);color:var(--ink);border-radius:99px;padding:6px 11px;font-size:12px;cursor:pointer;text-align:left}.chip:hover{border-color:var(--accent);color:var(--accent)}.chip:disabled{opacity:.5;cursor:default}.helpernote{display:flex;gap:var(--sp-3);padding:var(--sp-3);border-radius:var(--r);background:var(--warn-2);border:1px solid color-mix(in srgb,var(--warn) 30%,transparent);color:var(--warn);font-size:12.5px}.helpernote b{display:block}.helpernote span{display:block;color:var(--ink-2);margin-top:2px;line-height:1.45}.turn{display:flex;flex-direction:column;gap:4px}.turn.user{align-items:flex-end}.bubble{max-width:92%;padding:10px 13px;border-radius:14px;font-size:13.5px;line-height:1.5;white-space:pre-wrap}.turn.user .bubble{background:var(--accent);color:#041014;border-bottom-right-radius:4px}.turn.assistant .bubble{background:var(--s2);border:1px solid var(--line);border-bottom-left-radius:4px}.turn .used{font-size:11px;color:var(--ink-3);padding:0 4px}.bubble.thinking{display:flex;gap:4px;padding:12px 14px}.bubble.thinking span{width:6px;height:6px;border-radius:50%;background:var(--ink-3);animation:blink 1.2s infinite ease-in-out}.bubble.thinking span:nth-child(2){animation-delay:.2s}.bubble.thinking span:nth-child(3){animation-delay:.4s}@keyframes blink{0%,80%,to{opacity:.25}40%{opacity:1}}.helperask{display:flex;gap:var(--sp-2);padding:var(--sp-3) var(--sp-4);border-top:1px solid var(--line)}.helperask input{flex:1;min-width:0;background:var(--s2);border:1px solid var(--line);border-radius:var(--r);padding:10px 12px;font-size:13.5px;color:var(--ink)}.helperask input:focus{outline:none;border-color:var(--accent)}.helperask .btn{padding:0 12px}.quickhelp{margin-top:var(--sp-5);border-top:1px solid var(--line-2);padding-top:var(--sp-4)}.quickhelp dt{font-size:12.5px;font-weight:600;margin-top:var(--sp-3)}.quickhelp dd{font-size:12.5px;color:var(--ink-2);line-height:1.5;margin:3px 0 0}@media (max-width: 1100px){.helper{position:fixed;right:0;top:0;bottom:0;z-index:30;box-shadow:var(--shadow-lg)}}.search{display:flex;align-items:center;gap:8px;background:var(--s1);border:1px solid var(--line);border-radius:var(--r);padding:0 12px;height:36px;width:min(340px,100%);color:var(--ink-3)}.search input{flex:1;min-width:0;background:none;border:0;outline:none;color:var(--ink);font-size:13.5px}.search:focus-within{border-color:var(--accent)}.sheethead.who{align-items:center;gap:var(--sp-3)}.sheethead.who .grow{flex:1;min-width:0}.sheethead.who .note{margin-top:2px;font-size:12px}.sheethead .close{font-size:14px;line-height:1}.formsection .field:last-child{margin-bottom:0}.formsection.dangerzone{margin-top:var(--sp-6);padding:var(--sp-4);border:1px solid color-mix(in srgb,var(--bad) 30%,transparent);border-radius:var(--r);background:var(--bad-2)}.formsection.dangerzone h4{color:var(--bad);border-color:color-mix(in srgb,var(--bad) 30%,transparent)}tr.click{cursor:pointer}tr.click:hover td{background:var(--s2)}.loya-fab{position:fixed;top:var(--sp-4);right:var(--sp-5);z-index:20;display:flex;align-items:center;gap:8px;padding:8px 14px 8px 10px;border-radius:99px;border:1px solid color-mix(in srgb,var(--accent) 40%,transparent);background:var(--s1);color:var(--ink);box-shadow:var(--shadow-lg);cursor:pointer;font-size:13px;font-weight:600}.loya-fab img{width:20px;height:20px;object-fit:contain}.loya-fab:hover{background:var(--accent-3);border-color:var(--accent)}.finder{display:flex;flex-direction:column;gap:var(--sp-3);align-items:flex-start;padding:var(--sp-3) var(--sp-4);border:1px dashed var(--line);border-radius:var(--r);background:var(--s2)}.finder p{font-size:13px;color:var(--ink-2);line-height:1.5;margin:0}.finder.busy{flex-direction:row;align-items:center;color:var(--ink-2);font-size:13px;border-style:solid}.foundlist{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px}.foundlist li button{width:100%;display:flex;align-items:center;gap:var(--sp-3);padding:10px 12px;border-radius:var(--r);border:1px solid var(--line);background:var(--s2);color:var(--ink);cursor:pointer;text-align:left;font-size:13px}.foundlist li button:hover{border-color:var(--accent)}.foundlist li.picked button{border-color:var(--accent);background:var(--accent-3)}.foundlist .mono{font-family:var(--font-mono);font-size:12.5px;min-width:120px}.foundlist .what{flex:1;color:var(--ink-2)}.foundlist li.rescan button{width:auto;border:0;background:none;padding:4px 0;color:var(--ink-3)}.login .box.wide{max-width:640px}.choices{display:grid;grid-template-columns:1fr 1fr;gap:var(--sp-3);margin-top:var(--sp-2)}@media (max-width: 720px){.choices{grid-template-columns:1fr}}.choice{display:flex;flex-direction:column;align-items:flex-start;gap:8px;text-align:left;padding:var(--sp-4);border-radius:var(--r-lg);border:1px solid var(--line);background:var(--s2);color:var(--ink);cursor:pointer}.choice:hover{border-color:var(--accent);background:var(--accent-3)}.choice b{font-size:14px}.choice span{font-size:12.5px;color:var(--ink-2);line-height:1.5}.choice em{font-family:var(--font-mono);font-style:normal;font-size:11.5px}.choice svg{color:var(--accent)}.login .foot em{font-style:normal;color:var(--ink-2)}.linkbtn{display:inline-flex;align-items:center;gap:6px}.starter{margin-bottom:var(--sp-4)}.starter .panelhead{padding:var(--sp-3) var(--sp-4)}.starter .steps{margin:0;padding:var(--sp-3) var(--sp-4) 0}.starter .steps.compact li{padding:var(--sp-3) var(--sp-4) var(--sp-3) 52px}.starter .steps li .btn{margin-top:var(--sp-2)}.starter .note{padding:var(--sp-3) var(--sp-4);font-size:12px}.btn.ghost{background:none;border-color:transparent;color:var(--ink-3)}.btn.ghost:hover{color:var(--ink)} +:root{--bg: #0A0F13;--s1: #111A20;--s2: #17232B;--s3: #1E2D37;--line: #223038;--line-2: #1A252C;--ink: #ECF3F7;--ink-2: #A3B6C2;--ink-3: #6C808D;--accent: #40C4DC;--accent-2: #0F3B47;--accent-3: #0B2A33;--ok: #48C78E;--ok-2: #102E22;--warn: #EAAA3D;--warn-2: #31260F;--bad: #EC6A5C;--bad-2: #331815;--r-sm: 6px;--r: 10px;--r-lg: 14px;--sp-1: 4px;--sp-2: 8px;--sp-3: 12px;--sp-4: 16px;--sp-5: 20px;--sp-6: 24px;--sp-7: 32px;--sp-8: 40px;--shadow: 0 1px 2px rgb(0 0 0 / .4), 0 8px 24px -12px rgb(0 0 0 / .6);--shadow-lg: 0 2px 4px rgb(0 0 0 / .4), 0 24px 48px -16px rgb(0 0 0 / .7);--font: "Segoe UI Variable Text", "Segoe UI", Inter, -apple-system, BlinkMacSystemFont, system-ui, "Helvetica Neue", Arial, sans-serif;--font-display: "Segoe UI Variable Display", var(--font);--mono: "Cascadia Mono", "SFMono-Regular", ui-monospace, Menlo, Consolas, monospace;--ground: var(--bg);--surface: var(--s1);--surface-2: var(--s2);--line-soft: var(--line-2);--muted: var(--ink-3);--radius: var(--r);--accent-dim: var(--accent-3)}*{box-sizing:border-box;margin:0}html,body,#root{height:100%}body{background:var(--bg);color:var(--ink);font-family:var(--font);font-size:14px;line-height:1.5;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;overflow:hidden;-webkit-user-select:none;user-select:none}button,input,select,textarea{font:inherit;color:inherit}input,textarea{-webkit-user-select:text;user-select:text}:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:3px}::selection{background:var(--accent-2);color:var(--ink)}.num,.value,.metric-v,.when,.mono,.code,td{font-variant-numeric:tabular-nums}.mono,.code{font-family:var(--mono)}*{scrollbar-width:thin;scrollbar-color:var(--s3) transparent}*::-webkit-scrollbar{width:10px;height:10px}*::-webkit-scrollbar-track{background:transparent}*::-webkit-scrollbar-thumb{background:var(--s3);border-radius:99px;border:3px solid var(--bg)}*::-webkit-scrollbar-thumb:hover{background:#2a3d49}.shell{display:grid;grid-template-columns:232px 1fr auto;height:100%}.side{background:var(--s1);border-right:1px solid var(--line);display:flex;flex-direction:column;min-height:0}.side .brand{display:flex;align-items:center;gap:var(--sp-3);padding:var(--sp-5) var(--sp-5) var(--sp-4)}.side .brand .mark{width:30px;height:30px;flex:none;display:grid;place-items:center}.side .brand .mark img,.login .mark img{width:100%;height:100%;object-fit:contain;display:block}.side .brand .id{min-width:0}.side .brand h1{font-family:var(--font-display);font-size:15px;font-weight:600;letter-spacing:-.012em;line-height:1.2}.side .brand p{font-size:11.5px;color:var(--ink-3);margin-top:1px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.nav{padding:var(--sp-2) var(--sp-3);display:flex;flex-direction:column;gap:2px;flex:1}.nav button{position:relative;display:flex;align-items:center;gap:var(--sp-3);width:100%;background:none;border:0;border-radius:var(--r-sm);padding:9px var(--sp-3);color:var(--ink-2);cursor:pointer;text-align:left;font-size:13.5px;font-weight:450;transition:background .12s ease,color .12s ease}.nav button svg{flex:none;opacity:.9}.nav button:hover{background:var(--s2);color:var(--ink)}.nav button[aria-current=page]{background:var(--accent-3);color:var(--accent);font-weight:550}.nav button[aria-current=page]:before{content:"";position:absolute;left:-12px;top:7px;bottom:7px;width:2.5px;border-radius:0 2px 2px 0;background:var(--accent)}.enginebox{margin:var(--sp-3);padding:var(--sp-3) var(--sp-4) var(--sp-4);border:1px solid var(--line);border-radius:var(--r);background:var(--s2)}.enginebox .row{display:flex;align-items:center;gap:var(--sp-2)}.enginebox .state{font-size:12.5px;font-weight:600;letter-spacing:-.005em}.enginebox .label{display:block;color:var(--ink-3);font-size:11px;line-height:1.45;margin-top:3px;font-variant-numeric:tabular-nums}.enginebox .actions,.enginebox .controls{display:flex;gap:var(--sp-2);margin-top:var(--sp-3)}.enginebox .actions .btn,.enginebox .controls .btn{flex:1;justify-content:center;padding:6px 8px;font-size:12px}.side .who{padding:var(--sp-3) var(--sp-5) var(--sp-5);border-top:1px solid var(--line-2);display:flex;align-items:center;gap:var(--sp-3)}.side .who .id{min-width:0;flex:1}.side .who .id b{display:block;font-size:12.5px;font-weight:550}.side .who .id span{display:block;font-size:11px;color:var(--ink-3);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.main{min-width:0;min-height:0;overflow:auto;position:relative}.page{padding:var(--sp-6) var(--sp-7) var(--sp-8);max-width:1500px}.page>header{margin-bottom:var(--sp-5)}.page>header h2,.page h2{font-family:var(--font-display);font-size:22px;font-weight:600;letter-spacing:-.02em;line-height:1.2}.page>header p,.page header p{color:var(--ink-3);font-size:13px;margin-top:3px}.pagehead{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--sp-4);margin-bottom:var(--sp-5);flex-wrap:wrap}h3{font-size:11px;font-weight:600;letter-spacing:.085em;text-transform:uppercase;color:var(--ink-3)}.card{background:var(--s1);border:1px solid var(--line);border-radius:var(--r);padding:var(--sp-4)}.card>h3{margin-bottom:var(--sp-3)}.card.flush{padding:0;overflow:hidden}.panel{background:var(--s1);border:1px solid var(--line);border-radius:var(--r-lg);overflow:hidden;display:flex;flex-direction:column;min-height:0}.panel>.panelhead{display:flex;align-items:center;justify-content:space-between;gap:var(--sp-3);padding:var(--sp-3) var(--sp-4);border-bottom:1px solid var(--line-2);background:linear-gradient(var(--s2),var(--s1));flex:none}.panel>.panelhead h3{margin:0}.panel>.panelbody{padding:var(--sp-4);min-height:0;overflow:auto}.panel>.panelbody.flush{padding:0}.grid{display:grid;gap:var(--sp-4)}.cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}@media (max-width: 1180px){.cols-4{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (max-width: 980px){.cols-2,.cols-3{grid-template-columns:minmax(0,1fr)}}.metrics{display:grid;grid-template-columns:repeat(auto-fit,minmax(152px,1fr));gap:1px;background:var(--line);border:1px solid var(--line);border-radius:var(--r);overflow:hidden}.metric{background:var(--s1);padding:var(--sp-3) var(--sp-4) var(--sp-4)}.metric .metric-k{font-size:10.5px;font-weight:600;letter-spacing:.085em;text-transform:uppercase;color:var(--ink-3)}.metric .metric-v{font-family:var(--font-display);font-size:26px;font-weight:600;letter-spacing:-.025em;line-height:1.1;margin-top:5px}.metric .metric-s{font-size:11.5px;color:var(--ink-3);margin-top:3px;line-height:1.4}.metric.ok .metric-v{color:var(--ok)}.metric.warn .metric-v{color:var(--warn)}.metric.bad .metric-v{color:var(--bad)}.stat h3{margin-bottom:var(--sp-2)}.stat .value{font-family:var(--font-display);font-size:26px;font-weight:600;letter-spacing:-.025em;line-height:1.1}.stat .unit{font-size:15px;color:var(--ink-3);margin-left:3px}.stat .sub{font-size:11.5px;color:var(--ink-3);margin-top:4px;line-height:1.4}.dot{width:7px;height:7px;border-radius:99px;flex:none;background:var(--ink-3)}.dot.ok{background:var(--ok);box-shadow:0 0 0 3px color-mix(in srgb,var(--ok) 18%,transparent)}.dot.warn{background:var(--warn);box-shadow:0 0 0 3px color-mix(in srgb,var(--warn) 18%,transparent)}.dot.bad{background:var(--bad);box-shadow:0 0 0 3px color-mix(in srgb,var(--bad) 18%,transparent)}.dot.idle{background:var(--ink-3)}.dot.live{background:var(--ok);animation:pulse 2.4s ease-in-out infinite}@keyframes pulse{0%,to{box-shadow:0 0 color-mix(in srgb,var(--ok) 55%,transparent)}70%{box-shadow:0 0 0 6px color-mix(in srgb,var(--ok) 0%,transparent)}}.pill{display:inline-flex;align-items:center;gap:6px;padding:3px 9px 3px 7px;border-radius:99px;font-size:11px;font-weight:600;letter-spacing:.02em;background:var(--s3);color:var(--ink-2);border:1px solid var(--line);white-space:nowrap}.pill.ok{background:var(--ok-2);color:var(--ok);border-color:color-mix(in srgb,var(--ok) 28%,transparent)}.pill.warn{background:var(--warn-2);color:var(--warn);border-color:color-mix(in srgb,var(--warn) 28%,transparent)}.pill.bad{background:var(--bad-2);color:var(--bad);border-color:color-mix(in srgb,var(--bad) 28%,transparent)}.pill.accent{background:var(--accent-3);color:var(--accent);border-color:color-mix(in srgb,var(--accent) 30%,transparent)}.tag{display:inline-flex;align-items:center;padding:2px 7px;border-radius:var(--r-sm);font-size:10.5px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;background:var(--s3);color:var(--ink-2)}.tag.new{background:var(--accent-3);color:var(--accent)}.tag.seen{background:var(--ok-2);color:var(--ok)}.tag.miss{background:var(--warn-2);color:var(--warn)}.statusbar{display:flex;align-items:center;gap:var(--sp-5);flex-wrap:wrap;padding:var(--sp-3) var(--sp-4);background:var(--s1);border:1px solid var(--line);border-radius:var(--r);margin-bottom:var(--sp-4)}.statusbar .item{display:flex;align-items:center;gap:var(--sp-2);font-size:12.5px}.statusbar .item b{font-weight:600;letter-spacing:-.005em}.statusbar .item svg{color:var(--ink-3)}.statusbar .sep{width:1px;align-self:stretch;background:var(--line)}.statusbar .grow{flex:1}.arrivals{display:flex;flex-direction:column;gap:var(--sp-2);padding:var(--sp-3)}.arrival{display:grid;grid-template-columns:46px 1fr auto;gap:var(--sp-3);align-items:center;padding:var(--sp-3);border-radius:var(--r);background:var(--s2);border:1px solid var(--line-2);position:relative;overflow:hidden}.arrival:before{content:"";position:absolute;left:0;top:0;bottom:0;width:2.5px;background:var(--ink-3)}.arrival.is-new:before{background:var(--accent)}.arrival.is-seen:before{background:var(--ok)}.arrival.is-miss:before{background:var(--warn)}.arrival.fresh{animation:slidein .28s cubic-bezier(.2,.8,.3,1)}@keyframes slidein{0%{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:none}}.arrival .avatar{width:46px;height:46px;border-radius:var(--r-sm);display:grid;place-items:center;overflow:hidden;background:var(--s3);border:1px solid var(--line);font-family:var(--font-display);font-size:15px;font-weight:600;color:var(--ink-2);letter-spacing:-.01em;font-variant-numeric:tabular-nums}.arrival .avatar img{width:100%;height:100%;object-fit:cover}.arrival.is-new .avatar{background:var(--accent-3);color:var(--accent);border-color:color-mix(in srgb,var(--accent) 25%,transparent)}.arrival .who{min-width:0}.arrival .who .name{font-size:14.5px;font-weight:600;letter-spacing:-.01em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.arrival .who .meta{font-size:11.5px;color:var(--ink-3);margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.arrival .right{text-align:right;display:flex;flex-direction:column;align-items:flex-end;gap:5px}.arrival .right .when{font-size:11.5px;color:var(--ink-3)}.feeds{display:grid;gap:var(--sp-3);grid-template-columns:repeat(auto-fit,minmax(300px,1fr));padding:var(--sp-3)}.feed{position:relative;border-radius:var(--r);overflow:hidden;background:#05090c;border:1px solid var(--line);aspect-ratio:16 / 9}.feed img{width:100%;height:100%;object-fit:cover;display:block}.feed .placeholder{width:100%;height:100%;display:grid;place-items:center;color:var(--ink-3)}.feed .cap{position:absolute;left:0;right:0;bottom:0;display:flex;align-items:center;justify-content:space-between;gap:var(--sp-2);padding:var(--sp-5) var(--sp-3) var(--sp-3);background:linear-gradient(transparent,#000c);font-size:12.5px;font-weight:600;letter-spacing:-.005em}.events,.timeline{list-style:none;padding:0;display:flex;flex-direction:column}.events li{display:flex;align-items:center;gap:var(--sp-3);padding:9px var(--sp-4);border-bottom:1px solid var(--line-2);font-size:13px}.timeline li{display:flex;align-items:center;gap:var(--sp-3);padding:9px 0;border-bottom:1px solid var(--line-2);font-size:13px}.events li:last-child,.timeline li:last-child{border-bottom:0}.events .when,.timeline .when{font-size:11.5px;color:var(--ink-3);width:46px;flex:none}.empty{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--sp-3);padding:var(--sp-8) var(--sp-5);color:var(--ink-3);text-align:center;font-size:12.5px}.empty svg{opacity:.35}.empty b{display:block;color:var(--ink-2);font-size:13.5px;font-weight:550}.empty p{max-width:34ch;line-height:1.5}.tablewrap{overflow:auto}table{border-collapse:collapse;width:100%;font-size:13px}th{text-align:left;padding:9px var(--sp-4);font-size:10.5px;font-weight:600;letter-spacing:.085em;text-transform:uppercase;color:var(--ink-3);background:var(--s2);border-bottom:1px solid var(--line);position:sticky;top:0;z-index:1}td{padding:10px var(--sp-4);border-bottom:1px solid var(--line-2)}tbody tr:last-child td{border-bottom:0}tbody tr[role=button],tbody tr.clickable{cursor:pointer}tbody tr[role=button]:hover,tbody tr.clickable:hover{background:var(--s2)}.btn{display:inline-flex;align-items:center;gap:7px;padding:8px 14px;border-radius:var(--r-sm);background:var(--s3);color:var(--ink);border:1px solid var(--line);cursor:pointer;font-size:13px;font-weight:550;letter-spacing:-.005em;white-space:nowrap;transition:background .12s ease,border-color .12s ease,transform .06s ease}.btn:hover:not(:disabled){background:#253643;border-color:#2e414e}.btn:active:not(:disabled){transform:translateY(.5px)}.btn:disabled{opacity:.45;cursor:not-allowed}.btn svg{flex:none}.btn.primary{background:var(--accent);color:#04171c;border-color:transparent;font-weight:600}.btn.primary:hover:not(:disabled){background:#55d0e6}.btn.danger{background:var(--bad-2);color:var(--bad);border-color:color-mix(in srgb,var(--bad) 32%,transparent)}.btn.danger:hover:not(:disabled){background:#43201c}.btn.ghost{background:transparent}.btn.ghost:hover:not(:disabled){background:var(--s2)}.btn.sm{padding:5px 10px;font-size:12px}.btn.icon{padding:7px}.linkbtn{background:none;border:0;color:var(--accent);cursor:pointer;font-size:12.5px;padding:2px 0;text-align:left}.linkbtn:hover{text-decoration:underline}.seg{display:inline-flex;background:var(--s2);border:1px solid var(--line);border-radius:var(--r-sm);padding:2px;gap:2px}.seg button{background:none;border:0;border-radius:4px;padding:5px 11px;color:var(--ink-3);cursor:pointer;font-size:12.5px;font-weight:500}.seg button[aria-pressed=true]{background:var(--s3);color:var(--ink)}.field{display:block;margin-bottom:var(--sp-4)}.field>span{display:block;font-size:11.5px;font-weight:550;color:var(--ink-2);margin-bottom:6px}.field input,.field select,.field textarea{width:100%;padding:9px 11px;background:var(--s2);color:var(--ink);border:1px solid var(--line);border-radius:var(--r-sm);transition:border-color .12s ease,background .12s ease,box-shadow .12s ease}.field input::placeholder{color:var(--ink-3)}.field input:hover,.field select:hover,.field textarea:hover{border-color:#2c3d49}.field input:focus,.field select:focus,.field textarea:focus{outline:none;border-color:var(--accent);background:var(--s1);box-shadow:0 0 0 3px var(--accent-3)}.field .hint{display:block;font-size:11.5px;color:var(--ink-3);margin-top:5px;line-height:1.45}.fieldrow{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--sp-3)}.fieldrow.three{grid-template-columns:repeat(3,minmax(0,1fr))}.err{display:flex;align-items:flex-start;gap:var(--sp-2);background:var(--bad-2);color:var(--bad);border:1px solid color-mix(in srgb,var(--bad) 30%,transparent);border-radius:var(--r-sm);padding:9px 11px;font-size:12.5px;line-height:1.45;margin-bottom:var(--sp-4)}.err svg{flex:none;margin-top:1px}.note{color:var(--ink-3);font-size:12px;line-height:1.5}.note.warn{color:var(--warn)}.note.bad{color:var(--bad)}.lead{color:var(--ink-2);font-size:13.5px;line-height:1.55}.sm{font-size:12px}.lbl,.key{color:var(--ink-3);font-size:11.5px}.grow{flex:1}.row{display:flex;align-items:center;gap:var(--sp-3)}.login{height:100%;display:grid;place-items:center;padding:var(--sp-6);overflow:auto;background:radial-gradient(900px 480px at 50% -10%,#10303A 0%,transparent 62%),var(--bg)}.login .box{width:100%;max-width:396px;background:var(--s1);border:1px solid var(--line);border-radius:var(--r-lg);padding:var(--sp-7);box-shadow:var(--shadow-lg)}.login .mark{width:44px;height:44px;margin-bottom:var(--sp-4);display:grid;place-items:center}.login h1{font-family:var(--font-display);font-size:21px;font-weight:600;letter-spacing:-.022em}.login .lead{margin:6px 0 var(--sp-5)}.login .btn{width:100%;justify-content:center;margin-top:var(--sp-1)}.login .foot{font-size:11.5px;color:var(--ink-3);line-height:1.55;margin-top:var(--sp-5);padding-top:var(--sp-4);border-top:1px solid var(--line-2)}.login .alt{margin-top:var(--sp-4);display:flex;flex-direction:column;gap:var(--sp-3)}.login .alt .btn{margin-top:0}.login .note{margin:0}.drawer{position:fixed;top:0;right:0;bottom:0;left:0;z-index:40;background:#04080b99;display:flex;justify-content:flex-end;animation:fade .16s ease}@keyframes fade{0%{opacity:0}to{opacity:1}}.drawer .sheet{width:min(540px,100%);height:100%;overflow:auto;background:var(--s1);border-left:1px solid var(--line);box-shadow:var(--shadow-lg);animation:slidein-r .2s cubic-bezier(.2,.8,.3,1)}@keyframes slidein-r{0%{transform:translate(16px);opacity:.6}to{transform:none;opacity:1}}.drawer .sheethead{position:sticky;top:0;z-index:1;display:flex;align-items:center;justify-content:space-between;gap:var(--sp-3);padding:var(--sp-4) var(--sp-5);background:var(--s1);border-bottom:1px solid var(--line)}.drawer .sheethead h2{font-family:var(--font-display);font-size:17px;font-weight:600;letter-spacing:-.015em}.drawer .sheetbody{padding:var(--sp-5)}.drawer .close{background:none;border:0;color:var(--ink-3);cursor:pointer;padding:6px;border-radius:var(--r-sm);display:grid;place-items:center}.drawer .close:hover{background:var(--s2);color:var(--ink)}.avatar{width:44px;height:44px;border-radius:var(--r-sm);flex:none;display:grid;place-items:center;overflow:hidden;background:var(--s3);border:1px solid var(--line);font-weight:600;color:var(--ink-2);font-variant-numeric:tabular-nums}.avatar img{width:100%;height:100%;object-fit:cover}@media (prefers-reduced-motion: reduce){*,*:before,*:after{animation:none!important;transition:none!important}}.live-split{display:grid;gap:var(--sp-4);grid-template-columns:minmax(0,1.35fr) minmax(0,1fr);align-items:stretch;min-height:420px}.live-split>.panel{max-height:62vh}@media (max-width: 1100px){.live-split{grid-template-columns:minmax(0,1fr)}.live-split>.panel{max-height:none}}.arrivals-panel{max-height:64vh;margin-bottom:var(--sp-4)}.arrivals-panel .arrivals{display:grid;grid-template-columns:repeat(auto-fill,minmax(340px,1fr));gap:var(--sp-2)}.pagehead{display:flex;justify-content:space-between;align-items:flex-end;gap:var(--sp-4)}.page>header.pagehead{margin-bottom:var(--sp-5)}.camgrid{display:grid;gap:var(--sp-4);grid-template-columns:repeat(auto-fill,minmax(440px,1fr))}.camcard{background:var(--s1);border:1px solid var(--line);border-radius:var(--r-lg);overflow:hidden;display:flex;flex-direction:column}.camview{position:relative;aspect-ratio:16 / 9;background:#05090c}.camview img{width:100%;height:100%;object-fit:cover;display:block}.camview .placeholder{width:100%;height:100%;display:grid;place-items:center;color:var(--ink-3)}.camview .pill.over{position:absolute;top:10px;right:10px;-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px)}.cambody{padding:var(--sp-4);display:flex;flex-direction:column;gap:var(--sp-3)}.camtitle{display:flex;justify-content:space-between;align-items:flex-start;gap:var(--sp-3)}.camtitle h3{font-family:var(--font-display);font-size:16px;font-weight:600;letter-spacing:-.012em;text-transform:none;color:var(--ink);margin:0 0 2px}.camtitle .note{font-size:12px}.camactions{display:flex;gap:6px;flex:none}.camproof{display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:var(--sp-3);padding:var(--sp-3);border-radius:var(--r);background:var(--s2);border:1px solid var(--line-2)}.camproof .note{font-size:12px;line-height:1.45}@media (max-width: 640px){.camproof{grid-template-columns:1fr}}.empty.tall{padding:var(--sp-8) var(--sp-6)}.empty.tall .btn{margin-top:var(--sp-3)}.drawer .sheet{width:min(600px,100%)}.sheetbody .lead{color:var(--ink-2);font-size:13.5px;line-height:1.55;margin-bottom:var(--sp-5)}.formsection{margin-bottom:var(--sp-5)}.formsection h4{font-size:11px;font-weight:600;letter-spacing:.09em;text-transform:uppercase;color:var(--ink-3);margin:0 0 var(--sp-3);padding-bottom:6px;border-bottom:1px solid var(--line-2)}.field .hint{font-style:normal}.fieldrow{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}.fieldrow .field.narrow{max-width:140px}.fieldrow:has(.field.narrow){grid-template-columns:minmax(0,1fr) 140px}.field input.mono{font-family:var(--font-mono);font-size:12.5px}.sheetactions{display:flex;gap:var(--sp-2);justify-content:flex-end;padding-top:var(--sp-4);border-top:1px solid var(--line-2);margin-top:var(--sp-2)}.testresult{display:flex;gap:var(--sp-3);align-items:flex-start;padding:var(--sp-3) var(--sp-4);border-radius:var(--r);margin-bottom:var(--sp-4);border:1px solid;font-size:13px}.testresult.ok{background:var(--ok-2);border-color:color-mix(in srgb,var(--ok) 30%,transparent);color:var(--ok)}.testresult.bad{background:var(--bad-2);border-color:color-mix(in srgb,var(--bad) 30%,transparent);color:var(--bad)}.testresult b{display:block}.testresult span{display:block;color:var(--ink-2);margin-top:2px}.testresult img{width:100%;margin-top:var(--sp-3);border-radius:var(--r-sm);border:1px solid var(--line);display:block}.steps{list-style:none;counter-reset:step;margin:0 0 var(--sp-5);padding:0;display:grid;gap:var(--sp-3)}.steps li{counter-increment:step;position:relative;padding:var(--sp-3) var(--sp-4) var(--sp-3) 52px;border-radius:var(--r);border:1px solid var(--line-2);background:var(--s2);opacity:.55}.steps li.now,.steps li.done{opacity:1}.steps li:before{content:counter(step);position:absolute;left:16px;top:14px;width:24px;height:24px;border-radius:50%;display:grid;place-items:center;font-size:12px;font-weight:600;background:var(--s3);color:var(--ink-2);border:1px solid var(--line)}.steps li.now:before{background:var(--accent);color:#041014;border-color:transparent}.steps li.done:before{content:"✓";background:var(--ok-2);color:var(--ok)}.steps b{display:block;font-size:14px}.steps span{display:block;font-size:12.5px;color:var(--ink-2);line-height:1.5;margin-top:2px}.progress{height:4px;background:var(--s3);border-radius:2px;overflow:hidden;margin-top:var(--sp-3)}.progress>div{height:100%;background:var(--accent);transition:width .4s linear}.verdict{padding:var(--sp-4);border-radius:var(--r);border:1px solid var(--line);background:var(--s2)}.verdict.ok{border-color:color-mix(in srgb,var(--ok) 35%,transparent);background:var(--ok-2)}.verdict.warn{border-color:color-mix(in srgb,var(--warn) 35%,transparent);background:var(--warn-2)}.verdict.bad{border-color:color-mix(in srgb,var(--bad) 35%,transparent);background:var(--bad-2)}.verdict-head{display:flex;align-items:center;gap:var(--sp-2);font-size:15px}.verdict.ok .verdict-head{color:var(--ok)}.verdict.warn .verdict-head{color:var(--warn)}.verdict.bad .verdict-head{color:var(--bad)}.verdict ul{margin:var(--sp-3) 0 0 18px;font-size:13px;color:var(--ink);line-height:1.5}.verdict ul li{margin-bottom:5px}.verdict .note{margin-top:var(--sp-3)}.spinner{width:16px;height:16px;border-radius:50%;border:2px solid var(--line);border-top-color:var(--accent);animation:spin .8s linear infinite;display:inline-block}@keyframes spin{to{transform:rotate(360deg)}}.helpbtn{display:flex;align-items:center;gap:9px;margin:0 var(--sp-3) var(--sp-3);padding:9px 10px;border-radius:var(--r);border:1px solid var(--line);background:var(--s2);color:var(--ink);cursor:pointer;font-size:12.5px;font-weight:550;text-align:left}.helpbtn img{width:18px;height:18px;object-fit:contain}.helpbtn span{flex:1}.helpbtn:hover,.helpbtn[aria-pressed=true]{border-color:color-mix(in srgb,var(--accent) 45%,transparent);background:var(--accent-3)}.helper{width:380px;height:100%;display:flex;flex-direction:column;min-height:0;background:var(--s1);border-left:1px solid var(--line);animation:slidein-r .2s cubic-bezier(.2,.8,.3,1)}.helperhead{display:flex;align-items:center;gap:var(--sp-3);padding:var(--sp-4);border-bottom:1px solid var(--line)}.helperhead img{width:26px;height:26px;object-fit:contain}.helperhead div{flex:1;min-width:0}.helperhead b{display:block;font-family:var(--font-display);font-size:14.5px}.helperhead span{display:block;font-size:11.5px;color:var(--ink-3);margin-top:1px}.helperhead .close{background:none;border:0;color:var(--ink-3);cursor:pointer;padding:6px;border-radius:var(--r-sm);display:grid;place-items:center}.helperhead .close:hover{background:var(--s2);color:var(--ink)}.helperbody{flex:1;overflow:auto;padding:var(--sp-4);display:flex;flex-direction:column;gap:var(--sp-3)}.helperintro p{color:var(--ink-2);font-size:13px;line-height:1.55;margin-bottom:var(--sp-3)}.chips{display:flex;flex-wrap:wrap;gap:6px}.chip{border:1px solid var(--line);background:var(--s2);color:var(--ink);border-radius:99px;padding:6px 11px;font-size:12px;cursor:pointer;text-align:left}.chip:hover{border-color:var(--accent);color:var(--accent)}.chip:disabled{opacity:.5;cursor:default}.helpernote{display:flex;gap:var(--sp-3);padding:var(--sp-3);border-radius:var(--r);background:var(--warn-2);border:1px solid color-mix(in srgb,var(--warn) 30%,transparent);color:var(--warn);font-size:12.5px}.helpernote b{display:block}.helpernote span{display:block;color:var(--ink-2);margin-top:2px;line-height:1.45}.turn{display:flex;flex-direction:column;gap:4px}.turn.user{align-items:flex-end}.bubble{max-width:92%;padding:10px 13px;border-radius:14px;font-size:13.5px;line-height:1.5;white-space:pre-wrap}.turn.user .bubble{background:var(--accent);color:#041014;border-bottom-right-radius:4px}.turn.assistant .bubble{background:var(--s2);border:1px solid var(--line);border-bottom-left-radius:4px}.turn .used{font-size:11px;color:var(--ink-3);padding:0 4px}.bubble.thinking{display:flex;gap:4px;padding:12px 14px}.bubble.thinking span{width:6px;height:6px;border-radius:50%;background:var(--ink-3);animation:blink 1.2s infinite ease-in-out}.bubble.thinking span:nth-child(2){animation-delay:.2s}.bubble.thinking span:nth-child(3){animation-delay:.4s}@keyframes blink{0%,80%,to{opacity:.25}40%{opacity:1}}.helperask{display:flex;gap:var(--sp-2);padding:var(--sp-3) var(--sp-4);border-top:1px solid var(--line)}.helperask input{flex:1;min-width:0;background:var(--s2);border:1px solid var(--line);border-radius:var(--r);padding:10px 12px;font-size:13.5px;color:var(--ink)}.helperask input:focus{outline:none;border-color:var(--accent)}.helperask .btn{padding:0 12px}.quickhelp{margin-top:var(--sp-5);border-top:1px solid var(--line-2);padding-top:var(--sp-4)}.quickhelp dt{font-size:12.5px;font-weight:600;margin-top:var(--sp-3)}.quickhelp dd{font-size:12.5px;color:var(--ink-2);line-height:1.5;margin:3px 0 0}@media (max-width: 1100px){.helper{position:fixed;right:0;top:0;bottom:0;z-index:30;box-shadow:var(--shadow-lg)}}.search{display:flex;align-items:center;gap:8px;background:var(--s1);border:1px solid var(--line);border-radius:var(--r);padding:0 12px;height:36px;width:min(340px,100%);color:var(--ink-3)}.search input{flex:1;min-width:0;background:none;border:0;outline:none;color:var(--ink);font-size:13.5px}.search:focus-within{border-color:var(--accent)}.sheethead.who{align-items:center;gap:var(--sp-3)}.sheethead.who .grow{flex:1;min-width:0}.sheethead.who .note{margin-top:2px;font-size:12px}.sheethead .close{font-size:14px;line-height:1}.formsection .field:last-child{margin-bottom:0}.formsection.dangerzone{margin-top:var(--sp-6);padding:var(--sp-4);border:1px solid color-mix(in srgb,var(--bad) 30%,transparent);border-radius:var(--r);background:var(--bad-2)}.formsection.dangerzone h4{color:var(--bad);border-color:color-mix(in srgb,var(--bad) 30%,transparent)}tr.click{cursor:pointer}tr.click:hover td{background:var(--s2)}.loya-fab{position:fixed;top:var(--sp-4);right:var(--sp-5);z-index:20;display:flex;align-items:center;gap:8px;padding:8px 14px 8px 10px;border-radius:99px;border:1px solid color-mix(in srgb,var(--accent) 40%,transparent);background:var(--s1);color:var(--ink);box-shadow:var(--shadow-lg);cursor:pointer;font-size:13px;font-weight:600}.loya-fab img{width:20px;height:20px;object-fit:contain}.loya-fab:hover{background:var(--accent-3);border-color:var(--accent)}.finder{display:flex;flex-direction:column;gap:var(--sp-3);align-items:flex-start;padding:var(--sp-3) var(--sp-4);border:1px dashed var(--line);border-radius:var(--r);background:var(--s2)}.finder p{font-size:13px;color:var(--ink-2);line-height:1.5;margin:0}.finder.busy{flex-direction:row;align-items:center;color:var(--ink-2);font-size:13px;border-style:solid}.foundlist{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px}.foundlist li button{width:100%;display:flex;align-items:center;gap:var(--sp-3);padding:10px 12px;border-radius:var(--r);border:1px solid var(--line);background:var(--s2);color:var(--ink);cursor:pointer;text-align:left;font-size:13px}.foundlist li button:hover{border-color:var(--accent)}.foundlist li.picked button{border-color:var(--accent);background:var(--accent-3)}.foundlist .mono{font-family:var(--font-mono);font-size:12.5px;min-width:120px}.foundlist .what{flex:1;color:var(--ink-2)}.foundlist li.rescan button{width:auto;border:0;background:none;padding:4px 0;color:var(--ink-3)}.login .box.wide{max-width:640px}.choices{display:grid;grid-template-columns:1fr 1fr;gap:var(--sp-3);margin-top:var(--sp-2)}@media (max-width: 720px){.choices{grid-template-columns:1fr}}.choice{display:flex;flex-direction:column;align-items:flex-start;gap:8px;text-align:left;padding:var(--sp-4);border-radius:var(--r-lg);border:1px solid var(--line);background:var(--s2);color:var(--ink);cursor:pointer}.choice:hover{border-color:var(--accent);background:var(--accent-3)}.choice b{font-size:14px}.choice span{font-size:12.5px;color:var(--ink-2);line-height:1.5}.choice em{font-family:var(--font-mono);font-style:normal;font-size:11.5px}.choice svg{color:var(--accent)}.login .foot em{font-style:normal;color:var(--ink-2)}.linkbtn{display:inline-flex;align-items:center;gap:6px}.starter{margin-bottom:var(--sp-4)}.starter .panelhead{padding:var(--sp-3) var(--sp-4)}.starter .steps{margin:0;padding:var(--sp-3) var(--sp-4) 0}.starter .steps.compact li{padding:var(--sp-3) var(--sp-4) var(--sp-3) 52px}.starter .steps li .btn{margin-top:var(--sp-2)}.starter .note{padding:var(--sp-3) var(--sp-4);font-size:12px}.btn.ghost{background:none;border-color:transparent;color:var(--ink-3)}.btn.ghost:hover{color:var(--ink)}.viewing{display:flex;gap:10px;align-items:flex-start;padding:12px 14px;margin-bottom:14px;border:1px solid var(--line);border-radius:10px;background:color-mix(in srgb,var(--accent) 7%,transparent);color:var(--ink-2);font-size:13px;line-height:1.5}.viewing b{color:var(--ink);font-weight:600}.viewing svg{flex:none;margin-top:2px;color:var(--accent)} diff --git a/desktop/frontend/dist/index.html b/desktop/frontend/dist/index.html index 17cf62d..da0ce8a 100644 --- a/desktop/frontend/dist/index.html +++ b/desktop/frontend/dist/index.html @@ -4,8 +4,8 @@ Behavision - - + +
diff --git a/desktop/frontend/src/styles.css b/desktop/frontend/src/styles.css index 79b261b..37a9e08 100644 --- a/desktop/frontend/src/styles.css +++ b/desktop/frontend/src/styles.css @@ -634,3 +634,17 @@ tr.click { cursor: pointer; } tr.click:hover td { background: var(--s2); } .starter .note { padding: var(--sp-3) var(--sp-4); font-size: 12px; } .btn.ghost { background: none; border-color: transparent; color: var(--ink-3); } .btn.ghost:hover { color: var(--ink); } + +/* Viewer mode: this PC has no engine, so the screens show the company's own + data from head office. Informational, not an error - it is the ordinary + state of a laptop away from a shop, and styling it red would train people + to ignore the red that means something. */ +.viewing { + display: flex; gap: 10px; align-items: flex-start; + padding: 12px 14px; margin-bottom: 14px; + border: 1px solid var(--line); border-radius: 10px; + background: color-mix(in srgb, var(--accent) 7%, transparent); + color: var(--ink-2); font-size: 13px; line-height: 1.5; +} +.viewing b { color: var(--ink); font-weight: 600; } +.viewing svg { flex: none; margin-top: 2px; color: var(--accent); } diff --git a/desktop/frontend/src/views/Cameras.jsx b/desktop/frontend/src/views/Cameras.jsx index 6467bf4..71beb7e 100644 --- a/desktop/frontend/src/views/Cameras.jsx +++ b/desktop/frontend/src/views/Cameras.jsx @@ -19,7 +19,14 @@ export default function Cameras() { const [editing, setEditing] = useState(null) const [check, setCheck] = useState(null) const cams = data ?? [] - const streams = useStreamURLs(cams) + // Every camera is remote or none is: this list comes from the engine on + // loopback, and when that is unreachable the whole list comes from head + // office instead. A remote camera is on a network this computer cannot + // reach, so the picture is the shop PC's last snapshot and the buttons that + // would talk to the camera are not offered - one that cannot work is worse + // than one that is absent. + const remote = cams.some(c => c.remote) + const streams = useStreamURLs(remote ? [] : cams) async function remove(cam) { if (!confirm(`Remove ${cam.id}? Recognition from it stops immediately.`)) return @@ -31,13 +38,21 @@ export default function Cameras() {

Cameras

-

Add a camera, then prove it can see faces with a walk-past. Only then is it working.

+

{remote + ? 'The cameras across your shops, as the shop computers last reported them.' + : 'Add a camera, then prove it can see faces with a walk-past. Only then is it working.'}

- + }
+ {remote &&
+ Viewing your shops from here. These cameras are wired to the shop + computers, so they are set up and checked there. The picture is each + camera's most recent frame, not live video. +
} + {error &&
{error}
} {cams.length === 0 @@ -64,40 +79,57 @@ export default function Cameras() { } function CameraCard({ cam, stream, onEdit, onCheck, onRemove }) { - const conn = cam.connected === undefined ? { tone: 'idle', label: 'Engine stopped' } + // Three states, not two, and the third is why `connected` is a pointer on + // the wire: null means no shop computer has reported on this camera yet, + // which reads as waiting rather than as a fault to go and investigate. + const conn = cam.connected === undefined || cam.connected === null + ? (cam.remote ? { tone: 'idle', label: 'Waiting for the shop computer' } + : { tone: 'idle', label: 'Engine stopped' }) : cam.connected ? { tone: 'ok', label: 'Connected' } : { tone: 'bad', label: 'Not connecting' } // The last placement verdict, so "proven" survives closing the sheet. Only // `good` is a pass: marginal means half the visitors are silently discarded. - const { data: last } = usePolled(() => api.placementResult(cam.id), 15000, [cam.id]) + // Never asked for a remote camera: that answer lives on the shop computer, + // and polling loopback for it here only produces an error every 15 seconds. + const { data: last } = usePolled( + () => cam.remote ? Promise.resolve(null) : api.placementResult(cam.id), 15000, [cam.id]) const proof = !last || last.running || !last.verdict || last.verdict === 'starting' ? { tone: 'miss', label: 'Not yet proven', text: 'Walk past it once and Behavision will tell you if the placement works.' } : last.verdict === 'good' ? { tone: 'seen', label: 'Proven', text: last.headline || 'Faces recognised on a walk-past.' } : { tone: 'miss', label: 'Not proven', text: last.headline || 'Move the camera and check again.' } + const shot = cam.snapshot?.available ? cam.snapshot.url : '' return (
- {stream - ? {cam.id} + {stream || shot + ? {cam.id} :
} {conn.label}
-

{cam.id}

- {cam.host || cam.url}{cam.path ? ` · ${cam.path}` : ''} +

{cam.label || cam.camera_id || cam.id}

+ {cam.remote + ? cam.site || cam.site_slug || '' + : `${cam.host || cam.url}${cam.path ? ` · ${cam.path}` : ''}`}
-
+ {!cam.remote &&
-
-
-
- {proof.label} - {proof.text} - +
}
+ {cam.remote + ?
+ {cam.snapshot?.available + ? 'Last picture from the shop computer.' + : cam.snapshot?.reason || 'No picture yet from the shop computer.'} +
+ :
+ {proof.label} + {proof.text} + +
}
) diff --git a/desktop/frontend/src/views/Live.jsx b/desktop/frontend/src/views/Live.jsx index 2356ed4..5528e8b 100644 --- a/desktop/frontend/src/views/Live.jsx +++ b/desktop/frontend/src/views/Live.jsx @@ -23,15 +23,22 @@ export default function Live({ onNavigate }) { const cameras = data?.stats?.cameras ?? [] const gallery = data?.stats?.gallery ?? {} const events = data?.events ?? [] + // No engine on THIS PC, so everything below came from head office. It has to + // be said rather than implied: a laptop in a hotel showing "2 cameras live" + // without this line is claiming to watch a shop it cannot see. + const viewing = data?.viewing === true // fraction_below_gate is the number that decides a site: what share of the // faces this camera saw were too poor to enrol. Surfaced rather than buried, // because a high value looks exactly like "a quiet day". - const worst = cameras.reduce((acc, c) => { - const f = c?.pipeline?.best_quality?.fraction_below_gate - return typeof f === 'number' && f > acc ? f : acc - }, 0) - const up = cameras.filter(c => c.connected).length + const worst = viewing + ? (data?.stats?.fraction_below_gate ?? 0) + : cameras.reduce((acc, c) => { + const f = c?.pipeline?.best_quality?.fraction_below_gate + return typeof f === 'number' && f > acc ? f : acc + }, 0) + // Viewing: the server already summed these across the estate. + const up = viewing ? (data?.stats?.cameras_up ?? 0) : cameras.filter(c => c.connected).length const arrivals = events.filter(e => e.type === 'person.new' || e.type === 'person.seen') const freshest = useFreshest(arrivals[0]) @@ -40,14 +47,29 @@ export default function Live({ onNavigate }) {

Live

-

Who is in the shop, and whether it is reaching head office.

+

{viewing + ? 'Your shops, as head office sees them.' + : 'Who is in the shop, and whether it is reaching head office.'}

{error &&
{error}
} + {viewing && ( +
+ + Viewing your shops from here. This computer is not watching + any cameras itself — everything below is what your shop PCs reported. + To recognise people on this machine, it has to be on the same network + as a camera. +
+ )} + - + {/* Getting Started walks somebody through setting up a camera on THIS + PC — not what a viewer is doing, and not something they could finish + from here. */} + {!viewing && }
diff --git a/desktop/internal/cloud/client.go b/desktop/internal/cloud/client.go index 13932ed..268641c 100644 --- a/desktop/internal/cloud/client.go +++ b/desktop/internal/cloud/client.go @@ -39,6 +39,19 @@ type Client struct { // single-use refresh token. refreshMu sync.Mutex onRefresh func(Session) + + // Camera snapshots already fetched, keyed by camera id. The Cameras screen + // polls every 8 seconds and a snapshot is ~90 KB, so re-fetching one that + // has not changed would put megabytes an hour on the wire to redraw the + // same picture - the same trap the web app's useAuthedImage avoids by + // keying on the url rather than the object around it. + shotMu sync.Mutex + shots map[string]cachedShot +} + +type cachedShot struct { + at string // the server's snapshot_at; a new one is a new picture + uri string } type User struct { @@ -629,3 +642,107 @@ func (c *Client) RecordPurchase(ctx context.Context, visitorID string, "items": items, "source": "manual", "notes": notes, }, nil) } + +// ---------------------------------------------------------------- viewing -- +// +// A PC with no engine of its own is not broken, it is a VIEWER: somebody +// signed in on a laptop away from the shop. Everything below reads head +// office so those screens have something true to show instead of "engine not +// reachable", which is an accurate sentence and a useless one when the reader +// was never expecting an engine on that machine. + +// Arrival is one visit as the estate's feed reports it, across every shop - +// not just this PC's. `GET /api/visits`. +type Arrival struct { + VisitID string `json:"visit_id"` + VisitRef string `json:"visit_ref"` + OccurredAt string `json:"occurred_at"` + Site string `json:"site"` + SiteSlug string `json:"site_slug"` + CameraID string `json:"camera_id"` + VisitorID string `json:"visitor_id"` + Ref string `json:"ref"` + Label string `json:"label"` + IsNew bool `json:"is_new_visitor"` + Similarity float64 `json:"similarity"` + Attributes map[string]any `json:"attributes"` + Image Photo `json:"image"` +} + +// RemoteCamera is a camera as HEAD OFFICE knows it. Deliberately not the same +// type the local engine returns: this one can never be edited from here (the +// shop PC on that LAN is the only thing that can reach it) and it carries a +// snapshot rather than a stream. +type RemoteCamera struct { + ID string `json:"id"` + CameraID string `json:"camera_id"` + Label string `json:"label"` + Site string `json:"site"` + SiteSlug string `json:"site_slug"` + Enabled bool `json:"enabled"` + Connected *bool `json:"connected"` + LastSeenAt string `json:"last_seen_at"` + Snapshot Photo `json:"snapshot"` + SnapshotAt string `json:"snapshot_at"` +} + +// Arrivals reads the estate's recent visits, newest last. +func (c *Client) Arrivals(ctx context.Context, limit int) ([]Arrival, error) { + var out struct { + Arrivals []Arrival `json:"arrivals"` + } + if err := c.send(ctx, http.MethodGet, + fmt.Sprintf("/api/visits?limit=%d", limit), nil, &out); err != nil { + return nil, err + } + return out.Arrivals, nil +} + +// RemoteCameras lists every camera head office knows about for this company. +func (c *Client) RemoteCameras(ctx context.Context) ([]RemoteCamera, error) { + var out []RemoteCamera + if err := c.send(ctx, http.MethodGet, "/api/cameras", nil, &out); err != nil { + return nil, err + } + for i := range out { + out[i].Snapshot = c.resolveShot(ctx, out[i].ID, out[i].SnapshotAt, out[i].Snapshot) + } + return out, nil +} + +// resolveShot turns a camera snapshot into something the window can render. +// +// Same problem VisitorImage has and the same answer: a deployment with no +// object storage serves the picture from the API itself, so the url is +// relative and needs this session's bearer. A webview can supply +// neither - it resolves a relative src against wails:// and cannot set a +// header - so the bytes are fetched here and passed as a data: URI. +// +// A failure is an absence with a reason, never an error. Whether the camera is +// CONNECTED is the answer this screen exists to give; the photograph is +// decoration, and blanking the card because a picture would not load would +// hide the part that matters. +func (c *Client) resolveShot(ctx context.Context, camID, at string, p Photo) Photo { + if !p.Available || !p.Auth || p.URL == "" { + return p + } + c.shotMu.Lock() + hit, ok := c.shots[camID] + c.shotMu.Unlock() + if ok && hit.at == at && at != "" { + p.URL, p.Auth = hit.uri, false + return p + } + uri, err := c.fetchImage(ctx, p.URL) + if err != nil { + return Photo{Reason: "That camera's picture could not be loaded."} + } + c.shotMu.Lock() + if c.shots == nil { + c.shots = map[string]cachedShot{} + } + c.shots[camID] = cachedShot{at: at, uri: uri} + c.shotMu.Unlock() + p.URL, p.Auth = uri, false + return p +} diff --git a/desktop/viewing_test.go b/desktop/viewing_test.go new file mode 100644 index 0000000..fe7a461 --- /dev/null +++ b/desktop/viewing_test.go @@ -0,0 +1,166 @@ +package main + +import ( + "context" + "encoding/base64" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/loyaly/behavision-desktop/internal/cloud" + "github.com/loyaly/behavision-desktop/internal/local" +) + +// Viewer mode: what the app shows on a computer that is signed in and is not +// itself watching any cameras. +// +// This is the friend's-Mac case, and before it existed the app was honest and +// useless: Live() and Cameras() read ONLY the engine on 127.0.0.1, so a laptop +// with no engine got "engine not reachable at http://127.0.0.1:8010" and +// "0 of 0 cameras" - on an account whose shops were running and recognising +// people the whole time. Signing in is what the person did; the app answered +// as if they had not. +// +// The engine here is a port nothing listens on, which is precisely what a PC +// with no engine is. +const noEngine = "http://127.0.0.1:1" // reserved, refuses immediately + +func viewerApp(t *testing.T, srv *httptest.Server) *App { + t.Helper() + c := cloud.New(srv.URL) + c.SetSession(cloud.Session{Token: "test-token"}) + return &App{ + ctx: context.Background(), + cloud: c, + local: local.New(noEngine, "", ""), + } +} + +func TestLiveFallsBackToHeadOfficeWhenThereIsNoEngine(t *testing.T) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + switch { + case r.URL.Path == "/api/sites": + // Two shops. One is fine, one is the Office1 case. + w.Write([]byte(`[ + {"slug":"a","name":"A","cameras_up":2,"cameras_total":2,"fraction_below_gate":0.10}, + {"slug":"b","name":"B","cameras_up":1,"cameras_total":3,"fraction_below_gate":0.73} + ]`)) + case strings.HasPrefix(r.URL.Path, "/api/visits"): + w.Write([]byte(`{"arrivals":[ + {"visit_id":"v1","visitor_id":"p1","ref":"V-1","label":"Visitor 1","camera_id":"cam2","is_new_visitor":true}, + {"visit_id":"v2","visitor_id":"p1","ref":"V-1","label":"Visitor 1","camera_id":"cam2"}, + {"visit_id":"v3","camera_id":"entrance"} + ]}`)) + default: + t.Errorf("unexpected request %s", r.URL.Path) + } + })) + defer srv.Close() + + snap, err := viewerApp(t, srv).Live() + if err != nil { + t.Fatalf("Live: %v", err) + } + if !snap.Viewing { + t.Fatal("the snapshot did not say it was a view of somewhere else") + } + if got := snap.Stats["cameras_up"]; got != 3 { + t.Errorf("cameras_up = %v, want 3 summed across both shops", got) + } + if got := snap.Stats["cameras_total"]; got != 5 { + t.Errorf("cameras_total = %v, want 5", got) + } + // The WORST site, never an average. Averaging 0.10 against 0.73 reports + // 0.42 and hides the only shop anyone needs to go and fix - the same rule + // the heartbeat already follows with worst_site. + if got := snap.Stats["fraction_below_gate"]; got != 0.73 { + t.Errorf("fraction_below_gate = %v, want the worst shop's 0.73", got) + } + // Three arrivals, two of them the same person, one unidentified. A visit + // with no visitor_id is real footfall and an unknown person, so it counts + // as a sighting and not as somebody known. + g := snap.Stats["gallery"].(map[string]any) + if g["identities"] != 1 || g["sightings"] != 3 { + t.Errorf("gallery = %v, want 1 identity over 3 sightings", g) + } + if len(snap.Events) != 3 { + t.Fatalf("got %d events, want 3", len(snap.Events)) + } + if snap.Events[0]["type"] != "person.new" || snap.Events[1]["type"] != "person.seen" { + t.Errorf("arrival types wrong: %v", snap.Events) + } +} + +// Nobody signed in: the local failure is the honest answer. There is nothing +// else to show, and the person is most likely setting this PC up - telling +// them about head office would be telling them about something they have not +// got to yet. +func TestLiveWithNoEngineAndNoSessionReportsTheEngine(t *testing.T) { + a := &App{ctx: context.Background(), cloud: cloud.New("https://example.invalid"), + local: local.New(noEngine, "", "")} + if _, err := a.Live(); err == nil { + t.Fatal("want the engine error, got nil") + } +} + +// A remote camera is flagged, because the screen has to withhold every button +// that would talk to a camera on a network this computer cannot reach. An Edit +// button that cannot work is worse than one that is absent. +func TestRemoteCamerasAreFlaggedAndCarryNoCredentials(t *testing.T) { + jpeg := base64.StdEncoding.EncodeToString([]byte{0xFF, 0xD8, 0xFF, 0xD9}) + var shots int + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if strings.HasPrefix(r.URL.Path, "/api/camera-snapshots/") { + shots++ + w.Header().Set("Content-Type", "image/jpeg") + b, _ := base64.StdEncoding.DecodeString(jpeg) + w.Write(b) + return + } + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(`[ + {"id":"c1","camera_id":"cam2","label":"Open office","site":"Coimbatore", + "connected":true,"snapshot_at":"2026-09-30T10:00:00Z", + "snapshot":{"available":true,"url":"/api/camera-snapshots/c1.jpg","auth":true}} + ]`)) + })) + defer srv.Close() + + a := viewerApp(t, srv) + cams, err := a.Cameras() + if err != nil { + t.Fatalf("Cameras: %v", err) + } + if len(cams) != 1 { + t.Fatalf("got %d cameras, want 1", len(cams)) + } + if cams[0]["remote"] != true { + t.Error("the camera was not flagged remote") + } + // The RTSP details are a live path into the camera itself and the server + // does not send them to a tenant at all. Nothing here may invent them. + for _, k := range []string{"host", "port", "path", "username", "password"} { + if _, ok := cams[0][k]; ok { + t.Errorf("a remote camera carried %q", k) + } + } + + // The picture has to be fetched here: a webview resolves a relative + // src against wails:// and cannot send the session's bearer. + shot := cams[0]["snapshot"].(cloud.Photo) + if !strings.HasPrefix(shot.URL, "data:image/jpeg;base64,") || shot.Auth { + t.Errorf("snapshot url = %q auth=%v, want an inline data URI", shot.URL, shot.Auth) + } + + // And fetched ONCE. This screen polls every 8 seconds and a real snapshot + // is ~90 KB, so re-fetching an unchanged picture is megabytes an hour to + // redraw the same frame. + if _, err := a.Cameras(); err != nil { + t.Fatalf("second poll: %v", err) + } + if shots != 1 { + t.Errorf("fetched the same snapshot %d times across two polls", shots) + } +}