@liveroom-tech/react-immersive 1.0.3 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,22 +1,22 @@
1
1
  // src/components/ModelViewer/ModelViewer.tsx
2
2
  import {
3
- useRef as useRef9,
3
+ useRef as useRef10,
4
4
  useState as useState10,
5
- useEffect as useEffect13,
5
+ useEffect as useEffect14,
6
6
  useCallback as useCallback4,
7
- useMemo as useMemo12,
7
+ useMemo as useMemo13,
8
8
  Suspense as Suspense2
9
9
  } from "react";
10
10
  import { createPortal } from "react-dom";
11
- import { useFrame as useFrame7, useThree as useThree9 } from "@react-three/fiber";
11
+ import { useFrame as useFrame8, useThree as useThree10 } from "@react-three/fiber";
12
12
  import { createXRStore } from "@react-three/xr";
13
13
  import {
14
- Box3 as Box32,
14
+ Box3 as Box33,
15
15
  PerspectiveCamera as PerspectiveCamera3,
16
16
  Raycaster,
17
- Sphere,
17
+ Sphere as Sphere2,
18
18
  Vector2 as Vector25,
19
- Vector3 as Vector36
19
+ Vector3 as Vector37
20
20
  } from "three";
21
21
 
22
22
  // #style-inject:#style-inject
@@ -42,7 +42,7 @@ function styleInject(css, { insertAt } = {}) {
42
42
  }
43
43
 
44
44
  // src/styles/ModelViewer.css
45
- styleInject('.ri-viewer--license-checking,\n.ri-viewer--license-invalid {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n background-color: #f9f9f9;\n}\n.ri-viewer {\n --ri-font-family:\n Inter,\n "Segoe UI",\n Helvetica,\n Arial,\n sans-serif;\n --ri-viewer-bg: #1c1c1d;\n --ri-viewer-text: #111827;\n --ri-panel-bg: #ffffff;\n --ri-panel-border: #e5e7eb;\n --ri-panel-title: #111827;\n --ri-panel-muted-text: #4b5563;\n --ri-panel-subtle-text: #9ca3af;\n --ri-panel-surface: #f9fafb;\n --ri-panel-surface-hover: #f3f4f6;\n --ri-panel-surface-border: #e5e7eb;\n --ri-panel-button-text: #1f2937;\n --ri-panel-accent: #2563eb;\n --ri-panel-accent-contrast: #ffffff;\n --ri-license-overlay-bg: rgba(0, 0, 0, 0.04);\n --ri-license-overlay-border: rgba(0, 0, 0, 0.08);\n --ri-license-overlay-text: #888;\n --ri-license-overlay-error-bg: rgba(192, 57, 43, 0.06);\n --ri-license-overlay-error-border: rgba(192, 57, 43, 0.2);\n --ri-license-overlay-error-text: #c0392b;\n --ri-sidepanel-font-family:\n Inter,\n "Segoe UI",\n Helvetica,\n Arial,\n sans-serif;\n --ri-sidepanel-bg: #141824;\n --ri-sidepanel-open-btn-bg: rgba(20, 20, 20, 0.82);\n --ri-sidepanel-open-btn-bg-hover: #27282b7e;\n --ri-sidepanel-border: rgba(255, 255, 255, 0.06);\n --ri-sidepanel-divider: rgba(255, 255, 255, 0.04);\n --ri-sidepanel-border-strong: rgba(255, 255, 255, 0.08);\n --ri-sidepanel-border-active: rgba(255, 255, 255, 0.18);\n --ri-sidepanel-shadow-left: 4px 0 24px rgba(0, 0, 0, 0.35);\n --ri-sidepanel-shadow-right: -4px 0 24px rgba(0, 0, 0, 0.35);\n --ri-sidepanel-text: #f1f5f9;\n --ri-sidepanel-text-emphasis: #ffffff;\n --ri-sidepanel-text-muted: #94a3b8;\n --ri-sidepanel-text-subtle: #64748b;\n --ri-sidepanel-placeholder: #475569;\n --ri-sidepanel-surface: rgba(255, 255, 255, 0.05);\n --ri-sidepanel-surface-hover: rgba(255, 255, 255, 0.1);\n --ri-sidepanel-accent: #3b82f6;\n --ri-sidepanel-accent-soft: rgba(59, 130, 246, 0.12);\n --ri-databinding-bg: #111318;\n --ri-databinding-border: rgba(255, 255, 255, 0.06);\n --ri-databinding-border-strong: rgba(255, 255, 255, 0.08);\n --ri-databinding-border-hover: rgba(255, 255, 255, 0.15);\n --ri-databinding-shadow: 4px 0 24px rgba(0, 0, 0, 0.35);\n --ri-databinding-font-family:\n "Inter",\n "Segoe UI",\n sans-serif;\n --ri-databinding-mono-font-family:\n "JetBrains Mono",\n "Fira Code",\n "Menlo",\n monospace;\n --ri-databinding-text: #f1f5f9;\n --ri-databinding-text-muted: #94a3b8;\n --ri-databinding-text-subtle: #64748b;\n --ri-databinding-text-faint: #475569;\n --ri-databinding-text-hover: #cbd5e1;\n --ri-databinding-surface: rgba(255, 255, 255, 0.03);\n --ri-databinding-surface-hover: rgba(255, 255, 255, 0.06);\n --ri-databinding-accent: #93c5fd;\n --ri-databinding-accent-glow: rgba(147, 197, 253, 0.8);\n --ri-databinding-accent-border: rgba(96, 165, 250, 0.55);\n --ri-databinding-accent-gradient:\n linear-gradient(\n \n 135deg,\n #1e3a8a 0%,\n #1d4ed8 60%,\n #2563eb 100% );\n --ri-databinding-accent-gradient-active:\n linear-gradient(\n \n 135deg,\n #1e3a8a 0%,\n #1e40af 100% );\n display: flex;\n flex-direction: row;\n width: 100%;\n height: 100%;\n min-height: 0;\n overflow: hidden;\n background: var(--ri-viewer-bg);\n position: relative;\n color: var(--ri-viewer-text);\n font-family: var(--ri-font-family);\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n text-rendering: optimizeLegibility;\n font-synthesis: none;\n}\n.ri-viewer * {\n box-sizing: border-box;\n}\n.ri-viewerLeft {\n display: flex;\n flex: 1;\n min-height: 0;\n min-width: 0;\n position: relative;\n}\n.ri-viewerRight {\n display: flex;\n flex-shrink: 0;\n min-width: 0;\n}\n.ri-panel {\n width: 280px;\n min-height: 0;\n background: var(--ri-panel-bg);\n border-right: 1px solid var(--ri-panel-border);\n display: flex;\n flex-direction: column;\n padding: 20px;\n overflow-y: auto;\n flex-shrink: 0;\n transition: margin-left 0.3s ease, transform 0.3s ease;\n}\n.ri-panel--open {\n transform: translateX(0);\n margin-left: 0;\n}\n.ri-panel--closed {\n transform: translateX(-100%);\n margin-left: -280px;\n}\n.ri-panelHeader {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n margin-bottom: 12px;\n}\n.ri-panelTitle {\n margin: 0;\n font-size: 15px;\n font-weight: 700;\n color: var(--ri-panel-title);\n line-height: 1.4;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.ri-closeButton {\n border: 0;\n background: transparent;\n color: var(--ri-panel-subtle-text);\n font-size: 22px;\n line-height: 1;\n padding: 0;\n cursor: pointer;\n flex-shrink: 0;\n transition: color 0.2s ease;\n}\n.ri-closeButton:hover {\n color: var(--ri-panel-muted-text);\n}\n.ri-json {\n margin: 0 0 16px;\n padding: 12px;\n background: var(--ri-panel-surface);\n border: 1px solid var(--ri-panel-surface-border);\n border-radius: 12px;\n font-size: 12px;\n line-height: 1.55;\n color: var(--ri-panel-button-text);\n overflow-x: auto;\n white-space: pre-wrap;\n word-break: break-word;\n}\n.ri-divider {\n border-top: 1px solid var(--ri-panel-border);\n margin-bottom: 12px;\n}\n.ri-sectionLabel {\n margin-bottom: 8px;\n font-size: 11px;\n font-weight: 700;\n color: var(--ri-panel-subtle-text);\n text-transform: uppercase;\n letter-spacing: 0.16em;\n}\n.ri-actionList {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n.ri-actionButton {\n width: 100%;\n border-radius: 8px;\n border: 1px solid var(--ri-panel-surface-border);\n background: var(--ri-panel-surface);\n color: var(--ri-panel-button-text);\n padding: 10px 12px;\n text-align: left;\n font-size: 14px;\n line-height: 1.4;\n cursor: pointer;\n transition:\n background-color 0.2s ease,\n border-color 0.2s ease,\n color 0.2s ease;\n}\n.ri-actionButton:hover {\n background: var(--ri-panel-surface-hover);\n border-color: var(--ri-panel-border);\n}\n.ri-actionButton--active {\n background: var(--ri-panel-accent);\n border-color: var(--ri-panel-accent);\n color: var(--ri-panel-accent-contrast);\n}\n.ri-actionButton--active:hover {\n background: var(--ri-panel-accent);\n border-color: var(--ri-panel-accent);\n}\n.ri-canvasArea {\n flex: 1 1 auto;\n position: relative;\n min-width: 0;\n}\n.ri-canvasArea--measuring {\n cursor: crosshair;\n}\n.ri-canvasArea canvas {\n opacity: 1;\n transition: opacity 0.18s ease;\n}\n.ri-canvasArea--positioning canvas {\n opacity: 0;\n transition: none;\n}\n.ri-bindingsMismatchBanner {\n position: absolute;\n top: 12px;\n left: 50%;\n transform: translateX(-50%);\n z-index: 20;\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 7px 12px 7px 10px;\n border-radius: 8px;\n background: rgba(30, 20, 0, 0.88);\n border: 1px solid rgba(251, 191, 36, 0.35);\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n color: rgb(251, 191, 36);\n font-size: 12px;\n font-weight: 500;\n line-height: 1.4;\n white-space: nowrap;\n pointer-events: auto;\n max-width: calc(100% - 32px);\n white-space: normal;\n text-align: center;\n}\n.ri-bindingsMismatchBanner__icon {\n flex-shrink: 0;\n}\n.ri-bindingsMismatchBanner__dismiss {\n flex-shrink: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n margin-left: 4px;\n padding: 2px;\n background: none;\n border: none;\n border-radius: 4px;\n color: rgba(251, 191, 36, 0.7);\n cursor: pointer;\n transition: color 0.15s ease, background 0.15s ease;\n}\n.ri-bindingsMismatchBanner__dismiss:hover {\n color: rgb(251, 191, 36);\n background: rgba(251, 191, 36, 0.12);\n}\n.ri-measureTools {\n position: absolute;\n top: 16px;\n left: 16px;\n z-index: 10;\n display: flex;\n gap: 8px;\n align-items: center;\n}\n.ri-measureButton {\n width: 34px;\n height: 34px;\n padding: 0;\n background: rgba(20, 20, 20, 0.82);\n border: 1px solid rgba(255, 255, 255, 0.12);\n color: rgba(255, 255, 255, 0.75);\n border-radius: 8px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n touch-action: manipulation;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n transition:\n background 0.15s ease,\n color 0.15s ease,\n border-color 0.15s ease;\n}\n.ri-measureButton:hover {\n background: rgba(40, 40, 40, 0.92);\n color: #fff;\n border-color: rgba(255, 255, 255, 0.25);\n}\n.ri-measureButton:active {\n background: #38dbf0;\n color: #06343c;\n border-color: #38dbf0;\n}\n.ri-measureButton--active {\n background: #22d3ee;\n border-color: #22d3ee;\n color: #06343c;\n}\n.ri-measureButton--active:hover {\n background: #38dbf0;\n color: #06343c;\n}\n.ri-measureHint {\n padding: 6px 10px;\n border-radius: 8px;\n font-size: 12px;\n line-height: 1;\n color: #fff;\n background: rgba(20, 20, 20, 0.82);\n border: 1px solid rgba(255, 255, 255, 0.12);\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n white-space: nowrap;\n}\n.ri-explodeTools {\n position: absolute;\n top: 16px;\n left: 16px;\n z-index: 10;\n display: flex;\n gap: 8px;\n align-items: center;\n}\n.ri-explodeTools--stacked {\n top: 58px;\n}\n.ri-explodeSlider {\n display: flex;\n align-items: center;\n gap: 8px;\n height: 34px;\n padding: 0 12px;\n background: rgba(20, 20, 20, 0.82);\n border: 1px solid rgba(255, 255, 255, 0.12);\n border-radius: 8px;\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n}\n.ri-explodeSlider input[type=range] {\n width: 120px;\n accent-color: #22d3ee;\n cursor: pointer;\n}\n.ri-measureDot {\n width: 10px;\n height: 10px;\n border-radius: 50%;\n background: #22d3ee;\n border: 2px solid #fff;\n box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.4);\n pointer-events: none;\n}\n.ri-measureLabel {\n padding: 3px 7px;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 600;\n line-height: 1;\n white-space: nowrap;\n color: #06343c;\n background: #22d3ee;\n box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);\n pointer-events: none;\n user-select: none;\n}\n.ri-xrTools {\n position: absolute;\n top: 16px;\n left: 50%;\n transform: translateX(-50%);\n z-index: 10;\n display: flex;\n gap: 8px;\n align-items: center;\n}\n.ri-xrButton {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n height: 34px;\n padding: 0 10px;\n font-size: 13px;\n font-weight: 500;\n color: #fff;\n background: rgba(20, 20, 20, 0.82);\n border: 1px solid rgba(255, 255, 255, 0.16);\n border-radius: 10px;\n cursor: pointer;\n touch-action: manipulation;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n transition: background 0.15s ease, border-color 0.15s ease;\n}\n.ri-xrButton:hover {\n background: rgba(40, 40, 40, 0.92);\n border-color: rgba(255, 255, 255, 0.3);\n}\n.ri-arHintOverlay {\n position: absolute;\n top: 18px;\n left: 50%;\n transform: translateX(-50%);\n z-index: 10;\n width: min(calc(100% - 32px), 360px);\n pointer-events: none;\n}\n.ri-arHint {\n margin: 0 auto;\n width: fit-content;\n max-width: 100%;\n padding: 6px 12px;\n font-size: 11px;\n font-weight: 500;\n line-height: 1.2;\n text-align: center;\n color: rgba(255, 255, 255, 0.92);\n background: rgba(20, 20, 20, 0.58);\n border: 1px solid rgba(255, 255, 255, 0.12);\n border-radius: 999px;\n user-select: none;\n backdrop-filter: blur(4px);\n -webkit-backdrop-filter: blur(4px);\n}\n.ri-arOverlay {\n position: absolute;\n bottom: 24px;\n left: 50%;\n transform: translateX(-50%);\n z-index: 10;\n width: min(calc(100% - 32px), 420px);\n pointer-events: none;\n}\n.ri-arControls {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 10px;\n}\n.ri-arStatus,\n.ri-arActionButton {\n max-width: 100%;\n padding: 8px 16px;\n font-size: 13px;\n font-weight: 600;\n line-height: 1.3;\n text-align: center;\n white-space: nowrap;\n color: #fff;\n background: rgba(20, 20, 20, 0.82);\n border: 1px solid rgba(255, 255, 255, 0.16);\n border-radius: 19px;\n user-select: none;\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n}\n.ri-arStatus {\n pointer-events: none;\n}\n.ri-arActionButton {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n cursor: pointer;\n pointer-events: auto;\n touch-action: manipulation;\n -webkit-tap-highlight-color: transparent;\n transition:\n background 0.15s ease,\n border-color 0.15s ease,\n color 0.15s ease,\n transform 0.15s ease;\n}\n.ri-arActionButton svg {\n flex-shrink: 0;\n}\n.ri-arActionButton:hover {\n background: rgba(40, 40, 40, 0.92);\n border-color: rgba(255, 255, 255, 0.3);\n}\n.ri-arActionButton:active {\n transform: scale(0.98);\n}\n.ri-arActionButton--primary {\n color: #06343c;\n background: #22d3ee;\n border-color: transparent;\n box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);\n}\n.ri-arActionButton--primary:hover {\n background: #67e8f9;\n border-color: transparent;\n}\n.ri-arActionButton.ri-arActionButton--active,\n.ri-arActionButton[aria-pressed=true],\n.ri-arActionButton.ri-arActionButton--active:hover,\n.ri-arActionButton[aria-pressed=true]:hover,\n.ri-arActionButton.ri-arActionButton--active:active,\n.ri-arActionButton[aria-pressed=true]:active {\n color: #06343c;\n background: #22d3ee;\n border-color: transparent;\n box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);\n}\n.ri-hiddenInput {\n display: none;\n}\n.ri-popover {\n position: absolute;\n z-index: 50;\n background: #ffffff;\n border: 1px solid #e5e7eb;\n border-radius: 16px;\n box-shadow: 0 24px 48px rgba(15, 23, 42, 0.18);\n color: #111827;\n}\n.ri-popoverHeader {\n padding: 12px 16px;\n border-bottom: 1px solid #f3f4f6;\n}\n.ri-popoverLabel {\n margin: 0 0 8px;\n font-size: 11px;\n font-weight: 700;\n color: #6b7280;\n text-transform: uppercase;\n letter-spacing: 0.16em;\n}\n.ri-inlineRow {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n margin-top: 8px;\n}\n.ri-colorCode {\n font-size: 12px;\n color: #9ca3af;\n font-family:\n ui-monospace,\n SFMono-Regular,\n Menlo,\n Monaco,\n Consolas,\n monospace;\n text-transform: uppercase;\n}\n.ri-secondaryButton {\n border: 1px solid #e5e7eb;\n background: #f3f4f6;\n color: #374151;\n border-radius: 8px;\n padding: 6px 10px;\n font-size: 12px;\n cursor: pointer;\n transition: background-color 0.2s ease;\n}\n.ri-secondaryButton:hover {\n background: #e5e7eb;\n}\n.ri-materialBody {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 12px;\n}\n.ri-previewFrame {\n position: relative;\n height: 112px;\n border-radius: 12px;\n overflow: hidden;\n border: 1px solid #e5e7eb;\n}\n.ri-previewImage {\n width: 100%;\n height: 100%;\n object-fit: cover;\n display: block;\n}\n.ri-previewCaption {\n position: absolute;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.4);\n padding: 4px 8px;\n color: #ffffff;\n font-size: 10px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ri-emptyState {\n height: 112px;\n border-radius: 12px;\n border: 2px dashed #e5e7eb;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 4px;\n color: #d1d5db;\n text-align: center;\n}\n.ri-uploadButton {\n width: 100%;\n border: 0;\n border-radius: 10px;\n background: #111827;\n color: #ffffff;\n padding: 10px 12px;\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n transition: background-color 0.2s ease;\n}\n.ri-uploadButton:hover {\n background: #374151;\n}\n.ri-removeButton {\n width: 100%;\n border-radius: 10px;\n border: 1px solid #e5e7eb;\n background: #ffffff;\n color: #6b7280;\n padding: 8px 12px;\n font-size: 12px;\n cursor: pointer;\n transition: background-color 0.2s ease;\n}\n.ri-removeButton:hover {\n background: #f9fafb;\n}\n.ri-formatHint {\n margin: 0;\n text-align: center;\n font-size: 10px;\n color: #d1d5db;\n}\n.ri-buttonContainer {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 8px 12px;\n gap: 8px;\n position: absolute;\n bottom: 12px;\n left: 12px;\n right: 12px;\n z-index: 10;\n}\n.ri-tourControls {\n display: flex;\n gap: 6px;\n align-items: center;\n flex-shrink: 0;\n}\n.ri-rightControls {\n display: flex;\n gap: 6px;\n align-items: center;\n flex-shrink: 0;\n margin-left: auto;\n}\n.ri-rightControls:only-child {\n margin-left: auto;\n}\n.ri-navButtonSingle,\n.ri-tourButton,\n.ri-resetButton,\n.ri-downloadButton {\n height: 34px;\n background: rgba(20, 20, 20, 0.82);\n border: 1px solid rgba(255, 255, 255, 0.12);\n color: rgba(255, 255, 255, 0.75);\n border-radius: 8px;\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n cursor: pointer;\n transition:\n background 0.15s ease,\n color 0.15s ease,\n border-color 0.15s ease,\n transform 0.15s ease;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n}\n.ri-navButtonSingle:hover:not(:disabled),\n.ri-tourButton:hover:not(:disabled),\n.ri-resetButton:hover:not(:disabled) {\n background: rgba(40, 40, 40, 0.92);\n color: #fff;\n border-color: rgba(255, 255, 255, 0.25);\n}\n.ri-navButtonSingle:active:not(:disabled),\n.ri-tourButton:active:not(:disabled),\n.ri-resetButton:active:not(:disabled) {\n background: #38dbf0;\n color: #06343c;\n border-color: #38dbf0;\n}\n.ri-navButtonSingle:disabled,\n.ri-tourButton:disabled,\n.ri-resetButton:disabled {\n opacity: 0.45;\n cursor: not-allowed;\n}\n.ri-navButtonSingle {\n padding: 0 16px;\n font-size: 13px;\n font-weight: 500;\n display: flex;\n align-items: center;\n gap: 8px;\n white-space: nowrap;\n}\n.ri-tourButton {\n padding: 0 12px;\n font-size: 13px;\n font-weight: 500;\n display: flex;\n align-items: center;\n gap: 4px;\n white-space: nowrap;\n}\n.ri-tourButton svg {\n width: 15px;\n height: 15px;\n flex-shrink: 0;\n}\n.ri-tourButton--stop {\n border-color: rgba(255, 68, 68, 0.4);\n color: #ff6b6b;\n}\n.ri-tourButton--stop:hover:not(:disabled) {\n background: rgba(255, 68, 68, 0.15);\n border-color: rgba(255, 68, 68, 0.6);\n color: #ff6b6b;\n}\n.ri-tourButton--stop:active:not(:disabled) {\n background: #ff4444;\n color: #fff;\n border-color: #ff4444;\n}\n.ri-tourButton--next {\n border-color: rgba(56, 219, 240, 0.3);\n color: #38dbf0;\n}\n.ri-tourButton--next:hover:not(:disabled) {\n background: rgba(56, 219, 240, 0.12);\n border-color: rgba(56, 219, 240, 0.5);\n color: #38dbf0;\n}\n.ri-tourButton--next:active:not(:disabled) {\n background: #38dbf0;\n color: #06343c;\n border-color: #38dbf0;\n}\n.ri-resetButton {\n width: 34px;\n padding: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n.ri-resetButton svg {\n width: 15px;\n height: 15px;\n pointer-events: none;\n flex-shrink: 0;\n}\n.ri-resetButton:focus-visible {\n outline: 2px solid rgba(255, 255, 255, 0.5);\n outline-offset: 2px;\n}\n.ri-downloadWrapper {\n position: relative;\n display: inline-flex;\n flex-direction: column;\n align-items: stretch;\n}\n.ri-downloadButton {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n padding: 0 16px;\n font-size: 13px;\n font-weight: 500;\n white-space: nowrap;\n}\n.ri-downloadButton:hover:not(:disabled) {\n background: rgba(0, 0, 0, 0.72);\n border-color: rgba(255, 255, 255, 0.3);\n color: #fff;\n}\n.ri-downloadButton:active:not(:disabled) {\n background: #38dbf0;\n color: #06343c;\n border-color: #38dbf0;\n}\n.ri-downloadButton:disabled {\n opacity: 0.45;\n cursor: not-allowed;\n}\n.ri-downloadButton__spinner {\n display: inline-block;\n width: 13px;\n height: 13px;\n border: 2px solid rgba(255, 255, 255, 0.3);\n border-top-color: #fff;\n border-radius: 50%;\n animation: ri-spin 0.7s linear infinite;\n flex-shrink: 0;\n}\n.ri-downloadMenu {\n position: absolute;\n bottom: calc(100% + 6px);\n right: 0;\n min-width: 168px;\n background: rgba(20, 20, 20, 0.92);\n border: 1px solid rgba(255, 255, 255, 0.14);\n border-radius: 10px;\n overflow: hidden;\n z-index: 100;\n backdrop-filter: blur(10px);\n -webkit-backdrop-filter: blur(10px);\n animation: ri-menuReveal 0.12s ease;\n}\n@keyframes ri-menuReveal {\n from {\n opacity: 0;\n transform: translateY(4px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n.ri-downloadMenuItem {\n display: flex;\n align-items: center;\n gap: 9px;\n width: 100%;\n padding: 10px 14px;\n font-size: 13px;\n font-weight: 400;\n color: #fff;\n background: transparent;\n border: none;\n cursor: pointer;\n text-align: left;\n transition: background 0.1s ease;\n}\n.ri-downloadMenuItem:hover:not(:disabled) {\n background: rgba(255, 255, 255, 0.1);\n}\n.ri-downloadMenuItem:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n.ri-downloadMenuDivider {\n height: 1px;\n background: rgba(255, 255, 255, 0.1);\n margin: 0 10px;\n}\n@keyframes ri-spin {\n to {\n transform: rotate(360deg);\n }\n}\n.ri-annotationMarker {\n position: relative;\n width: 36px;\n height: 36px;\n border-radius: 50%;\n background: rgba(20, 20, 20, 0.82);\n border: 2px solid rgba(255, 255, 255, 0.9);\n color: #fff;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 14px;\n font-weight: 700;\n cursor: pointer;\n z-index: 20;\n user-select: none;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.45);\n pointer-events: auto;\n transition: opacity 0.3s ease, transform 0.3s ease;\n}\n.ri-annotationMarker:hover {\n transform: scale(1.08);\n box-shadow: 0 12px 28px rgba(15, 23, 42, 0.18);\n background: rgba(40, 40, 40, 0.92);\n}\n.ri-annotationPopup {\n position: absolute;\n width: min(270px, calc(100vw - 32px));\n background: rgba(20, 20, 20, 0.85);\n border: 1px solid rgba(255, 255, 255, 0.12);\n border-radius: 16px;\n padding: 16px;\n z-index: 30;\n display: flex;\n flex-direction: column;\n gap: 12px;\n box-shadow: 0 24px 48px rgba(15, 23, 42, 0.18);\n backdrop-filter: blur(10px);\n -webkit-backdrop-filter: blur(10px);\n}\n.ri-annotationInput,\n.ri-annotationTextarea {\n width: 100%;\n border: 1px solid rgba(255, 255, 255, 0.12);\n background: rgba(255, 255, 255, 0.05);\n color: #e5e7eb;\n border-radius: 10px;\n padding: 10px 12px;\n outline: none;\n font-family: inherit;\n transition: border-color 0.2s ease, box-shadow 0.2s ease;\n}\n.ri-annotationInput {\n font-size: 14px;\n font-weight: 500;\n}\n.ri-annotationTextarea {\n font-size: 13px;\n resize: vertical;\n min-height: 110px;\n}\n.ri-annotationInput:focus,\n.ri-annotationTextarea:focus {\n border-color: #38dbf0;\n box-shadow: 0 0 0 3px rgba(56, 219, 240, 0.12);\n}\n.ri-annotationActions {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n.ri-annotationOkBtn {\n border: 0;\n border-radius: 8px;\n background: rgba(56, 219, 240, 0.8);\n color: #06343c;\n padding: 8px 18px;\n font-size: 13px;\n font-weight: 600;\n cursor: pointer;\n transition: background-color 0.2s ease, opacity 0.2s ease;\n}\n.ri-annotationOkBtn:hover:not(:disabled) {\n background: #38dbf0;\n}\n.ri-annotationOkBtn:disabled,\n.ri-annotationOkBtn.disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.ri-annotationCancelBtn {\n border: 1px solid rgba(255, 255, 255, 0.12);\n background: transparent;\n color: #9ca3af;\n border-radius: 8px;\n padding: 8px 12px;\n font-size: 13px;\n cursor: pointer;\n transition: background-color 0.2s ease, border-color 0.2s ease;\n}\n.ri-annotationCancelBtn:hover {\n background: rgba(255, 255, 255, 0.05);\n border-color: rgba(255, 255, 255, 0.2);\n}\n.ri-annotationIcons {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 12px;\n color: #9ca3af;\n}\n.ri-annotationIcons svg {\n flex-shrink: 0;\n}\n.ri-viewAnnotationPopup {\n position: relative;\n background: rgba(20, 20, 20, 0.85);\n border: 1px solid rgba(255, 255, 255, 0.12);\n border-radius: 8px;\n padding: 12px 16px;\n min-width: 200px;\n max-width: min(280px, calc(100vw - 48px));\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);\n pointer-events: auto;\n backdrop-filter: blur(10px);\n -webkit-backdrop-filter: blur(10px);\n animation: ri-viewAnnotationFadeIn 0.15s ease-out;\n margin-left: 24px;\n margin-top: -20px;\n isolation: isolate;\n z-index: 1000;\n}\n.ri-viewAnnotationHeader {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 8px;\n}\n.ri-viewAnnotationTitle {\n margin: 0;\n color: #e2e8f0;\n font-size: 14px;\n font-weight: 600;\n}\n.ri-viewAnnotationClose {\n background: transparent;\n border: none;\n color: #b7bdc6;\n cursor: pointer;\n font-size: 18px;\n padding: 0 4px;\n transition: color 0.2s;\n line-height: 1;\n}\n.ri-viewAnnotationClose:hover {\n color: #e2e8f0;\n}\n.ri-viewAnnotationDescription {\n margin: 0;\n color: #cbd5e1;\n font-size: 13px;\n line-height: 1.4;\n word-wrap: break-word;\n}\n@keyframes ri-viewAnnotationFadeIn {\n from {\n opacity: 0;\n transform: translateY(-6px) scale(0.96);\n }\n to {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n}\n.ri-annotationNavSingle {\n position: absolute;\n bottom: 120px;\n left: 50%;\n transform: translateX(-50%);\n z-index: 10;\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 10px;\n pointer-events: none;\n}\n.ri-annotationNavSingle > * {\n pointer-events: auto;\n}\n.ri-navBadge {\n background: rgba(255, 255, 255, 0.1);\n padding: 2px 10px;\n border-radius: 4px;\n font-size: 12px;\n font-weight: 400;\n color: rgba(255, 255, 255, 0.7);\n}\n.ri-annotationTitleHint {\n background: rgba(0, 0, 0, 0.7);\n backdrop-filter: blur(8px);\n -webkit-backdrop-filter: blur(8px);\n color: white;\n padding: 8px 20px;\n border-radius: 8px;\n font-size: 13px;\n border: 1px solid rgba(255, 255, 255, 0.08);\n text-align: center;\n max-width: 350px;\n animation: slideUp 0.3s ease;\n}\n.ri-annotationHintTitle {\n font-weight: 600;\n color: #e5e7eb;\n}\n.ri-annotationHintDesc {\n font-size: 12px;\n opacity: 0.6;\n margin-top: 2px;\n color: #d1d5db;\n}\n@keyframes slideUp {\n from {\n opacity: 0;\n transform: translateY(10px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n@media (max-width: 768px) {\n .ri-xrTools {\n gap: 6px;\n }\n .ri-xrButton {\n height: 30px;\n gap: 6px;\n padding: 0 8px;\n font-size: 11px;\n }\n .ri-xrButton svg {\n width: 14px;\n height: 14px;\n }\n .ri-xrButtonPrefix {\n display: none;\n }\n .ri-annotationNavSingle {\n bottom: 100px;\n }\n .ri-navButtonSingle {\n padding: 8px 20px;\n font-size: 13px;\n min-width: 110px;\n }\n .ri-annotationTitleHint {\n padding: 6px 14px;\n font-size: 12px;\n max-width: 250px;\n }\n .ri-viewAnnotationPopup {\n min-width: 160px;\n max-width: 220px;\n padding: 10px 12px;\n }\n .ri-viewAnnotationTitle {\n font-size: 13px;\n }\n .ri-viewAnnotationDescription {\n font-size: 12px;\n }\n .ri-buttonContainer {\n flex-wrap: wrap;\n gap: 6px;\n }\n .ri-tourControls {\n flex-wrap: wrap;\n }\n .ri-rightControls {\n flex-wrap: wrap;\n }\n}\n@media (max-width: 480px) {\n .ri-xrTools {\n top: 12px;\n gap: 5px;\n }\n .ri-xrButton {\n height: 28px;\n gap: 5px;\n padding: 0 7px;\n font-size: 10px;\n }\n .ri-xrButton svg {\n width: 13px;\n height: 13px;\n }\n .ri-annotationNavSingle {\n bottom: 80px;\n }\n .ri-navButtonSingle {\n padding: 6px 16px;\n font-size: 12px;\n min-width: 90px;\n gap: 6px;\n }\n .ri-annotationTitleHint {\n padding: 4px 10px;\n font-size: 11px;\n max-width: 200px;\n }\n .ri-navBadge {\n font-size: 10px;\n padding: 1px 6px;\n }\n .ri-tourButton {\n padding: 0 8px;\n font-size: 12px;\n }\n .ri-tourButton span {\n display: none;\n }\n .ri-downloadButton {\n padding: 0 10px;\n font-size: 12px;\n }\n .ri-downloadButton span {\n display: none;\n }\n}\n.ri-canvasArea .bb-loadingOverlay {\n position: absolute;\n inset: 0;\n z-index: 12;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 16px;\n padding: 24px;\n text-align: center;\n border-radius: 14px;\n background: rgba(15, 23, 42, 0.46);\n backdrop-filter: blur(1px);\n}\n.ri-canvasArea .bb-loadingSpinner {\n width: 40px;\n height: 40px;\n border-radius: 999px;\n border: 3px solid rgba(226, 232, 240, 0.24);\n border-top-color: #93c5fd;\n animation: bb-spin 0.8s linear infinite;\n}\n.ri-canvasArea .bb-loadingMessage {\n max-width: 320px;\n color: #f7f8f9;\n font-size: 13px;\n font-weight: 600;\n line-height: 1.5;\n}\n@keyframes bb-spin {\n to {\n transform: rotate(360deg);\n }\n}\n');
45
+ styleInject('.ri-viewer--license-checking,\n.ri-viewer--license-invalid {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n background-color: #f9f9f9;\n}\n.ri-viewer {\n --ri-font-family:\n Inter,\n "Segoe UI",\n Helvetica,\n Arial,\n sans-serif;\n --ri-viewer-bg: #1c1c1d;\n --ri-viewer-text: #111827;\n --ri-panel-bg: #ffffff;\n --ri-panel-border: #e5e7eb;\n --ri-panel-title: #111827;\n --ri-panel-muted-text: #4b5563;\n --ri-panel-subtle-text: #9ca3af;\n --ri-panel-surface: #f9fafb;\n --ri-panel-surface-hover: #f3f4f6;\n --ri-panel-surface-border: #e5e7eb;\n --ri-panel-button-text: #1f2937;\n --ri-panel-accent: #2563eb;\n --ri-panel-accent-contrast: #ffffff;\n --ri-license-overlay-bg: rgba(0, 0, 0, 0.04);\n --ri-license-overlay-border: rgba(0, 0, 0, 0.08);\n --ri-license-overlay-text: #888;\n --ri-license-overlay-error-bg: rgba(192, 57, 43, 0.06);\n --ri-license-overlay-error-border: rgba(192, 57, 43, 0.2);\n --ri-license-overlay-error-text: #c0392b;\n --ri-sidepanel-font-family:\n Inter,\n "Segoe UI",\n Helvetica,\n Arial,\n sans-serif;\n --ri-sidepanel-bg: #141824;\n --ri-sidepanel-open-btn-bg: rgba(20, 20, 20, 0.82);\n --ri-sidepanel-open-btn-bg-hover: #27282b7e;\n --ri-sidepanel-border: rgba(255, 255, 255, 0.06);\n --ri-sidepanel-divider: rgba(255, 255, 255, 0.04);\n --ri-sidepanel-border-strong: rgba(255, 255, 255, 0.08);\n --ri-sidepanel-border-active: rgba(255, 255, 255, 0.18);\n --ri-sidepanel-shadow-left: 4px 0 24px rgba(0, 0, 0, 0.35);\n --ri-sidepanel-shadow-right: -4px 0 24px rgba(0, 0, 0, 0.35);\n --ri-sidepanel-text: #f1f5f9;\n --ri-sidepanel-text-emphasis: #ffffff;\n --ri-sidepanel-text-muted: #94a3b8;\n --ri-sidepanel-text-subtle: #64748b;\n --ri-sidepanel-placeholder: #475569;\n --ri-sidepanel-surface: rgba(255, 255, 255, 0.05);\n --ri-sidepanel-surface-hover: rgba(255, 255, 255, 0.1);\n --ri-sidepanel-accent: #3b82f6;\n --ri-sidepanel-accent-soft: rgba(59, 130, 246, 0.12);\n --ri-databinding-bg: #111318;\n --ri-databinding-border: rgba(255, 255, 255, 0.06);\n --ri-databinding-border-strong: rgba(255, 255, 255, 0.08);\n --ri-databinding-border-hover: rgba(255, 255, 255, 0.15);\n --ri-databinding-shadow: 4px 0 24px rgba(0, 0, 0, 0.35);\n --ri-databinding-font-family:\n "Inter",\n "Segoe UI",\n sans-serif;\n --ri-databinding-mono-font-family:\n "JetBrains Mono",\n "Fira Code",\n "Menlo",\n monospace;\n --ri-databinding-text: #f1f5f9;\n --ri-databinding-text-muted: #94a3b8;\n --ri-databinding-text-subtle: #64748b;\n --ri-databinding-text-faint: #475569;\n --ri-databinding-text-hover: #cbd5e1;\n --ri-databinding-surface: rgba(255, 255, 255, 0.03);\n --ri-databinding-surface-hover: rgba(255, 255, 255, 0.06);\n --ri-databinding-accent: #93c5fd;\n --ri-databinding-accent-glow: rgba(147, 197, 253, 0.8);\n --ri-databinding-accent-border: rgba(96, 165, 250, 0.55);\n --ri-databinding-accent-gradient:\n linear-gradient(\n \n 135deg,\n #1e3a8a 0%,\n #1d4ed8 60%,\n #2563eb 100% );\n --ri-databinding-accent-gradient-active:\n linear-gradient(\n \n 135deg,\n #1e3a8a 0%,\n #1e40af 100% );\n display: flex;\n flex-direction: row;\n width: 100%;\n height: 100%;\n min-height: 0;\n overflow: hidden;\n background: var(--ri-viewer-bg);\n position: relative;\n color: var(--ri-viewer-text);\n font-family: var(--ri-font-family);\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n text-rendering: optimizeLegibility;\n font-synthesis: none;\n}\n.ri-viewer * {\n box-sizing: border-box;\n}\n.ri-viewerLeft {\n display: flex;\n flex: 1;\n min-height: 0;\n min-width: 0;\n position: relative;\n}\n.ri-viewerRight {\n display: flex;\n flex-shrink: 0;\n min-width: 0;\n}\n.ri-panel {\n width: 280px;\n min-height: 0;\n background: var(--ri-panel-bg);\n border-right: 1px solid var(--ri-panel-border);\n display: flex;\n flex-direction: column;\n padding: 20px;\n overflow-y: auto;\n flex-shrink: 0;\n transition: margin-left 0.3s ease, transform 0.3s ease;\n}\n.ri-panel--open {\n transform: translateX(0);\n margin-left: 0;\n}\n.ri-panel--closed {\n transform: translateX(-100%);\n margin-left: -280px;\n}\n.ri-panelHeader {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n margin-bottom: 12px;\n}\n.ri-panelTitle {\n margin: 0;\n font-size: 15px;\n font-weight: 700;\n color: var(--ri-panel-title);\n line-height: 1.4;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.ri-closeButton {\n border: 0;\n background: transparent;\n color: var(--ri-panel-subtle-text);\n font-size: 22px;\n line-height: 1;\n padding: 0;\n cursor: pointer;\n flex-shrink: 0;\n transition: color 0.2s ease;\n}\n.ri-closeButton:hover {\n color: var(--ri-panel-muted-text);\n}\n.ri-json {\n margin: 0 0 16px;\n padding: 12px;\n background: var(--ri-panel-surface);\n border: 1px solid var(--ri-panel-surface-border);\n border-radius: 12px;\n font-size: 12px;\n line-height: 1.55;\n color: var(--ri-panel-button-text);\n overflow-x: auto;\n white-space: pre-wrap;\n word-break: break-word;\n}\n.ri-divider {\n border-top: 1px solid var(--ri-panel-border);\n margin-bottom: 12px;\n}\n.ri-sectionLabel {\n margin-bottom: 8px;\n font-size: 11px;\n font-weight: 700;\n color: var(--ri-panel-subtle-text);\n text-transform: uppercase;\n letter-spacing: 0.16em;\n}\n.ri-actionList {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n.ri-actionButton {\n width: 100%;\n border-radius: 8px;\n border: 1px solid var(--ri-panel-surface-border);\n background: var(--ri-panel-surface);\n color: var(--ri-panel-button-text);\n padding: 10px 12px;\n text-align: left;\n font-size: 14px;\n line-height: 1.4;\n cursor: pointer;\n transition:\n background-color 0.2s ease,\n border-color 0.2s ease,\n color 0.2s ease;\n}\n.ri-actionButton:hover {\n background: var(--ri-panel-surface-hover);\n border-color: var(--ri-panel-border);\n}\n.ri-actionButton--active {\n background: var(--ri-panel-accent);\n border-color: var(--ri-panel-accent);\n color: var(--ri-panel-accent-contrast);\n}\n.ri-actionButton--active:hover {\n background: var(--ri-panel-accent);\n border-color: var(--ri-panel-accent);\n}\n.ri-canvasArea {\n flex: 1 1 auto;\n position: relative;\n min-width: 0;\n}\n.ri-canvasArea--measuring {\n cursor: crosshair;\n}\n.ri-canvasArea canvas {\n opacity: 1;\n transition: opacity 0.18s ease;\n}\n.ri-canvasArea--positioning canvas {\n opacity: 0;\n transition: none;\n}\n.ri-bindingsMismatchBanner {\n position: absolute;\n top: 12px;\n left: 50%;\n transform: translateX(-50%);\n z-index: 20;\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 7px 12px 7px 10px;\n border-radius: 8px;\n background: rgba(30, 20, 0, 0.88);\n border: 1px solid rgba(251, 191, 36, 0.35);\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n color: rgb(251, 191, 36);\n font-size: 12px;\n font-weight: 500;\n line-height: 1.4;\n white-space: nowrap;\n pointer-events: auto;\n max-width: calc(100% - 32px);\n white-space: normal;\n text-align: center;\n}\n.ri-bindingsMismatchBanner__icon {\n flex-shrink: 0;\n}\n.ri-bindingsMismatchBanner__dismiss {\n flex-shrink: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n margin-left: 4px;\n padding: 2px;\n background: none;\n border: none;\n border-radius: 4px;\n color: rgba(251, 191, 36, 0.7);\n cursor: pointer;\n transition: color 0.15s ease, background 0.15s ease;\n}\n.ri-bindingsMismatchBanner__dismiss:hover {\n color: rgb(251, 191, 36);\n background: rgba(251, 191, 36, 0.12);\n}\n.ri-measureTools {\n position: absolute;\n top: 16px;\n left: 16px;\n z-index: 10;\n display: flex;\n gap: 8px;\n align-items: center;\n}\n.ri-measureButton {\n width: 34px;\n height: 34px;\n padding: 0;\n background: rgba(20, 20, 20, 0.82);\n border: 1px solid rgba(255, 255, 255, 0.12);\n color: rgba(255, 255, 255, 0.75);\n border-radius: 8px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n touch-action: manipulation;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n transition:\n background 0.15s ease,\n color 0.15s ease,\n border-color 0.15s ease;\n}\n.ri-measureButton:hover {\n background: rgba(40, 40, 40, 0.92);\n color: #fff;\n border-color: rgba(255, 255, 255, 0.25);\n}\n.ri-measureButton:active {\n background: #38dbf0;\n color: #06343c;\n border-color: #38dbf0;\n}\n.ri-measureButton--active {\n background: #22d3ee;\n border-color: #22d3ee;\n color: #06343c;\n}\n.ri-measureButton--active:hover {\n background: #38dbf0;\n color: #06343c;\n}\n.ri-measureHint {\n padding: 6px 10px;\n border-radius: 8px;\n font-size: 12px;\n line-height: 1;\n color: #fff;\n background: rgba(20, 20, 20, 0.82);\n border: 1px solid rgba(255, 255, 255, 0.12);\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n white-space: nowrap;\n}\n.ri-explodeTools {\n position: absolute;\n top: 16px;\n left: 16px;\n z-index: 10;\n display: flex;\n gap: 8px;\n align-items: center;\n}\n.ri-explodeTools--stacked {\n top: 58px;\n}\n.ri-explodeSlider {\n display: flex;\n align-items: center;\n gap: 8px;\n height: 34px;\n padding: 0 12px;\n background: rgba(20, 20, 20, 0.82);\n border: 1px solid rgba(255, 255, 255, 0.12);\n border-radius: 8px;\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n}\n.ri-explodeSlider input[type=range] {\n width: 120px;\n accent-color: #22d3ee;\n cursor: pointer;\n}\n.ri-cinematicTools {\n position: absolute;\n left: 16px;\n z-index: 10;\n display: flex;\n gap: 8px;\n align-items: center;\n}\n.ri-measureDot {\n width: 10px;\n height: 10px;\n border-radius: 50%;\n background: #22d3ee;\n border: 2px solid #fff;\n box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.4);\n pointer-events: none;\n}\n.ri-measureLabel {\n padding: 3px 7px;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 600;\n line-height: 1;\n white-space: nowrap;\n color: #06343c;\n background: #22d3ee;\n box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);\n pointer-events: none;\n user-select: none;\n}\n.ri-xrTools {\n position: absolute;\n top: 16px;\n left: 50%;\n transform: translateX(-50%);\n z-index: 10;\n display: flex;\n gap: 8px;\n align-items: center;\n}\n.ri-xrButton {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n height: 34px;\n padding: 0 10px;\n font-size: 13px;\n font-weight: 500;\n color: #fff;\n background: rgba(20, 20, 20, 0.82);\n border: 1px solid rgba(255, 255, 255, 0.16);\n border-radius: 10px;\n cursor: pointer;\n touch-action: manipulation;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n transition: background 0.15s ease, border-color 0.15s ease;\n}\n.ri-xrButton:hover {\n background: rgba(40, 40, 40, 0.92);\n border-color: rgba(255, 255, 255, 0.3);\n}\n.ri-arHintOverlay {\n position: absolute;\n top: 18px;\n left: 50%;\n transform: translateX(-50%);\n z-index: 10;\n width: min(calc(100% - 32px), 360px);\n pointer-events: none;\n}\n.ri-arHint {\n margin: 0 auto;\n width: fit-content;\n max-width: 100%;\n padding: 6px 12px;\n font-size: 11px;\n font-weight: 500;\n line-height: 1.2;\n text-align: center;\n color: rgba(255, 255, 255, 0.92);\n background: rgba(20, 20, 20, 0.58);\n border: 1px solid rgba(255, 255, 255, 0.12);\n border-radius: 999px;\n user-select: none;\n backdrop-filter: blur(4px);\n -webkit-backdrop-filter: blur(4px);\n}\n.ri-arOverlay {\n position: absolute;\n bottom: 24px;\n left: 50%;\n transform: translateX(-50%);\n z-index: 10;\n width: min(calc(100% - 32px), 420px);\n pointer-events: none;\n}\n.ri-arControls {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 10px;\n}\n.ri-arStatus,\n.ri-arActionButton {\n max-width: 100%;\n padding: 8px 16px;\n font-size: 13px;\n font-weight: 600;\n line-height: 1.3;\n text-align: center;\n white-space: nowrap;\n color: #fff;\n background: rgba(20, 20, 20, 0.82);\n border: 1px solid rgba(255, 255, 255, 0.16);\n border-radius: 19px;\n user-select: none;\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n}\n.ri-arStatus {\n pointer-events: none;\n}\n.ri-arActionButton {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n cursor: pointer;\n pointer-events: auto;\n touch-action: manipulation;\n -webkit-tap-highlight-color: transparent;\n transition:\n background 0.15s ease,\n border-color 0.15s ease,\n color 0.15s ease,\n transform 0.15s ease;\n}\n.ri-arActionButton svg {\n flex-shrink: 0;\n}\n.ri-arActionButton:hover {\n background: rgba(40, 40, 40, 0.92);\n border-color: rgba(255, 255, 255, 0.3);\n}\n.ri-arActionButton:active {\n transform: scale(0.98);\n}\n.ri-arActionButton--primary {\n color: #06343c;\n background: #22d3ee;\n border-color: transparent;\n box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);\n}\n.ri-arActionButton--primary:hover {\n background: #67e8f9;\n border-color: transparent;\n}\n.ri-arActionButton.ri-arActionButton--active,\n.ri-arActionButton[aria-pressed=true],\n.ri-arActionButton.ri-arActionButton--active:hover,\n.ri-arActionButton[aria-pressed=true]:hover,\n.ri-arActionButton.ri-arActionButton--active:active,\n.ri-arActionButton[aria-pressed=true]:active {\n color: #06343c;\n background: #22d3ee;\n border-color: transparent;\n box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);\n}\n.ri-hiddenInput {\n display: none;\n}\n.ri-popover {\n position: absolute;\n z-index: 50;\n background: #ffffff;\n border: 1px solid #e5e7eb;\n border-radius: 16px;\n box-shadow: 0 24px 48px rgba(15, 23, 42, 0.18);\n color: #111827;\n}\n.ri-popoverHeader {\n padding: 12px 16px;\n border-bottom: 1px solid #f3f4f6;\n}\n.ri-popoverLabel {\n margin: 0 0 8px;\n font-size: 11px;\n font-weight: 700;\n color: #6b7280;\n text-transform: uppercase;\n letter-spacing: 0.16em;\n}\n.ri-inlineRow {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n margin-top: 8px;\n}\n.ri-colorCode {\n font-size: 12px;\n color: #9ca3af;\n font-family:\n ui-monospace,\n SFMono-Regular,\n Menlo,\n Monaco,\n Consolas,\n monospace;\n text-transform: uppercase;\n}\n.ri-secondaryButton {\n border: 1px solid #e5e7eb;\n background: #f3f4f6;\n color: #374151;\n border-radius: 8px;\n padding: 6px 10px;\n font-size: 12px;\n cursor: pointer;\n transition: background-color 0.2s ease;\n}\n.ri-secondaryButton:hover {\n background: #e5e7eb;\n}\n.ri-materialBody {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 12px;\n}\n.ri-previewFrame {\n position: relative;\n height: 112px;\n border-radius: 12px;\n overflow: hidden;\n border: 1px solid #e5e7eb;\n}\n.ri-previewImage {\n width: 100%;\n height: 100%;\n object-fit: cover;\n display: block;\n}\n.ri-previewCaption {\n position: absolute;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.4);\n padding: 4px 8px;\n color: #ffffff;\n font-size: 10px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ri-emptyState {\n height: 112px;\n border-radius: 12px;\n border: 2px dashed #e5e7eb;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 4px;\n color: #d1d5db;\n text-align: center;\n}\n.ri-uploadButton {\n width: 100%;\n border: 0;\n border-radius: 10px;\n background: #111827;\n color: #ffffff;\n padding: 10px 12px;\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n transition: background-color 0.2s ease;\n}\n.ri-uploadButton:hover {\n background: #374151;\n}\n.ri-removeButton {\n width: 100%;\n border-radius: 10px;\n border: 1px solid #e5e7eb;\n background: #ffffff;\n color: #6b7280;\n padding: 8px 12px;\n font-size: 12px;\n cursor: pointer;\n transition: background-color 0.2s ease;\n}\n.ri-removeButton:hover {\n background: #f9fafb;\n}\n.ri-formatHint {\n margin: 0;\n text-align: center;\n font-size: 10px;\n color: #d1d5db;\n}\n.ri-buttonContainer {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 8px 12px;\n gap: 8px;\n position: absolute;\n bottom: 12px;\n left: 12px;\n right: 12px;\n z-index: 10;\n}\n.ri-tourControls {\n display: flex;\n gap: 6px;\n align-items: center;\n flex-shrink: 0;\n}\n.ri-rightControls {\n display: flex;\n gap: 6px;\n align-items: center;\n flex-shrink: 0;\n margin-left: auto;\n}\n.ri-rightControls:only-child {\n margin-left: auto;\n}\n.ri-navButtonSingle,\n.ri-tourButton,\n.ri-resetButton,\n.ri-downloadButton {\n height: 34px;\n background: rgba(20, 20, 20, 0.82);\n border: 1px solid rgba(255, 255, 255, 0.12);\n color: rgba(255, 255, 255, 0.75);\n border-radius: 8px;\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n cursor: pointer;\n transition:\n background 0.15s ease,\n color 0.15s ease,\n border-color 0.15s ease,\n transform 0.15s ease;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n}\n.ri-navButtonSingle:hover:not(:disabled),\n.ri-tourButton:hover:not(:disabled),\n.ri-resetButton:hover:not(:disabled) {\n background: rgba(40, 40, 40, 0.92);\n color: #fff;\n border-color: rgba(255, 255, 255, 0.25);\n}\n.ri-navButtonSingle:active:not(:disabled),\n.ri-tourButton:active:not(:disabled),\n.ri-resetButton:active:not(:disabled) {\n background: #38dbf0;\n color: #06343c;\n border-color: #38dbf0;\n}\n.ri-navButtonSingle:disabled,\n.ri-tourButton:disabled,\n.ri-resetButton:disabled {\n opacity: 0.45;\n cursor: not-allowed;\n}\n.ri-navButtonSingle {\n padding: 0 16px;\n font-size: 13px;\n font-weight: 500;\n display: flex;\n align-items: center;\n gap: 8px;\n white-space: nowrap;\n}\n.ri-tourButton {\n padding: 0 12px;\n font-size: 13px;\n font-weight: 500;\n display: flex;\n align-items: center;\n gap: 4px;\n white-space: nowrap;\n}\n.ri-tourButton svg {\n width: 15px;\n height: 15px;\n flex-shrink: 0;\n}\n.ri-tourButton--stop {\n border-color: rgba(255, 68, 68, 0.4);\n color: #ff6b6b;\n}\n.ri-tourButton--stop:hover:not(:disabled) {\n background: rgba(255, 68, 68, 0.15);\n border-color: rgba(255, 68, 68, 0.6);\n color: #ff6b6b;\n}\n.ri-tourButton--stop:active:not(:disabled) {\n background: #ff4444;\n color: #fff;\n border-color: #ff4444;\n}\n.ri-tourButton--next {\n border-color: rgba(56, 219, 240, 0.3);\n color: #38dbf0;\n}\n.ri-tourButton--next:hover:not(:disabled) {\n background: rgba(56, 219, 240, 0.12);\n border-color: rgba(56, 219, 240, 0.5);\n color: #38dbf0;\n}\n.ri-tourButton--next:active:not(:disabled) {\n background: #38dbf0;\n color: #06343c;\n border-color: #38dbf0;\n}\n.ri-resetButton {\n width: 34px;\n padding: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n.ri-resetButton svg {\n width: 15px;\n height: 15px;\n pointer-events: none;\n flex-shrink: 0;\n}\n.ri-resetButton:focus-visible {\n outline: 2px solid rgba(255, 255, 255, 0.5);\n outline-offset: 2px;\n}\n.ri-downloadWrapper {\n position: relative;\n display: inline-flex;\n flex-direction: column;\n align-items: stretch;\n}\n.ri-downloadButton {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n padding: 0 16px;\n font-size: 13px;\n font-weight: 500;\n white-space: nowrap;\n}\n.ri-downloadButton:hover:not(:disabled) {\n background: rgba(0, 0, 0, 0.72);\n border-color: rgba(255, 255, 255, 0.3);\n color: #fff;\n}\n.ri-downloadButton:active:not(:disabled) {\n background: #38dbf0;\n color: #06343c;\n border-color: #38dbf0;\n}\n.ri-downloadButton:disabled {\n opacity: 0.45;\n cursor: not-allowed;\n}\n.ri-downloadButton__spinner {\n display: inline-block;\n width: 13px;\n height: 13px;\n border: 2px solid rgba(255, 255, 255, 0.3);\n border-top-color: #fff;\n border-radius: 50%;\n animation: ri-spin 0.7s linear infinite;\n flex-shrink: 0;\n}\n.ri-downloadMenu {\n position: absolute;\n bottom: calc(100% + 6px);\n right: 0;\n min-width: 168px;\n background: rgba(20, 20, 20, 0.92);\n border: 1px solid rgba(255, 255, 255, 0.14);\n border-radius: 10px;\n overflow: hidden;\n z-index: 100;\n backdrop-filter: blur(10px);\n -webkit-backdrop-filter: blur(10px);\n animation: ri-menuReveal 0.12s ease;\n}\n@keyframes ri-menuReveal {\n from {\n opacity: 0;\n transform: translateY(4px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n.ri-downloadMenuItem {\n display: flex;\n align-items: center;\n gap: 9px;\n width: 100%;\n padding: 10px 14px;\n font-size: 13px;\n font-weight: 400;\n color: #fff;\n background: transparent;\n border: none;\n cursor: pointer;\n text-align: left;\n transition: background 0.1s ease;\n}\n.ri-downloadMenuItem:hover:not(:disabled) {\n background: rgba(255, 255, 255, 0.1);\n}\n.ri-downloadMenuItem:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n.ri-downloadMenuDivider {\n height: 1px;\n background: rgba(255, 255, 255, 0.1);\n margin: 0 10px;\n}\n@keyframes ri-spin {\n to {\n transform: rotate(360deg);\n }\n}\n.ri-annotationMarker {\n position: relative;\n width: 36px;\n height: 36px;\n border-radius: 50%;\n background: rgba(20, 20, 20, 0.82);\n border: 2px solid rgba(255, 255, 255, 0.9);\n color: #fff;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 14px;\n font-weight: 700;\n cursor: pointer;\n z-index: 20;\n user-select: none;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.45);\n pointer-events: auto;\n transition: opacity 0.3s ease, transform 0.3s ease;\n}\n.ri-annotationMarker:hover {\n transform: scale(1.08);\n box-shadow: 0 12px 28px rgba(15, 23, 42, 0.18);\n background: rgba(40, 40, 40, 0.92);\n}\n.ri-annotationPopup {\n position: absolute;\n width: min(270px, calc(100vw - 32px));\n background: rgba(20, 20, 20, 0.85);\n border: 1px solid rgba(255, 255, 255, 0.12);\n border-radius: 16px;\n padding: 16px;\n z-index: 30;\n display: flex;\n flex-direction: column;\n gap: 12px;\n box-shadow: 0 24px 48px rgba(15, 23, 42, 0.18);\n backdrop-filter: blur(10px);\n -webkit-backdrop-filter: blur(10px);\n}\n.ri-annotationInput,\n.ri-annotationTextarea {\n width: 100%;\n border: 1px solid rgba(255, 255, 255, 0.12);\n background: rgba(255, 255, 255, 0.05);\n color: #e5e7eb;\n border-radius: 10px;\n padding: 10px 12px;\n outline: none;\n font-family: inherit;\n transition: border-color 0.2s ease, box-shadow 0.2s ease;\n}\n.ri-annotationInput {\n font-size: 14px;\n font-weight: 500;\n}\n.ri-annotationTextarea {\n font-size: 13px;\n resize: vertical;\n min-height: 110px;\n}\n.ri-annotationInput:focus,\n.ri-annotationTextarea:focus {\n border-color: #38dbf0;\n box-shadow: 0 0 0 3px rgba(56, 219, 240, 0.12);\n}\n.ri-annotationActions {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n.ri-annotationOkBtn {\n border: 0;\n border-radius: 8px;\n background: rgba(56, 219, 240, 0.8);\n color: #06343c;\n padding: 8px 18px;\n font-size: 13px;\n font-weight: 600;\n cursor: pointer;\n transition: background-color 0.2s ease, opacity 0.2s ease;\n}\n.ri-annotationOkBtn:hover:not(:disabled) {\n background: #38dbf0;\n}\n.ri-annotationOkBtn:disabled,\n.ri-annotationOkBtn.disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.ri-annotationCancelBtn {\n border: 1px solid rgba(255, 255, 255, 0.12);\n background: transparent;\n color: #9ca3af;\n border-radius: 8px;\n padding: 8px 12px;\n font-size: 13px;\n cursor: pointer;\n transition: background-color 0.2s ease, border-color 0.2s ease;\n}\n.ri-annotationCancelBtn:hover {\n background: rgba(255, 255, 255, 0.05);\n border-color: rgba(255, 255, 255, 0.2);\n}\n.ri-annotationIcons {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 12px;\n color: #9ca3af;\n}\n.ri-annotationIcons svg {\n flex-shrink: 0;\n}\n.ri-viewAnnotationPopup {\n position: relative;\n background: rgba(20, 20, 20, 0.85);\n border: 1px solid rgba(255, 255, 255, 0.12);\n border-radius: 8px;\n padding: 12px 16px;\n min-width: 200px;\n max-width: min(280px, calc(100vw - 48px));\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);\n pointer-events: auto;\n backdrop-filter: blur(10px);\n -webkit-backdrop-filter: blur(10px);\n animation: ri-viewAnnotationFadeIn 0.15s ease-out;\n margin-left: 24px;\n margin-top: -20px;\n isolation: isolate;\n z-index: 1000;\n}\n.ri-viewAnnotationHeader {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 8px;\n}\n.ri-viewAnnotationTitle {\n margin: 0;\n color: #e2e8f0;\n font-size: 14px;\n font-weight: 600;\n}\n.ri-viewAnnotationClose {\n background: transparent;\n border: none;\n color: #b7bdc6;\n cursor: pointer;\n font-size: 18px;\n padding: 0 4px;\n transition: color 0.2s;\n line-height: 1;\n}\n.ri-viewAnnotationClose:hover {\n color: #e2e8f0;\n}\n.ri-viewAnnotationDescription {\n margin: 0;\n color: #cbd5e1;\n font-size: 13px;\n line-height: 1.4;\n word-wrap: break-word;\n}\n@keyframes ri-viewAnnotationFadeIn {\n from {\n opacity: 0;\n transform: translateY(-6px) scale(0.96);\n }\n to {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n}\n.ri-annotationNavSingle {\n position: absolute;\n bottom: 120px;\n left: 50%;\n transform: translateX(-50%);\n z-index: 10;\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 10px;\n pointer-events: none;\n}\n.ri-annotationNavSingle > * {\n pointer-events: auto;\n}\n.ri-navBadge {\n background: rgba(255, 255, 255, 0.1);\n padding: 2px 10px;\n border-radius: 4px;\n font-size: 12px;\n font-weight: 400;\n color: rgba(255, 255, 255, 0.7);\n}\n.ri-annotationTitleHint {\n background: rgba(0, 0, 0, 0.7);\n backdrop-filter: blur(8px);\n -webkit-backdrop-filter: blur(8px);\n color: white;\n padding: 8px 20px;\n border-radius: 8px;\n font-size: 13px;\n border: 1px solid rgba(255, 255, 255, 0.08);\n text-align: center;\n max-width: 350px;\n animation: slideUp 0.3s ease;\n}\n.ri-annotationHintTitle {\n font-weight: 600;\n color: #e5e7eb;\n}\n.ri-annotationHintDesc {\n font-size: 12px;\n opacity: 0.6;\n margin-top: 2px;\n color: #d1d5db;\n}\n@keyframes slideUp {\n from {\n opacity: 0;\n transform: translateY(10px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n@media (max-width: 768px) {\n .ri-xrTools {\n gap: 6px;\n }\n .ri-xrButton {\n height: 30px;\n gap: 6px;\n padding: 0 8px;\n font-size: 11px;\n }\n .ri-xrButton svg {\n width: 14px;\n height: 14px;\n }\n .ri-xrButtonPrefix {\n display: none;\n }\n .ri-annotationNavSingle {\n bottom: 100px;\n }\n .ri-navButtonSingle {\n padding: 8px 20px;\n font-size: 13px;\n min-width: 110px;\n }\n .ri-annotationTitleHint {\n padding: 6px 14px;\n font-size: 12px;\n max-width: 250px;\n }\n .ri-viewAnnotationPopup {\n min-width: 160px;\n max-width: 220px;\n padding: 10px 12px;\n }\n .ri-viewAnnotationTitle {\n font-size: 13px;\n }\n .ri-viewAnnotationDescription {\n font-size: 12px;\n }\n .ri-buttonContainer {\n flex-wrap: wrap;\n gap: 6px;\n }\n .ri-tourControls {\n flex-wrap: wrap;\n }\n .ri-rightControls {\n flex-wrap: wrap;\n }\n}\n@media (max-width: 480px) {\n .ri-xrTools {\n top: 12px;\n gap: 5px;\n }\n .ri-xrButton {\n height: 28px;\n gap: 5px;\n padding: 0 7px;\n font-size: 10px;\n }\n .ri-xrButton svg {\n width: 13px;\n height: 13px;\n }\n .ri-annotationNavSingle {\n bottom: 80px;\n }\n .ri-navButtonSingle {\n padding: 6px 16px;\n font-size: 12px;\n min-width: 90px;\n gap: 6px;\n }\n .ri-annotationTitleHint {\n padding: 4px 10px;\n font-size: 11px;\n max-width: 200px;\n }\n .ri-navBadge {\n font-size: 10px;\n padding: 1px 6px;\n }\n .ri-tourButton {\n padding: 0 8px;\n font-size: 12px;\n }\n .ri-tourButton span {\n display: none;\n }\n .ri-downloadButton {\n padding: 0 10px;\n font-size: 12px;\n }\n .ri-downloadButton span {\n display: none;\n }\n}\n.ri-canvasArea .bb-loadingOverlay {\n position: absolute;\n inset: 0;\n z-index: 12;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 16px;\n padding: 24px;\n text-align: center;\n border-radius: 14px;\n background: rgba(15, 23, 42, 0.46);\n backdrop-filter: blur(1px);\n}\n.ri-canvasArea .bb-loadingSpinner {\n width: 40px;\n height: 40px;\n border-radius: 999px;\n border: 3px solid rgba(226, 232, 240, 0.24);\n border-top-color: #93c5fd;\n animation: bb-spin 0.8s linear infinite;\n}\n.ri-canvasArea .bb-loadingMessage {\n max-width: 320px;\n color: #f7f8f9;\n font-size: 13px;\n font-weight: 600;\n line-height: 1.5;\n}\n@keyframes bb-spin {\n to {\n transform: rotate(360deg);\n }\n}\n');
46
46
 
47
47
  // src/components/LicenseOverlay.tsx
48
48
  import { AlertTriangle } from "lucide-react";
@@ -3902,8 +3902,142 @@ var XRModelPresenter = forwardRef2(
3902
3902
  );
3903
3903
  var XRModelPresenter_default = XRModelPresenter;
3904
3904
 
3905
+ // src/components/ModelViewer/CinematicCamera.tsx
3906
+ import { useEffect as useEffect10, useMemo as useMemo11, useRef as useRef8 } from "react";
3907
+ import { useFrame as useFrame7, useThree as useThree9 } from "@react-three/fiber";
3908
+ import {
3909
+ Box3 as Box32,
3910
+ CatmullRomCurve3,
3911
+ Sphere,
3912
+ Vector3 as Vector36
3913
+ } from "three";
3914
+ var ENTER_BLEND_SECONDS = 1.2;
3915
+ function CinematicCamera({
3916
+ controlsRef,
3917
+ sceneRef,
3918
+ config,
3919
+ playing,
3920
+ onFinished,
3921
+ onUserInteract
3922
+ }) {
3923
+ const invalidate = useThree9((state) => state.invalidate);
3924
+ const waypoints = useMemo11(() => config.waypoints ?? [], [config.waypoints]);
3925
+ const hasPath = waypoints.length >= 2;
3926
+ const loop = config.loop ?? true;
3927
+ const duration = config.duration ?? (hasPath ? Math.max(waypoints.length * 3.5, 8) : 24);
3928
+ const signature = useMemo11(
3929
+ () => waypoints.map((w) => [...w.position, ...w.target].join(",")).join(";"),
3930
+ [waypoints]
3931
+ );
3932
+ const curves = useMemo11(() => {
3933
+ if (!hasPath) return null;
3934
+ return {
3935
+ position: new CatmullRomCurve3(
3936
+ waypoints.map((w) => new Vector36(...w.position)),
3937
+ loop,
3938
+ "catmullrom",
3939
+ 0.5
3940
+ ),
3941
+ target: new CatmullRomCurve3(
3942
+ waypoints.map((w) => new Vector36(...w.target)),
3943
+ loop,
3944
+ "catmullrom",
3945
+ 0.5
3946
+ )
3947
+ };
3948
+ }, [signature, hasPath, loop]);
3949
+ const elapsedRef = useRef8(0);
3950
+ const enterElapsedRef = useRef8(0);
3951
+ const enterPos = useRef8(new Vector36());
3952
+ const enterTarget = useRef8(new Vector36());
3953
+ const startedRef = useRef8(false);
3954
+ const orbitRef = useRef8(null);
3955
+ const tmpPos = useMemo11(() => new Vector36(), []);
3956
+ const tmpTarget = useMemo11(() => new Vector36(), []);
3957
+ useEffect10(() => {
3958
+ if (!playing) {
3959
+ startedRef.current = false;
3960
+ return;
3961
+ }
3962
+ const controls = controlsRef.current;
3963
+ if (controls) {
3964
+ controls.getPosition(enterPos.current);
3965
+ controls.getTarget(enterTarget.current);
3966
+ }
3967
+ elapsedRef.current = 0;
3968
+ enterElapsedRef.current = 0;
3969
+ if (!hasPath) {
3970
+ const scene = sceneRef.current;
3971
+ if (scene) {
3972
+ const sphere = new Box32().setFromObject(scene).getBoundingSphere(new Sphere());
3973
+ orbitRef.current = {
3974
+ center: sphere.center.clone(),
3975
+ radius: sphere.radius > 0 ? sphere.radius : 1
3976
+ };
3977
+ }
3978
+ }
3979
+ startedRef.current = true;
3980
+ invalidate();
3981
+ }, [playing, hasPath, controlsRef, sceneRef, invalidate]);
3982
+ useEffect10(() => {
3983
+ if (!playing || !onUserInteract) return;
3984
+ const controls = controlsRef.current;
3985
+ if (!controls) return;
3986
+ const handle = () => onUserInteract();
3987
+ controls.addEventListener("controlstart", handle);
3988
+ return () => controls.removeEventListener("controlstart", handle);
3989
+ }, [playing, controlsRef, onUserInteract]);
3990
+ useFrame7((_, delta) => {
3991
+ if (!playing || !startedRef.current) return;
3992
+ const controls = controlsRef.current;
3993
+ if (!controls) return;
3994
+ elapsedRef.current += delta;
3995
+ enterElapsedRef.current += delta;
3996
+ let u = elapsedRef.current / duration;
3997
+ const finished = !loop && u >= 1;
3998
+ u = loop ? u % 1 : Math.min(u, 1);
3999
+ if (curves) {
4000
+ curves.position.getPointAt(u, tmpPos);
4001
+ curves.target.getPointAt(u, tmpTarget);
4002
+ } else {
4003
+ const orbit = orbitRef.current;
4004
+ if (!orbit) return;
4005
+ const angle = u * Math.PI * 2;
4006
+ const dist = orbit.radius * 2.2;
4007
+ tmpPos.set(
4008
+ orbit.center.x + Math.cos(angle) * dist,
4009
+ orbit.center.y + orbit.radius * 0.6,
4010
+ orbit.center.z + Math.sin(angle) * dist
4011
+ );
4012
+ tmpTarget.copy(orbit.center);
4013
+ }
4014
+ const blend = Math.min(enterElapsedRef.current / ENTER_BLEND_SECONDS, 1);
4015
+ if (blend < 1) {
4016
+ const eased = blend * blend * (3 - 2 * blend);
4017
+ tmpPos.lerpVectors(enterPos.current, tmpPos, eased);
4018
+ tmpTarget.lerpVectors(enterTarget.current, tmpTarget, eased);
4019
+ }
4020
+ controls.setLookAt(
4021
+ tmpPos.x,
4022
+ tmpPos.y,
4023
+ tmpPos.z,
4024
+ tmpTarget.x,
4025
+ tmpTarget.y,
4026
+ tmpTarget.z,
4027
+ false
4028
+ );
4029
+ if (finished) {
4030
+ startedRef.current = false;
4031
+ onFinished?.();
4032
+ return;
4033
+ }
4034
+ invalidate();
4035
+ });
4036
+ return null;
4037
+ }
4038
+
3905
4039
  // src/hooks/useLicense.ts
3906
- import { useEffect as useEffect10, useState as useState5 } from "react";
4040
+ import { useEffect as useEffect11, useState as useState5 } from "react";
3907
4041
  var LOCAL_HOSTNAMES = ["localhost", "127.0.0.1", "0.0.0.0"];
3908
4042
  var isLocalHostname = (hostname) => LOCAL_HOSTNAMES.includes(hostname) || hostname.endsWith(".local") || hostname.endsWith(".localhost");
3909
4043
  var LICENSE_SERVER = "https://react-immersive.liveroom.dev/api/validate-license";
@@ -4078,7 +4212,7 @@ var useLicense = (licenseKey) => {
4078
4212
  // window isn't available during SSR — useEffect below recomputes this on mount
4079
4213
  isLocalDomain: false
4080
4214
  });
4081
- useEffect10(() => {
4215
+ useEffect11(() => {
4082
4216
  if (!canUseBrowserAPI()) {
4083
4217
  return;
4084
4218
  }
@@ -4129,7 +4263,7 @@ var useLicense = (licenseKey) => {
4129
4263
  };
4130
4264
 
4131
4265
  // src/components/MouseController.tsx
4132
- import { useRef as useRef8, useState as useState6, useCallback as useCallback3, useEffect as useEffect11 } from "react";
4266
+ import { useRef as useRef9, useState as useState6, useCallback as useCallback3, useEffect as useEffect12 } from "react";
4133
4267
 
4134
4268
  // src/styles/MouseController.css
4135
4269
  styleInject(".mc__wrapper {\n position: fixed;\n bottom: -10px;\n left: 50%;\n transform: translateX(-50%);\n display: flex;\n flex-direction: column;\n align-items: center;\n z-index: 100;\n}\n.mc__dpad {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 2px;\n margin-bottom: 20px;\n}\n.mc__middle-row {\n display: flex;\n align-items: center;\n gap: 2px;\n}\n.mc__joystick {\n width: 60px;\n height: 60px;\n flex-shrink: 0;\n background:\n radial-gradient(\n circle at 40% 35%,\n rgba(255, 255, 255, 0.07),\n rgba(0, 0, 0, 0.55));\n border-radius: 50%;\n border: 1.5px solid rgba(255, 255, 255, 0.15);\n position: relative;\n cursor: grab;\n touch-action: none;\n transition: border-color 0.2s ease, box-shadow 0.2s ease;\n margin: 4px;\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.5), 0 0 0 4px rgba(255, 255, 255, 0.03);\n}\n.mc__joystick:hover {\n border-color: rgba(255, 255, 255, 0.28);\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.55), 0 0 0 4px rgba(66, 133, 244, 0.08);\n}\n.mc__joystick:active {\n cursor: grabbing;\n border-color: rgba(66, 133, 244, 0.6);\n box-shadow: 0 2px 12px rgba(0, 0, 0, 0.5), 0 0 4px rgba(66, 133, 244, 0.15);\n}\n.mc__knob {\n position: absolute;\n top: 50%;\n left: 50%;\n width: 34px;\n height: 34px;\n margin-left: -17px;\n margin-top: -17px;\n background:\n linear-gradient(\n 145deg,\n #5a9bf5,\n #1a66d9);\n border-radius: 50%;\n cursor: grab;\n box-shadow: 0 3px 10px rgba(26, 102, 217, 0.5), 0 1px 0 rgba(255, 255, 255, 0.25) inset;\n transition: transform 0.04s linear, box-shadow 0.1s ease;\n border: 1.5px solid rgba(255, 255, 255, 0.45);\n}\n.mc__joystick:active .mc__knob {\n box-shadow: 0 2px 16px rgba(66, 133, 244, 0.7), 0 1px 0 rgba(255, 255, 255, 0.25) inset;\n animation: pulse 0.6s ease infinite;\n}\n.mc__arrow {\n padding: 0;\n margin: 0;\n background: transparent;\n border: none;\n border-radius: 0;\n box-shadow: none;\n outline: none;\n -webkit-appearance: none;\n appearance: none;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n touch-action: manipulation;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n transition: transform 0.1s ease, filter 0.15s ease;\n overflow: visible;\n}\n.mc__arrow svg {\n display: block !important;\n pointer-events: none !important;\n user-select: none !important;\n -webkit-user-select: none !important;\n overflow: visible !important;\n}\n.mc__arrow--up {\n margin-bottom: 2px;\n}\n.mc__arrow--up:hover {\n transform: scale(1.15) translateY(-2px);\n filter: drop-shadow(0 4px 12px rgba(66, 133, 244, 0.8));\n}\n.mc__arrow--up:active {\n transform: scale(0.92) translateY(2px);\n}\n.mc__arrow--down {\n margin-top: 2px;\n}\n.mc__arrow--down:hover {\n transform: scale(1.15) translateY(2px);\n filter: drop-shadow(0 -4px 12px rgba(66, 133, 244, 0.8));\n}\n.mc__arrow--down:active {\n transform: scale(0.92) translateY(-2px);\n}\n.mc__arrow--left {\n margin-right: 2px;\n}\n.mc__arrow--left:hover {\n transform: scale(1.15) translateX(-2px);\n filter: drop-shadow(4px 0 12px rgba(66, 133, 244, 0.8));\n}\n.mc__arrow--left:active {\n transform: scale(0.92) translateX(2px);\n}\n.mc__arrow--right {\n margin-left: 2px;\n}\n.mc__arrow--right:hover {\n transform: scale(1.15) translateX(2px);\n filter: drop-shadow(-4px 0 12px rgba(66, 133, 244, 0.8));\n}\n.mc__arrow--right:active {\n transform: scale(0.92) translateX(-2px);\n}\n@keyframes pulse {\n 0%, 100% {\n box-shadow: 0 2px 16px rgba(66, 133, 244, 0.6), 0 1px 0 rgba(255, 255, 255, 0.25) inset;\n opacity: 0.85;\n }\n 50% {\n box-shadow: 0 4px 24px rgba(66, 133, 244, 0.95), 0 1px 0 rgba(255, 255, 255, 0.25) inset;\n opacity: 1;\n }\n}\n@media (max-width: 768px) {\n .mc__wrapper {\n bottom: -5px;\n }\n .mc__joystick {\n width: 68px;\n height: 68px;\n }\n .mc__knob {\n width: 30px;\n height: 30px;\n margin-left: -15px;\n margin-top: -15px;\n }\n .mc__arrow--up:hover {\n transform: scale(1.1) translateY(-1px);\n }\n .mc__arrow--down:hover {\n transform: scale(1.1) translateY(1px);\n }\n .mc__arrow--left:hover {\n transform: scale(1.1) translateX(-1px);\n }\n .mc__arrow--right:hover {\n transform: scale(1.1) translateX(1px);\n }\n}\n@media (max-width: 480px) {\n .mc__joystick {\n width: 56px;\n height: 56px;\n }\n .mc__knob {\n width: 26px;\n height: 26px;\n margin-left: -13px;\n margin-top: -13px;\n }\n .mc__arrow--up {\n margin-bottom: 1px;\n }\n .mc__arrow--down {\n margin-top: 1px;\n }\n .mc__arrow--left {\n margin-right: 1px;\n }\n .mc__arrow--right {\n margin-left: 1px;\n }\n}\n@media (hover: none) {\n .mc__arrow:hover {\n transform: none !important;\n filter: none !important;\n }\n .mc__arrow:active {\n transform: scale(0.92) !important;\n }\n}\n");
@@ -4214,14 +4348,14 @@ var MouseController = ({
4214
4348
  rotateSensitivity = 0.01,
4215
4349
  position = "center-bottom"
4216
4350
  }) => {
4217
- const joystickBaseRef = useRef8(null);
4351
+ const joystickBaseRef = useRef9(null);
4218
4352
  const [knobPosition, setKnobPosition] = useState6({ x: 0, y: 0 });
4219
- const isDragging = useRef8(false);
4220
- const rotateDirection = useRef8({ x: 0, y: 0 });
4221
- const animationFrame = useRef8();
4222
- const repeatAnimationRef = useRef8();
4223
- const activeRepeatActionRef = useRef8(null);
4224
- const lastRepeatTimeRef = useRef8(0);
4353
+ const isDragging = useRef9(false);
4354
+ const rotateDirection = useRef9({ x: 0, y: 0 });
4355
+ const animationFrame = useRef9();
4356
+ const repeatAnimationRef = useRef9();
4357
+ const activeRepeatActionRef = useRef9(null);
4358
+ const lastRepeatTimeRef = useRef9(0);
4225
4359
  const getControls = () => controlsRef.current;
4226
4360
  const clampToCircle = (dx, dy, maxR) => {
4227
4361
  const dist = Math.hypot(dx, dy);
@@ -4333,7 +4467,7 @@ var MouseController = ({
4333
4467
  lastRepeatTimeRef.current = 0;
4334
4468
  }
4335
4469
  }, []);
4336
- useEffect11(() => {
4470
+ useEffect12(() => {
4337
4471
  window.addEventListener("mousemove", onJoystickMove);
4338
4472
  window.addEventListener("mouseup", onJoystickEnd);
4339
4473
  window.addEventListener("touchmove", onJoystickMove, { passive: false });
@@ -4562,7 +4696,7 @@ var MaterialPicker = ({
4562
4696
  };
4563
4697
 
4564
4698
  // src/components/ModelViewer/ObjectBindingDataPanel.tsx
4565
- import { useEffect as useEffect12, useState as useState7 } from "react";
4699
+ import { useEffect as useEffect13, useState as useState7 } from "react";
4566
4700
 
4567
4701
  // src/styles/ObjectBindingDataPanel.css
4568
4702
  styleInject('.left-panel {\n width: 280px;\n min-height: 0;\n background: var(--ri-databinding-bg, #111318);\n border-right: 1px solid var(--ri-databinding-border, rgba(255, 255, 255, 0.06));\n display: flex;\n flex-direction: column;\n padding: 24px 18px;\n overflow-y: auto;\n font-family: var( --ri-databinding-font-family, "Inter", "Segoe UI", sans-serif );\n flex-shrink: 0;\n transition: margin-left 0.45s cubic-bezier(0.4, 0, 0.2, 1);\n box-shadow: var(--ri-databinding-shadow, 4px 0 24px rgba(0, 0, 0, 0.35));\n}\n.left-panel--open {\n margin-left: 0;\n}\n.left-panel--closed {\n margin-left: -280px;\n}\n.left-panel::-webkit-scrollbar {\n width: 4px;\n}\n.left-panel::-webkit-scrollbar-track {\n background: transparent;\n}\n.left-panel::-webkit-scrollbar-thumb {\n background: var(--ri-databinding-border-strong, rgba(255, 255, 255, 0.08));\n border-radius: 4px;\n}\n.left-panel__header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 16px;\n padding-bottom: 14px;\n border-bottom: 1px solid var(--ri-databinding-border-strong, rgba(255, 255, 255, 0.08));\n}\n.left-panel__title {\n font-size: 14px;\n font-weight: 600;\n color: var(--ri-databinding-text, #f1f5f9);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n padding-right: 8px;\n margin: 0;\n letter-spacing: 0.02em;\n}\n.left-panel__close-btn {\n width: 26px;\n height: 26px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--ri-databinding-surface, rgba(255, 255, 255, 0.03));\n border: 1px solid var(--ri-databinding-border-strong, rgba(255, 255, 255, 0.08));\n border-radius: 6px;\n color: var(--ri-databinding-text-subtle, #64748b);\n font-size: 13px;\n flex-shrink: 0;\n cursor: pointer;\n transition:\n background 0.15s ease,\n color 0.15s ease,\n border-color 0.15s ease;\n}\n.left-panel__close-btn:hover {\n background: var(--ri-databinding-surface-hover, rgba(255, 255, 255, 0.06));\n color: var(--ri-databinding-text-hover, #cbd5e1);\n border-color: var(--ri-databinding-border-hover, rgba(255, 255, 255, 0.15));\n}\n.left-panel__data {\n padding: 12px 6px;\n background: var(--ri-databinding-surface, rgba(255, 255, 255, 0.03));\n border: 1px solid var(--ri-databinding-border-strong, rgba(255, 255, 255, 0.08));\n border-radius: 10px;\n font-size: 11px;\n font-family: var( --ri-databinding-mono-font-family, "JetBrains Mono", "Fira Code", "Menlo", monospace );\n color: var(--ri-databinding-text-muted, #94a3b8);\n line-height: 1.7;\n overflow-x: auto;\n margin-bottom: 18px;\n}\n.left-panel__divider {\n border: none;\n border-top: 1px solid var(--ri-databinding-border, rgba(255, 255, 255, 0.06));\n margin-bottom: 16px;\n}\n.left-panel__actions-label {\n font-size: 10px;\n font-weight: 600;\n color: var(--ri-databinding-text-faint, #475569);\n text-transform: uppercase;\n letter-spacing: 0.12em;\n margin-bottom: 10px;\n}\n.left-panel__actions-list {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n.left-panel__action-btn {\n position: relative;\n padding: 9px 14px;\n border-radius: 8px;\n text-align: left;\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n overflow: hidden;\n border: 1px solid var(--ri-databinding-border-strong, rgba(255, 255, 255, 0.08));\n background: var(--ri-databinding-surface, rgba(255, 255, 255, 0.03));\n color: var(--ri-databinding-text-muted, #94a3b8);\n letter-spacing: 0.01em;\n}\n.left-panel__action-btn:active {\n transform: scale(0.97);\n}\n.left-panel__action-btn--active::before {\n content: "";\n position: absolute;\n left: 0;\n top: 20%;\n bottom: 20%;\n width: 3px;\n background: var(--ri-databinding-accent, #93c5fd);\n border-radius: 0 3px 3px 0;\n box-shadow: 0 0 8px var(--ri-databinding-accent-glow, rgba(147, 197, 253, 0.8));\n}\n.left-panel__action-btn--active:hover {\n background: var( --ri-databinding-accent-gradient, linear-gradient(135deg, #1e3a8a 0%, #1d4ed8 60%, #2563eb 100%) );\n border-color: var(--ri-databinding-accent-border, rgba(96, 165, 250, 0.55));\n}\n.left-panel__action-btn--active:active {\n transform: scale(0.97);\n background: var( --ri-databinding-accent-gradient-active, linear-gradient(135deg, #1e3a8a 0%, #1e40af 100%) );\n}\n.left-panel__data {\n display: flex;\n flex-direction: column;\n gap: 2px;\n margin-bottom: 18px;\n}\n.left-panel__data-row {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: 10px;\n padding: 7px 10px;\n border-radius: 7px;\n background: var(--ri-databinding-surface, rgba(255, 255, 255, 0.03));\n border: 1px solid transparent;\n transition: background 0.15s ease, border-color 0.15s ease;\n}\n.left-panel__data-row:hover {\n background: var(--ri-databinding-surface-hover, rgba(255, 255, 255, 0.06));\n border-color: var(--ri-databinding-border-strong, rgba(255, 255, 255, 0.08));\n}\n.left-panel__data-key {\n font-size: 11px;\n font-weight: 600;\n color: var(--ri-databinding-text-faint, #475569);\n text-transform: uppercase;\n letter-spacing: 0.07em;\n white-space: nowrap;\n flex-shrink: 0;\n}\n.left-panel__data-value {\n font-size: 12px;\n font-family: var( --ri-databinding-mono-font-family, "JetBrains Mono", "Fira Code", "Menlo", monospace );\n text-align: right;\n word-break: break-all;\n max-width: 150px;\n}\n@media (max-width: 900px) {\n .left-panel {\n position: absolute;\n top: 0;\n left: 0;\n bottom: 0;\n width: min(280px, 85vw);\n z-index: 200;\n min-height: 0;\n margin-left: 0;\n transition: transform 0.45s cubic-bezier(0.4, 0, 0.2, 1);\n }\n .left-panel--open {\n margin-left: 0;\n transform: translateX(0);\n }\n .left-panel--closed {\n margin-left: 0;\n transform: translateX(-100%);\n }\n}\n');
@@ -4577,7 +4711,7 @@ function ObjectBindingDataPanel({
4577
4711
  onAction
4578
4712
  }) {
4579
4713
  const [displayedObject, setDisplayedObject] = useState7(selectedObject);
4580
- useEffect12(() => {
4714
+ useEffect13(() => {
4581
4715
  if (selectedObject) setDisplayedObject(selectedObject);
4582
4716
  }, [selectedObject]);
4583
4717
  const objectToRender = selectedObject ?? displayedObject;
@@ -4643,7 +4777,7 @@ function ObjectBindingDataPanel({
4643
4777
  }
4644
4778
 
4645
4779
  // src/components/ModelViewer/SceneObjectsPanel.tsx
4646
- import { useMemo as useMemo11, useState as useState9 } from "react";
4780
+ import { useMemo as useMemo12, useState as useState9 } from "react";
4647
4781
  import { Boxes } from "lucide-react";
4648
4782
 
4649
4783
  // src/components/shared/SearchableSidePanel.tsx
@@ -4768,7 +4902,7 @@ function SceneObjectsPanel({
4768
4902
  }) {
4769
4903
  const [hoveredNode, setHoveredNode] = useState9(null);
4770
4904
  const [searchQuery, setSearchQuery] = useState9("");
4771
- const entries = useMemo11(
4905
+ const entries = useMemo12(
4772
4906
  () => Object.entries(objectBindings).filter(([nodeName, binding]) => {
4773
4907
  const q = searchQuery.toLowerCase();
4774
4908
  return nodeName.toLowerCase().includes(q) || (binding.label ?? "").toLowerCase().includes(q);
@@ -4873,7 +5007,9 @@ import {
4873
5007
  ChevronRight,
4874
5008
  RectangleGoggles,
4875
5009
  X,
4876
- Expand
5010
+ Expand,
5011
+ Play,
5012
+ Pause
4877
5013
  } from "lucide-react";
4878
5014
 
4879
5015
  // src/components/BindingBuilder/BindingBuilderContext.tsx
@@ -4928,7 +5064,7 @@ function findInfluencingBone(hit) {
4928
5064
  if (!skinIndex || !skinWeight || !position) return null;
4929
5065
  const localHit = mesh.worldToLocal(hit.point.clone());
4930
5066
  const vertices = [hit.face.a, hit.face.b, hit.face.c];
4931
- const vertex = new Vector36();
5067
+ const vertex = new Vector37();
4932
5068
  let nearest = vertices[0];
4933
5069
  let nearestDist = Infinity;
4934
5070
  for (const vi of vertices) {
@@ -4975,19 +5111,19 @@ function GuidedTourCameraFollow({
4975
5111
  controlsRef,
4976
5112
  offsetRef
4977
5113
  }) {
4978
- const scene = useThree9((state) => state.scene);
4979
- const nodeRef = useRef9(null);
4980
- const hasPrev = useRef9(false);
4981
- const live = useMemo12(() => new Vector36(), []);
4982
- const prev = useMemo12(() => new Vector36(), []);
4983
- const goal = useMemo12(() => new Vector36(), []);
5114
+ const scene = useThree10((state) => state.scene);
5115
+ const nodeRef = useRef10(null);
5116
+ const hasPrev = useRef10(false);
5117
+ const live = useMemo13(() => new Vector37(), []);
5118
+ const prev = useMemo13(() => new Vector37(), []);
5119
+ const goal = useMemo13(() => new Vector37(), []);
4984
5120
  const nodeName = annotation?.anchorNodeName;
4985
5121
  const localPosition = annotation?.anchorLocalPosition;
4986
- useEffect13(() => {
5122
+ useEffect14(() => {
4987
5123
  nodeRef.current = null;
4988
5124
  hasPrev.current = false;
4989
5125
  }, [nodeName, annotation?.id]);
4990
- useFrame7(() => {
5126
+ useFrame8(() => {
4991
5127
  const controls = controlsRef.current;
4992
5128
  const offset = offsetRef.current;
4993
5129
  if (!enabled || !controls || !offset || !nodeName || !localPosition) {
@@ -5019,8 +5155,8 @@ var DEFAULT_AR_OVERLAY_STATE = {
5019
5155
  isModelPlaced: false
5020
5156
  };
5021
5157
  function computeMeasureBox(scene) {
5022
- const box = new Box32().setFromObject(scene);
5023
- const size = new Vector36();
5158
+ const box = new Box33().setFromObject(scene);
5159
+ const size = new Vector37();
5024
5160
  box.getSize(size);
5025
5161
  return {
5026
5162
  min: [box.min.x, box.min.y, box.min.z],
@@ -5031,8 +5167,8 @@ function computeMeasureBox(scene) {
5031
5167
  function fitClippingPlanesToScene(scene, controls) {
5032
5168
  const camera = controls?.camera;
5033
5169
  if (!(camera instanceof PerspectiveCamera3)) return;
5034
- const box = new Box32().setFromObject(scene);
5035
- const size = box.getSize(new Vector36()).length();
5170
+ const box = new Box33().setFromObject(scene);
5171
+ const size = box.getSize(new Vector37()).length();
5036
5172
  if (!Number.isFinite(size) || size <= 0) return;
5037
5173
  camera.near = size / 100;
5038
5174
  camera.far = size * 100;
@@ -5100,6 +5236,7 @@ function ModelViewer({
5100
5236
  meshopt = true,
5101
5237
  showMeasureTools = false,
5102
5238
  showExplodeControls = false,
5239
+ cinematic,
5103
5240
  measurementUnit = "m",
5104
5241
  enableXR = false,
5105
5242
  usdzUrl,
@@ -5107,48 +5244,48 @@ function ModelViewer({
5107
5244
  }) {
5108
5245
  const { activeTab, activeSubTab } = useBindingBuilderContext();
5109
5246
  const license = useLicense(licenseKey);
5110
- const resolvedSceneConfig = useMemo12(
5247
+ const resolvedSceneConfig = useMemo13(
5111
5248
  () => sceneConfig ?? DEFAULT_SCENE_CONFIG,
5112
5249
  [sceneConfig]
5113
5250
  );
5114
- const viewerBackdropColor = useMemo12(() => {
5251
+ const viewerBackdropColor = useMemo13(() => {
5115
5252
  if (backgroundColor) return backgroundColor;
5116
5253
  const bg = resolvedSceneConfig.background;
5117
5254
  if (bg.enabled && bg.type === "color") return bg.color;
5118
5255
  return "#1c1c1d";
5119
5256
  }, [backgroundColor, resolvedSceneConfig]);
5120
- const decoderConfig = useMemo12(
5257
+ const decoderConfig = useMemo13(
5121
5258
  () => ({ dracoDecoderPath, ktx2TranscoderPath, meshopt }),
5122
5259
  [dracoDecoderPath, ktx2TranscoderPath, meshopt]
5123
5260
  );
5124
- const reduceGpuLoad = useMemo12(
5261
+ const reduceGpuLoad = useMemo13(
5125
5262
  () => shouldReduceGpuLoad(performanceProfile),
5126
5263
  [performanceProfile]
5127
5264
  );
5128
- const controlsRef = useRef9(null);
5129
- useEffect13(() => {
5265
+ const controlsRef = useRef10(null);
5266
+ useEffect14(() => {
5130
5267
  if (!controlsRef.current) return;
5131
5268
  controlsRef.current.dollySpeed = Math.max(zoomSensitivity, 0);
5132
5269
  }, [zoomSensitivity]);
5133
- const tourCameraOffsetRef = useRef9(null);
5134
- const annotationWorldPositionsRef = useRef9(/* @__PURE__ */ new Map());
5135
- const fileInputRef = useRef9(null);
5136
- const pendingObjectId = useRef9(null);
5137
- const rendererCaptureRef = useRef9(null);
5138
- const createdBlobUrls = useRef9({});
5139
- useEffect13(() => {
5270
+ const tourCameraOffsetRef = useRef10(null);
5271
+ const annotationWorldPositionsRef = useRef10(/* @__PURE__ */ new Map());
5272
+ const fileInputRef = useRef10(null);
5273
+ const pendingObjectId = useRef10(null);
5274
+ const rendererCaptureRef = useRef10(null);
5275
+ const createdBlobUrls = useRef10({});
5276
+ useEffect14(() => {
5140
5277
  return () => {
5141
5278
  for (const url of Object.values(createdBlobUrls.current)) {
5142
5279
  URL.revokeObjectURL(url);
5143
5280
  }
5144
5281
  };
5145
5282
  }, []);
5146
- const nodeRefsMap = useRef9({});
5147
- const materialRefsOut = useRef9({});
5148
- const viewerLeftRef = useRef9(null);
5149
- const canvasAreaRef = useRef9(null);
5150
- const xrPresenterRef = useRef9(null);
5151
- const viewAnnotationPopupRef = useRef9(null);
5283
+ const nodeRefsMap = useRef10({});
5284
+ const materialRefsOut = useRef10({});
5285
+ const viewerLeftRef = useRef10(null);
5286
+ const canvasAreaRef = useRef10(null);
5287
+ const xrPresenterRef = useRef10(null);
5288
+ const viewAnnotationPopupRef = useRef10(null);
5152
5289
  const [internalObjectBindings, setInternalObjectBindings] = useState10(objectBindings);
5153
5290
  const [itemMaterials, setItemMaterials] = useState10({});
5154
5291
  const [currentAction, setCurrentAction] = useState10(
@@ -5162,12 +5299,12 @@ function ModelViewer({
5162
5299
  null
5163
5300
  );
5164
5301
  const [isDownloading, setIsDownloading] = useState10(false);
5165
- const sceneRef = useRef9(null);
5166
- const fitAttemptedRef = useRef9(false);
5167
- const fitInFlightRef = useRef9(false);
5168
- const cameraProvidedRef = useRef9(Boolean(camera));
5302
+ const sceneRef = useRef10(null);
5303
+ const fitAttemptedRef = useRef10(false);
5304
+ const fitInFlightRef = useRef10(false);
5305
+ const cameraProvidedRef = useRef10(Boolean(camera));
5169
5306
  cameraProvidedRef.current = Boolean(camera);
5170
- const initialCameraStateRef = useRef9(null);
5307
+ const initialCameraStateRef = useRef10(null);
5171
5308
  const [isAutoFraming, setIsAutoFraming] = useState10(Boolean(modelUrl));
5172
5309
  const [cameraPositioned, setCameraPositioned] = useState10(
5173
5310
  !modelUrl || Boolean(onAutoFit) || Boolean(camera)
@@ -5177,14 +5314,14 @@ function ModelViewer({
5177
5314
  const [modelLoadFailed, setModelLoadFailed] = useState10(false);
5178
5315
  const [bindingsMismatch, setBindingsMismatch] = useState10(false);
5179
5316
  const [mismatchDismissed, setMismatchDismissed] = useState10(false);
5180
- const currentObjectBindingsRef = useRef9({});
5317
+ const currentObjectBindingsRef = useRef10({});
5181
5318
  const captureInitialCameraState = useCallback4(() => {
5182
5319
  const controls = controlsRef.current;
5183
5320
  if (!controls || initialCameraStateRef.current) {
5184
5321
  return;
5185
5322
  }
5186
- const position = controls.getPosition(new Vector36());
5187
- const target = controls.getTarget(new Vector36());
5323
+ const position = controls.getPosition(new Vector37());
5324
+ const target = controls.getTarget(new Vector37());
5188
5325
  initialCameraStateRef.current = {
5189
5326
  position: [position.x, position.y, position.z],
5190
5327
  target: [target.x, target.y, target.z]
@@ -5195,17 +5332,45 @@ function ModelViewer({
5195
5332
  const [measurePoints, setMeasurePoints] = useState10([]);
5196
5333
  const [showDimensions, setShowDimensions] = useState10(false);
5197
5334
  const [measureBox, setMeasureBox] = useState10(null);
5198
- const measureModeRef = useRef9(false);
5335
+ const measureModeRef = useRef10(false);
5199
5336
  measureModeRef.current = measureMode;
5200
- const measurePointCounter = useRef9(0);
5201
- const measureDownRef = useRef9(null);
5337
+ const measurePointCounter = useRef10(0);
5338
+ const measureDownRef = useRef10(null);
5202
5339
  const [explodeFactor, setExplodeFactor] = useState10(0);
5203
5340
  const [explodeOpen, setExplodeOpen] = useState10(false);
5204
- useEffect13(() => {
5341
+ useEffect14(() => {
5205
5342
  setExplodeFactor(0);
5206
5343
  setExplodeOpen(false);
5207
5344
  }, [modelUrl]);
5208
- const xrStore = useMemo12(() => {
5345
+ const cinematicConfig = useMemo13(() => {
5346
+ const sc = resolvedSceneConfig.cinematic;
5347
+ const base = sc?.enabled ? {
5348
+ waypoints: sc.waypoints,
5349
+ duration: sc.duration,
5350
+ loop: sc.loop,
5351
+ autoPlay: sc.autoPlay
5352
+ } : null;
5353
+ if (cinematic === void 0) {
5354
+ return base && (base.waypoints?.length ?? 0) >= 2 ? base : null;
5355
+ }
5356
+ if (cinematic === false) return null;
5357
+ if (cinematic === true) return base ?? {};
5358
+ return { ...base ?? {}, ...cinematic };
5359
+ }, [cinematic, resolvedSceneConfig]);
5360
+ const [cinematicPlaying, setCinematicPlaying] = useState10(false);
5361
+ const cinematicAutoStartedRef = useRef10(false);
5362
+ useEffect14(() => {
5363
+ setCinematicPlaying(false);
5364
+ cinematicAutoStartedRef.current = false;
5365
+ }, [modelUrl]);
5366
+ useEffect14(() => {
5367
+ if (!cinematicConfig?.autoPlay || cinematicAutoStartedRef.current) return;
5368
+ if (modelLoaded && cameraPositioned && !isAutoFraming) {
5369
+ cinematicAutoStartedRef.current = true;
5370
+ setCinematicPlaying(true);
5371
+ }
5372
+ }, [cinematicConfig, modelLoaded, cameraPositioned, isAutoFraming]);
5373
+ const xrStore = useMemo13(() => {
5209
5374
  if (!enableXR) return null;
5210
5375
  const xr = navigator.xr;
5211
5376
  if (!xr || typeof xr.isSessionSupported !== "function" || typeof xr.addEventListener !== "function") {
@@ -5219,7 +5384,7 @@ function ModelViewer({
5219
5384
  }, [enableXR]);
5220
5385
  const [arSupported, setArSupported] = useState10(false);
5221
5386
  const [vrSupported, setVrSupported] = useState10(false);
5222
- const quickLookSupported = useMemo12(
5387
+ const quickLookSupported = useMemo13(
5223
5388
  () => enableXR && Boolean(usdzUrl?.trim()) && isAppleHandheldDevice(),
5224
5389
  [enableXR, usdzUrl]
5225
5390
  );
@@ -5228,17 +5393,17 @@ function ModelViewer({
5228
5393
  DEFAULT_AR_OVERLAY_STATE
5229
5394
  );
5230
5395
  const [relocatePressed, setRelocatePressed] = useState10(false);
5231
- useEffect13(() => {
5396
+ useEffect14(() => {
5232
5397
  if (!xrStore) return;
5233
5398
  return xrStore.subscribe(
5234
5399
  (state) => setInARSession(state.session != null && state.mode === "immersive-ar")
5235
5400
  );
5236
5401
  }, [xrStore]);
5237
- useEffect13(() => {
5402
+ useEffect14(() => {
5238
5403
  setRelocatePressed(false);
5239
5404
  setArOverlayState(DEFAULT_AR_OVERLAY_STATE);
5240
5405
  }, [inARSession]);
5241
- useEffect13(() => {
5406
+ useEffect14(() => {
5242
5407
  if (!enableXR) return;
5243
5408
  const xr = navigator.xr;
5244
5409
  if (!xr?.isSessionSupported) return;
@@ -5273,7 +5438,7 @@ function ModelViewer({
5273
5438
  setRelocatePressed(true);
5274
5439
  xrPresenterRef.current?.startPlacementMode();
5275
5440
  }, []);
5276
- useEffect13(() => {
5441
+ useEffect14(() => {
5277
5442
  if (!relocatePressed) return;
5278
5443
  const onPointerUp = () => finishRelocation(true);
5279
5444
  const onPointerCancel = () => finishRelocation(false);
@@ -5336,7 +5501,7 @@ function ModelViewer({
5336
5501
  const [annotations, setAnnotations] = useState10(
5337
5502
  () => sceneConfig?.annotations ?? []
5338
5503
  );
5339
- useEffect13(() => {
5504
+ useEffect14(() => {
5340
5505
  if (annotations.length > 0) {
5341
5506
  const maxId = annotations.reduce((max, ann) => Math.max(max, ann.id), 0);
5342
5507
  annotationCounterRef.current = maxId;
@@ -5349,15 +5514,15 @@ function ModelViewer({
5349
5514
  title: "",
5350
5515
  description: ""
5351
5516
  });
5352
- const annotationCounterRef = useRef9(0);
5517
+ const annotationCounterRef = useRef10(0);
5353
5518
  const [internalViewingAnnotation, setInternalViewingAnnotation] = useState10(null);
5354
5519
  const [currentAnnotationIndex, setCurrentAnnotationIndex] = useState10(-1);
5355
5520
  const [isNavigating, setIsNavigating] = useState10(false);
5356
- const annotationSmoothRestoreTimeoutRef = useRef9(null);
5357
- const sortedAnnotations = useMemo12(() => {
5521
+ const annotationSmoothRestoreTimeoutRef = useRef10(null);
5522
+ const sortedAnnotations = useMemo13(() => {
5358
5523
  return [...annotations].sort((a, b) => a.id - b.id);
5359
5524
  }, [annotations]);
5360
- const currentAnnotation = useMemo12(() => {
5525
+ const currentAnnotation = useMemo13(() => {
5361
5526
  if (currentAnnotationIndex < 0 || currentAnnotationIndex >= sortedAnnotations.length) {
5362
5527
  return null;
5363
5528
  }
@@ -5378,7 +5543,7 @@ function ModelViewer({
5378
5543
  const controls = controlsRef.current;
5379
5544
  const scene = sceneRef.current;
5380
5545
  if (!controls) return;
5381
- const target = new Vector36();
5546
+ const target = new Vector37();
5382
5547
  const livePosition = annotationWorldPositionsRef.current.get(
5383
5548
  annotation.id
5384
5549
  );
@@ -5396,15 +5561,15 @@ function ModelViewer({
5396
5561
  controls.smoothTime = 0.6;
5397
5562
  const storedNormal = annotation.worldNormal;
5398
5563
  if (scene) {
5399
- const box = new Box32().setFromObject(scene);
5400
- const modelCenter = new Vector36();
5564
+ const box = new Box33().setFromObject(scene);
5565
+ const modelCenter = new Vector37();
5401
5566
  box.getCenter(modelCenter);
5402
- const modelSize = new Vector36();
5567
+ const modelSize = new Vector37();
5403
5568
  box.getSize(modelSize);
5404
5569
  const modelRadius = modelSize.length() * 0.2;
5405
5570
  let outwardDir;
5406
5571
  if (storedNormal) {
5407
- outwardDir = new Vector36(
5572
+ outwardDir = new Vector37(
5408
5573
  storedNormal[0],
5409
5574
  storedNormal[1],
5410
5575
  storedNormal[2]
@@ -5502,23 +5667,23 @@ function ModelViewer({
5502
5667
  );
5503
5668
  }
5504
5669
  }, [setActiveAnnotation]);
5505
- useEffect13(() => {
5670
+ useEffect14(() => {
5506
5671
  setCurrentAnnotationIndex(-1);
5507
5672
  setIsGuidedTourActive(false);
5508
5673
  }, [sortedAnnotations.length]);
5509
- useEffect13(() => {
5674
+ useEffect14(() => {
5510
5675
  return () => {
5511
5676
  if (annotationSmoothRestoreTimeoutRef.current) {
5512
5677
  clearTimeout(annotationSmoothRestoreTimeoutRef.current);
5513
5678
  }
5514
5679
  };
5515
5680
  }, []);
5516
- useEffect13(() => {
5681
+ useEffect14(() => {
5517
5682
  if (currentAnnotation && isGuidedTourActive) {
5518
5683
  setActiveAnnotation(currentAnnotation);
5519
5684
  }
5520
5685
  }, [currentAnnotation, setActiveAnnotation, isGuidedTourActive]);
5521
- useEffect13(() => {
5686
+ useEffect14(() => {
5522
5687
  if (sceneConfig?.annotations === void 0) {
5523
5688
  return;
5524
5689
  }
@@ -5542,7 +5707,7 @@ function ModelViewer({
5542
5707
  setInternalViewingAnnotation(null);
5543
5708
  }, [controlledActiveAnnotation, onActiveAnnotationChange]);
5544
5709
  const activeAnnotation = controlledActiveAnnotation !== void 0 ? controlledActiveAnnotation : internalViewingAnnotation?.annotation ?? null;
5545
- const activeViewingAnnotation = useMemo12(() => {
5710
+ const activeViewingAnnotation = useMemo13(() => {
5546
5711
  if (!activeAnnotation) {
5547
5712
  return null;
5548
5713
  }
@@ -5551,7 +5716,7 @@ function ModelViewer({
5551
5716
  );
5552
5717
  return annotation ?? null;
5553
5718
  }, [activeAnnotation, annotations]);
5554
- useEffect13(() => {
5719
+ useEffect14(() => {
5555
5720
  if (activeAnnotation && !activeViewingAnnotation) {
5556
5721
  closeViewingAnnotation();
5557
5722
  }
@@ -5559,7 +5724,7 @@ function ModelViewer({
5559
5724
  const isControlledBindings = onObjectBindingsChange !== void 0;
5560
5725
  const currentObjectBindings = isControlledBindings ? objectBindings : internalObjectBindings;
5561
5726
  currentObjectBindingsRef.current = currentObjectBindings;
5562
- const currentHiddenObjects = useMemo12(
5727
+ const currentHiddenObjects = useMemo13(
5563
5728
  () => Object.fromEntries(
5564
5729
  Object.entries(currentObjectBindings).filter(([, binding]) => binding.visible === false).map(([bindingKey]) => [bindingKey, true])
5565
5730
  ),
@@ -5599,12 +5764,12 @@ function ModelViewer({
5599
5764
  );
5600
5765
  const isPanelOpen = showObjectBindingDataPanel && currentSelectedObject !== null && currentSelectedObjectKey !== null;
5601
5766
  const currentPanelObject = (currentSelectedObjectKey ? currentObjectBindings[currentSelectedObjectKey] : null) ?? currentSelectedObject;
5602
- useEffect13(() => {
5767
+ useEffect14(() => {
5603
5768
  if (!isControlledBindings) {
5604
5769
  setInternalObjectBindings(objectBindings);
5605
5770
  }
5606
5771
  }, [isControlledBindings, objectBindings]);
5607
- useEffect13(() => {
5772
+ useEffect14(() => {
5608
5773
  onHiddenObjectsChange?.(currentHiddenObjects);
5609
5774
  }, [currentHiddenObjects, onHiddenObjectsChange]);
5610
5775
  const applySelection = useCallback4(
@@ -5711,8 +5876,8 @@ function ModelViewer({
5711
5876
  };
5712
5877
  if (canvasContainer && cameraControls && object) {
5713
5878
  const canvasRect = canvasContainer.getBoundingClientRect();
5714
- const objectCenter = new Vector36();
5715
- new Box32().setFromObject(object).getCenter(objectCenter);
5879
+ const objectCenter = new Vector37();
5880
+ new Box33().setFromObject(object).getCenter(objectCenter);
5716
5881
  objectCenter.project(cameraControls);
5717
5882
  const projectedX = (objectCenter.x + 1) / 2 * canvasRect.width + (canvasRect.left - rect.left);
5718
5883
  const projectedY = (1 - objectCenter.y) / 2 * canvasRect.height + (canvasRect.top - rect.top);
@@ -5797,24 +5962,24 @@ function ModelViewer({
5797
5962
  resolveObjectKey
5798
5963
  ]
5799
5964
  );
5800
- useEffect13(() => {
5965
+ useEffect14(() => {
5801
5966
  if (!colorPicker) return;
5802
5967
  window.addEventListener("pointerdown", closeColorPicker);
5803
5968
  return () => window.removeEventListener("pointerdown", closeColorPicker);
5804
5969
  }, [colorPicker, closeColorPicker]);
5805
- useEffect13(() => {
5970
+ useEffect14(() => {
5806
5971
  if (!materialPopup) return;
5807
5972
  const close = () => setMaterialPopup(null);
5808
5973
  window.addEventListener("pointerdown", close);
5809
5974
  return () => window.removeEventListener("pointerdown", close);
5810
5975
  }, [materialPopup]);
5811
- useEffect13(() => {
5976
+ useEffect14(() => {
5812
5977
  if (!downloadMenuOpen) return;
5813
5978
  const close = () => setDownloadMenuOpen(false);
5814
5979
  window.addEventListener("pointerdown", close);
5815
5980
  return () => window.removeEventListener("pointerdown", close);
5816
5981
  }, [downloadMenuOpen]);
5817
- useEffect13(() => {
5982
+ useEffect14(() => {
5818
5983
  if (!activeViewingAnnotation) return;
5819
5984
  const handleClickOutside = (event) => {
5820
5985
  if (viewAnnotationPopupRef.current && !viewAnnotationPopupRef.current.contains(event.target)) {
@@ -5832,7 +5997,7 @@ function ModelViewer({
5832
5997
  document.removeEventListener("touchstart", handleClickOutside);
5833
5998
  };
5834
5999
  }, [activeViewingAnnotation, closeViewingAnnotation]);
5835
- useEffect13(() => {
6000
+ useEffect14(() => {
5836
6001
  if (!pendingAnnotation) return;
5837
6002
  const handleClickOutside = (event) => {
5838
6003
  const target = event.target;
@@ -5913,7 +6078,7 @@ function ModelViewer({
5913
6078
  delete createdBlobUrls.current[targetKey];
5914
6079
  }
5915
6080
  };
5916
- const onViewerReadyRef = useRef9(onViewerReady);
6081
+ const onViewerReadyRef = useRef10(onViewerReady);
5917
6082
  onViewerReadyRef.current = onViewerReady;
5918
6083
  const notifyViewerReady = useCallback4(() => {
5919
6084
  const controls = controlsRef.current;
@@ -5938,14 +6103,14 @@ function ModelViewer({
5938
6103
  }
5939
6104
  try {
5940
6105
  if (flyIn) {
5941
- const target = controls.getTarget(new Vector36());
5942
- const position = controls.getPosition(new Vector36());
6106
+ const target = controls.getTarget(new Vector37());
6107
+ const position = controls.getPosition(new Vector37());
5943
6108
  const direction = position.clone().sub(target);
5944
6109
  if (direction.lengthSq() < 1e-6) {
5945
6110
  direction.set(0, 0, 1);
5946
6111
  }
5947
6112
  direction.normalize();
5948
- const sphere = new Box32().setFromObject(scene).getBoundingSphere(new Sphere());
6113
+ const sphere = new Box33().setFromObject(scene).getBoundingSphere(new Sphere2());
5949
6114
  const farDistance = Math.max(sphere.radius * 10, 20);
5950
6115
  const farPosition = target.clone().add(direction.multiplyScalar(farDistance));
5951
6116
  await controls.setLookAt(
@@ -5979,7 +6144,7 @@ function ModelViewer({
5979
6144
  },
5980
6145
  [captureInitialCameraState, notifyViewerReady]
5981
6146
  );
5982
- useEffect13(() => {
6147
+ useEffect14(() => {
5983
6148
  fitAttemptedRef.current = false;
5984
6149
  fitInFlightRef.current = false;
5985
6150
  initialCameraStateRef.current = null;
@@ -6051,7 +6216,7 @@ function ModelViewer({
6051
6216
  notifyViewerReady,
6052
6217
  onAutoFit
6053
6218
  ]);
6054
- useEffect13(() => {
6219
+ useEffect14(() => {
6055
6220
  if (!refitOnResize) {
6056
6221
  return;
6057
6222
  }
@@ -6082,6 +6247,7 @@ function ModelViewer({
6082
6247
  const handleNodeClick = useCallback4(
6083
6248
  (binding) => {
6084
6249
  if (measureModeRef.current) return;
6250
+ setCinematicPlaying(false);
6085
6251
  closeColorPicker();
6086
6252
  setMaterialPopup(null);
6087
6253
  setCurrentAction(null);
@@ -6159,7 +6325,7 @@ function ModelViewer({
6159
6325
  },
6160
6326
  [applySelection, resolveBindingKey2, disableZoom]
6161
6327
  );
6162
- useEffect13(() => {
6328
+ useEffect14(() => {
6163
6329
  notifyViewerReady();
6164
6330
  }, [notifyViewerReady]);
6165
6331
  const handleCameraChangeInternal = useCallback4(
@@ -6190,7 +6356,7 @@ function ModelViewer({
6190
6356
  },
6191
6357
  [onLoadError]
6192
6358
  );
6193
- useEffect13(() => {
6359
+ useEffect14(() => {
6194
6360
  const scene = sceneRef.current;
6195
6361
  if (!modelLoaded || !scene) {
6196
6362
  setBindingsMismatch(false);
@@ -6251,7 +6417,7 @@ function ModelViewer({
6251
6417
  },
6252
6418
  []
6253
6419
  );
6254
- useEffect13(() => {
6420
+ useEffect14(() => {
6255
6421
  setMeasurePoints([]);
6256
6422
  }, [modelUrl, resolvedSceneConfig.model.rotation]);
6257
6423
  const handleDownloadMesh = useCallback4(() => {
@@ -6560,6 +6726,17 @@ function ModelViewer({
6560
6726
  offsetRef: tourCameraOffsetRef
6561
6727
  }
6562
6728
  ),
6729
+ cinematicConfig && /* @__PURE__ */ jsx19(
6730
+ CinematicCamera,
6731
+ {
6732
+ controlsRef,
6733
+ sceneRef,
6734
+ config: cinematicConfig,
6735
+ playing: cinematicPlaying,
6736
+ onFinished: () => setCinematicPlaying(false),
6737
+ onUserInteract: () => setCinematicPlaying(false)
6738
+ }
6739
+ ),
6563
6740
  showMeasureTools && /* @__PURE__ */ jsx19(
6564
6741
  MeasurementLayer,
6565
6742
  {
@@ -6665,6 +6842,26 @@ function ModelViewer({
6665
6842
  ]
6666
6843
  }
6667
6844
  ),
6845
+ cinematicConfig && /* @__PURE__ */ jsx19(
6846
+ "div",
6847
+ {
6848
+ className: "ri-cinematicTools",
6849
+ style: {
6850
+ top: `${16 + ((showMeasureTools ? 1 : 0) + (showExplodeControls ? 1 : 0)) * 42}px`
6851
+ },
6852
+ children: /* @__PURE__ */ jsx19(
6853
+ "button",
6854
+ {
6855
+ type: "button",
6856
+ className: `ri-measureButton ${cinematicPlaying ? "ri-measureButton--active" : ""}`,
6857
+ onClick: () => setCinematicPlaying((prev) => !prev),
6858
+ title: cinematicPlaying ? "Pause cinematic" : "Play cinematic",
6859
+ "aria-pressed": cinematicPlaying,
6860
+ children: cinematicPlaying ? /* @__PURE__ */ jsx19(Pause, { size: 16 }) : /* @__PURE__ */ jsx19(Play, { size: 16 })
6861
+ }
6862
+ )
6863
+ }
6864
+ ),
6668
6865
  enableXR && (xrStore && (arSupported || vrSupported) || quickLookSupported) && /* @__PURE__ */ jsxs15("div", { className: "ri-xrTools", children: [
6669
6866
  !arSupported && quickLookSupported && /* @__PURE__ */ jsxs15(
6670
6867
  "button",
@@ -6950,7 +7147,7 @@ function ModelViewer({
6950
7147
  }
6951
7148
 
6952
7149
  // src/components/BindingBuilder/BindingBuilder.tsx
6953
- import { useCallback as useCallback8, useEffect as useEffect18, useMemo as useMemo15, useRef as useRef16, useState as useState24 } from "react";
7150
+ import { useCallback as useCallback8, useEffect as useEffect19, useMemo as useMemo16, useRef as useRef17, useState as useState24 } from "react";
6954
7151
  import {
6955
7152
  Redo2,
6956
7153
  Undo2,
@@ -6961,11 +7158,11 @@ import {
6961
7158
  } from "lucide-react";
6962
7159
 
6963
7160
  // src/styles/BindingBuilder.css
6964
- styleInject('.bb-root {\n --bb-font-family:\n Inter,\n "Segoe UI",\n Helvetica,\n Arial,\n sans-serif;\n --bb-app-bg:\n radial-gradient(\n \n circle at top left,\n rgba(226, 232, 240, 0.8),\n transparent 30% ),\n linear-gradient(\n 135deg,\n #f6f1e8 0%,\n #ebe8df 40%,\n #e9edf2 100%);\n --bb-app-text: #111827;\n --bb-sidebar-bg:\n linear-gradient(\n 180deg,\n rgba(20, 27, 45, 0.98),\n rgba(25, 34, 57, 0.95)),\n #121826;\n --bb-sidebar-text: #f8fafc;\n --bb-sidebar-muted-text: #b3bfd2;\n --bb-sidebar-subtle-text: #94a3b8;\n --bb-sidebar-border: rgba(148, 163, 184, 0.18);\n --bb-sidebar-border-hover: rgba(148, 163, 184, 0.32);\n --bb-sidebar-divider: rgba(148, 163, 184, 0.14);\n --bb-sidebar-surface: rgba(255, 255, 255, 0.04);\n --bb-sidebar-surface-hover: rgba(255, 255, 255, 0.08);\n --bb-sidebar-surface-strong: rgba(255, 255, 255, 0.05);\n --bb-sidebar-eyebrow-bg: rgba(148, 163, 184, 0.16);\n --bb-sidebar-eyebrow-text: #cbd5e1;\n --bb-sidebar-left-shadow: 18px 0 42px rgba(15, 23, 42, 0.12);\n --bb-sidebar-right-shadow: -4px 0 24px rgba(0, 0, 0, 0.35);\n --bb-sidebar-chip-bg: rgba(15, 23, 42, 0.42);\n --bb-sidebar-chip-border: rgba(148, 163, 184, 0.14);\n --bb-sidebar-accent: rgba(99, 102, 241, 0.18);\n --bb-sidebar-accent-border: rgba(99, 102, 241, 0.4);\n --bb-sidebar-accent-text: #f8fafc;\n --bb-menu-bg: #1a1f2e;\n --bb-menu-shadow: 0 16px 32px rgba(15, 23, 42, 0.4);\n display: flex;\n height: 100%;\n min-height: 0;\n width: 100%;\n background: var(--bb-app-bg);\n color: var(--bb-app-text);\n font-family: var(--bb-font-family);\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n text-rendering: optimizeLegibility;\n font-synthesis: none;\n}\n.bb-root * {\n box-sizing: border-box;\n}\n.bb-root input,\n.bb-root select,\n.bb-root textarea,\n.bb-root button {\n font-family: inherit;\n}\n.bb-sidebar {\n width: 400px;\n min-height: 0;\n flex-shrink: 0;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n background: var(--bb-sidebar-bg);\n color: var(--bb-sidebar-text);\n transition: width 0.24s ease;\n}\n.bb-sidebar--left {\n border-right: 1px solid var(--bb-sidebar-border);\n box-shadow: var(--bb-sidebar-left-shadow);\n}\n.bb-sidebar--right {\n width: 300px;\n border-left: 1px solid var(--bb-sidebar-border);\n box-shadow: var(--bb-sidebar-right-shadow);\n}\n.bb-sidebar--collapsed {\n width: 36px;\n}\n.bb-sidebar--collapsed .bb-header {\n padding: 5px;\n display: flex;\n justify-content: center;\n}\n.bb-sidebar--collapsed .bb-header--sidebar {\n min-height: 36px;\n}\n.bb-sidebar--collapsed .bb-sidebarToggle {\n width: 26px;\n height: 26px;\n font-size: 12px;\n}\n.bb-header {\n padding: 5px 20px;\n border-bottom: 1px solid var(--bb-sidebar-divider);\n}\n.bb-header--sidebar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n min-height: 55px;\n}\n.bb-eyebrow {\n display: inline-block;\n margin-bottom: 12px;\n padding: 6px 10px;\n border-radius: 999px;\n background: var(--bb-sidebar-eyebrow-bg);\n color: var(--bb-sidebar-eyebrow-text);\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.18em;\n text-transform: uppercase;\n}\n.bb-title {\n margin: 0 0 0px;\n font-size: 20px;\n line-height: 0.95;\n letter-spacing: -0.04em;\n font-weight: 800;\n color: var(--bb-sidebar-text);\n}\n.bb-sidebarToggle {\n width: 36px;\n height: 36px;\n flex-shrink: 0;\n border-radius: 6px;\n border: 1px solid var(--bb-sidebar-border);\n background: var(--bb-sidebar-surface-strong);\n color: var(--bb-sidebar-text);\n font-size: 16px;\n font-weight: 800;\n cursor: pointer;\n transition:\n transform 0.18s ease,\n background-color 0.18s ease,\n border-color 0.18s ease;\n}\n.bb-sidebarToggle:hover {\n transform: translateY(-1px);\n background: var(--bb-sidebar-surface-hover);\n border-color: var(--bb-sidebar-border-hover);\n}\n.bb-subtitle {\n margin: 0;\n max-width: 32ch;\n font-size: 15px;\n line-height: 1.7;\n color: var(--bb-sidebar-muted-text);\n}\n.bb-statRow {\n display: grid;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n gap: 10px;\n margin-top: 22px;\n}\n.bb-statChip {\n display: flex;\n flex-direction: column;\n gap: 4px;\n min-width: 0;\n padding: 12px 14px;\n border-radius: 8px;\n background: var(--bb-sidebar-chip-bg);\n border: 1px solid var(--bb-sidebar-chip-border);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04);\n}\n.bb-statChip strong {\n font-size: 13px;\n color: var(--bb-sidebar-text);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.bb-statLabel {\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n color: var(--bb-sidebar-subtle-text);\n}\n.bb-tabRow {\n display: flex;\n gap: 6px;\n padding: 10px 20px 10px;\n}\n.bb-tab {\n flex: 1;\n padding: 6px 10px;\n border-radius: 6px;\n border: 1px solid var(--bb-sidebar-border);\n background: var(--bb-sidebar-surface);\n color: var(--bb-sidebar-muted-text);\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n cursor: pointer;\n transition:\n background-color 0.18s ease,\n border-color 0.18s ease,\n color 0.18s ease;\n}\n.bb-tab:hover {\n background: var(--bb-sidebar-surface-hover);\n border-color: var(--bb-sidebar-border-hover);\n}\n.bb-tab--active {\n background: var(--bb-sidebar-accent);\n border-color: var(--bb-sidebar-accent-border);\n color: var(--bb-sidebar-accent-text);\n}\n.bb-toolbar {\n display: flex;\n justify-content: space-between;\n align-items: center;\n gap: 10px;\n padding: 8px 20px 8px;\n border-bottom: 1px solid var(--bb-sidebar-divider);\n flex-wrap: wrap;\n}\n.bb-toolbarGroup {\n display: flex;\n align-items: center;\n gap: 6px;\n min-width: 0;\n}\n.bb-toolbarDivider {\n width: 1px;\n align-self: stretch;\n background: var(--bb-sidebar-divider);\n margin: 0;\n}\n.bb-iconButton {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n padding: 0;\n border-radius: 7px;\n border: 1px solid var(--bb-sidebar-border);\n background: var(--bb-sidebar-surface);\n color: var(--bb-sidebar-text);\n cursor: pointer;\n transition: background-color 0.18s ease, border-color 0.18s ease;\n}\n.bb-iconButton:hover:not(:disabled) {\n background: var(--bb-sidebar-surface-hover);\n border-color: var(--bb-sidebar-border-hover);\n}\n.bb-iconButton:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n.bb-exportMenuWrapper {\n position: relative;\n display: inline-flex;\n}\n.bb-exportMenu {\n position: absolute;\n bottom: calc(100% + 6px);\n right: 0;\n z-index: 20;\n display: flex;\n flex-direction: column;\n min-width: 150px;\n padding: 6px;\n border-radius: 9px;\n border: 1px solid var(--bb-sidebar-border);\n background: var(--bb-menu-bg);\n box-shadow: var(--bb-menu-shadow);\n}\n.bb-exportMenuItem {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 10px;\n border: none;\n border-radius: 6px;\n background: transparent;\n color: var(--bb-sidebar-text);\n font-size: 12px;\n font-weight: 600;\n text-align: left;\n cursor: pointer;\n}\n.bb-exportMenuItem:hover {\n background: var(--bb-sidebar-surface-hover);\n}\n@media (max-width: 640px) {\n .bb-toolbarDivider {\n width: 100%;\n height: 1px;\n }\n}\n.bb-button,\n.bb-fileLabel {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n min-height: 32px;\n padding: 0 12px;\n border-radius: 7px;\n border: 1px solid var(--bb-sidebar-border);\n background: var(--bb-sidebar-surface);\n color: var(--bb-sidebar-text);\n font-size: 12px;\n font-weight: 700;\n cursor: pointer;\n transition:\n transform 0.18s ease,\n background-color 0.18s ease,\n border-color 0.18s ease,\n box-shadow 0.18s ease;\n}\n.bb-button:hover,\n.bb-fileLabel:hover {\n transform: translateY(-1px);\n background: rgba(255, 255, 255, 0.08);\n border-color: rgba(148, 163, 184, 0.32);\n box-shadow: 0 10px 18px rgba(15, 23, 42, 0.18);\n}\n.bb-button:disabled,\n.bb-fileLabel:has(.bb-hiddenInput:disabled) {\n opacity: 0.55;\n cursor: not-allowed;\n transform: none;\n box-shadow: none;\n}\n.bb-button--primary {\n background:\n linear-gradient(\n 135deg,\n #f59e0b,\n #fb7185);\n border-color: transparent;\n color: #111827;\n}\n.bb-button--primary:hover {\n background:\n linear-gradient(\n 135deg,\n #fbbf24,\n #fb7185);\n}\n.bb-button--ghost {\n background: rgba(248, 250, 252, 0.08);\n}\n.bb-hiddenInput {\n display: none;\n}\n.bb-statusCard {\n margin-top: 4px;\n padding: 14px 16px;\n border-radius: 9px;\n background: rgba(15, 23, 42, 0.44);\n border: 1px solid rgba(148, 163, 184, 0.15);\n}\n.bb-statusCard--error {\n background: rgba(127, 29, 29, 0.32);\n border-color: rgba(248, 113, 113, 0.24);\n}\n.bb-statusCard--success {\n background: rgba(6, 78, 59, 0.34);\n border-color: rgba(52, 211, 153, 0.24);\n}\n.bb-statusTitle {\n margin-bottom: 6px;\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n color: #cbd5e1;\n}\n.bb-status {\n font-size: 13px;\n line-height: 1.55;\n color: #e2e8f0;\n}\n.bb-status--error {\n color: #fecaca;\n}\n.bb-status--success {\n color: #bbf7d0;\n}\n.bb-input--error {\n border-color: #dc2626;\n}\n.bb-nodeList {\n display: flex;\n flex-direction: column;\n flex: 1 1 auto;\n min-height: 0;\n overflow: hidden;\n background: #141824;\n}\n.bb-sectionHead {\n padding: 24px 20px 16px;\n border-bottom: 1px solid rgba(255, 255, 255, 0.07);\n}\n.bb-sectionKicker {\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.12em;\n text-transform: uppercase;\n color: #64748b;\n}\n.bb-sectionHeading {\n margin-top: 4px;\n font-size: 22px;\n font-weight: 700;\n line-height: 1.2;\n color: #f1f5f9;\n}\n.bb-search {\n padding: 14px 16px;\n border-bottom: 1px solid rgba(255, 255, 255, 0.07);\n}\n.bb-input,\n.bb-select,\n.bb-textarea {\n width: 100%;\n border-radius: 10px;\n border: 1px solid rgba(255, 255, 255, 0.08);\n background: rgba(255, 255, 255, 0.05);\n color: #e2e8f0;\n padding: 10px 14px;\n font-size: 13px;\n line-height: 1.45;\n}\n.bb-input::placeholder,\n.bb-textarea::placeholder {\n color: #475569;\n}\n.bb-input--compact {\n width: 64px;\n max-width: 64px;\n flex: 0 0 auto;\n padding: 7px 4px;\n text-align: center;\n}\n.bb-input:focus,\n.bb-select:focus,\n.bb-textarea:focus {\n outline: none;\n border-color: rgba(255, 255, 255, 0.18);\n background: rgba(255, 255, 255, 0.08);\n box-shadow: none;\n}\n.bb-textarea {\n min-height: 110px;\n resize: vertical;\n font-family:\n ui-monospace,\n SFMono-Regular,\n Menlo,\n Monaco,\n Consolas,\n monospace;\n}\n.bb-nodeItems {\n min-height: 0;\n overflow-y: auto;\n padding: 10px 0;\n}\n.bb-nodeButton {\n width: 100%;\n border: 0;\n border-radius: 0;\n background: transparent;\n padding: 12px 20px;\n margin-bottom: 0;\n text-align: left;\n cursor: pointer;\n transition: background-color 0.12s ease, color 0.12s ease;\n border-bottom: 1px solid rgba(255, 255, 255, 0.04);\n}\n.bb-nodeButton:hover {\n background: rgba(255, 255, 255, 0.05);\n}\n.bb-nodeButton--active {\n background: rgba(255, 255, 255, 0.05);\n box-shadow: none;\n}\n.bb-nodeName {\n display: block;\n margin-bottom: 2px;\n font-size: 14px;\n font-weight: 600;\n color: #e2e8f0;\n}\n.bb-nodeMeta {\n display: block;\n font-size: 11px;\n color: #94a3b8;\n}\n.bb-editor {\n flex: 1 1 auto;\n min-height: 0;\n height: 100%;\n overflow-y: auto;\n padding: 16px 16px 28px;\n overscroll-behavior: contain;\n}\n.bb-editorHeader {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 12px;\n margin-bottom: 18px;\n padding: 18px;\n border-radius: 11px;\n background: rgba(255, 255, 255, 0.05);\n border: 1px solid rgba(148, 163, 184, 0.12);\n}\n.bb-editorTitle {\n margin: 8px 0 0;\n font-size: 24px;\n line-height: 1.1;\n color: #f8fafc;\n}\n.bb-editorMeta {\n display: flex;\n flex-wrap: wrap;\n gap: 8px 12px;\n margin-top: 10px;\n font-size: 12px;\n color: #94a3b8;\n}\n.bb-editorBadge {\n flex-shrink: 0;\n padding: 8px 12px;\n border-radius: 999px;\n background: rgba(251, 191, 36, 0.12);\n border: 1px solid rgba(251, 191, 36, 0.2);\n color: #fcd34d;\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.12em;\n text-transform: uppercase;\n}\n.bb-emptyEditor {\n padding: 28px 8px;\n font-size: 14px;\n color: #a7b1c3;\n}\n.bb-section {\n margin-bottom: 10px;\n border-radius: 7px;\n background: rgba(255, 255, 255, 0.03);\n border: 1px solid rgba(148, 163, 184, 0.12);\n overflow: hidden;\n}\n.bb-sectionHeaderButton {\n display: flex;\n width: 100%;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n border: none;\n background: rgba(255, 255, 255, 0.03);\n padding: 11px 14px;\n cursor: pointer;\n text-align: left;\n color: inherit;\n}\n.bb-sectionHeaderButton:hover {\n background: rgba(255, 255, 255, 0.06);\n}\n.bb-sectionHeaderMain {\n display: flex;\n min-width: 0;\n align-items: center;\n gap: 8px;\n}\n.bb-sectionChevron {\n flex-shrink: 0;\n color: #8b97ab;\n transform: rotate(-90deg);\n transition: transform 0.18s ease;\n}\n.bb-sectionChevron--open {\n transform: rotate(0deg);\n}\n.bb-sectionBody {\n display: flex;\n flex-direction: column;\n gap: 4px;\n padding: 14px;\n border-top: 1px solid rgba(148, 163, 184, 0.1);\n}\n.bb-sectionTitle {\n margin: 0;\n font-size: 11px;\n font-weight: 700;\n color: #c3cbdb;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n}\n.bb-sectionHelp {\n display: inline-flex;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n width: 18px;\n height: 18px;\n margin-left: 2px;\n border-radius: 999px;\n color: #b9c0ca;\n cursor: help;\n transition:\n background 0.18s ease,\n color 0.18s ease,\n transform 0.18s ease;\n}\n.bb-sectionHelp:hover {\n background: rgba(255, 255, 255, 0.08);\n color: #eef2ff;\n transform: translateY(-0.5px);\n}\n.bb-grid {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 10px;\n}\n.bb-presetGallery {\n display: flex;\n justify-items: center;\n flex-wrap: wrap;\n gap: 4px;\n margin-bottom: 14px;\n}\n.bb-presetSwatch {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 5px;\n width: 64px;\n padding: 6px 4px;\n border-radius: 8px;\n border: 1px solid rgba(148, 163, 184, 0.18);\n background: rgba(148, 163, 184, 0.06);\n cursor: pointer;\n transition: border-color 0.15s, background 0.15s;\n}\n.bb-presetSwatch:hover:not(:disabled) {\n border-color: rgba(148, 163, 184, 0.4);\n background: rgba(148, 163, 184, 0.12);\n}\n.bb-presetSwatch:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n.bb-presetSwatchColor {\n width: 32px;\n height: 32px;\n border-radius: 999px;\n border: 1px solid rgba(255, 255, 255, 0.18);\n}\n.bb-presetSwatchLabel {\n font-size: 10px;\n font-weight: 600;\n color: #cbd5e1;\n text-align: center;\n line-height: 1.2;\n}\n.bb-field {\n display: flex;\n flex-direction: column;\n gap: 5px;\n}\n.bb-field--full {\n grid-column: 1 / -1;\n}\n.bb-label {\n font-size: 12px;\n font-weight: 700;\n color: #d7dee9;\n}\n.bb-note {\n font-size: 11px;\n color: #8b97ab;\n}\n.bb-tierLock {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 16px 14px;\n border-radius: 7px;\n background: rgba(251, 191, 36, 0.08);\n border: 1px dashed rgba(251, 191, 36, 0.3);\n color: #fcd34d;\n}\n.bb-tierLockMessage {\n margin: 0;\n font-size: 12px;\n font-weight: 600;\n}\n.bb-root--licenseChecking,\n.bb-root--licenseInvalid {\n align-items: center;\n justify-content: center;\n}\n.bb-readonly {\n background: rgba(148, 163, 184, 0.08);\n color: #9ca3af;\n}\n.bb-checkboxRow {\n display: flex;\n flex-wrap: wrap;\n gap: 10px;\n}\n.bb-checkbox {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 6px 5px;\n border-radius: 7px;\n background: rgba(15, 23, 42, 0.22);\n border: 1px solid rgba(148, 163, 184, 0.12);\n font-size: 13px;\n color: #d7dee9;\n}\n.bb-checkbox input {\n accent-color: #f59e0b;\n}\n.bb-sliderField {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n.bb-slider {\n flex: 1 1 auto;\n min-width: 0;\n accent-color: #2aa8d8;\n}\n.bb-slider--unset {\n opacity: 0.45;\n}\n.bb-fileField {\n position: relative;\n display: flex;\n align-items: center;\n}\n.bb-fileField-input {\n width: 100%;\n padding-right: 40px;\n}\n.bb-fileFieldButton {\n position: absolute;\n right: 6px;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 26px;\n height: 26px;\n border: none;\n border-radius: 5px;\n background: transparent;\n color: #aab4c5;\n cursor: pointer;\n}\n.bb-fileFieldButton:hover {\n background: rgba(255, 255, 255, 0.08);\n color: #f8fafc;\n}\n.bb-materialPanel {\n margin-top: 8px;\n border: 1px solid rgba(148, 163, 184, 0.14);\n border-radius: 6px;\n background: rgba(15, 23, 42, 0.28);\n overflow: hidden;\n}\n.bb-materialPanel:first-of-type {\n margin-top: 0;\n}\n.bb-materialPanelHeader {\n display: flex;\n width: 100%;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n border: none;\n padding: 10px 12px;\n background: rgba(255, 255, 255, 0.03);\n cursor: pointer;\n text-align: left;\n color: inherit;\n}\n.bb-materialPanelHeader:hover {\n background: rgba(255, 255, 255, 0.06);\n}\n.bb-materialPanelTitleWrap {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n color: #dbe4f0;\n}\n.bb-materialPanelChevron {\n flex-shrink: 0;\n color: #8b97ab;\n transform: rotate(-90deg);\n transition: transform 0.18s ease;\n}\n.bb-materialPanelChevron--open {\n transform: rotate(0deg);\n}\n.bb-materialPanelTitle {\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n}\n.bb-materialPanelHelp {\n display: inline-flex;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n width: 18px;\n height: 18px;\n margin-left: 2px;\n border-radius: 999px;\n color: #b9c0ca;\n cursor: help;\n transition:\n background 0.18s ease,\n color 0.18s ease,\n transform 0.18s ease;\n}\n.bb-materialPanelHelp:hover {\n background: rgba(255, 255, 255, 0.08);\n color: #eef2ff;\n transform: translateY(-0.5px);\n}\n.bb-materialPanelBody {\n padding: 12px;\n border-top: 1px solid rgba(148, 163, 184, 0.1);\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n.bb-actionList {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n.bb-actionCard {\n border: 1px solid rgba(148, 163, 184, 0.14);\n border-radius: 9px;\n padding: 14px;\n background: rgba(15, 23, 42, 0.2);\n}\n.bb-actionCardHeader {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 10px;\n margin-bottom: 12px;\n}\n.bb-actionCardTitle {\n font-size: 13px;\n font-weight: 700;\n color: #f8fafc;\n}\n.bb-actionCardMeta {\n flex-shrink: 0;\n padding: 5px 9px;\n border-radius: 999px;\n background: rgba(148, 163, 184, 0.12);\n color: #94a3b8;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.12em;\n text-transform: uppercase;\n}\n.bb-actionGrid {\n display: grid;\n grid-template-columns: 1fr;\n gap: 10px;\n}\n.bb-actionFooter {\n margin-top: 12px;\n}\n.bb-actionRemove {\n width: 100%;\n}\n.bb-previewShell {\n flex: 1 1 auto;\n min-width: 0;\n min-height: 0;\n display: flex;\n flex-direction: column;\n padding: 5px;\n overflow: hidden;\n}\n.bb-previewHeader {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 16px;\n padding: 0 4px 10px;\n}\n.bb-previewControls {\n display: flex;\n align-items: flex-start;\n justify-content: flex-end;\n gap: 10px;\n flex-wrap: wrap;\n}\n.bb-previewTitle {\n margin: 2px 0 0;\n font-size: 20px;\n line-height: 1.05;\n letter-spacing: -0.03em;\n color: #1f2937;\n}\n.bb-previewToggles {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 2px;\n}\n.bb-checkbox--compact {\n gap: 5px;\n padding: 0;\n background: transparent;\n border: none;\n font-size: 10px;\n font-weight: 500;\n line-height: 1;\n color: #4b5563;\n}\n.bb-checkbox--compact:hover {\n background: transparent;\n transform: none;\n}\n.bb-checkbox--compact input[type=checkbox] {\n width: 11px;\n height: 11px;\n}\n.bb-previewMeta {\n display: flex;\n flex-wrap: wrap;\n justify-content: flex-end;\n gap: 8px;\n}\n.bb-removeModelButton {\n background-color: rgb(174, 54, 54);\n min-height: 34px;\n padding: 0 14px;\n}\n.bb-removeModelButton:hover {\n background-color: rgb(163, 20, 20);\n}\n.bb-previewMeta span {\n display: inline-flex;\n align-items: center;\n min-height: 34px;\n padding: 0 12px;\n border-radius: 999px;\n background: rgba(255, 255, 255, 0.7);\n border: 1px solid rgba(148, 163, 184, 0.18);\n color: #475569;\n font-size: 12px;\n font-weight: 700;\n}\n.bb-preview {\n position: relative;\n flex: 1 1 auto;\n min-width: 0;\n min-height: 0;\n overflow: hidden;\n border-radius: 14px;\n background:\n radial-gradient(\n circle at top left,\n rgba(255, 255, 255, 0.92),\n rgba(236, 240, 244, 0.82)),\n #f8fafc;\n border: 1px solid rgba(148, 163, 184, 0.24);\n box-shadow: 0 24px 48px rgba(148, 163, 184, 0.2), inset 0 1px 0 rgba(255, 255, 255, 0.7);\n}\n.bb-loadingOverlay {\n position: absolute;\n inset: 0;\n z-index: 5;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 16px;\n padding: 24px;\n text-align: center;\n border-radius: 14px;\n background: rgba(248, 250, 252, 0.82);\n backdrop-filter: blur(2px);\n}\n.bb-loadingSpinner {\n width: 40px;\n height: 40px;\n border-radius: 999px;\n border: 3px solid rgba(148, 163, 184, 0.3);\n border-top-color: #2563eb;\n animation: bb-spin 0.8s linear infinite;\n}\n@keyframes bb-spin {\n to {\n transform: rotate(360deg);\n }\n}\n.bb-loadingMessage {\n max-width: 320px;\n color: #475569;\n font-size: 13px;\n font-weight: 600;\n line-height: 1.5;\n}\n.bb-previewStatus {\n max-width: 280px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.bb-previewStatus--success {\n background: rgba(34, 197, 94, 0.12) !important;\n border-color: rgba(34, 197, 94, 0.3) !important;\n color: #15803d !important;\n}\n.bb-previewStatus--error {\n background: rgba(239, 68, 68, 0.12) !important;\n border-color: rgba(239, 68, 68, 0.3) !important;\n color: #b91c1c !important;\n}\n.bb-previewModelName {\n max-width: 200px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.bb-previewPlaceholder {\n position: relative;\n display: flex;\n height: 100%;\n width: 100%;\n align-items: center;\n justify-content: center;\n padding: 40px;\n text-align: center;\n color: #64748b;\n font-size: 15px;\n line-height: 1.7;\n border-radius: 14px;\n border: 2px dashed rgba(148, 163, 184, 0.24);\n background:\n radial-gradient(\n circle at top,\n rgba(251, 191, 36, 0.12),\n transparent 30%),\n linear-gradient(\n 180deg,\n rgba(255, 255, 255, 0.82),\n rgba(238, 242, 248, 0.9));\n transition:\n border-color 0.2s ease,\n background 0.2s ease,\n transform 0.2s ease,\n box-shadow 0.2s ease;\n}\n.bb-previewPlaceholder > div {\n max-width: 420px;\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 14px;\n}\n.bb-previewPlaceholderTitle {\n font-size: 28px;\n line-height: 1.05;\n letter-spacing: -0.03em;\n font-weight: 800;\n color: #1f2937;\n}\n.bb-previewPlaceholderButton {\n margin-top: 8px;\n border: 0;\n border-radius: 999px;\n padding: 13px 22px;\n background:\n linear-gradient(\n 135deg,\n #f59e0b,\n #fb7185);\n color: #111827;\n font-size: 14px;\n font-weight: 800;\n cursor: pointer;\n transition:\n transform 0.18s ease,\n box-shadow 0.18s ease,\n opacity 0.18s ease;\n}\n.bb-previewPlaceholderButton:hover:not(:disabled) {\n transform: translateY(-1px);\n box-shadow: 0 14px 30px rgba(251, 113, 133, 0.24);\n}\n.bb-previewPlaceholderButton:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n.bb-previewPlaceholder--dragActive {\n border-color: rgba(245, 158, 11, 0.75);\n background:\n radial-gradient(\n circle at top,\n rgba(251, 191, 36, 0.22),\n transparent 32%),\n linear-gradient(\n 180deg,\n rgba(255, 252, 245, 0.95),\n rgba(255, 244, 230, 0.92));\n transform: scale(0.995);\n box-shadow: inset 0 0 0 1px rgba(245, 158, 11, 0.14);\n}\n.bb-sectionHeader {\n cursor: pointer;\n user-select: none;\n}\n.bb-iconButton {\n background: none;\n border: none;\n cursor: pointer;\n font-size: 16px;\n color: var(--bb-sidebar-subtle-text);\n transition: color 0.2s ease;\n padding: 4px 8px;\n border-radius: 4px;\n}\n.bb-iconButton:hover {\n color: var(--bb-sidebar-text);\n background: var(--bb-sidebar-surface-strong);\n}\n.bb-cameraLive,\n.bb-cameraSaved {\n background: rgba(15, 23, 42, 0.44);\n border-radius: 8px;\n padding: 14px;\n margin-bottom: 16px;\n border: 1px solid rgba(148, 163, 184, 0.15);\n transition: all 0.2s ease;\n}\n.bb-cameraLive:hover,\n.bb-cameraSaved:hover {\n border-color: rgba(148, 163, 184, 0.25);\n background: rgba(15, 23, 42, 0.52);\n}\n.bb-cameraLiveHeader,\n.bb-cameraSavedHeader {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 12px;\n padding-bottom: 8px;\n border-bottom: 1px solid rgba(148, 163, 184, 0.15);\n}\n.bb-cameraLiveBadge {\n background:\n linear-gradient(\n 135deg,\n #10b981,\n #059669);\n color: white;\n font-size: 10px;\n font-weight: 700;\n padding: 3px 8px;\n border-radius: 10px;\n letter-spacing: 0.5px;\n text-transform: uppercase;\n}\n.bb-cameraSavedBadge {\n background:\n linear-gradient(\n 135deg,\n #3b82f6,\n #2563eb);\n color: white;\n font-size: 10px;\n font-weight: 700;\n padding: 3px 8px;\n border-radius: 10px;\n letter-spacing: 0.5px;\n text-transform: uppercase;\n}\n.bb-cameraLiveLabel,\n.bb-cameraSavedLabel {\n font-size: 12px;\n font-weight: 600;\n color: #cbd5e1;\n}\n.bb-cameraValues {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n.bb-cameraRow {\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 12px;\n flex-wrap: wrap;\n}\n.bb-cameraLabel {\n font-weight: 600;\n color: #94a3b8;\n min-width: 58px;\n font-size: 11px;\n letter-spacing: 0.02em;\n}\n.bb-cameraCode {\n font-family:\n "Monaco",\n "Menlo",\n "Ubuntu Mono",\n "Consolas",\n monospace;\n font-size: 11px;\n background: rgba(0, 0, 0, 0.3);\n padding: 3px 8px;\n border-radius: 4px;\n color: #fcd34d;\n letter-spacing: 0.02em;\n word-break: break-all;\n}\n.bb-cameraPlaceholder {\n color: #94a3b8;\n font-size: 12px;\n text-align: center;\n padding: 16px;\n font-style: italic;\n}\n.bb-cameraControls {\n display: flex;\n gap: 10px;\n margin-bottom: 14px;\n flex-wrap: wrap;\n}\n.bb-button--primary {\n background:\n linear-gradient(\n 135deg,\n #135bc0,\n #2c6e9a);\n border-color: transparent;\n color: #ffffff;\n}\n.bb-button--primary:hover:not(:disabled) {\n background:\n linear-gradient(\n 135deg,\n #135bc0,\n #2c6e9a);\n transform: translateY(-1px);\n}\n.bb-button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n transform: none;\n}\n.bb-cameraMessage {\n padding: 10px 14px;\n border-radius: 6px;\n margin-bottom: 14px;\n font-size: 12px;\n font-weight: 500;\n animation: slideIn 0.3s ease-out;\n}\n.bb-cameraMessage--success {\n background: rgba(16, 185, 129, 0.15);\n color: #6ee7b7;\n border: 1px solid rgba(16, 185, 129, 0.3);\n}\n.bb-cameraMessage--error {\n background: rgba(239, 68, 68, 0.15);\n color: #ffe5e55c;\n border: 1px solid rgba(255, 248, 248, 0.3);\n}\n@keyframes slideIn {\n from {\n opacity: 0;\n transform: translateY(-8px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n.bb-readonly {\n background: rgba(148, 163, 184, 0.08);\n color: #9ca3af;\n cursor: default;\n}\n@media (max-width: 1280px) {\n .bb-cameraRow {\n flex-direction: column;\n align-items: flex-start;\n gap: 4px;\n }\n .bb-cameraLabel {\n min-width: auto;\n }\n .bb-cameraControls {\n flex-direction: column;\n }\n .bb-cameraControls .bb-button {\n width: 100%;\n }\n}\n@media (max-width: 980px) {\n .bb-sidebar--left {\n width: 100%;\n max-height: 42vh;\n }\n .bb-sidebar--collapsed {\n width: 100%;\n }\n .bb-previewShell {\n padding: 14px;\n }\n .bb-preview {\n min-height: 44vh;\n }\n .bb-previewHeader {\n flex-direction: column;\n align-items: flex-start;\n }\n .bb-previewControls {\n width: 100%;\n justify-content: flex-start;\n }\n .bb-previewMeta {\n justify-content: flex-start;\n }\n .bb-grid,\n .bb-actionGrid,\n .bb-statRow {\n grid-template-columns: 1fr;\n }\n}\n.bb-annotationsInfo {\n margin-top: 8px;\n}\n.bb-emptyAnnotations {\n padding: 32px;\n text-align: center;\n background: #f5f5f5;\n border-radius: 4px;\n color: #666;\n font-size: 14px;\n}\n.bb-annotationsList {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n.bb-annotationCard {\n background: #f9f9f9;\n border: 1px solid #e0e0e0;\n border-radius: 4px;\n padding: 12px;\n transition: all 0.2s ease;\n}\n.bb-annotationCard:hover {\n background: #ffffff;\n border-color: #4caf50;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n}\n.bb-annotationHeader {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 8px;\n}\n.bb-annotationBadge {\n background: #4caf50;\n color: white;\n padding: 2px 8px;\n border-radius: 2px;\n font-size: 11px;\n font-weight: 600;\n}\n.bb-annotationActions {\n display: flex;\n gap: 8px;\n}\n.bb-annotationEdit,\n.bb-annotationDelete {\n background: transparent;\n border: none;\n cursor: pointer;\n padding: 4px;\n border-radius: 2px;\n font-size: 14px;\n transition: all 0.2s ease;\n}\n.bb-annotationEdit:hover {\n background: #e8f5e9;\n transform: scale(1.05);\n}\n.bb-annotationDelete:hover {\n background: #ffebee;\n transform: scale(1.05);\n}\n.bb-annotationTitle {\n font-weight: 600;\n font-size: 14px;\n color: #333;\n margin-bottom: 6px;\n}\n.bb-annotationDescription {\n font-size: 13px;\n color: #666;\n margin-bottom: 8px;\n line-height: 1.4;\n}\n.bb-annotationPosition {\n font-size: 11px;\n font-family: monospace;\n color: #999;\n background: #f0f0f0;\n padding: 4px 6px;\n border-radius: 2px;\n display: inline-block;\n}\n.bb-modalOverlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000;\n}\n.bb-modal {\n background: white;\n border-radius: 6px;\n width: 500px;\n max-width: 90%;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);\n animation: modalSlideIn 0.2s ease;\n}\n@keyframes modalSlideIn {\n from {\n transform: translateY(-20px);\n opacity: 0;\n }\n to {\n transform: translateY(0);\n opacity: 1;\n }\n}\n.bb-modalHeader {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 16px 20px;\n border-bottom: 1px solid #e0e0e0;\n}\n.bb-modalHeader h3 {\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n}\n.bb-modalClose {\n background: transparent;\n border: none;\n font-size: 20px;\n cursor: pointer;\n color: #999;\n transition: color 0.2s ease;\n}\n.bb-modalClose:hover {\n color: #333;\n}\n.bb-modalBody {\n padding: 20px;\n}\n.bb-modalFooter {\n display: flex;\n justify-content: flex-end;\n gap: 12px;\n padding: 16px 20px;\n border-top: 1px solid #e0e0e0;\n}\n.bb-annotationsInfo {\n margin-top: 8px;\n}\n.bb-emptyAnnotations {\n padding: 32px;\n text-align: center;\n background: rgba(15, 23, 42, 0.44);\n border-radius: 10px;\n border: 1px solid rgba(148, 163, 184, 0.15);\n color: #94a3b8;\n font-size: 14px;\n}\n.bb-annotationsList {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n.bb-annotationCard {\n background: rgba(15, 23, 42, 0.44);\n border-left: 4px solid #f59e0b;\n border-radius: 8px;\n padding: 16px;\n transition: all 0.2s ease;\n border: 1px solid rgba(148, 163, 184, 0.15);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03);\n}\n.bb-annotationCard:hover {\n transform: translateX(4px);\n background: rgba(15, 23, 42, 0.6);\n border-color: rgba(148, 163, 184, 0.25);\n box-shadow: 0 4px 12px rgba(15, 23, 42, 0.3);\n}\n.bb-annotationBadge {\n display: inline-block;\n background:\n linear-gradient(\n 135deg,\n #f59e0b,\n #fb7185);\n color: #111827;\n padding: 2px 10px;\n border-radius: 10px;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.5px;\n margin-bottom: 12px;\n text-transform: uppercase;\n}\n.bb-annotationTitle {\n font-weight: 700;\n font-size: 15px;\n color: #f8fafc;\n margin-bottom: 8px;\n line-height: 1.3;\n}\n.bb-annotationDescription {\n font-size: 13px;\n color: #cbd5e1;\n margin-bottom: 12px;\n line-height: 1.5;\n word-wrap: break-word;\n white-space: pre-wrap;\n}\n.bb-annotationPosition {\n font-size: 11px;\n font-family:\n "Monaco",\n "Menlo",\n "Ubuntu Mono",\n "Consolas",\n monospace;\n color: #94a3b8;\n background: rgba(0, 0, 0, 0.3);\n padding: 4px 8px;\n border-radius: 4px;\n display: inline-block;\n margin-top: 4px;\n letter-spacing: 0.02em;\n}\n.bb-annotationHeader {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 8px;\n}\n.bb-annotationActions {\n display: flex;\n gap: 4px;\n}\n.bb-annotationAction {\n background: none;\n border: none;\n color: #94a3b8;\n cursor: pointer;\n padding: 4px;\n border-radius: 2px;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s;\n}\n.bb-annotationAction--edit:hover {\n border-radius: 50%;\n background: rgba(59, 130, 246, 0.2);\n color: #3b82f6;\n}\n.bb-annotationAction--delete:hover {\n border-radius: 50%;\n background: rgba(239, 68, 68, 0.2);\n color: #ef4444;\n}\n.bb-editSpotOverlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.6);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000;\n}\n.bb-editSpotPopup {\n background:\n linear-gradient(\n 180deg,\n rgba(20, 27, 45, 0.98),\n rgba(25, 34, 57, 0.95)),\n #121826;\n border-radius: 8px;\n padding: 24px;\n min-width: 400px;\n max-width: 500px;\n border: 1px solid rgba(148, 163, 184, 0.18);\n box-shadow: 0 24px 48px rgba(15, 23, 42, 0.3);\n}\n.bb-editSpotHeader {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 16px;\n}\n.bb-editSpotTitle {\n color: #f8fafc;\n font-size: 18px;\n font-weight: 600;\n margin: 0;\n}\n.bb-editSpotClose {\n background: none;\n border: none;\n color: #94a3b8;\n font-size: 24px;\n cursor: pointer;\n padding: 0 4px;\n transition: color 0.2s ease;\n}\n.bb-editSpotClose:hover {\n color: #f8fafc;\n}\n.bb-editSpotBody {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n.bb-editSpotBody .bb-input,\n.bb-editSpotBody .bb-textarea {\n background: rgba(15, 23, 42, 0.4);\n border-color: rgba(148, 163, 184, 0.2);\n color: #f8fafc;\n}\n.bb-editSpotBody .bb-input:focus,\n.bb-editSpotBody .bb-textarea:focus {\n border-color: rgba(251, 191, 36, 0.65);\n box-shadow: 0 0 0 4px rgba(251, 191, 36, 0.12);\n}\n.bb-editSpotBody .bb-label {\n color: #d7dee9;\n}\n.bb-editSpotActions {\n display: flex;\n gap: 8px;\n margin-top: 8px;\n}\n.bb-editSpotActions .bb-button {\n background: rgba(255, 255, 255, 0.04);\n border: 1px solid rgba(148, 163, 184, 0.18);\n color: #e5edf7;\n}\n.bb-editSpotActions .bb-button:hover {\n background: rgba(255, 255, 255, 0.08);\n border-color: rgba(148, 163, 184, 0.32);\n transform: translateY(-1px);\n}\n.bb-button--danger {\n background: #ef4444;\n color: white;\n border: none;\n padding: 8px 16px;\n border-radius: 7px;\n cursor: pointer;\n font-size: 13px;\n font-weight: 700;\n display: flex;\n align-items: center;\n gap: 4px;\n transition: all 0.2s ease;\n}\n.bb-button--danger:hover {\n background: #dc2626;\n transform: translateY(-1px);\n box-shadow: 0 10px 18px rgba(239, 68, 68, 0.25);\n}\n.bb-button--primary {\n background:\n linear-gradient(\n 135deg,\n #f59e0b,\n #fb7185);\n border-color: transparent;\n color: #111827;\n}\n.bb-button--primary:hover {\n background:\n linear-gradient(\n 135deg,\n #fbbf24,\n #fb7185);\n}\n.bb-annotationCard {\n background: rgba(15, 23, 42, 0.44);\n border-left: 4px solid #f59e0b;\n border-radius: 8px;\n padding: 16px;\n transition: all 0.2s ease;\n border: 1px solid rgba(148, 163, 184, 0.15);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03);\n}\n.bb-annotationCard:hover {\n transform: translateX(4px);\n background: rgba(15, 23, 42, 0.6);\n border-color: rgba(148, 163, 184, 0.25);\n box-shadow: 0 4px 12px rgba(15, 23, 42, 0.3);\n}\n.bb-annotationBadge {\n display: inline-block;\n background:\n linear-gradient(\n 135deg,\n #f59e0b,\n #fb7185);\n color: #111827;\n padding: 2px 10px;\n border-radius: 10px;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.5px;\n margin-bottom: 12px;\n text-transform: uppercase;\n}\n.bb-annotationTitle {\n font-weight: 700;\n font-size: 15px;\n color: #f8fafc;\n margin-bottom: 8px;\n line-height: 1.3;\n}\n.bb-annotationDescription {\n font-size: 13px;\n color: #cbd5e1;\n margin-bottom: 12px;\n line-height: 1.5;\n word-wrap: break-word;\n white-space: pre-wrap;\n}\n.bb-annotationPosition {\n font-size: 11px;\n font-family:\n "Monaco",\n "Menlo",\n "Ubuntu Mono",\n "Consolas",\n monospace;\n color: #94a3b8;\n background: rgba(0, 0, 0, 0.3);\n padding: 4px 8px;\n border-radius: 4px;\n display: inline-block;\n margin-top: 4px;\n letter-spacing: 0.02em;\n}\n.bb-emptyAnnotations {\n padding: 32px;\n text-align: center;\n background: rgba(15, 23, 42, 0.44);\n border-radius: 10px;\n border: 1px solid rgba(148, 163, 184, 0.15);\n color: #94a3b8;\n font-size: 14px;\n}\n@media (max-width: 1280px) {\n .bb-cameraRow {\n flex-direction: column;\n align-items: flex-start;\n gap: 4px;\n }\n .bb-cameraLabel {\n min-width: auto;\n }\n .bb-cameraControls {\n flex-direction: column;\n }\n .bb-cameraControls .bb-button {\n width: 100%;\n }\n}\n@media (max-width: 1120px) {\n .bb-sidebar--left {\n width: 320px;\n }\n .bb-sidebar--right {\n width: 220px;\n }\n .bb-grid {\n grid-template-columns: 1fr;\n }\n .bb-statRow {\n grid-template-columns: 1fr;\n }\n}\n@media (max-width: 980px) {\n .bb-root {\n flex-direction: column;\n height: auto;\n min-height: 0;\n }\n .bb-sidebar--left {\n width: 100% !important;\n max-height: 45vh;\n border-right: none;\n border-bottom: 1px solid rgba(148, 163, 184, 0.18);\n box-shadow: 0 18px 42px rgba(15, 23, 42, 0.12);\n }\n .bb-sidebar--right {\n width: 100% !important;\n max-height: 45vh;\n border-left: none;\n border-top: 1px solid rgba(148, 163, 184, 0.18);\n box-shadow: 0 -18px 42px rgba(15, 23, 42, 0.12);\n order: 3;\n }\n .bb-sidebar--collapsed {\n width: 100% !important;\n max-height: 72px;\n overflow: hidden;\n }\n .bb-sidebar--collapsed .bb-nodeList,\n .bb-sidebar--collapsed .bb-tabRow,\n .bb-sidebar--collapsed .bb-toolbar {\n display: none;\n }\n .bb-previewShell {\n flex: 1 1 auto;\n min-height: 50vh;\n padding: 14px;\n order: 2;\n }\n .bb-preview {\n min-height: 44vh;\n border-radius: 10px;\n }\n .bb-previewHeader {\n flex-direction: column;\n align-items: flex-start;\n gap: 12px;\n }\n .bb-previewControls {\n width: 100%;\n justify-content: flex-start;\n flex-wrap: wrap;\n }\n .bb-previewMeta {\n justify-content: flex-start;\n }\n .bb-previewMeta span {\n font-size: 11px;\n padding: 0 10px;\n min-height: 30px;\n }\n .bb-grid,\n .bb-actionGrid,\n .bb-statRow {\n grid-template-columns: 1fr;\n }\n .bb-editor {\n padding: 12px 12px 20px;\n }\n .bb-editorHeader {\n flex-direction: column;\n align-items: stretch;\n padding: 14px;\n }\n .bb-editorTitle {\n font-size: 20px;\n }\n .bb-editorBadge {\n align-self: flex-start;\n }\n .bb-toolbar {\n flex-direction: column;\n gap: 12px;\n padding: 16px 20px 18px;\n }\n .bb-toolbarDivider {\n width: 100%;\n height: 1px;\n margin: 0;\n }\n .bb-tabRow {\n padding: 12px 16px 0;\n gap: 6px;\n }\n .bb-tab {\n font-size: 10px;\n padding: 8px 10px;\n }\n .bb-header {\n padding: 14px 16px 12px;\n }\n .bb-header--sidebar {\n min-height: 60px;\n }\n .bb-title {\n font-size: 17px;\n }\n .bb-subtitle {\n font-size: 13px;\n }\n .bb-statChip {\n padding: 10px 12px;\n }\n .bb-statChip strong {\n font-size: 12px;\n }\n .bb-nodeButton {\n padding: 10px 12px;\n }\n .bb-nodeName {\n font-size: 12px;\n }\n .bb-nodeMeta {\n font-size: 10px;\n }\n .bb-section {\n padding: 14px;\n }\n .bb-sectionTitle {\n font-size: 9px;\n }\n .bb-field {\n gap: 4px;\n }\n .bb-label {\n font-size: 11px;\n }\n .bb-input,\n .bb-select,\n .bb-textarea {\n padding: 9px 11px;\n font-size: 12px;\n }\n .bb-textarea {\n min-height: 80px;\n }\n .bb-button,\n .bb-fileLabel {\n min-height: 36px;\n padding: 0 14px;\n font-size: 12px;\n }\n .bb-previewPlaceholderTitle {\n font-size: 22px;\n }\n .bb-previewPlaceholderButton {\n padding: 10px 18px;\n font-size: 13px;\n }\n .bb-editSpotPopup {\n min-width: 90%;\n max-width: 90%;\n padding: 20px;\n }\n .bb-modal {\n width: 95%;\n max-width: 95%;\n }\n .bb-annotationCard {\n padding: 12px;\n }\n .bb-annotationTitle {\n font-size: 14px;\n }\n .bb-annotationDescription {\n font-size: 12px;\n }\n}\n@media (max-width: 640px) {\n .bb-root {\n flex-direction: column;\n height: auto;\n min-height: 0;\n }\n .bb-sidebar--left {\n max-height: 40vh;\n width: 100% !important;\n }\n .bb-sidebar--right {\n max-height: 40vh;\n width: 100% !important;\n }\n .bb-previewShell {\n min-height: 40vh;\n padding: 10px;\n }\n .bb-preview {\n min-height: 35vh;\n border-radius: 8px;\n }\n .bb-previewHeader {\n flex-direction: column;\n align-items: flex-start;\n gap: 8px;\n padding: 0 0 12px;\n }\n .bb-previewTitle {\n font-size: 17px;\n }\n .bb-previewControls {\n gap: 6px;\n }\n .bb-previewMeta span {\n font-size: 10px;\n padding: 0 8px;\n min-height: 26px;\n }\n .bb-editor {\n padding: 10px 10px 16px;\n }\n .bb-editorHeader {\n padding: 12px;\n flex-direction: column;\n align-items: stretch;\n }\n .bb-editorTitle {\n font-size: 18px;\n }\n .bb-editorMeta {\n font-size: 11px;\n gap: 6px 8px;\n }\n .bb-editorBadge {\n font-size: 9px;\n padding: 4px 10px;\n align-self: flex-start;\n }\n .bb-section {\n padding: 12px;\n margin-bottom: 12px;\n }\n .bb-sectionTitle {\n font-size: 9px;\n margin-bottom: 10px;\n }\n .bb-grid {\n gap: 8px;\n }\n .bb-field--full {\n grid-column: 1 / -1;\n }\n .bb-label {\n font-size: 11px;\n }\n .bb-input,\n .bb-select,\n .bb-textarea {\n padding: 8px 10px;\n font-size: 12px;\n border-radius: 6px;\n }\n .bb-textarea {\n min-height: 70px;\n }\n .bb-button,\n .bb-fileLabel {\n min-height: 34px;\n padding: 0 12px;\n font-size: 11px;\n border-radius: 6px;\n }\n .bb-toolbar {\n padding: 12px 14px 14px;\n gap: 10px;\n }\n .bb-tabRow {\n padding: 10px 12px 0;\n gap: 4px;\n }\n .bb-tab {\n font-size: 9px;\n padding: 6px 8px;\n border-radius: 4px;\n }\n .bb-header {\n padding: 10px 12px 8px;\n }\n .bb-header--sidebar {\n min-height: 52px;\n }\n .bb-title {\n font-size: 15px;\n }\n .bb-subtitle {\n font-size: 12px;\n }\n .bb-eyebrow {\n font-size: 9px;\n padding: 4px 8px;\n margin-bottom: 8px;\n }\n .bb-statRow {\n gap: 6px;\n margin-top: 14px;\n }\n .bb-statChip {\n padding: 8px 10px;\n border-radius: 6px;\n }\n .bb-statChip strong {\n font-size: 11px;\n }\n .bb-statLabel {\n font-size: 9px;\n }\n .bb-sidebarToggle {\n width: 30px;\n height: 30px;\n font-size: 14px;\n border-radius: 5px;\n }\n .bb-nodeItems {\n padding: 4px 6px;\n }\n .bb-nodeButton {\n padding: 8px 10px;\n border-radius: 6px;\n margin-bottom: 4px;\n }\n .bb-nodeName {\n font-size: 11px;\n }\n .bb-nodeMeta {\n font-size: 10px;\n }\n .bb-search {\n padding: 8px 10px;\n }\n .bb-statusCard {\n padding: 10px 12px;\n border-radius: 7px;\n }\n .bb-status {\n font-size: 12px;\n }\n .bb-statusTitle {\n font-size: 10px;\n }\n .bb-checkboxRow {\n gap: 6px;\n }\n .bb-checkbox {\n padding: 6px 10px;\n font-size: 12px;\n border-radius: 5px;\n }\n .bb-actionCard {\n padding: 10px;\n border-radius: 7px;\n }\n .bb-actionCardTitle {\n font-size: 12px;\n }\n .bb-actionCardMeta {\n font-size: 9px;\n padding: 3px 7px;\n }\n .bb-previewPlaceholder {\n padding: 24px;\n border-radius: 8px;\n }\n .bb-previewPlaceholderTitle {\n font-size: 18px;\n }\n .bb-previewPlaceholderButton {\n padding: 8px 14px;\n font-size: 12px;\n border-radius: 999px;\n }\n .bb-editSpotPopup {\n min-width: 95%;\n max-width: 95%;\n padding: 16px;\n border-radius: 6px;\n }\n .bb-editSpotTitle {\n font-size: 16px;\n }\n .bb-editSpotActions .bb-button {\n padding: 8px 12px;\n font-size: 12px;\n }\n .bb-modal {\n width: 98%;\n max-width: 98%;\n border-radius: 5px;\n }\n .bb-modalHeader {\n padding: 12px 16px;\n }\n .bb-modalHeader h3 {\n font-size: 16px;\n }\n .bb-modalBody {\n padding: 14px;\n }\n .bb-modalFooter {\n padding: 12px 16px;\n flex-direction: column;\n gap: 8px;\n }\n .bb-modalFooter .bb-button {\n width: 100%;\n justify-content: center;\n }\n .bb-annotationCard {\n padding: 10px;\n border-left-width: 3px;\n border-radius: 6px;\n }\n .bb-annotationTitle {\n font-size: 13px;\n }\n .bb-annotationDescription {\n font-size: 11px;\n }\n .bb-annotationPosition {\n font-size: 10px;\n padding: 3px 6px;\n }\n .bb-annotationBadge {\n font-size: 9px;\n padding: 2px 8px;\n }\n .bb-emptyAnnotations {\n padding: 20px;\n font-size: 12px;\n border-radius: 7px;\n }\n .bb-removeModelButton {\n min-height: 30px;\n padding: 0 10px;\n font-size: 11px;\n }\n .bb-annotationsList {\n gap: 8px;\n }\n .bb-cameraLive,\n .bb-cameraSaved {\n padding: 10px;\n border-radius: 6px;\n }\n .bb-cameraLiveHeader,\n .bb-cameraSavedHeader {\n flex-wrap: wrap;\n }\n .bb-cameraLiveLabel,\n .bb-cameraSavedLabel {\n font-size: 11px;\n }\n .bb-cameraRow {\n font-size: 11px;\n gap: 4px;\n }\n .bb-cameraCode {\n font-size: 10px;\n padding: 2px 6px;\n }\n .bb-cameraControls {\n flex-direction: column;\n gap: 6px;\n }\n .bb-cameraControls .bb-button {\n width: 100%;\n justify-content: center;\n }\n .bb-cameraMessage {\n font-size: 11px;\n padding: 8px 12px;\n }\n .bb-readonly {\n font-size: 11px;\n padding: 6px 8px;\n }\n}\n@media (max-width: 420px) {\n .bb-sidebar--left {\n max-height: 35vh;\n }\n .bb-sidebar--right {\n max-height: 35vh;\n }\n .bb-previewShell {\n min-height: 35vh;\n padding: 6px;\n }\n .bb-preview {\n min-height: 30vh;\n border-radius: 6px;\n }\n .bb-previewTitle {\n font-size: 15px;\n }\n .bb-previewMeta span {\n font-size: 9px;\n padding: 0 6px;\n min-height: 22px;\n }\n .bb-editorTitle {\n font-size: 16px;\n }\n .bb-title {\n font-size: 13px;\n }\n .bb-tab {\n font-size: 8px;\n padding: 5px 6px;\n }\n .bb-button,\n .bb-fileLabel {\n font-size: 10px;\n min-height: 30px;\n padding: 0 10px;\n }\n .bb-input,\n .bb-select,\n .bb-textarea {\n font-size: 11px;\n padding: 6px 8px;\n }\n .bb-statChip {\n padding: 6px 8px;\n }\n .bb-statChip strong {\n font-size: 10px;\n }\n .bb-previewPlaceholderTitle {\n font-size: 15px;\n }\n .bb-previewPlaceholderButton {\n font-size: 11px;\n padding: 6px 12px;\n }\n .bb-editSpotPopup {\n padding: 14px;\n border-radius: 5px;\n }\n .bb-editSpotTitle {\n font-size: 14px;\n }\n .bb-editSpotBody .bb-input,\n .bb-editSpotBody .bb-textarea {\n font-size: 12px;\n padding: 6px 8px;\n }\n .bb-modalHeader h3 {\n font-size: 14px;\n }\n .bb-annotationTitle {\n font-size: 12px;\n }\n}\n@media (max-width: 980px) {\n .bb-sidebar--collapsed .bb-header--sidebar {\n min-height: 52px;\n }\n .bb-sidebar--collapsed .bb-title {\n font-size: 14px;\n }\n .bb-sidebar--collapsed .bb-sidebarToggle {\n width: 28px;\n height: 28px;\n font-size: 12px;\n }\n}\n@media (hover: none) and (pointer: coarse) {\n .bb-nodeButton:hover,\n .bb-button:hover,\n .bb-fileLabel:hover,\n .bb-tab:hover,\n .bb-sidebarToggle:hover,\n .bb-annotationCard:hover {\n transform: none;\n }\n .bb-nodeButton:active,\n .bb-button:active,\n .bb-fileLabel:active,\n .bb-tab:active,\n .bb-sidebarToggle:active {\n transform: scale(0.97);\n transition: transform 0.1s ease;\n }\n}\n@media print {\n .bb-root {\n height: auto;\n min-height: 0;\n background: white;\n }\n .bb-sidebar--left,\n .bb-sidebar--right {\n width: 100% !important;\n max-height: none !important;\n background: white !important;\n color: black !important;\n border: 1px solid #ddd !important;\n box-shadow: none !important;\n }\n .bb-previewShell {\n min-height: auto !important;\n padding: 10px !important;\n }\n .bb-preview {\n min-height: auto !important;\n border-radius: 4px !important;\n border: 1px solid #ddd !important;\n box-shadow: none !important;\n }\n .bb-button,\n .bb-fileLabel,\n .bb-sidebarToggle,\n .bb-tab,\n .bb-annotationAction,\n .bb-iconButton {\n display: none !important;\n }\n .bb-nodeList {\n overflow: visible !important;\n }\n .bb-previewPlaceholderButton {\n display: none !important;\n }\n}\n.bb-checkboxRow {\n display: flex;\n flex-wrap: wrap;\n gap: 12px;\n align-items: center;\n padding: 4px 0;\n}\n.bb-checkbox {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n border-radius: 7px;\n font-weight: 600;\n font-size: 13px;\n color: #f3eeee;\n cursor: pointer;\n user-select: none;\n transition: all 0.2s ease;\n}\n.bb-checkbox input[type=checkbox] {\n width: 16px;\n height: 16px;\n accent-color: #f59e0b;\n cursor: pointer;\n flex-shrink: 0;\n}\n.bb-editorDivider {\n height: 1px;\n background: rgba(148, 163, 184, 0.14);\n margin: 4px 0 16px 0;\n width: 100%;\n}\n.bb-preview {\n position: relative;\n width: 100%;\n height: 100%;\n overflow: hidden;\n}\n.ri-viewer {\n width: 100% !important;\n height: 100% !important;\n position: relative !important;\n}\n.ri-buttonContainer {\n position: absolute !important;\n bottom: 20px !important;\n right: 20px !important;\n z-index: 10000 !important;\n display: flex !important;\n gap: 10px !important;\n pointer-events: auto !important;\n}\n.ri-downloadButton {\n display: flex !important;\n align-items: center !important;\n gap: 6px !important;\n padding: 10px 12px !important;\n background: #2f302f74 !important;\n color: white !important;\n border: none !important;\n border-radius: 6px !important;\n cursor: pointer !important;\n font-size: 12px !important;\n font-weight: 500 !important;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15) !important;\n opacity: 1 !important;\n visibility: visible !important;\n}\n.ri-downloadButton svg {\n width: 14px !important;\n height: 14px !important;\n}\n.ri-resetButton {\n display: flex !important;\n align-items: center !important;\n justify-content: center !important;\n padding: 6px !important;\n background: #2f302f74 !important;\n border: none !important;\n border-radius: 6px !important;\n cursor: pointer !important;\n opacity: 1 !important;\n visibility: visible !important;\n}\n.ri-resetButton svg {\n width: 14px !important;\n height: 14px !important;\n}\n.bb-reset-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.6);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000;\n backdrop-filter: blur(4px);\n animation: bb-fade-in 0.2s ease;\n}\n.bb-reset-dialog {\n background: #1a1f2e;\n border-radius: 12px;\n max-width: 480px;\n width: 90%;\n border: 1px solid rgba(148, 163, 184, 0.18);\n box-shadow: 0 24px 48px rgba(0, 0, 0, 0.5);\n animation: bb-slide-up 0.25s ease;\n}\n.bb-reset-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 20px 24px 16px;\n border-bottom: 1px solid rgba(148, 163, 184, 0.1);\n}\n.bb-reset-title {\n display: flex;\n align-items: center;\n gap: 10px;\n margin: 0;\n font-size: 18px;\n font-weight: 700;\n color: #f8fafc;\n}\n.bb-reset-title .bb-icon-warning {\n color: #f59e0b;\n flex-shrink: 0;\n}\n.bb-reset-close {\n background: transparent;\n border: none;\n color: #94a3b8;\n cursor: pointer;\n padding: 4px;\n border-radius: 4px;\n transition: all 0.2s ease;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n.bb-reset-close:hover {\n color: #f8fafc;\n background: rgba(255, 255, 255, 0.06);\n}\n.bb-reset-body {\n padding: 20px 24px 24px;\n}\n.bb-reset-message {\n margin: 0 0 12px 0;\n font-size: 15px;\n color: #e2e8f0;\n line-height: 1.5;\n}\n.bb-reset-message strong {\n color: #f8fafc;\n}\n.bb-reset-details {\n color: #94a3b8;\n text-align: center;\n font-size: 13px;\n line-height: 1.6;\n margin: 0 0 12px 0;\n}\n.bb-reset-warning {\n display: flex;\n align-items: center;\n gap: 8px;\n color: #f59e0b;\n font-size: 13px;\n font-weight: 600;\n margin: 4px 0 0 0;\n padding: 10px 12px;\n background: rgba(245, 158, 11, 0.08);\n border-radius: 6px;\n border: 1px solid rgba(245, 158, 11, 0.15);\n}\n.bb-reset-footer {\n display: flex;\n justify-content: flex-end;\n gap: 10px;\n padding: 16px 24px 20px;\n border-top: 1px solid rgba(148, 163, 184, 0.1);\n}\n.bb-reset-btn {\n padding: 8px 20px;\n border-radius: 6px;\n font-size: 13px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.2s ease;\n border: 1px solid rgba(148, 163, 184, 0.18);\n background: rgba(255, 255, 255, 0.04);\n color: #e2e8f0;\n}\n.bb-reset-btn:hover {\n background: rgba(255, 255, 255, 0.08);\n transform: translateY(-1px);\n}\n.bb-reset-btn-danger {\n padding: 8px 20px;\n border-radius: 6px;\n font-size: 13px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.2s ease;\n border: none;\n background: #dc2626;\n color: white;\n}\n.bb-reset-btn-danger:hover {\n background: #b91c1c;\n transform: translateY(-1px);\n box-shadow: 0 4px 12px rgba(220, 38, 38, 0.3);\n}\n@keyframes bb-fade-in {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n@keyframes bb-slide-up {\n from {\n opacity: 0;\n transform: translateY(20px) scale(0.96);\n }\n to {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n}\n');
7161
+ styleInject('.bb-root {\n --bb-font-family:\n Inter,\n "Segoe UI",\n Helvetica,\n Arial,\n sans-serif;\n --bb-app-bg:\n radial-gradient(\n \n circle at top left,\n rgba(226, 232, 240, 0.8),\n transparent 30% ),\n linear-gradient(\n 135deg,\n #f6f1e8 0%,\n #ebe8df 40%,\n #e9edf2 100%);\n --bb-app-text: #111827;\n --bb-sidebar-bg:\n linear-gradient(\n 180deg,\n rgba(20, 27, 45, 0.98),\n rgba(25, 34, 57, 0.95)),\n #121826;\n --bb-sidebar-text: #f8fafc;\n --bb-sidebar-muted-text: #b3bfd2;\n --bb-sidebar-subtle-text: #94a3b8;\n --bb-sidebar-border: rgba(148, 163, 184, 0.18);\n --bb-sidebar-border-hover: rgba(148, 163, 184, 0.32);\n --bb-sidebar-divider: rgba(148, 163, 184, 0.14);\n --bb-sidebar-surface: rgba(255, 255, 255, 0.04);\n --bb-sidebar-surface-hover: rgba(255, 255, 255, 0.08);\n --bb-sidebar-surface-strong: rgba(255, 255, 255, 0.05);\n --bb-sidebar-eyebrow-bg: rgba(148, 163, 184, 0.16);\n --bb-sidebar-eyebrow-text: #cbd5e1;\n --bb-sidebar-left-shadow: 18px 0 42px rgba(15, 23, 42, 0.12);\n --bb-sidebar-right-shadow: -4px 0 24px rgba(0, 0, 0, 0.35);\n --bb-sidebar-chip-bg: rgba(15, 23, 42, 0.42);\n --bb-sidebar-chip-border: rgba(148, 163, 184, 0.14);\n --bb-sidebar-accent: rgba(99, 102, 241, 0.18);\n --bb-sidebar-accent-border: rgba(99, 102, 241, 0.4);\n --bb-sidebar-accent-text: #f8fafc;\n --bb-menu-bg: #1a1f2e;\n --bb-menu-shadow: 0 16px 32px rgba(15, 23, 42, 0.4);\n display: flex;\n height: 100%;\n min-height: 0;\n width: 100%;\n background: var(--bb-app-bg);\n color: var(--bb-app-text);\n font-family: var(--bb-font-family);\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n text-rendering: optimizeLegibility;\n font-synthesis: none;\n}\n.bb-root * {\n box-sizing: border-box;\n}\n.bb-root input,\n.bb-root select,\n.bb-root textarea,\n.bb-root button {\n font-family: inherit;\n}\n.bb-sidebar {\n width: 400px;\n min-height: 0;\n flex-shrink: 0;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n background: var(--bb-sidebar-bg);\n color: var(--bb-sidebar-text);\n transition: width 0.24s ease;\n}\n.bb-sidebar--left {\n border-right: 1px solid var(--bb-sidebar-border);\n box-shadow: var(--bb-sidebar-left-shadow);\n}\n.bb-sidebar--right {\n width: 300px;\n border-left: 1px solid var(--bb-sidebar-border);\n box-shadow: var(--bb-sidebar-right-shadow);\n}\n.bb-sidebar--collapsed {\n width: 36px;\n}\n.bb-sidebar--collapsed .bb-header {\n padding: 5px;\n display: flex;\n justify-content: center;\n}\n.bb-sidebar--collapsed .bb-header--sidebar {\n min-height: 36px;\n}\n.bb-sidebar--collapsed .bb-sidebarToggle {\n width: 26px;\n height: 26px;\n font-size: 12px;\n}\n.bb-header {\n padding: 5px 20px;\n border-bottom: 1px solid var(--bb-sidebar-divider);\n}\n.bb-header--sidebar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n min-height: 55px;\n}\n.bb-eyebrow {\n display: inline-block;\n margin-bottom: 12px;\n padding: 6px 10px;\n border-radius: 999px;\n background: var(--bb-sidebar-eyebrow-bg);\n color: var(--bb-sidebar-eyebrow-text);\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.18em;\n text-transform: uppercase;\n}\n.bb-title {\n margin: 0 0 0px;\n font-size: 20px;\n line-height: 0.95;\n letter-spacing: -0.04em;\n font-weight: 800;\n color: var(--bb-sidebar-text);\n}\n.bb-sidebarToggle {\n width: 36px;\n height: 36px;\n flex-shrink: 0;\n border-radius: 6px;\n border: 1px solid var(--bb-sidebar-border);\n background: var(--bb-sidebar-surface-strong);\n color: var(--bb-sidebar-text);\n font-size: 16px;\n font-weight: 800;\n cursor: pointer;\n transition:\n transform 0.18s ease,\n background-color 0.18s ease,\n border-color 0.18s ease;\n}\n.bb-sidebarToggle:hover {\n transform: translateY(-1px);\n background: var(--bb-sidebar-surface-hover);\n border-color: var(--bb-sidebar-border-hover);\n}\n.bb-subtitle {\n margin: 0;\n max-width: 32ch;\n font-size: 15px;\n line-height: 1.7;\n color: var(--bb-sidebar-muted-text);\n}\n.bb-statRow {\n display: grid;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n gap: 10px;\n margin-top: 22px;\n}\n.bb-statChip {\n display: flex;\n flex-direction: column;\n gap: 4px;\n min-width: 0;\n padding: 12px 14px;\n border-radius: 8px;\n background: var(--bb-sidebar-chip-bg);\n border: 1px solid var(--bb-sidebar-chip-border);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04);\n}\n.bb-statChip strong {\n font-size: 13px;\n color: var(--bb-sidebar-text);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.bb-statLabel {\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n color: var(--bb-sidebar-subtle-text);\n}\n.bb-tabRow {\n display: flex;\n gap: 6px;\n padding: 10px 20px 10px;\n}\n.bb-tab {\n flex: 1;\n padding: 6px 10px;\n border-radius: 6px;\n border: 1px solid var(--bb-sidebar-border);\n background: var(--bb-sidebar-surface);\n color: var(--bb-sidebar-muted-text);\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n cursor: pointer;\n transition:\n background-color 0.18s ease,\n border-color 0.18s ease,\n color 0.18s ease;\n}\n.bb-tab:hover {\n background: var(--bb-sidebar-surface-hover);\n border-color: var(--bb-sidebar-border-hover);\n}\n.bb-tab--active {\n background: var(--bb-sidebar-accent);\n border-color: var(--bb-sidebar-accent-border);\n color: var(--bb-sidebar-accent-text);\n}\n.bb-toolbar {\n display: flex;\n justify-content: space-between;\n align-items: center;\n gap: 10px;\n padding: 8px 20px 8px;\n border-bottom: 1px solid var(--bb-sidebar-divider);\n flex-wrap: wrap;\n}\n.bb-toolbarGroup {\n display: flex;\n align-items: center;\n gap: 6px;\n min-width: 0;\n}\n.bb-toolbarDivider {\n width: 1px;\n align-self: stretch;\n background: var(--bb-sidebar-divider);\n margin: 0;\n}\n.bb-iconButton {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n padding: 0;\n border-radius: 7px;\n border: 1px solid var(--bb-sidebar-border);\n background: var(--bb-sidebar-surface);\n color: var(--bb-sidebar-text);\n cursor: pointer;\n transition: background-color 0.18s ease, border-color 0.18s ease;\n}\n.bb-iconButton:hover:not(:disabled) {\n background: var(--bb-sidebar-surface-hover);\n border-color: var(--bb-sidebar-border-hover);\n}\n.bb-iconButton:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n.bb-exportMenuWrapper {\n position: relative;\n display: inline-flex;\n}\n.bb-exportMenu {\n position: absolute;\n bottom: calc(100% + 6px);\n right: 0;\n z-index: 20;\n display: flex;\n flex-direction: column;\n min-width: 150px;\n padding: 6px;\n border-radius: 9px;\n border: 1px solid var(--bb-sidebar-border);\n background: var(--bb-menu-bg);\n box-shadow: var(--bb-menu-shadow);\n}\n.bb-exportMenuItem {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 10px;\n border: none;\n border-radius: 6px;\n background: transparent;\n color: var(--bb-sidebar-text);\n font-size: 12px;\n font-weight: 600;\n text-align: left;\n cursor: pointer;\n}\n.bb-exportMenuItem:hover {\n background: var(--bb-sidebar-surface-hover);\n}\n@media (max-width: 640px) {\n .bb-toolbarDivider {\n width: 100%;\n height: 1px;\n }\n}\n.bb-button,\n.bb-fileLabel {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n min-height: 32px;\n padding: 0 12px;\n border-radius: 7px;\n border: 1px solid var(--bb-sidebar-border);\n background: var(--bb-sidebar-surface);\n color: var(--bb-sidebar-text);\n font-size: 12px;\n font-weight: 700;\n cursor: pointer;\n transition:\n transform 0.18s ease,\n background-color 0.18s ease,\n border-color 0.18s ease,\n box-shadow 0.18s ease;\n}\n.bb-button:hover,\n.bb-fileLabel:hover {\n transform: translateY(-1px);\n background: rgba(255, 255, 255, 0.08);\n border-color: rgba(148, 163, 184, 0.32);\n box-shadow: 0 10px 18px rgba(15, 23, 42, 0.18);\n}\n.bb-button:disabled,\n.bb-fileLabel:has(.bb-hiddenInput:disabled) {\n opacity: 0.55;\n cursor: not-allowed;\n transform: none;\n box-shadow: none;\n}\n.bb-button--primary {\n background: #135bc0;\n border-color: transparent;\n color: #ffffff;\n}\n.bb-button--primary:hover {\n background: #1a68d6;\n}\n.bb-button--ghost {\n background: rgba(248, 250, 252, 0.08);\n}\n.bb-hiddenInput {\n display: none;\n}\n.bb-statusCard {\n margin-top: 4px;\n padding: 14px 16px;\n border-radius: 9px;\n background: rgba(15, 23, 42, 0.44);\n border: 1px solid rgba(148, 163, 184, 0.15);\n}\n.bb-statusCard--error {\n background: rgba(127, 29, 29, 0.32);\n border-color: rgba(248, 113, 113, 0.24);\n}\n.bb-statusCard--success {\n background: rgba(6, 78, 59, 0.34);\n border-color: rgba(52, 211, 153, 0.24);\n}\n.bb-statusTitle {\n margin-bottom: 6px;\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n color: #cbd5e1;\n}\n.bb-status {\n font-size: 13px;\n line-height: 1.55;\n color: #e2e8f0;\n}\n.bb-status--error {\n color: #fecaca;\n}\n.bb-status--success {\n color: #bbf7d0;\n}\n.bb-input--error {\n border-color: #dc2626;\n}\n.bb-nodeList {\n display: flex;\n flex-direction: column;\n flex: 1 1 auto;\n min-height: 0;\n overflow: hidden;\n background: #141824;\n}\n.bb-sectionHead {\n padding: 24px 20px 16px;\n border-bottom: 1px solid rgba(255, 255, 255, 0.07);\n}\n.bb-sectionKicker {\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.12em;\n text-transform: uppercase;\n color: #64748b;\n}\n.bb-sectionHeading {\n margin-top: 4px;\n font-size: 22px;\n font-weight: 700;\n line-height: 1.2;\n color: #f1f5f9;\n}\n.bb-search {\n padding: 14px 16px;\n border-bottom: 1px solid rgba(255, 255, 255, 0.07);\n}\n.bb-input,\n.bb-select,\n.bb-textarea {\n width: 100%;\n border-radius: 10px;\n border: 1px solid rgba(255, 255, 255, 0.08);\n background: rgba(255, 255, 255, 0.05);\n color: #e2e8f0;\n padding: 10px 14px;\n font-size: 13px;\n line-height: 1.45;\n}\n.bb-input::placeholder,\n.bb-textarea::placeholder {\n color: #475569;\n}\n.bb-input--compact {\n width: 64px;\n max-width: 64px;\n flex: 0 0 auto;\n padding: 7px 4px;\n text-align: center;\n}\n.bb-input:focus,\n.bb-select:focus,\n.bb-textarea:focus {\n outline: none;\n border-color: rgba(255, 255, 255, 0.18);\n background: rgba(255, 255, 255, 0.08);\n box-shadow: none;\n}\n.bb-textarea {\n min-height: 110px;\n resize: vertical;\n font-family:\n ui-monospace,\n SFMono-Regular,\n Menlo,\n Monaco,\n Consolas,\n monospace;\n}\n.bb-nodeItems {\n min-height: 0;\n overflow-y: auto;\n padding: 10px 0;\n}\n.bb-nodeButton {\n width: 100%;\n border: 0;\n border-radius: 0;\n background: transparent;\n padding: 12px 20px;\n margin-bottom: 0;\n text-align: left;\n cursor: pointer;\n transition: background-color 0.12s ease, color 0.12s ease;\n border-bottom: 1px solid rgba(255, 255, 255, 0.04);\n}\n.bb-nodeButton:hover {\n background: rgba(255, 255, 255, 0.05);\n}\n.bb-nodeButton--active {\n background: rgba(255, 255, 255, 0.05);\n box-shadow: none;\n}\n.bb-nodeName {\n display: block;\n margin-bottom: 2px;\n font-size: 14px;\n font-weight: 600;\n color: #e2e8f0;\n}\n.bb-nodeMeta {\n display: block;\n font-size: 11px;\n color: #94a3b8;\n}\n.bb-editor {\n flex: 1 1 auto;\n min-height: 0;\n height: 100%;\n overflow-y: auto;\n padding: 16px 16px 28px;\n overscroll-behavior: contain;\n}\n.bb-editorHeader {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 12px;\n margin-bottom: 18px;\n padding: 18px;\n border-radius: 11px;\n background: rgba(255, 255, 255, 0.05);\n border: 1px solid rgba(148, 163, 184, 0.12);\n}\n.bb-editorTitle {\n margin: 8px 0 0;\n font-size: 24px;\n line-height: 1.1;\n color: #f8fafc;\n}\n.bb-editorMeta {\n display: flex;\n flex-wrap: wrap;\n gap: 8px 12px;\n margin-top: 10px;\n font-size: 12px;\n color: #94a3b8;\n}\n.bb-editorBadge {\n flex-shrink: 0;\n padding: 8px 12px;\n border-radius: 999px;\n background: rgba(251, 191, 36, 0.12);\n border: 1px solid rgba(251, 191, 36, 0.2);\n color: #fcd34d;\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.12em;\n text-transform: uppercase;\n}\n.bb-emptyEditor {\n padding: 28px 8px;\n font-size: 14px;\n color: #a7b1c3;\n}\n.bb-section {\n margin-bottom: 10px;\n border-radius: 7px;\n background: rgba(255, 255, 255, 0.03);\n border: 1px solid rgba(148, 163, 184, 0.12);\n overflow: hidden;\n}\n.bb-sectionHeaderButton {\n display: flex;\n width: 100%;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n border: none;\n background: rgba(255, 255, 255, 0.03);\n padding: 11px 14px;\n cursor: pointer;\n text-align: left;\n color: inherit;\n}\n.bb-sectionHeaderButton:hover {\n background: rgba(255, 255, 255, 0.06);\n}\n.bb-sectionHeaderMain {\n display: flex;\n min-width: 0;\n align-items: center;\n gap: 8px;\n}\n.bb-sectionChevron {\n flex-shrink: 0;\n color: #8b97ab;\n transform: rotate(-90deg);\n transition: transform 0.18s ease;\n}\n.bb-sectionChevron--open {\n transform: rotate(0deg);\n}\n.bb-sectionBody {\n display: flex;\n flex-direction: column;\n gap: 4px;\n padding: 14px;\n border-top: 1px solid rgba(148, 163, 184, 0.1);\n}\n.bb-sectionTitle {\n margin: 0;\n font-size: 11px;\n font-weight: 700;\n color: #c3cbdb;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n}\n.bb-sectionHelp {\n display: inline-flex;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n width: 18px;\n height: 18px;\n margin-left: 2px;\n border-radius: 999px;\n color: #b9c0ca;\n cursor: help;\n transition:\n background 0.18s ease,\n color 0.18s ease,\n transform 0.18s ease;\n}\n.bb-sectionHelp:hover {\n background: rgba(255, 255, 255, 0.08);\n color: #eef2ff;\n transform: translateY(-0.5px);\n}\n.bb-grid {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 10px;\n}\n.bb-presetGallery {\n display: flex;\n justify-items: center;\n flex-wrap: wrap;\n gap: 4px;\n margin-bottom: 14px;\n}\n.bb-presetSwatch {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 5px;\n width: 64px;\n padding: 6px 4px;\n border-radius: 8px;\n border: 1px solid rgba(148, 163, 184, 0.18);\n background: rgba(148, 163, 184, 0.06);\n cursor: pointer;\n transition: border-color 0.15s, background 0.15s;\n}\n.bb-presetSwatch:hover:not(:disabled) {\n border-color: rgba(148, 163, 184, 0.4);\n background: rgba(148, 163, 184, 0.12);\n}\n.bb-presetSwatch:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n.bb-presetSwatchColor {\n width: 32px;\n height: 32px;\n border-radius: 999px;\n border: 1px solid rgba(255, 255, 255, 0.18);\n}\n.bb-presetSwatchLabel {\n font-size: 10px;\n font-weight: 600;\n color: #cbd5e1;\n text-align: center;\n line-height: 1.2;\n}\n.bb-field {\n display: flex;\n flex-direction: column;\n gap: 5px;\n}\n.bb-field--full {\n grid-column: 1 / -1;\n}\n.bb-label {\n font-size: 12px;\n font-weight: 700;\n color: #d7dee9;\n}\n.bb-note {\n font-size: 11px;\n color: #8b97ab;\n}\n.bb-tierLock {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 16px 14px;\n border-radius: 7px;\n background: rgba(251, 191, 36, 0.08);\n border: 1px dashed rgba(251, 191, 36, 0.3);\n color: #fcd34d;\n}\n.bb-tierLockMessage {\n margin: 0;\n font-size: 12px;\n font-weight: 600;\n}\n.bb-root--licenseChecking,\n.bb-root--licenseInvalid {\n align-items: center;\n justify-content: center;\n}\n.bb-readonly {\n background: rgba(148, 163, 184, 0.08);\n color: #9ca3af;\n}\n.bb-checkboxRow {\n display: flex;\n flex-wrap: wrap;\n gap: 10px;\n}\n.bb-checkbox {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 6px 5px;\n border-radius: 7px;\n background: rgba(15, 23, 42, 0.22);\n border: 1px solid rgba(148, 163, 184, 0.12);\n font-size: 13px;\n color: #d7dee9;\n}\n.bb-checkbox input {\n accent-color: #f59e0b;\n}\n.bb-sliderField {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n.bb-slider {\n flex: 1 1 auto;\n min-width: 0;\n accent-color: #2aa8d8;\n}\n.bb-slider--unset {\n opacity: 0.45;\n}\n.bb-fileField {\n position: relative;\n display: flex;\n align-items: center;\n}\n.bb-fileField-input {\n width: 100%;\n padding-right: 40px;\n}\n.bb-fileFieldButton {\n position: absolute;\n right: 6px;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 26px;\n height: 26px;\n border: none;\n border-radius: 5px;\n background: transparent;\n color: #aab4c5;\n cursor: pointer;\n}\n.bb-fileFieldButton:hover {\n background: rgba(255, 255, 255, 0.08);\n color: #f8fafc;\n}\n.bb-materialPanel {\n margin-top: 8px;\n border: 1px solid rgba(148, 163, 184, 0.14);\n border-radius: 6px;\n background: rgba(15, 23, 42, 0.28);\n overflow: hidden;\n}\n.bb-materialPanel:first-of-type {\n margin-top: 0;\n}\n.bb-materialPanelHeader {\n display: flex;\n width: 100%;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n border: none;\n padding: 10px 12px;\n background: rgba(255, 255, 255, 0.03);\n cursor: pointer;\n text-align: left;\n color: inherit;\n}\n.bb-materialPanelHeader:hover {\n background: rgba(255, 255, 255, 0.06);\n}\n.bb-materialPanelTitleWrap {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n color: #dbe4f0;\n}\n.bb-materialPanelChevron {\n flex-shrink: 0;\n color: #8b97ab;\n transform: rotate(-90deg);\n transition: transform 0.18s ease;\n}\n.bb-materialPanelChevron--open {\n transform: rotate(0deg);\n}\n.bb-materialPanelTitle {\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n}\n.bb-materialPanelHelp {\n display: inline-flex;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n width: 18px;\n height: 18px;\n margin-left: 2px;\n border-radius: 999px;\n color: #b9c0ca;\n cursor: help;\n transition:\n background 0.18s ease,\n color 0.18s ease,\n transform 0.18s ease;\n}\n.bb-materialPanelHelp:hover {\n background: rgba(255, 255, 255, 0.08);\n color: #eef2ff;\n transform: translateY(-0.5px);\n}\n.bb-materialPanelBody {\n padding: 12px;\n border-top: 1px solid rgba(148, 163, 184, 0.1);\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n.bb-actionList {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n.bb-actionCard {\n border: 1px solid rgba(148, 163, 184, 0.14);\n border-radius: 9px;\n padding: 14px;\n background: rgba(15, 23, 42, 0.2);\n}\n.bb-actionCardHeader {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 10px;\n margin-bottom: 12px;\n}\n.bb-actionCardTitle {\n font-size: 13px;\n font-weight: 700;\n color: #f8fafc;\n}\n.bb-actionCardMeta {\n flex-shrink: 0;\n padding: 5px 9px;\n border-radius: 999px;\n background: rgba(148, 163, 184, 0.12);\n color: #94a3b8;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.12em;\n text-transform: uppercase;\n}\n.bb-actionGrid {\n display: grid;\n grid-template-columns: 1fr;\n gap: 10px;\n}\n.bb-actionFooter {\n margin-top: 12px;\n}\n.bb-actionRemove {\n width: 100%;\n}\n.bb-previewShell {\n flex: 1 1 auto;\n min-width: 0;\n min-height: 0;\n display: flex;\n flex-direction: column;\n padding: 5px;\n overflow: hidden;\n}\n.bb-previewHeader {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 16px;\n padding: 0 4px 10px;\n}\n.bb-previewControls {\n display: flex;\n align-items: flex-start;\n justify-content: flex-end;\n gap: 10px;\n flex-wrap: wrap;\n}\n.bb-previewTitle {\n margin: 2px 0 0;\n font-size: 20px;\n line-height: 1.05;\n letter-spacing: -0.03em;\n color: #1f2937;\n}\n.bb-previewToggles {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 2px;\n}\n.bb-checkbox--compact {\n gap: 5px;\n padding: 0;\n background: transparent;\n border: none;\n font-size: 10px;\n font-weight: 500;\n line-height: 1;\n color: #4b5563;\n}\n.bb-checkbox--compact:hover {\n background: transparent;\n transform: none;\n}\n.bb-checkbox--compact input[type=checkbox] {\n width: 11px;\n height: 11px;\n}\n.bb-previewMeta {\n display: flex;\n flex-wrap: wrap;\n justify-content: flex-end;\n gap: 8px;\n}\n.bb-removeModelButton {\n background-color: rgb(174, 54, 54);\n min-height: 34px;\n padding: 0 14px;\n}\n.bb-removeModelButton:hover {\n background-color: rgb(163, 20, 20);\n}\n.bb-previewMeta span {\n display: inline-flex;\n align-items: center;\n min-height: 34px;\n padding: 0 12px;\n border-radius: 999px;\n background: rgba(255, 255, 255, 0.7);\n border: 1px solid rgba(148, 163, 184, 0.18);\n color: #475569;\n font-size: 12px;\n font-weight: 700;\n}\n.bb-preview {\n position: relative;\n flex: 1 1 auto;\n min-width: 0;\n min-height: 0;\n overflow: hidden;\n border-radius: 14px;\n background:\n radial-gradient(\n circle at top left,\n rgba(255, 255, 255, 0.92),\n rgba(236, 240, 244, 0.82)),\n #f8fafc;\n border: 1px solid rgba(148, 163, 184, 0.24);\n box-shadow: 0 24px 48px rgba(148, 163, 184, 0.2), inset 0 1px 0 rgba(255, 255, 255, 0.7);\n}\n.bb-loadingOverlay {\n position: absolute;\n inset: 0;\n z-index: 5;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 16px;\n padding: 24px;\n text-align: center;\n border-radius: 14px;\n background: rgba(248, 250, 252, 0.82);\n backdrop-filter: blur(2px);\n}\n.bb-loadingSpinner {\n width: 40px;\n height: 40px;\n border-radius: 999px;\n border: 3px solid rgba(148, 163, 184, 0.3);\n border-top-color: #2563eb;\n animation: bb-spin 0.8s linear infinite;\n}\n@keyframes bb-spin {\n to {\n transform: rotate(360deg);\n }\n}\n.bb-loadingMessage {\n max-width: 320px;\n color: #475569;\n font-size: 13px;\n font-weight: 600;\n line-height: 1.5;\n}\n.bb-previewStatus {\n max-width: 280px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.bb-previewStatus--success {\n background: rgba(34, 197, 94, 0.12) !important;\n border-color: rgba(34, 197, 94, 0.3) !important;\n color: #15803d !important;\n}\n.bb-previewStatus--error {\n background: rgba(239, 68, 68, 0.12) !important;\n border-color: rgba(239, 68, 68, 0.3) !important;\n color: #b91c1c !important;\n}\n.bb-previewModelName {\n max-width: 200px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.bb-previewPlaceholder {\n position: relative;\n display: flex;\n height: 100%;\n width: 100%;\n align-items: center;\n justify-content: center;\n padding: 40px;\n text-align: center;\n color: #64748b;\n font-size: 15px;\n line-height: 1.7;\n border-radius: 14px;\n border: 2px dashed rgba(148, 163, 184, 0.24);\n background:\n radial-gradient(\n circle at top,\n rgba(251, 191, 36, 0.12),\n transparent 30%),\n linear-gradient(\n 180deg,\n rgba(255, 255, 255, 0.82),\n rgba(238, 242, 248, 0.9));\n transition:\n border-color 0.2s ease,\n background 0.2s ease,\n transform 0.2s ease,\n box-shadow 0.2s ease;\n}\n.bb-previewPlaceholder > div {\n max-width: 420px;\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 14px;\n}\n.bb-previewPlaceholderTitle {\n font-size: 28px;\n line-height: 1.05;\n letter-spacing: -0.03em;\n font-weight: 800;\n color: #1f2937;\n}\n.bb-previewPlaceholderButton {\n margin-top: 8px;\n border: 0;\n border-radius: 999px;\n padding: 13px 22px;\n background: #135bc0;\n color: #ffffff;\n font-size: 14px;\n font-weight: 800;\n cursor: pointer;\n transition:\n transform 0.18s ease,\n box-shadow 0.18s ease,\n opacity 0.18s ease;\n}\n.bb-previewPlaceholderButton:hover:not(:disabled) {\n transform: translateY(-1px);\n box-shadow: 0 14px 30px rgba(19, 91, 192, 0.28);\n}\n.bb-previewPlaceholderButton:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n.bb-previewPlaceholder--dragActive {\n border-color: rgba(245, 158, 11, 0.75);\n background:\n radial-gradient(\n circle at top,\n rgba(251, 191, 36, 0.22),\n transparent 32%),\n linear-gradient(\n 180deg,\n rgba(255, 252, 245, 0.95),\n rgba(255, 244, 230, 0.92));\n transform: scale(0.995);\n box-shadow: inset 0 0 0 1px rgba(245, 158, 11, 0.14);\n}\n.bb-sectionHeader {\n cursor: pointer;\n user-select: none;\n}\n.bb-iconButton {\n background: none;\n border: none;\n cursor: pointer;\n font-size: 16px;\n color: var(--bb-sidebar-subtle-text);\n transition: color 0.2s ease;\n padding: 4px 8px;\n border-radius: 4px;\n}\n.bb-iconButton:hover {\n color: var(--bb-sidebar-text);\n background: var(--bb-sidebar-surface-strong);\n}\n.bb-cameraLive,\n.bb-cameraSaved {\n background: rgba(15, 23, 42, 0.44);\n border-radius: 8px;\n padding: 14px;\n margin-bottom: 16px;\n border: 1px solid rgba(148, 163, 184, 0.15);\n transition: all 0.2s ease;\n}\n.bb-cameraLive:hover,\n.bb-cameraSaved:hover {\n border-color: rgba(148, 163, 184, 0.25);\n background: rgba(15, 23, 42, 0.52);\n}\n.bb-cameraLiveHeader,\n.bb-cameraSavedHeader {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 12px;\n padding-bottom: 8px;\n border-bottom: 1px solid rgba(148, 163, 184, 0.15);\n}\n.bb-cameraLiveBadge {\n background:\n linear-gradient(\n 135deg,\n #10b981,\n #059669);\n color: white;\n font-size: 10px;\n font-weight: 700;\n padding: 3px 8px;\n border-radius: 10px;\n letter-spacing: 0.5px;\n text-transform: uppercase;\n}\n.bb-cameraSavedBadge {\n background:\n linear-gradient(\n 135deg,\n #3b82f6,\n #2563eb);\n color: white;\n font-size: 10px;\n font-weight: 700;\n padding: 3px 8px;\n border-radius: 10px;\n letter-spacing: 0.5px;\n text-transform: uppercase;\n}\n.bb-cameraLiveLabel,\n.bb-cameraSavedLabel {\n font-size: 12px;\n font-weight: 600;\n color: #cbd5e1;\n}\n.bb-cameraValues {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n.bb-cameraRow {\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 12px;\n flex-wrap: wrap;\n}\n.bb-cameraLabel {\n font-weight: 600;\n color: #94a3b8;\n min-width: 58px;\n font-size: 11px;\n letter-spacing: 0.02em;\n}\n.bb-cameraCode {\n font-family:\n "Monaco",\n "Menlo",\n "Ubuntu Mono",\n "Consolas",\n monospace;\n font-size: 11px;\n background: rgba(0, 0, 0, 0.3);\n padding: 3px 8px;\n border-radius: 4px;\n color: #fcd34d;\n letter-spacing: 0.02em;\n word-break: break-all;\n}\n.bb-cameraPlaceholder {\n color: #94a3b8;\n font-size: 12px;\n text-align: center;\n padding: 16px;\n font-style: italic;\n}\n.bb-cameraControls {\n display: flex;\n gap: 10px;\n margin-bottom: 14px;\n flex-wrap: wrap;\n}\n.bb-button--primary {\n background: #135bc0;\n border-color: transparent;\n color: #ffffff;\n}\n.bb-button--primary:hover:not(:disabled) {\n background: #1a68d6;\n transform: translateY(-1px);\n}\n.bb-button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n transform: none;\n}\n.bb-cameraMessage {\n padding: 10px 14px;\n border-radius: 6px;\n margin-bottom: 14px;\n font-size: 12px;\n font-weight: 500;\n animation: slideIn 0.3s ease-out;\n}\n.bb-cameraMessage--success {\n background: rgba(16, 185, 129, 0.15);\n color: #6ee7b7;\n border: 1px solid rgba(16, 185, 129, 0.3);\n}\n.bb-cameraMessage--error {\n background: rgba(239, 68, 68, 0.15);\n color: #ffe5e55c;\n border: 1px solid rgba(255, 248, 248, 0.3);\n}\n@keyframes slideIn {\n from {\n opacity: 0;\n transform: translateY(-8px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n.bb-readonly {\n background: rgba(148, 163, 184, 0.08);\n color: #9ca3af;\n cursor: default;\n}\n@media (max-width: 1280px) {\n .bb-cameraRow {\n flex-direction: column;\n align-items: flex-start;\n gap: 4px;\n }\n .bb-cameraLabel {\n min-width: auto;\n }\n .bb-cameraControls {\n flex-direction: column;\n }\n .bb-cameraControls .bb-button {\n width: 100%;\n }\n}\n@media (max-width: 980px) {\n .bb-sidebar--left {\n width: 100%;\n max-height: 42vh;\n }\n .bb-sidebar--collapsed {\n width: 100%;\n }\n .bb-previewShell {\n padding: 14px;\n }\n .bb-preview {\n min-height: 44vh;\n }\n .bb-previewHeader {\n flex-direction: column;\n align-items: flex-start;\n }\n .bb-previewControls {\n width: 100%;\n justify-content: flex-start;\n }\n .bb-previewMeta {\n justify-content: flex-start;\n }\n .bb-grid,\n .bb-actionGrid,\n .bb-statRow {\n grid-template-columns: 1fr;\n }\n}\n.bb-annotationsInfo {\n margin-top: 8px;\n}\n.bb-emptyAnnotations {\n padding: 32px;\n text-align: center;\n background: #f5f5f5;\n border-radius: 4px;\n color: #666;\n font-size: 14px;\n}\n.bb-annotationsList {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n.bb-annotationCard {\n background: #f9f9f9;\n border: 1px solid #e0e0e0;\n border-radius: 4px;\n padding: 12px;\n transition: all 0.2s ease;\n}\n.bb-annotationCard:hover {\n background: #ffffff;\n border-color: #4caf50;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n}\n.bb-annotationHeader {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 8px;\n}\n.bb-annotationBadge {\n background: #4caf50;\n color: white;\n padding: 2px 8px;\n border-radius: 2px;\n font-size: 11px;\n font-weight: 600;\n}\n.bb-annotationActions {\n display: flex;\n gap: 8px;\n}\n.bb-annotationEdit,\n.bb-annotationDelete {\n background: transparent;\n border: none;\n cursor: pointer;\n padding: 4px;\n border-radius: 2px;\n font-size: 14px;\n transition: all 0.2s ease;\n}\n.bb-annotationEdit:hover {\n background: #e8f5e9;\n transform: scale(1.05);\n}\n.bb-annotationDelete:hover {\n background: #ffebee;\n transform: scale(1.05);\n}\n.bb-annotationTitle {\n font-weight: 600;\n font-size: 14px;\n color: #333;\n margin-bottom: 6px;\n}\n.bb-annotationDescription {\n font-size: 13px;\n color: #666;\n margin-bottom: 8px;\n line-height: 1.4;\n}\n.bb-annotationPosition {\n font-size: 11px;\n font-family: monospace;\n color: #999;\n background: #f0f0f0;\n padding: 4px 6px;\n border-radius: 2px;\n display: inline-block;\n}\n.bb-modalOverlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000;\n}\n.bb-modal {\n background: white;\n border-radius: 6px;\n width: 500px;\n max-width: 90%;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);\n animation: modalSlideIn 0.2s ease;\n}\n@keyframes modalSlideIn {\n from {\n transform: translateY(-20px);\n opacity: 0;\n }\n to {\n transform: translateY(0);\n opacity: 1;\n }\n}\n.bb-modalHeader {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 16px 20px;\n border-bottom: 1px solid #e0e0e0;\n}\n.bb-modalHeader h3 {\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n}\n.bb-modalClose {\n background: transparent;\n border: none;\n font-size: 20px;\n cursor: pointer;\n color: #999;\n transition: color 0.2s ease;\n}\n.bb-modalClose:hover {\n color: #333;\n}\n.bb-modalBody {\n padding: 20px;\n}\n.bb-modalFooter {\n display: flex;\n justify-content: flex-end;\n gap: 12px;\n padding: 16px 20px;\n border-top: 1px solid #e0e0e0;\n}\n.bb-annotationsInfo {\n margin-top: 8px;\n}\n.bb-emptyAnnotations {\n padding: 32px;\n text-align: center;\n background: rgba(15, 23, 42, 0.44);\n border-radius: 10px;\n border: 1px solid rgba(148, 163, 184, 0.15);\n color: #94a3b8;\n font-size: 14px;\n}\n.bb-annotationsList {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n.bb-annotationCard {\n background: rgba(15, 23, 42, 0.44);\n border-left: 4px solid #22d3ee;\n border-radius: 8px;\n padding: 16px;\n transition: all 0.2s ease;\n border: 1px solid rgba(148, 163, 184, 0.15);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03);\n}\n.bb-annotationCard:hover {\n transform: translateX(4px);\n background: rgba(15, 23, 42, 0.6);\n border-color: rgba(148, 163, 184, 0.25);\n box-shadow: 0 4px 12px rgba(15, 23, 42, 0.3);\n}\n.bb-annotationBadge {\n display: inline-block;\n background: #22d3ee;\n color: #06343c;\n padding: 2px 10px;\n border-radius: 10px;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.5px;\n margin-bottom: 12px;\n text-transform: uppercase;\n}\n.bb-annotationTitle {\n font-weight: 700;\n font-size: 15px;\n color: #f8fafc;\n margin-bottom: 8px;\n line-height: 1.3;\n}\n.bb-annotationDescription {\n font-size: 13px;\n color: #cbd5e1;\n margin-bottom: 12px;\n line-height: 1.5;\n word-wrap: break-word;\n white-space: pre-wrap;\n}\n.bb-annotationPosition {\n font-size: 11px;\n font-family:\n "Monaco",\n "Menlo",\n "Ubuntu Mono",\n "Consolas",\n monospace;\n color: #94a3b8;\n background: rgba(0, 0, 0, 0.3);\n padding: 4px 8px;\n border-radius: 4px;\n display: inline-block;\n margin-top: 4px;\n letter-spacing: 0.02em;\n}\n.bb-annotationHeader {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 8px;\n}\n.bb-annotationActions {\n display: flex;\n gap: 4px;\n}\n.bb-annotationAction {\n background: none;\n border: none;\n color: #94a3b8;\n cursor: pointer;\n padding: 4px;\n border-radius: 2px;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s;\n}\n.bb-annotationAction--edit:hover {\n border-radius: 50%;\n background: rgba(59, 130, 246, 0.2);\n color: #3b82f6;\n}\n.bb-annotationAction--delete:hover {\n border-radius: 50%;\n background: rgba(239, 68, 68, 0.2);\n color: #ef4444;\n}\n.bb-editSpotOverlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.6);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000;\n}\n.bb-editSpotPopup {\n background:\n linear-gradient(\n 180deg,\n rgba(20, 27, 45, 0.98),\n rgba(25, 34, 57, 0.95)),\n #121826;\n border-radius: 8px;\n padding: 24px;\n min-width: 400px;\n max-width: 500px;\n border: 1px solid rgba(148, 163, 184, 0.18);\n box-shadow: 0 24px 48px rgba(15, 23, 42, 0.3);\n}\n.bb-editSpotHeader {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 16px;\n}\n.bb-editSpotTitle {\n color: #f8fafc;\n font-size: 18px;\n font-weight: 600;\n margin: 0;\n}\n.bb-editSpotClose {\n background: none;\n border: none;\n color: #94a3b8;\n font-size: 24px;\n cursor: pointer;\n padding: 0 4px;\n transition: color 0.2s ease;\n}\n.bb-editSpotClose:hover {\n color: #f8fafc;\n}\n.bb-editSpotBody {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n.bb-editSpotBody .bb-input,\n.bb-editSpotBody .bb-textarea {\n background: rgba(15, 23, 42, 0.4);\n border-color: rgba(148, 163, 184, 0.2);\n color: #f8fafc;\n}\n.bb-editSpotBody .bb-input:focus,\n.bb-editSpotBody .bb-textarea:focus {\n border-color: rgba(251, 191, 36, 0.65);\n box-shadow: 0 0 0 4px rgba(251, 191, 36, 0.12);\n}\n.bb-editSpotBody .bb-label {\n color: #d7dee9;\n}\n.bb-editSpotActions {\n display: flex;\n gap: 8px;\n margin-top: 8px;\n}\n.bb-editSpotActions .bb-button {\n background: rgba(255, 255, 255, 0.04);\n border: 1px solid rgba(148, 163, 184, 0.18);\n color: #e5edf7;\n}\n.bb-editSpotActions .bb-button:hover {\n background: rgba(255, 255, 255, 0.08);\n border-color: rgba(148, 163, 184, 0.32);\n transform: translateY(-1px);\n}\n.bb-button--danger {\n background: #ef4444;\n color: white;\n border: none;\n padding: 8px 16px;\n border-radius: 7px;\n cursor: pointer;\n font-size: 13px;\n font-weight: 700;\n display: flex;\n align-items: center;\n gap: 4px;\n transition: all 0.2s ease;\n}\n.bb-button--danger:hover {\n background: #dc2626;\n transform: translateY(-1px);\n box-shadow: 0 10px 18px rgba(239, 68, 68, 0.25);\n}\n.bb-button--primary {\n background: #135bc0;\n border-color: transparent;\n color: #ffffff;\n}\n.bb-button--primary:hover {\n background: #1a68d6;\n}\n.bb-annotationCard {\n background: rgba(15, 23, 42, 0.44);\n border-left: 4px solid #22d3ee;\n border-radius: 8px;\n padding: 16px;\n transition: all 0.2s ease;\n border: 1px solid rgba(148, 163, 184, 0.15);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03);\n}\n.bb-annotationCard:hover {\n transform: translateX(4px);\n background: rgba(15, 23, 42, 0.6);\n border-color: rgba(148, 163, 184, 0.25);\n box-shadow: 0 4px 12px rgba(15, 23, 42, 0.3);\n}\n.bb-annotationBadge {\n display: inline-block;\n background: #22d3ee;\n color: #06343c;\n padding: 2px 10px;\n border-radius: 10px;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.5px;\n margin-bottom: 12px;\n text-transform: uppercase;\n}\n.bb-annotationTitle {\n font-weight: 700;\n font-size: 15px;\n color: #f8fafc;\n margin-bottom: 8px;\n line-height: 1.3;\n}\n.bb-annotationDescription {\n font-size: 13px;\n color: #cbd5e1;\n margin-bottom: 12px;\n line-height: 1.5;\n word-wrap: break-word;\n white-space: pre-wrap;\n}\n.bb-annotationPosition {\n font-size: 11px;\n font-family:\n "Monaco",\n "Menlo",\n "Ubuntu Mono",\n "Consolas",\n monospace;\n color: #94a3b8;\n background: rgba(0, 0, 0, 0.3);\n padding: 4px 8px;\n border-radius: 4px;\n display: inline-block;\n margin-top: 4px;\n letter-spacing: 0.02em;\n}\n.bb-emptyAnnotations {\n padding: 32px;\n text-align: center;\n background: rgba(15, 23, 42, 0.44);\n border-radius: 10px;\n border: 1px solid rgba(148, 163, 184, 0.15);\n color: #94a3b8;\n font-size: 14px;\n}\n@media (max-width: 1280px) {\n .bb-cameraRow {\n flex-direction: column;\n align-items: flex-start;\n gap: 4px;\n }\n .bb-cameraLabel {\n min-width: auto;\n }\n .bb-cameraControls {\n flex-direction: column;\n }\n .bb-cameraControls .bb-button {\n width: 100%;\n }\n}\n@media (max-width: 1120px) {\n .bb-sidebar--left {\n width: 320px;\n }\n .bb-sidebar--right {\n width: 220px;\n }\n .bb-grid {\n grid-template-columns: 1fr;\n }\n .bb-statRow {\n grid-template-columns: 1fr;\n }\n}\n@media (max-width: 980px) {\n .bb-root {\n flex-direction: column;\n height: auto;\n min-height: 0;\n }\n .bb-sidebar--left {\n width: 100% !important;\n max-height: 45vh;\n border-right: none;\n border-bottom: 1px solid rgba(148, 163, 184, 0.18);\n box-shadow: 0 18px 42px rgba(15, 23, 42, 0.12);\n }\n .bb-sidebar--right {\n width: 100% !important;\n max-height: 45vh;\n border-left: none;\n border-top: 1px solid rgba(148, 163, 184, 0.18);\n box-shadow: 0 -18px 42px rgba(15, 23, 42, 0.12);\n order: 3;\n }\n .bb-sidebar--collapsed {\n width: 100% !important;\n max-height: 72px;\n overflow: hidden;\n }\n .bb-sidebar--collapsed .bb-nodeList,\n .bb-sidebar--collapsed .bb-tabRow,\n .bb-sidebar--collapsed .bb-toolbar {\n display: none;\n }\n .bb-previewShell {\n flex: 1 1 auto;\n min-height: 50vh;\n padding: 14px;\n order: 2;\n }\n .bb-preview {\n min-height: 44vh;\n border-radius: 10px;\n }\n .bb-previewHeader {\n flex-direction: column;\n align-items: flex-start;\n gap: 12px;\n }\n .bb-previewControls {\n width: 100%;\n justify-content: flex-start;\n flex-wrap: wrap;\n }\n .bb-previewMeta {\n justify-content: flex-start;\n }\n .bb-previewMeta span {\n font-size: 11px;\n padding: 0 10px;\n min-height: 30px;\n }\n .bb-grid,\n .bb-actionGrid,\n .bb-statRow {\n grid-template-columns: 1fr;\n }\n .bb-editor {\n padding: 12px 12px 20px;\n }\n .bb-editorHeader {\n flex-direction: column;\n align-items: stretch;\n padding: 14px;\n }\n .bb-editorTitle {\n font-size: 20px;\n }\n .bb-editorBadge {\n align-self: flex-start;\n }\n .bb-toolbar {\n flex-direction: column;\n gap: 12px;\n padding: 16px 20px 18px;\n }\n .bb-toolbarDivider {\n width: 100%;\n height: 1px;\n margin: 0;\n }\n .bb-tabRow {\n padding: 12px 16px 0;\n gap: 6px;\n }\n .bb-tab {\n font-size: 10px;\n padding: 8px 10px;\n }\n .bb-header {\n padding: 14px 16px 12px;\n }\n .bb-header--sidebar {\n min-height: 60px;\n }\n .bb-title {\n font-size: 17px;\n }\n .bb-subtitle {\n font-size: 13px;\n }\n .bb-statChip {\n padding: 10px 12px;\n }\n .bb-statChip strong {\n font-size: 12px;\n }\n .bb-nodeButton {\n padding: 10px 12px;\n }\n .bb-nodeName {\n font-size: 12px;\n }\n .bb-nodeMeta {\n font-size: 10px;\n }\n .bb-section {\n padding: 14px;\n }\n .bb-sectionTitle {\n font-size: 9px;\n }\n .bb-field {\n gap: 4px;\n }\n .bb-label {\n font-size: 11px;\n }\n .bb-input,\n .bb-select,\n .bb-textarea {\n padding: 9px 11px;\n font-size: 12px;\n }\n .bb-textarea {\n min-height: 80px;\n }\n .bb-button,\n .bb-fileLabel {\n min-height: 36px;\n padding: 0 14px;\n font-size: 12px;\n }\n .bb-previewPlaceholderTitle {\n font-size: 22px;\n }\n .bb-previewPlaceholderButton {\n padding: 10px 18px;\n font-size: 13px;\n }\n .bb-editSpotPopup {\n min-width: 90%;\n max-width: 90%;\n padding: 20px;\n }\n .bb-modal {\n width: 95%;\n max-width: 95%;\n }\n .bb-annotationCard {\n padding: 12px;\n }\n .bb-annotationTitle {\n font-size: 14px;\n }\n .bb-annotationDescription {\n font-size: 12px;\n }\n}\n@media (max-width: 640px) {\n .bb-root {\n flex-direction: column;\n height: auto;\n min-height: 0;\n }\n .bb-sidebar--left {\n max-height: 40vh;\n width: 100% !important;\n }\n .bb-sidebar--right {\n max-height: 40vh;\n width: 100% !important;\n }\n .bb-previewShell {\n min-height: 40vh;\n padding: 10px;\n }\n .bb-preview {\n min-height: 35vh;\n border-radius: 8px;\n }\n .bb-previewHeader {\n flex-direction: column;\n align-items: flex-start;\n gap: 8px;\n padding: 0 0 12px;\n }\n .bb-previewTitle {\n font-size: 17px;\n }\n .bb-previewControls {\n gap: 6px;\n }\n .bb-previewMeta span {\n font-size: 10px;\n padding: 0 8px;\n min-height: 26px;\n }\n .bb-editor {\n padding: 10px 10px 16px;\n }\n .bb-editorHeader {\n padding: 12px;\n flex-direction: column;\n align-items: stretch;\n }\n .bb-editorTitle {\n font-size: 18px;\n }\n .bb-editorMeta {\n font-size: 11px;\n gap: 6px 8px;\n }\n .bb-editorBadge {\n font-size: 9px;\n padding: 4px 10px;\n align-self: flex-start;\n }\n .bb-section {\n padding: 12px;\n margin-bottom: 12px;\n }\n .bb-sectionTitle {\n font-size: 9px;\n margin-bottom: 10px;\n }\n .bb-grid {\n gap: 8px;\n }\n .bb-field--full {\n grid-column: 1 / -1;\n }\n .bb-label {\n font-size: 11px;\n }\n .bb-input,\n .bb-select,\n .bb-textarea {\n padding: 8px 10px;\n font-size: 12px;\n border-radius: 6px;\n }\n .bb-textarea {\n min-height: 70px;\n }\n .bb-button,\n .bb-fileLabel {\n min-height: 34px;\n padding: 0 12px;\n font-size: 11px;\n border-radius: 6px;\n }\n .bb-toolbar {\n padding: 12px 14px 14px;\n gap: 10px;\n }\n .bb-tabRow {\n padding: 10px 12px 0;\n gap: 4px;\n }\n .bb-tab {\n font-size: 9px;\n padding: 6px 8px;\n border-radius: 4px;\n }\n .bb-header {\n padding: 10px 12px 8px;\n }\n .bb-header--sidebar {\n min-height: 52px;\n }\n .bb-title {\n font-size: 15px;\n }\n .bb-subtitle {\n font-size: 12px;\n }\n .bb-eyebrow {\n font-size: 9px;\n padding: 4px 8px;\n margin-bottom: 8px;\n }\n .bb-statRow {\n gap: 6px;\n margin-top: 14px;\n }\n .bb-statChip {\n padding: 8px 10px;\n border-radius: 6px;\n }\n .bb-statChip strong {\n font-size: 11px;\n }\n .bb-statLabel {\n font-size: 9px;\n }\n .bb-sidebarToggle {\n width: 30px;\n height: 30px;\n font-size: 14px;\n border-radius: 5px;\n }\n .bb-nodeItems {\n padding: 4px 6px;\n }\n .bb-nodeButton {\n padding: 8px 10px;\n border-radius: 6px;\n margin-bottom: 4px;\n }\n .bb-nodeName {\n font-size: 11px;\n }\n .bb-nodeMeta {\n font-size: 10px;\n }\n .bb-search {\n padding: 8px 10px;\n }\n .bb-statusCard {\n padding: 10px 12px;\n border-radius: 7px;\n }\n .bb-status {\n font-size: 12px;\n }\n .bb-statusTitle {\n font-size: 10px;\n }\n .bb-checkboxRow {\n gap: 6px;\n }\n .bb-checkbox {\n padding: 6px 10px;\n font-size: 12px;\n border-radius: 5px;\n }\n .bb-actionCard {\n padding: 10px;\n border-radius: 7px;\n }\n .bb-actionCardTitle {\n font-size: 12px;\n }\n .bb-actionCardMeta {\n font-size: 9px;\n padding: 3px 7px;\n }\n .bb-previewPlaceholder {\n padding: 24px;\n border-radius: 8px;\n }\n .bb-previewPlaceholderTitle {\n font-size: 18px;\n }\n .bb-previewPlaceholderButton {\n padding: 8px 14px;\n font-size: 12px;\n border-radius: 999px;\n }\n .bb-editSpotPopup {\n min-width: 95%;\n max-width: 95%;\n padding: 16px;\n border-radius: 6px;\n }\n .bb-editSpotTitle {\n font-size: 16px;\n }\n .bb-editSpotActions .bb-button {\n padding: 8px 12px;\n font-size: 12px;\n }\n .bb-modal {\n width: 98%;\n max-width: 98%;\n border-radius: 5px;\n }\n .bb-modalHeader {\n padding: 12px 16px;\n }\n .bb-modalHeader h3 {\n font-size: 16px;\n }\n .bb-modalBody {\n padding: 14px;\n }\n .bb-modalFooter {\n padding: 12px 16px;\n flex-direction: column;\n gap: 8px;\n }\n .bb-modalFooter .bb-button {\n width: 100%;\n justify-content: center;\n }\n .bb-annotationCard {\n padding: 10px;\n border-left-width: 3px;\n border-radius: 6px;\n }\n .bb-annotationTitle {\n font-size: 13px;\n }\n .bb-annotationDescription {\n font-size: 11px;\n }\n .bb-annotationPosition {\n font-size: 10px;\n padding: 3px 6px;\n }\n .bb-annotationBadge {\n font-size: 9px;\n padding: 2px 8px;\n }\n .bb-emptyAnnotations {\n padding: 20px;\n font-size: 12px;\n border-radius: 7px;\n }\n .bb-removeModelButton {\n min-height: 30px;\n padding: 0 10px;\n font-size: 11px;\n }\n .bb-annotationsList {\n gap: 8px;\n }\n .bb-cameraLive,\n .bb-cameraSaved {\n padding: 10px;\n border-radius: 6px;\n }\n .bb-cameraLiveHeader,\n .bb-cameraSavedHeader {\n flex-wrap: wrap;\n }\n .bb-cameraLiveLabel,\n .bb-cameraSavedLabel {\n font-size: 11px;\n }\n .bb-cameraRow {\n font-size: 11px;\n gap: 4px;\n }\n .bb-cameraCode {\n font-size: 10px;\n padding: 2px 6px;\n }\n .bb-cameraControls {\n flex-direction: column;\n gap: 6px;\n }\n .bb-cameraControls .bb-button {\n width: 100%;\n justify-content: center;\n }\n .bb-cameraMessage {\n font-size: 11px;\n padding: 8px 12px;\n }\n .bb-readonly {\n font-size: 11px;\n padding: 6px 8px;\n }\n}\n@media (max-width: 420px) {\n .bb-sidebar--left {\n max-height: 35vh;\n }\n .bb-sidebar--right {\n max-height: 35vh;\n }\n .bb-previewShell {\n min-height: 35vh;\n padding: 6px;\n }\n .bb-preview {\n min-height: 30vh;\n border-radius: 6px;\n }\n .bb-previewTitle {\n font-size: 15px;\n }\n .bb-previewMeta span {\n font-size: 9px;\n padding: 0 6px;\n min-height: 22px;\n }\n .bb-editorTitle {\n font-size: 16px;\n }\n .bb-title {\n font-size: 13px;\n }\n .bb-tab {\n font-size: 8px;\n padding: 5px 6px;\n }\n .bb-button,\n .bb-fileLabel {\n font-size: 10px;\n min-height: 30px;\n padding: 0 10px;\n }\n .bb-input,\n .bb-select,\n .bb-textarea {\n font-size: 11px;\n padding: 6px 8px;\n }\n .bb-statChip {\n padding: 6px 8px;\n }\n .bb-statChip strong {\n font-size: 10px;\n }\n .bb-previewPlaceholderTitle {\n font-size: 15px;\n }\n .bb-previewPlaceholderButton {\n font-size: 11px;\n padding: 6px 12px;\n }\n .bb-editSpotPopup {\n padding: 14px;\n border-radius: 5px;\n }\n .bb-editSpotTitle {\n font-size: 14px;\n }\n .bb-editSpotBody .bb-input,\n .bb-editSpotBody .bb-textarea {\n font-size: 12px;\n padding: 6px 8px;\n }\n .bb-modalHeader h3 {\n font-size: 14px;\n }\n .bb-annotationTitle {\n font-size: 12px;\n }\n}\n@media (max-width: 980px) {\n .bb-sidebar--collapsed .bb-header--sidebar {\n min-height: 52px;\n }\n .bb-sidebar--collapsed .bb-title {\n font-size: 14px;\n }\n .bb-sidebar--collapsed .bb-sidebarToggle {\n width: 28px;\n height: 28px;\n font-size: 12px;\n }\n}\n@media (hover: none) and (pointer: coarse) {\n .bb-nodeButton:hover,\n .bb-button:hover,\n .bb-fileLabel:hover,\n .bb-tab:hover,\n .bb-sidebarToggle:hover,\n .bb-annotationCard:hover {\n transform: none;\n }\n .bb-nodeButton:active,\n .bb-button:active,\n .bb-fileLabel:active,\n .bb-tab:active,\n .bb-sidebarToggle:active {\n transform: scale(0.97);\n transition: transform 0.1s ease;\n }\n}\n@media print {\n .bb-root {\n height: auto;\n min-height: 0;\n background: white;\n }\n .bb-sidebar--left,\n .bb-sidebar--right {\n width: 100% !important;\n max-height: none !important;\n background: white !important;\n color: black !important;\n border: 1px solid #ddd !important;\n box-shadow: none !important;\n }\n .bb-previewShell {\n min-height: auto !important;\n padding: 10px !important;\n }\n .bb-preview {\n min-height: auto !important;\n border-radius: 4px !important;\n border: 1px solid #ddd !important;\n box-shadow: none !important;\n }\n .bb-button,\n .bb-fileLabel,\n .bb-sidebarToggle,\n .bb-tab,\n .bb-annotationAction,\n .bb-iconButton {\n display: none !important;\n }\n .bb-nodeList {\n overflow: visible !important;\n }\n .bb-previewPlaceholderButton {\n display: none !important;\n }\n}\n.bb-checkboxRow {\n display: flex;\n flex-wrap: wrap;\n gap: 12px;\n align-items: center;\n padding: 4px 0;\n}\n.bb-checkbox {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n border-radius: 7px;\n font-weight: 600;\n font-size: 13px;\n color: #f3eeee;\n cursor: pointer;\n user-select: none;\n transition: all 0.2s ease;\n}\n.bb-checkbox input[type=checkbox] {\n width: 16px;\n height: 16px;\n accent-color: #f59e0b;\n cursor: pointer;\n flex-shrink: 0;\n}\n.bb-editorDivider {\n height: 1px;\n background: rgba(148, 163, 184, 0.14);\n margin: 4px 0 16px 0;\n width: 100%;\n}\n.bb-preview {\n position: relative;\n width: 100%;\n height: 100%;\n overflow: hidden;\n}\n.ri-viewer {\n width: 100% !important;\n height: 100% !important;\n position: relative !important;\n}\n.ri-buttonContainer {\n position: absolute !important;\n bottom: 20px !important;\n right: 20px !important;\n z-index: 10000 !important;\n display: flex !important;\n gap: 10px !important;\n pointer-events: auto !important;\n}\n.ri-downloadButton {\n display: flex !important;\n align-items: center !important;\n gap: 6px !important;\n padding: 10px 12px !important;\n background: #2f302f74 !important;\n color: white !important;\n border: none !important;\n border-radius: 6px !important;\n cursor: pointer !important;\n font-size: 12px !important;\n font-weight: 500 !important;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15) !important;\n opacity: 1 !important;\n visibility: visible !important;\n}\n.ri-downloadButton svg {\n width: 14px !important;\n height: 14px !important;\n}\n.ri-resetButton {\n display: flex !important;\n align-items: center !important;\n justify-content: center !important;\n padding: 6px !important;\n background: #2f302f74 !important;\n border: none !important;\n border-radius: 6px !important;\n cursor: pointer !important;\n opacity: 1 !important;\n visibility: visible !important;\n}\n.ri-resetButton svg {\n width: 14px !important;\n height: 14px !important;\n}\n.bb-reset-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.6);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000;\n backdrop-filter: blur(4px);\n animation: bb-fade-in 0.2s ease;\n}\n.bb-reset-dialog {\n background: #1a1f2e;\n border-radius: 12px;\n max-width: 480px;\n width: 90%;\n border: 1px solid rgba(148, 163, 184, 0.18);\n box-shadow: 0 24px 48px rgba(0, 0, 0, 0.5);\n animation: bb-slide-up 0.25s ease;\n}\n.bb-reset-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 20px 24px 16px;\n border-bottom: 1px solid rgba(148, 163, 184, 0.1);\n}\n.bb-reset-title {\n display: flex;\n align-items: center;\n gap: 10px;\n margin: 0;\n font-size: 18px;\n font-weight: 700;\n color: #f8fafc;\n}\n.bb-reset-title .bb-icon-warning {\n color: #f59e0b;\n flex-shrink: 0;\n}\n.bb-reset-close {\n background: transparent;\n border: none;\n color: #94a3b8;\n cursor: pointer;\n padding: 4px;\n border-radius: 4px;\n transition: all 0.2s ease;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n.bb-reset-close:hover {\n color: #f8fafc;\n background: rgba(255, 255, 255, 0.06);\n}\n.bb-reset-body {\n padding: 20px 24px 24px;\n}\n.bb-reset-message {\n margin: 0 0 12px 0;\n font-size: 15px;\n color: #e2e8f0;\n line-height: 1.5;\n}\n.bb-reset-message strong {\n color: #f8fafc;\n}\n.bb-reset-details {\n color: #94a3b8;\n text-align: center;\n font-size: 13px;\n line-height: 1.6;\n margin: 0 0 12px 0;\n}\n.bb-reset-warning {\n display: flex;\n align-items: center;\n gap: 8px;\n color: #f59e0b;\n font-size: 13px;\n font-weight: 600;\n margin: 4px 0 0 0;\n padding: 10px 12px;\n background: rgba(245, 158, 11, 0.08);\n border-radius: 6px;\n border: 1px solid rgba(245, 158, 11, 0.15);\n}\n.bb-reset-footer {\n display: flex;\n justify-content: flex-end;\n gap: 10px;\n padding: 16px 24px 20px;\n border-top: 1px solid rgba(148, 163, 184, 0.1);\n}\n.bb-reset-btn {\n padding: 8px 20px;\n border-radius: 6px;\n font-size: 13px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.2s ease;\n border: 1px solid rgba(148, 163, 184, 0.18);\n background: rgba(255, 255, 255, 0.04);\n color: #e2e8f0;\n}\n.bb-reset-btn:hover {\n background: rgba(255, 255, 255, 0.08);\n transform: translateY(-1px);\n}\n.bb-reset-btn-danger {\n padding: 8px 20px;\n border-radius: 6px;\n font-size: 13px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.2s ease;\n border: none;\n background: #dc2626;\n color: white;\n}\n.bb-reset-btn-danger:hover {\n background: #b91c1c;\n transform: translateY(-1px);\n box-shadow: 0 4px 12px rgba(220, 38, 38, 0.3);\n}\n@keyframes bb-fade-in {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n@keyframes bb-slide-up {\n from {\n opacity: 0;\n transform: translateY(20px) scale(0.96);\n }\n to {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n}\n');
6965
7162
 
6966
7163
  // src/hooks/useViewerCamera.ts
6967
- import { useCallback as useCallback5, useRef as useRef10, useState as useState11 } from "react";
6968
- import { Vector3 as Vector37 } from "three";
7164
+ import { useCallback as useCallback5, useRef as useRef11, useState as useState11 } from "react";
7165
+ import { Vector3 as Vector38 } from "three";
6969
7166
 
6970
7167
  // src/utils/resolveBindingKey.ts
6971
7168
  function resolveBindingKey(objectBindings, objectId) {
@@ -6989,8 +7186,8 @@ function toTuple(vector) {
6989
7186
  return [vector.x, vector.y, vector.z];
6990
7187
  }
6991
7188
  function buildCameraState(camera, controls) {
6992
- const position = new Vector37();
6993
- const target = new Vector37();
7189
+ const position = new Vector38();
7190
+ const target = new Vector38();
6994
7191
  controls.getPosition(position);
6995
7192
  controls.getTarget(target);
6996
7193
  return {
@@ -7016,7 +7213,7 @@ async function runSmoothCameraTransition2(controls, action, smoothTime = 0.6) {
7016
7213
  }
7017
7214
  }
7018
7215
  function useViewerCamera() {
7019
- const viewerRef = useRef10(null);
7216
+ const viewerRef = useRef11(null);
7020
7217
  const [cameraState, setCameraStateValue] = useState11(
7021
7218
  null
7022
7219
  );
@@ -7031,8 +7228,8 @@ function useViewerCamera() {
7031
7228
  },
7032
7229
  [updateCameraState]
7033
7230
  );
7034
- const homeCameraStateRef = useRef10(null);
7035
- const homeSceneRef = useRef10(null);
7231
+ const homeCameraStateRef = useRef11(null);
7232
+ const homeSceneRef = useRef11(null);
7036
7233
  const handleViewerReady = useCallback5((viewer) => {
7037
7234
  viewerRef.current = viewer;
7038
7235
  updateCameraState(buildCameraState(viewer.controls.camera, viewer.controls));
@@ -7202,18 +7399,18 @@ function useViewerCamera() {
7202
7399
  }
7203
7400
 
7204
7401
  // src/hooks/useViewerAnimations.ts
7205
- import { useCallback as useCallback6, useEffect as useEffect14, useRef as useRef11, useState as useState12 } from "react";
7402
+ import { useCallback as useCallback6, useEffect as useEffect15, useRef as useRef12, useState as useState12 } from "react";
7206
7403
  function useViewerAnimations() {
7207
- const controlsRef = useRef11(null);
7404
+ const controlsRef = useRef12(null);
7208
7405
  const [clips, setClips] = useState12([]);
7209
- const unsubscribeRef = useRef11(null);
7406
+ const unsubscribeRef = useRef12(null);
7210
7407
  const [clipDetails, setClipDetails] = useState12([]);
7211
7408
  const [currentClip, setCurrentClip] = useState12(null);
7212
7409
  const [isPlaying, setIsPlaying] = useState12(false);
7213
7410
  const [speed, setSpeedState] = useState12(1);
7214
7411
  const [time, setTime] = useState12(0);
7215
7412
  const [duration, setDuration] = useState12(0);
7216
- useEffect14(
7413
+ useEffect15(
7217
7414
  () => () => {
7218
7415
  unsubscribeRef.current?.();
7219
7416
  },
@@ -7475,7 +7672,7 @@ function inferMimeType(path) {
7475
7672
  }
7476
7673
 
7477
7674
  // src/components/BindingBuilder/SceneSettings/SceneSettingsTab.tsx
7478
- import { useState as useState19, useEffect as useEffect16 } from "react";
7675
+ import { useState as useState19, useEffect as useEffect17 } from "react";
7479
7676
 
7480
7677
  // src/components/BindingBuilder/SceneSettings/GeneralSceneSettings.tsx
7481
7678
  import { useState as useState14 } from "react";
@@ -7488,7 +7685,7 @@ import {
7488
7685
  import { HexColorInput as HexColorInput2, HexColorPicker as HexColorPicker3 } from "react-colorful";
7489
7686
 
7490
7687
  // src/components/BindingBuilder/SceneSettings/SceneControls.tsx
7491
- import { useLayoutEffect as useLayoutEffect2, useRef as useRef12, useState as useState13 } from "react";
7688
+ import { useLayoutEffect as useLayoutEffect2, useRef as useRef13, useState as useState13 } from "react";
7492
7689
  import { createPortal as createPortal2 } from "react-dom";
7493
7690
  import { ChevronDown, Info } from "lucide-react";
7494
7691
  import { HexColorInput, HexColorPicker as HexColorPicker2 } from "react-colorful";
@@ -7503,9 +7700,9 @@ function getContrastTextColor(hex) {
7503
7700
  }
7504
7701
 
7505
7702
  // src/hooks/useClickOutside.ts
7506
- import { useEffect as useEffect15 } from "react";
7703
+ import { useEffect as useEffect16 } from "react";
7507
7704
  function useClickOutside(refs, onOutside, active) {
7508
- useEffect15(() => {
7705
+ useEffect16(() => {
7509
7706
  if (!active) return;
7510
7707
  const refList = Array.isArray(refs) ? refs : [refs];
7511
7708
  const handlePointerDown = (event) => {
@@ -7529,7 +7726,7 @@ function ColorPopover({
7529
7726
  onClose,
7530
7727
  children
7531
7728
  }) {
7532
- const popoverRef = useRef12(null);
7729
+ const popoverRef = useRef13(null);
7533
7730
  const [position, setPosition] = useState13(null);
7534
7731
  useClickOutside([popoverRef, anchorRef], onClose, true);
7535
7732
  useLayoutEffect2(() => {
@@ -7725,7 +7922,7 @@ function ColorRow({
7725
7922
  onChange
7726
7923
  }) {
7727
7924
  const [open, setOpen] = useState13(false);
7728
- const anchorRef = useRef12(null);
7925
+ const anchorRef = useRef13(null);
7729
7926
  return /* @__PURE__ */ jsxs16("div", { className: "se-field", children: [
7730
7927
  /* @__PURE__ */ jsxs16("div", { className: "se-fieldInline", children: [
7731
7928
  /* @__PURE__ */ jsx20("span", { className: "se-fieldLabel", children: label }),
@@ -10582,8 +10779,8 @@ function LightingTab({ config, onChange }) {
10582
10779
  }
10583
10780
 
10584
10781
  // src/components/BindingBuilder/SceneSettings/AnimationsTab.tsx
10585
- import { useMemo as useMemo13, useState as useState17 } from "react";
10586
- import { ChevronDown as ChevronDown3, Pause, Play, Square as Square2, Trash2 as Trash22 } from "lucide-react";
10782
+ import { useMemo as useMemo14, useState as useState17 } from "react";
10783
+ import { ChevronDown as ChevronDown3, Pause as Pause2, Play as Play2, Square as Square2, Trash2 as Trash22 } from "lucide-react";
10587
10784
  import { jsx as jsx29, jsxs as jsxs25 } from "react/jsx-runtime";
10588
10785
  function formatDuration(duration) {
10589
10786
  if (!Number.isFinite(duration) || duration <= 0) return "Unknown duration";
@@ -10618,7 +10815,7 @@ function AnimationsTab({
10618
10815
  seek
10619
10816
  }) {
10620
10817
  const [showRemoved, setShowRemoved] = useState17(false);
10621
- const durationBySourceName = useMemo13(
10818
+ const durationBySourceName = useMemo14(
10622
10819
  () => new Map(
10623
10820
  clipDetails.map((clipDetail) => [
10624
10821
  clipDetail.sourceName,
@@ -10759,7 +10956,7 @@ function AnimationsTab({
10759
10956
  onClick: () => isActivePlaying ? pause() : play(clip.sourceName),
10760
10957
  "aria-label": isActivePlaying ? "Pause" : "Play",
10761
10958
  title: isActivePlaying ? "Pause" : "Play",
10762
- children: isActivePlaying ? /* @__PURE__ */ jsx29(Pause, { size: 16 }) : /* @__PURE__ */ jsx29(Play, { size: 16 })
10959
+ children: isActivePlaying ? /* @__PURE__ */ jsx29(Pause2, { size: 16 }) : /* @__PURE__ */ jsx29(Play2, { size: 16 })
10763
10960
  }
10764
10961
  );
10765
10962
  })(),
@@ -11081,8 +11278,161 @@ function AnnotationsTab({ config, onChange }) {
11081
11278
  ] });
11082
11279
  }
11083
11280
 
11281
+ // src/components/BindingBuilder/SceneSettings/CinematicTab.tsx
11282
+ import { ArrowDown, ArrowUp, MapPin as MapPin2, Plus as Plus2, Trash2 as Trash24 } from "lucide-react";
11283
+ import { jsx as jsx31, jsxs as jsxs27 } from "react/jsx-runtime";
11284
+ var DEFAULT_CINEMATIC = {
11285
+ enabled: false,
11286
+ waypoints: [],
11287
+ loop: true,
11288
+ autoPlay: false
11289
+ };
11290
+ function CinematicTab({
11291
+ config,
11292
+ onChange,
11293
+ cameraState,
11294
+ onPreviewWaypoint
11295
+ }) {
11296
+ const cinematic = config.cinematic ?? DEFAULT_CINEMATIC;
11297
+ const { waypoints } = cinematic;
11298
+ const update = (patch) => {
11299
+ onChange({ ...config, cinematic: { ...cinematic, ...patch } });
11300
+ };
11301
+ const addWaypoint = () => {
11302
+ if (!cameraState) return;
11303
+ update({
11304
+ waypoints: [
11305
+ ...waypoints,
11306
+ {
11307
+ position: [...cameraState.position],
11308
+ target: [...cameraState.target]
11309
+ }
11310
+ ]
11311
+ });
11312
+ };
11313
+ const deleteWaypoint = (index) => {
11314
+ update({ waypoints: waypoints.filter((_, i) => i !== index) });
11315
+ };
11316
+ const moveWaypoint = (index, delta) => {
11317
+ const next = index + delta;
11318
+ if (next < 0 || next >= waypoints.length) return;
11319
+ const reordered = [...waypoints];
11320
+ [reordered[index], reordered[next]] = [reordered[next], reordered[index]];
11321
+ update({ waypoints: reordered });
11322
+ };
11323
+ return /* @__PURE__ */ jsxs27(Section, { title: "Cinematic Camera", children: [
11324
+ /* @__PURE__ */ jsx31("p", { className: "se-cinematicHint", children: "Frame the model in the viewer, then capture it as a waypoint. The camera glides through the waypoints in order \u2014 enable and press play in the viewer to preview." }),
11325
+ /* @__PURE__ */ jsxs27(
11326
+ "button",
11327
+ {
11328
+ type: "button",
11329
+ className: "bb-button bb-button--primary se-cinematicAdd",
11330
+ onClick: addWaypoint,
11331
+ disabled: !cameraState,
11332
+ title: "Capture the current camera view as a waypoint",
11333
+ children: [
11334
+ /* @__PURE__ */ jsx31(Plus2, { size: 15 }),
11335
+ "Add waypoint from current view"
11336
+ ]
11337
+ }
11338
+ ),
11339
+ waypoints.length === 0 ? /* @__PURE__ */ jsx31("div", { className: "bb-emptyAnnotations", children: "No waypoints yet. Position the camera and add your first one." }) : /* @__PURE__ */ jsx31("div", { className: "se-waypointList", children: waypoints.map((waypoint, index) => /* @__PURE__ */ jsxs27("div", { className: "se-waypointCard", children: [
11340
+ /* @__PURE__ */ jsx31("div", { className: "se-waypointBadge", children: index + 1 }),
11341
+ /* @__PURE__ */ jsxs27("div", { className: "se-waypointBody", children: [
11342
+ /* @__PURE__ */ jsxs27("div", { className: "se-waypointCoords", children: [
11343
+ "pos [",
11344
+ waypoint.position.map((n) => n.toFixed(1)).join(", "),
11345
+ "]"
11346
+ ] }),
11347
+ /* @__PURE__ */ jsxs27("div", { className: "se-waypointCoords", children: [
11348
+ "look [",
11349
+ waypoint.target.map((n) => n.toFixed(1)).join(", "),
11350
+ "]"
11351
+ ] })
11352
+ ] }),
11353
+ /* @__PURE__ */ jsxs27("div", { className: "se-waypointActions", children: [
11354
+ /* @__PURE__ */ jsx31(
11355
+ "button",
11356
+ {
11357
+ className: "se-waypointAction",
11358
+ onClick: () => onPreviewWaypoint?.(waypoint),
11359
+ title: "Fly camera to this waypoint",
11360
+ disabled: !onPreviewWaypoint,
11361
+ children: /* @__PURE__ */ jsx31(MapPin2, { size: 15 })
11362
+ }
11363
+ ),
11364
+ /* @__PURE__ */ jsx31(
11365
+ "button",
11366
+ {
11367
+ className: "se-waypointAction",
11368
+ onClick: () => moveWaypoint(index, -1),
11369
+ title: "Move up",
11370
+ disabled: index === 0,
11371
+ children: /* @__PURE__ */ jsx31(ArrowUp, { size: 15 })
11372
+ }
11373
+ ),
11374
+ /* @__PURE__ */ jsx31(
11375
+ "button",
11376
+ {
11377
+ className: "se-waypointAction",
11378
+ onClick: () => moveWaypoint(index, 1),
11379
+ title: "Move down",
11380
+ disabled: index === waypoints.length - 1,
11381
+ children: /* @__PURE__ */ jsx31(ArrowDown, { size: 15 })
11382
+ }
11383
+ ),
11384
+ /* @__PURE__ */ jsx31(
11385
+ "button",
11386
+ {
11387
+ className: "se-waypointAction se-waypointAction--delete",
11388
+ onClick: () => deleteWaypoint(index),
11389
+ title: "Delete waypoint",
11390
+ children: /* @__PURE__ */ jsx31(Trash24, { size: 15 })
11391
+ }
11392
+ )
11393
+ ] })
11394
+ ] }, index)) }),
11395
+ /* @__PURE__ */ jsx31(
11396
+ ToggleRow,
11397
+ {
11398
+ label: "Enable cinematic camera",
11399
+ checked: cinematic.enabled,
11400
+ onChange: (enabled) => update({ enabled })
11401
+ }
11402
+ ),
11403
+ /* @__PURE__ */ jsx31(
11404
+ ToggleRow,
11405
+ {
11406
+ label: "Loop",
11407
+ checked: cinematic.loop,
11408
+ onChange: (loop) => update({ loop })
11409
+ }
11410
+ ),
11411
+ /* @__PURE__ */ jsx31(
11412
+ ToggleRow,
11413
+ {
11414
+ label: "Auto-play on load",
11415
+ checked: cinematic.autoPlay,
11416
+ onChange: (autoPlay) => update({ autoPlay })
11417
+ }
11418
+ ),
11419
+ /* @__PURE__ */ jsx31(
11420
+ SliderRow,
11421
+ {
11422
+ label: "Duration",
11423
+ value: cinematic.duration ?? Math.max(waypoints.length * 3.5, 8),
11424
+ min: 4,
11425
+ max: 120,
11426
+ step: 1,
11427
+ formatValue: (v) => `${v}s`,
11428
+ onChange: (duration) => update({ duration })
11429
+ }
11430
+ )
11431
+ ] });
11432
+ }
11433
+
11084
11434
  // src/styles/SceneSettingsTab.css
11085
- styleInject('.se-tabContent {\n display: flex;\n flex-direction: column;\n}\n.se-tabContent * {\n box-sizing: border-box;\n}\n.se-subTabRow {\n display: flex;\n justify-content: space-between;\n gap: 6px;\n padding-bottom: 16px;\n border-bottom: 1px solid rgba(148, 163, 184, 0.1);\n margin-bottom: 4px;\n}\n.se-subTab {\n padding: 8px 12px;\n display: flex;\n justify-items: center;\n border-radius: 10px;\n border: 1px solid rgba(148, 163, 184, 0.18);\n background: rgba(255, 255, 255, 0.04);\n color: #b3bfd2;\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n cursor: pointer;\n transition:\n background-color 0.18s ease,\n border-color 0.18s ease,\n color 0.18s ease;\n}\n.se-subTab:hover {\n background: rgba(255, 255, 255, 0.08);\n border-color: rgba(148, 163, 184, 0.32);\n}\n.se-subTab--active {\n background: rgba(99, 102, 241, 0.18);\n border-color: rgba(99, 102, 241, 0.4);\n color: #f8fafc;\n}\n.se-generalContent {\n display: flex;\n flex-direction: column;\n padding-top: 0;\n}\n.se-placeholder {\n font-size: 13px;\n color: #93a4c0;\n}\n.se-section {\n margin-bottom: 10px;\n border-radius: 7px;\n background: rgba(255, 255, 255, 0.03);\n border: 1px solid rgba(148, 163, 184, 0.12);\n overflow: hidden;\n padding: 0;\n}\n.se-section:last-child {\n margin-bottom: 0;\n}\n.se-sectionTitle {\n margin: 0;\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n color: #c3cbdb;\n padding: 11px 14px;\n border-bottom: 1px solid rgba(148, 163, 184, 0.1);\n}\n.se-sectionBody {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 14px;\n}\n.se-field {\n display: flex;\n flex-direction: column;\n gap: 5px;\n}\n.se-fieldInline {\n flex-direction: row;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n}\n.se-fieldLabel {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n font-size: 12px;\n font-weight: 700;\n color: #d7dee9;\n}\n.se-fieldValue {\n font-variant-numeric: tabular-nums;\n font-size: 12px;\n color: #93a4c0;\n}\n.se-slider {\n width: 100%;\n accent-color: #2aa8d8;\n cursor: pointer;\n}\n.se-checkbox {\n width: 18px;\n height: 18px;\n accent-color: #2aa8d8;\n cursor: pointer;\n}\n.se-select,\n.se-textInput,\n.se-numberInput {\n background: rgba(15, 23, 42, 0.28);\n border: 1px solid rgba(148, 163, 184, 0.2);\n border-radius: 7px;\n color: #f8fafc;\n font-size: 13px;\n padding: 11px 13px;\n outline: none;\n}\n.se-select:focus,\n.se-textInput:focus,\n.se-numberInput:focus {\n outline: none;\n border-color: rgba(42, 168, 216, 0.65);\n box-shadow: 0 0 0 4px rgba(42, 168, 216, 0.12);\n}\n.se-select {\n min-width: 130px;\n text-transform: capitalize;\n}\n.se-textInput {\n width: 100%;\n}\n.se-numberInput {\n width: 100%;\n}\n.se-swatch {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n min-width: 96px;\n height: 30px;\n padding: 0 8px;\n border-radius: 8px;\n border: 1px solid rgba(148, 163, 184, 0.35);\n cursor: pointer;\n box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.15);\n}\n.se-swatchCode {\n font-size: 11px;\n font-weight: 600;\n font-variant-numeric: tabular-nums;\n color: #fff;\n mix-blend-mode: difference;\n}\n.se-colorPicker {\n margin-top: 6px;\n display: flex;\n flex-direction: column;\n gap: 8px;\n width: fit-content;\n}\n.se-colorPicker .react-colorful {\n width: 100%;\n height: 140px;\n}\n.se-inlineColorPicker .react-colorful {\n width: 100%;\n height: 240px;\n}\n.se-colorPickerPopover {\n z-index: 1000;\n padding: 10px;\n border-radius: 8px;\n background: #1b2333;\n border: 1px solid rgba(148, 163, 184, 0.25);\n box-shadow: 0 12px 28px rgba(0, 0, 0, 0.4);\n}\n.se-colorPickerPopover .react-colorful {\n width: 200px;\n height: 160px;\n}\n.se-vectorRow {\n display: grid;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n gap: 8px;\n}\n.se-vectorAxis {\n display: flex;\n flex-direction: column;\n gap: 4px;\n font-size: 11px;\n color: #93a4c0;\n}\n.se-panel {\n margin-bottom: 10px;\n overflow: hidden;\n border: 1px solid rgba(148, 163, 184, 0.12);\n border-radius: 7px;\n background: rgba(255, 255, 255, 0.03);\n}\n.se-panel:last-child {\n margin-bottom: 0;\n}\n.se-panelHeader {\n display: flex;\n width: 100%;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n border: none;\n background: rgba(255, 255, 255, 0.03);\n padding: 11px 14px;\n color: #e5e7eb;\n cursor: pointer;\n text-align: left;\n}\n.se-panelHeader:hover {\n background: rgba(255, 255, 255, 0.06);\n}\n.se-panelHeaderMain {\n display: flex;\n min-width: 0;\n align-items: center;\n gap: 8px;\n}\n.se-panelChevron {\n flex-shrink: 0;\n color: #8b97ab;\n transform: rotate(-90deg);\n transition: transform 0.18s ease;\n}\n.se-panelChevron--open {\n transform: rotate(0deg);\n}\n.se-panelTitle {\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n color: #c3cbdb;\n}\n.se-panelHelp {\n display: inline-flex;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n width: 18px;\n height: 18px;\n margin-left: 2px;\n border-radius: 999px;\n color: #b9c0ca;\n cursor: help;\n transition:\n background 0.18s ease,\n color 0.18s ease,\n transform 0.18s ease;\n}\n.se-panelHelp:hover {\n background: rgba(255, 255, 255, 0.08);\n color: #eef2ff;\n transform: translateY(-0.5px);\n}\n.se-panelTrailing {\n display: flex;\n align-items: center;\n}\n.se-panelBody {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 14px;\n border-top: 1px solid rgba(148, 163, 184, 0.1);\n background: transparent;\n}\n.se-iconButton {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 30px;\n height: 30px;\n border: none;\n border-radius: 8px;\n background: rgba(255, 255, 255, 0.08);\n color: #d7dde8;\n cursor: pointer;\n}\n.se-iconButton:hover {\n background: rgba(255, 255, 255, 0.12);\n}\n.se-axisStepperGrid {\n display: grid;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n gap: 12px;\n}\n.se-axisStepper {\n display: grid;\n grid-template-columns: 40px 1fr 40px;\n align-items: center;\n min-height: 30px;\n border-radius: 10px;\n background: rgba(17, 24, 39, 0.62);\n border: 1px solid rgba(255, 255, 255, 0.05);\n}\n.se-axisStepperButton {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n height: 100%;\n border: none;\n background: transparent;\n color: #f3f4f6;\n cursor: pointer;\n}\n.se-axisStepperButton:hover {\n background: rgba(255, 255, 255, 0.04);\n}\n.se-axisStepperLabel {\n text-align: center;\n font-size: 15px;\n color: #f3f4f6;\n}\n.se-segmentedControl {\n display: grid;\n grid-auto-flow: column;\n overflow: hidden;\n border-radius: 10px;\n background: rgba(17, 24, 39, 0.62);\n}\n.se-segmentedOption {\n min-height: 35px;\n border: none;\n background: transparent;\n color: #d1d5db;\n font-size: 14px;\n text-transform: uppercase;\n cursor: pointer;\n}\n.se-segmentedOption--active {\n background: #2aa8d8;\n color: #fff;\n}\n.se-helperCard {\n padding: 32px;\n text-align: center;\n background: rgba(15, 23, 42, 0.44);\n border-radius: 10px;\n border: 1px solid rgba(148, 163, 184, 0.15);\n color: #94a3b8;\n font-size: 14px;\n}\n.se-helperCard strong {\n font-size: 14px;\n}\n.se-helperCard span {\n font-size: 13px;\n line-height: 1.4;\n color: #c7d0df;\n}\n.se-switch {\n position: relative;\n display: inline-flex;\n align-items: center;\n}\n.se-switch input[type=checkbox] {\n position: absolute;\n left: 0;\n top: 0;\n z-index: 10;\n width: 100%;\n height: 100%;\n cursor: pointer;\n opacity: 0;\n}\n.se-switchTrack {\n position: relative;\n display: inline-flex;\n align-items: center;\n gap: 8px;\n}\n.se-switchTrack::before {\n content: "";\n width: 36px;\n height: 20px;\n background: #4b5563;\n display: inline-block;\n border-radius: 20px;\n transition: background-color 0.2s ease-in;\n}\n.se-switchThumb {\n position: absolute;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n left: 2px;\n top: 2px;\n z-index: 2;\n background: #fff;\n box-shadow: 0 0 5px rgba(0, 0, 0, 0.2);\n transition: 0.2s ease-in;\n}\n.se-switch input[type=checkbox]:hover + .se-switchTrack .se-switchThumb {\n box-shadow: 0 2px 15px 0 rgba(0, 0, 0, 0.2), 0 3px 8px 0 rgba(0, 0, 0, 0.1);\n}\n.se-switch input[type=checkbox]:checked + .se-switchTrack::before {\n background: #376fcb;\n}\n.se-switch input[type=checkbox]:checked + .se-switchTrack .se-switchThumb {\n background: #4285f4;\n left: 18px;\n}\n.se-switchLabel {\n font-size: 12px;\n font-weight: 700;\n letter-spacing: 0.06em;\n color: #e5e7eb;\n}\n.se-switch input[type=checkbox]:checked ~ .se-switchTrack .se-switchLabel {\n color: #2aa8d8;\n}\n.se-lightList {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n.se-lightRow {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 10px;\n border-radius: 8px;\n border: 1px solid rgba(148, 163, 184, 0.1);\n background: rgba(255, 255, 255, 0.03);\n cursor: pointer;\n transition: background-color 0.15s ease, border-color 0.15s ease;\n}\n.se-lightRow:hover {\n background: rgba(255, 255, 255, 0.06);\n}\n.se-lightRow--active {\n background: rgba(42, 168, 216, 0.12);\n border-color: rgba(42, 168, 216, 0.35);\n}\n.se-lightRowLabel {\n font-size: 13px;\n color: #d8e0ee;\n}\n.se-lightActions {\n display: flex;\n gap: 8px;\n}\n.se-lightProps {\n display: flex;\n flex-direction: column;\n gap: 14px;\n padding-top: 4px;\n border-top: 1px solid rgba(148, 163, 184, 0.1);\n}\n.se-animationList {\n display: flex;\n flex-direction: column;\n gap: 14px;\n}\n.se-animationCard {\n display: flex;\n flex-direction: column;\n gap: 14px;\n padding: 16px;\n border-radius: 14px;\n border: 1px solid rgba(148, 163, 184, 0.16);\n background: rgba(15, 23, 42, 0.48);\n}\n.se-animationCard--active {\n border-color: rgba(42, 168, 216, 0.45);\n box-shadow: inset 0 0 0 1px rgba(42, 168, 216, 0.16);\n}\n.se-animationHeader {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 12px;\n}\n.se-animationHeading {\n display: flex;\n min-width: 0;\n flex-direction: column;\n gap: 6px;\n}\n.se-animationTitleRow {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 8px;\n}\n.se-animationStatus,\n.se-animationBadge {\n display: inline-flex;\n align-items: center;\n min-height: 22px;\n padding: 0 8px;\n border-radius: 999px;\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n}\n.se-animationStatus {\n background: rgba(42, 168, 216, 0.16);\n color: #9fe8ff;\n}\n.se-animationBadge {\n background: rgba(99, 102, 241, 0.18);\n color: #d9ddff;\n}\n.se-animationSource {\n font-size: 12px;\n color: #93a4c0;\n word-break: break-word;\n}\n.se-animationMeta {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n}\n.se-animationMeta span {\n border-radius: 999px;\n background: rgba(255, 255, 255, 0.06);\n padding: 4px 9px;\n font-size: 12px;\n color: #c6d0e2;\n}\n.se-animationDangerButton,\n.se-animationButton,\n.se-animationAutoplayButton {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n border: 1px solid rgba(148, 163, 184, 0.2);\n border-radius: 10px;\n background: rgba(255, 255, 255, 0.04);\n color: #e2e8f0;\n cursor: pointer;\n transition:\n background-color 0.18s ease,\n border-color 0.18s ease,\n color 0.18s ease;\n}\n.se-animationButton:disabled {\n opacity: 0.45;\n cursor: not-allowed;\n}\n.se-animationDangerButton {\n width: 36px;\n height: 36px;\n color: #fca5a5;\n}\n.se-animationDangerButton:hover,\n.se-animationButton:hover,\n.se-animationAutoplayButton:hover {\n background: rgba(255, 255, 255, 0.08);\n border-color: rgba(148, 163, 184, 0.34);\n}\n.se-animationButtonRow {\n display: flex;\n flex-wrap: wrap;\n gap: 10px;\n}\n.se-animationButton {\n min-height: 36px;\n padding: 0 14px;\n font-size: 13px;\n font-weight: 600;\n}\n.se-animationButton--icon {\n flex: 1;\n min-width: 0;\n padding: 0;\n height: 38px;\n}\n.se-animationSeekRow {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n.se-animationSeek {\n flex: 1;\n min-width: 0;\n}\n.se-animationSeekTime {\n flex-shrink: 0;\n min-width: 86px;\n text-align: right;\n font-variant-numeric: tabular-nums;\n font-size: 12px;\n color: #97a3b6;\n}\n.se-animationGrid {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 12px;\n}\n.se-animationSpeedRow {\n display: grid;\n grid-template-columns: minmax(0, 1fr) 88px;\n gap: 10px;\n align-items: center;\n}\n.se-animationSpeedInput {\n text-align: right;\n}\n.se-animationAutoplayButton {\n align-self: flex-start;\n min-height: 36px;\n padding: 0 14px;\n font-size: 12px;\n font-weight: 700;\n letter-spacing: 0.04em;\n text-transform: uppercase;\n}\n.se-animationAutoplayButton--active {\n background: rgba(42, 168, 216, 0.18);\n border-color: rgba(42, 168, 216, 0.42);\n color: #dff8ff;\n}\n.se-removedAnimations {\n overflow: hidden;\n border-radius: 12px;\n border: 1px solid rgba(148, 163, 184, 0.14);\n background: rgba(255, 255, 255, 0.03);\n}\n.se-removedAnimationList {\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 14px;\n}\n.se-removedAnimationRow {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n border-radius: 10px;\n background: rgba(15, 23, 42, 0.42);\n padding: 12px;\n}\n.se-removedAnimationName {\n font-size: 13px;\n font-weight: 600;\n color: #e2e8f0;\n}\n@media (max-width: 900px) {\n .se-animationGrid {\n grid-template-columns: 1fr;\n }\n .se-animationSpeedRow {\n grid-template-columns: 1fr;\n }\n}\n.bb-viewSpotOverlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.6);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000;\n backdrop-filter: blur(4px);\n animation: bb-fadeIn 0.2s ease;\n}\n.bb-viewSpotPopup {\n background: #1e1e20;\n border-radius: 12px;\n max-width: 480px;\n width: 90%;\n max-height: 80vh;\n overflow-y: auto;\n box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);\n border: 1px solid #333335;\n animation: bb-slideUp 0.25s ease;\n}\n.bb-viewSpotHeader {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 20px 24px;\n border-bottom: 1px solid #2a2a2c;\n}\n.bb-viewSpotTitle {\n font-size: 18px;\n font-weight: 600;\n color: #e8e8ea;\n margin: 0;\n}\n.bb-viewSpotClose {\n background: none;\n border: none;\n color: #888;\n font-size: 24px;\n cursor: pointer;\n padding: 0 4px;\n line-height: 1;\n transition: color 0.2s;\n}\n.bb-viewSpotClose:hover {\n color: #e8e8ea;\n}\n.bb-viewSpotBody {\n padding: 24px;\n}\n.bb-viewSpotDescription {\n color: #c8c8ca;\n font-size: 14px;\n line-height: 1.6;\n margin-bottom: 20px;\n white-space: pre-wrap;\n word-break: break-word;\n}\n.bb-viewSpotPosition {\n background: #141416;\n border-radius: 8px;\n padding: 12px 16px;\n margin-bottom: 24px;\n font-size: 13px;\n color: #a0a0a2;\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n}\n.bb-viewSpotLabel {\n font-weight: 500;\n color: #888;\n}\n.bb-viewSpotCode {\n background: #0a0a0c;\n padding: 4px 10px;\n border-radius: 4px;\n font-family:\n "Monaco",\n "Menlo",\n monospace;\n font-size: 12px;\n color: #8be9fd;\n}\n.bb-viewSpotActions {\n display: flex;\n justify-content: flex-end;\n gap: 10px;\n padding-top: 4px;\n}\n.bb-editAnnotationOverlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.6);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000;\n}\n.bb-editAnnotationPopup {\n background: #0f141e;\n border-radius: 16px;\n padding: 24px;\n min-width: 200px;\n max-width: 400px;\n width: 90%;\n border: 1px solid rgba(148, 163, 184, 0.18);\n}\n.bb-editAnnotationHeader {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 16px;\n padding-bottom: 12px;\n border-bottom: 1px solid rgba(148, 163, 184, 0.12);\n}\n.bb-editAnnotationTitle {\n color: #f8fafc;\n font-size: 18px;\n font-weight: 600;\n margin: 0;\n}\n.bb-editAnnotationClose {\n background: none;\n border: none;\n color: #94a3b8;\n font-size: 24px;\n cursor: pointer;\n padding: 0 4px;\n}\n.bb-editAnnotationClose:hover {\n color: #f8fafc;\n}\n.bb-editAnnotationBody {\n display: flex;\n flex-direction: column;\n gap: 14px;\n}\n.bb-editAnnotationActions {\n display: flex;\n gap: 8px;\n margin-top: 8px;\n}\n');
11435
+ styleInject('.se-tabContent {\n display: flex;\n flex-direction: column;\n}\n.se-tabContent * {\n box-sizing: border-box;\n}\n.se-subTabRow {\n display: flex;\n justify-content: space-between;\n gap: 6px;\n padding-bottom: 16px;\n border-bottom: 1px solid rgba(148, 163, 184, 0.1);\n margin-bottom: 4px;\n}\n.se-subTab {\n padding: 8px 12px;\n display: flex;\n justify-items: center;\n border-radius: 10px;\n border: 1px solid rgba(148, 163, 184, 0.18);\n background: rgba(255, 255, 255, 0.04);\n color: #b3bfd2;\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n cursor: pointer;\n transition:\n background-color 0.18s ease,\n border-color 0.18s ease,\n color 0.18s ease;\n}\n.se-subTab:hover {\n background: rgba(255, 255, 255, 0.08);\n border-color: rgba(148, 163, 184, 0.32);\n}\n.se-subTab--active {\n background: rgba(99, 102, 241, 0.18);\n border-color: rgba(99, 102, 241, 0.4);\n color: #f8fafc;\n}\n.se-generalContent {\n display: flex;\n flex-direction: column;\n padding-top: 0;\n}\n.se-placeholder {\n font-size: 13px;\n color: #93a4c0;\n}\n.se-section {\n margin-bottom: 10px;\n border-radius: 7px;\n background: rgba(255, 255, 255, 0.03);\n border: 1px solid rgba(148, 163, 184, 0.12);\n overflow: hidden;\n padding: 0;\n}\n.se-section:last-child {\n margin-bottom: 0;\n}\n.se-sectionTitle {\n margin: 0;\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n color: #c3cbdb;\n padding: 11px 14px;\n border-bottom: 1px solid rgba(148, 163, 184, 0.1);\n}\n.se-sectionBody {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 14px;\n}\n.se-field {\n display: flex;\n flex-direction: column;\n gap: 5px;\n}\n.se-fieldInline {\n flex-direction: row;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n}\n.se-fieldLabel {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n font-size: 12px;\n font-weight: 700;\n color: #d7dee9;\n}\n.se-fieldValue {\n font-variant-numeric: tabular-nums;\n font-size: 12px;\n color: #93a4c0;\n}\n.se-slider {\n width: 100%;\n accent-color: #2aa8d8;\n cursor: pointer;\n}\n.se-checkbox {\n width: 18px;\n height: 18px;\n accent-color: #2aa8d8;\n cursor: pointer;\n}\n.se-select,\n.se-textInput,\n.se-numberInput {\n background: rgba(15, 23, 42, 0.28);\n border: 1px solid rgba(148, 163, 184, 0.2);\n border-radius: 7px;\n color: #f8fafc;\n font-size: 13px;\n padding: 11px 13px;\n outline: none;\n}\n.se-select:focus,\n.se-textInput:focus,\n.se-numberInput:focus {\n outline: none;\n border-color: rgba(42, 168, 216, 0.65);\n box-shadow: 0 0 0 4px rgba(42, 168, 216, 0.12);\n}\n.se-select {\n min-width: 130px;\n text-transform: capitalize;\n}\n.se-textInput {\n width: 100%;\n}\n.se-numberInput {\n width: 100%;\n}\n.se-swatch {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n min-width: 96px;\n height: 30px;\n padding: 0 8px;\n border-radius: 8px;\n border: 1px solid rgba(148, 163, 184, 0.35);\n cursor: pointer;\n box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.15);\n}\n.se-swatchCode {\n font-size: 11px;\n font-weight: 600;\n font-variant-numeric: tabular-nums;\n color: #fff;\n mix-blend-mode: difference;\n}\n.se-colorPicker {\n margin-top: 6px;\n display: flex;\n flex-direction: column;\n gap: 8px;\n width: fit-content;\n}\n.se-colorPicker .react-colorful {\n width: 100%;\n height: 140px;\n}\n.se-inlineColorPicker .react-colorful {\n width: 100%;\n height: 240px;\n}\n.se-colorPickerPopover {\n z-index: 1000;\n padding: 10px;\n border-radius: 8px;\n background: #1b2333;\n border: 1px solid rgba(148, 163, 184, 0.25);\n box-shadow: 0 12px 28px rgba(0, 0, 0, 0.4);\n}\n.se-colorPickerPopover .react-colorful {\n width: 200px;\n height: 160px;\n}\n.se-vectorRow {\n display: grid;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n gap: 8px;\n}\n.se-vectorAxis {\n display: flex;\n flex-direction: column;\n gap: 4px;\n font-size: 11px;\n color: #93a4c0;\n}\n.se-panel {\n margin-bottom: 10px;\n overflow: hidden;\n border: 1px solid rgba(148, 163, 184, 0.12);\n border-radius: 7px;\n background: rgba(255, 255, 255, 0.03);\n}\n.se-panel:last-child {\n margin-bottom: 0;\n}\n.se-panelHeader {\n display: flex;\n width: 100%;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n border: none;\n background: rgba(255, 255, 255, 0.03);\n padding: 11px 14px;\n color: #e5e7eb;\n cursor: pointer;\n text-align: left;\n}\n.se-panelHeader:hover {\n background: rgba(255, 255, 255, 0.06);\n}\n.se-panelHeaderMain {\n display: flex;\n min-width: 0;\n align-items: center;\n gap: 8px;\n}\n.se-panelChevron {\n flex-shrink: 0;\n color: #8b97ab;\n transform: rotate(-90deg);\n transition: transform 0.18s ease;\n}\n.se-panelChevron--open {\n transform: rotate(0deg);\n}\n.se-panelTitle {\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n color: #c3cbdb;\n}\n.se-panelHelp {\n display: inline-flex;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n width: 18px;\n height: 18px;\n margin-left: 2px;\n border-radius: 999px;\n color: #b9c0ca;\n cursor: help;\n transition:\n background 0.18s ease,\n color 0.18s ease,\n transform 0.18s ease;\n}\n.se-panelHelp:hover {\n background: rgba(255, 255, 255, 0.08);\n color: #eef2ff;\n transform: translateY(-0.5px);\n}\n.se-panelTrailing {\n display: flex;\n align-items: center;\n}\n.se-panelBody {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 14px;\n border-top: 1px solid rgba(148, 163, 184, 0.1);\n background: transparent;\n}\n.se-iconButton {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 30px;\n height: 30px;\n border: none;\n border-radius: 8px;\n background: rgba(255, 255, 255, 0.08);\n color: #d7dde8;\n cursor: pointer;\n}\n.se-iconButton:hover {\n background: rgba(255, 255, 255, 0.12);\n}\n.se-axisStepperGrid {\n display: grid;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n gap: 12px;\n}\n.se-axisStepper {\n display: grid;\n grid-template-columns: 40px 1fr 40px;\n align-items: center;\n min-height: 30px;\n border-radius: 10px;\n background: rgba(17, 24, 39, 0.62);\n border: 1px solid rgba(255, 255, 255, 0.05);\n}\n.se-axisStepperButton {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n height: 100%;\n border: none;\n background: transparent;\n color: #f3f4f6;\n cursor: pointer;\n}\n.se-axisStepperButton:hover {\n background: rgba(255, 255, 255, 0.04);\n}\n.se-axisStepperLabel {\n text-align: center;\n font-size: 15px;\n color: #f3f4f6;\n}\n.se-segmentedControl {\n display: grid;\n grid-auto-flow: column;\n overflow: hidden;\n border-radius: 10px;\n background: rgba(17, 24, 39, 0.62);\n}\n.se-segmentedOption {\n min-height: 35px;\n border: none;\n background: transparent;\n color: #d1d5db;\n font-size: 14px;\n text-transform: uppercase;\n cursor: pointer;\n}\n.se-segmentedOption--active {\n background: #2aa8d8;\n color: #fff;\n}\n.se-helperCard {\n padding: 32px;\n text-align: center;\n background: rgba(15, 23, 42, 0.44);\n border-radius: 10px;\n border: 1px solid rgba(148, 163, 184, 0.15);\n color: #94a3b8;\n font-size: 14px;\n}\n.se-helperCard strong {\n font-size: 14px;\n}\n.se-helperCard span {\n font-size: 13px;\n line-height: 1.4;\n color: #c7d0df;\n}\n.se-switch {\n position: relative;\n display: inline-flex;\n align-items: center;\n}\n.se-switch input[type=checkbox] {\n position: absolute;\n left: 0;\n top: 0;\n z-index: 10;\n width: 100%;\n height: 100%;\n cursor: pointer;\n opacity: 0;\n}\n.se-switchTrack {\n position: relative;\n display: inline-flex;\n align-items: center;\n gap: 8px;\n}\n.se-switchTrack::before {\n content: "";\n width: 36px;\n height: 20px;\n background: #4b5563;\n display: inline-block;\n border-radius: 20px;\n transition: background-color 0.2s ease-in;\n}\n.se-switchThumb {\n position: absolute;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n left: 2px;\n top: 2px;\n z-index: 2;\n background: #fff;\n box-shadow: 0 0 5px rgba(0, 0, 0, 0.2);\n transition: 0.2s ease-in;\n}\n.se-switch input[type=checkbox]:hover + .se-switchTrack .se-switchThumb {\n box-shadow: 0 2px 15px 0 rgba(0, 0, 0, 0.2), 0 3px 8px 0 rgba(0, 0, 0, 0.1);\n}\n.se-switch input[type=checkbox]:checked + .se-switchTrack::before {\n background: #376fcb;\n}\n.se-switch input[type=checkbox]:checked + .se-switchTrack .se-switchThumb {\n background: #4285f4;\n left: 18px;\n}\n.se-switchLabel {\n font-size: 12px;\n font-weight: 700;\n letter-spacing: 0.06em;\n color: #e5e7eb;\n}\n.se-switch input[type=checkbox]:checked ~ .se-switchTrack .se-switchLabel {\n color: #2aa8d8;\n}\n.se-lightList {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n.se-lightRow {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 10px;\n border-radius: 8px;\n border: 1px solid rgba(148, 163, 184, 0.1);\n background: rgba(255, 255, 255, 0.03);\n cursor: pointer;\n transition: background-color 0.15s ease, border-color 0.15s ease;\n}\n.se-lightRow:hover {\n background: rgba(255, 255, 255, 0.06);\n}\n.se-lightRow--active {\n background: rgba(42, 168, 216, 0.12);\n border-color: rgba(42, 168, 216, 0.35);\n}\n.se-lightRowLabel {\n font-size: 13px;\n color: #d8e0ee;\n}\n.se-lightActions {\n display: flex;\n gap: 8px;\n}\n.se-lightProps {\n display: flex;\n flex-direction: column;\n gap: 14px;\n padding-top: 4px;\n border-top: 1px solid rgba(148, 163, 184, 0.1);\n}\n.se-animationList {\n display: flex;\n flex-direction: column;\n gap: 14px;\n}\n.se-animationCard {\n display: flex;\n flex-direction: column;\n gap: 14px;\n padding: 16px;\n border-radius: 14px;\n border: 1px solid rgba(148, 163, 184, 0.16);\n background: rgba(15, 23, 42, 0.48);\n}\n.se-animationCard--active {\n border-color: rgba(42, 168, 216, 0.45);\n box-shadow: inset 0 0 0 1px rgba(42, 168, 216, 0.16);\n}\n.se-animationHeader {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 12px;\n}\n.se-animationHeading {\n display: flex;\n min-width: 0;\n flex-direction: column;\n gap: 6px;\n}\n.se-animationTitleRow {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 8px;\n}\n.se-animationStatus,\n.se-animationBadge {\n display: inline-flex;\n align-items: center;\n min-height: 22px;\n padding: 0 8px;\n border-radius: 999px;\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n}\n.se-animationStatus {\n background: rgba(42, 168, 216, 0.16);\n color: #9fe8ff;\n}\n.se-animationBadge {\n background: rgba(99, 102, 241, 0.18);\n color: #d9ddff;\n}\n.se-animationSource {\n font-size: 12px;\n color: #93a4c0;\n word-break: break-word;\n}\n.se-animationMeta {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n}\n.se-animationMeta span {\n border-radius: 999px;\n background: rgba(255, 255, 255, 0.06);\n padding: 4px 9px;\n font-size: 12px;\n color: #c6d0e2;\n}\n.se-animationDangerButton,\n.se-animationButton,\n.se-animationAutoplayButton {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n border: 1px solid rgba(148, 163, 184, 0.2);\n border-radius: 10px;\n background: rgba(255, 255, 255, 0.04);\n color: #e2e8f0;\n cursor: pointer;\n transition:\n background-color 0.18s ease,\n border-color 0.18s ease,\n color 0.18s ease;\n}\n.se-animationButton:disabled {\n opacity: 0.45;\n cursor: not-allowed;\n}\n.se-animationDangerButton {\n width: 36px;\n height: 36px;\n color: #fca5a5;\n}\n.se-animationDangerButton:hover,\n.se-animationButton:hover,\n.se-animationAutoplayButton:hover {\n background: rgba(255, 255, 255, 0.08);\n border-color: rgba(148, 163, 184, 0.34);\n}\n.se-animationButtonRow {\n display: flex;\n flex-wrap: wrap;\n gap: 10px;\n}\n.se-animationButton {\n min-height: 36px;\n padding: 0 14px;\n font-size: 13px;\n font-weight: 600;\n}\n.se-animationButton--icon {\n flex: 1;\n min-width: 0;\n padding: 0;\n height: 38px;\n}\n.se-animationSeekRow {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n.se-animationSeek {\n flex: 1;\n min-width: 0;\n}\n.se-animationSeekTime {\n flex-shrink: 0;\n min-width: 86px;\n text-align: right;\n font-variant-numeric: tabular-nums;\n font-size: 12px;\n color: #97a3b6;\n}\n.se-animationGrid {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 12px;\n}\n.se-animationSpeedRow {\n display: grid;\n grid-template-columns: minmax(0, 1fr) 88px;\n gap: 10px;\n align-items: center;\n}\n.se-animationSpeedInput {\n text-align: right;\n}\n.se-animationAutoplayButton {\n align-self: flex-start;\n min-height: 36px;\n padding: 0 14px;\n font-size: 12px;\n font-weight: 700;\n letter-spacing: 0.04em;\n text-transform: uppercase;\n}\n.se-animationAutoplayButton--active {\n background: rgba(42, 168, 216, 0.18);\n border-color: rgba(42, 168, 216, 0.42);\n color: #dff8ff;\n}\n.se-removedAnimations {\n overflow: hidden;\n border-radius: 12px;\n border: 1px solid rgba(148, 163, 184, 0.14);\n background: rgba(255, 255, 255, 0.03);\n}\n.se-removedAnimationList {\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 14px;\n}\n.se-removedAnimationRow {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n border-radius: 10px;\n background: rgba(15, 23, 42, 0.42);\n padding: 12px;\n}\n.se-removedAnimationName {\n font-size: 13px;\n font-weight: 600;\n color: #e2e8f0;\n}\n@media (max-width: 900px) {\n .se-animationGrid {\n grid-template-columns: 1fr;\n }\n .se-animationSpeedRow {\n grid-template-columns: 1fr;\n }\n}\n.bb-viewSpotOverlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.6);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000;\n backdrop-filter: blur(4px);\n animation: bb-fadeIn 0.2s ease;\n}\n.bb-viewSpotPopup {\n background: #1e1e20;\n border-radius: 12px;\n max-width: 480px;\n width: 90%;\n max-height: 80vh;\n overflow-y: auto;\n box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);\n border: 1px solid #333335;\n animation: bb-slideUp 0.25s ease;\n}\n.bb-viewSpotHeader {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 20px 24px;\n border-bottom: 1px solid #2a2a2c;\n}\n.bb-viewSpotTitle {\n font-size: 18px;\n font-weight: 600;\n color: #e8e8ea;\n margin: 0;\n}\n.bb-viewSpotClose {\n background: none;\n border: none;\n color: #888;\n font-size: 24px;\n cursor: pointer;\n padding: 0 4px;\n line-height: 1;\n transition: color 0.2s;\n}\n.bb-viewSpotClose:hover {\n color: #e8e8ea;\n}\n.bb-viewSpotBody {\n padding: 24px;\n}\n.bb-viewSpotDescription {\n color: #c8c8ca;\n font-size: 14px;\n line-height: 1.6;\n margin-bottom: 20px;\n white-space: pre-wrap;\n word-break: break-word;\n}\n.bb-viewSpotPosition {\n background: #141416;\n border-radius: 8px;\n padding: 12px 16px;\n margin-bottom: 24px;\n font-size: 13px;\n color: #a0a0a2;\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n}\n.bb-viewSpotLabel {\n font-weight: 500;\n color: #888;\n}\n.bb-viewSpotCode {\n background: #0a0a0c;\n padding: 4px 10px;\n border-radius: 4px;\n font-family:\n "Monaco",\n "Menlo",\n monospace;\n font-size: 12px;\n color: #8be9fd;\n}\n.bb-viewSpotActions {\n display: flex;\n justify-content: flex-end;\n gap: 10px;\n padding-top: 4px;\n}\n.bb-editAnnotationOverlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.6);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000;\n}\n.bb-editAnnotationPopup {\n background: #0f141e;\n border-radius: 16px;\n padding: 24px;\n min-width: 200px;\n max-width: 400px;\n width: 90%;\n border: 1px solid rgba(148, 163, 184, 0.18);\n}\n.bb-editAnnotationHeader {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 16px;\n padding-bottom: 12px;\n border-bottom: 1px solid rgba(148, 163, 184, 0.12);\n}\n.bb-editAnnotationTitle {\n color: #f8fafc;\n font-size: 18px;\n font-weight: 600;\n margin: 0;\n}\n.bb-editAnnotationClose {\n background: none;\n border: none;\n color: #94a3b8;\n font-size: 24px;\n cursor: pointer;\n padding: 0 4px;\n}\n.bb-editAnnotationClose:hover {\n color: #f8fafc;\n}\n.bb-editAnnotationBody {\n display: flex;\n flex-direction: column;\n gap: 14px;\n}\n.bb-editAnnotationActions {\n display: flex;\n gap: 8px;\n margin-top: 8px;\n}\n.se-cinematicHint {\n margin: 0 0 10px;\n font-size: 12px;\n line-height: 1.5;\n color: rgba(255, 255, 255, 0.55);\n}\n.bb-button.se-cinematicAdd {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n width: 100%;\n margin-bottom: 12px;\n background: #135bc0;\n color: white;\n}\n.bb-button.se-cinematicAdd:hover:not(:disabled) {\n background: #1a68d6;\n}\n.se-waypointList {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-bottom: 14px;\n}\n.se-waypointCard {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 8px 10px;\n border: 1px solid rgba(255, 255, 255, 0.1);\n border-radius: 8px;\n background: rgba(255, 255, 255, 0.03);\n}\n.se-waypointBadge {\n flex: none;\n width: 22px;\n height: 22px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 12px;\n font-weight: 600;\n color: #06343c;\n background: #22d3ee;\n}\n.se-waypointBody {\n flex: 1;\n min-width: 0;\n}\n.se-waypointCoords {\n font-size: 11px;\n line-height: 1.4;\n color: rgba(255, 255, 255, 0.6);\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.se-waypointActions {\n flex: none;\n display: flex;\n gap: 2px;\n}\n.se-waypointAction {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 26px;\n height: 26px;\n padding: 0;\n border: none;\n border-radius: 6px;\n background: transparent;\n color: rgba(255, 255, 255, 0.6);\n cursor: pointer;\n transition: background 0.15s ease, color 0.15s ease;\n}\n.se-waypointAction:hover:not(:disabled) {\n background: rgba(255, 255, 255, 0.1);\n color: #fff;\n}\n.se-waypointAction:disabled {\n opacity: 0.35;\n cursor: default;\n}\n.se-waypointAction--delete:hover:not(:disabled) {\n background: rgba(248, 113, 113, 0.16);\n color: #f87171;\n}\n.bb-cameraWaypoint {\n align-items: flex-start;\n margin-bottom: 8px;\n}\n.bb-cameraWaypoint .bb-cameraLiveBadge,\n.bb-cameraWaypoint .bb-cameraSavedBadge {\n flex: none;\n}\n.bb-cameraWaypointLabel {\n font-size: 11px;\n font-weight: 600;\n color: rgba(255, 255, 255, 0.8);\n margin-bottom: 2px;\n}\n.bb-cameraControls--centered {\n justify-content: center;\n margin: 12px 0;\n}\n');
11086
11436
 
11087
11437
  // src/components/BindingBuilder/SceneSettings/SceneSettingsTab.tsx
11088
11438
  import {
@@ -11090,15 +11440,17 @@ import {
11090
11440
  Clapperboard,
11091
11441
  MessageSquareText,
11092
11442
  Sparkles,
11093
- Spotlight
11443
+ Spotlight,
11444
+ Video
11094
11445
  } from "lucide-react";
11095
- import { jsx as jsx31, jsxs as jsxs27 } from "react/jsx-runtime";
11446
+ import { jsx as jsx32, jsxs as jsxs28 } from "react/jsx-runtime";
11096
11447
  var SCENE_SUB_TABS = [
11097
- { label: "General", icon: /* @__PURE__ */ jsx31(Box2, {}) },
11098
- { label: "Lighting", icon: /* @__PURE__ */ jsx31(Spotlight, {}) },
11099
- { label: "Animations", icon: /* @__PURE__ */ jsx31(Clapperboard, {}) },
11100
- { label: "Post Processing", icon: /* @__PURE__ */ jsx31(Sparkles, {}) },
11101
- { label: "Annotations", icon: /* @__PURE__ */ jsx31(MessageSquareText, {}) }
11448
+ { label: "General", icon: /* @__PURE__ */ jsx32(Box2, {}) },
11449
+ { label: "Lighting", icon: /* @__PURE__ */ jsx32(Spotlight, {}) },
11450
+ { label: "Animations", icon: /* @__PURE__ */ jsx32(Clapperboard, {}) },
11451
+ { label: "Post Processing", icon: /* @__PURE__ */ jsx32(Sparkles, {}) },
11452
+ { label: "Annotations", icon: /* @__PURE__ */ jsx32(MessageSquareText, {}) },
11453
+ { label: "Cinematic", icon: /* @__PURE__ */ jsx32(Video, {}) }
11102
11454
  ];
11103
11455
  var FALLBACK_ANIMATION_PLAYBACK = {
11104
11456
  currentClip: null,
@@ -11121,6 +11473,8 @@ function SceneSettingsTab({
11121
11473
  onChange,
11122
11474
  activeSubTab: controlledActiveSubTab,
11123
11475
  onActiveSubTabChange,
11476
+ cameraState = null,
11477
+ onPreviewWaypoint,
11124
11478
  animationClipDetails = [],
11125
11479
  animationPlayback = FALLBACK_ANIMATION_PLAYBACK
11126
11480
  }) {
@@ -11130,7 +11484,7 @@ function SceneSettingsTab({
11130
11484
  const { tier } = useBindingBuilderContext();
11131
11485
  const canUseStarterFeatures = hasTier(tier, "starter");
11132
11486
  const canUseGrowthFeatures = hasTier(tier, "growth");
11133
- useEffect16(() => {
11487
+ useEffect17(() => {
11134
11488
  if (controlledActiveSubTab !== void 0) {
11135
11489
  setInternalActiveSubTab(controlledActiveSubTab);
11136
11490
  }
@@ -11141,8 +11495,8 @@ function SceneSettingsTab({
11141
11495
  }
11142
11496
  onActiveSubTabChange?.(subTab);
11143
11497
  };
11144
- return /* @__PURE__ */ jsxs27("div", { className: "se-tabContent", children: [
11145
- /* @__PURE__ */ jsx31("div", { className: "se-subTabRow", children: SCENE_SUB_TABS.map((tab) => /* @__PURE__ */ jsx31(
11498
+ return /* @__PURE__ */ jsxs28("div", { className: "se-tabContent", children: [
11499
+ /* @__PURE__ */ jsx32("div", { className: "se-subTabRow", children: SCENE_SUB_TABS.map((tab) => /* @__PURE__ */ jsx32(
11146
11500
  "button",
11147
11501
  {
11148
11502
  type: "button",
@@ -11153,9 +11507,9 @@ function SceneSettingsTab({
11153
11507
  },
11154
11508
  tab.label
11155
11509
  )) }),
11156
- activeSubTab === "General" && /* @__PURE__ */ jsx31(GeneralSceneSettings, { config, onChange }),
11157
- activeSubTab === "Lighting" && /* @__PURE__ */ jsx31(LightingTab, { config, onChange }),
11158
- activeSubTab === "Animations" && (canUseStarterFeatures ? /* @__PURE__ */ jsx31(
11510
+ activeSubTab === "General" && /* @__PURE__ */ jsx32(GeneralSceneSettings, { config, onChange }),
11511
+ activeSubTab === "Lighting" && /* @__PURE__ */ jsx32(LightingTab, { config, onChange }),
11512
+ activeSubTab === "Animations" && (canUseStarterFeatures ? /* @__PURE__ */ jsx32(
11159
11513
  AnimationsTab,
11160
11514
  {
11161
11515
  config,
@@ -11171,15 +11525,24 @@ function SceneSettingsTab({
11171
11525
  setSpeed: resolvedAnimationPlayback.setSpeed,
11172
11526
  seek: resolvedAnimationPlayback.seek
11173
11527
  }
11174
- ) : /* @__PURE__ */ jsx31(TierLock, { requiredTier: "starter", feature: "Animation configuration" })),
11175
- activeSubTab === "Post Processing" && (canUseGrowthFeatures ? /* @__PURE__ */ jsx31(PostProcessingTab, { config, onChange }) : /* @__PURE__ */ jsx31(TierLock, { requiredTier: "growth", feature: "Post-processing effects" })),
11176
- activeSubTab === "Annotations" && (canUseStarterFeatures ? /* @__PURE__ */ jsx31(AnnotationsTab, { config, onChange }) : /* @__PURE__ */ jsx31(TierLock, { requiredTier: "starter", feature: "Annotations" }))
11528
+ ) : /* @__PURE__ */ jsx32(TierLock, { requiredTier: "starter", feature: "Animation configuration" })),
11529
+ activeSubTab === "Post Processing" && (canUseGrowthFeatures ? /* @__PURE__ */ jsx32(PostProcessingTab, { config, onChange }) : /* @__PURE__ */ jsx32(TierLock, { requiredTier: "growth", feature: "Post-processing effects" })),
11530
+ activeSubTab === "Annotations" && (canUseStarterFeatures ? /* @__PURE__ */ jsx32(AnnotationsTab, { config, onChange }) : /* @__PURE__ */ jsx32(TierLock, { requiredTier: "starter", feature: "Annotations" })),
11531
+ activeSubTab === "Cinematic" && (canUseStarterFeatures ? /* @__PURE__ */ jsx32(
11532
+ CinematicTab,
11533
+ {
11534
+ config,
11535
+ onChange,
11536
+ cameraState,
11537
+ onPreviewWaypoint
11538
+ }
11539
+ ) : /* @__PURE__ */ jsx32(TierLock, { requiredTier: "starter", feature: "Cinematic camera" }))
11177
11540
  ] });
11178
11541
  }
11179
11542
 
11180
11543
  // src/components/BindingBuilder/components/Layout.tsx
11181
11544
  import { ChevronDown as ChevronDown4, Info as Info2 } from "lucide-react";
11182
- import { jsx as jsx32, jsxs as jsxs28 } from "react/jsx-runtime";
11545
+ import { jsx as jsx33, jsxs as jsxs29 } from "react/jsx-runtime";
11183
11546
  function MaterialPanel({
11184
11547
  title,
11185
11548
  trailing,
@@ -11190,8 +11553,8 @@ function MaterialPanel({
11190
11553
  tooltip
11191
11554
  }) {
11192
11555
  const headerTitle = tooltip ? `${tooltip}` : title;
11193
- return /* @__PURE__ */ jsxs28("div", { className: "bb-materialPanel", children: [
11194
- /* @__PURE__ */ jsxs28(
11556
+ return /* @__PURE__ */ jsxs29("div", { className: "bb-materialPanel", children: [
11557
+ /* @__PURE__ */ jsxs29(
11195
11558
  "button",
11196
11559
  {
11197
11560
  type: "button",
@@ -11200,16 +11563,16 @@ function MaterialPanel({
11200
11563
  "aria-expanded": open,
11201
11564
  title: headerTitle,
11202
11565
  children: [
11203
- /* @__PURE__ */ jsxs28("div", { className: "bb-materialPanelTitleWrap", children: [
11204
- /* @__PURE__ */ jsx32(
11566
+ /* @__PURE__ */ jsxs29("div", { className: "bb-materialPanelTitleWrap", children: [
11567
+ /* @__PURE__ */ jsx33(
11205
11568
  ChevronDown4,
11206
11569
  {
11207
11570
  size: 14,
11208
11571
  className: `bb-materialPanelChevron ${open ? "bb-materialPanelChevron--open" : ""}`
11209
11572
  }
11210
11573
  ),
11211
- /* @__PURE__ */ jsx32("span", { className: "bb-materialPanelTitle", children: title }),
11212
- tooltip ? /* @__PURE__ */ jsx32(
11574
+ /* @__PURE__ */ jsx33("span", { className: "bb-materialPanelTitle", children: title }),
11575
+ tooltip ? /* @__PURE__ */ jsx33(
11213
11576
  "span",
11214
11577
  {
11215
11578
  className: "bb-materialPanelHelp",
@@ -11217,15 +11580,15 @@ function MaterialPanel({
11217
11580
  "aria-label": headerTitle,
11218
11581
  role: "img",
11219
11582
  onClick: (event) => event.stopPropagation(),
11220
- children: /* @__PURE__ */ jsx32(Info2, { size: 12 })
11583
+ children: /* @__PURE__ */ jsx33(Info2, { size: 12 })
11221
11584
  }
11222
11585
  ) : null
11223
11586
  ] }),
11224
- trailing ? /* @__PURE__ */ jsx32("div", { onClick: (event) => event.stopPropagation(), children: trailing }) : null
11587
+ trailing ? /* @__PURE__ */ jsx33("div", { onClick: (event) => event.stopPropagation(), children: trailing }) : null
11225
11588
  ]
11226
11589
  }
11227
11590
  ),
11228
- open ? /* @__PURE__ */ jsx32(
11591
+ open ? /* @__PURE__ */ jsx33(
11229
11592
  "div",
11230
11593
  {
11231
11594
  className: "bb-materialPanelBody",
@@ -11244,8 +11607,8 @@ function BBSection({
11244
11607
  tooltip
11245
11608
  }) {
11246
11609
  const headerTitle = tooltip ? `${title}: ${tooltip}` : title;
11247
- return /* @__PURE__ */ jsxs28("div", { className: "bb-section", children: [
11248
- /* @__PURE__ */ jsxs28(
11610
+ return /* @__PURE__ */ jsxs29("div", { className: "bb-section", children: [
11611
+ /* @__PURE__ */ jsxs29(
11249
11612
  "button",
11250
11613
  {
11251
11614
  type: "button",
@@ -11254,16 +11617,16 @@ function BBSection({
11254
11617
  "aria-expanded": open,
11255
11618
  title: headerTitle,
11256
11619
  children: [
11257
- /* @__PURE__ */ jsxs28("span", { className: "bb-sectionHeaderMain", children: [
11258
- /* @__PURE__ */ jsx32(
11620
+ /* @__PURE__ */ jsxs29("span", { className: "bb-sectionHeaderMain", children: [
11621
+ /* @__PURE__ */ jsx33(
11259
11622
  ChevronDown4,
11260
11623
  {
11261
11624
  size: 15,
11262
11625
  className: `bb-sectionChevron ${open ? "bb-sectionChevron--open" : ""}`
11263
11626
  }
11264
11627
  ),
11265
- /* @__PURE__ */ jsx32("h2", { className: "bb-sectionTitle", children: title }),
11266
- tooltip ? /* @__PURE__ */ jsx32(
11628
+ /* @__PURE__ */ jsx33("h2", { className: "bb-sectionTitle", children: title }),
11629
+ tooltip ? /* @__PURE__ */ jsx33(
11267
11630
  "span",
11268
11631
  {
11269
11632
  className: "bb-sectionHelp",
@@ -11271,15 +11634,15 @@ function BBSection({
11271
11634
  "aria-label": headerTitle,
11272
11635
  role: "img",
11273
11636
  onClick: (event) => event.stopPropagation(),
11274
- children: /* @__PURE__ */ jsx32(Info2, { size: 13 })
11637
+ children: /* @__PURE__ */ jsx33(Info2, { size: 13 })
11275
11638
  }
11276
11639
  ) : null
11277
11640
  ] }),
11278
- trailing ? /* @__PURE__ */ jsx32("span", { onClick: (event) => event.stopPropagation(), children: trailing }) : null
11641
+ trailing ? /* @__PURE__ */ jsx33("span", { onClick: (event) => event.stopPropagation(), children: trailing }) : null
11279
11642
  ]
11280
11643
  }
11281
11644
  ),
11282
- open ? /* @__PURE__ */ jsx32("div", { className: "bb-sectionBody", children }) : null
11645
+ open ? /* @__PURE__ */ jsx33("div", { className: "bb-sectionBody", children }) : null
11283
11646
  ] });
11284
11647
  }
11285
11648
  function EnableToggle2({
@@ -11287,8 +11650,8 @@ function EnableToggle2({
11287
11650
  onChange,
11288
11651
  label
11289
11652
  }) {
11290
- return /* @__PURE__ */ jsxs28("label", { className: "se-switch", "aria-label": label, children: [
11291
- /* @__PURE__ */ jsx32(
11653
+ return /* @__PURE__ */ jsxs29("label", { className: "se-switch", "aria-label": label, children: [
11654
+ /* @__PURE__ */ jsx33(
11292
11655
  "input",
11293
11656
  {
11294
11657
  type: "checkbox",
@@ -11296,7 +11659,7 @@ function EnableToggle2({
11296
11659
  onChange: (event) => onChange(event.target.checked)
11297
11660
  }
11298
11661
  ),
11299
- /* @__PURE__ */ jsx32("span", { className: "se-switchTrack", children: /* @__PURE__ */ jsx32("span", { className: "se-switchThumb" }) })
11662
+ /* @__PURE__ */ jsx33("span", { className: "se-switchTrack", children: /* @__PURE__ */ jsx33("span", { className: "se-switchThumb" }) })
11300
11663
  ] });
11301
11664
  }
11302
11665
 
@@ -11308,7 +11671,7 @@ var MATERIAL_SIDES = ["front", "back", "double"];
11308
11671
  var MATERIAL_BLENDING_MODES = ["normal", "additive"];
11309
11672
 
11310
11673
  // src/components/BindingBuilder/components/MaterialPanelGroup.tsx
11311
- import { jsx as jsx33 } from "react/jsx-runtime";
11674
+ import { jsx as jsx34 } from "react/jsx-runtime";
11312
11675
  function MaterialPanelGroup({
11313
11676
  panelKey,
11314
11677
  title,
@@ -11325,7 +11688,7 @@ function MaterialPanelGroup({
11325
11688
  onSetMaterialPanelState
11326
11689
  } = controls;
11327
11690
  const locked = requiredTier?.locked ?? false;
11328
- return /* @__PURE__ */ jsx33(
11691
+ return /* @__PURE__ */ jsx34(
11329
11692
  MaterialPanel,
11330
11693
  {
11331
11694
  title,
@@ -11333,7 +11696,7 @@ function MaterialPanelGroup({
11333
11696
  disabled: locked || !materialPanelEnabled[panelKey],
11334
11697
  open: openMaterialPanels[panelKey],
11335
11698
  onToggle: () => onToggleMaterialPanel(panelKey),
11336
- trailing: /* @__PURE__ */ jsx33(
11699
+ trailing: /* @__PURE__ */ jsx34(
11337
11700
  EnableToggle2,
11338
11701
  {
11339
11702
  checked: !locked && materialPanelEnabled[panelKey],
@@ -11342,27 +11705,27 @@ function MaterialPanelGroup({
11342
11705
  label: toggleLabel ?? `Toggle ${title}`
11343
11706
  }
11344
11707
  ),
11345
- children: locked && requiredTier ? /* @__PURE__ */ jsx33(TierLock, { requiredTier: requiredTier.tier, feature: title }) : children
11708
+ children: locked && requiredTier ? /* @__PURE__ */ jsx34(TierLock, { requiredTier: requiredTier.tier, feature: title }) : children
11346
11709
  }
11347
11710
  );
11348
11711
  }
11349
11712
 
11350
11713
  // src/components/BindingBuilder/components/MaterialFields.tsx
11351
- import { useRef as useRef13, useState as useState20 } from "react";
11714
+ import { useRef as useRef14, useState as useState20 } from "react";
11352
11715
  import { HexColorInput as HexColorInput3, HexColorPicker as HexColorPicker4 } from "react-colorful";
11353
11716
  import { FileUp } from "lucide-react";
11354
- import { jsx as jsx34, jsxs as jsxs29 } from "react/jsx-runtime";
11717
+ import { jsx as jsx35, jsxs as jsxs30 } from "react/jsx-runtime";
11355
11718
  function MaterialTexturePathField({
11356
11719
  label,
11357
11720
  value,
11358
11721
  placeholder,
11359
11722
  onChange
11360
11723
  }) {
11361
- const inputRef = useRef13(null);
11362
- return /* @__PURE__ */ jsxs29("div", { className: "bb-field bb-field--full", children: [
11363
- /* @__PURE__ */ jsx34("label", { className: "bb-label", children: label }),
11364
- /* @__PURE__ */ jsxs29("div", { className: "bb-fileField", children: [
11365
- /* @__PURE__ */ jsx34(
11724
+ const inputRef = useRef14(null);
11725
+ return /* @__PURE__ */ jsxs30("div", { className: "bb-field bb-field--full", children: [
11726
+ /* @__PURE__ */ jsx35("label", { className: "bb-label", children: label }),
11727
+ /* @__PURE__ */ jsxs30("div", { className: "bb-fileField", children: [
11728
+ /* @__PURE__ */ jsx35(
11366
11729
  "input",
11367
11730
  {
11368
11731
  className: "bb-input bb-fileField-input",
@@ -11375,7 +11738,7 @@ function MaterialTexturePathField({
11375
11738
  }
11376
11739
  }
11377
11740
  ),
11378
- /* @__PURE__ */ jsx34(
11741
+ /* @__PURE__ */ jsx35(
11379
11742
  "button",
11380
11743
  {
11381
11744
  type: "button",
@@ -11383,10 +11746,10 @@ function MaterialTexturePathField({
11383
11746
  onClick: () => inputRef.current?.click(),
11384
11747
  "aria-label": `Select file for ${label}`,
11385
11748
  title: `Select file for ${label}`,
11386
- children: /* @__PURE__ */ jsx34(FileUp, { size: 20 })
11749
+ children: /* @__PURE__ */ jsx35(FileUp, { size: 20 })
11387
11750
  }
11388
11751
  ),
11389
- /* @__PURE__ */ jsx34(
11752
+ /* @__PURE__ */ jsx35(
11390
11753
  "input",
11391
11754
  {
11392
11755
  ref: inputRef,
@@ -11417,10 +11780,10 @@ function MaterialColorField({
11417
11780
  }) {
11418
11781
  const [open, setOpen] = useState20(false);
11419
11782
  const color = value || fallback;
11420
- const anchorRef = useRef13(null);
11421
- return /* @__PURE__ */ jsxs29("div", { className: "bb-field", children: [
11422
- /* @__PURE__ */ jsx34("span", { className: "bb-label", children: label }),
11423
- /* @__PURE__ */ jsx34("div", { className: "se-fieldInline", children: /* @__PURE__ */ jsx34("span", { ref: anchorRef, style: { display: "inline-flex" }, children: /* @__PURE__ */ jsx34(
11783
+ const anchorRef = useRef14(null);
11784
+ return /* @__PURE__ */ jsxs30("div", { className: "bb-field", children: [
11785
+ /* @__PURE__ */ jsx35("span", { className: "bb-label", children: label }),
11786
+ /* @__PURE__ */ jsx35("div", { className: "se-fieldInline", children: /* @__PURE__ */ jsx35("span", { ref: anchorRef, style: { display: "inline-flex" }, children: /* @__PURE__ */ jsx35(
11424
11787
  HexColorInput3,
11425
11788
  {
11426
11789
  prefixed: true,
@@ -11434,7 +11797,7 @@ function MaterialColorField({
11434
11797
  onChange
11435
11798
  }
11436
11799
  ) }) }),
11437
- open ? /* @__PURE__ */ jsx34(ColorPopover, { anchorRef, onClose: () => setOpen(false), children: /* @__PURE__ */ jsx34(HexColorPicker4, { color, onChange }) }) : null
11800
+ open ? /* @__PURE__ */ jsx35(ColorPopover, { anchorRef, onClose: () => setOpen(false), children: /* @__PURE__ */ jsx35(HexColorPicker4, { color, onChange }) }) : null
11438
11801
  ] });
11439
11802
  }
11440
11803
  function MaterialCheckboxField({
@@ -11442,8 +11805,8 @@ function MaterialCheckboxField({
11442
11805
  checked,
11443
11806
  onChange
11444
11807
  }) {
11445
- return /* @__PURE__ */ jsxs29("label", { className: "bb-checkbox", children: [
11446
- /* @__PURE__ */ jsx34(
11808
+ return /* @__PURE__ */ jsxs30("label", { className: "bb-checkbox", children: [
11809
+ /* @__PURE__ */ jsx35(
11447
11810
  "input",
11448
11811
  {
11449
11812
  type: "checkbox",
@@ -11463,9 +11826,9 @@ function MaterialNumberField({
11463
11826
  placeholder,
11464
11827
  onChange
11465
11828
  }) {
11466
- return /* @__PURE__ */ jsxs29("div", { className: "bb-field", children: [
11467
- /* @__PURE__ */ jsx34("label", { className: "bb-label", children: label }),
11468
- /* @__PURE__ */ jsx34(
11829
+ return /* @__PURE__ */ jsxs30("div", { className: "bb-field", children: [
11830
+ /* @__PURE__ */ jsx35("label", { className: "bb-label", children: label }),
11831
+ /* @__PURE__ */ jsx35(
11469
11832
  "input",
11470
11833
  {
11471
11834
  className: "bb-input",
@@ -11488,15 +11851,15 @@ function MaterialSelectField({
11488
11851
  options,
11489
11852
  onChange
11490
11853
  }) {
11491
- return /* @__PURE__ */ jsxs29("div", { className: "bb-field", children: [
11492
- /* @__PURE__ */ jsx34("label", { className: "bb-label", children: label }),
11493
- /* @__PURE__ */ jsx34(
11854
+ return /* @__PURE__ */ jsxs30("div", { className: "bb-field", children: [
11855
+ /* @__PURE__ */ jsx35("label", { className: "bb-label", children: label }),
11856
+ /* @__PURE__ */ jsx35(
11494
11857
  "select",
11495
11858
  {
11496
11859
  className: "bb-select",
11497
11860
  value,
11498
11861
  onChange: (event) => onChange(event.target.value),
11499
- children: options.map((option) => /* @__PURE__ */ jsx34("option", { value: option, children: option }, option))
11862
+ children: options.map((option) => /* @__PURE__ */ jsx35("option", { value: option, children: option }, option))
11500
11863
  }
11501
11864
  )
11502
11865
  ] });
@@ -11510,10 +11873,10 @@ function MaterialSliderField({
11510
11873
  onChange
11511
11874
  }) {
11512
11875
  const displayValue = value ?? min;
11513
- return /* @__PURE__ */ jsxs29("div", { className: "bb-field bb-field--full", children: [
11514
- /* @__PURE__ */ jsx34("label", { className: "bb-label", children: label }),
11515
- /* @__PURE__ */ jsxs29("div", { className: "bb-sliderField", children: [
11516
- /* @__PURE__ */ jsx34(
11876
+ return /* @__PURE__ */ jsxs30("div", { className: "bb-field bb-field--full", children: [
11877
+ /* @__PURE__ */ jsx35("label", { className: "bb-label", children: label }),
11878
+ /* @__PURE__ */ jsxs30("div", { className: "bb-sliderField", children: [
11879
+ /* @__PURE__ */ jsx35(
11517
11880
  "input",
11518
11881
  {
11519
11882
  className: `bb-slider ${value === void 0 ? "bb-slider--unset" : ""}`,
@@ -11525,7 +11888,7 @@ function MaterialSliderField({
11525
11888
  onChange: (event) => onChange(Number(event.target.value))
11526
11889
  }
11527
11890
  ),
11528
- /* @__PURE__ */ jsx34(
11891
+ /* @__PURE__ */ jsx35(
11529
11892
  "input",
11530
11893
  {
11531
11894
  className: "bb-input bb-input--compact",
@@ -11544,7 +11907,7 @@ function MaterialSliderField({
11544
11907
  }
11545
11908
 
11546
11909
  // src/components/BindingBuilder/components/MaterialSection.tsx
11547
- import { Fragment as Fragment9, jsx as jsx35, jsxs as jsxs30 } from "react/jsx-runtime";
11910
+ import { Fragment as Fragment9, jsx as jsx36, jsxs as jsxs31 } from "react/jsx-runtime";
11548
11911
  function MaterialSection({
11549
11912
  selectedItem,
11550
11913
  selectedMaterial,
@@ -11573,16 +11936,16 @@ function MaterialSection({
11573
11936
  const [anisotropyMode, setAnisotropyMode] = useState21(
11574
11937
  "factor"
11575
11938
  );
11576
- return /* @__PURE__ */ jsxs30(Fragment9, { children: [
11577
- /* @__PURE__ */ jsx35(
11939
+ return /* @__PURE__ */ jsxs31(Fragment9, { children: [
11940
+ /* @__PURE__ */ jsx36(
11578
11941
  MaterialPanelGroup,
11579
11942
  {
11580
11943
  panelKey: "pbr",
11581
11944
  title: "PBR Maps",
11582
11945
  tooltip: "Controls the base color and workflow used for physically based shading.",
11583
11946
  controls: panelControls,
11584
- children: canUseStarterFeatures ? /* @__PURE__ */ jsxs30("div", { className: "bb-grid", children: [
11585
- /* @__PURE__ */ jsx35(
11947
+ children: canUseStarterFeatures ? /* @__PURE__ */ jsxs31("div", { className: "bb-grid", children: [
11948
+ /* @__PURE__ */ jsx36(
11586
11949
  MaterialSelectField,
11587
11950
  {
11588
11951
  label: "Workflow",
@@ -11591,8 +11954,8 @@ function MaterialSection({
11591
11954
  onChange: onPbrWorkflowChange
11592
11955
  }
11593
11956
  ),
11594
- /* @__PURE__ */ jsx35("div", { className: "bb-field bb-field--full", children: /* @__PURE__ */ jsx35("p", { className: "bb-note", style: { margin: 0 }, children: pbrWorkflow === "metalness" ? "Metalness workflow maps directly to the underlying physical material." : "Specular workflow exposes the same physical material with non-metal specular controls; it does not switch to a separate spec/gloss shader." }) }),
11595
- /* @__PURE__ */ jsx35(
11957
+ /* @__PURE__ */ jsx36("div", { className: "bb-field bb-field--full", children: /* @__PURE__ */ jsx36("p", { className: "bb-note", style: { margin: 0 }, children: pbrWorkflow === "metalness" ? "Metalness workflow maps directly to the underlying physical material." : "Specular workflow exposes the same physical material with non-metal specular controls; it does not switch to a separate spec/gloss shader." }) }),
11958
+ /* @__PURE__ */ jsx36(
11596
11959
  MaterialTexturePathField,
11597
11960
  {
11598
11961
  label: pbrWorkflow === "specular" ? "Albedo texture path" : "Base color texture path",
@@ -11601,7 +11964,7 @@ function MaterialSection({
11601
11964
  onChange: (value) => setSelectedTexturePath(selectedItem, "texture", value)
11602
11965
  }
11603
11966
  ),
11604
- /* @__PURE__ */ jsx35(
11967
+ /* @__PURE__ */ jsx36(
11605
11968
  MaterialColorField,
11606
11969
  {
11607
11970
  label: pbrWorkflow === "specular" ? "Albedo color" : "Base color",
@@ -11612,8 +11975,8 @@ function MaterialSection({
11612
11975
  }))
11613
11976
  }
11614
11977
  ),
11615
- pbrWorkflow === "metalness" ? /* @__PURE__ */ jsxs30(Fragment9, { children: [
11616
- /* @__PURE__ */ jsx35(
11978
+ pbrWorkflow === "metalness" ? /* @__PURE__ */ jsxs31(Fragment9, { children: [
11979
+ /* @__PURE__ */ jsx36(
11617
11980
  MaterialSliderField,
11618
11981
  {
11619
11982
  label: "Metalness",
@@ -11627,7 +11990,7 @@ function MaterialSection({
11627
11990
  }))
11628
11991
  }
11629
11992
  ),
11630
- /* @__PURE__ */ jsx35(
11993
+ /* @__PURE__ */ jsx36(
11631
11994
  MaterialTexturePathField,
11632
11995
  {
11633
11996
  label: "Metalness map path",
@@ -11636,8 +11999,8 @@ function MaterialSection({
11636
11999
  onChange: (value) => setSelectedTexturePath(selectedItem, "metalnessMap", value)
11637
12000
  }
11638
12001
  )
11639
- ] }) : /* @__PURE__ */ jsxs30(Fragment9, { children: [
11640
- /* @__PURE__ */ jsx35(
12002
+ ] }) : /* @__PURE__ */ jsxs31(Fragment9, { children: [
12003
+ /* @__PURE__ */ jsx36(
11641
12004
  MaterialSliderField,
11642
12005
  {
11643
12006
  label: "Specular intensity",
@@ -11651,7 +12014,7 @@ function MaterialSection({
11651
12014
  }))
11652
12015
  }
11653
12016
  ),
11654
- /* @__PURE__ */ jsx35(
12017
+ /* @__PURE__ */ jsx36(
11655
12018
  MaterialColorField,
11656
12019
  {
11657
12020
  label: "Specular color",
@@ -11662,7 +12025,7 @@ function MaterialSection({
11662
12025
  }))
11663
12026
  }
11664
12027
  ),
11665
- /* @__PURE__ */ jsx35(
12028
+ /* @__PURE__ */ jsx36(
11666
12029
  MaterialTexturePathField,
11667
12030
  {
11668
12031
  label: "Specular intensity map path",
@@ -11675,7 +12038,7 @@ function MaterialSection({
11675
12038
  )
11676
12039
  }
11677
12040
  ),
11678
- /* @__PURE__ */ jsx35(
12041
+ /* @__PURE__ */ jsx36(
11679
12042
  MaterialTexturePathField,
11680
12043
  {
11681
12044
  label: "Specular color map path",
@@ -11689,7 +12052,7 @@ function MaterialSection({
11689
12052
  }
11690
12053
  )
11691
12054
  ] }),
11692
- /* @__PURE__ */ jsx35(
12055
+ /* @__PURE__ */ jsx36(
11693
12056
  MaterialSliderField,
11694
12057
  {
11695
12058
  label: "Specular F0 / reflectivity",
@@ -11703,8 +12066,8 @@ function MaterialSection({
11703
12066
  }))
11704
12067
  }
11705
12068
  )
11706
- ] }) : /* @__PURE__ */ jsxs30("div", { className: "bb-grid", children: [
11707
- /* @__PURE__ */ jsx35(
12069
+ ] }) : /* @__PURE__ */ jsxs31("div", { className: "bb-grid", children: [
12070
+ /* @__PURE__ */ jsx36(
11708
12071
  MaterialColorField,
11709
12072
  {
11710
12073
  label: "Base color",
@@ -11715,7 +12078,7 @@ function MaterialSection({
11715
12078
  }))
11716
12079
  }
11717
12080
  ),
11718
- /* @__PURE__ */ jsx35(
12081
+ /* @__PURE__ */ jsx36(
11719
12082
  MaterialSliderField,
11720
12083
  {
11721
12084
  label: "Metalness",
@@ -11729,7 +12092,7 @@ function MaterialSection({
11729
12092
  }))
11730
12093
  }
11731
12094
  ),
11732
- /* @__PURE__ */ jsx35("div", { className: "bb-field bb-field--full", children: /* @__PURE__ */ jsx35(
12095
+ /* @__PURE__ */ jsx36("div", { className: "bb-field bb-field--full", children: /* @__PURE__ */ jsx36(
11733
12096
  TierLock,
11734
12097
  {
11735
12098
  requiredTier: "starter",
@@ -11739,7 +12102,7 @@ function MaterialSection({
11739
12102
  ] })
11740
12103
  }
11741
12104
  ),
11742
- /* @__PURE__ */ jsx35(
12105
+ /* @__PURE__ */ jsx36(
11743
12106
  MaterialPanelGroup,
11744
12107
  {
11745
12108
  panelKey: "roughness",
@@ -11748,8 +12111,8 @@ function MaterialSection({
11748
12111
  tooltip: "Adjusts how matte or shiny the surface appears.",
11749
12112
  toggleLabel: "Toggle Roughness and Glossiness",
11750
12113
  controls: panelControls,
11751
- children: /* @__PURE__ */ jsxs30("div", { className: "bb-grid", children: [
11752
- /* @__PURE__ */ jsx35(
12114
+ children: /* @__PURE__ */ jsxs31("div", { className: "bb-grid", children: [
12115
+ /* @__PURE__ */ jsx36(
11753
12116
  MaterialSelectField,
11754
12117
  {
11755
12118
  label: "Mode",
@@ -11758,8 +12121,8 @@ function MaterialSection({
11758
12121
  onChange: setRoughnessMode
11759
12122
  }
11760
12123
  ),
11761
- /* @__PURE__ */ jsx35("div", { className: "bb-field bb-field--full", children: /* @__PURE__ */ jsx35("p", { className: "bb-note", style: { margin: 0 }, children: roughnessMode === "roughness" ? "Roughness: 0 is glossy, 1 is matte." : "Glossiness is the inverse of roughness (glossiness = 1 \u2212 roughness)" }) }),
11762
- roughnessMode === "roughness" ? /* @__PURE__ */ jsx35(
12124
+ /* @__PURE__ */ jsx36("div", { className: "bb-field bb-field--full", children: /* @__PURE__ */ jsx36("p", { className: "bb-note", style: { margin: 0 }, children: roughnessMode === "roughness" ? "Roughness: 0 is glossy, 1 is matte." : "Glossiness is the inverse of roughness (glossiness = 1 \u2212 roughness)" }) }),
12125
+ roughnessMode === "roughness" ? /* @__PURE__ */ jsx36(
11763
12126
  MaterialSliderField,
11764
12127
  {
11765
12128
  label: "Roughness",
@@ -11772,7 +12135,7 @@ function MaterialSection({
11772
12135
  roughness: value
11773
12136
  }))
11774
12137
  }
11775
- ) : /* @__PURE__ */ jsx35(
12138
+ ) : /* @__PURE__ */ jsx36(
11776
12139
  MaterialSliderField,
11777
12140
  {
11778
12141
  label: "Glossiness",
@@ -11786,7 +12149,7 @@ function MaterialSection({
11786
12149
  }))
11787
12150
  }
11788
12151
  ),
11789
- /* @__PURE__ */ jsx35(
12152
+ /* @__PURE__ */ jsx36(
11790
12153
  MaterialTexturePathField,
11791
12154
  {
11792
12155
  label: roughnessMode === "glossiness" ? "Glossiness map path (applied as roughness)" : "Roughness map path",
@@ -11798,7 +12161,7 @@ function MaterialSection({
11798
12161
  ] })
11799
12162
  }
11800
12163
  ),
11801
- /* @__PURE__ */ jsx35(
12164
+ /* @__PURE__ */ jsx36(
11802
12165
  MaterialPanelGroup,
11803
12166
  {
11804
12167
  panelKey: "emission",
@@ -11806,8 +12169,8 @@ function MaterialSection({
11806
12169
  title: "Emission",
11807
12170
  tooltip: "Makes the material self-illuminated with emissive color and intensity.",
11808
12171
  controls: panelControls,
11809
- children: /* @__PURE__ */ jsxs30("div", { className: "bb-grid", children: [
11810
- /* @__PURE__ */ jsx35(
12172
+ children: /* @__PURE__ */ jsxs31("div", { className: "bb-grid", children: [
12173
+ /* @__PURE__ */ jsx36(
11811
12174
  MaterialColorField,
11812
12175
  {
11813
12176
  label: "Emissive color",
@@ -11819,7 +12182,7 @@ function MaterialSection({
11819
12182
  }))
11820
12183
  }
11821
12184
  ),
11822
- /* @__PURE__ */ jsx35(
12185
+ /* @__PURE__ */ jsx36(
11823
12186
  MaterialSliderField,
11824
12187
  {
11825
12188
  label: "Emissive intensity",
@@ -11833,7 +12196,7 @@ function MaterialSection({
11833
12196
  }))
11834
12197
  }
11835
12198
  ),
11836
- /* @__PURE__ */ jsx35(
12199
+ /* @__PURE__ */ jsx36(
11837
12200
  MaterialTexturePathField,
11838
12201
  {
11839
12202
  label: "Emissive map path",
@@ -11845,7 +12208,7 @@ function MaterialSection({
11845
12208
  ] })
11846
12209
  }
11847
12210
  ),
11848
- /* @__PURE__ */ jsx35(
12211
+ /* @__PURE__ */ jsx36(
11849
12212
  MaterialPanelGroup,
11850
12213
  {
11851
12214
  panelKey: "opacity",
@@ -11853,8 +12216,8 @@ function MaterialSection({
11853
12216
  title: "Opacity",
11854
12217
  tooltip: "Controls transparency, alpha testing, and blending behavior.",
11855
12218
  controls: panelControls,
11856
- children: /* @__PURE__ */ jsxs30("div", { className: "bb-grid", children: [
11857
- /* @__PURE__ */ jsx35(
12219
+ children: /* @__PURE__ */ jsxs31("div", { className: "bb-grid", children: [
12220
+ /* @__PURE__ */ jsx36(
11858
12221
  MaterialSliderField,
11859
12222
  {
11860
12223
  label: "Opacity",
@@ -11868,7 +12231,7 @@ function MaterialSection({
11868
12231
  }))
11869
12232
  }
11870
12233
  ),
11871
- /* @__PURE__ */ jsx35(
12234
+ /* @__PURE__ */ jsx36(
11872
12235
  MaterialSliderField,
11873
12236
  {
11874
12237
  label: "Alpha test",
@@ -11882,7 +12245,7 @@ function MaterialSection({
11882
12245
  }))
11883
12246
  }
11884
12247
  ),
11885
- /* @__PURE__ */ jsx35(
12248
+ /* @__PURE__ */ jsx36(
11886
12249
  MaterialTexturePathField,
11887
12250
  {
11888
12251
  label: "Alpha map path",
@@ -11891,7 +12254,7 @@ function MaterialSection({
11891
12254
  onChange: (value) => setSelectedTexturePath(selectedItem, "alphaMap", value)
11892
12255
  }
11893
12256
  ),
11894
- /* @__PURE__ */ jsx35(
12257
+ /* @__PURE__ */ jsx36(
11895
12258
  MaterialSelectField,
11896
12259
  {
11897
12260
  label: "Blending mode",
@@ -11903,9 +12266,9 @@ function MaterialSection({
11903
12266
  }))
11904
12267
  }
11905
12268
  ),
11906
- /* @__PURE__ */ jsxs30("div", { className: "bb-field", children: [
11907
- /* @__PURE__ */ jsx35("label", { className: "bb-label", children: "Alpha hash" }),
11908
- /* @__PURE__ */ jsx35("div", { className: "bb-checkboxRow", children: /* @__PURE__ */ jsx35(
12269
+ /* @__PURE__ */ jsxs31("div", { className: "bb-field", children: [
12270
+ /* @__PURE__ */ jsx36("label", { className: "bb-label", children: "Alpha hash" }),
12271
+ /* @__PURE__ */ jsx36("div", { className: "bb-checkboxRow", children: /* @__PURE__ */ jsx36(
11909
12272
  MaterialCheckboxField,
11910
12273
  {
11911
12274
  label: "Use alpha hash",
@@ -11917,9 +12280,9 @@ function MaterialSection({
11917
12280
  }
11918
12281
  ) })
11919
12282
  ] }),
11920
- /* @__PURE__ */ jsxs30("div", { className: "bb-field", children: [
11921
- /* @__PURE__ */ jsx35("label", { className: "bb-label", children: "Dithering" }),
11922
- /* @__PURE__ */ jsx35("div", { className: "bb-checkboxRow", children: /* @__PURE__ */ jsx35(
12283
+ /* @__PURE__ */ jsxs31("div", { className: "bb-field", children: [
12284
+ /* @__PURE__ */ jsx36("label", { className: "bb-label", children: "Dithering" }),
12285
+ /* @__PURE__ */ jsx36("div", { className: "bb-checkboxRow", children: /* @__PURE__ */ jsx36(
11923
12286
  MaterialCheckboxField,
11924
12287
  {
11925
12288
  label: "Use dithering",
@@ -11934,7 +12297,7 @@ function MaterialSection({
11934
12297
  ] })
11935
12298
  }
11936
12299
  ),
11937
- /* @__PURE__ */ jsxs30(
12300
+ /* @__PURE__ */ jsxs31(
11938
12301
  MaterialPanelGroup,
11939
12302
  {
11940
12303
  panelKey: "normalBump",
@@ -11944,8 +12307,8 @@ function MaterialSection({
11944
12307
  toggleLabel: "Toggle Normal and Bump",
11945
12308
  controls: panelControls,
11946
12309
  children: [
11947
- /* @__PURE__ */ jsxs30("div", { className: "bb-grid", children: [
11948
- /* @__PURE__ */ jsx35(
12310
+ /* @__PURE__ */ jsxs31("div", { className: "bb-grid", children: [
12311
+ /* @__PURE__ */ jsx36(
11949
12312
  MaterialTexturePathField,
11950
12313
  {
11951
12314
  label: "Normal map path",
@@ -11954,7 +12317,7 @@ function MaterialSection({
11954
12317
  onChange: (value) => setSelectedTexturePath(selectedItem, "normalMap", value)
11955
12318
  }
11956
12319
  ),
11957
- /* @__PURE__ */ jsx35(
12320
+ /* @__PURE__ */ jsx36(
11958
12321
  MaterialSliderField,
11959
12322
  {
11960
12323
  label: "Normal scale",
@@ -11968,7 +12331,7 @@ function MaterialSection({
11968
12331
  }))
11969
12332
  }
11970
12333
  ),
11971
- /* @__PURE__ */ jsx35(
12334
+ /* @__PURE__ */ jsx36(
11972
12335
  MaterialTexturePathField,
11973
12336
  {
11974
12337
  label: "Bump map path",
@@ -11977,7 +12340,7 @@ function MaterialSection({
11977
12340
  onChange: (value) => setSelectedTexturePath(selectedItem, "bumpMap", value)
11978
12341
  }
11979
12342
  ),
11980
- /* @__PURE__ */ jsx35(
12343
+ /* @__PURE__ */ jsx36(
11981
12344
  MaterialSliderField,
11982
12345
  {
11983
12346
  label: "Bump scale",
@@ -11992,7 +12355,7 @@ function MaterialSection({
11992
12355
  }
11993
12356
  )
11994
12357
  ] }),
11995
- /* @__PURE__ */ jsx35("div", { className: "bb-checkboxRow", style: { marginTop: 12 }, children: /* @__PURE__ */ jsx35(
12358
+ /* @__PURE__ */ jsx36("div", { className: "bb-checkboxRow", style: { marginTop: 12 }, children: /* @__PURE__ */ jsx36(
11996
12359
  MaterialCheckboxField,
11997
12360
  {
11998
12361
  label: "Flip normal Y",
@@ -12006,7 +12369,7 @@ function MaterialSection({
12006
12369
  ]
12007
12370
  }
12008
12371
  ),
12009
- /* @__PURE__ */ jsx35(
12372
+ /* @__PURE__ */ jsx36(
12010
12373
  MaterialPanelGroup,
12011
12374
  {
12012
12375
  panelKey: "displacement",
@@ -12014,8 +12377,8 @@ function MaterialSection({
12014
12377
  title: "Displacement",
12015
12378
  tooltip: "Offsets geometry using a texture to create real surface height.",
12016
12379
  controls: panelControls,
12017
- children: /* @__PURE__ */ jsxs30("div", { className: "bb-grid", children: [
12018
- /* @__PURE__ */ jsx35(
12380
+ children: /* @__PURE__ */ jsxs31("div", { className: "bb-grid", children: [
12381
+ /* @__PURE__ */ jsx36(
12019
12382
  MaterialTexturePathField,
12020
12383
  {
12021
12384
  label: "Displacement map path",
@@ -12024,7 +12387,7 @@ function MaterialSection({
12024
12387
  onChange: (value) => setSelectedTexturePath(selectedItem, "displacementMap", value)
12025
12388
  }
12026
12389
  ),
12027
- /* @__PURE__ */ jsx35(
12390
+ /* @__PURE__ */ jsx36(
12028
12391
  MaterialSliderField,
12029
12392
  {
12030
12393
  label: "Displacement scale",
@@ -12038,7 +12401,7 @@ function MaterialSection({
12038
12401
  }))
12039
12402
  }
12040
12403
  ),
12041
- /* @__PURE__ */ jsx35(
12404
+ /* @__PURE__ */ jsx36(
12042
12405
  MaterialSliderField,
12043
12406
  {
12044
12407
  label: "Displacement bias",
@@ -12055,7 +12418,7 @@ function MaterialSection({
12055
12418
  ] })
12056
12419
  }
12057
12420
  ),
12058
- /* @__PURE__ */ jsx35(
12421
+ /* @__PURE__ */ jsx36(
12059
12422
  MaterialPanelGroup,
12060
12423
  {
12061
12424
  panelKey: "ao",
@@ -12063,8 +12426,8 @@ function MaterialSection({
12063
12426
  title: "Ambient Occlusion",
12064
12427
  tooltip: "Darkens creases and contact areas to add depth.",
12065
12428
  controls: panelControls,
12066
- children: /* @__PURE__ */ jsxs30("div", { className: "bb-grid", children: [
12067
- /* @__PURE__ */ jsx35(
12429
+ children: /* @__PURE__ */ jsxs31("div", { className: "bb-grid", children: [
12430
+ /* @__PURE__ */ jsx36(
12068
12431
  MaterialTexturePathField,
12069
12432
  {
12070
12433
  label: "AO map path",
@@ -12073,7 +12436,7 @@ function MaterialSection({
12073
12436
  onChange: (value) => setSelectedTexturePath(selectedItem, "aoMap", value)
12074
12437
  }
12075
12438
  ),
12076
- /* @__PURE__ */ jsx35(
12439
+ /* @__PURE__ */ jsx36(
12077
12440
  MaterialSliderField,
12078
12441
  {
12079
12442
  label: "AO intensity",
@@ -12090,7 +12453,7 @@ function MaterialSection({
12090
12453
  ] })
12091
12454
  }
12092
12455
  ),
12093
- /* @__PURE__ */ jsxs30(
12456
+ /* @__PURE__ */ jsxs31(
12094
12457
  MaterialPanelGroup,
12095
12458
  {
12096
12459
  panelKey: "clearcoat",
@@ -12099,8 +12462,8 @@ function MaterialSection({
12099
12462
  tooltip: "Adds a glossy protective layer on top of the base material.",
12100
12463
  controls: panelControls,
12101
12464
  children: [
12102
- /* @__PURE__ */ jsxs30("div", { className: "bb-grid", children: [
12103
- /* @__PURE__ */ jsx35(
12465
+ /* @__PURE__ */ jsxs31("div", { className: "bb-grid", children: [
12466
+ /* @__PURE__ */ jsx36(
12104
12467
  MaterialTexturePathField,
12105
12468
  {
12106
12469
  label: "Clearcoat map path",
@@ -12109,7 +12472,7 @@ function MaterialSection({
12109
12472
  onChange: (value) => setSelectedTexturePath(selectedItem, "clearcoatMap", value)
12110
12473
  }
12111
12474
  ),
12112
- /* @__PURE__ */ jsx35(
12475
+ /* @__PURE__ */ jsx36(
12113
12476
  MaterialSliderField,
12114
12477
  {
12115
12478
  label: "Clearcoat",
@@ -12123,7 +12486,7 @@ function MaterialSection({
12123
12486
  }))
12124
12487
  }
12125
12488
  ),
12126
- /* @__PURE__ */ jsx35(
12489
+ /* @__PURE__ */ jsx36(
12127
12490
  MaterialTexturePathField,
12128
12491
  {
12129
12492
  label: "Clearcoat roughness map path",
@@ -12136,7 +12499,7 @@ function MaterialSection({
12136
12499
  )
12137
12500
  }
12138
12501
  ),
12139
- /* @__PURE__ */ jsx35(
12502
+ /* @__PURE__ */ jsx36(
12140
12503
  MaterialSliderField,
12141
12504
  {
12142
12505
  label: "Clearcoat roughness",
@@ -12150,7 +12513,7 @@ function MaterialSection({
12150
12513
  }))
12151
12514
  }
12152
12515
  ),
12153
- /* @__PURE__ */ jsx35(
12516
+ /* @__PURE__ */ jsx36(
12154
12517
  MaterialTexturePathField,
12155
12518
  {
12156
12519
  label: "Clearcoat normal map path",
@@ -12159,7 +12522,7 @@ function MaterialSection({
12159
12522
  onChange: (value) => setSelectedTexturePath(selectedItem, "clearcoatNormalMap", value)
12160
12523
  }
12161
12524
  ),
12162
- /* @__PURE__ */ jsx35(
12525
+ /* @__PURE__ */ jsx36(
12163
12526
  MaterialSliderField,
12164
12527
  {
12165
12528
  label: "Clearcoat normal scale",
@@ -12174,7 +12537,7 @@ function MaterialSection({
12174
12537
  }
12175
12538
  )
12176
12539
  ] }),
12177
- /* @__PURE__ */ jsx35("div", { className: "bb-checkboxRow", style: { marginTop: 12 }, children: /* @__PURE__ */ jsx35(
12540
+ /* @__PURE__ */ jsx36("div", { className: "bb-checkboxRow", style: { marginTop: 12 }, children: /* @__PURE__ */ jsx36(
12178
12541
  MaterialCheckboxField,
12179
12542
  {
12180
12543
  label: "Flip clearcoat normal Y",
@@ -12188,7 +12551,7 @@ function MaterialSection({
12188
12551
  ]
12189
12552
  }
12190
12553
  ),
12191
- /* @__PURE__ */ jsx35(
12554
+ /* @__PURE__ */ jsx36(
12192
12555
  MaterialPanelGroup,
12193
12556
  {
12194
12557
  panelKey: "sheen",
@@ -12196,8 +12559,8 @@ function MaterialSection({
12196
12559
  title: "Sheen",
12197
12560
  tooltip: "Adds a soft fabric-like sheen on grazing angles.",
12198
12561
  controls: panelControls,
12199
- children: /* @__PURE__ */ jsxs30("div", { className: "bb-grid", children: [
12200
- /* @__PURE__ */ jsx35(
12562
+ children: /* @__PURE__ */ jsxs31("div", { className: "bb-grid", children: [
12563
+ /* @__PURE__ */ jsx36(
12201
12564
  MaterialSliderField,
12202
12565
  {
12203
12566
  label: "Sheen",
@@ -12211,7 +12574,7 @@ function MaterialSection({
12211
12574
  }))
12212
12575
  }
12213
12576
  ),
12214
- /* @__PURE__ */ jsx35(
12577
+ /* @__PURE__ */ jsx36(
12215
12578
  MaterialColorField,
12216
12579
  {
12217
12580
  label: "Sheen color",
@@ -12222,7 +12585,7 @@ function MaterialSection({
12222
12585
  }))
12223
12586
  }
12224
12587
  ),
12225
- /* @__PURE__ */ jsx35(
12588
+ /* @__PURE__ */ jsx36(
12226
12589
  MaterialTexturePathField,
12227
12590
  {
12228
12591
  label: "Sheen color map path",
@@ -12231,7 +12594,7 @@ function MaterialSection({
12231
12594
  onChange: (value) => setSelectedTexturePath(selectedItem, "sheenColorMap", value)
12232
12595
  }
12233
12596
  ),
12234
- /* @__PURE__ */ jsx35(
12597
+ /* @__PURE__ */ jsx36(
12235
12598
  MaterialSliderField,
12236
12599
  {
12237
12600
  label: "Sheen roughness",
@@ -12245,7 +12608,7 @@ function MaterialSection({
12245
12608
  }))
12246
12609
  }
12247
12610
  ),
12248
- /* @__PURE__ */ jsx35(
12611
+ /* @__PURE__ */ jsx36(
12249
12612
  MaterialTexturePathField,
12250
12613
  {
12251
12614
  label: "Sheen roughness map path",
@@ -12257,7 +12620,7 @@ function MaterialSection({
12257
12620
  ] })
12258
12621
  }
12259
12622
  ),
12260
- /* @__PURE__ */ jsx35(
12623
+ /* @__PURE__ */ jsx36(
12261
12624
  MaterialPanelGroup,
12262
12625
  {
12263
12626
  panelKey: "anisotropy",
@@ -12265,8 +12628,8 @@ function MaterialSection({
12265
12628
  title: "Anisotropy",
12266
12629
  tooltip: "Stretches reflections in one direction for brushed or fiber-like surfaces.",
12267
12630
  controls: panelControls,
12268
- children: /* @__PURE__ */ jsxs30("div", { className: "bb-grid", children: [
12269
- /* @__PURE__ */ jsx35(
12631
+ children: /* @__PURE__ */ jsxs31("div", { className: "bb-grid", children: [
12632
+ /* @__PURE__ */ jsx36(
12270
12633
  MaterialSliderField,
12271
12634
  {
12272
12635
  label: "Intensity",
@@ -12280,7 +12643,7 @@ function MaterialSection({
12280
12643
  }))
12281
12644
  }
12282
12645
  ),
12283
- /* @__PURE__ */ jsx35(
12646
+ /* @__PURE__ */ jsx36(
12284
12647
  MaterialSelectField,
12285
12648
  {
12286
12649
  label: "Tilt direction",
@@ -12289,7 +12652,7 @@ function MaterialSection({
12289
12652
  onChange: setAnisotropyMode
12290
12653
  }
12291
12654
  ),
12292
- anisotropyMode === "factor" ? /* @__PURE__ */ jsx35(
12655
+ anisotropyMode === "factor" ? /* @__PURE__ */ jsx36(
12293
12656
  MaterialSliderField,
12294
12657
  {
12295
12658
  label: "Rotation (rad)",
@@ -12302,7 +12665,7 @@ function MaterialSection({
12302
12665
  anisotropyRotation: value
12303
12666
  }))
12304
12667
  }
12305
- ) : /* @__PURE__ */ jsx35(
12668
+ ) : /* @__PURE__ */ jsx36(
12306
12669
  MaterialTexturePathField,
12307
12670
  {
12308
12671
  label: "Anisotropy map path",
@@ -12314,7 +12677,7 @@ function MaterialSection({
12314
12677
  ] })
12315
12678
  }
12316
12679
  ),
12317
- /* @__PURE__ */ jsx35(
12680
+ /* @__PURE__ */ jsx36(
12318
12681
  MaterialPanelGroup,
12319
12682
  {
12320
12683
  panelKey: "transmission",
@@ -12323,8 +12686,8 @@ function MaterialSection({
12323
12686
  tooltip: "Controls glass-like transmission and how light travels through volume.",
12324
12687
  toggleLabel: "Toggle Transmission and Volume",
12325
12688
  controls: panelControls,
12326
- children: /* @__PURE__ */ jsxs30("div", { className: "bb-grid", children: [
12327
- /* @__PURE__ */ jsx35(
12689
+ children: /* @__PURE__ */ jsxs31("div", { className: "bb-grid", children: [
12690
+ /* @__PURE__ */ jsx36(
12328
12691
  MaterialSliderField,
12329
12692
  {
12330
12693
  label: "Transmission",
@@ -12338,7 +12701,7 @@ function MaterialSection({
12338
12701
  }))
12339
12702
  }
12340
12703
  ),
12341
- /* @__PURE__ */ jsx35(
12704
+ /* @__PURE__ */ jsx36(
12342
12705
  MaterialTexturePathField,
12343
12706
  {
12344
12707
  label: "Transmission map path",
@@ -12347,7 +12710,7 @@ function MaterialSection({
12347
12710
  onChange: (value) => setSelectedTexturePath(selectedItem, "transmissionMap", value)
12348
12711
  }
12349
12712
  ),
12350
- /* @__PURE__ */ jsx35(
12713
+ /* @__PURE__ */ jsx36(
12351
12714
  MaterialSliderField,
12352
12715
  {
12353
12716
  label: "Thickness",
@@ -12361,7 +12724,7 @@ function MaterialSection({
12361
12724
  }))
12362
12725
  }
12363
12726
  ),
12364
- /* @__PURE__ */ jsx35(
12727
+ /* @__PURE__ */ jsx36(
12365
12728
  MaterialTexturePathField,
12366
12729
  {
12367
12730
  label: "Thickness map path",
@@ -12370,7 +12733,7 @@ function MaterialSection({
12370
12733
  onChange: (value) => setSelectedTexturePath(selectedItem, "thicknessMap", value)
12371
12734
  }
12372
12735
  ),
12373
- /* @__PURE__ */ jsx35(
12736
+ /* @__PURE__ */ jsx36(
12374
12737
  MaterialColorField,
12375
12738
  {
12376
12739
  label: "Volume tint",
@@ -12381,7 +12744,7 @@ function MaterialSection({
12381
12744
  }))
12382
12745
  }
12383
12746
  ),
12384
- /* @__PURE__ */ jsx35(
12747
+ /* @__PURE__ */ jsx36(
12385
12748
  MaterialNumberField,
12386
12749
  {
12387
12750
  label: "Attenuation distance",
@@ -12398,7 +12761,7 @@ function MaterialSection({
12398
12761
  ] })
12399
12762
  }
12400
12763
  ),
12401
- /* @__PURE__ */ jsx35(
12764
+ /* @__PURE__ */ jsx36(
12402
12765
  MaterialPanelGroup,
12403
12766
  {
12404
12767
  panelKey: "faces",
@@ -12406,7 +12769,7 @@ function MaterialSection({
12406
12769
  title: "Faces Rendering",
12407
12770
  tooltip: "Chooses which side of the mesh renders and how backfaces are handled.",
12408
12771
  controls: panelControls,
12409
- children: /* @__PURE__ */ jsx35("div", { className: "bb-grid", children: /* @__PURE__ */ jsx35(
12772
+ children: /* @__PURE__ */ jsx36("div", { className: "bb-grid", children: /* @__PURE__ */ jsx36(
12410
12773
  MaterialSelectField,
12411
12774
  {
12412
12775
  label: "Side",
@@ -12580,12 +12943,12 @@ var MATERIAL_PRESETS = [
12580
12943
  ];
12581
12944
 
12582
12945
  // src/components/BindingBuilder/components/MaterialPresetGallery.tsx
12583
- import { jsx as jsx36, jsxs as jsxs31 } from "react/jsx-runtime";
12946
+ import { jsx as jsx37, jsxs as jsxs32 } from "react/jsx-runtime";
12584
12947
  function MaterialPresetGallery({
12585
12948
  disabled,
12586
12949
  onApply
12587
12950
  }) {
12588
- return /* @__PURE__ */ jsx36("div", { className: "bb-presetGallery", children: MATERIAL_PRESETS.map((preset) => /* @__PURE__ */ jsxs31(
12951
+ return /* @__PURE__ */ jsx37("div", { className: "bb-presetGallery", children: MATERIAL_PRESETS.map((preset) => /* @__PURE__ */ jsxs32(
12589
12952
  "button",
12590
12953
  {
12591
12954
  type: "button",
@@ -12594,14 +12957,14 @@ function MaterialPresetGallery({
12594
12957
  title: `Apply ${preset.label} preset`,
12595
12958
  onClick: () => onApply(preset.material),
12596
12959
  children: [
12597
- /* @__PURE__ */ jsx36(
12960
+ /* @__PURE__ */ jsx37(
12598
12961
  "span",
12599
12962
  {
12600
12963
  className: "bb-presetSwatchColor",
12601
12964
  style: { background: preset.swatch }
12602
12965
  }
12603
12966
  ),
12604
- /* @__PURE__ */ jsx36("span", { className: "bb-presetSwatchLabel", children: preset.label })
12967
+ /* @__PURE__ */ jsx37("span", { className: "bb-presetSwatchLabel", children: preset.label })
12605
12968
  ]
12606
12969
  },
12607
12970
  preset.id
@@ -12609,9 +12972,9 @@ function MaterialPresetGallery({
12609
12972
  }
12610
12973
 
12611
12974
  // src/components/BindingBuilder/components/ToolbarExportMenu.tsx
12612
- import { useEffect as useEffect17, useRef as useRef14, useState as useState22 } from "react";
12975
+ import { useEffect as useEffect18, useRef as useRef15, useState as useState22 } from "react";
12613
12976
  import { ChevronDown as ChevronDown5, Download as Download2, FileCode2, FileJson } from "lucide-react";
12614
- import { jsx as jsx37, jsxs as jsxs32 } from "react/jsx-runtime";
12977
+ import { jsx as jsx38, jsxs as jsxs33 } from "react/jsx-runtime";
12615
12978
  function ToolbarExportMenu({
12616
12979
  disabled,
12617
12980
  exporting,
@@ -12619,8 +12982,8 @@ function ToolbarExportMenu({
12619
12982
  onExportTs
12620
12983
  }) {
12621
12984
  const [open, setOpen] = useState22(false);
12622
- const wrapperRef = useRef14(null);
12623
- useEffect17(() => {
12985
+ const wrapperRef = useRef15(null);
12986
+ useEffect18(() => {
12624
12987
  if (!open) return;
12625
12988
  const close = (event) => {
12626
12989
  if (!wrapperRef.current?.contains(event.target)) {
@@ -12630,8 +12993,8 @@ function ToolbarExportMenu({
12630
12993
  window.addEventListener("pointerdown", close);
12631
12994
  return () => window.removeEventListener("pointerdown", close);
12632
12995
  }, [open]);
12633
- return /* @__PURE__ */ jsxs32("div", { className: "bb-exportMenuWrapper", ref: wrapperRef, children: [
12634
- /* @__PURE__ */ jsxs32(
12996
+ return /* @__PURE__ */ jsxs33("div", { className: "bb-exportMenuWrapper", ref: wrapperRef, children: [
12997
+ /* @__PURE__ */ jsxs33(
12635
12998
  "button",
12636
12999
  {
12637
13000
  type: "button",
@@ -12640,14 +13003,14 @@ function ToolbarExportMenu({
12640
13003
  onClick: () => setOpen((prev) => !prev),
12641
13004
  title: "Export",
12642
13005
  children: [
12643
- /* @__PURE__ */ jsx37(Download2, { size: 14 }),
13006
+ /* @__PURE__ */ jsx38(Download2, { size: 14 }),
12644
13007
  exporting ? "Exporting\u2026" : "Export",
12645
- /* @__PURE__ */ jsx37(ChevronDown5, { size: 12 })
13008
+ /* @__PURE__ */ jsx38(ChevronDown5, { size: 12 })
12646
13009
  ]
12647
13010
  }
12648
13011
  ),
12649
- open && /* @__PURE__ */ jsxs32("div", { className: "bb-exportMenu", children: [
12650
- /* @__PURE__ */ jsxs32(
13012
+ open && /* @__PURE__ */ jsxs33("div", { className: "bb-exportMenu", children: [
13013
+ /* @__PURE__ */ jsxs33(
12651
13014
  "button",
12652
13015
  {
12653
13016
  type: "button",
@@ -12657,12 +13020,12 @@ function ToolbarExportMenu({
12657
13020
  void onExportJson();
12658
13021
  },
12659
13022
  children: [
12660
- /* @__PURE__ */ jsx37(FileJson, { size: 14 }),
13023
+ /* @__PURE__ */ jsx38(FileJson, { size: 14 }),
12661
13024
  "JSON"
12662
13025
  ]
12663
13026
  }
12664
13027
  ),
12665
- /* @__PURE__ */ jsxs32(
13028
+ /* @__PURE__ */ jsxs33(
12666
13029
  "button",
12667
13030
  {
12668
13031
  type: "button",
@@ -12672,7 +13035,7 @@ function ToolbarExportMenu({
12672
13035
  void onExportTs();
12673
13036
  },
12674
13037
  children: [
12675
- /* @__PURE__ */ jsx37(FileCode2, { size: 14 }),
13038
+ /* @__PURE__ */ jsx38(FileCode2, { size: 14 }),
12676
13039
  "TypeScript"
12677
13040
  ]
12678
13041
  }
@@ -12682,7 +13045,7 @@ function ToolbarExportMenu({
12682
13045
  }
12683
13046
 
12684
13047
  // src/components/BindingBuilder/components/IdentityPanel.tsx
12685
- import { jsx as jsx38, jsxs as jsxs33 } from "react/jsx-runtime";
13048
+ import { jsx as jsx39, jsxs as jsxs34 } from "react/jsx-runtime";
12686
13049
  function IdentityPanel({
12687
13050
  selectedItem,
12688
13051
  selectedBinding,
@@ -12691,11 +13054,11 @@ function IdentityPanel({
12691
13054
  updateBinding,
12692
13055
  idError
12693
13056
  }) {
12694
- return /* @__PURE__ */ jsx38(BBSection, { title: "Identity", open, onToggle, children: /* @__PURE__ */ jsxs33("div", { className: "bb-grid", children: [
12695
- /* @__PURE__ */ jsxs33("div", { className: "bb-field bb-field--full", children: [
12696
- /* @__PURE__ */ jsx38("label", { className: "bb-label", children: "Model Node ID" }),
12697
- /* @__PURE__ */ jsx38("span", { className: "bb-note", children: "This is the exact mesh name from the imported model and stays locked." }),
12698
- /* @__PURE__ */ jsx38(
13057
+ return /* @__PURE__ */ jsx39(BBSection, { title: "Identity", open, onToggle, children: /* @__PURE__ */ jsxs34("div", { className: "bb-grid", children: [
13058
+ /* @__PURE__ */ jsxs34("div", { className: "bb-field bb-field--full", children: [
13059
+ /* @__PURE__ */ jsx39("label", { className: "bb-label", children: "Model Node ID" }),
13060
+ /* @__PURE__ */ jsx39("span", { className: "bb-note", children: "This is the exact mesh name from the imported model and stays locked." }),
13061
+ /* @__PURE__ */ jsx39(
12699
13062
  "input",
12700
13063
  {
12701
13064
  className: "bb-input bb-readonly",
@@ -12704,9 +13067,9 @@ function IdentityPanel({
12704
13067
  }
12705
13068
  )
12706
13069
  ] }),
12707
- /* @__PURE__ */ jsxs33("div", { className: "bb-field bb-field--full", children: [
12708
- /* @__PURE__ */ jsx38("label", { className: "bb-label", children: "ID" }),
12709
- /* @__PURE__ */ jsx38(
13070
+ /* @__PURE__ */ jsxs34("div", { className: "bb-field bb-field--full", children: [
13071
+ /* @__PURE__ */ jsx39("label", { className: "bb-label", children: "ID" }),
13072
+ /* @__PURE__ */ jsx39(
12710
13073
  "input",
12711
13074
  {
12712
13075
  className: `bb-input ${idError ? "bb-input--error" : ""}`,
@@ -12717,7 +13080,7 @@ function IdentityPanel({
12717
13080
  }))
12718
13081
  }
12719
13082
  ),
12720
- idError ? /* @__PURE__ */ jsx38("span", { className: "bb-status bb-status--error", children: idError }) : null
13083
+ idError ? /* @__PURE__ */ jsx39("span", { className: "bb-status bb-status--error", children: idError }) : null
12721
13084
  ] })
12722
13085
  ] }) });
12723
13086
  }
@@ -12753,7 +13116,7 @@ var statusOptions = [
12753
13116
  ];
12754
13117
 
12755
13118
  // src/components/BindingBuilder/components/BasicsPanel.tsx
12756
- import { jsx as jsx39, jsxs as jsxs34 } from "react/jsx-runtime";
13119
+ import { jsx as jsx40, jsxs as jsxs35 } from "react/jsx-runtime";
12757
13120
  function BasicsPanel({
12758
13121
  selectedItem,
12759
13122
  selectedBinding,
@@ -12761,11 +13124,11 @@ function BasicsPanel({
12761
13124
  onToggle,
12762
13125
  updateBinding
12763
13126
  }) {
12764
- return /* @__PURE__ */ jsxs34(BBSection, { title: "Basics", open, onToggle, children: [
12765
- /* @__PURE__ */ jsxs34("div", { className: "bb-grid", children: [
12766
- /* @__PURE__ */ jsxs34("div", { className: "bb-field", children: [
12767
- /* @__PURE__ */ jsx39("label", { className: "bb-label", children: "Label" }),
12768
- /* @__PURE__ */ jsx39(
13127
+ return /* @__PURE__ */ jsxs35(BBSection, { title: "Basics", open, onToggle, children: [
13128
+ /* @__PURE__ */ jsxs35("div", { className: "bb-grid", children: [
13129
+ /* @__PURE__ */ jsxs35("div", { className: "bb-field", children: [
13130
+ /* @__PURE__ */ jsx40("label", { className: "bb-label", children: "Label" }),
13131
+ /* @__PURE__ */ jsx40(
12769
13132
  "input",
12770
13133
  {
12771
13134
  className: "bb-input",
@@ -12777,9 +13140,9 @@ function BasicsPanel({
12777
13140
  }
12778
13141
  )
12779
13142
  ] }),
12780
- /* @__PURE__ */ jsxs34("div", { className: "bb-field", children: [
12781
- /* @__PURE__ */ jsx39("label", { className: "bb-label", children: "Type" }),
12782
- /* @__PURE__ */ jsx39(
13143
+ /* @__PURE__ */ jsxs35("div", { className: "bb-field", children: [
13144
+ /* @__PURE__ */ jsx40("label", { className: "bb-label", children: "Type" }),
13145
+ /* @__PURE__ */ jsx40(
12783
13146
  "input",
12784
13147
  {
12785
13148
  className: "bb-input",
@@ -12791,11 +13154,11 @@ function BasicsPanel({
12791
13154
  }))
12792
13155
  }
12793
13156
  ),
12794
- /* @__PURE__ */ jsx39("datalist", { id: "binding-type-suggestions", children: typeSuggestions.map((option) => /* @__PURE__ */ jsx39("option", { value: option }, option)) })
13157
+ /* @__PURE__ */ jsx40("datalist", { id: "binding-type-suggestions", children: typeSuggestions.map((option) => /* @__PURE__ */ jsx40("option", { value: option }, option)) })
12795
13158
  ] }),
12796
- /* @__PURE__ */ jsxs34("div", { className: "bb-field", children: [
12797
- /* @__PURE__ */ jsx39("label", { className: "bb-label", children: "Status" }),
12798
- /* @__PURE__ */ jsx39(
13159
+ /* @__PURE__ */ jsxs35("div", { className: "bb-field", children: [
13160
+ /* @__PURE__ */ jsx40("label", { className: "bb-label", children: "Status" }),
13161
+ /* @__PURE__ */ jsx40(
12799
13162
  "select",
12800
13163
  {
12801
13164
  className: "bb-select",
@@ -12804,14 +13167,14 @@ function BasicsPanel({
12804
13167
  ...binding,
12805
13168
  status: event.target.value
12806
13169
  })),
12807
- children: statusOptions.map((option) => /* @__PURE__ */ jsx39("option", { value: option, children: option }, option))
13170
+ children: statusOptions.map((option) => /* @__PURE__ */ jsx40("option", { value: option, children: option }, option))
12808
13171
  }
12809
13172
  )
12810
13173
  ] })
12811
13174
  ] }),
12812
- /* @__PURE__ */ jsxs34("div", { className: "bb-checkboxRow", style: { marginTop: 12 }, children: [
12813
- /* @__PURE__ */ jsxs34("label", { className: "bb-checkbox", children: [
12814
- /* @__PURE__ */ jsx39(
13175
+ /* @__PURE__ */ jsxs35("div", { className: "bb-checkboxRow", style: { marginTop: 12 }, children: [
13176
+ /* @__PURE__ */ jsxs35("label", { className: "bb-checkbox", children: [
13177
+ /* @__PURE__ */ jsx40(
12815
13178
  "input",
12816
13179
  {
12817
13180
  type: "checkbox",
@@ -12824,8 +13187,8 @@ function BasicsPanel({
12824
13187
  ),
12825
13188
  "selectable"
12826
13189
  ] }),
12827
- /* @__PURE__ */ jsxs34("label", { className: "bb-checkbox", children: [
12828
- /* @__PURE__ */ jsx39(
13190
+ /* @__PURE__ */ jsxs35("label", { className: "bb-checkbox", children: [
13191
+ /* @__PURE__ */ jsx40(
12829
13192
  "input",
12830
13193
  {
12831
13194
  type: "checkbox",
@@ -12838,8 +13201,8 @@ function BasicsPanel({
12838
13201
  ),
12839
13202
  "hoverable"
12840
13203
  ] }),
12841
- /* @__PURE__ */ jsxs34("label", { className: "bb-checkbox", children: [
12842
- /* @__PURE__ */ jsx39(
13204
+ /* @__PURE__ */ jsxs35("label", { className: "bb-checkbox", children: [
13205
+ /* @__PURE__ */ jsx40(
12843
13206
  "input",
12844
13207
  {
12845
13208
  type: "checkbox",
@@ -12857,7 +13220,7 @@ function BasicsPanel({
12857
13220
  }
12858
13221
 
12859
13222
  // src/components/BindingBuilder/components/ActionsPanel.tsx
12860
- import { jsx as jsx40, jsxs as jsxs35 } from "react/jsx-runtime";
13223
+ import { jsx as jsx41, jsxs as jsxs36 } from "react/jsx-runtime";
12861
13224
  function ActionsPanel({
12862
13225
  selectedItem,
12863
13226
  selectedBinding,
@@ -12865,23 +13228,23 @@ function ActionsPanel({
12865
13228
  onToggle,
12866
13229
  updateBinding
12867
13230
  }) {
12868
- return /* @__PURE__ */ jsxs35(BBSection, { title: "Actions", open, onToggle, children: [
12869
- /* @__PURE__ */ jsx40("div", { className: "bb-actionList", children: (selectedBinding.actions ?? []).map((action, index) => (
13231
+ return /* @__PURE__ */ jsxs36(BBSection, { title: "Actions", open, onToggle, children: [
13232
+ /* @__PURE__ */ jsx41("div", { className: "bb-actionList", children: (selectedBinding.actions ?? []).map((action, index) => (
12870
13233
  // Keyed by position, not action.id: the id is an editable field, so
12871
13234
  // keying on it would remount the card on every keystroke and drop
12872
13235
  // input focus.
12873
- /* @__PURE__ */ jsxs35("div", { className: "bb-actionCard", children: [
12874
- /* @__PURE__ */ jsxs35("div", { className: "bb-actionCardHeader", children: [
12875
- /* @__PURE__ */ jsxs35("div", { className: "bb-actionCardTitle", children: [
13236
+ /* @__PURE__ */ jsxs36("div", { className: "bb-actionCard", children: [
13237
+ /* @__PURE__ */ jsxs36("div", { className: "bb-actionCardHeader", children: [
13238
+ /* @__PURE__ */ jsxs36("div", { className: "bb-actionCardTitle", children: [
12876
13239
  "Action ",
12877
13240
  index + 1
12878
13241
  ] }),
12879
- /* @__PURE__ */ jsx40("div", { className: "bb-actionCardMeta", children: action.type })
13242
+ /* @__PURE__ */ jsx41("div", { className: "bb-actionCardMeta", children: action.type })
12880
13243
  ] }),
12881
- /* @__PURE__ */ jsxs35("div", { className: "bb-actionGrid", children: [
12882
- /* @__PURE__ */ jsxs35("div", { className: "bb-field", children: [
12883
- /* @__PURE__ */ jsx40("label", { className: "bb-label", children: "Action ID" }),
12884
- /* @__PURE__ */ jsx40(
13244
+ /* @__PURE__ */ jsxs36("div", { className: "bb-actionGrid", children: [
13245
+ /* @__PURE__ */ jsxs36("div", { className: "bb-field", children: [
13246
+ /* @__PURE__ */ jsx41("label", { className: "bb-label", children: "Action ID" }),
13247
+ /* @__PURE__ */ jsx41(
12885
13248
  "input",
12886
13249
  {
12887
13250
  className: "bb-input",
@@ -12895,9 +13258,9 @@ function ActionsPanel({
12895
13258
  }
12896
13259
  )
12897
13260
  ] }),
12898
- /* @__PURE__ */ jsxs35("div", { className: "bb-field", children: [
12899
- /* @__PURE__ */ jsx40("label", { className: "bb-label", children: "Label" }),
12900
- /* @__PURE__ */ jsx40(
13261
+ /* @__PURE__ */ jsxs36("div", { className: "bb-field", children: [
13262
+ /* @__PURE__ */ jsx41("label", { className: "bb-label", children: "Label" }),
13263
+ /* @__PURE__ */ jsx41(
12901
13264
  "input",
12902
13265
  {
12903
13266
  className: "bb-input",
@@ -12912,7 +13275,7 @@ function ActionsPanel({
12912
13275
  )
12913
13276
  ] })
12914
13277
  ] }),
12915
- /* @__PURE__ */ jsx40("div", { className: "bb-actionFooter", children: /* @__PURE__ */ jsx40(
13278
+ /* @__PURE__ */ jsx41("div", { className: "bb-actionFooter", children: /* @__PURE__ */ jsx41(
12916
13279
  "button",
12917
13280
  {
12918
13281
  type: "button",
@@ -12928,7 +13291,7 @@ function ActionsPanel({
12928
13291
  ) })
12929
13292
  ] }, index)
12930
13293
  )) }),
12931
- /* @__PURE__ */ jsx40("div", { style: { marginTop: 10 }, children: /* @__PURE__ */ jsx40(
13294
+ /* @__PURE__ */ jsx41("div", { style: { marginTop: 10 }, children: /* @__PURE__ */ jsx41(
12932
13295
  "button",
12933
13296
  {
12934
13297
  type: "button",
@@ -12951,7 +13314,8 @@ function ActionsPanel({
12951
13314
  }
12952
13315
 
12953
13316
  // src/components/BindingBuilder/components/CameraPositionPanel.tsx
12954
- import { jsx as jsx41, jsxs as jsxs36 } from "react/jsx-runtime";
13317
+ import { MapPin as MapPin3, Trash2 as Trash25 } from "lucide-react";
13318
+ import { Fragment as Fragment10, jsx as jsx42, jsxs as jsxs37 } from "react/jsx-runtime";
12955
13319
  function CameraPositionPanel({
12956
13320
  selectedBinding,
12957
13321
  open,
@@ -12960,67 +13324,30 @@ function CameraPositionPanel({
12960
13324
  formattedCameraState,
12961
13325
  savedCameraPosition,
12962
13326
  onSaveCameraPosition,
13327
+ onGoToSavedCamera,
13328
+ onClearSavedCamera,
12963
13329
  onResetView,
12964
13330
  onFitScene
12965
13331
  }) {
12966
- return /* @__PURE__ */ jsxs36(BBSection, { title: "Camera Position", open, onToggle, children: [
12967
- /* @__PURE__ */ jsxs36("div", { className: "bb-cameraLive", children: [
12968
- /* @__PURE__ */ jsxs36("div", { className: "bb-cameraLiveHeader", children: [
12969
- /* @__PURE__ */ jsx41("span", { className: "bb-cameraLiveBadge", children: "LIVE" }),
12970
- /* @__PURE__ */ jsx41("span", { className: "bb-cameraLiveLabel", children: "Current Camera View" })
12971
- ] }),
12972
- cameraState ? /* @__PURE__ */ jsxs36("div", { className: "bb-cameraValues", children: [
12973
- /* @__PURE__ */ jsxs36("div", { className: "bb-cameraRow", children: [
12974
- /* @__PURE__ */ jsx41("span", { className: "bb-cameraLabel", children: "Position:" }),
12975
- /* @__PURE__ */ jsx41("code", { className: "bb-cameraCode", children: formattedCameraState?.position })
12976
- ] }),
12977
- /* @__PURE__ */ jsxs36("div", { className: "bb-cameraRow", children: [
12978
- /* @__PURE__ */ jsx41("span", { className: "bb-cameraLabel", children: "Target:" }),
12979
- /* @__PURE__ */ jsx41("code", { className: "bb-cameraCode", children: formattedCameraState?.target })
12980
- ] })
12981
- ] }) : /* @__PURE__ */ jsx41("div", { className: "bb-cameraPlaceholder", children: "Move the camera to see real-time position updates" })
12982
- ] }),
12983
- savedCameraPosition?.position && savedCameraPosition?.target && /* @__PURE__ */ jsxs36("div", { className: "bb-cameraSaved", children: [
12984
- /* @__PURE__ */ jsxs36("div", { className: "bb-cameraSavedHeader", children: [
12985
- /* @__PURE__ */ jsx41("span", { className: "bb-cameraSavedBadge", children: "SAVED" }),
12986
- /* @__PURE__ */ jsxs36("span", { className: "bb-cameraSavedLabel", children: [
12987
- "For ",
12988
- selectedBinding.label
12989
- ] })
12990
- ] }),
12991
- /* @__PURE__ */ jsxs36("div", { className: "bb-cameraValues", children: [
12992
- /* @__PURE__ */ jsxs36("div", { className: "bb-cameraRow", children: [
12993
- /* @__PURE__ */ jsx41("span", { className: "bb-cameraLabel", children: "Position:" }),
12994
- /* @__PURE__ */ jsxs36("code", { className: "bb-cameraCode", children: [
12995
- "[",
12996
- savedCameraPosition.position[0].toFixed(3),
12997
- ",",
12998
- " ",
12999
- savedCameraPosition.position[1].toFixed(3),
13000
- ",",
13001
- " ",
13002
- savedCameraPosition.position[2].toFixed(3),
13003
- "]"
13004
- ] })
13005
- ] }),
13006
- /* @__PURE__ */ jsxs36("div", { className: "bb-cameraRow", children: [
13007
- /* @__PURE__ */ jsx41("span", { className: "bb-cameraLabel", children: "Target:" }),
13008
- /* @__PURE__ */ jsxs36("code", { className: "bb-cameraCode", children: [
13009
- "[",
13010
- savedCameraPosition.target[0].toFixed(3),
13011
- ",",
13012
- " ",
13013
- savedCameraPosition.target[1].toFixed(3),
13014
- ",",
13015
- " ",
13016
- savedCameraPosition.target[2].toFixed(3),
13017
- "]"
13332
+ return /* @__PURE__ */ jsxs37(BBSection, { title: "Camera Position", open, onToggle, children: [
13333
+ /* @__PURE__ */ jsxs37("div", { className: "se-waypointCard bb-cameraWaypoint", children: [
13334
+ /* @__PURE__ */ jsx42("span", { className: "bb-cameraLiveBadge", children: "LIVE" }),
13335
+ /* @__PURE__ */ jsxs37("div", { className: "se-waypointBody", children: [
13336
+ /* @__PURE__ */ jsx42("div", { className: "bb-cameraWaypointLabel", children: "Current view" }),
13337
+ cameraState ? /* @__PURE__ */ jsxs37(Fragment10, { children: [
13338
+ /* @__PURE__ */ jsxs37("div", { className: "se-waypointCoords", children: [
13339
+ "pos ",
13340
+ formattedCameraState?.position
13341
+ ] }),
13342
+ /* @__PURE__ */ jsxs37("div", { className: "se-waypointCoords", children: [
13343
+ "look ",
13344
+ formattedCameraState?.target
13018
13345
  ] })
13019
- ] })
13346
+ ] }) : /* @__PURE__ */ jsx42("div", { className: "se-waypointCoords", children: "Move the camera to see live position" })
13020
13347
  ] })
13021
13348
  ] }),
13022
- /* @__PURE__ */ jsxs36("div", { className: "bb-cameraControls", children: [
13023
- /* @__PURE__ */ jsx41(
13349
+ /* @__PURE__ */ jsxs37("div", { className: "bb-cameraControls bb-cameraControls--centered", children: [
13350
+ /* @__PURE__ */ jsx42(
13024
13351
  "button",
13025
13352
  {
13026
13353
  type: "button",
@@ -13030,7 +13357,7 @@ function CameraPositionPanel({
13030
13357
  children: "Save"
13031
13358
  }
13032
13359
  ),
13033
- /* @__PURE__ */ jsx41(
13360
+ /* @__PURE__ */ jsx42(
13034
13361
  "button",
13035
13362
  {
13036
13363
  type: "button",
@@ -13039,7 +13366,7 @@ function CameraPositionPanel({
13039
13366
  children: "Reset View"
13040
13367
  }
13041
13368
  ),
13042
- /* @__PURE__ */ jsx41(
13369
+ /* @__PURE__ */ jsx42(
13043
13370
  "button",
13044
13371
  {
13045
13372
  type: "button",
@@ -13049,12 +13376,51 @@ function CameraPositionPanel({
13049
13376
  }
13050
13377
  )
13051
13378
  ] }),
13052
- /* @__PURE__ */ jsx41("span", { className: "bb-note", children: 'Tip: Position the camera how you want, then click "Save" to store this position for the selected object. The camera position updates in real-time as you navigate.' })
13379
+ savedCameraPosition?.position && savedCameraPosition?.target && /* @__PURE__ */ jsxs37("div", { className: "se-waypointCard bb-cameraWaypoint", children: [
13380
+ /* @__PURE__ */ jsx42("span", { className: "bb-cameraSavedBadge", children: "SAVED" }),
13381
+ /* @__PURE__ */ jsxs37("div", { className: "se-waypointBody", children: [
13382
+ /* @__PURE__ */ jsxs37("div", { className: "bb-cameraWaypointLabel", children: [
13383
+ "For ",
13384
+ selectedBinding.label
13385
+ ] }),
13386
+ /* @__PURE__ */ jsxs37("div", { className: "se-waypointCoords", children: [
13387
+ "pos [",
13388
+ savedCameraPosition.position.map((n) => n.toFixed(1)).join(", "),
13389
+ "]"
13390
+ ] }),
13391
+ /* @__PURE__ */ jsxs37("div", { className: "se-waypointCoords", children: [
13392
+ "look [",
13393
+ savedCameraPosition.target.map((n) => n.toFixed(1)).join(", "),
13394
+ "]"
13395
+ ] })
13396
+ ] }),
13397
+ /* @__PURE__ */ jsxs37("div", { className: "se-waypointActions", children: [
13398
+ /* @__PURE__ */ jsx42(
13399
+ "button",
13400
+ {
13401
+ className: "se-waypointAction",
13402
+ onClick: onGoToSavedCamera,
13403
+ title: "Fly camera to the saved position",
13404
+ children: /* @__PURE__ */ jsx42(MapPin3, { size: 15 })
13405
+ }
13406
+ ),
13407
+ /* @__PURE__ */ jsx42(
13408
+ "button",
13409
+ {
13410
+ className: "se-waypointAction se-waypointAction--delete",
13411
+ onClick: onClearSavedCamera,
13412
+ title: "Delete saved position",
13413
+ children: /* @__PURE__ */ jsx42(Trash25, { size: 15 })
13414
+ }
13415
+ )
13416
+ ] })
13417
+ ] }),
13418
+ /* @__PURE__ */ jsx42("span", { className: "bb-note", children: 'Tip: Position the camera how you want, then click "Save" to store this position for the selected object. The camera position updates in real-time as you navigate.' })
13053
13419
  ] });
13054
13420
  }
13055
13421
 
13056
13422
  // src/components/BindingBuilder/components/SceneNodesPanel.tsx
13057
- import { useMemo as useMemo14, useState as useState23 } from "react";
13423
+ import { useMemo as useMemo15, useState as useState23 } from "react";
13058
13424
 
13059
13425
  // src/components/BindingBuilder/utils/bindingFactory.ts
13060
13426
  import { GLTFLoader } from "three-stdlib";
@@ -13172,7 +13538,7 @@ function collectModelMetadata(modelUrl) {
13172
13538
  }
13173
13539
 
13174
13540
  // src/components/BindingBuilder/components/SceneNodesPanel.tsx
13175
- import { Fragment as Fragment10, jsx as jsx42, jsxs as jsxs37 } from "react/jsx-runtime";
13541
+ import { Fragment as Fragment11, jsx as jsx43, jsxs as jsxs38 } from "react/jsx-runtime";
13176
13542
  function SceneNodesPanel({
13177
13543
  bindings,
13178
13544
  selectedItem,
@@ -13182,8 +13548,8 @@ function SceneNodesPanel({
13182
13548
  onToggle
13183
13549
  }) {
13184
13550
  const [search, setSearch] = useState23("");
13185
- const nodeKeys = useMemo14(() => Object.keys(bindings), [bindings]);
13186
- const filteredNodeKeys = useMemo14(() => {
13551
+ const nodeKeys = useMemo15(() => Object.keys(bindings), [bindings]);
13552
+ const filteredNodeKeys = useMemo15(() => {
13187
13553
  const query = search.trim().toLowerCase();
13188
13554
  if (!query) return nodeKeys;
13189
13555
  return nodeKeys.filter((nodeKey) => {
@@ -13191,13 +13557,13 @@ function SceneNodesPanel({
13191
13557
  return nodeKey.toLowerCase().includes(query) || binding.label?.toLowerCase().includes(query) || binding.type.toLowerCase().includes(query);
13192
13558
  });
13193
13559
  }, [bindings, nodeKeys, search]);
13194
- return /* @__PURE__ */ jsxs37(
13560
+ return /* @__PURE__ */ jsxs38(
13195
13561
  "aside",
13196
13562
  {
13197
13563
  className: `bb-sidebar bb-sidebar--right ${isOpen ? "" : "bb-sidebar--collapsed"}`,
13198
13564
  children: [
13199
- /* @__PURE__ */ jsxs37("div", { className: "bb-header bb-header--sidebar", children: [
13200
- !isOpen && /* @__PURE__ */ jsx42(
13565
+ /* @__PURE__ */ jsxs38("div", { className: "bb-header bb-header--sidebar", children: [
13566
+ !isOpen && /* @__PURE__ */ jsx43(
13201
13567
  "button",
13202
13568
  {
13203
13569
  type: "button",
@@ -13208,15 +13574,15 @@ function SceneNodesPanel({
13208
13574
  children: "\u2190"
13209
13575
  }
13210
13576
  ),
13211
- isOpen && /* @__PURE__ */ jsxs37(Fragment10, { children: [
13212
- /* @__PURE__ */ jsxs37("div", { children: [
13213
- /* @__PURE__ */ jsx42("div", { className: "bb-sectionKicker", children: "Scene Nodes" }),
13214
- /* @__PURE__ */ jsxs37("div", { className: "bb-sectionHeading", children: [
13577
+ isOpen && /* @__PURE__ */ jsxs38(Fragment11, { children: [
13578
+ /* @__PURE__ */ jsxs38("div", { children: [
13579
+ /* @__PURE__ */ jsx43("div", { className: "bb-sectionKicker", children: "Scene Nodes" }),
13580
+ /* @__PURE__ */ jsxs38("div", { className: "bb-sectionHeading", children: [
13215
13581
  filteredNodeKeys.length,
13216
13582
  " visible entries"
13217
13583
  ] })
13218
13584
  ] }),
13219
- /* @__PURE__ */ jsx42(
13585
+ /* @__PURE__ */ jsx43(
13220
13586
  "button",
13221
13587
  {
13222
13588
  type: "button",
@@ -13229,8 +13595,8 @@ function SceneNodesPanel({
13229
13595
  )
13230
13596
  ] })
13231
13597
  ] }),
13232
- isOpen && /* @__PURE__ */ jsxs37("section", { className: "bb-nodeList", children: [
13233
- /* @__PURE__ */ jsx42("div", { className: "bb-search", children: /* @__PURE__ */ jsx42(
13598
+ isOpen && /* @__PURE__ */ jsxs38("section", { className: "bb-nodeList", children: [
13599
+ /* @__PURE__ */ jsx43("div", { className: "bb-search", children: /* @__PURE__ */ jsx43(
13234
13600
  "input",
13235
13601
  {
13236
13602
  className: "bb-input",
@@ -13239,10 +13605,10 @@ function SceneNodesPanel({
13239
13605
  onChange: (event) => setSearch(event.target.value)
13240
13606
  }
13241
13607
  ) }),
13242
- /* @__PURE__ */ jsx42("div", { className: "bb-nodeItems", children: filteredNodeKeys.map((nodeKey) => {
13608
+ /* @__PURE__ */ jsx43("div", { className: "bb-nodeItems", children: filteredNodeKeys.map((nodeKey) => {
13243
13609
  const binding = bindings[nodeKey];
13244
13610
  const isActive = nodeKey === selectedItem;
13245
- return /* @__PURE__ */ jsxs37(
13611
+ return /* @__PURE__ */ jsxs38(
13246
13612
  "button",
13247
13613
  {
13248
13614
  type: "button",
@@ -13251,8 +13617,8 @@ function SceneNodesPanel({
13251
13617
  onMouseEnter: () => onHover?.(nodeKey),
13252
13618
  onMouseLeave: () => onHover?.(null),
13253
13619
  children: [
13254
- /* @__PURE__ */ jsx42("span", { className: "bb-nodeName", children: nodeKey }),
13255
- /* @__PURE__ */ jsxs37("span", { className: "bb-nodeMeta", children: [
13620
+ /* @__PURE__ */ jsx43("span", { className: "bb-nodeName", children: nodeKey }),
13621
+ /* @__PURE__ */ jsxs38("span", { className: "bb-nodeMeta", children: [
13256
13622
  binding.type,
13257
13623
  binding.label === humanizeNodeName(nodeKey) ? "" : binding.label || "\xB7 Untitled"
13258
13624
  ] })
@@ -13268,7 +13634,7 @@ function SceneNodesPanel({
13268
13634
  }
13269
13635
 
13270
13636
  // src/components/BindingBuilder/components/JsonPanel.tsx
13271
- import { jsx as jsx43, jsxs as jsxs38 } from "react/jsx-runtime";
13637
+ import { jsx as jsx44, jsxs as jsxs39 } from "react/jsx-runtime";
13272
13638
  function JsonPanel({
13273
13639
  title,
13274
13640
  label,
@@ -13280,9 +13646,9 @@ function JsonPanel({
13280
13646
  onDraftChange,
13281
13647
  onCommit
13282
13648
  }) {
13283
- return /* @__PURE__ */ jsx43(BBSection, { title, open, onToggle, children: /* @__PURE__ */ jsxs38("div", { className: "bb-field", children: [
13284
- /* @__PURE__ */ jsx43("label", { className: "bb-label", children: label }),
13285
- /* @__PURE__ */ jsx43(
13649
+ return /* @__PURE__ */ jsx44(BBSection, { title, open, onToggle, children: /* @__PURE__ */ jsxs39("div", { className: "bb-field", children: [
13650
+ /* @__PURE__ */ jsx44("label", { className: "bb-label", children: label }),
13651
+ /* @__PURE__ */ jsx44(
13286
13652
  "textarea",
13287
13653
  {
13288
13654
  className: "bb-textarea",
@@ -13291,7 +13657,7 @@ function JsonPanel({
13291
13657
  onBlur: onCommit
13292
13658
  }
13293
13659
  ),
13294
- error ? /* @__PURE__ */ jsx43("span", { className: "bb-status bb-status--error", children: error }) : /* @__PURE__ */ jsxs38("span", { className: "bb-note", children: [
13660
+ error ? /* @__PURE__ */ jsx44("span", { className: "bb-status bb-status--error", children: error }) : /* @__PURE__ */ jsxs39("span", { className: "bb-note", children: [
13295
13661
  "Use a JSON object like ",
13296
13662
  example,
13297
13663
  "."
@@ -13326,7 +13692,7 @@ function mergeAnimationConfig(sceneConfig, animationDetails) {
13326
13692
  }
13327
13693
 
13328
13694
  // src/components/BindingBuilder/hooks/useUndoableState.ts
13329
- import { useCallback as useCallback7, useReducer, useRef as useRef15 } from "react";
13695
+ import { useCallback as useCallback7, useReducer, useRef as useRef16 } from "react";
13330
13696
  var MAX_HISTORY = 100;
13331
13697
  function historyReducer(state, action) {
13332
13698
  switch (action.type) {
@@ -13370,7 +13736,7 @@ function useUndoableState(initial, coalesceWindowMs = 600) {
13370
13736
  present: initial,
13371
13737
  future: []
13372
13738
  });
13373
- const lastSetAt = useRef15(0);
13739
+ const lastSetAt = useRef16(0);
13374
13740
  const setValue = useCallback7(
13375
13741
  (updater) => {
13376
13742
  const now = Date.now();
@@ -13617,7 +13983,7 @@ function removeMaterialPanelFields(material, panel) {
13617
13983
  }
13618
13984
 
13619
13985
  // src/components/BindingBuilder/BindingBuilder.tsx
13620
- import { Fragment as Fragment11, jsx as jsx44, jsxs as jsxs39 } from "react/jsx-runtime";
13986
+ import { Fragment as Fragment12, jsx as jsx45, jsxs as jsxs40 } from "react/jsx-runtime";
13621
13987
  var DEMO_MODEL_URL = "/room.glb";
13622
13988
  function isPlainObject(value) {
13623
13989
  return value !== null && typeof value === "object" && !Array.isArray(value);
@@ -13634,9 +14000,9 @@ function mergeSceneConfigSection(current, incoming) {
13634
14000
  return next;
13635
14001
  }
13636
14002
  function BindingBuilder({ licenseKey }) {
13637
- const modelInputRef = useRef16(null);
13638
- const bindingsInputRef = useRef16(null);
13639
- const sceneConfigInputRef = useRef16(null);
14003
+ const modelInputRef = useRef17(null);
14004
+ const bindingsInputRef = useRef17(null);
14005
+ const sceneConfigInputRef = useRef17(null);
13640
14006
  const [modelUrl, setModelUrl] = useState24(null);
13641
14007
  const [modelName, setModelName] = useState24("");
13642
14008
  const [selectedItem, setSelectedItem] = useState24(null);
@@ -13688,7 +14054,7 @@ function BindingBuilder({ licenseKey }) {
13688
14054
  const [activeSubTab, setActiveSubTab] = useState24("General");
13689
14055
  const [pbrWorkflow, setPbrWorkflow] = useState24("metalness");
13690
14056
  const [materialPanelEnabled, setMaterialPanelEnabled] = useState24(createMaterialPanelEnabledState());
13691
- const materialPanelItemRef = useRef16(null);
14057
+ const materialPanelItemRef = useRef17(null);
13692
14058
  const [zoomOnSelected, setZoomOnSelected] = useState24(false);
13693
14059
  const [highlightOnHover, setHighlightOnHover] = useState24(true);
13694
14060
  const [resetDialogOpen, setResetDialogOpen] = useState24(false);
@@ -13702,7 +14068,7 @@ function BindingBuilder({ licenseKey }) {
13702
14068
  const [panelHoveredBinding, setPanelHoveredBinding] = useState24(null);
13703
14069
  const license = useLicense(licenseKey);
13704
14070
  const tier = normalizeTier(license.tier);
13705
- const bindingBuilderContextValue = useMemo15(
14071
+ const bindingBuilderContextValue = useMemo16(
13706
14072
  () => ({
13707
14073
  isInsideEditor: true,
13708
14074
  activeTab: activeEditorTab,
@@ -13711,17 +14077,32 @@ function BindingBuilder({ licenseKey }) {
13711
14077
  }),
13712
14078
  [activeEditorTab, activeSubTab, tier]
13713
14079
  );
13714
- const uploadedModelRef = useRef16(null);
13715
- const previewDragDepthRef = useRef16(0);
13716
- const loadRequestRef = useRef16(0);
14080
+ const uploadedModelRef = useRef17(null);
14081
+ const previewDragDepthRef = useRef17(0);
14082
+ const loadRequestRef = useRef17(0);
13717
14083
  const {
13718
14084
  cameraState,
13719
14085
  resetView,
13720
14086
  focusObject,
13721
14087
  fitScene,
14088
+ setCameraState,
13722
14089
  handleCameraChange,
13723
14090
  handleViewerReady
13724
14091
  } = useViewerCamera();
14092
+ const handlePreviewWaypoint = useCallback8(
14093
+ (waypoint) => {
14094
+ setCameraState(
14095
+ {
14096
+ position: waypoint.position,
14097
+ target: waypoint.target,
14098
+ fov: cameraState?.fov ?? null,
14099
+ zoom: cameraState?.zoom ?? 1
14100
+ },
14101
+ true
14102
+ );
14103
+ },
14104
+ [setCameraState, cameraState]
14105
+ );
13725
14106
  const viewerAnimations = useViewerAnimations();
13726
14107
  const selectedBinding = selectedItem ? bindings[selectedItem] ?? null : null;
13727
14108
  const selectedMaterial = selectedBinding?.style?.material;
@@ -13756,7 +14137,7 @@ function BindingBuilder({ licenseKey }) {
13756
14137
  },
13757
14138
  []
13758
14139
  );
13759
- useEffect18(() => {
14140
+ useEffect19(() => {
13760
14141
  if (!selectedBinding) {
13761
14142
  setMetricsDraft("{}");
13762
14143
  setMetadataDraft("{}");
@@ -13769,7 +14150,7 @@ function BindingBuilder({ licenseKey }) {
13769
14150
  setMetricsError(null);
13770
14151
  setMetadataError(null);
13771
14152
  }, [selectedBinding]);
13772
- useEffect18(() => {
14153
+ useEffect19(() => {
13773
14154
  const derived = createMaterialPanelEnabledState(selectedMaterial);
13774
14155
  if (materialPanelItemRef.current !== selectedItem) {
13775
14156
  materialPanelItemRef.current = selectedItem;
@@ -13788,12 +14169,12 @@ function BindingBuilder({ licenseKey }) {
13788
14169
  return changed ? next : prev;
13789
14170
  });
13790
14171
  }, [selectedItem, selectedMaterial]);
13791
- useEffect18(() => {
14172
+ useEffect19(() => {
13792
14173
  return () => {
13793
14174
  uploadedModelRef.current?.cleanup();
13794
14175
  };
13795
14176
  }, []);
13796
- useEffect18(() => {
14177
+ useEffect19(() => {
13797
14178
  const handleKeyDown = (event) => {
13798
14179
  if (event.key.toLowerCase() !== "z" || !(event.metaKey || event.ctrlKey)) {
13799
14180
  return;
@@ -13814,7 +14195,7 @@ function BindingBuilder({ licenseKey }) {
13814
14195
  window.addEventListener("keydown", handleKeyDown);
13815
14196
  return () => window.removeEventListener("keydown", handleKeyDown);
13816
14197
  }, [activeEditorTab, bindingsHistory, sceneConfigHistory]);
13817
- useEffect18(() => {
14198
+ useEffect19(() => {
13818
14199
  if (loading || statusTone === "default" || statusTone === "success") return;
13819
14200
  setStatusBannerVisible(true);
13820
14201
  const timeout = window.setTimeout(
@@ -13823,8 +14204,8 @@ function BindingBuilder({ licenseKey }) {
13823
14204
  );
13824
14205
  return () => window.clearTimeout(timeout);
13825
14206
  }, [statusMessage, statusTone, loading]);
13826
- const nodeKeys = useMemo15(() => Object.keys(bindings), [bindings]);
13827
- const idError = useMemo15(() => {
14207
+ const nodeKeys = useMemo16(() => Object.keys(bindings), [bindings]);
14208
+ const idError = useMemo16(() => {
13828
14209
  if (!selectedItem || !selectedBinding) return null;
13829
14210
  const trimmed = selectedBinding.id.trim();
13830
14211
  if (!trimmed) return "ID is required.";
@@ -14091,7 +14472,8 @@ function BindingBuilder({ licenseKey }) {
14091
14472
  "groundShadows",
14092
14473
  "postProcessing",
14093
14474
  "animations",
14094
- "annotations"
14475
+ "annotations",
14476
+ "cinematic"
14095
14477
  ];
14096
14478
  const incoming = parsed;
14097
14479
  const matchedKeys = sceneConfigKeys.filter(
@@ -14222,6 +14604,25 @@ function BindingBuilder({ licenseKey }) {
14222
14604
  cameraState: currentCameraState
14223
14605
  }));
14224
14606
  }, [selectedItem, cameraState]);
14607
+ const goToSavedCameraPosition = useCallback8(() => {
14608
+ if (!savedCameraPosition?.position || !savedCameraPosition?.target) return;
14609
+ setCameraState(
14610
+ {
14611
+ position: savedCameraPosition.position,
14612
+ target: savedCameraPosition.target,
14613
+ fov: savedCameraPosition.fov ?? cameraState?.fov ?? null,
14614
+ zoom: savedCameraPosition.zoom ?? cameraState?.zoom ?? 1
14615
+ },
14616
+ true
14617
+ );
14618
+ }, [savedCameraPosition, setCameraState, cameraState]);
14619
+ const clearSavedCameraPosition = useCallback8(() => {
14620
+ if (!selectedItem) return;
14621
+ updateBinding(selectedItem, (binding) => ({
14622
+ ...binding,
14623
+ cameraState: void 0
14624
+ }));
14625
+ }, [selectedItem]);
14225
14626
  const handleResetView = useCallback8(async () => {
14226
14627
  await resetView();
14227
14628
  }, [resetView]);
@@ -14261,7 +14662,7 @@ function BindingBuilder({ licenseKey }) {
14261
14662
  closeResetDialog
14262
14663
  ]);
14263
14664
  if (license.status === "checking") {
14264
- return /* @__PURE__ */ jsx44("div", { className: "bb-root bb-root--licenseChecking", children: /* @__PURE__ */ jsx44(
14665
+ return /* @__PURE__ */ jsx45("div", { className: "bb-root bb-root--licenseChecking", children: /* @__PURE__ */ jsx45(
14265
14666
  LicenseOverlay,
14266
14667
  {
14267
14668
  title: "Verifying License",
@@ -14270,7 +14671,7 @@ function BindingBuilder({ licenseKey }) {
14270
14671
  ) });
14271
14672
  }
14272
14673
  if (license.status === "invalid") {
14273
- return /* @__PURE__ */ jsx44("div", { className: "bb-root bb-root--licenseInvalid", children: /* @__PURE__ */ jsx44(
14674
+ return /* @__PURE__ */ jsx45("div", { className: "bb-root bb-root--licenseInvalid", children: /* @__PURE__ */ jsx45(
14274
14675
  LicenseOverlay,
14275
14676
  {
14276
14677
  tone: "error",
@@ -14279,9 +14680,9 @@ function BindingBuilder({ licenseKey }) {
14279
14680
  }
14280
14681
  ) });
14281
14682
  }
14282
- return /* @__PURE__ */ jsxs39(BindingBuilderContext.Provider, { value: bindingBuilderContextValue, children: [
14283
- /* @__PURE__ */ jsxs39("div", { className: "bb-root", children: [
14284
- /* @__PURE__ */ jsx44(
14683
+ return /* @__PURE__ */ jsxs40(BindingBuilderContext.Provider, { value: bindingBuilderContextValue, children: [
14684
+ /* @__PURE__ */ jsxs40("div", { className: "bb-root", children: [
14685
+ /* @__PURE__ */ jsx45(
14285
14686
  "input",
14286
14687
  {
14287
14688
  ref: modelInputRef,
@@ -14292,7 +14693,7 @@ function BindingBuilder({ licenseKey }) {
14292
14693
  disabled: loading
14293
14694
  }
14294
14695
  ),
14295
- /* @__PURE__ */ jsx44(
14696
+ /* @__PURE__ */ jsx45(
14296
14697
  "input",
14297
14698
  {
14298
14699
  ref: bindingsInputRef,
@@ -14303,7 +14704,7 @@ function BindingBuilder({ licenseKey }) {
14303
14704
  disabled: loading
14304
14705
  }
14305
14706
  ),
14306
- /* @__PURE__ */ jsx44(
14707
+ /* @__PURE__ */ jsx45(
14307
14708
  "input",
14308
14709
  {
14309
14710
  ref: sceneConfigInputRef,
@@ -14314,14 +14715,14 @@ function BindingBuilder({ licenseKey }) {
14314
14715
  disabled: loading
14315
14716
  }
14316
14717
  ),
14317
- /* @__PURE__ */ jsxs39(
14718
+ /* @__PURE__ */ jsxs40(
14318
14719
  "aside",
14319
14720
  {
14320
14721
  className: `bb-sidebar bb-sidebar--left ${isEditorCollapsed ? "bb-sidebar--collapsed" : ""}`,
14321
14722
  children: [
14322
- /* @__PURE__ */ jsxs39("div", { className: "bb-header bb-header--sidebar", children: [
14323
- !isEditorCollapsed && /* @__PURE__ */ jsx44("h1", { className: "bb-title", children: "Binding Builder" }),
14324
- /* @__PURE__ */ jsx44(
14723
+ /* @__PURE__ */ jsxs40("div", { className: "bb-header bb-header--sidebar", children: [
14724
+ !isEditorCollapsed && /* @__PURE__ */ jsx45("h1", { className: "bb-title", children: "Binding Builder" }),
14725
+ /* @__PURE__ */ jsx45(
14325
14726
  "button",
14326
14727
  {
14327
14728
  type: "button",
@@ -14333,9 +14734,9 @@ function BindingBuilder({ licenseKey }) {
14333
14734
  }
14334
14735
  )
14335
14736
  ] }),
14336
- !isEditorCollapsed && /* @__PURE__ */ jsxs39(Fragment11, { children: [
14337
- /* @__PURE__ */ jsxs39("div", { className: "bb-tabRow", children: [
14338
- /* @__PURE__ */ jsx44(
14737
+ !isEditorCollapsed && /* @__PURE__ */ jsxs40(Fragment12, { children: [
14738
+ /* @__PURE__ */ jsxs40("div", { className: "bb-tabRow", children: [
14739
+ /* @__PURE__ */ jsx45(
14339
14740
  "button",
14340
14741
  {
14341
14742
  type: "button",
@@ -14344,7 +14745,7 @@ function BindingBuilder({ licenseKey }) {
14344
14745
  children: "Object"
14345
14746
  }
14346
14747
  ),
14347
- /* @__PURE__ */ jsx44(
14748
+ /* @__PURE__ */ jsx45(
14348
14749
  "button",
14349
14750
  {
14350
14751
  type: "button",
@@ -14354,26 +14755,36 @@ function BindingBuilder({ licenseKey }) {
14354
14755
  }
14355
14756
  )
14356
14757
  ] }),
14357
- /* @__PURE__ */ jsx44("section", { className: "bb-editor", children: activeEditorTab === "scene" ? /* @__PURE__ */ jsx44(Fragment11, { children: modelUrl ? /* @__PURE__ */ jsx44(
14758
+ /* @__PURE__ */ jsx45("section", { className: "bb-editor", children: activeEditorTab === "scene" ? /* @__PURE__ */ jsx45(Fragment12, { children: modelUrl ? /* @__PURE__ */ jsx45(
14358
14759
  SceneSettingsTab,
14359
14760
  {
14360
14761
  config: sceneConfig,
14361
14762
  onChange: setSceneConfig,
14362
14763
  activeSubTab,
14363
14764
  onActiveSubTabChange: setActiveSubTab,
14765
+ cameraState: (
14766
+ // Only feed the live camera while the Cinematic tab is
14767
+ // open — otherwise every orbit frame would re-render
14768
+ // the whole settings panel for no reason.
14769
+ activeSubTab === "Cinematic" && cameraState ? {
14770
+ position: cameraState.position,
14771
+ target: cameraState.target
14772
+ } : null
14773
+ ),
14774
+ onPreviewWaypoint: handlePreviewWaypoint,
14364
14775
  animationClipDetails: viewerAnimations.clipDetails,
14365
14776
  animationPlayback: viewerAnimations
14366
14777
  }
14367
- ) : /* @__PURE__ */ jsx44("div", { className: "bb-emptyEditor", children: "Upload a model to edit its scene settings." }) }) : !selectedBinding || !selectedItem ? /* @__PURE__ */ jsx44("div", { className: "bb-emptyEditor", children: "Select a generated node binding to edit its values." }) : /* @__PURE__ */ jsxs39(Fragment11, { children: [
14368
- /* @__PURE__ */ jsx44("div", { className: "bb-editorHeader", children: /* @__PURE__ */ jsxs39("div", { children: [
14369
- /* @__PURE__ */ jsx44("div", { className: "bb-sectionKicker", children: "Editing Binding" }),
14370
- /* @__PURE__ */ jsx44("h2", { className: "bb-editorTitle", children: selectedBinding.label || selectedItem }),
14371
- /* @__PURE__ */ jsxs39("div", { className: "bb-editorMeta", children: [
14372
- /* @__PURE__ */ jsx44("span", { children: selectedBinding.type }),
14373
- /* @__PURE__ */ jsx44("span", { children: selectedBinding.modelObjectId })
14778
+ ) : /* @__PURE__ */ jsx45("div", { className: "bb-emptyEditor", children: "Upload a model to edit its scene settings." }) }) : !selectedBinding || !selectedItem ? /* @__PURE__ */ jsx45("div", { className: "bb-emptyEditor", children: "Select a generated node binding to edit its values." }) : /* @__PURE__ */ jsxs40(Fragment12, { children: [
14779
+ /* @__PURE__ */ jsx45("div", { className: "bb-editorHeader", children: /* @__PURE__ */ jsxs40("div", { children: [
14780
+ /* @__PURE__ */ jsx45("div", { className: "bb-sectionKicker", children: "Editing Binding" }),
14781
+ /* @__PURE__ */ jsx45("h2", { className: "bb-editorTitle", children: selectedBinding.label || selectedItem }),
14782
+ /* @__PURE__ */ jsxs40("div", { className: "bb-editorMeta", children: [
14783
+ /* @__PURE__ */ jsx45("span", { children: selectedBinding.type }),
14784
+ /* @__PURE__ */ jsx45("span", { children: selectedBinding.modelObjectId })
14374
14785
  ] })
14375
14786
  ] }) }),
14376
- /* @__PURE__ */ jsx44(
14787
+ /* @__PURE__ */ jsx45(
14377
14788
  IdentityPanel,
14378
14789
  {
14379
14790
  selectedItem,
@@ -14384,7 +14795,7 @@ function BindingBuilder({ licenseKey }) {
14384
14795
  idError
14385
14796
  }
14386
14797
  ),
14387
- /* @__PURE__ */ jsx44(
14798
+ /* @__PURE__ */ jsx45(
14388
14799
  BasicsPanel,
14389
14800
  {
14390
14801
  selectedItem,
@@ -14394,7 +14805,7 @@ function BindingBuilder({ licenseKey }) {
14394
14805
  updateBinding
14395
14806
  }
14396
14807
  ),
14397
- /* @__PURE__ */ jsxs39(
14808
+ /* @__PURE__ */ jsxs40(
14398
14809
  BBSection,
14399
14810
  {
14400
14811
  title: "Material",
@@ -14402,7 +14813,7 @@ function BindingBuilder({ licenseKey }) {
14402
14813
  onToggle: () => toggleObjectSection("material"),
14403
14814
  tooltip: "Edit the selected object's base material, textures, and surface properties.",
14404
14815
  children: [
14405
- /* @__PURE__ */ jsx44(
14816
+ /* @__PURE__ */ jsx45(
14406
14817
  MaterialPresetGallery,
14407
14818
  {
14408
14819
  disabled: !selectedItem,
@@ -14412,7 +14823,7 @@ function BindingBuilder({ licenseKey }) {
14412
14823
  }))
14413
14824
  }
14414
14825
  ),
14415
- /* @__PURE__ */ jsx44(
14826
+ /* @__PURE__ */ jsx45(
14416
14827
  MaterialSection,
14417
14828
  {
14418
14829
  selectedItem,
@@ -14430,7 +14841,7 @@ function BindingBuilder({ licenseKey }) {
14430
14841
  ]
14431
14842
  }
14432
14843
  ),
14433
- /* @__PURE__ */ jsx44(
14844
+ /* @__PURE__ */ jsx45(
14434
14845
  ActionsPanel,
14435
14846
  {
14436
14847
  selectedItem,
@@ -14440,7 +14851,7 @@ function BindingBuilder({ licenseKey }) {
14440
14851
  updateBinding
14441
14852
  }
14442
14853
  ),
14443
- /* @__PURE__ */ jsx44(
14854
+ /* @__PURE__ */ jsx45(
14444
14855
  CameraPositionPanel,
14445
14856
  {
14446
14857
  selectedBinding,
@@ -14450,11 +14861,13 @@ function BindingBuilder({ licenseKey }) {
14450
14861
  formattedCameraState,
14451
14862
  savedCameraPosition,
14452
14863
  onSaveCameraPosition: saveCameraPositionForSelectedObject,
14864
+ onGoToSavedCamera: goToSavedCameraPosition,
14865
+ onClearSavedCamera: clearSavedCameraPosition,
14453
14866
  onResetView: handleResetView,
14454
14867
  onFitScene: handleFitScene
14455
14868
  }
14456
14869
  ),
14457
- /* @__PURE__ */ jsx44(
14870
+ /* @__PURE__ */ jsx45(
14458
14871
  JsonPanel,
14459
14872
  {
14460
14873
  title: "Metrics",
@@ -14468,7 +14881,7 @@ function BindingBuilder({ licenseKey }) {
14468
14881
  onCommit: () => applyJsonDraft("metrics", metricsDraft, setMetricsError)
14469
14882
  }
14470
14883
  ),
14471
- /* @__PURE__ */ jsx44(
14884
+ /* @__PURE__ */ jsx45(
14472
14885
  JsonPanel,
14473
14886
  {
14474
14887
  title: "Metadata",
@@ -14487,9 +14900,9 @@ function BindingBuilder({ licenseKey }) {
14487
14900
  }
14488
14901
  )
14489
14902
  ] }) }),
14490
- activeEditorTab === "object" && /* @__PURE__ */ jsxs39("div", { className: "bb-toolbar", children: [
14491
- /* @__PURE__ */ jsxs39("div", { className: "bb-toolbarGroup", children: [
14492
- /* @__PURE__ */ jsx44(
14903
+ activeEditorTab === "object" && /* @__PURE__ */ jsxs40("div", { className: "bb-toolbar", children: [
14904
+ /* @__PURE__ */ jsxs40("div", { className: "bb-toolbarGroup", children: [
14905
+ /* @__PURE__ */ jsx45(
14493
14906
  "button",
14494
14907
  {
14495
14908
  type: "button",
@@ -14497,10 +14910,10 @@ function BindingBuilder({ licenseKey }) {
14497
14910
  disabled: !bindingsHistory.canUndo,
14498
14911
  onClick: bindingsHistory.undo,
14499
14912
  title: "Undo (Cmd/Ctrl+Z)",
14500
- children: /* @__PURE__ */ jsx44(Undo2, { size: 16 })
14913
+ children: /* @__PURE__ */ jsx45(Undo2, { size: 16 })
14501
14914
  }
14502
14915
  ),
14503
- /* @__PURE__ */ jsx44(
14916
+ /* @__PURE__ */ jsx45(
14504
14917
  "button",
14505
14918
  {
14506
14919
  type: "button",
@@ -14508,10 +14921,10 @@ function BindingBuilder({ licenseKey }) {
14508
14921
  disabled: !nodeKeys.length,
14509
14922
  onClick: () => openResetDialog("bindings"),
14510
14923
  title: "Reset object bindings to default",
14511
- children: /* @__PURE__ */ jsx44(ListRestart, { size: 16 })
14924
+ children: /* @__PURE__ */ jsx45(ListRestart, { size: 16 })
14512
14925
  }
14513
14926
  ),
14514
- /* @__PURE__ */ jsx44(
14927
+ /* @__PURE__ */ jsx45(
14515
14928
  "button",
14516
14929
  {
14517
14930
  type: "button",
@@ -14519,12 +14932,12 @@ function BindingBuilder({ licenseKey }) {
14519
14932
  disabled: !bindingsHistory.canRedo,
14520
14933
  onClick: bindingsHistory.redo,
14521
14934
  title: "Redo (Cmd/Ctrl+Shift+Z)",
14522
- children: /* @__PURE__ */ jsx44(Redo2, { size: 16 })
14935
+ children: /* @__PURE__ */ jsx45(Redo2, { size: 16 })
14523
14936
  }
14524
14937
  )
14525
14938
  ] }),
14526
- /* @__PURE__ */ jsx44("div", { className: "bb-toolbarDivider" }),
14527
- /* @__PURE__ */ jsxs39(
14939
+ /* @__PURE__ */ jsx45("div", { className: "bb-toolbarDivider" }),
14940
+ /* @__PURE__ */ jsxs40(
14528
14941
  "button",
14529
14942
  {
14530
14943
  type: "button",
@@ -14533,13 +14946,13 @@ function BindingBuilder({ licenseKey }) {
14533
14946
  onClick: openBindingsImportPicker,
14534
14947
  title: "Re-apply a previously exported objectBindings.json onto this model",
14535
14948
  children: [
14536
- /* @__PURE__ */ jsx44(Upload, { size: 14 }),
14949
+ /* @__PURE__ */ jsx45(Upload, { size: 14 }),
14537
14950
  "Import"
14538
14951
  ]
14539
14952
  }
14540
14953
  ),
14541
- /* @__PURE__ */ jsx44("div", { className: "bb-toolbarDivider" }),
14542
- /* @__PURE__ */ jsx44(
14954
+ /* @__PURE__ */ jsx45("div", { className: "bb-toolbarDivider" }),
14955
+ /* @__PURE__ */ jsx45(
14543
14956
  ToolbarExportMenu,
14544
14957
  {
14545
14958
  disabled: !nodeKeys.length || isExporting,
@@ -14549,9 +14962,9 @@ function BindingBuilder({ licenseKey }) {
14549
14962
  }
14550
14963
  )
14551
14964
  ] }),
14552
- activeEditorTab === "scene" && /* @__PURE__ */ jsxs39("div", { className: "bb-toolbar", children: [
14553
- /* @__PURE__ */ jsxs39("div", { className: "bb-toolbarGroup", children: [
14554
- /* @__PURE__ */ jsx44(
14965
+ activeEditorTab === "scene" && /* @__PURE__ */ jsxs40("div", { className: "bb-toolbar", children: [
14966
+ /* @__PURE__ */ jsxs40("div", { className: "bb-toolbarGroup", children: [
14967
+ /* @__PURE__ */ jsx45(
14555
14968
  "button",
14556
14969
  {
14557
14970
  type: "button",
@@ -14559,20 +14972,20 @@ function BindingBuilder({ licenseKey }) {
14559
14972
  disabled: !sceneConfigHistory.canUndo,
14560
14973
  onClick: sceneConfigHistory.undo,
14561
14974
  title: "Undo (Cmd/Ctrl+Z)",
14562
- children: /* @__PURE__ */ jsx44(Undo2, { size: 16 })
14975
+ children: /* @__PURE__ */ jsx45(Undo2, { size: 16 })
14563
14976
  }
14564
14977
  ),
14565
- /* @__PURE__ */ jsx44(
14978
+ /* @__PURE__ */ jsx45(
14566
14979
  "button",
14567
14980
  {
14568
14981
  type: "button",
14569
14982
  className: "bb-iconButton",
14570
14983
  onClick: () => openResetDialog("scene"),
14571
14984
  title: "Reset scene config to default",
14572
- children: /* @__PURE__ */ jsx44(ListRestart, { size: 16 })
14985
+ children: /* @__PURE__ */ jsx45(ListRestart, { size: 16 })
14573
14986
  }
14574
14987
  ),
14575
- /* @__PURE__ */ jsx44(
14988
+ /* @__PURE__ */ jsx45(
14576
14989
  "button",
14577
14990
  {
14578
14991
  type: "button",
@@ -14580,12 +14993,12 @@ function BindingBuilder({ licenseKey }) {
14580
14993
  disabled: !sceneConfigHistory.canRedo,
14581
14994
  onClick: sceneConfigHistory.redo,
14582
14995
  title: "Redo (Cmd/Ctrl+Shift+Z)",
14583
- children: /* @__PURE__ */ jsx44(Redo2, { size: 16 })
14996
+ children: /* @__PURE__ */ jsx45(Redo2, { size: 16 })
14584
14997
  }
14585
14998
  )
14586
14999
  ] }),
14587
- /* @__PURE__ */ jsx44("div", { className: "bb-toolbarDivider" }),
14588
- /* @__PURE__ */ jsxs39(
15000
+ /* @__PURE__ */ jsx45("div", { className: "bb-toolbarDivider" }),
15001
+ /* @__PURE__ */ jsxs40(
14589
15002
  "button",
14590
15003
  {
14591
15004
  type: "button",
@@ -14594,13 +15007,13 @@ function BindingBuilder({ licenseKey }) {
14594
15007
  onClick: openSceneConfigImportPicker,
14595
15008
  title: "Re-apply a previously exported sceneConfig.json",
14596
15009
  children: [
14597
- /* @__PURE__ */ jsx44(Upload, { size: 14 }),
15010
+ /* @__PURE__ */ jsx45(Upload, { size: 14 }),
14598
15011
  "Import"
14599
15012
  ]
14600
15013
  }
14601
15014
  ),
14602
- /* @__PURE__ */ jsx44("div", { className: "bb-toolbarDivider" }),
14603
- /* @__PURE__ */ jsx44(
15015
+ /* @__PURE__ */ jsx45("div", { className: "bb-toolbarDivider" }),
15016
+ /* @__PURE__ */ jsx45(
14604
15017
  ToolbarExportMenu,
14605
15018
  {
14606
15019
  disabled: !nodeKeys.length || loading,
@@ -14621,16 +15034,16 @@ function BindingBuilder({ licenseKey }) {
14621
15034
  ]
14622
15035
  }
14623
15036
  ),
14624
- /* @__PURE__ */ jsxs39("main", { className: "bb-previewShell", children: [
14625
- /* @__PURE__ */ jsxs39("div", { className: "bb-previewHeader", children: [
14626
- /* @__PURE__ */ jsxs39("div", { className: "bb-previewToggles", children: [
14627
- /* @__PURE__ */ jsxs39(
15037
+ /* @__PURE__ */ jsxs40("main", { className: "bb-previewShell", children: [
15038
+ /* @__PURE__ */ jsxs40("div", { className: "bb-previewHeader", children: [
15039
+ /* @__PURE__ */ jsxs40("div", { className: "bb-previewToggles", children: [
15040
+ /* @__PURE__ */ jsxs40(
14628
15041
  "label",
14629
15042
  {
14630
15043
  className: "bb-checkbox bb-checkbox--compact",
14631
15044
  style: { color: "black" },
14632
15045
  children: [
14633
- /* @__PURE__ */ jsx44(
15046
+ /* @__PURE__ */ jsx45(
14634
15047
  "input",
14635
15048
  {
14636
15049
  type: "checkbox",
@@ -14642,13 +15055,13 @@ function BindingBuilder({ licenseKey }) {
14642
15055
  ]
14643
15056
  }
14644
15057
  ),
14645
- /* @__PURE__ */ jsxs39(
15058
+ /* @__PURE__ */ jsxs40(
14646
15059
  "label",
14647
15060
  {
14648
15061
  className: "bb-checkbox bb-checkbox--compact",
14649
15062
  style: { color: "black" },
14650
15063
  children: [
14651
- /* @__PURE__ */ jsx44(
15064
+ /* @__PURE__ */ jsx45(
14652
15065
  "input",
14653
15066
  {
14654
15067
  type: "checkbox",
@@ -14661,9 +15074,9 @@ function BindingBuilder({ licenseKey }) {
14661
15074
  }
14662
15075
  )
14663
15076
  ] }),
14664
- /* @__PURE__ */ jsxs39("div", { className: "bb-previewControls", children: [
14665
- /* @__PURE__ */ jsx44("div", { className: "bb-previewMeta", children: /* @__PURE__ */ jsx44("span", { children: selectedItem ? `Focused: ${selectedItem}` : "Select a node to inspect it" }) }),
14666
- modelUrl && /* @__PURE__ */ jsx44(
15077
+ /* @__PURE__ */ jsxs40("div", { className: "bb-previewControls", children: [
15078
+ /* @__PURE__ */ jsx45("div", { className: "bb-previewMeta", children: /* @__PURE__ */ jsx45("span", { children: selectedItem ? `Focused: ${selectedItem}` : "Select a node to inspect it" }) }),
15079
+ modelUrl && /* @__PURE__ */ jsx45(
14667
15080
  "button",
14668
15081
  {
14669
15082
  type: "button",
@@ -14674,8 +15087,8 @@ function BindingBuilder({ licenseKey }) {
14674
15087
  )
14675
15088
  ] })
14676
15089
  ] }),
14677
- /* @__PURE__ */ jsxs39("div", { className: "bb-preview", children: [
14678
- /* @__PURE__ */ jsx44(
15090
+ /* @__PURE__ */ jsxs40("div", { className: "bb-preview", children: [
15091
+ /* @__PURE__ */ jsx45(
14679
15092
  LoadingOverlay,
14680
15093
  {
14681
15094
  visible: loading || statusBannerVisible,
@@ -14684,7 +15097,7 @@ function BindingBuilder({ licenseKey }) {
14684
15097
  spinner: loading
14685
15098
  }
14686
15099
  ),
14687
- modelUrl ? /* @__PURE__ */ jsx44(
15100
+ modelUrl ? /* @__PURE__ */ jsx45(
14688
15101
  ModelViewer,
14689
15102
  {
14690
15103
  modelUrl,
@@ -14707,7 +15120,7 @@ function BindingBuilder({ licenseKey }) {
14707
15120
  panelHoveredObject: highlightOnHover ? panelHoveredBinding : null,
14708
15121
  showExplodeControls: true
14709
15122
  }
14710
- ) : /* @__PURE__ */ jsx44(
15123
+ ) : /* @__PURE__ */ jsx45(
14711
15124
  "div",
14712
15125
  {
14713
15126
  className: `bb-previewPlaceholder ${isPreviewDragActive ? "bb-previewPlaceholder--dragActive" : ""}`,
@@ -14715,9 +15128,9 @@ function BindingBuilder({ licenseKey }) {
14715
15128
  onDragOver: handlePreviewDragOver,
14716
15129
  onDragLeave: handlePreviewDragLeave,
14717
15130
  onDrop: handlePreviewDrop,
14718
- children: /* @__PURE__ */ jsxs39("div", { children: [
14719
- /* @__PURE__ */ jsx44("div", { children: "Upload a GLB/GLTF model or drag a .glb, .gltf, or .zip file here to generate editable object bindings." }),
14720
- /* @__PURE__ */ jsx44(
15131
+ children: /* @__PURE__ */ jsxs40("div", { children: [
15132
+ /* @__PURE__ */ jsx45("div", { children: "Upload a GLB/GLTF model or drag a .glb, .gltf, or .zip file here to generate editable object bindings." }),
15133
+ /* @__PURE__ */ jsx45(
14721
15134
  "button",
14722
15135
  {
14723
15136
  type: "button",
@@ -14732,7 +15145,7 @@ function BindingBuilder({ licenseKey }) {
14732
15145
  )
14733
15146
  ] })
14734
15147
  ] }),
14735
- /* @__PURE__ */ jsx44(
15148
+ /* @__PURE__ */ jsx45(
14736
15149
  SceneNodesPanel,
14737
15150
  {
14738
15151
  bindings,
@@ -14750,27 +15163,27 @@ function BindingBuilder({ licenseKey }) {
14750
15163
  }
14751
15164
  )
14752
15165
  ] }),
14753
- resetDialogOpen && /* @__PURE__ */ jsx44("div", { className: "bb-reset-overlay", onClick: closeResetDialog, children: /* @__PURE__ */ jsxs39("div", { className: "bb-reset-dialog", onClick: (e) => e.stopPropagation(), children: [
14754
- /* @__PURE__ */ jsxs39("div", { className: "bb-reset-header", children: [
14755
- /* @__PURE__ */ jsxs39("h3", { className: "bb-reset-title", children: [
14756
- /* @__PURE__ */ jsx44(AlertTriangle3, { size: 20, className: "bb-icon-warning" }),
15166
+ resetDialogOpen && /* @__PURE__ */ jsx45("div", { className: "bb-reset-overlay", onClick: closeResetDialog, children: /* @__PURE__ */ jsxs40("div", { className: "bb-reset-dialog", onClick: (e) => e.stopPropagation(), children: [
15167
+ /* @__PURE__ */ jsxs40("div", { className: "bb-reset-header", children: [
15168
+ /* @__PURE__ */ jsxs40("h3", { className: "bb-reset-title", children: [
15169
+ /* @__PURE__ */ jsx45(AlertTriangle3, { size: 20, className: "bb-icon-warning" }),
14757
15170
  "Confirm Reset"
14758
15171
  ] }),
14759
- /* @__PURE__ */ jsx44("button", { className: "bb-reset-close", onClick: closeResetDialog, children: /* @__PURE__ */ jsx44(X2, { size: 20 }) })
15172
+ /* @__PURE__ */ jsx45("button", { className: "bb-reset-close", onClick: closeResetDialog, children: /* @__PURE__ */ jsx45(X2, { size: 20 }) })
14760
15173
  ] }),
14761
- /* @__PURE__ */ jsxs39("div", { className: "bb-reset-body", children: [
14762
- /* @__PURE__ */ jsxs39("p", { className: "bb-reset-message", children: [
15174
+ /* @__PURE__ */ jsxs40("div", { className: "bb-reset-body", children: [
15175
+ /* @__PURE__ */ jsxs40("p", { className: "bb-reset-message", children: [
14763
15176
  "Are you sure you want to reset",
14764
15177
  " ",
14765
- /* @__PURE__ */ jsx44("strong", { children: resetTarget === "bindings" ? "Object Bindings" : "Scene Config" }),
15178
+ /* @__PURE__ */ jsx45("strong", { children: resetTarget === "bindings" ? "Object Bindings" : "Scene Config" }),
14766
15179
  "?"
14767
15180
  ] }),
14768
- /* @__PURE__ */ jsx44("p", { className: "bb-reset-details", children: resetTarget === "bindings" ? "This will remove all your custom changes to object properties, materials, actions, camera positions, metrics, and metadata." : "This will restore all scene settings (camera, lighting, environment, background, animations, annotations, etc.) to their default values." }),
14769
- /* @__PURE__ */ jsx44("p", { className: "bb-reset-warning", children: "This action cannot be undone!" })
15181
+ /* @__PURE__ */ jsx45("p", { className: "bb-reset-details", children: resetTarget === "bindings" ? "This will remove all your custom changes to object properties, materials, actions, camera positions, metrics, and metadata." : "This will restore all scene settings (camera, lighting, environment, background, animations, annotations, etc.) to their default values." }),
15182
+ /* @__PURE__ */ jsx45("p", { className: "bb-reset-warning", children: "This action cannot be undone!" })
14770
15183
  ] }),
14771
- /* @__PURE__ */ jsxs39("div", { className: "bb-reset-footer", children: [
14772
- /* @__PURE__ */ jsx44("button", { className: "bb-reset-btn", onClick: closeResetDialog, children: "Cancel" }),
14773
- /* @__PURE__ */ jsx44("button", { className: "bb-reset-btn-danger", onClick: executeReset, children: "Yes, Reset" })
15184
+ /* @__PURE__ */ jsxs40("div", { className: "bb-reset-footer", children: [
15185
+ /* @__PURE__ */ jsx45("button", { className: "bb-reset-btn", onClick: closeResetDialog, children: "Cancel" }),
15186
+ /* @__PURE__ */ jsx45("button", { className: "bb-reset-btn-danger", onClick: executeReset, children: "Yes, Reset" })
14774
15187
  ] })
14775
15188
  ] }) })
14776
15189
  ] });
@@ -14778,21 +15191,21 @@ function BindingBuilder({ licenseKey }) {
14778
15191
 
14779
15192
  // src/components/SimpleModelViewer/SimpleModelViewer.tsx
14780
15193
  import {
14781
- useRef as useRef17,
15194
+ useRef as useRef18,
14782
15195
  useState as useState25,
14783
15196
  useCallback as useCallback9,
14784
- useEffect as useEffect19,
14785
- useMemo as useMemo16,
15197
+ useEffect as useEffect20,
15198
+ useMemo as useMemo17,
14786
15199
  Component as Component3,
14787
15200
  Suspense as Suspense3
14788
15201
  } from "react";
14789
15202
  import { CameraControls as CameraControls2, Environment as Environment2 } from "@react-three/drei";
14790
- import { useFrame as useFrame8, useThree as useThree10 } from "@react-three/fiber";
15203
+ import { useFrame as useFrame9, useThree as useThree11 } from "@react-three/fiber";
14791
15204
  import { Canvas as Canvas3 } from "@react-three/fiber";
14792
15205
  import {
14793
- Box3 as Box33,
15206
+ Box3 as Box34,
14794
15207
  PerspectiveCamera as PerspectiveCamera4,
14795
- Vector3 as Vector38
15208
+ Vector3 as Vector39
14796
15209
  } from "three";
14797
15210
  import { SkeletonUtils as SkeletonUtils2 } from "three-stdlib";
14798
15211
 
@@ -14805,11 +15218,11 @@ import {
14805
15218
  Check,
14806
15219
  Settings,
14807
15220
  ChevronDown as ChevronDown6,
14808
- Boxes as Boxes3,
14809
- Trash2 as Trash24,
15221
+ Boxes as Boxes2,
15222
+ Trash2 as Trash26,
14810
15223
  Upload as Upload2
14811
15224
  } from "lucide-react";
14812
- import { Fragment as Fragment12, jsx as jsx45, jsxs as jsxs40 } from "react/jsx-runtime";
15225
+ import { Fragment as Fragment13, jsx as jsx46, jsxs as jsxs41 } from "react/jsx-runtime";
14813
15226
  var HIGHLIGHT2 = { color: "#93c5fd", intensity: 0.05 };
14814
15227
  var ENV_PRESETS = [
14815
15228
  "apartment",
@@ -14840,7 +15253,7 @@ function applyClippingPlanes(scene, controls) {
14840
15253
  if (!controls) return;
14841
15254
  const camera = controls.camera;
14842
15255
  if (!(camera instanceof PerspectiveCamera4)) return;
14843
- const size = new Box33().setFromObject(scene).getSize(new Vector38()).length();
15256
+ const size = new Box34().setFromObject(scene).getSize(new Vector39()).length();
14844
15257
  if (!Number.isFinite(size) || size <= 0) return;
14845
15258
  camera.near = size / 100;
14846
15259
  camera.far = size * 100;
@@ -14878,13 +15291,13 @@ function InnerModel({
14878
15291
  ktx2TranscoderPath: void 0,
14879
15292
  meshopt: true
14880
15293
  });
14881
- const invalidate = useThree10((state) => state.invalidate);
15294
+ const invalidate = useThree11((state) => state.invalidate);
14882
15295
  const [hoveredKey, setHoveredKey] = useState25(null);
14883
- const scene = useMemo16(
15296
+ const scene = useMemo17(
14884
15297
  () => SkeletonUtils2.clone(rawScene),
14885
15298
  [rawScene]
14886
15299
  );
14887
- const meshMap = useMemo16(() => {
15300
+ const meshMap = useMemo17(() => {
14888
15301
  const map = {};
14889
15302
  scene.traverse((obj) => {
14890
15303
  if (!isMeshNode2(obj)) return;
@@ -14893,7 +15306,7 @@ function InnerModel({
14893
15306
  });
14894
15307
  return map;
14895
15308
  }, [scene]);
14896
- const perKeyMaterials = useMemo16(() => {
15309
+ const perKeyMaterials = useMemo17(() => {
14897
15310
  const map = {};
14898
15311
  for (const [key, { ref }] of Object.entries(meshMap)) {
14899
15312
  const original = Array.isArray(ref.material) ? ref.material[0] : ref.material;
@@ -14909,14 +15322,14 @@ function InnerModel({
14909
15322
  }
14910
15323
  return map;
14911
15324
  }, [meshMap]);
14912
- useEffect19(
15325
+ useEffect20(
14913
15326
  () => () => {
14914
15327
  Object.values(perKeyMaterials).forEach((m) => m.dispose());
14915
15328
  },
14916
15329
  [perKeyMaterials]
14917
15330
  );
14918
- const notifiedUrl = useRef17(null);
14919
- useEffect19(() => {
15331
+ const notifiedUrl = useRef18(null);
15332
+ useEffect20(() => {
14920
15333
  if (notifiedUrl.current === url) return;
14921
15334
  notifiedUrl.current = url;
14922
15335
  scene.updateWorldMatrix(true, true);
@@ -14924,22 +15337,22 @@ function InnerModel({
14924
15337
  onMeshesReady(meshMap);
14925
15338
  onModelLoaded(scene);
14926
15339
  }, [url, scene, meshMap, onMeshesReady, onModelLoaded]);
14927
- useEffect19(() => {
15340
+ useEffect20(() => {
14928
15341
  for (const [key, { ref }] of Object.entries(meshMap)) {
14929
15342
  nodeRefsOut.current[key] = ref;
14930
15343
  }
14931
15344
  }, [meshMap, nodeRefsOut]);
14932
- useEffect19(() => {
15345
+ useEffect20(() => {
14933
15346
  if (!highlightOnHover && hoveredKey !== null) {
14934
15347
  setHoveredKey(null);
14935
15348
  }
14936
15349
  }, [highlightOnHover, hoveredKey]);
14937
- useEffect19(() => {
15350
+ useEffect20(() => {
14938
15351
  return () => {
14939
15352
  document.body.style.cursor = "default";
14940
15353
  };
14941
15354
  }, []);
14942
- useEffect19(() => {
15355
+ useEffect20(() => {
14943
15356
  for (const [key, { ref }] of Object.entries(meshMap)) {
14944
15357
  ref.visible = !hiddenKeys.has(key);
14945
15358
  const mat = perKeyMaterials[key];
@@ -14979,7 +15392,7 @@ function InnerModel({
14979
15392
  }
14980
15393
  return null;
14981
15394
  };
14982
- return /* @__PURE__ */ jsx45(
15395
+ return /* @__PURE__ */ jsx46(
14983
15396
  "primitive",
14984
15397
  {
14985
15398
  object: scene,
@@ -15012,15 +15425,15 @@ function CameraFitter({
15012
15425
  refitOnResize,
15013
15426
  onInitialFitComplete
15014
15427
  }) {
15015
- const fitted = useRef17(false);
15016
- const invalidate = useThree10((state) => state.invalidate);
15017
- const size = useThree10((state) => state.size);
15018
- const resizeTimerRef = useRef17(null);
15019
- useEffect19(() => {
15428
+ const fitted = useRef18(false);
15429
+ const invalidate = useThree11((state) => state.invalidate);
15430
+ const size = useThree11((state) => state.size);
15431
+ const resizeTimerRef = useRef18(null);
15432
+ useEffect20(() => {
15020
15433
  fitted.current = false;
15021
15434
  invalidate();
15022
15435
  }, [invalidate, scene]);
15023
- useFrame8(() => {
15436
+ useFrame9(() => {
15024
15437
  if (fitted.current) return;
15025
15438
  if (!scene || !controlsRef.current) {
15026
15439
  invalidate();
@@ -15036,7 +15449,7 @@ function CameraFitter({
15036
15449
  }).catch(() => {
15037
15450
  }).finally(onInitialFitComplete);
15038
15451
  });
15039
- useEffect19(() => {
15452
+ useEffect20(() => {
15040
15453
  if (!refitOnResize || !fitted.current || !scene || !controlsRef.current) {
15041
15454
  return;
15042
15455
  }
@@ -15062,9 +15475,9 @@ function CameraFitter({
15062
15475
  return null;
15063
15476
  }
15064
15477
  function ExposureController({ exposure }) {
15065
- const gl = useThree10((state) => state.gl);
15066
- const invalidate = useThree10((state) => state.invalidate);
15067
- useEffect19(() => {
15478
+ const gl = useThree11((state) => state.gl);
15479
+ const invalidate = useThree11((state) => state.invalidate);
15480
+ useEffect20(() => {
15068
15481
  gl.toneMappingExposure = exposure;
15069
15482
  invalidate();
15070
15483
  }, [gl, exposure, invalidate]);
@@ -15074,8 +15487,8 @@ function AutoRotator({
15074
15487
  enabled,
15075
15488
  controlsRef
15076
15489
  }) {
15077
- const invalidate = useThree10((state) => state.invalidate);
15078
- useFrame8((_, delta) => {
15490
+ const invalidate = useThree11((state) => state.invalidate);
15491
+ useFrame9((_, delta) => {
15079
15492
  if (!enabled || !controlsRef.current) return;
15080
15493
  controlsRef.current.rotate(delta * 0.2, 0, false);
15081
15494
  invalidate();
@@ -15090,13 +15503,13 @@ function ToggleRow2({
15090
15503
  checked,
15091
15504
  onChange
15092
15505
  }) {
15093
- return /* @__PURE__ */ jsxs40("div", { className: "env-row", children: [
15094
- /* @__PURE__ */ jsx45("span", { className: "env-row__label", children: label }),
15095
- /* @__PURE__ */ jsx45("div", { className: "env-row__value", children: /* @__PURE__ */ jsx45("button", { className: "env-checkbox-btn", onClick: () => onChange(!checked), children: /* @__PURE__ */ jsx45(
15506
+ return /* @__PURE__ */ jsxs41("div", { className: "env-row", children: [
15507
+ /* @__PURE__ */ jsx46("span", { className: "env-row__label", children: label }),
15508
+ /* @__PURE__ */ jsx46("div", { className: "env-row__value", children: /* @__PURE__ */ jsx46("button", { className: "env-checkbox-btn", onClick: () => onChange(!checked), children: /* @__PURE__ */ jsx46(
15096
15509
  "span",
15097
15510
  {
15098
15511
  className: `env-checkbox${checked ? " env-checkbox--checked" : ""}`,
15099
- children: checked && /* @__PURE__ */ jsx45(Check, { size: 9, strokeWidth: 2, color: "white" })
15512
+ children: checked && /* @__PURE__ */ jsx46(Check, { size: 9, strokeWidth: 2, color: "white" })
15100
15513
  }
15101
15514
  ) }) })
15102
15515
  ] });
@@ -15106,11 +15519,11 @@ function ColorRow2({
15106
15519
  value,
15107
15520
  onChange
15108
15521
  }) {
15109
- return /* @__PURE__ */ jsxs40("div", { className: "env-row", children: [
15110
- /* @__PURE__ */ jsx45("span", { className: "env-row__label", children: label }),
15111
- /* @__PURE__ */ jsx45("div", { className: "env-row__value", children: /* @__PURE__ */ jsxs40("div", { className: "env-color-swatch", style: { background: value }, children: [
15112
- /* @__PURE__ */ jsx45("span", { className: "env-color-swatch__hex", children: value }),
15113
- /* @__PURE__ */ jsx45(
15522
+ return /* @__PURE__ */ jsxs41("div", { className: "env-row", children: [
15523
+ /* @__PURE__ */ jsx46("span", { className: "env-row__label", children: label }),
15524
+ /* @__PURE__ */ jsx46("div", { className: "env-row__value", children: /* @__PURE__ */ jsxs41("div", { className: "env-color-swatch", style: { background: value }, children: [
15525
+ /* @__PURE__ */ jsx46("span", { className: "env-color-swatch__hex", children: value }),
15526
+ /* @__PURE__ */ jsx46(
15114
15527
  "input",
15115
15528
  {
15116
15529
  type: "color",
@@ -15130,10 +15543,10 @@ function SliderRow2({
15130
15543
  step,
15131
15544
  onChange
15132
15545
  }) {
15133
- return /* @__PURE__ */ jsxs40("div", { className: "env-row", children: [
15134
- /* @__PURE__ */ jsx45("span", { className: "env-row__label", children: label }),
15135
- /* @__PURE__ */ jsxs40("div", { className: "env-row__value env-row__value--slider", children: [
15136
- /* @__PURE__ */ jsx45(
15546
+ return /* @__PURE__ */ jsxs41("div", { className: "env-row", children: [
15547
+ /* @__PURE__ */ jsx46("span", { className: "env-row__label", children: label }),
15548
+ /* @__PURE__ */ jsxs41("div", { className: "env-row__value env-row__value--slider", children: [
15549
+ /* @__PURE__ */ jsx46(
15137
15550
  "input",
15138
15551
  {
15139
15552
  type: "range",
@@ -15145,7 +15558,7 @@ function SliderRow2({
15145
15558
  onChange: (e) => onChange(Number(e.target.value))
15146
15559
  }
15147
15560
  ),
15148
- /* @__PURE__ */ jsx45("span", { className: "env-slider__value", children: value.toFixed(2) })
15561
+ /* @__PURE__ */ jsx46("span", { className: "env-slider__value", children: value.toFixed(2) })
15149
15562
  ] })
15150
15563
  ] });
15151
15564
  }
@@ -15155,15 +15568,15 @@ function SelectRow2({
15155
15568
  options,
15156
15569
  onChange
15157
15570
  }) {
15158
- return /* @__PURE__ */ jsxs40("div", { className: "env-row", children: [
15159
- /* @__PURE__ */ jsx45("span", { className: "env-row__label", children: label }),
15160
- /* @__PURE__ */ jsx45("div", { className: "env-row__value", children: /* @__PURE__ */ jsx45(
15571
+ return /* @__PURE__ */ jsxs41("div", { className: "env-row", children: [
15572
+ /* @__PURE__ */ jsx46("span", { className: "env-row__label", children: label }),
15573
+ /* @__PURE__ */ jsx46("div", { className: "env-row__value", children: /* @__PURE__ */ jsx46(
15161
15574
  "select",
15162
15575
  {
15163
15576
  className: "env-select",
15164
15577
  value,
15165
15578
  onChange: (e) => onChange(e.target.value),
15166
- children: options.map((option) => /* @__PURE__ */ jsx45("option", { value: option, children: option }, option))
15579
+ children: options.map((option) => /* @__PURE__ */ jsx46("option", { value: option, children: option }, option))
15167
15580
  }
15168
15581
  ) })
15169
15582
  ] });
@@ -15173,14 +15586,14 @@ function EnvironmentSection({
15173
15586
  children
15174
15587
  }) {
15175
15588
  const [collapsed, setCollapsed] = useState25(false);
15176
- return /* @__PURE__ */ jsxs40("div", { className: "env-section", children: [
15177
- /* @__PURE__ */ jsxs40(
15589
+ return /* @__PURE__ */ jsxs41("div", { className: "env-section", children: [
15590
+ /* @__PURE__ */ jsxs41(
15178
15591
  "button",
15179
15592
  {
15180
15593
  className: "env-section__header",
15181
15594
  onClick: () => setCollapsed((c) => !c),
15182
15595
  children: [
15183
- /* @__PURE__ */ jsx45(
15596
+ /* @__PURE__ */ jsx46(
15184
15597
  ChevronDown6,
15185
15598
  {
15186
15599
  size: 12,
@@ -15188,11 +15601,11 @@ function EnvironmentSection({
15188
15601
  className: `env-section__chevron${collapsed ? " env-section__chevron--collapsed" : ""}`
15189
15602
  }
15190
15603
  ),
15191
- /* @__PURE__ */ jsx45("span", { children: title })
15604
+ /* @__PURE__ */ jsx46("span", { children: title })
15192
15605
  ]
15193
15606
  }
15194
15607
  ),
15195
- !collapsed && /* @__PURE__ */ jsx45("div", { className: "env-section__body", children })
15608
+ !collapsed && /* @__PURE__ */ jsx46("div", { className: "env-section__body", children })
15196
15609
  ] });
15197
15610
  }
15198
15611
  function EnvironmentPanel({
@@ -15201,40 +15614,40 @@ function EnvironmentPanel({
15201
15614
  settings,
15202
15615
  onChange
15203
15616
  }) {
15204
- return /* @__PURE__ */ jsxs40(
15617
+ return /* @__PURE__ */ jsxs41(
15205
15618
  "div",
15206
15619
  {
15207
15620
  className: `left-panel-wrapper ${isOpen ? "left-panel-wrapper--open" : "left-panel-wrapper--closed"}`,
15208
15621
  children: [
15209
- !isOpen && /* @__PURE__ */ jsx45(
15622
+ !isOpen && /* @__PURE__ */ jsx46(
15210
15623
  "button",
15211
15624
  {
15212
15625
  className: "side-panel__open-btn side-panel__open-btn--left",
15213
15626
  onClick: () => onToggle(true),
15214
- children: /* @__PURE__ */ jsx45(Settings, { size: 20, strokeWidth: 1.8 })
15627
+ children: /* @__PURE__ */ jsx46(Settings, { size: 20, strokeWidth: 1.8 })
15215
15628
  }
15216
15629
  ),
15217
- /* @__PURE__ */ jsxs40(
15630
+ /* @__PURE__ */ jsxs41(
15218
15631
  "div",
15219
15632
  {
15220
15633
  className: `side-panel side-panel--left ${isOpen ? "side-panel--open-left" : "side-panel--closed-left"}`,
15221
15634
  "aria-hidden": !isOpen,
15222
15635
  children: [
15223
- /* @__PURE__ */ jsx45("div", { className: "side-panel__header", children: /* @__PURE__ */ jsxs40("div", { className: "side-panel__header-top", children: [
15224
- /* @__PURE__ */ jsx45("span", { className: "side-panel__eyebrow", children: "Scene Settings" }),
15225
- /* @__PURE__ */ jsx45(
15636
+ /* @__PURE__ */ jsx46("div", { className: "side-panel__header", children: /* @__PURE__ */ jsxs41("div", { className: "side-panel__header-top", children: [
15637
+ /* @__PURE__ */ jsx46("span", { className: "side-panel__eyebrow", children: "Scene Settings" }),
15638
+ /* @__PURE__ */ jsx46(
15226
15639
  "button",
15227
15640
  {
15228
15641
  className: "side-panel__close-btn",
15229
15642
  onClick: () => onToggle(false),
15230
15643
  title: "Close",
15231
- children: /* @__PURE__ */ jsx45(X3, { size: 12, strokeWidth: 1.6 })
15644
+ children: /* @__PURE__ */ jsx46(X3, { size: 12, strokeWidth: 1.6 })
15232
15645
  }
15233
15646
  )
15234
15647
  ] }) }),
15235
- /* @__PURE__ */ jsxs40("div", { className: "env-controls", children: [
15236
- /* @__PURE__ */ jsxs40(EnvironmentSection, { title: "Background", children: [
15237
- /* @__PURE__ */ jsx45(
15648
+ /* @__PURE__ */ jsxs41("div", { className: "env-controls", children: [
15649
+ /* @__PURE__ */ jsxs41(EnvironmentSection, { title: "Background", children: [
15650
+ /* @__PURE__ */ jsx46(
15238
15651
  ToggleRow2,
15239
15652
  {
15240
15653
  label: "Background",
@@ -15242,7 +15655,7 @@ function EnvironmentPanel({
15242
15655
  onChange: (v) => onChange("backgroundEnabled", v)
15243
15656
  }
15244
15657
  ),
15245
- /* @__PURE__ */ jsx45(
15658
+ /* @__PURE__ */ jsx46(
15246
15659
  ColorRow2,
15247
15660
  {
15248
15661
  label: "Background Color",
@@ -15250,7 +15663,7 @@ function EnvironmentPanel({
15250
15663
  onChange: (v) => onChange("backgroundColor", v)
15251
15664
  }
15252
15665
  ),
15253
- /* @__PURE__ */ jsx45(
15666
+ /* @__PURE__ */ jsx46(
15254
15667
  SelectRow2,
15255
15668
  {
15256
15669
  label: "Environment",
@@ -15259,7 +15672,7 @@ function EnvironmentPanel({
15259
15672
  onChange: (v) => onChange("envPreset", v)
15260
15673
  }
15261
15674
  ),
15262
- /* @__PURE__ */ jsx45(
15675
+ /* @__PURE__ */ jsx46(
15263
15676
  ToggleRow2,
15264
15677
  {
15265
15678
  label: "Auto Rotate",
@@ -15268,8 +15681,8 @@ function EnvironmentPanel({
15268
15681
  }
15269
15682
  )
15270
15683
  ] }),
15271
- /* @__PURE__ */ jsxs40(EnvironmentSection, { title: "Lighting", children: [
15272
- /* @__PURE__ */ jsx45(
15684
+ /* @__PURE__ */ jsxs41(EnvironmentSection, { title: "Lighting", children: [
15685
+ /* @__PURE__ */ jsx46(
15273
15686
  SliderRow2,
15274
15687
  {
15275
15688
  label: "Exposure",
@@ -15280,7 +15693,7 @@ function EnvironmentPanel({
15280
15693
  onChange: (v) => onChange("exposure", v)
15281
15694
  }
15282
15695
  ),
15283
- /* @__PURE__ */ jsx45(
15696
+ /* @__PURE__ */ jsx46(
15284
15697
  SliderRow2,
15285
15698
  {
15286
15699
  label: "Ambient Light Intensity",
@@ -15291,7 +15704,7 @@ function EnvironmentPanel({
15291
15704
  onChange: (v) => onChange("ambientIntensity", v)
15292
15705
  }
15293
15706
  ),
15294
- /* @__PURE__ */ jsx45(
15707
+ /* @__PURE__ */ jsx46(
15295
15708
  ColorRow2,
15296
15709
  {
15297
15710
  label: "Ambient Light Color",
@@ -15299,7 +15712,7 @@ function EnvironmentPanel({
15299
15712
  onChange: (v) => onChange("ambientColor", v)
15300
15713
  }
15301
15714
  ),
15302
- /* @__PURE__ */ jsx45(
15715
+ /* @__PURE__ */ jsx46(
15303
15716
  SliderRow2,
15304
15717
  {
15305
15718
  label: "Direct Light Intensity",
@@ -15310,7 +15723,7 @@ function EnvironmentPanel({
15310
15723
  onChange: (v) => onChange("directionalIntensity", v)
15311
15724
  }
15312
15725
  ),
15313
- /* @__PURE__ */ jsx45(
15726
+ /* @__PURE__ */ jsx46(
15314
15727
  ColorRow2,
15315
15728
  {
15316
15729
  label: "Direct Light Color",
@@ -15342,37 +15755,37 @@ function SceneObjectsPanel2({
15342
15755
  const entries = Object.values(meshKeys).filter(
15343
15756
  (m) => m.label.toLowerCase().includes(search.toLowerCase())
15344
15757
  );
15345
- return /* @__PURE__ */ jsxs40(
15758
+ return /* @__PURE__ */ jsxs41(
15346
15759
  "div",
15347
15760
  {
15348
15761
  className: `right-panel-wrapper ${isOpen ? "right-panel-wrapper--open" : "right-panel-wrapper--closed"}`,
15349
15762
  children: [
15350
- !isOpen && /* @__PURE__ */ jsx45("button", { className: "side-panel__open-btn", onClick: () => onToggle(true), children: /* @__PURE__ */ jsx45(Boxes3, { size: 20, strokeWidth: 1.8 }) }),
15351
- /* @__PURE__ */ jsxs40(
15763
+ !isOpen && /* @__PURE__ */ jsx46("button", { className: "side-panel__open-btn", onClick: () => onToggle(true), children: /* @__PURE__ */ jsx46(Boxes2, { size: 20, strokeWidth: 1.8 }) }),
15764
+ /* @__PURE__ */ jsxs41(
15352
15765
  "div",
15353
15766
  {
15354
15767
  className: `side-panel ${isOpen ? "side-panel--open" : "side-panel--closed"}`,
15355
15768
  "aria-hidden": !isOpen,
15356
15769
  children: [
15357
- /* @__PURE__ */ jsxs40("div", { className: "side-panel__header", children: [
15358
- /* @__PURE__ */ jsxs40("div", { className: "side-panel__header-top", children: [
15359
- /* @__PURE__ */ jsx45("span", { className: "side-panel__eyebrow", children: "SCENE OBJECTS" }),
15360
- /* @__PURE__ */ jsx45(
15770
+ /* @__PURE__ */ jsxs41("div", { className: "side-panel__header", children: [
15771
+ /* @__PURE__ */ jsxs41("div", { className: "side-panel__header-top", children: [
15772
+ /* @__PURE__ */ jsx46("span", { className: "side-panel__eyebrow", children: "SCENE OBJECTS" }),
15773
+ /* @__PURE__ */ jsx46(
15361
15774
  "button",
15362
15775
  {
15363
15776
  className: "side-panel__close-btn",
15364
15777
  onClick: () => onToggle(false),
15365
15778
  title: "Close",
15366
- children: /* @__PURE__ */ jsx45(X3, { size: 12, strokeWidth: 1.6 })
15779
+ children: /* @__PURE__ */ jsx46(X3, { size: 12, strokeWidth: 1.6 })
15367
15780
  }
15368
15781
  )
15369
15782
  ] }),
15370
- /* @__PURE__ */ jsxs40("p", { className: "side-panel__title", children: [
15783
+ /* @__PURE__ */ jsxs41("p", { className: "side-panel__title", children: [
15371
15784
  entries.length,
15372
15785
  " objects"
15373
15786
  ] })
15374
15787
  ] }),
15375
- /* @__PURE__ */ jsx45("div", { className: "side-panel__search-wrap", children: /* @__PURE__ */ jsx45(
15788
+ /* @__PURE__ */ jsx46("div", { className: "side-panel__search-wrap", children: /* @__PURE__ */ jsx46(
15376
15789
  "input",
15377
15790
  {
15378
15791
  className: "side-panel__search",
@@ -15382,11 +15795,11 @@ function SceneObjectsPanel2({
15382
15795
  onChange: (e) => setSearch(e.target.value)
15383
15796
  }
15384
15797
  ) }),
15385
- /* @__PURE__ */ jsx45("div", { className: "side-panel__list", children: entries.map(({ key, label }) => {
15798
+ /* @__PURE__ */ jsx46("div", { className: "side-panel__list", children: entries.map(({ key, label }) => {
15386
15799
  const isVisible = !hiddenKeys.has(key);
15387
15800
  const isSelected = selectedKey === key;
15388
15801
  const isHovered = rowHovered === key;
15389
- return /* @__PURE__ */ jsxs40(
15802
+ return /* @__PURE__ */ jsxs41(
15390
15803
  "div",
15391
15804
  {
15392
15805
  onClick: () => onFocus(key),
@@ -15404,14 +15817,14 @@ function SceneObjectsPanel2({
15404
15817
  isSelected ? "side-panel__row--selected" : ""
15405
15818
  ].filter(Boolean).join(" "),
15406
15819
  children: [
15407
- /* @__PURE__ */ jsx45(
15820
+ /* @__PURE__ */ jsx46(
15408
15821
  "span",
15409
15822
  {
15410
15823
  className: `side-panel__label${isHovered || isSelected ? " side-panel__label--hovered" : ""}`,
15411
15824
  children: label
15412
15825
  }
15413
15826
  ),
15414
- /* @__PURE__ */ jsx45(
15827
+ /* @__PURE__ */ jsx46(
15415
15828
  "button",
15416
15829
  {
15417
15830
  className: "side-panel__vis-btn",
@@ -15420,11 +15833,11 @@ function SceneObjectsPanel2({
15420
15833
  e.stopPropagation();
15421
15834
  onToggleVisibility(key);
15422
15835
  },
15423
- children: /* @__PURE__ */ jsx45(
15836
+ children: /* @__PURE__ */ jsx46(
15424
15837
  "span",
15425
15838
  {
15426
15839
  className: `side-panel__checkbox${isVisible ? " side-panel__checkbox--checked" : ""}`,
15427
- children: isVisible && /* @__PURE__ */ jsx45(Check, { size: 9, strokeWidth: 5, color: "white" })
15840
+ children: isVisible && /* @__PURE__ */ jsx46(Check, { size: 9, strokeWidth: 5, color: "white" })
15428
15841
  }
15429
15842
  )
15430
15843
  }
@@ -15462,11 +15875,11 @@ function SimpleModelViewer({
15462
15875
  onModelLoaded,
15463
15876
  onLoadError
15464
15877
  }) {
15465
- const cameraControlsRef = useRef17(null);
15466
- const nodeRefsOut = useRef17({});
15467
- const fileInputRef = useRef17(null);
15468
- const uploadedModelRef = useRef17(null);
15469
- const dragDepthRef = useRef17(0);
15878
+ const cameraControlsRef = useRef18(null);
15879
+ const nodeRefsOut = useRef18({});
15880
+ const fileInputRef = useRef18(null);
15881
+ const uploadedModelRef = useRef18(null);
15882
+ const dragDepthRef = useRef18(0);
15470
15883
  const [loadedScene, setLoadedScene] = useState25(null);
15471
15884
  const [meshKeys, setMeshKeys] = useState25({});
15472
15885
  const [hiddenKeys, setHiddenKeys] = useState25(/* @__PURE__ */ new Set());
@@ -15544,7 +15957,7 @@ function SimpleModelViewer({
15544
15957
  },
15545
15958
  [handleUploadedModel]
15546
15959
  );
15547
- useEffect19(() => {
15960
+ useEffect20(() => {
15548
15961
  return () => {
15549
15962
  revokeUploadedModelUrl();
15550
15963
  };
@@ -15555,10 +15968,10 @@ function SimpleModelViewer({
15555
15968
  },
15556
15969
  []
15557
15970
  );
15558
- useEffect19(() => {
15971
+ useEffect20(() => {
15559
15972
  setEnvSettings((prev) => ({ ...prev, backgroundColor }));
15560
15973
  }, [backgroundColor]);
15561
- useEffect19(() => {
15974
+ useEffect20(() => {
15562
15975
  resetModelState();
15563
15976
  }, [activeModelUrl, resetModelState]);
15564
15977
  const handleMeshesReady = useCallback9((meshes) => {
@@ -15658,7 +16071,7 @@ function SimpleModelViewer({
15658
16071
  const showModelLoader = showLoadingOverlay && (isResolvingUpload || Boolean(
15659
16072
  activeModelUrl && !modelLoadFailed && (!loadedScene || !initialFitComplete)
15660
16073
  ));
15661
- return /* @__PURE__ */ jsxs40(
16074
+ return /* @__PURE__ */ jsxs41(
15662
16075
  "div",
15663
16076
  {
15664
16077
  className: "smv-root",
@@ -15668,7 +16081,7 @@ function SimpleModelViewer({
15668
16081
  onDragLeave: handleDragLeave,
15669
16082
  onDrop: handleDrop,
15670
16083
  children: [
15671
- showSceneSettingsPanel && (modelUrl || activeModelUrl) && /* @__PURE__ */ jsx45(
16084
+ showSceneSettingsPanel && (modelUrl || activeModelUrl) && /* @__PURE__ */ jsx46(
15672
16085
  EnvironmentPanel,
15673
16086
  {
15674
16087
  isOpen: isEnvPanelOpen,
@@ -15677,9 +16090,9 @@ function SimpleModelViewer({
15677
16090
  onChange: handleEnvSettingChange
15678
16091
  }
15679
16092
  ),
15680
- /* @__PURE__ */ jsxs40("div", { className: "smv-canvasArea", children: [
15681
- enableModelUpload ? /* @__PURE__ */ jsxs40(Fragment12, { children: [
15682
- /* @__PURE__ */ jsx45(
16093
+ /* @__PURE__ */ jsxs41("div", { className: "smv-canvasArea", children: [
16094
+ enableModelUpload ? /* @__PURE__ */ jsxs41(Fragment13, { children: [
16095
+ /* @__PURE__ */ jsx46(
15683
16096
  "input",
15684
16097
  {
15685
16098
  ref: fileInputRef,
@@ -15692,38 +16105,38 @@ function SimpleModelViewer({
15692
16105
  }
15693
16106
  }
15694
16107
  ),
15695
- activeModelUrl ? /* @__PURE__ */ jsx45("div", { className: "smv-upload-toolbar", children: /* @__PURE__ */ jsxs40(Fragment12, { children: [
15696
- /* @__PURE__ */ jsx45("span", { className: "smv-upload-toolbar__name", children: uploadedModelName ?? "Uploaded model" }),
15697
- /* @__PURE__ */ jsxs40(
16108
+ activeModelUrl ? /* @__PURE__ */ jsx46("div", { className: "smv-upload-toolbar", children: /* @__PURE__ */ jsxs41(Fragment13, { children: [
16109
+ /* @__PURE__ */ jsx46("span", { className: "smv-upload-toolbar__name", children: uploadedModelName ?? "Uploaded model" }),
16110
+ /* @__PURE__ */ jsxs41(
15698
16111
  "button",
15699
16112
  {
15700
16113
  type: "button",
15701
16114
  className: "smv-upload-toolbar__button smv-upload-toolbar__button--ghost",
15702
16115
  onClick: clearUploadedModel,
15703
16116
  children: [
15704
- /* @__PURE__ */ jsx45(Trash24, { size: 14 }),
16117
+ /* @__PURE__ */ jsx46(Trash26, { size: 14 }),
15705
16118
  "Clear"
15706
16119
  ]
15707
16120
  }
15708
16121
  )
15709
16122
  ] }) }) : null
15710
16123
  ] }) : null,
15711
- activeModelUrl ? /* @__PURE__ */ jsx45(
16124
+ activeModelUrl ? /* @__PURE__ */ jsx46(
15712
16125
  Canvas3,
15713
16126
  {
15714
16127
  dpr: [1, 2],
15715
16128
  resize: { debounce: 0, scroll: false },
15716
16129
  style: { background: envSettings.backgroundColor },
15717
16130
  camera: { position: [0, 0, 5], fov: 45, near: 0.1, far: 1e3 },
15718
- children: /* @__PURE__ */ jsxs40(Suspense3, { fallback: null, children: [
15719
- /* @__PURE__ */ jsx45(
16131
+ children: /* @__PURE__ */ jsxs41(Suspense3, { fallback: null, children: [
16132
+ /* @__PURE__ */ jsx46(
15720
16133
  "ambientLight",
15721
16134
  {
15722
16135
  intensity: envSettings.ambientIntensity,
15723
16136
  color: envSettings.ambientColor
15724
16137
  }
15725
16138
  ),
15726
- /* @__PURE__ */ jsx45(
16139
+ /* @__PURE__ */ jsx46(
15727
16140
  "directionalLight",
15728
16141
  {
15729
16142
  position: [-4, 5, 7],
@@ -15732,27 +16145,27 @@ function SimpleModelViewer({
15732
16145
  castShadow: true
15733
16146
  }
15734
16147
  ),
15735
- /* @__PURE__ */ jsx45("directionalLight", { position: [-5, 5, 5], intensity: 0.5 }),
15736
- /* @__PURE__ */ jsx45(Suspense3, { fallback: null, children: /* @__PURE__ */ jsx45(
16148
+ /* @__PURE__ */ jsx46("directionalLight", { position: [-5, 5, 5], intensity: 0.5 }),
16149
+ /* @__PURE__ */ jsx46(Suspense3, { fallback: null, children: /* @__PURE__ */ jsx46(
15737
16150
  Environment2,
15738
16151
  {
15739
16152
  preset: envSettings.envPreset,
15740
16153
  background: envSettings.backgroundEnabled
15741
16154
  }
15742
16155
  ) }),
15743
- /* @__PURE__ */ jsx45(ExposureController, { exposure: envSettings.exposure }),
15744
- /* @__PURE__ */ jsx45(
16156
+ /* @__PURE__ */ jsx46(ExposureController, { exposure: envSettings.exposure }),
16157
+ /* @__PURE__ */ jsx46(
15745
16158
  AutoRotator,
15746
16159
  {
15747
16160
  enabled: envSettings.autoRotate,
15748
16161
  controlsRef: cameraControlsRef
15749
16162
  }
15750
16163
  ),
15751
- /* @__PURE__ */ jsx45(
16164
+ /* @__PURE__ */ jsx46(
15752
16165
  ModelErrorBoundary2,
15753
16166
  {
15754
16167
  onLoadError: handleLoadError,
15755
- children: /* @__PURE__ */ jsx45(Suspense3, { fallback: null, children: /* @__PURE__ */ jsx45(
16168
+ children: /* @__PURE__ */ jsx46(Suspense3, { fallback: null, children: /* @__PURE__ */ jsx46(
15756
16169
  InnerModel,
15757
16170
  {
15758
16171
  url: activeModelUrl,
@@ -15769,7 +16182,7 @@ function SimpleModelViewer({
15769
16182
  },
15770
16183
  activeModelUrl
15771
16184
  ),
15772
- /* @__PURE__ */ jsx45(
16185
+ /* @__PURE__ */ jsx46(
15773
16186
  CameraFitter,
15774
16187
  {
15775
16188
  scene: loadedScene,
@@ -15778,7 +16191,7 @@ function SimpleModelViewer({
15778
16191
  onInitialFitComplete: () => setInitialFitComplete(true)
15779
16192
  }
15780
16193
  ),
15781
- /* @__PURE__ */ jsx45(
16194
+ /* @__PURE__ */ jsx46(
15782
16195
  CameraControls2,
15783
16196
  {
15784
16197
  ref: cameraControlsRef,
@@ -15788,31 +16201,31 @@ function SimpleModelViewer({
15788
16201
  )
15789
16202
  ] })
15790
16203
  }
15791
- ) : /* @__PURE__ */ jsx45(
16204
+ ) : /* @__PURE__ */ jsx46(
15792
16205
  "div",
15793
16206
  {
15794
16207
  className: "smv-upload-empty-state",
15795
16208
  style: { background: envSettings.backgroundColor },
15796
- children: /* @__PURE__ */ jsxs40("div", { className: "smv-upload-empty-state__card", children: [
15797
- /* @__PURE__ */ jsx45("div", { className: "smv-upload-empty-state__icon", children: /* @__PURE__ */ jsx45(Upload2, { size: 24 }) }),
15798
- /* @__PURE__ */ jsxs40("p", { className: "smv-upload-empty-state__body", children: [
16209
+ children: /* @__PURE__ */ jsxs41("div", { className: "smv-upload-empty-state__card", children: [
16210
+ /* @__PURE__ */ jsx46("div", { className: "smv-upload-empty-state__icon", children: /* @__PURE__ */ jsx46(Upload2, { size: 24 }) }),
16211
+ /* @__PURE__ */ jsxs41("p", { className: "smv-upload-empty-state__body", children: [
15799
16212
  "Drag and drop ",
15800
- /* @__PURE__ */ jsx45("code", { children: ".glb" }),
16213
+ /* @__PURE__ */ jsx46("code", { children: ".glb" }),
15801
16214
  ", ",
15802
- /* @__PURE__ */ jsx45("code", { children: ".gltf" }),
16215
+ /* @__PURE__ */ jsx46("code", { children: ".gltf" }),
15803
16216
  ", or",
15804
16217
  " ",
15805
- /* @__PURE__ */ jsx45("code", { children: ".zip" }),
16218
+ /* @__PURE__ */ jsx46("code", { children: ".zip" }),
15806
16219
  " file here"
15807
16220
  ] }),
15808
- /* @__PURE__ */ jsxs40(
16221
+ /* @__PURE__ */ jsxs41(
15809
16222
  "button",
15810
16223
  {
15811
16224
  type: "button",
15812
16225
  className: "smv-upload-empty-state__button",
15813
16226
  onClick: () => fileInputRef.current?.click(),
15814
16227
  children: [
15815
- /* @__PURE__ */ jsx45(Upload2, { size: 15 }),
16228
+ /* @__PURE__ */ jsx46(Upload2, { size: 15 }),
15816
16229
  "Choose model"
15817
16230
  ]
15818
16231
  }
@@ -15820,19 +16233,19 @@ function SimpleModelViewer({
15820
16233
  ] })
15821
16234
  }
15822
16235
  ),
15823
- /* @__PURE__ */ jsx45(
16236
+ /* @__PURE__ */ jsx46(
15824
16237
  LoadingOverlay,
15825
16238
  {
15826
16239
  visible: showModelLoader,
15827
16240
  message: isResolvingUpload ? "Preparing model..." : "Loading model..."
15828
16241
  }
15829
16242
  ),
15830
- enableModelUpload && isDragActive ? /* @__PURE__ */ jsx45("div", { className: "smv-upload-drop-overlay", children: /* @__PURE__ */ jsxs40("div", { className: "smv-upload-drop-overlay__card", children: [
15831
- /* @__PURE__ */ jsx45(Upload2, { size: 20 }),
15832
- /* @__PURE__ */ jsx45("span", { children: "Release to load your model" })
16243
+ enableModelUpload && isDragActive ? /* @__PURE__ */ jsx46("div", { className: "smv-upload-drop-overlay", children: /* @__PURE__ */ jsxs41("div", { className: "smv-upload-drop-overlay__card", children: [
16244
+ /* @__PURE__ */ jsx46(Upload2, { size: 20 }),
16245
+ /* @__PURE__ */ jsx46("span", { children: "Release to load your model" })
15833
16246
  ] }) }) : null
15834
16247
  ] }),
15835
- showSceneObjectsPanel && activeModelUrl && /* @__PURE__ */ jsx45(
16248
+ showSceneObjectsPanel && activeModelUrl && /* @__PURE__ */ jsx46(
15836
16249
  SceneObjectsPanel2,
15837
16250
  {
15838
16251
  meshKeys,
@@ -15874,9 +16287,9 @@ function useViewerSelection(initialSelectedObjectBinding = null) {
15874
16287
  }
15875
16288
 
15876
16289
  // src/hooks/useObjectBinding.ts
15877
- import { useMemo as useMemo17 } from "react";
16290
+ import { useMemo as useMemo18 } from "react";
15878
16291
  function useObjectBinding(objectBindings, objectId) {
15879
- return useMemo17(() => {
16292
+ return useMemo18(() => {
15880
16293
  if (!objectId) {
15881
16294
  return null;
15882
16295
  }
@@ -15891,16 +16304,16 @@ function useObjectBinding(objectBindings, objectId) {
15891
16304
  }
15892
16305
 
15893
16306
  // src/hooks/useObjectBindingIds.ts
15894
- import { useMemo as useMemo18 } from "react";
16307
+ import { useMemo as useMemo19 } from "react";
15895
16308
  function useObjectBindingIds(objectBindings) {
15896
- return useMemo18(
16309
+ return useMemo19(
15897
16310
  () => Object.values(objectBindings).map((binding) => binding.modelObjectId),
15898
16311
  [objectBindings]
15899
16312
  );
15900
16313
  }
15901
16314
 
15902
16315
  // src/hooks/useObjectVisibility.ts
15903
- import { useCallback as useCallback11, useMemo as useMemo19 } from "react";
16316
+ import { useCallback as useCallback11, useMemo as useMemo20 } from "react";
15904
16317
  function getHiddenObjects(objectBindings) {
15905
16318
  return Object.fromEntries(
15906
16319
  Object.entries(objectBindings).filter(([, binding]) => binding.visible === false).map(([bindingKey]) => [bindingKey, true])
@@ -15957,11 +16370,11 @@ function useObjectVisibility(objectBindings, onObjectBindingsChange) {
15957
16370
  )
15958
16371
  );
15959
16372
  }, [objectBindings, onObjectBindingsChange]);
15960
- const hiddenObjects = useMemo19(
16373
+ const hiddenObjects = useMemo20(
15961
16374
  () => getHiddenObjects(objectBindings),
15962
16375
  [objectBindings]
15963
16376
  );
15964
- const hiddenObjectIds = useMemo19(
16377
+ const hiddenObjectIds = useMemo20(
15965
16378
  () => Object.entries(hiddenObjects).map(
15966
16379
  ([bindingKey]) => objectBindings[bindingKey]?.id ?? bindingKey
15967
16380
  ),
@@ -16003,7 +16416,7 @@ function useViewerHover() {
16003
16416
 
16004
16417
  // src/hooks/useViewerModel.ts
16005
16418
  import { useCallback as useCallback13, useState as useState28 } from "react";
16006
- import { Box3 as Box34, Vector3 as Vector39 } from "three";
16419
+ import { Box3 as Box35, Vector3 as Vector310 } from "three";
16007
16420
  function toTuple2(vector) {
16008
16421
  return [vector.x, vector.y, vector.z];
16009
16422
  }
@@ -16014,11 +16427,11 @@ function useViewerModel() {
16014
16427
  const [bounds, setBounds] = useState28(null);
16015
16428
  const [objectCount, setObjectCount] = useState28(0);
16016
16429
  const handleModelLoaded = useCallback13((scene) => {
16017
- const box = new Box34().setFromObject(scene);
16430
+ const box = new Box35().setFromObject(scene);
16018
16431
  const min = box.min.clone();
16019
16432
  const max = box.max.clone();
16020
- const center = new Vector39();
16021
- const size = new Vector39();
16433
+ const center = new Vector310();
16434
+ const size = new Vector310();
16022
16435
  let nextObjectCount = 0;
16023
16436
  scene.traverse((child) => {
16024
16437
  if ("isMesh" in child && child.isMesh) {
@@ -16120,7 +16533,7 @@ function useViewerActions(objectBindings, options = {}) {
16120
16533
  }
16121
16534
 
16122
16535
  // src/hooks/useShareableViewerState.ts
16123
- import { useCallback as useCallback15, useEffect as useEffect20, useRef as useRef18, useState as useState29 } from "react";
16536
+ import { useCallback as useCallback15, useEffect as useEffect21, useRef as useRef19, useState as useState29 } from "react";
16124
16537
  function serializeState(cameraState, objectBindings, selectedObjectBinding) {
16125
16538
  const hidden = Object.values(objectBindings).filter((binding) => binding.visible === false).map((binding) => binding.id);
16126
16539
  return {
@@ -16140,7 +16553,7 @@ function useShareableViewerState({
16140
16553
  debounceMs = 400,
16141
16554
  autoSync = true
16142
16555
  }) {
16143
- const lastWrittenRawRef = useRef18(null);
16556
+ const lastWrittenRawRef = useRef19(null);
16144
16557
  const buildHash = useCallback15(() => {
16145
16558
  const state = serializeState(
16146
16559
  cameraState,
@@ -16162,7 +16575,7 @@ function useShareableViewerState({
16162
16575
  paramKey
16163
16576
  );
16164
16577
  }, [buildHash, paramKey]);
16165
- const latestStateRef = useRef18({
16578
+ const latestStateRef = useRef19({
16166
16579
  cameraState,
16167
16580
  objectBindings,
16168
16581
  selectedObjectBinding
@@ -16173,8 +16586,8 @@ function useShareableViewerState({
16173
16586
  selectedObjectBinding
16174
16587
  };
16175
16588
  const [restoreAttempted, setRestoreAttempted] = useState29(false);
16176
- const lastSyncedHashRef = useRef18(null);
16177
- useEffect20(() => {
16589
+ const lastSyncedHashRef = useRef19(null);
16590
+ useEffect21(() => {
16178
16591
  if (!autoSync || !restoreAttempted) return;
16179
16592
  const tick = () => {
16180
16593
  const current = latestStateRef.current;
@@ -16196,8 +16609,8 @@ function useShareableViewerState({
16196
16609
  const id = window.setInterval(tick, debounceMs);
16197
16610
  return () => window.clearInterval(id);
16198
16611
  }, [autoSync, restoreAttempted, debounceMs, paramKey]);
16199
- const lastRestoredRawRef = useRef18(null);
16200
- const restoringRawRef = useRef18(null);
16612
+ const lastRestoredRawRef = useRef19(null);
16613
+ const restoringRawRef = useRef19(null);
16201
16614
  const restoreFromUrl = useCallback15(async () => {
16202
16615
  setRestoreAttempted(true);
16203
16616
  const rawHash = window.location.hash.startsWith("#") ? window.location.hash.slice(1) : window.location.hash;
@@ -16251,7 +16664,7 @@ function useShareableViewerState({
16251
16664
  setCameraState,
16252
16665
  setSelectedObjectBinding
16253
16666
  ]);
16254
- useEffect20(() => {
16667
+ useEffect21(() => {
16255
16668
  const handleHashChange = () => {
16256
16669
  void restoreFromUrl();
16257
16670
  };