@liveroom-tech/react-immersive 2.0.0 → 2.1.1

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
@@ -5531,32 +5531,37 @@ function ObjectBindingDataPanel({
5531
5531
  /* @__PURE__ */ jsx15("hr", { className: "left-panel__divider" }),
5532
5532
  objectToRender.actions && objectToRender.actions.length > 0 && /* @__PURE__ */ jsxs11(Fragment6, { children: [
5533
5533
  /* @__PURE__ */ jsx15("div", { className: "left-panel__actions-label", children: "Actions" }),
5534
- /* @__PURE__ */ jsx15("div", { className: "left-panel__actions-list", children: objectToRender.actions.map((action) => {
5534
+ /* @__PURE__ */ jsx15("div", { className: "left-panel__actions-list", children: objectToRender.actions.map((action, index) => {
5535
5535
  const isActive = currentAction?.action.id === action.id && (currentAction.binding === objectToRender || currentAction.binding?.id === objectToRender.id || currentAction.binding?.modelObjectId === objectToRender.modelObjectId || currentAction.objectId === objectToRender.id || currentAction.objectId === objectToRender.modelObjectId);
5536
5536
  const extra = renderActionExtra?.(action.id);
5537
- return /* @__PURE__ */ jsxs11(Fragment5, { children: [
5538
- /* @__PURE__ */ jsx15(
5539
- "button",
5540
- {
5541
- onClick: (e) => onAction({
5542
- objectId: objectToRender.modelObjectId || objectToRender.id || "",
5543
- action,
5544
- binding: objectToRender,
5545
- screenX: e.clientX,
5546
- screenY: e.clientY
5547
- }),
5548
- className: [
5549
- "left-panel__action-btn",
5550
- `left-panel__action-btn--${action.id}`,
5551
- isActive ? "left-panel__action-btn--active" : ""
5552
- ].filter(Boolean).join(" "),
5553
- "data-action-id": action.id,
5554
- "data-label": action.label,
5555
- children: action.label
5556
- }
5557
- ),
5558
- /* @__PURE__ */ jsx15(ActionExtra, { content: extra })
5559
- ] }, action.id);
5537
+ return (
5538
+ // Keyed by position, not action.id: ids are author-supplied
5539
+ // and not guaranteed unique within a binding, and colliding
5540
+ // keys make React drop one of the buttons.
5541
+ /* @__PURE__ */ jsxs11(Fragment5, { children: [
5542
+ /* @__PURE__ */ jsx15(
5543
+ "button",
5544
+ {
5545
+ onClick: (e) => onAction({
5546
+ objectId: objectToRender.modelObjectId || objectToRender.id || "",
5547
+ action,
5548
+ binding: objectToRender,
5549
+ screenX: e.clientX,
5550
+ screenY: e.clientY
5551
+ }),
5552
+ className: [
5553
+ "left-panel__action-btn",
5554
+ `left-panel__action-btn--${action.id}`,
5555
+ isActive ? "left-panel__action-btn--active" : ""
5556
+ ].filter(Boolean).join(" "),
5557
+ "data-action-id": action.id,
5558
+ "data-label": action.label,
5559
+ children: action.label
5560
+ }
5561
+ ),
5562
+ /* @__PURE__ */ jsx15(ActionExtra, { content: extra })
5563
+ ] }, index)
5564
+ );
5560
5565
  }) })
5561
5566
  ] })
5562
5567
  ] })
@@ -8284,7 +8289,7 @@ function ModelViewer({
8284
8289
  }
8285
8290
 
8286
8291
  // src/components/BindingBuilder/BindingBuilder.tsx
8287
- import { useCallback as useCallback9, useEffect as useEffect20, useMemo as useMemo16, useRef as useRef18, useState as useState25 } from "react";
8292
+ import { useCallback as useCallback9, useEffect as useEffect21, useMemo as useMemo16, useRef as useRef18, useState as useState26 } from "react";
8288
8293
  import {
8289
8294
  Redo2,
8290
8295
  Undo2,
@@ -8295,7 +8300,7 @@ import {
8295
8300
  } from "lucide-react";
8296
8301
 
8297
8302
  // src/styles/BindingBuilder.css
8298
- 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');
8303
+ 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-linkButton {\n padding: 0;\n border: 0;\n background: none;\n color: inherit;\n font: inherit;\n font-weight: 700;\n text-decoration: underline;\n cursor: pointer;\n}\n.bb-linkButton:hover {\n color: #f8fafc;\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');
8299
8304
 
8300
8305
  // src/hooks/useViewerCamera.ts
8301
8306
  import { useCallback as useCallback5, useRef as useRef12, useState as useState11 } from "react";
@@ -11795,7 +11800,7 @@ function LightingTab({ config, onChange }) {
11795
11800
  {
11796
11801
  className: "se-segmentedControl",
11797
11802
  style: { gridAutoColumns: "1fr" },
11798
- children: ["shadowCatcher", "bakedAO"].map((m) => /* @__PURE__ */ jsx28(
11803
+ children: ["shadowCatcher"].map((m) => /* @__PURE__ */ jsx28(
11799
11804
  "button",
11800
11805
  {
11801
11806
  type: "button",
@@ -14538,7 +14543,98 @@ function BasicsPanel({
14538
14543
  }
14539
14544
 
14540
14545
  // src/components/BindingBuilder/components/ActionsPanel.tsx
14541
- import { jsx as jsx41, jsxs as jsxs36 } from "react/jsx-runtime";
14546
+ import { useEffect as useEffect20, useState as useState24 } from "react";
14547
+
14548
+ // src/components/BindingBuilder/utils/actionCatalog.ts
14549
+ var ACTION_CATALOG = [
14550
+ {
14551
+ id: "change-color",
14552
+ label: "Change Color",
14553
+ handling: "viewer",
14554
+ description: "Opens a color picker docked under the action and commits the result to style.material.baseColor."
14555
+ },
14556
+ {
14557
+ id: "change-material",
14558
+ label: "Change Material",
14559
+ handling: "viewer",
14560
+ description: "Opens the material picker so the viewer can swap the object's texture."
14561
+ },
14562
+ {
14563
+ id: "toggle-visibility",
14564
+ label: "Toggle Visibility",
14565
+ handling: "viewer",
14566
+ description: "Flips the binding's visible flag."
14567
+ },
14568
+ {
14569
+ id: "toggle-light",
14570
+ label: "Toggle Light",
14571
+ handling: "host",
14572
+ description: "Seeded on light bindings. Handle it in your onAction callback."
14573
+ },
14574
+ {
14575
+ id: "change-light-color",
14576
+ label: "Change Light Color",
14577
+ handling: "host",
14578
+ description: "Seeded on light bindings. Handle it in your onAction callback."
14579
+ },
14580
+ {
14581
+ id: "set-brightness",
14582
+ label: "Set Brightness",
14583
+ handling: "host",
14584
+ description: "Seeded on light bindings. Handle it in your onAction callback."
14585
+ }
14586
+ ];
14587
+ var CUSTOM_ACTION_ID = "__custom__";
14588
+ function findCatalogAction(id) {
14589
+ return ACTION_CATALOG.find((entry) => entry.id === id);
14590
+ }
14591
+ var VIEWER_ACTION_IDS = ACTION_CATALOG.filter(
14592
+ (entry) => entry.handling === "viewer"
14593
+ ).map((entry) => entry.id);
14594
+ var AUTO_LABELS = /* @__PURE__ */ new Set([
14595
+ "",
14596
+ "New Action",
14597
+ ...ACTION_CATALOG.map((entry) => entry.label)
14598
+ ]);
14599
+ function isAutoFilledLabel(label) {
14600
+ return AUTO_LABELS.has(label.trim());
14601
+ }
14602
+ function levenshtein(a, b) {
14603
+ let previous = Array.from({ length: b.length + 1 }, (_, index) => index);
14604
+ for (let i = 1; i <= a.length; i += 1) {
14605
+ const current = [i];
14606
+ for (let j = 1; j <= b.length; j += 1) {
14607
+ const substitution = previous[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1);
14608
+ current[j] = Math.min(current[j - 1] + 1, previous[j] + 1, substitution);
14609
+ }
14610
+ previous = current;
14611
+ }
14612
+ return previous[b.length];
14613
+ }
14614
+ function suggestActionId(id) {
14615
+ if (findCatalogAction(id)) return null;
14616
+ const candidate = id.trim().toLowerCase();
14617
+ if (!candidate) return null;
14618
+ const normalized = candidate.replace(/[^a-z0-9]/g, "");
14619
+ let best = null;
14620
+ for (const entry of ACTION_CATALOG) {
14621
+ const distance = Math.min(
14622
+ levenshtein(candidate, entry.id),
14623
+ levenshtein(normalized, entry.id.replace(/[^a-z0-9]/g, ""))
14624
+ );
14625
+ if (!best || distance < best.distance) {
14626
+ best = { id: entry.id, distance };
14627
+ }
14628
+ }
14629
+ return best && best.distance <= 2 ? best.id : null;
14630
+ }
14631
+
14632
+ // src/components/BindingBuilder/components/ActionsPanel.tsx
14633
+ import { Fragment as Fragment12, jsx as jsx41, jsxs as jsxs36 } from "react/jsx-runtime";
14634
+ var VIEWER_ACTIONS = ACTION_CATALOG.filter(
14635
+ (entry) => entry.handling === "viewer"
14636
+ );
14637
+ var HOST_ACTIONS = ACTION_CATALOG.filter((entry) => entry.handling === "host");
14542
14638
  function ActionsPanel({
14543
14639
  selectedItem,
14544
14640
  selectedBinding,
@@ -14546,85 +14642,183 @@ function ActionsPanel({
14546
14642
  onToggle,
14547
14643
  updateBinding
14548
14644
  }) {
14645
+ const [customRows, setCustomRows] = useState24(/* @__PURE__ */ new Set());
14646
+ useEffect20(() => {
14647
+ setCustomRows(/* @__PURE__ */ new Set());
14648
+ }, [selectedItem]);
14649
+ const actions = selectedBinding.actions ?? [];
14650
+ const updateAction = (index, patch) => updateBinding(selectedItem, (binding) => ({
14651
+ ...binding,
14652
+ actions: (binding.actions ?? []).map(
14653
+ (entry, entryIndex) => entryIndex === index ? patch(entry) : entry
14654
+ )
14655
+ }));
14656
+ const removeAction = (index) => {
14657
+ updateBinding(selectedItem, (binding) => ({
14658
+ ...binding,
14659
+ actions: (binding.actions ?? []).filter(
14660
+ (_, entryIndex) => entryIndex !== index
14661
+ )
14662
+ }));
14663
+ setCustomRows((previous) => {
14664
+ const next = /* @__PURE__ */ new Set();
14665
+ previous.forEach((row) => {
14666
+ if (row < index) next.add(row);
14667
+ else if (row > index) next.add(row - 1);
14668
+ });
14669
+ return next;
14670
+ });
14671
+ };
14672
+ const setCustomRow = (index, custom) => setCustomRows((previous) => {
14673
+ const next = new Set(previous);
14674
+ if (custom) next.add(index);
14675
+ else next.delete(index);
14676
+ return next;
14677
+ });
14549
14678
  return /* @__PURE__ */ jsxs36(BBSection, { title: "Actions", open, onToggle, children: [
14550
- /* @__PURE__ */ jsx41("div", { className: "bb-actionList", children: (selectedBinding.actions ?? []).map((action, index) => (
14551
- // Keyed by position, not action.id: the id is an editable field, so
14552
- // keying on it would remount the card on every keystroke and drop
14553
- // input focus.
14554
- /* @__PURE__ */ jsxs36("div", { className: "bb-actionCard", children: [
14555
- /* @__PURE__ */ jsxs36("div", { className: "bb-actionCardHeader", children: [
14556
- /* @__PURE__ */ jsxs36("div", { className: "bb-actionCardTitle", children: [
14557
- "Action ",
14558
- index + 1
14559
- ] }),
14560
- /* @__PURE__ */ jsx41("div", { className: "bb-actionCardMeta", children: action.type })
14561
- ] }),
14562
- /* @__PURE__ */ jsxs36("div", { className: "bb-actionGrid", children: [
14563
- /* @__PURE__ */ jsxs36("div", { className: "bb-field", children: [
14564
- /* @__PURE__ */ jsx41("label", { className: "bb-label", children: "Action ID" }),
14565
- /* @__PURE__ */ jsx41(
14566
- "input",
14567
- {
14568
- className: "bb-input",
14569
- value: action.id,
14570
- onChange: (event) => updateBinding(selectedItem, (binding) => ({
14571
- ...binding,
14572
- actions: (binding.actions ?? []).map(
14573
- (entry, entryIndex) => entryIndex === index ? { ...entry, id: event.target.value } : entry
14574
- )
14575
- }))
14576
- }
14577
- )
14679
+ /* @__PURE__ */ jsx41("div", { className: "bb-actionList", children: actions.map((action, index) => {
14680
+ const catalogEntry = findCatalogAction(action.id);
14681
+ const isCustom = customRows.has(index) || !catalogEntry;
14682
+ const trimmedId = action.id.trim();
14683
+ const suggestion = catalogEntry ? null : suggestActionId(action.id);
14684
+ const isDuplicate = trimmedId.length > 0 && actions.some(
14685
+ (other, otherIndex) => otherIndex !== index && other.id.trim() === trimmedId
14686
+ );
14687
+ return (
14688
+ // Keyed by position, not action.id: the id is an editable field, so
14689
+ // keying on it would remount the card on every keystroke and drop
14690
+ // input focus.
14691
+ /* @__PURE__ */ jsxs36("div", { className: "bb-actionCard", children: [
14692
+ /* @__PURE__ */ jsxs36("div", { className: "bb-actionCardHeader", children: [
14693
+ /* @__PURE__ */ jsxs36("div", { className: "bb-actionCardTitle", children: [
14694
+ "Action ",
14695
+ index + 1
14696
+ ] }),
14697
+ /* @__PURE__ */ jsx41("div", { className: "bb-actionCardMeta", children: action.type })
14578
14698
  ] }),
14579
- /* @__PURE__ */ jsxs36("div", { className: "bb-field", children: [
14580
- /* @__PURE__ */ jsx41("label", { className: "bb-label", children: "Label" }),
14581
- /* @__PURE__ */ jsx41(
14582
- "input",
14583
- {
14584
- className: "bb-input",
14585
- value: action.label,
14586
- onChange: (event) => updateBinding(selectedItem, (binding) => ({
14587
- ...binding,
14588
- actions: (binding.actions ?? []).map(
14589
- (entry, entryIndex) => entryIndex === index ? { ...entry, label: event.target.value } : entry
14590
- )
14591
- }))
14592
- }
14593
- )
14594
- ] })
14595
- ] }),
14596
- /* @__PURE__ */ jsx41("div", { className: "bb-actionFooter", children: /* @__PURE__ */ jsx41(
14597
- "button",
14598
- {
14599
- type: "button",
14600
- className: "bb-button bb-button--ghost bb-actionRemove",
14601
- onClick: () => updateBinding(selectedItem, (binding) => ({
14602
- ...binding,
14603
- actions: (binding.actions ?? []).filter(
14604
- (_, entryIndex) => entryIndex !== index
14699
+ /* @__PURE__ */ jsxs36("div", { className: "bb-actionGrid", children: [
14700
+ /* @__PURE__ */ jsxs36("div", { className: "bb-field", children: [
14701
+ /* @__PURE__ */ jsx41("label", { className: "bb-label", children: "Action ID" }),
14702
+ /* @__PURE__ */ jsxs36(
14703
+ "select",
14704
+ {
14705
+ className: "bb-select",
14706
+ value: isCustom ? CUSTOM_ACTION_ID : action.id,
14707
+ onChange: (event) => {
14708
+ const value = event.target.value;
14709
+ if (value === CUSTOM_ACTION_ID) {
14710
+ setCustomRow(index, true);
14711
+ return;
14712
+ }
14713
+ setCustomRow(index, false);
14714
+ const picked = findCatalogAction(value);
14715
+ updateAction(index, (current) => ({
14716
+ ...current,
14717
+ id: value,
14718
+ // Keep wording the author typed themselves.
14719
+ label: picked && isAutoFilledLabel(current.label) ? picked.label : current.label
14720
+ }));
14721
+ },
14722
+ children: [
14723
+ /* @__PURE__ */ jsx41("optgroup", { label: "Handled by the viewer", children: VIEWER_ACTIONS.map((entry) => /* @__PURE__ */ jsx41("option", { value: entry.id, children: entry.id }, entry.id)) }),
14724
+ /* @__PURE__ */ jsx41("optgroup", { label: "Handled by your app", children: HOST_ACTIONS.map((entry) => /* @__PURE__ */ jsx41("option", { value: entry.id, children: entry.id }, entry.id)) }),
14725
+ /* @__PURE__ */ jsx41("option", { value: CUSTOM_ACTION_ID, children: "Custom\u2026" })
14726
+ ]
14727
+ }
14605
14728
  )
14606
- })),
14607
- children: "Remove Action"
14608
- }
14609
- ) })
14610
- ] }, index)
14611
- )) }),
14729
+ ] }),
14730
+ isCustom ? /* @__PURE__ */ jsxs36("div", { className: "bb-field", children: [
14731
+ /* @__PURE__ */ jsx41("label", { className: "bb-label", children: "Custom action ID" }),
14732
+ /* @__PURE__ */ jsx41(
14733
+ "input",
14734
+ {
14735
+ className: "bb-input",
14736
+ value: action.id,
14737
+ placeholder: "open-datasheet",
14738
+ onChange: (event) => {
14739
+ setCustomRow(index, true);
14740
+ updateAction(index, (entry) => ({
14741
+ ...entry,
14742
+ id: event.target.value
14743
+ }));
14744
+ }
14745
+ }
14746
+ )
14747
+ ] }) : null,
14748
+ /* @__PURE__ */ jsxs36("div", { className: "bb-field", children: [
14749
+ /* @__PURE__ */ jsx41("label", { className: "bb-label", children: "Label" }),
14750
+ /* @__PURE__ */ jsx41(
14751
+ "input",
14752
+ {
14753
+ className: "bb-input",
14754
+ value: action.label,
14755
+ onChange: (event) => updateAction(index, (entry) => ({
14756
+ ...entry,
14757
+ label: event.target.value
14758
+ }))
14759
+ }
14760
+ )
14761
+ ] }),
14762
+ catalogEntry ? /* @__PURE__ */ jsx41("span", { className: "bb-note", children: catalogEntry.description }) : trimmedId.length === 0 ? /* @__PURE__ */ jsx41("span", { className: "bb-note", style: { color: "#fca5a5" }, children: "An action ID is required \u2014 the button renders but can never be identified in onAction." }) : /* @__PURE__ */ jsxs36("span", { className: "bb-note", style: { color: "#fcd34d" }, children: [
14763
+ "Unrecognised ID \u2014 the viewer has no built-in behaviour for it, so your app must handle it in onAction.",
14764
+ suggestion ? /* @__PURE__ */ jsxs36(Fragment12, { children: [
14765
+ " Did you mean ",
14766
+ /* @__PURE__ */ jsx41(
14767
+ "button",
14768
+ {
14769
+ type: "button",
14770
+ className: "bb-linkButton",
14771
+ onClick: () => {
14772
+ setCustomRow(index, false);
14773
+ const picked = findCatalogAction(suggestion);
14774
+ updateAction(index, (current) => ({
14775
+ ...current,
14776
+ id: suggestion,
14777
+ label: picked && isAutoFilledLabel(current.label) ? picked.label : current.label
14778
+ }));
14779
+ },
14780
+ children: suggestion
14781
+ }
14782
+ ),
14783
+ "?"
14784
+ ] }) : null
14785
+ ] }),
14786
+ isDuplicate ? /* @__PURE__ */ jsx41("span", { className: "bb-note", style: { color: "#fcd34d" }, children: "Another action on this binding uses the same ID \u2014 they will render as two identical buttons." }) : null
14787
+ ] }),
14788
+ /* @__PURE__ */ jsx41("div", { className: "bb-actionFooter", children: /* @__PURE__ */ jsx41(
14789
+ "button",
14790
+ {
14791
+ type: "button",
14792
+ className: "bb-button bb-button--ghost bb-actionRemove",
14793
+ onClick: () => removeAction(index),
14794
+ children: "Remove Action"
14795
+ }
14796
+ ) })
14797
+ ] }, index)
14798
+ );
14799
+ }) }),
14612
14800
  /* @__PURE__ */ jsx41("div", { style: { marginTop: 10 }, children: /* @__PURE__ */ jsx41(
14613
14801
  "button",
14614
14802
  {
14615
14803
  type: "button",
14616
14804
  className: "bb-button",
14617
- onClick: () => updateBinding(selectedItem, (binding) => ({
14618
- ...binding,
14619
- actions: [
14620
- ...binding.actions ?? [],
14621
- {
14622
- id: "new-action",
14623
- label: "New Action",
14624
- type: "command"
14625
- }
14626
- ]
14627
- })),
14805
+ onClick: () => {
14806
+ const unused = VIEWER_ACTIONS.find(
14807
+ (entry) => !actions.some((action) => action.id === entry.id)
14808
+ );
14809
+ updateBinding(selectedItem, (binding) => ({
14810
+ ...binding,
14811
+ actions: [
14812
+ ...binding.actions ?? [],
14813
+ {
14814
+ id: unused?.id ?? "",
14815
+ label: unused?.label ?? "New Action",
14816
+ type: "command"
14817
+ }
14818
+ ]
14819
+ }));
14820
+ if (!unused) setCustomRow(actions.length, true);
14821
+ },
14628
14822
  children: "Add Action"
14629
14823
  }
14630
14824
  ) })
@@ -14633,7 +14827,7 @@ function ActionsPanel({
14633
14827
 
14634
14828
  // src/components/BindingBuilder/components/CameraPositionPanel.tsx
14635
14829
  import { MapPin as MapPin3, Trash2 as Trash25 } from "lucide-react";
14636
- import { Fragment as Fragment12, jsx as jsx42, jsxs as jsxs37 } from "react/jsx-runtime";
14830
+ import { Fragment as Fragment13, jsx as jsx42, jsxs as jsxs37 } from "react/jsx-runtime";
14637
14831
  function CameraPositionPanel({
14638
14832
  selectedBinding,
14639
14833
  open,
@@ -14652,7 +14846,7 @@ function CameraPositionPanel({
14652
14846
  /* @__PURE__ */ jsx42("span", { className: "bb-cameraLiveBadge", children: "LIVE" }),
14653
14847
  /* @__PURE__ */ jsxs37("div", { className: "se-waypointBody", children: [
14654
14848
  /* @__PURE__ */ jsx42("div", { className: "bb-cameraWaypointLabel", children: "Current view" }),
14655
- cameraState ? /* @__PURE__ */ jsxs37(Fragment12, { children: [
14849
+ cameraState ? /* @__PURE__ */ jsxs37(Fragment13, { children: [
14656
14850
  /* @__PURE__ */ jsxs37("div", { className: "se-waypointCoords", children: [
14657
14851
  "pos ",
14658
14852
  formattedCameraState?.position
@@ -14738,7 +14932,7 @@ function CameraPositionPanel({
14738
14932
  }
14739
14933
 
14740
14934
  // src/components/BindingBuilder/components/SceneNodesPanel.tsx
14741
- import { useMemo as useMemo15, useState as useState24 } from "react";
14935
+ import { useMemo as useMemo15, useState as useState25 } from "react";
14742
14936
 
14743
14937
  // src/components/BindingBuilder/utils/bindingFactory.ts
14744
14938
  function slugify2(value) {
@@ -14818,7 +15012,7 @@ function collectNodeNames(scene) {
14818
15012
  }
14819
15013
 
14820
15014
  // src/components/BindingBuilder/components/SceneNodesPanel.tsx
14821
- import { Fragment as Fragment13, jsx as jsx43, jsxs as jsxs38 } from "react/jsx-runtime";
15015
+ import { Fragment as Fragment14, jsx as jsx43, jsxs as jsxs38 } from "react/jsx-runtime";
14822
15016
  function SceneNodesPanel({
14823
15017
  bindings,
14824
15018
  selectedItem,
@@ -14827,7 +15021,7 @@ function SceneNodesPanel({
14827
15021
  isOpen = true,
14828
15022
  onToggle
14829
15023
  }) {
14830
- const [search, setSearch] = useState24("");
15024
+ const [search, setSearch] = useState25("");
14831
15025
  const nodeKeys = useMemo15(() => Object.keys(bindings), [bindings]);
14832
15026
  const filteredNodeKeys = useMemo15(() => {
14833
15027
  const query = search.trim().toLowerCase();
@@ -14854,7 +15048,7 @@ function SceneNodesPanel({
14854
15048
  children: "\u2190"
14855
15049
  }
14856
15050
  ),
14857
- isOpen && /* @__PURE__ */ jsxs38(Fragment13, { children: [
15051
+ isOpen && /* @__PURE__ */ jsxs38(Fragment14, { children: [
14858
15052
  /* @__PURE__ */ jsxs38("div", { children: [
14859
15053
  /* @__PURE__ */ jsx43("div", { className: "bb-sectionKicker", children: "Scene Nodes" }),
14860
15054
  /* @__PURE__ */ jsxs38("div", { className: "bb-sectionHeading", children: [
@@ -15263,7 +15457,7 @@ function removeMaterialPanelFields(material2, panel) {
15263
15457
  }
15264
15458
 
15265
15459
  // src/components/BindingBuilder/BindingBuilder.tsx
15266
- import { Fragment as Fragment14, jsx as jsx45, jsxs as jsxs40 } from "react/jsx-runtime";
15460
+ import { Fragment as Fragment15, jsx as jsx45, jsxs as jsxs40 } from "react/jsx-runtime";
15267
15461
  var DEMO_MODEL_URL = "/room.glb";
15268
15462
  function isPlainObject(value) {
15269
15463
  return value !== null && typeof value === "object" && !Array.isArray(value);
@@ -15283,36 +15477,36 @@ function BindingBuilder({ licenseKey }) {
15283
15477
  const modelInputRef = useRef18(null);
15284
15478
  const bindingsInputRef = useRef18(null);
15285
15479
  const sceneConfigInputRef = useRef18(null);
15286
- const [modelUrl, setModelUrl] = useState25(null);
15287
- const [modelName, setModelName] = useState25("");
15288
- const [selectedItem, setSelectedItem] = useState25(null);
15480
+ const [modelUrl, setModelUrl] = useState26(null);
15481
+ const [modelName, setModelName] = useState26("");
15482
+ const [selectedItem, setSelectedItem] = useState26(null);
15289
15483
  const bindingsHistory = useUndoableState({});
15290
15484
  const bindings = bindingsHistory.value;
15291
15485
  const setBindings = bindingsHistory.setValue;
15292
- const [loading, setLoading] = useState25(false);
15293
- const [isExporting, setIsExporting] = useState25(false);
15294
- const [statusMessage, setStatusMessage] = useState25(
15486
+ const [loading, setLoading] = useState26(false);
15487
+ const [isExporting, setIsExporting] = useState26(false);
15488
+ const [statusMessage, setStatusMessage] = useState26(
15295
15489
  "Load the demo model or upload a GLB/GLTF model to generate bindings."
15296
15490
  );
15297
- const [statusTone, setStatusTone] = useState25("default");
15298
- const [statusBannerVisible, setStatusBannerVisible] = useState25(false);
15299
- const [metricsDraft, setMetricsDraft] = useState25("{}");
15300
- const [metadataDraft, setMetadataDraft] = useState25("{}");
15301
- const [metricsError, setMetricsError] = useState25(null);
15302
- const [metadataError, setMetadataError] = useState25(null);
15303
- const [isPreviewDragActive, setIsPreviewDragActive] = useState25(false);
15304
- const [isEditorCollapsed, setIsEditorCollapsed] = useState25(false);
15305
- const [isNodesCollapsed, setIsNodesCollapsed] = useState25(false);
15306
- const [openObjectSections, setOpenObjectSections] = useState25({
15491
+ const [statusTone, setStatusTone] = useState26("default");
15492
+ const [statusBannerVisible, setStatusBannerVisible] = useState26(false);
15493
+ const [metricsDraft, setMetricsDraft] = useState26("{}");
15494
+ const [metadataDraft, setMetadataDraft] = useState26("{}");
15495
+ const [metricsError, setMetricsError] = useState26(null);
15496
+ const [metadataError, setMetadataError] = useState26(null);
15497
+ const [isPreviewDragActive, setIsPreviewDragActive] = useState26(false);
15498
+ const [isEditorCollapsed, setIsEditorCollapsed] = useState26(false);
15499
+ const [isNodesCollapsed, setIsNodesCollapsed] = useState26(false);
15500
+ const [openObjectSections, setOpenObjectSections] = useState26({
15307
15501
  identity: true,
15308
15502
  basics: true,
15309
- material: true,
15503
+ material: false,
15310
15504
  actions: false,
15311
15505
  camera: false,
15312
15506
  metrics: false,
15313
15507
  metadata: false
15314
15508
  });
15315
- const [openMaterialPanels, setOpenMaterialPanels] = useState25({
15509
+ const [openMaterialPanels, setOpenMaterialPanels] = useState26({
15316
15510
  pbr: true,
15317
15511
  roughness: false,
15318
15512
  emission: false,
@@ -15328,24 +15522,24 @@ function BindingBuilder({ licenseKey }) {
15328
15522
  });
15329
15523
  const toggleObjectSection = (key) => setOpenObjectSections((prev) => ({ ...prev, [key]: !prev[key] }));
15330
15524
  const toggleMaterialPanel = (key) => setOpenMaterialPanels((prev) => ({ ...prev, [key]: !prev[key] }));
15331
- const [activeEditorTab, setActiveEditorTab] = useState25(
15525
+ const [activeEditorTab, setActiveEditorTab] = useState26(
15332
15526
  "object"
15333
15527
  );
15334
- const [activeSubTab, setActiveSubTab] = useState25("General");
15335
- const [pbrWorkflow, setPbrWorkflow] = useState25("metalness");
15336
- const [materialPanelEnabled, setMaterialPanelEnabled] = useState25(createMaterialPanelEnabledState());
15528
+ const [activeSubTab, setActiveSubTab] = useState26("General");
15529
+ const [pbrWorkflow, setPbrWorkflow] = useState26("metalness");
15530
+ const [materialPanelEnabled, setMaterialPanelEnabled] = useState26(createMaterialPanelEnabledState());
15337
15531
  const materialPanelItemRef = useRef18(null);
15338
- const [zoomOnSelected, setZoomOnSelected] = useState25(false);
15339
- const [highlightOnHover, setHighlightOnHover] = useState25(true);
15340
- const [resetDialogOpen, setResetDialogOpen] = useState25(false);
15341
- const [resetTarget, setResetTarget] = useState25(
15532
+ const [zoomOnSelected, setZoomOnSelected] = useState26(false);
15533
+ const [highlightOnHover, setHighlightOnHover] = useState26(true);
15534
+ const [resetDialogOpen, setResetDialogOpen] = useState26(false);
15535
+ const [resetTarget, setResetTarget] = useState26(
15342
15536
  null
15343
15537
  );
15344
15538
  const sceneConfigHistory = useUndoableState(DEFAULT_SCENE_CONFIG);
15345
15539
  const sceneConfig = sceneConfigHistory.value;
15346
15540
  const setSceneConfig = sceneConfigHistory.setValue;
15347
- const [activeAnnotation, setActiveAnnotation] = useState25(null);
15348
- const [panelHoveredBinding, setPanelHoveredBinding] = useState25(null);
15541
+ const [activeAnnotation, setActiveAnnotation] = useState26(null);
15542
+ const [panelHoveredBinding, setPanelHoveredBinding] = useState26(null);
15349
15543
  const license = useLicense(licenseKey);
15350
15544
  const tier = normalizeTier(license.tier);
15351
15545
  const bindingBuilderContextValue = useMemo16(
@@ -15421,7 +15615,7 @@ function BindingBuilder({ licenseKey }) {
15421
15615
  },
15422
15616
  []
15423
15617
  );
15424
- useEffect20(() => {
15618
+ useEffect21(() => {
15425
15619
  if (!selectedBinding) {
15426
15620
  setMetricsDraft("{}");
15427
15621
  setMetadataDraft("{}");
@@ -15434,7 +15628,7 @@ function BindingBuilder({ licenseKey }) {
15434
15628
  setMetricsError(null);
15435
15629
  setMetadataError(null);
15436
15630
  }, [selectedBinding]);
15437
- useEffect20(() => {
15631
+ useEffect21(() => {
15438
15632
  const derived = createMaterialPanelEnabledState(selectedMaterial);
15439
15633
  if (materialPanelItemRef.current !== selectedItem) {
15440
15634
  materialPanelItemRef.current = selectedItem;
@@ -15453,12 +15647,12 @@ function BindingBuilder({ licenseKey }) {
15453
15647
  return changed ? next : prev;
15454
15648
  });
15455
15649
  }, [selectedItem, selectedMaterial]);
15456
- useEffect20(() => {
15650
+ useEffect21(() => {
15457
15651
  return () => {
15458
15652
  uploadedModelRef.current?.cleanup();
15459
15653
  };
15460
15654
  }, []);
15461
- useEffect20(() => {
15655
+ useEffect21(() => {
15462
15656
  const handleKeyDown = (event) => {
15463
15657
  if (event.key.toLowerCase() !== "z" || !(event.metaKey || event.ctrlKey)) {
15464
15658
  return;
@@ -15479,7 +15673,7 @@ function BindingBuilder({ licenseKey }) {
15479
15673
  window.addEventListener("keydown", handleKeyDown);
15480
15674
  return () => window.removeEventListener("keydown", handleKeyDown);
15481
15675
  }, [activeEditorTab, bindingsHistory, sceneConfigHistory]);
15482
- useEffect20(() => {
15676
+ useEffect21(() => {
15483
15677
  if (loading || statusTone === "default" || statusTone === "success") return;
15484
15678
  setStatusBannerVisible(true);
15485
15679
  const timeout = window.setTimeout(
@@ -16059,7 +16253,7 @@ function BindingBuilder({ licenseKey }) {
16059
16253
  }
16060
16254
  )
16061
16255
  ] }),
16062
- !isEditorCollapsed && /* @__PURE__ */ jsxs40(Fragment14, { children: [
16256
+ !isEditorCollapsed && /* @__PURE__ */ jsxs40(Fragment15, { children: [
16063
16257
  /* @__PURE__ */ jsxs40("div", { className: "bb-tabRow", children: [
16064
16258
  /* @__PURE__ */ jsx45(
16065
16259
  "button",
@@ -16080,7 +16274,7 @@ function BindingBuilder({ licenseKey }) {
16080
16274
  }
16081
16275
  )
16082
16276
  ] }),
16083
- /* @__PURE__ */ jsx45("section", { className: "bb-editor", children: activeEditorTab === "scene" ? /* @__PURE__ */ jsx45(Fragment14, { children: modelUrl ? /* @__PURE__ */ jsx45(
16277
+ /* @__PURE__ */ jsx45("section", { className: "bb-editor", children: activeEditorTab === "scene" ? /* @__PURE__ */ jsx45(Fragment15, { children: modelUrl ? /* @__PURE__ */ jsx45(
16084
16278
  SceneSettingsTab,
16085
16279
  {
16086
16280
  config: sceneConfig,
@@ -16102,7 +16296,7 @@ function BindingBuilder({ licenseKey }) {
16102
16296
  animationClipDetails: viewerAnimations.clipDetails,
16103
16297
  animationPlayback: viewerAnimations
16104
16298
  }
16105
- ) : /* @__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(Fragment14, { children: [
16299
+ ) : /* @__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(Fragment15, { children: [
16106
16300
  /* @__PURE__ */ jsx45("div", { className: "bb-editorHeader", children: /* @__PURE__ */ jsxs40("div", { children: [
16107
16301
  /* @__PURE__ */ jsx45("div", { className: "bb-sectionKicker", children: "Editing Binding" }),
16108
16302
  /* @__PURE__ */ jsx45("h2", { className: "bb-editorTitle", children: selectedBinding.label || selectedItem }),
@@ -16522,9 +16716,9 @@ function BindingBuilder({ licenseKey }) {
16522
16716
  // src/components/SimpleModelViewer/SimpleModelViewer.tsx
16523
16717
  import {
16524
16718
  useRef as useRef19,
16525
- useState as useState26,
16719
+ useState as useState27,
16526
16720
  useCallback as useCallback10,
16527
- useEffect as useEffect21,
16721
+ useEffect as useEffect22,
16528
16722
  useMemo as useMemo17,
16529
16723
  Component as Component3,
16530
16724
  Suspense as Suspense3
@@ -16553,7 +16747,7 @@ import {
16553
16747
  Upload as Upload2,
16554
16748
  AlertTriangle as AlertTriangle4
16555
16749
  } from "lucide-react";
16556
- import { Fragment as Fragment15, jsx as jsx46, jsxs as jsxs41 } from "react/jsx-runtime";
16750
+ import { Fragment as Fragment16, jsx as jsx46, jsxs as jsxs41 } from "react/jsx-runtime";
16557
16751
  var HIGHLIGHT2 = { color: "#93c5fd", intensity: 0.05 };
16558
16752
  var ENV_PRESETS = [
16559
16753
  "apartment",
@@ -16613,7 +16807,7 @@ function InnerModel({
16613
16807
  meshopt: true
16614
16808
  });
16615
16809
  const invalidate = useThree11((state) => state.invalidate);
16616
- const [hoveredKey, setHoveredKey] = useState26(null);
16810
+ const [hoveredKey, setHoveredKey] = useState27(null);
16617
16811
  const scene = useMemo17(
16618
16812
  () => SkeletonUtils2.clone(rawScene),
16619
16813
  [rawScene]
@@ -16643,14 +16837,14 @@ function InnerModel({
16643
16837
  }
16644
16838
  return map;
16645
16839
  }, [meshMap]);
16646
- useEffect21(
16840
+ useEffect22(
16647
16841
  () => () => {
16648
16842
  Object.values(perKeyMaterials).forEach((m) => m.dispose());
16649
16843
  },
16650
16844
  [perKeyMaterials]
16651
16845
  );
16652
16846
  const notifiedUrl = useRef19(null);
16653
- useEffect21(() => {
16847
+ useEffect22(() => {
16654
16848
  if (notifiedUrl.current === url) return;
16655
16849
  notifiedUrl.current = url;
16656
16850
  scene.updateWorldMatrix(true, true);
@@ -16658,7 +16852,7 @@ function InnerModel({
16658
16852
  onMeshesReady(meshMap);
16659
16853
  onModelLoaded(scene);
16660
16854
  }, [url, scene, meshMap, onMeshesReady, onModelLoaded]);
16661
- useEffect21(() => {
16855
+ useEffect22(() => {
16662
16856
  const controller = new AbortController();
16663
16857
  void buildSceneBVH(scene, controller.signal);
16664
16858
  return () => {
@@ -16670,22 +16864,22 @@ function InnerModel({
16670
16864
  () => !exceedsBvhBudget(scene),
16671
16865
  [scene]
16672
16866
  );
16673
- useEffect21(() => {
16867
+ useEffect22(() => {
16674
16868
  for (const [key, { ref }] of Object.entries(meshMap)) {
16675
16869
  nodeRefsOut.current[key] = ref;
16676
16870
  }
16677
16871
  }, [meshMap, nodeRefsOut]);
16678
- useEffect21(() => {
16872
+ useEffect22(() => {
16679
16873
  if (!highlightOnHover && hoveredKey !== null) {
16680
16874
  setHoveredKey(null);
16681
16875
  }
16682
16876
  }, [highlightOnHover, hoveredKey]);
16683
- useEffect21(() => {
16877
+ useEffect22(() => {
16684
16878
  return () => {
16685
16879
  document.body.style.cursor = "default";
16686
16880
  };
16687
16881
  }, []);
16688
- useEffect21(() => {
16882
+ useEffect22(() => {
16689
16883
  for (const [key, { ref }] of Object.entries(meshMap)) {
16690
16884
  ref.visible = !hiddenKeys.has(key);
16691
16885
  const mat = perKeyMaterials[key];
@@ -16759,7 +16953,7 @@ function CameraFitter({
16759
16953
  const invalidate = useThree11((state) => state.invalidate);
16760
16954
  const size = useThree11((state) => state.size);
16761
16955
  const resizeTimerRef = useRef19(null);
16762
- useEffect21(() => {
16956
+ useEffect22(() => {
16763
16957
  fitted.current = false;
16764
16958
  invalidate();
16765
16959
  }, [invalidate, scene]);
@@ -16779,7 +16973,7 @@ function CameraFitter({
16779
16973
  }).catch(() => {
16780
16974
  }).finally(onInitialFitComplete);
16781
16975
  });
16782
- useEffect21(() => {
16976
+ useEffect22(() => {
16783
16977
  if (!refitOnResize || !fitted.current || !scene || !controlsRef.current) {
16784
16978
  return;
16785
16979
  }
@@ -16807,7 +17001,7 @@ function CameraFitter({
16807
17001
  function ExposureController({ exposure }) {
16808
17002
  const gl = useThree11((state) => state.gl);
16809
17003
  const invalidate = useThree11((state) => state.invalidate);
16810
- useEffect21(() => {
17004
+ useEffect22(() => {
16811
17005
  gl.toneMappingExposure = exposure;
16812
17006
  invalidate();
16813
17007
  }, [gl, exposure, invalidate]);
@@ -16915,7 +17109,7 @@ function EnvironmentSection({
16915
17109
  title,
16916
17110
  children
16917
17111
  }) {
16918
- const [collapsed, setCollapsed] = useState26(false);
17112
+ const [collapsed, setCollapsed] = useState27(false);
16919
17113
  return /* @__PURE__ */ jsxs41("div", { className: "env-section", children: [
16920
17114
  /* @__PURE__ */ jsxs41(
16921
17115
  "button",
@@ -17080,8 +17274,8 @@ function SceneObjectsPanel2({
17080
17274
  onHover,
17081
17275
  onToggleVisibility
17082
17276
  }) {
17083
- const [search, setSearch] = useState26("");
17084
- const [rowHovered, setRowHovered] = useState26(null);
17277
+ const [search, setSearch] = useState27("");
17278
+ const [rowHovered, setRowHovered] = useState27(null);
17085
17279
  const entries = Object.values(meshKeys).filter(
17086
17280
  (m) => m.label.toLowerCase().includes(search.toLowerCase())
17087
17281
  );
@@ -17210,30 +17404,30 @@ function SimpleModelViewer({
17210
17404
  const fileInputRef = useRef19(null);
17211
17405
  const uploadedModelRef = useRef19(null);
17212
17406
  const dragDepthRef = useRef19(0);
17213
- const [loadedScene, setLoadedScene] = useState26(null);
17214
- const [largeModelTriangles, setLargeModelTriangles] = useState26(
17407
+ const [loadedScene, setLoadedScene] = useState27(null);
17408
+ const [largeModelTriangles, setLargeModelTriangles] = useState27(
17215
17409
  null
17216
17410
  );
17217
- const [largeModelNoticeDismissed, setLargeModelNoticeDismissed] = useState26(false);
17218
- const [meshKeys, setMeshKeys] = useState26({});
17219
- const [hiddenKeys, setHiddenKeys] = useState26(/* @__PURE__ */ new Set());
17220
- const [selectedKey, setSelectedKey] = useState26(null);
17221
- const [panelHoveredKey, setPanelHoveredKey] = useState26(null);
17222
- const [uploadedModelUrl, setUploadedModelUrl] = useState26(null);
17223
- const [uploadedModelName, setUploadedModelName] = useState26(
17411
+ const [largeModelNoticeDismissed, setLargeModelNoticeDismissed] = useState27(false);
17412
+ const [meshKeys, setMeshKeys] = useState27({});
17413
+ const [hiddenKeys, setHiddenKeys] = useState27(/* @__PURE__ */ new Set());
17414
+ const [selectedKey, setSelectedKey] = useState27(null);
17415
+ const [panelHoveredKey, setPanelHoveredKey] = useState27(null);
17416
+ const [uploadedModelUrl, setUploadedModelUrl] = useState27(null);
17417
+ const [uploadedModelName, setUploadedModelName] = useState27(
17224
17418
  null
17225
17419
  );
17226
- const [modelLoadFailed, setModelLoadFailed] = useState26(false);
17227
- const [isResolvingUpload, setIsResolvingUpload] = useState26(false);
17228
- const [initialFitComplete, setInitialFitComplete] = useState26(false);
17229
- const [isDragActive, setIsDragActive] = useState26(false);
17230
- const [isPanelOpen, setIsPanelOpen] = useState26(
17420
+ const [modelLoadFailed, setModelLoadFailed] = useState27(false);
17421
+ const [isResolvingUpload, setIsResolvingUpload] = useState27(false);
17422
+ const [initialFitComplete, setInitialFitComplete] = useState27(false);
17423
+ const [isDragActive, setIsDragActive] = useState27(false);
17424
+ const [isPanelOpen, setIsPanelOpen] = useState27(
17231
17425
  () => typeof window === "undefined" ? true : window.innerWidth > 900
17232
17426
  );
17233
- const [isEnvPanelOpen, setIsEnvPanelOpen] = useState26(
17427
+ const [isEnvPanelOpen, setIsEnvPanelOpen] = useState27(
17234
17428
  () => typeof window === "undefined" ? true : window.innerWidth > 900
17235
17429
  );
17236
- const [envSettings, setEnvSettings] = useState26({
17430
+ const [envSettings, setEnvSettings] = useState27({
17237
17431
  backgroundEnabled,
17238
17432
  backgroundColor,
17239
17433
  autoRotate,
@@ -17316,7 +17510,7 @@ function SimpleModelViewer({
17316
17510
  },
17317
17511
  [handleUploadedModel]
17318
17512
  );
17319
- useEffect21(() => {
17513
+ useEffect22(() => {
17320
17514
  return () => {
17321
17515
  revokeUploadedModelUrl();
17322
17516
  };
@@ -17327,10 +17521,10 @@ function SimpleModelViewer({
17327
17521
  },
17328
17522
  []
17329
17523
  );
17330
- useEffect21(() => {
17524
+ useEffect22(() => {
17331
17525
  setEnvSettings((prev) => ({ ...prev, backgroundColor }));
17332
17526
  }, [backgroundColor]);
17333
- useEffect21(() => {
17527
+ useEffect22(() => {
17334
17528
  resetModelState();
17335
17529
  }, [activeModelUrl, resetModelState]);
17336
17530
  const handleMeshesReady = useCallback10((meshes) => {
@@ -17471,7 +17665,7 @@ function SimpleModelViewer({
17471
17665
  }
17472
17666
  )
17473
17667
  ] }),
17474
- enableModelUpload ? /* @__PURE__ */ jsxs41(Fragment15, { children: [
17668
+ enableModelUpload ? /* @__PURE__ */ jsxs41(Fragment16, { children: [
17475
17669
  /* @__PURE__ */ jsx46(
17476
17670
  "input",
17477
17671
  {
@@ -17485,7 +17679,7 @@ function SimpleModelViewer({
17485
17679
  }
17486
17680
  }
17487
17681
  ),
17488
- activeModelUrl ? /* @__PURE__ */ jsx46("div", { className: "smv-upload-toolbar", children: /* @__PURE__ */ jsxs41(Fragment15, { children: [
17682
+ activeModelUrl ? /* @__PURE__ */ jsx46("div", { className: "smv-upload-toolbar", children: /* @__PURE__ */ jsxs41(Fragment16, { children: [
17489
17683
  /* @__PURE__ */ jsx46("span", { className: "smv-upload-toolbar__name", children: uploadedModelName ?? "Uploaded model" }),
17490
17684
  /* @__PURE__ */ jsxs41(
17491
17685
  "button",
@@ -17646,9 +17840,9 @@ function SimpleModelViewer({
17646
17840
  }
17647
17841
 
17648
17842
  // src/hooks/useViewerSelection.ts
17649
- import { useCallback as useCallback11, useState as useState27 } from "react";
17843
+ import { useCallback as useCallback11, useState as useState28 } from "react";
17650
17844
  function useViewerSelection(initialSelectedObjectBinding = null) {
17651
- const [selectedObjectBinding, setSelectedObjectBinding] = useState27(
17845
+ const [selectedObjectBinding, setSelectedObjectBinding] = useState28(
17652
17846
  initialSelectedObjectBinding
17653
17847
  );
17654
17848
  const handleObjectSelect = useCallback11(
@@ -17695,17 +17889,14 @@ function useObjectBindingIds(objectBindings) {
17695
17889
  }
17696
17890
 
17697
17891
  // src/hooks/useObjectBindings.ts
17698
- import { useCallback as useCallback12, useMemo as useMemo20, useState as useState28 } from "react";
17892
+ import { useCallback as useCallback12, useMemo as useMemo20, useState as useState29 } from "react";
17699
17893
 
17700
- // src/utils/patchBindings.ts
17894
+ // src/utils/deepMerge.ts
17701
17895
  function isPlainObject2(value) {
17702
17896
  if (typeof value !== "object" || value === null) return false;
17703
17897
  const prototype = Object.getPrototypeOf(value);
17704
17898
  return prototype === Object.prototype || prototype === null;
17705
17899
  }
17706
- function isBindingEditArray(value) {
17707
- return Array.isArray(value) && value.length > 0 && typeof value[0] !== "string";
17708
- }
17709
17900
  function deepMerge(base, patch) {
17710
17901
  if (!isPlainObject2(patch)) return patch;
17711
17902
  const target = isPlainObject2(base) ? { ...base } : {};
@@ -17715,6 +17906,11 @@ function deepMerge(base, patch) {
17715
17906
  }
17716
17907
  return target;
17717
17908
  }
17909
+
17910
+ // src/utils/patchBindings.ts
17911
+ function isBindingEditArray(value) {
17912
+ return Array.isArray(value) && value.length > 0 && typeof value[0] !== "string";
17913
+ }
17718
17914
  function patchBindings(bindings, objectIdOrEdits, patch) {
17719
17915
  const edits = isBindingEditArray(objectIdOrEdits) ? objectIdOrEdits : [{ ids: objectIdOrEdits, patch: patch ?? {} }];
17720
17916
  let next = bindings;
@@ -17743,8 +17939,8 @@ function getHiddenObjects(objectBindings) {
17743
17939
  );
17744
17940
  }
17745
17941
  function useObjectBindings(initialBindings) {
17746
- const [objectBindings, setObjectBindings] = useState28(initialBindings);
17747
- const patch = useCallback12(
17942
+ const [objectBindings, setObjectBindings] = useState29(initialBindings);
17943
+ const updateObjectBindings = useCallback12(
17748
17944
  (objectIdOrEdits, patchValue) => {
17749
17945
  setObjectBindings(
17750
17946
  (current) => patchValue === void 0 ? patchBindings(current, objectIdOrEdits) : patchBindings(
@@ -17757,16 +17953,16 @@ function useObjectBindings(initialBindings) {
17757
17953
  []
17758
17954
  );
17759
17955
  const setMaterial = useCallback12(
17760
- (objectId, value) => patch(objectId, material(value)),
17761
- [patch]
17956
+ (objectId, value) => updateObjectBindings(objectId, material(value)),
17957
+ [updateObjectBindings]
17762
17958
  );
17763
17959
  const setBaseColor = useCallback12(
17764
17960
  (objectId, color) => setMaterial(objectId, { baseColor: color }),
17765
17961
  [setMaterial]
17766
17962
  );
17767
17963
  const resetMaterial = useCallback12(
17768
- (objectId) => patch(objectId, { style: { material: void 0 } }),
17769
- [patch]
17964
+ (objectId) => updateObjectBindings(objectId, { style: { material: void 0 } }),
17965
+ [updateObjectBindings]
17770
17966
  );
17771
17967
  const getMaterial = useCallback12(
17772
17968
  (objectId) => {
@@ -17776,12 +17972,12 @@ function useObjectBindings(initialBindings) {
17776
17972
  [objectBindings]
17777
17973
  );
17778
17974
  const hideObject = useCallback12(
17779
- (objectId) => patch(objectId, { visible: false }),
17780
- [patch]
17975
+ (objectId) => updateObjectBindings(objectId, { visible: false }),
17976
+ [updateObjectBindings]
17781
17977
  );
17782
17978
  const showObject = useCallback12(
17783
- (objectId) => patch(objectId, { visible: true }),
17784
- [patch]
17979
+ (objectId) => updateObjectBindings(objectId, { visible: true }),
17980
+ [updateObjectBindings]
17785
17981
  );
17786
17982
  const toggleObjectVisibility = useCallback12(
17787
17983
  (objectId) => {
@@ -17827,7 +18023,7 @@ function useObjectBindings(initialBindings) {
17827
18023
  return {
17828
18024
  objectBindings,
17829
18025
  setObjectBindings,
17830
- patch,
18026
+ updateObjectBindings,
17831
18027
  setMaterial,
17832
18028
  setBaseColor,
17833
18029
  resetMaterial,
@@ -17842,11 +18038,45 @@ function useObjectBindings(initialBindings) {
17842
18038
  };
17843
18039
  }
17844
18040
 
18041
+ // src/hooks/useSceneConfig.ts
18042
+ import { useCallback as useCallback13, useRef as useRef20, useState as useState30 } from "react";
18043
+
18044
+ // src/utils/patchSceneConfig.ts
18045
+ function patchSceneConfig(sceneConfig, patch) {
18046
+ return deepMerge(sceneConfig, patch);
18047
+ }
18048
+
18049
+ // src/hooks/useSceneConfig.ts
18050
+ function useSceneConfig(initialSceneConfig) {
18051
+ const initialSceneConfigRef = useRef20(null);
18052
+ const [sceneConfig, setSceneConfig] = useState30(() => {
18053
+ const initial = typeof initialSceneConfig === "function" ? initialSceneConfig() : initialSceneConfig;
18054
+ initialSceneConfigRef.current = initial;
18055
+ return initial;
18056
+ });
18057
+ const updateSceneConfig = useCallback13((update) => {
18058
+ setSceneConfig((current) => {
18059
+ const patch = typeof update === "function" ? update(current) : update;
18060
+ return patchSceneConfig(current, patch);
18061
+ });
18062
+ }, []);
18063
+ const resetSceneConfig = useCallback13(() => {
18064
+ const initial = initialSceneConfigRef.current;
18065
+ if (initial) setSceneConfig(initial);
18066
+ }, []);
18067
+ return {
18068
+ sceneConfig,
18069
+ setSceneConfig,
18070
+ updateSceneConfig,
18071
+ resetSceneConfig
18072
+ };
18073
+ }
18074
+
17845
18075
  // src/hooks/useViewerHover.ts
17846
- import { useCallback as useCallback13, useState as useState29 } from "react";
18076
+ import { useCallback as useCallback14, useState as useState31 } from "react";
17847
18077
  function useViewerHover() {
17848
- const [hoveredObjectBinding, setHoveredObjectBinding] = useState29(null);
17849
- const handleHoveredObject = useCallback13(
18078
+ const [hoveredObjectBinding, setHoveredObjectBinding] = useState31(null);
18079
+ const handleHoveredObject = useCallback14(
17850
18080
  (binding) => {
17851
18081
  setHoveredObjectBinding(binding);
17852
18082
  },
@@ -17859,18 +18089,18 @@ function useViewerHover() {
17859
18089
  }
17860
18090
 
17861
18091
  // src/hooks/useViewerModel.ts
17862
- import { useCallback as useCallback14, useState as useState30 } from "react";
18092
+ import { useCallback as useCallback15, useState as useState32 } from "react";
17863
18093
  import { Box3 as Box36, Vector3 as Vector310 } from "three";
17864
18094
  function toTuple2(vector) {
17865
18095
  return [vector.x, vector.y, vector.z];
17866
18096
  }
17867
18097
  function useViewerModel() {
17868
- const [isLoading, setIsLoading] = useState30(true);
17869
- const [isReady, setIsReady] = useState30(false);
17870
- const [error, setError] = useState30(null);
17871
- const [bounds, setBounds] = useState30(null);
17872
- const [objectCount, setObjectCount] = useState30(0);
17873
- const handleModelLoaded = useCallback14((scene) => {
18098
+ const [isLoading, setIsLoading] = useState32(true);
18099
+ const [isReady, setIsReady] = useState32(false);
18100
+ const [error, setError] = useState32(null);
18101
+ const [bounds, setBounds] = useState32(null);
18102
+ const [objectCount, setObjectCount] = useState32(0);
18103
+ const handleModelLoaded = useCallback15((scene) => {
17874
18104
  const box = new Box36().setFromObject(scene);
17875
18105
  const min = box.min.clone();
17876
18106
  const max = box.max.clone();
@@ -17895,14 +18125,14 @@ function useViewerModel() {
17895
18125
  setIsLoading(false);
17896
18126
  setIsReady(true);
17897
18127
  }, []);
17898
- const handleLoadError = useCallback14((nextError) => {
18128
+ const handleLoadError = useCallback15((nextError) => {
17899
18129
  setError(nextError);
17900
18130
  setBounds(null);
17901
18131
  setObjectCount(0);
17902
18132
  setIsLoading(false);
17903
18133
  setIsReady(false);
17904
18134
  }, []);
17905
- const resetModelState = useCallback14(() => {
18135
+ const resetModelState = useCallback15(() => {
17906
18136
  setIsLoading(true);
17907
18137
  setIsReady(false);
17908
18138
  setError(null);
@@ -17922,10 +18152,10 @@ function useViewerModel() {
17922
18152
  }
17923
18153
 
17924
18154
  // src/hooks/useViewerActions.ts
17925
- import { useCallback as useCallback15 } from "react";
18155
+ import { useCallback as useCallback16 } from "react";
17926
18156
  function useViewerActions(objectBindings, options = {}) {
17927
18157
  const { onAction, onObjectBindingsChange } = options;
17928
- const getActionsForObject = useCallback15(
18158
+ const getActionsForObject = useCallback16(
17929
18159
  (objectId) => {
17930
18160
  const bindingKey = resolveBindingKey(objectBindings, objectId);
17931
18161
  if (!bindingKey) {
@@ -17935,7 +18165,7 @@ function useViewerActions(objectBindings, options = {}) {
17935
18165
  },
17936
18166
  [objectBindings]
17937
18167
  );
17938
- const runAction = useCallback15(
18168
+ const runAction = useCallback16(
17939
18169
  (objectId, actionId) => {
17940
18170
  const bindingKey = resolveBindingKey(objectBindings, objectId);
17941
18171
  if (!bindingKey) {
@@ -17977,7 +18207,7 @@ function useViewerActions(objectBindings, options = {}) {
17977
18207
  }
17978
18208
 
17979
18209
  // src/hooks/useShareableViewerState.ts
17980
- import { useCallback as useCallback16, useEffect as useEffect22, useRef as useRef20, useState as useState31 } from "react";
18210
+ import { useCallback as useCallback17, useEffect as useEffect23, useRef as useRef21, useState as useState33 } from "react";
17981
18211
  function serializeState(cameraState, objectBindings, selectedObjectBinding) {
17982
18212
  const hidden = Object.values(objectBindings).filter((binding) => binding.visible === false).map((binding) => binding.id);
17983
18213
  return {
@@ -17997,8 +18227,8 @@ function useShareableViewerState({
17997
18227
  debounceMs = 400,
17998
18228
  autoSync = true
17999
18229
  }) {
18000
- const lastWrittenRawRef = useRef20(null);
18001
- const buildHash = useCallback16(() => {
18230
+ const lastWrittenRawRef = useRef21(null);
18231
+ const buildHash = useCallback17(() => {
18002
18232
  const state = serializeState(
18003
18233
  cameraState,
18004
18234
  objectBindings,
@@ -18006,12 +18236,12 @@ function useShareableViewerState({
18006
18236
  );
18007
18237
  return `${paramKey}=${encodeURIComponent(JSON.stringify(state))}`;
18008
18238
  }, [cameraState, objectBindings, selectedObjectBinding, paramKey]);
18009
- const getShareUrl = useCallback16(() => {
18239
+ const getShareUrl = useCallback17(() => {
18010
18240
  const url = new URL(window.location.href);
18011
18241
  url.hash = buildHash();
18012
18242
  return url.toString();
18013
18243
  }, [buildHash]);
18014
- const syncUrl = useCallback16(() => {
18244
+ const syncUrl = useCallback17(() => {
18015
18245
  const url = new URL(window.location.href);
18016
18246
  url.hash = buildHash();
18017
18247
  window.history.replaceState(null, "", url.toString());
@@ -18019,7 +18249,7 @@ function useShareableViewerState({
18019
18249
  paramKey
18020
18250
  );
18021
18251
  }, [buildHash, paramKey]);
18022
- const latestStateRef = useRef20({
18252
+ const latestStateRef = useRef21({
18023
18253
  cameraState,
18024
18254
  objectBindings,
18025
18255
  selectedObjectBinding
@@ -18029,9 +18259,9 @@ function useShareableViewerState({
18029
18259
  objectBindings,
18030
18260
  selectedObjectBinding
18031
18261
  };
18032
- const [restoreAttempted, setRestoreAttempted] = useState31(false);
18033
- const lastSyncedHashRef = useRef20(null);
18034
- useEffect22(() => {
18262
+ const [restoreAttempted, setRestoreAttempted] = useState33(false);
18263
+ const lastSyncedHashRef = useRef21(null);
18264
+ useEffect23(() => {
18035
18265
  if (!autoSync || !restoreAttempted) return;
18036
18266
  const tick = () => {
18037
18267
  const current = latestStateRef.current;
@@ -18053,9 +18283,9 @@ function useShareableViewerState({
18053
18283
  const id = window.setInterval(tick, debounceMs);
18054
18284
  return () => window.clearInterval(id);
18055
18285
  }, [autoSync, restoreAttempted, debounceMs, paramKey]);
18056
- const lastRestoredRawRef = useRef20(null);
18057
- const restoringRawRef = useRef20(null);
18058
- const restoreFromUrl = useCallback16(async () => {
18286
+ const lastRestoredRawRef = useRef21(null);
18287
+ const restoringRawRef = useRef21(null);
18288
+ const restoreFromUrl = useCallback17(async () => {
18059
18289
  setRestoreAttempted(true);
18060
18290
  const rawHash = window.location.hash.startsWith("#") ? window.location.hash.slice(1) : window.location.hash;
18061
18291
  const raw = new URLSearchParams(rawHash).get(paramKey);
@@ -18108,7 +18338,7 @@ function useShareableViewerState({
18108
18338
  setCameraState,
18109
18339
  setSelectedObjectBinding
18110
18340
  ]);
18111
- useEffect22(() => {
18341
+ useEffect23(() => {
18112
18342
  const handleHashChange = () => {
18113
18343
  void restoreFromUrl();
18114
18344
  };
@@ -18127,9 +18357,11 @@ export {
18127
18357
  SimpleModelViewer,
18128
18358
  material,
18129
18359
  patchBindings,
18360
+ patchSceneConfig,
18130
18361
  useObjectBinding,
18131
18362
  useObjectBindingIds,
18132
18363
  useObjectBindings,
18364
+ useSceneConfig,
18133
18365
  useShareableViewerState,
18134
18366
  useViewerActions,
18135
18367
  useViewerAnimations,