eb-player 2.0.4 → 2.0.7

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.
@@ -1 +1 @@
1
- {"version":3,"file":"eb-player.d.ts","sourceRoot":"","sources":["../../../src/eb-player.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AASH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAGjD;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,qFAAqF;IACrF,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,uEAAuE;IACvE,KAAK,IAAI,IAAI,CAAA;IACb,8EAA8E;IAC9E,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAA;CAC5B;AA4DD;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,aAAa,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,eAAe,CAiK3E;AAED;;;GAGG;AACH,wBAAgB,IAAI,IAAI,IAAI,CAK3B;AAED;;;GAGG;AACH,wBAAgB,OAAO,IAAI,IAAI,CAU9B"}
1
+ {"version":3,"file":"eb-player.d.ts","sourceRoot":"","sources":["../../../src/eb-player.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAWH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAGjD;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,qFAAqF;IACrF,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,uEAAuE;IACvE,KAAK,IAAI,IAAI,CAAA;IACb,8EAA8E;IAC9E,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAA;CAC5B;AA4DD;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,aAAa,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,eAAe,CAuL3E;AAED;;;GAGG;AACH,wBAAgB,IAAI,IAAI,IAAI,CAK3B;AAED;;;GAGG;AACH,wBAAgB,OAAO,IAAI,IAAI,CAU9B"}
@@ -12,12 +12,19 @@
12
12
  * CRITICAL: Never call driver.startLoad() from video 'play' event handler (Pitfall 3).
13
13
  */
14
14
  import { BaseEngine } from './base-engine';
15
+ import { CDNTokenManager } from './cdn-token-manager';
15
16
  export declare class HlsEngine extends BaseEngine {
16
17
  private driver;
17
18
  private tokenManager;
18
19
  private autoQuality;
19
20
  private eventState;
20
21
  getDriver(): unknown | null;
22
+ /**
23
+ * Returns the CDN token manager used by this engine.
24
+ * Allows the snapshot handler to share the same manager (avoids duplicate token requests).
25
+ * Returns null when no token URL is configured or before init().
26
+ */
27
+ getTokenManager(): CDNTokenManager | null;
21
28
  protected onAttach(): Promise<void>;
22
29
  protected onDetach(): void;
23
30
  protected recoverFromStall(attempt: number): void;
@@ -1 +1 @@
1
- {"version":3,"file":"hls.d.ts","sourceRoot":"","sources":["../../../../src/engines/hls.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAmJ1C,qBAAa,SAAU,SAAQ,UAAU;IAEvC,OAAO,CAAC,MAAM,CAAiC;IAC/C,OAAO,CAAC,YAAY,CAA+B;IACnD,OAAO,CAAC,WAAW,CAAO;IAE1B,OAAO,CAAC,UAAU,CAA2B;IAE7C,SAAS,IAAI,OAAO,GAAG,IAAI;cAQX,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAezC,SAAS,CAAC,QAAQ,IAAI,IAAI;IAkB1B,SAAS,CAAC,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAgBjD,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAa/B,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAKlC,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAKhC,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAKnC,OAAO,CAAC,gBAAgB,CAAQ;IAEvB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAajC,UAAU,IAAI,IAAI;IAWlB;;;OAGG;IACH,OAAO,KAAK,QAAQ,GAEnB;YAEa,IAAI;IAkKlB,OAAO,CAAC,oBAAoB;IAgB5B,OAAO,CAAC,iBAAiB;IAezB,OAAO,CAAC,cAAc;IAWtB,OAAO,CAAC,gBAAgB;IAWxB,OAAO,CAAC,qBAAqB;IAa7B,OAAO,CAAC,qBAAqB;IAO7B,OAAO,CAAC,wBAAwB;IAahC,OAAO,CAAC,sBAAsB;CAM/B"}
1
+ {"version":3,"file":"hls.d.ts","sourceRoot":"","sources":["../../../../src/engines/hls.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAE1C,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAiJrD,qBAAa,SAAU,SAAQ,UAAU;IAEvC,OAAO,CAAC,MAAM,CAAiC;IAC/C,OAAO,CAAC,YAAY,CAA+B;IACnD,OAAO,CAAC,WAAW,CAAO;IAE1B,OAAO,CAAC,UAAU,CAA2B;IAE7C,SAAS,IAAI,OAAO,GAAG,IAAI;IAI3B;;;;OAIG;IACH,eAAe,IAAI,eAAe,GAAG,IAAI;cAQzB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAezC,SAAS,CAAC,QAAQ,IAAI,IAAI;IAkB1B,SAAS,CAAC,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAgBjD,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAa/B,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAKlC,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAKhC,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAKnC,OAAO,CAAC,gBAAgB,CAAQ;IAEvB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAajC,UAAU,IAAI,IAAI;IAWlB;;;OAGG;IACH,OAAO,KAAK,QAAQ,GAEnB;YAEa,IAAI;IAkKlB,OAAO,CAAC,oBAAoB;IAgB5B,OAAO,CAAC,iBAAiB;IAezB,OAAO,CAAC,cAAc;IAWtB,OAAO,CAAC,gBAAgB;IAWxB,OAAO,CAAC,qBAAqB;IAa7B,OAAO,CAAC,qBAAqB;IAO7B,OAAO,CAAC,wBAAwB;IAahC,OAAO,CAAC,sBAAsB;CAM/B"}
@@ -1 +1 @@
1
- {"version":3,"file":"hls.d.ts","sourceRoot":"","sources":["../../../../../src/engines/snapshot/hls.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hls.d.ts","sourceRoot":"","sources":["../../../../../src/engines/snapshot/hls.ts"],"names":[],"mappings":"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"}
@@ -5,12 +5,25 @@ import { BaseComponent } from '../base-component';
5
5
  *
6
6
  * - Calls screenfull.toggle() with the closest .eb-player ancestor on click
7
7
  * - Subscribes to screenfull 'change' event to update state.isFullscreen
8
- * - Hidden when screenfull.isEnabled is false (e.g. iOS Safari)
8
+ * - Falls back to video-element fullscreen on mobile (iOS Safari webkitEnterFullscreen,
9
+ * Android Chrome video.requestFullscreen) when the Fullscreen API is not available
10
+ * for arbitrary elements
9
11
  * - Re-renders when state.isFullscreen changes
10
12
  */
11
13
  export declare class FullscreenButton extends BaseComponent {
12
14
  private changeHandler;
15
+ private videoEl;
16
+ private videoFullscreenBeginHandler;
17
+ private videoFullscreenEndHandler;
18
+ private useVideoFallback;
13
19
  protected onConnect(): void;
20
+ /**
21
+ * On mobile browsers the Fullscreen API may not work on arbitrary elements,
22
+ * but the <video> element itself supports fullscreen via webkitEnterFullscreen
23
+ * (iOS Safari) or video.requestFullscreen() (Android Chrome).
24
+ */
25
+ private initVideoFallback;
26
+ private handleDocFullscreenChange;
14
27
  private handleClick;
15
28
  protected template(): TemplateResult;
16
29
  }
@@ -1 +1 @@
1
- {"version":3,"file":"fullscreen-button.d.ts","sourceRoot":"","sources":["../../../../../src/skin/controls/fullscreen-button.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAE9C,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAGjD;;;;;;;GAOG;AACH,qBAAa,gBAAiB,SAAQ,aAAa;IACjD,OAAO,CAAC,aAAa,CAA4B;IAEjD,SAAS,CAAC,SAAS,IAAI,IAAI;IAoB3B,OAAO,CAAC,WAAW;IASnB,SAAS,CAAC,QAAQ,IAAI,cAAc;CAmBrC"}
1
+ {"version":3,"file":"fullscreen-button.d.ts","sourceRoot":"","sources":["../../../../../src/skin/controls/fullscreen-button.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAE9C,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAajD;;;;;;;;;GASG;AACH,qBAAa,gBAAiB,SAAQ,aAAa;IACjD,OAAO,CAAC,aAAa,CAA4B;IACjD,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,2BAA2B,CAA4B;IAC/D,OAAO,CAAC,yBAAyB,CAA4B;IAC7D,OAAO,CAAC,gBAAgB,CAAQ;IAEhC,SAAS,CAAC,SAAS,IAAI,IAAI;IAsB3B;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IA4CzB,OAAO,CAAC,yBAAyB,CAIhC;IAED,OAAO,CAAC,WAAW;IA0BnB,SAAS,CAAC,QAAQ,IAAI,cAAc;CAqBrC"}
@@ -23,17 +23,22 @@ export declare class Seekbar extends BaseComponent {
23
23
  private tooltipX;
24
24
  private previewVideoEl;
25
25
  private snapshotTake;
26
+ private trackEl;
27
+ private boundDocPointerMove;
26
28
  protected onConnect(): void;
29
+ disconnect(): void;
27
30
  private scheduleRender;
28
31
  /**
29
32
  * Converts a PointerEvent clientX to a time value, accounting for RTL mode.
30
33
  * Clamps result to [0, duration].
31
34
  */
32
35
  private eventToTime;
36
+ private startDocumentTracking;
37
+ private stopDocumentTracking;
38
+ private onDocPointerMove;
33
39
  private handlePointerDown;
34
40
  private handlePointerMove;
35
41
  private handlePointerUp;
36
- private handlePointerLeave;
37
42
  private updateTooltip;
38
43
  private findActiveChapter;
39
44
  private findSkippableChapter;
@@ -1 +1 @@
1
- {"version":3,"file":"seekbar.d.ts","sourceRoot":"","sources":["../../../../../src/skin/controls/seekbar.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAMjD;;;;;;;;;;;;;GAaG;AACH,qBAAa,OAAQ,SAAQ,aAAa;IACxC,OAAO,CAAC,UAAU,CAAQ;IAC1B,OAAO,CAAC,SAAS,CAAI;IACrB,OAAO,CAAC,UAAU,CAAQ;IAC1B,OAAO,CAAC,cAAc,CAAQ;IAC9B,OAAO,CAAC,WAAW,CAAI;IACvB,OAAO,CAAC,QAAQ,CAAI;IACpB,OAAO,CAAC,cAAc,CAAgC;IACtD,OAAO,CAAC,YAAY,CAAwC;IAE5D,SAAS,CAAC,SAAS,IAAI,IAAI;IAwB3B,OAAO,CAAC,cAAc;IAWtB;;;OAGG;IACH,OAAO,CAAC,WAAW;IAUnB,OAAO,CAAC,iBAAiB;IAczB,OAAO,CAAC,iBAAiB;IAkBzB,OAAO,CAAC,eAAe;IAiBvB,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,aAAa;IAyBrB,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,oBAAoB;IAY5B,OAAO,CAAC,iBAAiB;IA6BzB,SAAS,CAAC,QAAQ,IAAI,cAAc;CA2FrC"}
1
+ {"version":3,"file":"seekbar.d.ts","sourceRoot":"","sources":["../../../../../src/skin/controls/seekbar.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAMjD;;;;;;;;;;;;;GAaG;AACH,qBAAa,OAAQ,SAAQ,aAAa;IACxC,OAAO,CAAC,UAAU,CAAQ;IAC1B,OAAO,CAAC,SAAS,CAAI;IACrB,OAAO,CAAC,UAAU,CAAQ;IAC1B,OAAO,CAAC,cAAc,CAAQ;IAC9B,OAAO,CAAC,WAAW,CAAI;IACvB,OAAO,CAAC,QAAQ,CAAI;IACpB,OAAO,CAAC,cAAc,CAAgC;IACtD,OAAO,CAAC,YAAY,CAAwC;IAC5D,OAAO,CAAC,OAAO,CAA2B;IAC1C,OAAO,CAAC,mBAAmB,CAA+C;IAE1E,SAAS,CAAC,SAAS,IAAI,IAAI;IAsBlB,UAAU,IAAI,IAAI;IAO3B,OAAO,CAAC,cAAc;IAWtB;;;OAGG;IACH,OAAO,CAAC,WAAW;IAcnB,OAAO,CAAC,qBAAqB;IAO7B,OAAO,CAAC,oBAAoB;IAO5B,OAAO,CAAC,gBAAgB;IAmBxB,OAAO,CAAC,iBAAiB;IAezB,OAAO,CAAC,iBAAiB;IAkBzB,OAAO,CAAC,eAAe;IAiBvB,OAAO,CAAC,aAAa;IA6BrB,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,oBAAoB;IAY5B,OAAO,CAAC,iBAAiB;IA6BzB,SAAS,CAAC,QAAQ,IAAI,cAAc;CAyFrC"}
@@ -1,7 +1,7 @@
1
1
  var EBPlayerBundle = (function (exports) {
2
2
  'use strict';
3
3
 
4
- var __EB_PLAYER_VERSION__ = "2.0.3";
4
+ var __EB_PLAYER_VERSION__ = "2.0.6";
5
5
 
6
6
  function styleInject(css, ref) {
7
7
  if ( ref === void 0 ) ref = {};
@@ -51,7 +51,7 @@ var EBPlayerBundle = (function (exports) {
51
51
  var css_248z$1 = "/**\n * V2 theme — based on snrtlive.ma (Aloula) player styling\n *\n * Applied when the container has [data-theme=\"v2\"].\n * Dark UI with orange accent (#ff841f), Inter font, rounded container,\n * backdrop-blur panels, expandable volume, two-row bottom bar\n * (seekbar on top, buttons below), gradient overlays, centered\n * frosted-glass transport circles, and refined slide/fade animations.\n */\n\n/* ============================================================\n Google Fonts (Inter 300-700)\n ============================================================ */\n@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');\n\n/* ============================================================\n Root vars & container\n ============================================================ */\n[data-theme=\"v2\"] .eb-player {\n --eb-color-primary: #ff841f;\n --eb-color-progress: #ff841f;\n --eb-color-background: rgba(10, 10, 20, 0.85);\n --eb-accent: #ff841f;\n --eb-color-text: #fff;\n --eb-font-family: 'Inter', -apple-system, sans-serif;\n --eb-font-size-base: 14px;\n --eb-radius-control: 8px;\n --eb-duration-transition: 200ms;\n font-family: 'Inter', -apple-system, sans-serif;\n border-radius: 14px;\n box-shadow: 0 40px 80px rgba(0,0,0,.8), 0 0 0 1px rgba(255,255,255,.06);\n color: #fff;\n}\n\n/* ============================================================\n Icons: bolder stroke weight to match filled-style target\n ============================================================ */\n[data-theme=\"v2\"] .eb-player .eb-icon {\n stroke-width: 2.5;\n}\n\n/* Fullscreen: fill viewport, drop card styling */\n[data-theme=\"v2\"] .eb-player:fullscreen,\n[data-theme=\"v2\"] .eb-player:-webkit-full-screen {\n border-radius: 0;\n box-shadow: none;\n}\n\n/* ============================================================\n Gradients (top & bottom)\n ============================================================ */\n[data-theme=\"v2\"] .eb-player .eb-top-bar {\n background: linear-gradient(to bottom, rgba(0,0,0,.72), transparent);\n height: 110px;\n padding: 14px 16px;\n gap: 6px;\n}\n\n[data-theme=\"v2\"] .eb-player .eb-bottom-bar__gradient {\n height: 150px;\n background: linear-gradient(to top, rgba(0,0,0,.9), transparent);\n}\n\n/* ============================================================\n Top bar: slide-in from above\n ============================================================ */\n[data-theme=\"v2\"] .eb-player.eb-controls-visible .eb-top-bar {\n opacity: 1;\n transform: translateY(0);\n transition: opacity .3s, transform .3s;\n}\n\n[data-theme=\"v2\"] .eb-player.eb-controls-hidden .eb-top-bar {\n opacity: 0;\n transform: translateY(-6px);\n transition: opacity .3s, transform .3s;\n}\n\n[data-theme=\"v2\"] .eb-player .eb-top-bar__actions {\n gap: 6px;\n}\n\n/* Logo */\n[data-theme=\"v2\"] .eb-player .eb-top-bar__logo {\n max-height: 32px;\n max-width: 120px;\n opacity: .85;\n}\n\n/* ============================================================\n Bottom bar: slide-up animation + spacing\n ============================================================ */\n[data-theme=\"v2\"] .eb-player .eb-bottom-bar__controls-row {\n padding: 0 14px 12px;\n gap: 8px;\n}\n\n[data-theme=\"v2\"] .eb-player.eb-controls-visible .eb-bottom-bar {\n opacity: 1;\n transform: translateY(0);\n transition: opacity .3s, transform .3s;\n}\n\n[data-theme=\"v2\"] .eb-player.eb-controls-hidden .eb-bottom-bar {\n opacity: 0;\n transform: translateY(8px);\n transition: opacity .3s, transform .3s;\n}\n\n/* ============================================================\n Bottom bar: single-row layout\n play | live | time | ——seekbar—— | settings | volume | fullscreen\n PiP / Cast are in the top bar (via THEME_LAYOUTS.v2 in config.ts)\n ============================================================ */\n\n/* ============================================================\n Middle bar: glassmorphism transport circles\n ============================================================ */\n[data-theme=\"v2\"] .eb-player .eb-middle-bar {\n gap: 28px;\n}\n\n[data-theme=\"v2\"] .eb-player.eb-controls-visible .eb-middle-bar {\n opacity: 1;\n transition: opacity .3s;\n}\n\n[data-theme=\"v2\"] .eb-player.eb-controls-hidden .eb-middle-bar {\n opacity: 0;\n transition: opacity .3s;\n}\n\n/* Central play/pause — large frosted circle */\n[data-theme=\"v2\"] .eb-player .eb-middle-bar__play-btn {\n width: 80px;\n height: 80px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n background: rgba(255,255,255,.15);\n backdrop-filter: blur(8px);\n -webkit-backdrop-filter: blur(8px);\n border: 2px solid rgba(255,255,255,.25);\n color: #fff;\n padding: 0;\n transition: background .15s, transform .25s, opacity .25s;\n}\n\n[data-theme=\"v2\"] .eb-player .eb-middle-bar__play-btn:hover {\n background: rgba(255,255,255,.25);\n}\n\n[data-theme=\"v2\"] .eb-player .eb-middle-bar__play-btn:active {\n transform: scale(.92);\n}\n\n[data-theme=\"v2\"] .eb-player .eb-middle-bar__play-btn .eb-icon {\n width: 32px;\n height: 32px;\n stroke-width: 2.5;\n}\n\n/* Seek buttons — smaller frosted circles, same style as play button */\n[data-theme=\"v2\"] .eb-player .eb-middle-bar__seek-btn {\n width: 56px;\n height: 56px;\n border-radius: 50%;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n background: rgba(255,255,255,.15);\n backdrop-filter: blur(8px);\n -webkit-backdrop-filter: blur(8px);\n border: 2px solid rgba(255,255,255,.25);\n color: #fff;\n gap: 1px;\n padding: 0;\n transition: background .15s;\n}\n\n[data-theme=\"v2\"] .eb-player .eb-middle-bar__seek-btn:hover {\n background: rgba(255,255,255,.25);\n}\n\n[data-theme=\"v2\"] .eb-player .eb-seek-circle {\n width: 24px;\n height: 24px;\n color: rgba(255,255,255,.85);\n fill: currentColor;\n stroke: none;\n}\n\n[data-theme=\"v2\"] .eb-player .eb-seek-label {\n font-size: .75em;\n font-weight: 700;\n color: rgba(255,255,255,.9);\n line-height: 1;\n}\n\n/* ============================================================\n Buttons: rounded, soft color, scale on press\n ============================================================ */\n[data-theme=\"v2\"] .eb-player .eb-button:not(.eb-middle-bar__seek-btn):not(.eb-middle-bar__play-btn),\n[data-theme=\"v2\"] .eb-player .eb-play-pause,\n[data-theme=\"v2\"] .eb-player .eb-fullscreen,\n[data-theme=\"v2\"] .eb-player .eb-pip,\n[data-theme=\"v2\"] .eb-player .eb-cast,\n[data-theme=\"v2\"] .eb-player .eb-volume-mute,\n[data-theme=\"v2\"] .eb-player .eb-live-sync {\n color: rgba(255,255,255,.82);\n border-radius: 8px;\n width: 38px;\n height: 38px;\n transition: background .15s, color .15s, transform .1s;\n flex-shrink: 0;\n}\n\n[data-theme=\"v2\"] .eb-player .eb-button:not(.eb-middle-bar__seek-btn):not(.eb-middle-bar__play-btn):hover,\n[data-theme=\"v2\"] .eb-player .eb-play-pause:hover,\n[data-theme=\"v2\"] .eb-player .eb-fullscreen:hover,\n[data-theme=\"v2\"] .eb-player .eb-pip:hover,\n[data-theme=\"v2\"] .eb-player .eb-cast:hover,\n[data-theme=\"v2\"] .eb-player .eb-volume-mute:hover,\n[data-theme=\"v2\"] .eb-player .eb-live-sync:hover {\n background: rgba(255,255,255,.12);\n color: #fff;\n}\n\n[data-theme=\"v2\"] .eb-player .eb-button:not(.eb-middle-bar__seek-btn):not(.eb-middle-bar__play-btn):active,\n[data-theme=\"v2\"] .eb-player .eb-play-pause:active,\n[data-theme=\"v2\"] .eb-player .eb-fullscreen:active,\n[data-theme=\"v2\"] .eb-player .eb-pip:active,\n[data-theme=\"v2\"] .eb-player .eb-cast:active,\n[data-theme=\"v2\"] .eb-player .eb-volume-mute:active,\n[data-theme=\"v2\"] .eb-player .eb-live-sync:active {\n transform: scale(.88);\n}\n\n/* Icon size inside buttons (not middle bar transport) */\n[data-theme=\"v2\"] .eb-player .eb-button:not(.eb-middle-bar__seek-btn):not(.eb-middle-bar__play-btn) .eb-icon,\n[data-theme=\"v2\"] .eb-player .eb-play-pause .eb-icon,\n[data-theme=\"v2\"] .eb-player .eb-fullscreen .eb-icon,\n[data-theme=\"v2\"] .eb-player .eb-pip .eb-icon,\n[data-theme=\"v2\"] .eb-player .eb-cast .eb-icon,\n[data-theme=\"v2\"] .eb-player .eb-volume-mute .eb-icon {\n width: 22px;\n height: 22px;\n}\n\n/* Top bar icons slightly larger */\n[data-theme=\"v2\"] .eb-player .eb-top-bar .eb-icon {\n width: 24px;\n height: 24px;\n}\n\n/* Play/pause icons should be filled (solid), not stroke outlines */\n[data-theme=\"v2\"] .eb-player .eb-play-pause .eb-icon,\n[data-theme=\"v2\"] .eb-player .eb-middle-bar__play-btn .eb-icon {\n fill: currentColor;\n stroke: none;\n}\n\n/* Volume icon: fill the speaker body, keep stroke for sound waves */\n[data-theme=\"v2\"] .eb-player .eb-volume-mute .eb-icon path:first-child {\n fill: currentColor;\n}\n\n/* Cast button active state — cyan */\n[data-theme=\"v2\"] .eb-player .eb-cast-active {\n color: #1eb6d4 !important;\n background: rgba(30, 182, 212, .15) !important;\n}\n\n[data-theme=\"v2\"] .eb-player .eb-cast-active .eb-icon {\n filter: drop-shadow(0 0 4px rgba(30, 182, 212, .6));\n}\n\n/* ============================================================\n Seekbar\n ============================================================ */\n[data-theme=\"v2\"] .eb-player .eb-seekbar-track {\n height: 3px;\n background: rgba(255,255,255,.22);\n border-radius: 3px;\n transition: height .15s;\n}\n\n[data-theme=\"v2\"] .eb-player .eb-seekbar:hover .eb-seekbar-track {\n height: 5px;\n}\n\n[data-theme=\"v2\"] .eb-player .eb-seekbar-buffered {\n background: rgba(255,255,255,.28);\n border-radius: 3px;\n transition: height .15s;\n}\n\n/* Orange gradient fill */\n[data-theme=\"v2\"] .eb-player .eb-seekbar-progress {\n background: linear-gradient(90deg, #ff841f, color-mix(in srgb, #ff841f 70%, #fff));\n border-radius: 3px;\n transition: height .15s;\n}\n\n[data-theme=\"v2\"] .eb-player .eb-seekbar-thumb {\n width: 14px;\n height: 14px;\n right: -7px;\n background: #fff;\n box-shadow: 0 0 0 3px rgba(255,132,31,.45);\n}\n\n/* ============================================================\n Seekbar tooltip & preview\n ============================================================ */\n[data-theme=\"v2\"] .eb-player .eb-seekbar-tooltip {\n bottom: 28px;\n background: rgba(0,0,0,.88);\n border: 1px solid rgba(255,255,255,.12);\n border-radius: 7px;\n font-size: 11px;\n font-weight: 500;\n padding: 0;\n overflow: hidden;\n box-shadow: 0 4px 16px rgba(0,0,0,.5);\n display: flex;\n flex-direction: column;\n align-items: center;\n}\n\n[data-theme=\"v2\"] .eb-player .eb-seekbar-preview {\n width: 160px;\n height: 90px;\n background: #111;\n border: none;\n border-radius: 0;\n margin: 0;\n}\n\n/* ============================================================\n Chapter markers\n ============================================================ */\n[data-theme=\"v2\"] .eb-player .eb-chapter-marker {\n width: 2px;\n height: 7px;\n background: rgba(0,0,0,.45);\n border-radius: 1px;\n top: 50%;\n transform: translate(-50%, -50%);\n}\n\n/* ============================================================\n Volume: expandable slider, white fill\n ============================================================ */\n[data-theme=\"v2\"] .eb-player .eb-volume-control {\n flex-direction: row;\n gap: 0;\n margin-right: 0;\n}\n\n[data-theme=\"v2\"] .eb-player .eb-volume-track {\n width: 0;\n height: 3px;\n background: rgba(255,255,255,.22);\n border-radius: 3px;\n overflow: hidden;\n transition: width .25s ease, margin .25s ease;\n margin: 0;\n}\n\n[data-theme=\"v2\"] .eb-player .eb-volume-control:hover .eb-volume-track,\n[data-theme=\"v2\"] .eb-player .eb-volume-control:focus-within .eb-volume-track {\n width: 66px;\n margin: 0 6px 0 2px;\n}\n\n[data-theme=\"v2\"] .eb-player .eb-volume-fill {\n background: var(--eb-color-primary, #ff841f);\n}\n\n[data-theme=\"v2\"] .eb-player .eb-volume-thumb {\n width: 11px;\n height: 11px;\n background: #fff;\n box-shadow: 0 0 0 2px rgba(255,255,255,.35);\n transform: translate(-50%, -50%) scale(0);\n transition: transform .15s;\n}\n\n[data-theme=\"v2\"] .eb-player .eb-volume-control:hover .eb-volume-thumb {\n transform: translate(-50%, -50%) scale(1);\n}\n\n/* ============================================================\n Time display\n ============================================================ */\n[data-theme=\"v2\"] .eb-player .eb-time-display {\n font-size: 13px;\n color: rgba(255,255,255,.85);\n font-weight: 500;\n letter-spacing: .02em;\n}\n\n/* ============================================================\n Live badge — text badge with blinking dot (not icon)\n ============================================================ */\n[data-theme=\"v2\"] .eb-player .eb-live-sync {\n width: auto;\n height: auto;\n padding: 4px 10px;\n border-radius: 6px;\n font-size: 11px;\n font-weight: 700;\n letter-spacing: .08em;\n text-transform: uppercase;\n gap: 5px;\n}\n\n/* Hide icon, show dot + label */\n[data-theme=\"v2\"] .eb-player .eb-live-sync__icon {\n display: none;\n}\n\n[data-theme=\"v2\"] .eb-player .eb-live-sync__dot {\n display: block;\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: #fff;\n flex-shrink: 0;\n}\n\n[data-theme=\"v2\"] .eb-player .eb-live-sync__label {\n display: block;\n}\n\n/* Synced state — red background with blinking dot */\n[data-theme=\"v2\"] .eb-player .eb-live-synced {\n background: rgba(220,38,38,.9);\n color: #fff;\n box-shadow: 0 2px 8px rgba(220,38,38,.4);\n}\n\n[data-theme=\"v2\"] .eb-player .eb-live-synced .eb-live-sync__dot {\n animation: eb-v2-blink 1.2s ease infinite;\n}\n\n/* Delayed / not-at-live-edge */\n[data-theme=\"v2\"] .eb-player .eb-live-sync:not(.eb-live-synced) {\n background: rgba(60,60,80,.75);\n color: rgba(255,255,255,.7);\n box-shadow: none;\n}\n\n[data-theme=\"v2\"] .eb-player .eb-live-sync:not(.eb-live-synced) .eb-live-sync__dot {\n background: rgba(255,255,255,.5);\n animation: none;\n}\n\n[data-theme=\"v2\"] .eb-player .eb-live-sync:not(.eb-live-synced):hover {\n background: rgba(220,38,38,.7);\n color: #fff;\n}\n\n@keyframes eb-v2-blink {\n 0%, 100% { opacity: 1; }\n 50% { opacity: .15; }\n}\n\n/* ============================================================\n Settings panel: glassmorphism dropdown\n ============================================================ */\n[data-theme=\"v2\"] .eb-player .eb-settings-panel {\n background: rgba(10,10,20,.55);\n backdrop-filter: blur(18px) saturate(160%);\n -webkit-backdrop-filter: blur(18px) saturate(160%);\n border-radius: 12px;\n min-width: 300px;\n box-shadow: 0 16px 48px rgba(0,0,0,.6), 0 0 0 1px rgba(255,255,255,.1);\n}\n\n[data-theme=\"v2\"] .eb-player .eb-settings-menu {\n padding: 0;\n}\n\n[data-theme=\"v2\"] .eb-player .eb-settings-category,\n[data-theme=\"v2\"] .eb-player .eb-settings-item,\n[data-theme=\"v2\"] .eb-player .eb-settings-back {\n padding: 15px 20px;\n font-size: 13px;\n color: rgba(255,255,255,.9);\n border-bottom: 1px solid rgba(255,255,255,.06);\n transition: background .12s;\n}\n\n[data-theme=\"v2\"] .eb-player .eb-settings-category:last-child,\n[data-theme=\"v2\"] .eb-player .eb-settings-item:last-child {\n border-bottom: none;\n}\n\n[data-theme=\"v2\"] .eb-player .eb-settings-category:hover,\n[data-theme=\"v2\"] .eb-player .eb-settings-item:hover,\n[data-theme=\"v2\"] .eb-player .eb-settings-back:hover {\n background: rgba(255,255,255,.05);\n}\n\n/* Selected item text */\n[data-theme=\"v2\"] .eb-player .eb-settings-item--selected {\n color: #fff;\n font-weight: 500;\n}\n\n/* Active selection dot (filled orange) */\n[data-theme=\"v2\"] .eb-player .eb-settings-item--selected::after {\n content: '';\n width: 10px;\n height: 10px;\n border-radius: 50%;\n background: var(--eb-accent, #ff841f);\n box-shadow: 0 0 0 3px rgba(255,132,31,.25);\n flex-shrink: 0;\n}\n\n/* Non-selected items: hollow dot */\n[data-theme=\"v2\"] .eb-player .eb-settings-item:not(.eb-settings-item--selected)::after {\n content: '';\n width: 10px;\n height: 10px;\n border-radius: 50%;\n border: 2px solid rgba(255,255,255,.2);\n flex-shrink: 0;\n}\n\n/* Back button */\n[data-theme=\"v2\"] .eb-player .eb-settings-back {\n gap: 14px;\n font-weight: 600;\n}\n\n/* ============================================================\n Loading spinner\n ============================================================ */\n[data-theme=\"v2\"] .eb-player .eb-loading .eb-icon {\n width: 52px;\n height: 52px;\n color: rgba(255,255,255,.7);\n filter: drop-shadow(0 1px 6px rgba(0,0,0,.5));\n animation: eb-spin .75s linear infinite;\n}\n\n/* ============================================================\n Error overlay\n ============================================================ */\n[data-theme=\"v2\"] .eb-player .eb-error {\n background: rgba(10,10,20,.85);\n backdrop-filter: blur(12px);\n -webkit-backdrop-filter: blur(12px);\n}\n\n[data-theme=\"v2\"] .eb-player .eb-error-message {\n font-weight: 400;\n letter-spacing: .01em;\n}\n\n[data-theme=\"v2\"] .eb-player .eb-error-retry {\n border-radius: 8px;\n background: rgba(255,255,255,.1);\n border-color: rgba(255,255,255,.15);\n transition: background .2s, color .2s;\n}\n\n[data-theme=\"v2\"] .eb-player .eb-error-retry:hover {\n background: rgba(255,255,255,.18);\n}\n\n/* ============================================================\n Toast (keyboard hints, status messages)\n ============================================================ */\n[data-theme=\"v2\"] .eb-player .eb-toast {\n background: rgba(0,0,0,.72);\n backdrop-filter: blur(10px);\n -webkit-backdrop-filter: blur(10px);\n border-radius: 10px;\n font-size: 13px;\n padding: 8px 16px;\n}\n\n/* ============================================================\n Info & socials overlays\n ============================================================ */\n[data-theme=\"v2\"] .eb-player .eb-info-overlay,\n[data-theme=\"v2\"] .eb-player .eb-socials-overlay {\n background: rgba(10,10,20,.85);\n backdrop-filter: blur(12px);\n -webkit-backdrop-filter: blur(12px);\n}\n\n[data-theme=\"v2\"] .eb-player .eb-info-close,\n[data-theme=\"v2\"] .eb-player .eb-socials-close {\n border-radius: 8px;\n background: rgba(255,255,255,.07);\n border-color: rgba(255,255,255,.08);\n transition: background .2s, color .2s;\n}\n\n[data-theme=\"v2\"] .eb-player .eb-info-close:hover,\n[data-theme=\"v2\"] .eb-player .eb-socials-close:hover {\n background: rgba(255,255,255,.13);\n color: #fff;\n}\n\n[data-theme=\"v2\"] .eb-player .eb-socials-link {\n background: rgba(255,255,255,.07);\n border: 1px solid rgba(255,255,255,.08);\n border-radius: 8px;\n transition: background .2s, color .2s, border-color .2s;\n}\n\n[data-theme=\"v2\"] .eb-player .eb-socials-link:hover {\n background: rgba(255,132,31,.15);\n border-color: rgba(255,132,31,.35);\n color: #ffb980;\n}\n\n/* ============================================================\n Poster\n ============================================================ */\n[data-theme=\"v2\"] .eb-player .eb-poster {\n background: linear-gradient(135deg, #0f0c29, #302b63, #24243e);\n}\n\n/* ============================================================\n Radio overlay\n ============================================================ */\n[data-theme=\"v2\"] .eb-player .eb-radio-overlay {\n background: linear-gradient(135deg, #0f0c29, #302b63, #24243e);\n}\n\n[data-theme=\"v2\"] .eb-player .eb-radio-bar {\n background: rgba(255,132,31,.8);\n}\n\n/* ============================================================\n Chapter skip button\n ============================================================ */\n[data-theme=\"v2\"] .eb-player .eb-chapter-skip {\n background: rgba(10,10,20,.55);\n backdrop-filter: blur(12px);\n -webkit-backdrop-filter: blur(12px);\n border: 1px solid rgba(255,255,255,.15);\n border-radius: 8px;\n font-size: 12px;\n font-weight: 500;\n transition: background .15s;\n}\n\n[data-theme=\"v2\"] .eb-player .eb-chapter-skip:hover {\n background: rgba(255,255,255,.12);\n}\n";
52
52
  styleInject(css_248z$1);
53
53
 
54
- var css_248z = "/* eb-player skin CSS — BEM-prefixed structural styles */\n/* All class names use the eb- prefix to avoid collisions with consumer CSS */\n\n/* ============================================================\n Root container\n ============================================================ */\n.eb-player {\n position: relative;\n width: 100%;\n height: 100%;\n overflow: hidden;\n font-family: sans-serif;\n background: #000;\n color: #fff;\n box-sizing: border-box;\n user-select: none;\n}\n\n/* Video element fills the container */\n.eb-player video.eb-video {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n display: block;\n object-fit: contain;\n z-index: 1;\n}\n\n/* ============================================================\n Overlay zone (poster, radio, loading, error, socials, info)\n ============================================================ */\n.eb-overlay-zone {\n position: absolute;\n inset: 0;\n pointer-events: none;\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10;\n}\n\n/* Poster image */\n.eb-poster {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n object-fit: contain;\n pointer-events: none;\n background: #000;\n padding: 15%;\n box-sizing: border-box;\n}\n\n/* Ads container (for IMA SDK) */\n.eb-ads-container {\n position: absolute;\n inset: 0;\n z-index: 20;\n pointer-events: none;\n}\n\n/* ============================================================\n Top bar\n ============================================================ */\n.eb-top-bar {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n display: flex;\n flex-direction: row;\n align-items: center;\n justify-content: space-between;\n padding: 8px 12px;\n background: linear-gradient(to bottom, rgba(0,0,0,0.6), transparent);\n z-index: 30;\n pointer-events: auto;\n}\n\n.eb-top-bar__logo {\n height: 28px;\n width: auto;\n display: block;\n}\n\n.eb-top-bar__logo-link {\n display: inline-flex;\n text-decoration: none;\n}\n\n.eb-top-bar__actions {\n display: flex;\n align-items: center;\n gap: 4px;\n margin-left: auto;\n}\n\n/* ============================================================\n Bottom bar\n ============================================================ */\n.eb-bottom-bar {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n display: flex;\n flex-direction: column;\n z-index: 30;\n pointer-events: auto;\n}\n\n.eb-bottom-bar__gradient {\n position: absolute;\n left: 0;\n right: 0;\n bottom: 0;\n height: 100px;\n background: linear-gradient(to top, rgba(0,0,0,0.7), transparent);\n pointer-events: none;\n z-index: -1;\n}\n\n.eb-bottom-bar__controls-row {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: 4px;\n padding: 4px 8px 8px;\n}\n\n.eb-bottom-bar__seekbar-zone {\n flex: 1;\n min-width: 0;\n}\n\n/* ============================================================\n Middle bar\n ============================================================ */\n.eb-middle-bar {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n pointer-events: auto;\n z-index: 25;\n display: flex;\n align-items: center;\n gap: 16px;\n}\n\n.eb-middle-bar__play-btn {\n width: 64px;\n height: 64px;\n border-radius: 50%;\n background: rgba(0,0,0,0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.eb-middle-bar__seek-btn {\n flex-direction: column;\n gap: 2px;\n background: none;\n padding: 4px;\n}\n\n.eb-middle-bar__seek-btn:hover {\n background: rgba(255,255,255,0.1);\n border-radius: 8px;\n}\n\n.eb-seek-circle {\n width: 32px;\n height: 32px;\n display: block;\n pointer-events: none;\n fill: currentColor;\n}\n\n.eb-seek-label {\n font-size: 11px;\n font-weight: 600;\n line-height: 1;\n opacity: 0.85;\n}\n\n/* ============================================================\n Extension zones\n ============================================================ */\n.eb-extension-top-extra,\n.eb-extension-bottom-extra,\n.eb-extension-overlay,\n.eb-extension-controls-extra {\n position: absolute;\n pointer-events: none;\n z-index: 35;\n}\n\n.eb-extension-top-extra > *,\n.eb-extension-bottom-extra > *,\n.eb-extension-overlay > *,\n.eb-extension-controls-extra > * {\n pointer-events: auto;\n}\n\n.eb-extension-top-extra {\n top: 0;\n left: 0;\n right: 0;\n}\n\n.eb-extension-bottom-extra {\n bottom: 0;\n left: 0;\n right: 0;\n}\n\n.eb-extension-overlay {\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.eb-extension-controls-extra {\n bottom: 0;\n right: 0;\n display: flex;\n align-items: center;\n gap: 4px;\n padding: 4px 8px 8px;\n}\n\n/* ============================================================\n Time display\n ============================================================ */\n.eb-time-display {\n font-size: 13px;\n line-height: 1;\n white-space: nowrap;\n padding: 0 4px;\n color: #fff;\n font-variant-numeric: tabular-nums;\n}\n\n/* ============================================================\n Volume control\n ============================================================ */\n.eb-volume-control {\n display: flex;\n align-items: center;\n gap: 4px;\n margin-right: 8px;\n}\n\n.eb-volume-track {\n position: relative;\n width: 60px;\n height: 4px;\n background: rgba(255, 255, 255, 0.2);\n border-radius: 2px;\n cursor: pointer;\n}\n\n.eb-volume-fill {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n background: var(--eb-color-volume, var(--eb-color-primary, #fff));\n border-radius: 2px;\n pointer-events: none;\n}\n\n.eb-volume-thumb {\n position: absolute;\n top: 50%;\n width: 10px;\n height: 10px;\n border-radius: 50%;\n background: var(--eb-color-volume, var(--eb-color-primary, #fff));\n transform: translate(-50%, -50%);\n pointer-events: none;\n}\n\n/* ============================================================\n Settings panel\n ============================================================ */\n.eb-settings-wrapper {\n position: relative;\n}\n\n.eb-settings-panel {\n position: absolute;\n background: rgba(0, 0, 0, 0.85);\n border-radius: 6px;\n min-width: 160px;\n max-width: 220px;\n overflow: hidden;\n font-size: 12px;\n z-index: 40;\n}\n\n/* Vertical direction: up (default) or down */\n.eb-settings-panel--up {\n bottom: calc(100% + 8px);\n}\n\n.eb-settings-panel--down {\n top: calc(100% + 8px);\n}\n\n/* Horizontal alignment: right (default) or left */\n.eb-settings-panel--right {\n right: 0;\n}\n\n.eb-settings-panel--left {\n left: 0;\n}\n\n.eb-settings-menu {\n list-style: none;\n margin: 0;\n padding: 4px 0;\n}\n\n.eb-settings-submenu .eb-settings-menu {\n max-height: 200px;\n overflow-y: auto;\n}\n\n.eb-settings-submenu .eb-settings-menu::-webkit-scrollbar {\n width: 4px;\n}\n\n.eb-settings-submenu .eb-settings-menu::-webkit-scrollbar-thumb {\n background: rgba(255, 255, 255, 0.3);\n border-radius: 2px;\n}\n\n.eb-settings-category,\n.eb-settings-item,\n.eb-settings-back {\n display: flex;\n align-items: center;\n justify-content: space-between;\n width: 100%;\n padding: 5px 12px;\n border: none;\n background: none;\n color: #fff;\n cursor: pointer;\n font-size: 12px;\n text-align: left;\n}\n\n.eb-settings-category:hover,\n.eb-settings-item:hover,\n.eb-settings-back:hover {\n background: rgba(255, 255, 255, 0.1);\n}\n\n.eb-settings-item--selected {\n color: var(--eb-accent, var(--eb-color-primary, #e53935));\n}\n\n/* ============================================================\n Seekbar\n ============================================================ */\n.eb-seekbar {\n position: relative;\n width: 100%;\n padding: 8px 0;\n cursor: pointer;\n}\n\n.eb-seekbar-disabled {\n pointer-events: none;\n opacity: 0.4;\n}\n\n.eb-seekbar-track {\n position: relative;\n height: 4px;\n background: rgba(255, 255, 255, 0.2);\n border-radius: 2px;\n}\n\n.eb-seekbar-buffered {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n background: rgba(255, 255, 255, 0.4);\n border-radius: 2px;\n pointer-events: none;\n}\n\n.eb-seekbar-progress {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n background: var(--eb-color-progress, var(--eb-color-primary, #e53935));\n border-radius: 2px;\n pointer-events: none;\n}\n\n.eb-seekbar-thumb {\n position: absolute;\n right: -6px;\n top: 50%;\n width: 12px;\n height: 12px;\n border-radius: 50%;\n background: var(--eb-color-progress, var(--eb-color-primary, #e53935));\n transform: translateY(-50%) scale(0);\n transition: transform 0.15s;\n pointer-events: none;\n}\n\n.eb-seekbar:hover .eb-seekbar-thumb {\n transform: translateY(-50%) scale(1);\n}\n\n.eb-seekbar:hover .eb-seekbar-track {\n height: 6px;\n}\n\n.eb-seekbar-tooltip {\n position: absolute;\n bottom: calc(100% + 8px);\n transform: translateX(-50%);\n background: rgba(0, 0, 0, 0.8);\n color: #fff;\n font-size: 12px;\n padding: 4px 8px;\n border-radius: 3px;\n white-space: nowrap;\n pointer-events: none;\n text-align: center;\n}\n\n.eb-seekbar-preview {\n width: 120px;\n height: auto;\n display: block;\n margin-bottom: 4px;\n}\n\n.eb-chapter-marker {\n position: absolute;\n top: 0;\n width: 3px;\n height: 100%;\n background: rgba(255, 255, 255, 0.6);\n transform: translateX(-50%);\n pointer-events: none;\n}\n\n.eb-chapter-marker.eb-chapter-active {\n background: #fff;\n}\n\n.eb-chapter-skip {\n position: absolute;\n right: 0;\n bottom: calc(100% + 4px);\n background: rgba(0, 0, 0, 0.7);\n color: #fff;\n border: 1px solid rgba(255, 255, 255, 0.4);\n border-radius: 4px;\n padding: 6px 16px;\n font-size: 13px;\n cursor: pointer;\n}\n\n.eb-chapter-skip:hover {\n background: rgba(255, 255, 255, 0.2);\n}\n\n.eb-epg-segment {\n position: absolute;\n top: 0;\n height: 100%;\n border-right: 1px solid rgba(255, 255, 255, 0.3);\n pointer-events: none;\n}\n\n.eb-epg-segment.eb-epg-current {\n background: rgba(255, 255, 255, 0.1);\n}\n\n/* ============================================================\n Icons\n ============================================================ */\n.eb-icon {\n width: 20px;\n height: 20px;\n fill: none;\n stroke: currentColor;\n stroke-width: 2;\n stroke-linecap: round;\n stroke-linejoin: round;\n display: block;\n pointer-events: none;\n flex-shrink: 0;\n}\n\n/* ============================================================\n Buttons (base style)\n ============================================================ */\n.eb-button,\n.eb-play-pause,\n.eb-fullscreen,\n.eb-pip,\n.eb-cast,\n.eb-volume-mute,\n.eb-live-sync {\n cursor: pointer;\n background: none;\n border: none;\n padding: 6px;\n color: var(--eb-color-icon, inherit);\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 4px;\n transition: background 0.15s;\n -webkit-tap-highlight-color: transparent;\n}\n\n.eb-button:hover,\n.eb-play-pause:hover,\n.eb-fullscreen:hover,\n.eb-pip:hover,\n.eb-cast:hover,\n.eb-volume-mute:hover,\n.eb-live-sync:hover {\n background: rgba(255,255,255,0.15);\n}\n\n.eb-button:active,\n.eb-play-pause:active,\n.eb-fullscreen:active,\n.eb-pip:active,\n.eb-cast:active,\n.eb-volume-mute:active,\n.eb-live-sync:active {\n background: rgba(255,255,255,0.25);\n}\n\n/* ============================================================\n Radio overlay\n ============================================================ */\n.eb-radio-overlay {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: #000;\n z-index: 15;\n}\n\n.eb-radio-bars {\n display: flex;\n align-items: flex-end;\n gap: 4px;\n height: 40px;\n}\n\n.eb-radio-bar {\n display: block;\n width: 6px;\n background: #fff;\n border-radius: 3px;\n animation: eb-radio-bar-bounce 0.8s ease-in-out infinite;\n}\n\n.eb-radio-bar:nth-child(1) { animation-delay: 0s; height: 20px; }\n.eb-radio-bar:nth-child(2) { animation-delay: 0.1s; height: 32px; }\n.eb-radio-bar:nth-child(3) { animation-delay: 0.2s; height: 40px; }\n.eb-radio-bar:nth-child(4) { animation-delay: 0.3s; height: 28px; }\n.eb-radio-bar:nth-child(5) { animation-delay: 0.4s; height: 16px; }\n\n@keyframes eb-radio-bar-bounce {\n 0%, 100% { transform: scaleY(0.4); opacity: 0.7; }\n 50% { transform: scaleY(1); opacity: 1; }\n}\n\n/* Volume mute button — base styles shared via .eb-button group above */\n\n/* ============================================================\n Live sync button\n ============================================================ */\n.eb-live-sync[hidden] {\n display: none;\n}\n\n.eb-live-synced {\n color: var(--eb-accent, var(--eb-color-primary, #e53935));\n}\n\n/* Default theme: show icon only, hide text badge elements */\n.eb-live-sync__dot,\n.eb-live-sync__label {\n display: none;\n}\n\n/* ============================================================\n Overlay panels (error, info, socials — mounted in .eb-player)\n ============================================================ */\n.eb-error-slot,\n.eb-info-slot,\n.eb-socials-slot {\n position: absolute;\n inset: 0;\n z-index: 50;\n pointer-events: none;\n}\n\n.eb-toast-slot {\n position: absolute;\n bottom: 60px;\n left: 50%;\n transform: translateX(-50%);\n z-index: 50;\n pointer-events: none;\n}\n\n.eb-toast {\n background: rgba(0, 0, 0, 0.8);\n color: #fff;\n padding: 8px 20px;\n border-radius: 4px;\n font-size: 13px;\n white-space: nowrap;\n pointer-events: auto;\n}\n\n.eb-error,\n.eb-info-overlay,\n.eb-socials-overlay {\n position: absolute;\n inset: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n background: rgba(0, 0, 0, 0.75);\n color: #fff;\n z-index: 50;\n pointer-events: auto;\n padding: 24px;\n text-align: center;\n}\n\n.eb-error[hidden],\n.eb-info-overlay[hidden],\n.eb-socials-overlay[hidden] {\n display: none;\n}\n\n.eb-error-message {\n font-size: 16px;\n margin: 0 0 16px;\n}\n\n.eb-error-retry {\n background: rgba(255, 255, 255, 0.15);\n color: #fff;\n border: 1px solid rgba(255, 255, 255, 0.3);\n border-radius: 4px;\n padding: 8px 24px;\n font-size: 14px;\n cursor: pointer;\n}\n\n.eb-error-retry:hover {\n background: rgba(255, 255, 255, 0.25);\n}\n\n.eb-info-content {\n font-size: 14px;\n line-height: 1.5;\n max-width: 400px;\n margin-bottom: 16px;\n}\n\n.eb-info-close,\n.eb-socials-close {\n background: rgba(255, 255, 255, 0.15);\n color: #fff;\n border: 1px solid rgba(255, 255, 255, 0.3);\n border-radius: 4px;\n padding: 8px 24px;\n font-size: 14px;\n cursor: pointer;\n}\n\n.eb-info-close:hover,\n.eb-socials-close:hover {\n background: rgba(255, 255, 255, 0.25);\n}\n\n.eb-socials-links {\n display: flex;\n flex-wrap: wrap;\n gap: 12px;\n justify-content: center;\n margin-bottom: 16px;\n}\n\n.eb-socials-link {\n color: #fff;\n text-decoration: none;\n background: rgba(255, 255, 255, 0.1);\n border-radius: 4px;\n padding: 8px 16px;\n font-size: 14px;\n text-transform: capitalize;\n}\n\n.eb-socials-link:hover {\n background: rgba(255, 255, 255, 0.25);\n}\n\n/* ============================================================\n Loading spinner\n ============================================================ */\n.eb-loading {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 8px;\n color: #fff;\n pointer-events: none;\n}\n\n.eb-loading[hidden] {\n display: none;\n}\n\n.eb-loading .eb-icon {\n width: 40px;\n height: 40px;\n animation: eb-spin 1s linear infinite;\n}\n\n.eb-loading-text {\n font-size: 14px;\n}\n\n@keyframes eb-spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n}\n\n/* ============================================================\n Controls visibility\n ============================================================ */\n.eb-controls-visible .eb-top-bar,\n.eb-controls-visible .eb-bottom-bar,\n.eb-controls-visible .eb-middle-bar {\n opacity: 1;\n transition: opacity 0.3s;\n}\n\n.eb-controls-hidden .eb-top-bar,\n.eb-controls-hidden .eb-bottom-bar,\n.eb-controls-hidden .eb-middle-bar {\n opacity: 0;\n transition: opacity 0.3s;\n pointer-events: none;\n}\n\n/* ============================================================\n iOS native controls fallback\n ============================================================ */\n.eb-ios-native .eb-top-bar,\n.eb-ios-native .eb-bottom-bar,\n.eb-ios-native .eb-middle-bar,\n.eb-ios-native .eb-overlay-zone,\n.eb-ios-native .eb-error-slot,\n.eb-ios-native .eb-info-slot,\n.eb-ios-native .eb-socials-slot,\n.eb-ios-native .eb-toast-slot {\n display: none;\n}\n\n/* ============================================================\n RTL support\n ============================================================ */\n[dir=\"rtl\"] .eb-bottom-bar__controls-row {\n flex-direction: row-reverse;\n}\n\n[dir=\"rtl\"] .eb-top-bar {\n flex-direction: row-reverse;\n}\n\n/* ============================================================\n Responsive: ensure container fills parent\n ============================================================ */\n.eb-player,\n.eb-player video {\n max-width: 100%;\n max-height: 100%;\n}\n";
54
+ var css_248z = "/* eb-player skin CSS — BEM-prefixed structural styles */\n/* All class names use the eb- prefix to avoid collisions with consumer CSS */\n\n/* ============================================================\n Root container\n ============================================================ */\n.eb-player {\n position: relative;\n width: 100%;\n height: 100%;\n overflow: hidden;\n font-family: sans-serif;\n background: #000;\n color: #fff;\n box-sizing: border-box;\n user-select: none;\n}\n\n/* Video element fills the container.\n filter: brightness(1) is visually identical to no filter but forces Chrome to\n composite the video through the GPU filter pipeline instead of using a hardware\n overlay plane. Without this, DRM-protected video (Widevine) renders on a separate\n hardware overlay that sits above ALL HTML layers — making seekbar snapshot preview\n and other overlay elements invisible behind the main video. */\n.eb-player video.eb-video {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n display: block;\n object-fit: contain;\n z-index: 1;\n filter: brightness(1);\n}\n\n/* ============================================================\n Overlay zone (poster, radio, loading, error, socials, info)\n ============================================================ */\n.eb-overlay-zone {\n position: absolute;\n inset: 0;\n pointer-events: none;\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10;\n}\n\n/* Poster image */\n.eb-poster {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n object-fit: contain;\n pointer-events: none;\n background: #000;\n padding: 15%;\n box-sizing: border-box;\n}\n\n/* Ads container (for IMA SDK) */\n.eb-ads-container {\n position: absolute;\n inset: 0;\n z-index: 20;\n pointer-events: none;\n}\n\n/* ============================================================\n Top bar\n ============================================================ */\n.eb-top-bar {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n display: flex;\n flex-direction: row;\n align-items: center;\n justify-content: space-between;\n padding: 8px 12px;\n background: linear-gradient(to bottom, rgba(0,0,0,0.6), transparent);\n z-index: 30;\n pointer-events: auto;\n}\n\n.eb-top-bar__logo {\n height: 28px;\n width: auto;\n display: block;\n}\n\n.eb-top-bar__logo-link {\n display: inline-flex;\n text-decoration: none;\n}\n\n.eb-top-bar__actions {\n display: flex;\n align-items: center;\n gap: 4px;\n margin-left: auto;\n}\n\n/* ============================================================\n Bottom bar\n ============================================================ */\n.eb-bottom-bar {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n display: flex;\n flex-direction: column;\n z-index: 30;\n pointer-events: auto;\n}\n\n.eb-bottom-bar__gradient {\n position: absolute;\n left: 0;\n right: 0;\n bottom: 0;\n height: 100px;\n background: linear-gradient(to top, rgba(0,0,0,0.7), transparent);\n pointer-events: none;\n z-index: -1;\n}\n\n.eb-bottom-bar__controls-row {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: 4px;\n padding: 4px 8px 8px;\n}\n\n.eb-bottom-bar__seekbar-zone {\n flex: 1;\n min-width: 0;\n}\n\n/* ============================================================\n Middle bar\n ============================================================ */\n.eb-middle-bar {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n pointer-events: auto;\n z-index: 25;\n display: flex;\n align-items: center;\n gap: 16px;\n}\n\n.eb-middle-bar__play-btn {\n width: 64px;\n height: 64px;\n border-radius: 50%;\n background: rgba(0,0,0,0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.eb-middle-bar__seek-btn {\n flex-direction: column;\n gap: 2px;\n background: none;\n padding: 4px;\n}\n\n.eb-middle-bar__seek-btn:hover {\n background: rgba(255,255,255,0.1);\n border-radius: 8px;\n}\n\n.eb-seek-circle {\n width: 32px;\n height: 32px;\n display: block;\n pointer-events: none;\n fill: currentColor;\n}\n\n.eb-seek-label {\n font-size: 11px;\n font-weight: 600;\n line-height: 1;\n opacity: 0.85;\n}\n\n/* ============================================================\n Extension zones\n ============================================================ */\n.eb-extension-top-extra,\n.eb-extension-bottom-extra,\n.eb-extension-overlay,\n.eb-extension-controls-extra {\n position: absolute;\n pointer-events: none;\n z-index: 35;\n}\n\n.eb-extension-top-extra > *,\n.eb-extension-bottom-extra > *,\n.eb-extension-overlay > *,\n.eb-extension-controls-extra > * {\n pointer-events: auto;\n}\n\n.eb-extension-top-extra {\n top: 0;\n left: 0;\n right: 0;\n}\n\n.eb-extension-bottom-extra {\n bottom: 0;\n left: 0;\n right: 0;\n}\n\n.eb-extension-overlay {\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.eb-extension-controls-extra {\n bottom: 0;\n right: 0;\n display: flex;\n align-items: center;\n gap: 4px;\n padding: 4px 8px 8px;\n}\n\n/* ============================================================\n Time display\n ============================================================ */\n.eb-time-display {\n font-size: 13px;\n line-height: 1;\n white-space: nowrap;\n padding: 0 4px;\n color: #fff;\n font-variant-numeric: tabular-nums;\n}\n\n/* ============================================================\n Volume control\n ============================================================ */\n.eb-volume-control {\n display: flex;\n align-items: center;\n gap: 4px;\n margin-right: 8px;\n}\n\n.eb-volume-track {\n position: relative;\n width: 60px;\n height: 4px;\n background: rgba(255, 255, 255, 0.2);\n border-radius: 2px;\n cursor: pointer;\n}\n\n.eb-volume-fill {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n background: var(--eb-color-volume, var(--eb-color-primary, #fff));\n border-radius: 2px;\n pointer-events: none;\n}\n\n.eb-volume-thumb {\n position: absolute;\n top: 50%;\n width: 10px;\n height: 10px;\n border-radius: 50%;\n background: var(--eb-color-volume, var(--eb-color-primary, #fff));\n transform: translate(-50%, -50%);\n pointer-events: none;\n}\n\n/* ============================================================\n Settings panel\n ============================================================ */\n.eb-settings-wrapper {\n position: relative;\n}\n\n.eb-settings-panel {\n position: absolute;\n background: rgba(0, 0, 0, 0.85);\n border-radius: 6px;\n min-width: 160px;\n max-width: 220px;\n overflow: hidden;\n font-size: 12px;\n z-index: 40;\n}\n\n/* Vertical direction: up (default) or down */\n.eb-settings-panel--up {\n bottom: calc(100% + 8px);\n}\n\n.eb-settings-panel--down {\n top: calc(100% + 8px);\n}\n\n/* Horizontal alignment: right (default) or left */\n.eb-settings-panel--right {\n right: 0;\n}\n\n.eb-settings-panel--left {\n left: 0;\n}\n\n.eb-settings-menu {\n list-style: none;\n margin: 0;\n padding: 4px 0;\n}\n\n.eb-settings-submenu {\n max-height: 200px;\n overflow-y: auto;\n}\n\n.eb-settings-submenu::-webkit-scrollbar {\n width: 4px;\n}\n\n.eb-settings-submenu::-webkit-scrollbar-thumb {\n background: rgba(255, 255, 255, 0.3);\n border-radius: 2px;\n}\n\n.eb-settings-header {\n display: flex;\n align-items: center;\n gap: 4px;\n padding: 5px 12px;\n}\n\n.eb-settings-category,\n.eb-settings-item {\n display: flex;\n align-items: center;\n justify-content: space-between;\n width: 100%;\n padding: 5px 12px;\n border: none;\n background: none;\n color: #fff;\n cursor: pointer;\n font-size: 12px;\n text-align: left;\n}\n\n.eb-settings-back {\n display: flex;\n align-items: center;\n border: none;\n background: none;\n color: #fff;\n cursor: pointer;\n padding: 0;\n}\n\n.eb-settings-category:hover,\n.eb-settings-item:hover {\n background: rgba(255, 255, 255, 0.1);\n}\n\n.eb-settings-item--selected {\n color: var(--eb-accent, var(--eb-color-primary, #e53935));\n}\n\n/* ============================================================\n Seekbar\n ============================================================ */\n.eb-seekbar {\n position: relative;\n width: 100%;\n padding: 8px 0;\n cursor: pointer;\n}\n\n.eb-seekbar-disabled {\n pointer-events: none;\n opacity: 0.4;\n}\n\n.eb-seekbar-track {\n position: relative;\n height: 4px;\n background: rgba(255, 255, 255, 0.2);\n border-radius: 2px;\n}\n\n.eb-seekbar-buffered {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n background: rgba(255, 255, 255, 0.4);\n border-radius: 2px;\n pointer-events: none;\n}\n\n.eb-seekbar-progress {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n background: var(--eb-color-progress, var(--eb-color-primary, #e53935));\n border-radius: 2px;\n pointer-events: none;\n}\n\n.eb-seekbar-thumb {\n position: absolute;\n right: -6px;\n top: 50%;\n width: 12px;\n height: 12px;\n border-radius: 50%;\n background: var(--eb-color-progress, var(--eb-color-primary, #e53935));\n transform: translateY(-50%) scale(0);\n transition: transform 0.15s;\n pointer-events: none;\n}\n\n.eb-seekbar:hover .eb-seekbar-thumb {\n transform: translateY(-50%) scale(1);\n}\n\n.eb-seekbar:hover .eb-seekbar-track {\n height: 6px;\n}\n\n.eb-seekbar-tooltip {\n position: absolute;\n bottom: calc(100% + 8px);\n transform: translateX(-50%);\n background: rgba(0, 0, 0, 0.8);\n color: #fff;\n font-size: 12px;\n padding: 4px 8px;\n border-radius: 3px;\n white-space: nowrap;\n pointer-events: none;\n text-align: center;\n z-index: 50;\n overflow: hidden;\n}\n\n.eb-seekbar-preview {\n position: static !important;\n width: 120px;\n height: 68px;\n min-width: 0 !important;\n min-height: 0 !important;\n max-width: none !important;\n max-height: none !important;\n display: block !important;\n object-fit: contain;\n background: #000;\n margin: 0 !important;\n padding: 0 !important;\n border: none !important;\n inset: auto !important;\n transform: none !important;\n z-index: auto !important;\n}\n\n.eb-chapter-marker {\n position: absolute;\n top: 0;\n width: 3px;\n height: 100%;\n background: rgba(255, 255, 255, 0.6);\n transform: translateX(-50%);\n pointer-events: none;\n}\n\n.eb-chapter-marker.eb-chapter-active {\n background: #fff;\n}\n\n.eb-chapter-skip {\n position: absolute;\n right: 0;\n bottom: calc(100% + 4px);\n background: rgba(0, 0, 0, 0.7);\n color: #fff;\n border: 1px solid rgba(255, 255, 255, 0.4);\n border-radius: 4px;\n padding: 6px 16px;\n font-size: 13px;\n cursor: pointer;\n}\n\n.eb-chapter-skip:hover {\n background: rgba(255, 255, 255, 0.2);\n}\n\n.eb-epg-segment {\n position: absolute;\n top: 0;\n height: 100%;\n border-right: 1px solid rgba(255, 255, 255, 0.3);\n pointer-events: none;\n}\n\n.eb-epg-segment.eb-epg-current {\n background: rgba(255, 255, 255, 0.1);\n}\n\n/* ============================================================\n Icons\n ============================================================ */\n.eb-icon {\n width: 20px;\n height: 20px;\n fill: none;\n stroke: currentColor;\n stroke-width: 2;\n stroke-linecap: round;\n stroke-linejoin: round;\n display: block;\n pointer-events: none;\n flex-shrink: 0;\n}\n\n/* ============================================================\n Buttons (base style)\n ============================================================ */\n.eb-button,\n.eb-play-pause,\n.eb-fullscreen,\n.eb-pip,\n.eb-cast,\n.eb-volume-mute,\n.eb-live-sync {\n cursor: pointer;\n background: none;\n border: none;\n padding: 6px;\n color: var(--eb-color-icon, inherit);\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 4px;\n transition: background 0.15s;\n -webkit-tap-highlight-color: transparent;\n}\n\n.eb-button:hover,\n.eb-play-pause:hover,\n.eb-fullscreen:hover,\n.eb-pip:hover,\n.eb-cast:hover,\n.eb-volume-mute:hover,\n.eb-live-sync:hover {\n background: rgba(255,255,255,0.15);\n}\n\n.eb-button:active,\n.eb-play-pause:active,\n.eb-fullscreen:active,\n.eb-pip:active,\n.eb-cast:active,\n.eb-volume-mute:active,\n.eb-live-sync:active {\n background: rgba(255,255,255,0.25);\n}\n\n/* ============================================================\n Radio overlay\n ============================================================ */\n.eb-radio-overlay {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: #000;\n z-index: 15;\n}\n\n.eb-radio-bars {\n display: flex;\n align-items: flex-end;\n gap: 4px;\n height: 40px;\n}\n\n.eb-radio-bar {\n display: block;\n width: 6px;\n background: #fff;\n border-radius: 3px;\n animation: eb-radio-bar-bounce 0.8s ease-in-out infinite;\n}\n\n.eb-radio-bar:nth-child(1) { animation-delay: 0s; height: 20px; }\n.eb-radio-bar:nth-child(2) { animation-delay: 0.1s; height: 32px; }\n.eb-radio-bar:nth-child(3) { animation-delay: 0.2s; height: 40px; }\n.eb-radio-bar:nth-child(4) { animation-delay: 0.3s; height: 28px; }\n.eb-radio-bar:nth-child(5) { animation-delay: 0.4s; height: 16px; }\n\n@keyframes eb-radio-bar-bounce {\n 0%, 100% { transform: scaleY(0.4); opacity: 0.7; }\n 50% { transform: scaleY(1); opacity: 1; }\n}\n\n/* Volume mute button — base styles shared via .eb-button group above */\n\n/* ============================================================\n Live sync button\n ============================================================ */\n.eb-live-sync[hidden] {\n display: none;\n}\n\n.eb-live-synced {\n color: var(--eb-accent, var(--eb-color-primary, #e53935));\n}\n\n/* Default theme: show icon only, hide text badge elements */\n.eb-live-sync__dot,\n.eb-live-sync__label {\n display: none;\n}\n\n/* ============================================================\n Overlay panels (error, info, socials — mounted in .eb-player)\n ============================================================ */\n.eb-error-slot,\n.eb-info-slot,\n.eb-socials-slot {\n position: absolute;\n inset: 0;\n z-index: 50;\n pointer-events: none;\n}\n\n.eb-toast-slot {\n position: absolute;\n bottom: 60px;\n left: 50%;\n transform: translateX(-50%);\n z-index: 50;\n pointer-events: none;\n}\n\n.eb-toast {\n background: rgba(0, 0, 0, 0.8);\n color: #fff;\n padding: 8px 20px;\n border-radius: 4px;\n font-size: 13px;\n white-space: nowrap;\n pointer-events: auto;\n}\n\n.eb-error,\n.eb-info-overlay,\n.eb-socials-overlay {\n position: absolute;\n inset: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n background: rgba(0, 0, 0, 0.75);\n color: #fff;\n z-index: 50;\n pointer-events: auto;\n padding: 24px;\n text-align: center;\n}\n\n.eb-error[hidden],\n.eb-info-overlay[hidden],\n.eb-socials-overlay[hidden] {\n display: none;\n}\n\n.eb-error-message {\n font-size: 16px;\n margin: 0 0 16px;\n}\n\n.eb-error-retry {\n background: rgba(255, 255, 255, 0.15);\n color: #fff;\n border: 1px solid rgba(255, 255, 255, 0.3);\n border-radius: 4px;\n padding: 8px 24px;\n font-size: 14px;\n cursor: pointer;\n}\n\n.eb-error-retry:hover {\n background: rgba(255, 255, 255, 0.25);\n}\n\n.eb-info-content {\n font-size: 14px;\n line-height: 1.5;\n max-width: 400px;\n margin-bottom: 16px;\n}\n\n.eb-info-close,\n.eb-socials-close {\n background: rgba(255, 255, 255, 0.15);\n color: #fff;\n border: 1px solid rgba(255, 255, 255, 0.3);\n border-radius: 4px;\n padding: 8px 24px;\n font-size: 14px;\n cursor: pointer;\n}\n\n.eb-info-close:hover,\n.eb-socials-close:hover {\n background: rgba(255, 255, 255, 0.25);\n}\n\n.eb-socials-links {\n display: flex;\n flex-wrap: wrap;\n gap: 12px;\n justify-content: center;\n margin-bottom: 16px;\n}\n\n.eb-socials-link {\n color: #fff;\n text-decoration: none;\n background: rgba(255, 255, 255, 0.1);\n border-radius: 4px;\n padding: 8px 16px;\n font-size: 14px;\n text-transform: capitalize;\n}\n\n.eb-socials-link:hover {\n background: rgba(255, 255, 255, 0.25);\n}\n\n/* ============================================================\n Loading spinner\n ============================================================ */\n.eb-loading {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 8px;\n color: #fff;\n pointer-events: none;\n}\n\n.eb-loading[hidden] {\n display: none;\n}\n\n.eb-loading .eb-icon {\n width: 40px;\n height: 40px;\n animation: eb-spin 1s linear infinite;\n}\n\n.eb-loading-text {\n font-size: 14px;\n}\n\n@keyframes eb-spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n}\n\n/* ============================================================\n Controls visibility\n ============================================================ */\n.eb-controls-visible .eb-top-bar,\n.eb-controls-visible .eb-bottom-bar,\n.eb-controls-visible .eb-middle-bar {\n opacity: 1;\n transition: opacity 0.3s;\n}\n\n.eb-controls-hidden .eb-top-bar,\n.eb-controls-hidden .eb-bottom-bar,\n.eb-controls-hidden .eb-middle-bar {\n opacity: 0;\n transition: opacity 0.3s;\n pointer-events: none;\n}\n\n/* ============================================================\n iOS native controls fallback\n ============================================================ */\n.eb-ios-native .eb-top-bar,\n.eb-ios-native .eb-bottom-bar,\n.eb-ios-native .eb-middle-bar,\n.eb-ios-native .eb-overlay-zone,\n.eb-ios-native .eb-error-slot,\n.eb-ios-native .eb-info-slot,\n.eb-ios-native .eb-socials-slot,\n.eb-ios-native .eb-toast-slot {\n display: none;\n}\n\n/* ============================================================\n RTL support\n ============================================================ */\n[dir=\"rtl\"] .eb-bottom-bar__controls-row {\n flex-direction: row-reverse;\n}\n\n[dir=\"rtl\"] .eb-top-bar {\n flex-direction: row-reverse;\n}\n\n/* ============================================================\n Responsive: ensure container fills parent\n ============================================================ */\n.eb-player,\n.eb-player video.eb-video {\n max-width: 100%;\n max-height: 100%;\n}\n";
55
55
  styleInject(css_248z);
56
56
 
57
57
  /**
@@ -1360,6 +1360,8 @@ var EBPlayerBundle = (function (exports) {
1360
1360
  this.tooltipX = 0;
1361
1361
  this.previewVideoEl = null;
1362
1362
  this.snapshotTake = null;
1363
+ this.trackEl = null;
1364
+ this.boundDocPointerMove = null;
1363
1365
  }
1364
1366
  onConnect() {
1365
1367
  // Subscribe to snapshot handler readiness (emitted by eb-player.ts)
@@ -1380,6 +1382,10 @@ var EBPlayerBundle = (function (exports) {
1380
1382
  this.state.on('epgPrograms', () => this.scheduleRender(), { signal: this.signal });
1381
1383
  this.render();
1382
1384
  }
1385
+ disconnect() {
1386
+ this.stopDocumentTracking();
1387
+ super.disconnect();
1388
+ }
1383
1389
  // ---- rAF batching ----
1384
1390
  scheduleRender() {
1385
1391
  if (this.rafPending)
@@ -1402,11 +1408,47 @@ var EBPlayerBundle = (function (exports) {
1402
1408
  const clamped = Math.min(1, Math.max(0, percent));
1403
1409
  return clamped * this.state.duration;
1404
1410
  }
1411
+ // ---- Document-level pointer tracking ----
1412
+ // pointerleave/mouseleave on the track are unreliable when the tooltip (a DOM child)
1413
+ // is absolutely positioned above the track — some browsers consider the pointer still
1414
+ // "inside" the track's subtree. Instead, we track pointer position at the document level
1415
+ // and hide the tooltip when the pointer moves outside the track's bounding rect.
1416
+ startDocumentTracking() {
1417
+ if (this.boundDocPointerMove)
1418
+ return;
1419
+ this.boundDocPointerMove = (event) => this.onDocPointerMove(event);
1420
+ document.addEventListener('pointermove', this.boundDocPointerMove);
1421
+ document.addEventListener('mouseleave', this.boundDocPointerMove);
1422
+ }
1423
+ stopDocumentTracking() {
1424
+ if (!this.boundDocPointerMove)
1425
+ return;
1426
+ document.removeEventListener('pointermove', this.boundDocPointerMove);
1427
+ document.removeEventListener('mouseleave', this.boundDocPointerMove);
1428
+ this.boundDocPointerMove = null;
1429
+ }
1430
+ onDocPointerMove(event) {
1431
+ if (this.isDragging)
1432
+ return;
1433
+ if (!this.trackEl)
1434
+ return;
1435
+ const rect = this.trackEl.getBoundingClientRect();
1436
+ const inBounds = event.clientX >= rect.left
1437
+ && event.clientX <= rect.right
1438
+ && event.clientY >= rect.top
1439
+ && event.clientY <= rect.bottom;
1440
+ if (!inBounds) {
1441
+ this.tooltipVisible = false;
1442
+ this.stopDocumentTracking();
1443
+ this.render();
1444
+ }
1445
+ }
1405
1446
  // ---- Drag handlers ----
1406
1447
  handlePointerDown(event) {
1407
1448
  if (this.state.adPlaying)
1408
1449
  return;
1409
1450
  const trackEl = event.currentTarget;
1451
+ this.trackEl = trackEl;
1410
1452
  // setPointerCapture ensures events continue firing even if pointer leaves the element
1411
1453
  if (typeof trackEl.setPointerCapture === 'function') {
1412
1454
  trackEl.setPointerCapture(event.pointerId);
@@ -1437,20 +1479,16 @@ var EBPlayerBundle = (function (exports) {
1437
1479
  const seekTime = this.eventToTime(event, trackEl);
1438
1480
  this.isDragging = false;
1439
1481
  this.bus.emit('seek', { time: seekTime });
1440
- // Hide tooltip if pointer is outside the track bounds after drag ends
1441
- const rect = trackEl.getBoundingClientRect();
1442
- if (event.clientX < rect.left || event.clientX > rect.right) {
1443
- this.tooltipVisible = false;
1444
- }
1445
- this.render();
1446
- }
1447
- handlePointerLeave() {
1482
+ // Always hide tooltip on pointer up it reappears naturally via pointermove
1483
+ // if the pointer is still over the track.
1448
1484
  this.tooltipVisible = false;
1485
+ this.stopDocumentTracking();
1449
1486
  this.render();
1450
1487
  }
1451
1488
  // ---- Tooltip ----
1452
1489
  updateTooltip(event) {
1453
1490
  const trackEl = event.currentTarget;
1491
+ this.trackEl = trackEl;
1454
1492
  const rect = trackEl.getBoundingClientRect();
1455
1493
  // Compute hover time (use LTR calculation for tooltip position regardless of RTL)
1456
1494
  const rawPercent = (event.clientX - rect.left) / rect.width;
@@ -1461,6 +1499,8 @@ var EBPlayerBundle = (function (exports) {
1461
1499
  // Position tooltip at pointer X relative to track, clamped to track edges
1462
1500
  this.tooltipX = Math.min(rect.width, Math.max(0, event.clientX - rect.left));
1463
1501
  this.tooltipVisible = true;
1502
+ // Start document-level tracking to detect when pointer leaves the track
1503
+ this.startDocumentTracking();
1464
1504
  // Request snapshot frame for seekbar preview thumbnail
1465
1505
  if (this.snapshotTake !== null) {
1466
1506
  this.snapshotTake(this.tooltipTime);
@@ -1553,11 +1593,10 @@ var EBPlayerBundle = (function (exports) {
1553
1593
  const tooltip = b `
1554
1594
  <div
1555
1595
  class="eb-seekbar-tooltip"
1556
- style="left: ${this.tooltipX}px"
1557
- ?hidden="${!this.tooltipVisible}"
1596
+ style="left: ${this.tooltipX}px; visibility: ${this.tooltipVisible ? 'visible' : 'hidden'}"
1558
1597
  >
1559
- ${tooltipTimeText}
1560
1598
  ${this.previewVideoEl !== null ? this.previewVideoEl : b ``}
1599
+ ${tooltipTimeText}
1561
1600
  </div>
1562
1601
  `;
1563
1602
  return b `
@@ -1570,7 +1609,6 @@ var EBPlayerBundle = (function (exports) {
1570
1609
  @pointerdown="${(event) => this.handlePointerDown(event)}"
1571
1610
  @pointermove="${(event) => this.handlePointerMove(event)}"
1572
1611
  @pointerup="${(event) => this.handlePointerUp(event)}"
1573
- @pointerleave="${() => this.handlePointerLeave()}"
1574
1612
  >
1575
1613
  <div class="eb-seekbar-buffered" style="width: ${bufferedPercent.toFixed(2)}%"></div>
1576
1614
  <div class="eb-seekbar-progress" style="width: ${progressPercent.toFixed(2)}%">
@@ -2268,13 +2306,24 @@ var EBPlayerBundle = (function (exports) {
2268
2306
  *
2269
2307
  * - Calls screenfull.toggle() with the closest .eb-player ancestor on click
2270
2308
  * - Subscribes to screenfull 'change' event to update state.isFullscreen
2271
- * - Hidden when screenfull.isEnabled is false (e.g. iOS Safari)
2309
+ * - Falls back to video-element fullscreen on mobile (iOS Safari webkitEnterFullscreen,
2310
+ * Android Chrome video.requestFullscreen) when the Fullscreen API is not available
2311
+ * for arbitrary elements
2272
2312
  * - Re-renders when state.isFullscreen changes
2273
2313
  */
2274
2314
  class FullscreenButton extends BaseComponent {
2275
2315
  constructor() {
2276
2316
  super(...arguments);
2277
2317
  this.changeHandler = null;
2318
+ this.videoEl = null;
2319
+ this.videoFullscreenBeginHandler = null;
2320
+ this.videoFullscreenEndHandler = null;
2321
+ this.useVideoFallback = false;
2322
+ this.handleDocFullscreenChange = () => {
2323
+ const isFullscreen = document.fullscreenElement === this.videoEl;
2324
+ this.state.isFullscreen = isFullscreen;
2325
+ this.render();
2326
+ };
2278
2327
  }
2279
2328
  onConnect() {
2280
2329
  this.state.on('isFullscreen', () => this.render(), { signal: this.signal });
@@ -2284,25 +2333,90 @@ var EBPlayerBundle = (function (exports) {
2284
2333
  this.render();
2285
2334
  };
2286
2335
  screenfull$1.on('change', this.changeHandler);
2287
- // Unsubscribe when signal aborts (on disconnect)
2288
2336
  this.signal.addEventListener('abort', () => {
2289
2337
  screenfull$1.off('change', this.changeHandler);
2290
2338
  this.changeHandler = null;
2291
2339
  });
2292
2340
  }
2341
+ else {
2342
+ // Fullscreen API not available for elements — try video-element fallback (mobile)
2343
+ this.initVideoFallback();
2344
+ }
2293
2345
  this.render();
2294
2346
  }
2347
+ /**
2348
+ * On mobile browsers the Fullscreen API may not work on arbitrary elements,
2349
+ * but the <video> element itself supports fullscreen via webkitEnterFullscreen
2350
+ * (iOS Safari) or video.requestFullscreen() (Android Chrome).
2351
+ */
2352
+ initVideoFallback() {
2353
+ const playerRoot = this.el?.closest('.eb-player');
2354
+ if (!playerRoot)
2355
+ return;
2356
+ const video = playerRoot.querySelector('video.eb-video');
2357
+ if (!video)
2358
+ return;
2359
+ // iOS Safari: webkitSupportsFullscreen + webkitEnterFullscreen
2360
+ const hasWebkitFullscreen = typeof video.webkitEnterFullscreen === 'function';
2361
+ // Android Chrome: standard requestFullscreen on the video element
2362
+ const hasRequestFullscreen = typeof video.requestFullscreen === 'function';
2363
+ if (!hasWebkitFullscreen && !hasRequestFullscreen)
2364
+ return;
2365
+ this.videoEl = video;
2366
+ this.useVideoFallback = true;
2367
+ // Track fullscreen state changes on the video element
2368
+ this.videoFullscreenBeginHandler = () => {
2369
+ this.state.isFullscreen = true;
2370
+ this.render();
2371
+ };
2372
+ this.videoFullscreenEndHandler = () => {
2373
+ this.state.isFullscreen = false;
2374
+ this.render();
2375
+ };
2376
+ // iOS Safari fires these events on the video element
2377
+ video.addEventListener('webkitbeginfullscreen', this.videoFullscreenBeginHandler);
2378
+ video.addEventListener('webkitendfullscreen', this.videoFullscreenEndHandler);
2379
+ // Android Chrome fires standard fullscreenchange on the document
2380
+ document.addEventListener('fullscreenchange', this.handleDocFullscreenChange);
2381
+ this.signal.addEventListener('abort', () => {
2382
+ video.removeEventListener('webkitbeginfullscreen', this.videoFullscreenBeginHandler);
2383
+ video.removeEventListener('webkitendfullscreen', this.videoFullscreenEndHandler);
2384
+ document.removeEventListener('fullscreenchange', this.handleDocFullscreenChange);
2385
+ this.videoFullscreenBeginHandler = null;
2386
+ this.videoFullscreenEndHandler = null;
2387
+ this.videoEl = null;
2388
+ });
2389
+ }
2295
2390
  handleClick() {
2296
- if (!screenfull$1.isEnabled)
2391
+ if (screenfull$1.isEnabled) {
2392
+ const container = (this.el.closest('.eb-player') ?? this.el);
2393
+ screenfull$1.toggle(container);
2297
2394
  return;
2298
- // Use the closest .eb-player ancestor as the fullscreen target,
2299
- // falling back to this component's own element.
2300
- // this.el is always non-null when this handler is called from a mounted button.
2301
- const container = (this.el.closest('.eb-player') ?? this.el);
2302
- screenfull$1.toggle(container);
2395
+ }
2396
+ if (this.useVideoFallback && this.videoEl) {
2397
+ if (this.state.isFullscreen) {
2398
+ // Exit fullscreen
2399
+ if (typeof this.videoEl.webkitExitFullscreen === 'function') {
2400
+ this.videoEl.webkitExitFullscreen();
2401
+ }
2402
+ else if (document.exitFullscreen) {
2403
+ document.exitFullscreen();
2404
+ }
2405
+ }
2406
+ else {
2407
+ // Enter fullscreen
2408
+ if (typeof this.videoEl.webkitEnterFullscreen === 'function') {
2409
+ this.videoEl.webkitEnterFullscreen();
2410
+ }
2411
+ else if (typeof this.videoEl.requestFullscreen === 'function') {
2412
+ this.videoEl.requestFullscreen();
2413
+ }
2414
+ }
2415
+ }
2303
2416
  }
2304
2417
  template() {
2305
- if (!screenfull$1.isEnabled) {
2418
+ const canFullscreen = screenfull$1.isEnabled || this.useVideoFallback;
2419
+ if (!canFullscreen) {
2306
2420
  return b `<button class="eb-fullscreen" hidden aria-hidden="true">${icon('fullscreen')}</button>`;
2307
2421
  }
2308
2422
  const isFullscreen = this.state.isFullscreen;
@@ -5928,7 +6042,7 @@ var EBPlayerBundle = (function (exports) {
5928
6042
  init(HlsConstructor) {
5929
6043
  // Create an off-screen video element for the snapshot player
5930
6044
  const offscreenVideo = document.createElement('video');
5931
- offscreenVideo.preload = 'none';
6045
+ offscreenVideo.preload = 'metadata';
5932
6046
  this.offscreenVideo = offscreenVideo;
5933
6047
  // Capture tokenManager via closure (Pitfall 6)
5934
6048
  const tokenManager = this.tokenManager;
@@ -5959,11 +6073,17 @@ var EBPlayerBundle = (function (exports) {
5959
6073
  }
5960
6074
  PLoader = SnapshotPLoader;
5961
6075
  }
6076
+ // Strip player-specific keys that are NOT hls.js config options — they contain
6077
+ // non-serializable functions that cause DataCloneError when hls.js posts config to its worker.
6078
+ const rawSettings = { ...(this.config.engineSettings ?? {}) };
6079
+ delete rawSettings['extraParamsCallback'];
6080
+ delete rawSettings['onCDNTokenError'];
5962
6081
  const driverConfig = {
5963
6082
  startLevel: 0,
5964
6083
  enableWebVTT: false,
6084
+ enableWorker: false,
5965
6085
  maxBufferLength: 1,
5966
- ...(this.config.engineSettings ?? {}),
6086
+ ...rawSettings,
5967
6087
  ...(PLoader ? { pLoader: PLoader } : {})
5968
6088
  };
5969
6089
  const driver = new HlsConstructor(driverConfig);
@@ -6294,8 +6414,30 @@ var EBPlayerBundle = (function (exports) {
6294
6414
  else {
6295
6415
  const win = window;
6296
6416
  if (win.Hls) {
6297
- const handler = new HlsSnapshotHandler({ src, engineSettings: mergedConfig.engineSettings }, null);
6298
- handler.init(win.Hls)
6417
+ // Create a dedicated token manager for the snapshot handler (DRM license + manifest tokens)
6418
+ let snapshotTokenManager = null;
6419
+ if (mergedConfig.token) {
6420
+ snapshotTokenManager = new CDNTokenManager({
6421
+ token: mergedConfig.token,
6422
+ tokenType: mergedConfig.tokenType,
6423
+ srcInTokenRequest: mergedConfig.srcInTokenRequest,
6424
+ extraParamsCallback: (mergedConfig.engineSettings.extraParamsCallback ?? mergedConfig.extraParamsCallback),
6425
+ onCDNTokenError: mergedConfig.engineSettings.onCDNTokenError
6426
+ });
6427
+ }
6428
+ // Build DRM config (emeEnabled, drmSystems, licenseXhrSetup) for the snapshot hls.js instance
6429
+ const snapshotDrmConfigurator = new DrmConfigurator(snapshotTokenManager);
6430
+ const snapshotDrmConfig = snapshotDrmConfigurator.buildHlsConfig(mergedConfig.engineSettings);
6431
+ const handler = new HlsSnapshotHandler({ src, engineSettings: { ...mergedConfig.engineSettings, ...snapshotDrmConfig } }, snapshotTokenManager);
6432
+ // Fetch initial token before init (needed for manifest request)
6433
+ const tokenReady = snapshotTokenManager && src
6434
+ ? snapshotTokenManager.fetchToken({ src }).catch((error) => {
6435
+ console.warn('EBPlayer: Snapshot token fetch failed:', error);
6436
+ })
6437
+ : Promise.resolve();
6438
+ tokenReady.then(() => {
6439
+ return handler.init(win.Hls);
6440
+ })
6299
6441
  .then(() => {
6300
6442
  activeSnapshotDestroy = () => handler.destroy();
6301
6443
  const snapshotVideo = handler.getVideo();