#
tokens: 47920/50000 1/574 files (page 57/60)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 57 of 60. Use http://codebase.md/hangwin/mcp-chrome?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .gitattributes
├── .github
│   └── workflows
│       └── build-release.yml
├── .gitignore
├── .husky
│   ├── commit-msg
│   └── pre-commit
├── .prettierignore
├── .prettierrc.json
├── .vscode
│   └── extensions.json
├── app
│   ├── chrome-extension
│   │   ├── _locales
│   │   │   ├── de
│   │   │   │   └── messages.json
│   │   │   ├── en
│   │   │   │   └── messages.json
│   │   │   ├── ja
│   │   │   │   └── messages.json
│   │   │   ├── ko
│   │   │   │   └── messages.json
│   │   │   ├── zh_CN
│   │   │   │   └── messages.json
│   │   │   └── zh_TW
│   │   │       └── messages.json
│   │   ├── .env.example
│   │   ├── assets
│   │   │   └── vue.svg
│   │   ├── common
│   │   │   ├── agent-models.ts
│   │   │   ├── constants.ts
│   │   │   ├── element-marker-types.ts
│   │   │   ├── message-types.ts
│   │   │   ├── node-types.ts
│   │   │   ├── rr-v3-keepalive-protocol.ts
│   │   │   ├── step-types.ts
│   │   │   ├── tool-handler.ts
│   │   │   └── web-editor-types.ts
│   │   ├── entrypoints
│   │   │   ├── background
│   │   │   │   ├── element-marker
│   │   │   │   │   ├── element-marker-storage.ts
│   │   │   │   │   └── index.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── keepalive-manager.ts
│   │   │   │   ├── native-host.ts
│   │   │   │   ├── quick-panel
│   │   │   │   │   ├── agent-handler.ts
│   │   │   │   │   ├── commands.ts
│   │   │   │   │   └── tabs-handler.ts
│   │   │   │   ├── record-replay
│   │   │   │   │   ├── actions
│   │   │   │   │   │   ├── adapter.ts
│   │   │   │   │   │   ├── handlers
│   │   │   │   │   │   │   ├── assert.ts
│   │   │   │   │   │   │   ├── click.ts
│   │   │   │   │   │   │   ├── common.ts
│   │   │   │   │   │   │   ├── control-flow.ts
│   │   │   │   │   │   │   ├── delay.ts
│   │   │   │   │   │   │   ├── dom.ts
│   │   │   │   │   │   │   ├── drag.ts
│   │   │   │   │   │   │   ├── extract.ts
│   │   │   │   │   │   │   ├── fill.ts
│   │   │   │   │   │   │   ├── http.ts
│   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   ├── key.ts
│   │   │   │   │   │   │   ├── navigate.ts
│   │   │   │   │   │   │   ├── screenshot.ts
│   │   │   │   │   │   │   ├── script.ts
│   │   │   │   │   │   │   ├── scroll.ts
│   │   │   │   │   │   │   ├── tabs.ts
│   │   │   │   │   │   │   └── wait.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── registry.ts
│   │   │   │   │   │   └── types.ts
│   │   │   │   │   ├── engine
│   │   │   │   │   │   ├── constants.ts
│   │   │   │   │   │   ├── execution-mode.ts
│   │   │   │   │   │   ├── logging
│   │   │   │   │   │   │   └── run-logger.ts
│   │   │   │   │   │   ├── plugins
│   │   │   │   │   │   │   ├── breakpoint.ts
│   │   │   │   │   │   │   ├── manager.ts
│   │   │   │   │   │   │   └── types.ts
│   │   │   │   │   │   ├── policies
│   │   │   │   │   │   │   ├── retry.ts
│   │   │   │   │   │   │   └── wait.ts
│   │   │   │   │   │   ├── runners
│   │   │   │   │   │   │   ├── after-script-queue.ts
│   │   │   │   │   │   │   ├── control-flow-runner.ts
│   │   │   │   │   │   │   ├── step-executor.ts
│   │   │   │   │   │   │   ├── step-runner.ts
│   │   │   │   │   │   │   └── subflow-runner.ts
│   │   │   │   │   │   ├── scheduler.ts
│   │   │   │   │   │   ├── state-manager.ts
│   │   │   │   │   │   └── utils
│   │   │   │   │   │       └── expression.ts
│   │   │   │   │   ├── flow-runner.ts
│   │   │   │   │   ├── flow-store.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── legacy-types.ts
│   │   │   │   │   ├── nodes
│   │   │   │   │   │   ├── assert.ts
│   │   │   │   │   │   ├── click.ts
│   │   │   │   │   │   ├── conditional.ts
│   │   │   │   │   │   ├── download-screenshot-attr-event-frame-loop.ts
│   │   │   │   │   │   ├── drag.ts
│   │   │   │   │   │   ├── execute-flow.ts
│   │   │   │   │   │   ├── extract.ts
│   │   │   │   │   │   ├── fill.ts
│   │   │   │   │   │   ├── http.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── key.ts
│   │   │   │   │   │   ├── loops.ts
│   │   │   │   │   │   ├── navigate.ts
│   │   │   │   │   │   ├── script.ts
│   │   │   │   │   │   ├── scroll.ts
│   │   │   │   │   │   ├── tabs.ts
│   │   │   │   │   │   ├── types.ts
│   │   │   │   │   │   └── wait.ts
│   │   │   │   │   ├── recording
│   │   │   │   │   │   ├── browser-event-listener.ts
│   │   │   │   │   │   ├── content-injection.ts
│   │   │   │   │   │   ├── content-message-handler.ts
│   │   │   │   │   │   ├── flow-builder.ts
│   │   │   │   │   │   ├── recorder-manager.ts
│   │   │   │   │   │   └── session-manager.ts
│   │   │   │   │   ├── rr-utils.ts
│   │   │   │   │   ├── selector-engine.ts
│   │   │   │   │   ├── storage
│   │   │   │   │   │   └── indexeddb-manager.ts
│   │   │   │   │   ├── trigger-store.ts
│   │   │   │   │   └── types.ts
│   │   │   │   ├── record-replay-v3
│   │   │   │   │   ├── bootstrap.ts
│   │   │   │   │   ├── domain
│   │   │   │   │   │   ├── debug.ts
│   │   │   │   │   │   ├── errors.ts
│   │   │   │   │   │   ├── events.ts
│   │   │   │   │   │   ├── flow.ts
│   │   │   │   │   │   ├── ids.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── json.ts
│   │   │   │   │   │   ├── policy.ts
│   │   │   │   │   │   ├── triggers.ts
│   │   │   │   │   │   └── variables.ts
│   │   │   │   │   ├── engine
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── keepalive
│   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   └── offscreen-keepalive.ts
│   │   │   │   │   │   ├── kernel
│   │   │   │   │   │   │   ├── artifacts.ts
│   │   │   │   │   │   │   ├── breakpoints.ts
│   │   │   │   │   │   │   ├── debug-controller.ts
│   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   ├── kernel.ts
│   │   │   │   │   │   │   ├── recovery-kernel.ts
│   │   │   │   │   │   │   ├── runner.ts
│   │   │   │   │   │   │   └── traversal.ts
│   │   │   │   │   │   ├── plugins
│   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   ├── register-v2-replay-nodes.ts
│   │   │   │   │   │   │   ├── registry.ts
│   │   │   │   │   │   │   ├── types.ts
│   │   │   │   │   │   │   └── v2-action-adapter.ts
│   │   │   │   │   │   ├── queue
│   │   │   │   │   │   │   ├── enqueue-run.ts
│   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   ├── leasing.ts
│   │   │   │   │   │   │   ├── queue.ts
│   │   │   │   │   │   │   └── scheduler.ts
│   │   │   │   │   │   ├── recovery
│   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   └── recovery-coordinator.ts
│   │   │   │   │   │   ├── storage
│   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   └── storage-port.ts
│   │   │   │   │   │   ├── transport
│   │   │   │   │   │   │   ├── events-bus.ts
│   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   ├── rpc-server.ts
│   │   │   │   │   │   │   └── rpc.ts
│   │   │   │   │   │   └── triggers
│   │   │   │   │   │       ├── command-trigger.ts
│   │   │   │   │   │       ├── context-menu-trigger.ts
│   │   │   │   │   │       ├── cron-trigger.ts
│   │   │   │   │   │       ├── dom-trigger.ts
│   │   │   │   │   │       ├── index.ts
│   │   │   │   │   │       ├── interval-trigger.ts
│   │   │   │   │   │       ├── manual-trigger.ts
│   │   │   │   │   │       ├── once-trigger.ts
│   │   │   │   │   │       ├── trigger-handler.ts
│   │   │   │   │   │       ├── trigger-manager.ts
│   │   │   │   │   │       └── url-trigger.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── storage
│   │   │   │   │       ├── db.ts
│   │   │   │   │       ├── events.ts
│   │   │   │   │       ├── flows.ts
│   │   │   │   │       ├── import
│   │   │   │   │       │   ├── index.ts
│   │   │   │   │       │   ├── v2-reader.ts
│   │   │   │   │       │   └── v2-to-v3.ts
│   │   │   │   │       ├── index.ts
│   │   │   │   │       ├── persistent-vars.ts
│   │   │   │   │       ├── queue.ts
│   │   │   │   │       ├── runs.ts
│   │   │   │   │       └── triggers.ts
│   │   │   │   ├── semantic-similarity.ts
│   │   │   │   ├── storage-manager.ts
│   │   │   │   ├── tools
│   │   │   │   │   ├── base-browser.ts
│   │   │   │   │   ├── browser
│   │   │   │   │   │   ├── bookmark.ts
│   │   │   │   │   │   ├── common.ts
│   │   │   │   │   │   ├── computer.ts
│   │   │   │   │   │   ├── console-buffer.ts
│   │   │   │   │   │   ├── console.ts
│   │   │   │   │   │   ├── dialog.ts
│   │   │   │   │   │   ├── download.ts
│   │   │   │   │   │   ├── element-picker.ts
│   │   │   │   │   │   ├── file-upload.ts
│   │   │   │   │   │   ├── gif-auto-capture.ts
│   │   │   │   │   │   ├── gif-enhanced-renderer.ts
│   │   │   │   │   │   ├── gif-recorder.ts
│   │   │   │   │   │   ├── history.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── inject-script.ts
│   │   │   │   │   │   ├── interaction.ts
│   │   │   │   │   │   ├── javascript.ts
│   │   │   │   │   │   ├── keyboard.ts
│   │   │   │   │   │   ├── network-capture-debugger.ts
│   │   │   │   │   │   ├── network-capture-web-request.ts
│   │   │   │   │   │   ├── network-capture.ts
│   │   │   │   │   │   ├── network-request.ts
│   │   │   │   │   │   ├── performance.ts
│   │   │   │   │   │   ├── read-page.ts
│   │   │   │   │   │   ├── screenshot.ts
│   │   │   │   │   │   ├── userscript.ts
│   │   │   │   │   │   ├── vector-search.ts
│   │   │   │   │   │   ├── web-fetcher.ts
│   │   │   │   │   │   └── window.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── record-replay.ts
│   │   │   │   ├── utils
│   │   │   │   │   └── sidepanel.ts
│   │   │   │   └── web-editor
│   │   │   │       └── index.ts
│   │   │   ├── builder
│   │   │   │   ├── App.vue
│   │   │   │   ├── index.html
│   │   │   │   └── main.ts
│   │   │   ├── content.ts
│   │   │   ├── element-picker.content.ts
│   │   │   ├── offscreen
│   │   │   │   ├── gif-encoder.ts
│   │   │   │   ├── index.html
│   │   │   │   ├── main.ts
│   │   │   │   └── rr-keepalive.ts
│   │   │   ├── options
│   │   │   │   ├── App.vue
│   │   │   │   ├── index.html
│   │   │   │   └── main.ts
│   │   │   ├── popup
│   │   │   │   ├── App.vue
│   │   │   │   ├── components
│   │   │   │   │   ├── builder
│   │   │   │   │   │   ├── components
│   │   │   │   │   │   │   ├── Canvas.vue
│   │   │   │   │   │   │   ├── EdgePropertyPanel.vue
│   │   │   │   │   │   │   ├── KeyValueEditor.vue
│   │   │   │   │   │   │   ├── nodes
│   │   │   │   │   │   │   │   ├── node-util.ts
│   │   │   │   │   │   │   │   ├── NodeCard.vue
│   │   │   │   │   │   │   │   └── NodeIf.vue
│   │   │   │   │   │   │   ├── properties
│   │   │   │   │   │   │   │   ├── PropertyAssert.vue
│   │   │   │   │   │   │   │   ├── PropertyClick.vue
│   │   │   │   │   │   │   │   ├── PropertyCloseTab.vue
│   │   │   │   │   │   │   │   ├── PropertyDelay.vue
│   │   │   │   │   │   │   │   ├── PropertyDrag.vue
│   │   │   │   │   │   │   │   ├── PropertyExecuteFlow.vue
│   │   │   │   │   │   │   │   ├── PropertyExtract.vue
│   │   │   │   │   │   │   │   ├── PropertyFill.vue
│   │   │   │   │   │   │   │   ├── PropertyForeach.vue
│   │   │   │   │   │   │   │   ├── PropertyFormRenderer.vue
│   │   │   │   │   │   │   │   ├── PropertyFromSpec.vue
│   │   │   │   │   │   │   │   ├── PropertyHandleDownload.vue
│   │   │   │   │   │   │   │   ├── PropertyHttp.vue
│   │   │   │   │   │   │   │   ├── PropertyIf.vue
│   │   │   │   │   │   │   │   ├── PropertyKey.vue
│   │   │   │   │   │   │   │   ├── PropertyLoopElements.vue
│   │   │   │   │   │   │   │   ├── PropertyNavigate.vue
│   │   │   │   │   │   │   │   ├── PropertyOpenTab.vue
│   │   │   │   │   │   │   │   ├── PropertyScreenshot.vue
│   │   │   │   │   │   │   │   ├── PropertyScript.vue
│   │   │   │   │   │   │   │   ├── PropertyScroll.vue
│   │   │   │   │   │   │   │   ├── PropertySetAttribute.vue
│   │   │   │   │   │   │   │   ├── PropertySwitchFrame.vue
│   │   │   │   │   │   │   │   ├── PropertySwitchTab.vue
│   │   │   │   │   │   │   │   ├── PropertyTrigger.vue
│   │   │   │   │   │   │   │   ├── PropertyTriggerEvent.vue
│   │   │   │   │   │   │   │   ├── PropertyWait.vue
│   │   │   │   │   │   │   │   ├── PropertyWhile.vue
│   │   │   │   │   │   │   │   └── SelectorEditor.vue
│   │   │   │   │   │   │   ├── PropertyPanel.vue
│   │   │   │   │   │   │   ├── Sidebar.vue
│   │   │   │   │   │   │   └── TriggerPanel.vue
│   │   │   │   │   │   ├── model
│   │   │   │   │   │   │   ├── form-widget-registry.ts
│   │   │   │   │   │   │   ├── node-spec-registry.ts
│   │   │   │   │   │   │   ├── node-spec.ts
│   │   │   │   │   │   │   ├── node-specs-builtin.ts
│   │   │   │   │   │   │   ├── toast.ts
│   │   │   │   │   │   │   ├── transforms.ts
│   │   │   │   │   │   │   ├── ui-nodes.ts
│   │   │   │   │   │   │   ├── validation.ts
│   │   │   │   │   │   │   └── variables.ts
│   │   │   │   │   │   ├── store
│   │   │   │   │   │   │   └── useBuilderStore.ts
│   │   │   │   │   │   └── widgets
│   │   │   │   │   │       ├── FieldCode.vue
│   │   │   │   │   │       ├── FieldDuration.vue
│   │   │   │   │   │       ├── FieldExpression.vue
│   │   │   │   │   │       ├── FieldKeySequence.vue
│   │   │   │   │   │       ├── FieldSelector.vue
│   │   │   │   │   │       ├── FieldTargetLocator.vue
│   │   │   │   │   │       └── VarInput.vue
│   │   │   │   │   ├── ConfirmDialog.vue
│   │   │   │   │   ├── ElementMarkerManagement.vue
│   │   │   │   │   ├── icons
│   │   │   │   │   │   ├── BoltIcon.vue
│   │   │   │   │   │   ├── CheckIcon.vue
│   │   │   │   │   │   ├── DatabaseIcon.vue
│   │   │   │   │   │   ├── DocumentIcon.vue
│   │   │   │   │   │   ├── EditIcon.vue
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── MarkerIcon.vue
│   │   │   │   │   │   ├── RecordIcon.vue
│   │   │   │   │   │   ├── RefreshIcon.vue
│   │   │   │   │   │   ├── StopIcon.vue
│   │   │   │   │   │   ├── TabIcon.vue
│   │   │   │   │   │   ├── TrashIcon.vue
│   │   │   │   │   │   ├── VectorIcon.vue
│   │   │   │   │   │   └── WorkflowIcon.vue
│   │   │   │   │   ├── LocalModelPage.vue
│   │   │   │   │   ├── ModelCacheManagement.vue
│   │   │   │   │   ├── ProgressIndicator.vue
│   │   │   │   │   └── ScheduleDialog.vue
│   │   │   │   ├── index.html
│   │   │   │   ├── main.ts
│   │   │   │   └── style.css
│   │   │   ├── quick-panel.content.ts
│   │   │   ├── shared
│   │   │   │   ├── composables
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── useRRV3Rpc.ts
│   │   │   │   └── utils
│   │   │   │       ├── index.ts
│   │   │   │       └── rr-flow-convert.ts
│   │   │   ├── sidepanel
│   │   │   │   ├── App.vue
│   │   │   │   ├── components
│   │   │   │   │   ├── agent
│   │   │   │   │   │   ├── AttachmentPreview.vue
│   │   │   │   │   │   ├── ChatInput.vue
│   │   │   │   │   │   ├── CliSettings.vue
│   │   │   │   │   │   ├── ConnectionStatus.vue
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── MessageItem.vue
│   │   │   │   │   │   ├── MessageList.vue
│   │   │   │   │   │   ├── ProjectCreateForm.vue
│   │   │   │   │   │   └── ProjectSelector.vue
│   │   │   │   │   ├── agent-chat
│   │   │   │   │   │   ├── AgentChatShell.vue
│   │   │   │   │   │   ├── AgentComposer.vue
│   │   │   │   │   │   ├── AgentConversation.vue
│   │   │   │   │   │   ├── AgentOpenProjectMenu.vue
│   │   │   │   │   │   ├── AgentProjectMenu.vue
│   │   │   │   │   │   ├── AgentRequestThread.vue
│   │   │   │   │   │   ├── AgentSessionListItem.vue
│   │   │   │   │   │   ├── AgentSessionMenu.vue
│   │   │   │   │   │   ├── AgentSessionSettingsPanel.vue
│   │   │   │   │   │   ├── AgentSessionsView.vue
│   │   │   │   │   │   ├── AgentSettingsMenu.vue
│   │   │   │   │   │   ├── AgentTimeline.vue
│   │   │   │   │   │   ├── AgentTimelineItem.vue
│   │   │   │   │   │   ├── AgentTopBar.vue
│   │   │   │   │   │   ├── ApplyMessageChip.vue
│   │   │   │   │   │   ├── AttachmentCachePanel.vue
│   │   │   │   │   │   ├── ComposerDrawer.vue
│   │   │   │   │   │   ├── ElementChip.vue
│   │   │   │   │   │   ├── FakeCaretOverlay.vue
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── SelectionChip.vue
│   │   │   │   │   │   ├── timeline
│   │   │   │   │   │   │   ├── markstream-thinking.ts
│   │   │   │   │   │   │   ├── ThinkingNode.vue
│   │   │   │   │   │   │   ├── TimelineNarrativeStep.vue
│   │   │   │   │   │   │   ├── TimelineStatusStep.vue
│   │   │   │   │   │   │   ├── TimelineToolCallStep.vue
│   │   │   │   │   │   │   ├── TimelineToolResultCardStep.vue
│   │   │   │   │   │   │   └── TimelineUserPromptStep.vue
│   │   │   │   │   │   └── WebEditorChanges.vue
│   │   │   │   │   ├── AgentChat.vue
│   │   │   │   │   ├── rr-v3
│   │   │   │   │   │   └── DebuggerPanel.vue
│   │   │   │   │   ├── SidepanelNavigator.vue
│   │   │   │   │   └── workflows
│   │   │   │   │       ├── index.ts
│   │   │   │   │       ├── WorkflowListItem.vue
│   │   │   │   │       └── WorkflowsView.vue
│   │   │   │   ├── composables
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── useAgentChat.ts
│   │   │   │   │   ├── useAgentChatViewRoute.ts
│   │   │   │   │   ├── useAgentInputPreferences.ts
│   │   │   │   │   ├── useAgentProjects.ts
│   │   │   │   │   ├── useAgentServer.ts
│   │   │   │   │   ├── useAgentSessions.ts
│   │   │   │   │   ├── useAgentTheme.ts
│   │   │   │   │   ├── useAgentThreads.ts
│   │   │   │   │   ├── useAttachments.ts
│   │   │   │   │   ├── useFakeCaret.ts
│   │   │   │   │   ├── useFloatingDrag.ts
│   │   │   │   │   ├── useOpenProjectPreference.ts
│   │   │   │   │   ├── useRRV3Debugger.ts
│   │   │   │   │   ├── useRRV3Rpc.ts
│   │   │   │   │   ├── useTextareaAutoResize.ts
│   │   │   │   │   ├── useWebEditorTxState.ts
│   │   │   │   │   └── useWorkflowsV3.ts
│   │   │   │   ├── index.html
│   │   │   │   ├── main.ts
│   │   │   │   ├── styles
│   │   │   │   │   └── agent-chat.css
│   │   │   │   └── utils
│   │   │   │       └── loading-texts.ts
│   │   │   ├── styles
│   │   │   │   └── tailwind.css
│   │   │   ├── web-editor-v2
│   │   │   │   ├── attr-ui-refactor.md
│   │   │   │   ├── constants.ts
│   │   │   │   ├── core
│   │   │   │   │   ├── css-compare.ts
│   │   │   │   │   ├── cssom-styles-collector.ts
│   │   │   │   │   ├── debug-source.ts
│   │   │   │   │   ├── design-tokens
│   │   │   │   │   │   ├── design-tokens-service.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── token-detector.ts
│   │   │   │   │   │   ├── token-resolver.ts
│   │   │   │   │   │   └── types.ts
│   │   │   │   │   ├── editor.ts
│   │   │   │   │   ├── element-key.ts
│   │   │   │   │   ├── event-controller.ts
│   │   │   │   │   ├── execution-tracker.ts
│   │   │   │   │   ├── hmr-consistency.ts
│   │   │   │   │   ├── locator.ts
│   │   │   │   │   ├── message-listener.ts
│   │   │   │   │   ├── payload-builder.ts
│   │   │   │   │   ├── perf-monitor.ts
│   │   │   │   │   ├── position-tracker.ts
│   │   │   │   │   ├── props-bridge.ts
│   │   │   │   │   ├── snap-engine.ts
│   │   │   │   │   ├── transaction-aggregator.ts
│   │   │   │   │   └── transaction-manager.ts
│   │   │   │   ├── drag
│   │   │   │   │   └── drag-reorder-controller.ts
│   │   │   │   ├── overlay
│   │   │   │   │   ├── canvas-overlay.ts
│   │   │   │   │   └── handles-controller.ts
│   │   │   │   ├── selection
│   │   │   │   │   └── selection-engine.ts
│   │   │   │   ├── ui
│   │   │   │   │   ├── breadcrumbs.ts
│   │   │   │   │   ├── floating-drag.ts
│   │   │   │   │   ├── icons.ts
│   │   │   │   │   ├── property-panel
│   │   │   │   │   │   ├── class-editor.ts
│   │   │   │   │   │   ├── components
│   │   │   │   │   │   │   ├── alignment-grid.ts
│   │   │   │   │   │   │   ├── icon-button-group.ts
│   │   │   │   │   │   │   ├── input-container.ts
│   │   │   │   │   │   │   ├── slider-input.ts
│   │   │   │   │   │   │   └── token-pill.ts
│   │   │   │   │   │   ├── components-tree.ts
│   │   │   │   │   │   ├── controls
│   │   │   │   │   │   │   ├── appearance-control.ts
│   │   │   │   │   │   │   ├── background-control.ts
│   │   │   │   │   │   │   ├── border-control.ts
│   │   │   │   │   │   │   ├── color-field.ts
│   │   │   │   │   │   │   ├── css-helpers.ts
│   │   │   │   │   │   │   ├── effects-control.ts
│   │   │   │   │   │   │   ├── gradient-control.ts
│   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   ├── layout-control.ts
│   │   │   │   │   │   │   ├── number-stepping.ts
│   │   │   │   │   │   │   ├── position-control.ts
│   │   │   │   │   │   │   ├── size-control.ts
│   │   │   │   │   │   │   ├── spacing-control.ts
│   │   │   │   │   │   │   ├── token-picker.ts
│   │   │   │   │   │   │   └── typography-control.ts
│   │   │   │   │   │   ├── css-defaults.ts
│   │   │   │   │   │   ├── css-panel.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── property-panel.ts
│   │   │   │   │   │   ├── props-panel.ts
│   │   │   │   │   │   └── types.ts
│   │   │   │   │   ├── shadow-host.ts
│   │   │   │   │   └── toolbar.ts
│   │   │   │   └── utils
│   │   │   │       └── disposables.ts
│   │   │   ├── web-editor-v2.ts
│   │   │   └── welcome
│   │   │       ├── App.vue
│   │   │       ├── index.html
│   │   │       └── main.ts
│   │   ├── env.d.ts
│   │   ├── eslint.config.js
│   │   ├── inject-scripts
│   │   │   ├── accessibility-tree-helper.js
│   │   │   ├── click-helper.js
│   │   │   ├── dom-observer.js
│   │   │   ├── element-marker.js
│   │   │   ├── element-picker.js
│   │   │   ├── fill-helper.js
│   │   │   ├── inject-bridge.js
│   │   │   ├── interactive-elements-helper.js
│   │   │   ├── keyboard-helper.js
│   │   │   ├── network-helper.js
│   │   │   ├── props-agent.js
│   │   │   ├── recorder.js
│   │   │   ├── screenshot-helper.js
│   │   │   ├── wait-helper.js
│   │   │   ├── web-editor.js
│   │   │   └── web-fetcher-helper.js
│   │   ├── LICENSE
│   │   ├── package.json
│   │   ├── public
│   │   │   ├── icon
│   │   │   │   ├── 128.png
│   │   │   │   ├── 16.png
│   │   │   │   ├── 32.png
│   │   │   │   ├── 48.png
│   │   │   │   └── 96.png
│   │   │   ├── libs
│   │   │   │   └── ort.min.js
│   │   │   └── wxt.svg
│   │   ├── README.md
│   │   ├── shared
│   │   │   ├── element-picker
│   │   │   │   ├── controller.ts
│   │   │   │   └── index.ts
│   │   │   ├── quick-panel
│   │   │   │   ├── core
│   │   │   │   │   ├── agent-bridge.ts
│   │   │   │   │   ├── search-engine.ts
│   │   │   │   │   └── types.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── providers
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── tabs-provider.ts
│   │   │   │   └── ui
│   │   │   │       ├── ai-chat-panel.ts
│   │   │   │       ├── index.ts
│   │   │   │       ├── markdown-renderer.ts
│   │   │   │       ├── message-renderer.ts
│   │   │   │       ├── panel-shell.ts
│   │   │   │       ├── quick-entries.ts
│   │   │   │       ├── search-input.ts
│   │   │   │       ├── shadow-host.ts
│   │   │   │       └── styles.ts
│   │   │   └── selector
│   │   │       ├── dom-path.ts
│   │   │       ├── fingerprint.ts
│   │   │       ├── generator.ts
│   │   │       ├── index.ts
│   │   │       ├── locator.ts
│   │   │       ├── shadow-dom.ts
│   │   │       ├── stability.ts
│   │   │       ├── strategies
│   │   │       │   ├── anchor-relpath.ts
│   │   │       │   ├── aria.ts
│   │   │       │   ├── css-path.ts
│   │   │       │   ├── css-unique.ts
│   │   │       │   ├── index.ts
│   │   │       │   ├── testid.ts
│   │   │       │   └── text.ts
│   │   │       └── types.ts
│   │   ├── tailwind.config.ts
│   │   ├── tests
│   │   │   ├── __mocks__
│   │   │   │   └── hnswlib-wasm-static.ts
│   │   │   ├── record-replay
│   │   │   │   ├── _test-helpers.ts
│   │   │   │   ├── adapter-policy.contract.test.ts
│   │   │   │   ├── flow-store-strip-steps.contract.test.ts
│   │   │   │   ├── high-risk-actions.integration.test.ts
│   │   │   │   ├── hybrid-actions.integration.test.ts
│   │   │   │   ├── script-control-flow.integration.test.ts
│   │   │   │   ├── session-dag-sync.contract.test.ts
│   │   │   │   ├── step-executor.contract.test.ts
│   │   │   │   └── tab-cursor.integration.test.ts
│   │   │   ├── record-replay-v3
│   │   │   │   ├── command-trigger.test.ts
│   │   │   │   ├── context-menu-trigger.test.ts
│   │   │   │   ├── cron-trigger.test.ts
│   │   │   │   ├── debugger.contract.test.ts
│   │   │   │   ├── dom-trigger.test.ts
│   │   │   │   ├── e2e.integration.test.ts
│   │   │   │   ├── events.contract.test.ts
│   │   │   │   ├── interval-trigger.test.ts
│   │   │   │   ├── manual-trigger.test.ts
│   │   │   │   ├── once-trigger.test.ts
│   │   │   │   ├── queue.contract.test.ts
│   │   │   │   ├── recovery.test.ts
│   │   │   │   ├── rpc-api.test.ts
│   │   │   │   ├── runner.onError.contract.test.ts
│   │   │   │   ├── scheduler-integration.test.ts
│   │   │   │   ├── scheduler.test.ts
│   │   │   │   ├── spec-smoke.test.ts
│   │   │   │   ├── trigger-manager.test.ts
│   │   │   │   ├── triggers.test.ts
│   │   │   │   ├── url-trigger.test.ts
│   │   │   │   ├── v2-action-adapter.test.ts
│   │   │   │   ├── v2-adapter-integration.test.ts
│   │   │   │   ├── v2-to-v3-conversion.test.ts
│   │   │   │   └── v3-e2e-harness.ts
│   │   │   ├── vitest.setup.ts
│   │   │   └── web-editor-v2
│   │   │       ├── design-tokens.test.ts
│   │   │       ├── drag-reorder-controller.test.ts
│   │   │       ├── event-controller.test.ts
│   │   │       ├── locator.test.ts
│   │   │       ├── property-panel-live-sync.test.ts
│   │   │       ├── selection-engine.test.ts
│   │   │       ├── snap-engine.test.ts
│   │   │       └── test-utils
│   │   │           └── dom.ts
│   │   ├── tsconfig.json
│   │   ├── types
│   │   │   ├── gifenc.d.ts
│   │   │   └── icons.d.ts
│   │   ├── utils
│   │   │   ├── cdp-session-manager.ts
│   │   │   ├── content-indexer.ts
│   │   │   ├── i18n.ts
│   │   │   ├── image-utils.ts
│   │   │   ├── indexeddb-client.ts
│   │   │   ├── lru-cache.ts
│   │   │   ├── model-cache-manager.ts
│   │   │   ├── offscreen-manager.ts
│   │   │   ├── output-sanitizer.ts
│   │   │   ├── screenshot-context.ts
│   │   │   ├── semantic-similarity-engine.ts
│   │   │   ├── simd-math-engine.ts
│   │   │   ├── text-chunker.ts
│   │   │   └── vector-database.ts
│   │   ├── vitest.config.ts
│   │   ├── workers
│   │   │   ├── ort-wasm-simd-threaded.jsep.mjs
│   │   │   ├── ort-wasm-simd-threaded.jsep.wasm
│   │   │   ├── ort-wasm-simd-threaded.mjs
│   │   │   ├── ort-wasm-simd-threaded.wasm
│   │   │   ├── simd_math_bg.wasm
│   │   │   ├── simd_math.js
│   │   │   └── similarity.worker.js
│   │   └── wxt.config.ts
│   └── native-server
│       ├── .npmignore
│       ├── debug.sh
│       ├── install.md
│       ├── jest.config.js
│       ├── package.json
│       ├── README.md
│       ├── src
│       │   ├── agent
│       │   │   ├── attachment-service.ts
│       │   │   ├── ccr-detector.ts
│       │   │   ├── chat-service.ts
│       │   │   ├── db
│       │   │   │   ├── client.ts
│       │   │   │   ├── index.ts
│       │   │   │   └── schema.ts
│       │   │   ├── directory-picker.ts
│       │   │   ├── engines
│       │   │   │   ├── claude.ts
│       │   │   │   ├── codex.ts
│       │   │   │   └── types.ts
│       │   │   ├── message-service.ts
│       │   │   ├── open-project.ts
│       │   │   ├── project-service.ts
│       │   │   ├── project-types.ts
│       │   │   ├── session-service.ts
│       │   │   ├── storage.ts
│       │   │   ├── stream-manager.ts
│       │   │   ├── tool-bridge.ts
│       │   │   └── types.ts
│       │   ├── cli.ts
│       │   ├── constant
│       │   │   └── index.ts
│       │   ├── file-handler.ts
│       │   ├── index.ts
│       │   ├── mcp
│       │   │   ├── mcp-server-stdio.ts
│       │   │   ├── mcp-server.ts
│       │   │   ├── register-tools.ts
│       │   │   └── stdio-config.json
│       │   ├── native-messaging-host.ts
│       │   ├── scripts
│       │   │   ├── browser-config.ts
│       │   │   ├── build.ts
│       │   │   ├── constant.ts
│       │   │   ├── doctor.ts
│       │   │   ├── postinstall.ts
│       │   │   ├── register-dev.ts
│       │   │   ├── register.ts
│       │   │   ├── report.ts
│       │   │   ├── run_host.bat
│       │   │   ├── run_host.sh
│       │   │   └── utils.ts
│       │   ├── server
│       │   │   ├── index.ts
│       │   │   ├── routes
│       │   │   │   ├── agent.ts
│       │   │   │   └── index.ts
│       │   │   └── server.test.ts
│       │   ├── shims
│       │   │   └── devtools.d.ts
│       │   ├── trace-analyzer.ts
│       │   ├── types
│       │   │   └── devtools-frontend.d.ts
│       │   └── util
│       │       └── logger.ts
│       └── tsconfig.json
├── commitlint.config.cjs
├── docs
│   ├── ARCHITECTURE_zh.md
│   ├── ARCHITECTURE.md
│   ├── CHANGELOG.md
│   ├── CONTRIBUTING_zh.md
│   ├── CONTRIBUTING.md
│   ├── ISSUE.md
│   ├── mcp-cli-config.md
│   ├── TOOLS_zh.md
│   ├── TOOLS.md
│   ├── TROUBLESHOOTING_zh.md
│   ├── TROUBLESHOOTING.md
│   ├── VisualEditor_zh.md
│   ├── VisualEditor.md
│   └── WINDOWS_INSTALL_zh.md
├── eslint.config.js
├── LICENSE
├── package.json
├── packages
│   ├── shared
│   │   ├── package.json
│   │   ├── src
│   │   │   ├── agent-types.ts
│   │   │   ├── constants.ts
│   │   │   ├── index.ts
│   │   │   ├── labels.ts
│   │   │   ├── node-spec-registry.ts
│   │   │   ├── node-spec.ts
│   │   │   ├── node-specs-builtin.ts
│   │   │   ├── rr-graph.ts
│   │   │   ├── step-types.ts
│   │   │   ├── tools.ts
│   │   │   └── types.ts
│   │   └── tsconfig.json
│   └── wasm-simd
│       ├── .gitignore
│       ├── BUILD.md
│       ├── Cargo.toml
│       ├── package.json
│       ├── README.md
│       └── src
│           └── lib.rs
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
├── prompt
│   ├── content-analize.md
│   ├── excalidraw-prompt.md
│   └── modify-web.md
├── README_zh.md
├── README.md
└── releases
    ├── chrome-extension
    │   └── latest
    │       └── chrome-mcp-server-lastest.zip
    └── README.md
```

# Files

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/web-editor-v2/ui/shadow-host.ts:
--------------------------------------------------------------------------------

```typescript
   1 | /**
   2 |  * Shadow DOM Host
   3 |  *
   4 |  * Creates an isolated container for the Web Editor UI using Shadow DOM.
   5 |  * Provides:
   6 |  * - Style isolation (no CSS bleed in/out)
   7 |  * - Event isolation (UI events don't bubble to page)
   8 |  * - Overlay container for Canvas/visual feedback
   9 |  * - UI container for panels/controls
  10 |  */
  11 | 
  12 | import {
  13 |   WEB_EDITOR_V2_COLORS,
  14 |   WEB_EDITOR_V2_HOST_ID,
  15 |   WEB_EDITOR_V2_OVERLAY_ID,
  16 |   WEB_EDITOR_V2_UI_ID,
  17 |   WEB_EDITOR_V2_Z_INDEX,
  18 | } from '../constants';
  19 | import { Disposer } from '../utils/disposables';
  20 | 
  21 | // =============================================================================
  22 | // Types
  23 | // =============================================================================
  24 | 
  25 | /** Elements exposed by the shadow host */
  26 | export interface ShadowHostElements {
  27 |   /** The host element attached to the document */
  28 |   host: HTMLDivElement;
  29 |   /** The shadow root */
  30 |   shadowRoot: ShadowRoot;
  31 |   /** Container for overlay elements (Canvas, guides, etc.) */
  32 |   overlayRoot: HTMLDivElement;
  33 |   /** Container for UI elements (panels, toolbar, etc.) */
  34 |   uiRoot: HTMLDivElement;
  35 | }
  36 | 
  37 | /** Options for mounting the shadow host (placeholder for future extension) */
  38 | export type ShadowHostOptions = Record<string, never>;
  39 | 
  40 | /** Interface for the shadow host manager */
  41 | export interface ShadowHostManager {
  42 |   /** Get the shadow host elements (null if not mounted) */
  43 |   getElements(): ShadowHostElements | null;
  44 |   /** Check if a node is part of the editor overlay */
  45 |   isOverlayElement(node: unknown): boolean;
  46 |   /** Check if an event originated from the editor UI */
  47 |   isEventFromUi(event: Event): boolean;
  48 |   /** Dispose and unmount the shadow host */
  49 |   dispose(): void;
  50 | }
  51 | 
  52 | // =============================================================================
  53 | // Styles
  54 | // =============================================================================
  55 | 
  56 | const SHADOW_HOST_STYLES = /* css */ `
  57 |   :host {
  58 |     all: initial;
  59 | 
  60 |     /* Design tokens aligned with attr-ui.html design spec */
  61 |     /* Surface colors */
  62 |     --we-surface-bg: #ffffff;
  63 |     --we-surface-secondary: #fafafa;
  64 | 
  65 |     /* Control colors - input containers use gray bg */
  66 |     --we-control-bg: #f3f3f3;
  67 |     --we-control-bg-hover: #e8e8e8;
  68 |     --we-control-border-hover: #e0e0e0;
  69 |     --we-control-bg-focus: #ffffff;
  70 |     --we-control-border-focus: #3b82f6;
  71 | 
  72 |     /* Border colors */
  73 |     --we-border-subtle: #e5e5e5;
  74 |     --we-border-strong: #d4d4d4;
  75 |     --we-border-section: #f3f3f3;
  76 | 
  77 |     /* Text colors */
  78 |     --we-text-primary: #333333;
  79 |     --we-text-secondary: #737373;
  80 |     --we-text-muted: #a3a3a3;
  81 | 
  82 |     /* Accent surfaces (used by CSS/Props panels) */
  83 |     --we-accent-info-bg: rgba(59, 130, 246, 0.08);
  84 |     --we-accent-brand-bg: rgba(99, 102, 241, 0.12);
  85 |     --we-accent-brand-border: rgba(99, 102, 241, 0.25);
  86 |     --we-accent-warning-bg: rgba(251, 191, 36, 0.14);
  87 |     --we-accent-warning-border: rgba(251, 191, 36, 0.25);
  88 |     --we-accent-danger-bg: rgba(248, 113, 113, 0.12);
  89 |     --we-accent-danger-border: rgba(248, 113, 113, 0.25);
  90 | 
  91 |     /* Shadows - Tailwind-like shadow-xl */
  92 |     --we-shadow-subtle: 0 1px 2px rgba(0, 0, 0, 0.05);
  93 |     --we-shadow-panel: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);
  94 |     --we-shadow-tab: 0 1px 2px rgba(0, 0, 0, 0.05);
  95 | 
  96 |     /* Radii */
  97 |     --we-radius-panel: 8px;
  98 |     --we-radius-control: 6px;
  99 |     --we-radius-tab: 4px;
 100 | 
 101 |     /* Sizes */
 102 |     --we-icon-btn-size: 24px;
 103 | 
 104 |     /* Focus ring - blue inset border style */
 105 |     --we-focus-ring: #3b82f6;
 106 | 
 107 |     /* Motion - bounce easing for toolbar animations */
 108 |     --we-ease-bounce: cubic-bezier(0.68, -0.55, 0.265, 1.55);
 109 |   }
 110 | 
 111 |   *,
 112 |   *::before,
 113 |   *::after {
 114 |     box-sizing: border-box;
 115 |   }
 116 | 
 117 |   /* Overlay container - for Canvas and visual feedback */
 118 |   #${WEB_EDITOR_V2_OVERLAY_ID} {
 119 |     position: fixed;
 120 |     inset: 0;
 121 |     pointer-events: none;
 122 |     contain: layout style;
 123 |   }
 124 | 
 125 |   /* ==========================================================================
 126 |    * Resize Handles (Phase 4.9)
 127 |    * ========================================================================== */
 128 | 
 129 |   /* Handles layer - covers viewport, pass-through by default */
 130 |   .we-handles-layer {
 131 |     position: absolute;
 132 |     inset: 0;
 133 |     pointer-events: none;
 134 |     contain: layout style paint;
 135 |   }
 136 | 
 137 |   /* Selection frame - positioned by selection rect */
 138 |   .we-selection-frame {
 139 |     position: absolute;
 140 |     top: 0;
 141 |     left: 0;
 142 |     width: 0;
 143 |     height: 0;
 144 |     transform: translate3d(0, 0, 0);
 145 |     pointer-events: none;
 146 |     will-change: transform, width, height;
 147 |   }
 148 | 
 149 |   /* Individual resize handle */
 150 |   .we-resize-handle {
 151 |     position: absolute;
 152 |     width: 8px;
 153 |     height: 8px;
 154 |     border-radius: 2px;
 155 |     background: rgba(255, 255, 255, 0.98);
 156 |     border: 1px solid ${WEB_EDITOR_V2_COLORS.selectionBorder};
 157 |     box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
 158 |     pointer-events: auto;
 159 |     touch-action: none;
 160 |     user-select: none;
 161 |     transition: background-color 0.1s ease, border-color 0.1s ease, transform 0.1s ease;
 162 |   }
 163 | 
 164 |   .we-resize-handle:hover {
 165 |     background: ${WEB_EDITOR_V2_COLORS.selectionBorder};
 166 |     border-color: ${WEB_EDITOR_V2_COLORS.selectionBorder};
 167 |     transform: translate(-50%, -50%) scale(1.15);
 168 |   }
 169 | 
 170 |   .we-resize-handle:active {
 171 |     transform: translate(-50%, -50%) scale(1.0);
 172 |   }
 173 | 
 174 |   /* Handle positions - all use translate(-50%, -50%) as base */
 175 |   .we-resize-handle[data-dir="n"]  { left: 50%; top: 0; transform: translate(-50%, -50%); cursor: ns-resize; }
 176 |   .we-resize-handle[data-dir="s"]  { left: 50%; top: 100%; transform: translate(-50%, -50%); cursor: ns-resize; }
 177 |   .we-resize-handle[data-dir="e"]  { left: 100%; top: 50%; transform: translate(-50%, -50%); cursor: ew-resize; }
 178 |   .we-resize-handle[data-dir="w"]  { left: 0; top: 50%; transform: translate(-50%, -50%); cursor: ew-resize; }
 179 |   .we-resize-handle[data-dir="nw"] { left: 0; top: 0; transform: translate(-50%, -50%); cursor: nwse-resize; }
 180 |   .we-resize-handle[data-dir="ne"] { left: 100%; top: 0; transform: translate(-50%, -50%); cursor: nesw-resize; }
 181 |   .we-resize-handle[data-dir="sw"] { left: 0; top: 100%; transform: translate(-50%, -50%); cursor: nesw-resize; }
 182 |   .we-resize-handle[data-dir="se"] { left: 100%; top: 100%; transform: translate(-50%, -50%); cursor: nwse-resize; }
 183 | 
 184 |   /* Size HUD - shows W×H while resizing */
 185 |   .we-size-hud {
 186 |     position: absolute;
 187 |     left: 50%;
 188 |     top: 0;
 189 |     transform: translate(-50%, calc(-100% - 8px));
 190 |     padding: 3px 8px;
 191 |     font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
 192 |     font-size: 11px;
 193 |     font-weight: 600;
 194 |     line-height: 1.2;
 195 |     color: rgba(255, 255, 255, 0.98);
 196 |     background: rgba(15, 23, 42, 0.92);
 197 |     border: 1px solid rgba(51, 65, 85, 0.5);
 198 |     border-radius: 4px;
 199 |     pointer-events: none;
 200 |     user-select: none;
 201 |     white-space: nowrap;
 202 |     box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
 203 |     backdrop-filter: blur(4px);
 204 |     -webkit-backdrop-filter: blur(4px);
 205 |   }
 206 | 
 207 |   /* ==========================================================================
 208 |    * Performance HUD (Phase 5.3)
 209 |    * ========================================================================== */
 210 | 
 211 |   .we-perf-hud {
 212 |     position: fixed;
 213 |     left: 12px;
 214 |     bottom: 12px;
 215 |     padding: 8px 10px;
 216 |     border-radius: 10px;
 217 |     background: rgba(15, 23, 42, 0.78);
 218 |     border: 1px solid rgba(51, 65, 85, 0.45);
 219 |     color: rgba(255, 255, 255, 0.96);
 220 |     font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
 221 |     font-size: 12px;
 222 |     line-height: 1.25;
 223 |     pointer-events: none;
 224 |     user-select: none;
 225 |     white-space: nowrap;
 226 |     z-index: 10;
 227 |     box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18);
 228 |     backdrop-filter: blur(6px);
 229 |     -webkit-backdrop-filter: blur(6px);
 230 |     font-variant-numeric: tabular-nums;
 231 |   }
 232 | 
 233 |   .we-perf-hud-line + .we-perf-hud-line {
 234 |     margin-top: 4px;
 235 |   }
 236 | 
 237 |   /* UI container - for panels and controls */
 238 |   /* Position below toolbar: 16px (toolbar top) + 40px (toolbar height) + 8px (gap) = 64px */
 239 |   #${WEB_EDITOR_V2_UI_ID} {
 240 |     position: fixed;
 241 |     top: 64px;
 242 |     right: 16px;
 243 |     pointer-events: auto;
 244 |     /* Inter font with system fallbacks (aligned with design spec) */
 245 |     font-family: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
 246 |     font-size: 11px;
 247 |     line-height: 1.4;
 248 |     color: var(--we-text-primary);
 249 |     -webkit-font-smoothing: antialiased;
 250 |   }
 251 | 
 252 |   /* Panel styles */
 253 |   /* max-height: 100vh - 64px (top offset) - 16px (bottom margin) = 100vh - 80px */
 254 |   .we-panel {
 255 |     width: 280px;
 256 |     max-width: calc(100vw - 32px);
 257 |     max-height: calc(100vh - 80px);
 258 |     background: var(--we-surface-bg);
 259 |     border: 1px solid var(--we-border-subtle);
 260 |     border-radius: var(--we-radius-panel);
 261 |     box-shadow: var(--we-shadow-panel);
 262 |     overflow: hidden;
 263 |     contain: layout style paint;
 264 |   }
 265 | 
 266 |   .we-header {
 267 |     display: flex;
 268 |     align-items: center;
 269 |     justify-content: space-between;
 270 |     gap: 8px;
 271 |     padding: 8px 12px;
 272 |     background: var(--we-surface-bg);
 273 |     border-bottom: 1px solid var(--we-border-subtle);
 274 |     user-select: none;
 275 |   }
 276 | 
 277 |   .we-title {
 278 |     display: flex;
 279 |     align-items: center;
 280 |     gap: 6px;
 281 |     font-size: 11px;
 282 |     font-weight: 600;
 283 |     color: var(--we-text-primary);
 284 |   }
 285 | 
 286 |   .we-badge {
 287 |     font-size: 10px;
 288 |     font-weight: 500;
 289 |     padding: 2px 6px;
 290 |     background: linear-gradient(135deg, #6366f1, #8b5cf6);
 291 |     color: white;
 292 |     border-radius: 4px;
 293 |   }
 294 | 
 295 |   .we-btn {
 296 |     display: inline-flex;
 297 |     align-items: center;
 298 |     justify-content: center;
 299 |     gap: 6px;
 300 |     padding: 6px 12px;
 301 |     font-size: 12px;
 302 |     font-weight: 500;
 303 |     color: #475569;
 304 |     background: white;
 305 |     border: 1px solid rgba(148, 163, 184, 0.5);
 306 |     border-radius: 6px;
 307 |     cursor: pointer;
 308 |     transition: all 0.15s ease;
 309 |   }
 310 | 
 311 |   .we-btn:hover {
 312 |     background: #f8fafc;
 313 |     border-color: rgba(148, 163, 184, 0.7);
 314 |   }
 315 | 
 316 |   .we-btn:active {
 317 |     background: #f1f5f9;
 318 |   }
 319 | 
 320 |   .we-btn:focus-visible {
 321 |     outline: none;
 322 |     box-shadow: 0 0 0 2px var(--we-focus-ring);
 323 |   }
 324 | 
 325 |   .we-btn:disabled {
 326 |     opacity: 0.55;
 327 |     cursor: not-allowed;
 328 |   }
 329 | 
 330 |   .we-btn--primary {
 331 |     background: linear-gradient(135deg, #0f172a, #1e293b);
 332 |     color: #ffffff;
 333 |     border-color: rgba(15, 23, 42, 0.5);
 334 |   }
 335 | 
 336 |   .we-btn--primary:hover:not(:disabled) {
 337 |     background: linear-gradient(135deg, #1e293b, #334155);
 338 |     border-color: rgba(15, 23, 42, 0.65);
 339 |   }
 340 | 
 341 |   .we-btn--danger {
 342 |     color: #b91c1c;
 343 |     border-color: rgba(248, 113, 113, 0.45);
 344 |   }
 345 | 
 346 |   .we-btn--danger:hover:not(:disabled) {
 347 |     background: rgba(248, 113, 113, 0.08);
 348 |     border-color: rgba(248, 113, 113, 0.6);
 349 |   }
 350 | 
 351 |   /* Icon button (28x28) - used for window controls (close/minimize, etc.) */
 352 |   .we-icon-btn {
 353 |     width: var(--we-icon-btn-size);
 354 |     height: var(--we-icon-btn-size);
 355 |     display: inline-flex;
 356 |     align-items: center;
 357 |     justify-content: center;
 358 |     padding: 0;
 359 |     background: var(--we-control-bg);
 360 |     border: 0;
 361 |     border-radius: var(--we-radius-control);
 362 |     color: var(--we-text-secondary);
 363 |     cursor: pointer;
 364 |     transition: background 0.15s ease, box-shadow 0.15s ease;
 365 |   }
 366 | 
 367 |   .we-icon-btn:hover {
 368 |     background: var(--we-control-bg-hover);
 369 |     color: var(--we-text-primary);
 370 |   }
 371 | 
 372 |   .we-icon-btn:active {
 373 |     background: var(--we-control-bg-hover);
 374 |   }
 375 | 
 376 |   .we-icon-btn:focus-visible {
 377 |     outline: none;
 378 |     box-shadow: 0 0 0 2px var(--we-focus-ring);
 379 |   }
 380 | 
 381 |   .we-icon-btn svg {
 382 |     width: 16px;
 383 |     height: 16px;
 384 |     display: block;
 385 |   }
 386 | 
 387 |   /* Drag handle (grip) - used for repositioning floating UI */
 388 |   .we-drag-handle {
 389 |     width: var(--we-icon-btn-size);
 390 |     height: var(--we-icon-btn-size);
 391 |     display: inline-flex;
 392 |     align-items: center;
 393 |     justify-content: center;
 394 |     flex-shrink: 0;
 395 |     padding: 0;
 396 |     background: transparent;
 397 |     border: 0;
 398 |     border-radius: var(--we-radius-control);
 399 |     color: var(--we-text-muted);
 400 |     cursor: grab;
 401 |     touch-action: none;
 402 |     user-select: none;
 403 |     transition: background 0.15s ease, color 0.15s ease, box-shadow 0.15s ease;
 404 |   }
 405 | 
 406 |   .we-drag-handle:hover {
 407 |     background: var(--we-control-bg);
 408 |     color: var(--we-text-secondary);
 409 |   }
 410 | 
 411 |   .we-drag-handle:focus-visible {
 412 |     outline: none;
 413 |     box-shadow: 0 0 0 2px var(--we-focus-ring);
 414 |   }
 415 | 
 416 |   .we-drag-handle:active,
 417 |   .we-drag-handle[data-dragging="true"] {
 418 |     cursor: grabbing;
 419 |     background: var(--we-control-bg-hover);
 420 |     color: var(--we-text-primary);
 421 |   }
 422 | 
 423 |   .we-drag-handle svg {
 424 |     width: 14px;
 425 |     height: 14px;
 426 |     display: block;
 427 |   }
 428 | 
 429 |   /* ==========================================================================
 430 |    * Toolbar (Redesigned per toolbar-ui.html design spec)
 431 |    * - Bounce easing animations
 432 |    * - Collapsible pill (580x40 <-> 40x40)
 433 |    * - Grip icon rotation on collapse
 434 |    * ========================================================================== */
 435 | 
 436 |   .we-toolbar {
 437 |     position: fixed;
 438 |     left: 50%;
 439 |     top: 16px;
 440 |     transform: translateX(-50%);
 441 |     width: 580px;
 442 |     height: 40px;
 443 |     max-width: calc(100vw - 32px);
 444 |     display: flex;
 445 |     align-items: center;
 446 |     background: #ffffff;
 447 |     border-radius: 999px;
 448 |     box-shadow: 0 -4px 10px -6px rgba(15, 23, 42, 0.18),
 449 |       0 10px 15px -3px rgba(203, 213, 225, 0.5),
 450 |       0 4px 6px -4px rgba(203, 213, 225, 0.5);
 451 |     pointer-events: auto;
 452 |     user-select: none;
 453 |     font-family: 'Inter', system-ui, -apple-system, sans-serif;
 454 |     font-size: 11px;
 455 |     color: #475569;
 456 |     transition: width 500ms var(--we-ease-bounce), height 500ms var(--we-ease-bounce);
 457 |     overflow: visible;
 458 |     will-change: width, height;
 459 |   }
 460 | 
 461 |   .we-toolbar[data-position="bottom"] {
 462 |     top: auto;
 463 |     bottom: 16px;
 464 |   }
 465 | 
 466 |   /* Dragged toolbar: use left/top (inline styles) instead of docked centering */
 467 |   .we-toolbar[data-dragged="true"] {
 468 |     left: auto;
 469 |     right: auto;
 470 |     top: auto;
 471 |     bottom: auto;
 472 |     transform: none;
 473 |   }
 474 | 
 475 |   /* Collapsed toolbar - 40x40 circle */
 476 |   .we-toolbar[data-minimized="true"] {
 477 |     width: 40px;
 478 |     height: 40px;
 479 |   }
 480 | 
 481 |   /* Toolbar content row (collapses with toolbar) */
 482 |   .we-toolbar-content {
 483 |     display: flex;
 484 |     align-items: center;
 485 |     flex: 1;
 486 |     min-width: 0;
 487 |     gap: 10px;
 488 |     white-space: nowrap;
 489 |     padding-right: 8px;
 490 |     transition: opacity 350ms ease, transform 400ms var(--we-ease-bounce);
 491 |     will-change: opacity, transform;
 492 |   }
 493 | 
 494 |   .we-toolbar[data-minimized="true"] .we-toolbar-content {
 495 |     opacity: 0;
 496 |     transform: translateX(-16px) scale(0.95);
 497 |     pointer-events: none;
 498 |   }
 499 | 
 500 |   .we-toolbar[data-minimized="false"] .we-toolbar-content {
 501 |     opacity: 1;
 502 |     transform: translateX(0) scale(1);
 503 |     pointer-events: auto;
 504 |   }
 505 | 
 506 |   /* Grip toggle button (40x40, hover slate-50, active scale-90) */
 507 |   .we-toolbar .we-drag-handle {
 508 |     width: 40px;
 509 |     height: 40px;
 510 |     flex-shrink: 0;
 511 |     border-radius: 999px;
 512 |     cursor: pointer;
 513 |     transition: background-color 150ms ease, transform 150ms ease;
 514 |   }
 515 | 
 516 |   .we-toolbar .we-drag-handle:hover {
 517 |     background: #f8fafc;
 518 |   }
 519 | 
 520 |   .we-toolbar .we-drag-handle:active {
 521 |     transform: scale(0.9);
 522 |   }
 523 | 
 524 |   /* Grip icon rotation (collapsed 90deg -> expanded 0deg) */
 525 |   .we-toolbar .we-drag-handle svg {
 526 |     width: 16px;
 527 |     height: 16px;
 528 |     color: #94a3b8;
 529 |     transition: transform 500ms var(--we-ease-bounce);
 530 |     transform: rotate(0deg);
 531 |   }
 532 | 
 533 |   .we-toolbar[data-minimized="true"] .we-drag-handle svg {
 534 |     transform: rotate(90deg);
 535 |   }
 536 | 
 537 |   /* Status indicator: green dot + "Editor" label */
 538 |   .we-toolbar-indicator {
 539 |     display: inline-flex;
 540 |     align-items: center;
 541 |     gap: 6px;
 542 |     padding: 4px 10px;
 543 |     background: #f1f5f9;
 544 |     border-radius: 999px;
 545 |   }
 546 | 
 547 |   .we-toolbar-indicator-dot {
 548 |     width: 6px;
 549 |     height: 6px;
 550 |     border-radius: 999px;
 551 |     background: #10b981;
 552 |   }
 553 | 
 554 |   .we-toolbar-indicator-label {
 555 |     font-size: 11px;
 556 |     font-weight: 700;
 557 |     color: #334155;
 558 |     letter-spacing: 0.04em;
 559 |   }
 560 | 
 561 |   /* Status-driven dot color + pulse */
 562 |   .we-toolbar[data-status="progress"] .we-toolbar-indicator-dot {
 563 |     background: #6366f1;
 564 |     animation: we-toolbar-dot-pulse 1.5s ease-in-out infinite;
 565 |   }
 566 | 
 567 |   .we-toolbar[data-status="success"] .we-toolbar-indicator-dot {
 568 |     background: #10b981;
 569 |   }
 570 | 
 571 |   .we-toolbar[data-status="error"] .we-toolbar-indicator-dot {
 572 |     background: #ef4444;
 573 |   }
 574 | 
 575 |   @keyframes we-toolbar-dot-pulse {
 576 |     0%, 100% { opacity: 1; }
 577 |     50% { opacity: 0.55; }
 578 |   }
 579 | 
 580 |   /* Undo/Redo counts */
 581 |   .we-toolbar-history {
 582 |     display: flex;
 583 |     gap: 10px;
 584 |     font-size: 10px;
 585 |     font-weight: 500;
 586 |     color: #94a3b8;
 587 |     font-variant-numeric: tabular-nums;
 588 |   }
 589 | 
 590 |   .we-toolbar-history-value {
 591 |     color: #475569;
 592 |     font-weight: 700;
 593 |   }
 594 | 
 595 |   /* Divider */
 596 |   .we-toolbar-divider {
 597 |     width: 1px;
 598 |     height: 16px;
 599 |     background: #e2e8f0;
 600 |   }
 601 | 
 602 |   /* Structure group (Structure button + divider + Undo/Redo icons) */
 603 |   .we-toolbar-structure-group {
 604 |     display: inline-flex;
 605 |     align-items: center;
 606 |     background: #f1f5f9;
 607 |     border-radius: 999px;
 608 |     padding: 2px;
 609 |   }
 610 | 
 611 |   .we-toolbar-structure-btn {
 612 |     display: inline-flex;
 613 |     align-items: center;
 614 |     gap: 4px;
 615 |     padding: 4px 10px;
 616 |     font-size: 11px;
 617 |     font-weight: 500;
 618 |     color: #64748b;
 619 |     background: transparent;
 620 |     border: 0;
 621 |     border-radius: 999px;
 622 |     cursor: pointer;
 623 |     transition: color 150ms ease, background-color 150ms ease;
 624 |   }
 625 | 
 626 |   .we-toolbar-structure-btn:hover:not(:disabled) {
 627 |     color: #1e293b;
 628 |     background: #ffffff;
 629 |   }
 630 | 
 631 |   .we-toolbar-structure-btn:disabled {
 632 |     opacity: 0.55;
 633 |     cursor: not-allowed;
 634 |   }
 635 | 
 636 |   .we-toolbar-structure-btn svg {
 637 |     width: 10px;
 638 |     height: 10px;
 639 |     opacity: 0.5;
 640 |     display: block;
 641 |   }
 642 | 
 643 |   .we-toolbar-structure-separator {
 644 |     width: 1px;
 645 |     height: 12px;
 646 |     background: #e2e8f0;
 647 |   }
 648 | 
 649 |   .we-toolbar-group-icon-btn {
 650 |     display: inline-flex;
 651 |     align-items: center;
 652 |     justify-content: center;
 653 |     padding: 4px;
 654 |     background: transparent;
 655 |     border: 0;
 656 |     border-radius: 999px;
 657 |     color: #94a3b8;
 658 |     cursor: pointer;
 659 |     transition: color 150ms ease, background-color 150ms ease;
 660 |   }
 661 | 
 662 |   .we-toolbar-group-icon-btn:hover:not(:disabled) {
 663 |     color: #334155;
 664 |     background: #ffffff;
 665 |   }
 666 | 
 667 |   .we-toolbar-group-icon-btn:disabled {
 668 |     opacity: 0.45;
 669 |     cursor: not-allowed;
 670 |   }
 671 | 
 672 |   .we-toolbar-group-icon-btn svg {
 673 |     width: 14px;
 674 |     height: 14px;
 675 |     display: block;
 676 |   }
 677 | 
 678 |   /* End actions container: pushes apply + close to far right */
 679 |   .we-toolbar-end-actions {
 680 |     margin-left: auto;
 681 |     display: inline-flex;
 682 |     align-items: center;
 683 |     gap: 10px;
 684 |   }
 685 | 
 686 |   /* Apply button (indigo-500) */
 687 |   .we-toolbar-apply-btn {
 688 |     display: inline-flex;
 689 |     align-items: center;
 690 |     justify-content: center;
 691 |     padding: 6px 16px;
 692 |     font-size: 11px;
 693 |     font-weight: 700;
 694 |     color: #ffffff;
 695 |     background: #6366f1;
 696 |     border: 0;
 697 |     border-radius: 999px;
 698 |     cursor: pointer;
 699 |     transition: background-color 150ms ease, transform 150ms ease, box-shadow 150ms ease;
 700 |     box-shadow: 0 4px 6px -1px rgba(99, 102, 241, 0.25),
 701 |       0 2px 4px -2px rgba(99, 102, 241, 0.25);
 702 |   }
 703 | 
 704 |   .we-toolbar-apply-btn:hover:not(:disabled) {
 705 |     background: #4f46e5;
 706 |   }
 707 | 
 708 |   .we-toolbar-apply-btn:active:not(:disabled) {
 709 |     transform: scale(0.95);
 710 |   }
 711 | 
 712 |   .we-toolbar-apply-btn:disabled {
 713 |     opacity: 0.55;
 714 |     cursor: not-allowed;
 715 |     box-shadow: none;
 716 |   }
 717 | 
 718 |   /* Close button (red hover) */
 719 |   .we-toolbar-close-btn {
 720 |     display: inline-flex;
 721 |     align-items: center;
 722 |     justify-content: center;
 723 |     padding: 6px;
 724 |     background: transparent;
 725 |     border: 0;
 726 |     border-radius: 999px;
 727 |     color: #94a3b8;
 728 |     cursor: pointer;
 729 |     transition: color 150ms ease, background-color 150ms ease;
 730 |   }
 731 | 
 732 |   .we-toolbar-close-btn:hover:not(:disabled) {
 733 |     color: #ef4444;
 734 |     background: #fef2f2;
 735 |   }
 736 | 
 737 |   .we-toolbar-close-btn:disabled {
 738 |     opacity: 0.45;
 739 |     cursor: not-allowed;
 740 |   }
 741 | 
 742 |   .we-toolbar-close-btn svg {
 743 |     width: 14px;
 744 |     height: 14px;
 745 |     display: block;
 746 |   }
 747 | 
 748 |   /* Screen-reader-only utility */
 749 |   .we-sr-only {
 750 |     position: absolute;
 751 |     width: 1px;
 752 |     height: 1px;
 753 |     padding: 0;
 754 |     margin: -1px;
 755 |     overflow: hidden;
 756 |     clip: rect(0, 0, 0, 0);
 757 |     white-space: nowrap;
 758 |     border: 0;
 759 |   }
 760 | 
 761 |   /* Respect reduced motion preference */
 762 |   @media (prefers-reduced-motion: reduce) {
 763 |     .we-toolbar,
 764 |     .we-toolbar-content,
 765 |     .we-toolbar .we-drag-handle,
 766 |     .we-toolbar .we-drag-handle svg,
 767 |     .we-toolbar-structure-btn,
 768 |     .we-toolbar-group-icon-btn,
 769 |     .we-toolbar-apply-btn,
 770 |     .we-toolbar-close-btn {
 771 |       transition: none;
 772 |     }
 773 |   }
 774 | 
 775 |   /* ==========================================================================
 776 |      Breadcrumbs (Phase 2.2) - Anchored to selection element
 777 |      ========================================================================== */
 778 |   .we-breadcrumbs {
 779 |     position: fixed;
 780 |     /* left/top set dynamically via JS based on selection rect */
 781 |     left: 16px;
 782 |     top: 72px;
 783 |     width: auto;
 784 |     max-width: min(600px, calc(100vw - 400px));
 785 |     display: flex;
 786 |     align-items: center;
 787 |     gap: 2px;
 788 |     padding: 6px 12px;
 789 |     background: #5494D7;
 790 |     border: none;
 791 |     border-radius: 0;
 792 |     box-shadow: 0 2px 8px rgba(84, 148, 215, 0.3);
 793 |     pointer-events: auto;
 794 |     user-select: none;
 795 |     overflow-x: auto;
 796 |     white-space: nowrap;
 797 |     scrollbar-width: none;
 798 |     z-index: 5;
 799 |   }
 800 | 
 801 |   .we-breadcrumbs[data-hidden="true"] {
 802 |     display: none;
 803 |   }
 804 | 
 805 |   .we-breadcrumbs[data-position="bottom"] {
 806 |     top: auto;
 807 |     bottom: 72px;
 808 |   }
 809 | 
 810 |   .we-breadcrumbs::-webkit-scrollbar {
 811 |     display: none;
 812 |   }
 813 | 
 814 |   .we-crumb {
 815 |     display: inline-flex;
 816 |     align-items: center;
 817 |     max-width: 220px;
 818 |     padding: 2px 6px;
 819 |     border-radius: 3px;
 820 |     border: none;
 821 |     background: transparent;
 822 |     color: #ffffff;
 823 |     font-size: 12px;
 824 |     font-weight: 500;
 825 |     line-height: 1.2;
 826 |     cursor: pointer;
 827 |     overflow: hidden;
 828 |     text-overflow: ellipsis;
 829 |     transition: background 0.15s ease;
 830 |   }
 831 | 
 832 |   .we-crumb:hover {
 833 |     background: rgba(255, 255, 255, 0.15);
 834 |   }
 835 | 
 836 |   .we-crumb:active {
 837 |     background: rgba(255, 255, 255, 0.25);
 838 |   }
 839 | 
 840 |   .we-crumb:focus-visible {
 841 |     outline: none;
 842 |     box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5);
 843 |   }
 844 | 
 845 |   .we-crumb--current {
 846 |     background: rgba(255, 255, 255, 0.2);
 847 |   }
 848 | 
 849 |   .we-crumb-sep {
 850 |     display: inline-flex;
 851 |     align-items: center;
 852 |     justify-content: center;
 853 |     width: 14px;
 854 |     flex: 0 0 auto;
 855 |     color: rgba(255, 255, 255, 0.7);
 856 |     font-size: 12px;
 857 |   }
 858 | 
 859 |   .we-crumb-sep--shadow {
 860 |     color: rgba(255, 255, 255, 0.9);
 861 |   }
 862 | 
 863 |   .we-body {
 864 |     padding: 14px;
 865 |     color: #475569;
 866 |     font-size: 12px;
 867 |   }
 868 | 
 869 |   .we-status {
 870 |     display: flex;
 871 |     align-items: center;
 872 |     gap: 8px;
 873 |     padding: 8px 12px;
 874 |     background: rgba(34, 197, 94, 0.1);
 875 |     border-radius: 6px;
 876 |     color: #15803d;
 877 |     font-size: 12px;
 878 |   }
 879 | 
 880 |   .we-status-dot {
 881 |     width: 8px;
 882 |     height: 8px;
 883 |     background: #22c55e;
 884 |     border-radius: 50%;
 885 |     animation: pulse 2s ease-in-out infinite;
 886 |   }
 887 | 
 888 |   @keyframes pulse {
 889 |     0%, 100% { opacity: 1; }
 890 |     50% { opacity: 0.5; }
 891 |   }
 892 | 
 893 |   /* ==========================================================================
 894 |      Property Panel (Phase 3)
 895 |      ========================================================================== */
 896 | 
 897 |   .we-prop-panel {
 898 |     display: flex;
 899 |     flex-direction: column;
 900 |     max-height: calc(100vh - 80px);
 901 |   }
 902 | 
 903 |   /* Dragged property panel: becomes a floating fixed panel positioned via left/top (inline styles) */
 904 |   .we-prop-panel[data-dragged="true"][data-minimized="false"] {
 905 |     position: fixed;
 906 |     left: auto;
 907 |     right: auto;
 908 |     top: auto;
 909 |     bottom: auto;
 910 |   }
 911 | 
 912 |   /* Minimized property panel - becomes a small icon button fixed at top-right */
 913 |   .we-prop-panel[data-minimized="true"] {
 914 |     position: fixed;
 915 |     top: 16px;
 916 |     right: 16px;
 917 |     width: auto;
 918 |     max-height: none;
 919 |     background: transparent;
 920 |     border: 0;
 921 |     box-shadow: none;
 922 |     overflow: visible;
 923 |     z-index: 10;
 924 |   }
 925 | 
 926 |   .we-prop-panel[data-minimized="true"] .we-header {
 927 |     padding: 0;
 928 |     background: transparent;
 929 |     border-bottom: 0;
 930 |   }
 931 | 
 932 |   /* Symmetric header layout: drag (left) | tabs (center) | minimize (right) */
 933 |   .we-prop-panel .we-header {
 934 |     padding: 8px;
 935 |     gap: 4px;
 936 |   }
 937 | 
 938 |   .we-prop-panel .we-header .we-prop-tabs {
 939 |     flex: 1;
 940 |     justify-content: center;
 941 |   }
 942 | 
 943 |   /* Minimize button chevron rotation */
 944 |   .we-minimize-btn svg {
 945 |     transition: transform 200ms ease;
 946 |   }
 947 | 
 948 |   .we-prop-panel[data-minimized="true"] .we-minimize-btn svg {
 949 |     transform: rotate(180deg);
 950 |   }
 951 | 
 952 |   /* Header tooltips: show below to avoid being clipped by panel overflow */
 953 |   .we-prop-panel .we-header [data-tooltip]::after {
 954 |     bottom: auto;
 955 |     top: calc(100% + 6px);
 956 |   }
 957 | 
 958 |   .we-prop-panel .we-header [data-tooltip]::before {
 959 |     bottom: auto;
 960 |     top: calc(100% + 2px);
 961 |     border-top-color: transparent;
 962 |     border-bottom-color: var(--we-text-primary);
 963 |   }
 964 | 
 965 |   /* Tab container with pill/segmented style (aligned with design spec) */
 966 |   .we-prop-tabs {
 967 |     display: inline-flex;
 968 |     align-items: center;
 969 |     gap: 2px;
 970 |     padding: 2px;
 971 |     background: var(--we-control-bg);
 972 |     border-radius: var(--we-radius-tab);
 973 |   }
 974 | 
 975 |   .we-tab {
 976 |     border: 0;
 977 |     background: transparent;
 978 |     color: var(--we-text-secondary);
 979 |     padding: 4px 10px;
 980 |     border-radius: var(--we-radius-tab);
 981 |     cursor: pointer;
 982 |     font-size: 12px;
 983 |     font-weight: 500;
 984 |     transition: all 0.1s ease;
 985 |   }
 986 | 
 987 |   .we-tab:hover {
 988 |     color: var(--we-text-primary);
 989 |   }
 990 | 
 991 |   .we-tab:focus-visible {
 992 |     outline: none;
 993 |     box-shadow: inset 0 0 0 2px var(--we-focus-ring);
 994 |   }
 995 | 
 996 |   /* Active tab: white background with subtle shadow */
 997 |   .we-tab[aria-selected="true"] {
 998 |     background: var(--we-surface-bg);
 999 |     color: var(--we-text-primary);
1000 |     box-shadow: var(--we-shadow-tab);
1001 |   }
1002 | 
1003 |   .we-prop-body {
1004 |     flex: 1;
1005 |     overflow-y: auto;
1006 |     overflow-x: hidden;
1007 |     padding: 12px;
1008 |     padding-bottom: 80px; /* Extra space for scrolling (design spec: pb-20) */
1009 |     display: flex;
1010 |     flex-direction: column;
1011 |     gap: 12px;
1012 |     scrollbar-width: none; /* Firefox */
1013 |     -ms-overflow-style: none; /* IE 10+ */
1014 |   }
1015 | 
1016 |   /* Hide scrollbar for webkit browsers */
1017 |   .we-prop-body::-webkit-scrollbar {
1018 |     width: 0;
1019 |     height: 0;
1020 |   }
1021 | 
1022 |   /* Force hidden state for property panel sections during minimization */
1023 |   .we-prop-body[hidden],
1024 |   .we-prop-tabs[hidden] {
1025 |     display: none;
1026 |   }
1027 | 
1028 |   .we-prop-tab-content {
1029 |     display: flex;
1030 |     flex-direction: column;
1031 |     gap: 10px;
1032 |   }
1033 | 
1034 |   .we-prop-tab-content[hidden] {
1035 |     display: none;
1036 |   }
1037 | 
1038 |   .we-prop-empty {
1039 |     display: flex;
1040 |     align-items: center;
1041 |     justify-content: center;
1042 |     padding: 24px 12px;
1043 |     color: #64748b;
1044 |     font-size: 12px;
1045 |     text-align: center;
1046 |   }
1047 | 
1048 |   .we-prop-empty[hidden] {
1049 |     display: none;
1050 |   }
1051 | 
1052 |   .we-prop-panel[data-empty="true"] .we-prop-tab-content {
1053 |     display: none;
1054 |   }
1055 | 
1056 |   /* ==========================================================================
1057 |      Components Tree (Phase 3.2)
1058 |      ========================================================================== */
1059 | 
1060 |   .we-tree {
1061 |     font-size: 12px;
1062 |     line-height: 1.4;
1063 |   }
1064 | 
1065 |   .we-tree-empty {
1066 |     padding: 24px 12px;
1067 |     color: #64748b;
1068 |     text-align: center;
1069 |   }
1070 | 
1071 |   .we-tree-empty[hidden] {
1072 |     display: none;
1073 |   }
1074 | 
1075 |   .we-tree-list {
1076 |     display: flex;
1077 |     flex-direction: column;
1078 |   }
1079 | 
1080 |   .we-tree-list[hidden] {
1081 |     display: none;
1082 |   }
1083 | 
1084 |   .we-tree-item {
1085 |     display: flex;
1086 |     align-items: center;
1087 |     gap: 4px;
1088 |     padding: 6px 8px;
1089 |     cursor: pointer;
1090 |     border-radius: 4px;
1091 |     transition: background 0.12s;
1092 |     color: #475569;
1093 |   }
1094 | 
1095 |   .we-tree-item:hover {
1096 |     background: rgba(59, 130, 246, 0.08);
1097 |   }
1098 | 
1099 |   .we-tree-item--selected {
1100 |     background: rgba(59, 130, 246, 0.12);
1101 |     color: #1d4ed8;
1102 |     font-weight: 500;
1103 |   }
1104 | 
1105 |   .we-tree-item--selected:hover {
1106 |     background: rgba(59, 130, 246, 0.16);
1107 |   }
1108 | 
1109 |   .we-tree-item--ancestor {
1110 |     color: #64748b;
1111 |   }
1112 | 
1113 |   .we-tree-item--child {
1114 |     color: #64748b;
1115 |     font-size: 11px;
1116 |   }
1117 | 
1118 |   .we-tree-indent {
1119 |     color: #94a3b8;
1120 |     font-family: monospace;
1121 |     user-select: none;
1122 |   }
1123 | 
1124 |   .we-tree-icon {
1125 |     flex-shrink: 0;
1126 |     color: #94a3b8;
1127 |     font-size: 10px;
1128 |   }
1129 | 
1130 |   .we-tree-item--selected .we-tree-icon {
1131 |     color: #3b82f6;
1132 |   }
1133 | 
1134 |   .we-tree-label {
1135 |     flex: 1;
1136 |     min-width: 0;
1137 |     overflow: hidden;
1138 |     text-overflow: ellipsis;
1139 |     white-space: nowrap;
1140 |     font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
1141 |   }
1142 | 
1143 |   /* ==========================================================================
1144 |      Control Groups (Section style - aligned with design spec)
1145 |      Uses separator lines instead of card borders
1146 |      ========================================================================== */
1147 | 
1148 |   .we-group {
1149 |     /* No card-style border, use separator lines between sections */
1150 |     border: 0;
1151 |     border-radius: 0;
1152 |     overflow: visible;
1153 |     background: transparent;
1154 |   }
1155 | 
1156 |   /* Section separator - top border for non-first groups */
1157 |   .we-group + .we-group {
1158 |     border-top: 1px solid var(--we-border-section);
1159 |     padding-top: 12px;
1160 |     margin-top: 4px;
1161 |   }
1162 | 
1163 |   .we-group-header {
1164 |     width: 100%;
1165 |     display: flex;
1166 |     align-items: center;
1167 |     justify-content: space-between;
1168 |     gap: 6px;
1169 |     padding: 0 0 8px 0;
1170 |     background: transparent;
1171 |   }
1172 | 
1173 |   .we-group-toggle {
1174 |     flex: 1;
1175 |     min-width: 0;
1176 |     display: flex;
1177 |     align-items: center;
1178 |     justify-content: space-between;
1179 |     gap: 6px;
1180 |     padding: 0;
1181 |     background: transparent;
1182 |     border: 0;
1183 |     cursor: pointer;
1184 |     color: #333333;
1185 |     font-size: 11px;
1186 |     font-weight: 600;
1187 |     text-align: left;
1188 |     transition: color 0.1s ease;
1189 |   }
1190 | 
1191 |   .we-group-toggle:hover {
1192 |     color: var(--we-text-primary);
1193 |   }
1194 | 
1195 |   .we-group-toggle:focus-visible {
1196 |     outline: none;
1197 |     box-shadow: inset 0 0 0 2px var(--we-focus-ring);
1198 |     border-radius: 2px;
1199 |   }
1200 | 
1201 |   .we-group-toggle--static {
1202 |     cursor: default;
1203 |     pointer-events: none;
1204 |   }
1205 | 
1206 |   .we-group-header-actions {
1207 |     display: flex;
1208 |     align-items: center;
1209 |     gap: 2px;
1210 |     flex: 0 0 auto;
1211 |   }
1212 | 
1213 |   .we-group-body {
1214 |     padding: 0;
1215 |     background: transparent;
1216 |     border-top: 0;
1217 |   }
1218 | 
1219 |   .we-group[data-collapsed="true"] .we-group-body {
1220 |     display: none;
1221 |   }
1222 | 
1223 |   .we-chevron {
1224 |     width: 12px;
1225 |     height: 12px;
1226 |     flex: 0 0 auto;
1227 |     color: var(--we-text-muted);
1228 |     transition: transform 0.1s ease;
1229 |   }
1230 | 
1231 |   .we-group[data-collapsed="true"] .we-chevron {
1232 |     transform: rotate(-90deg);
1233 |   }
1234 | 
1235 |   /* ==========================================================================
1236 |      Form Controls (for Design controls)
1237 |      ========================================================================== */
1238 | 
1239 |   /* Field row: vertical stack (label on top, control below) */
1240 |   .we-field {
1241 |     display: flex;
1242 |     flex-direction: column;
1243 |     align-items: stretch;
1244 |     gap: 4px;
1245 |   }
1246 | 
1247 |   /* Horizontal field variant (label left, control right) */
1248 |   .we-field--horizontal {
1249 |     flex-direction: row;
1250 |     align-items: center;
1251 |     gap: 8px;
1252 |   }
1253 | 
1254 |   .we-field-label {
1255 |     flex: 0 0 auto;
1256 |     width: auto;
1257 |     font-size: 10px;
1258 |     font-weight: 500;
1259 |     color: var(--we-text-secondary);
1260 |   }
1261 | 
1262 |   /* Fixed width label for horizontal layout */
1263 |   .we-field--horizontal .we-field-label {
1264 |     width: 48px;
1265 |   }
1266 | 
1267 |   .we-field-label--short {
1268 |     width: 20px;
1269 |   }
1270 | 
1271 |   /* Hint text (small label above icon groups for H/V distinction) */
1272 |   .we-field-hint {
1273 |     font-size: 9px;
1274 |     color: var(--we-text-muted);
1275 |     text-align: center;
1276 |     line-height: 1;
1277 |   }
1278 | 
1279 |   /* Content container for complex controls (icon groups, grids, etc.) */
1280 |   .we-field-content {
1281 |     width: 100%;
1282 |     min-width: 0;
1283 |   }
1284 | 
1285 |   /* Input styling aligned with design spec:
1286 |    * - Gray background by default
1287 |    * - Inset border on hover
1288 |    * - White background + blue inset border on focus
1289 |    */
1290 |   .we-input {
1291 |     flex: 1 1 auto;
1292 |     flex-shrink: 0; /* Prevent height shrinking in column flex containers */
1293 |     min-width: 0;
1294 |     height: 28px; /* Design spec: h-[28px] */
1295 |     padding: 0 8px;
1296 |     font-size: 11px;
1297 |     line-height: 26px; /* Ensure vertical centering: 28px - 2px border */
1298 |     font-family: inherit;
1299 |     color: var(--we-text-primary);
1300 |     background: var(--we-control-bg);
1301 |     border: 1px solid transparent;
1302 |     border-radius: var(--we-radius-control);
1303 |     outline: none;
1304 |     transition: background 0.1s ease, border-color 0.1s ease, box-shadow 0.1s ease;
1305 |   }
1306 | 
1307 |   .we-input::placeholder {
1308 |     color: var(--we-text-muted);
1309 |   }
1310 | 
1311 |   .we-input:hover:not(:focus) {
1312 |     border-color: var(--we-control-border-hover);
1313 |   }
1314 | 
1315 |   .we-input:focus {
1316 |     background: var(--we-control-bg-focus);
1317 |     border-color: var(--we-control-border-focus);
1318 |   }
1319 | 
1320 |   /* ==========================================================================
1321 |    * Input Container (Phase 2.1)
1322 |    *
1323 |    * A wrapper for inputs with prefix/suffix support.
1324 |    * Container handles hover/focus-within styling instead of input itself.
1325 |    * ========================================================================== */
1326 |   .we-input-container {
1327 |     min-width: 0;
1328 |     display: flex;
1329 |     align-items: center;
1330 |     height: 28px; /* Design spec: h-[28px] - must be explicit, not flex-controlled */
1331 |     flex-shrink: 0; /* Prevent height shrinking in column flex containers */
1332 |     padding: 0 8px;
1333 |     gap: 4px;
1334 |     background: var(--we-control-bg);
1335 |     border: 1px solid transparent;
1336 |     border-radius: var(--we-radius-control);
1337 |     transition: background 0.1s ease, border-color 0.1s ease, box-shadow 0.1s ease;
1338 |   }
1339 | 
1340 |   /* In row flex containers, allow input-container to grow horizontally */
1341 |   .we-field-row > .we-input-container,
1342 |   .we-radius-control .we-field-row > .we-input-container {
1343 |     flex: 1 1 0;
1344 |   }
1345 | 
1346 |   .we-input-container:hover:not(:focus-within) {
1347 |     border-color: var(--we-control-border-hover);
1348 |   }
1349 | 
1350 |   .we-input-container:focus-within {
1351 |     background: var(--we-control-bg-focus);
1352 |     border-color: var(--we-control-border-focus);
1353 |   }
1354 | 
1355 |   .we-input-container__input {
1356 |     flex: 1;
1357 |     min-width: 0;
1358 |     height: 100%;
1359 |     padding: 0;
1360 |     font-size: 11px;
1361 |     line-height: 26px; /* Ensure vertical centering within 28px container */
1362 |     font-family: inherit;
1363 |     color: var(--we-text-primary);
1364 |     background: transparent;
1365 |     border: none;
1366 |     outline: none;
1367 |   }
1368 | 
1369 |   .we-input-container__input::placeholder {
1370 |     color: var(--we-text-muted);
1371 |   }
1372 | 
1373 |   /* Number inputs: right-aligned text in containers */
1374 |   .we-input-container__input[inputmode="decimal"],
1375 |   .we-input-container__input[inputmode="numeric"] {
1376 |     text-align: right;
1377 |   }
1378 | 
1379 |   /* Prefix and suffix elements */
1380 |   .we-input-container__prefix,
1381 |   .we-input-container__suffix {
1382 |     flex: 0 0 auto;
1383 |     font-size: 10px;
1384 |     color: var(--we-text-muted);
1385 |     user-select: none;
1386 |     pointer-events: none;
1387 |   }
1388 | 
1389 |   .we-input-container__prefix {
1390 |     margin-right: 2px;
1391 |   }
1392 | 
1393 |   .we-input-container__suffix {
1394 |     margin-left: 2px;
1395 |   }
1396 | 
1397 |   /* Icon in prefix/suffix */
1398 |   .we-input-container__prefix svg,
1399 |   .we-input-container__suffix svg {
1400 |     width: 12px;
1401 |     height: 12px;
1402 |     display: block;
1403 |   }
1404 | 
1405 |   /* ==========================================================================
1406 |    * Slider Input (Opacity and other numeric ranges)
1407 |    * ========================================================================== */
1408 |   .we-slider-input {
1409 |     display: flex;
1410 |     align-items: center;
1411 |     gap: 8px;
1412 |     width: 100%;
1413 |     min-width: 0;
1414 |   }
1415 | 
1416 |   .we-slider-input__slider {
1417 |     flex: 1 1 auto;
1418 |     min-width: 0;
1419 |     height: 28px;
1420 |     margin: 0;
1421 |     padding: 0;
1422 |     background: transparent;
1423 |     -webkit-appearance: none;
1424 |     appearance: none;
1425 |     cursor: pointer;
1426 |   }
1427 | 
1428 |   .we-slider-input__slider:disabled {
1429 |     opacity: 0.55;
1430 |     cursor: not-allowed;
1431 |   }
1432 | 
1433 |   .we-slider-input__slider::-webkit-slider-runnable-track {
1434 |     height: 4px;
1435 |     background: linear-gradient(
1436 |       to right,
1437 |       var(--we-control-border-focus) 0%,
1438 |       var(--we-control-border-focus) var(--progress, 0%),
1439 |       var(--we-control-bg) var(--progress, 0%),
1440 |       var(--we-control-bg) 100%
1441 |     );
1442 |     border: 1px solid var(--we-control-border-hover);
1443 |     border-radius: 999px;
1444 |   }
1445 | 
1446 |   .we-slider-input__slider::-webkit-slider-thumb {
1447 |     -webkit-appearance: none;
1448 |     appearance: none;
1449 |     width: 12px;
1450 |     height: 12px;
1451 |     margin-top: -5px; /* (12px thumb - 4px track) / 2 + border */
1452 |     border-radius: 999px;
1453 |     background: var(--we-control-bg-focus);
1454 |     border: 1px solid var(--we-border-strong);
1455 |     box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
1456 |   }
1457 | 
1458 |   .we-slider-input__slider:focus-visible {
1459 |     outline: none;
1460 |   }
1461 | 
1462 |   .we-slider-input__slider:focus-visible::-webkit-slider-thumb {
1463 |     border-color: var(--we-control-border-focus);
1464 |     box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.25);
1465 |   }
1466 | 
1467 |   .we-slider-input__slider::-moz-range-track {
1468 |     height: 4px;
1469 |     background: linear-gradient(
1470 |       to right,
1471 |       var(--we-control-border-focus) 0%,
1472 |       var(--we-control-border-focus) var(--progress, 0%),
1473 |       var(--we-control-bg) var(--progress, 0%),
1474 |       var(--we-control-bg) 100%
1475 |     );
1476 |     border: 1px solid var(--we-control-border-hover);
1477 |     border-radius: 999px;
1478 |   }
1479 | 
1480 |   .we-slider-input__slider::-moz-range-thumb {
1481 |     width: 12px;
1482 |     height: 12px;
1483 |     border-radius: 999px;
1484 |     background: var(--we-control-bg-focus);
1485 |     border: 1px solid var(--we-border-strong);
1486 |     box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
1487 |   }
1488 | 
1489 |   .we-slider-input__slider:focus-visible::-moz-range-thumb {
1490 |     border-color: var(--we-control-border-focus);
1491 |     box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.25);
1492 |   }
1493 | 
1494 |   .we-slider-input__number {
1495 |     flex: 0 0 auto;
1496 |   }
1497 | 
1498 |   /* ==========================================================================
1499 |    * Icon Button Group (Phase 4.1)
1500 |    *
1501 |    * A single-select grid of icon buttons (e.g. flex-direction control).
1502 |    * ========================================================================== */
1503 |   .we-icon-button-group {
1504 |     display: grid;
1505 |     gap: 4px;
1506 |   }
1507 | 
1508 |   .we-icon-button-group__btn {
1509 |     display: flex;
1510 |     align-items: center;
1511 |     justify-content: center;
1512 |     height: 28px; /* Design spec: h-[28px] */
1513 |     padding: 4px;
1514 |     background: var(--we-control-bg);
1515 |     border: 1px solid transparent;
1516 |     border-radius: var(--we-radius-control);
1517 |     cursor: pointer;
1518 |     transition: background-color 0.1s ease, border-color 0.1s ease;
1519 |   }
1520 | 
1521 |   .we-icon-button-group__btn:hover:not(:disabled) {
1522 |     background: var(--we-control-bg-hover);
1523 |   }
1524 | 
1525 |   .we-icon-button-group__btn:focus-visible {
1526 |     outline: none;
1527 |     border-color: var(--we-control-border-focus);
1528 |     box-shadow: inset 0 0 0 2px var(--we-control-border-focus); /* Design spec: 2px inset */
1529 |   }
1530 | 
1531 |   .we-icon-button-group__btn[data-selected="true"] {
1532 |     background: var(--we-control-bg-focus);
1533 |     border-color: var(--we-control-border-focus);
1534 |   }
1535 | 
1536 |   .we-icon-button-group__btn:disabled {
1537 |     opacity: 0.5;
1538 |     cursor: not-allowed;
1539 |   }
1540 | 
1541 |   .we-icon-button-group__btn svg {
1542 |     width: 14px;
1543 |     height: 14px;
1544 |     color: var(--we-text-secondary);
1545 |   }
1546 | 
1547 |   .we-icon-button-group__btn[data-selected="true"] svg {
1548 |     color: var(--we-control-border-focus);
1549 |   }
1550 | 
1551 |   /* ==========================================================================
1552 |    * Toggle Button
1553 |    *
1554 |    * A pressable toggle button (e.g. flip X/Y controls).
1555 |    * ========================================================================== */
1556 |   .we-toggle-btn {
1557 |     display: flex;
1558 |     align-items: center;
1559 |     justify-content: center;
1560 |     width: 28px;
1561 |     height: 28px;
1562 |     padding: 4px;
1563 |     background: var(--we-control-bg);
1564 |     border: 1px solid transparent;
1565 |     border-radius: var(--we-radius-control);
1566 |     cursor: pointer;
1567 |     transition: background-color 0.1s ease, border-color 0.1s ease;
1568 |   }
1569 | 
1570 |   .we-toggle-btn:hover:not(:disabled) {
1571 |     background: var(--we-control-bg-hover);
1572 |   }
1573 | 
1574 |   .we-toggle-btn[aria-pressed="true"] {
1575 |     background: var(--we-control-bg-focus);
1576 |     border-color: var(--we-control-border-focus);
1577 |   }
1578 | 
1579 |   .we-toggle-btn:disabled {
1580 |     opacity: 0.5;
1581 |     cursor: not-allowed;
1582 |   }
1583 | 
1584 |   .we-toggle-btn svg {
1585 |     width: 14px;
1586 |     height: 14px;
1587 |     color: var(--we-text-secondary);
1588 |   }
1589 | 
1590 |   .we-toggle-btn[aria-pressed="true"] svg {
1591 |     color: var(--we-control-border-focus);
1592 |   }
1593 | 
1594 |   /* ==========================================================================
1595 |    * Alignment Grid (Phase 4.2)
1596 |    *
1597 |    * 3×3 single-select grid for justify-content + align-items.
1598 |    * ========================================================================== */
1599 |   .we-alignment-grid {
1600 |     display: grid;
1601 |     grid-template-columns: repeat(3, 1fr);
1602 |     gap: 12px;
1603 |     padding: 8px;
1604 |     min-height: 90px;
1605 |     background: #f9f9f9;
1606 |     border: 1px solid #f0f0f0;
1607 |     border-radius: var(--we-radius-control);
1608 |     place-items: center;
1609 |   }
1610 | 
1611 |   .we-alignment-grid__cell {
1612 |     display: flex;
1613 |     align-items: center;
1614 |     justify-content: center;
1615 |     width: 20px;
1616 |     height: 20px;
1617 |     padding: 0;
1618 |     background: transparent;
1619 |     border: none;
1620 |     border-radius: 2px;
1621 |     cursor: pointer;
1622 |     transition: background-color 0.1s ease;
1623 |   }
1624 | 
1625 |   .we-alignment-grid__cell:hover:not(:disabled) {
1626 |     background: rgba(0, 0, 0, 0.05);
1627 |   }
1628 | 
1629 |   .we-alignment-grid__cell:focus-visible {
1630 |     outline: 2px solid var(--we-control-border-focus);
1631 |     outline-offset: 1px;
1632 |   }
1633 | 
1634 |   .we-alignment-grid__cell:disabled {
1635 |     opacity: 0.5;
1636 |     cursor: not-allowed;
1637 |   }
1638 | 
1639 |   /* Inactive dot */
1640 |   .we-alignment-grid__dot {
1641 |     width: 2px;
1642 |     height: 2px;
1643 |     background: var(--we-text-muted);
1644 |     border-radius: 50%;
1645 |   }
1646 | 
1647 |   /* Active marker (3 bars showing alignment) */
1648 |   .we-alignment-grid__marker {
1649 |     display: flex;
1650 |     flex-direction: column;
1651 |     align-items: center;
1652 |     justify-content: space-between;
1653 |     width: 12px;
1654 |     height: 12px;
1655 |   }
1656 | 
1657 |   .we-alignment-grid__bar {
1658 |     height: 2px;
1659 |     background: var(--we-control-border-focus);
1660 |     border-radius: 1px;
1661 |   }
1662 | 
1663 |   .we-alignment-grid__bar--1 { width: 8px; }
1664 |   .we-alignment-grid__bar--2 { width: 12px; }
1665 |   .we-alignment-grid__bar--3 { width: 4px; }
1666 | 
1667 |   /* ==========================================================================
1668 |    * Grid + Gap Two Column Layout (Layout Control)
1669 |    * ========================================================================== */
1670 | 
1671 |   .we-grid-gap-row {
1672 |     display: flex;
1673 |     gap: 8px;
1674 |   }
1675 | 
1676 |   .we-grid-gap-col {
1677 |     flex: 1;
1678 |     min-width: 0;
1679 |   }
1680 | 
1681 |   /* Keep Grid label space for alignment; hide text only when both columns are visible (grid mode) */
1682 |   .we-grid-gap-col--grid:not([hidden]):has(+ .we-grid-gap-col--gap:not([hidden])) .we-field-label {
1683 |     visibility: hidden;
1684 |   }
1685 | 
1686 |   .we-grid-gap-col .we-field-content {
1687 |     width: 100%;
1688 |     overflow: visible;
1689 |   }
1690 | 
1691 |   /* Gap inputs vertical layout */
1692 |   .we-grid-gap-inputs {
1693 |     display: flex;
1694 |     flex-direction: column;
1695 |     gap: 8px;
1696 |   }
1697 | 
1698 |   /* ==========================================================================
1699 |    * Grid Dimensions Picker (Layout Control)
1700 |    * ========================================================================== */
1701 | 
1702 |   .we-grid-dimensions-preview {
1703 |     width: 100%;
1704 |     height: 64px; /* Match two rows of gap inputs: 28px + 8px gap + 28px */
1705 |     display: flex;
1706 |     align-items: center;
1707 |     justify-content: center;
1708 |     gap: 4px;
1709 |     font-size: 12px;
1710 |     font-family: inherit;
1711 |     color: var(--we-text-primary);
1712 |     background: var(--we-control-bg);
1713 |     border: 1px solid transparent;
1714 |     border-radius: var(--we-radius-control);
1715 |     cursor: pointer;
1716 |     transition: background-color 0.1s ease, border-color 0.1s ease;
1717 |   }
1718 | 
1719 |   .we-grid-dimensions-preview:hover:not(:disabled) {
1720 |     background: var(--we-control-bg-hover);
1721 |   }
1722 | 
1723 |   .we-grid-dimensions-preview:disabled {
1724 |     opacity: 0.5;
1725 |     cursor: not-allowed;
1726 |   }
1727 | 
1728 |   .we-grid-dimensions-popover {
1729 |     position: absolute;
1730 |     top: calc(100% + 4px);
1731 |     left: 0;
1732 |     min-width: 220px;
1733 |     padding: 10px;
1734 |     background: var(--we-surface-bg);
1735 |     border: 1px solid var(--we-border-subtle);
1736 |     border-radius: 8px;
1737 |     box-shadow: 0 8px 24px rgba(15, 23, 42, 0.12);
1738 |     z-index: 60;
1739 |   }
1740 | 
1741 |   .we-grid-dimensions-popover[hidden] {
1742 |     display: none;
1743 |   }
1744 | 
1745 |   .we-grid-dimensions-inputs {
1746 |     display: flex;
1747 |     align-items: center;
1748 |     justify-content: center;
1749 |     gap: 8px;
1750 |     margin-bottom: 10px;
1751 |   }
1752 | 
1753 |   .we-grid-dimensions-times {
1754 |     font-size: 12px;
1755 |     color: var(--we-text-muted);
1756 |     user-select: none;
1757 |   }
1758 | 
1759 |   .we-grid-dimensions-matrix {
1760 |     display: grid;
1761 |     grid-template-columns: repeat(12, 1fr);
1762 |     gap: 3px;
1763 |     padding: 6px;
1764 |     background: var(--we-surface-secondary);
1765 |     border: 1px solid var(--we-border-subtle);
1766 |     border-radius: var(--we-radius-control);
1767 |   }
1768 | 
1769 |   .we-grid-dimensions-cell {
1770 |     width: 100%;
1771 |     aspect-ratio: 1 / 1;
1772 |     background: transparent;
1773 |     border: 1px solid rgba(0, 0, 0, 0.10);
1774 |     border-radius: 2px;
1775 |     padding: 0;
1776 |     cursor: pointer;
1777 |     transition: background-color 0.08s ease, border-color 0.08s ease;
1778 |   }
1779 | 
1780 |   .we-grid-dimensions-cell[data-active="true"] {
1781 |     border-color: rgba(59, 130, 246, 0.65);
1782 |     background: rgba(59, 130, 246, 0.10);
1783 |   }
1784 | 
1785 |   .we-grid-dimensions-cell[data-selected="true"] {
1786 |     border-color: rgba(59, 130, 246, 0.9);
1787 |     background: rgba(59, 130, 246, 0.16);
1788 |   }
1789 | 
1790 |   .we-grid-dimensions-tooltip {
1791 |     margin-top: 8px;
1792 |     text-align: center;
1793 |     font-size: 11px;
1794 |     color: var(--we-text-secondary);
1795 |   }
1796 | 
1797 |   .we-grid-dimensions-tooltip[hidden] {
1798 |     display: none;
1799 |   }
1800 | 
1801 |   .we-input--short {
1802 |     width: 56px;
1803 |     flex: 0 0 auto;
1804 |   }
1805 | 
1806 |   /* Number inputs: right-aligned text */
1807 |   .we-input[type="text"][inputmode="decimal"],
1808 |   .we-input[type="number"] {
1809 |     text-align: right;
1810 |   }
1811 | 
1812 |   .we-select {
1813 |     flex: 1 1 auto;
1814 |     flex-shrink: 0; /* Prevent height shrinking in column flex containers */
1815 |     min-width: 0;
1816 |     height: 28px; /* Design spec: h-[28px] */
1817 |     padding: 0 24px 0 8px;
1818 |     font-size: 11px;
1819 |     line-height: 26px; /* Ensure vertical centering: 28px - 2px border */
1820 |     font-family: inherit;
1821 |     color: var(--we-text-primary);
1822 |     background: var(--we-control-bg) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 10 10'%3E%3Cpath fill='%23737373' d='M2.5 3.5l2.5 3 2.5-3'/%3E%3C/svg%3E") no-repeat right 8px center;
1823 |     border: 1px solid transparent;
1824 |     border-radius: var(--we-radius-control);
1825 |     outline: none;
1826 |     -webkit-appearance: none;
1827 |     -moz-appearance: none;
1828 |     appearance: none;
1829 |     cursor: pointer;
1830 |     transition: background-color 0.1s ease, border-color 0.1s ease, box-shadow 0.1s ease;
1831 |   }
1832 | 
1833 |   .we-select:hover:not(:focus) {
1834 |     border-color: var(--we-control-border-hover);
1835 |   }
1836 | 
1837 |   .we-select:focus {
1838 |     background-color: var(--we-control-bg-focus);
1839 |     border-color: var(--we-control-border-focus);
1840 |   }
1841 | 
1842 |   /* Field row for multiple inputs side by side */
1843 |   .we-field-row {
1844 |     display: flex;
1845 |     align-items: stretch;
1846 |     gap: 8px;
1847 |   }
1848 | 
1849 |   /* Size field with mode select + input stacked vertically */
1850 |   .we-size-field {
1851 |     display: flex;
1852 |     flex-direction: column;
1853 |     gap: 4px;
1854 |     flex: 1;
1855 |     min-width: 0;
1856 |   }
1857 | 
1858 |   .we-size-mode-select {
1859 |     width: 100%;
1860 |   }
1861 | 
1862 |   .we-field-group {
1863 |     display: flex;
1864 |     flex-direction: column;
1865 |     gap: 6px;
1866 |   }
1867 | 
1868 |   /* ==========================================================================
1869 |      Effects (Box Shadow List)
1870 |      ========================================================================== */
1871 | 
1872 |   .we-effects-toolbar {
1873 |     display: flex;
1874 |     justify-content: flex-end;
1875 |     margin-bottom: 6px;
1876 |   }
1877 | 
1878 |   .we-effects-list {
1879 |     display: flex;
1880 |     flex-direction: column;
1881 |     gap: 6px;
1882 |   }
1883 | 
1884 |   .we-effects-item-wrap {
1885 |     position: relative;
1886 |   }
1887 | 
1888 |   .we-effects-item {
1889 |     height: 28px;
1890 |     display: flex;
1891 |     align-items: center;
1892 |     gap: 6px;
1893 |     padding: 0 6px;
1894 |     background: var(--we-control-bg);
1895 |     border: 1px solid transparent;
1896 |     border-radius: var(--we-radius-control);
1897 |     transition: background-color 0.1s ease, border-color 0.1s ease, opacity 0.1s ease;
1898 |   }
1899 | 
1900 |   .we-effects-item:hover {
1901 |     background: var(--we-control-bg-hover);
1902 |   }
1903 | 
1904 |   .we-effects-item[data-open="true"] {
1905 |     background: var(--we-control-bg-focus);
1906 |     border-color: var(--we-control-border-focus);
1907 |   }
1908 | 
1909 |   .we-effects-item[data-enabled="false"] {
1910 |     opacity: 0.55;
1911 |   }
1912 | 
1913 |   .we-effects-name {
1914 |     flex: 1;
1915 |     min-width: 0;
1916 |     padding: 0;
1917 |     border: 0;
1918 |     background: transparent;
1919 |     text-align: left;
1920 |     font-size: 11px;
1921 |     color: var(--we-text-primary);
1922 |     cursor: pointer;
1923 |     overflow: hidden;
1924 |     text-overflow: ellipsis;
1925 |     white-space: nowrap;
1926 |   }
1927 | 
1928 |   .we-effects-name:focus-visible {
1929 |     outline: none;
1930 |     box-shadow: inset 0 0 0 2px var(--we-focus-ring);
1931 |     border-radius: 4px;
1932 |   }
1933 | 
1934 |   .we-effects-icon-btn {
1935 |     width: 24px;
1936 |     height: 24px;
1937 |     display: flex;
1938 |     align-items: center;
1939 |     justify-content: center;
1940 |     background: transparent;
1941 |     border: 0;
1942 |     border-radius: var(--we-radius-control);
1943 |     color: var(--we-text-secondary);
1944 |     cursor: pointer;
1945 |     padding: 0;
1946 |     transition: background-color 0.1s ease, color 0.1s ease;
1947 |   }
1948 | 
1949 |   .we-effects-icon-btn:hover:not(:disabled) {
1950 |     background: rgba(0, 0, 0, 0.06);
1951 |     color: var(--we-text-primary);
1952 |   }
1953 | 
1954 |   .we-effects-icon-btn:focus-visible {
1955 |     outline: none;
1956 |     box-shadow: 0 0 0 2px var(--we-focus-ring);
1957 |   }
1958 | 
1959 |   .we-effects-icon-btn:disabled {
1960 |     opacity: 0.5;
1961 |     cursor: not-allowed;
1962 |   }
1963 | 
1964 |   .we-effects-icon-btn svg {
1965 |     width: 14px;
1966 |     height: 14px;
1967 |   }
1968 | 
1969 |   .we-effects-popover {
1970 |     position: absolute;
1971 |     top: calc(100% + 6px);
1972 |     left: 0;
1973 |     width: 220px;
1974 |     max-width: 220px;
1975 |     padding: 10px;
1976 |     background: var(--we-surface-bg);
1977 |     border: 1px solid var(--we-border-subtle);
1978 |     border-radius: 8px;
1979 |     box-shadow: 0 8px 24px rgba(15, 23, 42, 0.12);
1980 |     z-index: 60;
1981 |   }
1982 | 
1983 |   .we-effects-popover[hidden] {
1984 |     display: none;
1985 |   }
1986 | 
1987 |   .we-effects-popover-content {
1988 |     display: flex;
1989 |     flex-direction: column;
1990 |     gap: 8px;
1991 |   }
1992 | 
1993 |   /* ==========================================================================
1994 |      Gradient Preview Bar (Phase 4B)
1995 |      ========================================================================== */
1996 | 
1997 |   .we-gradient-bar-row {
1998 |     width: 100%;
1999 |     padding: 4px 0 8px;
2000 |   }
2001 | 
2002 |   .we-gradient-bar {
2003 |     position: relative;
2004 |     width: 100%;
2005 |     height: 60px;
2006 |     border-radius: 14px;
2007 |     border: 1px solid var(--we-border-subtle);
2008 |     background-color: var(--we-control-bg);
2009 |     background-image: none; /* set inline by GradientControl */
2010 |     box-shadow:
2011 |       inset 0 1px 2px rgba(0, 0, 0, 0.08),
2012 |       inset 0 0 0 1px rgba(255, 255, 255, 0.5);
2013 |     overflow: hidden;
2014 |   }
2015 | 
2016 |   /* Gradient thumbs container */
2017 |   .we-gradient-bar-thumbs {
2018 |     position: absolute;
2019 |     inset: 0;
2020 |     pointer-events: none; /* thumbs enable pointer events individually */
2021 |   }
2022 | 
2023 |   /* Gradient thumb (color stop marker) */
2024 |   .we-gradient-thumb {
2025 |     pointer-events: auto;
2026 |     position: absolute;
2027 |     top: 50%;
2028 |     left: 0;
2029 |     transform: translate(-50%, -50%);
2030 |     z-index: 1;
2031 |     width: 32px;
2032 |     height: 32px;
2033 |     border-radius: 6px;
2034 |     border: 2px solid rgba(255, 255, 255, 0.98);
2035 |     background-color: transparent; /* set inline */
2036 |     cursor: pointer;
2037 |     padding: 0;
2038 |     box-sizing: border-box;
2039 |     box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
2040 |     touch-action: none;
2041 |     user-select: none;
2042 |     transition: box-shadow 0.15s ease, z-index 0s;
2043 |   }
2044 | 
2045 |   .we-gradient-thumb:hover {
2046 |     box-shadow:
2047 |       0 0 0 2px rgba(59, 130, 246, 0.25),
2048 |       0 1px 3px rgba(0, 0, 0, 0.2);
2049 |   }
2050 | 
2051 |   .we-gradient-thumb:focus-visible {
2052 |     outline: none;
2053 |     box-shadow:
2054 |       0 0 0 3px rgba(59, 130, 246, 0.4),
2055 |       0 1px 3px rgba(0, 0, 0, 0.2);
2056 |   }
2057 | 
2058 |   /* Selected thumb state - raise above unselected thumbs */
2059 |   .we-gradient-thumb--active {
2060 |     z-index: 2;
2061 |     box-shadow:
2062 |       0 0 0 3px rgba(59, 130, 246, 0.4),
2063 |       0 1px 3px rgba(0, 0, 0, 0.2);
2064 |   }
2065 | 
2066 |   /* Dragging thumb - always on top when overlapping at same position */
2067 |   .we-gradient-thumb--dragging {
2068 |     z-index: 3;
2069 |   }
2070 | 
2071 |   /* Dragging state - cursor feedback on entire bar */
2072 |   .we-gradient-bar--dragging {
2073 |     cursor: grabbing;
2074 |   }
2075 | 
2076 |   .we-gradient-bar--dragging .we-gradient-thumb {
2077 |     cursor: grabbing;
2078 |   }
2079 | 
2080 |   /* ==========================================================================
2081 |      Gradient Stops List (Phase 4D)
2082 |      ========================================================================== */
2083 | 
2084 |   .we-gradient-stops-header {
2085 |     display: flex;
2086 |     align-items: center;
2087 |     justify-content: space-between;
2088 |     padding: 6px 0 4px;
2089 |   }
2090 | 
2091 |   .we-gradient-stops-title {
2092 |     font-size: 10px;
2093 |     font-weight: 600;
2094 |     color: var(--we-text-secondary);
2095 |     text-transform: uppercase;
2096 |     letter-spacing: 0.5px;
2097 |   }
2098 | 
2099 |   .we-gradient-stops-add,
2100 |   .we-gradient-stop-remove {
2101 |     font-size: 14px;
2102 |     font-weight: 500;
2103 |     line-height: 1;
2104 |   }
2105 | 
2106 |   .we-gradient-stops-add:disabled,
2107 |   .we-gradient-stop-remove:disabled {
2108 |     opacity: 0.4;
2109 |     cursor: not-allowed;
2110 |   }
2111 | 
2112 |   .we-gradient-stops-list {
2113 |     border: 1px solid var(--we-border-subtle);
2114 |     border-radius: 12px;
2115 |     background: rgba(255, 255, 255, 0.6);
2116 |     padding: 6px;
2117 |     display: flex;
2118 |     flex-direction: column;
2119 |     gap: 4px;
2120 |     max-height: 180px;
2121 |     overflow-y: auto;
2122 |     overscroll-behavior: contain;
2123 |   }
2124 | 
2125 |   .we-gradient-stop-row {
2126 |     display: flex;
2127 |     align-items: center;
2128 |     gap: 6px;
2129 |     padding: 5px 6px;
2130 |     border-radius: 8px;
2131 |     border: 1px solid transparent;
2132 |     background: rgba(255, 255, 255, 0.85);
2133 |     cursor: pointer;
2134 |     user-select: none;
2135 |     transition: border-color 0.15s ease, background 0.15s ease;
2136 |   }
2137 | 
2138 |   .we-gradient-stop-row:hover {
2139 |     background: rgba(59, 130, 246, 0.06);
2140 |   }
2141 | 
2142 |   .we-gradient-stop-row:focus-visible {
2143 |     outline: none;
2144 |     box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.35);
2145 |   }
2146 | 
2147 |   .we-gradient-stop-row--active {
2148 |     border-color: rgba(59, 130, 246, 0.6);
2149 |     box-shadow: 0 0 0 1px rgba(59, 130, 246, 0.2);
2150 |   }
2151 | 
2152 |   .we-gradient-stop-pos {
2153 |     flex: 0 0 auto;
2154 |     min-width: 44px;
2155 |     display: flex;
2156 |     align-items: center;
2157 |     justify-content: flex-end;
2158 |     text-align: right;
2159 |     font-variant-numeric: tabular-nums;
2160 |     font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
2161 |     font-size: 11px;
2162 |     color: var(--we-text-secondary);
2163 |     padding: 3px 6px;
2164 |     border-radius: 6px;
2165 |     background: var(--we-control-bg);
2166 |     cursor: pointer;
2167 |     transition: box-shadow 0.15s ease;
2168 |   }
2169 | 
2170 |   .we-gradient-stop-pos:hover {
2171 |     background: var(--we-control-bg-hover, var(--we-control-bg));
2172 |   }
2173 | 
2174 |   .we-gradient-stop-pos:focus-within {
2175 |     box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.25);
2176 |   }
2177 | 
2178 |   /* Static position display (visible when row is not selected) */
2179 |   .we-gradient-stop-pos-static {
2180 |     display: block;
2181 |     width: 100%;
2182 |     text-align: right;
2183 |   }
2184 | 
2185 |   /* Position editor slot (visible when row is selected) */
2186 |   .we-gradient-stop-pos-editor {
2187 |     display: none;
2188 |     width: 100%;
2189 |   }
2190 | 
2191 |   /* Show editor and hide static in active row */
2192 |   .we-gradient-stop-row--active .we-gradient-stop-pos-static {
2193 |     display: none;
2194 |   }
2195 | 
2196 |   .we-gradient-stop-row--active .we-gradient-stop-pos-editor {
2197 |     display: block;
2198 |   }
2199 | 
2200 |   /* Position input styling */
2201 |   .we-gradient-stop-pos-input {
2202 |     width: 100%;
2203 |     border: 0;
2204 |     padding: 0;
2205 |     margin: 0;
2206 |     background: transparent;
2207 |     color: inherit;
2208 |     font: inherit;
2209 |     text-align: right;
2210 |     outline: none;
2211 |     cursor: text;
2212 |   }
2213 | 
2214 |   .we-gradient-stop-pos-input::placeholder {
2215 |     color: var(--we-text-muted);
2216 |   }
2217 | 
2218 |   .we-gradient-stop-color {
2219 |     flex: 1;
2220 |     min-width: 0;
2221 |     display: flex;
2222 |     align-items: center;
2223 |     gap: 6px;
2224 |     padding: 3px 8px;
2225 |     border-radius: 6px;
2226 |     background: var(--we-control-bg);
2227 |   }
2228 | 
2229 |   /* Static color display (visible when row is not selected) */
2230 |   .we-gradient-stop-color-static {
2231 |     flex: 1;
2232 |     min-width: 0;
2233 |     display: flex;
2234 |     align-items: center;
2235 |     gap: 6px;
2236 |     padding: 0;
2237 |     border: 0;
2238 |     background: transparent;
2239 |     color: inherit;
2240 |     cursor: pointer;
2241 |     text-align: left;
2242 |     font-family: inherit;
2243 |   }
2244 | 
2245 |   .we-gradient-stop-color-static:focus-visible {
2246 |     outline: none;
2247 |     box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.25);
2248 |     border-radius: 4px;
2249 |   }
2250 | 
2251 |   /* Color editor slot (visible when row is selected) */
2252 |   .we-gradient-stop-color-editor {
2253 |     flex: 1;
2254 |     min-width: 0;
2255 |     display: none;
2256 |   }
2257 | 
2258 |   /* When row is active: hide static, show editor */
2259 |   .we-gradient-stop-row--active .we-gradient-stop-color {
2260 |     padding: 0;
2261 |     background: transparent;
2262 |   }
2263 | 
2264 |   .we-gradient-stop-row--active .we-gradient-stop-color-static {
2265 |     display: none;
2266 |   }
2267 | 
2268 |   .we-gradient-stop-row--active .we-gradient-stop-color-editor {
2269 |     display: block;
2270 |   }
2271 | 
2272 |   .we-gradient-stop-swatch {
2273 |     flex: 0 0 auto;
2274 |     width: 14px;
2275 |     height: 14px;
2276 |     border-radius: 3px;
2277 |     border: 1px solid rgba(0, 0, 0, 0.12);
2278 |     box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2);
2279 |     background: transparent;
2280 |   }
2281 | 
2282 |   .we-gradient-stop-color-text {
2283 |     flex: 1;
2284 |     min-width: 0;
2285 |     font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
2286 |     font-size: 11px;
2287 |     color: var(--we-text-primary);
2288 |     white-space: nowrap;
2289 |     overflow: hidden;
2290 |     text-overflow: ellipsis;
2291 |   }
2292 | 
2293 |   /* Spacing section (Padding / Margin) */
2294 |   .we-spacing-section {
2295 |     display: flex;
2296 |     flex-direction: column;
2297 |     gap: 6px;
2298 |   }
2299 | 
2300 |   .we-spacing-section + .we-spacing-section {
2301 |     margin-top: 10px;
2302 |   }
2303 | 
2304 |   .we-spacing-header {
2305 |     font-size: 10px;
2306 |     font-weight: 600;
2307 |     color: #6b7280;
2308 |   }
2309 | 
2310 |   /* Spacing 2x2 grid layout */
2311 |   .we-spacing-grid {
2312 |     display: grid;
2313 |     grid-template-columns: 1fr 1fr;
2314 |     gap: 8px;
2315 |   }
2316 | 
2317 |   /* ==========================================================================
2318 |    * Border Radius Control
2319 |    * ========================================================================== */
2320 | 
2321 |   .we-radius-control {
2322 |     flex: 1;
2323 |     min-width: 0;
2324 |     display: flex;
2325 |     flex-direction: column;
2326 |     gap: 8px;
2327 |   }
2328 | 
2329 |   .we-radius-corners-grid {
2330 |     display: grid;
2331 |     grid-template-columns: 1fr 1fr;
2332 |     gap: 8px;
2333 |   }
2334 | 
2335 |   /* ==========================================================================
2336 |      CSS Panel (Phase 4.6)
2337 |      ========================================================================== */
2338 | 
2339 |   .we-css-panel {
2340 |     font-size: 11px;
2341 |     line-height: 1.5;
2342 |   }
2343 | 
2344 |   /* Code-semantic elements use monospace font */
2345 |   .we-css-rule-selector,
2346 |   .we-css-decl-name,
2347 |   .we-css-decl-value,
2348 |   .we-css-decl-colon,
2349 |   .we-css-decl-semi,
2350 |   .we-css-decl-important,
2351 |   .we-css-rule-source,
2352 |   .we-css-rule-spec {
2353 |     font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
2354 |   }
2355 | 
2356 |   /* ==========================================================================
2357 |      Class Editor (Phase 4.7)
2358 |      ========================================================================== */
2359 | 
2360 |   .we-css-class-editor-mount {
2361 |     margin-bottom: 12px;
2362 |   }
2363 | 
2364 |   .we-class-editor {
2365 |     position: relative;
2366 |     display: flex;
2367 |     flex-wrap: wrap;
2368 |     align-items: center;
2369 |     gap: 6px;
2370 |     padding: 8px 10px;
2371 |     border: 1px solid var(--we-border-subtle);
2372 |     border-radius: var(--we-radius-panel);
2373 |     background: var(--we-surface-bg);
2374 |     font-family: system-ui, -apple-system, sans-serif;
2375 |   }
2376 | 
2377 |   .we-class-chips {
2378 |     display: flex;
2379 |     flex-wrap: wrap;
2380 |     gap: 6px;
2381 |     flex: 0 1 auto;
2382 |   }
2383 | 
2384 |   .we-class-chip {
2385 |     display: inline-flex;
2386 |     align-items: center;
2387 |     gap: 4px;
2388 |     padding: 3px 8px;
2389 |     border-radius: 999px;
2390 |     background: var(--we-accent-brand-bg);
2391 |     border: 1px solid var(--we-accent-brand-border);
2392 |     color: #4338ca;
2393 |     font-size: 11px;
2394 |     line-height: 1.2;
2395 |   }
2396 | 
2397 |   .we-class-chip-text {
2398 |     word-break: break-all;
2399 |   }
2400 | 
2401 |   .we-class-chip-remove {
2402 |     display: inline-flex;
2403 |     align-items: center;
2404 |     justify-content: center;
2405 |     width: 16px;
2406 |     height: 16px;
2407 |     padding: 0;
2408 |     border: none;
2409 |     border-radius: 999px;
2410 |     background: transparent;
2411 |     color: rgba(67, 56, 202, 0.8);
2412 |     cursor: pointer;
2413 |     font-size: 14px;
2414 |     line-height: 1;
2415 |     transition: background-color 0.15s ease;
2416 |   }
2417 | 
2418 |   .we-class-chip-remove:hover {
2419 |     background: rgba(99, 102, 241, 0.15);
2420 |     color: #4338ca;
2421 |   }
2422 | 
2423 |   .we-class-input {
2424 |     flex: 1 1 100px;
2425 |     min-width: 80px;
2426 |     padding: 5px 8px;
2427 |     font-size: 12px;
2428 |     border: 1px solid var(--we-border-subtle);
2429 |     border-radius: var(--we-radius-control);
2430 |     background: var(--we-control-bg-focus);
2431 |     outline: none;
2432 |     transition: border-color 0.15s ease, box-shadow 0.15s ease;
2433 |   }
2434 | 
2435 |   .we-class-input:focus {
2436 |     border-color: var(--we-control-border-focus);
2437 |     box-shadow: 0 0 0 2px var(--we-focus-ring);
2438 |   }
2439 | 
2440 |   .we-class-input:disabled {
2441 |     opacity: 0.5;
2442 |     cursor: not-allowed;
2443 |   }
2444 | 
2445 |   .we-class-input::placeholder {
2446 |     color: #94a3b8;
2447 |   }
2448 | 
2449 |   .we-class-suggestions {
2450 |     position: absolute;
2451 |     top: calc(100% + 4px);
2452 |     left: 0;
2453 |     right: 0;
2454 |     background: var(--we-surface-bg);
2455 |     border: 1px solid var(--we-border-subtle);
2456 |     border-radius: var(--we-radius-panel);
2457 |     box-shadow: 0 8px 24px rgba(15, 23, 42, 0.12);
2458 |     overflow: hidden;
2459 |     z-index: 20;
2460 |   }
2461 | 
2462 |   .we-class-suggestions[hidden] {
2463 |     display: none;
2464 |   }
2465 | 
2466 |   .we-class-suggestion {
2467 |     display: block;
2468 |     width: 100%;
2469 |     text-align: left;
2470 |     padding: 8px 10px;
2471 |     border: none;
2472 |     background: transparent;
2473 |     cursor: pointer;
2474 |     font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
2475 |     font-size: 11px;
2476 |     color: #0f172a;
2477 |     transition: background-color 0.1s ease;
2478 |   }
2479 | 
2480 |   .we-class-suggestion:hover {
2481 |     background: rgba(99, 102, 241, 0.08);
2482 |   }
2483 | 
2484 |   .we-class-suggestion:focus {
2485 |     outline: none;
2486 |     background: rgba(99, 102, 241, 0.12);
2487 |   }
2488 | 
2489 |   .we-css-info {
2490 |     padding: 8px 10px;
2491 |     background: var(--we-accent-info-bg);
2492 |     border-radius: var(--we-radius-control);
2493 |     color: var(--we-text-secondary);
2494 |     font-size: 10px;
2495 |     margin-bottom: 8px;
2496 |   }
2497 | 
2498 |   .we-css-info[hidden] {
2499 |     display: none;
2500 |   }
2501 | 
2502 |   .we-css-warnings {
2503 |     margin-bottom: 8px;
2504 |   }
2505 | 
2506 |   .we-css-warnings[hidden] {
2507 |     display: none;
2508 |   }
2509 | 
2510 |   .we-css-warning {
2511 |     padding: 6px 10px;
2512 |     background: var(--we-accent-warning-bg);
2513 |     border: 1px solid var(--we-accent-warning-border);
2514 |     border-radius: var(--we-radius-control);
2515 |     color: #92400e;
2516 |     font-size: 10px;
2517 |     margin-bottom: 4px;
2518 |   }
2519 | 
2520 |   .we-css-warning-more {
2521 |     padding: 4px 10px;
2522 |     color: #92400e;
2523 |     font-size: 10px;
2524 |     font-style: italic;
2525 |   }
2526 | 
2527 |   .we-css-empty {
2528 |     padding: 24px 12px;
2529 |     color: #64748b;
2530 |     text-align: center;
2531 |     font-family: system-ui, sans-serif;
2532 |     font-size: 12px;
2533 |   }
2534 | 
2535 |   .we-css-empty[hidden] {
2536 |     display: none;
2537 |   }
2538 | 
2539 |   .we-css-sections {
2540 |     display: flex;
2541 |     flex-direction: column;
2542 |     gap: 0;
2543 |   }
2544 | 
2545 |   .we-css-section {
2546 |     border: 0;
2547 |     border-radius: 0;
2548 |     overflow: visible;
2549 |     background: transparent;
2550 |   }
2551 | 
2552 |   .we-css-section + .we-css-section {
2553 |     border-top: 1px solid var(--we-border-section);
2554 |     padding-top: 12px;
2555 |     margin-top: 4px;
2556 |   }
2557 | 
2558 |   .we-css-section[data-kind="inherited"] {
2559 |     background: transparent;
2560 |   }
2561 | 
2562 |   .we-css-section-header {
2563 |     padding: 0 0 8px 0;
2564 |     background: transparent;
2565 |     border-bottom: 0;
2566 |     font-weight: 600;
2567 |     color: var(--we-text-primary);
2568 |     font-size: 11px;
2569 |     text-transform: none;
2570 |     letter-spacing: normal;
2571 |   }
2572 | 
2573 |   .we-css-section-rules {
2574 |     padding: 0;
2575 |   }
2576 | 
2577 |   /* Flat list style (computed-like view) */
2578 |   .we-css-rule {
2579 |     margin: 0;
2580 |     padding: 0;
2581 |     background: transparent;
2582 |     border: 0;
2583 |     border-radius: 0;
2584 |   }
2585 | 
2586 |   .we-css-rule + .we-css-rule {
2587 |     border-top: 1px solid var(--we-border-section);
2588 |     padding-top: 10px;
2589 |     margin-top: 10px;
2590 |   }
2591 | 
2592 |   .we-css-rule[data-origin="inline"] {
2593 |     background: transparent;
2594 |   }
2595 | 
2596 |   .we-css-rule-header {
2597 |     display: flex;
2598 |     align-items: baseline;
2599 |     gap: 8px;
2600 |     flex-wrap: nowrap;
2601 |     margin-bottom: 8px;
2602 |     padding-bottom: 0;
2603 |     border-bottom: 0;
2604 |   }
2605 | 
2606 |   .we-css-rule-selector {
2607 |     flex: 1 1 auto;
2608 |     min-width: 0;
2609 |     font-weight: 500;
2610 |     color: var(--we-text-secondary);
2611 |     white-space: nowrap;
2612 |     overflow: hidden;
2613 |     text-overflow: ellipsis;
2614 |   }
2615 | 
2616 |   .we-css-rule[data-origin="inline"] .we-css-rule-selector {
2617 |     color: #92400e;
2618 |     font-style: italic;
2619 |   }
2620 | 
2621 |   .we-css-rule-source {
2622 |     flex-shrink: 0;
2623 |     color: var(--we-text-muted);
2624 |     font-size: 10px;
2625 |     margin-left: auto;
2626 |     max-width: 45%;
2627 |     white-space: nowrap;
2628 |     overflow: hidden;
2629 |     text-overflow: ellipsis;
2630 |   }
2631 | 
2632 |   .we-css-rule-spec {
2633 |     flex-shrink: 0;
2634 |     color: var(--we-text-muted);
2635 |     font-size: 9px;
2636 |     padding: 1px 4px;
2637 |     background: var(--we-control-bg);
2638 |     border-radius: 3px;
2639 |   }
2640 | 
2641 |   .we-css-decls {
2642 |     padding-left: 0;
2643 |   }
2644 | 
2645 |   /* Two-column grid layout for declarations */
2646 |   .we-css-decl {
2647 |     display: grid;
2648 |     grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr);
2649 |     column-gap: 12px;
2650 |     align-items: baseline;
2651 |     padding: 5px 0;
2652 |     color: var(--we-text-primary);
2653 |   }
2654 | 
2655 | 
2656 |   .we-css-decl[data-status="overridden"] {
2657 |     text-decoration: line-through;
2658 |     color: var(--we-text-muted);
2659 |   }
2660 | 
2661 |   .we-css-decl-name {
2662 |     color: var(--we-text-secondary);
2663 |     overflow-wrap: anywhere;
2664 |   }
2665 | 
2666 |   /* Hide punctuation for computed-like view */
2667 |   .we-css-decl-colon,
2668 |   .we-css-decl-semi {
2669 |     display: none;
2670 |   }
2671 | 
2672 |   /* Value container for flex layout with !important */
2673 |   .we-css-decl-value-container {
2674 |     display: flex;
2675 |     align-items: baseline;
2676 |     gap: 4px;
2677 |     min-width: 0;
2678 |   }
2679 | 
2680 |   .we-css-decl-value {
2681 |     color: var(--we-text-primary);
2682 |     margin-left: 0;
2683 |     min-width: 0;
2684 |     white-space: nowrap;
2685 |     overflow: hidden;
2686 |     text-overflow: ellipsis;
2687 |   }
2688 | 
2689 |   .we-css-decl[data-status="overridden"] .we-css-decl-name,
2690 |   .we-css-decl[data-status="overridden"] .we-css-decl-value {
2691 |     color: var(--we-text-muted);
2692 |   }
2693 | 
2694 |   .we-css-decl-important {
2695 |     flex-shrink: 0;
2696 |     color: #dc2626;
2697 |     font-weight: 600;
2698 |     font-size: 10px;
2699 |   }
2700 | 
2701 |   .we-css-decl[data-status="overridden"] .we-css-decl-important {
2702 |     color: #b8c4d0;
2703 |   }
2704 | 
2705 |   /* ==========================================================================
2706 |    * Token Picker (Phase 5.4)
2707 |    * ========================================================================== */
2708 | 
2709 |   .we-token-picker {
2710 |     position: absolute;
2711 |     top: calc(100% + 4px);
2712 |     left: 0;
2713 |     right: 0;
2714 |     background: white;
2715 |     border: 1px solid rgba(226, 232, 240, 0.95);
2716 |     border-radius: 8px;
2717 |     box-shadow: 0 8px 24px rgba(15, 23, 42, 0.12);
2718 |     overflow: hidden;
2719 |     z-index: 30;
2720 |   }
2721 | 
2722 |   .we-token-picker[hidden] {
2723 |     display: none;
2724 |   }
2725 | 
2726 |   .we-token-filter {
2727 |     width: 100%;
2728 |     padding: 8px 10px;
2729 |     border: none;
2730 |     border-bottom: 1px solid rgba(226, 232, 240, 0.8);
2731 |     background: transparent;
2732 |     font-family: inherit;
2733 |     font-size: 11px;
2734 |     color: #0f172a;
2735 |     outline: none;
2736 |   }
2737 | 
2738 |   .we-token-filter::placeholder {
2739 |     color: #94a3b8;
2740 |   }
2741 | 
2742 |   .we-token-toggle-row {
2743 |     padding: 6px 10px;
2744 |     border-bottom: 1px solid rgba(226, 232, 240, 0.6);
2745 |     background: rgba(248, 250, 252, 0.5);
2746 |   }
2747 | 
2748 |   .we-token-toggle-label {
2749 |     display: flex;
2750 |     align-items: center;
2751 |     gap: 6px;
2752 |     font-size: 10px;
2753 |     color: #64748b;
2754 |     cursor: pointer;
2755 |   }
2756 | 
2757 |   .we-token-toggle-checkbox {
2758 |     width: 12px;
2759 |     height: 12px;
2760 |     margin: 0;
2761 |     cursor: pointer;
2762 |   }
2763 | 
2764 |   .we-token-list {
2765 |     overflow-y: auto;
2766 |     overscroll-behavior: contain;
2767 |   }
2768 | 
2769 |   .we-token-list[hidden] {
2770 |     display: none;
2771 |   }
2772 | 
2773 |   .we-token-item {
2774 |     display: flex;
2775 |     align-items: center;
2776 |     gap: 8px;
2777 |     width: 100%;
2778 |     text-align: left;
2779 |     padding: 8px 10px;
2780 |     border: none;
2781 |     background: transparent;
2782 |     cursor: pointer;
2783 |     transition: background-color 0.1s ease;
2784 |   }
2785 | 
2786 |   .we-token-item:hover {
2787 |     background: rgba(99, 102, 241, 0.06);
2788 |   }
2789 | 
2790 |   .we-token-item--selected,
2791 |   .we-token-item:focus {
2792 |     outline: none;
2793 |     background: rgba(99, 102, 241, 0.1);
2794 |   }
2795 | 
2796 |   .we-token-swatch {
2797 |     flex-shrink: 0;
2798 |     width: 14px;
2799 |     height: 14px;
2800 |     border-radius: 3px;
2801 |     border: 1px solid rgba(0, 0, 0, 0.1);
2802 |     box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2);
2803 |   }
2804 | 
2805 |   .we-token-name {
2806 |     flex: 1;
2807 |     font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
2808 |     font-size: 11px;
2809 |     color: #0f172a;
2810 |     white-space: nowrap;
2811 |     overflow: hidden;
2812 |     text-overflow: ellipsis;
2813 |   }
2814 | 
2815 |   .we-token-value {
2816 |     flex-shrink: 0;
2817 |     max-width: 80px;
2818 |     font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
2819 |     font-size: 10px;
2820 |     color: #64748b;
2821 |     white-space: nowrap;
2822 |     overflow: hidden;
2823 |     text-overflow: ellipsis;
2824 |   }
2825 | 
2826 |   .we-token-empty {
2827 |     padding: 16px 10px;
2828 |     text-align: center;
2829 |     color: #94a3b8;
2830 |     font-size: 11px;
2831 |   }
2832 | 
2833 |   .we-token-empty[hidden] {
2834 |     display: none;
2835 |   }
2836 | 
2837 |   /* Token button for input fields */
2838 |   .we-token-btn {
2839 |     display: flex;
2840 |     align-items: center;
2841 |     justify-content: center;
2842 |     width: 20px;
2843 |     height: 20px;
2844 |     padding: 0;
2845 |     border: none;
2846 |     border-radius: 4px;
2847 |     background: rgba(99, 102, 241, 0.08);
2848 |     color: #6366f1;
2849 |     cursor: pointer;
2850 |     transition: background-color 0.15s ease;
2851 |   }
2852 | 
2853 |   .we-token-btn:hover {
2854 |     background: rgba(99, 102, 241, 0.15);
2855 |   }
2856 | 
2857 |   .we-token-btn:focus {
2858 |     outline: none;
2859 |     box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.3);
2860 |   }
2861 | 
2862 |   .we-token-btn-icon {
2863 |     width: 12px;
2864 |     height: 12px;
2865 |   }
2866 | 
2867 |   /* ==========================================================================
2868 |    * Token Pill (Phase 5.3)
2869 |    *
2870 |    * Compact pill UI for displaying a CSS var() reference in input fields.
2871 |    * Used when ColorField value is a standalone var(--token) expression.
2872 |    * ========================================================================== */
2873 | 
2874 |   .we-token-pill {
2875 |     flex: 1;
2876 |     min-width: 0;
2877 |     height: 28px;
2878 |     display: flex;
2879 |     align-items: center;
2880 |     gap: 6px;
2881 |     padding: 0 6px 0 4px;
2882 |     background: var(--we-control-bg);
2883 |     border: 1px solid transparent;
2884 |     border-radius: var(--we-radius-control);
2885 |     transition: background 0.1s ease, border-color 0.1s ease;
2886 |   }
2887 | 
2888 |   .we-token-pill:hover:not([data-disabled="true"]) {
2889 |     border-color: var(--we-control-border-hover);
2890 |   }
2891 | 
2892 |   .we-token-pill:focus-within {
2893 |     background: var(--we-control-bg-focus);
2894 |     border-color: var(--we-control-border-focus);
2895 |   }
2896 | 
2897 |   .we-token-pill[data-disabled="true"] {
2898 |     opacity: 0.5;
2899 |     pointer-events: none;
2900 |   }
2901 | 
2902 |   .we-token-pill[hidden] {
2903 |     display: none;
2904 |   }
2905 | 
2906 |   /* Leading slot: holds external element (ColorField swatch) or internal swatch */
2907 |   .we-token-pill__leading {
2908 |     display: flex;
2909 |     align-items: center;
2910 |     flex: 0 0 auto;
2911 |   }
2912 | 
2913 |   /* Internal swatch (used when no external leading element provided) */
2914 |   .we-token-pill__swatch {
2915 |     width: 16px;
2916 |     height: 16px;
2917 |     border-radius: 4px;
2918 |     border: 1px solid rgba(0, 0, 0, 0.1);
2919 |     background: transparent;
2920 |   }
2921 | 
2922 |   /* Main clickable area */
2923 |   .we-token-pill__main {
2924 |     flex: 1;
2925 |     min-width: 0;
2926 |     height: 100%;
2927 |     display: flex;
2928 |     align-items: center;
2929 |     justify-content: space-between;
2930 |     gap: 8px;
2931 |     padding: 0;
2932 |     border: none;
2933 |     background: transparent;
2934 |     color: var(--we-text-primary);
2935 |     cursor: pointer;
2936 |     font-size: 11px;
2937 |     text-align: left;
2938 |   }
2939 | 
2940 |   .we-token-pill__main:focus {
2941 |     outline: none;
2942 |   }
2943 | 
2944 |   .we-token-pill__main:disabled {
2945 |     cursor: default;
2946 |   }
2947 | 
2948 |   /* Token name with ellipsis */
2949 |   .we-token-pill__name {
2950 |     min-width: 0;
2951 |     white-space: nowrap;
2952 |     overflow: hidden;
2953 |     text-overflow: ellipsis;
2954 |     font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
2955 |     font-size: 11px;
2956 |   }
2957 | 
2958 |   /* Link icon (rotated 45° to indicate variable binding) */
2959 |   .we-token-pill__icon {
2960 |     width: 14px;
2961 |     height: 14px;
2962 |     flex: 0 0 auto;
2963 |     color: var(--we-text-muted);
2964 |     transform: rotate(45deg);
2965 |   }
2966 | 
2967 |   /* Clear button (hover to reveal) */
2968 |   .we-token-pill__clear {
2969 |     width: 18px;
2970 |     height: 18px;
2971 |     display: flex;
2972 |     align-items: center;
2973 |     justify-content: center;
2974 |     padding: 0;
2975 |     border: none;
2976 |     border-radius: 4px;
2977 |     background: transparent;
2978 |     color: var(--we-text-muted);
2979 |     font-size: 14px;
2980 |     line-height: 1;
2981 |     cursor: pointer;
2982 |     opacity: 0;
2983 |     pointer-events: none;
2984 |     transition: opacity 0.12s ease, background 0.12s ease, color 0.12s ease;
2985 |   }
2986 | 
2987 |   .we-token-pill:hover .we-token-pill__clear {
2988 |     opacity: 1;
2989 |     pointer-events: auto;
2990 |   }
2991 | 
2992 |   .we-token-pill__clear:hover {
2993 |     background: rgba(15, 23, 42, 0.06);
2994 |     color: var(--we-text-primary);
2995 |   }
2996 | 
2997 |   .we-token-pill__clear:focus {
2998 |     outline: none;
2999 |     opacity: 1;
3000 |     pointer-events: auto;
3001 |   }
3002 | 
3003 |   .we-token-pill__clear:disabled {
3004 |     cursor: default;
3005 |   }
3006 | 
3007 |   /* ==========================================================================
3008 |      Props Panel (Phase 7.3)
3009 |      ========================================================================== */
3010 | 
3011 |   .we-props-panel {
3012 |     display: flex;
3013 |     flex-direction: column;
3014 |     gap: 10px;
3015 |   }
3016 | 
3017 |   .we-props-meta {
3018 |     padding: 0 0 8px 0;
3019 |     display: flex;
3020 |     flex-direction: column;
3021 |     gap: 6px;
3022 |   }
3023 | 
3024 |   .we-props-meta-title {
3025 |     display: flex;
3026 |     align-items: center;
3027 |     justify-content: space-between;
3028 |     gap: 10px;
3029 |     color: var(--we-text-primary);
3030 |     font-size: 11px;
3031 |     font-weight: 600;
3032 |   }
3033 | 
3034 |   .we-props-component {
3035 |     min-width: 0;
3036 |     overflow: hidden;
3037 |     text-overflow: ellipsis;
3038 |     white-space: nowrap;
3039 |   }
3040 | 
3041 |   .we-props-badge {
3042 |     flex: 0 0 auto;
3043 |     font-size: 10px;
3044 |     font-weight: 600;
3045 |     padding: 2px 6px;
3046 |     border-radius: 999px;
3047 |     background: var(--we-accent-brand-bg);
3048 |     color: #1d4ed8;
3049 |   }
3050 | 
3051 |   .we-props-status {
3052 |     font-size: 11px;
3053 |     color: #64748b;
3054 |     font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
3055 |   }
3056 | 
3057 |   .we-props-warning {
3058 |     font-size: 11px;
3059 |     color: #92400e;
3060 |     background: var(--we-accent-warning-bg);
3061 |     border: 1px solid var(--we-accent-warning-border);
3062 |     border-radius: var(--we-radius-control);
3063 |     padding: 6px 8px;
3064 |   }
3065 | 
3066 |   .we-props-error {
3067 |     font-size: 11px;
3068 |     color: #b91c1c;
3069 |     background: var(--we-accent-danger-bg);
3070 |     border: 1px solid var(--we-accent-danger-border);
3071 |     border-radius: var(--we-radius-control);
3072 |     padding: 6px 8px;
3073 |   }
3074 | 
3075 |   .we-props-source {
3076 |     display: flex;
3077 |     align-items: center;
3078 |     gap: 8px;
3079 |     font-size: 11px;
3080 |     padding: 4px 0;
3081 |   }
3082 | 
3083 |   .we-props-source[hidden] {
3084 |     display: none;
3085 |   }
3086 | 
3087 |   .we-props-source-label {
3088 |     flex: 0 0 auto;
3089 |     color: #64748b;
3090 |     font-weight: 500;
3091 |   }
3092 | 
3093 |   .we-props-source-path {
3094 |     flex: 1 1 auto;
3095 |     min-width: 0;
3096 |     overflow: hidden;
3097 |     text-overflow: ellipsis;
3098 |     white-space: nowrap;
3099 |     color: var(--we-text-primary);
3100 |     font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
3101 |   }
3102 | 
3103 |   .we-btn-small {
3104 |     padding: 2px 8px;
3105 |     font-size: 11px;
3106 |   }
3107 | 
3108 |   /* Source open button - minimal link style */
3109 |   .we-props-source-btn {
3110 |     flex: 0 0 auto;
3111 |     display: inline-flex;
3112 |     align-items: center;
3113 |     justify-content: center;
3114 |     padding: 2px;
3115 |     margin-left: 2px;
3116 |     border: none;
3117 |     background: none;
3118 |     color: #64748b;
3119 |     cursor: pointer;
3120 |     transition: color 0.12s ease;
3121 |   }
3122 | 
3123 |   .we-props-source-btn:hover:not(:disabled) {
3124 |     color: #3b82f6;
3125 |   }
3126 | 
3127 |   .we-props-source-btn:disabled {
3128 |     opacity: 0.35;
3129 |     cursor: not-allowed;
3130 |   }
3131 | 
3132 |   .we-props-source-btn svg {
3133 |     display: block;
3134 |   }
3135 | 
3136 |   /* Tooltip - fixed position, mounted at shadow root level */
3137 |   .we-tooltip {
3138 |     position: fixed;
3139 |     transform: translateX(-50%);
3140 |     padding: 4px 8px;
3141 |     font-size: 11px;
3142 |     font-weight: 500;
3143 |     line-height: 1.2;
3144 |     color: #fff;
3145 |     background: rgba(15, 23, 42, 0.92);
3146 |     border-radius: 4px;
3147 |     white-space: nowrap;
3148 |     pointer-events: none;
3149 |     z-index: 10000;
3150 |   }
3151 | 
3152 |   .we-tooltip[hidden] {
3153 |     display: none;
3154 |   }
3155 | 
3156 |   .we-props-title-left {
3157 |     display: flex;
3158 |     align-items: center;
3159 |     gap: 6px;
3160 |     min-width: 0;
3161 |   }
3162 | 
3163 |   .we-props-title-actions {
3164 |     display: flex;
3165 |     align-items: center;
3166 |     gap: 2px;
3167 |     margin-left: auto;
3168 |   }
3169 | 
3170 |   /* Action button - minimal icon style for title bar */
3171 |   .we-props-action-btn {
3172 |     display: inline-flex;
3173 |     align-items: center;
3174 |     justify-content: center;
3175 |     padding: 4px;
3176 |     border: none;
3177 |     background: none;
3178 |     color: var(--we-text-secondary);
3179 |     cursor: pointer;
3180 |     transition: color 0.12s ease;
3181 |   }
3182 | 
3183 |   .we-props-action-btn:hover:not(:disabled) {
3184 |     color: var(--we-text-primary);
3185 |   }
3186 | 
3187 |   .we-props-action-btn:disabled {
3188 |     opacity: 0.35;
3189 |     cursor: not-allowed;
3190 |   }
3191 | 
3192 |   .we-props-action-btn svg {
3193 |     display: block;
3194 |   }
3195 | 
3196 |   .we-props-list {
3197 |     overflow: hidden;
3198 |   }
3199 | 
3200 |   .we-props-empty {
3201 |     padding: 16px 12px;
3202 |     color: #64748b;
3203 |     font-size: 12px;
3204 |     text-align: center;
3205 |   }
3206 | 
3207 |   .we-props-empty[hidden] {
3208 |     display: none;
3209 |   }
3210 | 
3211 |   /* Loading animations */
3212 |   @keyframes we-shimmer {
3213 |     to {
3214 |       background-position: 200% center;
3215 |     }
3216 |   }
3217 | 
3218 |   @keyframes we-spin {
3219 |     to {
3220 |       transform: rotate(360deg);
3221 |     }
3222 |   }
3223 | 
3224 |   .we-props-empty.we-loading {
3225 |     display: flex;
3226 |     align-items: center;
3227 |     justify-content: center;
3228 |     gap: 8px;
3229 |   }
3230 | 
3231 |   .we-props-empty.we-loading svg {
3232 |     flex-shrink: 0;
3233 |     color: #94a3b8;
3234 |   }
3235 | 
3236 |   .we-props-empty.we-loading span {
3237 |     background: linear-gradient(
3238 |       90deg,
3239 |       #64748b 0%,
3240 |       #94a3b8 50%,
3241 |       #64748b 100%
3242 |     );
3243 |     background-size: 200% auto;
3244 |     color: transparent;
3245 |     -webkit-background-clip: text;
3246 |     background-clip: text;
3247 |     animation: we-shimmer 2s linear infinite;
3248 |   }
3249 | 
3250 |   .we-props-group {
3251 |     padding: 0 0 8px 0;
3252 |     margin-top: 4px;
3253 |     color: var(--we-text-primary);
3254 |     font-size: 11px;
3255 |     font-weight: 600;
3256 |     border-top: 1px solid var(--we-border-section);
3257 |     padding-top: 12px;
3258 |   }
3259 | 
3260 |   .we-props-group:first-child {
3261 |     border-top: 0;
3262 |     margin-top: 0;
3263 |     padding-top: 0;
3264 |   }
3265 | 
3266 |   .we-props-group + .we-props-row {
3267 |     border-top: 0;
3268 |   }
3269 | 
3270 |   .we-props-rows {
3271 |     display: flex;
3272 |     flex-direction: column;
3273 |   }
3274 | 
3275 |   .we-props-row {
3276 |     display: flex;
3277 |     align-items: center;
3278 |     gap: 10px;
3279 |     padding: 8px 0;
3280 |     border-top: 1px solid var(--we-border-section);
3281 |   }
3282 | 
3283 |   .we-props-row:first-child {
3284 |     border-top: 0;
3285 |   }
3286 | 
3287 |   .we-props-key {
3288 |     flex: 0 0 110px;
3289 |     min-width: 0;
3290 |     overflow: hidden;
3291 |     text-overflow: ellipsis;
3292 |     white-space: nowrap;
3293 |     font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
3294 |     font-size: 11px;
3295 |     color: #334155;
3296 |   }
3297 | 
3298 |   .we-props-value {
3299 |     flex: 1;
3300 |     min-width: 0;
3301 |     display: flex;
3302 |     align-items: center;
3303 |     justify-content: flex-end;
3304 |     gap: 8px;
3305 |   }
3306 | 
3307 |   .we-props-value--readonly {
3308 |     justify-content: flex-start;
3309 |     color: #475569;
3310 |     font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
3311 |     font-size: 11px;
3312 |     white-space: nowrap;
3313 |     overflow: hidden;
3314 |     text-overflow: ellipsis;
3315 |   }
3316 | 
3317 |   .we-props-input {
3318 |     width: 140px;
3319 |     max-width: 100%;
3320 |   }
3321 | 
3322 |   .we-props-input--invalid {
3323 |     border-color: rgba(248, 113, 113, 0.85);
3324 |   }
3325 | 
3326 |   .we-props-bool {
3327 |     display: inline-flex;
3328 |     align-items: center;
3329 |     gap: 8px;
3330 |     font-size: 11px;
3331 |     color: #475569;
3332 |     cursor: pointer;
3333 |   }
3334 | 
3335 |   .we-props-checkbox {
3336 |     width: 14px;
3337 |     height: 14px;
3338 |     accent-color: #6366f1;
3339 |     cursor: pointer;
3340 |   }
3341 | 
3342 |   .we-props-checkbox:disabled {
3343 |     cursor: not-allowed;
3344 |     opacity: 0.5;
3345 |   }
3346 | 
3347 |   /* ==========================================================================
3348 |    * Color Field (Phase 4.4)
3349 |    * ========================================================================== */
3350 | 
3351 |   .we-color-field {
3352 |     flex: 1;
3353 |     display: flex;
3354 |     align-items: center;
3355 |     gap: 6px;
3356 |     min-width: 0;
3357 |   }
3358 | 
3359 |   .we-color-swatch {
3360 |     flex: 0 0 auto;
3361 |     width: 24px;
3362 |     height: 24px;
3363 |     padding: 0;
3364 |     position: relative;
3365 |     border: 1px solid var(--we-border-subtle);
3366 |     border-radius: var(--we-radius-control);
3367 |     background: var(--we-control-bg);
3368 |     cursor: pointer;
3369 |     transition: border-color 0.15s ease, box-shadow 0.15s ease;
3370 |     overflow: hidden;
3371 |   }
3372 | 
3373 |   .we-color-swatch:hover {
3374 |     border-color: var(--we-border-strong);
3375 |   }
3376 | 
3377 |   .we-color-swatch:focus-visible,
3378 |   .we-color-swatch:focus-within {
3379 |     outline: none;
3380 |     box-shadow: 0 0 0 2px var(--we-focus-ring);
3381 |   }
3382 | 
3383 |   .we-color-swatch:disabled {
3384 |     opacity: 0.5;
3385 |     cursor: not-allowed;
3386 |   }
3387 | 
3388 |   /* Native color input overlays the swatch for direct click interaction */
3389 |   .we-color-native-input {
3390 |     position: absolute;
3391 |     inset: 0;
3392 |     width: 100%;
3393 |     height: 100%;
3394 |     opacity: 0;
3395 |     cursor: pointer;
3396 |     border: none;
3397 |     padding: 0;
3398 |     margin: 0;
3399 |   }
3400 | 
3401 |   .we-color-text {
3402 |     flex: 1;
3403 |     min-width: 0;
3404 |   }
3405 | 
3406 |   /* ==========================================================================
3407 |    * Tooltip (data-tooltip)
3408 |    *
3409 |    * CSS-only tooltips using the data-tooltip attribute.
3410 |    * Shows on hover/focus with minimal delay.
3411 |    * ========================================================================== */
3412 | 
3413 |   [data-tooltip] {
3414 |     position: relative;
3415 |   }
3416 | 
3417 |   [data-tooltip]::after {
3418 |     content: attr(data-tooltip);
3419 |     position: absolute;
3420 |     bottom: calc(100% + 6px);
3421 |     left: 50%;
3422 |     transform: translateX(-50%);
3423 |     padding: 4px 8px;
3424 |     font-size: 11px;
3425 |     font-family: inherit;
3426 |     font-weight: 400;
3427 |     line-height: 1.3;
3428 |     white-space: nowrap;
3429 |     color: var(--we-surface-bg);
3430 |     background-color: var(--we-text-primary);
3431 |     border-radius: var(--we-radius-control);
3432 |     opacity: 0;
3433 |     visibility: hidden;
3434 |     transition:
3435 |       opacity 100ms ease,
3436 |       visibility 100ms ease;
3437 |     pointer-events: none;
3438 |     z-index: 99999;
3439 |   }
3440 | 
3441 |   [data-tooltip]::before {
3442 |     content: '';
3443 |     position: absolute;
3444 |     bottom: calc(100% + 2px);
3445 |     left: 50%;
3446 |     transform: translateX(-50%);
3447 |     border: 4px solid transparent;
3448 |     border-top-color: var(--we-text-primary);
3449 |     opacity: 0;
3450 |     visibility: hidden;
3451 |     transition:
3452 |       opacity 100ms ease,
3453 |       visibility 100ms ease;
3454 |     pointer-events: none;
3455 |     z-index: 99999;
3456 |   }
3457 | 
3458 |   [data-tooltip]:hover::after,
3459 |   [data-tooltip]:focus-visible::after,
3460 |   [data-tooltip]:focus-within::after {
3461 |     opacity: 1;
3462 |     visibility: visible;
3463 |   }
3464 | 
3465 |   [data-tooltip]:hover::before,
3466 |   [data-tooltip]:focus-visible::before,
3467 |   [data-tooltip]:focus-within::before {
3468 |     opacity: 1;
3469 |     visibility: visible;
3470 |   }
3471 | 
3472 |   /* ==========================================================================
3473 |    * Global Hidden Rule
3474 |    * Ensures [hidden] attribute always hides elements, even when they have
3475 |    * explicit display values (flex, inline-flex, etc.)
3476 |    * ========================================================================== */
3477 |   [hidden] {
3478 |     display: none !important;
3479 |   }
3480 | `;
3481 | 
3482 | // =============================================================================
3483 | // Implementation
3484 | // =============================================================================
3485 | 
3486 | /**
3487 |  * Set a CSS property with !important flag
3488 |  */
3489 | function setImportantStyle(element: HTMLElement, property: string, value: string): void {
3490 |   element.style.setProperty(property, value, 'important');
3491 | }
3492 | 
3493 | // Note: The legacy createPanelContent has been replaced by createPropertyPanel (Phase 3)
3494 | 
3495 | /**
3496 |  * Mount the Shadow DOM host and return a manager interface
3497 |  */
3498 | export function mountShadowHost(options: ShadowHostOptions = {}): ShadowHostManager {
3499 |   const disposer = new Disposer();
3500 |   let elements: ShadowHostElements | null = null;
3501 | 
3502 |   // Clean up any existing host (from crash/reload)
3503 |   const existing = document.getElementById(WEB_EDITOR_V2_HOST_ID);
3504 |   if (existing) {
3505 |     try {
3506 |       existing.remove();
3507 |     } catch {
3508 |       // Best-effort cleanup
3509 |     }
3510 |   }
3511 | 
3512 |   // Create host element
3513 |   const host = document.createElement('div');
3514 |   host.id = WEB_EDITOR_V2_HOST_ID;
3515 |   host.setAttribute('data-mcp-web-editor', 'v2');
3516 | 
3517 |   // Apply host styles with !important to resist page CSS
3518 |   setImportantStyle(host, 'position', 'fixed');
3519 |   setImportantStyle(host, 'inset', '0');
3520 |   setImportantStyle(host, 'z-index', String(WEB_EDITOR_V2_Z_INDEX));
3521 |   setImportantStyle(host, 'pointer-events', 'none');
3522 |   setImportantStyle(host, 'contain', 'layout style paint');
3523 |   setImportantStyle(host, 'isolation', 'isolate');
3524 | 
3525 |   // Create shadow root
3526 |   const shadowRoot = host.attachShadow({ mode: 'open' });
3527 | 
3528 |   // Add styles
3529 |   const styleEl = document.createElement('style');
3530 |   styleEl.textContent = SHADOW_HOST_STYLES;
3531 |   shadowRoot.append(styleEl);
3532 | 
3533 |   // Create overlay container (for Canvas)
3534 |   const overlayRoot = document.createElement('div');
3535 |   overlayRoot.id = WEB_EDITOR_V2_OVERLAY_ID;
3536 | 
3537 |   // Create UI container (for panels)
3538 |   // Note: Property Panel is now created separately by editor.ts (Phase 3)
3539 |   const uiRoot = document.createElement('div');
3540 |   uiRoot.id = WEB_EDITOR_V2_UI_ID;
3541 | 
3542 |   shadowRoot.append(overlayRoot, uiRoot);
3543 | 
3544 |   // Mount to document
3545 |   const mountPoint = document.documentElement ?? document.body;
3546 |   mountPoint.append(host);
3547 |   disposer.add(() => host.remove());
3548 | 
3549 |   elements = { host, shadowRoot, overlayRoot, uiRoot };
3550 | 
3551 |   // Event isolation: prevent UI events from bubbling to page
3552 |   const blockedEvents = [
3553 |     'pointerdown',
3554 |     'pointerup',
3555 |     'pointermove',
3556 |     'pointerenter',
3557 |     'pointerleave',
3558 |     'mousedown',
3559 |     'mouseup',
3560 |     'mousemove',
3561 |     'mouseenter',
3562 |     'mouseleave',
3563 |     'click',
3564 |     'dblclick',
3565 |     'contextmenu',
3566 |     'keydown',
3567 |     'keyup',
3568 |     'keypress',
3569 |     'wheel',
3570 |     'touchstart',
3571 |     'touchmove',
3572 |     'touchend',
3573 |     'touchcancel',
3574 |     'focus',
3575 |     'blur',
3576 |     'input',
3577 |     'change',
3578 |   ];
3579 | 
3580 |   const stopPropagation = (event: Event) => {
3581 |     event.stopPropagation();
3582 |   };
3583 | 
3584 |   for (const eventType of blockedEvents) {
3585 |     disposer.listen(uiRoot, eventType, stopPropagation);
3586 |     // Also block overlay interactions (handles, guides) from bubbling to page
3587 |     // Note: capture-phase listeners on the page cannot be fully prevented
3588 |     disposer.listen(overlayRoot, eventType, stopPropagation);
3589 |   }
3590 | 
3591 |   // Helper: check if a node is part of the editor
3592 |   const isOverlayElement = (node: unknown): boolean => {
3593 |     if (!(node instanceof Node)) return false;
3594 |     if (node === host) return true;
3595 | 
3596 |     const root = typeof node.getRootNode === 'function' ? node.getRootNode() : null;
3597 |     return root instanceof ShadowRoot && root.host === host;
3598 |   };
3599 | 
3600 |   // Helper: check if an event came from the editor UI
3601 |   const isEventFromUi = (event: Event): boolean => {
3602 |     try {
3603 |       if (typeof event.composedPath === 'function') {
3604 |         return event.composedPath().some((el) => isOverlayElement(el));
3605 |       }
3606 |     } catch {
3607 |       // Fallback to target
3608 |     }
3609 |     return isOverlayElement(event.target);
3610 |   };
3611 | 
3612 |   return {
3613 |     getElements: () => elements,
3614 |     isOverlayElement,
3615 |     isEventFromUi,
3616 |     dispose: () => {
3617 |       elements = null;
3618 |       disposer.dispose();
3619 |     },
3620 |   };
3621 | }
3622 | 
```
Page 57/60FirstPrevNextLast