diff --git a/rulesdocs/refector_rules_and_tracking.md b/rulesdocs/refector_rules_and_tracking.md index 43107d78..e9f9a00a 100644 --- a/rulesdocs/refector_rules_and_tracking.md +++ b/rulesdocs/refector_rules_and_tracking.md @@ -28,7 +28,7 @@ # REFACTOR TRACKING ## Current phase - [x] Phase 1: Inventory + usage mapping (server + webui) -- [ ] Phase 2: Refactor highest-impact offenders first +- [x] Phase 2: Refactor highest-impact offenders first - [ ] Phase 3: Sweep remaining services/components - [ ] Phase 4: Dead code/file removal pass - [ ] Phase 5: Final regression validation @@ -68,7 +68,7 @@ - [x] mobile controls - [x] top down map - [x] video tile -- [ ] Sweep `webui` for unused or unneeded files/code with verification +- [x] Sweep `webui` for unused or unneeded files/code with verification ### COMPLETED COMPONENTS - mini summary app @@ -91,6 +91,8 @@ - Split `webui/src/components/GamepadMappingSettings.jsx` into `webui/src/components/GamepadMappingSettings/` with extracted constants/helpers/SliderField modules and removed the standalone component file. - Split `webui/src/components/MobileControls.jsx` into `webui/src/components/MobileControls/` with extracted joystick/aux/constants modules; preserved named exports and moved app import to folder entrypoint. - Split `webui/src/components/TopDownMap.jsx` into `webui/src/components/TopDownMap/` with extracted geometry/color helpers and visual SVG primitive modules; updated all consumers to folder entrypoint. +- Folderized all remaining top-level files under `webui/src/components/` into per-component `index.jsx` folders and rewired component imports to match the new structure. +- Removed unreferenced components `CameraServoPanel` and `ControlSummary` after dependency-map verification and successful rebuild. ## Done criteria (per item) - [ ] Folderized structure created. diff --git a/server/public/assets/index-DVTOmRBl.css b/server/public/assets/index-BmoiQy4X.css similarity index 97% rename from server/public/assets/index-DVTOmRBl.css rename to server/public/assets/index-BmoiQy4X.css index 6b8dc714..3aa1d31a 100644 --- a/server/public/assets/index-DVTOmRBl.css +++ b/server/public/assets/index-BmoiQy4X.css @@ -1 +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}}.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))}.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}.inset-0{inset:0}.inset-x-0{left:0;right:0}.bottom-0{bottom:0}.bottom-0\.5{bottom:.125rem}.bottom-1{bottom:.25rem}.bottom-2{bottom:.5rem}.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-30{z-index:30}.z-40{z-index:40}.z-50{z-index:50}.z-\[120\]{z-index:120}.order-1{order:1}.order-2{order:2}.col-span-full{grid-column:1 / -1}.m-0{margin:0}.mx-auto{margin-left:auto;margin-right:auto}.-mt-0\.5{margin-top:-.125rem}.mr-0{margin-right:0}.mt-0{margin-top:0}.mt-0\.5{margin-top:.125rem}.mt-auto{margin-top:auto}.\!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}.h-1\.5{height:.375rem}.h-10{height:2.5rem}.h-12{height:3rem}.h-2{height:.5rem}.h-2\.5{height:.625rem}.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-\[14rem\]{height:14rem}.h-\[2px\]{height:2px}.h-\[3\.5rem\]{height:3.5rem}.h-\[70\%\]{height:70%}.h-\[7rem\]{height:7rem}.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-\[85vh\]{max-height:85vh}.max-h-\[90vh\]{max-height:90vh}.max-h-screen{max-height:100vh}.min-h-0{min-height:0px}.min-h-\[1\.5rem\]{min-height:1.5rem}.min-h-\[14rem\]{min-height:14rem}.min-h-\[16rem\]{min-height:16rem}.min-h-\[3\.5rem\]{min-height:3.5rem}.min-h-\[5rem\]{min-height:5rem}.min-h-screen{min-height:100vh}.w-1\.5{width:.375rem}.w-1\/2{width:50%}.w-10{width:2.5rem}.w-12{width:3rem}.w-16{width:4rem}.w-2{width:.5rem}.w-2\.5{width:.625rem}.w-20{width:5rem}.w-28{width:7rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-7{width:1.75rem}.w-\[12\.5rem\]{width:12.5rem}.w-\[12rem\]{width:12rem}.w-\[20rem\]{width:20rem}.w-\[2px\]{width:2px}.w-\[9rem\]{width:9rem}.w-auto{width:auto}.w-fit{width:-moz-fit-content;width:fit-content}.w-full{width:100%}.w-screen{width:100vw}.min-w-0{min-width:0px}.min-w-\[0\]{min-width:0}.min-w-\[10rem\]{min-width:10rem}.min-w-\[12rem\]{min-width:12rem}.max-w-2xl{max-width:42rem}.max-w-4xl{max-width:56rem}.max-w-\[70vw\]{max-width:70vw}.max-w-\[80vw\]{max-width:80vw}.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-\[1\.1\]{flex:1.1}.flex-\[1\.22\]{flex:1.22}.flex-\[1_1_12rem\]{flex:1 1 12rem}.shrink-0{flex-shrink:0}.basis-full{flex-basis:100%}.-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-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}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-4{grid-template-columns:repeat(4,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\,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-rows-2{grid-template-rows:repeat(2,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-\[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-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.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-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}.gap-0{gap:0px}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-4{gap:1rem}.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-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-center{align-self:center}.self-stretch{align-self:stretch}.overflow-hidden{overflow:hidden}.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}.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}.border{border-width:1px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-l{border-left-width:1px}.border-r{border-right-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-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\/50{border-color:#a5f3fc80}.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-500\/40{border-color:#06b6d466}.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-300\/70{border-color:#6ee7b7b3}.border-emerald-400\/60{border-color:#34d39999}.border-emerald-500\/40{border-color:#10b98166}.border-emerald-700{--tw-border-opacity: 1;border-color:rgb(4 120 87 / var(--tw-border-opacity))}.border-emerald-700\/60{border-color:#04785799}.border-fuchsia-300\/70{border-color:#f0abfcb3}.border-indigo-200\/70{border-color:#c7d2feb3}.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-pink-200{--tw-border-opacity: 1;border-color:rgb(251 207 232 / var(--tw-border-opacity))}.border-red-200{--tw-border-opacity: 1;border-color:rgb(254 202 202 / var(--tw-border-opacity))}.border-red-400\/60{border-color:#f8717199}.border-rose-800{--tw-border-opacity: 1;border-color:rgb(159 18 57 / var(--tw-border-opacity))}.border-sky-400\/50{border-color:#38bdf880}.border-slate-200{--tw-border-opacity: 1;border-color:rgb(226 232 240 / var(--tw-border-opacity))}.border-slate-200\/30{border-color:#e2e8f04d}.border-slate-400\/60{border-color:#94a3b899}.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-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-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\/60{border-color:#fff9}.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-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-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\/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\/85{background-color:#000000d9}.bg-cyan-300\/80{background-color:#67e8f9cc}.bg-cyan-400\/10{background-color:#22d3ee1a}.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-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-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\/15{background-color:#064e3b26}.bg-emerald-900\/60{background-color:#064e3b99}.bg-emerald-900\/80{background-color:#064e3bcc}.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-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\/20{background-color:#312e8133}.bg-indigo-900\/60{background-color:#312e8199}.bg-indigo-950\/90{background-color:#1e1b4be6}.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\/20{background-color:#b91c1c33}.bg-red-700\/60{background-color:#b91c1c99}.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-rose-600{--tw-bg-opacity: 1;background-color:rgb(225 29 72 / var(--tw-bg-opacity))}.bg-rose-900\/80{background-color:#881337cc}.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-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-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\/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\/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-zinc-900{--tw-bg-opacity: 1;background-color:rgb(24 24 27 / var(--tw-bg-opacity))}.bg-zinc-900\/90{background-color:#18181be6}.bg-zinc-950{--tw-bg-opacity: 1;background-color:rgb(9 9 11 / var(--tw-bg-opacity))}.bg-zinc-950\/95{background-color:#09090bf2}.bg-cover{background-size:cover}.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-2{padding:.5rem}.p-4{padding:1rem}.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}.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-\[1px\]{padding-top:1px;padding-bottom:1px}.py-\[2px\]{padding-top:2px;padding-bottom:2px}.pb-0{padding-bottom:0}.pb-1{padding-bottom:.25rem}.pr-0{padding-right:0}.pt-0\.5{padding-top:.125rem}.pt-1{padding-top:.25rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.align-top{vertical-align:top}.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\.5rem\]{font-size:.5rem}.text-\[0\.65rem\]{font-size:.65rem}.text-\[0\.6rem\]{font-size:.6rem}.text-\[0\.72rem\]{font-size:.72rem}.text-\[0\.75rem\]{font-size:.75rem}.text-\[0\.7rem\]{font-size:.7rem}.text-\[0\.85rem\]{font-size:.85rem}.text-\[0\.8rem\]{font-size:.8rem}.text-\[1\.1rem\]{font-size:1.1rem}.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-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.capitalize{text-transform:capitalize}.italic{font-style:italic}.leading-none{line-height:1}.leading-normal{line-height:1.5}.leading-snug{line-height:1.375}.leading-tight{line-height:1.25}.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-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-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-100{--tw-text-opacity: 1;color:rgb(207 250 254 / 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-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-100\/80{color:#e0e7ffcc}.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-200{--tw-text-opacity: 1;color:rgb(217 249 157 / var(--tw-text-opacity))}.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-red-100\/90{color:#fee2e2e6}.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-700{--tw-text-opacity: 1;color:rgb(185 28 28 / var(--tw-text-opacity))}.text-rose-50{--tw-text-opacity: 1;color:rgb(255 241 242 / 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-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}.underline{text-decoration-line:underline}.accent-cyan-400{accent-color:#22d3ee}.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-60{opacity:.6}.opacity-70{opacity:.7}.opacity-80{opacity:.8}.opacity-90{opacity:.9}.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-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-black\/40{--tw-shadow-color: rgb(0 0 0 / .4);--tw-shadow: var(--tw-shadow-colored)}.outline{outline-style:solid}.outline-2{outline-width:2px}.outline-cyan-400\/60{outline-color:#22d3ee99}.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-amber-300{--tw-ring-opacity: 1;--tw-ring-color: rgb(252 211 77 / var(--tw-ring-opacity))}.ring-emerald-300\/70{--tw-ring-color: rgb(110 231 183 / .7)}.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)}.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)}.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}@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}.\[clip-path\:circle\(50\%\)\]{clip-path:circle(50%)}.\[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))}*{scrollbar-width:thin;scrollbar-color:#3b82f6 rgba(255,255,255,.08)}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}*::-webkit-scrollbar{width:1px;height:1px}*::-webkit-scrollbar-track{background:#ffffff14}*::-webkit-scrollbar-thumb{background-color:#3b82f6;border-radius:9999px}.ha-hue-slider{-webkit-appearance:none;-moz-appearance:none;appearance:none;width:100%;height:.45rem;border-radius:9999px;background:linear-gradient(90deg,red,#ff0,#0f0,#0ff,#00f,#f0f,red);cursor:pointer}.ha-hue-slider:disabled{opacity:.6;cursor:not-allowed}.ha-hue-slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:.75rem;height:.75rem;border-radius:9999px;border:1px solid rgba(255,255,255,.7);background:#fff}.ha-hue-slider::-webkit-slider-runnable-track{height:.45rem;border-radius:9999px;background:linear-gradient(90deg,red,#ff0,#0f0,#0ff,#00f,#f0f,red)}.ha-hue-slider::-moz-range-thumb{width:.75rem;height:.75rem;border-radius:9999px;border:1px solid rgba(255,255,255,.7);background:#fff}.ha-hue-slider::-moz-range-track{height:.45rem;border-radius:9999px;background:linear-gradient(90deg,red,#ff0,#0f0,#0ff,#00f,#f0f,red)}.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))}.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\:bg-amber-400:hover{--tw-bg-opacity: 1;background-color:rgb(251 191 36 / 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-emerald-800:hover{--tw-bg-opacity: 1;background-color:rgb(6 95 70 / 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-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-800:hover{--tw-bg-opacity: 1;background-color:rgb(159 18 57 / 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-100:hover{--tw-bg-opacity: 1;background-color:rgb(241 245 249 / 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-800\/80:hover{background-color:#1e293bcc}.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\: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-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\:hover\:bg-inherit:hover:disabled{background-color:inherit}@media(min-width:640px){.sm\:block{display:block}.sm\:min-h-\[18rem\]{min-height:18rem}.sm\:basis-auto{flex-basis:auto}.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\:items-center{align-items:center}.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\:min-h-\[18rem\]{min-height:18rem}.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-\[minmax\(0\,1\.1fr\)_minmax\(0\,1fr\)\]{grid-template-columns:minmax(0,1.1fr) minmax(0,1fr)}.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\,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-6{grid-template-columns:repeat(6,minmax(0,1fr))}.lg\:grid-cols-7{grid-template-columns:repeat(7,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}} +*,: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}}.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))}.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}.inset-0{inset:0}.inset-x-0{left:0;right:0}.bottom-0{bottom:0}.bottom-0\.5{bottom:.125rem}.bottom-1{bottom:.25rem}.bottom-2{bottom:.5rem}.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-30{z-index:30}.z-40{z-index:40}.z-50{z-index:50}.z-\[120\]{z-index:120}.order-1{order:1}.order-2{order:2}.col-span-full{grid-column:1 / -1}.m-0{margin:0}.mx-auto{margin-left:auto;margin-right:auto}.-mt-0\.5{margin-top:-.125rem}.mr-0{margin-right:0}.mt-0{margin-top:0}.mt-0\.5{margin-top:.125rem}.mt-auto{margin-top:auto}.\!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}.h-1\.5{height:.375rem}.h-10{height:2.5rem}.h-12{height:3rem}.h-2{height:.5rem}.h-2\.5{height:.625rem}.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-\[14rem\]{height:14rem}.h-\[2px\]{height:2px}.h-\[3\.5rem\]{height:3.5rem}.h-\[70\%\]{height:70%}.h-\[7rem\]{height:7rem}.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-\[85vh\]{max-height:85vh}.max-h-\[90vh\]{max-height:90vh}.max-h-screen{max-height:100vh}.min-h-0{min-height:0px}.min-h-\[1\.5rem\]{min-height:1.5rem}.min-h-\[14rem\]{min-height:14rem}.min-h-\[16rem\]{min-height:16rem}.min-h-\[3\.5rem\]{min-height:3.5rem}.min-h-\[5rem\]{min-height:5rem}.min-h-screen{min-height:100vh}.w-1\.5{width:.375rem}.w-1\/2{width:50%}.w-10{width:2.5rem}.w-12{width:3rem}.w-16{width:4rem}.w-2{width:.5rem}.w-2\.5{width:.625rem}.w-20{width:5rem}.w-28{width:7rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-7{width:1.75rem}.w-\[12\.5rem\]{width:12.5rem}.w-\[12rem\]{width:12rem}.w-\[20rem\]{width:20rem}.w-\[2px\]{width:2px}.w-\[9rem\]{width:9rem}.w-auto{width:auto}.w-fit{width:-moz-fit-content;width:fit-content}.w-full{width:100%}.w-screen{width:100vw}.min-w-0{min-width:0px}.min-w-\[0\]{min-width:0}.min-w-\[10rem\]{min-width:10rem}.min-w-\[12rem\]{min-width:12rem}.max-w-2xl{max-width:42rem}.max-w-4xl{max-width:56rem}.max-w-\[70vw\]{max-width:70vw}.max-w-\[80vw\]{max-width:80vw}.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-\[1\.1\]{flex:1.1}.flex-\[1\.22\]{flex:1.22}.flex-\[1_1_12rem\]{flex:1 1 12rem}.shrink-0{flex-shrink:0}.basis-full{flex-basis:100%}.-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-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}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-4{grid-template-columns:repeat(4,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\,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-rows-2{grid-template-rows:repeat(2,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-\[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-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.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-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}.gap-0{gap:0px}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-4{gap:1rem}.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-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-center{align-self:center}.self-stretch{align-self:stretch}.overflow-hidden{overflow:hidden}.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}.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}.border{border-width:1px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-l{border-left-width:1px}.border-r{border-right-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-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\/50{border-color:#a5f3fc80}.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-500\/40{border-color:#06b6d466}.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-300\/70{border-color:#6ee7b7b3}.border-emerald-400\/60{border-color:#34d39999}.border-emerald-500\/40{border-color:#10b98166}.border-emerald-700{--tw-border-opacity: 1;border-color:rgb(4 120 87 / var(--tw-border-opacity))}.border-emerald-700\/60{border-color:#04785799}.border-fuchsia-300\/70{border-color:#f0abfcb3}.border-indigo-200\/70{border-color:#c7d2feb3}.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-pink-200{--tw-border-opacity: 1;border-color:rgb(251 207 232 / var(--tw-border-opacity))}.border-red-200{--tw-border-opacity: 1;border-color:rgb(254 202 202 / var(--tw-border-opacity))}.border-red-400\/60{border-color:#f8717199}.border-rose-800{--tw-border-opacity: 1;border-color:rgb(159 18 57 / var(--tw-border-opacity))}.border-sky-400\/50{border-color:#38bdf880}.border-slate-200{--tw-border-opacity: 1;border-color:rgb(226 232 240 / var(--tw-border-opacity))}.border-slate-200\/30{border-color:#e2e8f04d}.border-slate-400\/60{border-color:#94a3b899}.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-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-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\/60{border-color:#fff9}.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-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-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\/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\/85{background-color:#000000d9}.bg-cyan-300\/80{background-color:#67e8f9cc}.bg-cyan-400\/10{background-color:#22d3ee1a}.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-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-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\/15{background-color:#064e3b26}.bg-emerald-900\/60{background-color:#064e3b99}.bg-emerald-900\/80{background-color:#064e3bcc}.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-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\/20{background-color:#312e8133}.bg-indigo-900\/60{background-color:#312e8199}.bg-indigo-950\/90{background-color:#1e1b4be6}.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\/20{background-color:#b91c1c33}.bg-red-700\/60{background-color:#b91c1c99}.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-rose-600{--tw-bg-opacity: 1;background-color:rgb(225 29 72 / var(--tw-bg-opacity))}.bg-rose-900\/80{background-color:#881337cc}.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-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-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\/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\/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-zinc-900{--tw-bg-opacity: 1;background-color:rgb(24 24 27 / var(--tw-bg-opacity))}.bg-zinc-900\/90{background-color:#18181be6}.bg-zinc-950{--tw-bg-opacity: 1;background-color:rgb(9 9 11 / var(--tw-bg-opacity))}.bg-zinc-950\/95{background-color:#09090bf2}.bg-cover{background-size:cover}.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-2{padding:.5rem}.p-4{padding:1rem}.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}.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-\[1px\]{padding-top:1px;padding-bottom:1px}.py-\[2px\]{padding-top:2px;padding-bottom:2px}.pb-0{padding-bottom:0}.pb-1{padding-bottom:.25rem}.pr-0{padding-right:0}.pt-0\.5{padding-top:.125rem}.pt-1{padding-top:.25rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.align-top{vertical-align:top}.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\.5rem\]{font-size:.5rem}.text-\[0\.65rem\]{font-size:.65rem}.text-\[0\.6rem\]{font-size:.6rem}.text-\[0\.72rem\]{font-size:.72rem}.text-\[0\.75rem\]{font-size:.75rem}.text-\[0\.7rem\]{font-size:.7rem}.text-\[0\.85rem\]{font-size:.85rem}.text-\[0\.8rem\]{font-size:.8rem}.text-\[1\.1rem\]{font-size:1.1rem}.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-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.capitalize{text-transform:capitalize}.italic{font-style:italic}.leading-none{line-height:1}.leading-normal{line-height:1.5}.leading-snug{line-height:1.375}.leading-tight{line-height:1.25}.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-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-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-100{--tw-text-opacity: 1;color:rgb(207 250 254 / 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-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-100\/80{color:#e0e7ffcc}.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-200{--tw-text-opacity: 1;color:rgb(217 249 157 / var(--tw-text-opacity))}.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-red-100\/90{color:#fee2e2e6}.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-700{--tw-text-opacity: 1;color:rgb(185 28 28 / var(--tw-text-opacity))}.text-rose-50{--tw-text-opacity: 1;color:rgb(255 241 242 / 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-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}.underline{text-decoration-line:underline}.accent-cyan-400{accent-color:#22d3ee}.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-60{opacity:.6}.opacity-70{opacity:.7}.opacity-80{opacity:.8}.opacity-90{opacity:.9}.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-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-black\/40{--tw-shadow-color: rgb(0 0 0 / .4);--tw-shadow: var(--tw-shadow-colored)}.outline{outline-style:solid}.outline-2{outline-width:2px}.outline-cyan-400\/60{outline-color:#22d3ee99}.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-amber-300{--tw-ring-opacity: 1;--tw-ring-color: rgb(252 211 77 / var(--tw-ring-opacity))}.ring-emerald-300\/70{--tw-ring-color: rgb(110 231 183 / .7)}.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)}.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)}.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}@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}.\[clip-path\:circle\(50\%\)\]{clip-path:circle(50%)}.\[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))}*{scrollbar-width:thin;scrollbar-color:#3b82f6 rgba(255,255,255,.08)}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}*::-webkit-scrollbar{width:1px;height:1px}*::-webkit-scrollbar-track{background:#ffffff14}*::-webkit-scrollbar-thumb{background-color:#3b82f6;border-radius:9999px}.ha-hue-slider{-webkit-appearance:none;-moz-appearance:none;appearance:none;width:100%;height:.45rem;border-radius:9999px;background:linear-gradient(90deg,red,#ff0,#0f0,#0ff,#00f,#f0f,red);cursor:pointer}.ha-hue-slider:disabled{opacity:.6;cursor:not-allowed}.ha-hue-slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:.75rem;height:.75rem;border-radius:9999px;border:1px solid rgba(255,255,255,.7);background:#fff}.ha-hue-slider::-webkit-slider-runnable-track{height:.45rem;border-radius:9999px;background:linear-gradient(90deg,red,#ff0,#0f0,#0ff,#00f,#f0f,red)}.ha-hue-slider::-moz-range-thumb{width:.75rem;height:.75rem;border-radius:9999px;border:1px solid rgba(255,255,255,.7);background:#fff}.ha-hue-slider::-moz-range-track{height:.45rem;border-radius:9999px;background:linear-gradient(90deg,red,#ff0,#0f0,#0ff,#00f,#f0f,red)}.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))}.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\:bg-amber-400:hover{--tw-bg-opacity: 1;background-color:rgb(251 191 36 / 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-emerald-800:hover{--tw-bg-opacity: 1;background-color:rgb(6 95 70 / 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-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-800:hover{--tw-bg-opacity: 1;background-color:rgb(159 18 57 / 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-100:hover{--tw-bg-opacity: 1;background-color:rgb(241 245 249 / 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-800\/80:hover{background-color:#1e293bcc}.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\: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-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\:hover\:bg-inherit:hover:disabled{background-color:inherit}@media(min-width:640px){.sm\:block{display:block}.sm\:min-h-\[18rem\]{min-height:18rem}.sm\:basis-auto{flex-basis:auto}.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\:items-center{align-items:center}.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-\[minmax\(0\,1\.4fr\)_minmax\(0\,1fr\)\]{grid-template-columns:minmax(0,1.4fr) 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-6{grid-template-columns:repeat(6,minmax(0,1fr))}.lg\:grid-cols-7{grid-template-columns:repeat(7,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-BSZxBzcC.js b/server/public/assets/index-D5igcM0e.js similarity index 100% rename from server/public/assets/index-BSZxBzcC.js rename to server/public/assets/index-D5igcM0e.js diff --git a/server/public/index.html b/server/public/index.html index eb2f133d..94a448b3 100644 --- a/server/public/index.html +++ b/server/public/index.html @@ -11,8 +11,8 @@ Multi Roomba Rover - - + +
diff --git a/webui/src/App.jsx b/webui/src/App.jsx index dae3550a..019af692 100644 --- a/webui/src/App.jsx +++ b/webui/src/App.jsx @@ -1,7 +1,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; -import TelemetryPanel from './components/TelemetryPanel.jsx'; -import ReplaySourcesPanel from './components/ReplaySourcesPanel.jsx'; -import AlertFeed from './components/AlertFeed.jsx'; +import TelemetryPanel from './components/TelemetryPanel/index.jsx'; +import ReplaySourcesPanel from './components/ReplaySourcesPanel/index.jsx'; +import AlertFeed from './components/AlertFeed/index.jsx'; import MobileControls, { MobileActionsColumn, MobileDriveColumn, @@ -12,31 +12,31 @@ import { GamepadInputManager, useControlSystem, } from './controls/index.js'; -import RoomCameraPanel from './components/RoomCameraPanel.jsx'; -import LogPanel from './components/LogPanel.jsx'; -import DriverVideoPanel from './components/DriverVideoPanel.jsx'; -import RightPaneTabs from './components/RightPaneTabs.jsx'; -import ModeGateOverlay from './components/ModeGateOverlay.jsx'; -import HomeAssistantControls from './components/HomeAssistantControls.jsx'; -import TurnAlertListener from './components/TurnAlertListener.jsx'; -import RawUserPilePanel from './components/RawUserPilePanel.jsx'; -import ChatPanel from './components/ChatPanel.jsx'; -import FullscreenPrompt from './components/FullscreenPrompt.jsx'; -import FloatingFullscreenButton from './components/FloatingFullscreenButton.jsx'; +import RoomCameraPanel from './components/RoomCameraPanel/index.jsx'; +import LogPanel from './components/LogPanel/index.jsx'; +import DriverVideoPanel from './components/DriverVideoPanel/index.jsx'; +import RightPaneTabs from './components/RightPaneTabs/index.jsx'; +import ModeGateOverlay from './components/ModeGateOverlay/index.jsx'; +import HomeAssistantControls from './components/HomeAssistantControls/index.jsx'; +import TurnAlertListener from './components/TurnAlertListener/index.jsx'; +import RawUserPilePanel from './components/RawUserPilePanel/index.jsx'; +import ChatPanel from './components/ChatPanel/index.jsx'; +import FullscreenPrompt from './components/FullscreenPrompt/index.jsx'; +import FloatingFullscreenButton from './components/FloatingFullscreenButton/index.jsx'; import { useFullscreenPrompt } from './hooks/useFullscreenPrompt.js'; import { useSettingsNamespace } from './settings/index.js'; -import HelpOverlay from './components/HelpOverlay.jsx'; -import HelpPanel from './components/HelpPanel.jsx'; -import SettingsPanel from './components/SettingsPanel.jsx'; -import Tabs, { Tab, TabList, TabPanel, TabPanels } from './components/Tabs.jsx'; +import HelpOverlay from './components/HelpOverlay/index.jsx'; +import HelpPanel from './components/HelpPanel/index.jsx'; +import SettingsPanel from './components/SettingsPanel/index.jsx'; +import Tabs, { Tab, TabList, TabPanel, TabPanels } from './components/Tabs/index.jsx'; import useDefaultNickname from './hooks/useDefaultNickname.js'; import useUserIdentitySync from './hooks/useUserIdentitySync.js'; -import CommunityGoalBanner from './components/CommunityGoalBanner.jsx'; -import RoverQueuesPanel from './components/RoverQueuesPanel.jsx'; -import VipPanel from './components/VipPanel.jsx'; +import CommunityGoalBanner from './components/CommunityGoalBanner/index.jsx'; +import RoverQueuesPanel from './components/RoverQueuesPanel/index.jsx'; +import VipPanel from './components/VipPanel/index.jsx'; import { useSessionSelector } from './context/SessionContext.jsx'; -import ButtonBoxPanel from './components/ButtonBoxPanel.jsx'; -import RewardRunOverlay from './components/RewardRunOverlay.jsx'; +import ButtonBoxPanel from './components/ButtonBoxPanel/index.jsx'; +import RewardRunOverlay from './components/RewardRunOverlay/index.jsx'; function useLayoutMode() { const [mode, setMode] = useState(() => { diff --git a/webui/src/components/AdminPanel/AdminPanelContent.jsx b/webui/src/components/AdminPanel/AdminPanelContent.jsx index c8228a9f..840b8ad1 100644 --- a/webui/src/components/AdminPanel/AdminPanelContent.jsx +++ b/webui/src/components/AdminPanel/AdminPanelContent.jsx @@ -1,7 +1,7 @@ // Admin control panel main composition and action handlers. import { useEffect, useMemo, useState } from 'react'; import { useSession } from '../../context/SessionContext.jsx'; -import RoverRoster from '../RoverRoster.jsx'; +import RoverRoster from '../RoverRoster/index.jsx'; import LlmCommentaryPanel from './LlmCommentaryPanel.jsx'; import ReplaySnapshotHealth from './ReplaySnapshotHealth.jsx'; import AdminIpLogPanel from './AdminIpLogPanel.jsx'; diff --git a/webui/src/components/AdminPanel/LlmCommentaryPanel.jsx b/webui/src/components/AdminPanel/LlmCommentaryPanel.jsx index e8b4742e..2aa4ccf6 100644 --- a/webui/src/components/AdminPanel/LlmCommentaryPanel.jsx +++ b/webui/src/components/AdminPanel/LlmCommentaryPanel.jsx @@ -1,6 +1,6 @@ // Admin LLM commentary monitor panel. import { useState } from 'react'; -import ChatMessageRow from '../ChatMessageRow.jsx'; +import ChatMessageRow from '../ChatMessageRow/index.jsx'; import { buildLlmLargeIndicatorFromState, buildLlmConversationRowsFromMessages } from './llmHelpers.js'; export default function LlmCommentaryPanel({ state, onClearHistory, clearingHistory }) { diff --git a/webui/src/components/AlertFeed.jsx b/webui/src/components/AlertFeed/index.jsx similarity index 93% rename from webui/src/components/AlertFeed.jsx rename to webui/src/components/AlertFeed/index.jsx index 5ef13b1e..15eea377 100644 --- a/webui/src/components/AlertFeed.jsx +++ b/webui/src/components/AlertFeed/index.jsx @@ -1,9 +1,9 @@ import { useEffect, useMemo, useState } from 'react'; -import { useSessionActions, useSessionSelector } from '../context/SessionContext.jsx'; -import { useSocket } from '../context/SocketContext.jsx'; -import ChatMessageRow from './ChatMessageRow.jsx'; -import ChatTypingRow from './ChatTypingRow.jsx'; -import ButtonBoxTile from './ButtonBoxTile.jsx'; +import { useSessionActions, useSessionSelector } from '../../context/SessionContext.jsx'; +import { useSocket } from '../../context/SocketContext.jsx'; +import ChatMessageRow from '../ChatMessageRow/index.jsx'; +import ChatTypingRow from '../ChatTypingRow/index.jsx'; +import ButtonBoxTile from '../ButtonBoxTile/index.jsx'; const LIFETIME_MS = 3000; const BUTTONBOX_LIFETIME_MS = 3500; diff --git a/webui/src/components/AuthPanel.jsx b/webui/src/components/AuthPanel/index.jsx similarity index 95% rename from webui/src/components/AuthPanel.jsx rename to webui/src/components/AuthPanel/index.jsx index f91e3336..02094a7f 100644 --- a/webui/src/components/AuthPanel.jsx +++ b/webui/src/components/AuthPanel/index.jsx @@ -1,5 +1,5 @@ import { useState } from 'react'; -import { useSessionActions } from '../context/SessionContext.jsx'; +import { useSessionActions } from '../../context/SessionContext.jsx'; export default function AuthPanel() { const { login, setRole } = useSessionActions(); diff --git a/webui/src/components/BatteryBar.jsx b/webui/src/components/BatteryBar/index.jsx similarity index 98% rename from webui/src/components/BatteryBar.jsx rename to webui/src/components/BatteryBar/index.jsx index 0825da0a..45bb2ea8 100644 --- a/webui/src/components/BatteryBar.jsx +++ b/webui/src/components/BatteryBar/index.jsx @@ -1,4 +1,4 @@ -import { WARN_DISPLAY_PERCENT } from '../lib/battery.js'; +import { WARN_DISPLAY_PERCENT } from '../../lib/battery.js'; const WARN_FLASH_MS = 1600; const URGENT_FLASH_MS = 800; diff --git a/webui/src/components/ButtonBoxPanel.jsx b/webui/src/components/ButtonBoxPanel/index.jsx similarity index 94% rename from webui/src/components/ButtonBoxPanel.jsx rename to webui/src/components/ButtonBoxPanel/index.jsx index 98e19ac2..0ba493e1 100644 --- a/webui/src/components/ButtonBoxPanel.jsx +++ b/webui/src/components/ButtonBoxPanel/index.jsx @@ -1,9 +1,9 @@ import { useEffect, useMemo, useRef, useState } from 'react'; -import { useSession } from '../context/SessionContext.jsx'; -import { useSocket } from '../context/SocketContext.jsx'; -import { useSettingsNamespace } from '../settings/index.js'; -import { AUDIO_SETTINGS_DEFAULTS } from '../settings/namespaces.js'; -import ButtonBoxTile from './ButtonBoxTile.jsx'; +import { useSession } from '../../context/SessionContext.jsx'; +import { useSocket } from '../../context/SocketContext.jsx'; +import { useSettingsNamespace } from '../../settings/index.js'; +import { AUDIO_SETTINGS_DEFAULTS } from '../../settings/namespaces.js'; +import ButtonBoxTile from '../ButtonBoxTile/index.jsx'; const FLASH_MS = 420; const REWARD_FLASH_MS = 1200; diff --git a/webui/src/components/ButtonBoxTile.jsx b/webui/src/components/ButtonBoxTile/index.jsx similarity index 100% rename from webui/src/components/ButtonBoxTile.jsx rename to webui/src/components/ButtonBoxTile/index.jsx diff --git a/webui/src/components/CameraServoPanel.jsx b/webui/src/components/CameraServoPanel.jsx deleted file mode 100644 index 3fbe70a2..00000000 --- a/webui/src/components/CameraServoPanel.jsx +++ /dev/null @@ -1,92 +0,0 @@ -import { useControlSystem } from '../controls/index.js'; -import { formatKeyLabel } from '../controls/keymapUtils.js'; -import NightVisionControl from './NightVisionControl.jsx'; -import HornControl from './HornControl.jsx'; -import CameraTiltControl from './CameraTiltControl.jsx'; - -const SLIDER_THROTTLE_MS = 150; - -export default function CameraServoPanel() { - const { - state: { roverId, camera, keymap, horn }, - pipeline, - actions: { setServoAngle, setNightVision, startHorn, stopHorn }, - } = useControlSystem(); - const config = camera?.config; - const enabled = Boolean(roverId && camera?.enabled && config); - const nightVisionAvailable = Boolean(roverId && pipeline?.nightVision); - const nightVisionState = pipeline?.nightVisionState; - const hornAvailable = Boolean(roverId && pipeline?.horn); - const nightVisionKey = formatKeyLabel(keymap?.nightVisionToggle?.[0]); - const hornKey = formatKeyLabel(keymap?.hornHonk?.[0]); - const hornBlocked = horn?.overheated; - const min = typeof config?.minAngle === 'number' ? config.minAngle : -30; - const max = typeof config?.maxAngle === 'number' ? config.maxAngle : 30; - const value = - typeof camera?.angle === 'number' - ? camera.angle - : typeof config?.homeAngle === 'number' - ? config.homeAngle - : (min + max) / 2; - - if (!enabled && !nightVisionAvailable && !hornAvailable) return null; - - const handleNightVisionToggle = (nextOn) => { - if (!nightVisionAvailable) return; - setNightVision(nextOn); - }; - - return ( -
- {nightVisionAvailable && ( - - )} - {hornAvailable && ( - - )} - {enabled && ( - - )} - {/*
- - - -
*/} - {/*

- Step: {formatDegrees(step)} ยท Pin GPIO {config?.pin} -

*/} -
- ); -} diff --git a/webui/src/components/CameraTiltControl.jsx b/webui/src/components/CameraTiltControl/index.jsx similarity index 100% rename from webui/src/components/CameraTiltControl.jsx rename to webui/src/components/CameraTiltControl/index.jsx diff --git a/webui/src/components/ChatMessageRow.jsx b/webui/src/components/ChatMessageRow/index.jsx similarity index 98% rename from webui/src/components/ChatMessageRow.jsx rename to webui/src/components/ChatMessageRow/index.jsx index e381b4cc..c0e252f5 100644 --- a/webui/src/components/ChatMessageRow.jsx +++ b/webui/src/components/ChatMessageRow/index.jsx @@ -1,5 +1,5 @@ import { FaDiscord } from 'react-icons/fa'; -import { roverBadgeStyle } from '../lib/roverColor.js'; +import { roverBadgeStyle } from '../../lib/roverColor.js'; function roleColors(role) { switch (role) { diff --git a/webui/src/components/ChatPanel.jsx b/webui/src/components/ChatPanel/index.jsx similarity index 96% rename from webui/src/components/ChatPanel.jsx rename to webui/src/components/ChatPanel/index.jsx index 9adfaada..3f599030 100644 --- a/webui/src/components/ChatPanel.jsx +++ b/webui/src/components/ChatPanel/index.jsx @@ -1,9 +1,9 @@ import { useEffect, useMemo, useRef, useState } from 'react'; -import { useChat } from '../context/ChatContext.jsx'; -import { useSessionSelector } from '../context/SessionContext.jsx'; -import { useSettingsNamespace } from '../settings/index.js'; -import ChatMessageRow from './ChatMessageRow.jsx'; -import ChatTypingRow from './ChatTypingRow.jsx'; +import { useChat } from '../../context/ChatContext.jsx'; +import { useSessionSelector } from '../../context/SessionContext.jsx'; +import { useSettingsNamespace } from '../../settings/index.js'; +import ChatMessageRow from '../ChatMessageRow/index.jsx'; +import ChatTypingRow from '../ChatTypingRow/index.jsx'; const FLITE_VOICES = ['kal', 'rms', 'slt', 'ksp', 'bdl']; const ESPEAK_PITCHES = Array.from({ length: 10 }, (_, idx) => idx * 10); diff --git a/webui/src/components/ChatTypingRow.jsx b/webui/src/components/ChatTypingRow/index.jsx similarity index 79% rename from webui/src/components/ChatTypingRow.jsx rename to webui/src/components/ChatTypingRow/index.jsx index 7674877b..6c5764a6 100644 --- a/webui/src/components/ChatTypingRow.jsx +++ b/webui/src/components/ChatTypingRow/index.jsx @@ -1,4 +1,4 @@ -import { ChatIdentity, chatRowClass } from './ChatMessageRow.jsx'; +import { ChatIdentity, chatRowClass } from '../ChatMessageRow/index.jsx'; export default function ChatTypingRow({ message }) { return ( diff --git a/webui/src/components/CommunityGoalBanner.jsx b/webui/src/components/CommunityGoalBanner/index.jsx similarity index 98% rename from webui/src/components/CommunityGoalBanner.jsx rename to webui/src/components/CommunityGoalBanner/index.jsx index b1406cb9..dd53fb08 100644 --- a/webui/src/components/CommunityGoalBanner.jsx +++ b/webui/src/components/CommunityGoalBanner/index.jsx @@ -1,5 +1,5 @@ import { useEffect, useMemo, useRef, useState } from 'react'; -import { useSession } from '../context/SessionContext.jsx'; +import { useSession } from '../../context/SessionContext.jsx'; const MOBILE_DISMISS_MS = 10000; const MAX_FONT_PX = 28; diff --git a/webui/src/components/ControlSummary.jsx b/webui/src/components/ControlSummary.jsx deleted file mode 100644 index 3a8d7603..00000000 --- a/webui/src/components/ControlSummary.jsx +++ /dev/null @@ -1,134 +0,0 @@ -import { useMemo, useState } from 'react'; -import { useSession } from '../context/SessionContext.jsx'; -import { useTelemetryFrame } from '../context/TelemetryContext.jsx'; -import { useControlSystem } from '../controls/index.js'; -import { formatKeyLabel } from '../controls/keymapUtils.js'; -import TopDownMap from './TopDownMap/index.jsx'; -import RoverRoster from './RoverRoster.jsx'; -import DriveDockAction, { useDriveDockState } from './DriveDockAction/index.jsx'; -import NightVisionControl from './NightVisionControl.jsx'; -import HornControl from './HornControl.jsx'; -import CameraTiltControl from './CameraTiltControl.jsx'; - -export function RoverRosterPanel({ title = 'Rovers' }) { - const { session, requestControl } = useSession(); - const [pending, setPending] = useState({}); - - const canRequest = useMemo(() => session?.role && session.role !== 'spectator', [session?.role]); - - async function handleRequest(targetRoverId) { - if (!targetRoverId) return; - setPending((prev) => ({ ...prev, [targetRoverId]: true })); - try { - await requestControl(targetRoverId); - } catch (err) { - alert(err.message); - } finally { - setPending((prev) => ({ ...prev, [targetRoverId]: false })); - } - } - - return ( - - canRequest ? ( - - ) : null - } - /> - ); -} - -export default function ControlSummary() { - const { - state: { roverId, keymap, camera, horn }, - pipeline, - actions: { setServoAngle, setNightVision, startHorn, stopHorn }, - } = useControlSystem(); - const frame = useTelemetryFrame(roverId); - const sensors = frame?.sensors || {}; - const driveDockState = useDriveDockState(roverId); - const hideInlineControls = driveDockState.docked && !driveDockState.driving; - - const config = camera?.config; - const cameraEnabled = Boolean(roverId && camera?.enabled && config); - const nightVisionAvailable = Boolean(roverId && pipeline?.nightVision); - const nightVisionState = pipeline?.nightVisionState; - const hornAvailable = Boolean(roverId && pipeline?.horn); - const hornBlocked = horn?.overheated; - const min = typeof config?.minAngle === 'number' ? config.minAngle : -30; - const max = typeof config?.maxAngle === 'number' ? config.maxAngle : 30; - const value = - typeof camera?.angle === 'number' - ? camera.angle - : typeof config?.homeAngle === 'number' - ? config.homeAngle - : (min + max) / 2; - const nightVisionLabel = formatKeyLabel(keymap?.nightVisionToggle?.[0]); - const hornLabel = formatKeyLabel(keymap?.hornHonk?.[0]); - const upLabel = formatKeyLabel(keymap?.cameraUp?.[0]); - const downLabel = formatKeyLabel(keymap?.cameraDown?.[0]); - - return ( -
-
-
-
- -
-
-
- - {!hideInlineControls ? ( -
- {nightVisionAvailable && ( - - )} - {hornAvailable && ( - - )} - {cameraEnabled && ( - - )} -
- ) : null} -
-
-
- ); -} diff --git a/webui/src/components/DeterrenceChaos.jsx b/webui/src/components/DeterrenceChaos/index.jsx similarity index 95% rename from webui/src/components/DeterrenceChaos.jsx rename to webui/src/components/DeterrenceChaos/index.jsx index 84cc254e..c8ea5b41 100644 --- a/webui/src/components/DeterrenceChaos.jsx +++ b/webui/src/components/DeterrenceChaos/index.jsx @@ -1,5 +1,5 @@ import { useEffect } from 'react'; -import { useSessionSelector } from '../context/SessionContext.jsx'; +import { useSessionSelector } from '../../context/SessionContext.jsx'; function getRandomElement(elements) { if (!elements || !elements.length) return null; diff --git a/webui/src/components/DriverVideoPanel.jsx b/webui/src/components/DriverVideoPanel/index.jsx similarity index 93% rename from webui/src/components/DriverVideoPanel.jsx rename to webui/src/components/DriverVideoPanel/index.jsx index 65a959b2..2d2ae008 100644 --- a/webui/src/components/DriverVideoPanel.jsx +++ b/webui/src/components/DriverVideoPanel/index.jsx @@ -1,10 +1,10 @@ import { useEffect, useMemo, useRef, useState } from 'react'; -import { useSessionSelector } from '../context/SessionContext.jsx'; -import { useTelemetryFrame } from '../context/TelemetryContext.jsx'; -import { useVideoRequests } from '../hooks/useVideoRequests.js'; -import { useRoverSnapshots } from '../hooks/useRoverSnapshots.js'; -import { useControlSystem } from '../controls/index.js'; -import VideoTile from './VideoTile.jsx'; +import { useSessionSelector } from '../../context/SessionContext.jsx'; +import { useTelemetryFrame } from '../../context/TelemetryContext.jsx'; +import { useVideoRequests } from '../../hooks/useVideoRequests.js'; +import { useRoverSnapshots } from '../../hooks/useRoverSnapshots.js'; +import { useControlSystem } from '../../controls/index.js'; +import VideoTile from '../VideoTile/index.jsx'; export default function DriverVideoPanel({layoutFormat = 'desktop'}) { const session = useSessionSelector((state) => state.session); diff --git a/webui/src/components/FloatingFullscreenButton.jsx b/webui/src/components/FloatingFullscreenButton/index.jsx similarity index 100% rename from webui/src/components/FloatingFullscreenButton.jsx rename to webui/src/components/FloatingFullscreenButton/index.jsx diff --git a/webui/src/components/FullscreenPrompt.jsx b/webui/src/components/FullscreenPrompt/index.jsx similarity index 100% rename from webui/src/components/FullscreenPrompt.jsx rename to webui/src/components/FullscreenPrompt/index.jsx diff --git a/webui/src/components/HelpContentView.jsx b/webui/src/components/HelpContentView/index.jsx similarity index 98% rename from webui/src/components/HelpContentView.jsx rename to webui/src/components/HelpContentView/index.jsx index f1def223..f003dfff 100644 --- a/webui/src/components/HelpContentView.jsx +++ b/webui/src/components/HelpContentView/index.jsx @@ -1,6 +1,6 @@ import { useMemo } from 'react'; -import { formatKeyLabel } from '../controls/keymapUtils.js'; -import { getHelpContent } from '../help/content.js'; +import { formatKeyLabel } from '../../controls/keymapUtils.js'; +import { getHelpContent } from '../../help/content.js'; function KeyPill({ actionId, keymap }) { const value = keymap?.[actionId]?.[0] ?? ''; diff --git a/webui/src/components/HelpOverlay.jsx b/webui/src/components/HelpOverlay/index.jsx similarity index 92% rename from webui/src/components/HelpOverlay.jsx rename to webui/src/components/HelpOverlay/index.jsx index b4858bfb..bb170a5d 100644 --- a/webui/src/components/HelpOverlay.jsx +++ b/webui/src/components/HelpOverlay/index.jsx @@ -1,5 +1,5 @@ -import { useControlSystem } from '../controls/index.js'; -import HelpContentView from './HelpContentView.jsx'; +import { useControlSystem } from '../../controls/index.js'; +import HelpContentView from '../HelpContentView/index.jsx'; export default function HelpOverlay({ visible, layout, onClose, showOnLoad, onToggleShowOnLoad }) { if (!visible) return null; diff --git a/webui/src/components/HelpPanel.jsx b/webui/src/components/HelpPanel/index.jsx similarity index 82% rename from webui/src/components/HelpPanel.jsx rename to webui/src/components/HelpPanel/index.jsx index 3513cbd0..1c47438c 100644 --- a/webui/src/components/HelpPanel.jsx +++ b/webui/src/components/HelpPanel/index.jsx @@ -1,5 +1,5 @@ -import { useControlSystem } from '../controls/index.js'; -import HelpContentView from './HelpContentView.jsx'; +import { useControlSystem } from '../../controls/index.js'; +import HelpContentView from '../HelpContentView/index.jsx'; export default function HelpPanel({ layout, onOpenOverlay }) { const { state } = useControlSystem(); diff --git a/webui/src/components/HomeAssistantControls.jsx b/webui/src/components/HomeAssistantControls/index.jsx similarity index 98% rename from webui/src/components/HomeAssistantControls.jsx rename to webui/src/components/HomeAssistantControls/index.jsx index 23f0755e..4f9f5219 100644 --- a/webui/src/components/HomeAssistantControls.jsx +++ b/webui/src/components/HomeAssistantControls/index.jsx @@ -1,7 +1,7 @@ import { useEffect, useMemo, useRef, useState } from 'react'; -import { useSessionActions, useSessionSelector } from '../context/SessionContext.jsx'; -import { useControlSystem } from '../controls/index.js'; -import { formatKeyLabel } from '../controls/keymapUtils.js'; +import { useSessionActions, useSessionSelector } from '../../context/SessionContext.jsx'; +import { useControlSystem } from '../../controls/index.js'; +import { formatKeyLabel } from '../../controls/keymapUtils.js'; function StatusBadge({ label, tone = 'muted' }) { const styles = diff --git a/webui/src/components/HornControl.jsx b/webui/src/components/HornControl/index.jsx similarity index 98% rename from webui/src/components/HornControl.jsx rename to webui/src/components/HornControl/index.jsx index d8bf5544..68dc5588 100644 --- a/webui/src/components/HornControl.jsx +++ b/webui/src/components/HornControl/index.jsx @@ -1,6 +1,6 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; -import { useSettingsNamespace } from '../settings/index.js'; -import { HORN_SETTINGS_DEFAULTS } from '../settings/namespaces.js'; +import { useSettingsNamespace } from '../../settings/index.js'; +import { HORN_SETTINGS_DEFAULTS } from '../../settings/namespaces.js'; function clampFreq(value) { const num = Number(value); diff --git a/webui/src/components/KeymapSettings.jsx b/webui/src/components/KeymapSettings/index.jsx similarity index 96% rename from webui/src/components/KeymapSettings.jsx rename to webui/src/components/KeymapSettings/index.jsx index b6732068..73d941d1 100644 --- a/webui/src/components/KeymapSettings.jsx +++ b/webui/src/components/KeymapSettings/index.jsx @@ -1,10 +1,10 @@ import { useMemo, useState, useEffect, useCallback } from 'react'; -import { useControlSystem } from '../controls/index.js'; -import { DEFAULT_KEYMAP } from '../controls/constants.js'; -import { canonicalizeKeyInput, formatKeyLabel } from '../controls/keymapUtils.js'; -import { setKeyboardCaptureLocked } from '../controls/inputs/keyboardCaptureLock.js'; -import { useSettingsNamespace } from '../settings/index.js'; -import { INPUT_SETTINGS_DEFAULTS } from '../settings/namespaces.js'; +import { useControlSystem } from '../../controls/index.js'; +import { DEFAULT_KEYMAP } from '../../controls/constants.js'; +import { canonicalizeKeyInput, formatKeyLabel } from '../../controls/keymapUtils.js'; +import { setKeyboardCaptureLocked } from '../../controls/inputs/keyboardCaptureLock.js'; +import { useSettingsNamespace } from '../../settings/index.js'; +import { INPUT_SETTINGS_DEFAULTS } from '../../settings/namespaces.js'; const KEY_ACTIONS = [ { id: 'driveForward', label: 'Drive Forward', group: 'Driving' }, diff --git a/webui/src/components/LogPanel.jsx b/webui/src/components/LogPanel/index.jsx similarity index 93% rename from webui/src/components/LogPanel.jsx rename to webui/src/components/LogPanel/index.jsx index 1e75e89e..5a919f62 100644 --- a/webui/src/components/LogPanel.jsx +++ b/webui/src/components/LogPanel/index.jsx @@ -1,4 +1,4 @@ -import { useSessionSelector } from '../context/SessionContext.jsx'; +import { useSessionSelector } from '../../context/SessionContext.jsx'; export default function LogPanel() { const logs = useSessionSelector((state) => state.logs); diff --git a/webui/src/components/MobileControls/MobileControlsContent.jsx b/webui/src/components/MobileControls/MobileControlsContent.jsx index 1e1e61a9..ec523a8e 100644 --- a/webui/src/components/MobileControls/MobileControlsContent.jsx +++ b/webui/src/components/MobileControls/MobileControlsContent.jsx @@ -3,9 +3,9 @@ import { useCallback, useEffect, useRef } from 'react'; import { useControlSystem } from '../../controls/index.js'; import { clampUnit } from '../../controls/controlMath.js'; import DriveDockAction, { useDriveDockState } from '../DriveDockAction/index.jsx'; -import NightVisionControl from '../NightVisionControl.jsx'; -import HornControl from '../HornControl.jsx'; -import CameraTiltControl from '../CameraTiltControl.jsx'; +import NightVisionControl from '../NightVisionControl/index.jsx'; +import HornControl from '../HornControl/index.jsx'; +import CameraTiltControl from '../CameraTiltControl/index.jsx'; import FloatingJoystick from './FloatingJoystick.jsx'; import MobileAuxButton from './MobileAuxButton.jsx'; import { diff --git a/webui/src/components/ModeGateOverlay.jsx b/webui/src/components/ModeGateOverlay/index.jsx similarity index 93% rename from webui/src/components/ModeGateOverlay.jsx rename to webui/src/components/ModeGateOverlay/index.jsx index ded6978a..47d662d3 100644 --- a/webui/src/components/ModeGateOverlay.jsx +++ b/webui/src/components/ModeGateOverlay/index.jsx @@ -1,9 +1,9 @@ import { useEffect, useMemo, useState } from 'react'; -import AuthPanel from './AuthPanel.jsx'; -import { useSessionSelector } from '../context/SessionContext.jsx'; -import SocialButton from './SocialButton.jsx'; -import ChatPanel from './ChatPanel.jsx'; -import NicknameForm from './NicknameForm.jsx'; +import AuthPanel from '../AuthPanel/index.jsx'; +import { useSessionSelector } from '../../context/SessionContext.jsx'; +import SocialButton from '../SocialButton/index.jsx'; +import ChatPanel from '../ChatPanel/index.jsx'; +import NicknameForm from '../NicknameForm/index.jsx'; const PRIVILEGED_ROLES = new Set(['admin', 'lockdown', 'lockdown-admin']); const LOCKDOWN_ROLES = new Set(['lockdown', 'lockdown-admin']); diff --git a/webui/src/components/NicknameForm.jsx b/webui/src/components/NicknameForm/index.jsx similarity index 92% rename from webui/src/components/NicknameForm.jsx rename to webui/src/components/NicknameForm/index.jsx index cff3c1f7..da29508e 100644 --- a/webui/src/components/NicknameForm.jsx +++ b/webui/src/components/NicknameForm/index.jsx @@ -1,6 +1,6 @@ import { useEffect, useState } from 'react'; -import { useSession } from '../context/SessionContext.jsx'; -import { useSettingsNamespace } from '../settings/index.js'; +import { useSession } from '../../context/SessionContext.jsx'; +import { useSettingsNamespace } from '../../settings/index.js'; export default function NicknameForm({ compact = false }) { const { session, setNickname } = useSession(); diff --git a/webui/src/components/NightVisionControl.jsx b/webui/src/components/NightVisionControl/index.jsx similarity index 100% rename from webui/src/components/NightVisionControl.jsx rename to webui/src/components/NightVisionControl/index.jsx diff --git a/webui/src/components/OvercurrentLimiterPanel.jsx b/webui/src/components/OvercurrentLimiterPanel/index.jsx similarity index 95% rename from webui/src/components/OvercurrentLimiterPanel.jsx rename to webui/src/components/OvercurrentLimiterPanel/index.jsx index 505fee91..cfdb9f94 100644 --- a/webui/src/components/OvercurrentLimiterPanel.jsx +++ b/webui/src/components/OvercurrentLimiterPanel/index.jsx @@ -1,6 +1,6 @@ import { useMemo } from 'react'; -import { useControlSystem } from '../controls/index.js'; -import { OVERCURRENT_GROUPS } from '../controls/overcurrentLimiter.js'; +import { useControlSystem } from '../../controls/index.js'; +import { OVERCURRENT_GROUPS } from '../../controls/overcurrentLimiter.js'; const GROUP_LABELS = { drive: 'Drive wheels', diff --git a/webui/src/components/RawUserPilePanel.jsx b/webui/src/components/RawUserPilePanel/index.jsx similarity index 94% rename from webui/src/components/RawUserPilePanel.jsx rename to webui/src/components/RawUserPilePanel/index.jsx index aa9409ca..4f836c8f 100644 --- a/webui/src/components/RawUserPilePanel.jsx +++ b/webui/src/components/RawUserPilePanel/index.jsx @@ -1,7 +1,7 @@ import { useMemo } from 'react'; -import { useSession } from '../context/SessionContext.jsx'; -import NicknameForm from './NicknameForm.jsx'; -import SocialButtonsGrid from './SocialButtonsGrid.jsx'; +import { useSession } from '../../context/SessionContext.jsx'; +import NicknameForm from '../NicknameForm/index.jsx'; +import SocialButtonsGrid from '../SocialButtonsGrid/index.jsx'; function roleColors(role) { switch (role) { diff --git a/webui/src/components/ReplaySourcesPanel.jsx b/webui/src/components/ReplaySourcesPanel/index.jsx similarity index 97% rename from webui/src/components/ReplaySourcesPanel.jsx rename to webui/src/components/ReplaySourcesPanel/index.jsx index a3485d12..f1f27678 100644 --- a/webui/src/components/ReplaySourcesPanel.jsx +++ b/webui/src/components/ReplaySourcesPanel/index.jsx @@ -1,7 +1,7 @@ import { useEffect, useMemo, useState } from 'react'; -import { useSession } from '../context/SessionContext.jsx'; -import { useSettingsNamespace } from '../settings/index.js'; -import { roverNameChromeStyle } from '../lib/roverColor.js'; +import { useSession } from '../../context/SessionContext.jsx'; +import { useSettingsNamespace } from '../../settings/index.js'; +import { roverNameChromeStyle } from '../../lib/roverColor.js'; function normalizeSources(list = []) { return list diff --git a/webui/src/components/RewardRunOverlay.jsx b/webui/src/components/RewardRunOverlay/index.jsx similarity index 94% rename from webui/src/components/RewardRunOverlay.jsx rename to webui/src/components/RewardRunOverlay/index.jsx index 52f7656a..f3aa46ba 100644 --- a/webui/src/components/RewardRunOverlay.jsx +++ b/webui/src/components/RewardRunOverlay/index.jsx @@ -1,7 +1,7 @@ import { useEffect, useRef, useState } from 'react'; -import { useSocket } from '../context/SocketContext.jsx'; -import { useSettingsNamespace } from '../settings/index.js'; -import { AUDIO_SETTINGS_DEFAULTS } from '../settings/namespaces.js'; +import { useSocket } from '../../context/SocketContext.jsx'; +import { useSettingsNamespace } from '../../settings/index.js'; +import { AUDIO_SETTINGS_DEFAULTS } from '../../settings/namespaces.js'; const SHOW_MS = 3200; diff --git a/webui/src/components/RightPaneTabs.jsx b/webui/src/components/RightPaneTabs/index.jsx similarity index 82% rename from webui/src/components/RightPaneTabs.jsx rename to webui/src/components/RightPaneTabs/index.jsx index c3254a1e..1e567344 100644 --- a/webui/src/components/RightPaneTabs.jsx +++ b/webui/src/components/RightPaneTabs/index.jsx @@ -1,25 +1,25 @@ -import RoomCameraPanel from './RoomCameraPanel.jsx'; -import HomeAssistantControls from './HomeAssistantControls.jsx'; -import SettingsPanel from './SettingsPanel.jsx'; -import HelpPanel from './HelpPanel.jsx'; -import ChatPanel from './ChatPanel.jsx'; -import { LinkButtonsPanel, NicknameEntryPanel } from './UserListPanel.jsx'; -import ReplaySourcesPanel from './ReplaySourcesPanel.jsx'; -import Tabs, { Tab, TabList, TabPanel, TabPanels } from './Tabs.jsx'; -import TopDownMap from './TopDownMap/index.jsx'; -import DriveDockAction, { useDriveDockState } from './DriveDockAction/index.jsx'; -import { useTelemetryFrame } from '../context/TelemetryContext.jsx'; -import { useControlSystem } from '../controls/index.js'; -import RoverQueuesPanel from './RoverQueuesPanel.jsx'; -import RawUserPilePanel from './RawUserPilePanel.jsx'; -import { formatKeyLabel } from '../controls/keymapUtils.js'; -import NightVisionControl from './NightVisionControl.jsx'; -import HornControl from './HornControl.jsx'; -import CameraTiltControl from './CameraTiltControl.jsx'; -import VipPanel from './VipPanel.jsx'; -import { useSessionSelector } from '../context/SessionContext.jsx'; -import { useSettingsNamespace } from '../settings/index.js'; -import ButtonBoxPanel from './ButtonBoxPanel.jsx'; +import RoomCameraPanel from '../RoomCameraPanel/index.jsx'; +import HomeAssistantControls from '../HomeAssistantControls/index.jsx'; +import SettingsPanel from '../SettingsPanel/index.jsx'; +import HelpPanel from '../HelpPanel/index.jsx'; +import ChatPanel from '../ChatPanel/index.jsx'; +import { LinkButtonsPanel, NicknameEntryPanel } from '../UserListPanel/index.jsx'; +import ReplaySourcesPanel from '../ReplaySourcesPanel/index.jsx'; +import Tabs, { Tab, TabList, TabPanel, TabPanels } from '../Tabs/index.jsx'; +import TopDownMap from '../TopDownMap/index.jsx'; +import DriveDockAction, { useDriveDockState } from '../DriveDockAction/index.jsx'; +import { useTelemetryFrame } from '../../context/TelemetryContext.jsx'; +import { useControlSystem } from '../../controls/index.js'; +import RoverQueuesPanel from '../RoverQueuesPanel/index.jsx'; +import RawUserPilePanel from '../RawUserPilePanel/index.jsx'; +import { formatKeyLabel } from '../../controls/keymapUtils.js'; +import NightVisionControl from '../NightVisionControl/index.jsx'; +import HornControl from '../HornControl/index.jsx'; +import CameraTiltControl from '../CameraTiltControl/index.jsx'; +import VipPanel from '../VipPanel/index.jsx'; +import { useSessionSelector } from '../../context/SessionContext.jsx'; +import { useSettingsNamespace } from '../../settings/index.js'; +import ButtonBoxPanel from '../ButtonBoxPanel/index.jsx'; function TopDownMapPanel() { const { diff --git a/webui/src/components/RoomCameraFeed.jsx b/webui/src/components/RoomCameraFeed/index.jsx similarity index 100% rename from webui/src/components/RoomCameraFeed.jsx rename to webui/src/components/RoomCameraFeed/index.jsx diff --git a/webui/src/components/RoomCameraPanel.jsx b/webui/src/components/RoomCameraPanel/index.jsx similarity index 93% rename from webui/src/components/RoomCameraPanel.jsx rename to webui/src/components/RoomCameraPanel/index.jsx index 99181b86..f71ae610 100644 --- a/webui/src/components/RoomCameraPanel.jsx +++ b/webui/src/components/RoomCameraPanel/index.jsx @@ -1,8 +1,8 @@ import { useEffect, useState } from 'react'; -import { useSession } from '../context/SessionContext.jsx'; -import { useSettingsNamespace } from '../settings/index.js'; -import { useRoomCameraSnapshots } from '../hooks/useRoomCameraSnapshots.js'; -import RoomCameraFeed from './RoomCameraFeed.jsx'; +import { useSession } from '../../context/SessionContext.jsx'; +import { useSettingsNamespace } from '../../settings/index.js'; +import { useRoomCameraSnapshots } from '../../hooks/useRoomCameraSnapshots.js'; +import RoomCameraFeed from '../RoomCameraFeed/index.jsx'; function EmptyState() { return ( diff --git a/webui/src/components/RoverQueuesPanel.jsx b/webui/src/components/RoverQueuesPanel/index.jsx similarity index 98% rename from webui/src/components/RoverQueuesPanel.jsx rename to webui/src/components/RoverQueuesPanel/index.jsx index e14f1c5a..f00e1bb5 100644 --- a/webui/src/components/RoverQueuesPanel.jsx +++ b/webui/src/components/RoverQueuesPanel/index.jsx @@ -1,6 +1,6 @@ import { useEffect, useMemo, useState } from 'react'; -import { useSessionActions, useSessionSelector } from '../context/SessionContext.jsx'; -import { roverNameChromeStyle } from '../lib/roverColor.js'; +import { useSessionActions, useSessionSelector } from '../../context/SessionContext.jsx'; +import { roverNameChromeStyle } from '../../lib/roverColor.js'; function classNames(...values) { return values.filter(Boolean).join(' '); diff --git a/webui/src/components/RoverRoster.jsx b/webui/src/components/RoverRoster/index.jsx similarity index 97% rename from webui/src/components/RoverRoster.jsx rename to webui/src/components/RoverRoster/index.jsx index 2c5f486f..19cc521d 100644 --- a/webui/src/components/RoverRoster.jsx +++ b/webui/src/components/RoverRoster/index.jsx @@ -1,4 +1,4 @@ -import { roverNameChromeStyle } from '../lib/roverColor.js'; +import { roverNameChromeStyle } from '../../lib/roverColor.js'; function classNames(...values) { return values.filter(Boolean).join(' '); diff --git a/webui/src/components/SessionSnapshot.jsx b/webui/src/components/SessionSnapshot/index.jsx similarity index 88% rename from webui/src/components/SessionSnapshot.jsx rename to webui/src/components/SessionSnapshot/index.jsx index 271d9903..b25409ba 100644 --- a/webui/src/components/SessionSnapshot.jsx +++ b/webui/src/components/SessionSnapshot/index.jsx @@ -1,5 +1,5 @@ import { useMemo } from 'react'; -import { useSession } from '../context/SessionContext.jsx'; +import { useSession } from '../../context/SessionContext.jsx'; export default function SessionSnapshot() { const { session } = useSession(); diff --git a/webui/src/components/SettingsPanel.jsx b/webui/src/components/SettingsPanel/index.jsx similarity index 93% rename from webui/src/components/SettingsPanel.jsx rename to webui/src/components/SettingsPanel/index.jsx index 43df8b3c..6c6fa7c4 100644 --- a/webui/src/components/SettingsPanel.jsx +++ b/webui/src/components/SettingsPanel/index.jsx @@ -1,17 +1,17 @@ import { useMemo } from 'react'; -import { useControlSystem } from '../controls/index.js'; -import AuthPanel from './AuthPanel.jsx'; -import AdminPanel from './AdminPanel/index.jsx'; -import KeymapSettings from './KeymapSettings.jsx'; -import GamepadMappingSettings from './GamepadMappingSettings/index.jsx'; -import OvercurrentLimiterPanel from './OvercurrentLimiterPanel.jsx'; -import Tabs, { Tab, TabList, TabPanel, TabPanels } from './Tabs.jsx'; -import SessionSnapshot from './SessionSnapshot.jsx'; -import SocketLogPanel from './SocketLogPanel.jsx'; -import { useHudMapSetting } from '../hooks/useHudMapSetting.js'; -import { useSettingsNamespace } from '../settings/index.js'; -import { useSocket } from '../context/SocketContext.jsx'; -import { AUDIO_SETTINGS_DEFAULTS } from '../settings/namespaces.js'; +import { useControlSystem } from '../../controls/index.js'; +import AuthPanel from '../AuthPanel/index.jsx'; +import AdminPanel from '../AdminPanel/index.jsx'; +import KeymapSettings from '../KeymapSettings/index.jsx'; +import GamepadMappingSettings from '../GamepadMappingSettings/index.jsx'; +import OvercurrentLimiterPanel from '../OvercurrentLimiterPanel/index.jsx'; +import Tabs, { Tab, TabList, TabPanel, TabPanels } from '../Tabs/index.jsx'; +import SessionSnapshot from '../SessionSnapshot/index.jsx'; +import SocketLogPanel from '../SocketLogPanel/index.jsx'; +import { useHudMapSetting } from '../../hooks/useHudMapSetting.js'; +import { useSettingsNamespace } from '../../settings/index.js'; +import { useSocket } from '../../context/SocketContext.jsx'; +import { AUDIO_SETTINGS_DEFAULTS } from '../../settings/namespaces.js'; const manualTabs = [ { key: 'start', label: 'Start OI' }, diff --git a/webui/src/components/SocialButton.jsx b/webui/src/components/SocialButton/index.jsx similarity index 100% rename from webui/src/components/SocialButton.jsx rename to webui/src/components/SocialButton/index.jsx diff --git a/webui/src/components/SocialButtonsGrid.jsx b/webui/src/components/SocialButtonsGrid/index.jsx similarity index 91% rename from webui/src/components/SocialButtonsGrid.jsx rename to webui/src/components/SocialButtonsGrid/index.jsx index f637a7a8..1993aa27 100644 --- a/webui/src/components/SocialButtonsGrid.jsx +++ b/webui/src/components/SocialButtonsGrid/index.jsx @@ -1,5 +1,5 @@ -import { useSession } from '../context/SessionContext.jsx'; -import SocialButton from './SocialButton.jsx'; +import { useSession } from '../../context/SessionContext.jsx'; +import SocialButton from '../SocialButton/index.jsx'; function normalizeSocials(session) { const configured = Array.isArray(session?.socials) ? session.socials : null; diff --git a/webui/src/components/SocketLogPanel.jsx b/webui/src/components/SocketLogPanel/index.jsx similarity index 98% rename from webui/src/components/SocketLogPanel.jsx rename to webui/src/components/SocketLogPanel/index.jsx index 75d78a24..b4d79c59 100644 --- a/webui/src/components/SocketLogPanel.jsx +++ b/webui/src/components/SocketLogPanel/index.jsx @@ -1,5 +1,5 @@ import { useEffect, useMemo, useState } from 'react'; -import { useSocket } from '../context/SocketContext.jsx'; +import { useSocket } from '../../context/SocketContext.jsx'; const MAX_ENTRIES = 300; const MAX_PAYLOAD_LENGTH = 240; diff --git a/webui/src/components/Tabs.jsx b/webui/src/components/Tabs/index.jsx similarity index 100% rename from webui/src/components/Tabs.jsx rename to webui/src/components/Tabs/index.jsx diff --git a/webui/src/components/TelemetryPanel.jsx b/webui/src/components/TelemetryPanel/index.jsx similarity index 98% rename from webui/src/components/TelemetryPanel.jsx rename to webui/src/components/TelemetryPanel/index.jsx index 66edaba0..8a77551e 100644 --- a/webui/src/components/TelemetryPanel.jsx +++ b/webui/src/components/TelemetryPanel/index.jsx @@ -1,7 +1,7 @@ import { useMemo } from 'react'; -import { useSessionSelector } from '../context/SessionContext.jsx'; -import { useTelemetryFrame } from '../context/TelemetryContext.jsx'; -import { useDockIr } from '../hooks/useDockIr.js'; +import { useSessionSelector } from '../../context/SessionContext.jsx'; +import { useTelemetryFrame } from '../../context/TelemetryContext.jsx'; +import { useDockIr } from '../../hooks/useDockIr.js'; function formatMetric(value, fallback = '--') { if (value == null || value === '') return fallback; diff --git a/webui/src/components/TurnAlertListener.jsx b/webui/src/components/TurnAlertListener/index.jsx similarity index 89% rename from webui/src/components/TurnAlertListener.jsx rename to webui/src/components/TurnAlertListener/index.jsx index e461f565..4c9313f6 100644 --- a/webui/src/components/TurnAlertListener.jsx +++ b/webui/src/components/TurnAlertListener/index.jsx @@ -1,8 +1,8 @@ import { useEffect, useMemo, useRef } from 'react'; -import turnSound from '../assets/turn_alert.mp3'; -import { useSessionActions, useSessionSelector } from '../context/SessionContext.jsx'; -import { useSettingsNamespace } from '../settings/index.js'; -import { AUDIO_SETTINGS_DEFAULTS } from '../settings/namespaces.js'; +import turnSound from '../../assets/turn_alert.mp3'; +import { useSessionActions, useSessionSelector } from '../../context/SessionContext.jsx'; +import { useSettingsNamespace } from '../../settings/index.js'; +import { AUDIO_SETTINGS_DEFAULTS } from '../../settings/namespaces.js'; function useAudio(src, volume = 1) { const audioRef = useRef(null); diff --git a/webui/src/components/UserListPanel.jsx b/webui/src/components/UserListPanel/index.jsx similarity index 97% rename from webui/src/components/UserListPanel.jsx rename to webui/src/components/UserListPanel/index.jsx index 98356722..62537631 100644 --- a/webui/src/components/UserListPanel.jsx +++ b/webui/src/components/UserListPanel/index.jsx @@ -1,8 +1,8 @@ import { useEffect, useMemo, useState, useCallback } from 'react'; -import { useSession } from '../context/SessionContext.jsx'; -import NicknameForm from './NicknameForm.jsx'; -import SocialButtonsGrid from './SocialButtonsGrid.jsx'; -import { roverBadgeStyle, roverNameChromeStyle } from '../lib/roverColor.js'; +import { useSession } from '../../context/SessionContext.jsx'; +import NicknameForm from '../NicknameForm/index.jsx'; +import SocialButtonsGrid from '../SocialButtonsGrid/index.jsx'; +import { roverBadgeStyle, roverNameChromeStyle } from '../../lib/roverColor.js'; export function NicknameEntryPanel({ compact = false }) { return ( diff --git a/webui/src/components/VideoTile.jsx b/webui/src/components/VideoTile/index.jsx similarity index 96% rename from webui/src/components/VideoTile.jsx rename to webui/src/components/VideoTile/index.jsx index 87911f01..0a12d4ed 100644 --- a/webui/src/components/VideoTile.jsx +++ b/webui/src/components/VideoTile/index.jsx @@ -1,25 +1,25 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { WhepPlayer } from '../lib/whepPlayer.js'; -import { useHudMapSetting } from '../hooks/useHudMapSetting.js'; -import { useSessionSelector } from '../context/SessionContext.jsx'; -import { useSettingsNamespace } from '../settings/index.js'; -import { AUDIO_SETTINGS_DEFAULTS } from '../settings/namespaces.js'; -import SocialButton from './SocialButton.jsx'; -import BatteryBar from './BatteryBar.jsx'; -import { buildBatteryVisual } from '../lib/battery.js'; -import TurnCueOverlay from './VideoTile/TurnCueOverlay.jsx'; -import HudOverlay from './VideoTile/HudOverlay.jsx'; -import OvercurrentOverlay from './VideoTile/OvercurrentOverlay.jsx'; -import LowBatteryOverlay from './VideoTile/LowBatteryOverlay.jsx'; -import LightBumpBars from './VideoTile/LightBumpBars.jsx'; -import HudChatInput from './VideoTile/HudChatInput.jsx'; +import { WhepPlayer } from '../../lib/whepPlayer.js'; +import { useHudMapSetting } from '../../hooks/useHudMapSetting.js'; +import { useSessionSelector } from '../../context/SessionContext.jsx'; +import { useSettingsNamespace } from '../../settings/index.js'; +import { AUDIO_SETTINGS_DEFAULTS } from '../../settings/namespaces.js'; +import SocialButton from '../SocialButton/index.jsx'; +import BatteryBar from '../BatteryBar/index.jsx'; +import { buildBatteryVisual } from '../../lib/battery.js'; +import TurnCueOverlay from './TurnCueOverlay.jsx'; +import HudOverlay from './HudOverlay.jsx'; +import OvercurrentOverlay from './OvercurrentOverlay.jsx'; +import LowBatteryOverlay from './LowBatteryOverlay.jsx'; +import LightBumpBars from './LightBumpBars.jsx'; +import HudChatInput from './HudChatInput.jsx'; import { RESTART_DELAY_MS, UNMUTE_RETRY_MS, AUDIO_RETRY_MS, BRUSH_CURRENT_THRESHOLD_MA, DUCK_RELEASE_FADE_MS, -} from './VideoTile/constants.js'; +} from './constants.js'; export default function VideoTile({ sessionInfo, diff --git a/webui/src/components/VipPanel.jsx b/webui/src/components/VipPanel/index.jsx similarity index 88% rename from webui/src/components/VipPanel.jsx rename to webui/src/components/VipPanel/index.jsx index bd21c360..d8890df9 100644 --- a/webui/src/components/VipPanel.jsx +++ b/webui/src/components/VipPanel/index.jsx @@ -1,12 +1,12 @@ import { useMemo, useState } from 'react'; -import { useSession } from '../context/SessionContext.jsx'; -import { useSettingsNamespace } from '../settings/index.js'; -import { COOKIE_KEY_REGEX, flowWrapClass } from './vip/constants.js'; -import VipAudioUploadCard from './vip/VipAudioUploadCard/index.jsx'; -import VipVerificationCard from './vip/VipVerificationCard.jsx'; -import VipIdentityCard from './vip/VipIdentityCard.jsx'; -import VipPrivateRoverAccessCard from './vip/VipPrivateRoverAccessCard.jsx'; -import VipNeatoCard from './vip/VipNeatoCard.jsx'; +import { useSession } from '../../context/SessionContext.jsx'; +import { useSettingsNamespace } from '../../settings/index.js'; +import { COOKIE_KEY_REGEX, flowWrapClass } from '../vip/constants.js'; +import VipAudioUploadCard from '../vip/VipAudioUploadCard/index.jsx'; +import VipVerificationCard from '../vip/VipVerificationCard.jsx'; +import VipIdentityCard from '../vip/VipIdentityCard.jsx'; +import VipPrivateRoverAccessCard from '../vip/VipPrivateRoverAccessCard.jsx'; +import VipNeatoCard from '../vip/VipNeatoCard.jsx'; export default function VipPanel() { const { diff --git a/webui/src/components/vip/VipVerificationCard.jsx b/webui/src/components/vip/VipVerificationCard.jsx index 739ff7aa..cb287850 100644 --- a/webui/src/components/vip/VipVerificationCard.jsx +++ b/webui/src/components/vip/VipVerificationCard.jsx @@ -1,5 +1,5 @@ import { useState } from 'react'; -import NicknameForm from '../NicknameForm.jsx'; +import NicknameForm from '../NicknameForm/index.jsx'; import { flowWrapClass, innerFlowClass, fieldClass } from './constants.js'; export default function VipVerificationCard({ diff --git a/webui/src/main.jsx b/webui/src/main.jsx index 455cb85f..22960df7 100644 --- a/webui/src/main.jsx +++ b/webui/src/main.jsx @@ -10,7 +10,7 @@ import { ChatProvider } from './context/ChatContext.jsx' import SpectatorApp from './spectate/SpectatorApp.jsx' import MiniSummaryApp from './mini/MiniSummaryApp.jsx' import { SettingsProvider } from './settings/index.js' -import DeterrenceChaos from './components/DeterrenceChaos.jsx' +import DeterrenceChaos from './components/DeterrenceChaos/index.jsx' createRoot(document.getElementById('root')).render( diff --git a/webui/src/mini/MiniSummaryApp/MiniSummaryAppRoot.jsx b/webui/src/mini/MiniSummaryApp/MiniSummaryAppRoot.jsx index 78c2f4c4..8e4e9ad5 100644 --- a/webui/src/mini/MiniSummaryApp/MiniSummaryAppRoot.jsx +++ b/webui/src/mini/MiniSummaryApp/MiniSummaryAppRoot.jsx @@ -1,6 +1,6 @@ // Top-level mini summary app shell with hard-refresh timer and alerts. import { useEffect } from 'react'; -import AlertFeed from '../../components/AlertFeed.jsx'; +import AlertFeed from '../../components/AlertFeed/index.jsx'; import MiniSummaryContent from './MiniSummaryContent.jsx'; import { HARD_REFRESH_MS } from './constants.js'; diff --git a/webui/src/mini/MiniSummaryApp/MiniSummaryContent.jsx b/webui/src/mini/MiniSummaryApp/MiniSummaryContent.jsx index 3b6b599c..74110aee 100644 --- a/webui/src/mini/MiniSummaryApp/MiniSummaryContent.jsx +++ b/webui/src/mini/MiniSummaryApp/MiniSummaryContent.jsx @@ -6,7 +6,7 @@ import { useVideoRequests } from '../../hooks/useVideoRequests.js'; import { useRoverSnapshots } from '../../hooks/useRoverSnapshots.js'; import { useSpectatorMode } from '../../hooks/useSpectatorMode.js'; import useDefaultNickname from '../../hooks/useDefaultNickname.js'; -import VideoTile from '../../components/VideoTile.jsx'; +import VideoTile from '../../components/VideoTile/index.jsx'; import FitViewportFrame from './components/FitViewportFrame.jsx'; import InfoColumn from './components/InfoColumn.jsx'; import { ROTATE_MS } from './constants.js'; diff --git a/webui/src/mini/MiniSummaryApp/components/InfoColumn.jsx b/webui/src/mini/MiniSummaryApp/components/InfoColumn.jsx index aae33879..de7bf420 100644 --- a/webui/src/mini/MiniSummaryApp/components/InfoColumn.jsx +++ b/webui/src/mini/MiniSummaryApp/components/InfoColumn.jsx @@ -1,6 +1,6 @@ // Rover information and optional preview column for mini summary UI. -import VideoTile from '../../../components/VideoTile.jsx'; -import BatteryBar from '../../../components/BatteryBar.jsx'; +import VideoTile from '../../../components/VideoTile/index.jsx'; +import BatteryBar from '../../../components/BatteryBar/index.jsx'; import { roverNameChromeStyle } from '../../../lib/roverColor.js'; import { getBatteryVisual } from '../utils.js'; import AutoFitText from './AutoFitText.jsx'; diff --git a/webui/src/spectate/SpectatorApp/SpectatorContent.jsx b/webui/src/spectate/SpectatorApp/SpectatorContent.jsx index 59352fb5..a5645860 100644 --- a/webui/src/spectate/SpectatorApp/SpectatorContent.jsx +++ b/webui/src/spectate/SpectatorApp/SpectatorContent.jsx @@ -5,13 +5,13 @@ import { useVideoRequests } from '../../hooks/useVideoRequests.js'; import { useRoverSnapshots } from '../../hooks/useRoverSnapshots.js'; import { useSpectatorMode } from '../../hooks/useSpectatorMode.js'; import useDefaultNickname from '../../hooks/useDefaultNickname.js'; -import ChatPanel from '../../components/ChatPanel.jsx'; -import AlertFeed from '../../components/AlertFeed.jsx'; -import CommunityGoalBanner from '../../components/CommunityGoalBanner.jsx'; -import RoverQueuesPanel from '../../components/RoverQueuesPanel.jsx'; -import RawUserPilePanel from '../../components/RawUserPilePanel.jsx'; -import ButtonBoxPanel from '../../components/ButtonBoxPanel.jsx'; -import RewardRunOverlay from '../../components/RewardRunOverlay.jsx'; +import ChatPanel from '../../components/ChatPanel/index.jsx'; +import AlertFeed from '../../components/AlertFeed/index.jsx'; +import CommunityGoalBanner from '../../components/CommunityGoalBanner/index.jsx'; +import RoverQueuesPanel from '../../components/RoverQueuesPanel/index.jsx'; +import RawUserPilePanel from '../../components/RawUserPilePanel/index.jsx'; +import ButtonBoxPanel from '../../components/ButtonBoxPanel/index.jsx'; +import RewardRunOverlay from '../../components/RewardRunOverlay/index.jsx'; import usePortraitLayout from './hooks/usePortraitLayout.js'; import RoverRow from './components/RoverRow.jsx'; import SecondaryRow from './components/SecondaryRow.jsx'; diff --git a/webui/src/spectate/SpectatorApp/components/LogsRow.jsx b/webui/src/spectate/SpectatorApp/components/LogsRow.jsx index c05fd9e9..93fbc1d8 100644 --- a/webui/src/spectate/SpectatorApp/components/LogsRow.jsx +++ b/webui/src/spectate/SpectatorApp/components/LogsRow.jsx @@ -1,5 +1,5 @@ // Log panel wrapper row for spectator layout. -import LogPanel from '../../../components/LogPanel.jsx'; +import LogPanel from '../../../components/LogPanel/index.jsx'; export default function LogsRow({ className = '' }) { return ( diff --git a/webui/src/spectate/SpectatorApp/components/RoverSpectatorCard.jsx b/webui/src/spectate/SpectatorApp/components/RoverSpectatorCard.jsx index 71656cd1..ace08452 100644 --- a/webui/src/spectate/SpectatorApp/components/RoverSpectatorCard.jsx +++ b/webui/src/spectate/SpectatorApp/components/RoverSpectatorCard.jsx @@ -1,5 +1,5 @@ // Single rover spectator card containing live/snapshot tile and HUD. -import VideoTile from '../../../components/VideoTile.jsx'; +import VideoTile from '../../../components/VideoTile/index.jsx'; import { formatDriverLabel } from '../utils.js'; export default function RoverSpectatorCard({ rover, frame, sessionInfo, videoMode, snapshotFeed, audioInfo, session }) { diff --git a/webui/src/spectate/SpectatorApp/components/SecondaryRow.jsx b/webui/src/spectate/SpectatorApp/components/SecondaryRow.jsx index 58b20e1e..51c6ca37 100644 --- a/webui/src/spectate/SpectatorApp/components/SecondaryRow.jsx +++ b/webui/src/spectate/SpectatorApp/components/SecondaryRow.jsx @@ -1,5 +1,5 @@ // Secondary panel row showing room cameras. -import RoomCameraPanel from '../../../components/RoomCameraPanel.jsx'; +import RoomCameraPanel from '../../../components/RoomCameraPanel/index.jsx'; export default function SecondaryRow() { return (