@xhub-short/ui 0.1.0-beta.1 → 0.1.0-beta.10

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.
Files changed (45) hide show
  1. package/dist/CommentSheet.css-BeCrEaUG.d.ts +221 -0
  2. package/dist/{chunk-2PTMP65P.js → chunk-2FSDVYER.js} +8 -9
  3. package/dist/{chunk-WKX2WBVO.js → chunk-3XPJHUYL.js} +1 -39
  4. package/dist/{chunk-ANGBSV7L.js → chunk-AC2IFAJR.js} +10 -5
  5. package/dist/{chunk-4YDIRPIN.js → chunk-ANCP53F3.js} +3 -3
  6. package/dist/chunk-AQHD6LPS.js +430 -0
  7. package/dist/{chunk-HW4LXTFT.js → chunk-CL6BS7GB.js} +7 -5
  8. package/dist/{chunk-YW23IBKF.js → chunk-ECR42RKK.js} +46 -5
  9. package/dist/chunk-EDWS2IPH.js +1 -0
  10. package/dist/chunk-FNXTPQ6L.js +2573 -0
  11. package/dist/{chunk-DHQJBXQW.js → chunk-KWHMZ6H5.js} +1 -1
  12. package/dist/{chunk-UXMA4KJZ.js → chunk-RMLTPW5S.js} +3 -2
  13. package/dist/{chunk-SSJDO24Q.js → chunk-SZXFH334.js} +1 -1
  14. package/dist/{chunk-4MN72OZH.js → chunk-UNV3NWN6.js} +4 -4
  15. package/dist/{chunk-ZZDQKP4R.js → chunk-WCRDTBCZ.js} +94 -155
  16. package/dist/{chunk-XAOEHLOX.js → chunk-XDIH66C4.js} +245 -52
  17. package/dist/components/ActionBar/index.js +1 -1
  18. package/dist/components/AuthorInfo/index.d.ts +5 -1
  19. package/dist/components/AuthorInfo/index.js +1 -1
  20. package/dist/components/BlurhashPlaceholder/index.d.ts +67 -0
  21. package/dist/components/BlurhashPlaceholder/index.js +150 -0
  22. package/dist/components/CommentSheet/index.d.ts +164 -0
  23. package/dist/components/CommentSheet/index.js +1 -0
  24. package/dist/components/ErrorBoundary/index.js +1 -1
  25. package/dist/components/OfflineIndicator/index.d.ts +56 -0
  26. package/dist/components/OfflineIndicator/index.js +151 -0
  27. package/dist/components/ProgressBar/index.d.ts +30 -2
  28. package/dist/components/ProgressBar/index.js +1 -1
  29. package/dist/components/Skeleton/index.js +1 -1
  30. package/dist/components/SubtitleDisplay/index.d.ts +94 -0
  31. package/dist/components/SubtitleDisplay/index.js +165 -0
  32. package/dist/components/VideoFeed/index.d.ts +11 -0
  33. package/dist/components/VideoFeed/index.js +1 -1
  34. package/dist/components/VideoInfo/index.js +1 -1
  35. package/dist/components/VideoPlayer/index.d.ts +14 -41
  36. package/dist/components/VideoPlayer/index.js +1 -1
  37. package/dist/components/VideoSlot/index.d.ts +124 -64
  38. package/dist/components/VideoSlot/index.js +1 -1
  39. package/dist/components/VirtualSlider/index.d.ts +339 -0
  40. package/dist/components/VirtualSlider/index.js +1 -0
  41. package/dist/components/icons/index.js +1 -1
  42. package/dist/index.d.ts +76 -93
  43. package/dist/index.js +75 -27
  44. package/package.json +53 -8
  45. package/dist/use-gesture-react.esm-3SV4QLEJ.js +0 -1893
@@ -1,48 +1,8 @@
1
- import { VideoSlotHeadlessProps, VideoItem, UIResourceState, UIPlayerState, UIPlayerControls } from '@xhub-short/contracts';
2
1
  import * as react from 'react';
3
2
  import { ReactNode } from 'react';
3
+ import { VideoSlotHeadlessProps, VideoItem, UIResourceState, UIPlayerState, UIPlayerControls } from '@xhub-short/contracts';
4
4
  import * as react_jsx_runtime from 'react/jsx-runtime';
5
5
 
6
- /**
7
- * VideoSlotHeadless - Headless Video Slot Component
8
- *
9
- * A headless component for displaying a single video slot in the feed.
10
- * Receives ALL data via props (no SDK dependency).
11
- *
12
- * Architecture (Two-Tier Export Pattern):
13
- * - @xhub-short/ui: Exports this headless component
14
- * - @xhub-short/sdk: Wraps with SDK hooks (wired/VideoSlot.tsx)
15
- *
16
- * Responsibilities:
17
- * - Display video poster/first frame/skeleton
18
- * - Provide context for compound components
19
- * - Handle tap-to-play/pause interaction
20
- * - Display loading/error states
21
- *
22
- * @example
23
- * ```tsx
24
- * // Headless usage (requires all props)
25
- * <VideoSlotHeadless
26
- * video={video}
27
- * resourceState={resourceState}
28
- * playerState={playerState}
29
- * playerControls={playerControls}
30
- * >
31
- * <VideoSlotPoster />
32
- * <VideoSlotOverlay>
33
- * <AuthorInfo />
34
- * <ActionBar />
35
- * </VideoSlotOverlay>
36
- * </VideoSlotHeadless>
37
- *
38
- * // Wired usage (SDK injects state)
39
- * import { VideoSlot } from '@xhub-short/sdk';
40
- * <VideoSlot video={video}>
41
- * <VideoSlotOverlay>...</VideoSlotOverlay>
42
- * </VideoSlot>
43
- * ```
44
- */
45
-
46
6
  interface VideoSlotHeadlessExtendedProps extends VideoSlotHeadlessProps {
47
7
  /** Children (overlay, poster, etc.) */
48
8
  children?: ReactNode;
@@ -83,11 +43,18 @@ interface VideoSlotHeadlessExtendedProps extends VideoSlotHeadlessProps {
83
43
  * - Wired layer (VideoSlot in SDK) for DOM manipulation
84
44
  * - Testing libraries for element selection
85
45
  * - Analytics for event tracking
46
+ *
47
+ * Wrapped with React.memo to prevent unnecessary re-renders.
86
48
  */
87
- declare function VideoSlotHeadless({ video, resourceState, playerState, playerControls, className, children, onVisible: _onVisible, onHidden: _onHidden, onTap, onDoubleTap, renderError, renderLoading, }: VideoSlotHeadlessExtendedProps): React.ReactElement;
88
- declare namespace VideoSlotHeadless {
89
- var displayName: string;
90
- }
49
+ declare function VideoSlotHeadlessBase({ video, resourceState, playerState, playerControls, className, children, onVisible: _onVisible, onHidden: _onHidden, onTap, onDoubleTap, renderError, renderLoading, restoreFrame, }: VideoSlotHeadlessExtendedProps): React.ReactElement;
50
+ /**
51
+ * VideoSlotHeadless - Memoized for performance
52
+ *
53
+ * Prevents re-renders when parent components update with same props.
54
+ * This is critical in virtualized feeds where slots may receive frequent
55
+ * prop updates from PlayerEngine and ResourceGovernor.
56
+ */
57
+ declare const VideoSlotHeadless: react.MemoExoticComponent<typeof VideoSlotHeadlessBase>;
91
58
 
92
59
  /**
93
60
  * VideoSlotPoster - Poster/Thumbnail/First Frame Compound Component
@@ -98,6 +65,14 @@ declare namespace VideoSlotHeadless {
98
65
  * 2. Poster image (from video.poster)
99
66
  * 3. Skeleton shimmer (fallback)
100
67
  *
68
+ * ## Re-render Optimization
69
+ *
70
+ * Uses the "Context Selector" pattern to prevent re-renders:
71
+ * - Outer component consumes context
72
+ * - Inner component is memoized and receives data via props
73
+ * - React.memo compares props → prevents re-render when context changes
74
+ * but the selected values are the same
75
+ *
101
76
  * @example
102
77
  * ```tsx
103
78
  * <VideoSlotHeadless {...props}>
@@ -126,9 +101,15 @@ interface VideoSlotPosterProps {
126
101
  * Compound component that renders the poster/thumbnail layer.
127
102
  * Must be used within a VideoSlotHeadless component.
128
103
  *
129
- * Automatically hides when video is playing via CSS.
104
+ * This outer component:
105
+ * 1. Consumes context (will re-render when context changes)
106
+ * 2. Selects only the needed values
107
+ * 3. Passes them as props to memoized inner component
108
+ *
109
+ * The inner component will only re-render when the selected values change,
110
+ * NOT when unrelated context values change (e.g., during seek operations).
130
111
  */
131
- declare function VideoSlotPoster({ className, forceSkeleton, posterUrl, loading, alt, onError, }: VideoSlotPosterProps): React.ReactElement;
112
+ declare function VideoSlotPoster(props: VideoSlotPosterProps): React.ReactElement;
132
113
  declare namespace VideoSlotPoster {
133
114
  var displayName: string;
134
115
  }
@@ -385,6 +366,73 @@ declare namespace VideoSlotLikeAnimation {
385
366
  var displayName: string;
386
367
  }
387
368
 
369
+ /**
370
+ * VideoSlotPlayIndicator - Shows play/pause icon when user taps video
371
+ *
372
+ * ONLY shows indicator when user explicitly taps to toggle play/pause.
373
+ * Does NOT show on: swipe, auto-play, video loop, video change.
374
+ *
375
+ * Uses `lastUserToggleTime` from VideoSlotContext to detect user taps.
376
+ *
377
+ * ## Re-render Optimization
378
+ *
379
+ * Uses the "Context Selector" pattern to prevent re-renders:
380
+ * - Outer component consumes context
381
+ * - Inner component is memoized and receives data via props
382
+ * - React.memo compares props → prevents re-render when context changes
383
+ * but the selected values are the same
384
+ *
385
+ * @example
386
+ * ```tsx
387
+ * // Inside VideoSlot - auto-hide after tap
388
+ * <VideoSlotPlayIndicator />
389
+ *
390
+ * // Persist play icon while paused (YouTube style)
391
+ * <VideoSlotPlayIndicator persistWhenPaused />
392
+ *
393
+ * // With custom icons
394
+ * <VideoSlotPlayIndicator
395
+ * playIcon={<CustomPlayIcon />}
396
+ * pauseIcon={<CustomPauseIcon />}
397
+ * />
398
+ * ```
399
+ */
400
+
401
+ interface VideoSlotPlayIndicatorProps {
402
+ /** Custom play icon */
403
+ playIcon?: ReactNode;
404
+ /** Custom pause icon */
405
+ pauseIcon?: ReactNode;
406
+ /** Icon size in pixels */
407
+ size?: number;
408
+ /** Animation duration in ms */
409
+ duration?: number;
410
+ /**
411
+ * Keep play icon visible while video is paused (YouTube style)
412
+ * When false, icon auto-hides after duration
413
+ */
414
+ persistWhenPaused?: boolean;
415
+ /** Additional CSS class */
416
+ className?: string;
417
+ /** Test ID */
418
+ testId?: string;
419
+ }
420
+ /**
421
+ * VideoSlotPlayIndicator Component
422
+ *
423
+ * This outer component:
424
+ * 1. Consumes context (will re-render when context changes)
425
+ * 2. Selects only the needed values
426
+ * 3. Passes them as props to memoized inner component
427
+ *
428
+ * The inner component will only re-render when the selected values change,
429
+ * NOT when unrelated context values change (e.g., during seek operations).
430
+ */
431
+ declare function VideoSlotPlayIndicator(props: VideoSlotPlayIndicatorProps): React.ReactElement | null;
432
+ declare namespace VideoSlotPlayIndicator {
433
+ var displayName: string;
434
+ }
435
+
388
436
  interface VideoSlotContextValue {
389
437
  /** Video item data */
390
438
  video: VideoItem;
@@ -398,6 +446,10 @@ interface VideoSlotContextValue {
398
446
  firstFrame: string | null;
399
447
  /** Set first frame data URL */
400
448
  setFirstFrame: (dataUrl: string) => void;
449
+ /** Timestamp of last user-initiated play toggle (for PlayIndicator) */
450
+ lastUserToggleTime: number;
451
+ /** Set last user toggle time */
452
+ setLastUserToggleTime: (time: number) => void;
401
453
  }
402
454
  /**
403
455
  * VideoSlot Context
@@ -525,42 +577,50 @@ declare const SLOT_PAUSED_CLASS = "sv-video-slot--paused";
525
577
  *
526
578
  * Layer Stack (bottom to top):
527
579
  * ┌─────────────────────────────────────────────┐
528
- * │ z-index: 4 - Progress Bar (topmost) │
529
- * │ z-index: 3 - Error UI / Play Indicator │
530
- * │ z-index: 2 - Overlay / Spinner
531
- * │ z-index: 1 - Poster / Video
580
+ * │ z-index: 5 - Progress Bar (topmost) │
581
+ * │ z-index: 4 - Error UI / Play Indicator │
582
+ * │ z-index: 3 - Poster / Thumbnail
583
+ * │ z-index: 2 - Loading Spinner
584
+ * │ z-index: 1 - Video Element │
532
585
  * │ z-index: 0 - Slot Container (base) │
533
586
  * └─────────────────────────────────────────────┘
534
587
  *
588
+ * IMPORTANT: Poster (3) > Spinner (2) ensures thumbnail shows over loading spinner.
589
+ *
535
590
  * @example Override via CSS variables
536
591
  * ```css
537
592
  * :root {
538
- * --sv-z-poster: 1;
539
- * --sv-z-overlay: 2;
540
- * --sv-z-indicator: 3;
541
- * --sv-z-progress: 4;
593
+ * --sv-z-poster: 3;
594
+ * --sv-z-slot-spinner: 2;
595
+ * --sv-z-indicator: 4;
596
+ * --sv-z-progress: 5;
542
597
  * }
543
598
  * ```
544
599
  */
545
600
  declare const Z_INDEX: {
546
601
  /** Base layer - slot container */
547
602
  readonly SLOT: 0;
548
- /** Poster image and video element */
549
- readonly POSTER: 1;
550
- /** Overlay content and loading spinner */
551
- readonly OVERLAY: 2;
603
+ /** Video element */
604
+ readonly VIDEO: 1;
605
+ /** Loading spinner */
606
+ readonly SPINNER: 2;
607
+ /** Poster/thumbnail (above spinner so thumbnail shows during loading) */
608
+ readonly POSTER: 3;
552
609
  /** Error UI and play/pause indicator */
553
- readonly INDICATOR: 3;
610
+ readonly INDICATOR: 4;
554
611
  /** Progress bar (always on top) */
555
- readonly PROGRESS: 4;
612
+ readonly PROGRESS: 5;
556
613
  };
557
614
  /**
558
615
  * CSS custom property names for z-index layers.
559
616
  * Use these to override default z-index values.
617
+ *
618
+ * NOTE: --sv-z-slot-spinner is used instead of --sv-z-overlay to avoid
619
+ * conflicts with global --sv-z-overlay used for other overlay purposes.
560
620
  */
561
621
  declare const Z_INDEX_CSS_VARS: {
562
622
  readonly POSTER: "--sv-z-poster";
563
- readonly OVERLAY: "--sv-z-overlay";
623
+ readonly SPINNER: "--sv-z-slot-spinner";
564
624
  readonly INDICATOR: "--sv-z-indicator";
565
625
  readonly PROGRESS: "--sv-z-progress";
566
626
  };
@@ -576,7 +636,7 @@ declare const Z_INDEX_CSS_VARS: {
576
636
  * - Avoids global selectors
577
637
  * - Follows BEM-like naming: sv-video-slot__[element]--[modifier]
578
638
  */
579
- declare const VIDEO_SLOT_CSS = "\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n * Z-INDEX LAYERING CONTRACT\n * \n * Override these variables to customize layer stacking:\n * --sv-z-poster: Poster/Video layer (default: 1)\n * --sv-z-overlay: Overlay/Spinner layer (default: 2)\n * --sv-z-indicator: Error/Play indicator layer (default: 3)\n * --sv-z-progress: Progress bar layer (default: 4)\n *\n * Layer Stack (bottom to top):\n * \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n * \u2502 z-index: 4 - Progress Bar (topmost) \u2502\n * \u2502 z-index: 3 - Error UI / Play Indicator \u2502\n * \u2502 z-index: 2 - Overlay / Spinner \u2502\n * \u2502 z-index: 1 - Poster / Video \u2502\n * \u2502 z-index: 0 - Slot Container (base) \u2502\n * \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n * \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n * VideoSlot - Container for individual video in feed\n * \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.sv-video-slot {\n position: relative;\n width: 100%;\n height: 100%;\n overflow: hidden;\n background-color: var(--sv-bg-primary, #000);\n /* Touch optimization */\n touch-action: none;\n user-select: none;\n -webkit-user-select: none;\n /* GPU acceleration */\n will-change: contents;\n contain: layout style paint;\n}\n\n/* Active slot (currently visible) */\n.sv-video-slot--active {\n z-index: 1;\n}\n\n/* Loading state */\n.sv-video-slot--loading .sv-video-slot__video {\n opacity: 0;\n}\n\n.sv-video-slot--loading .sv-video-slot__poster {\n opacity: 1;\n}\n\n/* Playing state */\n.sv-video-slot--playing .sv-video-slot__video {\n opacity: 1;\n}\n\n.sv-video-slot--playing .sv-video-slot__poster {\n opacity: 0;\n}\n\n/* Paused state - keep poster hidden (show paused video frame, not poster) */\n.sv-video-slot--paused .sv-video-slot__video {\n opacity: 1;\n}\n\n.sv-video-slot--paused .sv-video-slot__poster {\n opacity: 0;\n}\n\n/* Hidden poster (explicit class applied by VideoSlotPoster component) */\n.sv-video-slot__poster--hidden {\n opacity: 0;\n pointer-events: none;\n}\n\n/* Error state */\n.sv-video-slot--error {\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n * Video Element\n * \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.sv-video-slot__video-container {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.sv-video-slot__video {\n width: 100%;\n height: 100%;\n object-fit: cover;\n background-color: transparent;\n /* Smooth fade transition */\n transition: opacity var(--sv-transition-duration, 300ms) ease-out;\n}\n\n/* Video loaded but not playing yet */\n.sv-video-slot__video[data-loaded=\"true\"] {\n opacity: 1;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n * Poster / Thumbnail / First Frame\n * \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.sv-video-slot__poster {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background-color: var(--sv-bg-primary, #000);\n z-index: var(--sv-z-poster, 1);\n /* Smooth fade transition */\n transition: opacity var(--sv-transition-duration, 300ms) ease-out;\n pointer-events: none;\n}\n\n.sv-video-slot__poster-image {\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n\n/* First frame (client-captured) */\n.sv-video-slot__poster--first-frame .sv-video-slot__poster-image {\n image-rendering: auto;\n}\n\n/* Skeleton placeholder */\n.sv-video-slot__poster--skeleton {\n background: linear-gradient(\n 110deg,\n var(--sv-skeleton-bg, rgba(255, 255, 255, 0.05)) 0%,\n var(--sv-skeleton-shimmer, rgba(255, 255, 255, 0.1)) 50%,\n var(--sv-skeleton-bg, rgba(255, 255, 255, 0.05)) 100%\n );\n background-size: 200% 100%;\n animation: sv-slot-skeleton-shimmer 1.5s ease-in-out infinite;\n}\n\n@keyframes sv-slot-skeleton-shimmer {\n 0% {\n background-position: 200% 0;\n }\n 100% {\n background-position: -200% 0;\n }\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n * Overlay Container\n * \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.sv-video-slot__overlay {\n position: absolute;\n inset: 0;\n z-index: var(--sv-z-overlay, 2);\n /* Gradient background for better text visibility */\n background: linear-gradient(\n to top,\n rgba(0, 0, 0, 0.6) 0%,\n rgba(0, 0, 0, 0.3) 20%,\n transparent 40%,\n transparent 60%,\n rgba(0, 0, 0, 0.2) 80%,\n rgba(0, 0, 0, 0.4) 100%\n );\n pointer-events: none;\n}\n\n/* \n * Overlay sections should NOT block clicks (pass-through to container for play/pause)\n * Only actual interactive elements (buttons, links) inside sections should receive clicks.\n * \n * DO NOT use: .sv-video-slot__overlay > * { pointer-events: auto; }\n * This would make entire sections block clicks even on \"empty\" areas.\n */\n.sv-video-slot__overlay-top,\n.sv-video-slot__overlay-bottom,\n.sv-video-slot__overlay-left,\n.sv-video-slot__overlay-right,\n.sv-video-slot__actions,\n.sv-video-slot__author {\n pointer-events: none;\n}\n\n/* Only interactive elements inside overlay sections should receive clicks */\n.sv-video-slot__overlay button,\n.sv-video-slot__overlay a,\n.sv-video-slot__overlay [role=\"button\"],\n.sv-video-slot__overlay input,\n.sv-video-slot__overlay label,\n.sv-video-slot__overlay .sv-progress-bar,\n.sv-video-slot__overlay .sv-author-info,\n.sv-video-slot__overlay .sv-video-info,\n.sv-video-slot__overlay .sv-action-bar {\n pointer-events: auto;\n}\n\n/* Overlay sections */\n.sv-video-slot__overlay-top {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n padding: var(--sv-spacing-md, 16px);\n padding-top: calc(var(--sv-safe-area-top, 0px) + var(--sv-spacing-md, 16px));\n}\n\n.sv-video-slot__overlay-bottom {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0px;\n padding: var(--sv-spacing-md, 16px);\n padding-bottom: calc(var(--sv-safe-area-bottom, 0px) + var(--sv-spacing-md, 16px));\n display: flex;\n flex-direction: column;\n gap: var(--sv-spacing-sm, 8px);\n}\n\n.sv-video-slot__overlay-left {\n position: absolute;\n top: 50%;\n left: 0;\n transform: translateY(-50%);\n padding: var(--sv-spacing-md, 16px);\n}\n\n.sv-video-slot__overlay-right {\n position: absolute;\n top: 50%;\n right: 0;\n transform: translateY(-50%);\n padding: var(--sv-spacing-md, 16px);\n}\n\n/* Action bar (typically right side) */\n.sv-video-slot__actions {\n position: absolute;\n right: var(--sv-spacing-sm, 8px);\n bottom: calc(var(--sv-safe-area-bottom, 0px) + 80px);\n display: flex;\n flex-direction: column;\n align-items: center;\n}\n\n/* Author info (above bottom section, left side) */\n.sv-video-slot__author {\n position: absolute;\n left: var(--sv-spacing-md, 16px);\n /* Position above .sv-video-slot__overlay-bottom (which has ~80px content) */\n bottom: calc(var(--sv-safe-area-bottom, 0px) + 100px);\n right: 80px; /* Leave space for actions */\n max-width: calc(100% - 100px); /* Don't overlap with actions */\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n * Error State\n * \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.sv-video-slot__error {\n position: absolute;\n inset: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n background-color: var(--sv-bg-primary, #000);\n color: var(--sv-text-secondary, #999);\n text-align: center;\n padding: var(--sv-spacing-lg, 20px);\n gap: var(--sv-spacing-md, 16px);\n z-index: var(--sv-z-indicator, 3);\n}\n\n.sv-video-slot__error-icon {\n font-size: 48px;\n opacity: 0.6;\n}\n\n.sv-video-slot__error-message {\n font-size: var(--sv-font-size-md, 14px);\n max-width: 200px;\n}\n\n.sv-video-slot__error-retry {\n padding: var(--sv-spacing-sm, 8px) var(--sv-spacing-md, 16px);\n background-color: var(--sv-color-primary, #fe2c55);\n color: #fff;\n border: none;\n border-radius: var(--sv-radius-md, 8px);\n font-size: var(--sv-font-size-sm, 12px);\n font-weight: 600;\n cursor: pointer;\n transition: background-color 150ms ease;\n}\n\n.sv-video-slot__error-retry:hover {\n background-color: var(--sv-color-primary-hover, #e02850);\n}\n\n.sv-video-slot__error-retry:active {\n transform: scale(0.96);\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n * Loading Spinner\n * \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.sv-video-slot__spinner {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n z-index: var(--sv-z-overlay, 2);\n}\n\n.sv-video-slot__spinner-circle {\n width: 40px;\n height: 40px;\n border: 3px solid rgba(255, 255, 255, 0.2);\n border-top-color: var(--sv-color-primary, #fe2c55);\n border-radius: 50%;\n animation: sv-slot-spin 0.8s linear infinite;\n}\n\n@keyframes sv-slot-spin {\n to {\n transform: rotate(360deg);\n }\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n * Play/Pause Visual Indicator\n * \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.sv-video-slot__play-indicator {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 64px;\n height: 64px;\n display: flex;\n align-items: center;\n justify-content: center;\n background-color: rgba(0, 0, 0, 0.5);\n border-radius: 50%;\n z-index: var(--sv-z-indicator, 3);\n opacity: 0;\n transition: opacity 200ms ease;\n pointer-events: none;\n}\n\n.sv-video-slot__play-indicator--visible {\n opacity: 1;\n animation: sv-slot-play-indicator 400ms ease-out forwards;\n}\n\n@keyframes sv-slot-play-indicator {\n 0% {\n opacity: 1;\n transform: translate(-50%, -50%) scale(1);\n }\n 100% {\n opacity: 0;\n transform: translate(-50%, -50%) scale(1.5);\n }\n}\n\n.sv-video-slot__play-indicator-icon {\n color: #fff;\n font-size: 28px;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n * Progress Bar\n * \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.sv-video-slot__progress {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n height: 3px;\n background-color: rgba(255, 255, 255, 0.2);\n z-index: var(--sv-z-progress, 4);\n}\n\n.sv-video-slot__progress-bar {\n height: 100%;\n background-color: var(--sv-color-primary, #fe2c55);\n transition: width 100ms linear;\n}\n\n.sv-video-slot__progress-buffered {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n background-color: rgba(255, 255, 255, 0.3);\n}\n";
639
+ declare const VIDEO_SLOT_CSS = "\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n * Z-INDEX LAYERING CONTRACT\n * \n * Override these variables to customize layer stacking:\n * --sv-z-poster: Poster/Thumbnail layer (default: 3)\n * --sv-z-slot-spinner: Loading spinner layer (default: 2)\n * --sv-z-indicator: Error/Play indicator layer (default: 4)\n * --sv-z-progress: Progress bar layer (default: 5)\n *\n * Layer Stack (bottom to top):\n * \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n * \u2502 z-index: 5 - Progress Bar (topmost) \u2502\n * \u2502 z-index: 4 - Error UI / Play Indicator \u2502\n * \u2502 z-index: 3 - Poster / Thumbnail \u2502\n * \u2502 z-index: 2 - Loading Spinner \u2502\n * \u2502 z-index: 1 - Video Element \u2502\n * \u2502 z-index: 0 - Slot Container (base) \u2502\n * \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n * \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n * VideoSlot - Container for individual video in feed\n * \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.sv-video-slot {\n position: relative;\n width: 100%;\n height: 100%;\n overflow: hidden;\n background-color: var(--sv-bg-primary, #000);\n /* Touch optimization */\n touch-action: none;\n user-select: none;\n -webkit-user-select: none;\n /* GPU acceleration */\n will-change: contents;\n contain: layout style paint;\n}\n\n/* Active slot (currently visible) */\n.sv-video-slot--active {\n z-index: 1;\n}\n\n/* Loading state */\n.sv-video-slot--loading .sv-video-slot__video {\n opacity: 0;\n}\n\n.sv-video-slot--loading .sv-video-slot__poster {\n opacity: 1;\n}\n\n/* Playing state */\n.sv-video-slot--playing .sv-video-slot__video {\n opacity: 1;\n}\n\n.sv-video-slot--playing .sv-video-slot__poster {\n opacity: 0;\n}\n\n/* Paused state - keep poster hidden (show paused video frame, not poster) */\n.sv-video-slot--paused .sv-video-slot__video {\n opacity: 1;\n}\n\n.sv-video-slot--paused .sv-video-slot__poster {\n opacity: 0;\n}\n\n/* Hidden poster (explicit class applied by VideoSlotPoster component) */\n.sv-video-slot__poster--hidden {\n opacity: 0;\n pointer-events: none;\n}\n\n/* Error state */\n.sv-video-slot--error {\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n * Video Element\n * \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.sv-video-slot__video-container {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.sv-video-slot__video {\n width: 100%;\n height: 100%;\n object-fit: cover;\n background-color: transparent;\n /* Smooth fade transition */\n transition: opacity var(--sv-transition-duration, 300ms) ease-out;\n}\n\n/* Video loaded but not playing yet */\n.sv-video-slot__video[data-loaded=\"true\"] {\n opacity: 1;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n * Poster / Thumbnail / First Frame\n * \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.sv-video-slot__poster {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background-color: var(--sv-bg-primary, #000);\n /* Poster should be ABOVE spinner (z-index: 2) so thumbnail shows instead of spinner */\n z-index: var(--sv-z-poster, 3);\n /* Smooth fade transition */\n transition: opacity var(--sv-transition-duration, 300ms) ease-out;\n pointer-events: none;\n}\n\n.sv-video-slot__poster-image {\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n\n/* First frame (client-captured) */\n.sv-video-slot__poster--first-frame .sv-video-slot__poster-image {\n image-rendering: auto;\n}\n\n/* Skeleton placeholder */\n.sv-video-slot__poster--skeleton {\n background: linear-gradient(\n 110deg,\n var(--sv-skeleton-bg, rgba(255, 255, 255, 0.05)) 0%,\n var(--sv-skeleton-shimmer, rgba(255, 255, 255, 0.1)) 50%,\n var(--sv-skeleton-bg, rgba(255, 255, 255, 0.05)) 100%\n );\n background-size: 200% 100%;\n animation: sv-slot-skeleton-shimmer 1.5s ease-in-out infinite;\n}\n\n@keyframes sv-slot-skeleton-shimmer {\n 0% {\n background-position: 200% 0;\n }\n 100% {\n background-position: -200% 0;\n }\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n * Overlay Container\n * \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.sv-video-slot__overlay {\n position: absolute;\n inset: 0;\n z-index: var(--sv-z-overlay, 2);\n /* Gradient background for better text visibility */\n background: linear-gradient(\n to top,\n rgba(0, 0, 0, 0.6) 0%,\n rgba(0, 0, 0, 0.3) 20%,\n transparent 40%,\n transparent 60%,\n rgba(0, 0, 0, 0.2) 80%,\n rgba(0, 0, 0, 0.4) 100%\n );\n pointer-events: none;\n}\n\n/* \n * Overlay sections should NOT block clicks (pass-through to container for play/pause)\n * Only actual interactive elements (buttons, links) inside sections should receive clicks.\n * \n * DO NOT use: .sv-video-slot__overlay > * { pointer-events: auto; }\n * This would make entire sections block clicks even on \"empty\" areas.\n */\n.sv-video-slot__overlay-top,\n.sv-video-slot__overlay-bottom,\n.sv-video-slot__overlay-left,\n.sv-video-slot__overlay-right,\n.sv-video-slot__actions,\n.sv-video-slot__author {\n pointer-events: none;\n}\n\n/* Only interactive elements inside overlay sections should receive clicks */\n.sv-video-slot__overlay button,\n.sv-video-slot__overlay a,\n.sv-video-slot__overlay [role=\"button\"],\n.sv-video-slot__overlay input,\n.sv-video-slot__overlay label,\n.sv-video-slot__overlay .sv-progress-bar,\n.sv-video-slot__overlay .sv-author-info,\n.sv-video-slot__overlay .sv-video-info,\n.sv-video-slot__overlay .sv-action-bar {\n pointer-events: auto;\n}\n\n/* Overlay sections */\n.sv-video-slot__overlay-top {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n padding: var(--sv-spacing-md, 16px);\n padding-top: calc(var(--sv-safe-area-top, 0px) + var(--sv-spacing-md, 16px));\n}\n\n.sv-video-slot__overlay-bottom {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0px;\n padding: var(--sv-spacing-md, 16px);\n padding-bottom: calc(var(--sv-safe-area-bottom, 0px) + var(--sv-spacing-md, 16px));\n display: flex;\n flex-direction: column;\n gap: var(--sv-spacing-sm, 8px);\n}\n\n.sv-video-slot__overlay-left {\n position: absolute;\n top: 50%;\n left: 0;\n transform: translateY(-50%);\n padding: var(--sv-spacing-md, 16px);\n}\n\n.sv-video-slot__overlay-right {\n position: absolute;\n top: 50%;\n right: 0;\n transform: translateY(-50%);\n padding: var(--sv-spacing-md, 16px);\n}\n\n/* Action bar (typically right side) */\n.sv-video-slot__actions {\n position: absolute;\n right: var(--sv-spacing-sm, 8px);\n bottom: calc(var(--sv-safe-area-bottom, 0px) + 80px);\n display: flex;\n flex-direction: column;\n align-items: center;\n}\n\n/* Author info (above bottom section, left side) */\n.sv-video-slot__author {\n position: absolute;\n left: var(--sv-spacing-md, 16px);\n /* Position above .sv-video-slot__overlay-bottom (which has ~80px content) */\n bottom: calc(var(--sv-safe-area-bottom, 0px) + 100px);\n right: 80px; /* Leave space for actions */\n max-width: calc(100% - 100px); /* Don't overlap with actions */\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n * Error State\n * \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.sv-video-slot__error {\n position: absolute;\n inset: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n background-color: var(--sv-bg-primary, #000);\n color: var(--sv-text-secondary, #999);\n text-align: center;\n padding: var(--sv-spacing-lg, 20px);\n gap: var(--sv-spacing-md, 16px);\n z-index: var(--sv-z-indicator, 3);\n}\n\n.sv-video-slot__error-icon {\n font-size: 48px;\n opacity: 0.6;\n}\n\n.sv-video-slot__error-message {\n font-size: var(--sv-font-size-md, 14px);\n max-width: 200px;\n}\n\n.sv-video-slot__error-retry {\n padding: var(--sv-spacing-sm, 8px) var(--sv-spacing-md, 16px);\n background-color: var(--sv-color-primary, #fe2c55);\n color: #fff;\n border: none;\n border-radius: var(--sv-radius-md, 8px);\n font-size: var(--sv-font-size-sm, 12px);\n font-weight: 600;\n cursor: pointer;\n transition: background-color 150ms ease;\n}\n\n.sv-video-slot__error-retry:hover {\n background-color: var(--sv-color-primary-hover, #e02850);\n}\n\n.sv-video-slot__error-retry:active {\n transform: scale(0.96);\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n * Loading Spinner\n * \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.sv-video-slot__spinner {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n /* Use slot-specific z-index (not global --sv-z-overlay which may conflict) */\n z-index: var(--sv-z-slot-spinner, 2);\n}\n\n.sv-video-slot__spinner-circle {\n width: 40px;\n height: 40px;\n border: 3px solid rgba(255, 255, 255, 0.2);\n border-top-color: var(--sv-color-primary, #fe2c55);\n border-radius: 50%;\n animation: sv-slot-spin 0.8s linear infinite;\n}\n\n@keyframes sv-slot-spin {\n to {\n transform: rotate(360deg);\n }\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n * Play/Pause Visual Indicator\n * \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.sv-video-slot__play-indicator {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 64px;\n height: 64px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 50%;\n z-index: var(--sv-z-indicator, 3);\n opacity: 0;\n transition: opacity 200ms ease-out;\n pointer-events: none;\n}\n\n/* Show indicator */\n.sv-video-slot__play-indicator--visible {\n opacity: 0.65;\n}\n\n/* Persist mode: stay visible, no animation */\n.sv-video-slot__play-indicator--persist {\n opacity: 0.65;\n}\n\n/* Animating out: scale up + fade out */\n.sv-video-slot__play-indicator--animating-out {\n animation: sv-slot-indicator-out var(--sv-indicator-duration, 200ms) ease-out forwards;\n}\n\n@keyframes sv-slot-indicator-out {\n 0% {\n opacity: 1;\n transform: translate(-50%, -50%) scale(1);\n }\n 100% {\n opacity: 0;\n transform: translate(-50%, -50%) scale(1.5);\n }\n}\n\n.sv-video-slot__play-indicator-icon {\n color: #fff;\n font-size: 28px;\n width: 100%;\n height: 100%;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n * Progress Bar\n * \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.sv-video-slot__progress {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n height: 3px;\n background-color: rgba(255, 255, 255, 0.2);\n z-index: var(--sv-z-progress, 4);\n}\n\n.sv-video-slot__progress-bar {\n height: 100%;\n background-color: var(--sv-color-primary, #fe2c55);\n transition: width 100ms linear;\n}\n\n.sv-video-slot__progress-buffered {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n background-color: rgba(255, 255, 255, 0.3);\n}\n";
580
640
 
581
641
  /**
582
642
  * VideoSlotLikeAnimation CSS Styles
@@ -589,4 +649,4 @@ declare const VIDEO_SLOT_CSS = "\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\
589
649
  */
590
650
  declare const VIDEO_SLOT_LIKE_ANIMATION_CSS = "\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n Container\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.sv-like-animation-container {\n position: absolute;\n inset: 0;\n overflow: hidden;\n pointer-events: none;\n z-index: var(--sv-z-indicator, 3);\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n Single Heart\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.sv-like-heart {\n position: absolute;\n left: var(--sv-heart-x, 50%);\n top: var(--sv-heart-y, 50%);\n transform: translate(-50%, -50%);\n pointer-events: none;\n will-change: transform, opacity;\n}\n\n.sv-like-heart__icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: var(--sv-heart-size, 80px);\n height: var(--sv-heart-size, 80px);\n transform-origin: center;\n animation-duration: var(--sv-heart-duration, 1000ms);\n animation-fill-mode: forwards;\n animation-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n Float Animation (TikTok style)\n - Hearts float upward while wiggling\n - Fades out near the top\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.sv-like-heart--float .sv-like-heart__icon {\n animation-name: sv-heart-float;\n animation-timing-function: ease-out;\n}\n\n@keyframes sv-heart-float {\n 0% {\n opacity: 0;\n transform: scale(0) rotate(var(--sv-heart-rotation, 0deg));\n }\n 10% {\n opacity: 1;\n transform: scale(calc(var(--sv-heart-scale, 1) * 1.2)) rotate(var(--sv-heart-rotation, 0deg));\n }\n 20% {\n transform: \n scale(var(--sv-heart-scale, 1)) \n rotate(var(--sv-heart-rotation, 0deg)) \n translateY(-20px) \n translateX(10px);\n }\n 40% {\n transform: \n scale(var(--sv-heart-scale, 1)) \n rotate(calc(var(--sv-heart-rotation, 0deg) - 10deg)) \n translateY(-60px) \n translateX(-15px);\n }\n 60% {\n opacity: 1;\n transform: \n scale(var(--sv-heart-scale, 1)) \n rotate(calc(var(--sv-heart-rotation, 0deg) + 15deg)) \n translateY(-100px) \n translateX(10px);\n }\n 80% {\n opacity: 0.6;\n transform: \n scale(calc(var(--sv-heart-scale, 1) * 0.9)) \n rotate(calc(var(--sv-heart-rotation, 0deg) - 5deg)) \n translateY(-140px) \n translateX(-5px);\n }\n 100% {\n opacity: 0;\n transform: \n scale(calc(var(--sv-heart-scale, 1) * 0.7)) \n rotate(var(--sv-heart-rotation, 0deg)) \n translateY(-180px) \n translateX(0);\n }\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n Burst Animation\n - Heart bursts outward with energy\n - Scales up quickly then fades\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.sv-like-heart--burst .sv-like-heart__icon {\n animation-name: sv-heart-burst;\n animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1.275);\n}\n\n@keyframes sv-heart-burst {\n 0% {\n opacity: 0;\n transform: scale(0) rotate(var(--sv-heart-rotation, 0deg));\n }\n 20% {\n opacity: 1;\n transform: scale(calc(var(--sv-heart-scale, 1) * 1.5)) rotate(calc(var(--sv-heart-rotation, 0deg) + 15deg));\n }\n 40% {\n transform: scale(calc(var(--sv-heart-scale, 1) * 1.2)) rotate(calc(var(--sv-heart-rotation, 0deg) - 10deg));\n }\n 60% {\n opacity: 1;\n transform: scale(var(--sv-heart-scale, 1)) rotate(var(--sv-heart-rotation, 0deg)) translateY(-30px);\n }\n 80% {\n opacity: 0.5;\n transform: scale(calc(var(--sv-heart-scale, 1) * 0.9)) rotate(var(--sv-heart-rotation, 0deg)) translateY(-60px);\n }\n 100% {\n opacity: 0;\n transform: scale(calc(var(--sv-heart-scale, 1) * 0.6)) rotate(var(--sv-heart-rotation, 0deg)) translateY(-80px);\n }\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n Scale Animation\n - Simple scale up and fade\n - Clean and minimal\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.sv-like-heart--scale .sv-like-heart__icon {\n animation-name: sv-heart-scale;\n animation-timing-function: ease-out;\n}\n\n@keyframes sv-heart-scale {\n 0% {\n opacity: 0;\n transform: scale(0);\n }\n 20% {\n opacity: 1;\n transform: scale(calc(var(--sv-heart-scale, 1) * 1.3));\n }\n 40% {\n transform: scale(var(--sv-heart-scale, 1));\n }\n 70% {\n opacity: 1;\n transform: scale(var(--sv-heart-scale, 1));\n }\n 100% {\n opacity: 0;\n transform: scale(calc(var(--sv-heart-scale, 1) * 1.1));\n }\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n Bounce Animation\n - Heart bounces in place with energy\n - Playful and fun\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.sv-like-heart--bounce .sv-like-heart__icon {\n animation-name: sv-heart-bounce;\n animation-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55);\n}\n\n@keyframes sv-heart-bounce {\n 0% {\n opacity: 0;\n transform: scale(0) rotate(var(--sv-heart-rotation, 0deg)) translateY(0);\n }\n 15% {\n opacity: 1;\n transform: scale(calc(var(--sv-heart-scale, 1) * 1.4)) rotate(calc(var(--sv-heart-rotation, 0deg) + 10deg)) translateY(-30px);\n }\n 30% {\n transform: scale(calc(var(--sv-heart-scale, 1) * 0.9)) rotate(calc(var(--sv-heart-rotation, 0deg) - 5deg)) translateY(10px);\n }\n 45% {\n transform: scale(calc(var(--sv-heart-scale, 1) * 1.15)) rotate(calc(var(--sv-heart-rotation, 0deg) + 5deg)) translateY(-15px);\n }\n 60% {\n opacity: 1;\n transform: scale(var(--sv-heart-scale, 1)) rotate(var(--sv-heart-rotation, 0deg)) translateY(0);\n }\n 80% {\n opacity: 0.7;\n transform: scale(var(--sv-heart-scale, 1)) rotate(var(--sv-heart-rotation, 0deg)) translateY(-10px);\n }\n 100% {\n opacity: 0;\n transform: scale(calc(var(--sv-heart-scale, 1) * 0.8)) rotate(var(--sv-heart-rotation, 0deg)) translateY(-20px);\n }\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n Accessibility: Reduce motion\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n@media (prefers-reduced-motion: reduce) {\n .sv-like-heart__icon {\n animation-duration: 0.5s !important;\n }\n \n .sv-like-heart--float .sv-like-heart__icon,\n .sv-like-heart--burst .sv-like-heart__icon,\n .sv-like-heart--bounce .sv-like-heart__icon {\n animation-name: sv-heart-scale;\n }\n}\n";
591
651
 
592
- export { type HeartInstance, LOADING_ATTR, LOADING_DATASET_KEY, type LikeAnimationElement, type LikeAnimationStyle, PLAYER_STATE_ATTR, PLAYER_STATE_DATASET_KEY, SLOT_ACTIVE_ATTR, SLOT_ACTIVE_CLASS, SLOT_ACTIVE_DATASET_KEY, SLOT_CLASS, SLOT_ERROR_CLASS, SLOT_LOADING_CLASS, SLOT_PAUSED_CLASS, SLOT_PLAYING_CLASS, VIDEO_ID_ATTR, VIDEO_ID_DATASET_KEY, VIDEO_SLOT_CSS, VIDEO_SLOT_LIKE_ANIMATION_CSS, VideoSlotContext, type VideoSlotContextValue, VideoSlotHeadless, type VideoSlotHeadlessExtendedProps, VideoSlotLikeAnimation, type VideoSlotLikeAnimationProps, type VideoSlotLikeAnimationRef, VideoSlotOverlay, type VideoSlotOverlayProps, type VideoSlotOverlayRegionProps, VideoSlotPoster, type VideoSlotPosterProps, VideoSlotSkeleton, Z_INDEX, Z_INDEX_CSS_VARS, useOptionalVideoSlotContext, useVideoSlotContext, useVideoSlotIsActive, useVideoSlotIsPlaying, useVideoSlotPlayer, useVideoSlotResource, useVideoSlotVideo };
652
+ export { type HeartInstance, LOADING_ATTR, LOADING_DATASET_KEY, type LikeAnimationElement, type LikeAnimationStyle, PLAYER_STATE_ATTR, PLAYER_STATE_DATASET_KEY, SLOT_ACTIVE_ATTR, SLOT_ACTIVE_CLASS, SLOT_ACTIVE_DATASET_KEY, SLOT_CLASS, SLOT_ERROR_CLASS, SLOT_LOADING_CLASS, SLOT_PAUSED_CLASS, SLOT_PLAYING_CLASS, VIDEO_ID_ATTR, VIDEO_ID_DATASET_KEY, VIDEO_SLOT_CSS, VIDEO_SLOT_LIKE_ANIMATION_CSS, VideoSlotContext, type VideoSlotContextValue, VideoSlotHeadless, type VideoSlotHeadlessExtendedProps, VideoSlotLikeAnimation, type VideoSlotLikeAnimationProps, type VideoSlotLikeAnimationRef, VideoSlotOverlay, type VideoSlotOverlayProps, type VideoSlotOverlayRegionProps, VideoSlotPlayIndicator, type VideoSlotPlayIndicatorProps, VideoSlotPoster, type VideoSlotPosterProps, VideoSlotSkeleton, Z_INDEX, Z_INDEX_CSS_VARS, useOptionalVideoSlotContext, useVideoSlotContext, useVideoSlotIsActive, useVideoSlotIsPlaying, useVideoSlotPlayer, useVideoSlotResource, useVideoSlotVideo };
@@ -1 +1 @@
1
- export { LOADING_ATTR, LOADING_DATASET_KEY, PLAYER_STATE_ATTR, PLAYER_STATE_DATASET_KEY, SLOT_ACTIVE_ATTR, SLOT_ACTIVE_CLASS, SLOT_ACTIVE_DATASET_KEY, SLOT_CLASS, SLOT_ERROR_CLASS, SLOT_LOADING_CLASS, SLOT_PAUSED_CLASS, SLOT_PLAYING_CLASS, VIDEO_ID_ATTR, VIDEO_ID_DATASET_KEY, VIDEO_SLOT_CSS, VIDEO_SLOT_LIKE_ANIMATION_CSS, VideoSlotContext, VideoSlotHeadless, VideoSlotLikeAnimation, VideoSlotOverlay, VideoSlotPoster, VideoSlotSkeleton, Z_INDEX, Z_INDEX_CSS_VARS, useOptionalVideoSlotContext, useVideoSlotContext, useVideoSlotIsActive, useVideoSlotIsPlaying, useVideoSlotPlayer, useVideoSlotResource, useVideoSlotVideo } from '../../chunk-XAOEHLOX.js';
1
+ export { LOADING_ATTR, LOADING_DATASET_KEY, PLAYER_STATE_ATTR, PLAYER_STATE_DATASET_KEY, SLOT_ACTIVE_ATTR, SLOT_ACTIVE_CLASS, SLOT_ACTIVE_DATASET_KEY, SLOT_CLASS, SLOT_ERROR_CLASS, SLOT_LOADING_CLASS, SLOT_PAUSED_CLASS, SLOT_PLAYING_CLASS, VIDEO_ID_ATTR, VIDEO_ID_DATASET_KEY, VIDEO_SLOT_CSS, VIDEO_SLOT_LIKE_ANIMATION_CSS, VideoSlotContext, VideoSlotHeadless, VideoSlotLikeAnimation, VideoSlotOverlay, VideoSlotPlayIndicator, VideoSlotPoster, VideoSlotSkeleton, Z_INDEX, Z_INDEX_CSS_VARS, useOptionalVideoSlotContext, useVideoSlotContext, useVideoSlotIsActive, useVideoSlotIsPlaying, useVideoSlotPlayer, useVideoSlotResource, useVideoSlotVideo } from '../../chunk-XDIH66C4.js';