vidply 1.0.34 → 1.0.36

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 (42) hide show
  1. package/dist/dev/{vidply.SoundCloudRenderer-RIA3QKP3.js → vidply.SoundCloudRenderer-HCMKXHSX.js} +1 -3
  2. package/dist/dev/vidply.SoundCloudRenderer-HCMKXHSX.js.map +7 -0
  3. package/dist/dev/{vidply.TranscriptManager-T3BVTZHZ.js → vidply.TranscriptManager-EIIN5YOF.js} +2 -2
  4. package/dist/dev/{vidply.VimeoRenderer-DY2FG7LZ.js → vidply.VimeoRenderer-SLEBCZTT.js} +1 -2
  5. package/dist/dev/vidply.VimeoRenderer-SLEBCZTT.js.map +7 -0
  6. package/dist/dev/{vidply.YouTubeRenderer-EVXXE34A.js → vidply.YouTubeRenderer-E6F4UGVF.js} +1 -2
  7. package/dist/dev/vidply.YouTubeRenderer-E6F4UGVF.js.map +7 -0
  8. package/dist/dev/{vidply.chunk-74NJTDQI.js → vidply.chunk-AXXU22HR.js} +87 -10
  9. package/dist/dev/{vidply.chunk-74NJTDQI.js.map → vidply.chunk-AXXU22HR.js.map} +2 -2
  10. package/dist/dev/vidply.esm.js +83 -50
  11. package/dist/dev/vidply.esm.js.map +2 -2
  12. package/dist/legacy/vidply.js +170 -58
  13. package/dist/legacy/vidply.js.map +3 -3
  14. package/dist/legacy/vidply.min.js +1 -1
  15. package/dist/legacy/vidply.min.meta.json +18 -18
  16. package/dist/prod/vidply.SoundCloudRenderer-D2FNOEG6.min.js +6 -0
  17. package/dist/prod/{vidply.TranscriptManager-GPAOXEK4.min.js → vidply.TranscriptManager-VXCTCJ7X.min.js} +1 -1
  18. package/dist/prod/vidply.VimeoRenderer-QELFZVDU.min.js +6 -0
  19. package/dist/prod/vidply.YouTubeRenderer-ZL6YUHTF.min.js +6 -0
  20. package/dist/prod/{vidply.chunk-OM7DNW5P.min.js → vidply.chunk-Z6BHMOGK.min.js} +1 -1
  21. package/dist/prod/vidply.esm.min.js +3 -3
  22. package/dist/vidply.css +293 -108
  23. package/dist/vidply.esm.min.meta.json +33 -33
  24. package/dist/vidply.min.css +1 -1
  25. package/package.json +3 -3
  26. package/src/controls/ControlBar.js +3 -0
  27. package/src/controls/KeyboardManager.js +19 -0
  28. package/src/core/Player.js +75 -64
  29. package/src/core/SignLanguageManager.js +18 -4
  30. package/src/index.js +3 -1
  31. package/src/renderers/SoundCloudRenderer.js +0 -2
  32. package/src/renderers/VimeoRenderer.js +0 -1
  33. package/src/renderers/YouTubeRenderer.js +0 -1
  34. package/src/styles/vidply.css +293 -108
  35. package/src/utils/DraggableResizable.js +123 -12
  36. package/dist/dev/vidply.SoundCloudRenderer-RIA3QKP3.js.map +0 -7
  37. package/dist/dev/vidply.VimeoRenderer-DY2FG7LZ.js.map +0 -7
  38. package/dist/dev/vidply.YouTubeRenderer-EVXXE34A.js.map +0 -7
  39. package/dist/prod/vidply.SoundCloudRenderer-BFV5SSIU.min.js +0 -6
  40. package/dist/prod/vidply.VimeoRenderer-UQWHQ4LC.min.js +0 -6
  41. package/dist/prod/vidply.YouTubeRenderer-K7A57ICA.min.js +0 -6
  42. /package/dist/dev/{vidply.TranscriptManager-T3BVTZHZ.js.map → vidply.TranscriptManager-EIIN5YOF.js.map} +0 -0
package/dist/vidply.css CHANGED
@@ -149,13 +149,13 @@
149
149
 
150
150
  /* Z-Index Layering System */
151
151
  --vidply-z-base: 1; /* Video, base elements */
152
- --vidply-z-overlay: 2; /* Play overlay, video wrapper on mobile */
153
- --vidply-z-transcript: 5; /* Transcript window */
154
- --vidply-z-playlist: 15; /* Playlist panel in fullscreen */
155
- --vidply-z-menu: 20; /* Menus in normal mode */
156
152
  --vidply-z-controls: 30; /* Control bar */
157
- --vidply-z-menu-high: 100; /* Volume menu */
153
+ --vidply-z-menu: 20; /* Menus in normal mode */
158
154
  --vidply-z-menu-fullscreen: 1000; /* Menus in fullscreen mode */
155
+ --vidply-z-menu-high: 100; /* Volume menu */
156
+ --vidply-z-overlay: 2; /* Play overlay, video wrapper on mobile */
157
+ --vidply-z-playlist: 15; /* Playlist panel in fullscreen */
158
+ --vidply-z-transcript: 5; /* Transcript window */
159
159
  }
160
160
 
161
161
  /* Utility Classes */
@@ -426,7 +426,7 @@
426
426
  }
427
427
 
428
428
  /* Mobile: Ensure player contains all elements properly */
429
- @media (width < 48rem) {
429
+ @media (width <= 48rem), (width < 48rem) {
430
430
  .vidply-player {
431
431
  isolation: isolate; /* Create stacking context */
432
432
  overflow: visible; /* Allow menus to overflow but within bounds */
@@ -442,7 +442,6 @@
442
442
  /* Responsive container */
443
443
 
444
444
  .vidply-player.vidply-audio {
445
- aspect-ratio: auto;
446
445
  background: linear-gradient(135deg, var(--vidply-primary-20) 0%, rgb(var(--vidply-primary-dark-rgb), 0.2) 100%);
447
446
  height: auto;
448
447
  }
@@ -461,11 +460,14 @@
461
460
  }
462
461
 
463
462
  /* Mobile: Video element order and sizing */
464
- @media (width < 48rem) {
463
+ @media (width <= 48rem), (width < 48rem) {
465
464
  .vidply-player video,
466
465
  .vidply-player audio {
467
466
  flex: 0 0 auto; /* Don't grow or shrink */
468
- height: auto;
467
+
468
+ /* Player sets inline `height: 100%` on the media element.
469
+ On mobile we need `auto` to prevent wrapper height collapse / overlap. */
470
+ height: auto !important;
469
471
  order: 1; /* Before controls */
470
472
  }
471
473
 
@@ -500,6 +502,16 @@
500
502
  z-index: 1; /* Base video layer */
501
503
  }
502
504
 
505
+ /* Ensure the video wrapper establishes an intrinsic height in ALL non-fullscreen modes.
506
+ Controls are appended inside `.vidply-video-wrapper` and are positioned `absolute` by default.
507
+ If the wrapper has no intrinsic height,
508
+ the wrapper can collapse and the controls will overlap the content that follows (like `.vidply-track-info`).
509
+
510
+ We keep fullscreen behavior untouched (fullscreen rules already size the wrapper explicitly). */
511
+ .vidply-player.vidply-video:not(.vidply-fullscreen, :fullscreen) .vidply-video-wrapper {
512
+ height: auto;
513
+ }
514
+
503
515
  /* Allow controls and menus to overflow in fullscreen landscape mode */
504
516
  @media (orientation: landscape) {
505
517
  .vidply-player.vidply-fullscreen .vidply-video-wrapper,
@@ -520,17 +532,19 @@
520
532
  }
521
533
 
522
534
  /* Audio content in video player - uses 16:3 aspect ratio for a banner-like appearance */
535
+
523
536
  /* When audio files are played in a video element, we use CSS poster overlay for better control */
524
537
  .vidply-player.vidply-video.vidply-audio-content .vidply-video-wrapper {
525
- aspect-ratio: 16 / 3 !important;
526
538
  height: auto !important;
527
- min-height: 0 !important;
539
+
540
+ /* We still need a visible box for the poster banner */
541
+ min-height: 7.5rem !important;
528
542
  overflow: hidden;
529
543
  }
530
544
 
531
545
  .vidply-player.vidply-video.vidply-audio-content .vidply-video-wrapper.vidply-forced-poster {
532
- background-size: cover;
533
546
  background-position: center;
547
+ background-size: cover;
534
548
  }
535
549
 
536
550
  /* Hide the video element completely when playing audio - let the wrapper show the poster */
@@ -538,40 +552,48 @@
538
552
  display: none !important;
539
553
  }
540
554
 
541
- /* YouTube and Vimeo iframe containers - ensure 16:9 aspect ratio */
542
- .vidply-video-wrapper > div[id^="youtube-player-"],
555
+ /* YouTube and Vimeo iframe containers - keep classic 16:9 framing */
556
+ .vidply-video-wrapper > iframe[id^="youtube-player-"],
543
557
  .vidply-video-wrapper > div[id^="vimeo-player-"] {
544
- width: 100% !important;
545
558
  aspect-ratio: 16 / 9;
546
- max-height: 100%;
559
+ height: auto;
560
+ max-height: none !important; /* override renderer inline maxHeight=100% */
561
+ position: relative; /* ensure absolutely-positioned iframes size against this box */
562
+ width: 100% !important;
547
563
  }
548
564
 
549
565
  .vidply-video-wrapper > div[id^="youtube-player-"] iframe,
550
566
  .vidply-video-wrapper > div[id^="vimeo-player-"] iframe {
567
+ aspect-ratio: 16 / 9;
568
+
569
+ /* Some providers inject height:100% which can ignore the intended 16:9 sizing.
570
+ Force the iframe itself to be 16:9 and let height be derived. */
571
+ display: block;
572
+ height: auto !important;
573
+ position: relative;
551
574
  width: 100% !important;
552
- height: 100% !important;
553
575
  }
554
576
 
555
- /* SoundCloud iframe - uses 16:3 aspect ratio for single track audio banner appearance */
577
+ /* SoundCloud iframe - stable height */
556
578
  .vidply-video-wrapper > iframe.vidply-soundcloud-iframe {
579
+ height: clamp(6rem, 18vh, 12rem);
557
580
  width: 100% !important;
558
- aspect-ratio: 16 / 3;
559
- max-height: 100%;
560
581
  }
561
582
 
562
- /* SoundCloud playlist - uses 16:9 aspect ratio to show track list */
583
+ /* SoundCloud playlist - taller variant to show track list */
563
584
  .vidply-video-wrapper > iframe.vidply-soundcloud-iframe.vidply-soundcloud-playlist {
564
- aspect-ratio: 16 / 9;
585
+ height: clamp(12.5rem, 45vh, 28rem);
565
586
  }
566
587
 
567
588
  /* Hide VidPly controls when external renderer (YouTube, Vimeo, SoundCloud) is active */
589
+
568
590
  /* These services have their own native controls */
569
591
  .vidply-player.vidply-external-controls .vidply-controls {
570
592
  display: none !important;
571
593
  }
572
594
 
573
595
  /* Mobile: Simplify video wrapper but keep captions contained */
574
- @media (width < 48rem) {
596
+ @media (width <= 48rem), (width < 48rem) {
575
597
  .vidply-video-wrapper {
576
598
  display: block;
577
599
  height: auto;
@@ -589,30 +611,31 @@
589
611
  /* Ensure video doesn't collapse */
590
612
  .vidply-player video {
591
613
  display: block;
614
+ height: auto !important;
592
615
  position: relative;
593
- width: 100%;
616
+ width: 100%;
594
617
  }
595
618
 
596
619
  /* Override mobile rules in fullscreen mode - critical for iOS */
597
620
  .vidply-player.vidply-fullscreen .vidply-video-wrapper,
598
621
  .vidply-player:fullscreen .vidply-video-wrapper {
622
+ bottom: 0 !important;
599
623
  display: flex !important;
600
624
  height: 100% !important;
625
+ left: 0 !important;
601
626
  min-height: 0 !important;
602
627
  position: absolute !important;
603
- top: 0 !important;
604
- left: 0 !important;
605
628
  right: 0 !important;
606
- bottom: 0 !important;
629
+ top: 0 !important;
607
630
  }
608
631
 
609
632
  .vidply-player.vidply-fullscreen video,
610
633
  .vidply-player:fullscreen video {
634
+ height: 100% !important;
635
+ left: 0 !important;
611
636
  position: absolute !important;
612
637
  top: 0 !important;
613
- left: 0 !important;
614
638
  width: 100% !important;
615
- height: 100% !important;
616
639
  }
617
640
  }
618
641
 
@@ -628,6 +651,8 @@
628
651
 
629
652
  /* Centered Play Button Overlay */
630
653
  .vidply-play-overlay {
654
+ border: 0.125rem solid var(--vidply-primary);
655
+ border-radius: 50%;
631
656
  cursor: pointer;
632
657
  filter: drop-shadow(0 0.5rem 2rem rgb(0 0 0 / 30%));
633
658
  left: 50%;
@@ -636,8 +661,6 @@
636
661
  transform: translate(-50%, -50%);
637
662
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
638
663
  z-index: var(--vidply-z-overlay);
639
- border: 0.125rem solid var(--vidply-primary);
640
- border-radius: 50%;
641
664
  }
642
665
 
643
666
  .vidply-play-overlay:hover {
@@ -689,7 +712,7 @@
689
712
  }
690
713
 
691
714
  /* Mobile: Controls underneath video (but not in landscape fullscreen) */
692
- @media (width < 48rem) {
715
+ @media (width <= 48rem), (width < 48rem) {
693
716
  .vidply-controls {
694
717
  background: var(--vidply-black-90);
695
718
  border-top: 0.0625rem solid var(--vidply-border-light);
@@ -799,22 +822,22 @@
799
822
  color: var(--vidply-white);
800
823
  display: none;
801
824
  font-size: 0.75rem;
825
+ overflow: hidden;
802
826
  padding: 0;
803
827
  pointer-events: none;
804
828
  position: absolute;
805
829
  transform: translateX(-50%);
806
830
  white-space: nowrap;
807
- overflow: hidden;
808
831
  }
809
832
 
810
833
  .vidply-progress-preview {
811
- background-size: cover;
812
834
  background-position: center;
835
+ background-size: cover;
813
836
  border-radius: 0.25rem 0.25rem 0 0;
814
837
  display: none;
815
838
  height: 5rem;
816
- width: 8.888888889rem; /* 160px at 18px base */
817
839
  min-width: 8.888888889rem;
840
+ width: 8.888888889rem; /* 160px at 18px base */
818
841
  }
819
842
 
820
843
  .vidply-progress-tooltip-time {
@@ -1084,7 +1107,7 @@
1084
1107
  }
1085
1108
 
1086
1109
  /* Mobile: Don't use backdrop, menus position above buttons */
1087
- @media (width < 48rem) {
1110
+ @media (width <= 48rem), (width < 48rem) {
1088
1111
  .vidply-menu-backdrop {
1089
1112
  display: none !important;
1090
1113
  }
@@ -1123,6 +1146,11 @@
1123
1146
  }
1124
1147
 
1125
1148
  /* Menu positioned below button */
1149
+ .vidply-menu.vidply-menu-below {
1150
+ bottom: auto;
1151
+ top: calc(100% + 0.5rem);
1152
+ }
1153
+
1126
1154
  .vidply-menu.vidply-menu-below::after {
1127
1155
  border-bottom: 0.375rem solid var(--vidply-bg-menu);
1128
1156
  border-top: none;
@@ -1246,10 +1274,10 @@
1246
1274
  left: auto !important;
1247
1275
  max-width: calc(100vw - 1.25rem);
1248
1276
  right: 0 !important;
1249
- z-index: 1000; /* Ensure menu appears above all player elements including playlist */
1277
+ transform: none !important;
1250
1278
 
1251
1279
  /* Ensure menu stays within viewport */
1252
- transform: none !important;
1280
+ z-index: 1000; /* Ensure menu appears above all player elements including playlist */
1253
1281
  }
1254
1282
 
1255
1283
  /* Overflow menu items with icons */
@@ -1482,7 +1510,7 @@
1482
1510
  }
1483
1511
 
1484
1512
  /* Mobile: Caption positioning handled by JavaScript */
1485
- @media (width < 48rem) {
1513
+ @media (width <= 48rem), (width < 48rem) {
1486
1514
  .vidply-captions {
1487
1515
  /* Bottom position set dynamically by JS based on control height */
1488
1516
  left: 50%;
@@ -1695,9 +1723,84 @@
1695
1723
  /* Fullscreen Styles */
1696
1724
  .vidply-player.vidply-fullscreen,
1697
1725
  .vidply-player:fullscreen {
1726
+ background: #000;
1698
1727
  height: 100vh;
1699
1728
  max-width: none;
1700
1729
  width: 100vw;
1730
+ z-index: 2147483647; /* Maximum z-index to ensure we're above everything */
1731
+ }
1732
+
1733
+ /* Video wrapper also needs black background in fullscreen to prevent letterbox areas showing gray */
1734
+ .vidply-player.vidply-fullscreen .vidply-video-wrapper,
1735
+ .vidply-player:fullscreen .vidply-video-wrapper {
1736
+ background: #000 !important;
1737
+ }
1738
+
1739
+ /* Fullscreen backdrop - the pseudo-element behind the fullscreen element */
1740
+ .vidply-player:fullscreen::backdrop {
1741
+ background: #000;
1742
+ }
1743
+
1744
+ /* Webkit/Safari prefix for backdrop */
1745
+ .vidply-player:-webkit-full-screen::backdrop {
1746
+ background: #000;
1747
+ }
1748
+
1749
+ /* Hide fixed/sticky positioned elements when any vidply player is fullscreen */
1750
+ /* This ensures headers, nav bars, and other fixed elements don't appear above the player */
1751
+ :root:has(.vidply-player:fullscreen) .fixed-top,
1752
+ :root:has(.vidply-player:fullscreen) .fixed-bottom,
1753
+ :root:has(.vidply-player:fullscreen) .sticky-top,
1754
+ :root:has(.vidply-player:fullscreen) [style*="position: fixed"],
1755
+ :root:has(.vidply-player:fullscreen) [style*="position:fixed"] {
1756
+ display: none !important;
1757
+ }
1758
+
1759
+ /* Same for webkit fullscreen */
1760
+ :root:has(.vidply-player:-webkit-full-screen) .fixed-top,
1761
+ :root:has(.vidply-player:-webkit-full-screen) .fixed-bottom,
1762
+ :root:has(.vidply-player:-webkit-full-screen) .sticky-top,
1763
+ :root:has(.vidply-player:-webkit-full-screen) [style*="position: fixed"],
1764
+ :root:has(.vidply-player:-webkit-full-screen) [style*="position:fixed"] {
1765
+ display: none !important;
1766
+ }
1767
+
1768
+ /* Fallback: Also hide elements when pseudo-fullscreen class is applied (for iOS/Safari fallback) */
1769
+ :root:has(.vidply-player.vidply-fullscreen) .fixed-top,
1770
+ :root:has(.vidply-player.vidply-fullscreen) .fixed-bottom,
1771
+ :root:has(.vidply-player.vidply-fullscreen) .sticky-top {
1772
+ display: none !important;
1773
+ }
1774
+
1775
+ /* Additional fallback using body class for browsers that don't support :has() */
1776
+ body.vidply-fullscreen-active .fixed-top,
1777
+ body.vidply-fullscreen-active .fixed-bottom,
1778
+ body.vidply-fullscreen-active .sticky-top,
1779
+ body.vidply-fullscreen-active [class*="fixed-"] {
1780
+ display: none !important;
1781
+ }
1782
+
1783
+ /* Set body and html to black background when in fullscreen */
1784
+ body.vidply-fullscreen-active,
1785
+ html:has(body.vidply-fullscreen-active) {
1786
+ background: #000 !important;
1787
+ }
1788
+
1789
+ /* Also target when :has() is supported */
1790
+ body:has(.vidply-player.vidply-fullscreen),
1791
+ html:has(.vidply-player.vidply-fullscreen) {
1792
+ background: #000 !important;
1793
+ }
1794
+
1795
+ /* Hide all other video players when one is in fullscreen mode */
1796
+ /* This prevents stacking context issues where other players show through */
1797
+ body.vidply-fullscreen-active .vidply-player:not(.vidply-fullscreen) {
1798
+ visibility: hidden !important;
1799
+ }
1800
+
1801
+ /* Same rule using :has() for browsers that support it */
1802
+ body:has(.vidply-player.vidply-fullscreen) .vidply-player:not(.vidply-fullscreen) {
1803
+ visibility: hidden !important;
1701
1804
  }
1702
1805
 
1703
1806
  /* Pseudo-fullscreen for iOS and browsers without Fullscreen API support */
@@ -1705,15 +1808,15 @@
1705
1808
  background: #000;
1706
1809
  bottom: 0;
1707
1810
  left: 0;
1811
+ margin: 0 !important;
1812
+ padding: 0 !important;
1708
1813
  position: fixed !important;
1709
1814
  right: 0;
1710
- top: 0;
1711
- z-index: 999999;
1712
1815
 
1713
1816
  /* Critical iOS fixes */
1817
+ top: 0;
1714
1818
  transform: translate3d(0, 0, 0);
1715
- margin: 0 !important;
1716
- padding: 0 !important;
1819
+ z-index: 999999;
1717
1820
  }
1718
1821
 
1719
1822
  /* Fix controls in fullscreen LANDSCAPE mode - overlay them on video */
@@ -1886,44 +1989,44 @@
1886
1989
 
1887
1990
  .vidply-player.vidply-fullscreen,
1888
1991
  .vidply-player:fullscreen {
1889
- height: 100vh !important;
1890
- width: 100vw !important;
1891
1992
  display: flex !important;
1892
1993
  flex-direction: column !important;
1994
+ height: 100vh !important;
1995
+ width: 100vw !important;
1893
1996
  }
1894
1997
 
1895
1998
  /* Make video wrapper fill entire screen - positioned absolutely */
1896
1999
  .vidply-player.vidply-fullscreen .vidply-video-wrapper,
1897
2000
  .vidply-player:fullscreen .vidply-video-wrapper {
1898
- position: absolute !important;
1899
- top: 0 !important;
2001
+ align-items: center !important;
2002
+ bottom: 0 !important;
2003
+ display: flex !important;
2004
+ height: 100% !important;
2005
+ justify-content: center !important;
1900
2006
  left: 0 !important;
2007
+ position: absolute !important;
1901
2008
  right: 0 !important;
1902
- bottom: 0 !important;
2009
+ top: 0 !important;
1903
2010
  width: 100% !important;
1904
- height: 100% !important;
1905
2011
  z-index: 1 !important;
1906
- display: flex !important;
1907
- align-items: center !important;
1908
- justify-content: center !important;
1909
2012
  }
1910
2013
 
1911
2014
  .vidply-player.vidply-fullscreen video,
1912
2015
  .vidply-player:fullscreen video {
1913
- position: relative !important;
1914
- width: 100% !important;
1915
2016
  height: 100% !important;
1916
- object-fit: contain !important;
1917
- max-width: 100% !important;
1918
2017
  max-height: 100% !important;
2018
+ max-width: 100% !important;
2019
+ object-fit: contain !important;
2020
+ position: relative !important;
2021
+ width: 100% !important;
1919
2022
  }
1920
2023
 
1921
2024
  .vidply-player.vidply-fullscreen .vidply-controls,
1922
2025
  .vidply-player:fullscreen .vidply-controls {
1923
2026
  /* Position controls at the bottom */
1924
- position: absolute !important;
1925
2027
  bottom: 0 !important;
1926
2028
  left: 0 !important;
2029
+ position: absolute !important;
1927
2030
  right: 0 !important;
1928
2031
  z-index: 30 !important; /* Above playlist */
1929
2032
  }
@@ -1931,12 +2034,12 @@
1931
2034
  /* Force playlist to truly overlay - above video, below controls */
1932
2035
  .vidply-player.vidply-fullscreen .vidply-playlist-panel,
1933
2036
  .vidply-player:fullscreen .vidply-playlist-panel {
1934
- position: absolute !important; /* Absolute relative to player */
1935
2037
  bottom: 5rem !important; /* Above controls (typical control height is ~5rem) */
1936
2038
  left: 0 !important;
2039
+ margin: 0 !important;
2040
+ position: absolute !important; /* Absolute relative to player */
1937
2041
  right: 0 !important;
1938
2042
  z-index: 15 !important; /* Above video (z-index:1), below controls (z-index:30) */
1939
- margin: 0 !important;
1940
2043
  }
1941
2044
  }
1942
2045
 
@@ -2022,7 +2125,7 @@
2022
2125
  }
2023
2126
 
2024
2127
  /* Responsive Breakpoints */
2025
- @media (width < 48rem) {
2128
+ @media (width <= 48rem), (width < 48rem) {
2026
2129
  .vidply-controls {
2027
2130
  padding: 1rem 0.75rem 0.75rem;
2028
2131
  }
@@ -2144,7 +2247,7 @@
2144
2247
  }
2145
2248
 
2146
2249
  /* Landscape mobile optimization */
2147
- @media (width <= 56rem) and (orientation: landscape) {
2250
+ @media (width <= 56rem) and (orientation: landscape), (width <= 56rem) and (orientation: landscape) {
2148
2251
  .vidply-menu {
2149
2252
  max-height: 50vh;
2150
2253
  }
@@ -2162,11 +2265,11 @@
2162
2265
  .vidply-player:fullscreen .vidply-playlist-panel {
2163
2266
  bottom: 4.375rem; /* Directly above controls */
2164
2267
  max-height: 30vh; /* Less height in landscape */
2165
- padding: 0.625rem 0;
2166
- overflow-y: hidden;
2268
+ -webkit-overflow-scrolling: touch; /* iOS momentum scrolling */
2167
2269
  overflow-x: auto;
2270
+ overflow-y: hidden;
2271
+ padding: 0.625rem 0;
2168
2272
  touch-action: pan-x; /* Allow horizontal scrolling on touch devices */
2169
- -webkit-overflow-scrolling: touch; /* iOS momentum scrolling */
2170
2273
  }
2171
2274
 
2172
2275
  /* Landscape playlist list - horizontal row */
@@ -2181,10 +2284,10 @@
2181
2284
  /* Landscape items - smaller vertical cards */
2182
2285
  .vidply-player.vidply-fullscreen .vidply-playlist-item,
2183
2286
  .vidply-player:fullscreen .vidply-playlist-item {
2184
- width: 11.25rem;
2185
- min-width: 11.25rem;
2186
- max-width: 11.25rem;
2187
2287
  flex-shrink: 0;
2288
+ max-width: 11.25rem;
2289
+ min-width: 11.25rem;
2290
+ width: 11.25rem;
2188
2291
  }
2189
2292
 
2190
2293
  .vidply-player.vidply-fullscreen .vidply-playlist-item-button,
@@ -2196,9 +2299,9 @@
2196
2299
 
2197
2300
  .vidply-player.vidply-fullscreen .vidply-playlist-thumbnail,
2198
2301
  .vidply-player:fullscreen .vidply-playlist-thumbnail {
2302
+ border-radius: 0.5rem 0.5rem 0 0;
2199
2303
  height: 6.25rem;
2200
2304
  width: 100%;
2201
- border-radius: 0.5rem 0.5rem 0 0;
2202
2305
  }
2203
2306
 
2204
2307
  .vidply-player.vidply-fullscreen .vidply-playlist-item-info,
@@ -2208,14 +2311,14 @@
2208
2311
 
2209
2312
  .vidply-player.vidply-fullscreen .vidply-playlist-header,
2210
2313
  .vidply-player:fullscreen .vidply-playlist-header {
2211
- padding: 0 0.625rem 0.5rem;
2212
- font-size: 0.6875rem;
2213
2314
  flex-shrink: 0;
2315
+ font-size: 0.6875rem;
2316
+ padding: 0 0.625rem 0.5rem;
2214
2317
  }
2215
2318
  }
2216
2319
 
2217
2320
  /* Extra small screens */
2218
- @media (width <= 30rem) {
2321
+ @media (width <= 30rem), (width <= 30rem) {
2219
2322
  .vidply-speed-text {
2220
2323
  display: none;
2221
2324
  }
@@ -2261,12 +2364,12 @@
2261
2364
 
2262
2365
  /* Track Artwork - Displays album art/poster above audio player */
2263
2366
  .vidply-track-artwork {
2264
- aspect-ratio: 16 / 3;
2265
2367
  background-color: var(--vidply-black);
2266
2368
  background-position: center;
2267
2369
  background-repeat: no-repeat;
2268
2370
  background-size: cover;
2269
2371
  border-bottom: 0.0625rem solid var(--vidply-border-light);
2372
+ height: clamp(7.5rem, 22vh, 12rem);
2270
2373
  order: 1; /* Before video-wrapper */
2271
2374
  overflow: hidden;
2272
2375
  position: relative;
@@ -2329,15 +2432,15 @@
2329
2432
  }
2330
2433
 
2331
2434
  .vidply-track-description {
2435
+ -webkit-box-orient: vertical;
2332
2436
  color: var(--vidply-white-60);
2437
+ display: -webkit-box;
2333
2438
  font-size: 0.8125rem;
2439
+ -webkit-line-clamp: 2;
2334
2440
  line-height: 1.4;
2335
2441
  margin-top: 0.5rem;
2336
2442
  max-height: 3.5em;
2337
2443
  overflow: hidden;
2338
- display: -webkit-box;
2339
- -webkit-line-clamp: 2;
2340
- -webkit-box-orient: vertical;
2341
2444
  }
2342
2445
 
2343
2446
  /* Playlist Panel */
@@ -2345,7 +2448,6 @@
2345
2448
  background: var(--vidply-bg-playlist);
2346
2449
  border-top: 0.0625rem solid var(--vidply-border-light);
2347
2450
  max-height: 25rem;
2348
- transform: translate3d(0, 0, 0);
2349
2451
  order: 3; /* After track info */
2350
2452
  -webkit-overflow-scrolling: touch; /* iOS momentum scrolling */
2351
2453
  overflow-y: auto;
@@ -2355,11 +2457,77 @@
2355
2457
  z-index: var(--vidply-z-base); /* Below menus but part of player stacking context */
2356
2458
  }
2357
2459
 
2460
+ /* iOS Safari fix: Ensure playlist panel doesn't cause overflow issues */
2461
+ @supports (-webkit-touch-callout: none) {
2462
+ /* Remove transform in normal mode that creates stacking context issues on iOS */
2463
+
2464
+ /* The transform was only for hardware acceleration, but causes layout issues */
2465
+
2466
+ /* Fullscreen mode overrides with its own transform, so this is safe */
2467
+ .vidply-playlist-panel {
2468
+ transform: none;
2469
+
2470
+ /* Ensure it's properly positioned in flex layout */
2471
+ flex-shrink: 0;
2472
+
2473
+ /* Remove z-index in normal mode to prevent overlaying content below */
2474
+ z-index: auto;
2475
+
2476
+ /* Ensure it participates in normal document flow */
2477
+ position: relative;
2478
+
2479
+ /* Explicitly set display to ensure it's in flow */
2480
+ display: block;
2481
+ }
2482
+
2483
+ /* Ensure player container properly contains playlist on iOS */
2484
+ .vidply-player.vidply-has-playlist {
2485
+ /* Remove contain: layout to allow container to expand with playlist */
2486
+
2487
+ /* Keep style containment for performance, but allow layout to flow */
2488
+ contain: style;
2489
+
2490
+ /* Don't use isolation as it creates stacking context that causes overlay issues */
2491
+
2492
+ /* Override mobile isolation rule for playlists */
2493
+ isolation: auto;
2494
+
2495
+ /* Ensure container expands to contain all children */
2496
+ min-height: 0;
2497
+
2498
+ /* Ensure height is calculated based on content - override any fixed heights */
2499
+ height: auto !important;
2500
+
2501
+ /* Ensure overflow doesn't clip the playlist */
2502
+ overflow: visible;
2503
+ }
2504
+
2505
+ /* Override mobile isolation for playlists on iOS */
2506
+ @media (width <= 48rem), (width < 48rem) {
2507
+ .vidply-player.vidply-has-playlist {
2508
+ isolation: auto;
2509
+ }
2510
+ }
2511
+
2512
+ /* Ensure playlist panel doesn't overflow container on iOS */
2513
+ .vidply-player.vidply-has-playlist .vidply-playlist-panel {
2514
+ /* Prevent overflow that causes content below to be overlapped */
2515
+ max-width: 100%;
2516
+ width: 100%;
2517
+
2518
+ /* Ensure it's part of the flex layout flow */
2519
+ flex-basis: auto;
2520
+
2521
+ /* Explicitly ensure it's in normal flow */
2522
+ float: none;
2523
+ }
2524
+ }
2525
+
2358
2526
  /* Fullscreen Playlist Panel - YouTube-style overlay above controls */
2359
2527
  .vidply-player.vidply-fullscreen .vidply-playlist-panel,
2360
2528
  .vidply-player:fullscreen .vidply-playlist-panel {
2361
- background: linear-gradient(to top, rgb(0 0 0 / 95%) 0%, rgb(0 0 0 / 90%) 100%);
2362
2529
  backdrop-filter: blur(0.625rem);
2530
+ background: linear-gradient(to top, rgb(0 0 0 / 95%) 0%, rgb(0 0 0 / 90%) 100%);
2363
2531
  border: none;
2364
2532
  border-top: 0.0625rem solid var(--vidply-border);
2365
2533
  bottom: 5rem; /* Directly above controls */
@@ -2401,11 +2569,11 @@
2401
2569
  display: flex;
2402
2570
  flex-direction: row;
2403
2571
  gap: 0.75rem;
2404
- padding: 0.5rem 1rem;
2572
+ -webkit-overflow-scrolling: touch;
2405
2573
  overflow-x: auto;
2406
2574
  overflow-y: hidden;
2575
+ padding: 0.5rem 1rem;
2407
2576
  scroll-snap-type: x mandatory;
2408
- -webkit-overflow-scrolling: touch;
2409
2577
  }
2410
2578
 
2411
2579
  /* Fullscreen playlist header - more subtle */
@@ -2421,28 +2589,28 @@
2421
2589
  .vidply-player.vidply-fullscreen .vidply-playlist-item,
2422
2590
  .vidply-player:fullscreen .vidply-playlist-item {
2423
2591
  flex: 0 0 auto;
2424
- min-width: 17.5rem;
2425
2592
  max-width: 20rem;
2593
+ min-width: 17.5rem;
2426
2594
  scroll-snap-align: start;
2427
2595
  }
2428
2596
 
2429
2597
  .vidply-player.vidply-fullscreen .vidply-playlist-item-button,
2430
2598
  .vidply-player:fullscreen .vidply-playlist-item-button {
2431
- flex-direction: column;
2432
2599
  align-items: stretch;
2433
- gap: 0.5rem;
2434
- padding: 0;
2435
2600
  background: var(--vidply-black-40);
2436
2601
  border-radius: 0.5rem;
2602
+ flex-direction: column;
2603
+ gap: 0.5rem;
2437
2604
  overflow: hidden;
2605
+ padding: 0;
2438
2606
  transition: all 0.2s ease;
2439
2607
  }
2440
2608
 
2441
2609
  .vidply-player.vidply-fullscreen .vidply-playlist-item-button:hover,
2442
2610
  .vidply-player:fullscreen .vidply-playlist-item-button:hover {
2443
2611
  background: var(--vidply-white-10);
2444
- transform: translateY(-0.25rem);
2445
2612
  box-shadow: 0 0.5rem 1.5rem var(--vidply-black-60);
2613
+ transform: translateY(-0.25rem);
2446
2614
  }
2447
2615
 
2448
2616
  /* Fullscreen thumbnail container - takes full width of card */
@@ -2454,9 +2622,9 @@
2454
2622
 
2455
2623
  .vidply-player.vidply-fullscreen .vidply-playlist-thumbnail,
2456
2624
  .vidply-player:fullscreen .vidply-playlist-thumbnail {
2457
- width: 100%;
2458
- height: 10rem;
2459
2625
  border-radius: 0;
2626
+ height: 10rem;
2627
+ width: 100%;
2460
2628
  }
2461
2629
 
2462
2630
  /* Larger duration badge in fullscreen for better visibility */
@@ -2481,15 +2649,15 @@
2481
2649
 
2482
2650
  .vidply-player.vidply-fullscreen .vidply-playlist-item-title,
2483
2651
  .vidply-player:fullscreen .vidply-playlist-item-title {
2652
+ -webkit-box-orient: vertical;
2653
+ display: -webkit-box;
2484
2654
  font-size: 0.875rem;
2485
2655
  font-weight: 600;
2656
+ -webkit-line-clamp: 2;
2486
2657
  margin-bottom: 0.25rem;
2487
- white-space: normal;
2488
2658
  overflow: hidden;
2489
2659
  text-overflow: ellipsis;
2490
- display: -webkit-box;
2491
- -webkit-line-clamp: 2;
2492
- -webkit-box-orient: vertical;
2660
+ white-space: normal;
2493
2661
  }
2494
2662
 
2495
2663
  .vidply-player.vidply-fullscreen .vidply-playlist-item-artist,
@@ -2557,8 +2725,8 @@
2557
2725
  /* Active item styling in fullscreen */
2558
2726
  .vidply-player.vidply-fullscreen .vidply-playlist-item-active .vidply-playlist-item-button,
2559
2727
  .vidply-player:fullscreen .vidply-playlist-item-active .vidply-playlist-item-button {
2560
- border: 0.125rem solid var(--vidply-primary-light);
2561
2728
  background: var(--vidply-primary-15);
2729
+ border: 0.125rem solid var(--vidply-primary-light);
2562
2730
  }
2563
2731
 
2564
2732
  .vidply-player.vidply-fullscreen .vidply-playlist-item-active .vidply-playlist-item-title,
@@ -3621,6 +3789,9 @@
3621
3789
  min-height: 6.25rem;
3622
3790
  overflow: visible; /* Allow menu to overflow */
3623
3791
  position: absolute;
3792
+
3793
+ /* Mobile/touch: make the entire overlay a reliable drag surface (touch-action doesn't inherit) */
3794
+ touch-action: none;
3624
3795
  transition: opacity 0.3s ease;
3625
3796
  width: 17.5rem;
3626
3797
  z-index: 3;
@@ -3650,6 +3821,12 @@
3650
3821
  user-select: none;
3651
3822
  }
3652
3823
 
3824
+ /* iOS/Android: `touch-action` does NOT inherit.
3825
+ Ensure touching any child inside the header still counts as a drag gesture area. */
3826
+ .vidply-sign-language-header * {
3827
+ touch-action: none;
3828
+ }
3829
+
3653
3830
  .vidply-sign-language-header:focus,
3654
3831
  .vidply-sign-language-header:focus-visible {
3655
3832
  box-shadow: 0 0 0 0.25rem rgb(91 144 255 / 35%);
@@ -3807,6 +3984,13 @@
3807
3984
  flex: 1;
3808
3985
  }
3809
3986
 
3987
+ /* Mobile: drag is always available via touch, so hide the keyboard drag mode toggle */
3988
+ @media (width <= 48rem), (width < 48rem) {
3989
+ .vidply-sign-language-settings-item[data-setting="keyboard-drag"] {
3990
+ display: none !important;
3991
+ }
3992
+ }
3993
+
3810
3994
  .vidply-sign-language-header h3 {
3811
3995
  color: var(--vidply-white);
3812
3996
  font-size: var(--vidply-font-lg);
@@ -3867,6 +4051,7 @@
3867
4051
  /* Sign Language Resize Handles */
3868
4052
  .vidply-sign-resize-handle {
3869
4053
  position: absolute;
4054
+ touch-action: none;
3870
4055
  z-index: 10;
3871
4056
  }
3872
4057
 
@@ -4025,7 +4210,7 @@
4025
4210
  }
4026
4211
 
4027
4212
  /* Responsive Sign Language Video */
4028
- @media (width < 48rem) {
4213
+ @media (width <= 48rem), (width < 48rem) {
4029
4214
  .vidply-sign-language-wrapper {
4030
4215
  min-width: 7.5rem;
4031
4216
  width: 35%;
@@ -4038,7 +4223,7 @@
4038
4223
  }
4039
4224
 
4040
4225
  /* Responsive Adjustments */
4041
- @media (width < 48rem) {
4226
+ @media (width <= 48rem), (width < 48rem) {
4042
4227
  .vidply-playlist-thumbnail {
4043
4228
  height: 2.125rem;
4044
4229
  width: 3.75rem;
@@ -4050,7 +4235,7 @@
4050
4235
  }
4051
4236
 
4052
4237
  .vidply-track-artwork {
4053
- aspect-ratio: 16 / 3;
4238
+ height: clamp(6.5rem, 20vh, 10rem);
4054
4239
  }
4055
4240
 
4056
4241
  .vidply-track-info {
@@ -4066,12 +4251,12 @@
4066
4251
  .vidply-player:fullscreen .vidply-playlist-panel {
4067
4252
  bottom: 6.25rem; /* Directly above controls with extra space */
4068
4253
  max-height: 35vh; /* Compact height */
4069
- padding: 0.75rem 0; /* Vertical padding only */
4070
- overflow-y: hidden; /* No vertical scrolling */
4254
+ -webkit-overflow-scrolling: touch; /* iOS momentum scrolling */
4071
4255
  overflow-x: auto; /* Horizontal scrolling */
4256
+ overflow-y: hidden; /* No vertical scrolling */
4257
+ padding: 0.75rem 0; /* Vertical padding only */
4072
4258
  position: absolute !important; /* Force absolute over video */
4073
4259
  touch-action: pan-x; /* Allow horizontal scrolling on touch devices */
4074
- -webkit-overflow-scrolling: touch; /* iOS momentum scrolling */
4075
4260
  }
4076
4261
 
4077
4262
  /* Mobile playlist list - horizontal row, no wrapping */
@@ -4080,8 +4265,8 @@
4080
4265
  flex-direction: row; /* Items side by side */
4081
4266
  flex-wrap: nowrap; /* Never wrap */
4082
4267
  gap: 0.5rem;
4083
- padding: 0 0.75rem;
4084
4268
  -webkit-overflow-scrolling: touch;
4269
+ padding: 0 0.75rem;
4085
4270
  scroll-behavior: smooth;
4086
4271
  touch-action: pan-x; /* Ensure horizontal swipe gestures work */
4087
4272
  }
@@ -4089,11 +4274,11 @@
4089
4274
  /* Mobile playlist items - only show thumbnails */
4090
4275
  .vidply-player.vidply-fullscreen .vidply-playlist-item,
4091
4276
  .vidply-player:fullscreen .vidply-playlist-item {
4092
- width: 7.5rem; /* Smaller width for thumbnail-only */
4093
- min-width: 7.5rem;
4094
- max-width: 7.5rem;
4095
4277
  flex-shrink: 0; /* Don't shrink */
4278
+ max-width: 7.5rem;
4279
+ min-width: 7.5rem;
4096
4280
  scroll-snap-align: start;
4281
+ width: 7.5rem; /* Smaller width for thumbnail-only */
4097
4282
  }
4098
4283
 
4099
4284
  .vidply-player.vidply-fullscreen .vidply-playlist-item-button,
@@ -4105,10 +4290,10 @@
4105
4290
 
4106
4291
  .vidply-player.vidply-fullscreen .vidply-playlist-thumbnail,
4107
4292
  .vidply-player:fullscreen .vidply-playlist-thumbnail {
4293
+ border-radius: 0.5rem; /* Fully rounded for thumbnail-only */
4294
+ flex-shrink: 0;
4108
4295
  height: 5.625rem; /* Square-ish thumbnail */
4109
4296
  width: 100%; /* Full width of card */
4110
- flex-shrink: 0;
4111
- border-radius: 0.5rem; /* Fully rounded for thumbnail-only */
4112
4297
  }
4113
4298
 
4114
4299
  /* Hide text info on mobile - show only thumbnails */
@@ -4120,13 +4305,13 @@
4120
4305
  /* If no thumbnail, show only text */
4121
4306
  .vidply-player.vidply-fullscreen .vidply-playlist-item:not(:has(.vidply-playlist-thumbnail)) .vidply-playlist-item-info,
4122
4307
  .vidply-player:fullscreen .vidply-playlist-item:not(:has(.vidply-playlist-thumbnail)) .vidply-playlist-item-info {
4308
+ align-items: center;
4123
4309
  display: flex;
4124
4310
  flex-direction: column;
4125
- padding: 0.5rem;
4311
+ height: 5.625rem;
4126
4312
  justify-content: center;
4127
- align-items: center;
4313
+ padding: 0.5rem;
4128
4314
  text-align: center;
4129
- height: 5.625rem;
4130
4315
  }
4131
4316
 
4132
4317
  .vidply-player.vidply-fullscreen .vidply-playlist-item:not(:has(.vidply-playlist-thumbnail)) .vidply-playlist-item-title,
@@ -4142,9 +4327,9 @@
4142
4327
 
4143
4328
  .vidply-player.vidply-fullscreen .vidply-playlist-header,
4144
4329
  .vidply-player:fullscreen .vidply-playlist-header {
4145
- padding: 0 0.75rem 0.5rem;
4146
- font-size: 0.6875rem;
4147
4330
  flex-shrink: 0;
4331
+ font-size: 0.6875rem;
4332
+ padding: 0 0.75rem 0.5rem;
4148
4333
  }
4149
4334
 
4150
4335
  /* Mobile transcript underneath video and controls */