From b7ff447fd473a29e6336433121d7dbce8e9fbd99 Mon Sep 17 00:00:00 2001 From: legop3 Date: Thu, 18 Jun 2026 20:47:02 -0400 Subject: [PATCH] better barcode games --- server/public/assets/index-BlEp2Rqv.css | 1 + server/public/assets/index-CBt8gbRK.css | 1 - .../{index-I6c2my6z.js => index-DvrSfmPb.js} | 2 +- server/public/index.html | 4 +- .../barcodeGameService/games/scanQuest.js | 40 ++- .../games/scansPerSecond.js | 52 +++- .../src/services/barcodeGameService/index.js | 254 +++++++++++++++--- .../src/services/barcodeGameService/store.js | 29 +- .../components/BarcodeGamesPanel/index.jsx | 6 +- 9 files changed, 324 insertions(+), 65 deletions(-) create mode 100644 server/public/assets/index-BlEp2Rqv.css delete mode 100644 server/public/assets/index-CBt8gbRK.css rename server/public/assets/{index-I6c2my6z.js => index-DvrSfmPb.js} (98%) diff --git a/server/public/assets/index-BlEp2Rqv.css b/server/public/assets/index-BlEp2Rqv.css new file mode 100644 index 00000000..aef9b3a9 --- /dev/null +++ b/server/public/assets/index-BlEp2Rqv.css @@ -0,0 +1 @@ +*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.\!container{width:100%!important}.container{width:100%}@media(min-width:640px){.\!container{max-width:640px!important}.container{max-width:640px}}@media(min-width:768px){.\!container{max-width:768px!important}.container{max-width:768px}}@media(min-width:1024px){.\!container{max-width:1024px!important}.container{max-width:1024px}}@media(min-width:1280px){.\!container{max-width:1280px!important}.container{max-width:1280px}}@media(min-width:1536px){.\!container{max-width:1536px!important}.container{max-width:1536px}}.pride-page-bg{background-color:#050505;background-image:linear-gradient(#0000,#0000),repeating-linear-gradient(45deg,#5bcefa 0 1.818%,#f5a9b8 1.818% 3.636%,#fff 3.636% 5.455%,#f5a9b8 5.455% 7.273%,#5bcefa 7.273% 9.091%,#e40303 9.091% 10.909%,#ff8c00 10.909% 12.727%,#ffed00 12.727% 14.545%,#00a651 14.545% 16.364%,#06f 16.364% 18.182%,#9b2fae 18.182% 20%);background-attachment:fixed}.panel{border-radius:.375rem;--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity));padding:0;--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.panel-section{border-radius:.375rem;--tw-bg-opacity: 1;background-color:rgb(23 23 23 / var(--tw-bg-opacity));padding:0;--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.panel-muted{border-radius:.375rem;font-size:.875rem;line-height:1.25rem;--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity))}.surface{border-radius:.375rem;--tw-bg-opacity: 1;background-color:rgb(38 38 38 / var(--tw-bg-opacity));padding:.125rem;--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.surface-muted{border-radius:.375rem;--tw-bg-opacity: 1;background-color:rgb(64 64 64 / var(--tw-bg-opacity));padding:.125rem;--tw-text-opacity: 1;color:rgb(245 245 245 / var(--tw-text-opacity))}.field-input{border-radius:.375rem;border-width:1px;--tw-border-opacity: 1;border-color:rgb(82 82 82 / var(--tw-border-opacity));--tw-bg-opacity: 1;background-color:rgb(64 64 64 / var(--tw-bg-opacity));padding:.125rem;--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.field-input::-moz-placeholder{--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity))}.field-input::placeholder{--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity))}.field-input:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000);--tw-ring-opacity: 1;--tw-ring-color: rgb(14 165 233 / var(--tw-ring-opacity))}.button-dark{border-radius:.375rem;border-width:1px;--tw-border-opacity: 1;border-color:rgb(125 211 252 / var(--tw-border-opacity));--tw-bg-opacity: 1;background-color:rgb(2 132 199 / var(--tw-bg-opacity));padding:.125rem;font-size:.875rem;line-height:1.25rem;font-weight:500;--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.button-dark:hover{--tw-border-opacity: 1;border-color:rgb(14 165 233 / var(--tw-border-opacity));--tw-bg-opacity: 1;background-color:rgb(14 165 233 / var(--tw-bg-opacity))}.button-danger{border-radius:.375rem;--tw-bg-opacity: 1;background-color:rgb(225 29 72 / var(--tw-bg-opacity));padding:.125rem;font-size:.875rem;line-height:1.25rem;font-weight:500;--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.button-danger:hover{--tw-bg-opacity: 1;background-color:rgb(244 63 94 / var(--tw-bg-opacity))}.chat-composer{container-type:inline-size;display:flex;flex-wrap:wrap;align-items:stretch;gap:.125rem;min-width:0;overflow:hidden}.chat-composer-nickname{flex:0 0 5rem;min-width:0;order:1}.chat-composer-input{flex:1 1 0%;min-width:0;order:2}.chat-composer-send{flex:0 0 auto;align-self:stretch;order:3}.chat-composer-tts{display:flex;flex:1 1 100%;align-items:center;gap:.125rem;min-width:0;overflow:hidden;order:4}@container (min-width: 44rem){.chat-composer{flex-wrap:nowrap}.chat-composer-nickname{flex-basis:7rem;order:1}.chat-composer-input{order:2}.chat-composer-tts{flex:0 1 auto;order:3}.chat-composer-send{order:4}}.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.\!visible{visibility:visible!important}.visible{visibility:visible}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.inset-x-0{left:0;right:0}.inset-x-1{left:.25rem;right:.25rem}.bottom-0{bottom:0}.bottom-0\.5{bottom:.125rem}.bottom-1{bottom:.25rem}.bottom-2{bottom:.5rem}.bottom-\[calc\(100\%\+0\.125rem\)\]{bottom:calc(100% + .125rem)}.left-0{left:0}.left-0\.5{left:.125rem}.left-1{left:.25rem}.left-1\/2{left:50%}.left-2{left:.5rem}.right-0{right:0}.right-0\.5{right:.125rem}.right-1{right:.25rem}.right-2{right:.5rem}.top-0{top:0}.top-0\.5{top:.125rem}.top-1{top:.25rem}.top-1\/2{top:50%}.top-10{top:2.5rem}.top-6{top:1.5rem}.z-10{z-index:10}.z-20{z-index:20}.z-30{z-index:30}.z-40{z-index:40}.z-50{z-index:50}.z-\[1000\]{z-index:1000}.z-\[110\]{z-index:110}.z-\[120\]{z-index:120}.z-\[130\]{z-index:130}.z-\[70\]{z-index:70}.order-1{order:1}.order-2{order:2}.col-span-full{grid-column:1 / -1}.m-0{margin:0}.-mx-0\.5{margin-left:-.125rem;margin-right:-.125rem}.mx-auto{margin-left:auto;margin-right:auto}.my-\[-0\.125rem\]{margin-top:-.125rem;margin-bottom:-.125rem}.-mt-0\.5{margin-top:-.125rem}.mb-0\.5{margin-bottom:.125rem}.ml-1{margin-left:.25rem}.mr-0{margin-right:0}.mt-0{margin-top:0}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-\[0\.35vh\]{margin-top:.35vh}.mt-auto{margin-top:auto}.line-clamp-2{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}.\!block{display:block!important}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.grid{display:grid}.hidden{display:none}.aspect-\[4\/3\]{aspect-ratio:4/3}.aspect-square{aspect-ratio:1 / 1}.aspect-video{aspect-ratio:16 / 9}.h-1{height:.25rem}.h-1\.5{height:.375rem}.h-10{height:2.5rem}.h-2{height:.5rem}.h-28{height:7rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-4{height:1rem}.h-40{height:10rem}.h-48{height:12rem}.h-5{height:1.25rem}.h-64{height:16rem}.h-\[2px\]{height:2px}.h-\[3\.5rem\]{height:3.5rem}.h-\[70\%\]{height:70%}.h-\[7rem\]{height:7rem}.h-\[8vh\]{height:8vh}.h-\[calc\(1rem\+0\.25rem\)\]{height:1.25rem}.h-\[min\(100svh\,32rem\)\]{height:min(100svh,32rem)}.h-\[min\(60svh\,24rem\)\]{height:min(60svh,24rem)}.h-full{height:100%}.h-px{height:1px}.h-screen{height:100vh}.max-h-32{max-height:8rem}.max-h-40{max-height:10rem}.max-h-52{max-height:13rem}.max-h-72{max-height:18rem}.max-h-80{max-height:20rem}.max-h-\[14rem\]{max-height:14rem}.max-h-\[70vh\]{max-height:70vh}.max-h-\[72vh\]{max-height:72vh}.max-h-\[85vh\]{max-height:85vh}.max-h-\[90vh\]{max-height:90vh}.max-h-screen{max-height:100vh}.min-h-0{min-height:0px}.min-h-5{min-height:1.25rem}.min-h-9{min-height:2.25rem}.min-h-\[10rem\]{min-height:10rem}.min-h-\[14rem\]{min-height:14rem}.min-h-\[16rem\]{min-height:16rem}.min-h-\[3\.5rem\]{min-height:3.5rem}.min-h-\[4\.25rem\]{min-height:4.25rem}.min-h-\[4rem\]{min-height:4rem}.min-h-\[5rem\]{min-height:5rem}.min-h-screen{min-height:100vh}.w-1{width:.25rem}.w-1\.5{width:.375rem}.w-1\/2{width:50%}.w-10{width:2.5rem}.w-2{width:.5rem}.w-20{width:5rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-\[12\.5rem\]{width:12.5rem}.w-\[12rem\]{width:12rem}.w-\[20rem\]{width:20rem}.w-\[2px\]{width:2px}.w-\[3\.25rem\]{width:3.25rem}.w-\[4\.5rem\]{width:4.5rem}.w-\[5\.5rem\]{width:5.5rem}.w-\[5rem\]{width:5rem}.w-\[9rem\]{width:9rem}.w-\[calc\(1rem\+0\.25rem\)\]{width:1.25rem}.w-\[min\(20rem\,calc\(100vw-1rem\)\)\]{width:min(20rem,calc(100vw - 1rem))}.w-auto{width:auto}.w-fit{width:-moz-fit-content;width:fit-content}.w-full{width:100%}.w-max{width:-moz-max-content;width:max-content}.w-px{width:1px}.w-screen{width:100vw}.min-w-0{min-width:0px}.min-w-28{min-width:7rem}.min-w-\[0\]{min-width:0}.min-w-\[3\.7rem\]{min-width:3.7rem}.min-w-\[9\.5rem\]{min-width:9.5rem}.max-w-2xl{max-width:42rem}.max-w-3xl{max-width:48rem}.max-w-4xl{max-width:56rem}.max-w-\[40vw\]{max-width:40vw}.max-w-\[70vw\]{max-width:70vw}.max-w-\[80vw\]{max-width:80vw}.max-w-\[92\%\]{max-width:92%}.max-w-\[92vw\]{max-width:92vw}.max-w-\[94\%\]{max-width:94%}.max-w-full{max-width:100%}.max-w-lg{max-width:32rem}.max-w-md{max-width:28rem}.max-w-sm{max-width:24rem}.max-w-xl{max-width:36rem}.flex-1{flex:1 1 0%}.flex-\[0\.72\]{flex:.72}.flex-\[1\.1\]{flex:1.1}.flex-\[1\.22\]{flex:1.22}.flex-\[1\.28\]{flex:1.28}.shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.origin-top-left{transform-origin:top left}.-translate-x-1\/2{--tw-translate-x: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-y-1\/2{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-180{--tw-rotate: 180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.cursor-default{cursor:default}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.resize{resize:both}.auto-rows-fr{grid-auto-rows:minmax(0,1fr)}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.grid-cols-\[1\.25rem_minmax\(0\,1fr\)_auto\]{grid-template-columns:1.25rem minmax(0,1fr) auto}.grid-cols-\[auto_minmax\(0\,1fr\)\]{grid-template-columns:auto minmax(0,1fr)}.grid-cols-\[minmax\(0\,0\.7fr\)_minmax\(0\,2\.1fr\)_minmax\(0\,0\.7fr\)\]{grid-template-columns:minmax(0,.7fr) minmax(0,2.1fr) minmax(0,.7fr)}.grid-cols-\[minmax\(0\,1\.2fr\)_repeat\(4\,minmax\(0\,1fr\)\)\]{grid-template-columns:minmax(0,1.2fr) repeat(4,minmax(0,1fr))}.grid-cols-\[minmax\(0\,1\.35fr\)_minmax\(0\,0\.95fr\)\]{grid-template-columns:minmax(0,1.35fr) minmax(0,.95fr)}.grid-cols-\[minmax\(0\,1\.3fr\)_minmax\(0\,0\.22fr\)\]{grid-template-columns:minmax(0,1.3fr) minmax(0,.22fr)}.grid-cols-\[minmax\(0\,1\.45fr\)_minmax\(0\,0\.85fr\)\]{grid-template-columns:minmax(0,1.45fr) minmax(0,.85fr)}.grid-cols-\[minmax\(0\,1fr\)_4\.75rem\]{grid-template-columns:minmax(0,1fr) 4.75rem}.grid-cols-\[minmax\(0\,1fr\)_auto\]{grid-template-columns:minmax(0,1fr) auto}.grid-cols-\[minmax\(0\,1fr\)_minmax\(0\,0\.7fr\)_minmax\(0\,1\.9fr\)_minmax\(0\,0\.7fr\)\]{grid-template-columns:minmax(0,1fr) minmax(0,.7fr) minmax(0,1.9fr) minmax(0,.7fr)}.grid-cols-\[minmax\(0\,1fr\)_minmax\(0\,0\.9fr\)_minmax\(0\,0\.75fr\)\]{grid-template-columns:minmax(0,1fr) minmax(0,.9fr) minmax(0,.75fr)}.grid-cols-\[minmax\(0\,1fr\)_minmax\(0\,1\.4fr\)\]{grid-template-columns:minmax(0,1fr) minmax(0,1.4fr)}.grid-cols-\[minmax\(0\,1fr\)_minmax\(0\,1fr\)\]{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}.grid-rows-2{grid-template-rows:repeat(2,minmax(0,1fr))}.grid-rows-3{grid-template-rows:repeat(3,minmax(0,1fr))}.grid-rows-\[auto_1fr\]{grid-template-rows:auto 1fr}.grid-rows-\[auto_auto_1fr\]{grid-template-rows:auto auto 1fr}.grid-rows-\[auto_auto_1fr_auto\]{grid-template-rows:auto auto 1fr auto}.grid-rows-\[auto_minmax\(0\,1fr\)\]{grid-template-rows:auto minmax(0,1fr)}.grid-rows-\[auto_minmax\(0\,1fr\)_auto\]{grid-template-rows:auto minmax(0,1fr) auto}.grid-rows-\[minmax\(0\,1fr\)_auto\]{grid-template-rows:minmax(0,1fr) auto}.grid-rows-\[minmax\(0\,1fr\)_minmax\(0\,1fr\)_minmax\(0\,1fr\)\]{grid-template-rows:minmax(0,1fr) minmax(0,1fr) minmax(0,1fr)}.\!flex-row{flex-direction:row!important}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.place-items-center{place-items:center}.content-center{align-content:center}.content-start{align-content:flex-start}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.items-baseline{align-items:baseline}.items-stretch{align-items:stretch}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.justify-stretch{justify-content:stretch}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-4{gap:1rem}.gap-\[0\.55vh\]{gap:.55vh}.gap-\[0\.8vw\]{gap:.8vw}.gap-\[1vw\]{gap:1vw}.gap-\[4vh\]{gap:4vh}.gap-x-3{-moz-column-gap:.75rem;column-gap:.75rem}.space-y-0\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.125rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.125rem * var(--tw-space-y-reverse))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.self-start{align-self:flex-start}.self-stretch{align-self:stretch}.overflow-hidden{overflow:hidden}.overflow-visible{overflow:visible}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-none{border-radius:0}.rounded-sm{border-radius:.125rem}.rounded-xl{border-radius:.75rem}.rounded-t{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.border{border-width:1px}.border-2{border-width:2px}.border-4{border-width:4px}.border-b{border-bottom-width:1px}.border-l{border-left-width:1px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-amber-200{--tw-border-opacity: 1;border-color:rgb(253 230 138 / var(--tw-border-opacity))}.border-amber-200\/30{border-color:#fde68a4d}.border-amber-200\/70{border-color:#fde68ab3}.border-amber-200\/90{border-color:#fde68ae6}.border-amber-300\/70{border-color:#fcd34db3}.border-amber-300\/80{border-color:#fcd34dcc}.border-amber-400\/30{border-color:#fbbf244d}.border-amber-400\/60{border-color:#fbbf2499}.border-amber-600\/60{border-color:#d9770699}.border-amber-700\/60{border-color:#b4530999}.border-blue-600{--tw-border-opacity: 1;border-color:rgb(37 99 235 / var(--tw-border-opacity))}.border-cyan-200{--tw-border-opacity: 1;border-color:rgb(165 243 252 / var(--tw-border-opacity))}.border-cyan-200\/90{border-color:#a5f3fce6}.border-cyan-300{--tw-border-opacity: 1;border-color:rgb(103 232 249 / var(--tw-border-opacity))}.border-cyan-300\/70{border-color:#67e8f9b3}.border-cyan-300\/80{border-color:#67e8f9cc}.border-cyan-500\/40{border-color:#06b6d466}.border-cyan-500\/60{border-color:#06b6d499}.border-emerald-100\/80{border-color:#d1fae5cc}.border-emerald-200{--tw-border-opacity: 1;border-color:rgb(167 243 208 / var(--tw-border-opacity))}.border-emerald-200\/70{border-color:#a7f3d0b3}.border-emerald-200\/90{border-color:#a7f3d0e6}.border-emerald-300{--tw-border-opacity: 1;border-color:rgb(110 231 183 / var(--tw-border-opacity))}.border-emerald-300\/70{border-color:#6ee7b7b3}.border-emerald-400\/60{border-color:#34d39999}.border-emerald-500\/40{border-color:#10b98166}.border-emerald-500\/70{border-color:#10b981b3}.border-emerald-700\/60{border-color:#04785799}.border-emerald-700\/70{border-color:#047857b3}.border-emerald-950{--tw-border-opacity: 1;border-color:rgb(2 44 34 / var(--tw-border-opacity))}.border-fuchsia-300\/70{border-color:#f0abfcb3}.border-green-400{--tw-border-opacity: 1;border-color:rgb(74 222 128 / var(--tw-border-opacity))}.border-indigo-200\/70{border-color:#c7d2feb3}.border-indigo-200\/95{border-color:#c7d2fef2}.border-indigo-300\/70{border-color:#a5b4fcb3}.border-indigo-400\/30{border-color:#818cf84d}.border-indigo-700{--tw-border-opacity: 1;border-color:rgb(67 56 202 / var(--tw-border-opacity))}.border-indigo-700\/60{border-color:#4338ca99}.border-neutral-500\/50{border-color:#73737380}.border-neutral-500\/60{border-color:#73737399}.border-neutral-600{--tw-border-opacity: 1;border-color:rgb(82 82 82 / var(--tw-border-opacity))}.border-neutral-700{--tw-border-opacity: 1;border-color:rgb(64 64 64 / var(--tw-border-opacity))}.border-pink-200{--tw-border-opacity: 1;border-color:rgb(251 207 232 / var(--tw-border-opacity))}.border-red-100{--tw-border-opacity: 1;border-color:rgb(254 226 226 / var(--tw-border-opacity))}.border-red-200{--tw-border-opacity: 1;border-color:rgb(254 202 202 / var(--tw-border-opacity))}.border-red-300\/90{border-color:#fca5a5e6}.border-red-400{--tw-border-opacity: 1;border-color:rgb(248 113 113 / var(--tw-border-opacity))}.border-red-400\/60{border-color:#f8717199}.border-rose-300{--tw-border-opacity: 1;border-color:rgb(253 164 175 / var(--tw-border-opacity))}.border-sky-300{--tw-border-opacity: 1;border-color:rgb(125 211 252 / var(--tw-border-opacity))}.border-sky-400\/50{border-color:#38bdf880}.border-slate-200\/30{border-color:#e2e8f04d}.border-slate-400{--tw-border-opacity: 1;border-color:rgb(148 163 184 / var(--tw-border-opacity))}.border-slate-400\/60{border-color:#94a3b899}.border-slate-500\/70{border-color:#64748bb3}.border-slate-600{--tw-border-opacity: 1;border-color:rgb(71 85 105 / var(--tw-border-opacity))}.border-slate-600\/40{border-color:#47556966}.border-slate-600\/60{border-color:#47556999}.border-slate-600\/70{border-color:#475569b3}.border-slate-600\/80{border-color:#475569cc}.border-slate-700{--tw-border-opacity: 1;border-color:rgb(51 65 85 / var(--tw-border-opacity))}.border-slate-700\/60{border-color:#33415599}.border-slate-700\/70{border-color:#334155b3}.border-slate-700\/80{border-color:#334155cc}.border-slate-800{--tw-border-opacity: 1;border-color:rgb(30 41 59 / var(--tw-border-opacity))}.border-slate-800\/60{border-color:#1e293b99}.border-slate-800\/80{border-color:#1e293bcc}.border-transparent{border-color:transparent}.border-white{--tw-border-opacity: 1;border-color:rgb(255 255 255 / var(--tw-border-opacity))}.border-white\/40{border-color:#fff6}.border-white\/80{border-color:#fffc}.bg-amber-100\/95{background-color:#fef3c7f2}.bg-amber-400{--tw-bg-opacity: 1;background-color:rgb(251 191 36 / var(--tw-bg-opacity))}.bg-amber-400\/80{background-color:#fbbf24cc}.bg-amber-500{--tw-bg-opacity: 1;background-color:rgb(245 158 11 / var(--tw-bg-opacity))}.bg-amber-500\/30{background-color:#f59e0b4d}.bg-amber-600{--tw-bg-opacity: 1;background-color:rgb(217 119 6 / var(--tw-bg-opacity))}.bg-amber-600\/70{background-color:#d97706b3}.bg-amber-700\/20{background-color:#b4530933}.bg-amber-700\/30{background-color:#b453094d}.bg-amber-700\/35{background-color:#b4530959}.bg-amber-700\/80{background-color:#b45309cc}.bg-amber-800{--tw-bg-opacity: 1;background-color:rgb(146 64 14 / var(--tw-bg-opacity))}.bg-amber-900{--tw-bg-opacity: 1;background-color:rgb(120 53 15 / var(--tw-bg-opacity))}.bg-amber-900\/40{background-color:#78350f66}.bg-amber-900\/60{background-color:#78350f99}.bg-amber-900\/90{background-color:#78350fe6}.bg-amber-950\/70{background-color:#451a03b3}.bg-black{--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity))}.bg-black\/20{background-color:#0003}.bg-black\/30{background-color:#0000004d}.bg-black\/40{background-color:#0006}.bg-black\/45{background-color:#00000073}.bg-black\/50{background-color:#00000080}.bg-black\/55{background-color:#0000008c}.bg-black\/60{background-color:#0009}.bg-black\/70{background-color:#000000b3}.bg-black\/75{background-color:#000000bf}.bg-black\/80{background-color:#000c}.bg-black\/95{background-color:#000000f2}.bg-blue-500{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity))}.bg-cyan-300{--tw-bg-opacity: 1;background-color:rgb(103 232 249 / var(--tw-bg-opacity))}.bg-cyan-500{--tw-bg-opacity: 1;background-color:rgb(6 182 212 / var(--tw-bg-opacity))}.bg-cyan-500\/80{background-color:#06b6d4cc}.bg-cyan-900{--tw-bg-opacity: 1;background-color:rgb(22 78 99 / var(--tw-bg-opacity))}.bg-cyan-900\/45{background-color:#164e6373}.bg-cyan-900\/90{background-color:#164e63e6}.bg-emerald-200{--tw-bg-opacity: 1;background-color:rgb(167 243 208 / var(--tw-bg-opacity))}.bg-emerald-400{--tw-bg-opacity: 1;background-color:rgb(52 211 153 / var(--tw-bg-opacity))}.bg-emerald-500{--tw-bg-opacity: 1;background-color:rgb(16 185 129 / var(--tw-bg-opacity))}.bg-emerald-500\/45{background-color:#10b98173}.bg-emerald-500\/80{background-color:#10b981cc}.bg-emerald-600{--tw-bg-opacity: 1;background-color:rgb(5 150 105 / var(--tw-bg-opacity))}.bg-emerald-600\/70{background-color:#059669b3}.bg-emerald-600\/80{background-color:#059669cc}.bg-emerald-700{--tw-bg-opacity: 1;background-color:rgb(4 120 87 / var(--tw-bg-opacity))}.bg-emerald-700\/20{background-color:#04785733}.bg-emerald-700\/60{background-color:#04785799}.bg-emerald-800{--tw-bg-opacity: 1;background-color:rgb(6 95 70 / var(--tw-bg-opacity))}.bg-emerald-900{--tw-bg-opacity: 1;background-color:rgb(6 78 59 / var(--tw-bg-opacity))}.bg-emerald-900\/60{background-color:#064e3b99}.bg-emerald-900\/90{background-color:#064e3be6}.bg-emerald-950{--tw-bg-opacity: 1;background-color:rgb(2 44 34 / var(--tw-bg-opacity))}.bg-emerald-950\/50{background-color:#022c2280}.bg-fuchsia-200{--tw-bg-opacity: 1;background-color:rgb(245 208 254 / var(--tw-bg-opacity))}.bg-fuchsia-600{--tw-bg-opacity: 1;background-color:rgb(192 38 211 / var(--tw-bg-opacity))}.bg-fuchsia-700{--tw-bg-opacity: 1;background-color:rgb(162 28 175 / var(--tw-bg-opacity))}.bg-fuchsia-800{--tw-bg-opacity: 1;background-color:rgb(134 25 143 / var(--tw-bg-opacity))}.bg-fuchsia-900\/45{background-color:#701a7573}.bg-green-600{--tw-bg-opacity: 1;background-color:rgb(22 163 74 / var(--tw-bg-opacity))}.bg-indigo-600{--tw-bg-opacity: 1;background-color:rgb(79 70 229 / var(--tw-bg-opacity))}.bg-indigo-600\/70{background-color:#4f46e5b3}.bg-indigo-900{--tw-bg-opacity: 1;background-color:rgb(49 46 129 / var(--tw-bg-opacity))}.bg-indigo-900\/60{background-color:#312e8199}.bg-indigo-950{--tw-bg-opacity: 1;background-color:rgb(30 27 75 / var(--tw-bg-opacity))}.bg-indigo-950\/90{background-color:#1e1b4be6}.bg-neutral-700{--tw-bg-opacity: 1;background-color:rgb(64 64 64 / var(--tw-bg-opacity))}.bg-neutral-800{--tw-bg-opacity: 1;background-color:rgb(38 38 38 / var(--tw-bg-opacity))}.bg-neutral-800\/80{background-color:#262626cc}.bg-neutral-900{--tw-bg-opacity: 1;background-color:rgb(23 23 23 / var(--tw-bg-opacity))}.bg-neutral-900\/100{background-color:#171717}.bg-neutral-900\/70{background-color:#171717b3}.bg-neutral-900\/95{background-color:#171717f2}.bg-neutral-950{--tw-bg-opacity: 1;background-color:rgb(10 10 10 / var(--tw-bg-opacity))}.bg-pink-500{--tw-bg-opacity: 1;background-color:rgb(236 72 153 / var(--tw-bg-opacity))}.bg-red-500{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity))}.bg-red-500\/45{background-color:#ef444473}.bg-red-600{--tw-bg-opacity: 1;background-color:rgb(220 38 38 / var(--tw-bg-opacity))}.bg-red-600\/70{background-color:#dc2626b3}.bg-red-700{--tw-bg-opacity: 1;background-color:rgb(185 28 28 / var(--tw-bg-opacity))}.bg-red-700\/20{background-color:#b91c1c33}.bg-red-700\/60{background-color:#b91c1c99}.bg-red-800{--tw-bg-opacity: 1;background-color:rgb(153 27 27 / var(--tw-bg-opacity))}.bg-red-900\/35{background-color:#7f1d1d59}.bg-red-900\/40{background-color:#7f1d1d66}.bg-red-900\/45{background-color:#7f1d1d73}.bg-red-900\/50{background-color:#7f1d1d80}.bg-red-900\/80{background-color:#7f1d1dcc}.bg-rose-500{--tw-bg-opacity: 1;background-color:rgb(244 63 94 / var(--tw-bg-opacity))}.bg-rose-600{--tw-bg-opacity: 1;background-color:rgb(225 29 72 / var(--tw-bg-opacity))}.bg-sky-600{--tw-bg-opacity: 1;background-color:rgb(2 132 199 / var(--tw-bg-opacity))}.bg-sky-700\/20{background-color:#0369a133}.bg-slate-300{--tw-bg-opacity: 1;background-color:rgb(203 213 225 / var(--tw-bg-opacity))}.bg-slate-500{--tw-bg-opacity: 1;background-color:rgb(100 116 139 / var(--tw-bg-opacity))}.bg-slate-600{--tw-bg-opacity: 1;background-color:rgb(71 85 105 / var(--tw-bg-opacity))}.bg-slate-600\/70{background-color:#475569b3}.bg-slate-700{--tw-bg-opacity: 1;background-color:rgb(51 65 85 / var(--tw-bg-opacity))}.bg-slate-700\/30{background-color:#3341554d}.bg-slate-700\/60{background-color:#33415599}.bg-slate-700\/70{background-color:#334155b3}.bg-slate-700\/80{background-color:#334155cc}.bg-slate-800{--tw-bg-opacity: 1;background-color:rgb(30 41 59 / var(--tw-bg-opacity))}.bg-slate-800\/60{background-color:#1e293b99}.bg-slate-800\/70{background-color:#1e293bb3}.bg-slate-800\/80{background-color:#1e293bcc}.bg-slate-900{--tw-bg-opacity: 1;background-color:rgb(15 23 42 / var(--tw-bg-opacity))}.bg-slate-900\/35{background-color:#0f172a59}.bg-slate-900\/40{background-color:#0f172a66}.bg-slate-900\/70{background-color:#0f172ab3}.bg-slate-950{--tw-bg-opacity: 1;background-color:rgb(2 6 23 / var(--tw-bg-opacity))}.bg-slate-950\/80{background-color:#020617cc}.bg-slate-950\/90{background-color:#020617e6}.bg-transparent{background-color:transparent}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity))}.bg-white\/10{background-color:#ffffff1a}.bg-yellow-100{--tw-bg-opacity: 1;background-color:rgb(254 249 195 / var(--tw-bg-opacity))}.bg-yellow-500{--tw-bg-opacity: 1;background-color:rgb(234 179 8 / var(--tw-bg-opacity))}.bg-zinc-900{--tw-bg-opacity: 1;background-color:rgb(24 24 27 / var(--tw-bg-opacity))}.bg-zinc-900\/90{background-color:#18181be6}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.from-lime-800{--tw-gradient-from: #3f6212 var(--tw-gradient-from-position);--tw-gradient-to: rgb(63 98 18 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.from-neutral-800{--tw-gradient-from: #262626 var(--tw-gradient-from-position);--tw-gradient-to: rgb(38 38 38 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.via-neutral-700{--tw-gradient-to: rgb(64 64 64 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), #404040 var(--tw-gradient-via-position), var(--tw-gradient-to)}.to-neutral-600{--tw-gradient-to: #525252 var(--tw-gradient-to-position)}.to-teal-800{--tw-gradient-to: #115e59 var(--tw-gradient-to-position)}.bg-cover{background-size:cover}.bg-center{background-position:center}.bg-no-repeat{background-repeat:no-repeat}.fill-slate-200{fill:#e2e8f0}.object-contain{-o-object-fit:contain;object-fit:contain}.object-cover{-o-object-fit:cover;object-fit:cover}.p-0{padding:0}.p-0\.5{padding:.125rem}.p-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-2{padding:.5rem}.p-4{padding:1rem}.p-\[0\.85vw\]{padding:.85vw}.px-0{padding-left:0;padding-right:0}.px-0\.5{padding-left:.125rem;padding-right:.125rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-\[1\.6vw\]{padding-left:1.6vw;padding-right:1.6vw}.px-\[1vw\]{padding-left:1vw;padding-right:1vw}.px-\[3vw\]{padding-left:3vw;padding-right:3vw}.px-\[4vw\]{padding-left:4vw;padding-right:4vw}.py-0{padding-top:0;padding-bottom:0}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-\[0\.55vh\]{padding-top:.55vh;padding-bottom:.55vh}.py-\[1px\]{padding-top:1px;padding-bottom:1px}.py-\[2\.5vh\]{padding-top:2.5vh;padding-bottom:2.5vh}.py-\[2px\]{padding-top:2px;padding-bottom:2px}.py-\[4vh\]{padding-top:4vh;padding-bottom:4vh}.pb-0{padding-bottom:0}.pb-1{padding-bottom:.25rem}.pb-3{padding-bottom:.75rem}.pr-0{padding-right:0}.pt-1{padding-top:.25rem}.pt-1\.5{padding-top:.375rem}.pt-5{padding-top:1.25rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.align-top{vertical-align:top}.align-middle{vertical-align:middle}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-4xl{font-size:2.25rem;line-height:2.5rem}.text-5xl{font-size:3rem;line-height:1}.text-\[0\.45rem\]{font-size:.45rem}.text-\[0\.55em\]{font-size:.55em}.text-\[0\.55rem\]{font-size:.55rem}.text-\[0\.58rem\]{font-size:.58rem}.text-\[0\.5rem\]{font-size:.5rem}.text-\[0\.62rem\]{font-size:.62rem}.text-\[0\.65rem\]{font-size:.65rem}.text-\[0\.68rem\]{font-size:.68rem}.text-\[0\.6rem\]{font-size:.6rem}.text-\[0\.72rem\]{font-size:.72rem}.text-\[0\.75rem\]{font-size:.75rem}.text-\[0\.78rem\]{font-size:.78rem}.text-\[0\.7rem\]{font-size:.7rem}.text-\[0\.82rem\]{font-size:.82rem}.text-\[0\.85rem\]{font-size:.85rem}.text-\[0\.8rem\]{font-size:.8rem}.text-\[1\.1rem\]{font-size:1.1rem}.text-\[1\.5rem\]{font-size:1.5rem}.text-\[15vw\]{font-size:15vw}.text-\[1rem\]{font-size:1rem}.text-\[5vw\]{font-size:5vw}.text-\[6vw\]{font-size:6vw}.text-\[clamp\(1\.25rem\,2\.5vh\,2\.6rem\)\]{font-size:clamp(1.25rem,2.5vh,2.6rem)}.text-\[clamp\(1\.2rem\,2\.9vh\,3rem\)\]{font-size:clamp(1.2rem,2.9vh,3rem)}.text-\[clamp\(2\.5rem\,5\.3vh\,5\.8rem\)\]{font-size:clamp(2.5rem,5.3vh,5.8rem)}.text-\[clamp\(2\.5rem\,7vh\,7rem\)\]{font-size:clamp(2.5rem,7vh,7rem)}.text-\[clamp\(2\.5rem\,8vh\,8rem\)\]{font-size:clamp(2.5rem,8vh,8rem)}.text-\[clamp\(2rem\,5vh\,5\.2rem\)\]{font-size:clamp(2rem,5vh,5.2rem)}.text-\[clamp\(3\.2rem\,11vh\,11rem\)\]{font-size:clamp(3.2rem,11vh,11rem)}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-black{font-weight:900}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.capitalize{text-transform:capitalize}.italic{font-style:italic}.leading-\[0\.95\]{line-height:.95}.leading-none{line-height:1}.leading-snug{line-height:1.375}.leading-tight{line-height:1.25}.tracking-normal{letter-spacing:0em}.tracking-tight{letter-spacing:-.025em}.tracking-wide{letter-spacing:.025em}.text-amber-100{--tw-text-opacity: 1;color:rgb(254 243 199 / var(--tw-text-opacity))}.text-amber-200{--tw-text-opacity: 1;color:rgb(253 230 138 / var(--tw-text-opacity))}.text-amber-200\/80{color:#fde68acc}.text-amber-200\/85{color:#fde68ad9}.text-amber-300{--tw-text-opacity: 1;color:rgb(252 211 77 / var(--tw-text-opacity))}.text-amber-400{--tw-text-opacity: 1;color:rgb(251 191 36 / var(--tw-text-opacity))}.text-amber-50{--tw-text-opacity: 1;color:rgb(255 251 235 / var(--tw-text-opacity))}.text-amber-50\/90{color:#fffbebe6}.text-amber-950{--tw-text-opacity: 1;color:rgb(69 26 3 / var(--tw-text-opacity))}.text-black{--tw-text-opacity: 1;color:rgb(0 0 0 / var(--tw-text-opacity))}.text-blue-300{--tw-text-opacity: 1;color:rgb(147 197 253 / var(--tw-text-opacity))}.text-blue-400{--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity))}.text-blue-800{--tw-text-opacity: 1;color:rgb(30 64 175 / var(--tw-text-opacity))}.text-cyan-200{--tw-text-opacity: 1;color:rgb(165 243 252 / var(--tw-text-opacity))}.text-cyan-300{--tw-text-opacity: 1;color:rgb(103 232 249 / var(--tw-text-opacity))}.text-cyan-50{--tw-text-opacity: 1;color:rgb(236 254 255 / var(--tw-text-opacity))}.text-emerald-100{--tw-text-opacity: 1;color:rgb(209 250 229 / var(--tw-text-opacity))}.text-emerald-100\/80{color:#d1fae5cc}.text-emerald-200{--tw-text-opacity: 1;color:rgb(167 243 208 / var(--tw-text-opacity))}.text-emerald-300{--tw-text-opacity: 1;color:rgb(110 231 183 / var(--tw-text-opacity))}.text-emerald-400{--tw-text-opacity: 1;color:rgb(52 211 153 / var(--tw-text-opacity))}.text-emerald-50{--tw-text-opacity: 1;color:rgb(236 253 245 / var(--tw-text-opacity))}.text-emerald-50\/90{color:#ecfdf5e6}.text-emerald-950{--tw-text-opacity: 1;color:rgb(2 44 34 / var(--tw-text-opacity))}.text-fuchsia-50{--tw-text-opacity: 1;color:rgb(253 244 255 / var(--tw-text-opacity))}.text-fuchsia-900{--tw-text-opacity: 1;color:rgb(112 26 117 / var(--tw-text-opacity))}.text-indigo-200{--tw-text-opacity: 1;color:rgb(199 210 254 / var(--tw-text-opacity))}.text-indigo-50{--tw-text-opacity: 1;color:rgb(238 242 255 / var(--tw-text-opacity))}.text-indigo-50\/90{color:#eef2ffe6}.text-lime-300{--tw-text-opacity: 1;color:rgb(190 242 100 / var(--tw-text-opacity))}.text-lime-400{--tw-text-opacity: 1;color:rgb(163 230 53 / var(--tw-text-opacity))}.text-neutral-100{--tw-text-opacity: 1;color:rgb(245 245 245 / var(--tw-text-opacity))}.text-neutral-200{--tw-text-opacity: 1;color:rgb(229 229 229 / var(--tw-text-opacity))}.text-neutral-300{--tw-text-opacity: 1;color:rgb(212 212 212 / var(--tw-text-opacity))}.text-neutral-400{--tw-text-opacity: 1;color:rgb(163 163 163 / var(--tw-text-opacity))}.text-neutral-50{--tw-text-opacity: 1;color:rgb(250 250 250 / var(--tw-text-opacity))}.text-neutral-500{--tw-text-opacity: 1;color:rgb(115 115 115 / var(--tw-text-opacity))}.text-red-100{--tw-text-opacity: 1;color:rgb(254 226 226 / var(--tw-text-opacity))}.text-red-100\/90{color:#fee2e2e6}.text-red-100\/95{color:#fee2e2f2}.text-red-200{--tw-text-opacity: 1;color:rgb(254 202 202 / var(--tw-text-opacity))}.text-red-300{--tw-text-opacity: 1;color:rgb(252 165 165 / var(--tw-text-opacity))}.text-red-400{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity))}.text-red-50{--tw-text-opacity: 1;color:rgb(254 242 242 / var(--tw-text-opacity))}.text-red-700{--tw-text-opacity: 1;color:rgb(185 28 28 / var(--tw-text-opacity))}.text-rose-300{--tw-text-opacity: 1;color:rgb(253 164 175 / var(--tw-text-opacity))}.text-sky-200{--tw-text-opacity: 1;color:rgb(186 230 253 / var(--tw-text-opacity))}.text-sky-300{--tw-text-opacity: 1;color:rgb(125 211 252 / var(--tw-text-opacity))}.text-slate-100{--tw-text-opacity: 1;color:rgb(241 245 249 / var(--tw-text-opacity))}.text-slate-200{--tw-text-opacity: 1;color:rgb(226 232 240 / var(--tw-text-opacity))}.text-slate-300{--tw-text-opacity: 1;color:rgb(203 213 225 / var(--tw-text-opacity))}.text-slate-400{--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity))}.text-slate-400\/60{color:#94a3b899}.text-slate-500{--tw-text-opacity: 1;color:rgb(100 116 139 / var(--tw-text-opacity))}.text-slate-900{--tw-text-opacity: 1;color:rgb(15 23 42 / var(--tw-text-opacity))}.text-slate-950{--tw-text-opacity: 1;color:rgb(2 6 23 / var(--tw-text-opacity))}.text-teal-400{--tw-text-opacity: 1;color:rgb(45 212 191 / var(--tw-text-opacity))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.text-white\/90{color:#ffffffe6}.text-yellow-400{--tw-text-opacity: 1;color:rgb(250 204 21 / var(--tw-text-opacity))}.underline{text-decoration-line:underline}.accent-cyan-500{accent-color:#06b6d4}.accent-emerald-400{accent-color:#34d399}.accent-emerald-500{accent-color:#10b981}.opacity-0{opacity:0}.opacity-100{opacity:1}.opacity-40{opacity:.4}.opacity-50{opacity:.5}.opacity-70{opacity:.7}.opacity-80{opacity:.8}.opacity-85{opacity:.85}.opacity-90{opacity:.9}.shadow{--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1);--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-2xl{--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / .25);--tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-\[0_1px_0_rgba\(255\,255\,255\,0\.05\)_inset\,0_10px_24px_rgba\(0\,0\,0\,0\.28\)\]{--tw-shadow: 0 1px 0 rgba(255,255,255,.05) inset,0 10px 24px rgba(0,0,0,.28);--tw-shadow-colored: inset 0 1px 0 var(--tw-shadow-color), 0 10px 24px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-inner{--tw-shadow: inset 0 2px 4px 0 rgb(0 0 0 / .05);--tw-shadow-colored: inset 0 2px 4px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-black\/30{--tw-shadow-color: rgb(0 0 0 / .3);--tw-shadow: var(--tw-shadow-colored)}.shadow-cyan-950\/50{--tw-shadow-color: rgb(8 51 68 / .5);--tw-shadow: var(--tw-shadow-colored)}.ring-1{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-2{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-4{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-amber-300{--tw-ring-opacity: 1;--tw-ring-color: rgb(252 211 77 / var(--tw-ring-opacity))}.ring-amber-300\/80{--tw-ring-color: rgb(252 211 77 / .8)}.ring-emerald-300\/70{--tw-ring-color: rgb(110 231 183 / .7)}.ring-red-500\/80{--tw-ring-color: rgb(239 68 68 / .8)}.ring-red-500\/90{--tw-ring-color: rgb(239 68 68 / .9)}.ring-sky-400\/80{--tw-ring-color: rgb(56 189 248 / .8)}.blur{--tw-blur: blur(8px);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.grayscale{--tw-grayscale: grayscale(100%);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.invert{--tw-invert: invert(100%);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.sepia{--tw-sepia: sepia(100%);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur-sm{--tw-backdrop-blur: blur(4px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\[width\,height\]{transition-property:width,height;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-500{transition-duration:.5s}.duration-700{transition-duration:.7s}@keyframes batteryTickWarn{0%,49%{background-color:#fffffff2}50%,to{background-color:#ef4444f2}}.battery-tick-warn{animation:batteryTickWarn .4s steps(2,end) infinite}@keyframes batteryUrgentFlash{0%,49%{opacity:1}50%,to{opacity:.25}}.battery-urgent-flash{animation:batteryUrgentFlash .4s steps(2,end) infinite}.no-touch-select{-moz-user-select:none;user-select:none;-webkit-user-select:none;-ms-user-select:none;-webkit-touch-callout:none}.mobile-touch-control{user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-webkit-touch-callout:none;-webkit-tap-highlight-color:transparent;touch-action:manipulation;overscroll-behavior:contain}.mobile-drag-control{touch-action:none}.mobile-text-entry{font-size:16px;-webkit-text-size-adjust:100%;touch-action:manipulation}.\[writing-mode\:vertical-rl\]{writing-mode:vertical-rl}:root{font-family:Inter,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity));--tw-text-opacity: 1;color:rgb(241 245 249 / var(--tw-text-opacity))}body{margin:0;min-height:100vh;--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity));--tw-text-opacity: 1;color:rgb(241 245 249 / var(--tw-text-opacity))}html,body,*{scrollbar-width:thin;scrollbar-color:rgba(148,163,184,.55) transparent}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}*::-webkit-scrollbar{width:2px;height:2px}*::-webkit-scrollbar-track{background:transparent}*::-webkit-scrollbar-thumb{background-color:#94a3b88c;border-radius:9999px}@supports (scrollbar-width: thin){@media(pointer:coarse){html,body,*{scrollbar-width:none}}}.placeholder\:text-slate-400::-moz-placeholder{--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity))}.placeholder\:text-slate-400::placeholder{--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity))}.last\:mb-0:last-child{margin-bottom:0}.hover\:-translate-y-0\.5:hover{--tw-translate-y: -.125rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\:scale-110:hover{--tw-scale-x: 1.1;--tw-scale-y: 1.1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\:border-green-100:hover{--tw-border-opacity: 1;border-color:rgb(220 252 231 / var(--tw-border-opacity))}.hover\:border-rose-500:hover{--tw-border-opacity: 1;border-color:rgb(244 63 94 / var(--tw-border-opacity))}.hover\:border-sky-500:hover{--tw-border-opacity: 1;border-color:rgb(14 165 233 / var(--tw-border-opacity))}.hover\:border-white:hover{--tw-border-opacity: 1;border-color:rgb(255 255 255 / var(--tw-border-opacity))}.hover\:border-white\/60:hover{border-color:#fff9}.hover\:bg-amber-400:hover{--tw-bg-opacity: 1;background-color:rgb(251 191 36 / var(--tw-bg-opacity))}.hover\:bg-amber-600:hover{--tw-bg-opacity: 1;background-color:rgb(217 119 6 / var(--tw-bg-opacity))}.hover\:bg-amber-800:hover{--tw-bg-opacity: 1;background-color:rgb(146 64 14 / var(--tw-bg-opacity))}.hover\:bg-cyan-400:hover{--tw-bg-opacity: 1;background-color:rgb(34 211 238 / var(--tw-bg-opacity))}.hover\:bg-cyan-800:hover{--tw-bg-opacity: 1;background-color:rgb(21 94 117 / var(--tw-bg-opacity))}.hover\:bg-emerald-400:hover{--tw-bg-opacity: 1;background-color:rgb(52 211 153 / var(--tw-bg-opacity))}.hover\:bg-emerald-500:hover{--tw-bg-opacity: 1;background-color:rgb(16 185 129 / var(--tw-bg-opacity))}.hover\:bg-emerald-700:hover{--tw-bg-opacity: 1;background-color:rgb(4 120 87 / var(--tw-bg-opacity))}.hover\:bg-fuchsia-500:hover{--tw-bg-opacity: 1;background-color:rgb(217 70 239 / var(--tw-bg-opacity))}.hover\:bg-green-400:hover{--tw-bg-opacity: 1;background-color:rgb(74 222 128 / var(--tw-bg-opacity))}.hover\:bg-indigo-500:hover{--tw-bg-opacity: 1;background-color:rgb(99 102 241 / var(--tw-bg-opacity))}.hover\:bg-indigo-800:hover{--tw-bg-opacity: 1;background-color:rgb(55 48 163 / var(--tw-bg-opacity))}.hover\:bg-pink-400:hover{--tw-bg-opacity: 1;background-color:rgb(244 114 182 / var(--tw-bg-opacity))}.hover\:bg-red-600:hover{--tw-bg-opacity: 1;background-color:rgb(220 38 38 / var(--tw-bg-opacity))}.hover\:bg-rose-500:hover{--tw-bg-opacity: 1;background-color:rgb(244 63 94 / var(--tw-bg-opacity))}.hover\:bg-sky-500:hover{--tw-bg-opacity: 1;background-color:rgb(14 165 233 / var(--tw-bg-opacity))}.hover\:bg-slate-600:hover{--tw-bg-opacity: 1;background-color:rgb(71 85 105 / var(--tw-bg-opacity))}.hover\:bg-slate-700\/70:hover{background-color:#334155b3}.hover\:bg-zinc-900:hover{--tw-bg-opacity: 1;background-color:rgb(24 24 27 / var(--tw-bg-opacity))}.hover\:text-blue-500:hover{--tw-text-opacity: 1;color:rgb(59 130 246 / var(--tw-text-opacity))}.hover\:text-white:hover{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.hover\:opacity-90:hover{opacity:.9}.hover\:shadow-xl:hover{--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.hover\:brightness-110:hover{--tw-brightness: brightness(1.1);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-emerald-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(16 185 129 / var(--tw-ring-opacity))}.focus-visible\:outline-none:focus-visible{outline:2px solid transparent;outline-offset:2px}.focus-visible\:outline:focus-visible{outline-style:solid}.focus-visible\:outline-1:focus-visible{outline-width:1px}.focus-visible\:outline-offset-1:focus-visible{outline-offset:1px}.focus-visible\:outline-slate-500:focus-visible{outline-color:#64748b}.focus-visible\:ring-2:focus-visible{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus-visible\:ring-amber-300:focus-visible{--tw-ring-opacity: 1;--tw-ring-color: rgb(252 211 77 / var(--tw-ring-opacity))}.focus-visible\:ring-cyan-300:focus-visible{--tw-ring-opacity: 1;--tw-ring-color: rgb(103 232 249 / var(--tw-ring-opacity))}.focus-visible\:ring-emerald-200:focus-visible{--tw-ring-opacity: 1;--tw-ring-color: rgb(167 243 208 / var(--tw-ring-opacity))}.focus-visible\:ring-emerald-300:focus-visible{--tw-ring-opacity: 1;--tw-ring-color: rgb(110 231 183 / var(--tw-ring-opacity))}.focus-visible\:ring-indigo-300:focus-visible{--tw-ring-opacity: 1;--tw-ring-color: rgb(165 180 252 / var(--tw-ring-opacity))}.active\:scale-95:active{--tw-scale-x: .95;--tw-scale-y: .95;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.active\:scale-\[0\.99\]:active{--tw-scale-x: .99;--tw-scale-y: .99;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.active\:brightness-125:active{--tw-brightness: brightness(1.25);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-30:disabled{opacity:.3}.disabled\:opacity-40:disabled{opacity:.4}.disabled\:opacity-50:disabled{opacity:.5}.disabled\:opacity-60:disabled{opacity:.6}.disabled\:opacity-70:disabled{opacity:.7}@media(max-width:520px){.max-\[520px\]\:grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.max-\[520px\]\:justify-start{justify-content:flex-start}}@media(max-width:420px){.max-\[420px\]\:grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.max-\[420px\]\:grid-cols-\[auto_minmax\(0\,1fr\)\]{grid-template-columns:auto minmax(0,1fr)}.max-\[420px\]\:justify-start{justify-content:flex-start}}@media(min-width:640px){.sm\:block{display:block}.sm\:min-h-\[18rem\]{min-height:18rem}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:grid-cols-\[minmax\(0\,0\.8fr\)_minmax\(0\,1\.2fr\)\]{grid-template-columns:minmax(0,.8fr) minmax(0,1.2fr)}.sm\:text-2xl{font-size:1.5rem;line-height:2rem}.sm\:text-3xl{font-size:1.875rem;line-height:2.25rem}}@media(min-width:768px){.md\:h-full{height:100%}.md\:h-screen{height:100vh}.md\:min-h-0{min-height:0px}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-\[0\.9fr_1fr_1\.3fr\]{grid-template-columns:.9fr 1fr 1.3fr}.md\:grid-cols-\[minmax\(0\,1\.4fr\)_minmax\(0\,1fr\)\]{grid-template-columns:minmax(0,1.4fr) minmax(0,1fr)}.md\:grid-cols-\[minmax\(0\,1\.5fr\)_minmax\(0\,1fr\)\]{grid-template-columns:minmax(0,1.5fr) minmax(0,1fr)}.md\:grid-cols-\[minmax\(0\,1fr\)_18rem\]{grid-template-columns:minmax(0,1fr) 18rem}.md\:overflow-hidden{overflow:hidden}.md\:overflow-y-auto{overflow-y:auto}.md\:text-lg{font-size:1.125rem;line-height:1.75rem}}@media(min-width:1024px){.lg\:col-span-2{grid-column:span 2 / span 2}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.lg\:grid-cols-\[minmax\(0\,1\.25fr\)_minmax\(0\,0\.9fr\)\]{grid-template-columns:minmax(0,1.25fr) minmax(0,.9fr)}.lg\:grid-cols-\[minmax\(0\,1fr\)_20rem\]{grid-template-columns:minmax(0,1fr) 20rem}} diff --git a/server/public/assets/index-CBt8gbRK.css b/server/public/assets/index-CBt8gbRK.css deleted file mode 100644 index 6d677b89..00000000 --- a/server/public/assets/index-CBt8gbRK.css +++ /dev/null @@ -1 +0,0 @@ -*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.\!container{width:100%!important}.container{width:100%}@media(min-width:640px){.\!container{max-width:640px!important}.container{max-width:640px}}@media(min-width:768px){.\!container{max-width:768px!important}.container{max-width:768px}}@media(min-width:1024px){.\!container{max-width:1024px!important}.container{max-width:1024px}}@media(min-width:1280px){.\!container{max-width:1280px!important}.container{max-width:1280px}}@media(min-width:1536px){.\!container{max-width:1536px!important}.container{max-width:1536px}}.pride-page-bg{background-color:#050505;background-image:linear-gradient(#0000,#0000),repeating-linear-gradient(45deg,#5bcefa 0 1.818%,#f5a9b8 1.818% 3.636%,#fff 3.636% 5.455%,#f5a9b8 5.455% 7.273%,#5bcefa 7.273% 9.091%,#e40303 9.091% 10.909%,#ff8c00 10.909% 12.727%,#ffed00 12.727% 14.545%,#00a651 14.545% 16.364%,#06f 16.364% 18.182%,#9b2fae 18.182% 20%);background-attachment:fixed}.panel{border-radius:.375rem;--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity));padding:0;--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.panel-section{border-radius:.375rem;--tw-bg-opacity: 1;background-color:rgb(23 23 23 / var(--tw-bg-opacity));padding:0;--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.panel-muted{border-radius:.375rem;font-size:.875rem;line-height:1.25rem;--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity))}.surface{border-radius:.375rem;--tw-bg-opacity: 1;background-color:rgb(38 38 38 / var(--tw-bg-opacity));padding:.125rem;--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.surface-muted{border-radius:.375rem;--tw-bg-opacity: 1;background-color:rgb(64 64 64 / var(--tw-bg-opacity));padding:.125rem;--tw-text-opacity: 1;color:rgb(245 245 245 / var(--tw-text-opacity))}.field-input{border-radius:.375rem;border-width:1px;--tw-border-opacity: 1;border-color:rgb(82 82 82 / var(--tw-border-opacity));--tw-bg-opacity: 1;background-color:rgb(64 64 64 / var(--tw-bg-opacity));padding:.125rem;--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.field-input::-moz-placeholder{--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity))}.field-input::placeholder{--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity))}.field-input:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000);--tw-ring-opacity: 1;--tw-ring-color: rgb(14 165 233 / var(--tw-ring-opacity))}.button-dark{border-radius:.375rem;border-width:1px;--tw-border-opacity: 1;border-color:rgb(125 211 252 / var(--tw-border-opacity));--tw-bg-opacity: 1;background-color:rgb(2 132 199 / var(--tw-bg-opacity));padding:.125rem;font-size:.875rem;line-height:1.25rem;font-weight:500;--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.button-dark:hover{--tw-border-opacity: 1;border-color:rgb(14 165 233 / var(--tw-border-opacity));--tw-bg-opacity: 1;background-color:rgb(14 165 233 / var(--tw-bg-opacity))}.button-danger{border-radius:.375rem;--tw-bg-opacity: 1;background-color:rgb(225 29 72 / var(--tw-bg-opacity));padding:.125rem;font-size:.875rem;line-height:1.25rem;font-weight:500;--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.button-danger:hover{--tw-bg-opacity: 1;background-color:rgb(244 63 94 / var(--tw-bg-opacity))}.chat-composer{container-type:inline-size;display:flex;flex-wrap:wrap;align-items:stretch;gap:.125rem;min-width:0;overflow:hidden}.chat-composer-nickname{flex:0 0 5rem;min-width:0;order:1}.chat-composer-input{flex:1 1 0%;min-width:0;order:2}.chat-composer-send{flex:0 0 auto;align-self:stretch;order:3}.chat-composer-tts{display:flex;flex:1 1 100%;align-items:center;gap:.125rem;min-width:0;overflow:hidden;order:4}@container (min-width: 44rem){.chat-composer{flex-wrap:nowrap}.chat-composer-nickname{flex-basis:7rem;order:1}.chat-composer-input{order:2}.chat-composer-tts{flex:0 1 auto;order:3}.chat-composer-send{order:4}}.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.\!visible{visibility:visible!important}.visible{visibility:visible}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.inset-x-0{left:0;right:0}.inset-x-1{left:.25rem;right:.25rem}.bottom-0{bottom:0}.bottom-0\.5{bottom:.125rem}.bottom-1{bottom:.25rem}.bottom-2{bottom:.5rem}.bottom-\[calc\(100\%\+0\.125rem\)\]{bottom:calc(100% + .125rem)}.left-0{left:0}.left-0\.5{left:.125rem}.left-1{left:.25rem}.left-1\/2{left:50%}.left-2{left:.5rem}.right-0{right:0}.right-0\.5{right:.125rem}.right-1{right:.25rem}.right-2{right:.5rem}.top-0{top:0}.top-0\.5{top:.125rem}.top-1{top:.25rem}.top-1\/2{top:50%}.top-10{top:2.5rem}.top-6{top:1.5rem}.z-10{z-index:10}.z-20{z-index:20}.z-30{z-index:30}.z-40{z-index:40}.z-50{z-index:50}.z-\[1000\]{z-index:1000}.z-\[110\]{z-index:110}.z-\[120\]{z-index:120}.z-\[130\]{z-index:130}.z-\[70\]{z-index:70}.order-1{order:1}.order-2{order:2}.col-span-full{grid-column:1 / -1}.m-0{margin:0}.-mx-0\.5{margin-left:-.125rem;margin-right:-.125rem}.mx-auto{margin-left:auto;margin-right:auto}.my-\[-0\.125rem\]{margin-top:-.125rem;margin-bottom:-.125rem}.-mt-0\.5{margin-top:-.125rem}.mb-0\.5{margin-bottom:.125rem}.ml-1{margin-left:.25rem}.mr-0{margin-right:0}.mt-0{margin-top:0}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-\[0\.35vh\]{margin-top:.35vh}.mt-auto{margin-top:auto}.line-clamp-2{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}.\!block{display:block!important}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.grid{display:grid}.hidden{display:none}.aspect-\[4\/3\]{aspect-ratio:4/3}.aspect-square{aspect-ratio:1 / 1}.aspect-video{aspect-ratio:16 / 9}.h-1{height:.25rem}.h-1\.5{height:.375rem}.h-10{height:2.5rem}.h-2{height:.5rem}.h-28{height:7rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-4{height:1rem}.h-40{height:10rem}.h-48{height:12rem}.h-5{height:1.25rem}.h-64{height:16rem}.h-\[2px\]{height:2px}.h-\[3\.5rem\]{height:3.5rem}.h-\[70\%\]{height:70%}.h-\[7rem\]{height:7rem}.h-\[8vh\]{height:8vh}.h-\[calc\(1rem\+0\.25rem\)\]{height:1.25rem}.h-\[min\(100svh\,32rem\)\]{height:min(100svh,32rem)}.h-\[min\(60svh\,24rem\)\]{height:min(60svh,24rem)}.h-full{height:100%}.h-px{height:1px}.h-screen{height:100vh}.max-h-32{max-height:8rem}.max-h-40{max-height:10rem}.max-h-52{max-height:13rem}.max-h-72{max-height:18rem}.max-h-80{max-height:20rem}.max-h-\[14rem\]{max-height:14rem}.max-h-\[70vh\]{max-height:70vh}.max-h-\[72vh\]{max-height:72vh}.max-h-\[85vh\]{max-height:85vh}.max-h-\[90vh\]{max-height:90vh}.max-h-screen{max-height:100vh}.min-h-0{min-height:0px}.min-h-5{min-height:1.25rem}.min-h-9{min-height:2.25rem}.min-h-\[10rem\]{min-height:10rem}.min-h-\[14rem\]{min-height:14rem}.min-h-\[16rem\]{min-height:16rem}.min-h-\[3\.5rem\]{min-height:3.5rem}.min-h-\[4\.25rem\]{min-height:4.25rem}.min-h-\[4rem\]{min-height:4rem}.min-h-\[5rem\]{min-height:5rem}.min-h-screen{min-height:100vh}.w-1{width:.25rem}.w-1\.5{width:.375rem}.w-1\/2{width:50%}.w-10{width:2.5rem}.w-2{width:.5rem}.w-20{width:5rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-\[12\.5rem\]{width:12.5rem}.w-\[12rem\]{width:12rem}.w-\[20rem\]{width:20rem}.w-\[2px\]{width:2px}.w-\[3\.25rem\]{width:3.25rem}.w-\[4\.5rem\]{width:4.5rem}.w-\[5\.5rem\]{width:5.5rem}.w-\[5rem\]{width:5rem}.w-\[9rem\]{width:9rem}.w-\[calc\(1rem\+0\.25rem\)\]{width:1.25rem}.w-\[min\(20rem\,calc\(100vw-1rem\)\)\]{width:min(20rem,calc(100vw - 1rem))}.w-auto{width:auto}.w-fit{width:-moz-fit-content;width:fit-content}.w-full{width:100%}.w-max{width:-moz-max-content;width:max-content}.w-px{width:1px}.w-screen{width:100vw}.min-w-0{min-width:0px}.min-w-28{min-width:7rem}.min-w-\[0\]{min-width:0}.min-w-\[3\.7rem\]{min-width:3.7rem}.min-w-\[9\.5rem\]{min-width:9.5rem}.max-w-2xl{max-width:42rem}.max-w-3xl{max-width:48rem}.max-w-4xl{max-width:56rem}.max-w-\[40vw\]{max-width:40vw}.max-w-\[70vw\]{max-width:70vw}.max-w-\[80vw\]{max-width:80vw}.max-w-\[92\%\]{max-width:92%}.max-w-\[92vw\]{max-width:92vw}.max-w-\[94\%\]{max-width:94%}.max-w-full{max-width:100%}.max-w-lg{max-width:32rem}.max-w-md{max-width:28rem}.max-w-sm{max-width:24rem}.max-w-xl{max-width:36rem}.flex-1{flex:1 1 0%}.flex-\[0\.72\]{flex:.72}.flex-\[1\.1\]{flex:1.1}.flex-\[1\.22\]{flex:1.22}.flex-\[1\.28\]{flex:1.28}.shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.origin-top-left{transform-origin:top left}.-translate-x-1\/2{--tw-translate-x: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-y-1\/2{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-180{--tw-rotate: 180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.cursor-default{cursor:default}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.resize{resize:both}.auto-rows-fr{grid-auto-rows:minmax(0,1fr)}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.grid-cols-\[1\.25rem_minmax\(0\,1fr\)_auto\]{grid-template-columns:1.25rem minmax(0,1fr) auto}.grid-cols-\[auto_minmax\(0\,1fr\)\]{grid-template-columns:auto minmax(0,1fr)}.grid-cols-\[minmax\(0\,0\.7fr\)_minmax\(0\,2\.1fr\)_minmax\(0\,0\.7fr\)\]{grid-template-columns:minmax(0,.7fr) minmax(0,2.1fr) minmax(0,.7fr)}.grid-cols-\[minmax\(0\,1\.2fr\)_repeat\(4\,minmax\(0\,1fr\)\)\]{grid-template-columns:minmax(0,1.2fr) repeat(4,minmax(0,1fr))}.grid-cols-\[minmax\(0\,1\.35fr\)_minmax\(0\,0\.95fr\)\]{grid-template-columns:minmax(0,1.35fr) minmax(0,.95fr)}.grid-cols-\[minmax\(0\,1\.3fr\)_minmax\(0\,0\.22fr\)\]{grid-template-columns:minmax(0,1.3fr) minmax(0,.22fr)}.grid-cols-\[minmax\(0\,1\.45fr\)_minmax\(0\,0\.85fr\)\]{grid-template-columns:minmax(0,1.45fr) minmax(0,.85fr)}.grid-cols-\[minmax\(0\,1fr\)_4\.75rem\]{grid-template-columns:minmax(0,1fr) 4.75rem}.grid-cols-\[minmax\(0\,1fr\)_auto\]{grid-template-columns:minmax(0,1fr) auto}.grid-cols-\[minmax\(0\,1fr\)_minmax\(0\,0\.7fr\)_minmax\(0\,1\.9fr\)_minmax\(0\,0\.7fr\)\]{grid-template-columns:minmax(0,1fr) minmax(0,.7fr) minmax(0,1.9fr) minmax(0,.7fr)}.grid-cols-\[minmax\(0\,1fr\)_minmax\(0\,0\.9fr\)_minmax\(0\,0\.75fr\)\]{grid-template-columns:minmax(0,1fr) minmax(0,.9fr) minmax(0,.75fr)}.grid-cols-\[minmax\(0\,1fr\)_minmax\(0\,1\.4fr\)\]{grid-template-columns:minmax(0,1fr) minmax(0,1.4fr)}.grid-cols-\[minmax\(0\,1fr\)_minmax\(0\,1fr\)\]{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}.grid-rows-2{grid-template-rows:repeat(2,minmax(0,1fr))}.grid-rows-3{grid-template-rows:repeat(3,minmax(0,1fr))}.grid-rows-\[auto_1fr\]{grid-template-rows:auto 1fr}.grid-rows-\[auto_auto_1fr\]{grid-template-rows:auto auto 1fr}.grid-rows-\[auto_auto_1fr_auto\]{grid-template-rows:auto auto 1fr auto}.grid-rows-\[auto_minmax\(0\,1fr\)\]{grid-template-rows:auto minmax(0,1fr)}.grid-rows-\[auto_minmax\(0\,1fr\)_auto\]{grid-template-rows:auto minmax(0,1fr) auto}.grid-rows-\[minmax\(0\,1fr\)_auto\]{grid-template-rows:minmax(0,1fr) auto}.grid-rows-\[minmax\(0\,1fr\)_minmax\(0\,1fr\)_minmax\(0\,1fr\)\]{grid-template-rows:minmax(0,1fr) minmax(0,1fr) minmax(0,1fr)}.\!flex-row{flex-direction:row!important}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.place-items-center{place-items:center}.content-center{align-content:center}.content-start{align-content:flex-start}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.items-baseline{align-items:baseline}.items-stretch{align-items:stretch}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.justify-stretch{justify-content:stretch}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-4{gap:1rem}.gap-\[0\.55vh\]{gap:.55vh}.gap-\[0\.8vw\]{gap:.8vw}.gap-\[1vw\]{gap:1vw}.gap-\[4vh\]{gap:4vh}.gap-x-3{-moz-column-gap:.75rem;column-gap:.75rem}.space-y-0\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.125rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.125rem * var(--tw-space-y-reverse))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.self-start{align-self:flex-start}.self-stretch{align-self:stretch}.overflow-hidden{overflow:hidden}.overflow-visible{overflow:visible}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-none{border-radius:0}.rounded-sm{border-radius:.125rem}.rounded-xl{border-radius:.75rem}.rounded-t{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.border{border-width:1px}.border-2{border-width:2px}.border-4{border-width:4px}.border-b{border-bottom-width:1px}.border-l{border-left-width:1px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-amber-200{--tw-border-opacity: 1;border-color:rgb(253 230 138 / var(--tw-border-opacity))}.border-amber-200\/30{border-color:#fde68a4d}.border-amber-200\/70{border-color:#fde68ab3}.border-amber-200\/90{border-color:#fde68ae6}.border-amber-300\/70{border-color:#fcd34db3}.border-amber-300\/80{border-color:#fcd34dcc}.border-amber-400\/30{border-color:#fbbf244d}.border-amber-400\/60{border-color:#fbbf2499}.border-amber-600\/60{border-color:#d9770699}.border-amber-700\/60{border-color:#b4530999}.border-blue-600{--tw-border-opacity: 1;border-color:rgb(37 99 235 / var(--tw-border-opacity))}.border-cyan-200{--tw-border-opacity: 1;border-color:rgb(165 243 252 / var(--tw-border-opacity))}.border-cyan-200\/90{border-color:#a5f3fce6}.border-cyan-300{--tw-border-opacity: 1;border-color:rgb(103 232 249 / var(--tw-border-opacity))}.border-cyan-300\/70{border-color:#67e8f9b3}.border-cyan-300\/80{border-color:#67e8f9cc}.border-cyan-500\/40{border-color:#06b6d466}.border-emerald-100\/80{border-color:#d1fae5cc}.border-emerald-200{--tw-border-opacity: 1;border-color:rgb(167 243 208 / var(--tw-border-opacity))}.border-emerald-200\/70{border-color:#a7f3d0b3}.border-emerald-200\/90{border-color:#a7f3d0e6}.border-emerald-300{--tw-border-opacity: 1;border-color:rgb(110 231 183 / var(--tw-border-opacity))}.border-emerald-300\/70{border-color:#6ee7b7b3}.border-emerald-400\/60{border-color:#34d39999}.border-emerald-500\/40{border-color:#10b98166}.border-emerald-500\/70{border-color:#10b981b3}.border-emerald-700\/60{border-color:#04785799}.border-emerald-700\/70{border-color:#047857b3}.border-emerald-950{--tw-border-opacity: 1;border-color:rgb(2 44 34 / var(--tw-border-opacity))}.border-fuchsia-300\/70{border-color:#f0abfcb3}.border-green-400{--tw-border-opacity: 1;border-color:rgb(74 222 128 / var(--tw-border-opacity))}.border-indigo-200\/70{border-color:#c7d2feb3}.border-indigo-200\/95{border-color:#c7d2fef2}.border-indigo-300\/70{border-color:#a5b4fcb3}.border-indigo-400\/30{border-color:#818cf84d}.border-indigo-700{--tw-border-opacity: 1;border-color:rgb(67 56 202 / var(--tw-border-opacity))}.border-indigo-700\/60{border-color:#4338ca99}.border-neutral-500\/50{border-color:#73737380}.border-neutral-500\/60{border-color:#73737399}.border-neutral-600{--tw-border-opacity: 1;border-color:rgb(82 82 82 / var(--tw-border-opacity))}.border-neutral-700{--tw-border-opacity: 1;border-color:rgb(64 64 64 / var(--tw-border-opacity))}.border-pink-200{--tw-border-opacity: 1;border-color:rgb(251 207 232 / var(--tw-border-opacity))}.border-red-100{--tw-border-opacity: 1;border-color:rgb(254 226 226 / var(--tw-border-opacity))}.border-red-200{--tw-border-opacity: 1;border-color:rgb(254 202 202 / var(--tw-border-opacity))}.border-red-300\/90{border-color:#fca5a5e6}.border-red-400{--tw-border-opacity: 1;border-color:rgb(248 113 113 / var(--tw-border-opacity))}.border-red-400\/60{border-color:#f8717199}.border-rose-300{--tw-border-opacity: 1;border-color:rgb(253 164 175 / var(--tw-border-opacity))}.border-sky-300{--tw-border-opacity: 1;border-color:rgb(125 211 252 / var(--tw-border-opacity))}.border-sky-400\/50{border-color:#38bdf880}.border-slate-200\/30{border-color:#e2e8f04d}.border-slate-400{--tw-border-opacity: 1;border-color:rgb(148 163 184 / var(--tw-border-opacity))}.border-slate-400\/60{border-color:#94a3b899}.border-slate-500\/70{border-color:#64748bb3}.border-slate-600{--tw-border-opacity: 1;border-color:rgb(71 85 105 / var(--tw-border-opacity))}.border-slate-600\/40{border-color:#47556966}.border-slate-600\/60{border-color:#47556999}.border-slate-600\/70{border-color:#475569b3}.border-slate-600\/80{border-color:#475569cc}.border-slate-700{--tw-border-opacity: 1;border-color:rgb(51 65 85 / var(--tw-border-opacity))}.border-slate-700\/60{border-color:#33415599}.border-slate-700\/70{border-color:#334155b3}.border-slate-700\/80{border-color:#334155cc}.border-slate-800{--tw-border-opacity: 1;border-color:rgb(30 41 59 / var(--tw-border-opacity))}.border-slate-800\/60{border-color:#1e293b99}.border-slate-800\/80{border-color:#1e293bcc}.border-transparent{border-color:transparent}.border-white{--tw-border-opacity: 1;border-color:rgb(255 255 255 / var(--tw-border-opacity))}.border-white\/40{border-color:#fff6}.border-white\/80{border-color:#fffc}.bg-amber-100\/95{background-color:#fef3c7f2}.bg-amber-400{--tw-bg-opacity: 1;background-color:rgb(251 191 36 / var(--tw-bg-opacity))}.bg-amber-400\/80{background-color:#fbbf24cc}.bg-amber-500{--tw-bg-opacity: 1;background-color:rgb(245 158 11 / var(--tw-bg-opacity))}.bg-amber-500\/30{background-color:#f59e0b4d}.bg-amber-600{--tw-bg-opacity: 1;background-color:rgb(217 119 6 / var(--tw-bg-opacity))}.bg-amber-600\/70{background-color:#d97706b3}.bg-amber-700\/20{background-color:#b4530933}.bg-amber-700\/30{background-color:#b453094d}.bg-amber-700\/35{background-color:#b4530959}.bg-amber-700\/80{background-color:#b45309cc}.bg-amber-800{--tw-bg-opacity: 1;background-color:rgb(146 64 14 / var(--tw-bg-opacity))}.bg-amber-900{--tw-bg-opacity: 1;background-color:rgb(120 53 15 / var(--tw-bg-opacity))}.bg-amber-900\/40{background-color:#78350f66}.bg-amber-900\/60{background-color:#78350f99}.bg-amber-900\/90{background-color:#78350fe6}.bg-amber-950\/70{background-color:#451a03b3}.bg-black{--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity))}.bg-black\/20{background-color:#0003}.bg-black\/30{background-color:#0000004d}.bg-black\/40{background-color:#0006}.bg-black\/45{background-color:#00000073}.bg-black\/50{background-color:#00000080}.bg-black\/55{background-color:#0000008c}.bg-black\/60{background-color:#0009}.bg-black\/70{background-color:#000000b3}.bg-black\/75{background-color:#000000bf}.bg-black\/80{background-color:#000c}.bg-black\/95{background-color:#000000f2}.bg-blue-500{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity))}.bg-cyan-300{--tw-bg-opacity: 1;background-color:rgb(103 232 249 / var(--tw-bg-opacity))}.bg-cyan-500{--tw-bg-opacity: 1;background-color:rgb(6 182 212 / var(--tw-bg-opacity))}.bg-cyan-500\/80{background-color:#06b6d4cc}.bg-cyan-900{--tw-bg-opacity: 1;background-color:rgb(22 78 99 / var(--tw-bg-opacity))}.bg-cyan-900\/45{background-color:#164e6373}.bg-cyan-900\/90{background-color:#164e63e6}.bg-emerald-200{--tw-bg-opacity: 1;background-color:rgb(167 243 208 / var(--tw-bg-opacity))}.bg-emerald-400{--tw-bg-opacity: 1;background-color:rgb(52 211 153 / var(--tw-bg-opacity))}.bg-emerald-500{--tw-bg-opacity: 1;background-color:rgb(16 185 129 / var(--tw-bg-opacity))}.bg-emerald-500\/45{background-color:#10b98173}.bg-emerald-500\/80{background-color:#10b981cc}.bg-emerald-600{--tw-bg-opacity: 1;background-color:rgb(5 150 105 / var(--tw-bg-opacity))}.bg-emerald-600\/70{background-color:#059669b3}.bg-emerald-600\/80{background-color:#059669cc}.bg-emerald-700{--tw-bg-opacity: 1;background-color:rgb(4 120 87 / var(--tw-bg-opacity))}.bg-emerald-700\/20{background-color:#04785733}.bg-emerald-700\/60{background-color:#04785799}.bg-emerald-800{--tw-bg-opacity: 1;background-color:rgb(6 95 70 / var(--tw-bg-opacity))}.bg-emerald-900{--tw-bg-opacity: 1;background-color:rgb(6 78 59 / var(--tw-bg-opacity))}.bg-emerald-900\/60{background-color:#064e3b99}.bg-emerald-900\/90{background-color:#064e3be6}.bg-emerald-950{--tw-bg-opacity: 1;background-color:rgb(2 44 34 / var(--tw-bg-opacity))}.bg-emerald-950\/50{background-color:#022c2280}.bg-fuchsia-200{--tw-bg-opacity: 1;background-color:rgb(245 208 254 / var(--tw-bg-opacity))}.bg-fuchsia-600{--tw-bg-opacity: 1;background-color:rgb(192 38 211 / var(--tw-bg-opacity))}.bg-fuchsia-700{--tw-bg-opacity: 1;background-color:rgb(162 28 175 / var(--tw-bg-opacity))}.bg-fuchsia-800{--tw-bg-opacity: 1;background-color:rgb(134 25 143 / var(--tw-bg-opacity))}.bg-fuchsia-900\/45{background-color:#701a7573}.bg-green-600{--tw-bg-opacity: 1;background-color:rgb(22 163 74 / var(--tw-bg-opacity))}.bg-indigo-600{--tw-bg-opacity: 1;background-color:rgb(79 70 229 / var(--tw-bg-opacity))}.bg-indigo-600\/70{background-color:#4f46e5b3}.bg-indigo-900{--tw-bg-opacity: 1;background-color:rgb(49 46 129 / var(--tw-bg-opacity))}.bg-indigo-900\/60{background-color:#312e8199}.bg-indigo-950{--tw-bg-opacity: 1;background-color:rgb(30 27 75 / var(--tw-bg-opacity))}.bg-indigo-950\/90{background-color:#1e1b4be6}.bg-neutral-700{--tw-bg-opacity: 1;background-color:rgb(64 64 64 / var(--tw-bg-opacity))}.bg-neutral-800{--tw-bg-opacity: 1;background-color:rgb(38 38 38 / var(--tw-bg-opacity))}.bg-neutral-800\/80{background-color:#262626cc}.bg-neutral-900{--tw-bg-opacity: 1;background-color:rgb(23 23 23 / var(--tw-bg-opacity))}.bg-neutral-900\/100{background-color:#171717}.bg-neutral-900\/70{background-color:#171717b3}.bg-neutral-900\/95{background-color:#171717f2}.bg-neutral-950{--tw-bg-opacity: 1;background-color:rgb(10 10 10 / var(--tw-bg-opacity))}.bg-pink-500{--tw-bg-opacity: 1;background-color:rgb(236 72 153 / var(--tw-bg-opacity))}.bg-red-500{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity))}.bg-red-500\/45{background-color:#ef444473}.bg-red-600{--tw-bg-opacity: 1;background-color:rgb(220 38 38 / var(--tw-bg-opacity))}.bg-red-600\/70{background-color:#dc2626b3}.bg-red-700{--tw-bg-opacity: 1;background-color:rgb(185 28 28 / var(--tw-bg-opacity))}.bg-red-700\/20{background-color:#b91c1c33}.bg-red-700\/60{background-color:#b91c1c99}.bg-red-800{--tw-bg-opacity: 1;background-color:rgb(153 27 27 / var(--tw-bg-opacity))}.bg-red-900\/35{background-color:#7f1d1d59}.bg-red-900\/40{background-color:#7f1d1d66}.bg-red-900\/45{background-color:#7f1d1d73}.bg-red-900\/50{background-color:#7f1d1d80}.bg-red-900\/80{background-color:#7f1d1dcc}.bg-rose-500{--tw-bg-opacity: 1;background-color:rgb(244 63 94 / var(--tw-bg-opacity))}.bg-rose-600{--tw-bg-opacity: 1;background-color:rgb(225 29 72 / var(--tw-bg-opacity))}.bg-sky-600{--tw-bg-opacity: 1;background-color:rgb(2 132 199 / var(--tw-bg-opacity))}.bg-sky-700\/20{background-color:#0369a133}.bg-slate-300{--tw-bg-opacity: 1;background-color:rgb(203 213 225 / var(--tw-bg-opacity))}.bg-slate-500{--tw-bg-opacity: 1;background-color:rgb(100 116 139 / var(--tw-bg-opacity))}.bg-slate-600{--tw-bg-opacity: 1;background-color:rgb(71 85 105 / var(--tw-bg-opacity))}.bg-slate-600\/70{background-color:#475569b3}.bg-slate-700{--tw-bg-opacity: 1;background-color:rgb(51 65 85 / var(--tw-bg-opacity))}.bg-slate-700\/30{background-color:#3341554d}.bg-slate-700\/60{background-color:#33415599}.bg-slate-700\/70{background-color:#334155b3}.bg-slate-700\/80{background-color:#334155cc}.bg-slate-800{--tw-bg-opacity: 1;background-color:rgb(30 41 59 / var(--tw-bg-opacity))}.bg-slate-800\/60{background-color:#1e293b99}.bg-slate-800\/70{background-color:#1e293bb3}.bg-slate-800\/80{background-color:#1e293bcc}.bg-slate-900{--tw-bg-opacity: 1;background-color:rgb(15 23 42 / var(--tw-bg-opacity))}.bg-slate-900\/35{background-color:#0f172a59}.bg-slate-900\/40{background-color:#0f172a66}.bg-slate-900\/70{background-color:#0f172ab3}.bg-slate-950{--tw-bg-opacity: 1;background-color:rgb(2 6 23 / var(--tw-bg-opacity))}.bg-slate-950\/80{background-color:#020617cc}.bg-slate-950\/90{background-color:#020617e6}.bg-transparent{background-color:transparent}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity))}.bg-white\/10{background-color:#ffffff1a}.bg-yellow-100{--tw-bg-opacity: 1;background-color:rgb(254 249 195 / var(--tw-bg-opacity))}.bg-yellow-500{--tw-bg-opacity: 1;background-color:rgb(234 179 8 / var(--tw-bg-opacity))}.bg-zinc-900{--tw-bg-opacity: 1;background-color:rgb(24 24 27 / var(--tw-bg-opacity))}.bg-zinc-900\/90{background-color:#18181be6}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.from-lime-800{--tw-gradient-from: #3f6212 var(--tw-gradient-from-position);--tw-gradient-to: rgb(63 98 18 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.from-neutral-800{--tw-gradient-from: #262626 var(--tw-gradient-from-position);--tw-gradient-to: rgb(38 38 38 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.via-neutral-700{--tw-gradient-to: rgb(64 64 64 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), #404040 var(--tw-gradient-via-position), var(--tw-gradient-to)}.to-neutral-600{--tw-gradient-to: #525252 var(--tw-gradient-to-position)}.to-teal-800{--tw-gradient-to: #115e59 var(--tw-gradient-to-position)}.bg-cover{background-size:cover}.bg-center{background-position:center}.bg-no-repeat{background-repeat:no-repeat}.fill-slate-200{fill:#e2e8f0}.object-contain{-o-object-fit:contain;object-fit:contain}.object-cover{-o-object-fit:cover;object-fit:cover}.p-0{padding:0}.p-0\.5{padding:.125rem}.p-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-2{padding:.5rem}.p-4{padding:1rem}.p-\[0\.85vw\]{padding:.85vw}.px-0{padding-left:0;padding-right:0}.px-0\.5{padding-left:.125rem;padding-right:.125rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-\[1\.6vw\]{padding-left:1.6vw;padding-right:1.6vw}.px-\[1vw\]{padding-left:1vw;padding-right:1vw}.px-\[3vw\]{padding-left:3vw;padding-right:3vw}.px-\[4vw\]{padding-left:4vw;padding-right:4vw}.py-0{padding-top:0;padding-bottom:0}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-\[0\.55vh\]{padding-top:.55vh;padding-bottom:.55vh}.py-\[1px\]{padding-top:1px;padding-bottom:1px}.py-\[2\.5vh\]{padding-top:2.5vh;padding-bottom:2.5vh}.py-\[2px\]{padding-top:2px;padding-bottom:2px}.py-\[4vh\]{padding-top:4vh;padding-bottom:4vh}.pb-0{padding-bottom:0}.pb-1{padding-bottom:.25rem}.pb-3{padding-bottom:.75rem}.pr-0{padding-right:0}.pt-1{padding-top:.25rem}.pt-1\.5{padding-top:.375rem}.pt-5{padding-top:1.25rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.align-top{vertical-align:top}.align-middle{vertical-align:middle}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-4xl{font-size:2.25rem;line-height:2.5rem}.text-5xl{font-size:3rem;line-height:1}.text-\[0\.45rem\]{font-size:.45rem}.text-\[0\.55em\]{font-size:.55em}.text-\[0\.55rem\]{font-size:.55rem}.text-\[0\.58rem\]{font-size:.58rem}.text-\[0\.5rem\]{font-size:.5rem}.text-\[0\.62rem\]{font-size:.62rem}.text-\[0\.65rem\]{font-size:.65rem}.text-\[0\.68rem\]{font-size:.68rem}.text-\[0\.6rem\]{font-size:.6rem}.text-\[0\.72rem\]{font-size:.72rem}.text-\[0\.75rem\]{font-size:.75rem}.text-\[0\.78rem\]{font-size:.78rem}.text-\[0\.7rem\]{font-size:.7rem}.text-\[0\.82rem\]{font-size:.82rem}.text-\[0\.85rem\]{font-size:.85rem}.text-\[0\.8rem\]{font-size:.8rem}.text-\[1\.1rem\]{font-size:1.1rem}.text-\[1\.5rem\]{font-size:1.5rem}.text-\[15vw\]{font-size:15vw}.text-\[1rem\]{font-size:1rem}.text-\[5vw\]{font-size:5vw}.text-\[6vw\]{font-size:6vw}.text-\[clamp\(1\.25rem\,2\.5vh\,2\.6rem\)\]{font-size:clamp(1.25rem,2.5vh,2.6rem)}.text-\[clamp\(1\.2rem\,2\.9vh\,3rem\)\]{font-size:clamp(1.2rem,2.9vh,3rem)}.text-\[clamp\(2\.5rem\,5\.3vh\,5\.8rem\)\]{font-size:clamp(2.5rem,5.3vh,5.8rem)}.text-\[clamp\(2\.5rem\,7vh\,7rem\)\]{font-size:clamp(2.5rem,7vh,7rem)}.text-\[clamp\(2\.5rem\,8vh\,8rem\)\]{font-size:clamp(2.5rem,8vh,8rem)}.text-\[clamp\(2rem\,5vh\,5\.2rem\)\]{font-size:clamp(2rem,5vh,5.2rem)}.text-\[clamp\(3\.2rem\,11vh\,11rem\)\]{font-size:clamp(3.2rem,11vh,11rem)}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-black{font-weight:900}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.capitalize{text-transform:capitalize}.italic{font-style:italic}.leading-\[0\.95\]{line-height:.95}.leading-none{line-height:1}.leading-snug{line-height:1.375}.leading-tight{line-height:1.25}.tracking-normal{letter-spacing:0em}.tracking-tight{letter-spacing:-.025em}.tracking-wide{letter-spacing:.025em}.text-amber-100{--tw-text-opacity: 1;color:rgb(254 243 199 / var(--tw-text-opacity))}.text-amber-200{--tw-text-opacity: 1;color:rgb(253 230 138 / var(--tw-text-opacity))}.text-amber-200\/80{color:#fde68acc}.text-amber-200\/85{color:#fde68ad9}.text-amber-300{--tw-text-opacity: 1;color:rgb(252 211 77 / var(--tw-text-opacity))}.text-amber-400{--tw-text-opacity: 1;color:rgb(251 191 36 / var(--tw-text-opacity))}.text-amber-50{--tw-text-opacity: 1;color:rgb(255 251 235 / var(--tw-text-opacity))}.text-amber-50\/90{color:#fffbebe6}.text-amber-950{--tw-text-opacity: 1;color:rgb(69 26 3 / var(--tw-text-opacity))}.text-black{--tw-text-opacity: 1;color:rgb(0 0 0 / var(--tw-text-opacity))}.text-blue-300{--tw-text-opacity: 1;color:rgb(147 197 253 / var(--tw-text-opacity))}.text-blue-400{--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity))}.text-blue-800{--tw-text-opacity: 1;color:rgb(30 64 175 / var(--tw-text-opacity))}.text-cyan-200{--tw-text-opacity: 1;color:rgb(165 243 252 / var(--tw-text-opacity))}.text-cyan-300{--tw-text-opacity: 1;color:rgb(103 232 249 / var(--tw-text-opacity))}.text-cyan-50{--tw-text-opacity: 1;color:rgb(236 254 255 / var(--tw-text-opacity))}.text-emerald-100{--tw-text-opacity: 1;color:rgb(209 250 229 / var(--tw-text-opacity))}.text-emerald-100\/80{color:#d1fae5cc}.text-emerald-200{--tw-text-opacity: 1;color:rgb(167 243 208 / var(--tw-text-opacity))}.text-emerald-300{--tw-text-opacity: 1;color:rgb(110 231 183 / var(--tw-text-opacity))}.text-emerald-400{--tw-text-opacity: 1;color:rgb(52 211 153 / var(--tw-text-opacity))}.text-emerald-50{--tw-text-opacity: 1;color:rgb(236 253 245 / var(--tw-text-opacity))}.text-emerald-50\/90{color:#ecfdf5e6}.text-emerald-950{--tw-text-opacity: 1;color:rgb(2 44 34 / var(--tw-text-opacity))}.text-fuchsia-50{--tw-text-opacity: 1;color:rgb(253 244 255 / var(--tw-text-opacity))}.text-fuchsia-900{--tw-text-opacity: 1;color:rgb(112 26 117 / var(--tw-text-opacity))}.text-indigo-200{--tw-text-opacity: 1;color:rgb(199 210 254 / var(--tw-text-opacity))}.text-indigo-50{--tw-text-opacity: 1;color:rgb(238 242 255 / var(--tw-text-opacity))}.text-indigo-50\/90{color:#eef2ffe6}.text-lime-300{--tw-text-opacity: 1;color:rgb(190 242 100 / var(--tw-text-opacity))}.text-lime-400{--tw-text-opacity: 1;color:rgb(163 230 53 / var(--tw-text-opacity))}.text-neutral-100{--tw-text-opacity: 1;color:rgb(245 245 245 / var(--tw-text-opacity))}.text-neutral-200{--tw-text-opacity: 1;color:rgb(229 229 229 / var(--tw-text-opacity))}.text-neutral-300{--tw-text-opacity: 1;color:rgb(212 212 212 / var(--tw-text-opacity))}.text-neutral-400{--tw-text-opacity: 1;color:rgb(163 163 163 / var(--tw-text-opacity))}.text-neutral-50{--tw-text-opacity: 1;color:rgb(250 250 250 / var(--tw-text-opacity))}.text-neutral-500{--tw-text-opacity: 1;color:rgb(115 115 115 / var(--tw-text-opacity))}.text-red-100{--tw-text-opacity: 1;color:rgb(254 226 226 / var(--tw-text-opacity))}.text-red-100\/90{color:#fee2e2e6}.text-red-100\/95{color:#fee2e2f2}.text-red-200{--tw-text-opacity: 1;color:rgb(254 202 202 / var(--tw-text-opacity))}.text-red-300{--tw-text-opacity: 1;color:rgb(252 165 165 / var(--tw-text-opacity))}.text-red-400{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity))}.text-red-50{--tw-text-opacity: 1;color:rgb(254 242 242 / var(--tw-text-opacity))}.text-red-700{--tw-text-opacity: 1;color:rgb(185 28 28 / var(--tw-text-opacity))}.text-rose-300{--tw-text-opacity: 1;color:rgb(253 164 175 / var(--tw-text-opacity))}.text-sky-200{--tw-text-opacity: 1;color:rgb(186 230 253 / var(--tw-text-opacity))}.text-sky-300{--tw-text-opacity: 1;color:rgb(125 211 252 / var(--tw-text-opacity))}.text-slate-100{--tw-text-opacity: 1;color:rgb(241 245 249 / var(--tw-text-opacity))}.text-slate-200{--tw-text-opacity: 1;color:rgb(226 232 240 / var(--tw-text-opacity))}.text-slate-300{--tw-text-opacity: 1;color:rgb(203 213 225 / var(--tw-text-opacity))}.text-slate-400{--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity))}.text-slate-400\/60{color:#94a3b899}.text-slate-500{--tw-text-opacity: 1;color:rgb(100 116 139 / var(--tw-text-opacity))}.text-slate-900{--tw-text-opacity: 1;color:rgb(15 23 42 / var(--tw-text-opacity))}.text-slate-950{--tw-text-opacity: 1;color:rgb(2 6 23 / var(--tw-text-opacity))}.text-teal-400{--tw-text-opacity: 1;color:rgb(45 212 191 / var(--tw-text-opacity))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.text-white\/90{color:#ffffffe6}.text-yellow-400{--tw-text-opacity: 1;color:rgb(250 204 21 / var(--tw-text-opacity))}.underline{text-decoration-line:underline}.accent-cyan-500{accent-color:#06b6d4}.accent-emerald-400{accent-color:#34d399}.accent-emerald-500{accent-color:#10b981}.opacity-0{opacity:0}.opacity-100{opacity:1}.opacity-40{opacity:.4}.opacity-50{opacity:.5}.opacity-70{opacity:.7}.opacity-80{opacity:.8}.opacity-85{opacity:.85}.opacity-90{opacity:.9}.shadow{--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1);--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-2xl{--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / .25);--tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-\[0_1px_0_rgba\(255\,255\,255\,0\.05\)_inset\,0_10px_24px_rgba\(0\,0\,0\,0\.28\)\]{--tw-shadow: 0 1px 0 rgba(255,255,255,.05) inset,0 10px 24px rgba(0,0,0,.28);--tw-shadow-colored: inset 0 1px 0 var(--tw-shadow-color), 0 10px 24px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-inner{--tw-shadow: inset 0 2px 4px 0 rgb(0 0 0 / .05);--tw-shadow-colored: inset 0 2px 4px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-black\/30{--tw-shadow-color: rgb(0 0 0 / .3);--tw-shadow: var(--tw-shadow-colored)}.shadow-cyan-950\/50{--tw-shadow-color: rgb(8 51 68 / .5);--tw-shadow: var(--tw-shadow-colored)}.ring-1{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-2{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-4{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-amber-300{--tw-ring-opacity: 1;--tw-ring-color: rgb(252 211 77 / var(--tw-ring-opacity))}.ring-amber-300\/80{--tw-ring-color: rgb(252 211 77 / .8)}.ring-emerald-300\/70{--tw-ring-color: rgb(110 231 183 / .7)}.ring-red-500\/80{--tw-ring-color: rgb(239 68 68 / .8)}.ring-red-500\/90{--tw-ring-color: rgb(239 68 68 / .9)}.ring-sky-400\/80{--tw-ring-color: rgb(56 189 248 / .8)}.blur{--tw-blur: blur(8px);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.grayscale{--tw-grayscale: grayscale(100%);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.invert{--tw-invert: invert(100%);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.sepia{--tw-sepia: sepia(100%);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur-sm{--tw-backdrop-blur: blur(4px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\[width\,height\]{transition-property:width,height;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-500{transition-duration:.5s}.duration-700{transition-duration:.7s}@keyframes batteryTickWarn{0%,49%{background-color:#fffffff2}50%,to{background-color:#ef4444f2}}.battery-tick-warn{animation:batteryTickWarn .4s steps(2,end) infinite}@keyframes batteryUrgentFlash{0%,49%{opacity:1}50%,to{opacity:.25}}.battery-urgent-flash{animation:batteryUrgentFlash .4s steps(2,end) infinite}.no-touch-select{-moz-user-select:none;user-select:none;-webkit-user-select:none;-ms-user-select:none;-webkit-touch-callout:none}.mobile-touch-control{user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-webkit-touch-callout:none;-webkit-tap-highlight-color:transparent;touch-action:manipulation;overscroll-behavior:contain}.mobile-drag-control{touch-action:none}.mobile-text-entry{font-size:16px;-webkit-text-size-adjust:100%;touch-action:manipulation}.\[writing-mode\:vertical-rl\]{writing-mode:vertical-rl}:root{font-family:Inter,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity));--tw-text-opacity: 1;color:rgb(241 245 249 / var(--tw-text-opacity))}body{margin:0;min-height:100vh;--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity));--tw-text-opacity: 1;color:rgb(241 245 249 / var(--tw-text-opacity))}html,body,*{scrollbar-width:thin;scrollbar-color:rgba(148,163,184,.55) transparent}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}*::-webkit-scrollbar{width:2px;height:2px}*::-webkit-scrollbar-track{background:transparent}*::-webkit-scrollbar-thumb{background-color:#94a3b88c;border-radius:9999px}@supports (scrollbar-width: thin){@media(pointer:coarse){html,body,*{scrollbar-width:none}}}.placeholder\:text-slate-400::-moz-placeholder{--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity))}.placeholder\:text-slate-400::placeholder{--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity))}.last\:mb-0:last-child{margin-bottom:0}.hover\:-translate-y-0\.5:hover{--tw-translate-y: -.125rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\:scale-110:hover{--tw-scale-x: 1.1;--tw-scale-y: 1.1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\:border-green-100:hover{--tw-border-opacity: 1;border-color:rgb(220 252 231 / var(--tw-border-opacity))}.hover\:border-rose-500:hover{--tw-border-opacity: 1;border-color:rgb(244 63 94 / var(--tw-border-opacity))}.hover\:border-sky-500:hover{--tw-border-opacity: 1;border-color:rgb(14 165 233 / var(--tw-border-opacity))}.hover\:border-white:hover{--tw-border-opacity: 1;border-color:rgb(255 255 255 / var(--tw-border-opacity))}.hover\:border-white\/60:hover{border-color:#fff9}.hover\:bg-amber-400:hover{--tw-bg-opacity: 1;background-color:rgb(251 191 36 / var(--tw-bg-opacity))}.hover\:bg-amber-600:hover{--tw-bg-opacity: 1;background-color:rgb(217 119 6 / var(--tw-bg-opacity))}.hover\:bg-amber-800:hover{--tw-bg-opacity: 1;background-color:rgb(146 64 14 / var(--tw-bg-opacity))}.hover\:bg-cyan-400:hover{--tw-bg-opacity: 1;background-color:rgb(34 211 238 / var(--tw-bg-opacity))}.hover\:bg-cyan-800:hover{--tw-bg-opacity: 1;background-color:rgb(21 94 117 / var(--tw-bg-opacity))}.hover\:bg-emerald-400:hover{--tw-bg-opacity: 1;background-color:rgb(52 211 153 / var(--tw-bg-opacity))}.hover\:bg-emerald-500:hover{--tw-bg-opacity: 1;background-color:rgb(16 185 129 / var(--tw-bg-opacity))}.hover\:bg-emerald-700:hover{--tw-bg-opacity: 1;background-color:rgb(4 120 87 / var(--tw-bg-opacity))}.hover\:bg-fuchsia-500:hover{--tw-bg-opacity: 1;background-color:rgb(217 70 239 / var(--tw-bg-opacity))}.hover\:bg-green-400:hover{--tw-bg-opacity: 1;background-color:rgb(74 222 128 / var(--tw-bg-opacity))}.hover\:bg-indigo-500:hover{--tw-bg-opacity: 1;background-color:rgb(99 102 241 / var(--tw-bg-opacity))}.hover\:bg-indigo-800:hover{--tw-bg-opacity: 1;background-color:rgb(55 48 163 / var(--tw-bg-opacity))}.hover\:bg-pink-400:hover{--tw-bg-opacity: 1;background-color:rgb(244 114 182 / var(--tw-bg-opacity))}.hover\:bg-red-600:hover{--tw-bg-opacity: 1;background-color:rgb(220 38 38 / var(--tw-bg-opacity))}.hover\:bg-rose-500:hover{--tw-bg-opacity: 1;background-color:rgb(244 63 94 / var(--tw-bg-opacity))}.hover\:bg-sky-500:hover{--tw-bg-opacity: 1;background-color:rgb(14 165 233 / var(--tw-bg-opacity))}.hover\:bg-slate-600:hover{--tw-bg-opacity: 1;background-color:rgb(71 85 105 / var(--tw-bg-opacity))}.hover\:bg-slate-700\/70:hover{background-color:#334155b3}.hover\:bg-zinc-900:hover{--tw-bg-opacity: 1;background-color:rgb(24 24 27 / var(--tw-bg-opacity))}.hover\:text-blue-500:hover{--tw-text-opacity: 1;color:rgb(59 130 246 / var(--tw-text-opacity))}.hover\:text-white:hover{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.hover\:opacity-90:hover{opacity:.9}.hover\:shadow-xl:hover{--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.hover\:brightness-110:hover{--tw-brightness: brightness(1.1);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-emerald-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(16 185 129 / var(--tw-ring-opacity))}.focus-visible\:outline-none:focus-visible{outline:2px solid transparent;outline-offset:2px}.focus-visible\:outline:focus-visible{outline-style:solid}.focus-visible\:outline-1:focus-visible{outline-width:1px}.focus-visible\:outline-offset-1:focus-visible{outline-offset:1px}.focus-visible\:outline-slate-500:focus-visible{outline-color:#64748b}.focus-visible\:ring-2:focus-visible{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus-visible\:ring-amber-300:focus-visible{--tw-ring-opacity: 1;--tw-ring-color: rgb(252 211 77 / var(--tw-ring-opacity))}.focus-visible\:ring-cyan-300:focus-visible{--tw-ring-opacity: 1;--tw-ring-color: rgb(103 232 249 / var(--tw-ring-opacity))}.focus-visible\:ring-emerald-200:focus-visible{--tw-ring-opacity: 1;--tw-ring-color: rgb(167 243 208 / var(--tw-ring-opacity))}.focus-visible\:ring-emerald-300:focus-visible{--tw-ring-opacity: 1;--tw-ring-color: rgb(110 231 183 / var(--tw-ring-opacity))}.focus-visible\:ring-indigo-300:focus-visible{--tw-ring-opacity: 1;--tw-ring-color: rgb(165 180 252 / var(--tw-ring-opacity))}.active\:scale-95:active{--tw-scale-x: .95;--tw-scale-y: .95;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.active\:scale-\[0\.99\]:active{--tw-scale-x: .99;--tw-scale-y: .99;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.active\:brightness-125:active{--tw-brightness: brightness(1.25);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-30:disabled{opacity:.3}.disabled\:opacity-40:disabled{opacity:.4}.disabled\:opacity-50:disabled{opacity:.5}.disabled\:opacity-60:disabled{opacity:.6}.disabled\:opacity-70:disabled{opacity:.7}@media(max-width:520px){.max-\[520px\]\:grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.max-\[520px\]\:justify-start{justify-content:flex-start}}@media(max-width:420px){.max-\[420px\]\:grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.max-\[420px\]\:grid-cols-\[auto_minmax\(0\,1fr\)\]{grid-template-columns:auto minmax(0,1fr)}.max-\[420px\]\:justify-start{justify-content:flex-start}}@media(min-width:640px){.sm\:block{display:block}.sm\:min-h-\[18rem\]{min-height:18rem}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:grid-cols-\[minmax\(0\,0\.8fr\)_minmax\(0\,1\.2fr\)\]{grid-template-columns:minmax(0,.8fr) minmax(0,1.2fr)}.sm\:text-2xl{font-size:1.5rem;line-height:2rem}.sm\:text-3xl{font-size:1.875rem;line-height:2.25rem}}@media(min-width:768px){.md\:h-full{height:100%}.md\:h-screen{height:100vh}.md\:min-h-0{min-height:0px}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-\[0\.9fr_1fr_1\.3fr\]{grid-template-columns:.9fr 1fr 1.3fr}.md\:grid-cols-\[minmax\(0\,1\.4fr\)_minmax\(0\,1fr\)\]{grid-template-columns:minmax(0,1.4fr) minmax(0,1fr)}.md\:grid-cols-\[minmax\(0\,1\.5fr\)_minmax\(0\,1fr\)\]{grid-template-columns:minmax(0,1.5fr) minmax(0,1fr)}.md\:grid-cols-\[minmax\(0\,1fr\)_18rem\]{grid-template-columns:minmax(0,1fr) 18rem}.md\:overflow-hidden{overflow:hidden}.md\:overflow-y-auto{overflow-y:auto}.md\:text-lg{font-size:1.125rem;line-height:1.75rem}}@media(min-width:1024px){.lg\:col-span-2{grid-column:span 2 / span 2}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.lg\:grid-cols-\[minmax\(0\,1\.25fr\)_minmax\(0\,0\.9fr\)\]{grid-template-columns:minmax(0,1.25fr) minmax(0,.9fr)}.lg\:grid-cols-\[minmax\(0\,1fr\)_20rem\]{grid-template-columns:minmax(0,1fr) 20rem}} diff --git a/server/public/assets/index-I6c2my6z.js b/server/public/assets/index-DvrSfmPb.js similarity index 98% rename from server/public/assets/index-I6c2my6z.js rename to server/public/assets/index-DvrSfmPb.js index e3bf9e80..f10679ab 100644 --- a/server/public/assets/index-I6c2my6z.js +++ b/server/public/assets/index-DvrSfmPb.js @@ -15,7 +15,7 @@ ${C}`}).join(` ---------------------------------------- `):""};return r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("div",{className:"panel-muted text-xs uppercase",children:"Overseer Control"}),r.jsx("div",{className:"flex gap-0.5 text-xs",children:r.jsx("button",{type:"button",onClick:a,disabled:!!l,className:"button-danger disabled:cursor-not-allowed disabled:opacity-60",children:l?"Clearing...":"Clear Overseer History"})}),r.jsxs("div",{className:"surface flex flex-wrap gap-0.5 text-xs",children:[r.jsxs("span",{className:"surface-muted",children:["running: ",d.running?"yes":"no"]}),r.jsxs("span",{className:"surface-muted",children:["phase: ",d.phase||"--"]}),r.jsxs("span",{className:"surface-muted",children:["tick: ",d.tickCount??0]}),r.jsxs("span",{className:"surface-muted",children:["trigger: ",d.lastTriggerReason||"--"]}),r.jsxs("span",{className:"surface-muted",children:["name: ",p.name||"--"]}),r.jsxs("span",{className:"surface-muted",children:["model: ",p.model||"--"]}),r.jsxs("span",{className:"surface-muted",children:["observeOnly: ",p.observeOnly?"yes":"no"]}),r.jsxs("span",{className:"surface-muted",children:["decision: ",v.normalized||"--"]}),r.jsxs("span",{className:"surface-muted",children:["outcome: ",v.outcome||"--"]}),r.jsxs("span",{className:"surface-muted",children:["reason: ",v.reason||"--"]}),r.jsxs("span",{className:"surface-muted",children:["last gen: ",g.lastGenerationMs!=null?`${g.lastGenerationMs}ms`:"--"]})]}),r.jsxs("details",{className:"surface text-xs text-slate-200",open:!0,children:[r.jsx("summary",{className:"cursor-pointer select-none text-slate-300",children:"Latest Context"}),r.jsx("pre",{className:"mt-0.5 whitespace-pre-wrap break-words text-[0.72rem] text-slate-200",children:x.stateUpdate||""})]}),r.jsxs("details",{className:"surface text-xs text-slate-200",open:!0,children:[r.jsx("summary",{className:"cursor-pointer select-none text-slate-300",children:"Exact Model Input (messages[])"}),r.jsx("pre",{className:"mt-0.5 whitespace-pre-wrap break-words text-[0.72rem] text-slate-200",children:b()})]}),r.jsxs("details",{className:"surface text-xs text-slate-200",children:[r.jsx("summary",{className:"cursor-pointer select-none text-slate-300",children:"Exact System Prompt"}),r.jsx("pre",{className:"mt-0.5 whitespace-pre-wrap break-words text-[0.72rem] text-slate-200",children:x.systemPrompt||""})]}),r.jsxs("details",{className:"surface text-xs text-slate-200",children:[r.jsx("summary",{className:"cursor-pointer select-none text-slate-300",children:"Exact Transcript Rows"}),r.jsx("pre",{className:"mt-0.5 whitespace-pre-wrap break-words text-[0.72rem] text-slate-200",children:JSON.stringify(x.transcript||[],null,2)})]}),r.jsxs("details",{className:"surface text-xs text-slate-200",children:[r.jsx("summary",{className:"cursor-pointer select-none text-slate-300",children:"Tool Availability"}),r.jsx("pre",{className:"mt-0.5 whitespace-pre-wrap break-words text-[0.72rem] text-slate-200",children:JSON.stringify({available:x.availableTools,blocked:x.blockedTools},null,2)})]}),r.jsxs("details",{className:"surface text-xs text-slate-200",open:!0,children:[r.jsx("summary",{className:"cursor-pointer select-none text-slate-300",children:"Exact Model Output"}),r.jsx("pre",{className:"mt-0.5 whitespace-pre-wrap break-words text-[0.72rem] text-slate-200",children:v.raw||""})]}),r.jsxs("details",{className:"surface text-xs text-slate-200",open:!0,children:[r.jsx("summary",{className:"cursor-pointer select-none text-slate-300",children:"Live Tool Calls"}),r.jsx("pre",{className:"mt-0.5 whitespace-pre-wrap break-words text-[0.72rem] text-slate-200",children:JSON.stringify(v.liveToolCalls||[],null,2)})]}),r.jsxs("div",{className:"surface text-xs text-slate-200",children:[r.jsxs("div",{children:["Normalized decision: ",v.normalized||"--"]}),r.jsxs("div",{children:["Model input at: ",x.modelInputAt?new Date(x.modelInputAt).toLocaleString():"n/a"]}),r.jsxs("div",{children:["Model output at: ",v.outputAt?new Date(v.outputAt).toLocaleString():"n/a"]})]}),z.message?r.jsxs("div",{className:"surface text-xs text-red-300",children:["Error: ",z.message]}):null,r.jsxs("details",{className:"surface text-xs text-slate-200",children:[r.jsx("summary",{className:"cursor-pointer select-none text-slate-300",children:"Recent Runs"}),r.jsx("pre",{className:"mt-0.5 whitespace-pre-wrap break-words text-[0.72rem] text-slate-200",children:JSON.stringify(t.history||[],null,2)})]}),r.jsx("button",{type:"button",className:"button-dark text-xs",onClick:()=>u(y=>!y),children:o?"Hide Full Payload":"Show Full Payload"}),o?r.jsx("pre",{className:"surface whitespace-pre-wrap break-words text-[0.72rem] text-slate-200",children:JSON.stringify(t,null,2)}):null]})}function _K({health:t,roster:a=[]}){if(!t)return null;const l=t.replay||{sources:[],readyCount:0,totalCount:0},o=t.snapshots||{rovers:[],rooms:[]},u=g=>a.find(x=>String(x.id)===String(g))?.color||null,d=`${l.readyCount}/${l.totalCount} sources ready`,p=o.rovers.filter(g=>g.stale).length,v=o.rooms.filter(g=>g.stale).length;return r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("div",{className:"panel-muted text-xs uppercase",children:"Health"}),r.jsxs("div",{className:"surface space-y-0.5 text-xs text-slate-200",children:[r.jsxs("div",{className:"flex items-center justify-between",children:[r.jsx("span",{children:"Replay segments"}),r.jsx("span",{className:"text-slate-400",children:d})]}),r.jsxs("div",{className:"flex items-center justify-between",children:[r.jsx("span",{children:"Rover snapshots"}),r.jsxs("span",{className:p?"text-amber-300":"text-emerald-300",children:[o.rovers.length-p,"/",o.rovers.length," ok"]})]}),r.jsxs("div",{className:"flex items-center justify-between",children:[r.jsx("span",{children:"Room cameras"}),r.jsxs("span",{className:v?"text-amber-300":"text-emerald-300",children:[o.rooms.length-v,"/",o.rooms.length," ok"]})]})]}),r.jsx("div",{className:"space-y-0.5 text-xs text-slate-300",children:l.sources.map(g=>r.jsxs("div",{className:"flex items-center justify-between",children:[g.type==="rover"?r.jsx(V4,{name:g.label,color:u(g.id),fallback:g.id}):r.jsx("span",{children:g.label}),r.jsxs("span",{className:g.ready?"text-emerald-300":"text-amber-300",children:[g.recentCount,"/",g.neededCount]})]},`${g.type}:${g.id}`))}),r.jsx("div",{className:"space-y-0.5 text-xs text-slate-300",children:o.rovers.map(g=>r.jsxs("div",{className:"flex items-center justify-between",children:[r.jsx(V4,{name:g.name,color:u(g.id),fallback:g.id}),r.jsx("span",{className:g.stale?"text-amber-300":"text-emerald-300",children:g.stale?"stale":"ok"})]},`rover:${g.id}`))}),r.jsx("div",{className:"space-y-0.5 text-xs text-slate-300",children:o.rooms.map(g=>r.jsxs("div",{className:"flex items-center justify-between",children:[r.jsx("span",{children:g.name}),r.jsx("span",{className:g.stale?"text-amber-300":"text-emerald-300",children:g.error?"error":g.stale?"stale":"ok"})]},`room:${g.id}`))})]})}function UK({entries:t}){const a=t||[];return r.jsxs(B1,{title:"Admin IP log",meta:a.length,bodyClassName:"space-y-0.5 text-base",children:[r.jsx("div",{className:"h-64 overflow-y-auto font-mono text-xs",children:a.length===0?r.jsx("p",{children:"No admin log entries yet."}):a.slice().reverse().map(l=>r.jsxs("div",{children:[r.jsx("span",{className:"text-amber-400",children:l.ts?new Date(l.ts).toLocaleTimeString():"--"})," ",l.label&&r.jsxs("span",{className:"text-teal-400",children:["[",l.label,"]"]})," ",r.jsx("span",{className:"text-slate-200",children:l.message})," ",l.ip&&r.jsx("span",{className:"text-cyan-300",children:l.ip})," ",l.meta&&r.jsx("span",{className:"text-slate-500",children:JSON.stringify(l.meta)})]},l.id))}),r.jsx("p",{className:"text-xs text-slate-500",children:"Admin-only log stream; IPs never appear in user data."})]})}const ZK=[{key:"open",label:"Open"},{key:"turns",label:"Turns"},{key:"admin",label:"Admin"},{key:"lockdown",label:"Lockdown"}];function $K(){const{session:t,lockRover:a,setMode:l,requestControl:o,setGlobalObjective:u,setAdminReason:d,rebootRover:p,updateRover:v,rebootServer:g,setAudioLevels:x,setPrivateSafety:z,llmControl:b,overseerControl:y,adminLogs:H,llmCommentaryState:S,overseerControlState:w}=C0(),C=f.useMemo(()=>t?.roster??[],[t?.roster]),[V,A]=f.useState({}),[B,k]=f.useState({}),[D,j]=f.useState({}),[N,E]=f.useState(!1),[Z,q]=f.useState(!1),[T,P]=f.useState(!1),o1=t?.health||null,n1=t?.globalObjective?.text||"",I=t?.globalObjective?.updatedAt||null,[R,G]=f.useState(n1),e1=t?.adminReason?.text||"",d1=t?.adminReason?.updatedAt||null,[u1,F]=f.useState(e1),_=t?.audioLevels||{},[t1,r1]=f.useState({hornGain:Number.isFinite(_.hornGain)?_.hornGain:1,ttsGain:Number.isFinite(_.ttsGain)?_.ttsGain:1,forwardGain:Number.isFinite(_.forwardGain)?_.forwardGain:1}),[f1,m1]=f.useState({}),S1=t?.role==="admin"||t?.role==="lockdown"||t?.role==="lockdown-admin",V1=t?.role==="lockdown"||t?.role==="lockdown-admin",A1=t?.mode??"open",U1=async(J,a1)=>{try{await a(J,a1),A(C1=>({...C1,[J]:a1}))}catch(C1){alert(C1.message)}},Y1=async J=>{const a1=J.target.value;try{await l(a1)}catch(C1){alert(C1.message)}},E1=async J=>{try{await o(J,{force:!0})}catch(a1){alert(a1.message)}},g1=async J=>{if(!(!J?.id||!window.confirm(`Reboot rover "${J.name||J.id}" now?`))){k(C1=>({...C1,[J.id]:!0}));try{await p(J.id)}catch(C1){alert(C1.message)}finally{k(C1=>({...C1,[J.id]:!1}))}}},Q=async J=>{if(!(!J?.id||!window.confirm(`Update rover "${J.name||J.id}" now? The rover will git pull the repo, run the roverd installer, and may disconnect while services restart.`))){j(C1=>({...C1,[J.id]:!0}));try{await v(J.id)}catch(C1){alert(C1.message)}finally{j(C1=>({...C1,[J.id]:!1}))}}},v1=async()=>{if(window.confirm("Reboot the server host now? This will disconnect all users.")){E(!0);try{await g()}catch(a1){alert(a1.message),E(!1)}}},z1=async()=>{if(window.confirm("Clear LLM commentary history now? This resets chat context, bot memory, and rover activity metrics for narration.")){q(!0);try{await b("clearHistory")}catch(a1){alert(a1.message)}finally{q(!1)}}},p1=async()=>{if(window.confirm("Clear Overseer Control history now?")){P(!0);try{await y("clearHistory")}catch(a1){alert(a1.message)}finally{P(!1)}}},F1=async()=>{try{await u(R)}catch(J){alert(J.message)}},n2=async()=>{try{await u(null)}catch(J){alert(J.message)}},x1=async()=>{try{await d(u1)}catch(J){alert(J.message)}},M1=async()=>{try{await d(null)}catch(J){alert(J.message)}},G1=J=>a1=>{const C1=Number(a1.target.value);r1(l2=>({...l2||{},[J]:Number.isFinite(C1)?C1:1}))},A2=async()=>{try{await x(t1)}catch(J){alert(J.message)}},P1=async()=>{window.dispatchEvent(new CustomEvent("buttonBox:rewardRunLocalTest",{detail:{rewardName:"Admin Mode",count:1e3,goal:1e3}}))};f.useEffect(()=>{G(n1)},[n1]),f.useEffect(()=>{F(e1)},[e1]),f.useEffect(()=>{r1({hornGain:Number.isFinite(_.hornGain)?_.hornGain:1,ttsGain:Number.isFinite(_.ttsGain)?_.ttsGain:1,forwardGain:Number.isFinite(_.forwardGain)?_.forwardGain:1})},[_.forwardGain,_.hornGain,_.ttsGain]),f.useEffect(()=>{const J={};(C||[]).forEach(a1=>{a1?.private?.enabled&&(J[a1.id]={speedLimitEnabled:!!a1?.private?.safety?.speedLimitEnabled,speedLimitMaxWheelSpeed:Number.isFinite(a1?.private?.safety?.speedLimitMaxWheelSpeed)?a1.private.safety.speedLimitMaxWheelSpeed:250,hardOvercurrentEnabled:!!a1?.private?.safety?.hardOvercurrentEnabled,overcurrentStopMs:Number.isFinite(a1?.private?.safety?.overcurrentStopMs)?a1.private.safety.overcurrentStopMs:300,hardBumpEnabled:!!a1?.private?.safety?.hardBumpEnabled,bumpBackoffSpeed:Number.isFinite(a1?.private?.safety?.bumpBackoffSpeed)?a1.private.safety.bumpBackoffSpeed:250,bumpBackoffMs:Number.isFinite(a1?.private?.safety?.bumpBackoffMs)?a1.private.safety.bumpBackoffMs:350,cliffEnabled:!!a1?.private?.safety?.cliffEnabled,cliffBackoffSpeed:Number.isFinite(a1?.private?.safety?.cliffBackoffSpeed)?a1.private.safety.cliffBackoffSpeed:250,cliffBackoffMs:Number.isFinite(a1?.private?.safety?.cliffBackoffMs)?a1.private.safety.cliffBackoffMs:500,triggerCooldownMs:Number.isFinite(a1?.private?.safety?.triggerCooldownMs)?a1.private.safety.triggerCooldownMs:800})}),m1(J)},[C]);const v2=f.useMemo(()=>{const J={};return C.forEach(a1=>{J[a1.id]=V[a1.id]??a1.locked}),J},[C,V]),X1=(J,a1={})=>{m1(C1=>({...C1||{},[J]:{...C1?.[J]||{},...a1||{}}}))},J1=async J=>{try{const a1=f1?.[J];if(!a1)return;await z(J,a1)}catch(a1){alert(a1.message)}};if(!S1)return null;const s1=r.jsx("select",{value:A1,onChange:Y1,className:"field-input text-sm",children:ZK.map(J=>r.jsx("option",{value:J.key,children:J.label},J.key))});return r.jsxs(B1,{title:"Admin controls",actions:s1,bodyClassName:"space-y-0.5 text-base",children:[r.jsxs("div",{className:"flex gap-0.5 text-xs",children:[r.jsx("button",{type:"button",onClick:v1,disabled:N,className:"button-danger disabled:cursor-not-allowed disabled:opacity-60",children:N?"Server rebooting...":"Reboot Server"}),r.jsx("button",{type:"button",onClick:P1,className:"button-dark",children:"Test Reward Popup"})]}),r.jsxs("div",{className:"space-y-0.5",children:[r.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-400",children:[r.jsx("span",{children:"Global audio levels"}),t?.audioLevels?.updatedAt?r.jsxs("span",{children:["Updated ",new Date(t.audioLevels.updatedAt).toLocaleString()]}):null]}),r.jsxs("label",{className:"grid gap-0.5 text-xs text-slate-200",children:[r.jsxs("div",{className:"flex items-center justify-between gap-0.5",children:[r.jsx("span",{children:"Horn gain"}),r.jsxs("span",{children:[t1.hornGain.toFixed(2),"x"]})]}),r.jsx("input",{type:"range",min:"0",max:"4",step:"0.01",value:t1.hornGain,onChange:G1("hornGain"),className:"w-full accent-emerald-500"})]}),r.jsxs("label",{className:"grid gap-0.5 text-xs text-slate-200",children:[r.jsxs("div",{className:"flex items-center justify-between gap-0.5",children:[r.jsx("span",{children:"TTS gain"}),r.jsxs("span",{children:[t1.ttsGain.toFixed(2),"x"]})]}),r.jsx("input",{type:"range",min:"0",max:"4",step:"0.01",value:t1.ttsGain,onChange:G1("ttsGain"),className:"w-full accent-emerald-500"})]}),r.jsxs("label",{className:"grid gap-0.5 text-xs text-slate-200",children:[r.jsxs("div",{className:"flex items-center justify-between gap-0.5",children:[r.jsx("span",{children:"Forward gain"}),r.jsxs("span",{children:[t1.forwardGain.toFixed(2),"x"]})]}),r.jsx("input",{type:"range",min:"0",max:"4",step:"0.01",value:t1.forwardGain,onChange:G1("forwardGain"),className:"w-full accent-emerald-500"})]}),r.jsx("div",{className:"flex gap-0.5 text-xs",children:r.jsx("button",{type:"button",onClick:A2,className:"button-dark",children:"Apply audio levels"})})]}),r.jsxs("div",{className:"space-y-0.5",children:[r.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-400",children:[r.jsx("span",{children:"Global objective"}),I?r.jsxs("span",{children:["Updated ",new Date(I).toLocaleString()]}):null]}),r.jsx("input",{type:"text",value:R,onChange:J=>G(J.target.value),placeholder:"Set a global objective",className:"field-input text-sm"}),r.jsxs("div",{className:"flex gap-0.5 text-xs",children:[r.jsx("button",{type:"button",onClick:F1,className:"button-dark",children:"Set objective"}),r.jsx("button",{type:"button",onClick:n2,className:"button-danger",children:"Clear"})]})]}),r.jsxs("div",{className:"space-y-0.5",children:[r.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-400",children:[r.jsx("span",{children:"Admin mode reason"}),d1?r.jsxs("span",{children:["Updated ",new Date(d1).toLocaleString()]}):null]}),r.jsx("textarea",{value:u1,onChange:J=>F(J.target.value),placeholder:"Set an admin mode reason",className:"field-input text-sm min-h-[3.5rem]"}),r.jsxs("div",{className:"flex gap-0.5 text-xs",children:[r.jsx("button",{type:"button",onClick:x1,className:"button-dark",children:"Set reason"}),r.jsx("button",{type:"button",onClick:M1,className:"button-danger",children:"Clear"})]})]}),r.jsx(EK,{roster:C,renderActions:J=>r.jsxs("div",{className:"flex flex-wrap items-center gap-0.5 text-xs",children:[r.jsx("button",{type:"button",onClick:()=>U1(J.id,!v2[J.id]),className:"button-dark",children:v2[J.id]?"Unlock":"Lock"}),r.jsx("button",{type:"button",onClick:()=>E1(J.id),className:"button-dark",children:"Force"}),r.jsx("button",{type:"button",onClick:()=>g1(J),disabled:!!B[J.id],className:"button-danger disabled:cursor-not-allowed disabled:opacity-60",children:B[J.id]?"Rebooting...":"Reboot"}),r.jsx("button",{type:"button",onClick:()=>Q(J),disabled:!!D[J.id],className:"button-danger disabled:cursor-not-allowed disabled:opacity-60",children:D[J.id]?"Updating...":"Update"}),J?.private?.enabled?r.jsxs("div",{className:"w-full rounded border border-slate-700/70 p-0.5 space-y-0.5 text-[0.7rem]",children:[r.jsx("div",{className:"text-slate-300",children:"Private safety"}),r.jsxs("label",{className:"flex items-center gap-0.5",children:[r.jsx("input",{type:"checkbox",checked:!!f1?.[J.id]?.speedLimitEnabled,disabled:!V1,onChange:a1=>X1(J.id,{speedLimitEnabled:!!a1.target.checked})}),r.jsx("span",{children:"Speed limit"})]}),r.jsx("input",{type:"number",min:"1",max:"500",className:"field-input text-xs w-20",disabled:!V1,value:f1?.[J.id]?.speedLimitMaxWheelSpeed??250,onChange:a1=>X1(J.id,{speedLimitMaxWheelSpeed:Number(a1.target.value)||250})}),r.jsxs("label",{className:"flex items-center gap-0.5",children:[r.jsx("input",{type:"checkbox",checked:!!f1?.[J.id]?.hardOvercurrentEnabled,disabled:!V1,onChange:a1=>X1(J.id,{hardOvercurrentEnabled:!!a1.target.checked})}),r.jsx("span",{children:"Hard overcurrent"})]}),r.jsxs("label",{className:"flex items-center gap-0.5",children:[r.jsx("input",{type:"checkbox",checked:!!f1?.[J.id]?.hardBumpEnabled,disabled:!V1,onChange:a1=>X1(J.id,{hardBumpEnabled:!!a1.target.checked})}),r.jsx("span",{children:"Hard bump"})]}),r.jsxs("label",{className:"flex items-center gap-0.5",children:[r.jsx("input",{type:"checkbox",checked:!!f1?.[J.id]?.cliffEnabled,disabled:!V1,onChange:a1=>X1(J.id,{cliffEnabled:!!a1.target.checked})}),r.jsx("span",{children:"Cliff safety"})]}),r.jsx("button",{type:"button",disabled:!V1,onClick:()=>J1(J.id),className:"button-dark disabled:opacity-50",children:"Apply Safety"})]}):null]})}),r.jsx(_K,{health:o1,roster:C}),r.jsx(DK,{state:S,onClearHistory:z1,clearingHistory:Z}),r.jsx(OK,{state:w,onClearHistory:p1,clearingHistory:T}),r.jsx(UK,{entries:H})]})}const GK=[{id:"driveForward",label:"Drive Forward",group:"Driving"},{id:"driveBackward",label:"Drive Backward",group:"Driving"},{id:"driveLeft",label:"Turn Left",group:"Driving"},{id:"driveRight",label:"Turn Right",group:"Driving"},{id:"boostModifier",label:"Boost Modifier",group:"Driving"},{id:"slowModifier",label:"Slow Modifier",group:"Driving"},{id:"auxMainForward",label:"Main Brush Forward",group:"Aux Motors"},{id:"auxMainReverse",label:"Main Brush Reverse",group:"Aux Motors"},{id:"auxSideForward",label:"Side Brush Forward",group:"Aux Motors"},{id:"auxSideReverse",label:"Side Brush Reverse",group:"Aux Motors"},{id:"auxVacuumFast",label:"Vacuum Max",group:"Aux Motors"},{id:"auxVacuumSlow",label:"Vacuum Low",group:"Aux Motors"},{id:"auxAllForward",label:"All Aux Forward",group:"Aux Motors"},{id:"cameraUp",label:"Camera Up",group:"Camera"},{id:"cameraDown",label:"Camera Down",group:"Camera"},{id:"nightVisionToggle",label:"Toggle Night Vision",group:"Camera"},{id:"videoFilterCycle",label:"Cycle Video Filter",group:"Camera"},{id:"hornHonk",label:"Horn (Hold)",group:"Audio"},{id:"micPtt",label:"Mic Push To Talk",group:"Audio"},{id:"driveMacro",label:"Drive Macro",group:"Macros"},{id:"dockMacro",label:"Dock Macro",group:"Macros"},{id:"chatFocus",label:"Toggle Chat",group:"Chat"},{id:"songNoteUp",label:"Song Note Up",group:"Audio"},{id:"songNoteDown",label:"Song Note Down",group:"Audio"},{id:"homeAssistantOn",label:"Room Controls On (Cycle)",group:"Room Controls"},{id:"homeAssistantOff",label:"Room Controls Off (Cycle)",group:"Room Controls"}];function PK(t){return t.reduce((a,l)=>((a[l.group]||(a[l.group]=[])).push(l),a),{})}function IK(t,a){const l=Number(t);return Number.isFinite(l)?Math.max(0,Math.min(500,l)):a}const aa=5,k8=500,R8=1,na=100;function YK(t,a){const l=Number(t);return Number.isFinite(l)?Math.max(aa,Math.min(k8,l)):a}function F7(t,a){const l=Number(t);return Number.isFinite(l)?Math.max(R8,Math.min(na,l)):a}function KK(t){const l=(F7(t,t)-R8)/(na-R8),o=k8-l*(k8-aa);return Math.round(o)}function QK(t){const a=YK(t,t),l=(k8-a)/(k8-aa),o=R8+l*(na-R8);return Math.round(o)}function XK(t){const[a,l]=f.useState(null),o=f.useCallback(p=>{T9(!0),l(p)},[]),u=f.useCallback(()=>{T9(!1),l(null)},[]),d=f.useCallback((p,v)=>{if(v.preventDefault(),v.stopPropagation(),v.key==="Escape"){u();return}const g=O8(v.key??"");g&&(t(p,g,v),u())},[u,t]);return f.useEffect(()=>()=>T9(!1),[]),{active:a,startCapture:o,cancel:u,captureFromEvent:d}}function W9({children:t}){return r.jsx("p",{className:"mx-auto w-full max-w-lg text-sm font-semibold text-white",children:t})}function h7({label:t,description:a,value:l,onChange:o,min:u=0,max:d=500,step:p=5}){return r.jsxs("label",{className:"mx-auto block w-full max-w-lg rounded bg-neutral-800/80 px-1.5 py-1",children:[r.jsxs("div",{className:"grid grid-cols-[minmax(0,1fr)_4.75rem] items-start gap-1.5 text-sm text-white",children:[r.jsx("span",{className:"min-w-0 font-semibold text-white",children:t}),r.jsx("input",{type:"number",min:u,max:d,value:l,onChange:v=>o(v.target.value),className:"w-full rounded border border-neutral-600 bg-neutral-900 px-1 py-0.5 text-right text-xs font-mono text-white"})]}),a&&r.jsx("p",{className:"mt-0.5 text-xs leading-snug text-white",children:a}),r.jsxs("div",{className:"mt-1 flex items-center gap-1.5",children:[r.jsx("input",{type:"range",min:u,max:d,step:p,value:l,onChange:v=>o(v.target.value),className:"h-2 min-w-0 flex-1 accent-emerald-400"}),r.jsx("span",{className:"w-10 text-right text-xs font-mono text-white",children:l})]})]})}function JK({action:t,value:a,isActive:l,onCaptureStart:o,onCancel:u,onCaptureKeyDown:d}){return r.jsxs("div",{className:"mx-auto grid w-full max-w-lg grid-cols-[minmax(0,1fr)_auto] items-center gap-1.5 rounded bg-neutral-800/80 px-1.5 py-1 text-sm max-[420px]:grid-cols-1",children:[r.jsxs("div",{className:"min-w-0",children:[r.jsx("p",{className:"font-semibold leading-snug text-white",children:t.label}),r.jsx("p",{className:"mt-0.5 text-xs leading-snug text-white",children:q2(a)})]}),r.jsxs("div",{className:"flex flex-wrap items-center justify-end gap-1 max-[420px]:justify-start",children:[l&&r.jsx("button",{type:"button",onClick:u,className:"button-danger px-1 py-0.5 text-xs",children:"Cancel"}),r.jsx("button",{type:"button",onClick:o,onKeyDown:l?d:void 0,autoFocus:l,className:`${l?"rounded-md bg-emerald-500 px-1 py-0.5 text-emerald-950 hover:bg-emerald-400":"button-dark px-1 py-0.5"} text-xs font-medium transition-colors`,children:l?"Press a key...":"Change"})]})]})}function WK(){const t=R1(w=>w.state.keymap),{updateKeyBinding:a,resetKeyBindings:l}=p3(),{value:o,save:u}=N1("inputs",k4),d={...k4.keyboard,...o.keyboard??{}},p=f.useMemo(()=>typeof d.tiltSpeed=="number"?F7(d.tiltSpeed,k4.keyboard.tiltSpeed):typeof d.tiltIntervalMs=="number"?F7(QK(d.tiltIntervalMs),k4.keyboard.tiltSpeed):k4.keyboard.tiltSpeed,[d.tiltIntervalMs,d.tiltSpeed]),v=f.useMemo(()=>PK(GK),[]),{active:g,startCapture:x,cancel:z,captureFromEvent:b}=XK((w,C)=>{a(w,C)}),y=f.useCallback(w=>t?.[w]?.[0]||V8[w]?.[0]||"",[t]),H=f.useCallback((w,C)=>{const V=k4.keyboard,A=IK(C,V[w]);u(B=>({...B??{},keyboard:{...B?.keyboard??V,[w]:A}}))},[u]),S=f.useCallback(w=>{const C=k4.keyboard,V=F7(w,C.tiltSpeed),A=KK(V);u(B=>({...B??{},keyboard:{...B?.keyboard??C,tiltSpeed:V,tiltIntervalMs:A}}))},[u]);return r.jsxs(B1,{title:"Keyboard layout",meta:"Per-browser · click to change a binding",actions:r.jsx("button",{type:"button",onClick:()=>l(),className:"button-dark px-1 py-0.5 text-xs",children:"Reset defaults"}),bodyClassName:"space-y-2 p-1 text-sm",children:[r.jsxs("div",{className:"space-y-1",children:[r.jsx(W9,{children:"Keyboard speeds"}),r.jsxs("div",{className:"grid gap-1",children:[r.jsx(h7,{label:"Base speed",description:"Normal driving speed",value:d.baseSpeed,onChange:w=>H("baseSpeed",w)}),r.jsx(h7,{label:"Turbo speed",description:"Used when holding the boost modifier",value:d.turboSpeed,onChange:w=>H("turboSpeed",w)}),r.jsx(h7,{label:"Precision speed",description:"Used when holding the precision modifier",value:d.precisionSpeed,onChange:w=>H("precisionSpeed",w)})]})]}),r.jsxs("div",{className:"space-y-1",children:[r.jsx(W9,{children:"Camera tilt"}),r.jsx(h7,{label:"Tilt speed",description:"Higher = faster when holding tilt keys",value:p,onChange:S,min:1,max:100,step:1})]}),r.jsx("div",{className:"space-y-2",children:Object.entries(v).map(([w,C])=>r.jsxs("div",{className:"space-y-1",children:[r.jsx(W9,{children:w}),r.jsx("div",{className:"grid gap-1",children:C.map(V=>{const A=y(V.id),B=g===V.id;return r.jsx(JK,{action:V,value:A,isActive:B,onCancel:()=>z(),onCaptureStart:()=>x(V.id),onCaptureKeyDown:k=>b(V.id,k)},V.id)})})]},w))})]})}const u3=new Intl.NumberFormat(void 0,{maximumFractionDigits:2}),tQ=[{id:"drive",label:"Drive stick",kind:"axisPair",section:"Driving",invertDefaults:{invertX:!1,invertY:!0}},{id:"cameraTilt",label:"Camera tilt",kind:"axis",section:"Camera",invertDefaults:{invert:!0}},{id:"mainBrush",label:"Main brush",kind:"axis",section:"Brushes",invertDefaults:{invert:!1}},{id:"sideBrush",label:"Side brush",kind:"axis",section:"Brushes",invertDefaults:{invert:!1}},{id:"vacuum",label:"Vacuum",kind:"button",section:"Aux buttons"},{id:"allAux",label:"All aux",kind:"button",section:"Aux buttons"},{id:"mainReverse",label:"Main reverse toggle",kind:"button",section:"Brush toggles"},{id:"sideReverse",label:"Side reverse toggle",kind:"button",section:"Brush toggles"},{id:"driveMacro",label:"Drive macro",kind:"button",section:"Mode macros"},{id:"dockMacro",label:"Dock macro",kind:"button",section:"Mode macros"},{id:"nightVisionToggle",label:"Night vision toggle",kind:"button",section:"Camera"}],gs=.45,eQ=.6;function C8({label:t,description:a,min:l,max:o,step:u,value:d,onChange:p}){return r.jsxs("label",{className:"mx-auto block w-full max-w-lg rounded bg-neutral-800/80 px-1.5 py-1",children:[r.jsxs("div",{className:"grid grid-cols-[minmax(0,1fr)_auto] items-start gap-1.5 text-sm text-white",children:[r.jsx("span",{className:"min-w-0 font-semibold text-white",children:t}),r.jsx("span",{className:"rounded bg-neutral-900 px-1 py-0.5 font-mono text-xs text-white",children:u3.format(d)})]}),a&&r.jsx("p",{className:"mt-0.5 text-xs leading-snug text-white",children:a}),r.jsx("input",{type:"range",min:l,max:o,step:u,value:d,onChange:v=>p(Number(v.target.value)),className:"mt-1 w-full accent-emerald-400"})]})}function cQ(t){if(!t)return"Unassigned";if(t.kind==="axisPair"){const a=`${t.invertX?"X inv ":""}${t.invertY?"Y inv":""}`.trim();return`Axes ${t.x}/${t.y}${a?` (${a})`:""}`}if(t.kind==="axis")return`Axis ${t.index}${t.invert?" (inv)":""}`;if(t.kind==="button")return`Button ${t.index}`;if(t.kind==="buttonAxis")return`Button ${t.index} (analog)`;if(t.kind==="axisButton"){const a=t.direction<0?"<":">";return`Axis ${t.index} ${a} ${u3.format(t.threshold??.6)}`}return"Unassigned"}function aQ(t){return t.reduce((a,l)=>((a[l.section]||(a[l.section]=[])).push(l),a),{})}function nQ(t,a){if(!t||t.length===0)return null;if(a){const l=t.find(o=>o.signature===a);if(l)return l}return t[0]}function lQ(t){return{axes:Array.from(t.axes??[]),buttons:(t.buttons??[]).map(a=>({pressed:!!a?.pressed,value:typeof a?.value=="number"?a.value:a?.pressed?1:0}))}}function rQ(t,a,l){const o=(t.axes??[]).map((d,p)=>({index:p,delta:Math.abs((d??0)-(a.axes?.[p]??0)),value:d}));if(o.sort((d,p)=>p.delta-d.delta),l.kind==="axisPair"){const d=o.filter(p=>p.delta>gs).slice(0,2);return d.length<2?null:{kind:"axisPair",x:d[0].index,y:d[1].index,...l.invertDefaults??{}}}const u=o.find(d=>d.delta>gs);return u?{kind:"axis",index:u.index,...l.invertDefaults??{}}:null}function iQ(t,a,l){const o=t.buttons??[];for(let d=0;deQ)return l.kind==="axis"?{kind:"buttonAxis",index:d}:{kind:"button",index:d}}const u=t.axes??[];for(let d=0;d.7&&v>.5)return l.kind==="axis"?{kind:"axis",index:d,...l.invertDefaults??{}}:{kind:"axisButton",index:d,direction:p>=0?1:-1,threshold:.6}}return null}function sQ(t,a,l){if(!t)return null;if(l.kind==="axis"||l.kind==="axisPair"){const o=rQ(t,a,l);if(o)return o}return iQ(t,a,l)}function d7({children:t}){return r.jsx("p",{className:"mx-auto w-full max-w-lg text-sm font-semibold text-white",children:t})}function oQ({action:t,source:a,isCapturing:l,onClear:o,onCapture:u,onInvert:d}){return r.jsxs("div",{className:"mx-auto grid w-full max-w-lg grid-cols-[minmax(0,1fr)_auto] items-center gap-1.5 rounded bg-neutral-800/80 px-1.5 py-1 text-sm max-[520px]:grid-cols-1",children:[r.jsxs("div",{className:"min-w-0",children:[r.jsx("p",{className:"font-semibold leading-snug text-white",children:t.label}),r.jsx("p",{className:"mt-0.5 text-xs leading-snug text-white",children:cQ(a)})]}),r.jsxs("div",{className:"flex flex-wrap items-center justify-end gap-1 max-[520px]:justify-start",children:[t.kind==="axisPair"&&r.jsxs(r.Fragment,{children:[r.jsx("button",{type:"button",disabled:!a,onClick:()=>d(t,"invertX"),className:"button-dark px-1 py-0.5 text-xs font-medium disabled:opacity-50",children:"Invert X"}),r.jsx("button",{type:"button",disabled:!a,onClick:()=>d(t,"invertY"),className:"button-dark px-1 py-0.5 text-xs font-medium disabled:opacity-50",children:"Invert Y"})]}),t.kind==="axis"&&r.jsx("button",{type:"button",disabled:!a,onClick:()=>d(t),className:"button-dark px-1 py-0.5 text-xs font-medium disabled:opacity-50",children:"Invert"}),r.jsx("button",{type:"button",onClick:()=>o(t),className:"button-dark px-1 py-0.5 text-xs",children:"Clear"}),r.jsx("button",{type:"button",onClick:()=>u(t),className:`${l?"rounded-md bg-emerald-500 px-1 py-0.5 text-emerald-950 hover:bg-emerald-400":"button-dark px-1 py-0.5"} text-xs font-medium`,children:l?"Waiting...":"Capture"})]})]})}function uQ(){const t=nY(),{value:a,save:l}=N1("gamepad",_3),[o,u]=f.useState(null),d=f.useRef(null),p=f.useMemo(()=>aQ(tQ),[]),v=f.useMemo(()=>nQ(t.pads,a.activeSignature),[t.pads,a.activeSignature]),g=v?.signature??null,x=f.useMemo(()=>g?a?.profiles?.[g]??a?.defaults?.profile??h3:a?.defaults?.profile??h3,[g,a?.defaults?.profile,a?.profiles]);f.useEffect(()=>{!v||!g||a?.profiles?.[g]||l(C=>{const V=C??_3;if(V.profiles?.[g])return V;const A=V?.defaults?.profile??h3,B=iu(v,A);return{...V,profiles:{...V.profiles??{},[g]:B}}})},[v,g,a?.profiles,l]),f.useEffect(()=>{d.current=null},[o,g]),f.useEffect(()=>{if(!o||!v)return;if(!d.current){d.current=lQ(v);return}const C=sQ(v,d.current,o);C&&(l(V=>{const A=V??_3,B=A.profiles?.[g]??A?.defaults?.profile??h3,k={...B,bindings:{...B.bindings??{},[o.id]:{...B.bindings?.[o.id]??{},kind:o.kind,sources:[C]}}};return{...A,profiles:{...A.profiles??{},[g]:k}}}),u(null))},[v,g,o,l]);const z=f.useCallback(C=>{l(V=>({...V??_3,activeSignature:C||null}))},[l]),b=f.useCallback(C=>{l(V=>{const A=V??_3,B=(g&&A.profiles?.[g])??A?.defaults?.profile??h3,k={...B,calibration:{...B.calibration??{},...C}};return g?{...A,profiles:{...A.profiles??{},[g]:k}}:{...A,defaults:{...A.defaults??{},profile:k}}})},[g,l]),y=f.useCallback((C,V)=>{l(A=>{const B=A??_3,k=(g&&B.profiles?.[g])??B?.defaults?.profile??h3,D=V(k.bindings?.[C]??{}),j={...k,bindings:{...k.bindings??{},[C]:D}};return g?{...B,profiles:{...B.profiles??{},[g]:j}}:{...B,defaults:{...B.defaults??{},profile:j}}})},[g,l]),H=f.useCallback(C=>{y(C.id,V=>({...V,sources:[]}))},[y]),S=f.useCallback((C,V)=>{y(C.id,A=>{const B=Array.isArray(A.sources)?[...A.sources]:[];if(!B[0])return A;const k={...B[0]};return V==="invertX"?k.invertX=!k.invertX:V==="invertY"?k.invertY=!k.invertY:k.invert=!k.invert,B[0]=k,{...A,sources:B}})},[y]),w=f.useMemo(()=>v?{outputs:su(v,x)}:null,[v,x]);return r.jsxs(B1,{title:"Controller",meta:v?"Move sticks or press buttons to bind":"Connect a controller to configure.",bodyClassName:"space-y-2 p-1 text-sm",children:[o&&r.jsxs("p",{className:"mx-auto w-full max-w-lg rounded bg-emerald-950/50 px-1.5 py-1 text-sm text-white",children:["Capturing ",o.label,"..."]}),r.jsxs("div",{className:"space-y-1",children:[r.jsx(d7,{children:"Connected controller"}),t.pads.length===0?r.jsx("p",{className:"mx-auto w-full max-w-lg text-sm text-white",children:"No controller detected."}):r.jsxs("div",{className:"mx-auto grid w-full max-w-lg grid-cols-[minmax(0,1fr)_auto] items-center gap-1.5 rounded bg-neutral-800/80 px-1.5 py-1 text-sm max-[420px]:grid-cols-1",children:[r.jsx("select",{value:g??"",onChange:C=>z(C.target.value),className:"min-w-0 rounded border border-neutral-600 bg-neutral-900 px-1 py-0.5 text-sm text-white",children:t.pads.map(C=>r.jsx("option",{value:C.signature,children:C.id||"Unknown controller"},C.signature))}),r.jsx("span",{className:"rounded bg-neutral-900 px-1 py-0.5 text-xs text-white",children:v?.mapping??"unknown"})]})]}),r.jsxs("div",{className:"space-y-1",children:[r.jsx(d7,{children:"Calibration"}),r.jsxs("div",{className:"grid gap-1",children:[r.jsx(C8,{label:"Drive deadzone",description:"Ignore small drive stick drift",min:0,max:.6,step:.01,value:x.calibration?.driveDeadzone??.18,onChange:C=>b({driveDeadzone:C})}),r.jsx(C8,{label:"Camera deadzone",description:"Ignore small camera tilt drift",min:0,max:.4,step:.01,value:x.calibration?.cameraDeadzone??.08,onChange:C=>b({cameraDeadzone:C})}),r.jsx(C8,{label:"Aux deadzone",description:"Ignore small trigger noise",min:0,max:.4,step:.01,value:x.calibration?.auxDeadzone??.05,onChange:C=>b({auxDeadzone:C})}),r.jsx(C8,{label:"Side brush scale",description:"Scale side brush output",min:.3,max:1,step:.05,value:x.calibration?.auxSideScale??.55,onChange:C=>b({auxSideScale:C})}),r.jsxs("label",{className:"mx-auto block w-full max-w-lg rounded bg-neutral-800/80 px-1.5 py-1",children:[r.jsxs("div",{className:"grid grid-cols-[minmax(0,1fr)_auto] items-start gap-1.5 text-sm text-white",children:[r.jsx("span",{className:"min-w-0 font-semibold text-white",children:"Camera mode"}),r.jsxs("select",{value:x.calibration?.cameraMode??"absolute",onChange:C=>b({cameraMode:C.target.value}),className:"rounded border border-neutral-600 bg-neutral-900 px-1 py-0.5 text-sm text-white",children:[r.jsx("option",{value:"absolute",children:"Absolute"}),r.jsx("option",{value:"velocity",children:"Velocity"})]})]}),r.jsx("p",{className:"mt-0.5 text-xs leading-snug text-white",children:"Absolute maps stick to angle; velocity moves over time."})]}),r.jsx(C8,{label:"Camera sensitivity",description:"Velocity mode degrees per second",min:10,max:180,step:5,value:x.calibration?.cameraSensitivity??60,onChange:C=>b({cameraSensitivity:C})})]})]}),r.jsx("div",{className:"space-y-2",children:Object.entries(p).map(([C,V])=>r.jsxs("div",{className:"space-y-1",children:[r.jsx(d7,{children:C}),r.jsx("div",{className:"grid gap-1",children:V.map(A=>{const k=x.bindings?.[A.id]?.sources?.[0]??null;return r.jsx(oQ,{action:A,source:k,isCapturing:o?.id===A.id,onClear:H,onCapture:u,onInvert:S},A.id)})})]},C))}),r.jsxs("div",{className:"space-y-1",children:[r.jsx(d7,{children:"Diagnostics"}),v?r.jsxs("div",{className:"mx-auto w-full max-w-lg space-y-1 rounded bg-neutral-900/70 px-1.5 py-1 text-xs text-white",children:[r.jsx("p",{className:"text-white",children:"Raw axes"}),r.jsx("div",{className:"grid grid-cols-2 gap-1",children:v.axes.map((C,V)=>r.jsxs("span",{className:"font-mono text-white",children:["A",V,": ",u3.format(C)]},`axis-${V}`))}),r.jsx("p",{className:"text-white",children:"Raw buttons"}),r.jsx("div",{className:"grid grid-cols-2 gap-1",children:v.buttons.map((C,V)=>r.jsxs("span",{className:"font-mono text-white",children:["B",V,": ",u3.format(C.value)," ",C.pressed?"●":""]},`btn-${V}`))}),w?.outputs&&r.jsxs(r.Fragment,{children:[r.jsx("p",{className:"text-white",children:"Mapped outputs"}),r.jsxs("div",{className:"grid grid-cols-2 gap-1",children:[r.jsxs("span",{className:"font-mono text-white",children:["Drive: ",u3.format(w.outputs.driveVector.x),","," ",u3.format(w.outputs.driveVector.y)]}),r.jsxs("span",{className:"font-mono text-white",children:["Camera: ",u3.format(w.outputs.cameraAxis)]}),r.jsxs("span",{className:"font-mono text-white",children:["Main: ",u3.format(w.outputs.auxAxis.main)]}),r.jsxs("span",{className:"font-mono text-white",children:["Side: ",u3.format(w.outputs.auxAxis.side)]})]})]})]}):r.jsx("p",{className:"mx-auto w-full max-w-lg text-sm text-white",children:"No controller detected."})]})]})}const hQ={drive:"Drive wheels",aux:"Aux motors"};function xs(t){return Number.isFinite(t)?`${Math.round(t*100)}%`:"--"}function dQ({value:t,color:a="bg-emerald-500"}){const l=`${Math.round(Math.max(0,Math.min(1,t))*100)}%`;return r.jsx("div",{className:"h-2 w-full overflow-hidden rounded bg-slate-800",children:r.jsx("div",{className:`h-full ${a}`,style:{width:l}})})}function fQ(){const t=R1(o=>o.state.roverId),a=R1(o=>o.overcurrentLimiter),l=f.useMemo(()=>d6.map(o=>o.key),[]);return r.jsx(B1,{title:"Overcurrent limiter",meta:a?.adminImmune?"Admin immune":"Active",bodyClassName:"space-y-0.5 text-sm",children:t?r.jsxs("div",{className:"space-y-0.5",children:[l.map(o=>{const u=a?.caps?.[o]?.cap??0,d=a?.overcurrent?.groups?.[o]??!1,p=a?.scales?.perGroup?.[o]??1;return r.jsxs("div",{className:"space-y-0.5",children:[r.jsxs("div",{className:"flex items-center justify-between text-xs",children:[r.jsx("span",{className:"text-slate-200",children:hQ[o]||o}),r.jsx("span",{className:d?"text-red-300":"text-slate-400",children:d?"overcurrent":"ok"})]}),r.jsxs("div",{className:"space-y-0.5",children:[r.jsxs("div",{className:"flex items-center justify-between text-[0.7rem] text-slate-400",children:[r.jsx("span",{children:"Cap"}),r.jsx("span",{children:xs(u)})]}),r.jsx(dQ,{value:u,color:"bg-amber-500"}),r.jsxs("div",{className:"flex items-center justify-between text-[0.7rem] text-slate-400",children:[r.jsx("span",{children:"Scale"}),r.jsx("span",{children:xs(p)})]})]})]},o)}),r.jsxs("div",{className:"text-[0.7rem] text-slate-400",children:[r.jsx("div",{children:`Down rate ${a?.config?.downRatePerSec}/s · Up rate ${a?.config?.upRatePerSec}/s`}),r.jsx("div",{children:`Release delay ${a?.config?.releaseDelaySec}s`}),r.jsx("div",{children:`Output rate ${a?.config?.outputRateMs}ms`})]})]}):r.jsx("p",{className:"text-xs text-slate-500",children:"Assign a rover to view limiter status."})})}const mQ="pride-page-bg",C2="gap-0.5",v6="space-y-0.5",Nu=f.createContext(null);function Fu(){const t=f.useContext(Nu);if(!t)throw new Error("Tabs components must be used within a provider.");return t}const zs={primary:{base:"flex-1 px-0.5 py-0.5 text-sm font-medium focus-visible:outline focus-visible:outline-1 focus-visible:outline-offset-1 focus-visible:outline-slate-500 rounded-md border border-slate-800",active:"bg-sky-600 text-white border-white",inactive:"bg-zinc-900 text-slate-300 hover:bg-sky-500 hover:text-white"}},bs={none:"",pink:"bg-pink-500 text-white border-pink-200 hover:bg-pink-400",green:"bg-emerald-500 text-white border-emerald-200 hover:bg-emerald-400",pinkblue:"bg-gradient-to-r from-lime-800 to-teal-800"},ku="primary";function la(...t){return t.filter(Boolean).join(" ")}function ra({children:t,defaultTab:a,currentTab:l,onTabChange:o,variant:u=ku}){const[d,p]=f.useState(a??null),[v,g]=f.useState([]),x=f.useCallback(H=>H?(g(S=>S.includes(H)?S:[...S,H]),()=>g(S=>S.includes(H)?S.filter(w=>w!==H):S)):()=>{},[]),z=l??d??v[0]??null;f.useEffect(()=>{a&&l===void 0&&p(a)},[a,l]);const b=f.useCallback(H=>{H&&(l===void 0&&p(H),o&&o(H))},[l,o]),y=f.useMemo(()=>({activeTab:z,setActiveTab:b,registerTab:x,variant:u}),[z,b,x,u]);return r.jsx(Nu.Provider,{value:y,children:t})}function ia({children:t,className:a=""}){return r.jsx("div",{className:la("panel-section overflow-hidden border border-neutral-500/60 bg-neutral-900/95 p-0.5",a),children:r.jsx("div",{className:`flex ${C2}`,children:t})})}function I2({id:t,children:a,className:l="",disabled:o=!1,highlight:u="none"}){const{activeTab:d,setActiveTab:p,registerTab:v,variant:g}=Fu();f.useEffect(()=>v(t),[t,v]);const x=zs[g]??zs[ku],z=d===t,b=bs[u]||bs.none,y=la(x.base,b||(z?x.active:x.inactive),o?"cursor-not-allowed opacity-50":"",l);return r.jsx("button",{type:"button",className:y,onClick:()=>!o&&p(t),"aria-pressed":z,"aria-disabled":o,children:a})}function sa({children:t,className:a=""}){return r.jsx("div",{className:la("mt-0",v6,a),children:t})}function Y2({id:t,children:a,keepMounted:l=!1}){const{activeTab:o}=Fu(),u=o===t;return!u&&!l?null:!u&&l?r.jsx("div",{hidden:!0,"aria-hidden":"true",children:a}):r.jsx(r.Fragment,{children:a})}function vQ(){const{session:t}=C0(),a=f.useMemo(()=>JSON.stringify(t??{},null,2),[t]);return r.jsx(B1,{title:"Session snapshot",className:"min-h-0",bodyClassName:"flex min-h-0 flex-col gap-0.5 text-xs",children:r.jsx("pre",{className:"min-h-0 flex-1 overflow-y-auto font-mono text-[0.7rem] text-lime-300",children:a})})}const Ms=300,ys=240;function pQ(t,a){if(t===void 0)return"undefined";if(t===null)return"null";if(typeof t=="string")return t;if(typeof t=="number"||typeof t=="boolean")return String(t);if(typeof t=="function")return"[Function]";if(t instanceof Error)return`${t.name}: ${t.message}`;if(t instanceof Date)return t.toISOString();if(a||(a=new WeakSet),typeof t=="object"){if(a.has(t))return"[Circular]";a.add(t)}try{return JSON.stringify(t,(l,o)=>typeof o=="function"?"[Function]":o instanceof Error?`${o.name}: ${o.message}`:o,0)}catch{return String(t)}}function gQ(t){const l=t.map(o=>pQ(o)).join(" ");return l.length>ys?`${l.slice(0,ys)}…`:l}function xQ(){const t=R2(),[a,l]=f.useState([]),o=f.useMemo(()=>a.reduce((d,p)=>(p.direction==="in"&&(d.in+=1),p.direction==="out"&&(d.out+=1),d),{in:0,out:0}),[a]);f.useEffect(()=>{const d=(x,z,b)=>{l(y=>{const H=[...y,{id:`${Date.now()}-${Math.random().toString(16).slice(2)}`,direction:x,event:z,payload:gQ(b),timestamp:new Date().toLocaleTimeString()}];return H.length>Ms?H.slice(-Ms):H})},p=(x,...z)=>d("in",x,z),v=(x,...z)=>d("out",x,z);t.onAny(p);let g=null;if(typeof t.onAnyOutgoing=="function")t.onAnyOutgoing(v),g=()=>t.offAnyOutgoing(v);else{const x=t.emit.bind(t);t.emit=(z,...b)=>(v(z,...b),x(z,...b)),g=()=>{t.emit=x}}return()=>{t.offAny(p),g&&g()}},[t]);const u=r.jsxs("div",{className:"flex items-center gap-0.5 text-xs text-slate-400",children:[r.jsxs("span",{children:["in ",o.in]}),r.jsxs("span",{children:["out ",o.out]}),r.jsx("button",{type:"button",className:"button-dark text-xs",onClick:()=>l([]),children:"Clear"})]});return r.jsxs(B1,{title:"Socket activity",actions:u,bodyClassName:"space-y-0.5 text-base",children:[r.jsx("div",{className:"h-64 overflow-y-auto font-mono text-xs",children:a.length===0?r.jsx("p",{children:"No socket activity yet."}):a.slice().reverse().map(d=>r.jsxs("div",{children:[r.jsx("span",{className:"text-slate-400",children:d.timestamp})," ",r.jsxs("span",{className:d.direction==="in"?"text-emerald-400":"text-amber-400",children:["[",d.direction.toUpperCase(),"]"]})," ",r.jsx("span",{className:"text-cyan-300",children:d.event})," ",r.jsx("span",{className:"text-slate-200",children:d.payload})]},d.id))}),r.jsx("p",{className:"text-xs text-slate-500",children:"Logs are collected only while this panel is open."})]})}function $7({label:t}){return t?r.jsx("span",{className:"rounded border border-white/40 px-1 text-[0.7rem] text-white",children:t}):null}const zQ=[{key:"start",label:"Start OI"},{key:"safe",label:"Safe"},{key:"full",label:"Full"},{key:"passive",label:"Passive"},{key:"dock",label:"Dock"}],Ru=[{key:"none",label:"Color"},{key:"grayscale",label:"Gray"},{key:"greenscale",label:"Green"}];function Cs(t){return Ru.some(a=>a.key===t)?t:w0.colorFilter}function i0({children:t,className:a=""}){return r.jsx("label",{className:`mx-auto grid w-full max-w-lg grid-cols-[minmax(0,1fr)_auto] items-center gap-1.5 rounded bg-neutral-800/80 px-1.5 py-1 text-sm text-white max-[420px]:grid-cols-1 ${a}`,children:t})}function f7({children:t}){return r.jsx("p",{className:"mx-auto w-full max-w-lg text-xs leading-snug text-white",children:t})}function m7({label:t,value:a,disabled:l=!1,onChange:o}){return r.jsxs("label",{className:"mx-auto block w-full max-w-lg rounded bg-neutral-800/80 px-1.5 py-1 text-sm text-white",children:[r.jsxs("div",{className:"grid grid-cols-[minmax(0,1fr)_auto] items-center gap-1.5",children:[r.jsx("span",{className:"min-w-0 font-semibold text-white",children:t}),r.jsxs("span",{className:"rounded bg-neutral-900 px-1 py-0.5 text-xs text-white",children:[Math.round(a*100),"%"]})]}),r.jsx("input",{type:"range",min:"0",max:"1",step:"0.01",value:a,onChange:o,className:"mt-1 w-full accent-emerald-500 disabled:opacity-50",disabled:l})]})}function Eu(){const t=R1(R=>R.state.keymap),a=R1(R=>R.state.roverId),{sendOiCommand:l,setSensorStream:o}=p3(),u=!!a,[d,p]=yu(),v=R2(),{value:g,save:x}=N1("page",{hudMapDesktop:!1,connectionTransport:"websocket",swapMobileControlColumns:!1,driveMacroBackoffEnabled:!0}),{value:z,save:b}=N1("audio",p2),{value:y,save:H}=N1("video",w0),S=g?.connectionTransport||"websocket",w=!!g?.swapMobileControlColumns,C=typeof g?.driveMacroBackoffEnabled=="boolean"?g.driveMacroBackoffEnabled:!0,V=Number.isFinite(z?.masterVolume)?z.masterVolume:p2.masterVolume,A=Number.isFinite(z?.alertVolume)?z.alertVolume:p2.alertVolume,B=Number.isFinite(z?.roverVolume)?z.roverVolume:p2.roverVolume,k=typeof z?.mainBrushDuckEnabled=="boolean"?z.mainBrushDuckEnabled:typeof z?.autoLevelEnabled=="boolean"?z.autoLevelEnabled:p2.mainBrushDuckEnabled,D=Number.isFinite(z?.mainBrushDuckAmount)?Math.max(0,Math.min(1,z.mainBrushDuckAmount)):p2.mainBrushDuckAmount,j=Cs(y?.colorFilter),N=q2(t?.videoFilterCycle?.[0]),E=f.useMemo(()=>[{key:"start",label:"Enable stream",enable:!0},{key:"stop",label:"Disable stream",enable:!1}],[]),Z=R=>{a&&o(R)},q=R=>{const G=R.target.value;x(e1=>({...e1??{},connectionTransport:G})),v?.io?.opts&&(v.io.opts.transports=G==="polling"?["polling"]:["websocket","polling"],v.disconnect(),v.connect())},T=R=>G=>{const e1=Number(G.target.value),d1=Number.isFinite(e1)?Math.max(0,Math.min(1,e1)):0;b(u1=>({...u1??{},[R]:d1}))},P=R=>{const G=!!R.target.checked;b(e1=>({...e1??{},mainBrushDuckEnabled:G}))},o1=R=>{const G=!!R.target.checked;x(e1=>({...e1??{},swapMobileControlColumns:G}))},n1=R=>{const G=!!R.target.checked;x(e1=>({...e1??{},driveMacroBackoffEnabled:G}))},I=R=>{const G=Cs(R.target.value);H(e1=>({...e1??{},colorFilter:G}))};return r.jsxs(ra,{defaultTab:"keybindings",children:[r.jsxs(ia,{children:[r.jsx(I2,{id:"keybindings",children:"Keybindings"}),r.jsx(I2,{id:"controller",children:"Controller"}),r.jsx(I2,{id:"page",children:"Page settings"}),r.jsx(I2,{id:"admin",children:"Admin"})]}),r.jsxs(sa,{children:[r.jsx(Y2,{id:"keybindings",children:r.jsx("div",{className:"space-y-0.5",children:r.jsx(WK,{})})}),r.jsx(Y2,{id:"controller",children:r.jsx("div",{className:"space-y-0.5",children:r.jsx(uQ,{})})}),r.jsx(Y2,{id:"page",children:r.jsxs("div",{className:"grid gap-1.5 lg:grid-cols-2",children:[r.jsxs(B1,{title:"HUD",bodyClassName:"space-y-1 p-1 text-sm",children:[r.jsxs(i0,{className:"grid-cols-[auto_minmax(0,1fr)] max-[420px]:grid-cols-[auto_minmax(0,1fr)]",children:[r.jsx("input",{type:"checkbox",className:"h-3.5 w-3.5 accent-emerald-500",checked:d,onChange:R=>p(R.target.checked)}),r.jsx("span",{className:"font-semibold text-white",children:"Show top-down map in HUD (desktop)"})]}),r.jsx(f7,{children:"Mobile HUD keeps the map on by default."})]}),r.jsxs(B1,{title:"Video",bodyClassName:"space-y-1 p-1 text-sm",children:[r.jsxs(i0,{children:[r.jsx("span",{className:"font-semibold text-white",children:"Rover video filter"}),r.jsx("select",{value:j,onChange:I,className:"field-input min-w-28 px-1 py-0.5 text-sm",children:Ru.map(R=>r.jsx("option",{value:R.key,children:R.label},R.key))})]}),r.jsx(f7,{children:r.jsxs("span",{className:"inline-flex flex-wrap items-center gap-1",children:[r.jsx("span",{children:"Use"}),N?r.jsx($7,{label:N}):null,r.jsx("span",{children:"to cycle filters. Applies only to rover camera pixels; HUD overlays stay full color."})]})})]}),r.jsx(B1,{title:"Mobile controls",bodyClassName:"space-y-1 p-1 text-sm",children:r.jsxs(i0,{className:"grid-cols-[auto_minmax(0,1fr)] max-[420px]:grid-cols-[auto_minmax(0,1fr)]",children:[r.jsx("input",{type:"checkbox",className:"h-3.5 w-3.5 accent-emerald-500",checked:w,onChange:o1}),r.jsx("span",{className:"font-semibold text-white",children:"Swap control columns (put joystick on the left)"})]})}),r.jsx(B1,{title:"Macros",bodyClassName:"space-y-1 p-1 text-sm",children:r.jsxs(i0,{className:"grid-cols-[auto_minmax(0,1fr)] max-[420px]:grid-cols-[auto_minmax(0,1fr)]",children:[r.jsx("input",{type:"checkbox",className:"h-3.5 w-3.5 accent-emerald-500",checked:C,onChange:n1}),r.jsx("span",{className:"font-semibold text-white",children:"Enable backward bump in drive macro"})]})}),r.jsxs(B1,{title:"Audio",className:"lg:col-span-2",bodyClassName:"space-y-1 p-1 text-sm",children:[r.jsx(m7,{label:"Master volume",value:V,onChange:T("masterVolume")}),r.jsx(m7,{label:"Alert/page sounds",value:A,onChange:T("alertVolume")}),r.jsx(m7,{label:"Rover audio",value:B,onChange:T("roverVolume")}),r.jsxs(i0,{children:[r.jsx("span",{className:"font-semibold text-white",children:"Main brush ducking"}),r.jsx("input",{type:"checkbox",className:"h-3.5 w-3.5 accent-emerald-500",checked:k,onChange:P})]}),r.jsx(m7,{label:"Main brush duck amount",value:D,onChange:T("mainBrushDuckAmount"),disabled:!k}),r.jsx(f7,{children:"Lowers rover audio only while the main brush is running."})]}),r.jsxs(B1,{title:"Connection",bodyClassName:"space-y-1 p-1 text-sm",children:[r.jsxs(i0,{children:[r.jsx("span",{className:"font-semibold text-white",children:"Transport"}),r.jsxs("select",{value:S,onChange:q,className:"field-input min-w-28 px-1 py-0.5 text-sm",children:[r.jsx("option",{value:"websocket",children:"WebSocket"}),r.jsx("option",{value:"polling",children:"Polling"})]})]}),r.jsx(f7,{children:"Switching reconnects your session."})]})]})}),r.jsx(Y2,{id:"admin",children:r.jsxs("div",{className:"space-y-0.5",children:[r.jsx(B1,{title:"Manual OI commands",bodyClassName:"space-y-0.5 text-sm",children:r.jsx("div",{className:"flex flex-wrap gap-0.5",children:zQ.map(R=>r.jsx("button",{type:"button",onClick:()=>l(R.key),disabled:!u,className:"button-dark text-xs disabled:opacity-30",children:R.label},R.key))})}),r.jsxs(B1,{title:"Sensor stream",bodyClassName:"space-y-0.5 text-sm",children:[r.jsx("div",{className:"flex gap-0.5",children:E.map(R=>r.jsx("button",{type:"button",onClick:()=>Z(R.enable),disabled:!u,className:"flex-1 button-dark text-xs disabled:opacity-30",children:R.label},R.key))}),!u&&r.jsx("p",{className:"text-xs text-slate-500",children:"Assign a rover to toggle streams."})]}),r.jsx(ju,{}),r.jsx(fQ,{}),r.jsx($K,{}),r.jsx(vQ,{}),r.jsx(xQ,{})]})})]})]})}const ws={desktop:{main:[{type:"list",title:"Chat and nicknames",items:["Set a nickname in the user list panel, on the bottom left of the page below the rover video.",{segments:["Toggle chat focus with ",{action:"chatFocus"},". Press ",{action:"chatFocus"}," again to send."]}]},{type:"list",title:"Driving the rover",items:[{segments:['Press the "Start Driving" button onscreen, or press ',{action:"driveMacro"}," on your keyboard to put the rover into driving mode."]},"Refer to the controls for the controls for the rover."]},{type:"list",title:"Docking the rover",items:['If the rover shows "Docking in Progress", it is already auto-seeking the dock.',"To dock manually, enter Docking Assist from the drive panel.",{segments:['Press "Enter Docking Assist", or press ',{action:"dockMacro"},"."]},"In assist mode, camera tilts down and driving speed is limited for precise alignment."]}],aside:[{type:"keyboard",title:"Keyboard controls",footnote:"Per-browser; adjust bindings and speeds in Settings → Keybindings.",groups:[{id:"movement",title:"Movement",items:[{action:"driveForward",label:"Forward"},{action:"driveBackward",label:"Backward"},{action:"driveLeft",label:"Turn left"},{action:"driveRight",label:"Turn right"},{action:"boostModifier",label:"Boost speed"},{action:"slowModifier",label:"Precision speed"}]},{id:"macros",title:"Rover modes & chat",items:[{action:"driveMacro",label:"Drive macro"},{action:"dockMacro",label:"Docking assist toggle"},{action:"chatFocus",label:"Chat focus"}]},{id:"camera",title:"Camera",items:[{action:"cameraUp",label:"Tilt up"},{action:"cameraDown",label:"Tilt down"},{action:"nightVisionToggle",label:"Toggle night vision"}]},{id:"motors",title:"Rover Aux Motors",items:[{action:"auxMainForward",label:"Main brush forward"},{action:"auxMainReverse",label:"Main brush reverse"},{action:"auxSideForward",label:"Side brush forward"},{action:"auxSideReverse",label:"Side brush reverse"},{action:"auxVacuumFast",label:"Vacuum max"},{action:"auxVacuumSlow",label:"Vacuum low"},{action:"auxAllForward",label:"All motors forward"}]},{id:"audio",title:"Audio / Song",items:[{action:"songNoteUp",label:"Song note up"},{action:"songNoteDown",label:"Song note down"}]},{id:"room-controls",title:"Room Controls",items:[{action:"homeAssistantOn",label:"Next room control on"},{action:"homeAssistantOff",label:"Next room control off (reverse)"}]}]},{type:"gamepad",title:"Gamepad / joystick",items:["Gamepad controls are fully configurable to handle inconsistent browser mappings.","Use Settings → Controller to bind inputs, calibrate deadzones, and view diagnostics.","If inputs behave oddly, the diagnostics panel will show raw axis/button values."]}]},"mobile-portrait":{main:[{type:"list",title:"Chat and nicknames",items:["Set a nickname in the user list panel below.","Tap in the chat box to send messages in the chat."]},{type:"list",title:"Driving the rover",items:[{segments:['Press the "Start Driving" button onscreen to put the rover into driving mode.']},"Look below the rover video. Use the joystick column to move the rover, and hold the aux buttons in the other control column."]},{type:"list",title:"Docking the rover",items:['If you see "Docking in Progress", the rover is currently auto-seeking the dock.',{segments:['For manual docking, press "Enter Docking Assist".']},"Assist mode tilts camera down and limits speed for precise alignment."]}]},"mobile-landscape":{main:[{type:"list",title:"Chat and nicknames",items:["Scroll down to see more of the page.","Set a nickname in the user list panel below.","Tap in the chat box to send messages in the chat."]},{type:"list",title:"Driving the rover",items:[{segments:['Press the "Start Driving" button, or the "Drive" button to put the rover into driving mode.']},"Use the joystick column beside the video feed to move the rover, and hold the aux buttons in the other control column."]},{type:"list",title:"Docking the rover",items:['If you see "Docking in Progress", the rover is currently auto-seeking the dock.',{segments:['For manual docking, press "Enter Docking Assist" (or "Dock" button).']},"Assist mode tilts camera down and limits speed for precise alignment."]}]}};function bQ(t){return ws[t]||ws.desktop}function Tu({actionId:t,keymap:a}){const l=a?.[t]?.[0]??"";return r.jsx("span",{className:"rounded border border-slate-600 bg-slate-900/40 px-1 text-[0.7rem] text-slate-200",children:q2(l)})}function Hs(t,a){return t.map((l,o)=>typeof l=="string"?r.jsx("span",{children:l},`text-${o}`):l?.action?r.jsx(Tu,{actionId:l.action,keymap:a},`pill-${l.action}-${o}`):l?.text?r.jsx("span",{children:l.text},`span-${o}`):null)}function nt(t,a,l){if(typeof t=="string")return t;if(Array.isArray(t?.segments))return Hs(t.segments,a);if(t&&typeof t=="object"){if(typeof t.text=="string")return t.text;if(Array.isArray(t))return Hs(t,a)}return String(t??"")}function MQ({hero:t,keymap:a}){return t?r.jsxs("div",{className:"surface space-y-0.5 px-0.5 py-0.5",children:[r.jsxs("div",{className:"flex flex-wrap items-center justify-between gap-0.5",children:[r.jsxs("div",{children:[r.jsx("p",{className:"text-sm font-semibold text-white",children:t.title}),t.subtitle&&r.jsx("p",{className:"text-xs text-slate-300",children:t.subtitle})]}),t.chips&&r.jsx("div",{className:"flex flex-wrap gap-0.5 text-[0.7rem] text-slate-200",children:t.chips.map(l=>r.jsx("span",{className:"rounded border border-slate-700 px-1 py-[2px]",children:l},l))})]}),t.bullets&&r.jsx("ul",{className:"space-y-0.5 text-[0.85rem] text-slate-200",children:t.bullets.map((l,o)=>{const u=Array.isArray(l?.segments)?`hero-${o}`:`hero-${o}`;return r.jsx("li",{className:"surface-muted px-0.5 py-0.25",children:nt(l,a)},u)})})]}):null}function yQ({block:t,keymap:a}){return r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-xs font-semibold text-slate-200",children:t.title}),r.jsx("ul",{className:"space-y-0.5 text-[0.8rem] text-slate-300",children:t.items.map((l,o)=>{const u=`item-${o}`;return r.jsx("li",{className:"surface-muted flex flex-wrap items-center gap-0.5 px-0.5 py-0.25",children:nt(l,a)},u)})})]})}function CQ({block:t}){const a=t.tone==="info"?"border-cyan-500/40":"border-slate-700";return r.jsxs("div",{className:`surface space-y-0.5 border ${a} px-0.5 py-0.5`,children:[r.jsx("p",{className:"text-xs font-semibold text-slate-100",children:t.title}),t.body&&r.jsx("ul",{className:"space-y-0.5 text-[0.8rem] text-slate-300",children:t.body.map((l,o)=>r.jsx("li",{className:"surface-muted px-0.5 py-0.25",children:nt(l,{})},`callout-${o}`))})]})}function wQ({group:t,keymap:a}){return r.jsxs("div",{className:"space-y-0.5 surface",children:[r.jsx("p",{className:"px-0.5 py-0.25 text-[0.75rem] font-semibold text-slate-200",children:t.title}),r.jsx("div",{className:"space-y-0.5 px-0.5 pb-0.25",children:t.items.map(l=>r.jsxs("div",{className:"surface-muted flex items-center justify-between gap-0.5 px-0.5 py-0.25 text-[0.8rem]",children:[r.jsx("span",{className:"text-slate-200",children:l.label}),r.jsx(Tu,{actionId:l.action,keymap:a})]},l.action))})]})}function HQ({block:t,keymap:a}){return t?r.jsxs("div",{className:"space-y-0.5",children:[r.jsxs("div",{className:"flex items-center justify-between text-xs text-slate-200",children:[r.jsx("span",{className:"font-semibold",children:t.title}),t.footnote&&r.jsx("span",{className:"text-[0.7rem] text-slate-400",children:t.footnote})]}),r.jsx("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-0.5",children:t.groups?.map(l=>r.jsx(wQ,{group:l,keymap:a},l.id))})]}):null}function SQ({block:t}){return t?r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-xs font-semibold text-slate-200",children:t.title}),r.jsx("ul",{className:"space-y-0.5 text-[0.8rem] text-slate-300",children:t.items?.map((a,l)=>r.jsx("li",{className:"surface-muted px-0.5 py-0.25",children:nt(a,{})},`gamepad-${l}`))})]}):null}function Ss({block:t,keymap:a}){if(!t)return null;switch(t.type){case"list":return r.jsx(yQ,{block:t,keymap:a});case"callout":return r.jsx(CQ,{block:t});case"keyboard":return r.jsx(HQ,{block:t,keymap:a});case"gamepad":return r.jsx(SQ,{block:t});default:return null}}function qu({layout:t,keymap:a}){const l=f.useMemo(()=>bQ(t),[t]),o=a||{},u=l.main||[],d=l.aside||[];return r.jsxs("div",{className:"space-y-0.5",children:[r.jsx(MQ,{hero:l.hero,keymap:o}),r.jsxs("div",{className:"grid gap-0.5",style:{gridTemplateColumns:"repeat(auto-fit, minmax(360px, 1fr))"},children:[r.jsx("div",{className:"space-y-0.5",children:u.map((p,v)=>r.jsx(Ss,{block:p,keymap:o},p.title||v))}),r.jsx("div",{className:"space-y-0.5",children:d.map((p,v)=>r.jsx(Ss,{block:p,keymap:o},p.title||v))})]})]})}function Du({layout:t,onOpenOverlay:a}){const l=R1(u=>u.state.keymap),o=r.jsx("button",{type:"button",onClick:a,className:"button-dark px-1 py-0.25 text-[0.75rem]",children:"Open full help"});return r.jsx(B1,{title:"Help",actions:o,bodyClassName:"space-y-0.5 text-sm",children:r.jsx(qu,{layout:t,keymap:l||{}})})}function lt({compact:t=!1}){const{setNickname:a}=$2(),{value:l,save:o}=N1("profile",{nickname:""}),[u,d]=f.useState(l.nickname||""),[p,v]=f.useState(!1);f.useEffect(()=>{d(l.nickname||"")},[l.nickname]);async function g(x){x.preventDefault();const z=(u||"").trim().slice(0,32);if(z){v(!0);try{await a(z),o({nickname:z})}catch(b){alert(b.message)}finally{v(!1)}}}return r.jsxs("div",{className:"grid w-full min-w-0 grid-cols-[minmax(0,1fr)_auto] items-stretch gap-0.5",children:[r.jsx("input",{className:"field-input flex-1 min-w-0",value:u,onChange:x=>d(x.target.value),onKeyDown:x=>{x.key==="Enter"&&(x.preventDefault(),g(x))},maxLength:32,placeholder:"Enter a nickname"}),r.jsx("button",{type:"button",onClick:g,disabled:p,className:"button-dark h-full shrink-0 whitespace-nowrap px-0.5 py-0 disabled:opacity-50",children:p?"Saving…":t?"Set":"Save"})]})}const Vs=["kal","rms","slt","ksp","bdl"],As=["sfg","iob","iog","iol","iom","tpc","tpd","tpf"],VQ=Array.from({length:10},(t,a)=>a*10),Ls=[.5,.75,1,1.25,1.5,1.75,2],Bs=1,u0={engine:"flite",voice:"rms",pitch:50,googlePitch:Bs,googleSpeed:Bs};function AQ(t){return{engine:t?.engine||u0.engine,voice:t?.voice||u0.voice,pitch:Number.isFinite(t?.pitch)?t.pitch:u0.pitch,googlePitch:Number.isFinite(t?.googlePitch)?t.googlePitch:u0.googlePitch,googleSpeed:Number.isFinite(t?.googleSpeed)?t.googleSpeed:u0.googleSpeed}}function LQ(t){const a=h1(d=>d.session?.role||null),l=h1(d=>d.session?.assignment?.roverId||null),o=h1(d=>d.session?.roster??[]),u=f.useMemo(()=>o.find(d=>String(d.id)===String(l))||null,[l,o]);return{canChat:a!=="spectator"||t,ttsSupported:!!u?.audio?.ttsEnabled}}function BQ({fillHeight:t=!1}){const{messages:a,typing:l}=_I(),o=f.useRef(null),u=f.useMemo(()=>a.slice(-200),[a]),d=f.useMemo(()=>l||[],[l]),p=t?"flex-1 min-h-0 overflow-y-auto":"h-48 overflow-y-auto";return f.useEffect(()=>{o.current&&(o.current.scrollTop=o.current.scrollHeight)},[u,d]),r.jsxs("div",{className:`overflow-y-auto space-y-0.5 px-0 ${p}`,ref:o,children:[u.length===0&&d.length===0?r.jsx("p",{className:"text-sm text-slate-500",children:"No messages yet."}):u.map(v=>r.jsx(Pc,{message:v},v.id)),d.map(v=>r.jsx(Uo,{message:v},`typing-${v.typingId||v.id}`))]})}const jQ=f.memo(lt);function NQ({ttsSupported:t,speak:a,onSpeakChange:l,engine:o,voice:u,pitch:d,googlePitch:p,googleSpeed:v,saveTtsSettings:g}){if(!t)return null;const x="field-input min-w-0 max-w-full px-0 text-xs";return r.jsxs("div",{className:"chat-composer-tts",children:[r.jsxs("label",{className:"flex shrink-0 items-center gap-0.5 whitespace-nowrap text-xs text-slate-300",children:[r.jsx("input",{type:"checkbox",checked:a,onChange:z=>l(z.target.checked),className:"accent-cyan-500"}),r.jsx("span",{children:"Speak"})]}),r.jsxs("select",{value:o,onChange:z=>{const b=z.target.value,y=b==="chromegtts"&&!As.includes(u)?"tpf":b==="flite"&&!Vs.includes(u)?"rms":u;g(H=>({...H||{},engine:b,voice:y}))},className:`${x} w-[5.5rem] shrink`,children:[r.jsx("option",{value:"flite",children:"flite"}),r.jsx("option",{value:"espeak",children:"espeak"}),r.jsx("option",{value:"chromegtts",children:"Google speech"})]}),o==="flite"||o==="chromegtts"?r.jsxs(r.Fragment,{children:[r.jsx("select",{value:u,onChange:z=>{const b=z.target.value;g(y=>({...y||{},voice:b}))},className:`${x} w-[3.25rem] shrink`,children:(o==="chromegtts"?As:Vs).map(z=>r.jsx("option",{value:z,children:z},z))}),o==="chromegtts"&&r.jsxs(r.Fragment,{children:[r.jsx("select",{value:p,onChange:z=>{const b=Number(z.target.value);g(y=>({...y||{},googlePitch:b}))},className:`${x} w-[5rem] shrink`,children:Ls.map(z=>r.jsxs("option",{value:z,children:["pitch ",z.toFixed(2)]},`pitch-${z}`))}),r.jsx("select",{value:v,onChange:z=>{const b=Number(z.target.value);g(y=>({...y||{},googleSpeed:b}))},className:`${x} w-[5rem] shrink`,children:Ls.map(z=>r.jsxs("option",{value:z,children:["speed ",z.toFixed(2)]},`speed-${z}`))})]})]}):r.jsx("select",{value:d,onChange:z=>{const b=Number(z.target.value);g(y=>({...y||{},pitch:b}))},className:`${x} w-[4.5rem] shrink`,children:VQ.map(z=>r.jsxs("option",{value:z,children:["pitch ",z]},z))})]})}function FQ({allowSpectatorInput:t=!1,hideSpectatorNotice:a=!1}){const{sendMessage:l,registerInputRef:o,onInputFocus:u,onInputBlur:d,blurChat:p,setTypingActive:v}=Qc(),{canChat:g,ttsSupported:x}=LQ(t),{value:z,save:b}=N1("tts",u0),{engine:y,voice:H,pitch:S,googlePitch:w,googleSpeed:C}=AQ(z),[V,A]=f.useState(""),[B,k]=f.useState(!1),[D,j]=f.useState(!0),N=x&&D,E=f.useMemo(()=>N?y==="espeak"?{speak:!0,engine:y,pitch:S}:y==="chromegtts"?{speak:!0,engine:y,voice:H,pitch:w,speed:C}:{speak:!0,engine:y,voice:H}:null,[N,y,w,C,S,H]);async function Z(q){if(q.preventDefault(),!g)return;const P=V.replace(/\\n/g,` -`).trim();if(P){k(!0);try{await l(P,E),A(""),p(),v(!1)}catch(o1){alert(o1.message)}finally{k(!1)}}}return r.jsxs("form",{className:"chat-composer",onSubmit:Z,children:[r.jsx("div",{className:"chat-composer-nickname",children:r.jsx(jQ,{compact:!0})}),r.jsx("input",{className:"field-input chat-composer-input",value:V,onChange:q=>{const T=q.target.value;A(T),v(!!T.trim())},onFocus:q=>{u(q),v(!!V.trim())},onBlur:q=>{d(q),v(!1)},onKeyDown:q=>{q.key==="Enter"&&!V.trim()&&(q.preventDefault(),p(),v(!1))},ref:q=>o(q,{target:"panel"}),placeholder:g?"Type a message…":a?"":"Spectators cannot chat",disabled:!g}),r.jsx("button",{type:"submit",disabled:!g||B,className:"button-dark chat-composer-send disabled:opacity-50",children:B?"...":"Send"}),r.jsx(NQ,{ttsSupported:x,speak:N,onSpeakChange:j,engine:y,voice:H,pitch:S,googlePitch:w,googleSpeed:C,saveTtsSettings:b})]})}const kQ=f.memo(BQ),RQ=f.memo(FQ);function U8({hideInput:t=!1,hideSpectatorNotice:a=!1,fillHeight:l=!1,allowSpectatorInput:o=!1,title:u="Chat and speech",minimal:d=!1}){const p=d||t,v=d?"":u;return r.jsxs(B1,{title:v,hideHeader:d||!v,fillHeight:l,bodyClassName:"space-y-0.5 text-base",children:[r.jsx(kQ,{fillHeight:l}),!p&&r.jsx(RQ,{allowSpectatorInput:o,hideSpectatorNotice:a})]})}function oa({className:t=""}){const a=h1(l=>l.session?.socials??[]).slice(0,4);return a.length?r.jsx("div",{className:`grid grid-cols-2 grid-rows-2 gap-0.5 ${t}`,children:a.map(l=>r.jsx(et,{id:l?.id||l?.key||l?.service||null},`${l?.id||l?.label||"social"}-${l?.url||""}`))}):null}function EQ(){return r.jsx(B1,{title:"Links!",fillHeight:!0,bodyClassName:"flex flex-1 min-h-0 flex-col gap-0.5 text-base",children:r.jsx(oa,{className:"flex-1 min-h-0"})})}function tc(...t){return t.filter(Boolean).join(" ")}function TQ(t){const a=t?.batteryState?.percentDisplay;return a==null?"--":`${a}%`}function qQ(t){return t?.batteryState?t.batteryState.urgentActive?"text-red-400":t.batteryState.warnActive?"text-amber-300":"text-emerald-300":"text-slate-400"}function DQ(t){switch(t){case"admin":case"lockdown":case"lockdown-admin":return"text-amber-300";case"spectator":return"text-slate-400";default:return"text-sky-300"}}function OQ(t,a){if(!t)return"";const l=t.nickname||t.socketId?.slice(0,6)||"unknown";return t.socketId&&t.socketId===a?`${l} (you)`:l}function rt({title:t="Rovers"}){const a=h1(N=>N.session?.role||null),l=h1(N=>N.session?.roster??[]),o=h1(N=>N.session?.turnQueues??{}),u=h1(N=>N.session?.users??[]),d=h1(N=>N.session?.socketId||null),p=h1(N=>String(N.session?.assignment?.roverId||"").trim()),v=h1(N=>{const E=String(N.session?.assignment?.roverId||"").trim();return E?(N.session?.roster||[]).find(q=>String(q?.id)===E)?.name||E:""}),{requestControl:g,rebootOwnRover:x}=$2(),[z,b]=f.useState({}),[y,H]=f.useState(!1),S=f.useMemo(()=>a&&a!=="spectator",[a]),w=f.useMemo(()=>a==="admin"||a==="lockdown"||a==="lockdown-admin",[a]),C=f.useMemo(()=>Object.values(o||{}).some(N=>N?.deadline||N?.idleDeadline),[o]),V=D8(1e3,C),A=f.useMemo(()=>{const N=new Set(l.map(Z=>String(Z.id))),E=Object.keys(o||{}).filter(Z=>!N.has(String(Z))).map(Z=>({id:Z,name:Z,locked:!1,batteryState:null}));return[...l,...E]},[l,o]);async function B(N){if(N){b(E=>({...E,[N]:!0}));try{await g(N)}catch(E){alert(E.message)}finally{b(E=>({...E,[N]:!1}))}}}const k=N=>u.find(E=>E.socketId===N)||{socketId:N,nickname:null,role:null};async function D(){if(!(y||!window.confirm(`Reboot your rover "${v}" now?`))){H(!0);try{await x(),alert("Reboot command sent.")}catch(E){alert(E.message)}finally{H(!1)}}}const j=a!=="spectator"&&p?r.jsx("button",{type:"button",onClick:D,disabled:y,className:"button-dark bg-amber-700/80 text-amber-50 hover:bg-amber-600 disabled:opacity-40",title:"Reboot your assigned rover",children:y?"Rebooting...":"Reboot My Rover"}):null;return r.jsx(B1,{title:t,actions:j,bodyClassName:"space-y-0.5 text-sm",children:A.length===0?r.jsx("p",{className:"text-sm text-slate-500",children:"No rovers registered."}):r.jsx("ul",{className:"space-y-0.5 text-sm",children:A.map(N=>{const E=String(N.id),Z=o?.[E]||null,q=Z?.queue||[],T=Z?.idleDeadline||Z?.deadline||null,P=T&&T>V?Math.ceil((T-V)/1e3):T?0:null,o1=Z?.current||null,n1=o1?q.findIndex(m1=>m1===o1):-1,I=q.length>1?n1>=0?q[(n1+1)%q.length]:q[0]:null,R=!!(d&&o1&&o1===d),G=!!(d&&I&&I===d),e1=P!=null&&(R||G),d1=!!(N?.private?.enabled&&N?.private?.open),u1=!!(N?.private?.enabled&&!N?.private?.open),F=!!N.locked,_=F&&!w&&!u1,t1=N.lockReason?`locked: ${N.lockReason}`:"locked",r1=z[E]?"...":F&&!u1?t1:"request",f1=S&&!_&&!z[E];return r.jsxs("li",{className:tc("surface flex flex-wrap items-start justify-between gap-0.5",f1&&"cursor-pointer",F?"bg-red-900/40":d1?"bg-amber-700/35 border border-amber-200/30":null),onClick:()=>{f1&&B(N.id)},children:[r.jsxs("div",{className:"flex min-w-0 flex-1 flex-col gap-0.5",children:[r.jsxs("div",{className:"flex items-center justify-between gap-0.5",children:[r.jsxs("div",{className:"flex min-w-0 items-center gap-0.5",children:[r.jsxs("p",{className:"min-w-0 flex items-center gap-0.5 whitespace-nowrap text-slate-200",children:[r.jsx(V4,{rover:N,fallback:E}),N.description?r.jsx("span",{className:"min-w-0 flex-1 truncate text-[0.7rem] text-slate-400",children:N.description}):null]}),e1?r.jsx("span",{className:"rounded bg-slate-800 px-1 text-[0.7rem] text-slate-200",children:R?`${P}s left`:`Your turn in ${P}s`}):null]}),r.jsx("span",{className:tc("text-[0.75rem] font-semibold",qQ(N)),children:TQ(N)})]}),q.length===0?r.jsx("p",{className:"text-[0.7rem] text-slate-500",children:"No queue."}):r.jsx("div",{className:"flex flex-wrap items-center gap-0.5",children:q.map((m1,S1)=>{const V1=k(m1),A1=m1===o1,U1=!!(I&&m1===I&&!A1),Y1=A1?"bg-sky-600 text-white ring-2 ring-amber-300":U1?"bg-emerald-700/60 text-emerald-100 ring-1 ring-emerald-300/70":"bg-slate-800 text-slate-200";return r.jsxs("span",{className:`flex items-center gap-0.5 rounded px-1 text-[0.7rem] ${Y1}`,children:[r.jsx("span",{className:`${DQ(V1.role)} font-semibold`,children:OQ(V1,d)}),A1&&r.jsx("span",{className:"text-[0.65rem] text-slate-200",children:"now"}),U1&&r.jsx("span",{className:"text-[0.65rem] text-emerald-100",children:"next"})]},`${E}-${m1}-${S1}`)})})]}),S?r.jsx("button",{type:"button",onClick:m1=>{m1.stopPropagation(),B(N.id)},disabled:z[E]||_,className:tc("button-dark disabled:opacity-40",F&&"bg-red-600/70 text-white hover:bg-red-600"),children:r1}):null]},N.id)})})})}function _Q(t){switch(t){case"admin":case"lockdown":case"lockdown-admin":return"text-amber-300";case"spectator":return"text-slate-400";default:return"text-sky-300"}}function UQ(t,a){if(!t)return"";const l=t.nickname||t.socketId?.slice(0,6)||"unknown";return t.socketId&&t.socketId===a?`${l} (you)`:l}function G7({hideNicknameForm:t=!1,hideHeader:a=!1,className:l="",fillHeight:o=!1,compact:u=!1}){const d=h1(x=>x.session?.users??[]),p=h1(x=>x.session?.socketId||null),v=f.useMemo(()=>[...d].sort((x,z)=>x.socketId===p?-1:z.socketId===p?1:(x.nickname||"").localeCompare(z.nickname||"")),[p,d]),g=o?"flex-1 min-h-0 overflow-y-auto":u?"h-28 overflow-y-auto":"h-48 overflow-y-auto";return r.jsxs(B1,{title:a?"":"Users",hideHeader:a,fillHeight:o,className:l,bodyClassName:"space-y-0.5 text-base",children:[!t&&r.jsx("div",{className:"space-y-0.5",children:r.jsxs("div",{className:"grid gap-0.5 md:grid-cols-[minmax(0,1.4fr)_minmax(0,1fr)]",children:[r.jsx("div",{className:"min-w-0",children:r.jsx("div",{className:"flex w-full items-center px-0 py-0",children:r.jsx(lt,{compact:u})})}),r.jsx(oa,{})]})}),r.jsx("div",{className:`space-y-0.5 ${o?"flex flex-1 min-h-0 flex-col":""}`,children:r.jsx("div",{className:`flex flex-wrap content-start items-start gap-0.5 px-0 pb-0 ${g} ${u?"text-[0.8rem]":""}`,children:v.length===0?r.jsx("p",{className:"text-sm text-slate-500",children:"Waiting for users…"}):v.map(x=>r.jsx("span",{className:`rounded px-1 py-0.25 text-[0.7rem] ${_Q(x.role)}`,children:UQ(x,p)},x.socketId))})})]})}function v7(t){return typeof t!="number"||Number.isNaN(t)?"—":`${t>0?"+":""}${t.toFixed(1)}°`}function js({label:t}){return t?r.jsx("span",{className:"rounded bg-slate-800 px-1 py-0.5 text-[0.6rem] font-semibold text-slate-200",children:t}):null}function ZQ({value:t,min:a,max:l,step:o=.5,label:u="Camera Tilt",orientation:d="horizontal",onChange:p,onCommit:v,throttleMs:g=0,disabled:x=!1,keyDownLabel:z,keyUpLabel:b,className:y="",labelRowClass:H="",labelClass:S="",valueClass:w="",sliderClass:C="",endpointClass:V="",endpointLabelClass:A="",accentClass:B="",showValue:k=!0,showEndpoints:D}){const j=d==="vertical",E=typeof D=="boolean"?D:!j,[Z,q]=f.useState(t),T=f.useRef(!1),P=f.useRef(null);f.useEffect(()=>{T.current||q(t)},[t]),f.useEffect(()=>()=>{P.current&&clearTimeout(P.current)},[]);const o1=e1=>{if(p){if(g>0){P.current&&clearTimeout(P.current),P.current=setTimeout(()=>{p(e1)},g);return}p(e1)}},n1=e1=>{const d1=Number.parseFloat(e1.target.value);Number.isNaN(d1)||(T.current=!0,q(d1),o1(d1))},I=()=>{T.current=!1,P.current&&(clearTimeout(P.current),P.current=null),v?v(Z):g>0&&p?.(Z)};if(typeof a!="number"||typeof l!="number")return null;const R=[j?"h-28 w-5":"w-full",B,C].filter(Boolean).join(" "),G=r.jsx("input",{type:"range",orient:j?"vertical":void 0,min:a,max:l,step:o,value:Number.isFinite(Z)?Z:a,onChange:n1,onMouseUp:I,onTouchEnd:I,onPointerUp:I,disabled:x,className:R,style:j?{writingMode:"bt-lr",WebkitAppearance:"slider-vertical"}:void 0});return j?r.jsxs("div",{className:`flex h-full flex-col items-center gap-0.5 ${y}`.trim(),children:[k?r.jsx("div",{className:`flex flex-col items-center ${H}`.trim(),children:r.jsx("span",{className:w,children:v7(t)})}):null,r.jsxs("div",{className:"flex min-h-0 flex-1 items-center gap-0.5",children:[r.jsx("span",{className:S,children:u}),G]})]}):r.jsxs("div",{className:`space-y-0.5 ${x?"opacity-50":""} ${y}`.trim(),children:[r.jsxs("div",{className:`flex items-center justify-between ${H}`.trim(),children:[r.jsx("span",{className:S,children:u}),k?r.jsx("span",{className:`rounded bg-slate-800 px-1 py-0.5 text-[0.7rem] font-semibold ${w}`.trim(),children:v7(t)}):null]}),r.jsxs("div",{children:[G,E?r.jsxs("div",{className:`mt-0 flex items-center justify-between ${V}`.trim(),children:[r.jsxs("span",{className:`flex items-center gap-0.5 ${A}`.trim(),children:[z?r.jsx(js,{label:z}):null,v7(a)]}),r.jsxs("span",{className:`flex items-center gap-0.5 ${A}`.trim(),children:[v7(l),b?r.jsx(js,{label:b}):null]})]}):null]})]})}const $Q=/^cu_[a-f0-9]{32}$/,z0="field-input w-full max-w-sm text-left focus:ring-emerald-500",Z8="mx-auto w-full max-w-xl flex justify-center",v0="mx-auto flex w-full max-w-md flex-col items-center space-y-0.5 text-center";function GQ(t){const a=String(t||"").trim();return a?a.length<=10?`${a.slice(0,2)}***${a.slice(-2)}`:`${a.slice(0,6)}...${a.slice(-6)}`:""}const Ns=8*1024*1024,u6=16e3,PQ={iceServers:[{urls:"stun:stun.l.google.com:19302"}],bundlePolicy:"max-bundle",rtcpMuxPolicy:"require"};function Fs(t){let a="";for(let o=0;o{if(!t||t.iceGatheringState==="complete"){l();return}const o=setTimeout(()=>{t.removeEventListener("icegatheringstatechange",u),l()},a);function u(){t.iceGatheringState==="complete"&&(clearTimeout(o),t.removeEventListener("icegatheringstatechange",u),l())}t.addEventListener("icegatheringstatechange",u)})}function ks(t){if(!t)return!1;const a=t.connectionState,l=t.iceConnectionState;return a==="connected"||l==="connected"||l==="completed"}function QQ(t,a=1e4){return new Promise((l,o)=>{if(!t){o(new Error("Peer connection missing"));return}if(ks(t)){l();return}const u=setTimeout(()=>{p(),o(new Error("Peer connection timeout"))},a),d=()=>{ks(t)?(p(),l()):(t.connectionState==="failed"||t.connectionState==="closed"||t.iceConnectionState==="failed")&&(p(),o(new Error(`Peer connection ${t.connectionState||t.iceConnectionState}`)))};function p(){clearTimeout(u),t.removeEventListener("connectionstatechange",d),t.removeEventListener("iceconnectionstatechange",d)}t.addEventListener("connectionstatechange",d),t.addEventListener("iceconnectionstatechange",d)})}async function XQ(t){if(!t?.getParameters||!t?.setParameters)return;const a=t.getParameters()||{},l=a.encodings&&a.encodings[0]||{};a.encodings=[{...l,maxBitrate:64e3,dtx:"disabled"}];try{await t.setParameters(a)}catch{}}function JQ(t,a=6e3){return new Promise((l,o)=>{if(!t){o(new Error("Peer connection missing"));return}const u=Date.now();let d=-1;const p=setInterval(async()=>{if(Date.now()-u>a){clearInterval(p),o(new Error("WHIP connected but no outbound audio flow"));return}try{const v=t.getSenders().filter(g=>g.track?.kind==="audio");for(const g of v){const x=await g.getStats();for(const z of x.values()){if(z.type!=="outbound-rtp"||z.kind!=="audio")continue;const b=Number(z.bytesSent||0),y=Number(z.packetsSent||0);if(d<0)d=b;else if(b>d+200||y>5){clearInterval(p),l();return}}}}catch{}},250)})}function WQ(t){const a=t.reduce((u,d)=>u+d.length,0),l=new Float32Array(a);let o=0;for(const u of t)l.set(u,o),o+=u.length;return l}function tX(t){const a=new Int16Array(t.length);for(let l=0;l{for(let z=0;zx1.state.keymap),g=R1(x1=>!!x1.state.mic?.pttActive),{value:x,save:z}=N1("vipAudio",{openMicEnabled:!1,pttMode:"live"}),b=String(t||"").trim(),[y,H]=f.useState(null),[S,w]=f.useState(!1),C=!!x?.openMicEnabled,A=(x?.pttMode==="clip"?"clip":"live")==="clip",[B,k]=f.useState("idle"),[D,j]=f.useState("idle"),[N,E]=f.useState(""),Z=f.useRef(null),q=f.useRef(null),T=f.useRef(null),P=f.useRef(!1),o1=f.useRef(""),n1=f.useRef(null),I=f.useRef(null),R=f.useRef(null),G=f.useRef(null),e1=f.useRef(null),d1=f.useRef([]),u1=f.useRef(!1),F=f.useRef(u6),_=f.useMemo(()=>b&&a?.[b]||null,[a,b]),t1=!!(_&&_.state!=="offline"&&!_.error),r1=_?.source==="upload"&&_?.state==="playing",f1=_?.source==="mic-whip"&&(_?.state==="starting"||_?.state==="playing"),m1=!!(!A&&q.current&&(C||g)),S1=!A&&(B==="live"||B==="starting"),V1=A&&D==="recording",A1=A&&D==="sending",U1=q2(v?.micPtt?.[0])||"M",Y1=f.useCallback(x1=>{const M1=x1==="clip"?"clip":"live";z(G1=>({...G1||{},pttMode:M1}))},[z]),E1=async()=>{if(!b){E("Take control of your rover first.");return}if(!y){E("Select an audio file first.");return}if(y.size>Ns){E(`File too large (max ${Ns} bytes).`);return}w(!0),E("");try{const x1=await y.arrayBuffer(),M1=Fs(new Uint8Array(x1));await l?.({roverId:b,name:y.name,mime:y.type||"",dataBase64:M1}),E("Upload playback started.")}catch(x1){E(x1?.message||"Failed to play upload.")}finally{w(!1)}},g1=async()=>{if(!b){E("Take control of your rover first.");return}w(!0),E("");try{await o?.(b),E("Upload playback stopped.")}catch(x1){E(x1?.message||"Failed to stop upload.")}finally{w(!1)}},Q=f.useCallback(async x1=>{const M1=String(x1||o1.current||"").trim();if(P.current=!1,k("idle"),T.current){try{T.current.getSenders().forEach(G1=>G1.track?.stop())}catch{}try{T.current.close()}catch{}}if(T.current=null,Z.current)try{Z.current.getTracks().forEach(G1=>G1.stop())}catch{}if(Z.current=null,q.current=null,M1)try{await p?.(M1)}catch{}o1.current=""},[p]),v1=f.useCallback(async x1=>{const M1=await u?.(x1),G1=String(M1?.whipUrl||"").trim(),A2=String(M1?.token||"").trim();if(!G1||!A2)throw new Error("WHIP endpoint unavailable");const P1=await navigator.mediaDevices.getUserMedia({audio:{channelCount:1,sampleRate:u6,echoCancellation:!1,noiseSuppression:!1,autoGainControl:!1}});Z.current=P1;const v2=P1.getAudioTracks()?.[0];if(q.current=v2||null,v2&&(v2.enabled=!!(C||g)),v2?.applyConstraints)try{await v2.applyConstraints({channelCount:1,sampleRate:u6,echoCancellation:!1,noiseSuppression:!1,autoGainControl:!1})}catch{}const X1=new RTCPeerConnection(PQ);T.current=X1,X1.onconnectionstatechange=()=>{if(!P.current)return;const a1=X1.connectionState;if(a1==="connected"){k("live");return}(a1==="failed"||a1==="disconnected"||a1==="closed")&&(k("error"),E(`WHIP transport ${a1}.`))},P1.getAudioTracks().forEach(a1=>{const C1=X1.addTrack(a1,P1);XQ(C1)});const J1=await X1.createOffer({offerToReceiveAudio:!1,offerToReceiveVideo:!1});await X1.setLocalDescription(J1),await KQ(X1,1800);const s1=await fetch(G1,{method:"POST",headers:{"Content-Type":"application/sdp",...YQ(A2)},body:X1.localDescription?.sdp||J1.sdp});if(!s1.ok)throw new Error(`WHIP request failed: ${s1.status}`);const J=await s1.text();await X1.setRemoteDescription({type:"answer",sdp:J}),await QQ(X1,1e4),await JQ(X1,6e3),await d?.(x1)},[C,g,d,u]),z1=f.useCallback(async()=>{if(u1.current=!1,G.current){try{G.current.disconnect()}catch{}G.current.onaudioprocess=null}if(R.current)try{R.current.disconnect()}catch{}if(e1.current)try{e1.current.disconnect()}catch{}if(n1.current)try{n1.current.getTracks().forEach(x1=>x1.stop())}catch{}if(I.current)try{await I.current.close()}catch{}d1.current=[],G.current=null,R.current=null,e1.current=null,n1.current=null,I.current=null,F.current=u6},[]),p1=f.useCallback(async()=>{if(n1.current&&I.current&&G.current)return;j(v2=>v2==="recording"?v2:"arming");const x1=await navigator.mediaDevices.getUserMedia({audio:{channelCount:1,sampleRate:u6,echoCancellation:!1,noiseSuppression:!1,autoGainControl:!1}}),M1=new AudioContext({latencyHint:"interactive"}),G1=M1.createMediaStreamSource(x1),A2=M1.createScriptProcessor(1024,1,1),P1=M1.createGain();P1.gain.value=0,A2.onaudioprocess=v2=>{if(!u1.current)return;const X1=v2.inputBuffer.getChannelData(0);d1.current.push(new Float32Array(X1))},G1.connect(A2),A2.connect(P1),P1.connect(M1.destination),n1.current=x1,I.current=M1,R.current=G1,G.current=A2,e1.current=P1,F.current=M1.sampleRate||u6},[]),F1=f.useCallback(async({send:x1})=>{const M1=u1.current;if(u1.current=!1,!M1){x1||j("idle");return}const G1=d1.current;if(d1.current=[],!x1){j("idle");return}if(!b){j("idle");return}if(!G1.length){j("idle");return}j("sending");try{const A2=WQ(G1),P1=eX(A2,F.current||u6),v2=Fs(P1);await l?.({roverId:b,name:`ptt-${Date.now()}.wav`,mime:"audio/wav",dataBase64:v2}),j("idle")}catch(A2){j("error"),E(A2?.message||"Failed to send PTT clip.")}},[l,b]),n2=f.useCallback(async()=>{if(!b){E("Take control of your rover first.");return}await p1(),d1.current=[],u1.current=!0,j("recording")},[p1,b]);return f.useEffect(()=>{let x1=!1;async function M1(){if(A){await Q(o1.current||b);return}if(!b){await Q(o1.current);return}if(!(P.current&&o1.current===b)&&!(!C&&!g))try{k("starting"),E(""),P.current=!0,o1.current=b,await v1(b),x1||k("live")}catch(G1){x1||(k("error"),E(G1?.message||"Failed to start mic forwarding.")),await Q(b)}}return M1(),()=>{x1=!0}},[A,C,g,b,v1,Q]),f.useEffect(()=>{const x1=q.current;x1&&(x1.enabled=!!(!A&&(C||g)))},[A,C,g]),f.useEffect(()=>{let x1=!1;async function M1(){if(!A||!b){u1.current&&await F1({send:!1}),j("idle");return}try{await p1(),!x1&&!u1.current&&j("idle")}catch(G1){x1||(j("error"),E(G1?.message||"Microphone access failed for clip mode."))}}return M1(),()=>{x1=!0}},[A,p1,F1,b]),f.useEffect(()=>{let x1=!1;async function M1(){if(A){if(!b){u1.current&&await F1({send:!1});return}if(g){if(!u1.current)try{E(""),await n2()}catch(G1){x1||(j("error"),E(G1?.message||"Failed to start clip recording."))}}else u1.current&&await F1({send:!0})}}return M1(),()=>{x1=!0}},[A,F1,g,b,n2]),f.useEffect(()=>()=>{Q(o1.current||b),z1()},[b,Q,z1]),r.jsx(B1,{title:"Audio Controls",children:r.jsxs("div",{className:"grid gap-1",children:[r.jsx("section",{className:"surface",children:r.jsxs("div",{className:"flex items-center justify-center gap-0.5 py-0.25 text-xs text-slate-300",children:[r.jsx("span",{children:"Push-to-Talk Key"}),r.jsx($7,{label:U1})]})}),r.jsxs("div",{className:"grid gap-0.5 grid-cols-1 lg:grid-cols-2",children:[r.jsx("section",{className:"surface h-full",children:r.jsxs("div",{className:"grid h-full gap-0.5 grid-rows-[auto_auto_1fr]",children:[r.jsx("p",{className:"text-sm text-slate-200 text-center",children:"PTT Mode"}),r.jsx("p",{className:"text-xs text-slate-400 text-center",children:"Choose how holding your PTT key behaves: live mic stream, or record-and-send clip. If the live stream doesn't work, switch to clip mode."}),r.jsxs("div",{className:"grid grid-cols-2 gap-0.5",children:[r.jsx("button",{type:"button",className:`button-dark w-full text-sm ${A?"":"bg-emerald-500 text-white hover:bg-emerald-500"}`,onClick:()=>Y1("live"),children:"Live Mic (WHIP)"}),r.jsx("button",{type:"button",className:`button-dark w-full text-sm ${A?"bg-emerald-500 text-white hover:bg-emerald-500":""}`,onClick:()=>Y1("clip"),children:"Record Clip"})]})]})}),r.jsx("section",{className:"surface h-full",children:r.jsxs("div",{className:"grid h-full gap-0.5 grid-rows-[auto_1fr]",children:[r.jsx("p",{className:"text-sm text-slate-200 text-center",children:"PTT Clip Status"}),r.jsxs("div",{className:"grid gap-0.5 content-start",children:[r.jsx(w4,{label:"Mode",active:A,detail:A?"record clip":"live mic"}),r.jsx(w4,{label:"PTT hold",active:g,detail:g?"held":"released"}),r.jsx(w4,{label:"Recording",active:V1,detail:V1?"capturing":"idle"}),r.jsx(w4,{label:"Playback",active:A&&r1,detail:A&&r1?"playing":"idle"})]})]})})]}),r.jsxs("div",{className:"grid gap-0.5 grid-cols-1 lg:grid-cols-2",children:[r.jsx("section",{className:"surface h-full",children:r.jsxs("div",{className:"grid h-full gap-0.5 grid-rows-[auto_auto_1fr_auto]",children:[r.jsx("p",{className:"text-sm text-slate-200 text-center",children:"Audio Upload"}),r.jsxs("label",{className:"mx-auto grid w-full max-w-sm gap-0.5 text-xs text-slate-300 text-center",children:[r.jsx("span",{children:"Audio file (mp3 / wav / ogg)"}),r.jsx("input",{className:`${z0} text-center`,type:"file",accept:".mp3,.wav,.ogg,audio/mpeg,audio/wav,audio/ogg",disabled:S||!b,onChange:x1=>H(x1.target.files?.[0]||null)})]}),r.jsx("div",{className:"mx-auto w-full max-w-sm text-center",children:y?r.jsxs("div",{className:"surface-muted text-xs text-slate-300",children:[y.name," (",y.size," bytes)"]}):r.jsx("div",{className:"surface-muted text-xs text-slate-500",children:"No file selected"})}),r.jsxs("div",{className:"flex justify-center gap-0.5",children:[r.jsx("button",{type:"button",className:"button-dark text-sm",disabled:S||!b,onClick:E1,children:S?"Working...":"Play Upload"}),r.jsx("button",{type:"button",className:"button-dark text-sm",disabled:S||!b,onClick:g1,children:"Stop"})]})]})}),r.jsx("section",{className:"surface h-full",children:r.jsxs("div",{className:"grid h-full gap-0.5 grid-rows-[auto_auto_1fr]",children:[r.jsx("p",{className:"text-sm text-slate-200 text-center",children:"Live Microphone"}),r.jsxs("label",{className:"surface-muted mx-auto flex w-full max-w-sm items-center justify-center gap-0.5 px-0.5 py-0.5 text-xs text-slate-300",children:[r.jsx("input",{type:"checkbox",checked:C,disabled:!b||A,onChange:x1=>z(M1=>({...M1||{},openMicEnabled:!!x1.target.checked}))}),r.jsx("span",{children:A?"Open mic (live mode only)":"Open mic"})]}),r.jsxs("div",{className:"grid gap-0.5 content-start",children:[r.jsx(w4,{label:"WHIP link",active:S1,detail:B}),r.jsx(w4,{label:"Mic hot",active:m1,detail:m1?"transmitting":"muted"}),r.jsxs("div",{className:"surface-muted text-center text-xs text-slate-400",children:["Hold ",r.jsx($7,{label:U1})," to talk"]})]})]})})]}),r.jsx("section",{className:"surface",children:r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-sm text-slate-200 text-center",children:"Audio Status"}),r.jsxs("div",{className:"grid gap-0.5 grid-cols-1 sm:grid-cols-2 lg:grid-cols-6",children:[r.jsx(w4,{label:"Forward pipe",active:t1,detail:t1?"connected":"offline"}),r.jsx(w4,{label:"Upload playback",active:r1,detail:r1?"playing":"idle"}),r.jsx(w4,{label:"Mic relay",active:f1,detail:f1?"active":"idle"}),r.jsx(w4,{label:"WHIP transport",active:S1,detail:B}),r.jsx(w4,{label:"Clip recording",active:V1,detail:V1?"recording":"idle"}),r.jsx(w4,{label:"Clip sending",active:A1,detail:A1?"sending":"idle"})]}),N?r.jsx("div",{className:"text-xs text-slate-400 text-center",children:N}):null]})})]})})}function aX({pendingRequestId:t,currentStoredKey:a,nickname:l,requestVerification:o,applyIdentityKey:u,onMessage:d,fullWidth:p=!1}){const[v,g]=f.useState(0),[x,z]=f.useState(""),[b,y]=f.useState(!1),[H,S]=f.useState(!1),w=()=>{g(1),z(a),y(!1),d?.("")},C=()=>{g(0),y(!1)},V=async B=>{if(B.preventDefault(),!b){d?.("Please confirm your nickname agreement before sending.");return}if(v<3){d?.("Complete all request steps before sending.");return}S(!0),d?.("");try{const k=await u(x);await o?.(),z(k),g(0),y(!1),d?.("Verification request sent to lockdown admins.")}catch(k){d?.(k.message||"Failed to submit request.")}finally{S(!1)}},A=p?"w-full":Z8;return t?r.jsx(B1,{title:"Verification",className:A,bodyClassName:"text-sm text-slate-300",children:r.jsxs("div",{className:v0,children:["Verification request pending: ",t]})}):v===0?r.jsx(B1,{title:"Verification",className:A,children:r.jsx("div",{className:v0,children:r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:w,disabled:H,children:"Request Verification"})})}):r.jsx(B1,{title:"Request verification",className:A,children:r.jsx("form",{onSubmit:V,children:r.jsxs("div",{className:v0,children:[r.jsxs("p",{className:"text-xs text-slate-500",children:["Step ",v," of 3"]}),v===1?r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-xs text-slate-400",children:"Confirm your nickname, which is used as part of the verification process. You can edit it here before requesting."}),r.jsx("div",{className:"mx-auto w-full max-w-sm",children:r.jsx(lt,{compact:!0})}),r.jsxs("div",{className:"surface-muted mx-auto w-full max-w-sm text-xs text-slate-300 text-center",children:["Current nickname: ",r.jsx("span",{className:"font-semibold",children:l||"(not set)"})]}),r.jsxs("label",{className:"flex items-center justify-center gap-0.5 text-xs text-slate-300 text-center",children:[r.jsx("input",{type:"checkbox",className:"accent-emerald-500",checked:b,onChange:B=>y(B.target.checked)}),r.jsx("span",{children:"I understand this nickname is tied to my verification."})]}),r.jsxs("div",{className:"flex justify-center gap-0.5",children:[r.jsx("button",{type:"button",className:"button-dark text-sm",disabled:!l||!b,onClick:()=>g(2),children:"Continue"}),r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:C,children:"Cancel"})]})]}):null,v===2?r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-xs text-slate-400",children:"Save your identity key in a safe place. You can use it to restore your identity in another browser."}),r.jsx("input",{className:z0,type:"password",name:"identity_key_request",autoComplete:"current-password",maxLength:35,value:x,onChange:B=>z(B.target.value.toLowerCase()),placeholder:"Identity key for this request"}),r.jsxs("div",{className:"flex justify-center gap-0.5",children:[r.jsx("button",{type:"button",className:"button-dark text-sm",disabled:!x,onClick:()=>navigator.clipboard?.writeText(x),children:"Copy Key"}),r.jsx("button",{type:"button",className:"button-dark text-sm",disabled:!x,onClick:()=>g(3),children:"Continue"}),r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:()=>g(1),children:"Back"})]})]}):null,v===3?r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-xs text-slate-400",children:"Final step: confirm and send your verification request."}),r.jsx("input",{className:z0,type:"password",name:"identity_key_request_final",autoComplete:"current-password",maxLength:35,value:x,onChange:B=>z(B.target.value.toLowerCase()),placeholder:"Identity key for this request"}),r.jsxs("div",{className:"flex justify-center gap-0.5",children:[r.jsx("button",{type:"submit",className:"button-dark text-sm",disabled:H||!x,children:"Confirm Request"}),r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:()=>g(2),children:"Back"})]})]}):null]})})})}function nX({currentStoredKey:t,applyIdentityKey:a,onMessage:l,fullWidth:o=!1}){const[u,d]=f.useState(0),[p,v]=f.useState(""),[g,x]=f.useState(!1),z=()=>{d(1),v(""),l?.("")},b=()=>{d(0),v("")},y=async S=>{if(S.preventDefault(),!(u<2)){x(!0),l?.("");try{await a(p),d(0),v(""),l?.("Identity key restored.")}catch(w){l?.(w.message||"Failed to restore key.")}finally{x(!1)}}},H=o?"w-full":Z8;return r.jsx(B1,{title:"Identity key",className:H,children:r.jsxs("div",{className:v0,children:[r.jsxs("p",{className:"text-xs text-slate-500",children:["Current: ",GQ(t)||"not set yet"]}),r.jsx("input",{className:z0,type:"password",name:"identity_key_current",autoComplete:"current-password",maxLength:35,value:t,readOnly:!0,placeholder:"Identity key"}),u===0?r.jsxs("div",{className:"flex justify-center gap-0.5",children:[r.jsx("button",{type:"button",className:"button-dark text-sm",disabled:!t,onClick:()=>navigator.clipboard?.writeText(t),children:"Copy Key"}),r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:z,disabled:g,children:"Restore Key"})]}):null,u===1?r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-xs text-slate-400",children:"Restoring your key should only be done when needed. Use this to move your identity to another browser."}),r.jsxs("div",{className:"flex justify-center gap-0.5",children:[r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:()=>d(2),children:"Continue"}),r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:b,children:"Cancel"})]})]}):null,u===2?r.jsxs("form",{className:"space-y-0.5",onSubmit:y,children:[r.jsx("input",{className:z0,type:"password",name:"identity_key_restore",autoComplete:"current-password",maxLength:35,value:p,onChange:S=>v(S.target.value.toLowerCase()),placeholder:"Paste key to restore"}),r.jsxs("div",{className:"flex justify-center gap-0.5",children:[r.jsx("button",{type:"submit",className:"button-dark text-sm",disabled:g||!p,children:"Confirm Restore"}),r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:()=>d(1),children:"Back"})]})]}):null]})})}function lX({requestableRovers:t=[],pendingRequests:a=[],requestPrivateRoverAccess:l,onMessage:o,fullWidth:u=!1}){const[d,p]=f.useState({}),v=f.useMemo(()=>{const z=new Map;return(a||[]).forEach(b=>{b?.roverId&&z.set(String(b.roverId),b)}),z},[a]),g=u?"w-full":Z8,x=async z=>{if(z){p(b=>({...b,[z]:!0})),o?.("");try{(await l?.(z))?.existing?o?.("You already have a pending request for that rover."):o?.("Private rover access request sent to lockdown admins.")}catch(b){o?.(b.message||"Failed to send request.")}finally{p(b=>({...b,[z]:!1}))}}};return r.jsx(B1,{title:"Private rover access requests",className:g,bodyClassName:"text-sm text-slate-300",children:r.jsx("div",{className:v0,children:t.length===0?r.jsx("p",{className:"text-xs text-slate-500",children:"No closed private rovers are available to request right now."}):r.jsx("div",{className:"w-full space-y-0.5",children:t.map(z=>{const b=String(z.id),y=!!d[b],H=v.get(b);return r.jsxs("div",{className:"surface-muted flex items-center justify-between gap-0.5 px-1 py-0.5",children:[r.jsxs("div",{className:"min-w-0 text-left",children:[r.jsx("p",{className:"truncate text-xs",children:r.jsx(V4,{rover:z,fallback:b,className:"max-w-full truncate align-middle"})}),r.jsx("p",{className:"truncate text-[0.7rem] text-slate-500",children:b})]}),H?r.jsx("span",{className:"rounded bg-amber-700/30 px-1 py-0.5 text-[0.7rem] text-amber-200",children:"Pending"}):r.jsx("button",{type:"button",onClick:()=>x(b),disabled:y,className:"button-dark text-xs disabled:opacity-50",children:y?"Sending...":"Request"})]},b)})})})})}function k7(t){return String(t||"").trim()}function rX(t){const a=k7(t);return a?a.includes("DOCKINGRUNNING")?"Returning":a.includes("PAUSED")?"Paused":a.includes("HOUSECLEANINGRUNNING")?"Cleaning":a.includes("SPOTCLEANINGRUNNING")?"Spot cleaning":a.includes("STATE_START")?"Starting":a.includes("STATE_IDLE")||a.includes("STATE_STANDBY")?"Idle":a:"--"}function Ou(t){return t==="good"?"bg-emerald-600 text-white":t==="warn"?"bg-amber-500 text-slate-900":t==="danger"?"bg-rose-600 text-white":"bg-slate-700 text-slate-100"}function p7({label:t,value:a,tone:l="muted",valueClass:o="",hideLabel:u=!1}){return r.jsxs("div",{className:`rounded-md px-1 py-0.75 text-center ${Ou(l)}`,children:[u?null:r.jsx("div",{className:"text-[0.72rem] opacity-90",children:t}),r.jsx("div",{className:`text-sm font-semibold ${o}`,title:String(a||""),children:a})]})}function iX({neato:t,onStart:a,onSendHome:l,onLocate:o,onClearErrors:u,onPowerCycle:d,fullWidth:p=!1}){const[v,g]=f.useState(""),x=p?"w-full":"w-full max-w-xl",z=!!t?.configured,b=!!(t?.enabled&&t?.connected),y=!!t?.telemetry?.extPowerPresent,H=!!t?.telemetry?.chargingActive,S=rX(t?.telemetry?.uiState),w=k7(t?.telemetry?.robotState)||"--",C=t?.telemetry?.batteryPercent,V=Number.isFinite(C)?`${C}%`:"--",A=t?.telemetry?.batteryVoltage,B=Number.isFinite(A)?`${A.toFixed(2)} V`:"--",k=k7(t?.telemetry?.robotError)||"--",D=k7(t?.telemetry?.robotAlert)||"--",j=t?.controls||{},N=!!j?.start?.available,E=!!j?.sendHome?.available,Z=!!j?.locate?.available,q=!!j?.clearErrors?.available,T=!!j?.powerCycle?.available,P=z&&b&&N,o1=z&&b&&E,n1=z&&b&&Z,I=z&&b&&q,R=z&&b&&T,G=async(_,t1)=>{if(t1){g(_);try{await t1()}catch{}finally{g("")}}},e1=z?b?"Online":"Offline":"Not configured",d1=z?b?"good":"danger":"warn",u1=C==null?"muted":C>=60?"good":C>=25?"warn":"danger",F=y?"Docked":S!=="--"?S:"Away from dock";return r.jsx(B1,{title:"Neato Controls",className:x,bodyClassName:"text-sm text-slate-200",actions:r.jsx("span",{className:`inline-flex w-auto rounded px-1 py-0.25 text-xs font-semibold ${Ou(d1)}`,children:e1}),children:r.jsxs("div",{className:"grid gap-0.5",children:[r.jsx("div",{className:"text-center",children:r.jsx("p",{className:"text-xs text-slate-400",children:"Control the autonomous Neato robovac. Be nice to him."})}),r.jsxs("div",{className:"grid grid-cols-1 lg:grid-cols-2 gap-0.5",children:[r.jsxs("div",{className:"surface-muted grid gap-0.5",children:[r.jsx("p",{className:"text-xs text-slate-300 text-center",children:"Controls"}),r.jsx("button",{type:"button",disabled:!P||!!v,onClick:()=>G("start",a),className:"rounded-md border border-sky-300 bg-emerald-600 px-1 py-1 text-base font-semibold text-white transition hover:border-sky-500 hover:bg-emerald-500 disabled:opacity-50",children:v==="start"?"Starting...":"Start cleaning"}),r.jsx("button",{type:"button",disabled:!o1||!!v,onClick:()=>G("sendHome",l),className:"rounded-md border border-sky-300 bg-sky-600 px-1 py-1 text-base font-semibold text-white transition hover:border-sky-500 hover:bg-sky-500 disabled:opacity-50",children:v==="sendHome"?"Sending...":"Send to dock"}),r.jsxs("div",{className:"grid grid-cols-3 gap-0.5",children:[r.jsx("button",{type:"button",disabled:!n1||!!v,onClick:()=>G("locate",o),className:"w-full rounded-md border border-sky-300 bg-fuchsia-600 px-1 py-0.5 text-xs font-semibold text-white transition hover:border-sky-500 hover:bg-fuchsia-500 disabled:opacity-50",children:v==="locate"?"Playing...":"Play sound"}),r.jsx("button",{type:"button",disabled:!I||!!v,onClick:()=>G("clearErrors",u),className:"w-full rounded-md border border-sky-300 bg-amber-500 px-1 py-0.5 text-xs font-semibold text-slate-900 transition hover:border-sky-500 hover:bg-amber-400 disabled:opacity-50",children:v==="clearErrors"?"Clearing...":"Clear errors"}),r.jsx("button",{type:"button",disabled:!R||!!v,onClick:()=>G("powerCycle",d),className:"w-full rounded-md border border-rose-300 bg-rose-600 px-1 py-0.5 text-xs font-semibold text-white transition hover:border-rose-500 hover:bg-rose-500 disabled:opacity-50",children:v==="powerCycle"?"Cycling...":"Power cycle"})]}),r.jsxs("div",{className:"surface-muted grid gap-0.5 pt-0.25",children:[r.jsx("p",{className:"text-xs text-slate-300 text-center",children:"Power status"}),r.jsxs("div",{className:"grid grid-cols-2 gap-0.5",children:[r.jsx(p7,{label:"Battery",value:V,tone:u1}),r.jsx(p7,{label:"Voltage",value:B,tone:"muted"}),r.jsx(p7,{label:"Docked",value:y?"Docked":"Not docked",tone:y?"good":"muted",hideLabel:!0}),r.jsx(p7,{label:"Charging",value:H?"charging":"not charging",tone:H?"good":"muted",hideLabel:!0})]})]})]}),r.jsx("div",{className:"grid gap-0.5",children:r.jsxs("div",{className:"surface-muted grid gap-0.5",children:[r.jsx("p",{className:"text-xs text-slate-300 text-center",children:"Robot Status"}),r.jsxs("div",{className:"grid gap-0.5",children:[r.jsxs("div",{className:"rounded-md bg-slate-800 px-1 py-0.5",children:[r.jsx("div",{className:"text-[0.72rem] text-slate-300",children:"Robot state (raw)"}),r.jsx("div",{className:"font-mono text-sm text-slate-100 break-all",children:w})]}),r.jsxs("div",{className:"rounded-md bg-slate-800 px-1 py-0.5",children:[r.jsx("div",{className:"text-[0.72rem] text-slate-300",children:"Basic state"}),r.jsx("div",{className:"font-mono text-sm text-slate-100 break-all",children:F})]}),r.jsxs("div",{className:"rounded-md bg-slate-800 px-1 py-0.5",children:[r.jsx("div",{className:"text-[0.72rem] text-slate-300",children:"UI state"}),r.jsx("div",{className:"font-mono text-sm text-slate-100 break-all",children:S})]}),r.jsxs("div",{className:"rounded-md bg-slate-800 px-1 py-0.5",children:[r.jsx("div",{className:"text-[0.72rem] text-slate-300",children:"Robot error"}),r.jsx("div",{className:"font-mono text-sm text-slate-100 break-all",children:k})]}),r.jsxs("div",{className:"rounded-md bg-slate-800 px-1 py-0.5",children:[r.jsx("div",{className:"text-[0.72rem] text-slate-300",children:"Robot alert"}),r.jsx("div",{className:"font-mono text-sm text-slate-100 break-all",children:D})]})]})]})})]}),!z||!b||!N||!E||!Z||!q||!T?r.jsx("p",{className:"text-xs text-slate-400 text-center",children:z?b?"Waiting for required Neato entities in Home Assistant.":"Home Assistant is offline.":"Set homeAssistant.neato.device in server config to enable Neato controls."}):null]})})}function sX(t){return t==="good"?"bg-emerald-600 text-white":t==="warn"?"bg-amber-500 text-slate-900":t==="danger"?"bg-rose-600 text-white":"bg-slate-700 text-slate-100"}function oX(t){return t==="up"?"Up":t==="down"?"Down":t==="stopped"?"Transitioning...":t==="conflict"?"Conflict":"--"}function uX({lift:t,onUp:a,onDown:l,fullWidth:o=!1}){const[u,d]=f.useState(""),[p,v]=f.useState(()=>Date.now()),g=o?"w-full":"w-full max-w-xl",x=!!t?.configured,z=!!(t?.enabled&&t?.connected),b=!!t?.busy,y=String(t?.target||"").toLowerCase(),H=String(t?.position||"").toLowerCase(),S=!!t?.availability?.upSwitch,w=!!t?.availability?.downSwitch,C=Number(t?.lastActionAt||0),V=Math.max(0,Number(t?.commandCooldownMs||0)),A=Math.max(0,C+V-p),B=!b&&A>0,k=b||B;f.useEffect(()=>{if(!k)return;const q=setInterval(()=>{v(Date.now())},100);return()=>clearInterval(q)},[k]),f.useEffect(()=>{v(Date.now())},[C,V,b]);const D=x?z?k?"Busy":"Ready":"Offline":"Not configured",j=x?z?k?"warn":"good":"danger":"warn",N=x&&z&&!k&&!u&&S&&w,E=f.useMemo(()=>Math.ceil(A/1e3),[A]),Z=async(q,T)=>{if(T){d(q);try{await T()}catch{}finally{d("")}}};return r.jsxs(B1,{title:"Lift Controls",className:`relative ${g}`,bodyClassName:"text-sm text-slate-200",actions:r.jsx("span",{className:`inline-flex w-auto rounded px-1 py-0.25 text-xs font-semibold ${sX(j)}`,children:D}),children:[k?r.jsx("div",{className:"absolute inset-0 z-20 flex items-center justify-center rounded-md bg-slate-950/80 px-1.5 text-center",children:r.jsxs("div",{className:"space-y-0.25",children:[r.jsx("p",{className:"text-sm font-semibold text-slate-100",children:b?"Preparing to move":"Moving!"}),r.jsx("p",{className:"text-xs text-slate-300",children:"Controls are disabled while the lift is moving, otherwise its tiny brain would get confused."}),!b&&B?r.jsxs("p",{className:"text-xs text-slate-400",children:["About ",E,"s remaining."]}):null]})}):null,r.jsxs("div",{className:"grid gap-0.5",children:[r.jsx("div",{className:"text-center",children:r.jsx("p",{className:"text-xs text-slate-400",children:"Move the lift up and down. Please don't break anything..."})}),r.jsx("section",{className:"surface-muted px-0.5 py-0.5",children:r.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-3 gap-0.5",children:[r.jsxs("div",{className:"rounded-md bg-slate-800 px-1 py-0.75 text-center",children:[r.jsx("div",{className:"text-xs text-slate-300",children:"Position"}),r.jsx("div",{className:"text-base font-semibold text-slate-100",children:oX(H)})]}),r.jsx("button",{type:"button",disabled:!N,onClick:()=>Z("down",l),className:`button-dark w-full text-sm disabled:opacity-50 ${H==="down"||y==="down"?"bg-emerald-500 text-white hover:bg-emerald-500":""}`,children:u==="down"||y==="down"?"Lowering...":"Down"}),r.jsx("button",{type:"button",disabled:!N,onClick:()=>Z("up",a),className:`button-dark w-full text-sm disabled:opacity-50 ${H==="up"||y==="up"?"bg-emerald-500 text-white hover:bg-emerald-500":""}`,children:u==="up"||y==="up"?"Raising...":"Up"})]})}),!x||!z||!S||!w?r.jsx("p",{className:"text-xs text-slate-400 text-center",children:x?z?"Waiting for required lift switch entities in Home Assistant.":"Home Assistant is offline.":"Set homeAssistant.lift.upSwitch and homeAssistant.lift.downSwitch in server config."}):null,t?.lastError?r.jsxs("p",{className:"text-xs text-rose-300 text-center",children:["Last error: ",t.lastError]}):null]})]})}function hX(t){const a=String(t||"").trim();if(!a)return"";try{const l=new URL(a),o=String(l.protocol||"").toLowerCase();return o!=="http:"&&o!=="https:"?"":l.toString()}catch{return""}}function dX({isVerified:t=!1,fullWidth:a=!1,onMessage:l}){const{value:o,save:u}=N1("profile",{nickname:"",profileImageUrl:""}),d=String(o?.profileImageUrl||"").trim(),[p,v]=f.useState(d),[g,x]=f.useState(!1);f.useEffect(()=>{v(d)},[d]);const z=f.useMemo(()=>hX(p),[p]),b=String(p||"").trim()!==d,y=!!(t&&b&&(z||!String(p||"").trim())),H=a?"w-full":Z8,S=async w=>{if(w.preventDefault(),!t){l?.("Verify your account before setting a profile image URL.");return}x(!0),l?.("");try{const C=z||"";u(V=>({...V||{},profileImageUrl:C})),l?.(C?"Profile image URL saved.":"Profile image URL cleared.")}finally{x(!1)}};return r.jsx(B1,{title:"Chat profile image URL",className:H,bodyClassName:"text-sm text-slate-300",children:r.jsxs("form",{className:v0,onSubmit:S,children:[r.jsx("p",{className:"text-xs text-slate-500",children:"Verified users can set a custom avatar for chat and Discord bridge messages."}),r.jsx("input",{className:z0,type:"url",name:"vip_profile_image_url",placeholder:"https://example.com/avatar.png",value:p,onChange:w=>v(w.target.value),disabled:!t||g}),!z&&String(p||"").trim()?r.jsx("p",{className:"text-xs text-amber-300",children:"Enter a valid http/https image URL."}):null,r.jsxs("div",{className:"flex justify-center gap-0.5",children:[r.jsx("button",{type:"submit",className:"button-dark text-sm disabled:opacity-50",disabled:!y||g,children:g?"Saving...":"Save URL"}),r.jsx("button",{type:"button",className:"button-dark text-sm disabled:opacity-50",disabled:!t||g||!d,onClick:()=>v(""),children:"Clear"})]})]})})}function _u({isActive:t=!0}){const a=h1(n1=>n1.session),{identifySession:l,requestVerification:o,requestPrivateRoverAccess:u,playUploadedAudio:d,stopUploadedAudio:p,startMicWhip:v,readyMicWhip:g,stopMicWhip:x,neatoStart:z,neatoSendHome:b,neatoLocate:y,neatoClearErrors:H,neatoPowerCycle:S,liftUp:w,liftDown:C}=$2(),{value:V,save:A}=N1("identity",{cookieUserId:""}),{value:B}=N1("profile",{nickname:""}),k=f.useMemo(()=>(V?.cookieUserId||"").trim(),[V?.cookieUserId]),D=(B?.nickname||"").trim(),j=!!a?.isVerified,N=a?.verification?.pendingRequestId||null,E=a?.privateRoverAccess?.requestableRovers||[],Z=a?.privateRoverAccess?.pendingRequests||[],q=String(a?.assignment?.roverId||"").trim(),[T,P]=f.useState(""),o1=async n1=>{const I=String(n1||"").trim().toLowerCase();if(!I)throw new Error("Identity key required.");if(!$Q.test(I))throw new Error("Identity key must match format: cu_ + 32 lowercase hex chars.");return A(R=>({...R||{},cookieUserId:I})),await l({cookieUserId:I,nickname:D}),I};return r.jsxs("section",{className:"space-y-2 text-base",children:[r.jsxs("div",{className:"grid gap-2 grid-cols-1 lg:grid-cols-[minmax(0,1.25fr)_minmax(0,0.9fr)]",children:[j?null:r.jsx("div",{className:"lg:col-span-2",children:r.jsx(aX,{pendingRequestId:N,currentStoredKey:k,nickname:D,requestVerification:o,applyIdentityKey:o1,onMessage:P,fullWidth:!0})}),r.jsx("div",{className:"lg:col-span-2",children:j?r.jsxs("div",{className:"space-y-2",children:[r.jsx(iX,{neato:a?.neato||null,onStart:z,onSendHome:b,onLocate:y,onClearErrors:H,onPowerCycle:S,fullWidth:!0}),r.jsx(uX,{lift:a?.lift||null,onUp:w,onDown:C,fullWidth:!0}),r.jsx(cX,{ownRoverId:q,audioForwardByRover:a?.audioForward||{},playUploadedAudio:d,stopUploadedAudio:p,startMicWhip:v,readyMicWhip:g,stopMicWhip:x})]}):r.jsx("section",{className:"surface h-full",children:r.jsx("div",{className:"flex h-full flex-col items-center justify-center text-center text-xs text-slate-400",children:"Verify your account to unlock VIP controls."})})}),r.jsx("div",{className:"lg:col-span-2",children:r.jsx(lX,{requestableRovers:E,pendingRequests:Z,requestPrivateRoverAccess:u,onMessage:P,fullWidth:!0})}),r.jsx("div",{className:"lg:col-span-2",children:r.jsx(nX,{currentStoredKey:k,applyIdentityKey:o1,onMessage:P,fullWidth:!0})}),r.jsx("div",{className:"lg:col-span-2",children:r.jsx(dX,{isVerified:j,onMessage:P,fullWidth:!0})})]}),T?r.jsx("div",{className:Z8,children:r.jsx("p",{className:"text-xs text-slate-400 text-center",children:T})}):null]})}const fX=420,mX=1200,vX={1:262,2:330,3:392,4:523};function P7(){const t=h1(w=>w.session?.buttonBox?.buttons??[]),a=R2(),{value:l}=N1("audio",p2),o=Number.isFinite(l?.masterVolume)?l.masterVolume:p2.masterVolume,u=Number.isFinite(l?.alertVolume)?l.alertVolume:p2.alertVolume,d=Math.max(0,Math.min(1,o*u)),p=f.useMemo(()=>{const w=Array.isArray(t)?t:[];return w.length===4?w:[1,2,3,4].map(C=>w.find(V=>Number(V?.id)===C)||{id:C,count:0,goal:0,rewardName:null,rewardId:null,rewardNumber:null,lastRewardAt:null})},[t]),[v,g]=f.useState({}),[x,z]=f.useState({}),b=f.useRef(new Map),y=f.useRef(new Map),H=f.useRef({}),S=f.useRef(null);return f.useEffect(()=>{const w={};p.forEach(V=>{w[V.id]=Number(V.lastRewardAt||0)});const C=H.current||{};p.forEach(V=>{const A=Number(V.id),B=w[A]||0,k=Number(C[A]||0);if(B>0&&B!==k){z(N=>({...N,[A]:!0}));const D=y.current.get(A);D&&clearTimeout(D);const j=setTimeout(()=>{z(N=>({...N,[A]:!1})),y.current.delete(A)},mX);y.current.set(A,j)}}),H.current=w},[p]),f.useEffect(()=>{function w(V){const A=vX[V];if(!A||d<=0)return;let B=S.current;if(!B){const N=window.AudioContext||window.webkitAudioContext;if(!N)return;B=new N,S.current=B}const k=B.createOscillator(),D=B.createGain();k.type="square",k.frequency.value=A;const j=.14*d;D.gain.setValueAtTime(1e-4,B.currentTime),D.gain.exponentialRampToValueAtTime(Math.max(1e-4,j),B.currentTime+.01),D.gain.exponentialRampToValueAtTime(1e-4,B.currentTime+.2),k.connect(D),D.connect(B.destination),k.start(),k.stop(B.currentTime+.21)}function C(V={}){const A=Number(V.buttonId);if(!Number.isFinite(A)||A<1||A>4)return;g(D=>({...D,[A]:!0}));const B=b.current.get(A);B&&clearTimeout(B);const k=setTimeout(()=>{g(D=>({...D,[A]:!1})),b.current.delete(A)},fX);b.current.set(A,k),w(A)}return a.on("buttonBox:increment",C),()=>{a.off("buttonBox:increment",C)}},[d,a]),r.jsx(B1,{title:"Button Box",bodyClassName:"space-y-0.5 text-base",children:r.jsx("div",{className:"grid grid-cols-4 gap-0.5",children:p.map(w=>{const C=Number(w.id),V=Number.isFinite(w.count)?w.count:0,A=Number.isFinite(w.goal)?w.goal:0,B=typeof w.rewardName=="string"&&w.rewardName.trim()?w.rewardName.trim():"Unassigned",k=Number.isFinite(w.rewardNumber)?w.rewardNumber:"?",D=!!v[C],j=!!x[C];return r.jsx(Zo,{buttonId:C,count:V,goal:A,rewardNumber:k,rewardName:B,className:[D?"bg-cyan-900/45":"",j?"bg-fuchsia-900/45":""].filter(Boolean).join(" ")},C)})})})}const wc={activeGameId:null,games:[],activeGame:null,counters:{objects:[],rovers:[],codes:[]},recentEvents:[],ownPlayer:null};function Rs(t={}){return{...wc,...t&&typeof t=="object"?t:{},games:Array.isArray(t?.games)?t.games:[],counters:{...wc.counters,...t?.counters&&typeof t.counters=="object"?t.counters:{}},recentEvents:Array.isArray(t?.recentEvents)?t.recentEvents:[]}}function Uu(){const t=R2(),[a,l]=f.useState(wc);f.useEffect(()=>{function u(d={}){l(Rs(d))}return t.emit("barcodeGame:subscribe",{},(d={})=>{d.state&&u(d.state)}),t.on("barcodeGame:state",u),()=>{t.off("barcodeGame:state",u)}},[t]);const o=f.useCallback(u=>new Promise((d,p)=>{t.emit("barcodeGame:vote",{gameId:u},(v={})=>{if(v.error){p(new Error(v.error));return}v.state&&l(Rs(v.state)),d(v)})}),[t]);return f.useMemo(()=>({state:a,voteForGame:o}),[a,o])}function pX(t){const[a,l]=f.useState(()=>Date.now());return f.useEffect(()=>{if(!t)return;const o=window.setInterval(()=>{l(Date.now())},500);return()=>window.clearInterval(o)},[t]),a}function gX(t,a){if(!Number.isFinite(t))return null;const l=Math.max(0,Math.ceil((t-a)/1e3)),o=Math.floor(l/60),u=l%60;return o>0?`${o}:${String(u).padStart(2,"0")}`:`${u}s`}function xX({game:t,disabled:a,onVote:l}){return r.jsxs("button",{type:"button",disabled:a||t.active,onClick:()=>l(t.id),className:["button-dark flex min-h-[4.25rem] flex-col items-start justify-between px-1.5 py-1 text-left disabled:opacity-70",t.active?"border-emerald-500/70":""].filter(Boolean).join(" "),children:[r.jsxs("span",{className:"flex w-full items-start justify-between gap-1",children:[r.jsx("span",{className:"text-sm font-semibold leading-tight text-neutral-50",children:t.title}),r.jsx("span",{className:"shrink-0 font-mono text-[0.72rem] text-neutral-300",children:t.voteCount||0})]}),r.jsx("span",{className:"mt-0.5 line-clamp-2 text-xs leading-snug text-neutral-300",children:t.description}),r.jsx("span",{className:"mt-0.5 text-[0.7rem] font-semibold text-neutral-200",children:t.active?"Active":t.actionLabel||"Start"})]})}function zX({stats:t}){return!Array.isArray(t)||!t.length?null:r.jsx("div",{className:"grid gap-1 sm:grid-cols-3",children:t.slice(0,6).map(a=>r.jsxs("div",{className:"border border-neutral-700 px-1.5 py-1",children:[r.jsx("p",{className:"truncate text-[0.68rem] text-neutral-400",children:a.label}),r.jsx("p",{className:"truncate text-sm font-semibold text-neutral-100",children:a.value})]},a.label))})}function Es({title:t,entries:a}){return!Array.isArray(a)||!a.length?null:r.jsxs("div",{children:[r.jsx("p",{className:"mb-0.5 text-xs font-semibold text-neutral-300",children:t}),r.jsx("div",{className:"space-y-0.5",children:a.slice(0,4).map(l=>r.jsxs("div",{className:"flex justify-between gap-2 text-xs",children:[r.jsx("span",{className:"min-w-0 truncate text-neutral-200",children:l.label||l.entityId||l.code}),r.jsx("span",{className:"font-mono text-neutral-300",children:l.count||0})]},`${l.entityId||l.code}-${l.type||"counter"}`))})]})}function bX({players:t,ownPlayer:a}){const l=a&&Number.isFinite(a.totalPoints);return r.jsx("div",{className:"space-y-1",children:r.jsxs("div",{className:"grid gap-1 sm:grid-cols-[minmax(0,0.8fr)_minmax(0,1.2fr)]",children:[r.jsxs("div",{className:"border border-neutral-700 px-1.5 py-1",children:[r.jsx("p",{className:"text-xs text-neutral-400",children:"Your points"}),r.jsx("p",{className:"text-lg font-semibold leading-tight text-neutral-50",children:l?a.totalPoints:0}),a?.rank?r.jsxs("p",{className:"text-[0.7rem] text-neutral-400",children:["Rank ",a.rank]}):null]}),r.jsxs("div",{className:"border border-neutral-700 px-1.5 py-1",children:[r.jsx("p",{className:"mb-0.5 text-xs font-semibold text-neutral-300",children:"Leaderboard"}),Array.isArray(t)&&t.length?r.jsx("div",{className:"space-y-0.5",children:t.slice(0,4).map((o,u)=>r.jsxs("div",{className:"grid grid-cols-[1.25rem_minmax(0,1fr)_auto] gap-1 text-xs",children:[r.jsx("span",{className:"font-mono text-neutral-500",children:u+1}),r.jsx("span",{className:"truncate text-neutral-200",children:o.nickname}),r.jsx("span",{className:"font-mono text-neutral-300",children:o.totalPoints})]},o.playerKey))}):r.jsx("p",{className:"text-xs text-neutral-500",children:"No points yet"})]})]})})}function Zu(){const{state:t,voteForGame:a}=Uu(),[l,o]=f.useState(null),u=t.activeGame,d=u?.display||{},p=d.timer?.endsAt,v=pX(Number.isFinite(p)),g=gX(p,v),x=f.useMemo(()=>Array.isArray(t.games)?t.games:[],[t.games]),z=async b=>{o(b);try{await a(b)}finally{o(null)}};return r.jsxs(B1,{title:"Barcode games",bodyClassName:"space-y-2 p-1.5 text-sm",children:[r.jsx("div",{className:"grid gap-1 sm:grid-cols-2",children:x.map(b=>r.jsx(xX,{game:b,disabled:!!l,onVote:z},b.id))}),r.jsxs("section",{className:"space-y-1 border-t border-neutral-700 pt-1.5",children:[r.jsxs("div",{className:"flex items-start justify-between gap-2",children:[r.jsxs("div",{className:"min-w-0",children:[r.jsx("p",{className:"text-xs font-semibold text-neutral-400",children:d.title||u?.title||"No game active"}),r.jsx("p",{className:"break-words text-lg font-semibold leading-tight text-neutral-50",children:d.primary||"Vote for a game to start"}),d.secondary?r.jsx("p",{className:"mt-0.5 break-words text-sm leading-snug text-neutral-300",children:d.secondary}):null]}),g?r.jsxs("div",{className:"shrink-0 text-right",children:[r.jsx("p",{className:"text-[0.68rem] text-neutral-400",children:d.timer?.label||"Time"}),r.jsx("p",{className:"font-mono text-base font-semibold text-neutral-50",children:g})]}):null]}),r.jsx(zX,{stats:d.stats})]}),r.jsx(bX,{players:t.leaderboard,ownPlayer:t.ownPlayer}),r.jsxs("div",{className:"grid gap-2 border-t border-neutral-700 pt-1.5 sm:grid-cols-2",children:[r.jsx(Es,{title:"Most scanned objects",entries:t.counters?.objects}),r.jsx(Es,{title:"Most scanned rovers",entries:t.counters?.rovers})]})]})}function MX(t){const a=Number(t);if(!Number.isFinite(a)||a<=0)return null;const l=new Date(a);return Number.isNaN(l.getTime())?null:l.toLocaleTimeString([],{hour:"numeric",minute:"2-digit",second:"2-digit"})}function yX({memory:t,onClose:a}){const l=typeof t?.summary=="string"&&t.summary.trim()?t.summary:"Memory unavailable.",o=MX(t?.updatedAt);return r.jsx("div",{className:"fixed inset-0 z-40 flex items-center justify-center bg-black/75 p-1",onClick:a,children:r.jsx("div",{className:"w-full max-w-2xl",onClick:u=>u.stopPropagation(),children:r.jsx(B1,{title:"Overseer memory",meta:o?`Updated ${o}`:null,actions:r.jsx("button",{type:"button",onClick:a,className:"button-dark px-1 py-0.25 text-[0.75rem]",children:"Close"}),bodyClassName:"p-0.5 text-xs",children:r.jsx("pre",{className:"surface max-h-[70vh] overflow-y-auto whitespace-pre-wrap break-words font-mono text-[0.72rem] text-slate-200",children:l})})})})}function $u(){const{identifySession:t}=$2(),a=h1(H=>H.session?.overseerVote||null),l=h1(H=>H.overseerMemory||null),{value:o}=N1("profile",{nickname:""}),{value:u}=N1("identity",{cookieUserId:""}),{value:d,save:p}=N1("overseerPreference",{enabled:!1}),[v,g]=f.useState(!1),x=!!d?.enabled,z=!!a?.running,b=async H=>{const S=!!H?.target?.checked;await p(w=>({...w||{},enabled:S})),await t({cookieUserId:String(u?.cookieUserId||"").trim(),nickname:String(o?.nickname||"").trim(),overseerEnabled:S})},y=r.jsx("button",{type:"button",onClick:()=>g(!0),className:"button-dark px-1 py-0.25 text-[0.75rem]",title:"View overseer memory",children:"Memory"});return r.jsxs(r.Fragment,{children:[r.jsxs(B1,{title:"Overseer vote",actions:y,bodyClassName:"space-y-0.5 text-center text-xs text-slate-200",children:[r.jsxs("label",{className:`flex items-center justify-center gap-1.5 rounded px-1 py-0.5 ${x?"bg-emerald-600/80 text-emerald-50":"bg-slate-600/70 text-slate-100"}`,children:[r.jsx("input",{type:"checkbox",checked:x,onChange:b}),r.jsxs("span",{children:["Your vote: ",x?"Yes":"No"]})]}),r.jsx("span",{className:"text-xs",children:"Enable a local LLM in chat"}),r.jsxs("div",{className:"mt-0.5 text-slate-400",children:["Yes!: ",Number(a?.yesCount||0),", No...: ",Number(a?.noCount||0)]}),r.jsx("div",{className:"mt-0.5 flex justify-center",children:r.jsx("span",{className:`inline-flex items-center rounded px-1.5 py-0.5 text-[0.7rem] font-medium ${z?"bg-emerald-600/80 text-emerald-50":"bg-slate-600/70 text-slate-100"}`,children:z?"Running!":"Stopped by vote."})})]}),v?r.jsx(yX,{memory:l,onClose:()=>g(!1)}):null]})}const CX=224,wX=144,HX=300,SX=8;function VX(){const t=R1(l=>l.state.roverId),a=U4(t,Dc,Vo);return r.jsx(B1,{title:"Roomba sensor view",children:r.jsx("div",{className:"aspect-square w-full",children:r.jsx(wu,{mapTelemetry:a})})})}function AX(){const t=R1(T=>T.state.roverId),a=R1(T=>T.state.keymap),l=R1(T=>T.state.camera),o=R1(T=>T.state.horn),u=R1(T=>T.pipeline?.nightVision),d=R1(T=>T.pipeline?.nightVisionState),p=R1(T=>T.pipeline?.horn),{setServoAngle:v,setNightVision:g,startHorn:x,stopHorn:z}=p3(),b=H0(),y=vu(t),H=y.docked&&!y.driving,S=l?.config,w=!!(t&&l?.enabled&&S),C=!!(t&&u),V=!!(t&&p),A=o?.overheated,B=typeof S?.minAngle=="number"?S.minAngle:-30,k=typeof S?.maxAngle=="number"?S.maxAngle:30,D=typeof l?.angle=="number"?l.angle:typeof S?.homeAngle=="number"?S.homeAngle:(B+k)/2,j=q2(a?.nightVisionToggle?.[0]),N=q2(a?.hornHonk?.[0]),E=q2(a?.cameraUp?.[0]),Z=q2(a?.cameraDown?.[0]),q=!!(!t||b.cameraLocked);return r.jsxs(B1,{hideHeader:!0,className:"h-full",bodyClassName:`grid h-full min-h-0 grid-rows-[minmax(0,1fr)_auto] ${C2}`,children:[r.jsx(pu,{layout:"desktop",expand:!0,driveDockState:y}),H?null:r.jsxs("div",{className:`${v6} p-0 text-sm text-slate-200`,children:[C&&r.jsx(fu,{nightVisionOn:d?.nightVisionOn,disabled:!t,onToggle:g,keyLabel:j}),V&&r.jsx(du,{disabled:!t||A,onStart:x,onStop:z,keyLabel:N,active:o?.active,heat:o?.heat}),w&&r.jsx(ZQ,{value:D,min:B,max:k,label:"Camera tilt",disabled:q,onChange:v,keyDownLabel:Z,keyUpLabel:E,className:`${v6} rounded-xl border-2 border-emerald-300/70 bg-emerald-900 px-1 py-1 text-emerald-50`,labelRowClass:"text-xs text-emerald-100",labelClass:"text-sm font-semibold",valueClass:"font-mono text-slate-100",sliderClass:"w-full",accentClass:"accent-emerald-400",endpointClass:"text-[0.7rem] text-emerald-100/80"})]})]})}function LX({layout:t,onOpenHelpOverlay:a}){const[l,o]=f.useState("telemetry"),u=f.useRef(null),[d,p]=f.useState(CX),v=h1(V=>!!V.session?.isVerified),g=h1(V=>String(V.session?.assignment?.roverId||"").trim()),x=h1(V=>{const A=String(V.session?.assignment?.roverId||"").trim();return A&&V.session?.audioForward?.[A]||null}),z=R1(V=>!!V.state.mic?.pttActive),{value:b}=N1("vipAudio",{openMicEnabled:!1,pttMode:"live"}),y=v?"bg-emerald-400":"bg-red-600",H=!!b?.openMicEnabled,S=b?.pttMode==="clip"?"clip":"live",w=!!(g&&v&&(S==="clip"?z:H||z)),C=!!(g&&v&&S==="clip"&&x?.source==="upload"&&x?.state==="playing");return f.useLayoutEffect(()=>{const V=u.current;if(!V)return;let A=0,B=0,k=0;const D=()=>{A=0;const{top:Z}=V.getBoundingClientRect(),q=window.innerHeight-Z-SX,T=Math.round(Math.max(wX,Math.min(HX,q)));p(P=>Math.abs(P-T)>1?T:P)},j=()=>{A||(A=window.requestAnimationFrame(D))},N=()=>{B=window.requestAnimationFrame(()=>{B=0,k=window.requestAnimationFrame(()=>{k=0,D()})})},E=typeof ResizeObserver=="function"?new ResizeObserver(()=>{j()}):null;return j(),N(),E&&V.parentElement&&E.observe(V.parentElement),window.addEventListener("resize",j),document.addEventListener("scroll",j,{capture:!0,passive:!0}),()=>{A&&window.cancelAnimationFrame(A),B&&window.cancelAnimationFrame(B),k&&window.cancelAnimationFrame(k),E?.disconnect(),window.removeEventListener("resize",j),document.removeEventListener("scroll",j,{capture:!0})}},[l]),r.jsx("section",{className:"text-base",children:r.jsxs(ra,{defaultTab:"telemetry",currentTab:l,onTabChange:o,children:[r.jsxs(ia,{children:[r.jsx(I2,{id:"telemetry",children:"Controls"}),r.jsx(I2,{id:"activities",children:"Activities"}),r.jsx(I2,{id:"vip",highlight:C?"green":w?"pink":"none",children:r.jsxs("span",{className:"inline-flex items-center gap-2",children:[r.jsx("span",{children:"VIP"}),r.jsx("span",{className:`inline-block h-3 w-3 rounded-full ${y}`,"aria-hidden":"true",title:v?"Verified":"Not verified"})]})}),r.jsx(I2,{id:"help",children:"Help"}),r.jsx(I2,{id:"settings",children:"Settings"})]}),r.jsxs(sa,{children:[r.jsx(Y2,{id:"telemetry",children:r.jsxs("div",{className:`flex flex-col ${C2}`,children:[r.jsxs("div",{className:`flex flex-col ${C2}`,children:[r.jsxs("div",{className:`grid items-stretch ${C2} grid-cols-[minmax(0,1.35fr)_minmax(0,0.95fr)]`,children:[r.jsx(VX,{}),r.jsx(AX,{})]}),r.jsxs("div",{className:`grid ${C2} grid-cols-[minmax(0,1fr)_minmax(0,0.9fr)_minmax(0,0.75fr)]`,children:[r.jsx(rt,{}),r.jsx($c,{panelId:"replay-sources-desktop",fillHeight:!0}),r.jsx(EQ,{})]}),r.jsxs("div",{ref:u,className:`grid min-h-0 items-stretch ${C2} grid-cols-[minmax(0,1.3fr)_minmax(0,0.22fr)]`,style:{height:`${d}px`},children:[r.jsx(U8,{fillHeight:!0}),r.jsxs("div",{className:`grid min-h-0 ${C2} grid-rows-[auto_minmax(0,1fr)]`,children:[r.jsx($u,{}),r.jsx(G7,{compact:!0,hideNicknameForm:!0,fillHeight:!0})]})]})]}),r.jsx(Bu,{}),r.jsx(ta,{defaultOrientation:"horizontal",panelId:"rightpane-telemetry"})]})}),r.jsx(Y2,{id:"activities",children:r.jsxs("div",{className:`flex flex-col ${C2}`,children:[r.jsx(Zu,{}),r.jsx(P7,{}),r.jsx(zu,{})]})}),r.jsx(Y2,{id:"vip",keepMounted:!0,children:r.jsx(_u,{isActive:l==="vip"})}),r.jsx(Y2,{id:"help",children:r.jsx(Du,{layout:t,onOpenOverlay:a})}),r.jsx(Y2,{id:"settings",children:r.jsx(Eu,{})})]})]})})}const BX=new Set(["admin","lockdown","lockdown-admin"]),jX=new Set(["lockdown","lockdown-admin"]),NX=new Set(["admin","lockdown"]);function FX(t="admin"){return t==="lockdown"?{title:"Lockdown mode active",description:"Only the server owners can access the interface at this time."}:{title:"Admin mode active"}}function kX(){const t=h1(x=>x.session?.mode||null),a=h1(x=>x.session?.role||null),l=h1(x=>x.session?.adminReason?.text||""),o=h1(x=>x.session?.timezone||"UTC"),u=NX.has(t),d=t==="lockdown"?jX.has(a):BX.has(a),p=D8(1e3,u&&!d),v=f.useMemo(()=>{const x=new Date(p);try{return new Intl.DateTimeFormat("en-US",{timeZone:o,hour:"numeric",minute:"2-digit",second:"2-digit"}).format(x)}catch{return x.toLocaleTimeString()}},[p,o]);if(!u||d)return null;const g=FX(t);return r.jsx("div",{className:"pointer-events-auto fixed inset-0 z-50 flex items-center justify-center bg-black px-0.5 py-0.5",children:r.jsxs("div",{className:"surface w-full max-w-md space-y-0.5 text-slate-100 shadow-2xl",children:[r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-lg font-semibold",children:g.title}),r.jsx("p",{className:"text-sm text-slate-300",children:g.description})]}),r.jsxs("div",{className:"surface-muted space-y-0.5",children:[r.jsx("p",{className:"text-[0.7rem] tracking-wide text-slate-400",children:"Reason for locking:"}),r.jsx("p",{className:"text-lg font-semibold text-slate-100",children:l||"No reason set."}),r.jsxs("p",{className:"text-center text-sm text-slate-300",children:["Server time: ",v]})]}),r.jsx("div",{className:"surface-muted",children:r.jsx(ju,{})}),r.jsx("div",{className:"w-full justify-center items-center",children:r.jsx(et,{id:"discord",label:"Join our Discord server for updates!"})}),"You can still use the chat while the server is locked:",r.jsx("div",{className:"max-h-80 overflow-y-auto",children:r.jsx(U8,{nicknameLayout:"stacked"})})]})})}const RX="/assets/turn_alert-DVO8EZJa.mp3";function EX(t,a=1){const l=f.useRef(null);return f.useEffect(()=>{l.current=new Audio(t),l.current.volume=a,l.current.load()},[t,a]),()=>{l.current&&(l.current.volume=a,l.current.currentTime=0,l.current.play().catch(()=>{}))}}function TX(){const t=h1(z=>z.session?.socketId||null),a=h1(z=>z.session?.turnQueues||{}),l=h1(z=>z.session?.roster||[]),{pushAlert:o}=$2(),{value:u}=N1("audio",p2),d=Number.isFinite(u?.masterVolume)?u.masterVolume:p2.masterVolume,p=Number.isFinite(u?.alertVolume)?u.alertVolume:p2.alertVolume,v=Math.max(0,Math.min(1,d*p)),g=EX(RX,v),x=f.useRef(new Set);return f.useEffect(()=>{if(!t)return;const z=[];Object.entries(a).forEach(([b,y])=>{!y||!y.current||y.current!==t||x.current.has(b)||(z.push(b),x.current.add(b))}),Object.keys(a).forEach(b=>{(!a[b]||a[b].current!==t)&&x.current.delete(b)}),z.length!==0&&(z.forEach(b=>{const y=l.find(H=>String(H.id)===String(b))?.name||b;o({title:"Your turn!",message:`You now control ${y}.`,color:"#ff5722"})}),g())},[a,g,o,l,t]),null}function qX({visible:t,mode:a,onEnterFullscreen:l,onDismiss:o}){if(!t)return null;const u=a==="pwa-hint";return r.jsx("div",{className:"fixed inset-0 z-40 flex items-center justify-center p-1 pointer-events-none bg-black/20",children:r.jsx("div",{className:"pointer-events-auto w-full max-w-sm surface center",children:r.jsxs("div",{className:"space-y-0.5 p-1 text-sm text-slate-100",children:[r.jsx("h2",{className:"text-base font-semibold text-white border-b border-slate-700",children:"Better in fullscreen!"}),u?r.jsxs("p",{className:"text-slate-300",children:["For fullscreen on iOS, open Safari's share menu and pick ",r.jsx("strong",{children:"Add to Home Screen"}),". Launching from the home screen then makes it fullscreen."]}):r.jsx("p",{className:"text-slate-300",children:"Enable fullscreen to free up more space for the video feed and controls. You can exit fullscreen at any time via the system back or home gesture."}),r.jsx("p",{className:"text-xs border-t border-b border-slate-700 p-0.5 text-blue-300 text-center",children:"This will only show once just to let you know. There is a fullscreen button in the bottom right for later use."}),r.jsxs("div",{className:"flex justify-end gap-0.5 pt-1 text-sm",children:[r.jsx("button",{type:"button",className:"rounded border border-slate-600 px-3 py-1 text-slate-200",onClick:o,children:u?"Got it":"Not now"}),!u&&r.jsx("button",{type:"button",className:"rounded bg-cyan-500 px-3 py-1 font-semibold text-black hover:bg-cyan-400",onClick:l,children:"Enter fullscreen"})]})]})})})}function DX({side:t="right",onClick:a}){const l=t==="left"?"left-2":"right-2";return r.jsx("button",{type:"button",onClick:a,className:`fixed bottom-2 ${l} z-30 flex h-10 w-10 items-center justify-center rounded-full border border-slate-700 bg-black text-slate-200 transition hover:bg-zinc-900 active:scale-95`,"aria-label":"Enter fullscreen",title:"Enter fullscreen",children:r.jsx("svg",{viewBox:"0 0 24 24",width:"18",height:"18","aria-hidden":"true",className:"pointer-events-none",children:r.jsx("path",{d:"M4 9V4h5M15 4h5v5M20 15v5h-5M9 20H4v-5",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})})})}const OX=new Set(["mobile-portrait","mobile-landscape"]),_X=()=>{if(typeof navigator>"u")return!1;const t=navigator.userAgent||navigator.platform||"";return/iphone|ipad|ipod/i.test(t)},Ts=()=>{if(typeof document>"u")return!1;const t=document;return!!(t.fullscreenEnabled||t.webkitFullscreenEnabled||t.mozFullScreenEnabled||t.msFullscreenEnabled)};function UX(t){const{value:a,save:l,status:o}=N1("page",{fullscreenPromptShown:!1}),u=OX.has(t),[d,p]=f.useState(!1),[v,g]=f.useState("native"),[x,z]=f.useState(!1),b=!!a?.fullscreenPromptShown,y=f.useMemo(()=>_X(),[]),H=f.useMemo(()=>Ts(),[]),S=f.useCallback(()=>{o==="ready"&&l(B=>B?.fullscreenPromptShown?B:{...B||{},fullscreenPromptShown:!0})},[l,o]),w=f.useCallback(()=>{if(o!=="ready"){p(!1);return}if(!u){p(!1),z(!1);return}if(!x&&b){p(!1);return}if(y){g("pwa-hint"),p(!0);return}if(!Ts()){p(!1);return}if(typeof document<"u"&&document.fullscreenElement){p(!1);return}g("native"),p(!0)},[x,b,y,u,o]);f.useEffect(()=>{w()},[w]),f.useEffect(()=>{if(typeof document>"u")return;const B=()=>{if(document.fullscreenElement){z(!1),p(!1);return}w()};return document.addEventListener("fullscreenchange",B),document.addEventListener("webkitfullscreenchange",B),()=>{document.removeEventListener("fullscreenchange",B),document.removeEventListener("webkitfullscreenchange",B)}},[w]);const C=f.useCallback(()=>{S(),z(!1),p(!1)},[S]),V=f.useCallback(async()=>{if(!u||y||typeof document>"u")return!1;if(document.fullscreenElement)return!0;const B=document.documentElement;if(!B)return!1;const k=B.requestFullscreen||B.webkitRequestFullscreen||B.mozRequestFullScreen||B.msRequestFullscreen;if(!k)return!1;try{const D=k.call(B);return D&&typeof D.then=="function"&&await D,S(),z(!1),p(!1),!0}catch(D){return console.warn("Failed to enter fullscreen",D),!1}},[y,u,S]),A=f.useCallback(()=>{u&&(z(!0),g(y?"pwa-hint":"native"),p(!0))},[y,u]);return f.useEffect(()=>{u||(z(!1),p(!1))},[u]),{visible:d,mode:v,isIOS:y,nativeSupported:H,enterFullscreen:V,dismiss:C,showPrompt:A}}function ZX({visible:t,layout:a,onClose:l,showOnLoad:o,onToggleShowOnLoad:u}){const d=R1(v=>v.state.keymap);if(!t)return null;const p=v=>{const g=!v.target.checked;u?.(g)};return r.jsx("div",{className:"fixed inset-0 z-30 flex items-center justify-center bg-black/80 px-0.5 py-0.5",children:r.jsxs("div",{className:"pointer-events-auto surface w-full max-w-4xl max-h-[90vh] overflow-hidden shadow-2xl",children:[r.jsxs("div",{className:"flex items-center justify-between border-b border-slate-700 px-0.5 py-0.25 text-sm text-slate-200",children:[r.jsx("span",{className:"font-semibold",children:"Help & controls"}),r.jsxs("div",{className:"flex items-center gap-0.5 text-[0.8rem] text-slate-300",children:[r.jsxs("label",{className:"flex items-center gap-0.5",children:[r.jsx("input",{type:"checkbox",checked:!o,onChange:p,className:"accent-cyan-500"}),r.jsx("span",{children:"Don't show again"})]}),r.jsx("button",{type:"button",onClick:l,className:"button-dark px-2 py-0.25 text-[0.8rem]",children:"Close"})]})]}),r.jsx("div",{className:"max-h-[85vh] overflow-y-auto p-0.5",children:r.jsx(qu,{layout:a,keymap:d||{}})})]})})}function s0({label:t,keyLabel:a}){return r.jsxs("div",{className:"surface-muted flex items-center justify-between gap-0.5 px-0.5 py-0.35 text-[0.8rem] text-slate-200",children:[r.jsx("span",{children:t}),r.jsx($7,{label:a})]})}function $X({keymap:t}){return r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-sm text-slate-200",children:'1. Press "Start Driving" put your rover into driving mode.'}),r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-sm text-slate-200",children:"2. Use the drive controls to move your rover:"}),r.jsxs("div",{className:"space-y-0.5",children:[r.jsx(s0,{label:"Forward",keyLabel:q2(t?.driveForward?.[0])}),r.jsx(s0,{label:"Backward",keyLabel:q2(t?.driveBackward?.[0])}),r.jsx(s0,{label:"Turn Left",keyLabel:q2(t?.driveLeft?.[0])}),r.jsx(s0,{label:"Turn Right",keyLabel:q2(t?.driveRight?.[0])}),r.jsx(s0,{label:"Move faster",keyLabel:q2(t?.boostModifier?.[0])}),r.jsx(s0,{label:"Move slower",keyLabel:q2(t?.slowModifier?.[0])})]})]}),r.jsx("p",{className:"text-sm text-slate-200",children:'3. When done, enter "Docking Assist" and line up the front sensor with the dock sensor.'})]})}function GX(){return r.jsxs("div",{className:"space-y-0.5 text-sm text-slate-200",children:[r.jsx("p",{children:'1. Press "Start Driving" put your rover into driving mode.'}),r.jsx("p",{children:"2. Touch and hold in Joystick area to move."}),r.jsx("p",{children:"3. Use the other column for motor, horn, and camera controls."}),r.jsx("p",{children:'4. When done, enter "Docking Assist" and line up the front sensor with the dock sensor.'})]})}function PX({visible:t,layout:a,showOnLoad:l,onToggleShowOnLoad:o,onOpenHelp:u,onClose:d}){const p=R1(z=>z.state.keymap),v=a==="desktop",g=f.useMemo(()=>p||{},[p]);if(!t)return null;const x=z=>{const b=!z.target.checked;o?.(b)};return r.jsx("div",{className:"fixed inset-0 z-40 flex items-end justify-center bg-black/75 p-0.5 items-center",children:r.jsxs("div",{className:"pointer-events-auto surface w-full max-w-3xl overflow-hidden shadow-2xl",children:[r.jsx("div",{className:"flex items-center justify-between border-b border-slate-700 px-0.5 py-0.35 text-sm text-slate-200",children:r.jsx("span",{className:"font-semibold text-xl",children:"Welcome! To get started:"})}),r.jsxs("div",{className:`grid gap-0.5 p-0.5 ${v?"md:grid-cols-[minmax(0,1.5fr)_minmax(0,1fr)]":"grid-cols-1"}`,children:[r.jsx("section",{className:"space-y-0.5 border-b border-slate-700",children:v?r.jsx($X,{keymap:g}):r.jsx(GX,{})}),r.jsxs("section",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-left",children:"Next..."}),r.jsxs("div",{className:"surface space-y-0.5 p-0.5 border-b border-slate-700",children:[r.jsx("p",{className:"text-xl font-semibold text-slate-200",children:"Set your nickname"}),r.jsx("p",{className:"text-sm font-semibold text-slate-200",children:"Nicknames are assigned randomly by default, you can change yours here."}),r.jsx(lt,{compact:!0})]}),r.jsxs("div",{className:"surface p-0.5",children:[r.jsx("p",{className:"text-xl font-semibold text-slate-200",children:"Join our Discord server!"}),r.jsx("p",{className:"text-sm font-semibold text-slate-200",children:"We have an active and welcoming community :3"}),r.jsx(et,{id:"discord",label:"Join Discord"})]})]})]}),r.jsxs("div",{className:"flex flex-wrap items-center justify-between gap-0.5 border-t border-slate-700 px-0.5 py-0.35 text-[0.8rem]",children:[r.jsxs("label",{className:"flex items-center gap-0.5 text-slate-300",children:[r.jsx("input",{type:"checkbox",checked:!l,onChange:x,className:"accent-cyan-500"}),r.jsx("span",{children:"Don't show again"})]}),r.jsx("div",{className:"flex items-center gap-0.5",children:r.jsx("button",{type:"button",onClick:d,className:"button-dark px-1 py-0.25 text-2xl bg-green-600 hover:bg-green-400 hover:border-green-100 border-green-400",children:"Got it! Let me in!"})})]})]})})}const IX=`aroma +`).trim();if(P){k(!0);try{await l(P,E),A(""),p(),v(!1)}catch(o1){alert(o1.message)}finally{k(!1)}}}return r.jsxs("form",{className:"chat-composer",onSubmit:Z,children:[r.jsx("div",{className:"chat-composer-nickname",children:r.jsx(jQ,{compact:!0})}),r.jsx("input",{className:"field-input chat-composer-input",value:V,onChange:q=>{const T=q.target.value;A(T),v(!!T.trim())},onFocus:q=>{u(q),v(!!V.trim())},onBlur:q=>{d(q),v(!1)},onKeyDown:q=>{q.key==="Enter"&&!V.trim()&&(q.preventDefault(),p(),v(!1))},ref:q=>o(q,{target:"panel"}),placeholder:g?"Type a message…":a?"":"Spectators cannot chat",disabled:!g}),r.jsx("button",{type:"submit",disabled:!g||B,className:"button-dark chat-composer-send disabled:opacity-50",children:B?"...":"Send"}),r.jsx(NQ,{ttsSupported:x,speak:N,onSpeakChange:j,engine:y,voice:H,pitch:S,googlePitch:w,googleSpeed:C,saveTtsSettings:b})]})}const kQ=f.memo(BQ),RQ=f.memo(FQ);function U8({hideInput:t=!1,hideSpectatorNotice:a=!1,fillHeight:l=!1,allowSpectatorInput:o=!1,title:u="Chat and speech",minimal:d=!1}){const p=d||t,v=d?"":u;return r.jsxs(B1,{title:v,hideHeader:d||!v,fillHeight:l,bodyClassName:"space-y-0.5 text-base",children:[r.jsx(kQ,{fillHeight:l}),!p&&r.jsx(RQ,{allowSpectatorInput:o,hideSpectatorNotice:a})]})}function oa({className:t=""}){const a=h1(l=>l.session?.socials??[]).slice(0,4);return a.length?r.jsx("div",{className:`grid grid-cols-2 grid-rows-2 gap-0.5 ${t}`,children:a.map(l=>r.jsx(et,{id:l?.id||l?.key||l?.service||null},`${l?.id||l?.label||"social"}-${l?.url||""}`))}):null}function EQ(){return r.jsx(B1,{title:"Links!",fillHeight:!0,bodyClassName:"flex flex-1 min-h-0 flex-col gap-0.5 text-base",children:r.jsx(oa,{className:"flex-1 min-h-0"})})}function tc(...t){return t.filter(Boolean).join(" ")}function TQ(t){const a=t?.batteryState?.percentDisplay;return a==null?"--":`${a}%`}function qQ(t){return t?.batteryState?t.batteryState.urgentActive?"text-red-400":t.batteryState.warnActive?"text-amber-300":"text-emerald-300":"text-slate-400"}function DQ(t){switch(t){case"admin":case"lockdown":case"lockdown-admin":return"text-amber-300";case"spectator":return"text-slate-400";default:return"text-sky-300"}}function OQ(t,a){if(!t)return"";const l=t.nickname||t.socketId?.slice(0,6)||"unknown";return t.socketId&&t.socketId===a?`${l} (you)`:l}function rt({title:t="Rovers"}){const a=h1(N=>N.session?.role||null),l=h1(N=>N.session?.roster??[]),o=h1(N=>N.session?.turnQueues??{}),u=h1(N=>N.session?.users??[]),d=h1(N=>N.session?.socketId||null),p=h1(N=>String(N.session?.assignment?.roverId||"").trim()),v=h1(N=>{const E=String(N.session?.assignment?.roverId||"").trim();return E?(N.session?.roster||[]).find(q=>String(q?.id)===E)?.name||E:""}),{requestControl:g,rebootOwnRover:x}=$2(),[z,b]=f.useState({}),[y,H]=f.useState(!1),S=f.useMemo(()=>a&&a!=="spectator",[a]),w=f.useMemo(()=>a==="admin"||a==="lockdown"||a==="lockdown-admin",[a]),C=f.useMemo(()=>Object.values(o||{}).some(N=>N?.deadline||N?.idleDeadline),[o]),V=D8(1e3,C),A=f.useMemo(()=>{const N=new Set(l.map(Z=>String(Z.id))),E=Object.keys(o||{}).filter(Z=>!N.has(String(Z))).map(Z=>({id:Z,name:Z,locked:!1,batteryState:null}));return[...l,...E]},[l,o]);async function B(N){if(N){b(E=>({...E,[N]:!0}));try{await g(N)}catch(E){alert(E.message)}finally{b(E=>({...E,[N]:!1}))}}}const k=N=>u.find(E=>E.socketId===N)||{socketId:N,nickname:null,role:null};async function D(){if(!(y||!window.confirm(`Reboot your rover "${v}" now?`))){H(!0);try{await x(),alert("Reboot command sent.")}catch(E){alert(E.message)}finally{H(!1)}}}const j=a!=="spectator"&&p?r.jsx("button",{type:"button",onClick:D,disabled:y,className:"button-dark bg-amber-700/80 text-amber-50 hover:bg-amber-600 disabled:opacity-40",title:"Reboot your assigned rover",children:y?"Rebooting...":"Reboot My Rover"}):null;return r.jsx(B1,{title:t,actions:j,bodyClassName:"space-y-0.5 text-sm",children:A.length===0?r.jsx("p",{className:"text-sm text-slate-500",children:"No rovers registered."}):r.jsx("ul",{className:"space-y-0.5 text-sm",children:A.map(N=>{const E=String(N.id),Z=o?.[E]||null,q=Z?.queue||[],T=Z?.idleDeadline||Z?.deadline||null,P=T&&T>V?Math.ceil((T-V)/1e3):T?0:null,o1=Z?.current||null,n1=o1?q.findIndex(m1=>m1===o1):-1,I=q.length>1?n1>=0?q[(n1+1)%q.length]:q[0]:null,R=!!(d&&o1&&o1===d),G=!!(d&&I&&I===d),e1=P!=null&&(R||G),d1=!!(N?.private?.enabled&&N?.private?.open),u1=!!(N?.private?.enabled&&!N?.private?.open),F=!!N.locked,_=F&&!w&&!u1,t1=N.lockReason?`locked: ${N.lockReason}`:"locked",r1=z[E]?"...":F&&!u1?t1:"request",f1=S&&!_&&!z[E];return r.jsxs("li",{className:tc("surface flex flex-wrap items-start justify-between gap-0.5",f1&&"cursor-pointer",F?"bg-red-900/40":d1?"bg-amber-700/35 border border-amber-200/30":null),onClick:()=>{f1&&B(N.id)},children:[r.jsxs("div",{className:"flex min-w-0 flex-1 flex-col gap-0.5",children:[r.jsxs("div",{className:"flex items-center justify-between gap-0.5",children:[r.jsxs("div",{className:"flex min-w-0 items-center gap-0.5",children:[r.jsxs("p",{className:"min-w-0 flex items-center gap-0.5 whitespace-nowrap text-slate-200",children:[r.jsx(V4,{rover:N,fallback:E}),N.description?r.jsx("span",{className:"min-w-0 flex-1 truncate text-[0.7rem] text-slate-400",children:N.description}):null]}),e1?r.jsx("span",{className:"rounded bg-slate-800 px-1 text-[0.7rem] text-slate-200",children:R?`${P}s left`:`Your turn in ${P}s`}):null]}),r.jsx("span",{className:tc("text-[0.75rem] font-semibold",qQ(N)),children:TQ(N)})]}),q.length===0?r.jsx("p",{className:"text-[0.7rem] text-slate-500",children:"No queue."}):r.jsx("div",{className:"flex flex-wrap items-center gap-0.5",children:q.map((m1,S1)=>{const V1=k(m1),A1=m1===o1,U1=!!(I&&m1===I&&!A1),Y1=A1?"bg-sky-600 text-white ring-2 ring-amber-300":U1?"bg-emerald-700/60 text-emerald-100 ring-1 ring-emerald-300/70":"bg-slate-800 text-slate-200";return r.jsxs("span",{className:`flex items-center gap-0.5 rounded px-1 text-[0.7rem] ${Y1}`,children:[r.jsx("span",{className:`${DQ(V1.role)} font-semibold`,children:OQ(V1,d)}),A1&&r.jsx("span",{className:"text-[0.65rem] text-slate-200",children:"now"}),U1&&r.jsx("span",{className:"text-[0.65rem] text-emerald-100",children:"next"})]},`${E}-${m1}-${S1}`)})})]}),S?r.jsx("button",{type:"button",onClick:m1=>{m1.stopPropagation(),B(N.id)},disabled:z[E]||_,className:tc("button-dark disabled:opacity-40",F&&"bg-red-600/70 text-white hover:bg-red-600"),children:r1}):null]},N.id)})})})}function _Q(t){switch(t){case"admin":case"lockdown":case"lockdown-admin":return"text-amber-300";case"spectator":return"text-slate-400";default:return"text-sky-300"}}function UQ(t,a){if(!t)return"";const l=t.nickname||t.socketId?.slice(0,6)||"unknown";return t.socketId&&t.socketId===a?`${l} (you)`:l}function G7({hideNicknameForm:t=!1,hideHeader:a=!1,className:l="",fillHeight:o=!1,compact:u=!1}){const d=h1(x=>x.session?.users??[]),p=h1(x=>x.session?.socketId||null),v=f.useMemo(()=>[...d].sort((x,z)=>x.socketId===p?-1:z.socketId===p?1:(x.nickname||"").localeCompare(z.nickname||"")),[p,d]),g=o?"flex-1 min-h-0 overflow-y-auto":u?"h-28 overflow-y-auto":"h-48 overflow-y-auto";return r.jsxs(B1,{title:a?"":"Users",hideHeader:a,fillHeight:o,className:l,bodyClassName:"space-y-0.5 text-base",children:[!t&&r.jsx("div",{className:"space-y-0.5",children:r.jsxs("div",{className:"grid gap-0.5 md:grid-cols-[minmax(0,1.4fr)_minmax(0,1fr)]",children:[r.jsx("div",{className:"min-w-0",children:r.jsx("div",{className:"flex w-full items-center px-0 py-0",children:r.jsx(lt,{compact:u})})}),r.jsx(oa,{})]})}),r.jsx("div",{className:`space-y-0.5 ${o?"flex flex-1 min-h-0 flex-col":""}`,children:r.jsx("div",{className:`flex flex-wrap content-start items-start gap-0.5 px-0 pb-0 ${g} ${u?"text-[0.8rem]":""}`,children:v.length===0?r.jsx("p",{className:"text-sm text-slate-500",children:"Waiting for users…"}):v.map(x=>r.jsx("span",{className:`rounded px-1 py-0.25 text-[0.7rem] ${_Q(x.role)}`,children:UQ(x,p)},x.socketId))})})]})}function v7(t){return typeof t!="number"||Number.isNaN(t)?"—":`${t>0?"+":""}${t.toFixed(1)}°`}function js({label:t}){return t?r.jsx("span",{className:"rounded bg-slate-800 px-1 py-0.5 text-[0.6rem] font-semibold text-slate-200",children:t}):null}function ZQ({value:t,min:a,max:l,step:o=.5,label:u="Camera Tilt",orientation:d="horizontal",onChange:p,onCommit:v,throttleMs:g=0,disabled:x=!1,keyDownLabel:z,keyUpLabel:b,className:y="",labelRowClass:H="",labelClass:S="",valueClass:w="",sliderClass:C="",endpointClass:V="",endpointLabelClass:A="",accentClass:B="",showValue:k=!0,showEndpoints:D}){const j=d==="vertical",E=typeof D=="boolean"?D:!j,[Z,q]=f.useState(t),T=f.useRef(!1),P=f.useRef(null);f.useEffect(()=>{T.current||q(t)},[t]),f.useEffect(()=>()=>{P.current&&clearTimeout(P.current)},[]);const o1=e1=>{if(p){if(g>0){P.current&&clearTimeout(P.current),P.current=setTimeout(()=>{p(e1)},g);return}p(e1)}},n1=e1=>{const d1=Number.parseFloat(e1.target.value);Number.isNaN(d1)||(T.current=!0,q(d1),o1(d1))},I=()=>{T.current=!1,P.current&&(clearTimeout(P.current),P.current=null),v?v(Z):g>0&&p?.(Z)};if(typeof a!="number"||typeof l!="number")return null;const R=[j?"h-28 w-5":"w-full",B,C].filter(Boolean).join(" "),G=r.jsx("input",{type:"range",orient:j?"vertical":void 0,min:a,max:l,step:o,value:Number.isFinite(Z)?Z:a,onChange:n1,onMouseUp:I,onTouchEnd:I,onPointerUp:I,disabled:x,className:R,style:j?{writingMode:"bt-lr",WebkitAppearance:"slider-vertical"}:void 0});return j?r.jsxs("div",{className:`flex h-full flex-col items-center gap-0.5 ${y}`.trim(),children:[k?r.jsx("div",{className:`flex flex-col items-center ${H}`.trim(),children:r.jsx("span",{className:w,children:v7(t)})}):null,r.jsxs("div",{className:"flex min-h-0 flex-1 items-center gap-0.5",children:[r.jsx("span",{className:S,children:u}),G]})]}):r.jsxs("div",{className:`space-y-0.5 ${x?"opacity-50":""} ${y}`.trim(),children:[r.jsxs("div",{className:`flex items-center justify-between ${H}`.trim(),children:[r.jsx("span",{className:S,children:u}),k?r.jsx("span",{className:`rounded bg-slate-800 px-1 py-0.5 text-[0.7rem] font-semibold ${w}`.trim(),children:v7(t)}):null]}),r.jsxs("div",{children:[G,E?r.jsxs("div",{className:`mt-0 flex items-center justify-between ${V}`.trim(),children:[r.jsxs("span",{className:`flex items-center gap-0.5 ${A}`.trim(),children:[z?r.jsx(js,{label:z}):null,v7(a)]}),r.jsxs("span",{className:`flex items-center gap-0.5 ${A}`.trim(),children:[v7(l),b?r.jsx(js,{label:b}):null]})]}):null]})]})}const $Q=/^cu_[a-f0-9]{32}$/,z0="field-input w-full max-w-sm text-left focus:ring-emerald-500",Z8="mx-auto w-full max-w-xl flex justify-center",v0="mx-auto flex w-full max-w-md flex-col items-center space-y-0.5 text-center";function GQ(t){const a=String(t||"").trim();return a?a.length<=10?`${a.slice(0,2)}***${a.slice(-2)}`:`${a.slice(0,6)}...${a.slice(-6)}`:""}const Ns=8*1024*1024,u6=16e3,PQ={iceServers:[{urls:"stun:stun.l.google.com:19302"}],bundlePolicy:"max-bundle",rtcpMuxPolicy:"require"};function Fs(t){let a="";for(let o=0;o{if(!t||t.iceGatheringState==="complete"){l();return}const o=setTimeout(()=>{t.removeEventListener("icegatheringstatechange",u),l()},a);function u(){t.iceGatheringState==="complete"&&(clearTimeout(o),t.removeEventListener("icegatheringstatechange",u),l())}t.addEventListener("icegatheringstatechange",u)})}function ks(t){if(!t)return!1;const a=t.connectionState,l=t.iceConnectionState;return a==="connected"||l==="connected"||l==="completed"}function QQ(t,a=1e4){return new Promise((l,o)=>{if(!t){o(new Error("Peer connection missing"));return}if(ks(t)){l();return}const u=setTimeout(()=>{p(),o(new Error("Peer connection timeout"))},a),d=()=>{ks(t)?(p(),l()):(t.connectionState==="failed"||t.connectionState==="closed"||t.iceConnectionState==="failed")&&(p(),o(new Error(`Peer connection ${t.connectionState||t.iceConnectionState}`)))};function p(){clearTimeout(u),t.removeEventListener("connectionstatechange",d),t.removeEventListener("iceconnectionstatechange",d)}t.addEventListener("connectionstatechange",d),t.addEventListener("iceconnectionstatechange",d)})}async function XQ(t){if(!t?.getParameters||!t?.setParameters)return;const a=t.getParameters()||{},l=a.encodings&&a.encodings[0]||{};a.encodings=[{...l,maxBitrate:64e3,dtx:"disabled"}];try{await t.setParameters(a)}catch{}}function JQ(t,a=6e3){return new Promise((l,o)=>{if(!t){o(new Error("Peer connection missing"));return}const u=Date.now();let d=-1;const p=setInterval(async()=>{if(Date.now()-u>a){clearInterval(p),o(new Error("WHIP connected but no outbound audio flow"));return}try{const v=t.getSenders().filter(g=>g.track?.kind==="audio");for(const g of v){const x=await g.getStats();for(const z of x.values()){if(z.type!=="outbound-rtp"||z.kind!=="audio")continue;const b=Number(z.bytesSent||0),y=Number(z.packetsSent||0);if(d<0)d=b;else if(b>d+200||y>5){clearInterval(p),l();return}}}}catch{}},250)})}function WQ(t){const a=t.reduce((u,d)=>u+d.length,0),l=new Float32Array(a);let o=0;for(const u of t)l.set(u,o),o+=u.length;return l}function tX(t){const a=new Int16Array(t.length);for(let l=0;l{for(let z=0;zx1.state.keymap),g=R1(x1=>!!x1.state.mic?.pttActive),{value:x,save:z}=N1("vipAudio",{openMicEnabled:!1,pttMode:"live"}),b=String(t||"").trim(),[y,H]=f.useState(null),[S,w]=f.useState(!1),C=!!x?.openMicEnabled,A=(x?.pttMode==="clip"?"clip":"live")==="clip",[B,k]=f.useState("idle"),[D,j]=f.useState("idle"),[N,E]=f.useState(""),Z=f.useRef(null),q=f.useRef(null),T=f.useRef(null),P=f.useRef(!1),o1=f.useRef(""),n1=f.useRef(null),I=f.useRef(null),R=f.useRef(null),G=f.useRef(null),e1=f.useRef(null),d1=f.useRef([]),u1=f.useRef(!1),F=f.useRef(u6),_=f.useMemo(()=>b&&a?.[b]||null,[a,b]),t1=!!(_&&_.state!=="offline"&&!_.error),r1=_?.source==="upload"&&_?.state==="playing",f1=_?.source==="mic-whip"&&(_?.state==="starting"||_?.state==="playing"),m1=!!(!A&&q.current&&(C||g)),S1=!A&&(B==="live"||B==="starting"),V1=A&&D==="recording",A1=A&&D==="sending",U1=q2(v?.micPtt?.[0])||"M",Y1=f.useCallback(x1=>{const M1=x1==="clip"?"clip":"live";z(G1=>({...G1||{},pttMode:M1}))},[z]),E1=async()=>{if(!b){E("Take control of your rover first.");return}if(!y){E("Select an audio file first.");return}if(y.size>Ns){E(`File too large (max ${Ns} bytes).`);return}w(!0),E("");try{const x1=await y.arrayBuffer(),M1=Fs(new Uint8Array(x1));await l?.({roverId:b,name:y.name,mime:y.type||"",dataBase64:M1}),E("Upload playback started.")}catch(x1){E(x1?.message||"Failed to play upload.")}finally{w(!1)}},g1=async()=>{if(!b){E("Take control of your rover first.");return}w(!0),E("");try{await o?.(b),E("Upload playback stopped.")}catch(x1){E(x1?.message||"Failed to stop upload.")}finally{w(!1)}},Q=f.useCallback(async x1=>{const M1=String(x1||o1.current||"").trim();if(P.current=!1,k("idle"),T.current){try{T.current.getSenders().forEach(G1=>G1.track?.stop())}catch{}try{T.current.close()}catch{}}if(T.current=null,Z.current)try{Z.current.getTracks().forEach(G1=>G1.stop())}catch{}if(Z.current=null,q.current=null,M1)try{await p?.(M1)}catch{}o1.current=""},[p]),v1=f.useCallback(async x1=>{const M1=await u?.(x1),G1=String(M1?.whipUrl||"").trim(),A2=String(M1?.token||"").trim();if(!G1||!A2)throw new Error("WHIP endpoint unavailable");const P1=await navigator.mediaDevices.getUserMedia({audio:{channelCount:1,sampleRate:u6,echoCancellation:!1,noiseSuppression:!1,autoGainControl:!1}});Z.current=P1;const v2=P1.getAudioTracks()?.[0];if(q.current=v2||null,v2&&(v2.enabled=!!(C||g)),v2?.applyConstraints)try{await v2.applyConstraints({channelCount:1,sampleRate:u6,echoCancellation:!1,noiseSuppression:!1,autoGainControl:!1})}catch{}const X1=new RTCPeerConnection(PQ);T.current=X1,X1.onconnectionstatechange=()=>{if(!P.current)return;const a1=X1.connectionState;if(a1==="connected"){k("live");return}(a1==="failed"||a1==="disconnected"||a1==="closed")&&(k("error"),E(`WHIP transport ${a1}.`))},P1.getAudioTracks().forEach(a1=>{const C1=X1.addTrack(a1,P1);XQ(C1)});const J1=await X1.createOffer({offerToReceiveAudio:!1,offerToReceiveVideo:!1});await X1.setLocalDescription(J1),await KQ(X1,1800);const s1=await fetch(G1,{method:"POST",headers:{"Content-Type":"application/sdp",...YQ(A2)},body:X1.localDescription?.sdp||J1.sdp});if(!s1.ok)throw new Error(`WHIP request failed: ${s1.status}`);const J=await s1.text();await X1.setRemoteDescription({type:"answer",sdp:J}),await QQ(X1,1e4),await JQ(X1,6e3),await d?.(x1)},[C,g,d,u]),z1=f.useCallback(async()=>{if(u1.current=!1,G.current){try{G.current.disconnect()}catch{}G.current.onaudioprocess=null}if(R.current)try{R.current.disconnect()}catch{}if(e1.current)try{e1.current.disconnect()}catch{}if(n1.current)try{n1.current.getTracks().forEach(x1=>x1.stop())}catch{}if(I.current)try{await I.current.close()}catch{}d1.current=[],G.current=null,R.current=null,e1.current=null,n1.current=null,I.current=null,F.current=u6},[]),p1=f.useCallback(async()=>{if(n1.current&&I.current&&G.current)return;j(v2=>v2==="recording"?v2:"arming");const x1=await navigator.mediaDevices.getUserMedia({audio:{channelCount:1,sampleRate:u6,echoCancellation:!1,noiseSuppression:!1,autoGainControl:!1}}),M1=new AudioContext({latencyHint:"interactive"}),G1=M1.createMediaStreamSource(x1),A2=M1.createScriptProcessor(1024,1,1),P1=M1.createGain();P1.gain.value=0,A2.onaudioprocess=v2=>{if(!u1.current)return;const X1=v2.inputBuffer.getChannelData(0);d1.current.push(new Float32Array(X1))},G1.connect(A2),A2.connect(P1),P1.connect(M1.destination),n1.current=x1,I.current=M1,R.current=G1,G.current=A2,e1.current=P1,F.current=M1.sampleRate||u6},[]),F1=f.useCallback(async({send:x1})=>{const M1=u1.current;if(u1.current=!1,!M1){x1||j("idle");return}const G1=d1.current;if(d1.current=[],!x1){j("idle");return}if(!b){j("idle");return}if(!G1.length){j("idle");return}j("sending");try{const A2=WQ(G1),P1=eX(A2,F.current||u6),v2=Fs(P1);await l?.({roverId:b,name:`ptt-${Date.now()}.wav`,mime:"audio/wav",dataBase64:v2}),j("idle")}catch(A2){j("error"),E(A2?.message||"Failed to send PTT clip.")}},[l,b]),n2=f.useCallback(async()=>{if(!b){E("Take control of your rover first.");return}await p1(),d1.current=[],u1.current=!0,j("recording")},[p1,b]);return f.useEffect(()=>{let x1=!1;async function M1(){if(A){await Q(o1.current||b);return}if(!b){await Q(o1.current);return}if(!(P.current&&o1.current===b)&&!(!C&&!g))try{k("starting"),E(""),P.current=!0,o1.current=b,await v1(b),x1||k("live")}catch(G1){x1||(k("error"),E(G1?.message||"Failed to start mic forwarding.")),await Q(b)}}return M1(),()=>{x1=!0}},[A,C,g,b,v1,Q]),f.useEffect(()=>{const x1=q.current;x1&&(x1.enabled=!!(!A&&(C||g)))},[A,C,g]),f.useEffect(()=>{let x1=!1;async function M1(){if(!A||!b){u1.current&&await F1({send:!1}),j("idle");return}try{await p1(),!x1&&!u1.current&&j("idle")}catch(G1){x1||(j("error"),E(G1?.message||"Microphone access failed for clip mode."))}}return M1(),()=>{x1=!0}},[A,p1,F1,b]),f.useEffect(()=>{let x1=!1;async function M1(){if(A){if(!b){u1.current&&await F1({send:!1});return}if(g){if(!u1.current)try{E(""),await n2()}catch(G1){x1||(j("error"),E(G1?.message||"Failed to start clip recording."))}}else u1.current&&await F1({send:!0})}}return M1(),()=>{x1=!0}},[A,F1,g,b,n2]),f.useEffect(()=>()=>{Q(o1.current||b),z1()},[b,Q,z1]),r.jsx(B1,{title:"Audio Controls",children:r.jsxs("div",{className:"grid gap-1",children:[r.jsx("section",{className:"surface",children:r.jsxs("div",{className:"flex items-center justify-center gap-0.5 py-0.25 text-xs text-slate-300",children:[r.jsx("span",{children:"Push-to-Talk Key"}),r.jsx($7,{label:U1})]})}),r.jsxs("div",{className:"grid gap-0.5 grid-cols-1 lg:grid-cols-2",children:[r.jsx("section",{className:"surface h-full",children:r.jsxs("div",{className:"grid h-full gap-0.5 grid-rows-[auto_auto_1fr]",children:[r.jsx("p",{className:"text-sm text-slate-200 text-center",children:"PTT Mode"}),r.jsx("p",{className:"text-xs text-slate-400 text-center",children:"Choose how holding your PTT key behaves: live mic stream, or record-and-send clip. If the live stream doesn't work, switch to clip mode."}),r.jsxs("div",{className:"grid grid-cols-2 gap-0.5",children:[r.jsx("button",{type:"button",className:`button-dark w-full text-sm ${A?"":"bg-emerald-500 text-white hover:bg-emerald-500"}`,onClick:()=>Y1("live"),children:"Live Mic (WHIP)"}),r.jsx("button",{type:"button",className:`button-dark w-full text-sm ${A?"bg-emerald-500 text-white hover:bg-emerald-500":""}`,onClick:()=>Y1("clip"),children:"Record Clip"})]})]})}),r.jsx("section",{className:"surface h-full",children:r.jsxs("div",{className:"grid h-full gap-0.5 grid-rows-[auto_1fr]",children:[r.jsx("p",{className:"text-sm text-slate-200 text-center",children:"PTT Clip Status"}),r.jsxs("div",{className:"grid gap-0.5 content-start",children:[r.jsx(w4,{label:"Mode",active:A,detail:A?"record clip":"live mic"}),r.jsx(w4,{label:"PTT hold",active:g,detail:g?"held":"released"}),r.jsx(w4,{label:"Recording",active:V1,detail:V1?"capturing":"idle"}),r.jsx(w4,{label:"Playback",active:A&&r1,detail:A&&r1?"playing":"idle"})]})]})})]}),r.jsxs("div",{className:"grid gap-0.5 grid-cols-1 lg:grid-cols-2",children:[r.jsx("section",{className:"surface h-full",children:r.jsxs("div",{className:"grid h-full gap-0.5 grid-rows-[auto_auto_1fr_auto]",children:[r.jsx("p",{className:"text-sm text-slate-200 text-center",children:"Audio Upload"}),r.jsxs("label",{className:"mx-auto grid w-full max-w-sm gap-0.5 text-xs text-slate-300 text-center",children:[r.jsx("span",{children:"Audio file (mp3 / wav / ogg)"}),r.jsx("input",{className:`${z0} text-center`,type:"file",accept:".mp3,.wav,.ogg,audio/mpeg,audio/wav,audio/ogg",disabled:S||!b,onChange:x1=>H(x1.target.files?.[0]||null)})]}),r.jsx("div",{className:"mx-auto w-full max-w-sm text-center",children:y?r.jsxs("div",{className:"surface-muted text-xs text-slate-300",children:[y.name," (",y.size," bytes)"]}):r.jsx("div",{className:"surface-muted text-xs text-slate-500",children:"No file selected"})}),r.jsxs("div",{className:"flex justify-center gap-0.5",children:[r.jsx("button",{type:"button",className:"button-dark text-sm",disabled:S||!b,onClick:E1,children:S?"Working...":"Play Upload"}),r.jsx("button",{type:"button",className:"button-dark text-sm",disabled:S||!b,onClick:g1,children:"Stop"})]})]})}),r.jsx("section",{className:"surface h-full",children:r.jsxs("div",{className:"grid h-full gap-0.5 grid-rows-[auto_auto_1fr]",children:[r.jsx("p",{className:"text-sm text-slate-200 text-center",children:"Live Microphone"}),r.jsxs("label",{className:"surface-muted mx-auto flex w-full max-w-sm items-center justify-center gap-0.5 px-0.5 py-0.5 text-xs text-slate-300",children:[r.jsx("input",{type:"checkbox",checked:C,disabled:!b||A,onChange:x1=>z(M1=>({...M1||{},openMicEnabled:!!x1.target.checked}))}),r.jsx("span",{children:A?"Open mic (live mode only)":"Open mic"})]}),r.jsxs("div",{className:"grid gap-0.5 content-start",children:[r.jsx(w4,{label:"WHIP link",active:S1,detail:B}),r.jsx(w4,{label:"Mic hot",active:m1,detail:m1?"transmitting":"muted"}),r.jsxs("div",{className:"surface-muted text-center text-xs text-slate-400",children:["Hold ",r.jsx($7,{label:U1})," to talk"]})]})]})})]}),r.jsx("section",{className:"surface",children:r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-sm text-slate-200 text-center",children:"Audio Status"}),r.jsxs("div",{className:"grid gap-0.5 grid-cols-1 sm:grid-cols-2 lg:grid-cols-6",children:[r.jsx(w4,{label:"Forward pipe",active:t1,detail:t1?"connected":"offline"}),r.jsx(w4,{label:"Upload playback",active:r1,detail:r1?"playing":"idle"}),r.jsx(w4,{label:"Mic relay",active:f1,detail:f1?"active":"idle"}),r.jsx(w4,{label:"WHIP transport",active:S1,detail:B}),r.jsx(w4,{label:"Clip recording",active:V1,detail:V1?"recording":"idle"}),r.jsx(w4,{label:"Clip sending",active:A1,detail:A1?"sending":"idle"})]}),N?r.jsx("div",{className:"text-xs text-slate-400 text-center",children:N}):null]})})]})})}function aX({pendingRequestId:t,currentStoredKey:a,nickname:l,requestVerification:o,applyIdentityKey:u,onMessage:d,fullWidth:p=!1}){const[v,g]=f.useState(0),[x,z]=f.useState(""),[b,y]=f.useState(!1),[H,S]=f.useState(!1),w=()=>{g(1),z(a),y(!1),d?.("")},C=()=>{g(0),y(!1)},V=async B=>{if(B.preventDefault(),!b){d?.("Please confirm your nickname agreement before sending.");return}if(v<3){d?.("Complete all request steps before sending.");return}S(!0),d?.("");try{const k=await u(x);await o?.(),z(k),g(0),y(!1),d?.("Verification request sent to lockdown admins.")}catch(k){d?.(k.message||"Failed to submit request.")}finally{S(!1)}},A=p?"w-full":Z8;return t?r.jsx(B1,{title:"Verification",className:A,bodyClassName:"text-sm text-slate-300",children:r.jsxs("div",{className:v0,children:["Verification request pending: ",t]})}):v===0?r.jsx(B1,{title:"Verification",className:A,children:r.jsx("div",{className:v0,children:r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:w,disabled:H,children:"Request Verification"})})}):r.jsx(B1,{title:"Request verification",className:A,children:r.jsx("form",{onSubmit:V,children:r.jsxs("div",{className:v0,children:[r.jsxs("p",{className:"text-xs text-slate-500",children:["Step ",v," of 3"]}),v===1?r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-xs text-slate-400",children:"Confirm your nickname, which is used as part of the verification process. You can edit it here before requesting."}),r.jsx("div",{className:"mx-auto w-full max-w-sm",children:r.jsx(lt,{compact:!0})}),r.jsxs("div",{className:"surface-muted mx-auto w-full max-w-sm text-xs text-slate-300 text-center",children:["Current nickname: ",r.jsx("span",{className:"font-semibold",children:l||"(not set)"})]}),r.jsxs("label",{className:"flex items-center justify-center gap-0.5 text-xs text-slate-300 text-center",children:[r.jsx("input",{type:"checkbox",className:"accent-emerald-500",checked:b,onChange:B=>y(B.target.checked)}),r.jsx("span",{children:"I understand this nickname is tied to my verification."})]}),r.jsxs("div",{className:"flex justify-center gap-0.5",children:[r.jsx("button",{type:"button",className:"button-dark text-sm",disabled:!l||!b,onClick:()=>g(2),children:"Continue"}),r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:C,children:"Cancel"})]})]}):null,v===2?r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-xs text-slate-400",children:"Save your identity key in a safe place. You can use it to restore your identity in another browser."}),r.jsx("input",{className:z0,type:"password",name:"identity_key_request",autoComplete:"current-password",maxLength:35,value:x,onChange:B=>z(B.target.value.toLowerCase()),placeholder:"Identity key for this request"}),r.jsxs("div",{className:"flex justify-center gap-0.5",children:[r.jsx("button",{type:"button",className:"button-dark text-sm",disabled:!x,onClick:()=>navigator.clipboard?.writeText(x),children:"Copy Key"}),r.jsx("button",{type:"button",className:"button-dark text-sm",disabled:!x,onClick:()=>g(3),children:"Continue"}),r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:()=>g(1),children:"Back"})]})]}):null,v===3?r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-xs text-slate-400",children:"Final step: confirm and send your verification request."}),r.jsx("input",{className:z0,type:"password",name:"identity_key_request_final",autoComplete:"current-password",maxLength:35,value:x,onChange:B=>z(B.target.value.toLowerCase()),placeholder:"Identity key for this request"}),r.jsxs("div",{className:"flex justify-center gap-0.5",children:[r.jsx("button",{type:"submit",className:"button-dark text-sm",disabled:H||!x,children:"Confirm Request"}),r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:()=>g(2),children:"Back"})]})]}):null]})})})}function nX({currentStoredKey:t,applyIdentityKey:a,onMessage:l,fullWidth:o=!1}){const[u,d]=f.useState(0),[p,v]=f.useState(""),[g,x]=f.useState(!1),z=()=>{d(1),v(""),l?.("")},b=()=>{d(0),v("")},y=async S=>{if(S.preventDefault(),!(u<2)){x(!0),l?.("");try{await a(p),d(0),v(""),l?.("Identity key restored.")}catch(w){l?.(w.message||"Failed to restore key.")}finally{x(!1)}}},H=o?"w-full":Z8;return r.jsx(B1,{title:"Identity key",className:H,children:r.jsxs("div",{className:v0,children:[r.jsxs("p",{className:"text-xs text-slate-500",children:["Current: ",GQ(t)||"not set yet"]}),r.jsx("input",{className:z0,type:"password",name:"identity_key_current",autoComplete:"current-password",maxLength:35,value:t,readOnly:!0,placeholder:"Identity key"}),u===0?r.jsxs("div",{className:"flex justify-center gap-0.5",children:[r.jsx("button",{type:"button",className:"button-dark text-sm",disabled:!t,onClick:()=>navigator.clipboard?.writeText(t),children:"Copy Key"}),r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:z,disabled:g,children:"Restore Key"})]}):null,u===1?r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-xs text-slate-400",children:"Restoring your key should only be done when needed. Use this to move your identity to another browser."}),r.jsxs("div",{className:"flex justify-center gap-0.5",children:[r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:()=>d(2),children:"Continue"}),r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:b,children:"Cancel"})]})]}):null,u===2?r.jsxs("form",{className:"space-y-0.5",onSubmit:y,children:[r.jsx("input",{className:z0,type:"password",name:"identity_key_restore",autoComplete:"current-password",maxLength:35,value:p,onChange:S=>v(S.target.value.toLowerCase()),placeholder:"Paste key to restore"}),r.jsxs("div",{className:"flex justify-center gap-0.5",children:[r.jsx("button",{type:"submit",className:"button-dark text-sm",disabled:g||!p,children:"Confirm Restore"}),r.jsx("button",{type:"button",className:"button-dark text-sm",onClick:()=>d(1),children:"Back"})]})]}):null]})})}function lX({requestableRovers:t=[],pendingRequests:a=[],requestPrivateRoverAccess:l,onMessage:o,fullWidth:u=!1}){const[d,p]=f.useState({}),v=f.useMemo(()=>{const z=new Map;return(a||[]).forEach(b=>{b?.roverId&&z.set(String(b.roverId),b)}),z},[a]),g=u?"w-full":Z8,x=async z=>{if(z){p(b=>({...b,[z]:!0})),o?.("");try{(await l?.(z))?.existing?o?.("You already have a pending request for that rover."):o?.("Private rover access request sent to lockdown admins.")}catch(b){o?.(b.message||"Failed to send request.")}finally{p(b=>({...b,[z]:!1}))}}};return r.jsx(B1,{title:"Private rover access requests",className:g,bodyClassName:"text-sm text-slate-300",children:r.jsx("div",{className:v0,children:t.length===0?r.jsx("p",{className:"text-xs text-slate-500",children:"No closed private rovers are available to request right now."}):r.jsx("div",{className:"w-full space-y-0.5",children:t.map(z=>{const b=String(z.id),y=!!d[b],H=v.get(b);return r.jsxs("div",{className:"surface-muted flex items-center justify-between gap-0.5 px-1 py-0.5",children:[r.jsxs("div",{className:"min-w-0 text-left",children:[r.jsx("p",{className:"truncate text-xs",children:r.jsx(V4,{rover:z,fallback:b,className:"max-w-full truncate align-middle"})}),r.jsx("p",{className:"truncate text-[0.7rem] text-slate-500",children:b})]}),H?r.jsx("span",{className:"rounded bg-amber-700/30 px-1 py-0.5 text-[0.7rem] text-amber-200",children:"Pending"}):r.jsx("button",{type:"button",onClick:()=>x(b),disabled:y,className:"button-dark text-xs disabled:opacity-50",children:y?"Sending...":"Request"})]},b)})})})})}function k7(t){return String(t||"").trim()}function rX(t){const a=k7(t);return a?a.includes("DOCKINGRUNNING")?"Returning":a.includes("PAUSED")?"Paused":a.includes("HOUSECLEANINGRUNNING")?"Cleaning":a.includes("SPOTCLEANINGRUNNING")?"Spot cleaning":a.includes("STATE_START")?"Starting":a.includes("STATE_IDLE")||a.includes("STATE_STANDBY")?"Idle":a:"--"}function Ou(t){return t==="good"?"bg-emerald-600 text-white":t==="warn"?"bg-amber-500 text-slate-900":t==="danger"?"bg-rose-600 text-white":"bg-slate-700 text-slate-100"}function p7({label:t,value:a,tone:l="muted",valueClass:o="",hideLabel:u=!1}){return r.jsxs("div",{className:`rounded-md px-1 py-0.75 text-center ${Ou(l)}`,children:[u?null:r.jsx("div",{className:"text-[0.72rem] opacity-90",children:t}),r.jsx("div",{className:`text-sm font-semibold ${o}`,title:String(a||""),children:a})]})}function iX({neato:t,onStart:a,onSendHome:l,onLocate:o,onClearErrors:u,onPowerCycle:d,fullWidth:p=!1}){const[v,g]=f.useState(""),x=p?"w-full":"w-full max-w-xl",z=!!t?.configured,b=!!(t?.enabled&&t?.connected),y=!!t?.telemetry?.extPowerPresent,H=!!t?.telemetry?.chargingActive,S=rX(t?.telemetry?.uiState),w=k7(t?.telemetry?.robotState)||"--",C=t?.telemetry?.batteryPercent,V=Number.isFinite(C)?`${C}%`:"--",A=t?.telemetry?.batteryVoltage,B=Number.isFinite(A)?`${A.toFixed(2)} V`:"--",k=k7(t?.telemetry?.robotError)||"--",D=k7(t?.telemetry?.robotAlert)||"--",j=t?.controls||{},N=!!j?.start?.available,E=!!j?.sendHome?.available,Z=!!j?.locate?.available,q=!!j?.clearErrors?.available,T=!!j?.powerCycle?.available,P=z&&b&&N,o1=z&&b&&E,n1=z&&b&&Z,I=z&&b&&q,R=z&&b&&T,G=async(_,t1)=>{if(t1){g(_);try{await t1()}catch{}finally{g("")}}},e1=z?b?"Online":"Offline":"Not configured",d1=z?b?"good":"danger":"warn",u1=C==null?"muted":C>=60?"good":C>=25?"warn":"danger",F=y?"Docked":S!=="--"?S:"Away from dock";return r.jsx(B1,{title:"Neato Controls",className:x,bodyClassName:"text-sm text-slate-200",actions:r.jsx("span",{className:`inline-flex w-auto rounded px-1 py-0.25 text-xs font-semibold ${Ou(d1)}`,children:e1}),children:r.jsxs("div",{className:"grid gap-0.5",children:[r.jsx("div",{className:"text-center",children:r.jsx("p",{className:"text-xs text-slate-400",children:"Control the autonomous Neato robovac. Be nice to him."})}),r.jsxs("div",{className:"grid grid-cols-1 lg:grid-cols-2 gap-0.5",children:[r.jsxs("div",{className:"surface-muted grid gap-0.5",children:[r.jsx("p",{className:"text-xs text-slate-300 text-center",children:"Controls"}),r.jsx("button",{type:"button",disabled:!P||!!v,onClick:()=>G("start",a),className:"rounded-md border border-sky-300 bg-emerald-600 px-1 py-1 text-base font-semibold text-white transition hover:border-sky-500 hover:bg-emerald-500 disabled:opacity-50",children:v==="start"?"Starting...":"Start cleaning"}),r.jsx("button",{type:"button",disabled:!o1||!!v,onClick:()=>G("sendHome",l),className:"rounded-md border border-sky-300 bg-sky-600 px-1 py-1 text-base font-semibold text-white transition hover:border-sky-500 hover:bg-sky-500 disabled:opacity-50",children:v==="sendHome"?"Sending...":"Send to dock"}),r.jsxs("div",{className:"grid grid-cols-3 gap-0.5",children:[r.jsx("button",{type:"button",disabled:!n1||!!v,onClick:()=>G("locate",o),className:"w-full rounded-md border border-sky-300 bg-fuchsia-600 px-1 py-0.5 text-xs font-semibold text-white transition hover:border-sky-500 hover:bg-fuchsia-500 disabled:opacity-50",children:v==="locate"?"Playing...":"Play sound"}),r.jsx("button",{type:"button",disabled:!I||!!v,onClick:()=>G("clearErrors",u),className:"w-full rounded-md border border-sky-300 bg-amber-500 px-1 py-0.5 text-xs font-semibold text-slate-900 transition hover:border-sky-500 hover:bg-amber-400 disabled:opacity-50",children:v==="clearErrors"?"Clearing...":"Clear errors"}),r.jsx("button",{type:"button",disabled:!R||!!v,onClick:()=>G("powerCycle",d),className:"w-full rounded-md border border-rose-300 bg-rose-600 px-1 py-0.5 text-xs font-semibold text-white transition hover:border-rose-500 hover:bg-rose-500 disabled:opacity-50",children:v==="powerCycle"?"Cycling...":"Power cycle"})]}),r.jsxs("div",{className:"surface-muted grid gap-0.5 pt-0.25",children:[r.jsx("p",{className:"text-xs text-slate-300 text-center",children:"Power status"}),r.jsxs("div",{className:"grid grid-cols-2 gap-0.5",children:[r.jsx(p7,{label:"Battery",value:V,tone:u1}),r.jsx(p7,{label:"Voltage",value:B,tone:"muted"}),r.jsx(p7,{label:"Docked",value:y?"Docked":"Not docked",tone:y?"good":"muted",hideLabel:!0}),r.jsx(p7,{label:"Charging",value:H?"charging":"not charging",tone:H?"good":"muted",hideLabel:!0})]})]})]}),r.jsx("div",{className:"grid gap-0.5",children:r.jsxs("div",{className:"surface-muted grid gap-0.5",children:[r.jsx("p",{className:"text-xs text-slate-300 text-center",children:"Robot Status"}),r.jsxs("div",{className:"grid gap-0.5",children:[r.jsxs("div",{className:"rounded-md bg-slate-800 px-1 py-0.5",children:[r.jsx("div",{className:"text-[0.72rem] text-slate-300",children:"Robot state (raw)"}),r.jsx("div",{className:"font-mono text-sm text-slate-100 break-all",children:w})]}),r.jsxs("div",{className:"rounded-md bg-slate-800 px-1 py-0.5",children:[r.jsx("div",{className:"text-[0.72rem] text-slate-300",children:"Basic state"}),r.jsx("div",{className:"font-mono text-sm text-slate-100 break-all",children:F})]}),r.jsxs("div",{className:"rounded-md bg-slate-800 px-1 py-0.5",children:[r.jsx("div",{className:"text-[0.72rem] text-slate-300",children:"UI state"}),r.jsx("div",{className:"font-mono text-sm text-slate-100 break-all",children:S})]}),r.jsxs("div",{className:"rounded-md bg-slate-800 px-1 py-0.5",children:[r.jsx("div",{className:"text-[0.72rem] text-slate-300",children:"Robot error"}),r.jsx("div",{className:"font-mono text-sm text-slate-100 break-all",children:k})]}),r.jsxs("div",{className:"rounded-md bg-slate-800 px-1 py-0.5",children:[r.jsx("div",{className:"text-[0.72rem] text-slate-300",children:"Robot alert"}),r.jsx("div",{className:"font-mono text-sm text-slate-100 break-all",children:D})]})]})]})})]}),!z||!b||!N||!E||!Z||!q||!T?r.jsx("p",{className:"text-xs text-slate-400 text-center",children:z?b?"Waiting for required Neato entities in Home Assistant.":"Home Assistant is offline.":"Set homeAssistant.neato.device in server config to enable Neato controls."}):null]})})}function sX(t){return t==="good"?"bg-emerald-600 text-white":t==="warn"?"bg-amber-500 text-slate-900":t==="danger"?"bg-rose-600 text-white":"bg-slate-700 text-slate-100"}function oX(t){return t==="up"?"Up":t==="down"?"Down":t==="stopped"?"Transitioning...":t==="conflict"?"Conflict":"--"}function uX({lift:t,onUp:a,onDown:l,fullWidth:o=!1}){const[u,d]=f.useState(""),[p,v]=f.useState(()=>Date.now()),g=o?"w-full":"w-full max-w-xl",x=!!t?.configured,z=!!(t?.enabled&&t?.connected),b=!!t?.busy,y=String(t?.target||"").toLowerCase(),H=String(t?.position||"").toLowerCase(),S=!!t?.availability?.upSwitch,w=!!t?.availability?.downSwitch,C=Number(t?.lastActionAt||0),V=Math.max(0,Number(t?.commandCooldownMs||0)),A=Math.max(0,C+V-p),B=!b&&A>0,k=b||B;f.useEffect(()=>{if(!k)return;const q=setInterval(()=>{v(Date.now())},100);return()=>clearInterval(q)},[k]),f.useEffect(()=>{v(Date.now())},[C,V,b]);const D=x?z?k?"Busy":"Ready":"Offline":"Not configured",j=x?z?k?"warn":"good":"danger":"warn",N=x&&z&&!k&&!u&&S&&w,E=f.useMemo(()=>Math.ceil(A/1e3),[A]),Z=async(q,T)=>{if(T){d(q);try{await T()}catch{}finally{d("")}}};return r.jsxs(B1,{title:"Lift Controls",className:`relative ${g}`,bodyClassName:"text-sm text-slate-200",actions:r.jsx("span",{className:`inline-flex w-auto rounded px-1 py-0.25 text-xs font-semibold ${sX(j)}`,children:D}),children:[k?r.jsx("div",{className:"absolute inset-0 z-20 flex items-center justify-center rounded-md bg-slate-950/80 px-1.5 text-center",children:r.jsxs("div",{className:"space-y-0.25",children:[r.jsx("p",{className:"text-sm font-semibold text-slate-100",children:b?"Preparing to move":"Moving!"}),r.jsx("p",{className:"text-xs text-slate-300",children:"Controls are disabled while the lift is moving, otherwise its tiny brain would get confused."}),!b&&B?r.jsxs("p",{className:"text-xs text-slate-400",children:["About ",E,"s remaining."]}):null]})}):null,r.jsxs("div",{className:"grid gap-0.5",children:[r.jsx("div",{className:"text-center",children:r.jsx("p",{className:"text-xs text-slate-400",children:"Move the lift up and down. Please don't break anything..."})}),r.jsx("section",{className:"surface-muted px-0.5 py-0.5",children:r.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-3 gap-0.5",children:[r.jsxs("div",{className:"rounded-md bg-slate-800 px-1 py-0.75 text-center",children:[r.jsx("div",{className:"text-xs text-slate-300",children:"Position"}),r.jsx("div",{className:"text-base font-semibold text-slate-100",children:oX(H)})]}),r.jsx("button",{type:"button",disabled:!N,onClick:()=>Z("down",l),className:`button-dark w-full text-sm disabled:opacity-50 ${H==="down"||y==="down"?"bg-emerald-500 text-white hover:bg-emerald-500":""}`,children:u==="down"||y==="down"?"Lowering...":"Down"}),r.jsx("button",{type:"button",disabled:!N,onClick:()=>Z("up",a),className:`button-dark w-full text-sm disabled:opacity-50 ${H==="up"||y==="up"?"bg-emerald-500 text-white hover:bg-emerald-500":""}`,children:u==="up"||y==="up"?"Raising...":"Up"})]})}),!x||!z||!S||!w?r.jsx("p",{className:"text-xs text-slate-400 text-center",children:x?z?"Waiting for required lift switch entities in Home Assistant.":"Home Assistant is offline.":"Set homeAssistant.lift.upSwitch and homeAssistant.lift.downSwitch in server config."}):null,t?.lastError?r.jsxs("p",{className:"text-xs text-rose-300 text-center",children:["Last error: ",t.lastError]}):null]})]})}function hX(t){const a=String(t||"").trim();if(!a)return"";try{const l=new URL(a),o=String(l.protocol||"").toLowerCase();return o!=="http:"&&o!=="https:"?"":l.toString()}catch{return""}}function dX({isVerified:t=!1,fullWidth:a=!1,onMessage:l}){const{value:o,save:u}=N1("profile",{nickname:"",profileImageUrl:""}),d=String(o?.profileImageUrl||"").trim(),[p,v]=f.useState(d),[g,x]=f.useState(!1);f.useEffect(()=>{v(d)},[d]);const z=f.useMemo(()=>hX(p),[p]),b=String(p||"").trim()!==d,y=!!(t&&b&&(z||!String(p||"").trim())),H=a?"w-full":Z8,S=async w=>{if(w.preventDefault(),!t){l?.("Verify your account before setting a profile image URL.");return}x(!0),l?.("");try{const C=z||"";u(V=>({...V||{},profileImageUrl:C})),l?.(C?"Profile image URL saved.":"Profile image URL cleared.")}finally{x(!1)}};return r.jsx(B1,{title:"Chat profile image URL",className:H,bodyClassName:"text-sm text-slate-300",children:r.jsxs("form",{className:v0,onSubmit:S,children:[r.jsx("p",{className:"text-xs text-slate-500",children:"Verified users can set a custom avatar for chat and Discord bridge messages."}),r.jsx("input",{className:z0,type:"url",name:"vip_profile_image_url",placeholder:"https://example.com/avatar.png",value:p,onChange:w=>v(w.target.value),disabled:!t||g}),!z&&String(p||"").trim()?r.jsx("p",{className:"text-xs text-amber-300",children:"Enter a valid http/https image URL."}):null,r.jsxs("div",{className:"flex justify-center gap-0.5",children:[r.jsx("button",{type:"submit",className:"button-dark text-sm disabled:opacity-50",disabled:!y||g,children:g?"Saving...":"Save URL"}),r.jsx("button",{type:"button",className:"button-dark text-sm disabled:opacity-50",disabled:!t||g||!d,onClick:()=>v(""),children:"Clear"})]})]})})}function _u({isActive:t=!0}){const a=h1(n1=>n1.session),{identifySession:l,requestVerification:o,requestPrivateRoverAccess:u,playUploadedAudio:d,stopUploadedAudio:p,startMicWhip:v,readyMicWhip:g,stopMicWhip:x,neatoStart:z,neatoSendHome:b,neatoLocate:y,neatoClearErrors:H,neatoPowerCycle:S,liftUp:w,liftDown:C}=$2(),{value:V,save:A}=N1("identity",{cookieUserId:""}),{value:B}=N1("profile",{nickname:""}),k=f.useMemo(()=>(V?.cookieUserId||"").trim(),[V?.cookieUserId]),D=(B?.nickname||"").trim(),j=!!a?.isVerified,N=a?.verification?.pendingRequestId||null,E=a?.privateRoverAccess?.requestableRovers||[],Z=a?.privateRoverAccess?.pendingRequests||[],q=String(a?.assignment?.roverId||"").trim(),[T,P]=f.useState(""),o1=async n1=>{const I=String(n1||"").trim().toLowerCase();if(!I)throw new Error("Identity key required.");if(!$Q.test(I))throw new Error("Identity key must match format: cu_ + 32 lowercase hex chars.");return A(R=>({...R||{},cookieUserId:I})),await l({cookieUserId:I,nickname:D}),I};return r.jsxs("section",{className:"space-y-2 text-base",children:[r.jsxs("div",{className:"grid gap-2 grid-cols-1 lg:grid-cols-[minmax(0,1.25fr)_minmax(0,0.9fr)]",children:[j?null:r.jsx("div",{className:"lg:col-span-2",children:r.jsx(aX,{pendingRequestId:N,currentStoredKey:k,nickname:D,requestVerification:o,applyIdentityKey:o1,onMessage:P,fullWidth:!0})}),r.jsx("div",{className:"lg:col-span-2",children:j?r.jsxs("div",{className:"space-y-2",children:[r.jsx(iX,{neato:a?.neato||null,onStart:z,onSendHome:b,onLocate:y,onClearErrors:H,onPowerCycle:S,fullWidth:!0}),r.jsx(uX,{lift:a?.lift||null,onUp:w,onDown:C,fullWidth:!0}),r.jsx(cX,{ownRoverId:q,audioForwardByRover:a?.audioForward||{},playUploadedAudio:d,stopUploadedAudio:p,startMicWhip:v,readyMicWhip:g,stopMicWhip:x})]}):r.jsx("section",{className:"surface h-full",children:r.jsx("div",{className:"flex h-full flex-col items-center justify-center text-center text-xs text-slate-400",children:"Verify your account to unlock VIP controls."})})}),r.jsx("div",{className:"lg:col-span-2",children:r.jsx(lX,{requestableRovers:E,pendingRequests:Z,requestPrivateRoverAccess:u,onMessage:P,fullWidth:!0})}),r.jsx("div",{className:"lg:col-span-2",children:r.jsx(nX,{currentStoredKey:k,applyIdentityKey:o1,onMessage:P,fullWidth:!0})}),r.jsx("div",{className:"lg:col-span-2",children:r.jsx(dX,{isVerified:j,onMessage:P,fullWidth:!0})})]}),T?r.jsx("div",{className:Z8,children:r.jsx("p",{className:"text-xs text-slate-400 text-center",children:T})}):null]})}const fX=420,mX=1200,vX={1:262,2:330,3:392,4:523};function P7(){const t=h1(w=>w.session?.buttonBox?.buttons??[]),a=R2(),{value:l}=N1("audio",p2),o=Number.isFinite(l?.masterVolume)?l.masterVolume:p2.masterVolume,u=Number.isFinite(l?.alertVolume)?l.alertVolume:p2.alertVolume,d=Math.max(0,Math.min(1,o*u)),p=f.useMemo(()=>{const w=Array.isArray(t)?t:[];return w.length===4?w:[1,2,3,4].map(C=>w.find(V=>Number(V?.id)===C)||{id:C,count:0,goal:0,rewardName:null,rewardId:null,rewardNumber:null,lastRewardAt:null})},[t]),[v,g]=f.useState({}),[x,z]=f.useState({}),b=f.useRef(new Map),y=f.useRef(new Map),H=f.useRef({}),S=f.useRef(null);return f.useEffect(()=>{const w={};p.forEach(V=>{w[V.id]=Number(V.lastRewardAt||0)});const C=H.current||{};p.forEach(V=>{const A=Number(V.id),B=w[A]||0,k=Number(C[A]||0);if(B>0&&B!==k){z(N=>({...N,[A]:!0}));const D=y.current.get(A);D&&clearTimeout(D);const j=setTimeout(()=>{z(N=>({...N,[A]:!1})),y.current.delete(A)},mX);y.current.set(A,j)}}),H.current=w},[p]),f.useEffect(()=>{function w(V){const A=vX[V];if(!A||d<=0)return;let B=S.current;if(!B){const N=window.AudioContext||window.webkitAudioContext;if(!N)return;B=new N,S.current=B}const k=B.createOscillator(),D=B.createGain();k.type="square",k.frequency.value=A;const j=.14*d;D.gain.setValueAtTime(1e-4,B.currentTime),D.gain.exponentialRampToValueAtTime(Math.max(1e-4,j),B.currentTime+.01),D.gain.exponentialRampToValueAtTime(1e-4,B.currentTime+.2),k.connect(D),D.connect(B.destination),k.start(),k.stop(B.currentTime+.21)}function C(V={}){const A=Number(V.buttonId);if(!Number.isFinite(A)||A<1||A>4)return;g(D=>({...D,[A]:!0}));const B=b.current.get(A);B&&clearTimeout(B);const k=setTimeout(()=>{g(D=>({...D,[A]:!1})),b.current.delete(A)},fX);b.current.set(A,k),w(A)}return a.on("buttonBox:increment",C),()=>{a.off("buttonBox:increment",C)}},[d,a]),r.jsx(B1,{title:"Button Box",bodyClassName:"space-y-0.5 text-base",children:r.jsx("div",{className:"grid grid-cols-4 gap-0.5",children:p.map(w=>{const C=Number(w.id),V=Number.isFinite(w.count)?w.count:0,A=Number.isFinite(w.goal)?w.goal:0,B=typeof w.rewardName=="string"&&w.rewardName.trim()?w.rewardName.trim():"Unassigned",k=Number.isFinite(w.rewardNumber)?w.rewardNumber:"?",D=!!v[C],j=!!x[C];return r.jsx(Zo,{buttonId:C,count:V,goal:A,rewardNumber:k,rewardName:B,className:[D?"bg-cyan-900/45":"",j?"bg-fuchsia-900/45":""].filter(Boolean).join(" ")},C)})})})}const wc={activeGameId:null,games:[],activeGame:null,counters:{objects:[],rovers:[],codes:[]},recentEvents:[],ownPlayer:null};function Rs(t={}){return{...wc,...t&&typeof t=="object"?t:{},games:Array.isArray(t?.games)?t.games:[],counters:{...wc.counters,...t?.counters&&typeof t.counters=="object"?t.counters:{}},recentEvents:Array.isArray(t?.recentEvents)?t.recentEvents:[]}}function Uu(){const t=R2(),[a,l]=f.useState(wc);f.useEffect(()=>{function u(d={}){l(Rs(d))}return t.emit("barcodeGame:subscribe",{},(d={})=>{d.state&&u(d.state)}),t.on("barcodeGame:state",u),()=>{t.off("barcodeGame:state",u)}},[t]);const o=f.useCallback(u=>new Promise((d,p)=>{t.emit("barcodeGame:vote",{gameId:u},(v={})=>{if(v.error){p(new Error(v.error));return}v.state&&l(Rs(v.state)),d(v)})}),[t]);return f.useMemo(()=>({state:a,voteForGame:o}),[a,o])}function pX(t){const[a,l]=f.useState(()=>Date.now());return f.useEffect(()=>{if(!t)return;const o=window.setInterval(()=>{l(Date.now())},500);return()=>window.clearInterval(o)},[t]),a}function gX(t,a){if(!Number.isFinite(t))return null;const l=Math.max(0,Math.ceil((t-a)/1e3)),o=Math.floor(l/60),u=l%60;return o>0?`${o}:${String(u).padStart(2,"0")}`:`${u}s`}function xX({game:t,disabled:a,onVote:l}){return r.jsxs("button",{type:"button",disabled:a||t.active,onClick:()=>l(t.id),className:["button-dark flex min-h-[4.25rem] flex-col items-start justify-between px-1.5 py-1 text-left disabled:opacity-70",t.active?"border-emerald-500/70":"",t.selected&&!t.active?"border-cyan-500/60":""].filter(Boolean).join(" "),children:[r.jsxs("span",{className:"flex w-full items-start justify-between gap-1",children:[r.jsx("span",{className:"text-sm font-semibold leading-tight text-neutral-50",children:t.title}),r.jsx("span",{className:"shrink-0 font-mono text-[0.72rem] text-neutral-300",children:t.voteCount||0})]}),r.jsx("span",{className:"mt-0.5 line-clamp-2 text-xs leading-snug text-neutral-300",children:t.description}),r.jsx("span",{className:"mt-0.5 text-[0.7rem] font-semibold text-neutral-200",children:t.active?"Active":t.selected?"Selected":t.actionLabel||"Vote"})]})}function zX({stats:t}){return!Array.isArray(t)||!t.length?null:r.jsx("div",{className:"grid gap-1 sm:grid-cols-3",children:t.slice(0,6).map(a=>r.jsxs("div",{className:"border border-neutral-700 px-1.5 py-1",children:[r.jsx("p",{className:"truncate text-[0.68rem] text-neutral-400",children:a.label}),r.jsx("p",{className:"truncate text-sm font-semibold text-neutral-100",children:a.value})]},a.label))})}function Es({title:t,entries:a}){return!Array.isArray(a)||!a.length?null:r.jsxs("div",{children:[r.jsx("p",{className:"mb-0.5 text-xs font-semibold text-neutral-300",children:t}),r.jsx("div",{className:"space-y-0.5",children:a.slice(0,4).map(l=>r.jsxs("div",{className:"flex justify-between gap-2 text-xs",children:[r.jsx("span",{className:"min-w-0 truncate text-neutral-200",children:l.label||l.entityId||l.code}),r.jsx("span",{className:"font-mono text-neutral-300",children:l.count||0})]},`${l.entityId||l.code}-${l.type||"counter"}`))})]})}function bX({players:t,ownPlayer:a}){const l=a&&Number.isFinite(a.totalPoints);return r.jsx("div",{className:"space-y-1",children:r.jsxs("div",{className:"grid gap-1 sm:grid-cols-[minmax(0,0.8fr)_minmax(0,1.2fr)]",children:[r.jsxs("div",{className:"border border-neutral-700 px-1.5 py-1",children:[r.jsx("p",{className:"text-xs text-neutral-400",children:"Your points"}),r.jsx("p",{className:"text-lg font-semibold leading-tight text-neutral-50",children:l?a.totalPoints:0}),a?.rank?r.jsxs("p",{className:"text-[0.7rem] text-neutral-400",children:["Rank ",a.rank]}):null]}),r.jsxs("div",{className:"border border-neutral-700 px-1.5 py-1",children:[r.jsx("p",{className:"mb-0.5 text-xs font-semibold text-neutral-300",children:"Leaderboard"}),Array.isArray(t)&&t.length?r.jsx("div",{className:"space-y-0.5",children:t.slice(0,4).map((o,u)=>r.jsxs("div",{className:"grid grid-cols-[1.25rem_minmax(0,1fr)_auto] gap-1 text-xs",children:[r.jsx("span",{className:"font-mono text-neutral-500",children:u+1}),r.jsx("span",{className:"truncate text-neutral-200",children:o.nickname}),r.jsx("span",{className:"font-mono text-neutral-300",children:o.totalPoints})]},o.playerKey))}):r.jsx("p",{className:"text-xs text-neutral-500",children:"No points yet"})]})]})})}function Zu(){const{state:t,voteForGame:a}=Uu(),[l,o]=f.useState(null),u=t.activeGame,d=u?.display||{},p=t.phase==="starting"||t.phase==="running",v=d.timer?.endsAt,g=pX(Number.isFinite(v)),x=gX(v,g),z=f.useMemo(()=>Array.isArray(t.games)?t.games:[],[t.games]),b=async y=>{o(y);try{await a(y)}finally{o(null)}};return r.jsxs(B1,{title:"Barcode games",bodyClassName:"space-y-2 p-1.5 text-sm",children:[r.jsx("div",{className:"grid gap-1 sm:grid-cols-2",children:z.map(y=>r.jsx(xX,{game:y,disabled:!!l||p,onVote:b},y.id))}),r.jsxs("section",{className:"space-y-1 border-t border-neutral-700 pt-1.5",children:[r.jsxs("div",{className:"flex items-start justify-between gap-2",children:[r.jsxs("div",{className:"min-w-0",children:[r.jsx("p",{className:"text-xs font-semibold text-neutral-400",children:d.title||u?.title||"No game active"}),r.jsx("p",{className:"break-words text-lg font-semibold leading-tight text-neutral-50",children:d.primary||"Vote for a game to start"}),d.secondary?r.jsx("p",{className:"mt-0.5 break-words text-sm leading-snug text-neutral-300",children:d.secondary}):null]}),x?r.jsxs("div",{className:"shrink-0 text-right",children:[r.jsx("p",{className:"text-[0.68rem] text-neutral-400",children:d.timer?.label||"Time"}),r.jsx("p",{className:"font-mono text-base font-semibold text-neutral-50",children:x})]}):null]}),r.jsx(zX,{stats:d.stats})]}),r.jsx(bX,{players:t.leaderboard,ownPlayer:t.ownPlayer}),r.jsxs("div",{className:"grid gap-2 border-t border-neutral-700 pt-1.5 sm:grid-cols-2",children:[r.jsx(Es,{title:"Most scanned objects",entries:t.counters?.objects}),r.jsx(Es,{title:"Most scanned rovers",entries:t.counters?.rovers})]})]})}function MX(t){const a=Number(t);if(!Number.isFinite(a)||a<=0)return null;const l=new Date(a);return Number.isNaN(l.getTime())?null:l.toLocaleTimeString([],{hour:"numeric",minute:"2-digit",second:"2-digit"})}function yX({memory:t,onClose:a}){const l=typeof t?.summary=="string"&&t.summary.trim()?t.summary:"Memory unavailable.",o=MX(t?.updatedAt);return r.jsx("div",{className:"fixed inset-0 z-40 flex items-center justify-center bg-black/75 p-1",onClick:a,children:r.jsx("div",{className:"w-full max-w-2xl",onClick:u=>u.stopPropagation(),children:r.jsx(B1,{title:"Overseer memory",meta:o?`Updated ${o}`:null,actions:r.jsx("button",{type:"button",onClick:a,className:"button-dark px-1 py-0.25 text-[0.75rem]",children:"Close"}),bodyClassName:"p-0.5 text-xs",children:r.jsx("pre",{className:"surface max-h-[70vh] overflow-y-auto whitespace-pre-wrap break-words font-mono text-[0.72rem] text-slate-200",children:l})})})})}function $u(){const{identifySession:t}=$2(),a=h1(H=>H.session?.overseerVote||null),l=h1(H=>H.overseerMemory||null),{value:o}=N1("profile",{nickname:""}),{value:u}=N1("identity",{cookieUserId:""}),{value:d,save:p}=N1("overseerPreference",{enabled:!1}),[v,g]=f.useState(!1),x=!!d?.enabled,z=!!a?.running,b=async H=>{const S=!!H?.target?.checked;await p(w=>({...w||{},enabled:S})),await t({cookieUserId:String(u?.cookieUserId||"").trim(),nickname:String(o?.nickname||"").trim(),overseerEnabled:S})},y=r.jsx("button",{type:"button",onClick:()=>g(!0),className:"button-dark px-1 py-0.25 text-[0.75rem]",title:"View overseer memory",children:"Memory"});return r.jsxs(r.Fragment,{children:[r.jsxs(B1,{title:"Overseer vote",actions:y,bodyClassName:"space-y-0.5 text-center text-xs text-slate-200",children:[r.jsxs("label",{className:`flex items-center justify-center gap-1.5 rounded px-1 py-0.5 ${x?"bg-emerald-600/80 text-emerald-50":"bg-slate-600/70 text-slate-100"}`,children:[r.jsx("input",{type:"checkbox",checked:x,onChange:b}),r.jsxs("span",{children:["Your vote: ",x?"Yes":"No"]})]}),r.jsx("span",{className:"text-xs",children:"Enable a local LLM in chat"}),r.jsxs("div",{className:"mt-0.5 text-slate-400",children:["Yes!: ",Number(a?.yesCount||0),", No...: ",Number(a?.noCount||0)]}),r.jsx("div",{className:"mt-0.5 flex justify-center",children:r.jsx("span",{className:`inline-flex items-center rounded px-1.5 py-0.5 text-[0.7rem] font-medium ${z?"bg-emerald-600/80 text-emerald-50":"bg-slate-600/70 text-slate-100"}`,children:z?"Running!":"Stopped by vote."})})]}),v?r.jsx(yX,{memory:l,onClose:()=>g(!1)}):null]})}const CX=224,wX=144,HX=300,SX=8;function VX(){const t=R1(l=>l.state.roverId),a=U4(t,Dc,Vo);return r.jsx(B1,{title:"Roomba sensor view",children:r.jsx("div",{className:"aspect-square w-full",children:r.jsx(wu,{mapTelemetry:a})})})}function AX(){const t=R1(T=>T.state.roverId),a=R1(T=>T.state.keymap),l=R1(T=>T.state.camera),o=R1(T=>T.state.horn),u=R1(T=>T.pipeline?.nightVision),d=R1(T=>T.pipeline?.nightVisionState),p=R1(T=>T.pipeline?.horn),{setServoAngle:v,setNightVision:g,startHorn:x,stopHorn:z}=p3(),b=H0(),y=vu(t),H=y.docked&&!y.driving,S=l?.config,w=!!(t&&l?.enabled&&S),C=!!(t&&u),V=!!(t&&p),A=o?.overheated,B=typeof S?.minAngle=="number"?S.minAngle:-30,k=typeof S?.maxAngle=="number"?S.maxAngle:30,D=typeof l?.angle=="number"?l.angle:typeof S?.homeAngle=="number"?S.homeAngle:(B+k)/2,j=q2(a?.nightVisionToggle?.[0]),N=q2(a?.hornHonk?.[0]),E=q2(a?.cameraUp?.[0]),Z=q2(a?.cameraDown?.[0]),q=!!(!t||b.cameraLocked);return r.jsxs(B1,{hideHeader:!0,className:"h-full",bodyClassName:`grid h-full min-h-0 grid-rows-[minmax(0,1fr)_auto] ${C2}`,children:[r.jsx(pu,{layout:"desktop",expand:!0,driveDockState:y}),H?null:r.jsxs("div",{className:`${v6} p-0 text-sm text-slate-200`,children:[C&&r.jsx(fu,{nightVisionOn:d?.nightVisionOn,disabled:!t,onToggle:g,keyLabel:j}),V&&r.jsx(du,{disabled:!t||A,onStart:x,onStop:z,keyLabel:N,active:o?.active,heat:o?.heat}),w&&r.jsx(ZQ,{value:D,min:B,max:k,label:"Camera tilt",disabled:q,onChange:v,keyDownLabel:Z,keyUpLabel:E,className:`${v6} rounded-xl border-2 border-emerald-300/70 bg-emerald-900 px-1 py-1 text-emerald-50`,labelRowClass:"text-xs text-emerald-100",labelClass:"text-sm font-semibold",valueClass:"font-mono text-slate-100",sliderClass:"w-full",accentClass:"accent-emerald-400",endpointClass:"text-[0.7rem] text-emerald-100/80"})]})]})}function LX({layout:t,onOpenHelpOverlay:a}){const[l,o]=f.useState("telemetry"),u=f.useRef(null),[d,p]=f.useState(CX),v=h1(V=>!!V.session?.isVerified),g=h1(V=>String(V.session?.assignment?.roverId||"").trim()),x=h1(V=>{const A=String(V.session?.assignment?.roverId||"").trim();return A&&V.session?.audioForward?.[A]||null}),z=R1(V=>!!V.state.mic?.pttActive),{value:b}=N1("vipAudio",{openMicEnabled:!1,pttMode:"live"}),y=v?"bg-emerald-400":"bg-red-600",H=!!b?.openMicEnabled,S=b?.pttMode==="clip"?"clip":"live",w=!!(g&&v&&(S==="clip"?z:H||z)),C=!!(g&&v&&S==="clip"&&x?.source==="upload"&&x?.state==="playing");return f.useLayoutEffect(()=>{const V=u.current;if(!V)return;let A=0,B=0,k=0;const D=()=>{A=0;const{top:Z}=V.getBoundingClientRect(),q=window.innerHeight-Z-SX,T=Math.round(Math.max(wX,Math.min(HX,q)));p(P=>Math.abs(P-T)>1?T:P)},j=()=>{A||(A=window.requestAnimationFrame(D))},N=()=>{B=window.requestAnimationFrame(()=>{B=0,k=window.requestAnimationFrame(()=>{k=0,D()})})},E=typeof ResizeObserver=="function"?new ResizeObserver(()=>{j()}):null;return j(),N(),E&&V.parentElement&&E.observe(V.parentElement),window.addEventListener("resize",j),document.addEventListener("scroll",j,{capture:!0,passive:!0}),()=>{A&&window.cancelAnimationFrame(A),B&&window.cancelAnimationFrame(B),k&&window.cancelAnimationFrame(k),E?.disconnect(),window.removeEventListener("resize",j),document.removeEventListener("scroll",j,{capture:!0})}},[l]),r.jsx("section",{className:"text-base",children:r.jsxs(ra,{defaultTab:"telemetry",currentTab:l,onTabChange:o,children:[r.jsxs(ia,{children:[r.jsx(I2,{id:"telemetry",children:"Controls"}),r.jsx(I2,{id:"activities",children:"Activities"}),r.jsx(I2,{id:"vip",highlight:C?"green":w?"pink":"none",children:r.jsxs("span",{className:"inline-flex items-center gap-2",children:[r.jsx("span",{children:"VIP"}),r.jsx("span",{className:`inline-block h-3 w-3 rounded-full ${y}`,"aria-hidden":"true",title:v?"Verified":"Not verified"})]})}),r.jsx(I2,{id:"help",children:"Help"}),r.jsx(I2,{id:"settings",children:"Settings"})]}),r.jsxs(sa,{children:[r.jsx(Y2,{id:"telemetry",children:r.jsxs("div",{className:`flex flex-col ${C2}`,children:[r.jsxs("div",{className:`flex flex-col ${C2}`,children:[r.jsxs("div",{className:`grid items-stretch ${C2} grid-cols-[minmax(0,1.35fr)_minmax(0,0.95fr)]`,children:[r.jsx(VX,{}),r.jsx(AX,{})]}),r.jsxs("div",{className:`grid ${C2} grid-cols-[minmax(0,1fr)_minmax(0,0.9fr)_minmax(0,0.75fr)]`,children:[r.jsx(rt,{}),r.jsx($c,{panelId:"replay-sources-desktop",fillHeight:!0}),r.jsx(EQ,{})]}),r.jsxs("div",{ref:u,className:`grid min-h-0 items-stretch ${C2} grid-cols-[minmax(0,1.3fr)_minmax(0,0.22fr)]`,style:{height:`${d}px`},children:[r.jsx(U8,{fillHeight:!0}),r.jsxs("div",{className:`grid min-h-0 ${C2} grid-rows-[auto_minmax(0,1fr)]`,children:[r.jsx($u,{}),r.jsx(G7,{compact:!0,hideNicknameForm:!0,fillHeight:!0})]})]})]}),r.jsx(Bu,{}),r.jsx(ta,{defaultOrientation:"horizontal",panelId:"rightpane-telemetry"})]})}),r.jsx(Y2,{id:"activities",children:r.jsxs("div",{className:`flex flex-col ${C2}`,children:[r.jsx(Zu,{}),r.jsx(P7,{}),r.jsx(zu,{})]})}),r.jsx(Y2,{id:"vip",keepMounted:!0,children:r.jsx(_u,{isActive:l==="vip"})}),r.jsx(Y2,{id:"help",children:r.jsx(Du,{layout:t,onOpenOverlay:a})}),r.jsx(Y2,{id:"settings",children:r.jsx(Eu,{})})]})]})})}const BX=new Set(["admin","lockdown","lockdown-admin"]),jX=new Set(["lockdown","lockdown-admin"]),NX=new Set(["admin","lockdown"]);function FX(t="admin"){return t==="lockdown"?{title:"Lockdown mode active",description:"Only the server owners can access the interface at this time."}:{title:"Admin mode active"}}function kX(){const t=h1(x=>x.session?.mode||null),a=h1(x=>x.session?.role||null),l=h1(x=>x.session?.adminReason?.text||""),o=h1(x=>x.session?.timezone||"UTC"),u=NX.has(t),d=t==="lockdown"?jX.has(a):BX.has(a),p=D8(1e3,u&&!d),v=f.useMemo(()=>{const x=new Date(p);try{return new Intl.DateTimeFormat("en-US",{timeZone:o,hour:"numeric",minute:"2-digit",second:"2-digit"}).format(x)}catch{return x.toLocaleTimeString()}},[p,o]);if(!u||d)return null;const g=FX(t);return r.jsx("div",{className:"pointer-events-auto fixed inset-0 z-50 flex items-center justify-center bg-black px-0.5 py-0.5",children:r.jsxs("div",{className:"surface w-full max-w-md space-y-0.5 text-slate-100 shadow-2xl",children:[r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-lg font-semibold",children:g.title}),r.jsx("p",{className:"text-sm text-slate-300",children:g.description})]}),r.jsxs("div",{className:"surface-muted space-y-0.5",children:[r.jsx("p",{className:"text-[0.7rem] tracking-wide text-slate-400",children:"Reason for locking:"}),r.jsx("p",{className:"text-lg font-semibold text-slate-100",children:l||"No reason set."}),r.jsxs("p",{className:"text-center text-sm text-slate-300",children:["Server time: ",v]})]}),r.jsx("div",{className:"surface-muted",children:r.jsx(ju,{})}),r.jsx("div",{className:"w-full justify-center items-center",children:r.jsx(et,{id:"discord",label:"Join our Discord server for updates!"})}),"You can still use the chat while the server is locked:",r.jsx("div",{className:"max-h-80 overflow-y-auto",children:r.jsx(U8,{nicknameLayout:"stacked"})})]})})}const RX="/assets/turn_alert-DVO8EZJa.mp3";function EX(t,a=1){const l=f.useRef(null);return f.useEffect(()=>{l.current=new Audio(t),l.current.volume=a,l.current.load()},[t,a]),()=>{l.current&&(l.current.volume=a,l.current.currentTime=0,l.current.play().catch(()=>{}))}}function TX(){const t=h1(z=>z.session?.socketId||null),a=h1(z=>z.session?.turnQueues||{}),l=h1(z=>z.session?.roster||[]),{pushAlert:o}=$2(),{value:u}=N1("audio",p2),d=Number.isFinite(u?.masterVolume)?u.masterVolume:p2.masterVolume,p=Number.isFinite(u?.alertVolume)?u.alertVolume:p2.alertVolume,v=Math.max(0,Math.min(1,d*p)),g=EX(RX,v),x=f.useRef(new Set);return f.useEffect(()=>{if(!t)return;const z=[];Object.entries(a).forEach(([b,y])=>{!y||!y.current||y.current!==t||x.current.has(b)||(z.push(b),x.current.add(b))}),Object.keys(a).forEach(b=>{(!a[b]||a[b].current!==t)&&x.current.delete(b)}),z.length!==0&&(z.forEach(b=>{const y=l.find(H=>String(H.id)===String(b))?.name||b;o({title:"Your turn!",message:`You now control ${y}.`,color:"#ff5722"})}),g())},[a,g,o,l,t]),null}function qX({visible:t,mode:a,onEnterFullscreen:l,onDismiss:o}){if(!t)return null;const u=a==="pwa-hint";return r.jsx("div",{className:"fixed inset-0 z-40 flex items-center justify-center p-1 pointer-events-none bg-black/20",children:r.jsx("div",{className:"pointer-events-auto w-full max-w-sm surface center",children:r.jsxs("div",{className:"space-y-0.5 p-1 text-sm text-slate-100",children:[r.jsx("h2",{className:"text-base font-semibold text-white border-b border-slate-700",children:"Better in fullscreen!"}),u?r.jsxs("p",{className:"text-slate-300",children:["For fullscreen on iOS, open Safari's share menu and pick ",r.jsx("strong",{children:"Add to Home Screen"}),". Launching from the home screen then makes it fullscreen."]}):r.jsx("p",{className:"text-slate-300",children:"Enable fullscreen to free up more space for the video feed and controls. You can exit fullscreen at any time via the system back or home gesture."}),r.jsx("p",{className:"text-xs border-t border-b border-slate-700 p-0.5 text-blue-300 text-center",children:"This will only show once just to let you know. There is a fullscreen button in the bottom right for later use."}),r.jsxs("div",{className:"flex justify-end gap-0.5 pt-1 text-sm",children:[r.jsx("button",{type:"button",className:"rounded border border-slate-600 px-3 py-1 text-slate-200",onClick:o,children:u?"Got it":"Not now"}),!u&&r.jsx("button",{type:"button",className:"rounded bg-cyan-500 px-3 py-1 font-semibold text-black hover:bg-cyan-400",onClick:l,children:"Enter fullscreen"})]})]})})})}function DX({side:t="right",onClick:a}){const l=t==="left"?"left-2":"right-2";return r.jsx("button",{type:"button",onClick:a,className:`fixed bottom-2 ${l} z-30 flex h-10 w-10 items-center justify-center rounded-full border border-slate-700 bg-black text-slate-200 transition hover:bg-zinc-900 active:scale-95`,"aria-label":"Enter fullscreen",title:"Enter fullscreen",children:r.jsx("svg",{viewBox:"0 0 24 24",width:"18",height:"18","aria-hidden":"true",className:"pointer-events-none",children:r.jsx("path",{d:"M4 9V4h5M15 4h5v5M20 15v5h-5M9 20H4v-5",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})})})}const OX=new Set(["mobile-portrait","mobile-landscape"]),_X=()=>{if(typeof navigator>"u")return!1;const t=navigator.userAgent||navigator.platform||"";return/iphone|ipad|ipod/i.test(t)},Ts=()=>{if(typeof document>"u")return!1;const t=document;return!!(t.fullscreenEnabled||t.webkitFullscreenEnabled||t.mozFullScreenEnabled||t.msFullscreenEnabled)};function UX(t){const{value:a,save:l,status:o}=N1("page",{fullscreenPromptShown:!1}),u=OX.has(t),[d,p]=f.useState(!1),[v,g]=f.useState("native"),[x,z]=f.useState(!1),b=!!a?.fullscreenPromptShown,y=f.useMemo(()=>_X(),[]),H=f.useMemo(()=>Ts(),[]),S=f.useCallback(()=>{o==="ready"&&l(B=>B?.fullscreenPromptShown?B:{...B||{},fullscreenPromptShown:!0})},[l,o]),w=f.useCallback(()=>{if(o!=="ready"){p(!1);return}if(!u){p(!1),z(!1);return}if(!x&&b){p(!1);return}if(y){g("pwa-hint"),p(!0);return}if(!Ts()){p(!1);return}if(typeof document<"u"&&document.fullscreenElement){p(!1);return}g("native"),p(!0)},[x,b,y,u,o]);f.useEffect(()=>{w()},[w]),f.useEffect(()=>{if(typeof document>"u")return;const B=()=>{if(document.fullscreenElement){z(!1),p(!1);return}w()};return document.addEventListener("fullscreenchange",B),document.addEventListener("webkitfullscreenchange",B),()=>{document.removeEventListener("fullscreenchange",B),document.removeEventListener("webkitfullscreenchange",B)}},[w]);const C=f.useCallback(()=>{S(),z(!1),p(!1)},[S]),V=f.useCallback(async()=>{if(!u||y||typeof document>"u")return!1;if(document.fullscreenElement)return!0;const B=document.documentElement;if(!B)return!1;const k=B.requestFullscreen||B.webkitRequestFullscreen||B.mozRequestFullScreen||B.msRequestFullscreen;if(!k)return!1;try{const D=k.call(B);return D&&typeof D.then=="function"&&await D,S(),z(!1),p(!1),!0}catch(D){return console.warn("Failed to enter fullscreen",D),!1}},[y,u,S]),A=f.useCallback(()=>{u&&(z(!0),g(y?"pwa-hint":"native"),p(!0))},[y,u]);return f.useEffect(()=>{u||(z(!1),p(!1))},[u]),{visible:d,mode:v,isIOS:y,nativeSupported:H,enterFullscreen:V,dismiss:C,showPrompt:A}}function ZX({visible:t,layout:a,onClose:l,showOnLoad:o,onToggleShowOnLoad:u}){const d=R1(v=>v.state.keymap);if(!t)return null;const p=v=>{const g=!v.target.checked;u?.(g)};return r.jsx("div",{className:"fixed inset-0 z-30 flex items-center justify-center bg-black/80 px-0.5 py-0.5",children:r.jsxs("div",{className:"pointer-events-auto surface w-full max-w-4xl max-h-[90vh] overflow-hidden shadow-2xl",children:[r.jsxs("div",{className:"flex items-center justify-between border-b border-slate-700 px-0.5 py-0.25 text-sm text-slate-200",children:[r.jsx("span",{className:"font-semibold",children:"Help & controls"}),r.jsxs("div",{className:"flex items-center gap-0.5 text-[0.8rem] text-slate-300",children:[r.jsxs("label",{className:"flex items-center gap-0.5",children:[r.jsx("input",{type:"checkbox",checked:!o,onChange:p,className:"accent-cyan-500"}),r.jsx("span",{children:"Don't show again"})]}),r.jsx("button",{type:"button",onClick:l,className:"button-dark px-2 py-0.25 text-[0.8rem]",children:"Close"})]})]}),r.jsx("div",{className:"max-h-[85vh] overflow-y-auto p-0.5",children:r.jsx(qu,{layout:a,keymap:d||{}})})]})})}function s0({label:t,keyLabel:a}){return r.jsxs("div",{className:"surface-muted flex items-center justify-between gap-0.5 px-0.5 py-0.35 text-[0.8rem] text-slate-200",children:[r.jsx("span",{children:t}),r.jsx($7,{label:a})]})}function $X({keymap:t}){return r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-sm text-slate-200",children:'1. Press "Start Driving" put your rover into driving mode.'}),r.jsxs("div",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-sm text-slate-200",children:"2. Use the drive controls to move your rover:"}),r.jsxs("div",{className:"space-y-0.5",children:[r.jsx(s0,{label:"Forward",keyLabel:q2(t?.driveForward?.[0])}),r.jsx(s0,{label:"Backward",keyLabel:q2(t?.driveBackward?.[0])}),r.jsx(s0,{label:"Turn Left",keyLabel:q2(t?.driveLeft?.[0])}),r.jsx(s0,{label:"Turn Right",keyLabel:q2(t?.driveRight?.[0])}),r.jsx(s0,{label:"Move faster",keyLabel:q2(t?.boostModifier?.[0])}),r.jsx(s0,{label:"Move slower",keyLabel:q2(t?.slowModifier?.[0])})]})]}),r.jsx("p",{className:"text-sm text-slate-200",children:'3. When done, enter "Docking Assist" and line up the front sensor with the dock sensor.'})]})}function GX(){return r.jsxs("div",{className:"space-y-0.5 text-sm text-slate-200",children:[r.jsx("p",{children:'1. Press "Start Driving" put your rover into driving mode.'}),r.jsx("p",{children:"2. Touch and hold in Joystick area to move."}),r.jsx("p",{children:"3. Use the other column for motor, horn, and camera controls."}),r.jsx("p",{children:'4. When done, enter "Docking Assist" and line up the front sensor with the dock sensor.'})]})}function PX({visible:t,layout:a,showOnLoad:l,onToggleShowOnLoad:o,onOpenHelp:u,onClose:d}){const p=R1(z=>z.state.keymap),v=a==="desktop",g=f.useMemo(()=>p||{},[p]);if(!t)return null;const x=z=>{const b=!z.target.checked;o?.(b)};return r.jsx("div",{className:"fixed inset-0 z-40 flex items-end justify-center bg-black/75 p-0.5 items-center",children:r.jsxs("div",{className:"pointer-events-auto surface w-full max-w-3xl overflow-hidden shadow-2xl",children:[r.jsx("div",{className:"flex items-center justify-between border-b border-slate-700 px-0.5 py-0.35 text-sm text-slate-200",children:r.jsx("span",{className:"font-semibold text-xl",children:"Welcome! To get started:"})}),r.jsxs("div",{className:`grid gap-0.5 p-0.5 ${v?"md:grid-cols-[minmax(0,1.5fr)_minmax(0,1fr)]":"grid-cols-1"}`,children:[r.jsx("section",{className:"space-y-0.5 border-b border-slate-700",children:v?r.jsx($X,{keymap:g}):r.jsx(GX,{})}),r.jsxs("section",{className:"space-y-0.5",children:[r.jsx("p",{className:"text-left",children:"Next..."}),r.jsxs("div",{className:"surface space-y-0.5 p-0.5 border-b border-slate-700",children:[r.jsx("p",{className:"text-xl font-semibold text-slate-200",children:"Set your nickname"}),r.jsx("p",{className:"text-sm font-semibold text-slate-200",children:"Nicknames are assigned randomly by default, you can change yours here."}),r.jsx(lt,{compact:!0})]}),r.jsxs("div",{className:"surface p-0.5",children:[r.jsx("p",{className:"text-xl font-semibold text-slate-200",children:"Join our Discord server!"}),r.jsx("p",{className:"text-sm font-semibold text-slate-200",children:"We have an active and welcoming community :3"}),r.jsx(et,{id:"discord",label:"Join Discord"})]})]})]}),r.jsxs("div",{className:"flex flex-wrap items-center justify-between gap-0.5 border-t border-slate-700 px-0.5 py-0.35 text-[0.8rem]",children:[r.jsxs("label",{className:"flex items-center gap-0.5 text-slate-300",children:[r.jsx("input",{type:"checkbox",checked:!l,onChange:x,className:"accent-cyan-500"}),r.jsx("span",{children:"Don't show again"})]}),r.jsx("div",{className:"flex items-center gap-0.5",children:r.jsx("button",{type:"button",onClick:d,className:"button-dark px-1 py-0.25 text-2xl bg-green-600 hover:bg-green-400 hover:border-green-100 border-green-400",children:"Got it! Let me in!"})})]})]})})}const IX=`aroma bagel batter beans diff --git a/server/public/index.html b/server/public/index.html index 41a83290..df366dec 100644 --- a/server/public/index.html +++ b/server/public/index.html @@ -18,8 +18,8 @@ Roomba Rover - - + +
diff --git a/server/src/services/barcodeGameService/games/scanQuest.js b/server/src/services/barcodeGameService/games/scanQuest.js index 586c7b93..a2b32064 100644 --- a/server/src/services/barcodeGameService/games/scanQuest.js +++ b/server/src/services/barcodeGameService/games/scanQuest.js @@ -6,10 +6,14 @@ const GAME_ID = 'scanQuest'; const QUEST_LENGTH_OPTIONS = [1, 2]; const REQUEST_TIMEOUT_MS = 90 * 1000; +const ROUND_DURATION_MS = 5 * 60 * 1000; function createInitialState() { return { currentQuest: null, + status: 'idle', + roundStartedAt: null, + roundEndsAt: null, progressIndex: 0, scores: {}, completedQuests: 0, @@ -24,6 +28,9 @@ function normalizeState(rawState = {}) { return { ...base, currentQuest: rawState.currentQuest && typeof rawState.currentQuest === 'object' ? rawState.currentQuest : null, + status: rawState.status === 'running' || rawState.status === 'ended' ? rawState.status : 'idle', + roundStartedAt: Number.isFinite(rawState.roundStartedAt) ? rawState.roundStartedAt : null, + roundEndsAt: Number.isFinite(rawState.roundEndsAt) ? rawState.roundEndsAt : null, progressIndex: Number.isFinite(rawState.progressIndex) ? Math.max(0, Math.floor(rawState.progressIndex)) : 0, scores: rawState.scores && typeof rawState.scores === 'object' ? rawState.scores : {}, completedQuests: Number.isFinite(rawState.completedQuests) ? Math.max(0, Math.floor(rawState.completedQuests)) : 0, @@ -117,16 +124,18 @@ function buildAwards(participants = [], points, reason) { })); } -function activate(rawState, context = {}) { - const state = normalizeState(rawState); +function start(_rawState, context = {}) { + const now = context.now || Date.now(); + const state = createInitialState(); + state.status = 'running'; + state.roundStartedAt = now; + state.roundEndsAt = now + ROUND_DURATION_MS; ensureQuest(state, context); return state; } -function reset(_rawState, context = {}) { - const state = createInitialState(); - ensureQuest(state, context); - return state; +function activate(rawState, context = {}) { + return start(rawState, context); } function skipExpiredQuest(state, context = {}) { @@ -152,6 +161,7 @@ function skipExpiredQuest(state, context = {}) { function handleScan(rawState, scan, context = {}) { const state = normalizeState(rawState); + if (state.status !== 'running') return { state, awards: [] }; ensureQuest(state, context); skipExpiredQuest(state, context); @@ -206,6 +216,19 @@ function handleScan(rawState, scan, context = {}) { function tick(rawState, context = {}) { const state = normalizeState(rawState); + const now = context.now || Date.now(); + if (state.status === 'running' && state.roundEndsAt && now >= state.roundEndsAt) { + return { + state: { + ...state, + status: 'ended', + lastMessage: `finished ${state.completedQuests} quests`, + }, + awards: [], + done: true, + display: getPublicState({ ...state, status: 'ended' }, context).display, + }; + } ensureQuest(state, context); return skipExpiredQuest(state, context); } @@ -230,7 +253,7 @@ function getPublicState(rawState, context = {}) { return { id: GAME_ID, title: 'Scan quest', - status: state.currentQuest ? 'running' : 'needs_objects', + status: state.status === 'running' && state.currentQuest ? 'running' : state.status, headline: state.lastMessage || formatQuestPrompt(state), detail: state.currentQuest?.steps?.length ? state.currentQuest.steps.map((step) => step.label).join(' then ') @@ -257,6 +280,7 @@ function getPublicState(rawState, context = {}) { stats: [ { label: 'Completed', value: state.completedQuests }, { label: 'Quest points', value: getTopScores(state)[0]?.points || 0 }, + { label: 'Round', value: state.status === 'ended' ? 'Done' : 'Running' }, ], results: state.recentEvents.slice(0, 3).map((event) => ({ label: event.kind === 'timeout' ? 'Skipped' : event.kind, @@ -276,7 +300,7 @@ module.exports = { createInitialState, normalizeState, activate, - reset, + start, handleScan, tick, onActivated: activate, diff --git a/server/src/services/barcodeGameService/games/scansPerSecond.js b/server/src/services/barcodeGameService/games/scansPerSecond.js index c87cf78f..fd16583a 100644 --- a/server/src/services/barcodeGameService/games/scansPerSecond.js +++ b/server/src/services/barcodeGameService/games/scansPerSecond.js @@ -14,6 +14,8 @@ function createInitialState() { startedAt: null, endsAt: null, scans: [], + bestRate: 0, + bestRateAt: null, finalResult: null, worldRecord: null, recentRounds: [], @@ -32,6 +34,8 @@ function normalizeState(rawState = {}) { startedAt: Number.isFinite(rawState.startedAt) ? rawState.startedAt : null, endsAt: Number.isFinite(rawState.endsAt) ? rawState.endsAt : null, scans: Array.isArray(rawState.scans) ? rawState.scans.filter((entry) => Number.isFinite(entry?.at)) : [], + bestRate: Number.isFinite(rawState.bestRate) ? Math.max(0, rawState.bestRate) : 0, + bestRateAt: Number.isFinite(rawState.bestRateAt) ? rawState.bestRateAt : null, finalResult: rawState.finalResult && typeof rawState.finalResult === 'object' ? rawState.finalResult : null, worldRecord: rawState.worldRecord && typeof rawState.worldRecord === 'object' ? rawState.worldRecord : null, recentRounds: Array.isArray(rawState.recentRounds) ? rawState.recentRounds.slice(-10) : [], @@ -49,12 +53,14 @@ function calculateRate(scanCount, startedAt, endedAt) { function buildResult(state, endedAt = Date.now()) { const scanCount = state.scans.length; - const rate = calculateRate(scanCount, state.startedAt, endedAt); + const finalRate = calculateRate(scanCount, state.startedAt, endedAt); + const bestRate = Math.max(Number(state.bestRate || 0), finalRate); return { roundId: state.roundId, scanCount, durationMs: Math.max(0, endedAt - (state.startedAt || endedAt)), - scansPerSecond: Number(rate.toFixed(3)), + scansPerSecond: Number(bestRate.toFixed(3)), + finalScansPerSecond: Number(finalRate.toFixed(3)), startedAt: state.startedAt, endedAt, participants: Object.values(state.participantCounts || {}).sort((a, b) => (b.scanCount || 0) - (a.scanCount || 0)), @@ -111,6 +117,8 @@ function startRound(rawState, now = Date.now()) { startedAt: now, endsAt: now + ROUND_DURATION_MS, scans: [], + bestRate: 0, + bestRateAt: null, finalResult: null, participantCounts: {}, endedAt: null, @@ -118,13 +126,17 @@ function startRound(rawState, now = Date.now()) { }; } -function activate(rawState, context = {}) { +function start(rawState, context = {}) { const state = normalizeState(rawState); const now = context.now || Date.now(); - // Activation is game-defined, but the shared service calls it generically. - // For this game, active rounds keep running while idle or ended rounds start - // cleanly so returning to the game always produces a playable challenge. - return state.status === 'running' ? state : startRound(state, now); + // Starting a game should always produce a fresh playable round. The global + // service owns when a round starts, so this module does not need to preserve a + // prior ended/running state across lifecycle transitions. + return startRound(state, now); +} + +function activate(rawState, context = {}) { + return start(rawState, context); } function reset(rawState, context = {}) { @@ -155,7 +167,14 @@ function handleScan(rawState, scan, context = {}) { state = finishRound(state, state.endsAt); awards = buildAwards(state.finalResult, state); } - if (state.status !== 'running') return { state, awards }; + if (state.status !== 'running') { + return { + state, + awards, + done: Boolean(awards.length), + display: awards.length ? getPublicState(state, { ...context, now }).display : null, + }; + } // This game deliberately counts every submitted scan, including unknown and // invalid barcodes, because the challenge is about physically getting scans @@ -175,10 +194,16 @@ function handleScan(rawState, scan, context = {}) { return { state, awards: buildAwards(state.finalResult, state), + done: true, + display: getPublicState(state, { ...context, now }).display, }; } const currentRate = calculateRate(state.scans.length, state.startedAt, now); + if (currentRate > (state.bestRate || 0)) { + state.bestRate = Number(currentRate.toFixed(3)); + state.bestRateAt = now; + } state.lastMessage = `${currentRate.toFixed(2)} scans per second`; return { state, awards: [] }; } @@ -191,6 +216,8 @@ function tick(rawState, context = {}) { return { state: finished, awards: buildAwards(finished.finalResult, finished), + done: true, + display: getPublicState(finished, { ...context, now }).display, }; } if (state.status === 'ended' && state.endedAt && now - state.endedAt >= RESULT_IDLE_MS) { @@ -204,6 +231,8 @@ function tick(rawState, context = {}) { startedAt: null, endsAt: null, scans: [], + bestRate: 0, + bestRateAt: null, participantCounts: {}, lastMessage: 'vote to start scans per second', }; @@ -226,8 +255,9 @@ function getPublicState(rawState, context = {}) { const worldRecordText = state.worldRecord ? `${Number(state.worldRecord.scansPerSecond || 0).toFixed(2)} scans per second` : 'none yet'; + const bestRate = Math.max(Number(state.bestRate || 0), state.finalResult?.scansPerSecond || 0); const primary = state.status === 'running' - ? `${Number(currentRate).toFixed(2)} scans per second` + ? `${Number(bestRate).toFixed(2)} scans per second` : state.finalResult ? `${Number(state.finalResult.scansPerSecond || 0).toFixed(2)} scans per second` : 'Scan anything'; @@ -244,6 +274,7 @@ function getPublicState(rawState, context = {}) { : 'five minute scan challenge', scanCount: state.scans.length, scansPerSecond: Number(currentRate.toFixed(3)), + bestRate: Number(bestRate.toFixed(3)), remainingMs, finalResult: state.finalResult, worldRecord: state.worldRecord, @@ -269,7 +300,7 @@ function getPublicState(rawState, context = {}) { : null, stats: [ { label: 'Scans', value: state.scans.length }, - { label: 'Current rate', value: Number(currentRate).toFixed(2) }, + { label: 'Live rate', value: Number(currentRate).toFixed(2) }, { label: 'World record', value: worldRecordText }, ], results: state.recentRounds.slice(0, 3).map((round) => ({ @@ -287,6 +318,7 @@ module.exports = { createInitialState, normalizeState, activate, + start, reset, handleScan, tick, diff --git a/server/src/services/barcodeGameService/index.js b/server/src/services/barcodeGameService/index.js index cf8c100b..7b9f96fc 100644 --- a/server/src/services/barcodeGameService/index.js +++ b/server/src/services/barcodeGameService/index.js @@ -17,6 +17,9 @@ const GAME_SOCKET_ROOM = 'barcode-game'; const RECENT_EVENT_LIMIT = 20; const ROVER_ATTRIBUTION_WINDOW_MS = 60 * 1000; const GAME_TICK_MS = 5 * 1000; +const VOTING_WINDOW_MS = 10 * 1000; +const STARTING_WINDOW_MS = 5 * 1000; +const RESULTS_WINDOW_MS = 45 * 1000; const GAME_DEFINITIONS = [scanQuest, scansPerSecond]; const GAMES_BY_ID = Object.fromEntries(GAME_DEFINITIONS.map((game) => [game.id, game])); @@ -49,6 +52,12 @@ function normalizePlayerKey(identity = {}, socketId = '', roverId = '') { return null; } +function normalizeIdentityPlayerKey(identity = {}) { + // Permanent scoring is identity-only. Socket and rover IDs are useful runtime + // evidence, but they are unstable and should not create leaderboard entries. + return identity.cookieUserId ? `identity:${identity.cookieUserId}` : null; +} + function resolveRoverParticipant(roverId) { const normalizedRoverId = String(roverId || '').trim(); if (!normalizedRoverId) return null; @@ -56,7 +65,7 @@ function resolveRoverParticipant(roverId) { const socketId = activeDrivers?.[normalizedRoverId] || null; const socket = socketId ? io.sockets.sockets.get(socketId) : null; const identity = socket ? getIdentitySummary(socket) : {}; - const playerKey = normalizePlayerKey(identity, socketId, normalizedRoverId); + const playerKey = normalizeIdentityPlayerKey(identity); return { playerKey, @@ -152,7 +161,7 @@ function recordPlayerParticipation(draft, gameId, participants, now) { if (!gameId || !Array.isArray(participants) || !participants.length) return; draft.players = draft.players || {}; participants.forEach((participant) => { - if (!participant?.playerKey) return; + if (!participant?.playerKey || !String(participant.playerKey).startsWith('identity:')) return; const previous = draft.players[participant.playerKey] || {}; const previousGames = previous.games || {}; const previousGame = previousGames[gameId] || {}; @@ -182,7 +191,7 @@ function applyPointAwards(draft, gameId, awards = [], now) { awards.forEach((award) => { const playerKey = award?.playerKey; const points = Number.isFinite(award?.points) ? Math.max(0, Math.floor(award.points)) : 0; - if (!playerKey || !points) return; + if (!playerKey || !String(playerKey).startsWith('identity:') || !points) return; const previous = draft.players[playerKey] || {}; const previousGames = previous.games || {}; @@ -241,17 +250,27 @@ function buildGameContext(draft, extras = {}) { }; } -function activateGame(draft, gameId, now) { +function startGame(draft, gameId, now) { const definition = getGameDefinition(gameId); if (!definition) return false; const currentState = ensureGameState(draft, gameId); - const nextState = definition.activate - ? definition.activate(currentState, buildGameContext(draft, { now })) + const nextState = definition.start + ? definition.start(currentState, buildGameContext(draft, { now })) + : definition.activate + ? definition.activate(currentState, buildGameContext(draft, { now })) : currentState; draft.games[gameId] = nextState; + draft.phase = 'running'; + draft.runningGameId = gameId; + draft.selectedGameId = gameId; draft.activeGameId = gameId; + draft.voteEndsAt = null; + draft.startsAt = null; + draft.resultsUntil = null; + draft.resultGameId = null; + draft.resultDisplay = null; addRecentEvent(draft, { - kind: 'gameActivated', + kind: 'gameStarted', gameId, title: definition.title, }); @@ -266,9 +285,11 @@ function normalizeGameResult(result, fallbackState) { return { state: result.state, awards: Array.isArray(result.awards) ? result.awards : [], + done: Boolean(result.done), + display: result.display && typeof result.display === 'object' ? result.display : null, }; } - return { state: result, awards: [] }; + return { state: result, awards: [], done: false, display: null }; } function countVotes(votes = {}) { @@ -285,11 +306,11 @@ function chooseVoteWinner(draft) { const entries = Object.entries(counts).sort((a, b) => b[1] - a[1]); if (!entries.length) return null; const [topGameId, topCount] = entries[0]; - const activeCount = draft.activeGameId ? counts[draft.activeGameId] || 0 : 0; + const selectedCount = draft.selectedGameId ? counts[draft.selectedGameId] || 0 : 0; - // Ties keep the current game so a single equalizing vote does not cause the - // room display to flicker back and forth between games. - if (draft.activeGameId && activeCount === topCount) return draft.activeGameId; + // Ties keep the currently selected pending game so a single equalizing vote + // does not make the room display flicker during the voting window. + if (draft.selectedGameId && selectedCount === topCount) return draft.selectedGameId; return topGameId; } @@ -306,6 +327,10 @@ function setVote(socket, gameId) { const now = Date.now(); const voterKey = getVoterKey(socket); const identity = getIdentitySummary(socket); + const current = loadStore(); + if (current.phase === 'running' || current.phase === 'starting') { + return { error: 'a barcode game is already starting or running' }; + } withGameStore((draft) => { draft.votes = draft.votes || {}; @@ -317,17 +342,18 @@ function setVote(socket, gameId) { votedAt: now, }; - const winner = chooseVoteWinner(draft); - const existingWinnerState = winner ? draft.games?.[winner] : null; - const shouldActivateWinner = Boolean( - winner && - (!draft.activeGameId || - winner !== draft.activeGameId || - existingWinnerState?.status === 'ended' || - existingWinnerState?.status === 'idle'), - ); - if (shouldActivateWinner) { - activateGame(draft, winner, now); + const winner = chooseVoteWinner(draft) || definition.id; + draft.selectedGameId = winner; + if (draft.phase === 'idle' || draft.phase === 'results') { + draft.phase = 'voting'; + draft.voteEndsAt = now + VOTING_WINDOW_MS; + draft.startsAt = null; + draft.runningGameId = null; + draft.activeGameId = null; + draft.resultsUntil = null; + draft.resultDisplay = null; + } else if (draft.phase === 'voting') { + draft.voteEndsAt = Math.max(draft.voteEndsAt || 0, now + VOTING_WINDOW_MS); } }); @@ -337,20 +363,78 @@ function setVote(socket, gameId) { function settleActiveGameIfNeeded() { const store = loadStore(); - const activeGameId = store.activeGameId; + const now = Date.now(); + const phase = store.phase || 'idle'; + + if (phase === 'voting' && store.voteEndsAt && now >= store.voteEndsAt) { + const winner = chooseVoteWinner(store) || store.selectedGameId; + if (!getGameDefinition(winner)) return false; + withGameStore((draft) => { + draft.selectedGameId = winner; + draft.phase = 'starting'; + draft.startsAt = now + STARTING_WINDOW_MS; + draft.voteEndsAt = null; + addRecentEvent(draft, { + kind: 'gameStarting', + gameId: winner, + title: getGameDefinition(winner)?.title, + }); + }); + return true; + } + + if (phase === 'starting' && store.startsAt && now >= store.startsAt) { + const gameId = store.selectedGameId; + if (!getGameDefinition(gameId)) return false; + withGameStore((draft) => { + startGame(draft, gameId, now); + }); + return true; + } + + if (phase === 'results' && store.resultsUntil && now >= store.resultsUntil) { + withGameStore((draft) => { + draft.phase = 'idle'; + draft.selectedGameId = null; + draft.runningGameId = null; + draft.activeGameId = null; + draft.voteEndsAt = null; + draft.startsAt = null; + draft.resultsUntil = null; + draft.resultGameId = null; + draft.resultDisplay = null; + draft.votes = {}; + }); + return true; + } + + const activeGameId = store.phase === 'running' ? store.runningGameId : null; const definition = getGameDefinition(activeGameId); const currentState = activeGameId ? store.games?.[activeGameId] : null; - const now = Date.now(); if (!definition?.tick || !currentState) return false; const normalizedState = ensureReadonlyGameState(store, activeGameId); const gameResult = definition.tick(normalizedState, buildGameContext(store, { now })); - const { state: nextState, awards } = normalizeGameResult(gameResult, normalizedState); - if (JSON.stringify(nextState) === JSON.stringify(normalizedState)) return false; + const { state: nextState, awards, done, display } = normalizeGameResult(gameResult, normalizedState); + if (JSON.stringify(nextState) === JSON.stringify(normalizedState) && !done) return false; withGameStore((draft) => { draft.games[activeGameId] = nextState; applyPointAwards(draft, activeGameId, awards, now); + if (done) { + const publicState = definition.getPublicState + ? definition.getPublicState(nextState, buildGameContext(draft, { now })) + : null; + draft.phase = 'results'; + draft.resultGameId = activeGameId; + draft.resultDisplay = display || publicState?.display || null; + draft.resultsUntil = now + RESULTS_WINDOW_MS; + draft.runningGameId = null; + draft.activeGameId = null; + draft.startsAt = null; + draft.voteEndsAt = null; + draft.votes = {}; + } }); return true; } @@ -361,7 +445,7 @@ function handleScan(scan) { updateGlobalCounters(draft, scan, now); recordRoverSighting(draft, scan, now); const participants = getProximityParticipants(draft, now); - const activeGameId = draft.activeGameId; + const activeGameId = draft.phase === 'running' ? draft.runningGameId : null; const definition = getGameDefinition(activeGameId); if (definition) { @@ -369,10 +453,24 @@ function handleScan(scan) { const gameResult = definition.handleScan ? definition.handleScan(currentState, scan, buildGameContext(draft, { now, participants })) : currentState; - const { state: nextState, awards } = normalizeGameResult(gameResult, currentState); + const { state: nextState, awards, done, display } = normalizeGameResult(gameResult, currentState); draft.games[activeGameId] = nextState; recordPlayerParticipation(draft, activeGameId, participants, now); applyPointAwards(draft, activeGameId, awards, now); + if (done) { + const publicState = definition.getPublicState + ? definition.getPublicState(nextState, buildGameContext(draft, { now })) + : null; + draft.phase = 'results'; + draft.resultGameId = activeGameId; + draft.resultDisplay = display || publicState?.display || null; + draft.resultsUntil = now + RESULTS_WINDOW_MS; + draft.runningGameId = null; + draft.activeGameId = null; + draft.startsAt = null; + draft.voteEndsAt = null; + draft.votes = {}; + } } addRecentEvent(draft, { @@ -396,7 +494,7 @@ function topCounters(bucket = {}, limit = 5) { function getPlayerForSocket(store, socket) { if (!socket) return null; const identity = getIdentitySummary(socket); - const playerKey = normalizePlayerKey(identity, socket.id, ''); + const playerKey = normalizeIdentityPlayerKey(identity); const player = playerKey ? store.players?.[playerKey] || null : null; if (!player) { return { @@ -408,7 +506,7 @@ function getPlayerForSocket(store, socket) { }; } const rankedPlayers = Object.values(store.players || {}) - .filter((entry) => Number.isFinite(entry?.totalPoints) && entry.totalPoints > 0) + .filter((entry) => String(entry?.playerKey || '').startsWith('identity:') && Number.isFinite(entry?.totalPoints) && entry.totalPoints > 0) .sort((a, b) => (b.totalPoints || 0) - (a.totalPoints || 0)); const rank = rankedPlayers.findIndex((entry) => entry.playerKey === player.playerKey) + 1; return { @@ -425,24 +523,34 @@ function buildStatePayload(socket = null) { const store = loadStore(); const now = Date.now(); const voteCounts = countVotes(store.votes); - const activeDefinition = getGameDefinition(store.activeGameId); + const selectedDefinition = getGameDefinition(store.selectedGameId); + const runningDefinition = getGameDefinition(store.runningGameId); const context = buildGameContext(store, { now }); - const activeGame = activeDefinition - ? activeDefinition.getPublicState(ensureReadonlyGameState(store, activeDefinition.id), context) + const runningGame = runningDefinition + ? runningDefinition.getPublicState(ensureReadonlyGameState(store, runningDefinition.id), context) : null; + const activeGame = buildLifecycleGameState(store, { + now, + selectedDefinition, + runningDefinition, + runningGame, + voteCounts, + }); const leaderboard = topPlayers(store.players); return { - activeGameId: store.activeGameId, + phase: store.phase, + selectedGameId: store.selectedGameId, + runningGameId: store.runningGameId, + activeGameId: store.runningGameId, games: GAME_DEFINITIONS.map((game) => ({ id: game.id, title: game.title, description: game.description, voteCount: voteCounts[game.id] || 0, - active: game.id === store.activeGameId, - actionLabel: game.id === store.activeGameId && activeGame?.actionLabel - ? activeGame.actionLabel - : 'Start', + active: game.id === store.runningGameId, + selected: game.id === store.selectedGameId, + actionLabel: store.phase === 'idle' || store.phase === 'results' ? 'Vote' : game.id === store.selectedGameId ? 'Selected' : 'Vote', })), activeGame, leaderboard, @@ -456,9 +564,75 @@ function buildStatePayload(socket = null) { }; } +function buildLifecycleGameState(store, { now, selectedDefinition, runningDefinition, runningGame, voteCounts }) { + if (store.phase === 'running' && runningGame) return runningGame; + if (store.phase === 'results') { + return { + id: store.resultGameId, + title: getGameDefinition(store.resultGameId)?.title || 'Results', + status: 'results', + display: { + title: 'Results', + primary: store.resultDisplay?.primary || 'Round complete', + secondary: store.resultDisplay?.secondary || 'Vote to start another game', + timer: store.resultsUntil ? { label: 'Results clear in', endsAt: store.resultsUntil } : null, + stats: store.resultDisplay?.stats || [], + results: store.resultDisplay?.results || [], + }, + }; + } + if (store.phase === 'starting') { + return { + id: store.selectedGameId, + title: selectedDefinition?.title || 'Starting', + status: 'starting', + display: { + title: 'Starting', + primary: selectedDefinition ? `${selectedDefinition.title} starts soon` : 'Game starts soon', + secondary: 'Get ready', + timer: store.startsAt ? { label: 'Starts in', endsAt: store.startsAt } : null, + stats: [], + results: [], + }, + }; + } + if (store.phase === 'voting') { + const selectedTitle = selectedDefinition?.title || 'a barcode game'; + return { + id: store.selectedGameId, + title: 'Voting', + status: 'voting', + display: { + title: 'Voting', + primary: `Voting for ${selectedTitle}`, + secondary: 'Most votes starts the next game', + timer: store.voteEndsAt ? { label: 'Voting ends in', endsAt: store.voteEndsAt } : null, + stats: GAME_DEFINITIONS.map((game) => ({ + label: game.title, + value: voteCounts[game.id] || 0, + })), + results: [], + }, + }; + } + return { + id: null, + title: 'Barcode games', + status: 'idle', + display: { + title: 'Barcode games', + primary: 'Choose a game', + secondary: 'Vote to start the next round', + timer: null, + stats: [], + results: [], + }, + }; +} + function topPlayers(players = {}, limit = 6) { return Object.values(players || {}) - .filter((player) => Number.isFinite(player?.totalPoints) && player.totalPoints > 0) + .filter((player) => String(player?.playerKey || '').startsWith('identity:') && Number.isFinite(player?.totalPoints) && player.totalPoints > 0) .sort((a, b) => (b.totalPoints || 0) - (a.totalPoints || 0)) .slice(0, limit) .map((player) => ({ diff --git a/server/src/services/barcodeGameService/store.js b/server/src/services/barcodeGameService/store.js index 71a97bc4..a2495e77 100644 --- a/server/src/services/barcodeGameService/store.js +++ b/server/src/services/barcodeGameService/store.js @@ -13,6 +13,14 @@ function createDefaultStore() { return { version: STORE_VERSION, updatedAt: Date.now(), + phase: 'idle', + selectedGameId: null, + runningGameId: null, + voteEndsAt: null, + startsAt: null, + resultsUntil: null, + resultGameId: null, + resultDisplay: null, activeGameId: null, votes: {}, globalCounters: { @@ -89,11 +97,30 @@ function normalizeStoreShape(raw = {}) { if (vote) votes[key] = { ...vote, voterKey: vote.voterKey || key }; }); + const phase = ['idle', 'voting', 'starting', 'running', 'results'].includes(raw.phase) + ? raw.phase + : raw.activeGameId + ? 'running' + : 'idle'; + const runningGameId = typeof raw.runningGameId === 'string' + ? raw.runningGameId + : typeof raw.activeGameId === 'string' + ? raw.activeGameId + : null; + return { ...base, version: STORE_VERSION, updatedAt: Number.isFinite(raw.updatedAt) ? raw.updatedAt : Date.now(), - activeGameId: typeof raw.activeGameId === 'string' ? raw.activeGameId : null, + phase, + selectedGameId: typeof raw.selectedGameId === 'string' ? raw.selectedGameId : runningGameId, + runningGameId, + voteEndsAt: Number.isFinite(raw.voteEndsAt) ? raw.voteEndsAt : null, + startsAt: Number.isFinite(raw.startsAt) ? raw.startsAt : null, + resultsUntil: Number.isFinite(raw.resultsUntil) ? raw.resultsUntil : null, + resultGameId: typeof raw.resultGameId === 'string' ? raw.resultGameId : null, + resultDisplay: raw.resultDisplay && typeof raw.resultDisplay === 'object' ? raw.resultDisplay : null, + activeGameId: runningGameId, votes, globalCounters: { codes: normalizeCounterBucket(raw.globalCounters?.codes), diff --git a/webui/src/components/BarcodeGamesPanel/index.jsx b/webui/src/components/BarcodeGamesPanel/index.jsx index cfa111d7..2e029728 100644 --- a/webui/src/components/BarcodeGamesPanel/index.jsx +++ b/webui/src/components/BarcodeGamesPanel/index.jsx @@ -36,6 +36,7 @@ function GameChoice({ game, disabled, onVote }) { className={[ 'button-dark flex min-h-[4.25rem] flex-col items-start justify-between px-1.5 py-1 text-left disabled:opacity-70', game.active ? 'border-emerald-500/70' : '', + game.selected && !game.active ? 'border-cyan-500/60' : '', ].filter(Boolean).join(' ')} > @@ -44,7 +45,7 @@ function GameChoice({ game, disabled, onVote }) { {game.description} - {game.active ? 'Active' : game.actionLabel || 'Start'} + {game.active ? 'Active' : game.selected ? 'Selected' : game.actionLabel || 'Vote'} ); @@ -119,6 +120,7 @@ export default function BarcodeGamesPanel() { const [pendingGameId, setPendingGameId] = useState(null); const activeGame = state.activeGame; const display = activeGame?.display || {}; + const votingDisabled = state.phase === 'starting' || state.phase === 'running'; const timerEndsAt = display.timer?.endsAt; const now = useClock(Number.isFinite(timerEndsAt)); const timerText = formatTimer(timerEndsAt, now); @@ -140,7 +142,7 @@ export default function BarcodeGamesPanel() { ))}