saltfish 0.2.65 → 0.2.66

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.
@@ -3719,26 +3719,28 @@ __publicField(_ManagerOrchestrator, "prevState", {
3719
3719
  isMinimized: false
3720
3720
  });
3721
3721
  let ManagerOrchestrator = _ManagerOrchestrator;
3722
- const baseResetCss = "/* \n * CSS Reset for the Saltfish playlist Player\n * Minimal reset for the Shadow DOM to ensure consistent rendering\n */\n\n:host {\n all: initial;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;\n box-sizing: border-box;\n}\n\n:host *,\n:host *::before,\n:host *::after {\n box-sizing: inherit;\n margin: 0;\n padding: 0;\n}\n\nbutton {\n background: none;\n border: none;\n cursor: pointer;\n font: inherit;\n outline: none;\n padding: 0;\n} ";
3722
+ const baseResetCss = "/* \n * CSS Reset for the Saltfish playlist Player\n * Minimal reset for the Shadow DOM to ensure consistent rendering\n */\n\n:host {\n all: initial;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;\n box-sizing: border-box;\n}\n\n:host *,\n:host *::before,\n:host *::after {\n box-sizing: inherit;\n margin: 0;\n padding: 0;\n}\n\nbutton {\n background: none;\n border: none;\n cursor: pointer;\n font: inherit;\n outline: none;\n padding: 0;\n}\n\n/* Utility classes for CSP-compliant styling */\n.sf-hidden {\n display: none !important;\n} ";
3723
3723
  const baseVariablesCss = "/* \n * Variables for the Saltfish playlist Player\n * Defines all design tokens used throughout the application\n */\n\n:host {\n /* Colors */\n --sf-primary-color: #4a9bff;\n --sf-secondary-color: #6ccfff;\n --sf-background-color: #1e1e1e;\n --sf-text-color: #ffffff;\n --sf-button-bg: rgba(0, 0, 0, 0.5);\n --sf-button-hover-bg: rgba(0, 0, 0, 0.7);\n --sf-overlay-gradient: linear-gradient(180deg, rgba(0, 0, 0, 0.7) 0%, transparent 30%, transparent 70%, rgba(0, 0, 0, 0.7) 100%);\n --sf-progress-gradient: linear-gradient(90deg, var(--sf-primary-color), var(--sf-secondary-color));\n --sf-error-color: #ff4d4d;\n --sf-error-bg: rgba(255, 77, 77, 0.1);\n \n /* Spacing */\n --sf-spacing-xs: 4px;\n --sf-spacing-sm: 8px;\n --sf-spacing-md: 12px;\n --sf-spacing-lg: 16px;\n --sf-spacing-xl: 24px;\n \n /* Sizes */\n --sf-player-width: 240px;\n --sf-player-height: 336px;\n --sf-player-min-width: 80px;\n --sf-player-min-height: 80px;\n --sf-control-button-size: 24px;\n --sf-play-button-size: 60px;\n --sf-minimize-button-size: 34px;\n --sf-mute-button-size: 32px;\n --sf-cc-button-size: 32px;\n --sf-cursor-size: 32px;\n \n /* Border radius */\n --sf-border-radius-sm: 4px;\n --sf-border-radius-md: 8px;\n --sf-border-radius-lg: 16px;\n --sf-border-radius-circle: 50%;\n \n /* Transitions */\n --sf-transition-fast: 0.1s ease;\n --sf-transition-normal: 0.2s ease;\n --sf-transition-slow: 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);\n \n /* Shadows */\n --sf-shadow-small: 0 2px 5px rgba(0, 0, 0, 0.2);\n --sf-shadow-medium: 0 4px 8px rgba(0, 0, 0, 0.15);\n --sf-shadow-large: 0 10px 25px rgba(0, 0, 0, 0.2);\n \n /* Z-index layering */\n --sf-z-index-base: 1;\n --sf-z-index-overlay: 2;\n --sf-z-index-controls: 10;\n --sf-z-index-cursor: 9999;\n --sf-z-index-player: 2147483648;\n \n /* Font sizes */\n --sf-font-size-sm: 14px;\n --sf-font-size-md: 16px;\n --sf-font-size-lg: 18px;\n --sf-font-size-xl: 24px;\n} \n\n/* Mobile device responsive adjustments - make player smaller for mobile screens */\n@media (max-width: 768px) {\n :host {\n /* Reduce player size on mobile for better space utilization */\n --sf-player-width: 180px; /* 25% smaller than desktop (240px -> 180px) */\n --sf-player-height: 252px; /* 25% smaller than desktop (336px -> 252px) */\n --sf-player-min-width: 60px; /* Smaller when minimized (80px -> 60px) */\n --sf-player-min-height: 60px; /* Smaller when minimized (80px -> 60px) */\n \n /* Keep controls touch-friendly despite smaller player size */\n --sf-play-button-size: 44px; /* Smaller but still touch-friendly (60px -> 44px) */\n --sf-control-button-size: 28px; /* Keep larger for touch targets (24px -> 28px) */\n --sf-mute-button-size: 26px; /* Smaller for mobile (32px -> 26px) */\n --sf-cc-button-size: 26px; /* Smaller for mobile (32px -> 26px) */\n --sf-minimize-button-size: 26px; /* Match other mobile button sizes */\n }\n}\n\n/* Touch device specific adjustments (tablets and larger touch devices, excluding mobile) */\n@media (pointer: coarse) and (min-width: 769px) {\n :host {\n /* Ensure touch-friendly sizes even on larger touch devices */\n --sf-control-button-size: 28px;\n --sf-mute-button-size: 38px;\n --sf-cc-button-size: 38px;\n --sf-minimize-button-size: 38px; /* Match other touch device button sizes */\n }\n} ";
3724
- const componentsPlayerCss = "/* \n * Player component styles for the Saltfish playlist Player\n * Following BEM naming convention\n */\n\n/* Main player container */\n.sf-player {\n border-radius: var(--sf-border-radius-lg);\n box-shadow: 0 25px 50px rgba(0, 0, 0, 0.45), 0 10px 20px rgba(0, 0, 0, 0.3), 0 0 0 2px rgba(255, 255, 255, 0.08);\n transition: all var(--sf-transition-slow);\n position: relative;\n backdrop-filter: blur(10px);\n -webkit-backdrop-filter: blur(10px);\n opacity: 0;\n transition: opacity 0.3s ease-in-out;\n}\n\n/* Player visible state - show with fade in */\n.sf-player--visible {\n opacity: 1;\n}\n\n/* Dark gradient overlay at bottom of player */\n.sf-player::before {\n content: '';\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n height: 33.33%; /* One third of player height */\n background: linear-gradient(to top, rgba(0, 0, 0, 0.8) 0%, rgba(0, 0, 0, 0) 100%);\n pointer-events: none;\n z-index: var(--sf-z-index-overlay);\n border-radius: 8px;\n}\n\n/* Hide gradient overlay when minimized */\n.sf-player--minimized::before {\n display: none;\n}\n\n/* Full-size player state */\n.sf-player:not(.sf-player--minimized) {\n width: var(--sf-player-width);\n height: var(--sf-player-height);\n}\n\n/* Autoplay fallback state - ensure play button is visible */\n.sf-player--waiting-for-user-interaction .sf-controls-container__play-button {\n display: flex !important;\n opacity: 1 !important;\n visibility: visible !important;\n}\n\n/* Also show the center play button in autoplay fallback state */\n.sf-player--waiting-for-user-interaction .sf-player__center-play-button {\n display: flex !important;\n opacity: 1 !important;\n z-index: calc(var(--sf-z-index-controls) + 20) !important; /* Higher z-index to appear above overlay */\n}\n\n/* Make the autoplay fallback state more prominent to indicate need for interaction */\n.sf-player--waiting-for-user-interaction::after {\n content: '';\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.3);\n pointer-events: none;\n z-index: var(--sf-z-index-overlay);\n}\n\n/* Player state: minimized */\n.sf-player--minimized {\n /* Equal width and height are essential for maintaining a perfect circle when using border-radius: 50% */\n width: var(--sf-player-min-width);\n height: var(--sf-player-min-height);\n border-radius: var(--sf-border-radius-circle);\n box-shadow: 0 15px 30px rgba(0, 0, 0, 0.35), 0 5px 15px rgba(0, 0, 0, 0.25), 0 0 0 2px rgba(255, 255, 255, 0.08);\n cursor: pointer;\n /* Force overriding any inline styles that might be applied */\n max-width: var(--sf-player-min-width) !important;\n max-height: var(--sf-player-min-height) !important;\n min-width: var(--sf-player-min-width) !important;\n min-height: var(--sf-player-min-height) !important;\n}\n\n/* Hide controls when minimized */\n.sf-player--minimized .sf-controls-container {\n display: none;\n}\n\n/* Only show the minimize button when hovering on minimized player */\n.sf-player--minimized .sf-player__minimize-button {\n opacity: 0;\n}\n\n.sf-player--minimized:hover .sf-player__minimize-button {\n opacity: 1;\n}\n\n/* Player root element */\n#sf-player-root {\n position: fixed;\n z-index: var(--sf-z-index-player);\n}\n\n/* Fixed positioning classes */\n.sf-player-root--bottom-left {\n bottom: 20px;\n left: 20px;\n}\n\n.sf-player-root--bottom-right {\n bottom: 20px;\n right: 20px;\n}\n\n/* Player error message */\n.sf-player__error {\n padding: var(--sf-spacing-md);\n color: var(--sf-error-color);\n background-color: var(--sf-error-bg);\n border-radius: var(--sf-border-radius-md);\n margin: var(--sf-spacing-sm);\n font-size: var(--sf-font-size-sm);\n border-left: 4px solid var(--sf-error-color);\n}\n\n/* Minimize button */\n.sf-player__minimize-button {\n position: absolute;\n top: 8px;\n right: 6px;\n width: var(--sf-minimize-button-size);\n height: var(--sf-minimize-button-size);\n background-color: transparent;\n border-radius: var(--sf-border-radius-circle);\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n z-index: var(--sf-z-index-controls);\n color: white;\n border: none;\n font-size: calc(var(--sf-font-size-sm) + 4px);\n transition: all var(--sf-transition-normal);\n text-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);\n opacity: 0;\n}\n\n/* Minimize button hover state */\n.sf-player__minimize-button:hover {\n transform: scale(1.1);\n}\n\n/* Show minimize button on player hover */\n.sf-player:hover .sf-player__minimize-button {\n opacity: 1;\n}\n\n/* Mobile and touch device overrides for minimize button visibility */\n/* Ensure minimize button is always visible on touch devices, even when minimized */\n@media (pointer: coarse) {\n .sf-player__minimize-button {\n opacity: 1 !important;\n z-index: calc(var(--sf-z-index-controls) + 50) !important; /* Much higher z-index for touch devices */\n }\n \n .sf-player--minimized .sf-player__minimize-button {\n opacity: 1 !important;\n z-index: calc(var(--sf-z-index-controls) + 50) !important; /* Much higher z-index for touch devices */\n }\n}\n\n/* Ensure minimize button is always visible on mobile screens under 768px */\n@media (max-width: 768px) {\n .sf-player__minimize-button {\n opacity: 1 !important;\n z-index: calc(var(--sf-z-index-controls) + 50) !important; /* Much higher z-index for mobile */\n /* Position closer to top right corner on mobile */\n top: var(--sf-spacing-xs) !important; /* 4px from top instead of 16px */\n right: var(--sf-spacing-xs) !important; /* 4px from right instead of 12px */\n }\n \n .sf-player--minimized .sf-player__minimize-button {\n opacity: 1 !important;\n z-index: calc(var(--sf-z-index-controls) + 50) !important; /* Much higher z-index for mobile */\n /* Position closer to top right corner on mobile */\n top: var(--sf-spacing-xs) !important; /* 4px from top instead of 16px */\n right: var(--sf-spacing-xs) !important; /* 4px from right instead of 12px */\n }\n}\n\n/* Player title */\n.sf-player__title {\n position: absolute;\n top: var(--sf-spacing-md);\n left: var(--sf-spacing-md);\n color: var(--sf-text-color);\n font-size: var(--sf-font-size-md);\n font-weight: 600;\n z-index: var(--sf-z-index-controls);\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);\n max-width: 70%;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n/* Centered play/pause button overlay */\n.sf-player__center-play-button {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: var(--sf-play-button-size);\n height: var(--sf-play-button-size);\n background-color: rgba(0, 0, 0, 0.5);\n border-radius: var(--sf-border-radius-circle);\n display: none; /* Hidden by default */\n justify-content: center;\n align-items: center;\n z-index: calc(var(--sf-z-index-controls) + 10); /* Ensure higher z-index than other elements */\n color: white;\n border: none;\n font-size: var(--sf-control-button-size);\n cursor: pointer;\n transition: transform var(--sf-transition-normal), background-color var(--sf-transition-normal);\n backdrop-filter: blur(3px);\n -webkit-backdrop-filter: blur(3px);\n box-shadow: 0 15px 30px rgba(0, 0, 0, 0.5), 0 5px 15px rgba(0, 0, 0, 0.3), 0 0 0 2px rgba(255, 255, 255, 0.08);\n pointer-events: auto; /* Enable pointer events to capture clicks */\n}\n\n/* Center play button hover state */\n.sf-player__center-play-button:hover {\n transform: translate(-50%, -50%) scale(1.1);\n background-color: rgba(0, 0, 0, 0.7);\n}\n\n/* Hide center play button in minimized state */\n.sf-player--minimized .sf-player__center-play-button {\n display: none !important;\n}\n\n/* Center play button with adjustment for 3+ buttons */\n.sf-player__center-play-button--with-many-buttons {\n transform: translate(-50%, -85%) !important; /* Move up by adjusting Y offset */\n}\n\n/* Exit button for minimized mode */\n.sf-player__exit-button {\n position: absolute;\n top: -22px; /* Position it above the player */\n right: 0;\n width: 20px;\n height: 20px;\n background-color: var(--sf-button-bg);\n border-radius: var(--sf-border-radius-circle);\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n z-index: var(--sf-z-index-controls);\n color: white;\n border: none;\n font-size: var(--sf-font-size-md);\n transition: all var(--sf-transition-normal);\n text-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);\n box-shadow: 0 5px 15px rgba(0, 0, 0, 0.35);\n}\n\n/* Exit button hover state */\n.sf-player__exit-button:hover {\n transform: scale(1.1);\n background-color: var(--sf-button-hover-bg);\n}\n\n/* Show exit button on minimized player hover */\n.sf-player--minimized:hover .sf-player__exit-button {\n opacity: 1;\n}\n\n/* Saltfish logo */\n.sf-player__logo {\n position: absolute;\n bottom: var(--sf-spacing-xs);\n left: 0;\n right: 0;\n height: 18px;\n z-index: var(--sf-z-index-controls);\n opacity: 0.7;\n transition: opacity var(--sf-transition-normal);\n cursor: pointer;\n display: flex;\n justify-content: center;\n align-items: center;\n}\n\n.sf-player__logo svg {\n width: 55px;\n height: 20px;\n}\n\n/* Logo hover state */\n.sf-player:hover .sf-player__logo {\n opacity: 0.9;\n}\n\n/* Hide logo when minimized */\n.sf-player--minimized .sf-player__logo {\n display: none;\n} ";
3725
- const componentsVideoCss = "/* \n * Video component styles for the Saltfish playlist Player\n * Following BEM naming convention\n */\n\n/* Video container */\n.sf-video-container {\n position: relative;\n width: 100%;\n height: 100%;\n border-radius: var(--sf-border-radius-md);\n overflow: hidden;\n pointer-events: auto; /* Ensure clicks on video container are captured */\n background-color: #000; /* Fallback background for audio-only mode */\n}\n\n/* Audio fallback poster image */\n.sf-video-container--audio-fallback {\n background-size: cover;\n background-position: center;\n background-repeat: no-repeat;\n}\n\n/* Audio fallback overlay */\n.sf-audio-fallback-overlay {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n background: rgba(0, 0, 0, 0.6);\n z-index: 2;\n pointer-events: none;\n}\n\n.sf-audio-fallback-overlay__icon {\n width: 60px;\n height: 60px;\n margin-bottom: 16px;\n color: white;\n opacity: 0.9;\n}\n\n.sf-audio-fallback-overlay__text {\n color: white;\n font-size: 14px;\n text-align: center;\n padding: 0 20px;\n text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);\n opacity: 0.9;\n}\n\n/* Video element */\n.sf-video-container__video {\n width: 100%;\n height: 100%;\n object-fit: cover;\n /* Ensure video is visible on mobile */\n display: block;\n /* Add explicit positioning to ensure video is visible */\n position: relative;\n z-index: 1;\n}\n\n\n\n/* Mobile-specific video styles */\n@media (max-width: 768px) {\n \n .sf-video-container__video {\n /* Force video dimensions on mobile */\n width: 100% !important;\n height: 100% !important;\n object-fit: cover !important;\n /* Prevent video from being hidden */\n opacity: 1 !important;\n visibility: visible !important;\n /* Ensure video is above any potential overlays */\n z-index: 10 !important;\n position: relative !important;\n }\n \n /* Make controls more touch-friendly on mobile */\n .sf-video-container__controls {\n height: 5px; /* Thicker on mobile for easier touch */\n }\n \n .sf-video-container__mute-button {\n /* Use CSS variable for consistent sizing */\n min-width: var(--sf-mute-button-size) !important;\n min-height: var(--sf-mute-button-size) !important;\n opacity: 1; /* Always visible on mobile (no hover) */\n }\n \n .sf-video-container__cc-button {\n /* Use CSS variable for consistent sizing */\n min-width: var(--sf-cc-button-size) !important;\n min-height: var(--sf-cc-button-size) !important;\n opacity: 1; /* Always visible on mobile (no hover) */\n }\n}\n\n/* Touch device specific styles */\n@media (pointer: coarse) {\n .sf-video-container__controls:hover {\n height: 5px; /* Keep consistent height on touch devices */\n }\n \n .sf-video-container__mute-button {\n opacity: 1; /* Always show on touch devices */\n }\n \n .sf-video-container__cc-button {\n opacity: 1; /* Always show on touch devices */\n }\n \n .sf-video-container:hover .sf-video-container__mute-button {\n opacity: 1;\n }\n}\n\n/* Video in minimized state */\n.sf-player--minimized .sf-video-container {\n border-radius: var(--sf-border-radius-circle);\n cursor: pointer;\n z-index: var(--sf-z-index-base);\n width: 100%;\n height: 100%;\n}\n\n.sf-player--minimized .sf-video-container__video {\n border-radius: var(--sf-border-radius-circle);\n object-fit: cover;\n width: 100%;\n height: 100%;\n}\n\n/* Hide progress bar in minimized state */\n.sf-player--minimized .sf-video-container__controls {\n display: none !important;\n}\n\n/* Also hide mute button in minimized state */\n.sf-player--minimized .sf-video-container__mute-button {\n display: none !important;\n}\n\n/* Also hide CC button in minimized state */\n.sf-player--minimized .sf-video-container__cc-button {\n display: none !important;\n}\n\n/* Progress bar container */\n.sf-video-container__controls {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 3px;\n background-color: rgba(255, 255, 255, 0.25);\n z-index: var(--sf-z-index-controls);\n border-radius: var(--sf-border-radius-md) var(--sf-border-radius-md) 0 0;\n cursor: pointer;\n}\n\n/* Show slightly thicker progress bar on hover for better UX */\n.sf-video-container__controls:hover {\n height: 5px;\n}\n\n/* Progress indicator */\n.sf-video-container__progress {\n height: 100%;\n background: rgba(255, 255, 255, 0.8);\n width: 0%;\n transition: width 0.1s linear;\n border-radius: var(--sf-border-radius-md) 0 0 0;\n cursor: pointer;\n transform-origin: left;\n}\n\n/* Mute button */\n.sf-video-container__mute-button {\n position: absolute;\n top: calc(8px + var(--sf-minimize-button-size) + 6px);\n right: 6px;\n width: var(--sf-mute-button-size);\n height: var(--sf-mute-button-size);\n background-color: transparent;\n border-radius: var(--sf-border-radius-circle);\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n z-index: var(--sf-z-index-controls);\n color: white;\n border: none;\n font-size: calc(var(--sf-font-size-md) + 2px);\n transition: all var(--sf-transition-normal);\n text-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);\n opacity: 0;\n}\n\n/* Mute button hover state */\n.sf-video-container__mute-button:hover {\n transform: scale(1.1);\n}\n\n/* Show mute button on container hover */\n.sf-video-container:hover .sf-video-container__mute-button {\n opacity: 1;\n}\n\n/* CC button */\n.sf-video-container__cc-button {\n position: absolute;\n top: calc(8px + var(--sf-minimize-button-size) + 6px + var(--sf-mute-button-size) + 6px);\n right: 6px;\n width: var(--sf-cc-button-size);\n height: var(--sf-cc-button-size);\n background-color: transparent;\n border-radius: 50%; /* Ensure perfect circle */\n padding: 0; /* Remove default button padding */\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n z-index: var(--sf-z-index-controls);\n color: white;\n border: none;\n font-size: calc(var(--sf-font-size-md) + 2px);\n transition: all var(--sf-transition-normal);\n text-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);\n opacity: 0;\n}\n\n.sf-video-container__cc-button svg {\n display: block;\n margin: auto;\n width: 60%;\n height: 60%;\n}\n\n/* CC button hover state */\n.sf-video-container__cc-button:hover {\n transform: scale(1.1);\n}\n\n/* Hide mute and cc buttons in autoplayBlocked and idleMode states */\n.sf-player--autoplayBlocked .sf-video-container__mute-button,\n.sf-player--autoplayBlocked .sf-video-container__cc-button,\n.sf-player--idleMode .sf-video-container__mute-button,\n.sf-player--idleMode .sf-video-container__cc-button {\n display: none !important;\n}\n\n/* Show mute and cc buttons on hover in playing/paused states */\n.sf-player--playing .sf-video-container:hover .sf-video-container__mute-button,\n.sf-player--playing .sf-video-container:hover .sf-video-container__cc-button,\n.sf-player--paused .sf-video-container:hover .sf-video-container__mute-button,\n.sf-player--paused .sf-video-container:hover .sf-video-container__cc-button {\n opacity: 1;\n} ";
3724
+ const componentsPlayerCss = "/* \n * Player component styles for the Saltfish playlist Player\n * Following BEM naming convention\n */\n\n/* Main player container */\n.sf-player {\n border-radius: var(--sf-border-radius-lg);\n box-shadow: 0 25px 50px rgba(0, 0, 0, 0.45), 0 10px 20px rgba(0, 0, 0, 0.3), 0 0 0 2px rgba(255, 255, 255, 0.08);\n transition: all var(--sf-transition-slow);\n position: relative;\n backdrop-filter: blur(10px);\n -webkit-backdrop-filter: blur(10px);\n opacity: 0;\n transition: opacity 0.3s ease-in-out;\n}\n\n/* Player visible state - show with fade in */\n.sf-player--visible {\n opacity: 1;\n}\n\n/* Dark gradient overlay at bottom of player */\n.sf-player::before {\n content: '';\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n height: 33.33%; /* One third of player height */\n background: linear-gradient(to top, rgba(0, 0, 0, 0.8) 0%, rgba(0, 0, 0, 0) 100%);\n pointer-events: none;\n z-index: var(--sf-z-index-overlay);\n border-radius: 8px;\n}\n\n/* Hide gradient overlay when minimized */\n.sf-player--minimized::before {\n display: none;\n}\n\n/* Full-size player state */\n.sf-player:not(.sf-player--minimized) {\n width: var(--sf-player-width);\n height: var(--sf-player-height);\n}\n\n/* Autoplay fallback state - ensure play button is visible */\n.sf-player--waiting-for-user-interaction .sf-controls-container__play-button {\n display: flex !important;\n opacity: 1 !important;\n visibility: visible !important;\n}\n\n/* Also show the center play button in autoplay fallback state */\n.sf-player--waiting-for-user-interaction .sf-player__center-play-button {\n display: flex !important;\n opacity: 1 !important;\n z-index: calc(var(--sf-z-index-controls) + 20) !important; /* Higher z-index to appear above overlay */\n}\n\n/* Make the autoplay fallback state more prominent to indicate need for interaction */\n.sf-player--waiting-for-user-interaction::after {\n content: '';\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.3);\n pointer-events: none;\n z-index: var(--sf-z-index-overlay);\n}\n\n/* Player state: minimized */\n.sf-player--minimized {\n /* Equal width and height are essential for maintaining a perfect circle when using border-radius: 50% */\n width: var(--sf-player-min-width);\n height: var(--sf-player-min-height);\n border-radius: var(--sf-border-radius-circle);\n box-shadow: 0 15px 30px rgba(0, 0, 0, 0.35), 0 5px 15px rgba(0, 0, 0, 0.25), 0 0 0 2px rgba(255, 255, 255, 0.08);\n cursor: pointer;\n /* Force overriding any inline styles that might be applied */\n max-width: var(--sf-player-min-width) !important;\n max-height: var(--sf-player-min-height) !important;\n min-width: var(--sf-player-min-width) !important;\n min-height: var(--sf-player-min-height) !important;\n}\n\n/* Hide controls when minimized */\n.sf-player--minimized .sf-controls-container {\n display: none;\n}\n\n/* Only show the minimize button when hovering on minimized player */\n.sf-player--minimized .sf-player__minimize-button {\n opacity: 0;\n}\n\n.sf-player--minimized:hover .sf-player__minimize-button {\n opacity: 1;\n}\n\n/* Player root element */\n#sf-player-root {\n position: fixed;\n z-index: var(--sf-z-index-player);\n}\n\n/* Fixed positioning classes */\n.sf-player-root--bottom-left {\n bottom: 20px;\n left: 20px;\n}\n\n.sf-player-root--bottom-right {\n bottom: 20px;\n right: 20px;\n}\n\n/* Player error message */\n.sf-player__error {\n padding: var(--sf-spacing-md);\n color: var(--sf-error-color);\n background-color: var(--sf-error-bg);\n border-radius: var(--sf-border-radius-md);\n margin: var(--sf-spacing-sm);\n font-size: var(--sf-font-size-sm);\n border-left: 4px solid var(--sf-error-color);\n}\n\n/* Minimize button */\n.sf-player__minimize-button {\n position: absolute;\n top: 8px;\n right: 6px;\n width: var(--sf-minimize-button-size);\n height: var(--sf-minimize-button-size);\n background-color: transparent;\n border-radius: var(--sf-border-radius-circle);\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n z-index: var(--sf-z-index-controls);\n color: white;\n border: none;\n font-size: calc(var(--sf-font-size-sm) + 4px);\n transition: all var(--sf-transition-normal);\n text-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);\n opacity: 0;\n}\n\n/* Minimize button hover state */\n.sf-player__minimize-button:hover {\n transform: scale(1.1);\n}\n\n/* Show minimize button on player hover */\n.sf-player:hover .sf-player__minimize-button {\n opacity: 1;\n}\n\n/* Mobile and touch device overrides for minimize button visibility */\n/* Ensure minimize button is always visible on touch devices, even when minimized */\n@media (pointer: coarse) {\n .sf-player__minimize-button {\n opacity: 1 !important;\n z-index: calc(var(--sf-z-index-controls) + 50) !important; /* Much higher z-index for touch devices */\n }\n \n .sf-player--minimized .sf-player__minimize-button {\n opacity: 1 !important;\n z-index: calc(var(--sf-z-index-controls) + 50) !important; /* Much higher z-index for touch devices */\n }\n}\n\n/* Ensure minimize button is always visible on mobile screens under 768px */\n@media (max-width: 768px) {\n .sf-player__minimize-button {\n opacity: 1 !important;\n z-index: calc(var(--sf-z-index-controls) + 50) !important; /* Much higher z-index for mobile */\n /* Position closer to top right corner on mobile */\n top: var(--sf-spacing-xs) !important; /* 4px from top instead of 16px */\n right: var(--sf-spacing-xs) !important; /* 4px from right instead of 12px */\n }\n \n .sf-player--minimized .sf-player__minimize-button {\n opacity: 1 !important;\n z-index: calc(var(--sf-z-index-controls) + 50) !important; /* Much higher z-index for mobile */\n /* Position closer to top right corner on mobile */\n top: var(--sf-spacing-xs) !important; /* 4px from top instead of 16px */\n right: var(--sf-spacing-xs) !important; /* 4px from right instead of 12px */\n }\n}\n\n/* Player title */\n.sf-player__title {\n position: absolute;\n top: var(--sf-spacing-md);\n left: var(--sf-spacing-md);\n color: var(--sf-text-color);\n font-size: var(--sf-font-size-md);\n font-weight: 600;\n z-index: var(--sf-z-index-controls);\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);\n max-width: 70%;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n/* Centered play/pause button overlay */\n.sf-player__center-play-button {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: var(--sf-play-button-size);\n height: var(--sf-play-button-size);\n background-color: rgba(0, 0, 0, 0.5);\n border-radius: var(--sf-border-radius-circle);\n display: none; /* Hidden by default */\n justify-content: center;\n align-items: center;\n z-index: calc(var(--sf-z-index-controls) + 10); /* Ensure higher z-index than other elements */\n color: white;\n border: none;\n font-size: var(--sf-control-button-size);\n cursor: pointer;\n transition: transform var(--sf-transition-normal), background-color var(--sf-transition-normal);\n backdrop-filter: blur(3px);\n -webkit-backdrop-filter: blur(3px);\n box-shadow: 0 15px 30px rgba(0, 0, 0, 0.5), 0 5px 15px rgba(0, 0, 0, 0.3), 0 0 0 2px rgba(255, 255, 255, 0.08);\n pointer-events: auto; /* Enable pointer events to capture clicks */\n}\n\n/* Center play button visible state */\n.sf-player__center-play-button--visible {\n display: flex !important;\n}\n\n/* Center play button prominent state (for autoplay blocked, idle mode) */\n.sf-player__center-play-button--prominent {\n opacity: 1 !important;\n pointer-events: auto !important;\n}\n\n/* Center play button hover state */\n.sf-player__center-play-button:hover {\n transform: translate(-50%, -50%) scale(1.1);\n background-color: rgba(0, 0, 0, 0.7);\n}\n\n/* Hide center play button in minimized state */\n.sf-player--minimized .sf-player__center-play-button {\n display: none !important;\n}\n\n/* Center play button with adjustment for 3+ buttons */\n.sf-player__center-play-button--with-many-buttons {\n transform: translate(-50%, -85%) !important; /* Move up by adjusting Y offset */\n}\n\n/* Exit button for minimized mode */\n.sf-player__exit-button {\n position: absolute;\n top: -22px; /* Position it above the player */\n right: 0;\n width: 20px;\n height: 20px;\n background-color: var(--sf-button-bg);\n border-radius: var(--sf-border-radius-circle);\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n z-index: var(--sf-z-index-controls);\n color: white;\n border: none;\n font-size: var(--sf-font-size-md);\n transition: all var(--sf-transition-normal);\n text-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);\n box-shadow: 0 5px 15px rgba(0, 0, 0, 0.35);\n}\n\n/* Exit button hover state */\n.sf-player__exit-button:hover {\n transform: scale(1.1);\n background-color: var(--sf-button-hover-bg);\n}\n\n/* Show exit button on minimized player hover */\n.sf-player--minimized:hover .sf-player__exit-button {\n opacity: 1;\n}\n\n/* Saltfish logo */\n.sf-player__logo {\n position: absolute;\n bottom: var(--sf-spacing-xs);\n left: 0;\n right: 0;\n height: 18px;\n z-index: var(--sf-z-index-controls);\n opacity: 0.7;\n transition: opacity var(--sf-transition-normal);\n cursor: pointer;\n display: flex;\n justify-content: center;\n align-items: center;\n}\n\n.sf-player__logo svg {\n width: 55px;\n height: 20px;\n}\n\n/* Logo hover state */\n.sf-player:hover .sf-player__logo {\n opacity: 0.9;\n}\n\n/* Hide logo when minimized */\n.sf-player--minimized .sf-player__logo {\n display: none;\n} ";
3725
+ const componentsVideoCss = "/* \n * Video component styles for the Saltfish playlist Player\n * Following BEM naming convention\n */\n\n/* Video container */\n.sf-video-container {\n position: relative;\n width: 100%;\n height: 100%;\n border-radius: var(--sf-border-radius-md);\n overflow: hidden;\n pointer-events: auto; /* Ensure clicks on video container are captured */\n background-color: #000; /* Fallback background for audio-only mode */\n}\n\n/* Audio fallback poster image */\n.sf-video-container--audio-fallback {\n background-size: cover;\n background-position: center;\n background-repeat: no-repeat;\n background-image: var(--sf-audio-poster-url, none);\n}\n\n/* Audio fallback overlay */\n.sf-audio-fallback-overlay {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n background: rgba(0, 0, 0, 0.6);\n z-index: 2;\n pointer-events: none;\n}\n\n.sf-audio-fallback-overlay__icon {\n width: 60px;\n height: 60px;\n margin-bottom: 16px;\n color: white;\n opacity: 0.9;\n}\n\n.sf-audio-fallback-overlay__text {\n color: white;\n font-size: 14px;\n text-align: center;\n padding: 0 20px;\n text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);\n opacity: 0.9;\n}\n\n/* Video element */\n.sf-video-container__video {\n width: 100%;\n height: 100%;\n object-fit: cover;\n /* Ensure video is visible on mobile */\n display: block;\n /* Add explicit positioning to ensure video is visible */\n position: relative;\n z-index: 1;\n}\n\n/* Video blur effect (for end-of-video transitions) */\n.sf-video-container__video--blurred {\n filter: blur(3px);\n transition: filter 0.3s ease-out;\n}\n\n\n\n/* Mobile-specific video styles */\n@media (max-width: 768px) {\n \n .sf-video-container__video {\n /* Force video dimensions on mobile */\n width: 100% !important;\n height: 100% !important;\n object-fit: cover !important;\n /* Prevent video from being hidden */\n opacity: 1 !important;\n visibility: visible !important;\n /* Ensure video is above any potential overlays */\n z-index: 10 !important;\n position: relative !important;\n }\n \n /* Make controls more touch-friendly on mobile */\n .sf-video-container__controls {\n height: 5px; /* Thicker on mobile for easier touch */\n }\n \n .sf-video-container__mute-button {\n /* Use CSS variable for consistent sizing */\n min-width: var(--sf-mute-button-size) !important;\n min-height: var(--sf-mute-button-size) !important;\n opacity: 1; /* Always visible on mobile (no hover) */\n }\n \n .sf-video-container__cc-button {\n /* Use CSS variable for consistent sizing */\n min-width: var(--sf-cc-button-size) !important;\n min-height: var(--sf-cc-button-size) !important;\n opacity: 1; /* Always visible on mobile (no hover) */\n }\n}\n\n/* Touch device specific styles */\n@media (pointer: coarse) {\n .sf-video-container__controls:hover {\n height: 5px; /* Keep consistent height on touch devices */\n }\n \n .sf-video-container__mute-button {\n opacity: 1; /* Always show on touch devices */\n }\n \n .sf-video-container__cc-button {\n opacity: 1; /* Always show on touch devices */\n }\n \n .sf-video-container:hover .sf-video-container__mute-button {\n opacity: 1;\n }\n}\n\n/* Video in minimized state */\n.sf-player--minimized .sf-video-container {\n border-radius: var(--sf-border-radius-circle);\n cursor: pointer;\n z-index: var(--sf-z-index-base);\n width: 100%;\n height: 100%;\n}\n\n.sf-player--minimized .sf-video-container__video {\n border-radius: var(--sf-border-radius-circle);\n object-fit: cover;\n width: 100%;\n height: 100%;\n}\n\n/* Hide progress bar in minimized state */\n.sf-player--minimized .sf-video-container__controls {\n display: none !important;\n}\n\n/* Also hide mute button in minimized state */\n.sf-player--minimized .sf-video-container__mute-button {\n display: none !important;\n}\n\n/* Also hide CC button in minimized state */\n.sf-player--minimized .sf-video-container__cc-button {\n display: none !important;\n}\n\n/* Progress bar container */\n.sf-video-container__controls {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 3px;\n background-color: rgba(255, 255, 255, 0.25);\n z-index: var(--sf-z-index-controls);\n border-radius: var(--sf-border-radius-md) var(--sf-border-radius-md) 0 0;\n cursor: pointer;\n}\n\n/* Show slightly thicker progress bar on hover for better UX */\n.sf-video-container__controls:hover {\n height: 5px;\n}\n\n/* Progress indicator */\n.sf-video-container__progress {\n height: 100%;\n background: rgba(255, 255, 255, 0.8);\n width: 0%;\n transition: width 0.1s linear;\n border-radius: var(--sf-border-radius-md) 0 0 0;\n cursor: pointer;\n transform-origin: left;\n}\n\n/* Mute button */\n.sf-video-container__mute-button {\n position: absolute;\n top: calc(8px + var(--sf-minimize-button-size) + 6px);\n right: 6px;\n width: var(--sf-mute-button-size);\n height: var(--sf-mute-button-size);\n background-color: transparent;\n border-radius: var(--sf-border-radius-circle);\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n z-index: var(--sf-z-index-controls);\n color: white;\n border: none;\n font-size: calc(var(--sf-font-size-md) + 2px);\n transition: all var(--sf-transition-normal);\n text-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);\n opacity: 0;\n}\n\n/* Mute button hover state */\n.sf-video-container__mute-button:hover {\n transform: scale(1.1);\n}\n\n/* Show mute button on container hover */\n.sf-video-container:hover .sf-video-container__mute-button {\n opacity: 1;\n}\n\n/* CC button */\n.sf-video-container__cc-button {\n position: absolute;\n top: calc(8px + var(--sf-minimize-button-size) + 6px + var(--sf-mute-button-size) + 6px);\n right: 6px;\n width: var(--sf-cc-button-size);\n height: var(--sf-cc-button-size);\n background-color: transparent;\n border-radius: 50%; /* Ensure perfect circle */\n padding: 0; /* Remove default button padding */\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n z-index: var(--sf-z-index-controls);\n color: white;\n border: none;\n font-size: calc(var(--sf-font-size-md) + 2px);\n transition: all var(--sf-transition-normal);\n text-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);\n opacity: 0;\n}\n\n.sf-video-container__cc-button svg {\n display: block;\n margin: auto;\n width: 60%;\n height: 60%;\n}\n\n/* CC button hover state */\n.sf-video-container__cc-button:hover {\n transform: scale(1.1);\n}\n\n/* Hide mute and cc buttons in autoplayBlocked and idleMode states */\n.sf-player--autoplayBlocked .sf-video-container__mute-button,\n.sf-player--autoplayBlocked .sf-video-container__cc-button,\n.sf-player--idleMode .sf-video-container__mute-button,\n.sf-player--idleMode .sf-video-container__cc-button {\n display: none !important;\n}\n\n/* Show mute and cc buttons on hover in playing/paused states */\n.sf-player--playing .sf-video-container:hover .sf-video-container__mute-button,\n.sf-player--playing .sf-video-container:hover .sf-video-container__cc-button,\n.sf-player--paused .sf-video-container:hover .sf-video-container__mute-button,\n.sf-player--paused .sf-video-container:hover .sf-video-container__cc-button {\n opacity: 1;\n} ";
3726
3726
  const componentsControlsCss = "/* \n * Controls component styles for the Saltfish playlist Player\n * Following BEM naming convention\n */\n\n/* Main controls container */\n.sf-controls-container {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n display: flex;\n justify-content: center;\n align-items: center;\n background-color: transparent;\n z-index: var(--sf-z-index-controls);\n pointer-events: auto;\n}\n\n/* Play button */\n.sf-controls-container__play-button {\n background-color: rgba(0, 0, 0, 0.6);\n border: none;\n color: var(--sf-text-color);\n font-size: var(--sf-control-button-size);\n cursor: pointer;\n width: var(--sf-play-button-size);\n height: var(--sf-play-button-size);\n border-radius: var(--sf-border-radius-circle);\n display: flex;\n justify-content: center;\n align-items: center;\n transition: transform var(--sf-transition-normal), background-color var(--sf-transition-normal);\n padding-left: 4px; /* Optical centering for play icon */\n}\n\n/* Button hover state */\n.sf-controls-container__play-button:hover {\n background-color: rgba(0, 0, 0, 0.4);\n transform: scale(1.1);\n}\n\n/* Button container for interactive buttons */\n.sf-controls-container__buttons {\n display: flex;\n justify-content: center;\n align-items: center;\n gap: var(--sf-spacing-md);\n}\n\n/* Interactive button */\n.sf-controls-container__interactive-button {\n background-color: rgba(255, 255, 255, 0.2);\n backdrop-filter: blur(8px);\n -webkit-backdrop-filter: blur(8px);\n color: white;\n border: none;\n border-radius: var(--sf-border-radius-md);\n padding: var(--sf-spacing-xs) var(--sf-spacing-md);\n font-size: var(--sf-font-size-sm);\n cursor: pointer;\n transition: background-color var(--sf-transition-normal), transform var(--sf-transition-fast);\n box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);\n}\n\n.sf-controls-container__interactive-button:hover {\n background-color: rgba(255, 255, 255, 0.3);\n transform: translateY(-2px);\n}\n\n/* \n * Choice buttons container and buttons - positioned inside player\n */\n\n/* Choice buttons container - positioned inside the player at the bottom */\n.sf-choice-buttons-container {\n position: absolute;\n bottom: var(--sf-spacing-xl);\n left: 50%;\n transform: translateX(-50%);\n width: calc(100% - var(--sf-spacing-lg));\n max-width: calc(100% - var(--sf-spacing-lg));\n z-index: calc(var(--sf-z-index-controls) + 1);\n gap: var(--sf-spacing-sm);\n pointer-events: auto;\n align-items: center;\n display: flex;\n flex-direction: column;\n /* Ensure container is fully transparent */\n background: transparent;\n border: none;\n outline: none;\n}\n\n/* Choice buttons container with scrolling for 4+ buttons */\n.sf-choice-buttons-container--scrollable {\n max-height: 132px; /* Show ~3 buttons with gaps (3 * 36px button + 3 * 8px gap) */\n overflow-y: auto;\n overflow-x: hidden;\n scrollbar-width: thin; /* Show thin scrollbar in Firefox */\n scrollbar-color: rgba(255, 255, 255, 0.2) transparent; /* More transparent Firefox scrollbar */\n scroll-behavior: smooth;\n /* Important: Use block display for scrollable container */\n display: block !important;\n}\n\n/* Style webkit scrollbar to be visible but subtle */\n.sf-choice-buttons-container--scrollable::-webkit-scrollbar {\n width: 4px;\n}\n\n.sf-choice-buttons-container--scrollable::-webkit-scrollbar-track {\n background: transparent;\n}\n\n.sf-choice-buttons-container--scrollable::-webkit-scrollbar-thumb {\n background: rgba(255, 255, 255, 0.2); /* More transparent */\n border-radius: 2px;\n}\n\n.sf-choice-buttons-container--scrollable::-webkit-scrollbar-thumb:hover {\n background: rgba(255, 255, 255, 0.4); /* Still subtle on hover */\n}\n\n/* Removed fade gradient to keep container fully transparent */\n\n/* Choice button styles - solid rounded buttons matching the image */\n.sf-choice-button {\n width: 100%;\n max-width: none;\n background: rgba(0, 0, 0, 4);\n backdrop-filter: blur(8px);\n -webkit-backdrop-filter: blur(8px);\n color: white;\n border: none;\n border-radius: 24px; /* More rounded for pill shape */\n padding: var(--sf-spacing-md) var(--sf-spacing-md);\n font-size: 12px;\n cursor: pointer;\n transition: all 0.2s ease;\n text-align: center;\n outline: none;\n font-family: inherit;\n margin-bottom: 0;\n position: relative;\n overflow: hidden;\n opacity: 0; /* Start hidden for animation */\n pointer-events: none;\n /* Animation will be triggered by JavaScript when video reaches 90% */\n}\n\n/* Add margin between buttons in scrollable container */\n.sf-choice-buttons-container--scrollable .sf-choice-button {\n margin-bottom: var(--sf-spacing-sm);\n}\n\n/* Remove margin from last button */\n.sf-choice-buttons-container--scrollable .sf-choice-button:last-child {\n margin-bottom: 0;\n}\n\n/* Hover state for buttons */\n.sf-choice-button:hover {\n background: rgba(0, 0, 0, 0.9);\n transform: translateY(-2px);\n box-shadow: 0 6px 16px rgba(0, 0, 0, 0.8);\n}\n\n/* Active state for all buttons */\n.sf-choice-button:active {\n transform: translateY(0) scale(0.98);\n transition: all 0.1s ease;\n}\n\n/* Remove specific action type styling - use consistent dark buttons */\n.sf-choice-button--goto,\n.sf-choice-button--url,\n.sf-choice-button--next,\n.sf-choice-button--dom,\n.sf-choice-button--function {\n background: rgba(0, 0, 0, 0.4);\n border: none;\n}\n\n.sf-choice-button--goto:hover,\n.sf-choice-button--url:hover,\n.sf-choice-button--next:hover,\n.sf-choice-button--dom:hover,\n.sf-choice-button--function:hover {\n background: rgba(0, 0, 0, 0.9);\n transform: translateY(-2px);\n box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4);\n}\n\n/* Hide choice buttons in minimized state */\n.sf-player--minimized .sf-choice-buttons-container {\n display: none;\n}\n\n/* Hide choice buttons in autoplayBlocked and idleMode states */\n.sf-player--autoplayBlocked .sf-choice-buttons-container,\n.sf-player--idleMode .sf-choice-buttons-container {\n display: none !important;\n}\n\n/* Smaller mobile screens - maintain same layout but with tighter spacing */\n@media (max-width: 480px) {\n .sf-choice-buttons-container {\n bottom: var(--sf-spacing-sm);\n width: calc(100% - var(--sf-spacing-md));\n max-width: calc(100% - var(--sf-spacing-md));\n gap: calc(var(--sf-spacing-xs) + 2px);\n }\n \n .sf-choice-button {\n padding: var(--sf-spacing-sm) var(--sf-spacing-sm);\n font-size: 11px;\n border-radius: 20px;\n }\n}\n\n/* Touch device specific adjustments */\n@media (pointer: coarse) {\n .sf-choice-buttons-container {\n gap: var(--sf-spacing-sm);\n }\n \n .sf-choice-button {\n min-height: 44px; /* Apple's recommended minimum touch target size */\n padding: var(--sf-spacing-md) var(--sf-spacing-md);\n }\n}\n\n/* Button fade-in animation */\n@keyframes buttonFadeIn {\n from {\n opacity: 0;\n transform: translateY(10px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n/* Animation class that will be added by JavaScript at 90% video progress */\n.sf-choice-button.sf-show-button {\n animation: buttonFadeIn 0.3s ease-out forwards;\n pointer-events: auto;\n}\n\n/* Staggered animation delays for multiple buttons when shown */\n.sf-choice-button.sf-show-button:nth-child(1) {\n animation-delay: 0s;\n}\n\n.sf-choice-button.sf-show-button:nth-child(2) {\n animation-delay: 0.15s;\n}\n\n.sf-choice-button.sf-show-button:nth-child(3) {\n animation-delay: 0.3s;\n}\n\n.sf-choice-button.sf-show-button:nth-child(4) {\n animation-delay: 0.45s;\n}\n\n.sf-choice-button.sf-show-button:nth-child(5) {\n animation-delay: 0.6s;\n}\n\n.sf-choice-button.sf-show-button:nth-child(6) {\n animation-delay: 0.75s;\n}\n\n.sf-choice-button.sf-show-button:nth-child(7) {\n animation-delay: 0.9s;\n}\n\n.sf-choice-button.sf-show-button:nth-child(8) {\n animation-delay: 1.05s;\n}\n\n/* Scroll indicator arrow */\n.sf-scroll-indicator {\n position: absolute;\n bottom: calc(var(--sf-spacing-xl) - 25px);\n left: 50%;\n transform: translateX(-50%);\n color: rgba(255, 255, 255, 0.6);\n font-size: 20px;\n pointer-events: none;\n z-index: calc(var(--sf-z-index-controls) + 2);\n transition: opacity 0.3s ease;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n opacity: 0; /* Start hidden */\n}\n\n/* Show scroll indicator with animation - appears after buttons */\n.sf-scroll-indicator.sf-show-scroll-indicator {\n animation: scrollIndicatorFadeIn 0.4s ease-out 1.2s forwards, bounceArrow 1.5s ease-in-out 1.6s infinite;\n}\n\n/* Hide arrow when scrolled to bottom */\n.sf-scroll-indicator--hidden {\n opacity: 0;\n}\n\n/* Fade in animation for scroll indicator */\n@keyframes scrollIndicatorFadeIn {\n from {\n opacity: 0;\n transform: translateX(-50%) translateY(-10px);\n }\n to {\n opacity: 1;\n transform: translateX(-50%) translateY(0);\n }\n}\n\n/* Bounce animation for the arrow */\n@keyframes bounceArrow {\n 0%, 100% {\n transform: translateX(-50%) translateY(0);\n }\n 50% {\n transform: translateX(-50%) translateY(5px);\n }\n}\n\n/* Hide scroll indicator in minimized state */\n.sf-player--minimized .sf-scroll-indicator {\n display: none;\n}\n\n/* Hide scroll indicator in autoplayBlocked and idleMode states */\n.sf-player--autoplayBlocked .sf-scroll-indicator,\n.sf-player--idleMode .sf-scroll-indicator {\n display: none !important;\n} ";
3727
3727
  const componentsTranscriptCss = "/* \n * Transcript component styles for the Saltfish Playlist Player\n * Following BEM naming convention\n */\n\n/* Transcript container */\n.sf-transcript {\n position: absolute;\n bottom: 40px; /* Above the progress bar */\n left: 0;\n right: 0;\n max-height: 200px;\n background: transparent;\n margin: 0 var(--sf-spacing-md);\n overflow: hidden;\n z-index: var(--sf-z-index-overlay);\n opacity: 0;\n transform: translateY(20px);\n transition: all 0.3s ease-out;\n pointer-events: none;\n}\n\n/* Visible state */\n.sf-transcript--visible {\n opacity: 1;\n transform: translateY(0);\n pointer-events: auto;\n}\n\n/* Transcript content */\n.sf-transcript__content {\n max-height: 180px;\n overflow: visible;\n padding: 0;\n}\n\n/* Webkit scrollbar styling */\n.sf-transcript__content::-webkit-scrollbar {\n width: 4px;\n}\n\n.sf-transcript__content::-webkit-scrollbar-track {\n background: transparent;\n}\n\n.sf-transcript__content::-webkit-scrollbar-thumb {\n background: rgba(255, 255, 255, 0.3);\n border-radius: 2px;\n}\n\n.sf-transcript__content::-webkit-scrollbar-thumb:hover {\n background: rgba(255, 255, 255, 0.5);\n}\n\n/* Word-by-word display container */\n.sf-transcript__word-container {\n display: flex;\n justify-content: center;\n align-items: center;\n gap: 0; /* Remove flex gap - spacing handled by margin on preview word */\n min-height: 60px;\n padding: 0;\n}\n\n/* Individual word styling */\n.sf-transcript__word {\n color: rgba(255, 255, 255, 0.9);\n font-size: var(--sf-font-size-lg);\n font-weight: 600;\n line-height: 1.2;\n transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);\n transform: translateY(0);\n filter: blur(0px);\n opacity: 1;\n text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);\n}\n\n/* Active word (currently speaking) */\n.sf-transcript__word--active {\n opacity: 1;\n filter: blur(0px);\n transform: translateY(0) scale(1);\n color: rgba(255, 255, 255, 1);\n font-weight: 600;\n}\n\n/* Preview word (clean, unblurred when shown) */\n.sf-transcript__word--preview {\n opacity: 0.8;\n filter: blur(0px);\n transform: translateY(0) scale(0.98);\n color: rgba(255, 255, 255, 0.8);\n font-weight: 400;\n margin-left: 0.2em; /* Minimal spacing - just enough for a natural gap */\n}\n\n/* Legacy segment styling (kept for compatibility) */\n.sf-transcript__segment {\n color: rgba(255, 255, 255);\n font-size: var(--sf-font-size-sm);\n line-height: 1.4;\n padding: var(--sf-spacing-xs) 0;\n cursor: pointer;\n transition: all 0.2s ease;\n padding-left: var(--sf-spacing-xs);\n padding-right: var(--sf-spacing-xs);\n}\n\n/* CC button styling removed - now handled via icon toggling */\n\n/* Mobile responsive styles */\n@media (max-width: 768px) {\n .sf-transcript {\n bottom: 50px; /* More space on mobile */\n margin: 0 var(--sf-spacing-sm);\n max-height: 150px; /* Smaller on mobile */\n }\n \n .sf-transcript__content {\n max-height: 130px;\n padding: var(--sf-spacing-sm);\n }\n \n .sf-transcript__word-container {\n min-height: 50px;\n padding: var(--sf-spacing-sm);\n gap: var(--sf-spacing-xs);\n }\n \n .sf-transcript__word {\n font-size: var(--sf-font-size-md);\n font-weight: 500;\n }\n \n .sf-transcript__segment {\n font-size: var(--sf-font-size-xs);\n padding: var(--sf-spacing-xs) var(--sf-spacing-sm);\n }\n}\n\n/* Touch device optimizations */\n@media (pointer: coarse) {\n .sf-transcript__segment {\n padding: var(--sf-spacing-sm) var(--sf-spacing-xs);\n min-height: 44px; /* Larger touch target */\n display: flex;\n align-items: center;\n }\n}\n\n/* Hide transcript in minimized state */\n.sf-player--minimized .sf-transcript {\n display: none !important;\n}\n\n/* Hide transcript in autoplayBlocked and idleMode states */\n.sf-player--autoplayBlocked .sf-transcript,\n.sf-player--idleMode .sf-transcript {\n display: none !important;\n}\n\n/* Animation for transcript appearance */\n@keyframes transcriptFadeIn {\n from {\n opacity: 0;\n transform: translateY(20px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n@keyframes transcriptFadeOut {\n from {\n opacity: 1;\n transform: translateY(0);\n }\n to {\n opacity: 0;\n transform: translateY(20px);\n }\n}";
3728
3728
  const componentsErrorCss = "/* \n * Error Display component styles for the Saltfish playlist Player\n * Clean and subtle full-widget error overlay\n */\n\n/* Error display overlay - covers full widget */\n.sf-error-display {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: rgba(0, 0, 0, 0.75);\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: calc(var(--sf-z-index-controls) + 30);\n border-radius: var(--sf-border-radius-lg);\n opacity: 0;\n transition: opacity var(--sf-transition-slow);\n pointer-events: auto;\n}\n\n/* Error display visible state */\n.sf-error-display--visible {\n opacity: 1;\n}\n\n/* Error content container */\n.sf-error-display__content {\n background-color: rgba(20, 20, 20, 0.9);\n border-radius: var(--sf-border-radius-md);\n padding: var(--sf-spacing-lg) var(--sf-spacing-xl);\n backdrop-filter: blur(10px);\n -webkit-backdrop-filter: blur(10px);\n border: 1px solid rgba(255, 255, 255, 0.08);\n transform: translateY(8px);\n transition: transform var(--sf-transition-slow);\n max-width: 85%;\n text-align: center;\n}\n\n/* Error content visible animation */\n.sf-error-display--visible .sf-error-display__content {\n transform: translateY(0);\n}\n\n/* Error message */\n.sf-error-display__message {\n color: rgba(255, 255, 255, 0.95);\n font-size: var(--sf-font-size-md);\n line-height: 1.5;\n margin: 0;\n text-align: center;\n font-weight: 500;\n}\n\n/* Mobile responsive adjustments */\n@media (max-width: 768px) {\n .sf-error-display__content {\n padding: var(--sf-spacing-md) var(--sf-spacing-lg);\n max-width: 90%;\n }\n\n .sf-error-display__message {\n font-size: var(--sf-font-size-sm);\n }\n}\n\n/* Minimized player state adjustments */\n.sf-player--minimized .sf-error-display__content {\n padding: var(--sf-spacing-sm) var(--sf-spacing-md);\n max-width: 80%;\n}\n\n.sf-player--minimized .sf-error-display__message {\n font-size: var(--sf-font-size-sm);\n font-weight: 400;\n}\n\n/* High contrast mode support */\n@media (prefers-contrast: high) {\n .sf-error-display__content {\n background-color: rgba(0, 0, 0, 0.95);\n border: 2px solid rgba(255, 255, 255, 0.3);\n }\n\n .sf-error-display__message {\n color: rgba(255, 255, 255, 0.95);\n }\n}\n\n/* Reduced motion support */\n@media (prefers-reduced-motion: reduce) {\n .sf-error-display,\n .sf-error-display__content {\n transition: none;\n }\n}";
3729
3729
  const componentsLoadingCss = "/**\n * Loading spinner styles\n */\n\n.sf-loading-spinner {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background-color: rgba(255, 255, 255, 0.95);\n backdrop-filter: blur(2px);\n z-index: 100;\n border-radius: 12px;\n opacity: 1 !important; /* Always visible when shown, overrides parent opacity */\n}\n\n.sf-loading-spinner__content {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n padding: 24px;\n}\n\n.sf-loading-spinner__icon {\n display: flex;\n align-items: center;\n justify-content: center;\n color: #000000;\n opacity: 0.9;\n}\n\n.sf-loading-spinner__icon svg {\n width: 60px;\n height: 60px;\n /* Remove the rotation animation since we now have internal SVG animation */\n}\n\n.sf-loading-spinner__text {\n color: #333333;\n font-size: 14px;\n font-weight: 500;\n text-align: center;\n opacity: 0.8;\n letter-spacing: 0.5px;\n}\n\n/* CSS keyframe animation removed - using SVG animateTransform instead */\n\n/* Responsive adjustments */\n@media (max-width: 480px) {\n .sf-loading-spinner__content {\n padding: 20px;\n gap: 10px;\n }\n \n .sf-loading-spinner__icon svg {\n width: 50px;\n height: 50px;\n }\n \n .sf-loading-spinner__text {\n font-size: 13px;\n }\n}";
3730
+ const componentsCursorCss = "/*\n * Cursor animation component styles for the Saltfish playlist Player\n * CSP-compliant: All styles use CSS classes and CSS custom properties instead of inline styles\n */\n\n/* Base cursor element */\n.sf-cursor {\n position: fixed;\n top: 0;\n left: 0;\n width: 36px;\n height: 36px;\n z-index: 9999999;\n pointer-events: none;\n display: none;\n will-change: transform;\n transform: var(--sf-cursor-transform, translate(0, 0));\n opacity: var(--sf-cursor-opacity, 1);\n}\n\n.sf-cursor--visible {\n display: block;\n}\n\n/* Selection element */\n.sf-selection {\n position: fixed;\n pointer-events: none;\n display: none;\n z-index: 9999998;\n border: 2px solid #ff7614;\n background: rgba(255, 118, 20, 0.1);\n left: var(--sf-selection-left, 0);\n top: var(--sf-selection-top, 0);\n width: var(--sf-selection-width, 0);\n height: var(--sf-selection-height, 0);\n}\n\n.sf-selection--visible {\n display: block;\n}\n\n/* Flashlight overlay */\n.sf-flashlight-overlay {\n position: fixed;\n top: 0;\n left: 0;\n width: 100vw;\n height: 100vh;\n pointer-events: none;\n z-index: 999997;\n display: none;\n background: var(--sf-flashlight-bg, radial-gradient(circle 150px at 50% 50%, transparent 0%, rgba(0, 0, 0, 0.4) 100%));\n clip-path: var(--sf-flashlight-clip, none);\n}\n\n.sf-flashlight-overlay--visible {\n display: block;\n}\n";
3730
3731
  const animationsTransitionsCss = "/* \n * Transitions and animations for Saltfish playlist Player\n */\n\n/* Fade in animation */\n@keyframes sf-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n.sf-fade-in {\n animation: sf-fade-in 0.3s ease-in-out forwards;\n}\n\n/* Slide in from bottom animation */\n@keyframes sf-slide-in-bottom {\n from { transform: translateY(100%); opacity: 0; }\n to { transform: translateY(0); opacity: 1; }\n}\n\n.sf-slide-in-bottom {\n animation: sf-slide-in-bottom 0.3s cubic-bezier(0.25, 0.8, 0.25, 1) forwards;\n}\n\n/* Slide in from right animation */\n@keyframes sf-slide-in-right {\n from { transform: translateX(100%); opacity: 0; }\n to { transform: translateX(0); opacity: 1; }\n}\n\n.sf-slide-in-right {\n animation: sf-slide-in-right 0.3s cubic-bezier(0.25, 0.8, 0.25, 1) forwards;\n}\n\n/* Scale in animation */\n@keyframes sf-scale-in {\n from { transform: scale(0.8); opacity: 0; }\n to { transform: scale(1); opacity: 1; }\n}\n\n.sf-scale-in {\n animation: sf-scale-in 0.3s cubic-bezier(0.25, 0.8, 0.25, 1) forwards;\n}\n\n/* Scale out animation */\n@keyframes sf-scale-out {\n from { transform: scale(1); opacity: 1; }\n to { transform: scale(0.8); opacity: 0; }\n}\n\n.sf-scale-out {\n animation: sf-scale-out 0.3s cubic-bezier(0.25, 0.8, 0.25, 1) forwards;\n} ";
3731
3732
  const getPlayerStyles = () => `
3732
3733
  ${baseResetCss}
3733
3734
  ${baseVariablesCss}
3734
-
3735
+
3735
3736
  ${componentsPlayerCss}
3736
3737
  ${componentsVideoCss}
3737
3738
  ${componentsControlsCss}
3738
3739
  ${componentsTranscriptCss}
3739
3740
  ${componentsErrorCss}
3740
3741
  ${componentsLoadingCss}
3741
-
3742
+ ${componentsCursorCss}
3743
+
3742
3744
  ${animationsTransitionsCss}
3743
3745
  `;
3744
3746
  class ShadowDOMManager {
@@ -3759,9 +3761,15 @@ class ShadowDOMManager {
3759
3761
  this.container.appendChild(document.createTextNode("​"));
3760
3762
  document.body.appendChild(this.container);
3761
3763
  this.shadowRoot = this.container.attachShadow({ mode: "open" });
3762
- this.styleElement = document.createElement("style");
3763
- this.styleElement.textContent = this.getBaseStyles();
3764
- this.shadowRoot.appendChild(this.styleElement);
3764
+ try {
3765
+ const sheet = new CSSStyleSheet();
3766
+ sheet.replaceSync(this.getBaseStyles());
3767
+ this.shadowRoot.adoptedStyleSheets = [sheet];
3768
+ } catch (error2) {
3769
+ this.styleElement = document.createElement("style");
3770
+ this.styleElement.textContent = this.getBaseStyles();
3771
+ this.shadowRoot.appendChild(this.styleElement);
3772
+ }
3765
3773
  const rootElement = document.createElement("div");
3766
3774
  rootElement.id = "sf-player-root";
3767
3775
  this.shadowRoot.appendChild(rootElement);
@@ -3786,10 +3794,22 @@ class ShadowDOMManager {
3786
3794
  * Adds a stylesheet to the shadow DOM
3787
3795
  */
3788
3796
  addStyles(styles) {
3789
- if (!this.styleElement) {
3797
+ if (!this.shadowRoot) {
3790
3798
  return;
3791
3799
  }
3792
- this.styleElement.textContent += styles;
3800
+ if (this.shadowRoot.adoptedStyleSheets && this.shadowRoot.adoptedStyleSheets.length > 0) {
3801
+ try {
3802
+ const existingSheet = this.shadowRoot.adoptedStyleSheets[0];
3803
+ const currentRules = Array.from(existingSheet.cssRules).map((rule) => rule.cssText).join("\n");
3804
+ existingSheet.replaceSync(currentRules + "\n" + styles);
3805
+ return;
3806
+ } catch (error2) {
3807
+ console.warn("ShadowDOMManager: Failed to append to adoptedStyleSheet:", error2);
3808
+ }
3809
+ }
3810
+ if (this.styleElement) {
3811
+ this.styleElement.textContent += styles;
3812
+ }
3793
3813
  }
3794
3814
  /**
3795
3815
  * Removes the shadow DOM container
@@ -4217,7 +4237,7 @@ const _TranscriptManager = class _TranscriptManager {
4217
4237
  }
4218
4238
  this.transcriptContainer = document.createElement("div");
4219
4239
  this.transcriptContainer.className = "sf-transcript";
4220
- this.transcriptContainer.style.display = "none";
4240
+ this.transcriptContainer.classList.add("sf-hidden");
4221
4241
  this.transcriptContent = document.createElement("div");
4222
4242
  this.transcriptContent.className = "sf-transcript__content";
4223
4243
  if (this.transcriptContainer && this.transcriptContent) {
@@ -4240,7 +4260,7 @@ const _TranscriptManager = class _TranscriptManager {
4240
4260
  showTranscript() {
4241
4261
  log(`TranscriptManager: showTranscript called, container exists: ${!!this.transcriptContainer}`);
4242
4262
  if (this.transcriptContainer) {
4243
- this.transcriptContainer.style.display = "block";
4263
+ this.transcriptContainer.classList.remove("sf-hidden");
4244
4264
  this.transcriptContainer.classList.add("sf-transcript--visible");
4245
4265
  }
4246
4266
  }
@@ -4250,7 +4270,7 @@ const _TranscriptManager = class _TranscriptManager {
4250
4270
  hideTranscript() {
4251
4271
  log(`TranscriptManager: hideTranscript called, container exists: ${!!this.transcriptContainer}`);
4252
4272
  if (this.transcriptContainer) {
4253
- this.transcriptContainer.style.display = "none";
4273
+ this.transcriptContainer.classList.add("sf-hidden");
4254
4274
  this.transcriptContainer.classList.remove("sf-transcript--visible");
4255
4275
  }
4256
4276
  }
@@ -4263,10 +4283,10 @@ const _TranscriptManager = class _TranscriptManager {
4263
4283
  return;
4264
4284
  }
4265
4285
  if (hasTranscript) {
4266
- this.ccButton.style.display = "block";
4286
+ this.ccButton.classList.remove("sf-hidden");
4267
4287
  (_a = this.videoManager) == null ? void 0 : _a.updateCCButtonIcon(this.isVisible);
4268
4288
  } else {
4269
- this.ccButton.style.display = "none";
4289
+ this.ccButton.classList.add("sf-hidden");
4270
4290
  }
4271
4291
  }
4272
4292
  /**
@@ -4391,8 +4411,8 @@ class MobilePlaybackHandler extends DevicePlaybackHandler {
4391
4411
  }
4392
4412
  configureControlElement(element) {
4393
4413
  const config = this.getControlsConfig();
4394
- element.style.minWidth = config.buttonMinSize.width;
4395
- element.style.minHeight = config.buttonMinSize.height;
4414
+ element.style.setProperty("min-width", config.buttonMinSize.width);
4415
+ element.style.setProperty("min-height", config.buttonMinSize.height);
4396
4416
  }
4397
4417
  async handlePlayAttempt(video, hasUserInteracted) {
4398
4418
  const store = useSaltfishStore.getState();
@@ -4547,7 +4567,7 @@ class VideoControlsUI {
4547
4567
  var _a;
4548
4568
  this.lastTimeupdateEvent = Date.now();
4549
4569
  if (this.progressBar && ((_a = this.videoElement) == null ? void 0 : _a.paused) === false) {
4550
- this.progressBar.style.transition = "width 0.1s linear";
4570
+ this.progressBar.style.setProperty("transition", "width 0.1s linear");
4551
4571
  }
4552
4572
  if (this.videoElement && !this.buttonsShownAt90Percent && this.videoElement.duration > 0) {
4553
4573
  const progress = this.videoElement.currentTime / this.videoElement.duration;
@@ -4562,7 +4582,7 @@ class VideoControlsUI {
4562
4582
  */
4563
4583
  __publicField(this, "handleSeeking", () => {
4564
4584
  if (this.progressBar) {
4565
- this.progressBar.style.transition = "none";
4585
+ this.progressBar.style.setProperty("transition", "none");
4566
4586
  this.updateProgress();
4567
4587
  }
4568
4588
  });
@@ -4574,7 +4594,7 @@ class VideoControlsUI {
4574
4594
  if (!this.videoElement.paused) {
4575
4595
  this.updateProgress();
4576
4596
  void this.progressBar.offsetWidth;
4577
- this.progressBar.style.transition = "width 0.1s linear";
4597
+ this.progressBar.style.setProperty("transition", "width 0.1s linear");
4578
4598
  }
4579
4599
  }
4580
4600
  });
@@ -4588,8 +4608,8 @@ class VideoControlsUI {
4588
4608
  const clickPosition = (event.clientX - rect.left) / rect.width;
4589
4609
  const seekTime = this.videoElement.duration * clickPosition;
4590
4610
  if (this.progressBar) {
4591
- this.progressBar.style.transition = "none";
4592
- this.progressBar.style.width = `${clickPosition * 100}%`;
4611
+ this.progressBar.style.setProperty("transition", "none");
4612
+ this.progressBar.style.setProperty("width", `${clickPosition * 100}%`);
4593
4613
  }
4594
4614
  this.callbacks.onSeek(seekTime);
4595
4615
  event.preventDefault();
@@ -4681,8 +4701,8 @@ class VideoControlsUI {
4681
4701
  this.buttonsShownAt90Percent = false;
4682
4702
  this.lastTimeupdateEvent = 0;
4683
4703
  if (this.progressBar) {
4684
- this.progressBar.style.transition = "none";
4685
- this.progressBar.style.width = "0%";
4704
+ this.progressBar.style.setProperty("transition", "none");
4705
+ this.progressBar.style.setProperty("width", "0%");
4686
4706
  }
4687
4707
  }
4688
4708
  /**
@@ -4759,8 +4779,8 @@ class VideoControlsUI {
4759
4779
  updateProgressImmediate(currentTime, duration) {
4760
4780
  if (!this.progressBar || duration <= 0) return;
4761
4781
  const percent = currentTime / duration * 100;
4762
- this.progressBar.style.transition = "none";
4763
- this.progressBar.style.width = `${percent}%`;
4782
+ this.progressBar.style.setProperty("transition", "none");
4783
+ this.progressBar.style.setProperty("width", `${percent}%`);
4764
4784
  }
4765
4785
  /**
4766
4786
  * Updates the progress bar based on current playback position
@@ -4771,15 +4791,15 @@ class VideoControlsUI {
4771
4791
  const duration = this.videoElement.duration || 0;
4772
4792
  if (duration > 0) {
4773
4793
  const percent = currentTime / duration * 100;
4774
- const wasTransitioning = this.progressBar.style.transition !== "none";
4794
+ const wasTransitioning = this.progressBar.style.getPropertyValue("transition") !== "none";
4775
4795
  const timeSinceLastUpdate = Date.now() - this.lastTimeupdateEvent;
4776
- this.progressBar.style.transition = "none";
4777
- this.progressBar.style.width = `${percent}%`;
4796
+ this.progressBar.style.setProperty("transition", "none");
4797
+ this.progressBar.style.setProperty("width", `${percent}%`);
4778
4798
  void this.progressBar.offsetWidth;
4779
4799
  if (this.videoElement.ended) {
4780
- this.progressBar.style.transition = "width 0.2s ease-out";
4800
+ this.progressBar.style.setProperty("transition", "width 0.2s ease-out");
4781
4801
  } else if (!this.videoElement.paused && !this.videoElement.seeking && wasTransitioning && timeSinceLastUpdate < 500) {
4782
- this.progressBar.style.transition = "width 0.1s linear";
4802
+ this.progressBar.style.setProperty("transition", "width 0.1s linear");
4783
4803
  }
4784
4804
  }
4785
4805
  }
@@ -4856,16 +4876,16 @@ class VideoControlsUI {
4856
4876
  const playerElement = this.controlsElement.closest(".sf-player");
4857
4877
  const isMinimized = playerElement == null ? void 0 : playerElement.classList.contains("sf-player--minimized");
4858
4878
  if (!isMinimized) {
4859
- this.controlsElement.style.display = "block";
4879
+ this.controlsElement.classList.remove("sf-hidden");
4860
4880
  } else {
4861
- this.controlsElement.style.display = "";
4881
+ this.controlsElement.classList.remove("sf-hidden");
4862
4882
  }
4863
4883
  }
4864
4884
  if (this.progressBar) {
4865
- this.progressBar.style.transition = "none";
4866
- this.progressBar.style.width = "0%";
4885
+ this.progressBar.style.setProperty("transition", "none");
4886
+ this.progressBar.style.setProperty("width", "0%");
4867
4887
  void this.progressBar.offsetWidth;
4868
- this.progressBar.style.transition = "width 0.1s linear";
4888
+ this.progressBar.style.setProperty("transition", "width 0.1s linear");
4869
4889
  }
4870
4890
  }
4871
4891
  /**
@@ -4873,7 +4893,7 @@ class VideoControlsUI {
4873
4893
  */
4874
4894
  hideProgressBar() {
4875
4895
  if (this.controlsElement) {
4876
- this.controlsElement.style.display = "none";
4896
+ this.controlsElement.classList.add("sf-hidden");
4877
4897
  }
4878
4898
  }
4879
4899
  /**
@@ -4881,7 +4901,7 @@ class VideoControlsUI {
4881
4901
  */
4882
4902
  showMuteButton() {
4883
4903
  if (this.muteButton) {
4884
- this.muteButton.style.display = "block";
4904
+ this.muteButton.classList.remove("sf-hidden");
4885
4905
  }
4886
4906
  }
4887
4907
  /**
@@ -4889,7 +4909,7 @@ class VideoControlsUI {
4889
4909
  */
4890
4910
  hideMuteButton() {
4891
4911
  if (this.muteButton) {
4892
- this.muteButton.style.display = "none";
4912
+ this.muteButton.classList.add("sf-hidden");
4893
4913
  }
4894
4914
  }
4895
4915
  /**
@@ -4942,8 +4962,7 @@ class VideoManager {
4942
4962
  this.controls.updateProgressImmediate(activeVideo.duration, activeVideo.duration);
4943
4963
  }
4944
4964
  if (activeVideo) {
4945
- activeVideo.style.filter = "blur(3px)";
4946
- activeVideo.style.transition = "filter 0.3s ease-out";
4965
+ activeVideo.classList.add("sf-video-container__video--blurred");
4947
4966
  }
4948
4967
  if (this.completionPolicy === "auto") {
4949
4968
  this.handleAutoVideoEnded();
@@ -5016,9 +5035,8 @@ class VideoManager {
5016
5035
  this.deviceHandler.configureVideoElement(this.currentVideo);
5017
5036
  this.container.appendChild(this.currentVideo);
5018
5037
  this.nextVideo = document.createElement("video");
5019
- this.nextVideo.className = "sf-video-container__video sf-video-container__video--next";
5038
+ this.nextVideo.className = "sf-video-container__video sf-video-container__video--next sf-hidden";
5020
5039
  this.deviceHandler.configureVideoElement(this.nextVideo);
5021
- this.nextVideo.style.display = "none";
5022
5040
  this.container.appendChild(this.nextVideo);
5023
5041
  const store = useSaltfishStore.getState();
5024
5042
  if (store.isMuted) {
@@ -5077,8 +5095,8 @@ class VideoManager {
5077
5095
  activeVideo.pause();
5078
5096
  const store = useSaltfishStore.getState();
5079
5097
  inactiveVideo.muted = store.isMuted;
5080
- activeVideo.style.display = "none";
5081
- inactiveVideo.style.display = "block";
5098
+ activeVideo.classList.add("sf-hidden");
5099
+ inactiveVideo.classList.remove("sf-hidden");
5082
5100
  this.activeVideoIndex = this.activeVideoIndex === 0 ? 1 : 0;
5083
5101
  this.currentVideoUrl = this.nextVideoUrl;
5084
5102
  this.nextVideoUrl = "";
@@ -5269,10 +5287,7 @@ class VideoManager {
5269
5287
  console.error("VideoManager: No active video element found");
5270
5288
  return;
5271
5289
  }
5272
- if (activeVideo.style.filter) {
5273
- activeVideo.style.filter = "";
5274
- activeVideo.style.transition = "";
5275
- }
5290
+ activeVideo.classList.remove("sf-video-container__video--blurred");
5276
5291
  if (activeVideo.ended) {
5277
5292
  activeVideo.currentTime = 0;
5278
5293
  }
@@ -5603,7 +5618,7 @@ class VideoManager {
5603
5618
  return;
5604
5619
  }
5605
5620
  if (posterUrl) {
5606
- this.container.style.backgroundImage = `url('${posterUrl}')`;
5621
+ this.container.style.setProperty("--sf-audio-poster-url", `url('${posterUrl}')`);
5607
5622
  this.container.classList.add("sf-video-container--audio-fallback");
5608
5623
  }
5609
5624
  if (!this.audioFallbackOverlay) {
@@ -5631,7 +5646,7 @@ class VideoManager {
5631
5646
  if (!this.container) {
5632
5647
  return;
5633
5648
  }
5634
- this.container.style.backgroundImage = "";
5649
+ this.container.style.removeProperty("--sf-audio-poster-url");
5635
5650
  this.container.classList.remove("sf-video-container--audio-fallback");
5636
5651
  if (this.audioFallbackOverlay && this.audioFallbackOverlay.parentNode) {
5637
5652
  this.audioFallbackOverlay.parentNode.removeChild(this.audioFallbackOverlay);
@@ -6054,42 +6069,92 @@ class CursorManager {
6054
6069
  * Creates the virtual cursor element
6055
6070
  */
6056
6071
  create() {
6072
+ this.injectCursorStyles();
6057
6073
  this.cursor = document.createElement("div");
6058
- this.cursor.style.position = "fixed";
6059
- this.cursor.style.top = "0";
6060
- this.cursor.style.left = "0";
6061
- this.cursor.style.width = "36px";
6062
- this.cursor.style.height = "36px";
6063
- this.cursor.style.zIndex = "9999999";
6064
- this.cursor.style.pointerEvents = "none";
6065
- this.cursor.style.display = "none";
6066
- this.cursor.style.willChange = "transform";
6074
+ this.cursor.className = "sf-cursor";
6067
6075
  this.cursor.innerHTML = `
6068
6076
  <svg xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="#ff7614" stroke-width="2">
6069
6077
  <path d="M3 3L10.5 21L13.5 13.5L21 10.5L3 3Z" fill="#ff7614" />
6070
6078
  </svg>
6071
6079
  `;
6072
6080
  this.selectionElement = document.createElement("div");
6073
- this.selectionElement.style.position = "fixed";
6074
- this.selectionElement.style.pointerEvents = "none";
6075
- this.selectionElement.style.display = "none";
6076
- this.selectionElement.style.zIndex = "9999998";
6081
+ this.selectionElement.className = "sf-selection";
6077
6082
  document.body.appendChild(this.cursor);
6078
6083
  document.body.appendChild(this.selectionElement);
6079
6084
  this.flashlightOverlay = document.createElement("div");
6080
- this.flashlightOverlay.style.position = "fixed";
6081
- this.flashlightOverlay.style.top = "0";
6082
- this.flashlightOverlay.style.left = "0";
6083
- this.flashlightOverlay.style.width = "100vw";
6084
- this.flashlightOverlay.style.height = "100vh";
6085
- this.flashlightOverlay.style.pointerEvents = "none";
6086
- this.flashlightOverlay.style.zIndex = "999997";
6087
- this.flashlightOverlay.style.display = "none";
6088
- this.flashlightOverlay.style.background = "radial-gradient(circle 150px at 50% 50%, transparent 0%, rgba(0, 0, 0, 0.4) 100%)";
6085
+ this.flashlightOverlay.className = "sf-flashlight-overlay";
6089
6086
  document.body.appendChild(this.flashlightOverlay);
6090
6087
  this.boundScrollHandler = this.handleScroll.bind(this);
6091
6088
  window.addEventListener("scroll", this.boundScrollHandler, { passive: true });
6092
6089
  }
6090
+ /**
6091
+ * Injects cursor styles into the main document for CSP compliance
6092
+ * Uses adoptedStyleSheets API when available, falls back to <style> element
6093
+ */
6094
+ injectCursorStyles() {
6095
+ const cursorStyles = `
6096
+ .sf-cursor {
6097
+ position: fixed;
6098
+ top: 0;
6099
+ left: 0;
6100
+ width: 36px;
6101
+ height: 36px;
6102
+ z-index: 9999999;
6103
+ pointer-events: none;
6104
+ display: none;
6105
+ will-change: transform;
6106
+ transform: var(--sf-cursor-transform, translate(0, 0));
6107
+ opacity: var(--sf-cursor-opacity, 1);
6108
+ }
6109
+
6110
+ .sf-cursor--visible {
6111
+ display: block;
6112
+ }
6113
+
6114
+ .sf-selection {
6115
+ position: fixed;
6116
+ pointer-events: none;
6117
+ display: none;
6118
+ z-index: 9999998;
6119
+ border: 2px solid #ff7614;
6120
+ background: rgba(255, 118, 20, 0.1);
6121
+ left: var(--sf-selection-left, 0);
6122
+ top: var(--sf-selection-top, 0);
6123
+ width: var(--sf-selection-width, 0);
6124
+ height: var(--sf-selection-height, 0);
6125
+ }
6126
+
6127
+ .sf-selection--visible {
6128
+ display: block;
6129
+ }
6130
+
6131
+ .sf-flashlight-overlay {
6132
+ position: fixed;
6133
+ top: 0;
6134
+ left: 0;
6135
+ width: 100vw;
6136
+ height: 100vh;
6137
+ pointer-events: none;
6138
+ z-index: 9999997;
6139
+ display: none;
6140
+ background: var(--sf-flashlight-bg, radial-gradient(circle 150px at 50% 50%, transparent 0%, rgba(0, 0, 0, 0.4) 100%));
6141
+ clip-path: var(--sf-flashlight-clip, none);
6142
+ }
6143
+
6144
+ .sf-flashlight-overlay--visible {
6145
+ display: block;
6146
+ }
6147
+ `;
6148
+ try {
6149
+ const sheet = new CSSStyleSheet();
6150
+ sheet.replaceSync(cursorStyles);
6151
+ document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
6152
+ } catch (error2) {
6153
+ const styleElement = document.createElement("style");
6154
+ styleElement.textContent = cursorStyles;
6155
+ document.head.appendChild(styleElement);
6156
+ }
6157
+ }
6093
6158
  /**
6094
6159
  * Handles scrolling to keep the cursor positioned on the target element
6095
6160
  * Uses requestAnimationFrame for smooth 60fps updates
@@ -6186,12 +6251,12 @@ class CursorManager {
6186
6251
  this.lastCursorX = x;
6187
6252
  this.lastCursorY = y;
6188
6253
  if (this.cursor) {
6189
- this.cursor.style.display = "block";
6190
- this.cursor.style.transform = `translate(${x}px, ${y}px) translate(-50%, -50%)`;
6254
+ this.cursor.classList.add("sf-cursor--visible");
6255
+ this.cursor.style.setProperty("--sf-cursor-transform", `translate(${x}px, ${y}px) translate(-50%, -50%)`);
6191
6256
  }
6192
6257
  if (this.flashlightOverlay) {
6193
- this.flashlightOverlay.style.display = "block";
6194
- this.flashlightOverlay.style.background = `radial-gradient(circle 150px at ${x}px ${y}px, transparent 0%, rgba(0, 0, 0, 0.4) 100%)`;
6258
+ this.flashlightOverlay.classList.add("sf-flashlight-overlay--visible");
6259
+ this.flashlightOverlay.style.setProperty("--sf-flashlight-bg", `radial-gradient(circle 150px at ${x}px ${y}px, transparent 0%, rgba(0, 0, 0, 0.4) 100%)`);
6195
6260
  }
6196
6261
  }
6197
6262
  /**
@@ -6199,13 +6264,13 @@ class CursorManager {
6199
6264
  */
6200
6265
  hideCursorElements() {
6201
6266
  if (this.cursor) {
6202
- this.cursor.style.display = "none";
6267
+ this.cursor.classList.remove("sf-cursor--visible");
6203
6268
  }
6204
6269
  if (this.selectionElement) {
6205
- this.selectionElement.style.display = "none";
6270
+ this.selectionElement.classList.remove("sf-selection--visible");
6206
6271
  }
6207
6272
  if (this.flashlightOverlay) {
6208
- this.flashlightOverlay.style.display = "none";
6273
+ this.flashlightOverlay.classList.remove("sf-flashlight-overlay--visible");
6209
6274
  this.resetFlashlightOverlay();
6210
6275
  }
6211
6276
  }
@@ -6216,10 +6281,10 @@ class CursorManager {
6216
6281
  if (!this.flashlightOverlay) {
6217
6282
  return;
6218
6283
  }
6219
- this.flashlightOverlay.style.clipPath = "none";
6284
+ this.flashlightOverlay.style.setProperty("--sf-flashlight-clip", "none");
6220
6285
  const x = this.lastCursorX;
6221
6286
  const y = this.lastCursorY;
6222
- this.flashlightOverlay.style.background = `radial-gradient(circle 150px at ${x}px ${y}px, transparent 0%, rgba(0, 0, 0, 0.4) 100%)`;
6287
+ this.flashlightOverlay.style.setProperty("--sf-flashlight-bg", `radial-gradient(circle 150px at ${x}px ${y}px, transparent 0%, rgba(0, 0, 0, 0.4) 100%)`);
6223
6288
  }
6224
6289
  /**
6225
6290
  * Animates the cursor along a path
@@ -6235,7 +6300,7 @@ class CursorManager {
6235
6300
  this.isSelectionMode = false;
6236
6301
  this.resetFlashlightOverlay();
6237
6302
  if (this.selectionElement) {
6238
- this.selectionElement.style.display = "none";
6303
+ this.selectionElement.classList.remove("sf-selection--visible");
6239
6304
  }
6240
6305
  if (!(animation == null ? void 0 : animation.targetSelector)) {
6241
6306
  console.warn("CursorManager: No targetSelector provided in animation");
@@ -6277,7 +6342,7 @@ class CursorManager {
6277
6342
  this.targetX = targetRect.left + targetRect.width / 2 + this.POINTER_HORIZONTAL_OFFSET;
6278
6343
  this.targetY = targetRect.top + targetRect.height / 2 + this.POINTER_VERTICAL_OFFSET;
6279
6344
  if (this.selectionElement) {
6280
- this.selectionElement.style.display = "none";
6345
+ this.selectionElement.classList.remove("sf-selection--visible");
6281
6346
  }
6282
6347
  }
6283
6348
  if (this.targetX !== null && this.targetY !== null && this.startX !== null && this.startY !== null) {
@@ -6326,7 +6391,7 @@ class CursorManager {
6326
6391
  this.selectionElement.style.setProperty("--sf-selection-border-radius", animation.selectionStyles.borderRadius);
6327
6392
  }
6328
6393
  }
6329
- this.selectionElement.style.display = "none";
6394
+ this.selectionElement.classList.remove("sf-selection--visible");
6330
6395
  }
6331
6396
  this.calculateControlPoint();
6332
6397
  this.currentAnimation = { ...animation };
@@ -6368,11 +6433,11 @@ class CursorManager {
6368
6433
  this.dragStartY,
6369
6434
  currentY - this.SELECTION_VERTICAL_OFFSET
6370
6435
  ));
6371
- this.selectionElement.style.left = `${left}px`;
6372
- this.selectionElement.style.top = `${top}px`;
6373
- this.selectionElement.style.width = `${width}px`;
6374
- this.selectionElement.style.height = `${height}px`;
6375
- this.selectionElement.style.display = "block";
6436
+ this.selectionElement.style.setProperty("--sf-selection-left", `${left}px`);
6437
+ this.selectionElement.style.setProperty("--sf-selection-top", `${top}px`);
6438
+ this.selectionElement.style.setProperty("--sf-selection-width", `${width}px`);
6439
+ this.selectionElement.style.setProperty("--sf-selection-height", `${height}px`);
6440
+ this.selectionElement.classList.add("sf-selection--visible");
6376
6441
  this.updateFlashlightWithCutout(left, top, width, height);
6377
6442
  }
6378
6443
  /**
@@ -6420,15 +6485,15 @@ class CursorManager {
6420
6485
  if (this.cursor) {
6421
6486
  const x = this.lastCursorX;
6422
6487
  const y = this.lastCursorY;
6423
- this.cursor.style.transform = `translate(${x}px, ${y}px) translate(-50%, -50%) scale(0.9)`;
6424
- this.cursor.style.opacity = "0.9";
6488
+ this.cursor.style.setProperty("--sf-cursor-transform", `translate(${x}px, ${y}px) translate(-50%, -50%) scale(0.9)`);
6489
+ this.cursor.style.setProperty("--sf-cursor-opacity", "0.9");
6425
6490
  }
6426
6491
  if (this.selectionElement) {
6427
- this.selectionElement.style.left = `${this.dragStartX}px`;
6428
- this.selectionElement.style.top = `${this.dragStartY}px`;
6429
- this.selectionElement.style.width = "0px";
6430
- this.selectionElement.style.height = "0px";
6431
- this.selectionElement.style.display = "block";
6492
+ this.selectionElement.style.setProperty("--sf-selection-left", `${this.dragStartX}px`);
6493
+ this.selectionElement.style.setProperty("--sf-selection-top", `${this.dragStartY}px`);
6494
+ this.selectionElement.style.setProperty("--sf-selection-width", "0px");
6495
+ this.selectionElement.style.setProperty("--sf-selection-height", "0px");
6496
+ this.selectionElement.classList.add("sf-selection--visible");
6432
6497
  }
6433
6498
  }
6434
6499
  /**
@@ -6439,8 +6504,8 @@ class CursorManager {
6439
6504
  if (this.cursor) {
6440
6505
  const x = this.lastCursorX;
6441
6506
  const y = this.lastCursorY;
6442
- this.cursor.style.transform = `translate(${x}px, ${y}px) translate(-50%, -50%)`;
6443
- this.cursor.style.opacity = "1";
6507
+ this.cursor.style.setProperty("--sf-cursor-transform", `translate(${x}px, ${y}px) translate(-50%, -50%)`);
6508
+ this.cursor.style.setProperty("--sf-cursor-opacity", "1");
6444
6509
  }
6445
6510
  if (this.animationFrameId !== null) {
6446
6511
  cancelAnimationFrame(this.animationFrameId);
@@ -6603,10 +6668,10 @@ class CursorManager {
6603
6668
  0% ${top}px /* Close cutout path */
6604
6669
  )
6605
6670
  `;
6606
- this.flashlightOverlay.style.clipPath = clipPath;
6671
+ this.flashlightOverlay.style.setProperty("--sf-flashlight-clip", clipPath);
6607
6672
  const x = this.lastCursorX;
6608
6673
  const y = this.lastCursorY;
6609
- this.flashlightOverlay.style.background = `radial-gradient(circle 150px at ${x}px ${y}px, transparent 0%, rgba(0, 0, 0, 0.4) 100%)`;
6674
+ this.flashlightOverlay.style.setProperty("--sf-flashlight-bg", `radial-gradient(circle 150px at ${x}px ${y}px, transparent 0%, rgba(0, 0, 0, 0.4) 100%)`);
6610
6675
  }
6611
6676
  /**
6612
6677
  * Calculates a control point for curved cursor movement
@@ -6691,16 +6756,16 @@ class CursorManager {
6691
6756
  if (this.cursor) {
6692
6757
  const x = this.lastCursorX;
6693
6758
  const y = this.lastCursorY;
6694
- this.cursor.style.transform = `translate(${x}px, ${y}px) translate(-50%, -50%) scale(0.8)`;
6695
- this.cursor.style.opacity = "0.8";
6759
+ this.cursor.style.setProperty("--sf-cursor-transform", `translate(${x}px, ${y}px) translate(-50%, -50%) scale(0.8)`);
6760
+ this.cursor.style.setProperty("--sf-cursor-opacity", "0.8");
6696
6761
  setTimeout(() => {
6697
6762
  if (this.cursor) {
6698
6763
  if (!this.canShowCursor()) {
6699
6764
  this.hideCursorElements();
6700
6765
  return;
6701
6766
  }
6702
- this.cursor.style.transform = `translate(${x}px, ${y}px) translate(-50%, -50%)`;
6703
- this.cursor.style.opacity = "1";
6767
+ this.cursor.style.setProperty("--sf-cursor-transform", `translate(${x}px, ${y}px) translate(-50%, -50%)`);
6768
+ this.cursor.style.setProperty("--sf-cursor-opacity", "1");
6704
6769
  }
6705
6770
  }, 300);
6706
6771
  }
@@ -6722,12 +6787,12 @@ class CursorManager {
6722
6787
  if (this.cursor) {
6723
6788
  const x = this.lastCursorX;
6724
6789
  const y = this.lastCursorY;
6725
- this.cursor.style.transform = `translate(${x}px, ${y}px) translate(-50%, -50%)`;
6726
- this.cursor.style.opacity = "1";
6790
+ this.cursor.style.setProperty("--sf-cursor-transform", `translate(${x}px, ${y}px) translate(-50%, -50%)`);
6791
+ this.cursor.style.setProperty("--sf-cursor-opacity", "1");
6727
6792
  }
6728
6793
  this.resetFlashlightOverlay();
6729
6794
  if (this.selectionElement) {
6730
- this.selectionElement.style.display = "none";
6795
+ this.selectionElement.classList.remove("sf-selection--visible");
6731
6796
  }
6732
6797
  }
6733
6798
  /**
@@ -6755,11 +6820,11 @@ class CursorManager {
6755
6820
  this.isFirstAnimation = true;
6756
6821
  this.shouldShowCursor = false;
6757
6822
  if (this.cursor) {
6758
- this.cursor.style.display = "none";
6823
+ this.cursor.classList.remove("sf-cursor--visible");
6759
6824
  }
6760
6825
  this.isSelectionMode = false;
6761
6826
  if (this.selectionElement) {
6762
- this.selectionElement.style.display = "none";
6827
+ this.selectionElement.classList.remove("sf-selection--visible");
6763
6828
  }
6764
6829
  this.dragStartX = null;
6765
6830
  this.dragStartY = null;
@@ -6917,7 +6982,7 @@ class InteractionManager {
6917
6982
  }
6918
6983
  const store = useSaltfishStore.getState();
6919
6984
  if (store.isMinimized) {
6920
- this.buttonContainer.style.display = "none";
6985
+ this.buttonContainer.classList.add("sf-hidden");
6921
6986
  }
6922
6987
  }
6923
6988
  /**
@@ -6996,15 +7061,15 @@ class InteractionManager {
6996
7061
  }
6997
7062
  const store = useSaltfishStore.getState();
6998
7063
  if (store.isMinimized) {
6999
- this.buttonContainer.style.display = "none";
7064
+ this.buttonContainer.classList.add("sf-hidden");
7000
7065
  if (this.scrollIndicator) {
7001
- this.scrollIndicator.style.display = "none";
7066
+ this.scrollIndicator.classList.add("sf-hidden");
7002
7067
  }
7003
7068
  return;
7004
7069
  } else {
7005
- this.buttonContainer.style.display = "flex";
7070
+ this.buttonContainer.classList.remove("sf-hidden");
7006
7071
  if (this.scrollIndicator) {
7007
- this.scrollIndicator.style.display = "flex";
7072
+ this.scrollIndicator.classList.remove("sf-hidden");
7008
7073
  }
7009
7074
  }
7010
7075
  this.buttonContainer.className = "sf-choice-buttons-container";
@@ -9501,9 +9566,9 @@ class MinimizeButton {
9501
9566
  }
9502
9567
  updateVisibility(isMinimized) {
9503
9568
  if (isMinimized) {
9504
- this.button.style.display = "none";
9569
+ this.button.classList.add("sf-hidden");
9505
9570
  } else {
9506
- this.button.style.display = "";
9571
+ this.button.classList.remove("sf-hidden");
9507
9572
  }
9508
9573
  }
9509
9574
  destroy() {
@@ -9595,9 +9660,9 @@ class ExitButton {
9595
9660
  }
9596
9661
  updateVisibility(isMinimized) {
9597
9662
  if (isMinimized) {
9598
- this.button.style.display = "flex";
9663
+ this.button.classList.remove("sf-hidden");
9599
9664
  } else {
9600
- this.button.style.display = "none";
9665
+ this.button.classList.add("sf-hidden");
9601
9666
  }
9602
9667
  }
9603
9668
  destroy() {
@@ -9621,7 +9686,7 @@ class ErrorDisplay {
9621
9686
  createErrorDisplay() {
9622
9687
  this.errorOverlay = document.createElement("div");
9623
9688
  this.errorOverlay.className = CSS_CLASSES.ERROR_DISPLAY;
9624
- this.errorOverlay.style.display = "none";
9689
+ this.errorOverlay.classList.add("sf-hidden");
9625
9690
  const errorContent = document.createElement("div");
9626
9691
  errorContent.className = CSS_CLASSES.ERROR_DISPLAY_CONTENT;
9627
9692
  const errorMessage = document.createElement("p");
@@ -9638,7 +9703,7 @@ class ErrorDisplay {
9638
9703
  if (newOptions) {
9639
9704
  this.updateOptions(newOptions);
9640
9705
  }
9641
- this.errorOverlay.style.display = "flex";
9706
+ this.errorOverlay.classList.remove("sf-hidden");
9642
9707
  this.isVisible = true;
9643
9708
  requestAnimationFrame(() => {
9644
9709
  this.errorOverlay.classList.add(CSS_CLASSES.ERROR_DISPLAY_VISIBLE);
@@ -9653,7 +9718,7 @@ class ErrorDisplay {
9653
9718
  }
9654
9719
  this.errorOverlay.classList.remove(CSS_CLASSES.ERROR_DISPLAY_VISIBLE);
9655
9720
  setTimeout(() => {
9656
- this.errorOverlay.style.display = "none";
9721
+ this.errorOverlay.classList.add("sf-hidden");
9657
9722
  this.isVisible = false;
9658
9723
  }, 300);
9659
9724
  }
@@ -9718,7 +9783,7 @@ class LoadingSpinner {
9718
9783
  */
9719
9784
  show() {
9720
9785
  if (this.spinnerElement) {
9721
- this.spinnerElement.style.display = "flex";
9786
+ this.spinnerElement.classList.remove("sf-hidden");
9722
9787
  return;
9723
9788
  }
9724
9789
  this.spinnerElement = document.createElement("div");
@@ -9763,7 +9828,7 @@ class LoadingSpinner {
9763
9828
  */
9764
9829
  hide() {
9765
9830
  if (this.spinnerElement) {
9766
- this.spinnerElement.style.display = "none";
9831
+ this.spinnerElement.classList.add("sf-hidden");
9767
9832
  }
9768
9833
  }
9769
9834
  /**
@@ -9885,9 +9950,6 @@ class UIManager {
9885
9950
  console.warn(`UIManager: Invalid position "${positionToUse}", defaulting to bottom-right`);
9886
9951
  positionToUse = "bottom-right";
9887
9952
  }
9888
- this.playerRoot.style.left = "";
9889
- this.playerRoot.style.top = "";
9890
- this.playerRoot.style.transform = "";
9891
9953
  this.playerRoot.classList.remove("sf-player-root--bottom-left", "sf-player-root--bottom-right");
9892
9954
  if (positionToUse === "bottom-left") {
9893
9955
  this.playerRoot.classList.add("sf-player-root--bottom-left");
@@ -10117,7 +10179,7 @@ class UIManager {
10117
10179
  */
10118
10180
  updatePlayPauseButton(state) {
10119
10181
  if (this.playButton) {
10120
- this.playButton.style.display = "none";
10182
+ this.playButton.classList.add("sf-hidden");
10121
10183
  }
10122
10184
  if (!this.centerPlayButton && this.playerElement) {
10123
10185
  this.centerPlayButton = this.playerElement.querySelector(".sf-player__center-play-button");
@@ -10129,12 +10191,14 @@ class UIManager {
10129
10191
  return;
10130
10192
  }
10131
10193
  if (state === "playing" || state === "completed") {
10132
- this.centerPlayButton.style.display = "none";
10194
+ this.centerPlayButton.classList.remove("sf-player__center-play-button--visible");
10195
+ this.centerPlayButton.classList.remove("sf-player__center-play-button--prominent");
10133
10196
  } else {
10134
- this.centerPlayButton.style.display = "flex";
10197
+ this.centerPlayButton.classList.add("sf-player__center-play-button--visible");
10135
10198
  if (state === "autoplayBlocked" || state === "idleMode") {
10136
- this.centerPlayButton.style.opacity = "1";
10137
- this.centerPlayButton.style.pointerEvents = "auto";
10199
+ this.centerPlayButton.classList.add("sf-player__center-play-button--prominent");
10200
+ } else {
10201
+ this.centerPlayButton.classList.remove("sf-player__center-play-button--prominent");
10138
10202
  }
10139
10203
  }
10140
10204
  }
@@ -10221,8 +10285,7 @@ class UIManager {
10221
10285
  */
10222
10286
  enablePlayButtonProminent() {
10223
10287
  if (this.centerPlayButton) {
10224
- this.centerPlayButton.style.opacity = "1";
10225
- this.centerPlayButton.style.pointerEvents = "auto";
10288
+ this.centerPlayButton.classList.add("sf-player__center-play-button--prominent");
10226
10289
  }
10227
10290
  }
10228
10291
  /**
@@ -10230,8 +10293,7 @@ class UIManager {
10230
10293
  */
10231
10294
  disablePlayButtonProminent() {
10232
10295
  if (this.centerPlayButton) {
10233
- this.centerPlayButton.style.opacity = "";
10234
- this.centerPlayButton.style.pointerEvents = "";
10296
+ this.centerPlayButton.classList.remove("sf-player__center-play-button--prominent");
10235
10297
  }
10236
10298
  }
10237
10299
  /**
@@ -10614,7 +10676,7 @@ const SaltfishPlayer$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.de
10614
10676
  __proto__: null,
10615
10677
  SaltfishPlayer
10616
10678
  }, Symbol.toStringTag, { value: "Module" }));
10617
- const version = "0.2.65";
10679
+ const version = "0.2.66";
10618
10680
  const packageJson = {
10619
10681
  version
10620
10682
  };