@livepeer-frameworks/player-wc 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/components/fw-dev-mode-panel.js +1 -0
- package/dist/cjs/components/fw-dev-mode-panel.js.map +1 -1
- package/dist/cjs/components/fw-player-controls.js +12 -3
- package/dist/cjs/components/fw-player-controls.js.map +1 -1
- package/dist/cjs/components/fw-player.js +50 -10
- package/dist/cjs/components/fw-player.js.map +1 -1
- package/dist/cjs/components/fw-settings-menu.js +18 -1
- package/dist/cjs/components/fw-settings-menu.js.map +1 -1
- package/dist/cjs/controllers/player-controller-host.js +23 -1
- package/dist/cjs/controllers/player-controller-host.js.map +1 -1
- package/dist/cjs/styles/shared-styles.js +18 -0
- package/dist/cjs/styles/shared-styles.js.map +1 -1
- package/dist/esm/components/fw-dev-mode-panel.js +1 -0
- package/dist/esm/components/fw-dev-mode-panel.js.map +1 -1
- package/dist/esm/components/fw-player-controls.js +12 -3
- package/dist/esm/components/fw-player-controls.js.map +1 -1
- package/dist/esm/components/fw-player.js +50 -10
- package/dist/esm/components/fw-player.js.map +1 -1
- package/dist/esm/components/fw-settings-menu.js +18 -1
- package/dist/esm/components/fw-settings-menu.js.map +1 -1
- package/dist/esm/controllers/player-controller-host.js +23 -1
- package/dist/esm/controllers/player-controller-host.js.map +1 -1
- package/dist/esm/styles/shared-styles.js +18 -0
- package/dist/esm/styles/shared-styles.js.map +1 -1
- package/dist/fw-player.iife.js +40 -20
- package/dist/types/components/fw-player-controls.d.ts +1 -0
- package/dist/types/components/fw-player.d.ts +4 -0
- package/dist/types/components/fw-settings-menu.d.ts +1 -0
- package/package.json +2 -2
- package/src/components/fw-dev-mode-panel.ts +1 -0
- package/src/components/fw-player-controls.ts +10 -3
- package/src/components/fw-player.ts +50 -10
- package/src/components/fw-settings-menu.ts +41 -1
- package/src/controllers/player-controller-host.ts +23 -1
- package/src/styles/shared-styles.ts +18 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"player-controller-host.js","sources":["../../../../src/controllers/player-controller-host.ts"],"sourcesContent":["/**\n * PlayerControllerHost — Lit ReactiveController wrapping the headless PlayerController.\n * Direct port of usePlayerController.ts from player-react.\n */\nimport type { ReactiveController, ReactiveControllerHost } from \"lit\";\nimport {\n PlayerController,\n type PlayerControllerConfig,\n type PlayerState,\n type StreamState,\n type StreamInfo,\n type PlaybackQuality,\n type ContentEndpoints,\n type ContentMetadata,\n type ClassifiedError,\n} from \"@livepeer-frameworks/player-core\";\n\nexport interface PlayerControllerHostState {\n state: PlayerState;\n streamState: StreamState | null;\n endpoints: ContentEndpoints | null;\n metadata: ContentMetadata | null;\n videoElement: HTMLVideoElement | null;\n currentTime: number;\n duration: number;\n isPlaying: boolean;\n isPaused: boolean;\n isBuffering: boolean;\n isMuted: boolean;\n volume: number;\n error: string | null;\n errorDetails: ClassifiedError[\"details\"] | null;\n isPassiveError: boolean;\n hasPlaybackStarted: boolean;\n isHoldingSpeed: boolean;\n holdSpeed: number;\n isHovering: boolean;\n shouldShowControls: boolean;\n isLoopEnabled: boolean;\n isFullscreen: boolean;\n isPiPActive: boolean;\n isEffectivelyLive: boolean;\n shouldShowIdleScreen: boolean;\n currentPlayerInfo: { name: string; shortname: string } | null;\n currentSourceInfo: { url: string; type: string } | null;\n playbackQuality: PlaybackQuality | null;\n subtitlesEnabled: boolean;\n qualities: Array<{\n id: string;\n label: string;\n bitrate?: number;\n width?: number;\n height?: number;\n isAuto?: boolean;\n active?: boolean;\n }>;\n textTracks: Array<{ id: string; label: string; lang?: string; active: boolean }>;\n streamInfo: StreamInfo | null;\n toast: { message: string; timestamp: number } | null;\n}\n\nconst initialState: PlayerControllerHostState = {\n state: \"booting\",\n streamState: null,\n endpoints: null,\n metadata: null,\n videoElement: null,\n currentTime: 0,\n duration: NaN,\n isPlaying: false,\n isPaused: true,\n isBuffering: false,\n isMuted: true,\n volume: 1,\n error: null,\n errorDetails: null,\n isPassiveError: false,\n hasPlaybackStarted: false,\n isHoldingSpeed: false,\n holdSpeed: 2,\n isHovering: false,\n shouldShowControls: false,\n isLoopEnabled: false,\n isFullscreen: false,\n isPiPActive: false,\n isEffectivelyLive: false,\n shouldShowIdleScreen: true,\n currentPlayerInfo: null,\n currentSourceInfo: null,\n playbackQuality: null,\n subtitlesEnabled: false,\n qualities: [],\n textTracks: [],\n streamInfo: null,\n toast: null,\n};\n\ntype HostElement = ReactiveControllerHost & HTMLElement;\n\nexport class PlayerControllerHost implements ReactiveController {\n host: HostElement;\n private controller: PlayerController | null = null;\n private unsubs: Array<() => void> = [];\n private currentConfig: PlayerControllerConfig | null = null;\n\n s: PlayerControllerHostState = { ...initialState };\n\n constructor(host: HostElement) {\n this.host = host;\n host.addController(this);\n }\n\n // ---- Configuration & Lifecycle ----\n\n configure(config: PlayerControllerConfig) {\n this.currentConfig = config;\n }\n\n async attach(container: HTMLDivElement) {\n if (!this.currentConfig) return;\n this.teardown();\n\n const controller = new PlayerController({\n contentId: this.currentConfig.contentId,\n contentType: this.currentConfig.contentType,\n endpoints: this.currentConfig.endpoints,\n gatewayUrl: this.currentConfig.gatewayUrl,\n mistUrl: this.currentConfig.mistUrl,\n authToken: this.currentConfig.authToken,\n autoplay: this.currentConfig.autoplay,\n muted: this.currentConfig.muted,\n controls: this.currentConfig.controls,\n poster: this.currentConfig.poster,\n debug: this.currentConfig.debug,\n });\n\n this.controller = controller;\n this.subscribeToEvents(controller);\n\n this.update({ isLoopEnabled: controller.isLoopEnabled() });\n\n try {\n await controller.attach(container);\n } catch (err) {\n console.warn(\"[PlayerControllerHost] Attach failed:\", err);\n }\n }\n\n hostConnected() {\n // Controller attachment happens in firstUpdated of the host element\n }\n\n hostDisconnected() {\n this.teardown();\n this.s = { ...initialState };\n }\n\n private teardown() {\n this.unsubs.forEach((fn) => fn());\n this.unsubs = [];\n this.controller?.destroy();\n this.controller = null;\n }\n\n // ---- State Updates ----\n\n private update(partial: Partial<PlayerControllerHostState>) {\n Object.assign(this.s, partial);\n this.host.requestUpdate();\n }\n\n private syncState() {\n if (!this.controller) return;\n const c = this.controller;\n this.update({\n isPlaying: c.isPlaying(),\n isPaused: c.isPaused(),\n isBuffering: c.isBuffering(),\n isMuted: c.isMuted(),\n volume: c.getVolume(),\n hasPlaybackStarted: c.hasPlaybackStarted(),\n shouldShowControls: c.shouldShowControls(),\n shouldShowIdleScreen: c.shouldShowIdleScreen(),\n playbackQuality: c.getPlaybackQuality(),\n isLoopEnabled: c.isLoopEnabled(),\n subtitlesEnabled: c.isSubtitlesEnabled(),\n qualities: c.getQualities(),\n textTracks: c.getTextTracks(),\n streamInfo: c.getStreamInfo(),\n });\n }\n\n // ---- Event Subscriptions (mirrors usePlayerController exactly) ----\n\n private subscribeToEvents(controller: PlayerController) {\n const u = this.unsubs;\n\n u.push(\n controller.on(\"stateChange\", ({ state }) => {\n this.update({ state });\n this.dispatchEvent(\"fw-state-change\", { state });\n })\n );\n\n u.push(\n controller.on(\"streamStateChange\", ({ state: streamState }) => {\n this.update({\n streamState,\n metadata: controller.getMetadata(),\n isEffectivelyLive: controller.isEffectivelyLive(),\n shouldShowIdleScreen: controller.shouldShowIdleScreen(),\n });\n this.dispatchEvent(\"fw-stream-state\", { state: streamState });\n })\n );\n\n u.push(\n controller.on(\"timeUpdate\", ({ currentTime, duration }) => {\n this.update({ currentTime, duration });\n this.dispatchEvent(\"fw-time-update\", { currentTime, duration });\n })\n );\n\n u.push(\n controller.on(\"error\", ({ error }) => {\n this.update({\n error,\n isPassiveError: controller.isPassiveError(),\n });\n this.dispatchEvent(\"fw-error\", { error });\n })\n );\n\n u.push(\n controller.on(\"errorCleared\", () => {\n this.update({ error: null, isPassiveError: false });\n })\n );\n\n u.push(\n controller.on(\"ready\", ({ videoElement }) => {\n this.update({\n videoElement,\n endpoints: controller.getEndpoints(),\n metadata: controller.getMetadata(),\n streamInfo: controller.getStreamInfo(),\n isEffectivelyLive: controller.isEffectivelyLive(),\n shouldShowIdleScreen: controller.shouldShowIdleScreen(),\n currentPlayerInfo: controller.getCurrentPlayerInfo(),\n currentSourceInfo: controller.getCurrentSourceInfo(),\n qualities: controller.getQualities(),\n textTracks: controller.getTextTracks(),\n });\n this.dispatchEvent(\"fw-ready\", { videoElement });\n\n const handleVideoEvent = () => {\n if (this.controller?.shouldSuppressVideoEvents?.()) return;\n this.syncState();\n };\n videoElement.addEventListener(\"play\", handleVideoEvent);\n videoElement.addEventListener(\"pause\", handleVideoEvent);\n videoElement.addEventListener(\"waiting\", handleVideoEvent);\n videoElement.addEventListener(\"playing\", handleVideoEvent);\n u.push(() => {\n videoElement.removeEventListener(\"play\", handleVideoEvent);\n videoElement.removeEventListener(\"pause\", handleVideoEvent);\n videoElement.removeEventListener(\"waiting\", handleVideoEvent);\n videoElement.removeEventListener(\"playing\", handleVideoEvent);\n });\n })\n );\n\n u.push(\n controller.on(\"playerSelected\", ({ player: _player, source }) => {\n this.update({\n currentPlayerInfo: controller.getCurrentPlayerInfo(),\n currentSourceInfo: { url: source.url, type: source.type },\n qualities: controller.getQualities(),\n textTracks: controller.getTextTracks(),\n });\n })\n );\n\n u.push(\n controller.on(\"volumeChange\", ({ volume, muted }) => {\n this.update({ volume, isMuted: muted });\n this.dispatchEvent(\"fw-volume-change\", { volume, muted });\n })\n );\n\n u.push(\n controller.on(\"loopChange\", ({ isLoopEnabled }) => {\n this.update({ isLoopEnabled });\n })\n );\n\n u.push(\n controller.on(\"fullscreenChange\", ({ isFullscreen }) => {\n this.update({ isFullscreen });\n this.dispatchEvent(\"fw-fullscreen-change\", { isFullscreen });\n })\n );\n\n u.push(\n controller.on(\"pipChange\", ({ isPiP }) => {\n this.update({ isPiPActive: isPiP });\n this.dispatchEvent(\"fw-pip-change\", { isPiP });\n })\n );\n\n u.push(\n controller.on(\"holdSpeedStart\", ({ speed }) => {\n this.update({ isHoldingSpeed: true, holdSpeed: speed });\n })\n );\n\n u.push(\n controller.on(\"holdSpeedEnd\", () => {\n this.update({ isHoldingSpeed: false });\n })\n );\n\n u.push(\n controller.on(\"hoverStart\", () => {\n this.update({ isHovering: true, shouldShowControls: true });\n })\n );\n\n u.push(\n controller.on(\"hoverEnd\", () => {\n this.update({\n isHovering: false,\n shouldShowControls: controller.shouldShowControls(),\n });\n })\n );\n\n u.push(\n controller.on(\"captionsChange\", ({ enabled }) => {\n this.update({ subtitlesEnabled: enabled, textTracks: controller.getTextTracks() });\n })\n );\n\n u.push(\n controller.on(\"protocolSwapped\", (data) => {\n const message = `Switched to ${data.toProtocol}`;\n this.update({ toast: { message, timestamp: Date.now() } });\n this.dispatchEvent(\"fw-protocol-swapped\", data);\n })\n );\n\n u.push(\n controller.on(\"playbackFailed\", (data) => {\n this.update({\n error: data.message,\n errorDetails: data.details ?? null,\n isPassiveError: false,\n });\n this.dispatchEvent(\"fw-playback-failed\", {\n code: data.code,\n message: data.message,\n });\n })\n );\n }\n\n // ---- Event Dispatching ----\n\n private dispatchEvent(name: string, detail: unknown) {\n this.host.dispatchEvent(new CustomEvent(name, { detail, bubbles: true, composed: true }));\n }\n\n // ---- Action Methods ----\n\n async play() {\n await this.controller?.play();\n }\n pause() {\n this.controller?.pause();\n }\n togglePlay() {\n this.controller?.togglePlay();\n }\n seek(time: number) {\n this.controller?.seek(time);\n }\n seekBy(delta: number) {\n this.controller?.seekBy(delta);\n }\n jumpToLive() {\n this.controller?.jumpToLive();\n }\n setVolume(volume: number) {\n this.controller?.setVolume(volume);\n }\n toggleMute() {\n this.controller?.toggleMute();\n }\n toggleLoop() {\n this.controller?.toggleLoop();\n }\n async toggleFullscreen() {\n await this.controller?.toggleFullscreen();\n }\n async togglePiP() {\n await this.controller?.togglePictureInPicture();\n }\n toggleSubtitles() {\n this.controller?.toggleSubtitles();\n }\n\n clearError() {\n this.controller?.clearError();\n this.update({ error: null, errorDetails: null, isPassiveError: false });\n }\n\n dismissToast() {\n this.update({ toast: null });\n }\n\n async retry() {\n await this.controller?.retry();\n }\n async reload() {\n await this.controller?.reload();\n }\n\n getQualities() {\n return this.controller?.getQualities() ?? [];\n }\n selectQuality(id: string) {\n this.controller?.selectQuality(id);\n }\n getTextTracks() {\n return this.controller?.getTextTracks() ?? [];\n }\n selectTextTrack(id: string | null) {\n this.controller?.selectTextTrack(id);\n }\n setPlaybackRate(rate: number) {\n this.controller?.setPlaybackRate(rate);\n }\n getSeekableStart() {\n return this.controller?.getSeekableStart() ?? 0;\n }\n getLiveEdge() {\n return this.controller?.getLiveEdge() ?? 0;\n }\n canSeekStream() {\n return this.controller?.canSeekStream() ?? false;\n }\n getBufferedRanges() {\n return this.controller?.getBufferedRanges() ?? null;\n }\n async getStats() {\n return this.controller?.getStats();\n }\n\n handleMouseEnter() {\n this.controller?.handleMouseEnter();\n }\n handleMouseLeave() {\n this.controller?.handleMouseLeave();\n }\n handleMouseMove() {\n this.controller?.handleMouseMove();\n }\n handleTouchStart() {\n this.controller?.handleTouchStart();\n }\n\n async setDevModeOptions(options: {\n forcePlayer?: string;\n forceType?: string;\n forceSource?: number;\n playbackMode?: \"auto\" | \"low-latency\" | \"quality\" | \"vod\";\n }) {\n await this.controller?.setDevModeOptions(options);\n }\n\n getController(): PlayerController | null {\n return this.controller;\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"player-controller-host.js","sources":["../../../../src/controllers/player-controller-host.ts"],"sourcesContent":["/**\n * PlayerControllerHost — Lit ReactiveController wrapping the headless PlayerController.\n * Direct port of usePlayerController.ts from player-react.\n */\nimport type { ReactiveController, ReactiveControllerHost } from \"lit\";\nimport {\n PlayerController,\n type PlayerControllerConfig,\n type PlayerState,\n type StreamState,\n type StreamInfo,\n type PlaybackQuality,\n type ContentEndpoints,\n type ContentMetadata,\n type ClassifiedError,\n} from \"@livepeer-frameworks/player-core\";\n\nexport interface PlayerControllerHostState {\n state: PlayerState;\n streamState: StreamState | null;\n endpoints: ContentEndpoints | null;\n metadata: ContentMetadata | null;\n videoElement: HTMLVideoElement | null;\n currentTime: number;\n duration: number;\n isPlaying: boolean;\n isPaused: boolean;\n isBuffering: boolean;\n isMuted: boolean;\n volume: number;\n error: string | null;\n errorDetails: ClassifiedError[\"details\"] | null;\n isPassiveError: boolean;\n hasPlaybackStarted: boolean;\n isHoldingSpeed: boolean;\n holdSpeed: number;\n isHovering: boolean;\n shouldShowControls: boolean;\n isLoopEnabled: boolean;\n isFullscreen: boolean;\n isPiPActive: boolean;\n isEffectivelyLive: boolean;\n shouldShowIdleScreen: boolean;\n currentPlayerInfo: { name: string; shortname: string } | null;\n currentSourceInfo: { url: string; type: string } | null;\n playbackQuality: PlaybackQuality | null;\n subtitlesEnabled: boolean;\n qualities: Array<{\n id: string;\n label: string;\n bitrate?: number;\n width?: number;\n height?: number;\n isAuto?: boolean;\n active?: boolean;\n }>;\n textTracks: Array<{ id: string; label: string; lang?: string; active: boolean }>;\n streamInfo: StreamInfo | null;\n toast: { message: string; timestamp: number } | null;\n}\n\nconst initialState: PlayerControllerHostState = {\n state: \"booting\",\n streamState: null,\n endpoints: null,\n metadata: null,\n videoElement: null,\n currentTime: 0,\n duration: NaN,\n isPlaying: false,\n isPaused: true,\n isBuffering: false,\n isMuted: true,\n volume: 1,\n error: null,\n errorDetails: null,\n isPassiveError: false,\n hasPlaybackStarted: false,\n isHoldingSpeed: false,\n holdSpeed: 2,\n isHovering: false,\n shouldShowControls: false,\n isLoopEnabled: false,\n isFullscreen: false,\n isPiPActive: false,\n isEffectivelyLive: false,\n shouldShowIdleScreen: true,\n currentPlayerInfo: null,\n currentSourceInfo: null,\n playbackQuality: null,\n subtitlesEnabled: false,\n qualities: [],\n textTracks: [],\n streamInfo: null,\n toast: null,\n};\n\ntype HostElement = ReactiveControllerHost & HTMLElement;\n\nexport class PlayerControllerHost implements ReactiveController {\n host: HostElement;\n private controller: PlayerController | null = null;\n private unsubs: Array<() => void> = [];\n private currentConfig: PlayerControllerConfig | null = null;\n\n s: PlayerControllerHostState = { ...initialState };\n\n constructor(host: HostElement) {\n this.host = host;\n host.addController(this);\n }\n\n // ---- Configuration & Lifecycle ----\n\n configure(config: PlayerControllerConfig) {\n this.currentConfig = config;\n }\n\n async attach(container: HTMLDivElement) {\n if (!this.currentConfig) return;\n this.teardown();\n\n const controller = new PlayerController({\n contentId: this.currentConfig.contentId,\n contentType: this.currentConfig.contentType,\n endpoints: this.currentConfig.endpoints,\n gatewayUrl: this.currentConfig.gatewayUrl,\n mistUrl: this.currentConfig.mistUrl,\n authToken: this.currentConfig.authToken,\n autoplay: this.currentConfig.autoplay,\n muted: this.currentConfig.muted,\n controls: this.currentConfig.controls,\n poster: this.currentConfig.poster,\n debug: this.currentConfig.debug,\n });\n\n this.controller = controller;\n this.subscribeToEvents(controller);\n\n this.update({ isLoopEnabled: controller.isLoopEnabled() });\n\n try {\n await controller.attach(container);\n } catch (err) {\n console.warn(\"[PlayerControllerHost] Attach failed:\", err);\n }\n }\n\n hostConnected() {\n // Controller attachment happens in firstUpdated of the host element\n }\n\n hostDisconnected() {\n this.teardown();\n this.s = { ...initialState };\n }\n\n private teardown() {\n this.unsubs.forEach((fn) => fn());\n this.unsubs = [];\n this.controller?.destroy();\n this.controller = null;\n }\n\n // ---- State Updates ----\n\n private update(partial: Partial<PlayerControllerHostState>) {\n Object.assign(this.s, partial);\n this.host.requestUpdate();\n }\n\n private syncState() {\n if (!this.controller) return;\n const c = this.controller;\n this.update({\n isPlaying: c.isPlaying(),\n isPaused: c.isPaused(),\n isBuffering: c.isBuffering(),\n isMuted: c.isMuted(),\n volume: c.getVolume(),\n hasPlaybackStarted: c.hasPlaybackStarted(),\n shouldShowControls: c.shouldShowControls(),\n shouldShowIdleScreen: c.shouldShowIdleScreen(),\n playbackQuality: c.getPlaybackQuality(),\n isLoopEnabled: c.isLoopEnabled(),\n subtitlesEnabled: c.isSubtitlesEnabled(),\n qualities: c.getQualities(),\n textTracks: c.getTextTracks(),\n streamInfo: c.getStreamInfo(),\n });\n }\n\n // ---- Event Subscriptions (mirrors usePlayerController exactly) ----\n\n private subscribeToEvents(controller: PlayerController) {\n const u = this.unsubs;\n\n u.push(\n controller.on(\"stateChange\", ({ state }) => {\n this.update({ state });\n this.dispatchEvent(\"fw-state-change\", { state });\n })\n );\n\n u.push(\n controller.on(\"streamStateChange\", ({ state: streamState }) => {\n this.update({\n streamState,\n metadata: controller.getMetadata(),\n isEffectivelyLive: controller.isEffectivelyLive(),\n shouldShowIdleScreen: controller.shouldShowIdleScreen(),\n });\n this.dispatchEvent(\"fw-stream-state\", { state: streamState });\n })\n );\n\n u.push(\n controller.on(\"timeUpdate\", ({ currentTime, duration }) => {\n const next: Partial<PlayerControllerHostState> = {\n currentTime,\n duration,\n shouldShowControls: controller.shouldShowControls(),\n };\n if (this.s.qualities.length === 0) {\n const qualities = controller.getQualities();\n if (qualities.length > 0) {\n next.qualities = qualities;\n }\n }\n this.update(next);\n this.dispatchEvent(\"fw-time-update\", { currentTime, duration });\n })\n );\n\n u.push(\n controller.on(\"error\", ({ error }) => {\n this.update({\n error,\n isPassiveError: controller.isPassiveError(),\n });\n this.dispatchEvent(\"fw-error\", { error });\n })\n );\n\n u.push(\n controller.on(\"errorCleared\", () => {\n this.update({ error: null, isPassiveError: false });\n })\n );\n\n u.push(\n controller.on(\"ready\", ({ videoElement }) => {\n this.update({\n videoElement,\n endpoints: controller.getEndpoints(),\n metadata: controller.getMetadata(),\n streamInfo: controller.getStreamInfo(),\n isEffectivelyLive: controller.isEffectivelyLive(),\n shouldShowIdleScreen: controller.shouldShowIdleScreen(),\n currentPlayerInfo: controller.getCurrentPlayerInfo(),\n currentSourceInfo: controller.getCurrentSourceInfo(),\n qualities: controller.getQualities(),\n textTracks: controller.getTextTracks(),\n });\n this.dispatchEvent(\"fw-ready\", { videoElement });\n this.syncState();\n\n const handleVideoEvent = () => {\n if (this.controller?.shouldSuppressVideoEvents?.()) return;\n this.syncState();\n };\n videoElement.addEventListener(\"play\", handleVideoEvent);\n videoElement.addEventListener(\"pause\", handleVideoEvent);\n videoElement.addEventListener(\"waiting\", handleVideoEvent);\n videoElement.addEventListener(\"playing\", handleVideoEvent);\n u.push(() => {\n videoElement.removeEventListener(\"play\", handleVideoEvent);\n videoElement.removeEventListener(\"pause\", handleVideoEvent);\n videoElement.removeEventListener(\"waiting\", handleVideoEvent);\n videoElement.removeEventListener(\"playing\", handleVideoEvent);\n });\n })\n );\n\n u.push(\n controller.on(\"playerSelected\", ({ player: _player, source }) => {\n this.update({\n currentPlayerInfo: controller.getCurrentPlayerInfo(),\n currentSourceInfo: { url: source.url, type: source.type },\n qualities: controller.getQualities(),\n textTracks: controller.getTextTracks(),\n });\n this.syncState();\n })\n );\n\n u.push(\n controller.on(\"volumeChange\", ({ volume, muted }) => {\n this.update({ volume, isMuted: muted });\n this.dispatchEvent(\"fw-volume-change\", { volume, muted });\n })\n );\n\n u.push(\n controller.on(\"loopChange\", ({ isLoopEnabled }) => {\n this.update({ isLoopEnabled });\n })\n );\n\n u.push(\n controller.on(\"fullscreenChange\", ({ isFullscreen }) => {\n this.update({ isFullscreen });\n this.dispatchEvent(\"fw-fullscreen-change\", { isFullscreen });\n })\n );\n\n u.push(\n controller.on(\"pipChange\", ({ isPiP }) => {\n this.update({ isPiPActive: isPiP });\n this.dispatchEvent(\"fw-pip-change\", { isPiP });\n })\n );\n\n u.push(\n controller.on(\"holdSpeedStart\", ({ speed }) => {\n this.update({ isHoldingSpeed: true, holdSpeed: speed });\n })\n );\n\n u.push(\n controller.on(\"holdSpeedEnd\", () => {\n this.update({ isHoldingSpeed: false });\n })\n );\n\n u.push(\n controller.on(\"hoverStart\", () => {\n this.update({ isHovering: true, shouldShowControls: true });\n })\n );\n\n u.push(\n controller.on(\"hoverEnd\", () => {\n this.update({\n isHovering: false,\n shouldShowControls: controller.shouldShowControls(),\n });\n })\n );\n\n u.push(\n controller.on(\"captionsChange\", ({ enabled }) => {\n this.update({ subtitlesEnabled: enabled, textTracks: controller.getTextTracks() });\n })\n );\n\n u.push(\n controller.on(\"protocolSwapped\", (data) => {\n const message = `Switched to ${data.toProtocol}`;\n this.update({ toast: { message, timestamp: Date.now() } });\n this.dispatchEvent(\"fw-protocol-swapped\", data);\n })\n );\n\n u.push(\n controller.on(\"playbackFailed\", (data) => {\n this.update({\n error: data.message,\n errorDetails: data.details ?? null,\n isPassiveError: false,\n });\n this.dispatchEvent(\"fw-playback-failed\", {\n code: data.code,\n message: data.message,\n });\n })\n );\n }\n\n // ---- Event Dispatching ----\n\n private dispatchEvent(name: string, detail: unknown) {\n this.host.dispatchEvent(new CustomEvent(name, { detail, bubbles: true, composed: true }));\n }\n\n // ---- Action Methods ----\n\n async play() {\n await this.controller?.play();\n }\n pause() {\n this.controller?.pause();\n }\n togglePlay() {\n this.controller?.togglePlay();\n }\n seek(time: number) {\n this.controller?.seek(time);\n }\n seekBy(delta: number) {\n this.controller?.seekBy(delta);\n }\n jumpToLive() {\n this.controller?.jumpToLive();\n }\n setVolume(volume: number) {\n this.controller?.setVolume(volume);\n }\n toggleMute() {\n this.controller?.toggleMute();\n }\n toggleLoop() {\n this.controller?.toggleLoop();\n }\n async toggleFullscreen() {\n await this.controller?.toggleFullscreen();\n }\n async togglePiP() {\n await this.controller?.togglePictureInPicture();\n }\n toggleSubtitles() {\n this.controller?.toggleSubtitles();\n }\n\n clearError() {\n this.controller?.clearError();\n this.update({ error: null, errorDetails: null, isPassiveError: false });\n }\n\n dismissToast() {\n this.update({ toast: null });\n }\n\n async retry() {\n await this.controller?.retry();\n }\n async reload() {\n await this.controller?.reload();\n }\n\n getQualities() {\n return this.controller?.getQualities() ?? [];\n }\n selectQuality(id: string) {\n this.controller?.selectQuality(id);\n }\n getTextTracks() {\n return this.controller?.getTextTracks() ?? [];\n }\n selectTextTrack(id: string | null) {\n this.controller?.selectTextTrack(id);\n }\n setPlaybackRate(rate: number) {\n this.controller?.setPlaybackRate(rate);\n }\n getSeekableStart() {\n return this.controller?.getSeekableStart() ?? 0;\n }\n getLiveEdge() {\n return this.controller?.getLiveEdge() ?? 0;\n }\n canSeekStream() {\n return this.controller?.canSeekStream() ?? false;\n }\n getBufferedRanges() {\n return this.controller?.getBufferedRanges() ?? null;\n }\n async getStats() {\n return this.controller?.getStats();\n }\n\n handleMouseEnter() {\n this.controller?.handleMouseEnter();\n this.update({ isHovering: true, shouldShowControls: true });\n }\n handleMouseLeave() {\n this.controller?.handleMouseLeave();\n this.update({\n isHovering: false,\n shouldShowControls: this.controller?.shouldShowControls() ?? false,\n });\n }\n handleMouseMove() {\n this.controller?.handleMouseMove();\n if (this.controller) {\n this.update({ shouldShowControls: this.controller.shouldShowControls() });\n }\n }\n handleTouchStart() {\n this.controller?.handleTouchStart();\n this.update({ shouldShowControls: true });\n }\n\n async setDevModeOptions(options: {\n forcePlayer?: string;\n forceType?: string;\n forceSource?: number;\n playbackMode?: \"auto\" | \"low-latency\" | \"quality\" | \"vod\";\n }) {\n await this.controller?.setDevModeOptions(options);\n }\n\n getController(): PlayerController | null {\n return this.controller;\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -442,11 +442,20 @@ const sharedStyles = css `
|
|
|
442
442
|
.fw-context-menu-item {
|
|
443
443
|
position: relative;
|
|
444
444
|
display: flex;
|
|
445
|
+
width: 100%;
|
|
446
|
+
justify-content: flex-start;
|
|
447
|
+
gap: 0.5rem;
|
|
445
448
|
cursor: pointer;
|
|
446
449
|
user-select: none;
|
|
447
450
|
align-items: center;
|
|
448
451
|
padding: 0.5rem 0.75rem;
|
|
449
452
|
font-size: 0.875rem;
|
|
453
|
+
text-align: left;
|
|
454
|
+
font: inherit;
|
|
455
|
+
border: none;
|
|
456
|
+
background: transparent;
|
|
457
|
+
appearance: none;
|
|
458
|
+
-webkit-appearance: none;
|
|
450
459
|
outline: none;
|
|
451
460
|
color: hsl(var(--tn-fg));
|
|
452
461
|
transition:
|
|
@@ -485,11 +494,20 @@ const sharedStyles = css `
|
|
|
485
494
|
.fw-context-menu-checkbox {
|
|
486
495
|
position: relative;
|
|
487
496
|
display: flex;
|
|
497
|
+
width: 100%;
|
|
498
|
+
justify-content: flex-start;
|
|
499
|
+
gap: 0.5rem;
|
|
488
500
|
cursor: pointer;
|
|
489
501
|
user-select: none;
|
|
490
502
|
align-items: center;
|
|
491
503
|
padding: 0.5rem 0.5rem 0.5rem 2rem;
|
|
492
504
|
font-size: 0.875rem;
|
|
505
|
+
text-align: left;
|
|
506
|
+
font: inherit;
|
|
507
|
+
border: none;
|
|
508
|
+
background: transparent;
|
|
509
|
+
appearance: none;
|
|
510
|
+
-webkit-appearance: none;
|
|
493
511
|
outline: none;
|
|
494
512
|
color: hsl(var(--tn-fg));
|
|
495
513
|
transition:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared-styles.js","sources":["../../../../src/styles/shared-styles.ts"],"sourcesContent":["// AUTO-GENERATED — do not edit. Run `pnpm run build:css` to regenerate.\n// Source: packages/core/src/styles/player.css\nimport { css } from \"lit\";\n\nexport const sharedStyles = css`\n/*\n * FrameWorks Player CSS\n * Plain CSS - no build step required (just copy to dist).\n * CSS variables are OUTSIDE layer for guaranteed availability.\n * Component styles are in @layer fw-player for cascade isolation.\n */\n\n/* =====================================================\n CSS VARIABLES - OUTSIDE LAYER (always available)\n ===================================================== */\n\n/*\n * Player-scoped CSS variables - on .fw-player-surface to avoid :root pollution.\n * All player components must be wrapped in .fw-player-surface to inherit these.\n * These are OUTSIDE the layer so they're always available regardless of cascade.\n */\n.fw-player-surface {\n /* Tokyo Night color palette */\n --tn-bg-dark: 235 21% 11%; /* #1a1b26 - Darkest (slab backgrounds) */\n --tn-bg: 233 23% 17%; /* #24283b - Main background */\n --tn-bg-highlight: 233 23% 21%; /* #292e42 - Elevated surfaces */\n --tn-bg-visual: 232 27% 25%; /* #33395e - Selection/active states */\n\n /* Text hierarchy */\n --tn-fg: 223 27% 76%; /* #a9b1d6 - Primary text */\n --tn-fg-bright: 220 13% 91%; /* #e2e4ea - Bright/highlighted text */\n --tn-fg-dark: 224 16% 53%; /* #787c99 - Secondary text (muted) */\n --tn-fg-gutter: 228 15% 45%; /* #5a607f - Borders, seams */\n\n /* Accent colors (semantic) */\n --tn-blue: 218 79% 73%; /* #7aa2f7 - Primary actions */\n --tn-green: 95 53% 55%; /* #9ece6a - Success */\n --tn-red: 348 74% 64%; /* #f7768e - Destructive, live */\n --tn-yellow: 35 79% 64%; /* #e0af68 - Warnings */\n --tn-purple: 267 82% 77%; /* #bb9af7 - Secondary accent */\n --tn-cyan: 178 64% 63%; /* #7dcfff - Info */\n --tn-teal: 162 66% 62%; /* #73daca - Terminal green */\n\n /* Player-internal variables (not shared with host) */\n --fw-background: var(--tn-bg);\n --fw-foreground: var(--tn-fg);\n --fw-card: var(--tn-bg-highlight);\n --fw-card-foreground: var(--tn-fg);\n --fw-popover: var(--tn-bg-highlight);\n --fw-popover-foreground: var(--tn-fg);\n --fw-primary: var(--tn-blue);\n --fw-primary-foreground: var(--tn-bg-dark);\n --fw-secondary: var(--tn-bg-visual);\n --fw-secondary-foreground: var(--tn-fg);\n --fw-muted: var(--tn-bg-highlight);\n --fw-muted-foreground: var(--tn-fg-dark);\n --fw-accent: var(--tn-bg-visual);\n --fw-accent-foreground: var(--tn-fg);\n --fw-destructive: var(--tn-red);\n --fw-destructive-foreground: var(--tn-bg-dark);\n --fw-border: var(--tn-fg-gutter);\n --fw-input: var(--tn-bg-highlight);\n --fw-ring: var(--tn-blue);\n --fw-radius: 0;\n\n /* Controls-specific variables */\n --fw-controls-bg: hsl(var(--tn-bg-dark) / 0.85);\n --fw-controls-fg: hsl(var(--tn-fg));\n --fw-seam: hsl(var(--tn-fg-gutter) / 0.3);\n color: var(--fw-controls-fg);\n}\n\n/* Declare layer upfront for lowest priority */\n@layer fw-player;\n\n/* Component styles in the fw-player layer */\n@layer fw-player {\n /* =====================================================\n ANIMATIONS\n ===================================================== */\n @keyframes float {\n 0%,\n 100% {\n transform: translateY(0px) scale(1);\n }\n 50% {\n transform: translateY(-12px) scale(0.95);\n }\n }\n\n @keyframes spin-slow {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n }\n\n @keyframes spin {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n }\n\n .animate-spin-slow {\n animation: spin-slow 18s linear infinite;\n }\n\n .animate-spin {\n animation: spin 1s linear infinite;\n }\n\n /* =====================================================\n SLAB SYSTEM - Player Controls & Overlays\n ===================================================== */\n\n .fw-player-surface button {\n font: inherit;\n }\n\n /* Slab base - solid structural block */\n .fw-slab {\n background: var(--fw-controls-bg);\n border: 1px solid var(--fw-seam);\n }\n\n /* Slab header - uppercase, padded */\n .fw-slab-header {\n padding: 0.5rem 1rem;\n border-bottom: 1px solid var(--fw-seam);\n font-size: 0.75rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: hsl(var(--tn-fg-dark));\n }\n\n /* Slab body - padded content area */\n .fw-slab-body {\n padding: 1rem;\n }\n\n /* Slab actions - flush buttons, seams between */\n .fw-slab-actions {\n display: flex;\n border-top: 1px solid var(--fw-seam);\n }\n\n .fw-slab-actions > * {\n flex: 1;\n border-radius: 0 !important;\n }\n\n .fw-slab-actions > * + * {\n border-left: 1px solid var(--fw-seam);\n }\n\n /* Control bar - stacked layout (seekbar above controls row) */\n .fw-control-bar {\n display: flex;\n flex-direction: column;\n width: 100%;\n background: var(--fw-controls-bg);\n border-top: 1px solid var(--fw-seam);\n backdrop-filter: blur(8px);\n pointer-events: auto;\n }\n\n /* Control group - seamed sections within bar */\n .fw-control-group {\n display: flex;\n align-items: center;\n padding: 0.5rem;\n }\n\n .fw-control-group + .fw-control-group {\n border-left: 1px solid var(--fw-seam);\n }\n\n /* Flush button - fills space, no radius */\n .fw-btn-flush {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 0.5rem 0.75rem;\n border-radius: 0;\n background: transparent;\n color: hsl(var(--tn-fg));\n transition:\n background-color 0.15s,\n color 0.15s;\n cursor: pointer;\n border: none;\n }\n\n .fw-btn-flush:hover {\n background: hsl(var(--tn-bg-visual) / 0.5);\n }\n\n .fw-btn-flush:active {\n background: hsl(var(--tn-bg-visual));\n }\n\n .fw-btn-flush--active {\n color: hsl(var(--tn-blue));\n }\n\n /* Status indicators */\n .fw-status-online {\n color: hsl(var(--tn-green));\n }\n .fw-status-offline {\n color: hsl(var(--tn-red));\n }\n .fw-status-warning {\n color: hsl(var(--tn-yellow));\n }\n .fw-status-info {\n color: hsl(var(--tn-cyan));\n }\n\n /* =====================================================\n PLAYER CONTAINER STYLES (Slab-based)\n ===================================================== */\n\n .fw-player-root {\n position: relative;\n height: 100%;\n width: 100%;\n overflow: hidden;\n border-radius: 0; /* Slabs don't have rounded corners */\n background-color: hsl(var(--tn-bg-dark));\n border: 1px solid hsl(var(--tn-fg-gutter) / 0.3);\n }\n\n .fw-player-container {\n height: 100%;\n width: 100%;\n overflow: hidden;\n border-radius: 0;\n background-color: hsl(var(--tn-bg-dark));\n }\n\n .fw-player-video {\n height: 100%;\n width: 100%;\n border-radius: 0;\n object-fit: contain;\n background-color: hsl(var(--tn-bg-dark));\n }\n\n .fw-player-embed {\n height: 100%;\n width: 100%;\n min-height: 300px;\n border-radius: 0;\n border-width: 0;\n background-color: hsl(var(--tn-bg-dark));\n }\n\n .fw-player-dvd {\n position: absolute;\n pointer-events: none;\n user-select: none;\n -webkit-user-select: none;\n }\n\n /* =====================================================\n VIDEO.JS CONTAINER CONSTRAINTS\n ===================================================== */\n\n /* VideoJS wrapper must respect container bounds */\n .fw-player-container .video-js {\n width: 100% !important;\n height: 100% !important;\n max-width: 100% !important;\n max-height: 100% !important;\n min-width: 0 !important;\n min-height: 0 !important;\n padding: 0 !important;\n background: black;\n }\n\n /* Ensure video tech fills the wrapper with letterboxing */\n .fw-player-container .video-js .vjs-tech {\n width: 100% !important;\n height: 100% !important;\n object-fit: contain !important;\n }\n\n /* =====================================================\n VIDEO.JS UI HIDING (when using custom controls)\n ===================================================== */\n\n /* Hide all VideoJS chrome when using our custom controls */\n .vjs-fw-custom-controls .vjs-control-bar,\n .vjs-fw-custom-controls .vjs-big-play-button,\n .vjs-fw-custom-controls .vjs-loading-spinner,\n .vjs-fw-custom-controls .vjs-text-track-display,\n .vjs-fw-custom-controls .vjs-error-display,\n .vjs-fw-custom-controls .vjs-modal-dialog,\n .vjs-fw-custom-controls .vjs-poster,\n .vjs-fw-custom-controls .vjs-live-control,\n .vjs-fw-custom-controls .vjs-title-bar {\n display: none !important;\n }\n\n /* Ensure video element fills container (absolute positioning) */\n .vjs-fw-custom-controls .vjs-tech {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n object-fit: contain;\n }\n\n /* =====================================================\n SEMANTIC COMPONENT CLASSES (npm_studio pattern)\n All component styling - no Tailwind utilities needed\n ===================================================== */\n\n /* --- Settings Menu --- */\n .fw-settings-menu {\n position: absolute;\n bottom: 3rem;\n right: 0;\n width: 12rem;\n max-height: 70vh;\n overflow-y: auto;\n background: hsl(var(--tn-bg-dark));\n border: 1px solid hsl(var(--tn-fg-gutter) / 0.3);\n box-shadow:\n 0 10px 15px -3px rgb(0 0 0 / 0.1),\n 0 4px 6px -4px rgb(0 0 0 / 0.1);\n border-radius: 0.25rem;\n z-index: 50;\n }\n\n .fw-settings-section {\n padding: 0.5rem;\n border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.3);\n }\n\n .fw-settings-section:last-child {\n border-bottom: none;\n }\n\n .fw-settings-label {\n font-size: 10px;\n color: hsl(var(--tn-fg-dark));\n text-transform: uppercase;\n font-weight: 600;\n margin-bottom: 0.25rem;\n padding-left: 0.25rem;\n }\n\n .fw-settings-options {\n display: flex;\n gap: 0.25rem;\n }\n\n .fw-settings-options--wrap {\n flex-wrap: wrap;\n }\n\n .fw-settings-btn {\n flex: 1;\n padding: 0.375rem 0.25rem;\n font-size: 10px;\n border-radius: 0.25rem;\n background: hsl(var(--tn-bg));\n color: hsl(var(--tn-fg));\n border: none;\n cursor: pointer;\n transition:\n background-color 0.15s,\n color 0.15s;\n }\n\n .fw-settings-btn:hover {\n background: hsl(var(--tn-bg-highlight));\n }\n\n .fw-settings-btn--active {\n background: hsl(var(--tn-blue));\n color: hsl(var(--tn-bg-dark));\n }\n\n .fw-settings-btn--active:hover {\n background: hsl(var(--tn-blue));\n }\n\n .fw-settings-list {\n display: flex;\n flex-direction: column;\n gap: 0.125rem;\n max-height: 8rem;\n overflow-y: auto;\n }\n\n .fw-settings-list-item {\n padding: 0.25rem 0.5rem;\n font-size: 0.75rem;\n text-align: left;\n border-radius: 0.25rem;\n background: transparent;\n color: hsl(var(--tn-fg));\n border: none;\n cursor: pointer;\n transition: background-color 0.15s;\n }\n\n .fw-settings-list-item:hover {\n background: hsl(var(--tn-bg-highlight));\n }\n\n .fw-settings-list-item--active {\n background: hsl(var(--tn-blue) / 0.2);\n color: hsl(var(--tn-blue));\n }\n\n /* --- Context Menu (bits-ui wrapper) --- */\n .fw-context-menu {\n z-index: 50;\n min-width: 8rem;\n overflow: hidden;\n border-radius: 0.25rem;\n border: 1px solid hsl(var(--tn-fg-gutter) / 0.3);\n background: hsl(var(--tn-bg-dark));\n padding: 0;\n color: hsl(var(--tn-fg));\n box-shadow:\n 0 10px 15px -3px rgb(0 0 0 / 0.1),\n 0 4px 6px -4px rgb(0 0 0 / 0.1);\n }\n\n .fw-context-menu-item {\n position: relative;\n display: flex;\n cursor: pointer;\n user-select: none;\n align-items: center;\n padding: 0.5rem 0.75rem;\n font-size: 0.875rem;\n outline: none;\n color: hsl(var(--tn-fg));\n transition:\n background-color 0.15s,\n color 0.15s;\n }\n\n .fw-context-menu-item:hover,\n .fw-context-menu-item:focus {\n background: hsl(var(--tn-bg-visual));\n color: white;\n }\n\n .fw-context-menu-item[data-disabled] {\n pointer-events: none;\n opacity: 0.5;\n }\n\n .fw-context-menu-item--inset {\n padding-left: 2rem;\n }\n\n .fw-context-menu-separator {\n margin: 0.25rem -0.25rem;\n height: 1px;\n background: hsl(var(--tn-fg-gutter) / 0.3);\n }\n\n .fw-context-menu-label {\n padding: 0.375rem 0.5rem;\n font-size: 0.75rem;\n font-weight: 600;\n color: hsl(var(--tn-fg-dark));\n }\n\n .fw-context-menu-checkbox {\n position: relative;\n display: flex;\n cursor: pointer;\n user-select: none;\n align-items: center;\n padding: 0.5rem 0.5rem 0.5rem 2rem;\n font-size: 0.875rem;\n outline: none;\n color: hsl(var(--tn-fg));\n transition:\n background-color 0.15s,\n color 0.15s;\n }\n\n .fw-context-menu-checkbox:hover,\n .fw-context-menu-checkbox:focus {\n background: hsl(var(--tn-bg-visual));\n color: white;\n }\n\n .fw-context-menu-indicator {\n position: absolute;\n left: 0.5rem;\n display: flex;\n height: 0.875rem;\n width: 0.875rem;\n align-items: center;\n justify-content: center;\n }\n\n /* --- Live Badge --- */\n .fw-live-badge {\n display: flex;\n align-items: center;\n gap: 0.25rem;\n padding: 0.125rem 0.5rem;\n font-size: 10px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n transition:\n background-color 0.15s,\n color 0.15s;\n border: none;\n cursor: pointer;\n }\n\n .fw-live-badge--active {\n background: rgb(220 38 38);\n color: white;\n cursor: default;\n }\n\n .fw-live-badge--behind {\n background: #414868;\n color: #a9b1d6;\n }\n\n .fw-live-badge--behind:hover {\n background: rgb(220 38 38);\n color: white;\n }\n\n /* --- Volume Control --- */\n .fw-volume-group {\n display: flex;\n align-items: center;\n border-radius: 0;\n transition:\n background-color 0.2s,\n width 0.2s;\n cursor: pointer;\n }\n\n .fw-volume-group--expanded {\n background: hsl(var(--tn-bg-visual) / 0.5);\n }\n\n .fw-volume-group:hover {\n background: hsl(var(--tn-bg-visual) / 0.5);\n }\n\n .fw-volume-group--expanded:hover {\n background: hsl(var(--tn-bg-visual) / 0.5);\n }\n\n .fw-volume-group--disabled {\n opacity: 0.4;\n cursor: not-allowed;\n }\n\n .fw-volume-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 2rem;\n height: 2rem;\n color: rgb(255 255 255 / 0.8);\n background: transparent;\n border: none;\n cursor: pointer;\n transition: color 0.15s;\n }\n\n .fw-volume-btn:hover {\n color: white;\n }\n\n .fw-volume-slider-wrapper {\n display: flex;\n align-items: center;\n overflow: hidden;\n transition:\n width 0.2s ease-out,\n opacity 0.2s ease-out;\n }\n\n .fw-volume-slider-wrapper--collapsed {\n width: 0;\n opacity: 0;\n }\n\n .fw-volume-slider-wrapper--expanded {\n width: 7rem;\n padding-right: 0.5rem;\n opacity: 1;\n }\n\n /* --- Slider Component (shared by React/Svelte) --- */\n .fw-slider {\n position: relative;\n display: flex;\n width: 100%;\n height: 1.25rem;\n touch-action: none;\n user-select: none;\n align-items: center;\n cursor: pointer;\n }\n\n .fw-slider--vertical {\n flex-direction: column;\n width: 1.25rem;\n height: 100%;\n }\n\n .fw-slider-track {\n position: absolute;\n left: 0;\n right: 0;\n height: 4px;\n border-radius: 9999px;\n background: hsl(var(--tn-fg-gutter) / 0.4);\n overflow: hidden;\n transition: height 0.15s ease;\n }\n\n .fw-slider:hover .fw-slider-track {\n height: 6px;\n }\n\n .fw-slider--vertical .fw-slider-track {\n top: 0;\n bottom: 0;\n left: 50%;\n right: auto;\n width: 4px;\n height: auto;\n transform: translateX(-50%);\n }\n\n .fw-slider--vertical:hover .fw-slider-track {\n width: 6px;\n height: auto;\n }\n\n .fw-slider-range {\n position: absolute;\n height: 100%;\n border-radius: 9999px;\n background: hsl(var(--tn-fg));\n }\n\n .fw-slider-range--accent {\n background: hsl(var(--tn-cyan));\n }\n\n .fw-slider--vertical .fw-slider-range {\n width: 100%;\n height: auto;\n bottom: 0;\n }\n\n .fw-slider-thumb {\n display: block;\n width: 10px;\n height: 10px;\n border-radius: 50%;\n background: hsl(var(--tn-fg));\n border: none;\n box-shadow: 0 2px 4px rgb(0 0 0 / 0.3);\n cursor: pointer;\n transition:\n width 0.15s ease,\n height 0.15s ease,\n box-shadow 0.15s ease;\n }\n\n .fw-slider:hover .fw-slider-thumb {\n width: 14px;\n height: 14px;\n }\n\n .fw-slider-thumb:focus {\n outline: none;\n }\n\n .fw-slider-thumb:focus-visible {\n box-shadow: 0 0 0 2px hsl(var(--tn-fg) / 0.5);\n }\n\n .fw-slider-thumb--accent {\n background: hsl(var(--tn-cyan));\n }\n\n .fw-slider-thumb[data-disabled] {\n pointer-events: none;\n opacity: 0.5;\n }\n\n /* --- Time Display --- */\n .fw-time-display {\n font-family: ui-monospace, monospace;\n font-size: 11px;\n line-height: 1;\n color: hsl(var(--tn-fg-dark));\n white-space: nowrap;\n padding: 0 0.5rem;\n }\n\n /* --- Controls Wrapper --- */\n .fw-controls-wrapper {\n position: absolute;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n flex-direction: column;\n justify-content: flex-end;\n transition: opacity 0.2s;\n pointer-events: none;\n }\n\n .fw-controls-wrapper--visible {\n opacity: 1;\n }\n\n .fw-controls-wrapper--hidden {\n opacity: 0;\n }\n\n .fw-controls-row {\n width: 100%;\n display: flex;\n align-items: center;\n justify-content: space-between;\n }\n\n .fw-controls-left {\n display: flex;\n align-items: center;\n min-width: 0;\n flex-shrink: 1;\n }\n\n .fw-controls-right {\n display: flex;\n align-items: center;\n }\n\n .fw-seek-wrapper {\n width: 100%;\n padding: 0 0.5rem;\n margin-bottom: -0.25rem;\n }\n\n /* --- SeekBar Component --- */\n .fw-seek-track {\n position: absolute;\n left: 0;\n right: 0;\n height: 4px;\n border-radius: 9999px;\n background: hsl(var(--tn-fg-gutter) / 0.4);\n transition: height 0.15s ease;\n }\n\n .fw-seek-wrapper:hover .fw-seek-track,\n .fw-seek-track--active {\n height: 6px;\n }\n\n .fw-seek-buffered {\n position: absolute;\n height: 100%;\n border-radius: 9999px;\n background: hsl(var(--tn-fg) / 0.3);\n transition: all 0.2s ease;\n }\n\n .fw-seek-progress {\n position: absolute;\n height: 100%;\n border-radius: 9999px;\n background: hsl(var(--tn-blue));\n transition: width 0.1s linear;\n }\n\n .fw-seek-thumb {\n position: absolute;\n top: 50%;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n background: hsl(var(--tn-blue));\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);\n transform: translate(-50%, -50%);\n transition:\n opacity 0.15s ease,\n transform 0.15s ease;\n opacity: 0;\n pointer-events: none;\n }\n\n .fw-seek-wrapper:hover .fw-seek-thumb,\n .fw-seek-thumb--active {\n opacity: 1;\n transform: translate(-50%, -50%) scale(1);\n }\n\n .fw-seek-thumb--hidden {\n opacity: 0;\n transform: translate(-50%, -50%) scale(0.75);\n }\n\n .fw-seek-tooltip {\n position: absolute;\n bottom: 100%;\n margin-bottom: 8px;\n padding: 4px 8px;\n font-family: ui-monospace, monospace;\n font-size: 12px;\n background: hsl(var(--tn-bg-dark) / 0.95);\n color: hsl(var(--tn-fg));\n border-radius: 4px;\n white-space: nowrap;\n pointer-events: none;\n transform: translateX(-50%);\n }\n\n /* --- Stats Panel --- */\n .fw-stats-panel {\n position: absolute;\n top: 0.5rem;\n right: 0.5rem;\n width: 18rem;\n max-height: 80%;\n overflow-y: auto;\n background: hsl(var(--tn-bg-dark) / 0.85);\n backdrop-filter: blur(4px);\n border: 1px solid hsl(var(--tn-fg-gutter) / 0.3);\n font-family: ui-monospace, monospace;\n font-size: 0.75rem;\n z-index: 30;\n }\n\n .fw-stats-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0.5rem;\n border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.3);\n font-size: 10px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: hsl(var(--tn-fg-dark));\n }\n\n .fw-stats-section {\n padding: 0.5rem;\n border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.3);\n }\n\n .fw-stats-section:last-child {\n border-bottom: none;\n }\n\n .fw-stats-row {\n display: flex;\n justify-content: space-between;\n padding: 0.125rem 0;\n color: hsl(var(--tn-fg-dark));\n }\n\n .fw-stats-value {\n color: hsl(var(--tn-fg));\n }\n\n /* --- Loading Screen --- */\n .fw-loading-screen {\n position: absolute;\n inset: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n background: hsl(var(--tn-bg-dark));\n z-index: 20;\n }\n\n .fw-loading-spinner {\n width: 2rem;\n height: 2rem;\n border: 3px solid hsl(var(--tn-fg-gutter) / 0.3);\n border-top-color: hsl(var(--tn-blue));\n border-radius: 50%;\n animation: spin 1s linear infinite;\n }\n\n .fw-loading-text {\n margin-top: 1rem;\n font-size: 0.875rem;\n color: hsl(var(--tn-fg-dark));\n }\n\n /* --- Idle Screen --- */\n .fw-idle-screen {\n position: absolute;\n inset: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n background: hsl(var(--tn-bg-dark));\n z-index: 20;\n }\n\n .fw-idle-message {\n font-size: 0.875rem;\n color: hsl(var(--tn-fg-dark));\n }\n\n /* --- Stream State Overlay --- */\n .fw-stream-state-overlay {\n position: absolute;\n inset: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n background: hsl(var(--tn-bg-dark) / 0.9);\n gap: 1rem;\n z-index: 20;\n }\n\n .fw-stream-state-icon {\n color: hsl(var(--tn-fg-dark));\n }\n\n .fw-stream-state-text {\n font-size: 0.875rem;\n color: hsl(var(--tn-fg-dark));\n }\n\n /* --- Title Overlay --- */\n .fw-title-overlay {\n padding: 1rem;\n background: linear-gradient(to bottom, hsl(var(--tn-bg-dark) / 0.8), transparent);\n }\n\n /* --- Thumbnail Overlay --- */\n .fw-player-thumbnail {\n position: relative;\n display: flex;\n height: 100%;\n min-height: 280px;\n width: 100%;\n cursor: pointer;\n align-items: center;\n justify-content: center;\n overflow: hidden;\n background: hsl(var(--tn-bg-dark));\n }\n\n /* --- Speed Indicator --- */\n .fw-speed-indicator {\n pointer-events: none;\n background: none;\n backdrop-filter: none;\n }\n\n /* --- Skip Indicator --- */\n .fw-skip-indicator {\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n /* --- Error Display --- */\n .fw-player-error {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 1rem;\n padding: 1.5rem;\n min-height: 280px;\n background: hsl(var(--tn-bg-dark));\n color: hsl(var(--tn-fg));\n text-align: center;\n }\n\n /* --- Error Popup Overlay --- */\n .fw-error-overlay {\n position: absolute;\n z-index: 20;\n }\n\n .fw-error-overlay--fullscreen {\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: hsl(0 0% 0% / 0.6);\n backdrop-filter: blur(4px);\n }\n\n .fw-error-overlay--passive {\n top: 0.75rem;\n left: 0.75rem;\n right: 0.75rem;\n }\n\n .fw-error-popup {\n background: hsl(var(--tn-bg-dark));\n border: 1px solid hsl(var(--tn-fg-gutter) / 0.3);\n box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.3);\n overflow: hidden;\n }\n\n .fw-error-popup--fullscreen {\n min-width: 280px;\n max-width: 320px;\n }\n\n .fw-error-popup--passive {\n max-width: 28rem;\n }\n\n .fw-error-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0.5rem 0.75rem;\n border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.3);\n }\n\n .fw-error-header--error {\n background: hsl(0 70% 50% / 0.1);\n }\n\n .fw-error-header--warning {\n background: hsl(45 100% 50% / 0.1);\n }\n\n .fw-error-title {\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n }\n\n .fw-error-title--error {\n color: hsl(0 70% 60%);\n }\n\n .fw-error-title--warning {\n color: hsl(45 100% 50%);\n }\n\n .fw-error-close {\n width: 24px;\n height: 24px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 4px;\n background: transparent;\n border: none;\n color: hsl(var(--tn-fg-dark));\n cursor: pointer;\n transition:\n background-color 0.15s ease,\n color 0.15s ease;\n }\n\n .fw-error-close:hover {\n background: hsl(var(--tn-fg) / 0.1);\n color: hsl(var(--tn-fg));\n }\n\n .fw-error-body {\n padding: 0.75rem;\n }\n\n .fw-error-message {\n font-size: 14px;\n color: hsl(var(--tn-fg));\n }\n\n .fw-error-actions {\n display: flex;\n border-top: 1px solid hsl(var(--tn-fg-gutter) / 0.3);\n }\n\n .fw-error-btn {\n flex: 1;\n padding: 0.5rem 1rem;\n font-size: 12px;\n font-weight: 500;\n color: hsl(var(--tn-fg));\n background: transparent;\n border: none;\n cursor: pointer;\n transition:\n background-color 0.15s ease,\n color 0.15s ease;\n }\n\n .fw-error-btn:hover {\n background: hsl(var(--tn-fg) / 0.05);\n color: hsl(var(--tn-fg-bright));\n }\n\n /* --- Context Menu Animations (for bits-ui) --- */\n @keyframes fw-context-menu-in {\n from {\n opacity: 0;\n transform: scale(0.95);\n }\n to {\n opacity: 1;\n transform: scale(1);\n }\n }\n\n @keyframes fw-context-menu-out {\n from {\n opacity: 1;\n transform: scale(1);\n }\n to {\n opacity: 0;\n transform: scale(0.95);\n }\n }\n\n .fw-context-menu[data-state=\"open\"] {\n animation: fw-context-menu-in 150ms cubic-bezier(0.4, 0, 0.2, 1);\n }\n\n .fw-context-menu[data-state=\"closed\"] {\n animation: fw-context-menu-out 150ms cubic-bezier(0.4, 0, 0.2, 1);\n }\n\n /* Slide animations for different sides */\n .fw-context-menu[data-side=\"top\"] {\n transform-origin: bottom center;\n }\n\n .fw-context-menu[data-side=\"bottom\"] {\n transform-origin: top center;\n }\n\n .fw-context-menu[data-side=\"left\"] {\n transform-origin: right center;\n }\n\n .fw-context-menu[data-side=\"right\"] {\n transform-origin: left center;\n }\n\n /* =====================================================\n DEV MODE PANEL (Advanced settings sidebar)\n ===================================================== */\n\n /* Main panel - side panel container */\n .fw-dev-panel {\n z-index: 40;\n pointer-events: auto;\n background: hsl(var(--tn-bg-dark));\n border-left: 1px solid hsl(var(--tn-fg-gutter) / 0.5);\n color: hsl(var(--tn-fg));\n font-family: ui-monospace, monospace;\n font-size: 0.75rem;\n width: 280px;\n display: flex;\n flex-direction: column;\n height: 100%;\n flex-shrink: 0;\n }\n\n /* Header with tabs */\n .fw-dev-header {\n display: flex;\n align-items: center;\n border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.3);\n background: hsl(var(--tn-bg) / 0.5);\n }\n\n /* Tab buttons */\n .fw-dev-tab {\n padding: 0.5rem 0.75rem;\n font-size: 10px;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n font-weight: 600;\n background: transparent;\n border: none;\n border-right: 1px solid hsl(var(--tn-fg-gutter) / 0.3);\n color: hsl(var(--tn-fg-dark));\n cursor: pointer;\n transition:\n background-color 0.15s,\n color 0.15s;\n }\n\n .fw-dev-tab:hover {\n background: hsl(var(--tn-bg-dark) / 0.5);\n color: hsl(var(--tn-fg));\n }\n\n .fw-dev-tab--active {\n background: hsl(var(--tn-bg-dark));\n color: hsl(var(--tn-fg-bright));\n }\n\n .fw-dev-close {\n padding: 0.5rem;\n width: 2rem;\n height: 2rem;\n display: flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: none;\n color: hsl(var(--tn-fg-dark));\n cursor: pointer;\n transition: color 0.15s;\n margin-left: auto;\n }\n\n .fw-dev-close:hover {\n color: hsl(var(--tn-fg));\n }\n\n /* Spacer - pushes close button to right */\n .fw-dev-spacer {\n flex: 1;\n }\n\n /* Content area (body) */\n .fw-dev-content,\n .fw-dev-body {\n flex: 1;\n overflow-y: auto;\n }\n\n /* Section with label */\n .fw-dev-section {\n padding: 0.75rem;\n border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.3);\n }\n\n .fw-dev-section-label,\n .fw-dev-label {\n font-size: 10px;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n font-weight: 600;\n color: hsl(var(--tn-fg-dark));\n margin-bottom: 0.25rem;\n }\n\n .fw-dev-section-value,\n .fw-dev-value {\n font-size: 0.875rem;\n color: hsl(var(--tn-fg-bright));\n }\n\n .fw-dev-value-arrow {\n color: hsl(var(--tn-fg-dark));\n }\n\n .fw-dev-value-muted {\n font-size: 10px;\n color: hsl(var(--tn-fg-dark));\n margin-top: 0.25rem;\n }\n\n .fw-dev-section-sub {\n font-size: 10px;\n color: hsl(var(--tn-fg-dark));\n margin-top: 0.25rem;\n }\n\n /* Mode selector buttons */\n .fw-dev-mode-options,\n .fw-dev-mode-group {\n display: flex;\n gap: 0.25rem;\n margin-top: 0.5rem;\n }\n\n .fw-dev-mode-desc {\n font-size: 10px;\n color: hsl(var(--tn-fg-dark));\n margin-top: 0.5rem;\n font-style: italic;\n }\n\n .fw-dev-mode-btn {\n flex: 1;\n padding: 0.375rem 0.5rem;\n font-size: 10px;\n font-weight: 500;\n border-radius: 0.25rem;\n background: hsl(var(--tn-bg-highlight));\n border: none;\n color: hsl(var(--tn-fg-dark));\n cursor: pointer;\n transition:\n background-color 0.15s,\n color 0.15s;\n }\n\n .fw-dev-mode-btn:hover {\n color: hsl(var(--tn-fg));\n background: hsl(var(--tn-bg-visual));\n }\n\n .fw-dev-mode-btn--active {\n background: hsl(var(--tn-blue));\n color: hsl(var(--tn-bg-dark));\n }\n\n /* Action buttons row */\n .fw-dev-actions {\n display: flex;\n border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.3);\n }\n\n .fw-dev-action-btn {\n flex: 1;\n padding: 0.5rem 0.75rem;\n background: hsl(var(--tn-bg-highlight));\n border: none;\n border-right: 1px solid hsl(var(--tn-fg-gutter) / 0.3);\n color: hsl(var(--tn-fg));\n font-size: 0.75rem;\n cursor: pointer;\n transition:\n background-color 0.15s,\n color 0.15s;\n }\n\n .fw-dev-action-btn:last-child {\n border-right: none;\n }\n\n .fw-dev-action-btn:hover {\n background: hsl(var(--tn-bg-visual));\n color: hsl(var(--tn-fg-bright));\n }\n\n /* Combo list header */\n .fw-dev-list-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0.5rem 0.75rem;\n background: hsl(var(--tn-bg) / 0.5);\n }\n\n .fw-dev-list-title {\n font-size: 10px;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n font-weight: 600;\n color: hsl(var(--tn-fg-dark));\n }\n\n .fw-dev-list-toggle {\n font-size: 10px;\n color: hsl(var(--tn-fg-dark));\n background: transparent;\n border: none;\n cursor: pointer;\n display: flex;\n align-items: center;\n gap: 0.25rem;\n padding: 0.25rem;\n transition: color 0.15s;\n }\n\n .fw-dev-list-toggle:hover {\n color: hsl(var(--tn-fg));\n }\n\n /* Combo list items */\n .fw-dev-combo-list {\n border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.3);\n }\n\n .fw-dev-combo-item {\n width: 100%;\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.5rem 0.75rem;\n text-align: left;\n background: transparent;\n border: none;\n border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.2);\n color: hsl(var(--tn-fg));\n cursor: pointer;\n transition: background-color 0.15s;\n }\n\n .fw-dev-combo-item:last-child {\n border-bottom: none;\n }\n\n .fw-dev-combo-item:hover {\n background: hsl(var(--tn-bg) / 0.5);\n }\n\n .fw-dev-combo-item--active {\n background: hsl(var(--tn-bg-visual));\n color: hsl(var(--tn-fg-bright));\n box-shadow: inset 0 0 0 1px hsl(var(--tn-blue));\n }\n\n .fw-dev-combo-item--disabled {\n opacity: 0.4;\n }\n\n .fw-dev-combo-item--warning {\n background: hsl(var(--tn-yellow) / 0.05);\n }\n\n .fw-dev-combo-item--warning:hover {\n background: hsl(var(--tn-yellow) / 0.1);\n }\n\n .fw-dev-combo-rank {\n width: 1.25rem;\n height: 1.25rem;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 10px;\n font-weight: 700;\n border-radius: 0.125rem;\n background: hsl(var(--tn-fg-gutter) / 0.5);\n color: hsl(var(--tn-fg-dark));\n flex-shrink: 0;\n }\n\n .fw-dev-combo-rank--active {\n background: hsl(var(--tn-blue));\n color: hsl(var(--tn-bg-dark));\n }\n\n .fw-dev-combo-rank--warning {\n background: hsl(var(--tn-yellow) / 0.3);\n color: hsl(var(--tn-yellow));\n }\n\n .fw-dev-combo-name {\n flex: 1;\n font-size: 0.75rem;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .fw-dev-combo-arrow {\n color: hsl(var(--tn-fg-dark));\n }\n\n .fw-dev-combo-type {\n color: hsl(var(--tn-cyan));\n }\n\n .fw-dev-combo-type--warning {\n color: hsl(var(--tn-yellow));\n }\n\n .fw-dev-combo-score {\n font-size: 10px;\n font-family: ui-monospace, monospace;\n font-variant-numeric: tabular-nums;\n padding: 0.125rem 0.375rem;\n border-radius: 0.125rem;\n flex-shrink: 0;\n }\n\n .fw-dev-combo-score--high {\n background: hsl(var(--tn-green) / 0.2);\n color: hsl(var(--tn-green));\n }\n\n .fw-dev-combo-score--medium {\n background: hsl(var(--tn-blue) / 0.2);\n color: hsl(var(--tn-blue));\n }\n\n .fw-dev-combo-score--low {\n background: hsl(var(--tn-yellow) / 0.2);\n color: hsl(var(--tn-yellow));\n }\n\n /* Tooltip for combo details */\n .fw-dev-tooltip {\n position: absolute;\n left: 0;\n z-index: 50;\n background: hsl(var(--tn-bg-dark));\n border: 1px solid hsl(var(--tn-fg-gutter));\n box-shadow:\n 0 4px 6px -1px rgb(0 0 0 / 0.1),\n 0 2px 4px -2px rgb(0 0 0 / 0.1);\n padding: 0.5rem;\n font-size: 10px;\n white-space: nowrap;\n pointer-events: none;\n min-width: 220px;\n }\n\n .fw-dev-tooltip--above {\n bottom: 100%;\n margin-bottom: 0.25rem;\n }\n\n .fw-dev-tooltip--below {\n top: 100%;\n margin-top: 0.25rem;\n }\n\n .fw-dev-tooltip-header {\n margin-bottom: 0.5rem;\n padding-bottom: 0.5rem;\n border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.5);\n }\n\n .fw-dev-tooltip-title {\n font-weight: 700;\n color: hsl(var(--tn-fg-bright));\n }\n\n .fw-dev-tooltip-subtitle {\n color: hsl(var(--tn-cyan));\n }\n\n .fw-dev-tooltip-info {\n color: hsl(var(--tn-fg-dark));\n margin-top: 0.25rem;\n }\n\n .fw-dev-tooltip-score {\n font-weight: 700;\n color: hsl(var(--tn-fg-bright));\n margin-bottom: 0.25rem;\n }\n\n .fw-dev-tooltip-breakdown {\n color: hsl(var(--tn-fg-dark));\n }\n\n .fw-dev-tooltip-breakdown-value {\n color: hsl(var(--tn-fg));\n }\n\n .fw-dev-tooltip-breakdown-weight {\n color: hsl(var(--tn-fg-gutter));\n }\n\n .fw-dev-tooltip-error {\n color: hsl(var(--tn-red));\n }\n\n /* Stats panel content */\n .fw-dev-stats-hero {\n padding: 0.75rem;\n border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.3);\n }\n\n .fw-dev-stats-rate {\n font-size: 1.5rem;\n font-weight: 700;\n font-variant-numeric: tabular-nums;\n }\n\n .fw-dev-stats-rate--good {\n color: hsl(var(--tn-green));\n }\n\n .fw-dev-stats-rate--catching {\n color: hsl(var(--tn-blue));\n }\n\n .fw-dev-stats-rate--slow {\n color: hsl(var(--tn-yellow));\n }\n\n .fw-dev-stats-rate--stalling {\n color: hsl(var(--tn-red));\n }\n\n .fw-dev-stats-label {\n font-size: 10px;\n color: hsl(var(--tn-fg-dark));\n }\n\n .fw-dev-stats-metrics {\n display: flex;\n gap: 1rem;\n margin-top: 0.5rem;\n font-size: 10px;\n }\n\n .fw-dev-stats-metric--good {\n color: hsl(var(--tn-green));\n }\n\n .fw-dev-stats-metric--warning {\n color: hsl(var(--tn-yellow));\n }\n\n .fw-dev-stats-metric--bad {\n color: hsl(var(--tn-red));\n }\n\n /* Stats rows */\n .fw-dev-stats-rows {\n border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.3);\n }\n\n .fw-dev-stats-row {\n display: flex;\n justify-content: space-between;\n padding: 0.5rem 0.75rem;\n border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.2);\n }\n\n .fw-dev-stats-row:last-child {\n border-bottom: none;\n }\n\n .fw-dev-stats-key {\n color: hsl(var(--tn-fg-dark));\n }\n\n .fw-dev-stats-value {\n color: hsl(var(--tn-fg-bright));\n }\n\n .fw-dev-stats-value--cyan {\n color: hsl(var(--tn-cyan));\n }\n\n .fw-dev-stats-value--warning {\n color: hsl(var(--tn-yellow));\n }\n\n .fw-dev-stats-value--bad {\n color: hsl(var(--tn-red));\n }\n\n .fw-dev-stats-value--good {\n color: hsl(var(--tn-green));\n }\n\n /* Track badges */\n .fw-dev-track-badge {\n font-size: 10px;\n font-family: ui-monospace, monospace;\n text-transform: uppercase;\n padding: 0.125rem 0.375rem;\n }\n\n .fw-dev-track-badge--video {\n background: hsl(var(--tn-blue) / 0.2);\n color: hsl(var(--tn-blue));\n }\n\n .fw-dev-track-badge--audio {\n background: hsl(var(--tn-green) / 0.2);\n color: hsl(var(--tn-green));\n }\n\n .fw-dev-track-badge--other {\n background: hsl(var(--tn-yellow) / 0.2);\n color: hsl(var(--tn-yellow));\n }\n\n .fw-dev-track-info {\n display: flex;\n flex-wrap: wrap;\n gap: 0.5rem;\n font-size: 10px;\n color: hsl(var(--tn-fg-dark));\n }\n\n /* Empty state */\n .fw-dev-empty,\n .fw-dev-list-empty {\n padding: 1rem 0.75rem;\n text-align: center;\n color: hsl(var(--tn-fg-dark));\n }\n\n /* Chevron icon (expand/collapse toggle) */\n .fw-dev-chevron {\n transition: transform 0.15s;\n }\n\n .fw-dev-chevron--open {\n transform: rotate(180deg);\n }\n\n /* Combo wrapper (relative for tooltip positioning) */\n .fw-dev-combo {\n position: relative;\n }\n\n /* Combo button (alias for fw-dev-combo-item) */\n .fw-dev-combo-btn {\n width: 100%;\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.5rem 0.75rem;\n text-align: left;\n background: transparent;\n border: none;\n border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.2);\n color: hsl(var(--tn-fg));\n cursor: pointer;\n transition: background-color 0.15s;\n }\n\n .fw-dev-combo-btn:last-child {\n border-bottom: none;\n }\n\n .fw-dev-combo-btn:hover {\n background: hsl(var(--tn-bg) / 0.5);\n }\n\n .fw-dev-combo-btn--active {\n background: hsl(var(--tn-bg-visual));\n color: hsl(var(--tn-fg-bright));\n box-shadow: inset 0 0 0 1px hsl(var(--tn-blue));\n }\n\n .fw-dev-combo-btn--disabled {\n opacity: 0.4;\n }\n\n .fw-dev-combo-btn--codec-warn {\n background: hsl(var(--tn-yellow) / 0.05);\n }\n\n .fw-dev-combo-btn--codec-warn:hover {\n background: hsl(var(--tn-yellow) / 0.1);\n }\n\n /* Combo rank modifiers */\n .fw-dev-combo-rank--disabled {\n background: hsl(var(--tn-fg-gutter) / 0.3);\n color: hsl(var(--tn-fg-gutter));\n }\n\n .fw-dev-combo-rank--warn {\n background: hsl(var(--tn-yellow) / 0.3);\n color: hsl(var(--tn-yellow));\n }\n\n /* Combo type modifiers */\n .fw-dev-combo-type--disabled {\n color: hsl(var(--tn-fg-gutter));\n }\n\n .fw-dev-combo-type--warn {\n color: hsl(var(--tn-yellow));\n }\n\n /* Combo score modifiers (additional) */\n .fw-dev-combo-score--disabled {\n background: hsl(var(--tn-fg-gutter) / 0.2);\n color: hsl(var(--tn-fg-gutter));\n }\n\n .fw-dev-combo-score--mid {\n background: hsl(var(--tn-blue) / 0.2);\n color: hsl(var(--tn-blue));\n }\n\n /* Tooltip additional classes */\n .fw-dev-tooltip-tracks {\n color: hsl(var(--tn-fg-dark));\n margin-top: 0.25rem;\n }\n\n .fw-dev-tooltip-value {\n color: hsl(var(--tn-fg));\n }\n\n .fw-dev-tooltip-row {\n color: hsl(var(--tn-fg-dark));\n margin-bottom: 0.125rem;\n }\n\n .fw-dev-tooltip-bonus {\n color: hsl(var(--tn-green));\n }\n\n .fw-dev-tooltip-penalty {\n color: hsl(var(--tn-red));\n }\n\n .fw-dev-tooltip-weight {\n color: hsl(var(--tn-fg-gutter));\n font-size: 9px;\n }\n\n /* =====================================================\n DEV MODE STATS TAB\n ===================================================== */\n\n /* Section header modifier */\n .fw-dev-section-header {\n margin-top: 0.5rem;\n }\n\n /* Playback rate hero section */\n .fw-dev-rate {\n display: flex;\n align-items: baseline;\n gap: 0.5rem;\n margin-bottom: 0.5rem;\n }\n\n .fw-dev-rate-value {\n font-size: 1.5rem;\n font-weight: 700;\n font-variant-numeric: tabular-nums;\n }\n\n .fw-dev-rate-status {\n font-size: 10px;\n color: hsl(var(--tn-fg-dark));\n text-transform: uppercase;\n }\n\n .fw-dev-rate-stats {\n display: flex;\n gap: 1rem;\n font-size: 10px;\n color: hsl(var(--tn-fg-dark));\n }\n\n /* Stat value modifiers */\n .fw-dev-stat-value--good {\n color: hsl(var(--tn-green));\n }\n\n .fw-dev-stat-value--accent {\n color: hsl(var(--tn-blue));\n }\n\n .fw-dev-stat-value--warn {\n color: hsl(var(--tn-yellow));\n }\n\n .fw-dev-stat-value--bad {\n color: hsl(var(--tn-red));\n }\n\n /* Stats row (key-value pair) */\n .fw-dev-stat {\n display: flex;\n justify-content: space-between;\n padding: 0.5rem 0.75rem;\n border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.2);\n }\n\n .fw-dev-stat:last-child {\n border-bottom: none;\n }\n\n .fw-dev-stat-label {\n color: hsl(var(--tn-fg-dark));\n }\n\n .fw-dev-stat-value {\n color: hsl(var(--tn-fg-bright));\n }\n\n /* Track display */\n .fw-dev-track {\n padding: 0.5rem 0.75rem;\n border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.2);\n }\n\n .fw-dev-track:last-child {\n border-bottom: none;\n }\n\n .fw-dev-track-header {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n margin-bottom: 0.25rem;\n }\n\n .fw-dev-track-codec {\n font-size: 10px;\n color: hsl(var(--tn-fg));\n font-weight: 500;\n }\n\n .fw-dev-track-id {\n font-size: 10px;\n color: hsl(var(--tn-fg-gutter));\n margin-left: auto;\n }\n\n .fw-dev-track-meta {\n display: flex;\n flex-wrap: wrap;\n gap: 0.5rem;\n font-size: 10px;\n color: hsl(var(--tn-fg-dark));\n }\n\n /* No tracks state */\n .fw-dev-no-tracks {\n padding: 0.75rem;\n text-align: center;\n }\n\n .fw-dev-no-tracks-text {\n color: hsl(var(--tn-fg-dark));\n font-size: 10px;\n }\n\n .fw-dev-no-tracks-type {\n color: hsl(var(--tn-fg-gutter));\n margin-left: 0.25rem;\n }\n}\n\n`;\n"],"names":[],"mappings":";;AAAA;AACA;AAGO,MAAM,YAAY,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
1
|
+
{"version":3,"file":"shared-styles.js","sources":["../../../../src/styles/shared-styles.ts"],"sourcesContent":["// AUTO-GENERATED — do not edit. Run `pnpm run build:css` to regenerate.\n// Source: packages/core/src/styles/player.css\nimport { css } from \"lit\";\n\nexport const sharedStyles = css`\n/*\n * FrameWorks Player CSS\n * Plain CSS - no build step required (just copy to dist).\n * CSS variables are OUTSIDE layer for guaranteed availability.\n * Component styles are in @layer fw-player for cascade isolation.\n */\n\n/* =====================================================\n CSS VARIABLES - OUTSIDE LAYER (always available)\n ===================================================== */\n\n/*\n * Player-scoped CSS variables - on .fw-player-surface to avoid :root pollution.\n * All player components must be wrapped in .fw-player-surface to inherit these.\n * These are OUTSIDE the layer so they're always available regardless of cascade.\n */\n.fw-player-surface {\n /* Tokyo Night color palette */\n --tn-bg-dark: 235 21% 11%; /* #1a1b26 - Darkest (slab backgrounds) */\n --tn-bg: 233 23% 17%; /* #24283b - Main background */\n --tn-bg-highlight: 233 23% 21%; /* #292e42 - Elevated surfaces */\n --tn-bg-visual: 232 27% 25%; /* #33395e - Selection/active states */\n\n /* Text hierarchy */\n --tn-fg: 223 27% 76%; /* #a9b1d6 - Primary text */\n --tn-fg-bright: 220 13% 91%; /* #e2e4ea - Bright/highlighted text */\n --tn-fg-dark: 224 16% 53%; /* #787c99 - Secondary text (muted) */\n --tn-fg-gutter: 228 15% 45%; /* #5a607f - Borders, seams */\n\n /* Accent colors (semantic) */\n --tn-blue: 218 79% 73%; /* #7aa2f7 - Primary actions */\n --tn-green: 95 53% 55%; /* #9ece6a - Success */\n --tn-red: 348 74% 64%; /* #f7768e - Destructive, live */\n --tn-yellow: 35 79% 64%; /* #e0af68 - Warnings */\n --tn-purple: 267 82% 77%; /* #bb9af7 - Secondary accent */\n --tn-cyan: 178 64% 63%; /* #7dcfff - Info */\n --tn-teal: 162 66% 62%; /* #73daca - Terminal green */\n\n /* Player-internal variables (not shared with host) */\n --fw-background: var(--tn-bg);\n --fw-foreground: var(--tn-fg);\n --fw-card: var(--tn-bg-highlight);\n --fw-card-foreground: var(--tn-fg);\n --fw-popover: var(--tn-bg-highlight);\n --fw-popover-foreground: var(--tn-fg);\n --fw-primary: var(--tn-blue);\n --fw-primary-foreground: var(--tn-bg-dark);\n --fw-secondary: var(--tn-bg-visual);\n --fw-secondary-foreground: var(--tn-fg);\n --fw-muted: var(--tn-bg-highlight);\n --fw-muted-foreground: var(--tn-fg-dark);\n --fw-accent: var(--tn-bg-visual);\n --fw-accent-foreground: var(--tn-fg);\n --fw-destructive: var(--tn-red);\n --fw-destructive-foreground: var(--tn-bg-dark);\n --fw-border: var(--tn-fg-gutter);\n --fw-input: var(--tn-bg-highlight);\n --fw-ring: var(--tn-blue);\n --fw-radius: 0;\n\n /* Controls-specific variables */\n --fw-controls-bg: hsl(var(--tn-bg-dark) / 0.85);\n --fw-controls-fg: hsl(var(--tn-fg));\n --fw-seam: hsl(var(--tn-fg-gutter) / 0.3);\n color: var(--fw-controls-fg);\n}\n\n/* Declare layer upfront for lowest priority */\n@layer fw-player;\n\n/* Component styles in the fw-player layer */\n@layer fw-player {\n /* =====================================================\n ANIMATIONS\n ===================================================== */\n @keyframes float {\n 0%,\n 100% {\n transform: translateY(0px) scale(1);\n }\n 50% {\n transform: translateY(-12px) scale(0.95);\n }\n }\n\n @keyframes spin-slow {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n }\n\n @keyframes spin {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n }\n\n .animate-spin-slow {\n animation: spin-slow 18s linear infinite;\n }\n\n .animate-spin {\n animation: spin 1s linear infinite;\n }\n\n /* =====================================================\n SLAB SYSTEM - Player Controls & Overlays\n ===================================================== */\n\n .fw-player-surface button {\n font: inherit;\n }\n\n /* Slab base - solid structural block */\n .fw-slab {\n background: var(--fw-controls-bg);\n border: 1px solid var(--fw-seam);\n }\n\n /* Slab header - uppercase, padded */\n .fw-slab-header {\n padding: 0.5rem 1rem;\n border-bottom: 1px solid var(--fw-seam);\n font-size: 0.75rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: hsl(var(--tn-fg-dark));\n }\n\n /* Slab body - padded content area */\n .fw-slab-body {\n padding: 1rem;\n }\n\n /* Slab actions - flush buttons, seams between */\n .fw-slab-actions {\n display: flex;\n border-top: 1px solid var(--fw-seam);\n }\n\n .fw-slab-actions > * {\n flex: 1;\n border-radius: 0 !important;\n }\n\n .fw-slab-actions > * + * {\n border-left: 1px solid var(--fw-seam);\n }\n\n /* Control bar - stacked layout (seekbar above controls row) */\n .fw-control-bar {\n display: flex;\n flex-direction: column;\n width: 100%;\n background: var(--fw-controls-bg);\n border-top: 1px solid var(--fw-seam);\n backdrop-filter: blur(8px);\n pointer-events: auto;\n }\n\n /* Control group - seamed sections within bar */\n .fw-control-group {\n display: flex;\n align-items: center;\n padding: 0.5rem;\n }\n\n .fw-control-group + .fw-control-group {\n border-left: 1px solid var(--fw-seam);\n }\n\n /* Flush button - fills space, no radius */\n .fw-btn-flush {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 0.5rem 0.75rem;\n border-radius: 0;\n background: transparent;\n color: hsl(var(--tn-fg));\n transition:\n background-color 0.15s,\n color 0.15s;\n cursor: pointer;\n border: none;\n }\n\n .fw-btn-flush:hover {\n background: hsl(var(--tn-bg-visual) / 0.5);\n }\n\n .fw-btn-flush:active {\n background: hsl(var(--tn-bg-visual));\n }\n\n .fw-btn-flush--active {\n color: hsl(var(--tn-blue));\n }\n\n /* Status indicators */\n .fw-status-online {\n color: hsl(var(--tn-green));\n }\n .fw-status-offline {\n color: hsl(var(--tn-red));\n }\n .fw-status-warning {\n color: hsl(var(--tn-yellow));\n }\n .fw-status-info {\n color: hsl(var(--tn-cyan));\n }\n\n /* =====================================================\n PLAYER CONTAINER STYLES (Slab-based)\n ===================================================== */\n\n .fw-player-root {\n position: relative;\n height: 100%;\n width: 100%;\n overflow: hidden;\n border-radius: 0; /* Slabs don't have rounded corners */\n background-color: hsl(var(--tn-bg-dark));\n border: 1px solid hsl(var(--tn-fg-gutter) / 0.3);\n }\n\n .fw-player-container {\n height: 100%;\n width: 100%;\n overflow: hidden;\n border-radius: 0;\n background-color: hsl(var(--tn-bg-dark));\n }\n\n .fw-player-video {\n height: 100%;\n width: 100%;\n border-radius: 0;\n object-fit: contain;\n background-color: hsl(var(--tn-bg-dark));\n }\n\n .fw-player-embed {\n height: 100%;\n width: 100%;\n min-height: 300px;\n border-radius: 0;\n border-width: 0;\n background-color: hsl(var(--tn-bg-dark));\n }\n\n .fw-player-dvd {\n position: absolute;\n pointer-events: none;\n user-select: none;\n -webkit-user-select: none;\n }\n\n /* =====================================================\n VIDEO.JS CONTAINER CONSTRAINTS\n ===================================================== */\n\n /* VideoJS wrapper must respect container bounds */\n .fw-player-container .video-js {\n width: 100% !important;\n height: 100% !important;\n max-width: 100% !important;\n max-height: 100% !important;\n min-width: 0 !important;\n min-height: 0 !important;\n padding: 0 !important;\n background: black;\n }\n\n /* Ensure video tech fills the wrapper with letterboxing */\n .fw-player-container .video-js .vjs-tech {\n width: 100% !important;\n height: 100% !important;\n object-fit: contain !important;\n }\n\n /* =====================================================\n VIDEO.JS UI HIDING (when using custom controls)\n ===================================================== */\n\n /* Hide all VideoJS chrome when using our custom controls */\n .vjs-fw-custom-controls .vjs-control-bar,\n .vjs-fw-custom-controls .vjs-big-play-button,\n .vjs-fw-custom-controls .vjs-loading-spinner,\n .vjs-fw-custom-controls .vjs-text-track-display,\n .vjs-fw-custom-controls .vjs-error-display,\n .vjs-fw-custom-controls .vjs-modal-dialog,\n .vjs-fw-custom-controls .vjs-poster,\n .vjs-fw-custom-controls .vjs-live-control,\n .vjs-fw-custom-controls .vjs-title-bar {\n display: none !important;\n }\n\n /* Ensure video element fills container (absolute positioning) */\n .vjs-fw-custom-controls .vjs-tech {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n object-fit: contain;\n }\n\n /* =====================================================\n SEMANTIC COMPONENT CLASSES (npm_studio pattern)\n All component styling - no Tailwind utilities needed\n ===================================================== */\n\n /* --- Settings Menu --- */\n .fw-settings-menu {\n position: absolute;\n bottom: 3rem;\n right: 0;\n width: 12rem;\n max-height: 70vh;\n overflow-y: auto;\n background: hsl(var(--tn-bg-dark));\n border: 1px solid hsl(var(--tn-fg-gutter) / 0.3);\n box-shadow:\n 0 10px 15px -3px rgb(0 0 0 / 0.1),\n 0 4px 6px -4px rgb(0 0 0 / 0.1);\n border-radius: 0.25rem;\n z-index: 50;\n }\n\n .fw-settings-section {\n padding: 0.5rem;\n border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.3);\n }\n\n .fw-settings-section:last-child {\n border-bottom: none;\n }\n\n .fw-settings-label {\n font-size: 10px;\n color: hsl(var(--tn-fg-dark));\n text-transform: uppercase;\n font-weight: 600;\n margin-bottom: 0.25rem;\n padding-left: 0.25rem;\n }\n\n .fw-settings-options {\n display: flex;\n gap: 0.25rem;\n }\n\n .fw-settings-options--wrap {\n flex-wrap: wrap;\n }\n\n .fw-settings-btn {\n flex: 1;\n padding: 0.375rem 0.25rem;\n font-size: 10px;\n border-radius: 0.25rem;\n background: hsl(var(--tn-bg));\n color: hsl(var(--tn-fg));\n border: none;\n cursor: pointer;\n transition:\n background-color 0.15s,\n color 0.15s;\n }\n\n .fw-settings-btn:hover {\n background: hsl(var(--tn-bg-highlight));\n }\n\n .fw-settings-btn--active {\n background: hsl(var(--tn-blue));\n color: hsl(var(--tn-bg-dark));\n }\n\n .fw-settings-btn--active:hover {\n background: hsl(var(--tn-blue));\n }\n\n .fw-settings-list {\n display: flex;\n flex-direction: column;\n gap: 0.125rem;\n max-height: 8rem;\n overflow-y: auto;\n }\n\n .fw-settings-list-item {\n padding: 0.25rem 0.5rem;\n font-size: 0.75rem;\n text-align: left;\n border-radius: 0.25rem;\n background: transparent;\n color: hsl(var(--tn-fg));\n border: none;\n cursor: pointer;\n transition: background-color 0.15s;\n }\n\n .fw-settings-list-item:hover {\n background: hsl(var(--tn-bg-highlight));\n }\n\n .fw-settings-list-item--active {\n background: hsl(var(--tn-blue) / 0.2);\n color: hsl(var(--tn-blue));\n }\n\n /* --- Context Menu (bits-ui wrapper) --- */\n .fw-context-menu {\n z-index: 50;\n min-width: 8rem;\n overflow: hidden;\n border-radius: 0.25rem;\n border: 1px solid hsl(var(--tn-fg-gutter) / 0.3);\n background: hsl(var(--tn-bg-dark));\n padding: 0;\n color: hsl(var(--tn-fg));\n box-shadow:\n 0 10px 15px -3px rgb(0 0 0 / 0.1),\n 0 4px 6px -4px rgb(0 0 0 / 0.1);\n }\n\n .fw-context-menu-item {\n position: relative;\n display: flex;\n width: 100%;\n justify-content: flex-start;\n gap: 0.5rem;\n cursor: pointer;\n user-select: none;\n align-items: center;\n padding: 0.5rem 0.75rem;\n font-size: 0.875rem;\n text-align: left;\n font: inherit;\n border: none;\n background: transparent;\n appearance: none;\n -webkit-appearance: none;\n outline: none;\n color: hsl(var(--tn-fg));\n transition:\n background-color 0.15s,\n color 0.15s;\n }\n\n .fw-context-menu-item:hover,\n .fw-context-menu-item:focus {\n background: hsl(var(--tn-bg-visual));\n color: white;\n }\n\n .fw-context-menu-item[data-disabled] {\n pointer-events: none;\n opacity: 0.5;\n }\n\n .fw-context-menu-item--inset {\n padding-left: 2rem;\n }\n\n .fw-context-menu-separator {\n margin: 0.25rem -0.25rem;\n height: 1px;\n background: hsl(var(--tn-fg-gutter) / 0.3);\n }\n\n .fw-context-menu-label {\n padding: 0.375rem 0.5rem;\n font-size: 0.75rem;\n font-weight: 600;\n color: hsl(var(--tn-fg-dark));\n }\n\n .fw-context-menu-checkbox {\n position: relative;\n display: flex;\n width: 100%;\n justify-content: flex-start;\n gap: 0.5rem;\n cursor: pointer;\n user-select: none;\n align-items: center;\n padding: 0.5rem 0.5rem 0.5rem 2rem;\n font-size: 0.875rem;\n text-align: left;\n font: inherit;\n border: none;\n background: transparent;\n appearance: none;\n -webkit-appearance: none;\n outline: none;\n color: hsl(var(--tn-fg));\n transition:\n background-color 0.15s,\n color 0.15s;\n }\n\n .fw-context-menu-checkbox:hover,\n .fw-context-menu-checkbox:focus {\n background: hsl(var(--tn-bg-visual));\n color: white;\n }\n\n .fw-context-menu-indicator {\n position: absolute;\n left: 0.5rem;\n display: flex;\n height: 0.875rem;\n width: 0.875rem;\n align-items: center;\n justify-content: center;\n }\n\n /* --- Live Badge --- */\n .fw-live-badge {\n display: flex;\n align-items: center;\n gap: 0.25rem;\n padding: 0.125rem 0.5rem;\n font-size: 10px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n transition:\n background-color 0.15s,\n color 0.15s;\n border: none;\n cursor: pointer;\n }\n\n .fw-live-badge--active {\n background: rgb(220 38 38);\n color: white;\n cursor: default;\n }\n\n .fw-live-badge--behind {\n background: #414868;\n color: #a9b1d6;\n }\n\n .fw-live-badge--behind:hover {\n background: rgb(220 38 38);\n color: white;\n }\n\n /* --- Volume Control --- */\n .fw-volume-group {\n display: flex;\n align-items: center;\n border-radius: 0;\n transition:\n background-color 0.2s,\n width 0.2s;\n cursor: pointer;\n }\n\n .fw-volume-group--expanded {\n background: hsl(var(--tn-bg-visual) / 0.5);\n }\n\n .fw-volume-group:hover {\n background: hsl(var(--tn-bg-visual) / 0.5);\n }\n\n .fw-volume-group--expanded:hover {\n background: hsl(var(--tn-bg-visual) / 0.5);\n }\n\n .fw-volume-group--disabled {\n opacity: 0.4;\n cursor: not-allowed;\n }\n\n .fw-volume-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 2rem;\n height: 2rem;\n color: rgb(255 255 255 / 0.8);\n background: transparent;\n border: none;\n cursor: pointer;\n transition: color 0.15s;\n }\n\n .fw-volume-btn:hover {\n color: white;\n }\n\n .fw-volume-slider-wrapper {\n display: flex;\n align-items: center;\n overflow: hidden;\n transition:\n width 0.2s ease-out,\n opacity 0.2s ease-out;\n }\n\n .fw-volume-slider-wrapper--collapsed {\n width: 0;\n opacity: 0;\n }\n\n .fw-volume-slider-wrapper--expanded {\n width: 7rem;\n padding-right: 0.5rem;\n opacity: 1;\n }\n\n /* --- Slider Component (shared by React/Svelte) --- */\n .fw-slider {\n position: relative;\n display: flex;\n width: 100%;\n height: 1.25rem;\n touch-action: none;\n user-select: none;\n align-items: center;\n cursor: pointer;\n }\n\n .fw-slider--vertical {\n flex-direction: column;\n width: 1.25rem;\n height: 100%;\n }\n\n .fw-slider-track {\n position: absolute;\n left: 0;\n right: 0;\n height: 4px;\n border-radius: 9999px;\n background: hsl(var(--tn-fg-gutter) / 0.4);\n overflow: hidden;\n transition: height 0.15s ease;\n }\n\n .fw-slider:hover .fw-slider-track {\n height: 6px;\n }\n\n .fw-slider--vertical .fw-slider-track {\n top: 0;\n bottom: 0;\n left: 50%;\n right: auto;\n width: 4px;\n height: auto;\n transform: translateX(-50%);\n }\n\n .fw-slider--vertical:hover .fw-slider-track {\n width: 6px;\n height: auto;\n }\n\n .fw-slider-range {\n position: absolute;\n height: 100%;\n border-radius: 9999px;\n background: hsl(var(--tn-fg));\n }\n\n .fw-slider-range--accent {\n background: hsl(var(--tn-cyan));\n }\n\n .fw-slider--vertical .fw-slider-range {\n width: 100%;\n height: auto;\n bottom: 0;\n }\n\n .fw-slider-thumb {\n display: block;\n width: 10px;\n height: 10px;\n border-radius: 50%;\n background: hsl(var(--tn-fg));\n border: none;\n box-shadow: 0 2px 4px rgb(0 0 0 / 0.3);\n cursor: pointer;\n transition:\n width 0.15s ease,\n height 0.15s ease,\n box-shadow 0.15s ease;\n }\n\n .fw-slider:hover .fw-slider-thumb {\n width: 14px;\n height: 14px;\n }\n\n .fw-slider-thumb:focus {\n outline: none;\n }\n\n .fw-slider-thumb:focus-visible {\n box-shadow: 0 0 0 2px hsl(var(--tn-fg) / 0.5);\n }\n\n .fw-slider-thumb--accent {\n background: hsl(var(--tn-cyan));\n }\n\n .fw-slider-thumb[data-disabled] {\n pointer-events: none;\n opacity: 0.5;\n }\n\n /* --- Time Display --- */\n .fw-time-display {\n font-family: ui-monospace, monospace;\n font-size: 11px;\n line-height: 1;\n color: hsl(var(--tn-fg-dark));\n white-space: nowrap;\n padding: 0 0.5rem;\n }\n\n /* --- Controls Wrapper --- */\n .fw-controls-wrapper {\n position: absolute;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n flex-direction: column;\n justify-content: flex-end;\n transition: opacity 0.2s;\n pointer-events: none;\n }\n\n .fw-controls-wrapper--visible {\n opacity: 1;\n }\n\n .fw-controls-wrapper--hidden {\n opacity: 0;\n }\n\n .fw-controls-row {\n width: 100%;\n display: flex;\n align-items: center;\n justify-content: space-between;\n }\n\n .fw-controls-left {\n display: flex;\n align-items: center;\n min-width: 0;\n flex-shrink: 1;\n }\n\n .fw-controls-right {\n display: flex;\n align-items: center;\n }\n\n .fw-seek-wrapper {\n width: 100%;\n padding: 0 0.5rem;\n margin-bottom: -0.25rem;\n }\n\n /* --- SeekBar Component --- */\n .fw-seek-track {\n position: absolute;\n left: 0;\n right: 0;\n height: 4px;\n border-radius: 9999px;\n background: hsl(var(--tn-fg-gutter) / 0.4);\n transition: height 0.15s ease;\n }\n\n .fw-seek-wrapper:hover .fw-seek-track,\n .fw-seek-track--active {\n height: 6px;\n }\n\n .fw-seek-buffered {\n position: absolute;\n height: 100%;\n border-radius: 9999px;\n background: hsl(var(--tn-fg) / 0.3);\n transition: all 0.2s ease;\n }\n\n .fw-seek-progress {\n position: absolute;\n height: 100%;\n border-radius: 9999px;\n background: hsl(var(--tn-blue));\n transition: width 0.1s linear;\n }\n\n .fw-seek-thumb {\n position: absolute;\n top: 50%;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n background: hsl(var(--tn-blue));\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);\n transform: translate(-50%, -50%);\n transition:\n opacity 0.15s ease,\n transform 0.15s ease;\n opacity: 0;\n pointer-events: none;\n }\n\n .fw-seek-wrapper:hover .fw-seek-thumb,\n .fw-seek-thumb--active {\n opacity: 1;\n transform: translate(-50%, -50%) scale(1);\n }\n\n .fw-seek-thumb--hidden {\n opacity: 0;\n transform: translate(-50%, -50%) scale(0.75);\n }\n\n .fw-seek-tooltip {\n position: absolute;\n bottom: 100%;\n margin-bottom: 8px;\n padding: 4px 8px;\n font-family: ui-monospace, monospace;\n font-size: 12px;\n background: hsl(var(--tn-bg-dark) / 0.95);\n color: hsl(var(--tn-fg));\n border-radius: 4px;\n white-space: nowrap;\n pointer-events: none;\n transform: translateX(-50%);\n }\n\n /* --- Stats Panel --- */\n .fw-stats-panel {\n position: absolute;\n top: 0.5rem;\n right: 0.5rem;\n width: 18rem;\n max-height: 80%;\n overflow-y: auto;\n background: hsl(var(--tn-bg-dark) / 0.85);\n backdrop-filter: blur(4px);\n border: 1px solid hsl(var(--tn-fg-gutter) / 0.3);\n font-family: ui-monospace, monospace;\n font-size: 0.75rem;\n z-index: 30;\n }\n\n .fw-stats-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0.5rem;\n border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.3);\n font-size: 10px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: hsl(var(--tn-fg-dark));\n }\n\n .fw-stats-section {\n padding: 0.5rem;\n border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.3);\n }\n\n .fw-stats-section:last-child {\n border-bottom: none;\n }\n\n .fw-stats-row {\n display: flex;\n justify-content: space-between;\n padding: 0.125rem 0;\n color: hsl(var(--tn-fg-dark));\n }\n\n .fw-stats-value {\n color: hsl(var(--tn-fg));\n }\n\n /* --- Loading Screen --- */\n .fw-loading-screen {\n position: absolute;\n inset: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n background: hsl(var(--tn-bg-dark));\n z-index: 20;\n }\n\n .fw-loading-spinner {\n width: 2rem;\n height: 2rem;\n border: 3px solid hsl(var(--tn-fg-gutter) / 0.3);\n border-top-color: hsl(var(--tn-blue));\n border-radius: 50%;\n animation: spin 1s linear infinite;\n }\n\n .fw-loading-text {\n margin-top: 1rem;\n font-size: 0.875rem;\n color: hsl(var(--tn-fg-dark));\n }\n\n /* --- Idle Screen --- */\n .fw-idle-screen {\n position: absolute;\n inset: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n background: hsl(var(--tn-bg-dark));\n z-index: 20;\n }\n\n .fw-idle-message {\n font-size: 0.875rem;\n color: hsl(var(--tn-fg-dark));\n }\n\n /* --- Stream State Overlay --- */\n .fw-stream-state-overlay {\n position: absolute;\n inset: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n background: hsl(var(--tn-bg-dark) / 0.9);\n gap: 1rem;\n z-index: 20;\n }\n\n .fw-stream-state-icon {\n color: hsl(var(--tn-fg-dark));\n }\n\n .fw-stream-state-text {\n font-size: 0.875rem;\n color: hsl(var(--tn-fg-dark));\n }\n\n /* --- Title Overlay --- */\n .fw-title-overlay {\n padding: 1rem;\n background: linear-gradient(to bottom, hsl(var(--tn-bg-dark) / 0.8), transparent);\n }\n\n /* --- Thumbnail Overlay --- */\n .fw-player-thumbnail {\n position: relative;\n display: flex;\n height: 100%;\n min-height: 280px;\n width: 100%;\n cursor: pointer;\n align-items: center;\n justify-content: center;\n overflow: hidden;\n background: hsl(var(--tn-bg-dark));\n }\n\n /* --- Speed Indicator --- */\n .fw-speed-indicator {\n pointer-events: none;\n background: none;\n backdrop-filter: none;\n }\n\n /* --- Skip Indicator --- */\n .fw-skip-indicator {\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n /* --- Error Display --- */\n .fw-player-error {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 1rem;\n padding: 1.5rem;\n min-height: 280px;\n background: hsl(var(--tn-bg-dark));\n color: hsl(var(--tn-fg));\n text-align: center;\n }\n\n /* --- Error Popup Overlay --- */\n .fw-error-overlay {\n position: absolute;\n z-index: 20;\n }\n\n .fw-error-overlay--fullscreen {\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: hsl(0 0% 0% / 0.6);\n backdrop-filter: blur(4px);\n }\n\n .fw-error-overlay--passive {\n top: 0.75rem;\n left: 0.75rem;\n right: 0.75rem;\n }\n\n .fw-error-popup {\n background: hsl(var(--tn-bg-dark));\n border: 1px solid hsl(var(--tn-fg-gutter) / 0.3);\n box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.3);\n overflow: hidden;\n }\n\n .fw-error-popup--fullscreen {\n min-width: 280px;\n max-width: 320px;\n }\n\n .fw-error-popup--passive {\n max-width: 28rem;\n }\n\n .fw-error-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0.5rem 0.75rem;\n border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.3);\n }\n\n .fw-error-header--error {\n background: hsl(0 70% 50% / 0.1);\n }\n\n .fw-error-header--warning {\n background: hsl(45 100% 50% / 0.1);\n }\n\n .fw-error-title {\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n }\n\n .fw-error-title--error {\n color: hsl(0 70% 60%);\n }\n\n .fw-error-title--warning {\n color: hsl(45 100% 50%);\n }\n\n .fw-error-close {\n width: 24px;\n height: 24px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 4px;\n background: transparent;\n border: none;\n color: hsl(var(--tn-fg-dark));\n cursor: pointer;\n transition:\n background-color 0.15s ease,\n color 0.15s ease;\n }\n\n .fw-error-close:hover {\n background: hsl(var(--tn-fg) / 0.1);\n color: hsl(var(--tn-fg));\n }\n\n .fw-error-body {\n padding: 0.75rem;\n }\n\n .fw-error-message {\n font-size: 14px;\n color: hsl(var(--tn-fg));\n }\n\n .fw-error-actions {\n display: flex;\n border-top: 1px solid hsl(var(--tn-fg-gutter) / 0.3);\n }\n\n .fw-error-btn {\n flex: 1;\n padding: 0.5rem 1rem;\n font-size: 12px;\n font-weight: 500;\n color: hsl(var(--tn-fg));\n background: transparent;\n border: none;\n cursor: pointer;\n transition:\n background-color 0.15s ease,\n color 0.15s ease;\n }\n\n .fw-error-btn:hover {\n background: hsl(var(--tn-fg) / 0.05);\n color: hsl(var(--tn-fg-bright));\n }\n\n /* --- Context Menu Animations (for bits-ui) --- */\n @keyframes fw-context-menu-in {\n from {\n opacity: 0;\n transform: scale(0.95);\n }\n to {\n opacity: 1;\n transform: scale(1);\n }\n }\n\n @keyframes fw-context-menu-out {\n from {\n opacity: 1;\n transform: scale(1);\n }\n to {\n opacity: 0;\n transform: scale(0.95);\n }\n }\n\n .fw-context-menu[data-state=\"open\"] {\n animation: fw-context-menu-in 150ms cubic-bezier(0.4, 0, 0.2, 1);\n }\n\n .fw-context-menu[data-state=\"closed\"] {\n animation: fw-context-menu-out 150ms cubic-bezier(0.4, 0, 0.2, 1);\n }\n\n /* Slide animations for different sides */\n .fw-context-menu[data-side=\"top\"] {\n transform-origin: bottom center;\n }\n\n .fw-context-menu[data-side=\"bottom\"] {\n transform-origin: top center;\n }\n\n .fw-context-menu[data-side=\"left\"] {\n transform-origin: right center;\n }\n\n .fw-context-menu[data-side=\"right\"] {\n transform-origin: left center;\n }\n\n /* =====================================================\n DEV MODE PANEL (Advanced settings sidebar)\n ===================================================== */\n\n /* Main panel - side panel container */\n .fw-dev-panel {\n z-index: 40;\n pointer-events: auto;\n background: hsl(var(--tn-bg-dark));\n border-left: 1px solid hsl(var(--tn-fg-gutter) / 0.5);\n color: hsl(var(--tn-fg));\n font-family: ui-monospace, monospace;\n font-size: 0.75rem;\n width: 280px;\n display: flex;\n flex-direction: column;\n height: 100%;\n flex-shrink: 0;\n }\n\n /* Header with tabs */\n .fw-dev-header {\n display: flex;\n align-items: center;\n border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.3);\n background: hsl(var(--tn-bg) / 0.5);\n }\n\n /* Tab buttons */\n .fw-dev-tab {\n padding: 0.5rem 0.75rem;\n font-size: 10px;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n font-weight: 600;\n background: transparent;\n border: none;\n border-right: 1px solid hsl(var(--tn-fg-gutter) / 0.3);\n color: hsl(var(--tn-fg-dark));\n cursor: pointer;\n transition:\n background-color 0.15s,\n color 0.15s;\n }\n\n .fw-dev-tab:hover {\n background: hsl(var(--tn-bg-dark) / 0.5);\n color: hsl(var(--tn-fg));\n }\n\n .fw-dev-tab--active {\n background: hsl(var(--tn-bg-dark));\n color: hsl(var(--tn-fg-bright));\n }\n\n .fw-dev-close {\n padding: 0.5rem;\n width: 2rem;\n height: 2rem;\n display: flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: none;\n color: hsl(var(--tn-fg-dark));\n cursor: pointer;\n transition: color 0.15s;\n margin-left: auto;\n }\n\n .fw-dev-close:hover {\n color: hsl(var(--tn-fg));\n }\n\n /* Spacer - pushes close button to right */\n .fw-dev-spacer {\n flex: 1;\n }\n\n /* Content area (body) */\n .fw-dev-content,\n .fw-dev-body {\n flex: 1;\n overflow-y: auto;\n }\n\n /* Section with label */\n .fw-dev-section {\n padding: 0.75rem;\n border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.3);\n }\n\n .fw-dev-section-label,\n .fw-dev-label {\n font-size: 10px;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n font-weight: 600;\n color: hsl(var(--tn-fg-dark));\n margin-bottom: 0.25rem;\n }\n\n .fw-dev-section-value,\n .fw-dev-value {\n font-size: 0.875rem;\n color: hsl(var(--tn-fg-bright));\n }\n\n .fw-dev-value-arrow {\n color: hsl(var(--tn-fg-dark));\n }\n\n .fw-dev-value-muted {\n font-size: 10px;\n color: hsl(var(--tn-fg-dark));\n margin-top: 0.25rem;\n }\n\n .fw-dev-section-sub {\n font-size: 10px;\n color: hsl(var(--tn-fg-dark));\n margin-top: 0.25rem;\n }\n\n /* Mode selector buttons */\n .fw-dev-mode-options,\n .fw-dev-mode-group {\n display: flex;\n gap: 0.25rem;\n margin-top: 0.5rem;\n }\n\n .fw-dev-mode-desc {\n font-size: 10px;\n color: hsl(var(--tn-fg-dark));\n margin-top: 0.5rem;\n font-style: italic;\n }\n\n .fw-dev-mode-btn {\n flex: 1;\n padding: 0.375rem 0.5rem;\n font-size: 10px;\n font-weight: 500;\n border-radius: 0.25rem;\n background: hsl(var(--tn-bg-highlight));\n border: none;\n color: hsl(var(--tn-fg-dark));\n cursor: pointer;\n transition:\n background-color 0.15s,\n color 0.15s;\n }\n\n .fw-dev-mode-btn:hover {\n color: hsl(var(--tn-fg));\n background: hsl(var(--tn-bg-visual));\n }\n\n .fw-dev-mode-btn--active {\n background: hsl(var(--tn-blue));\n color: hsl(var(--tn-bg-dark));\n }\n\n /* Action buttons row */\n .fw-dev-actions {\n display: flex;\n border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.3);\n }\n\n .fw-dev-action-btn {\n flex: 1;\n padding: 0.5rem 0.75rem;\n background: hsl(var(--tn-bg-highlight));\n border: none;\n border-right: 1px solid hsl(var(--tn-fg-gutter) / 0.3);\n color: hsl(var(--tn-fg));\n font-size: 0.75rem;\n cursor: pointer;\n transition:\n background-color 0.15s,\n color 0.15s;\n }\n\n .fw-dev-action-btn:last-child {\n border-right: none;\n }\n\n .fw-dev-action-btn:hover {\n background: hsl(var(--tn-bg-visual));\n color: hsl(var(--tn-fg-bright));\n }\n\n /* Combo list header */\n .fw-dev-list-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0.5rem 0.75rem;\n background: hsl(var(--tn-bg) / 0.5);\n }\n\n .fw-dev-list-title {\n font-size: 10px;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n font-weight: 600;\n color: hsl(var(--tn-fg-dark));\n }\n\n .fw-dev-list-toggle {\n font-size: 10px;\n color: hsl(var(--tn-fg-dark));\n background: transparent;\n border: none;\n cursor: pointer;\n display: flex;\n align-items: center;\n gap: 0.25rem;\n padding: 0.25rem;\n transition: color 0.15s;\n }\n\n .fw-dev-list-toggle:hover {\n color: hsl(var(--tn-fg));\n }\n\n /* Combo list items */\n .fw-dev-combo-list {\n border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.3);\n }\n\n .fw-dev-combo-item {\n width: 100%;\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.5rem 0.75rem;\n text-align: left;\n background: transparent;\n border: none;\n border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.2);\n color: hsl(var(--tn-fg));\n cursor: pointer;\n transition: background-color 0.15s;\n }\n\n .fw-dev-combo-item:last-child {\n border-bottom: none;\n }\n\n .fw-dev-combo-item:hover {\n background: hsl(var(--tn-bg) / 0.5);\n }\n\n .fw-dev-combo-item--active {\n background: hsl(var(--tn-bg-visual));\n color: hsl(var(--tn-fg-bright));\n box-shadow: inset 0 0 0 1px hsl(var(--tn-blue));\n }\n\n .fw-dev-combo-item--disabled {\n opacity: 0.4;\n }\n\n .fw-dev-combo-item--warning {\n background: hsl(var(--tn-yellow) / 0.05);\n }\n\n .fw-dev-combo-item--warning:hover {\n background: hsl(var(--tn-yellow) / 0.1);\n }\n\n .fw-dev-combo-rank {\n width: 1.25rem;\n height: 1.25rem;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 10px;\n font-weight: 700;\n border-radius: 0.125rem;\n background: hsl(var(--tn-fg-gutter) / 0.5);\n color: hsl(var(--tn-fg-dark));\n flex-shrink: 0;\n }\n\n .fw-dev-combo-rank--active {\n background: hsl(var(--tn-blue));\n color: hsl(var(--tn-bg-dark));\n }\n\n .fw-dev-combo-rank--warning {\n background: hsl(var(--tn-yellow) / 0.3);\n color: hsl(var(--tn-yellow));\n }\n\n .fw-dev-combo-name {\n flex: 1;\n font-size: 0.75rem;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .fw-dev-combo-arrow {\n color: hsl(var(--tn-fg-dark));\n }\n\n .fw-dev-combo-type {\n color: hsl(var(--tn-cyan));\n }\n\n .fw-dev-combo-type--warning {\n color: hsl(var(--tn-yellow));\n }\n\n .fw-dev-combo-score {\n font-size: 10px;\n font-family: ui-monospace, monospace;\n font-variant-numeric: tabular-nums;\n padding: 0.125rem 0.375rem;\n border-radius: 0.125rem;\n flex-shrink: 0;\n }\n\n .fw-dev-combo-score--high {\n background: hsl(var(--tn-green) / 0.2);\n color: hsl(var(--tn-green));\n }\n\n .fw-dev-combo-score--medium {\n background: hsl(var(--tn-blue) / 0.2);\n color: hsl(var(--tn-blue));\n }\n\n .fw-dev-combo-score--low {\n background: hsl(var(--tn-yellow) / 0.2);\n color: hsl(var(--tn-yellow));\n }\n\n /* Tooltip for combo details */\n .fw-dev-tooltip {\n position: absolute;\n left: 0;\n z-index: 50;\n background: hsl(var(--tn-bg-dark));\n border: 1px solid hsl(var(--tn-fg-gutter));\n box-shadow:\n 0 4px 6px -1px rgb(0 0 0 / 0.1),\n 0 2px 4px -2px rgb(0 0 0 / 0.1);\n padding: 0.5rem;\n font-size: 10px;\n white-space: nowrap;\n pointer-events: none;\n min-width: 220px;\n }\n\n .fw-dev-tooltip--above {\n bottom: 100%;\n margin-bottom: 0.25rem;\n }\n\n .fw-dev-tooltip--below {\n top: 100%;\n margin-top: 0.25rem;\n }\n\n .fw-dev-tooltip-header {\n margin-bottom: 0.5rem;\n padding-bottom: 0.5rem;\n border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.5);\n }\n\n .fw-dev-tooltip-title {\n font-weight: 700;\n color: hsl(var(--tn-fg-bright));\n }\n\n .fw-dev-tooltip-subtitle {\n color: hsl(var(--tn-cyan));\n }\n\n .fw-dev-tooltip-info {\n color: hsl(var(--tn-fg-dark));\n margin-top: 0.25rem;\n }\n\n .fw-dev-tooltip-score {\n font-weight: 700;\n color: hsl(var(--tn-fg-bright));\n margin-bottom: 0.25rem;\n }\n\n .fw-dev-tooltip-breakdown {\n color: hsl(var(--tn-fg-dark));\n }\n\n .fw-dev-tooltip-breakdown-value {\n color: hsl(var(--tn-fg));\n }\n\n .fw-dev-tooltip-breakdown-weight {\n color: hsl(var(--tn-fg-gutter));\n }\n\n .fw-dev-tooltip-error {\n color: hsl(var(--tn-red));\n }\n\n /* Stats panel content */\n .fw-dev-stats-hero {\n padding: 0.75rem;\n border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.3);\n }\n\n .fw-dev-stats-rate {\n font-size: 1.5rem;\n font-weight: 700;\n font-variant-numeric: tabular-nums;\n }\n\n .fw-dev-stats-rate--good {\n color: hsl(var(--tn-green));\n }\n\n .fw-dev-stats-rate--catching {\n color: hsl(var(--tn-blue));\n }\n\n .fw-dev-stats-rate--slow {\n color: hsl(var(--tn-yellow));\n }\n\n .fw-dev-stats-rate--stalling {\n color: hsl(var(--tn-red));\n }\n\n .fw-dev-stats-label {\n font-size: 10px;\n color: hsl(var(--tn-fg-dark));\n }\n\n .fw-dev-stats-metrics {\n display: flex;\n gap: 1rem;\n margin-top: 0.5rem;\n font-size: 10px;\n }\n\n .fw-dev-stats-metric--good {\n color: hsl(var(--tn-green));\n }\n\n .fw-dev-stats-metric--warning {\n color: hsl(var(--tn-yellow));\n }\n\n .fw-dev-stats-metric--bad {\n color: hsl(var(--tn-red));\n }\n\n /* Stats rows */\n .fw-dev-stats-rows {\n border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.3);\n }\n\n .fw-dev-stats-row {\n display: flex;\n justify-content: space-between;\n padding: 0.5rem 0.75rem;\n border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.2);\n }\n\n .fw-dev-stats-row:last-child {\n border-bottom: none;\n }\n\n .fw-dev-stats-key {\n color: hsl(var(--tn-fg-dark));\n }\n\n .fw-dev-stats-value {\n color: hsl(var(--tn-fg-bright));\n }\n\n .fw-dev-stats-value--cyan {\n color: hsl(var(--tn-cyan));\n }\n\n .fw-dev-stats-value--warning {\n color: hsl(var(--tn-yellow));\n }\n\n .fw-dev-stats-value--bad {\n color: hsl(var(--tn-red));\n }\n\n .fw-dev-stats-value--good {\n color: hsl(var(--tn-green));\n }\n\n /* Track badges */\n .fw-dev-track-badge {\n font-size: 10px;\n font-family: ui-monospace, monospace;\n text-transform: uppercase;\n padding: 0.125rem 0.375rem;\n }\n\n .fw-dev-track-badge--video {\n background: hsl(var(--tn-blue) / 0.2);\n color: hsl(var(--tn-blue));\n }\n\n .fw-dev-track-badge--audio {\n background: hsl(var(--tn-green) / 0.2);\n color: hsl(var(--tn-green));\n }\n\n .fw-dev-track-badge--other {\n background: hsl(var(--tn-yellow) / 0.2);\n color: hsl(var(--tn-yellow));\n }\n\n .fw-dev-track-info {\n display: flex;\n flex-wrap: wrap;\n gap: 0.5rem;\n font-size: 10px;\n color: hsl(var(--tn-fg-dark));\n }\n\n /* Empty state */\n .fw-dev-empty,\n .fw-dev-list-empty {\n padding: 1rem 0.75rem;\n text-align: center;\n color: hsl(var(--tn-fg-dark));\n }\n\n /* Chevron icon (expand/collapse toggle) */\n .fw-dev-chevron {\n transition: transform 0.15s;\n }\n\n .fw-dev-chevron--open {\n transform: rotate(180deg);\n }\n\n /* Combo wrapper (relative for tooltip positioning) */\n .fw-dev-combo {\n position: relative;\n }\n\n /* Combo button (alias for fw-dev-combo-item) */\n .fw-dev-combo-btn {\n width: 100%;\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.5rem 0.75rem;\n text-align: left;\n background: transparent;\n border: none;\n border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.2);\n color: hsl(var(--tn-fg));\n cursor: pointer;\n transition: background-color 0.15s;\n }\n\n .fw-dev-combo-btn:last-child {\n border-bottom: none;\n }\n\n .fw-dev-combo-btn:hover {\n background: hsl(var(--tn-bg) / 0.5);\n }\n\n .fw-dev-combo-btn--active {\n background: hsl(var(--tn-bg-visual));\n color: hsl(var(--tn-fg-bright));\n box-shadow: inset 0 0 0 1px hsl(var(--tn-blue));\n }\n\n .fw-dev-combo-btn--disabled {\n opacity: 0.4;\n }\n\n .fw-dev-combo-btn--codec-warn {\n background: hsl(var(--tn-yellow) / 0.05);\n }\n\n .fw-dev-combo-btn--codec-warn:hover {\n background: hsl(var(--tn-yellow) / 0.1);\n }\n\n /* Combo rank modifiers */\n .fw-dev-combo-rank--disabled {\n background: hsl(var(--tn-fg-gutter) / 0.3);\n color: hsl(var(--tn-fg-gutter));\n }\n\n .fw-dev-combo-rank--warn {\n background: hsl(var(--tn-yellow) / 0.3);\n color: hsl(var(--tn-yellow));\n }\n\n /* Combo type modifiers */\n .fw-dev-combo-type--disabled {\n color: hsl(var(--tn-fg-gutter));\n }\n\n .fw-dev-combo-type--warn {\n color: hsl(var(--tn-yellow));\n }\n\n /* Combo score modifiers (additional) */\n .fw-dev-combo-score--disabled {\n background: hsl(var(--tn-fg-gutter) / 0.2);\n color: hsl(var(--tn-fg-gutter));\n }\n\n .fw-dev-combo-score--mid {\n background: hsl(var(--tn-blue) / 0.2);\n color: hsl(var(--tn-blue));\n }\n\n /* Tooltip additional classes */\n .fw-dev-tooltip-tracks {\n color: hsl(var(--tn-fg-dark));\n margin-top: 0.25rem;\n }\n\n .fw-dev-tooltip-value {\n color: hsl(var(--tn-fg));\n }\n\n .fw-dev-tooltip-row {\n color: hsl(var(--tn-fg-dark));\n margin-bottom: 0.125rem;\n }\n\n .fw-dev-tooltip-bonus {\n color: hsl(var(--tn-green));\n }\n\n .fw-dev-tooltip-penalty {\n color: hsl(var(--tn-red));\n }\n\n .fw-dev-tooltip-weight {\n color: hsl(var(--tn-fg-gutter));\n font-size: 9px;\n }\n\n /* =====================================================\n DEV MODE STATS TAB\n ===================================================== */\n\n /* Section header modifier */\n .fw-dev-section-header {\n margin-top: 0.5rem;\n }\n\n /* Playback rate hero section */\n .fw-dev-rate {\n display: flex;\n align-items: baseline;\n gap: 0.5rem;\n margin-bottom: 0.5rem;\n }\n\n .fw-dev-rate-value {\n font-size: 1.5rem;\n font-weight: 700;\n font-variant-numeric: tabular-nums;\n }\n\n .fw-dev-rate-status {\n font-size: 10px;\n color: hsl(var(--tn-fg-dark));\n text-transform: uppercase;\n }\n\n .fw-dev-rate-stats {\n display: flex;\n gap: 1rem;\n font-size: 10px;\n color: hsl(var(--tn-fg-dark));\n }\n\n /* Stat value modifiers */\n .fw-dev-stat-value--good {\n color: hsl(var(--tn-green));\n }\n\n .fw-dev-stat-value--accent {\n color: hsl(var(--tn-blue));\n }\n\n .fw-dev-stat-value--warn {\n color: hsl(var(--tn-yellow));\n }\n\n .fw-dev-stat-value--bad {\n color: hsl(var(--tn-red));\n }\n\n /* Stats row (key-value pair) */\n .fw-dev-stat {\n display: flex;\n justify-content: space-between;\n padding: 0.5rem 0.75rem;\n border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.2);\n }\n\n .fw-dev-stat:last-child {\n border-bottom: none;\n }\n\n .fw-dev-stat-label {\n color: hsl(var(--tn-fg-dark));\n }\n\n .fw-dev-stat-value {\n color: hsl(var(--tn-fg-bright));\n }\n\n /* Track display */\n .fw-dev-track {\n padding: 0.5rem 0.75rem;\n border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.2);\n }\n\n .fw-dev-track:last-child {\n border-bottom: none;\n }\n\n .fw-dev-track-header {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n margin-bottom: 0.25rem;\n }\n\n .fw-dev-track-codec {\n font-size: 10px;\n color: hsl(var(--tn-fg));\n font-weight: 500;\n }\n\n .fw-dev-track-id {\n font-size: 10px;\n color: hsl(var(--tn-fg-gutter));\n margin-left: auto;\n }\n\n .fw-dev-track-meta {\n display: flex;\n flex-wrap: wrap;\n gap: 0.5rem;\n font-size: 10px;\n color: hsl(var(--tn-fg-dark));\n }\n\n /* No tracks state */\n .fw-dev-no-tracks {\n padding: 0.75rem;\n text-align: center;\n }\n\n .fw-dev-no-tracks-text {\n color: hsl(var(--tn-fg-dark));\n font-size: 10px;\n }\n\n .fw-dev-no-tracks-type {\n color: hsl(var(--tn-fg-gutter));\n margin-left: 0.25rem;\n }\n}\n\n`;\n"],"names":[],"mappings":";;AAAA;AACA;AAGO,MAAM,YAAY,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|