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
@@ -143,13 +143,13 @@
143
143
 
144
144
  /* Z-Index Layering System */
145
145
  --vidply-z-base: 1; /* Video, base elements */
146
- --vidply-z-overlay: 2; /* Play overlay, video wrapper on mobile */
147
- --vidply-z-transcript: 5; /* Transcript window */
148
- --vidply-z-playlist: 15; /* Playlist panel in fullscreen */
149
- --vidply-z-menu: 20; /* Menus in normal mode */
150
146
  --vidply-z-controls: 30; /* Control bar */
151
- --vidply-z-menu-high: 100; /* Volume menu */
147
+ --vidply-z-menu: 20; /* Menus in normal mode */
152
148
  --vidply-z-menu-fullscreen: 1000; /* Menus in fullscreen mode */
149
+ --vidply-z-menu-high: 100; /* Volume menu */
150
+ --vidply-z-overlay: 2; /* Play overlay, video wrapper on mobile */
151
+ --vidply-z-playlist: 15; /* Playlist panel in fullscreen */
152
+ --vidply-z-transcript: 5; /* Transcript window */
153
153
  }
154
154
 
155
155
  /* Utility Classes */
@@ -420,7 +420,7 @@
420
420
  }
421
421
 
422
422
  /* Mobile: Ensure player contains all elements properly */
423
- @media (width < 48rem) {
423
+ @media (width <= 48rem), (width < 48rem) {
424
424
  .vidply-player {
425
425
  isolation: isolate; /* Create stacking context */
426
426
  overflow: visible; /* Allow menus to overflow but within bounds */
@@ -436,7 +436,6 @@
436
436
  /* Responsive container */
437
437
 
438
438
  .vidply-player.vidply-audio {
439
- aspect-ratio: auto;
440
439
  background: linear-gradient(135deg, var(--vidply-primary-20) 0%, rgb(var(--vidply-primary-dark-rgb), 0.2) 100%);
441
440
  height: auto;
442
441
  }
@@ -455,11 +454,14 @@
455
454
  }
456
455
 
457
456
  /* Mobile: Video element order and sizing */
458
- @media (width < 48rem) {
457
+ @media (width <= 48rem), (width < 48rem) {
459
458
  .vidply-player video,
460
459
  .vidply-player audio {
461
460
  flex: 0 0 auto; /* Don't grow or shrink */
462
- height: auto;
461
+
462
+ /* Player sets inline `height: 100%` on the media element.
463
+ On mobile we need `auto` to prevent wrapper height collapse / overlap. */
464
+ height: auto !important;
463
465
  order: 1; /* Before controls */
464
466
  }
465
467
 
@@ -494,6 +496,16 @@
494
496
  z-index: 1; /* Base video layer */
495
497
  }
496
498
 
499
+ /* Ensure the video wrapper establishes an intrinsic height in ALL non-fullscreen modes.
500
+ Controls are appended inside `.vidply-video-wrapper` and are positioned `absolute` by default.
501
+ If the wrapper has no intrinsic height,
502
+ the wrapper can collapse and the controls will overlap the content that follows (like `.vidply-track-info`).
503
+
504
+ We keep fullscreen behavior untouched (fullscreen rules already size the wrapper explicitly). */
505
+ .vidply-player.vidply-video:not(.vidply-fullscreen, :fullscreen) .vidply-video-wrapper {
506
+ height: auto;
507
+ }
508
+
497
509
  /* Allow controls and menus to overflow in fullscreen landscape mode */
498
510
  @media (orientation: landscape) {
499
511
  .vidply-player.vidply-fullscreen .vidply-video-wrapper,
@@ -514,17 +526,19 @@
514
526
  }
515
527
 
516
528
  /* Audio content in video player - uses 16:3 aspect ratio for a banner-like appearance */
529
+
517
530
  /* When audio files are played in a video element, we use CSS poster overlay for better control */
518
531
  .vidply-player.vidply-video.vidply-audio-content .vidply-video-wrapper {
519
- aspect-ratio: 16 / 3 !important;
520
532
  height: auto !important;
521
- min-height: 0 !important;
533
+
534
+ /* We still need a visible box for the poster banner */
535
+ min-height: 7.5rem !important;
522
536
  overflow: hidden;
523
537
  }
524
538
 
525
539
  .vidply-player.vidply-video.vidply-audio-content .vidply-video-wrapper.vidply-forced-poster {
526
- background-size: cover;
527
540
  background-position: center;
541
+ background-size: cover;
528
542
  }
529
543
 
530
544
  /* Hide the video element completely when playing audio - let the wrapper show the poster */
@@ -532,40 +546,48 @@
532
546
  display: none !important;
533
547
  }
534
548
 
535
- /* YouTube and Vimeo iframe containers - ensure 16:9 aspect ratio */
536
- .vidply-video-wrapper > div[id^="youtube-player-"],
549
+ /* YouTube and Vimeo iframe containers - keep classic 16:9 framing */
550
+ .vidply-video-wrapper > iframe[id^="youtube-player-"],
537
551
  .vidply-video-wrapper > div[id^="vimeo-player-"] {
538
- width: 100% !important;
539
552
  aspect-ratio: 16 / 9;
540
- max-height: 100%;
553
+ height: auto;
554
+ max-height: none !important; /* override renderer inline maxHeight=100% */
555
+ position: relative; /* ensure absolutely-positioned iframes size against this box */
556
+ width: 100% !important;
541
557
  }
542
558
 
543
559
  .vidply-video-wrapper > div[id^="youtube-player-"] iframe,
544
560
  .vidply-video-wrapper > div[id^="vimeo-player-"] iframe {
561
+ aspect-ratio: 16 / 9;
562
+
563
+ /* Some providers inject height:100% which can ignore the intended 16:9 sizing.
564
+ Force the iframe itself to be 16:9 and let height be derived. */
565
+ display: block;
566
+ height: auto !important;
567
+ position: relative;
545
568
  width: 100% !important;
546
- height: 100% !important;
547
569
  }
548
570
 
549
- /* SoundCloud iframe - uses 16:3 aspect ratio for single track audio banner appearance */
571
+ /* SoundCloud iframe - stable height */
550
572
  .vidply-video-wrapper > iframe.vidply-soundcloud-iframe {
573
+ height: clamp(6rem, 18vh, 12rem);
551
574
  width: 100% !important;
552
- aspect-ratio: 16 / 3;
553
- max-height: 100%;
554
575
  }
555
576
 
556
- /* SoundCloud playlist - uses 16:9 aspect ratio to show track list */
577
+ /* SoundCloud playlist - taller variant to show track list */
557
578
  .vidply-video-wrapper > iframe.vidply-soundcloud-iframe.vidply-soundcloud-playlist {
558
- aspect-ratio: 16 / 9;
579
+ height: clamp(12.5rem, 45vh, 28rem);
559
580
  }
560
581
 
561
582
  /* Hide VidPly controls when external renderer (YouTube, Vimeo, SoundCloud) is active */
583
+
562
584
  /* These services have their own native controls */
563
585
  .vidply-player.vidply-external-controls .vidply-controls {
564
586
  display: none !important;
565
587
  }
566
588
 
567
589
  /* Mobile: Simplify video wrapper but keep captions contained */
568
- @media (width < 48rem) {
590
+ @media (width <= 48rem), (width < 48rem) {
569
591
  .vidply-video-wrapper {
570
592
  display: block;
571
593
  height: auto;
@@ -583,30 +605,31 @@
583
605
  /* Ensure video doesn't collapse */
584
606
  .vidply-player video {
585
607
  display: block;
608
+ height: auto !important;
586
609
  position: relative;
587
- width: 100%;
610
+ width: 100%;
588
611
  }
589
612
 
590
613
  /* Override mobile rules in fullscreen mode - critical for iOS */
591
614
  .vidply-player.vidply-fullscreen .vidply-video-wrapper,
592
615
  .vidply-player:fullscreen .vidply-video-wrapper {
616
+ bottom: 0 !important;
593
617
  display: flex !important;
594
618
  height: 100% !important;
619
+ left: 0 !important;
595
620
  min-height: 0 !important;
596
621
  position: absolute !important;
597
- top: 0 !important;
598
- left: 0 !important;
599
622
  right: 0 !important;
600
- bottom: 0 !important;
623
+ top: 0 !important;
601
624
  }
602
625
 
603
626
  .vidply-player.vidply-fullscreen video,
604
627
  .vidply-player:fullscreen video {
628
+ height: 100% !important;
629
+ left: 0 !important;
605
630
  position: absolute !important;
606
631
  top: 0 !important;
607
- left: 0 !important;
608
632
  width: 100% !important;
609
- height: 100% !important;
610
633
  }
611
634
  }
612
635
 
@@ -622,6 +645,8 @@
622
645
 
623
646
  /* Centered Play Button Overlay */
624
647
  .vidply-play-overlay {
648
+ border: 0.125rem solid var(--vidply-primary);
649
+ border-radius: 50%;
625
650
  cursor: pointer;
626
651
  filter: drop-shadow(0 0.5rem 2rem rgb(0 0 0 / 30%));
627
652
  left: 50%;
@@ -630,8 +655,6 @@
630
655
  transform: translate(-50%, -50%);
631
656
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
632
657
  z-index: var(--vidply-z-overlay);
633
- border: 0.125rem solid var(--vidply-primary);
634
- border-radius: 50%;
635
658
  }
636
659
 
637
660
  .vidply-play-overlay:hover {
@@ -683,7 +706,7 @@
683
706
  }
684
707
 
685
708
  /* Mobile: Controls underneath video (but not in landscape fullscreen) */
686
- @media (width < 48rem) {
709
+ @media (width <= 48rem), (width < 48rem) {
687
710
  .vidply-controls {
688
711
  background: var(--vidply-black-90);
689
712
  border-top: 0.0625rem solid var(--vidply-border-light);
@@ -793,22 +816,22 @@
793
816
  color: var(--vidply-white);
794
817
  display: none;
795
818
  font-size: 0.75rem;
819
+ overflow: hidden;
796
820
  padding: 0;
797
821
  pointer-events: none;
798
822
  position: absolute;
799
823
  transform: translateX(-50%);
800
824
  white-space: nowrap;
801
- overflow: hidden;
802
825
  }
803
826
 
804
827
  .vidply-progress-preview {
805
- background-size: cover;
806
828
  background-position: center;
829
+ background-size: cover;
807
830
  border-radius: 0.25rem 0.25rem 0 0;
808
831
  display: none;
809
832
  height: 5rem;
810
- width: 8.888888889rem; /* 160px at 18px base */
811
833
  min-width: 8.888888889rem;
834
+ width: 8.888888889rem; /* 160px at 18px base */
812
835
  }
813
836
 
814
837
  .vidply-progress-tooltip-time {
@@ -1078,7 +1101,7 @@
1078
1101
  }
1079
1102
 
1080
1103
  /* Mobile: Don't use backdrop, menus position above buttons */
1081
- @media (width < 48rem) {
1104
+ @media (width <= 48rem), (width < 48rem) {
1082
1105
  .vidply-menu-backdrop {
1083
1106
  display: none !important;
1084
1107
  }
@@ -1117,6 +1140,11 @@
1117
1140
  }
1118
1141
 
1119
1142
  /* Menu positioned below button */
1143
+ .vidply-menu.vidply-menu-below {
1144
+ bottom: auto;
1145
+ top: calc(100% + 0.5rem);
1146
+ }
1147
+
1120
1148
  .vidply-menu.vidply-menu-below::after {
1121
1149
  border-bottom: 0.375rem solid var(--vidply-bg-menu);
1122
1150
  border-top: none;
@@ -1240,10 +1268,10 @@
1240
1268
  left: auto !important;
1241
1269
  max-width: calc(100vw - 1.25rem);
1242
1270
  right: 0 !important;
1243
- z-index: 1000; /* Ensure menu appears above all player elements including playlist */
1271
+ transform: none !important;
1244
1272
 
1245
1273
  /* Ensure menu stays within viewport */
1246
- transform: none !important;
1274
+ z-index: 1000; /* Ensure menu appears above all player elements including playlist */
1247
1275
  }
1248
1276
 
1249
1277
  /* Overflow menu items with icons */
@@ -1476,7 +1504,7 @@
1476
1504
  }
1477
1505
 
1478
1506
  /* Mobile: Caption positioning handled by JavaScript */
1479
- @media (width < 48rem) {
1507
+ @media (width <= 48rem), (width < 48rem) {
1480
1508
  .vidply-captions {
1481
1509
  /* Bottom position set dynamically by JS based on control height */
1482
1510
  left: 50%;
@@ -1689,9 +1717,84 @@
1689
1717
  /* Fullscreen Styles */
1690
1718
  .vidply-player.vidply-fullscreen,
1691
1719
  .vidply-player:fullscreen {
1720
+ background: #000;
1692
1721
  height: 100vh;
1693
1722
  max-width: none;
1694
1723
  width: 100vw;
1724
+ z-index: 2147483647; /* Maximum z-index to ensure we're above everything */
1725
+ }
1726
+
1727
+ /* Video wrapper also needs black background in fullscreen to prevent letterbox areas showing gray */
1728
+ .vidply-player.vidply-fullscreen .vidply-video-wrapper,
1729
+ .vidply-player:fullscreen .vidply-video-wrapper {
1730
+ background: #000 !important;
1731
+ }
1732
+
1733
+ /* Fullscreen backdrop - the pseudo-element behind the fullscreen element */
1734
+ .vidply-player:fullscreen::backdrop {
1735
+ background: #000;
1736
+ }
1737
+
1738
+ /* Webkit/Safari prefix for backdrop */
1739
+ .vidply-player:-webkit-full-screen::backdrop {
1740
+ background: #000;
1741
+ }
1742
+
1743
+ /* Hide fixed/sticky positioned elements when any vidply player is fullscreen */
1744
+ /* This ensures headers, nav bars, and other fixed elements don't appear above the player */
1745
+ :root:has(.vidply-player:fullscreen) .fixed-top,
1746
+ :root:has(.vidply-player:fullscreen) .fixed-bottom,
1747
+ :root:has(.vidply-player:fullscreen) .sticky-top,
1748
+ :root:has(.vidply-player:fullscreen) [style*="position: fixed"],
1749
+ :root:has(.vidply-player:fullscreen) [style*="position:fixed"] {
1750
+ display: none !important;
1751
+ }
1752
+
1753
+ /* Same for webkit fullscreen */
1754
+ :root:has(.vidply-player:-webkit-full-screen) .fixed-top,
1755
+ :root:has(.vidply-player:-webkit-full-screen) .fixed-bottom,
1756
+ :root:has(.vidply-player:-webkit-full-screen) .sticky-top,
1757
+ :root:has(.vidply-player:-webkit-full-screen) [style*="position: fixed"],
1758
+ :root:has(.vidply-player:-webkit-full-screen) [style*="position:fixed"] {
1759
+ display: none !important;
1760
+ }
1761
+
1762
+ /* Fallback: Also hide elements when pseudo-fullscreen class is applied (for iOS/Safari fallback) */
1763
+ :root:has(.vidply-player.vidply-fullscreen) .fixed-top,
1764
+ :root:has(.vidply-player.vidply-fullscreen) .fixed-bottom,
1765
+ :root:has(.vidply-player.vidply-fullscreen) .sticky-top {
1766
+ display: none !important;
1767
+ }
1768
+
1769
+ /* Additional fallback using body class for browsers that don't support :has() */
1770
+ body.vidply-fullscreen-active .fixed-top,
1771
+ body.vidply-fullscreen-active .fixed-bottom,
1772
+ body.vidply-fullscreen-active .sticky-top,
1773
+ body.vidply-fullscreen-active [class*="fixed-"] {
1774
+ display: none !important;
1775
+ }
1776
+
1777
+ /* Set body and html to black background when in fullscreen */
1778
+ body.vidply-fullscreen-active,
1779
+ html:has(body.vidply-fullscreen-active) {
1780
+ background: #000 !important;
1781
+ }
1782
+
1783
+ /* Also target when :has() is supported */
1784
+ body:has(.vidply-player.vidply-fullscreen),
1785
+ html:has(.vidply-player.vidply-fullscreen) {
1786
+ background: #000 !important;
1787
+ }
1788
+
1789
+ /* Hide all other video players when one is in fullscreen mode */
1790
+ /* This prevents stacking context issues where other players show through */
1791
+ body.vidply-fullscreen-active .vidply-player:not(.vidply-fullscreen) {
1792
+ visibility: hidden !important;
1793
+ }
1794
+
1795
+ /* Same rule using :has() for browsers that support it */
1796
+ body:has(.vidply-player.vidply-fullscreen) .vidply-player:not(.vidply-fullscreen) {
1797
+ visibility: hidden !important;
1695
1798
  }
1696
1799
 
1697
1800
  /* Pseudo-fullscreen for iOS and browsers without Fullscreen API support */
@@ -1699,15 +1802,15 @@
1699
1802
  background: #000;
1700
1803
  bottom: 0;
1701
1804
  left: 0;
1805
+ margin: 0 !important;
1806
+ padding: 0 !important;
1702
1807
  position: fixed !important;
1703
1808
  right: 0;
1704
- top: 0;
1705
- z-index: 999999;
1706
1809
 
1707
1810
  /* Critical iOS fixes */
1811
+ top: 0;
1708
1812
  transform: translate3d(0, 0, 0);
1709
- margin: 0 !important;
1710
- padding: 0 !important;
1813
+ z-index: 999999;
1711
1814
  }
1712
1815
 
1713
1816
  /* Fix controls in fullscreen LANDSCAPE mode - overlay them on video */
@@ -1880,44 +1983,44 @@
1880
1983
 
1881
1984
  .vidply-player.vidply-fullscreen,
1882
1985
  .vidply-player:fullscreen {
1883
- height: 100vh !important;
1884
- width: 100vw !important;
1885
1986
  display: flex !important;
1886
1987
  flex-direction: column !important;
1988
+ height: 100vh !important;
1989
+ width: 100vw !important;
1887
1990
  }
1888
1991
 
1889
1992
  /* Make video wrapper fill entire screen - positioned absolutely */
1890
1993
  .vidply-player.vidply-fullscreen .vidply-video-wrapper,
1891
1994
  .vidply-player:fullscreen .vidply-video-wrapper {
1892
- position: absolute !important;
1893
- top: 0 !important;
1995
+ align-items: center !important;
1996
+ bottom: 0 !important;
1997
+ display: flex !important;
1998
+ height: 100% !important;
1999
+ justify-content: center !important;
1894
2000
  left: 0 !important;
2001
+ position: absolute !important;
1895
2002
  right: 0 !important;
1896
- bottom: 0 !important;
2003
+ top: 0 !important;
1897
2004
  width: 100% !important;
1898
- height: 100% !important;
1899
2005
  z-index: 1 !important;
1900
- display: flex !important;
1901
- align-items: center !important;
1902
- justify-content: center !important;
1903
2006
  }
1904
2007
 
1905
2008
  .vidply-player.vidply-fullscreen video,
1906
2009
  .vidply-player:fullscreen video {
1907
- position: relative !important;
1908
- width: 100% !important;
1909
2010
  height: 100% !important;
1910
- object-fit: contain !important;
1911
- max-width: 100% !important;
1912
2011
  max-height: 100% !important;
2012
+ max-width: 100% !important;
2013
+ object-fit: contain !important;
2014
+ position: relative !important;
2015
+ width: 100% !important;
1913
2016
  }
1914
2017
 
1915
2018
  .vidply-player.vidply-fullscreen .vidply-controls,
1916
2019
  .vidply-player:fullscreen .vidply-controls {
1917
2020
  /* Position controls at the bottom */
1918
- position: absolute !important;
1919
2021
  bottom: 0 !important;
1920
2022
  left: 0 !important;
2023
+ position: absolute !important;
1921
2024
  right: 0 !important;
1922
2025
  z-index: 30 !important; /* Above playlist */
1923
2026
  }
@@ -1925,12 +2028,12 @@
1925
2028
  /* Force playlist to truly overlay - above video, below controls */
1926
2029
  .vidply-player.vidply-fullscreen .vidply-playlist-panel,
1927
2030
  .vidply-player:fullscreen .vidply-playlist-panel {
1928
- position: absolute !important; /* Absolute relative to player */
1929
2031
  bottom: 5rem !important; /* Above controls (typical control height is ~5rem) */
1930
2032
  left: 0 !important;
2033
+ margin: 0 !important;
2034
+ position: absolute !important; /* Absolute relative to player */
1931
2035
  right: 0 !important;
1932
2036
  z-index: 15 !important; /* Above video (z-index:1), below controls (z-index:30) */
1933
- margin: 0 !important;
1934
2037
  }
1935
2038
  }
1936
2039
 
@@ -2016,7 +2119,7 @@
2016
2119
  }
2017
2120
 
2018
2121
  /* Responsive Breakpoints */
2019
- @media (width < 48rem) {
2122
+ @media (width <= 48rem), (width < 48rem) {
2020
2123
  .vidply-controls {
2021
2124
  padding: 1rem 0.75rem 0.75rem;
2022
2125
  }
@@ -2138,7 +2241,7 @@
2138
2241
  }
2139
2242
 
2140
2243
  /* Landscape mobile optimization */
2141
- @media (width <= 56rem) and (orientation: landscape) {
2244
+ @media (width <= 56rem) and (orientation: landscape), (width <= 56rem) and (orientation: landscape) {
2142
2245
  .vidply-menu {
2143
2246
  max-height: 50vh;
2144
2247
  }
@@ -2156,11 +2259,11 @@
2156
2259
  .vidply-player:fullscreen .vidply-playlist-panel {
2157
2260
  bottom: 4.375rem; /* Directly above controls */
2158
2261
  max-height: 30vh; /* Less height in landscape */
2159
- padding: 0.625rem 0;
2160
- overflow-y: hidden;
2262
+ -webkit-overflow-scrolling: touch; /* iOS momentum scrolling */
2161
2263
  overflow-x: auto;
2264
+ overflow-y: hidden;
2265
+ padding: 0.625rem 0;
2162
2266
  touch-action: pan-x; /* Allow horizontal scrolling on touch devices */
2163
- -webkit-overflow-scrolling: touch; /* iOS momentum scrolling */
2164
2267
  }
2165
2268
 
2166
2269
  /* Landscape playlist list - horizontal row */
@@ -2175,10 +2278,10 @@
2175
2278
  /* Landscape items - smaller vertical cards */
2176
2279
  .vidply-player.vidply-fullscreen .vidply-playlist-item,
2177
2280
  .vidply-player:fullscreen .vidply-playlist-item {
2178
- width: 11.25rem;
2179
- min-width: 11.25rem;
2180
- max-width: 11.25rem;
2181
2281
  flex-shrink: 0;
2282
+ max-width: 11.25rem;
2283
+ min-width: 11.25rem;
2284
+ width: 11.25rem;
2182
2285
  }
2183
2286
 
2184
2287
  .vidply-player.vidply-fullscreen .vidply-playlist-item-button,
@@ -2190,9 +2293,9 @@
2190
2293
 
2191
2294
  .vidply-player.vidply-fullscreen .vidply-playlist-thumbnail,
2192
2295
  .vidply-player:fullscreen .vidply-playlist-thumbnail {
2296
+ border-radius: 0.5rem 0.5rem 0 0;
2193
2297
  height: 6.25rem;
2194
2298
  width: 100%;
2195
- border-radius: 0.5rem 0.5rem 0 0;
2196
2299
  }
2197
2300
 
2198
2301
  .vidply-player.vidply-fullscreen .vidply-playlist-item-info,
@@ -2202,14 +2305,14 @@
2202
2305
 
2203
2306
  .vidply-player.vidply-fullscreen .vidply-playlist-header,
2204
2307
  .vidply-player:fullscreen .vidply-playlist-header {
2205
- padding: 0 0.625rem 0.5rem;
2206
- font-size: 0.6875rem;
2207
2308
  flex-shrink: 0;
2309
+ font-size: 0.6875rem;
2310
+ padding: 0 0.625rem 0.5rem;
2208
2311
  }
2209
2312
  }
2210
2313
 
2211
2314
  /* Extra small screens */
2212
- @media (width <= 30rem) {
2315
+ @media (width <= 30rem), (width <= 30rem) {
2213
2316
  .vidply-speed-text {
2214
2317
  display: none;
2215
2318
  }
@@ -2255,12 +2358,12 @@
2255
2358
 
2256
2359
  /* Track Artwork - Displays album art/poster above audio player */
2257
2360
  .vidply-track-artwork {
2258
- aspect-ratio: 16 / 3;
2259
2361
  background-color: var(--vidply-black);
2260
2362
  background-position: center;
2261
2363
  background-repeat: no-repeat;
2262
2364
  background-size: cover;
2263
2365
  border-bottom: 0.0625rem solid var(--vidply-border-light);
2366
+ height: clamp(7.5rem, 22vh, 12rem);
2264
2367
  order: 1; /* Before video-wrapper */
2265
2368
  overflow: hidden;
2266
2369
  position: relative;
@@ -2323,15 +2426,15 @@
2323
2426
  }
2324
2427
 
2325
2428
  .vidply-track-description {
2429
+ -webkit-box-orient: vertical;
2326
2430
  color: var(--vidply-white-60);
2431
+ display: -webkit-box;
2327
2432
  font-size: 0.8125rem;
2433
+ -webkit-line-clamp: 2;
2328
2434
  line-height: 1.4;
2329
2435
  margin-top: 0.5rem;
2330
2436
  max-height: 3.5em;
2331
2437
  overflow: hidden;
2332
- display: -webkit-box;
2333
- -webkit-line-clamp: 2;
2334
- -webkit-box-orient: vertical;
2335
2438
  }
2336
2439
 
2337
2440
  /* Playlist Panel */
@@ -2339,7 +2442,6 @@
2339
2442
  background: var(--vidply-bg-playlist);
2340
2443
  border-top: 0.0625rem solid var(--vidply-border-light);
2341
2444
  max-height: 25rem;
2342
- transform: translate3d(0, 0, 0);
2343
2445
  order: 3; /* After track info */
2344
2446
  -webkit-overflow-scrolling: touch; /* iOS momentum scrolling */
2345
2447
  overflow-y: auto;
@@ -2349,11 +2451,77 @@
2349
2451
  z-index: var(--vidply-z-base); /* Below menus but part of player stacking context */
2350
2452
  }
2351
2453
 
2454
+ /* iOS Safari fix: Ensure playlist panel doesn't cause overflow issues */
2455
+ @supports (-webkit-touch-callout: none) {
2456
+ /* Remove transform in normal mode that creates stacking context issues on iOS */
2457
+
2458
+ /* The transform was only for hardware acceleration, but causes layout issues */
2459
+
2460
+ /* Fullscreen mode overrides with its own transform, so this is safe */
2461
+ .vidply-playlist-panel {
2462
+ transform: none;
2463
+
2464
+ /* Ensure it's properly positioned in flex layout */
2465
+ flex-shrink: 0;
2466
+
2467
+ /* Remove z-index in normal mode to prevent overlaying content below */
2468
+ z-index: auto;
2469
+
2470
+ /* Ensure it participates in normal document flow */
2471
+ position: relative;
2472
+
2473
+ /* Explicitly set display to ensure it's in flow */
2474
+ display: block;
2475
+ }
2476
+
2477
+ /* Ensure player container properly contains playlist on iOS */
2478
+ .vidply-player.vidply-has-playlist {
2479
+ /* Remove contain: layout to allow container to expand with playlist */
2480
+
2481
+ /* Keep style containment for performance, but allow layout to flow */
2482
+ contain: style;
2483
+
2484
+ /* Don't use isolation as it creates stacking context that causes overlay issues */
2485
+
2486
+ /* Override mobile isolation rule for playlists */
2487
+ isolation: auto;
2488
+
2489
+ /* Ensure container expands to contain all children */
2490
+ min-height: 0;
2491
+
2492
+ /* Ensure height is calculated based on content - override any fixed heights */
2493
+ height: auto !important;
2494
+
2495
+ /* Ensure overflow doesn't clip the playlist */
2496
+ overflow: visible;
2497
+ }
2498
+
2499
+ /* Override mobile isolation for playlists on iOS */
2500
+ @media (width <= 48rem), (width < 48rem) {
2501
+ .vidply-player.vidply-has-playlist {
2502
+ isolation: auto;
2503
+ }
2504
+ }
2505
+
2506
+ /* Ensure playlist panel doesn't overflow container on iOS */
2507
+ .vidply-player.vidply-has-playlist .vidply-playlist-panel {
2508
+ /* Prevent overflow that causes content below to be overlapped */
2509
+ max-width: 100%;
2510
+ width: 100%;
2511
+
2512
+ /* Ensure it's part of the flex layout flow */
2513
+ flex-basis: auto;
2514
+
2515
+ /* Explicitly ensure it's in normal flow */
2516
+ float: none;
2517
+ }
2518
+ }
2519
+
2352
2520
  /* Fullscreen Playlist Panel - YouTube-style overlay above controls */
2353
2521
  .vidply-player.vidply-fullscreen .vidply-playlist-panel,
2354
2522
  .vidply-player:fullscreen .vidply-playlist-panel {
2355
- background: linear-gradient(to top, rgb(0 0 0 / 95%) 0%, rgb(0 0 0 / 90%) 100%);
2356
2523
  backdrop-filter: blur(0.625rem);
2524
+ background: linear-gradient(to top, rgb(0 0 0 / 95%) 0%, rgb(0 0 0 / 90%) 100%);
2357
2525
  border: none;
2358
2526
  border-top: 0.0625rem solid var(--vidply-border);
2359
2527
  bottom: 5rem; /* Directly above controls */
@@ -2395,11 +2563,11 @@
2395
2563
  display: flex;
2396
2564
  flex-direction: row;
2397
2565
  gap: 0.75rem;
2398
- padding: 0.5rem 1rem;
2566
+ -webkit-overflow-scrolling: touch;
2399
2567
  overflow-x: auto;
2400
2568
  overflow-y: hidden;
2569
+ padding: 0.5rem 1rem;
2401
2570
  scroll-snap-type: x mandatory;
2402
- -webkit-overflow-scrolling: touch;
2403
2571
  }
2404
2572
 
2405
2573
  /* Fullscreen playlist header - more subtle */
@@ -2415,28 +2583,28 @@
2415
2583
  .vidply-player.vidply-fullscreen .vidply-playlist-item,
2416
2584
  .vidply-player:fullscreen .vidply-playlist-item {
2417
2585
  flex: 0 0 auto;
2418
- min-width: 17.5rem;
2419
2586
  max-width: 20rem;
2587
+ min-width: 17.5rem;
2420
2588
  scroll-snap-align: start;
2421
2589
  }
2422
2590
 
2423
2591
  .vidply-player.vidply-fullscreen .vidply-playlist-item-button,
2424
2592
  .vidply-player:fullscreen .vidply-playlist-item-button {
2425
- flex-direction: column;
2426
2593
  align-items: stretch;
2427
- gap: 0.5rem;
2428
- padding: 0;
2429
2594
  background: var(--vidply-black-40);
2430
2595
  border-radius: 0.5rem;
2596
+ flex-direction: column;
2597
+ gap: 0.5rem;
2431
2598
  overflow: hidden;
2599
+ padding: 0;
2432
2600
  transition: all 0.2s ease;
2433
2601
  }
2434
2602
 
2435
2603
  .vidply-player.vidply-fullscreen .vidply-playlist-item-button:hover,
2436
2604
  .vidply-player:fullscreen .vidply-playlist-item-button:hover {
2437
2605
  background: var(--vidply-white-10);
2438
- transform: translateY(-0.25rem);
2439
2606
  box-shadow: 0 0.5rem 1.5rem var(--vidply-black-60);
2607
+ transform: translateY(-0.25rem);
2440
2608
  }
2441
2609
 
2442
2610
  /* Fullscreen thumbnail container - takes full width of card */
@@ -2448,9 +2616,9 @@
2448
2616
 
2449
2617
  .vidply-player.vidply-fullscreen .vidply-playlist-thumbnail,
2450
2618
  .vidply-player:fullscreen .vidply-playlist-thumbnail {
2451
- width: 100%;
2452
- height: 10rem;
2453
2619
  border-radius: 0;
2620
+ height: 10rem;
2621
+ width: 100%;
2454
2622
  }
2455
2623
 
2456
2624
  /* Larger duration badge in fullscreen for better visibility */
@@ -2475,15 +2643,15 @@
2475
2643
 
2476
2644
  .vidply-player.vidply-fullscreen .vidply-playlist-item-title,
2477
2645
  .vidply-player:fullscreen .vidply-playlist-item-title {
2646
+ -webkit-box-orient: vertical;
2647
+ display: -webkit-box;
2478
2648
  font-size: 0.875rem;
2479
2649
  font-weight: 600;
2650
+ -webkit-line-clamp: 2;
2480
2651
  margin-bottom: 0.25rem;
2481
- white-space: normal;
2482
2652
  overflow: hidden;
2483
2653
  text-overflow: ellipsis;
2484
- display: -webkit-box;
2485
- -webkit-line-clamp: 2;
2486
- -webkit-box-orient: vertical;
2654
+ white-space: normal;
2487
2655
  }
2488
2656
 
2489
2657
  .vidply-player.vidply-fullscreen .vidply-playlist-item-artist,
@@ -2551,8 +2719,8 @@
2551
2719
  /* Active item styling in fullscreen */
2552
2720
  .vidply-player.vidply-fullscreen .vidply-playlist-item-active .vidply-playlist-item-button,
2553
2721
  .vidply-player:fullscreen .vidply-playlist-item-active .vidply-playlist-item-button {
2554
- border: 0.125rem solid var(--vidply-primary-light);
2555
2722
  background: var(--vidply-primary-15);
2723
+ border: 0.125rem solid var(--vidply-primary-light);
2556
2724
  }
2557
2725
 
2558
2726
  .vidply-player.vidply-fullscreen .vidply-playlist-item-active .vidply-playlist-item-title,
@@ -3615,6 +3783,9 @@
3615
3783
  min-height: 6.25rem;
3616
3784
  overflow: visible; /* Allow menu to overflow */
3617
3785
  position: absolute;
3786
+
3787
+ /* Mobile/touch: make the entire overlay a reliable drag surface (touch-action doesn't inherit) */
3788
+ touch-action: none;
3618
3789
  transition: opacity 0.3s ease;
3619
3790
  width: 17.5rem;
3620
3791
  z-index: 3;
@@ -3644,6 +3815,12 @@
3644
3815
  user-select: none;
3645
3816
  }
3646
3817
 
3818
+ /* iOS/Android: `touch-action` does NOT inherit.
3819
+ Ensure touching any child inside the header still counts as a drag gesture area. */
3820
+ .vidply-sign-language-header * {
3821
+ touch-action: none;
3822
+ }
3823
+
3647
3824
  .vidply-sign-language-header:focus,
3648
3825
  .vidply-sign-language-header:focus-visible {
3649
3826
  box-shadow: 0 0 0 0.25rem rgb(91 144 255 / 35%);
@@ -3801,6 +3978,13 @@
3801
3978
  flex: 1;
3802
3979
  }
3803
3980
 
3981
+ /* Mobile: drag is always available via touch, so hide the keyboard drag mode toggle */
3982
+ @media (width <= 48rem), (width < 48rem) {
3983
+ .vidply-sign-language-settings-item[data-setting="keyboard-drag"] {
3984
+ display: none !important;
3985
+ }
3986
+ }
3987
+
3804
3988
  .vidply-sign-language-header h3 {
3805
3989
  color: var(--vidply-white);
3806
3990
  font-size: var(--vidply-font-lg);
@@ -3861,6 +4045,7 @@
3861
4045
  /* Sign Language Resize Handles */
3862
4046
  .vidply-sign-resize-handle {
3863
4047
  position: absolute;
4048
+ touch-action: none;
3864
4049
  z-index: 10;
3865
4050
  }
3866
4051
 
@@ -4019,7 +4204,7 @@
4019
4204
  }
4020
4205
 
4021
4206
  /* Responsive Sign Language Video */
4022
- @media (width < 48rem) {
4207
+ @media (width <= 48rem), (width < 48rem) {
4023
4208
  .vidply-sign-language-wrapper {
4024
4209
  min-width: 7.5rem;
4025
4210
  width: 35%;
@@ -4032,7 +4217,7 @@
4032
4217
  }
4033
4218
 
4034
4219
  /* Responsive Adjustments */
4035
- @media (width < 48rem) {
4220
+ @media (width <= 48rem), (width < 48rem) {
4036
4221
  .vidply-playlist-thumbnail {
4037
4222
  height: 2.125rem;
4038
4223
  width: 3.75rem;
@@ -4044,7 +4229,7 @@
4044
4229
  }
4045
4230
 
4046
4231
  .vidply-track-artwork {
4047
- aspect-ratio: 16 / 3;
4232
+ height: clamp(6.5rem, 20vh, 10rem);
4048
4233
  }
4049
4234
 
4050
4235
  .vidply-track-info {
@@ -4060,12 +4245,12 @@
4060
4245
  .vidply-player:fullscreen .vidply-playlist-panel {
4061
4246
  bottom: 6.25rem; /* Directly above controls with extra space */
4062
4247
  max-height: 35vh; /* Compact height */
4063
- padding: 0.75rem 0; /* Vertical padding only */
4064
- overflow-y: hidden; /* No vertical scrolling */
4248
+ -webkit-overflow-scrolling: touch; /* iOS momentum scrolling */
4065
4249
  overflow-x: auto; /* Horizontal scrolling */
4250
+ overflow-y: hidden; /* No vertical scrolling */
4251
+ padding: 0.75rem 0; /* Vertical padding only */
4066
4252
  position: absolute !important; /* Force absolute over video */
4067
4253
  touch-action: pan-x; /* Allow horizontal scrolling on touch devices */
4068
- -webkit-overflow-scrolling: touch; /* iOS momentum scrolling */
4069
4254
  }
4070
4255
 
4071
4256
  /* Mobile playlist list - horizontal row, no wrapping */
@@ -4074,8 +4259,8 @@
4074
4259
  flex-direction: row; /* Items side by side */
4075
4260
  flex-wrap: nowrap; /* Never wrap */
4076
4261
  gap: 0.5rem;
4077
- padding: 0 0.75rem;
4078
4262
  -webkit-overflow-scrolling: touch;
4263
+ padding: 0 0.75rem;
4079
4264
  scroll-behavior: smooth;
4080
4265
  touch-action: pan-x; /* Ensure horizontal swipe gestures work */
4081
4266
  }
@@ -4083,11 +4268,11 @@
4083
4268
  /* Mobile playlist items - only show thumbnails */
4084
4269
  .vidply-player.vidply-fullscreen .vidply-playlist-item,
4085
4270
  .vidply-player:fullscreen .vidply-playlist-item {
4086
- width: 7.5rem; /* Smaller width for thumbnail-only */
4087
- min-width: 7.5rem;
4088
- max-width: 7.5rem;
4089
4271
  flex-shrink: 0; /* Don't shrink */
4272
+ max-width: 7.5rem;
4273
+ min-width: 7.5rem;
4090
4274
  scroll-snap-align: start;
4275
+ width: 7.5rem; /* Smaller width for thumbnail-only */
4091
4276
  }
4092
4277
 
4093
4278
  .vidply-player.vidply-fullscreen .vidply-playlist-item-button,
@@ -4099,10 +4284,10 @@
4099
4284
 
4100
4285
  .vidply-player.vidply-fullscreen .vidply-playlist-thumbnail,
4101
4286
  .vidply-player:fullscreen .vidply-playlist-thumbnail {
4287
+ border-radius: 0.5rem; /* Fully rounded for thumbnail-only */
4288
+ flex-shrink: 0;
4102
4289
  height: 5.625rem; /* Square-ish thumbnail */
4103
4290
  width: 100%; /* Full width of card */
4104
- flex-shrink: 0;
4105
- border-radius: 0.5rem; /* Fully rounded for thumbnail-only */
4106
4291
  }
4107
4292
 
4108
4293
  /* Hide text info on mobile - show only thumbnails */
@@ -4114,13 +4299,13 @@
4114
4299
  /* If no thumbnail, show only text */
4115
4300
  .vidply-player.vidply-fullscreen .vidply-playlist-item:not(:has(.vidply-playlist-thumbnail)) .vidply-playlist-item-info,
4116
4301
  .vidply-player:fullscreen .vidply-playlist-item:not(:has(.vidply-playlist-thumbnail)) .vidply-playlist-item-info {
4302
+ align-items: center;
4117
4303
  display: flex;
4118
4304
  flex-direction: column;
4119
- padding: 0.5rem;
4305
+ height: 5.625rem;
4120
4306
  justify-content: center;
4121
- align-items: center;
4307
+ padding: 0.5rem;
4122
4308
  text-align: center;
4123
- height: 5.625rem;
4124
4309
  }
4125
4310
 
4126
4311
  .vidply-player.vidply-fullscreen .vidply-playlist-item:not(:has(.vidply-playlist-thumbnail)) .vidply-playlist-item-title,
@@ -4136,9 +4321,9 @@
4136
4321
 
4137
4322
  .vidply-player.vidply-fullscreen .vidply-playlist-header,
4138
4323
  .vidply-player:fullscreen .vidply-playlist-header {
4139
- padding: 0 0.75rem 0.5rem;
4140
- font-size: 0.6875rem;
4141
4324
  flex-shrink: 0;
4325
+ font-size: 0.6875rem;
4326
+ padding: 0 0.75rem 0.5rem;
4142
4327
  }
4143
4328
 
4144
4329
  /* Mobile transcript underneath video and controls */