@livepeer-frameworks/player-core 0.0.3

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 (120) hide show
  1. package/dist/cjs/index.js +19493 -0
  2. package/dist/cjs/index.js.map +1 -0
  3. package/dist/esm/index.js +19398 -0
  4. package/dist/esm/index.js.map +1 -0
  5. package/dist/player.css +2140 -0
  6. package/dist/types/core/ABRController.d.ts +164 -0
  7. package/dist/types/core/CodecUtils.d.ts +54 -0
  8. package/dist/types/core/Disposable.d.ts +61 -0
  9. package/dist/types/core/EventEmitter.d.ts +73 -0
  10. package/dist/types/core/GatewayClient.d.ts +144 -0
  11. package/dist/types/core/InteractionController.d.ts +121 -0
  12. package/dist/types/core/LiveDurationProxy.d.ts +102 -0
  13. package/dist/types/core/MetaTrackManager.d.ts +220 -0
  14. package/dist/types/core/MistReporter.d.ts +163 -0
  15. package/dist/types/core/MistSignaling.d.ts +148 -0
  16. package/dist/types/core/PlayerController.d.ts +665 -0
  17. package/dist/types/core/PlayerInterface.d.ts +230 -0
  18. package/dist/types/core/PlayerManager.d.ts +182 -0
  19. package/dist/types/core/PlayerRegistry.d.ts +27 -0
  20. package/dist/types/core/QualityMonitor.d.ts +184 -0
  21. package/dist/types/core/ScreenWakeLockManager.d.ts +70 -0
  22. package/dist/types/core/SeekingUtils.d.ts +142 -0
  23. package/dist/types/core/StreamStateClient.d.ts +108 -0
  24. package/dist/types/core/SubtitleManager.d.ts +111 -0
  25. package/dist/types/core/TelemetryReporter.d.ts +79 -0
  26. package/dist/types/core/TimeFormat.d.ts +97 -0
  27. package/dist/types/core/TimerManager.d.ts +83 -0
  28. package/dist/types/core/UrlUtils.d.ts +81 -0
  29. package/dist/types/core/detector.d.ts +149 -0
  30. package/dist/types/core/index.d.ts +49 -0
  31. package/dist/types/core/scorer.d.ts +167 -0
  32. package/dist/types/core/selector.d.ts +9 -0
  33. package/dist/types/index.d.ts +45 -0
  34. package/dist/types/lib/utils.d.ts +2 -0
  35. package/dist/types/players/DashJsPlayer.d.ts +102 -0
  36. package/dist/types/players/HlsJsPlayer.d.ts +70 -0
  37. package/dist/types/players/MewsWsPlayer/SourceBufferManager.d.ts +119 -0
  38. package/dist/types/players/MewsWsPlayer/WebSocketManager.d.ts +60 -0
  39. package/dist/types/players/MewsWsPlayer/index.d.ts +220 -0
  40. package/dist/types/players/MewsWsPlayer/types.d.ts +89 -0
  41. package/dist/types/players/MistPlayer.d.ts +25 -0
  42. package/dist/types/players/MistWebRTCPlayer/index.d.ts +133 -0
  43. package/dist/types/players/NativePlayer.d.ts +143 -0
  44. package/dist/types/players/VideoJsPlayer.d.ts +59 -0
  45. package/dist/types/players/WebCodecsPlayer/JitterBuffer.d.ts +118 -0
  46. package/dist/types/players/WebCodecsPlayer/LatencyProfiles.d.ts +64 -0
  47. package/dist/types/players/WebCodecsPlayer/RawChunkParser.d.ts +63 -0
  48. package/dist/types/players/WebCodecsPlayer/SyncController.d.ts +174 -0
  49. package/dist/types/players/WebCodecsPlayer/WebSocketController.d.ts +164 -0
  50. package/dist/types/players/WebCodecsPlayer/index.d.ts +149 -0
  51. package/dist/types/players/WebCodecsPlayer/polyfills/MediaStreamTrackGenerator.d.ts +105 -0
  52. package/dist/types/players/WebCodecsPlayer/types.d.ts +395 -0
  53. package/dist/types/players/WebCodecsPlayer/worker/decoder.worker.d.ts +13 -0
  54. package/dist/types/players/WebCodecsPlayer/worker/types.d.ts +197 -0
  55. package/dist/types/players/index.d.ts +14 -0
  56. package/dist/types/styles/index.d.ts +11 -0
  57. package/dist/types/types.d.ts +363 -0
  58. package/dist/types/vanilla/FrameWorksPlayer.d.ts +143 -0
  59. package/dist/types/vanilla/index.d.ts +19 -0
  60. package/dist/workers/decoder.worker.js +989 -0
  61. package/dist/workers/decoder.worker.js.map +1 -0
  62. package/package.json +80 -0
  63. package/src/core/ABRController.ts +550 -0
  64. package/src/core/CodecUtils.ts +257 -0
  65. package/src/core/Disposable.ts +120 -0
  66. package/src/core/EventEmitter.ts +113 -0
  67. package/src/core/GatewayClient.ts +439 -0
  68. package/src/core/InteractionController.ts +712 -0
  69. package/src/core/LiveDurationProxy.ts +270 -0
  70. package/src/core/MetaTrackManager.ts +753 -0
  71. package/src/core/MistReporter.ts +543 -0
  72. package/src/core/MistSignaling.ts +346 -0
  73. package/src/core/PlayerController.ts +2829 -0
  74. package/src/core/PlayerInterface.ts +432 -0
  75. package/src/core/PlayerManager.ts +900 -0
  76. package/src/core/PlayerRegistry.ts +149 -0
  77. package/src/core/QualityMonitor.ts +597 -0
  78. package/src/core/ScreenWakeLockManager.ts +163 -0
  79. package/src/core/SeekingUtils.ts +364 -0
  80. package/src/core/StreamStateClient.ts +457 -0
  81. package/src/core/SubtitleManager.ts +297 -0
  82. package/src/core/TelemetryReporter.ts +308 -0
  83. package/src/core/TimeFormat.ts +205 -0
  84. package/src/core/TimerManager.ts +209 -0
  85. package/src/core/UrlUtils.ts +179 -0
  86. package/src/core/detector.ts +382 -0
  87. package/src/core/index.ts +140 -0
  88. package/src/core/scorer.ts +553 -0
  89. package/src/core/selector.ts +16 -0
  90. package/src/global.d.ts +11 -0
  91. package/src/index.ts +75 -0
  92. package/src/lib/utils.ts +6 -0
  93. package/src/players/DashJsPlayer.ts +642 -0
  94. package/src/players/HlsJsPlayer.ts +483 -0
  95. package/src/players/MewsWsPlayer/SourceBufferManager.ts +572 -0
  96. package/src/players/MewsWsPlayer/WebSocketManager.ts +241 -0
  97. package/src/players/MewsWsPlayer/index.ts +1065 -0
  98. package/src/players/MewsWsPlayer/types.ts +106 -0
  99. package/src/players/MistPlayer.ts +188 -0
  100. package/src/players/MistWebRTCPlayer/index.ts +703 -0
  101. package/src/players/NativePlayer.ts +820 -0
  102. package/src/players/VideoJsPlayer.ts +643 -0
  103. package/src/players/WebCodecsPlayer/JitterBuffer.ts +299 -0
  104. package/src/players/WebCodecsPlayer/LatencyProfiles.ts +151 -0
  105. package/src/players/WebCodecsPlayer/RawChunkParser.ts +151 -0
  106. package/src/players/WebCodecsPlayer/SyncController.ts +456 -0
  107. package/src/players/WebCodecsPlayer/WebSocketController.ts +564 -0
  108. package/src/players/WebCodecsPlayer/index.ts +1650 -0
  109. package/src/players/WebCodecsPlayer/polyfills/MediaStreamTrackGenerator.ts +379 -0
  110. package/src/players/WebCodecsPlayer/types.ts +542 -0
  111. package/src/players/WebCodecsPlayer/worker/decoder.worker.ts +1360 -0
  112. package/src/players/WebCodecsPlayer/worker/types.ts +276 -0
  113. package/src/players/index.ts +22 -0
  114. package/src/styles/animations.css +21 -0
  115. package/src/styles/index.ts +52 -0
  116. package/src/styles/player.css +2126 -0
  117. package/src/styles/tailwind.css +1015 -0
  118. package/src/types.ts +421 -0
  119. package/src/vanilla/FrameWorksPlayer.ts +367 -0
  120. package/src/vanilla/index.ts +22 -0
@@ -0,0 +1,2140 @@
1
+ /*
2
+ * FrameWorks Player CSS
3
+ * Plain CSS - no build step required (just copy to dist).
4
+ * CSS variables are OUTSIDE layer for guaranteed availability.
5
+ * Component styles are in @layer fw-player for cascade isolation.
6
+ */
7
+
8
+ /* =====================================================
9
+ CSS VARIABLES - OUTSIDE LAYER (always available)
10
+ ===================================================== */
11
+
12
+ /*
13
+ * Player-scoped CSS variables - on .fw-player-surface to avoid :root pollution.
14
+ * All player components must be wrapped in .fw-player-surface to inherit these.
15
+ * These are OUTSIDE the layer so they're always available regardless of cascade.
16
+ */
17
+ .fw-player-surface {
18
+ /* Tokyo Night color palette */
19
+ --tn-bg-dark: 235 21% 11%; /* #1a1b26 - Darkest (slab backgrounds) */
20
+ --tn-bg: 233 23% 17%; /* #24283b - Main background */
21
+ --tn-bg-highlight: 233 23% 21%; /* #292e42 - Elevated surfaces */
22
+ --tn-bg-visual: 232 27% 25%; /* #33395e - Selection/active states */
23
+
24
+ /* Text hierarchy */
25
+ --tn-fg: 223 27% 76%; /* #a9b1d6 - Primary text */
26
+ --tn-fg-bright: 220 13% 91%; /* #e2e4ea - Bright/highlighted text */
27
+ --tn-fg-dark: 224 16% 53%; /* #787c99 - Secondary text (muted) */
28
+ --tn-fg-gutter: 228 15% 45%; /* #5a607f - Borders, seams */
29
+
30
+ /* Accent colors (semantic) */
31
+ --tn-blue: 218 79% 73%; /* #7aa2f7 - Primary actions */
32
+ --tn-green: 95 53% 55%; /* #9ece6a - Success */
33
+ --tn-red: 348 74% 64%; /* #f7768e - Destructive, live */
34
+ --tn-yellow: 35 79% 64%; /* #e0af68 - Warnings */
35
+ --tn-purple: 267 82% 77%; /* #bb9af7 - Secondary accent */
36
+ --tn-cyan: 178 64% 63%; /* #7dcfff - Info */
37
+ --tn-teal: 162 66% 62%; /* #73daca - Terminal green */
38
+
39
+ /* Player-internal variables (not shared with host) */
40
+ --fw-background: var(--tn-bg);
41
+ --fw-foreground: var(--tn-fg);
42
+ --fw-card: var(--tn-bg-highlight);
43
+ --fw-card-foreground: var(--tn-fg);
44
+ --fw-popover: var(--tn-bg-highlight);
45
+ --fw-popover-foreground: var(--tn-fg);
46
+ --fw-primary: var(--tn-blue);
47
+ --fw-primary-foreground: var(--tn-bg-dark);
48
+ --fw-secondary: var(--tn-bg-visual);
49
+ --fw-secondary-foreground: var(--tn-fg);
50
+ --fw-muted: var(--tn-bg-highlight);
51
+ --fw-muted-foreground: var(--tn-fg-dark);
52
+ --fw-accent: var(--tn-bg-visual);
53
+ --fw-accent-foreground: var(--tn-fg);
54
+ --fw-destructive: var(--tn-red);
55
+ --fw-destructive-foreground: var(--tn-bg-dark);
56
+ --fw-border: var(--tn-fg-gutter);
57
+ --fw-input: var(--tn-bg-highlight);
58
+ --fw-ring: var(--tn-blue);
59
+ --fw-radius: 0;
60
+
61
+ /* Controls-specific variables */
62
+ --fw-controls-bg: hsl(var(--tn-bg-dark) / 0.85);
63
+ --fw-controls-fg: hsl(var(--tn-fg));
64
+ --fw-seam: hsl(var(--tn-fg-gutter) / 0.3);
65
+ color: var(--fw-controls-fg);
66
+ }
67
+
68
+ /* Declare layer upfront for lowest priority */
69
+ @layer fw-player;
70
+
71
+ /* Component styles in the fw-player layer */
72
+ @layer fw-player {
73
+ /* =====================================================
74
+ ANIMATIONS
75
+ ===================================================== */
76
+ @keyframes float {
77
+ 0%, 100% {
78
+ transform: translateY(0px) scale(1);
79
+ }
80
+ 50% {
81
+ transform: translateY(-12px) scale(0.95);
82
+ }
83
+ }
84
+
85
+ @keyframes spin-slow {
86
+ from {
87
+ transform: rotate(0deg);
88
+ }
89
+ to {
90
+ transform: rotate(360deg);
91
+ }
92
+ }
93
+
94
+ @keyframes spin {
95
+ from {
96
+ transform: rotate(0deg);
97
+ }
98
+ to {
99
+ transform: rotate(360deg);
100
+ }
101
+ }
102
+
103
+ .animate-spin-slow {
104
+ animation: spin-slow 18s linear infinite;
105
+ }
106
+
107
+ .animate-spin {
108
+ animation: spin 1s linear infinite;
109
+ }
110
+
111
+ /* =====================================================
112
+ SLAB SYSTEM - Player Controls & Overlays
113
+ ===================================================== */
114
+
115
+ .fw-player-surface button {
116
+ font: inherit;
117
+ }
118
+
119
+ /* Slab base - solid structural block */
120
+ .fw-slab {
121
+ background: var(--fw-controls-bg);
122
+ border: 1px solid var(--fw-seam);
123
+ }
124
+
125
+ /* Slab header - uppercase, padded */
126
+ .fw-slab-header {
127
+ padding: 0.5rem 1rem;
128
+ border-bottom: 1px solid var(--fw-seam);
129
+ font-size: 0.75rem;
130
+ font-weight: 600;
131
+ text-transform: uppercase;
132
+ letter-spacing: 0.05em;
133
+ color: hsl(var(--tn-fg-dark));
134
+ }
135
+
136
+ /* Slab body - padded content area */
137
+ .fw-slab-body {
138
+ padding: 1rem;
139
+ }
140
+
141
+ /* Slab actions - flush buttons, seams between */
142
+ .fw-slab-actions {
143
+ display: flex;
144
+ border-top: 1px solid var(--fw-seam);
145
+ }
146
+
147
+ .fw-slab-actions > * {
148
+ flex: 1;
149
+ border-radius: 0 !important;
150
+ }
151
+
152
+ .fw-slab-actions > * + * {
153
+ border-left: 1px solid var(--fw-seam);
154
+ }
155
+
156
+ /* Control bar - stacked layout (seekbar above controls row) */
157
+ .fw-control-bar {
158
+ display: flex;
159
+ flex-direction: column;
160
+ width: 100%;
161
+ background: var(--fw-controls-bg);
162
+ border-top: 1px solid var(--fw-seam);
163
+ backdrop-filter: blur(8px);
164
+ }
165
+
166
+ /* Control group - seamed sections within bar */
167
+ .fw-control-group {
168
+ display: flex;
169
+ align-items: center;
170
+ padding: 0.5rem;
171
+ }
172
+
173
+ .fw-control-group + .fw-control-group {
174
+ border-left: 1px solid var(--fw-seam);
175
+ }
176
+
177
+ /* Flush button - fills space, no radius */
178
+ .fw-btn-flush {
179
+ display: flex;
180
+ align-items: center;
181
+ justify-content: center;
182
+ padding: 0.5rem 0.75rem;
183
+ border-radius: 0;
184
+ background: transparent;
185
+ color: hsl(var(--tn-fg));
186
+ transition: background-color 0.15s, color 0.15s;
187
+ cursor: pointer;
188
+ border: none;
189
+ }
190
+
191
+ .fw-btn-flush:hover {
192
+ background: hsl(var(--tn-bg-visual) / 0.5);
193
+ }
194
+
195
+ .fw-btn-flush:active {
196
+ background: hsl(var(--tn-bg-visual));
197
+ }
198
+
199
+ .fw-btn-flush--active {
200
+ color: hsl(var(--tn-blue));
201
+ }
202
+
203
+ /* Status indicators */
204
+ .fw-status-online { color: hsl(var(--tn-green)); }
205
+ .fw-status-offline { color: hsl(var(--tn-red)); }
206
+ .fw-status-warning { color: hsl(var(--tn-yellow)); }
207
+ .fw-status-info { color: hsl(var(--tn-cyan)); }
208
+
209
+ /* =====================================================
210
+ PLAYER CONTAINER STYLES (Slab-based)
211
+ ===================================================== */
212
+
213
+ .fw-player-root {
214
+ position: relative;
215
+ height: 100%;
216
+ width: 100%;
217
+ overflow: hidden;
218
+ border-radius: 0; /* Slabs don't have rounded corners */
219
+ background-color: hsl(var(--tn-bg-dark));
220
+ border: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
221
+ }
222
+
223
+ .fw-player-container {
224
+ height: 100%;
225
+ width: 100%;
226
+ overflow: hidden;
227
+ border-radius: 0;
228
+ background-color: hsl(var(--tn-bg-dark));
229
+ }
230
+
231
+ .fw-player-video {
232
+ height: 100%;
233
+ width: 100%;
234
+ border-radius: 0;
235
+ object-fit: contain;
236
+ background-color: hsl(var(--tn-bg-dark));
237
+ }
238
+
239
+ .fw-player-embed {
240
+ height: 100%;
241
+ width: 100%;
242
+ min-height: 300px;
243
+ border-radius: 0;
244
+ border-width: 0;
245
+ background-color: hsl(var(--tn-bg-dark));
246
+ }
247
+
248
+ .fw-player-dvd {
249
+ position: absolute;
250
+ pointer-events: none;
251
+ user-select: none;
252
+ -webkit-user-select: none;
253
+ }
254
+
255
+ /* =====================================================
256
+ UTILITY CLASSES - All utilities needed by components
257
+ ===================================================== */
258
+
259
+ /* Layout */
260
+ .flex { display: flex; }
261
+ .inline-flex { display: inline-flex; }
262
+ .hidden { display: none; }
263
+ .block { display: block; }
264
+ .relative { position: relative; }
265
+ .absolute { position: absolute; }
266
+ .fixed { position: fixed; }
267
+
268
+ /* Flex utilities */
269
+ .flex-col { flex-direction: column; }
270
+ .flex-row { flex-direction: row; }
271
+ .flex-wrap { flex-wrap: wrap; }
272
+ .flex-1 { flex: 1 1 0%; }
273
+ .flex-shrink { flex-shrink: 1; }
274
+ .flex-shrink-0 { flex-shrink: 0; }
275
+ .items-center { align-items: center; }
276
+ .items-start { align-items: flex-start; }
277
+ .items-end { align-items: flex-end; }
278
+ .justify-center { justify-content: center; }
279
+ .justify-between { justify-content: space-between; }
280
+ .justify-end { justify-content: flex-end; }
281
+
282
+ /* Positioning */
283
+ .inset-0 { inset: 0; }
284
+ .inset-x-0 { left: 0; right: 0; }
285
+ .inset-y-0 { top: 0; bottom: 0; }
286
+ .top-0 { top: 0; }
287
+ .top-2 { top: 0.5rem; }
288
+ .top-3 { top: 0.75rem; }
289
+ .right-0 { right: 0; }
290
+ .right-2 { right: 0.5rem; }
291
+ .right-3 { right: 0.75rem; }
292
+ .bottom-0 { bottom: 0; }
293
+ .bottom-2 { bottom: 0.5rem; }
294
+ .left-0 { left: 0; }
295
+ .left-2 { left: 0.5rem; }
296
+ .left-3 { left: 0.75rem; }
297
+
298
+ /* Z-index */
299
+ .z-10 { z-index: 10; }
300
+ .z-20 { z-index: 20; }
301
+ .z-30 { z-index: 30; }
302
+ .z-50 { z-index: 50; }
303
+
304
+ /* Sizing */
305
+ .w-0 { width: 0; }
306
+ .w-2 { width: 0.5rem; }
307
+ .w-2\.5 { width: 0.625rem; }
308
+ .w-4 { width: 1rem; }
309
+ .w-5 { width: 1.25rem; }
310
+ .w-6 { width: 1.5rem; }
311
+ .w-8 { width: 2rem; }
312
+ .w-10 { width: 2.5rem; }
313
+ .w-12 { width: 3rem; }
314
+ .w-24 { width: 6rem; }
315
+ .w-28 { width: 7rem; }
316
+ .w-48 { width: 12rem; }
317
+ .w-full { width: 100%; }
318
+ .h-1 { height: 0.25rem; }
319
+ .h-1\.5 { height: 0.375rem; }
320
+ .h-2 { height: 0.5rem; }
321
+ .h-2\.5 { height: 0.625rem; }
322
+ .h-4 { height: 1rem; }
323
+ .h-5 { height: 1.25rem; }
324
+ .h-6 { height: 1.5rem; }
325
+ .h-8 { height: 2rem; }
326
+ .h-10 { height: 2.5rem; }
327
+ .h-12 { height: 3rem; }
328
+ .h-full { height: 100%; }
329
+ .min-w-0 { min-width: 0; }
330
+ .min-w-\[280px\] { min-width: 280px; }
331
+ .max-w-xs { max-width: 20rem; }
332
+ .max-w-sm { max-width: 24rem; }
333
+ .max-w-md { max-width: 28rem; }
334
+ .max-h-32 { max-height: 8rem; }
335
+ .max-h-\[80\%\] { max-height: 80%; }
336
+ .max-w-\[320px\] { max-width: 320px; }
337
+
338
+ /* Spacing */
339
+ .gap-0\.5 { gap: 0.125rem; }
340
+ .gap-1 { gap: 0.25rem; }
341
+ .gap-2 { gap: 0.5rem; }
342
+ .gap-3 { gap: 0.75rem; }
343
+ .gap-4 { gap: 1rem; }
344
+ .space-x-1 > * + * { margin-left: 0.25rem; }
345
+ .space-x-2 > * + * { margin-left: 0.5rem; }
346
+ .space-y-1 > * + * { margin-top: 0.25rem; }
347
+ .p-1 { padding: 0.25rem; }
348
+ .p-2 { padding: 0.5rem; }
349
+ .p-3 { padding: 0.75rem; }
350
+ .p-4 { padding: 1rem; }
351
+ .px-1 { padding-left: 0.25rem; padding-right: 0.25rem; }
352
+ .px-1\.5 { padding-left: 0.375rem; padding-right: 0.375rem; }
353
+ .px-2 { padding-left: 0.5rem; padding-right: 0.5rem; }
354
+ .px-3 { padding-left: 0.75rem; padding-right: 0.75rem; }
355
+ .px-4 { padding-left: 1rem; padding-right: 1rem; }
356
+ .py-0\.5 { padding-top: 0.125rem; padding-bottom: 0.125rem; }
357
+ .py-1 { padding-top: 0.25rem; padding-bottom: 0.25rem; }
358
+ .py-2 { padding-top: 0.5rem; padding-bottom: 0.5rem; }
359
+ .py-3 { padding-top: 0.75rem; padding-bottom: 0.75rem; }
360
+ .pr-2 { padding-right: 0.5rem; }
361
+ .pr-5 { padding-right: 1.25rem; }
362
+ .m-0 { margin: 0; }
363
+ .mx-auto { margin-left: auto; margin-right: auto; }
364
+ .mt-1 { margin-top: 0.25rem; }
365
+ .mt-2 { margin-top: 0.5rem; }
366
+ .mb-1 { margin-bottom: 0.25rem; }
367
+ .mb-2 { margin-bottom: 0.5rem; }
368
+ .-mb-1 { margin-bottom: -0.25rem; }
369
+ .-ml-4 { margin-left: -1rem; }
370
+ .ml-0\.5 { margin-left: 0.125rem; }
371
+
372
+ /* Typography */
373
+ .text-\[10px\] { font-size: 10px; }
374
+ .text-\[11px\] { font-size: 11px; }
375
+ .text-xs { font-size: 0.75rem; line-height: 1rem; }
376
+ .text-sm { font-size: 0.875rem; line-height: 1.25rem; }
377
+ .text-base { font-size: 1rem; line-height: 1.5rem; }
378
+ .text-center { text-align: center; }
379
+ .text-left { text-align: left; }
380
+ .text-right { text-align: right; }
381
+ .font-medium { font-weight: 500; }
382
+ .font-semibold { font-weight: 600; }
383
+ .font-bold { font-weight: 700; }
384
+ .font-mono { font-family: ui-monospace, monospace; }
385
+ .uppercase { text-transform: uppercase; }
386
+ .leading-none { line-height: 1; }
387
+ .tracking-wide { letter-spacing: 0.025em; }
388
+ .tracking-wider { letter-spacing: 0.05em; }
389
+ .whitespace-nowrap { white-space: nowrap; }
390
+ .truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
391
+
392
+ /* Colors - Generic */
393
+ .text-white { color: white; }
394
+ .text-white\/50 { color: rgb(255 255 255 / 0.5); }
395
+ .text-white\/60 { color: rgb(255 255 255 / 0.6); }
396
+ .text-white\/70 { color: rgb(255 255 255 / 0.7); }
397
+ .text-white\/80 { color: rgb(255 255 255 / 0.8); }
398
+ .text-white\/90 { color: rgb(255 255 255 / 0.9); }
399
+ .bg-transparent { background-color: transparent; }
400
+ .bg-black { background-color: black; }
401
+ .bg-black\/40 { background-color: rgb(0 0 0 / 0.4); }
402
+ .bg-black\/50 { background-color: rgb(0 0 0 / 0.5); }
403
+ .bg-black\/60 { background-color: rgb(0 0 0 / 0.6); }
404
+ .bg-black\/70 { background-color: rgb(0 0 0 / 0.7); }
405
+ .bg-black\/80 { background-color: rgb(0 0 0 / 0.8); }
406
+ .bg-black\/90 { background-color: rgb(0 0 0 / 0.9); }
407
+ .bg-white { background-color: white; }
408
+ .bg-white\/5 { background-color: rgb(255 255 255 / 0.05); }
409
+ .bg-white\/10 { background-color: rgb(255 255 255 / 0.1); }
410
+ .bg-white\/20 { background-color: rgb(255 255 255 / 0.2); }
411
+ .bg-white\/30 { background-color: rgb(255 255 255 / 0.3); }
412
+ .bg-white\/50 { background-color: rgb(255 255 255 / 0.5); }
413
+ .border-white\/10 { border-color: rgb(255 255 255 / 0.1); }
414
+
415
+ /* Colors - Tokyo Night */
416
+ .bg-tn-bg-dark { background-color: hsl(var(--tn-bg-dark)); }
417
+ .bg-tn-bg { background-color: hsl(var(--tn-bg)); }
418
+ .bg-tn-bg-highlight { background-color: hsl(var(--tn-bg-highlight)); }
419
+ .bg-tn-bg-visual { background-color: hsl(var(--tn-bg-visual)); }
420
+ .text-tn-fg { color: hsl(var(--tn-fg)); }
421
+ .text-tn-fg-dark { color: hsl(var(--tn-fg-dark)); }
422
+ .text-tn-blue { color: hsl(var(--tn-blue)); }
423
+ .text-tn-bg-dark { color: hsl(var(--tn-bg-dark)); }
424
+ .text-tn-accent { color: hsl(var(--tn-bg-visual)); }
425
+ .bg-tn-blue { background-color: hsl(var(--tn-blue)); }
426
+ .bg-tn-blue\/20 { background-color: hsl(var(--tn-blue) / 0.2); }
427
+ .bg-tn-accent { background-color: hsl(var(--tn-bg-visual)); }
428
+ .border-tn-seam { border-color: hsl(var(--tn-fg-gutter) / 0.3); }
429
+ .bg-tn-seam { background-color: hsl(var(--tn-fg-gutter) / 0.3); }
430
+
431
+ /* Colors - Semantic */
432
+ .bg-red-500\/10 { background-color: rgb(239 68 68 / 0.1); }
433
+ .bg-red-600 { background-color: rgb(220 38 38); }
434
+ .bg-yellow-500\/10 { background-color: rgb(234 179 8 / 0.1); }
435
+ .text-red-400 { color: rgb(248 113 113); }
436
+ .text-yellow-400 { color: rgb(250 204 21); }
437
+
438
+ /* Special button colors from component */
439
+ .bg-\[\#414868\] { background-color: #414868; }
440
+ .text-\[\#a9b1d6\] { color: #a9b1d6; }
441
+ .bg-\[hsl\(var\(--tn-fg-dark\)\)\] { background-color: hsl(var(--tn-fg-dark)); }
442
+ .text-\[hsl\(var\(--tn-fg-dark\)\)\] { color: hsl(var(--tn-fg-dark)); }
443
+ .bg-\[hsl\(var\(--tn-accent\)\)\] { color: hsl(var(--tn-accent)); }
444
+ .text-\[hsl\(var\(--tn-accent\)\)\] { color: hsl(var(--tn-accent)); }
445
+
446
+ /* Borders & Rounded */
447
+ .border { border-width: 1px; }
448
+ .border-b { border-bottom-width: 1px; }
449
+ .border-t { border-top-width: 1px; }
450
+ .border-l { border-left-width: 1px; }
451
+ .rounded { border-radius: 0.25rem; }
452
+ .rounded-md { border-radius: 0.375rem; }
453
+ .rounded-lg { border-radius: 0.5rem; }
454
+ .rounded-full { border-radius: 9999px; }
455
+
456
+ /* Effects */
457
+ .shadow-lg { box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); }
458
+ .backdrop-blur-sm { backdrop-filter: blur(4px); }
459
+
460
+ /* Opacity */
461
+ .opacity-0 { opacity: 0; }
462
+ .opacity-40 { opacity: 0.4; }
463
+ .opacity-50 { opacity: 0.5; }
464
+ .opacity-70 { opacity: 0.7; }
465
+ .opacity-100 { opacity: 1; }
466
+
467
+ /* Transitions */
468
+ .transition {
469
+ transition-property: color, background-color, border-color, fill, stroke, opacity, box-shadow, transform, filter, width;
470
+ transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
471
+ transition-duration: 150ms;
472
+ }
473
+ .transition-all {
474
+ transition-property: all;
475
+ transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
476
+ transition-duration: 150ms;
477
+ }
478
+ .transition-opacity {
479
+ transition-property: opacity;
480
+ transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
481
+ transition-duration: 150ms;
482
+ }
483
+ .transition-colors {
484
+ transition-property: color, background-color, border-color, fill, stroke;
485
+ transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
486
+ transition-duration: 150ms;
487
+ }
488
+ .transition-transform {
489
+ transition-property: transform;
490
+ transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
491
+ transition-duration: 150ms;
492
+ }
493
+ .duration-200 { transition-duration: 200ms; }
494
+ .ease-out { transition-timing-function: cubic-bezier(0, 0, 0.2, 1); }
495
+
496
+ /* Interactive */
497
+ .cursor-pointer { cursor: pointer; }
498
+ .cursor-default { cursor: default; }
499
+ .cursor-not-allowed { cursor: not-allowed; }
500
+ .select-none { user-select: none; }
501
+ .touch-none { touch-action: none; }
502
+ .pointer-events-none { pointer-events: none; }
503
+ .pointer-events-auto { pointer-events: auto; }
504
+
505
+ /* Overflow */
506
+ .overflow-hidden { overflow: hidden; }
507
+ .overflow-auto { overflow: auto; }
508
+ .overflow-y-auto { overflow-y: auto; }
509
+
510
+ /* =====================================================
511
+ HOVER & FOCUS STATES
512
+ ===================================================== */
513
+ .hover\:text-white:hover { color: white; }
514
+ .hover\:bg-white\/5:hover { background-color: rgb(255 255 255 / 0.05); }
515
+ .hover\:bg-white\/10:hover { background-color: rgb(255 255 255 / 0.1); }
516
+ .hover\:bg-tn-bg-highlight:hover { background-color: hsl(var(--tn-bg-highlight)); }
517
+ .hover\:bg-tn-bg-visual:hover { background-color: hsl(var(--tn-bg-visual)); }
518
+ .hover\:bg-red-600:hover { background-color: rgb(220 38 38); }
519
+ .focus\:bg-tn-bg-visual:focus { background-color: hsl(var(--tn-bg-visual)); }
520
+ .focus\:text-white:focus { color: white; }
521
+
522
+ /* Group hover states */
523
+ .group:hover .group-hover\:rotate-90 { transform: rotate(90deg); }
524
+ .group:hover .group-hover\:h-1\.5 { height: 0.375rem; }
525
+
526
+ /* =====================================================
527
+ RESPONSIVE - sm: breakpoint (640px)
528
+ ===================================================== */
529
+ @media (min-width: 640px) {
530
+ .sm\:flex { display: flex; }
531
+ }
532
+
533
+ /* =====================================================
534
+ VIDEO.JS CONTAINER CONSTRAINTS
535
+ ===================================================== */
536
+
537
+ /* VideoJS wrapper must respect container bounds */
538
+ .fw-player-container .video-js {
539
+ width: 100% !important;
540
+ height: 100% !important;
541
+ max-width: 100% !important;
542
+ max-height: 100% !important;
543
+ min-width: 0 !important;
544
+ min-height: 0 !important;
545
+ padding: 0 !important;
546
+ background: black;
547
+ }
548
+
549
+ /* Ensure video tech fills the wrapper with letterboxing */
550
+ .fw-player-container .video-js .vjs-tech {
551
+ width: 100% !important;
552
+ height: 100% !important;
553
+ object-fit: contain !important;
554
+ }
555
+
556
+ /* =====================================================
557
+ VIDEO.JS UI HIDING (when using custom controls)
558
+ ===================================================== */
559
+
560
+ /* Hide all VideoJS chrome when using our custom controls */
561
+ .vjs-fw-custom-controls .vjs-control-bar,
562
+ .vjs-fw-custom-controls .vjs-big-play-button,
563
+ .vjs-fw-custom-controls .vjs-loading-spinner,
564
+ .vjs-fw-custom-controls .vjs-text-track-display,
565
+ .vjs-fw-custom-controls .vjs-error-display,
566
+ .vjs-fw-custom-controls .vjs-modal-dialog,
567
+ .vjs-fw-custom-controls .vjs-poster,
568
+ .vjs-fw-custom-controls .vjs-live-control,
569
+ .vjs-fw-custom-controls .vjs-title-bar {
570
+ display: none !important;
571
+ }
572
+
573
+ /* Ensure video element fills container (absolute positioning) */
574
+ .vjs-fw-custom-controls .vjs-tech {
575
+ position: absolute;
576
+ top: 0;
577
+ left: 0;
578
+ width: 100%;
579
+ height: 100%;
580
+ object-fit: contain;
581
+ }
582
+
583
+ /* =====================================================
584
+ SEMANTIC COMPONENT CLASSES (npm_studio pattern)
585
+ All component styling - no Tailwind utilities needed
586
+ ===================================================== */
587
+
588
+ /* --- Settings Menu --- */
589
+ .fw-settings-menu {
590
+ position: absolute;
591
+ bottom: 3rem;
592
+ right: 0;
593
+ width: 12rem;
594
+ max-height: 70vh;
595
+ overflow-y: auto;
596
+ background: hsl(var(--tn-bg-dark));
597
+ border: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
598
+ box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
599
+ border-radius: 0.25rem;
600
+ z-index: 50;
601
+ }
602
+
603
+ .fw-settings-section {
604
+ padding: 0.5rem;
605
+ border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
606
+ }
607
+
608
+ .fw-settings-section:last-child {
609
+ border-bottom: none;
610
+ }
611
+
612
+ .fw-settings-label {
613
+ font-size: 10px;
614
+ color: hsl(var(--tn-fg-dark));
615
+ text-transform: uppercase;
616
+ font-weight: 600;
617
+ margin-bottom: 0.25rem;
618
+ padding-left: 0.25rem;
619
+ }
620
+
621
+ .fw-settings-options {
622
+ display: flex;
623
+ gap: 0.25rem;
624
+ }
625
+
626
+ .fw-settings-options--wrap {
627
+ flex-wrap: wrap;
628
+ }
629
+
630
+ .fw-settings-btn {
631
+ flex: 1;
632
+ padding: 0.375rem 0.25rem;
633
+ font-size: 10px;
634
+ border-radius: 0.25rem;
635
+ background: hsl(var(--tn-bg));
636
+ color: hsl(var(--tn-fg));
637
+ border: none;
638
+ cursor: pointer;
639
+ transition: background-color 0.15s, color 0.15s;
640
+ }
641
+
642
+ .fw-settings-btn:hover {
643
+ background: hsl(var(--tn-bg-highlight));
644
+ }
645
+
646
+ .fw-settings-btn--active {
647
+ background: hsl(var(--tn-blue));
648
+ color: hsl(var(--tn-bg-dark));
649
+ }
650
+
651
+ .fw-settings-btn--active:hover {
652
+ background: hsl(var(--tn-blue));
653
+ }
654
+
655
+ .fw-settings-list {
656
+ display: flex;
657
+ flex-direction: column;
658
+ gap: 0.125rem;
659
+ max-height: 8rem;
660
+ overflow-y: auto;
661
+ }
662
+
663
+ .fw-settings-list-item {
664
+ padding: 0.25rem 0.5rem;
665
+ font-size: 0.75rem;
666
+ text-align: left;
667
+ border-radius: 0.25rem;
668
+ background: transparent;
669
+ color: hsl(var(--tn-fg));
670
+ border: none;
671
+ cursor: pointer;
672
+ transition: background-color 0.15s;
673
+ }
674
+
675
+ .fw-settings-list-item:hover {
676
+ background: hsl(var(--tn-bg-highlight));
677
+ }
678
+
679
+ .fw-settings-list-item--active {
680
+ background: hsl(var(--tn-blue) / 0.2);
681
+ color: hsl(var(--tn-blue));
682
+ }
683
+
684
+ /* --- Context Menu (bits-ui wrapper) --- */
685
+ .fw-context-menu {
686
+ z-index: 50;
687
+ min-width: 8rem;
688
+ overflow: hidden;
689
+ border-radius: 0.25rem;
690
+ border: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
691
+ background: hsl(var(--tn-bg-dark));
692
+ padding: 0;
693
+ color: hsl(var(--tn-fg));
694
+ box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
695
+ }
696
+
697
+ .fw-context-menu-item {
698
+ position: relative;
699
+ display: flex;
700
+ cursor: pointer;
701
+ user-select: none;
702
+ align-items: center;
703
+ padding: 0.5rem 0.75rem;
704
+ font-size: 0.875rem;
705
+ outline: none;
706
+ color: hsl(var(--tn-fg));
707
+ transition: background-color 0.15s, color 0.15s;
708
+ }
709
+
710
+ .fw-context-menu-item:hover,
711
+ .fw-context-menu-item:focus {
712
+ background: hsl(var(--tn-bg-visual));
713
+ color: white;
714
+ }
715
+
716
+ .fw-context-menu-item[data-disabled] {
717
+ pointer-events: none;
718
+ opacity: 0.5;
719
+ }
720
+
721
+ .fw-context-menu-item--inset {
722
+ padding-left: 2rem;
723
+ }
724
+
725
+ .fw-context-menu-separator {
726
+ margin: 0.25rem -0.25rem;
727
+ height: 1px;
728
+ background: hsl(var(--tn-fg-gutter) / 0.3);
729
+ }
730
+
731
+ .fw-context-menu-label {
732
+ padding: 0.375rem 0.5rem;
733
+ font-size: 0.75rem;
734
+ font-weight: 600;
735
+ color: hsl(var(--tn-fg-dark));
736
+ }
737
+
738
+ .fw-context-menu-checkbox {
739
+ position: relative;
740
+ display: flex;
741
+ cursor: pointer;
742
+ user-select: none;
743
+ align-items: center;
744
+ padding: 0.5rem 0.5rem 0.5rem 2rem;
745
+ font-size: 0.875rem;
746
+ outline: none;
747
+ color: hsl(var(--tn-fg));
748
+ transition: background-color 0.15s, color 0.15s;
749
+ }
750
+
751
+ .fw-context-menu-checkbox:hover,
752
+ .fw-context-menu-checkbox:focus {
753
+ background: hsl(var(--tn-bg-visual));
754
+ color: white;
755
+ }
756
+
757
+ .fw-context-menu-indicator {
758
+ position: absolute;
759
+ left: 0.5rem;
760
+ display: flex;
761
+ height: 0.875rem;
762
+ width: 0.875rem;
763
+ align-items: center;
764
+ justify-content: center;
765
+ }
766
+
767
+ /* --- Live Badge --- */
768
+ .fw-live-badge {
769
+ display: flex;
770
+ align-items: center;
771
+ gap: 0.25rem;
772
+ padding: 0.125rem 0.5rem;
773
+ font-size: 10px;
774
+ font-weight: 700;
775
+ text-transform: uppercase;
776
+ letter-spacing: 0.05em;
777
+ transition: background-color 0.15s, color 0.15s;
778
+ border: none;
779
+ cursor: pointer;
780
+ }
781
+
782
+ .fw-live-badge--active {
783
+ background: rgb(220 38 38);
784
+ color: white;
785
+ cursor: default;
786
+ }
787
+
788
+ .fw-live-badge--behind {
789
+ background: #414868;
790
+ color: #a9b1d6;
791
+ }
792
+
793
+ .fw-live-badge--behind:hover {
794
+ background: rgb(220 38 38);
795
+ color: white;
796
+ }
797
+
798
+ .fw-live-badge--nodvr {
799
+ background: #2c2f45;
800
+ color: #c0c7e6;
801
+ cursor: default;
802
+ }
803
+
804
+ .fw-live-badge__nodvr {
805
+ font-size: 9px;
806
+ font-weight: 600;
807
+ letter-spacing: 0.04em;
808
+ opacity: 0.85;
809
+ }
810
+
811
+ /* --- Volume Control --- */
812
+ .fw-volume-group {
813
+ display: flex;
814
+ align-items: center;
815
+ border-radius: 0.25rem;
816
+ transition: background-color 0.2s, width 0.2s;
817
+ cursor: pointer;
818
+ }
819
+
820
+ .fw-volume-group--expanded {
821
+ background: rgb(255 255 255 / 0.1);
822
+ }
823
+
824
+ .fw-volume-group:hover {
825
+ background: rgb(255 255 255 / 0.05);
826
+ }
827
+
828
+ .fw-volume-group--expanded:hover {
829
+ background: rgb(255 255 255 / 0.1);
830
+ }
831
+
832
+ .fw-volume-group--disabled {
833
+ opacity: 0.4;
834
+ cursor: not-allowed;
835
+ }
836
+
837
+ .fw-volume-btn {
838
+ display: flex;
839
+ align-items: center;
840
+ justify-content: center;
841
+ width: 2rem;
842
+ height: 2rem;
843
+ color: rgb(255 255 255 / 0.8);
844
+ background: transparent;
845
+ border: none;
846
+ cursor: pointer;
847
+ transition: color 0.15s;
848
+ }
849
+
850
+ .fw-volume-btn:hover {
851
+ color: white;
852
+ }
853
+
854
+ .fw-volume-slider-wrapper {
855
+ display: flex;
856
+ align-items: center;
857
+ overflow: hidden;
858
+ transition: width 0.2s ease-out, opacity 0.2s ease-out;
859
+ }
860
+
861
+ .fw-volume-slider-wrapper--collapsed {
862
+ width: 0;
863
+ opacity: 0;
864
+ }
865
+
866
+ .fw-volume-slider-wrapper--expanded {
867
+ width: 7rem;
868
+ padding-right: 0.5rem;
869
+ opacity: 1;
870
+ }
871
+
872
+ /* --- Time Display --- */
873
+ .fw-time-display {
874
+ font-family: ui-monospace, monospace;
875
+ font-size: 11px;
876
+ line-height: 1;
877
+ color: hsl(var(--tn-fg-dark));
878
+ white-space: nowrap;
879
+ padding: 0 0.5rem;
880
+ }
881
+
882
+ /* --- Controls Wrapper --- */
883
+ .fw-controls-wrapper {
884
+ position: absolute;
885
+ left: 0;
886
+ right: 0;
887
+ bottom: 0;
888
+ display: flex;
889
+ flex-direction: column;
890
+ justify-content: flex-end;
891
+ transition: opacity 0.2s;
892
+ pointer-events: none;
893
+ }
894
+
895
+ .fw-controls-wrapper--visible {
896
+ opacity: 1;
897
+ }
898
+
899
+ .fw-controls-wrapper--hidden {
900
+ opacity: 0;
901
+ }
902
+
903
+ .fw-controls-row {
904
+ width: 100%;
905
+ display: flex;
906
+ align-items: center;
907
+ justify-content: space-between;
908
+ }
909
+
910
+ .fw-controls-left {
911
+ display: flex;
912
+ align-items: center;
913
+ min-width: 0;
914
+ flex-shrink: 1;
915
+ }
916
+
917
+ .fw-controls-right {
918
+ display: flex;
919
+ align-items: center;
920
+ }
921
+
922
+ .fw-seek-wrapper {
923
+ width: 100%;
924
+ padding: 0 0.5rem;
925
+ margin-bottom: -0.25rem;
926
+ }
927
+
928
+ /* --- SeekBar Component --- */
929
+ .fw-seek-track {
930
+ position: absolute;
931
+ left: 0;
932
+ right: 0;
933
+ height: 4px;
934
+ border-radius: 9999px;
935
+ background: hsl(var(--tn-fg-gutter) / 0.4);
936
+ transition: height 0.15s ease;
937
+ }
938
+
939
+ .fw-seek-wrapper:hover .fw-seek-track,
940
+ .fw-seek-track--active {
941
+ height: 6px;
942
+ }
943
+
944
+ .fw-seek-buffered {
945
+ position: absolute;
946
+ height: 100%;
947
+ border-radius: 9999px;
948
+ background: hsl(var(--tn-fg) / 0.3);
949
+ transition: all 0.2s ease;
950
+ }
951
+
952
+ .fw-seek-progress {
953
+ position: absolute;
954
+ height: 100%;
955
+ border-radius: 9999px;
956
+ background: hsl(var(--tn-blue));
957
+ transition: width 0.1s linear;
958
+ }
959
+
960
+ .fw-seek-thumb {
961
+ position: absolute;
962
+ top: 50%;
963
+ width: 16px;
964
+ height: 16px;
965
+ border-radius: 50%;
966
+ background: hsl(var(--tn-blue));
967
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
968
+ transform: translate(-50%, -50%);
969
+ transition: opacity 0.15s ease, transform 0.15s ease;
970
+ opacity: 0;
971
+ pointer-events: none;
972
+ }
973
+
974
+ .fw-seek-wrapper:hover .fw-seek-thumb,
975
+ .fw-seek-thumb--active {
976
+ opacity: 1;
977
+ transform: translate(-50%, -50%) scale(1);
978
+ }
979
+
980
+ .fw-seek-thumb--hidden {
981
+ opacity: 0;
982
+ transform: translate(-50%, -50%) scale(0.75);
983
+ }
984
+
985
+ .fw-seek-tooltip {
986
+ position: absolute;
987
+ bottom: 100%;
988
+ margin-bottom: 8px;
989
+ padding: 4px 8px;
990
+ font-family: ui-monospace, monospace;
991
+ font-size: 12px;
992
+ background: hsl(var(--tn-bg-dark) / 0.95);
993
+ color: hsl(var(--tn-fg));
994
+ border-radius: 4px;
995
+ white-space: nowrap;
996
+ pointer-events: none;
997
+ transform: translateX(-50%);
998
+ }
999
+
1000
+ /* --- Stats Panel --- */
1001
+ .fw-stats-panel {
1002
+ position: absolute;
1003
+ top: 0.5rem;
1004
+ right: 0.5rem;
1005
+ width: 18rem;
1006
+ max-height: 80%;
1007
+ overflow-y: auto;
1008
+ background: hsl(var(--tn-bg-dark) / 0.85);
1009
+ backdrop-filter: blur(4px);
1010
+ border: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
1011
+ font-family: ui-monospace, monospace;
1012
+ font-size: 0.75rem;
1013
+ z-index: 30;
1014
+ }
1015
+
1016
+ .fw-stats-header {
1017
+ display: flex;
1018
+ align-items: center;
1019
+ justify-content: space-between;
1020
+ padding: 0.5rem;
1021
+ border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
1022
+ font-size: 10px;
1023
+ font-weight: 600;
1024
+ text-transform: uppercase;
1025
+ letter-spacing: 0.05em;
1026
+ color: hsl(var(--tn-fg-dark));
1027
+ }
1028
+
1029
+ .fw-stats-section {
1030
+ padding: 0.5rem;
1031
+ border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
1032
+ }
1033
+
1034
+ .fw-stats-section:last-child {
1035
+ border-bottom: none;
1036
+ }
1037
+
1038
+ .fw-stats-row {
1039
+ display: flex;
1040
+ justify-content: space-between;
1041
+ padding: 0.125rem 0;
1042
+ color: hsl(var(--tn-fg-dark));
1043
+ }
1044
+
1045
+ .fw-stats-value {
1046
+ color: hsl(var(--tn-fg));
1047
+ }
1048
+
1049
+ /* --- Loading Screen --- */
1050
+ .fw-loading-screen {
1051
+ position: absolute;
1052
+ inset: 0;
1053
+ display: flex;
1054
+ flex-direction: column;
1055
+ align-items: center;
1056
+ justify-content: center;
1057
+ background: hsl(var(--tn-bg-dark));
1058
+ z-index: 20;
1059
+ }
1060
+
1061
+ .fw-loading-spinner {
1062
+ width: 2rem;
1063
+ height: 2rem;
1064
+ border: 3px solid hsl(var(--tn-fg-gutter) / 0.3);
1065
+ border-top-color: hsl(var(--tn-blue));
1066
+ border-radius: 50%;
1067
+ animation: spin 1s linear infinite;
1068
+ }
1069
+
1070
+ .fw-loading-text {
1071
+ margin-top: 1rem;
1072
+ font-size: 0.875rem;
1073
+ color: hsl(var(--tn-fg-dark));
1074
+ }
1075
+
1076
+ /* --- Idle Screen --- */
1077
+ .fw-idle-screen {
1078
+ position: absolute;
1079
+ inset: 0;
1080
+ display: flex;
1081
+ flex-direction: column;
1082
+ align-items: center;
1083
+ justify-content: center;
1084
+ background: hsl(var(--tn-bg-dark));
1085
+ z-index: 20;
1086
+ }
1087
+
1088
+ .fw-idle-message {
1089
+ font-size: 0.875rem;
1090
+ color: hsl(var(--tn-fg-dark));
1091
+ }
1092
+
1093
+ /* --- Stream State Overlay --- */
1094
+ .fw-stream-state-overlay {
1095
+ position: absolute;
1096
+ inset: 0;
1097
+ display: flex;
1098
+ flex-direction: column;
1099
+ align-items: center;
1100
+ justify-content: center;
1101
+ background: hsl(var(--tn-bg-dark) / 0.9);
1102
+ gap: 1rem;
1103
+ z-index: 20;
1104
+ }
1105
+
1106
+ .fw-stream-state-icon {
1107
+ color: hsl(var(--tn-fg-dark));
1108
+ }
1109
+
1110
+ .fw-stream-state-text {
1111
+ font-size: 0.875rem;
1112
+ color: hsl(var(--tn-fg-dark));
1113
+ }
1114
+
1115
+ /* --- Title Overlay --- */
1116
+ .fw-title-overlay {
1117
+ padding: 1rem;
1118
+ background: linear-gradient(to bottom, hsl(var(--tn-bg-dark) / 0.8), transparent);
1119
+ }
1120
+
1121
+ /* --- Thumbnail Overlay --- */
1122
+ .fw-player-thumbnail {
1123
+ position: relative;
1124
+ display: flex;
1125
+ height: 100%;
1126
+ min-height: 280px;
1127
+ width: 100%;
1128
+ cursor: pointer;
1129
+ align-items: center;
1130
+ justify-content: center;
1131
+ overflow: hidden;
1132
+ background: hsl(var(--tn-bg-dark));
1133
+ }
1134
+
1135
+ /* --- Speed Indicator --- */
1136
+ .fw-speed-indicator {
1137
+ display: flex;
1138
+ align-items: center;
1139
+ justify-content: center;
1140
+ background: hsl(var(--tn-bg-dark) / 0.6);
1141
+ backdrop-filter: blur(4px);
1142
+ }
1143
+
1144
+ /* --- Skip Indicator --- */
1145
+ .fw-skip-indicator {
1146
+ display: flex;
1147
+ align-items: center;
1148
+ justify-content: center;
1149
+ }
1150
+
1151
+ /* --- Error Display --- */
1152
+ .fw-player-error {
1153
+ display: flex;
1154
+ flex-direction: column;
1155
+ align-items: center;
1156
+ justify-content: center;
1157
+ gap: 1rem;
1158
+ padding: 1.5rem;
1159
+ min-height: 280px;
1160
+ background: hsl(var(--tn-bg-dark));
1161
+ color: hsl(var(--tn-fg));
1162
+ text-align: center;
1163
+ }
1164
+
1165
+ /* --- Error Popup Overlay --- */
1166
+ .fw-error-overlay {
1167
+ position: absolute;
1168
+ z-index: 20;
1169
+ }
1170
+
1171
+ .fw-error-overlay--fullscreen {
1172
+ inset: 0;
1173
+ display: flex;
1174
+ align-items: center;
1175
+ justify-content: center;
1176
+ background: hsl(0 0% 0% / 0.6);
1177
+ backdrop-filter: blur(4px);
1178
+ }
1179
+
1180
+ .fw-error-overlay--passive {
1181
+ top: 0.75rem;
1182
+ left: 0.75rem;
1183
+ right: 0.75rem;
1184
+ }
1185
+
1186
+ .fw-error-popup {
1187
+ background: hsl(var(--tn-bg-dark));
1188
+ border: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
1189
+ box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.3);
1190
+ overflow: hidden;
1191
+ }
1192
+
1193
+ .fw-error-popup--fullscreen {
1194
+ min-width: 280px;
1195
+ max-width: 320px;
1196
+ }
1197
+
1198
+ .fw-error-popup--passive {
1199
+ max-width: 28rem;
1200
+ }
1201
+
1202
+ .fw-error-header {
1203
+ display: flex;
1204
+ align-items: center;
1205
+ justify-content: space-between;
1206
+ padding: 0.5rem 0.75rem;
1207
+ border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
1208
+ }
1209
+
1210
+ .fw-error-header--error {
1211
+ background: hsl(0 70% 50% / 0.1);
1212
+ }
1213
+
1214
+ .fw-error-header--warning {
1215
+ background: hsl(45 100% 50% / 0.1);
1216
+ }
1217
+
1218
+ .fw-error-title {
1219
+ font-size: 11px;
1220
+ font-weight: 600;
1221
+ text-transform: uppercase;
1222
+ letter-spacing: 0.05em;
1223
+ }
1224
+
1225
+ .fw-error-title--error {
1226
+ color: hsl(0 70% 60%);
1227
+ }
1228
+
1229
+ .fw-error-title--warning {
1230
+ color: hsl(45 100% 50%);
1231
+ }
1232
+
1233
+ .fw-error-close {
1234
+ width: 24px;
1235
+ height: 24px;
1236
+ display: flex;
1237
+ align-items: center;
1238
+ justify-content: center;
1239
+ border-radius: 4px;
1240
+ background: transparent;
1241
+ border: none;
1242
+ color: hsl(var(--tn-fg-dark));
1243
+ cursor: pointer;
1244
+ transition: background-color 0.15s ease, color 0.15s ease;
1245
+ }
1246
+
1247
+ .fw-error-close:hover {
1248
+ background: hsl(var(--tn-fg) / 0.1);
1249
+ color: hsl(var(--tn-fg));
1250
+ }
1251
+
1252
+ .fw-error-body {
1253
+ padding: 0.75rem;
1254
+ }
1255
+
1256
+ .fw-error-message {
1257
+ font-size: 14px;
1258
+ color: hsl(var(--tn-fg));
1259
+ }
1260
+
1261
+ .fw-error-actions {
1262
+ display: flex;
1263
+ border-top: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
1264
+ }
1265
+
1266
+ .fw-error-btn {
1267
+ flex: 1;
1268
+ padding: 0.5rem 1rem;
1269
+ font-size: 12px;
1270
+ font-weight: 500;
1271
+ color: hsl(var(--tn-fg));
1272
+ background: transparent;
1273
+ border: none;
1274
+ cursor: pointer;
1275
+ transition: background-color 0.15s ease, color 0.15s ease;
1276
+ }
1277
+
1278
+ .fw-error-btn:hover {
1279
+ background: hsl(var(--tn-fg) / 0.05);
1280
+ color: hsl(var(--tn-fg-bright));
1281
+ }
1282
+
1283
+ /* --- Context Menu Animations (for bits-ui) --- */
1284
+ @keyframes fw-context-menu-in {
1285
+ from {
1286
+ opacity: 0;
1287
+ transform: scale(0.95);
1288
+ }
1289
+ to {
1290
+ opacity: 1;
1291
+ transform: scale(1);
1292
+ }
1293
+ }
1294
+
1295
+ @keyframes fw-context-menu-out {
1296
+ from {
1297
+ opacity: 1;
1298
+ transform: scale(1);
1299
+ }
1300
+ to {
1301
+ opacity: 0;
1302
+ transform: scale(0.95);
1303
+ }
1304
+ }
1305
+
1306
+ .fw-context-menu[data-state="open"] {
1307
+ animation: fw-context-menu-in 150ms cubic-bezier(0.4, 0, 0.2, 1);
1308
+ }
1309
+
1310
+ .fw-context-menu[data-state="closed"] {
1311
+ animation: fw-context-menu-out 150ms cubic-bezier(0.4, 0, 0.2, 1);
1312
+ }
1313
+
1314
+ /* Slide animations for different sides */
1315
+ .fw-context-menu[data-side="top"] {
1316
+ transform-origin: bottom center;
1317
+ }
1318
+
1319
+ .fw-context-menu[data-side="bottom"] {
1320
+ transform-origin: top center;
1321
+ }
1322
+
1323
+ .fw-context-menu[data-side="left"] {
1324
+ transform-origin: right center;
1325
+ }
1326
+
1327
+ .fw-context-menu[data-side="right"] {
1328
+ transform-origin: left center;
1329
+ }
1330
+
1331
+ /* =====================================================
1332
+ DEV MODE PANEL (Advanced settings sidebar)
1333
+ ===================================================== */
1334
+
1335
+ /* Toggle button - small icon button in corner */
1336
+ .fw-dev-toggle {
1337
+ position: absolute;
1338
+ bottom: 3.5rem;
1339
+ right: 0.5rem;
1340
+ z-index: 40;
1341
+ width: 2rem;
1342
+ height: 2rem;
1343
+ display: flex;
1344
+ align-items: center;
1345
+ justify-content: center;
1346
+ padding: 0;
1347
+ border-radius: 0.375rem;
1348
+ background: hsl(var(--tn-bg-dark));
1349
+ border: 1px solid hsl(var(--tn-fg-gutter) / 0.5);
1350
+ color: hsl(var(--tn-fg-dark));
1351
+ cursor: pointer;
1352
+ transition: background-color 0.15s, color 0.15s, border-color 0.15s, opacity 0.2s;
1353
+ }
1354
+
1355
+ .fw-dev-toggle:hover {
1356
+ background: hsl(var(--tn-bg-highlight));
1357
+ border-color: hsl(var(--tn-fg-gutter));
1358
+ color: hsl(var(--tn-fg));
1359
+ }
1360
+
1361
+ .fw-dev-toggle--visible {
1362
+ opacity: 1;
1363
+ pointer-events: auto;
1364
+ }
1365
+
1366
+ .fw-dev-toggle--hidden {
1367
+ opacity: 0;
1368
+ pointer-events: none;
1369
+ }
1370
+
1371
+ /* Main panel - side panel container */
1372
+ .fw-dev-panel {
1373
+ z-index: 40;
1374
+ pointer-events: auto;
1375
+ background: hsl(var(--tn-bg-dark));
1376
+ border-left: 1px solid hsl(var(--tn-fg-gutter) / 0.5);
1377
+ color: hsl(var(--tn-fg));
1378
+ font-family: ui-monospace, monospace;
1379
+ font-size: 0.75rem;
1380
+ width: 280px;
1381
+ display: flex;
1382
+ flex-direction: column;
1383
+ height: 100%;
1384
+ flex-shrink: 0;
1385
+ }
1386
+
1387
+ /* Header with tabs */
1388
+ .fw-dev-header {
1389
+ display: flex;
1390
+ align-items: center;
1391
+ border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
1392
+ background: hsl(var(--tn-bg) / 0.5);
1393
+ }
1394
+
1395
+ /* Tab buttons */
1396
+ .fw-dev-tab {
1397
+ padding: 0.5rem 0.75rem;
1398
+ font-size: 10px;
1399
+ text-transform: uppercase;
1400
+ letter-spacing: 0.05em;
1401
+ font-weight: 600;
1402
+ background: transparent;
1403
+ border: none;
1404
+ border-right: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
1405
+ color: hsl(var(--tn-fg-dark));
1406
+ cursor: pointer;
1407
+ transition: background-color 0.15s, color 0.15s;
1408
+ }
1409
+
1410
+ .fw-dev-tab:hover {
1411
+ background: hsl(var(--tn-bg-dark) / 0.5);
1412
+ color: hsl(var(--tn-fg));
1413
+ }
1414
+
1415
+ .fw-dev-tab--active {
1416
+ background: hsl(var(--tn-bg-dark));
1417
+ color: hsl(var(--tn-fg-bright));
1418
+ }
1419
+
1420
+ .fw-dev-close {
1421
+ padding: 0.5rem;
1422
+ width: 2rem;
1423
+ height: 2rem;
1424
+ display: flex;
1425
+ align-items: center;
1426
+ justify-content: center;
1427
+ background: transparent;
1428
+ border: none;
1429
+ color: hsl(var(--tn-fg-dark));
1430
+ cursor: pointer;
1431
+ transition: color 0.15s;
1432
+ margin-left: auto;
1433
+ }
1434
+
1435
+ .fw-dev-close:hover {
1436
+ color: hsl(var(--tn-fg));
1437
+ }
1438
+
1439
+ /* Spacer - pushes close button to right */
1440
+ .fw-dev-spacer {
1441
+ flex: 1;
1442
+ }
1443
+
1444
+ /* Content area (body) */
1445
+ .fw-dev-content,
1446
+ .fw-dev-body {
1447
+ flex: 1;
1448
+ overflow-y: auto;
1449
+ }
1450
+
1451
+ /* Section with label */
1452
+ .fw-dev-section {
1453
+ padding: 0.75rem;
1454
+ border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
1455
+ }
1456
+
1457
+ .fw-dev-section-label,
1458
+ .fw-dev-label {
1459
+ font-size: 10px;
1460
+ text-transform: uppercase;
1461
+ letter-spacing: 0.05em;
1462
+ font-weight: 600;
1463
+ color: hsl(var(--tn-fg-dark));
1464
+ margin-bottom: 0.25rem;
1465
+ }
1466
+
1467
+ .fw-dev-section-value,
1468
+ .fw-dev-value {
1469
+ font-size: 0.875rem;
1470
+ color: hsl(var(--tn-fg-bright));
1471
+ }
1472
+
1473
+ .fw-dev-value-arrow {
1474
+ color: hsl(var(--tn-fg-dark));
1475
+ }
1476
+
1477
+ .fw-dev-value-muted {
1478
+ font-size: 10px;
1479
+ color: hsl(var(--tn-fg-dark));
1480
+ margin-top: 0.25rem;
1481
+ }
1482
+
1483
+ .fw-dev-section-sub {
1484
+ font-size: 10px;
1485
+ color: hsl(var(--tn-fg-dark));
1486
+ margin-top: 0.25rem;
1487
+ }
1488
+
1489
+ /* Mode selector buttons */
1490
+ .fw-dev-mode-options,
1491
+ .fw-dev-mode-group {
1492
+ display: flex;
1493
+ gap: 0.25rem;
1494
+ margin-top: 0.5rem;
1495
+ }
1496
+
1497
+ .fw-dev-mode-desc {
1498
+ font-size: 10px;
1499
+ color: hsl(var(--tn-fg-dark));
1500
+ margin-top: 0.5rem;
1501
+ font-style: italic;
1502
+ }
1503
+
1504
+ .fw-dev-mode-btn {
1505
+ flex: 1;
1506
+ padding: 0.375rem 0.5rem;
1507
+ font-size: 10px;
1508
+ font-weight: 500;
1509
+ border-radius: 0.25rem;
1510
+ background: hsl(var(--tn-bg-highlight));
1511
+ border: none;
1512
+ color: hsl(var(--tn-fg-dark));
1513
+ cursor: pointer;
1514
+ transition: background-color 0.15s, color 0.15s;
1515
+ }
1516
+
1517
+ .fw-dev-mode-btn:hover {
1518
+ color: hsl(var(--tn-fg));
1519
+ background: hsl(var(--tn-bg-visual));
1520
+ }
1521
+
1522
+ .fw-dev-mode-btn--active {
1523
+ background: hsl(var(--tn-blue));
1524
+ color: hsl(var(--tn-bg-dark));
1525
+ }
1526
+
1527
+ /* Action buttons row */
1528
+ .fw-dev-actions {
1529
+ display: flex;
1530
+ border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
1531
+ }
1532
+
1533
+ .fw-dev-action-btn {
1534
+ flex: 1;
1535
+ padding: 0.5rem 0.75rem;
1536
+ background: hsl(var(--tn-bg-highlight));
1537
+ border: none;
1538
+ border-right: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
1539
+ color: hsl(var(--tn-fg));
1540
+ font-size: 0.75rem;
1541
+ cursor: pointer;
1542
+ transition: background-color 0.15s, color 0.15s;
1543
+ }
1544
+
1545
+ .fw-dev-action-btn:last-child {
1546
+ border-right: none;
1547
+ }
1548
+
1549
+ .fw-dev-action-btn:hover {
1550
+ background: hsl(var(--tn-bg-visual));
1551
+ color: hsl(var(--tn-fg-bright));
1552
+ }
1553
+
1554
+ /* Combo list header */
1555
+ .fw-dev-list-header {
1556
+ display: flex;
1557
+ align-items: center;
1558
+ justify-content: space-between;
1559
+ padding: 0.5rem 0.75rem;
1560
+ background: hsl(var(--tn-bg) / 0.5);
1561
+ }
1562
+
1563
+ .fw-dev-list-title {
1564
+ font-size: 10px;
1565
+ text-transform: uppercase;
1566
+ letter-spacing: 0.05em;
1567
+ font-weight: 600;
1568
+ color: hsl(var(--tn-fg-dark));
1569
+ }
1570
+
1571
+ .fw-dev-list-toggle {
1572
+ font-size: 10px;
1573
+ color: hsl(var(--tn-fg-dark));
1574
+ background: transparent;
1575
+ border: none;
1576
+ cursor: pointer;
1577
+ display: flex;
1578
+ align-items: center;
1579
+ gap: 0.25rem;
1580
+ padding: 0.25rem;
1581
+ transition: color 0.15s;
1582
+ }
1583
+
1584
+ .fw-dev-list-toggle:hover {
1585
+ color: hsl(var(--tn-fg));
1586
+ }
1587
+
1588
+ /* Combo list items */
1589
+ .fw-dev-combo-list {
1590
+ border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
1591
+ }
1592
+
1593
+ .fw-dev-combo-item {
1594
+ width: 100%;
1595
+ display: flex;
1596
+ align-items: center;
1597
+ gap: 0.5rem;
1598
+ padding: 0.5rem 0.75rem;
1599
+ text-align: left;
1600
+ background: transparent;
1601
+ border: none;
1602
+ border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.2);
1603
+ color: hsl(var(--tn-fg));
1604
+ cursor: pointer;
1605
+ transition: background-color 0.15s;
1606
+ }
1607
+
1608
+ .fw-dev-combo-item:last-child {
1609
+ border-bottom: none;
1610
+ }
1611
+
1612
+ .fw-dev-combo-item:hover {
1613
+ background: hsl(var(--tn-bg) / 0.5);
1614
+ }
1615
+
1616
+ .fw-dev-combo-item--active {
1617
+ background: hsl(var(--tn-bg-visual));
1618
+ color: hsl(var(--tn-fg-bright));
1619
+ box-shadow: inset 0 0 0 1px hsl(var(--tn-blue));
1620
+ }
1621
+
1622
+ .fw-dev-combo-item--disabled {
1623
+ opacity: 0.4;
1624
+ }
1625
+
1626
+ .fw-dev-combo-item--warning {
1627
+ background: hsl(var(--tn-yellow) / 0.05);
1628
+ }
1629
+
1630
+ .fw-dev-combo-item--warning:hover {
1631
+ background: hsl(var(--tn-yellow) / 0.1);
1632
+ }
1633
+
1634
+ .fw-dev-combo-rank {
1635
+ width: 1.25rem;
1636
+ height: 1.25rem;
1637
+ display: flex;
1638
+ align-items: center;
1639
+ justify-content: center;
1640
+ font-size: 10px;
1641
+ font-weight: 700;
1642
+ border-radius: 0.125rem;
1643
+ background: hsl(var(--tn-fg-gutter) / 0.5);
1644
+ color: hsl(var(--tn-fg-dark));
1645
+ flex-shrink: 0;
1646
+ }
1647
+
1648
+ .fw-dev-combo-rank--active {
1649
+ background: hsl(var(--tn-blue));
1650
+ color: hsl(var(--tn-bg-dark));
1651
+ }
1652
+
1653
+ .fw-dev-combo-rank--warning {
1654
+ background: hsl(var(--tn-yellow) / 0.3);
1655
+ color: hsl(var(--tn-yellow));
1656
+ }
1657
+
1658
+ .fw-dev-combo-name {
1659
+ flex: 1;
1660
+ font-size: 0.75rem;
1661
+ overflow: hidden;
1662
+ text-overflow: ellipsis;
1663
+ white-space: nowrap;
1664
+ }
1665
+
1666
+ .fw-dev-combo-arrow {
1667
+ color: hsl(var(--tn-fg-dark));
1668
+ }
1669
+
1670
+ .fw-dev-combo-type {
1671
+ color: hsl(var(--tn-cyan));
1672
+ }
1673
+
1674
+ .fw-dev-combo-type--warning {
1675
+ color: hsl(var(--tn-yellow));
1676
+ }
1677
+
1678
+ .fw-dev-combo-score {
1679
+ font-size: 10px;
1680
+ font-family: ui-monospace, monospace;
1681
+ font-variant-numeric: tabular-nums;
1682
+ padding: 0.125rem 0.375rem;
1683
+ border-radius: 0.125rem;
1684
+ flex-shrink: 0;
1685
+ }
1686
+
1687
+ .fw-dev-combo-score--high {
1688
+ background: hsl(var(--tn-green) / 0.2);
1689
+ color: hsl(var(--tn-green));
1690
+ }
1691
+
1692
+ .fw-dev-combo-score--medium {
1693
+ background: hsl(var(--tn-blue) / 0.2);
1694
+ color: hsl(var(--tn-blue));
1695
+ }
1696
+
1697
+ .fw-dev-combo-score--low {
1698
+ background: hsl(var(--tn-yellow) / 0.2);
1699
+ color: hsl(var(--tn-yellow));
1700
+ }
1701
+
1702
+ /* Tooltip for combo details */
1703
+ .fw-dev-tooltip {
1704
+ position: absolute;
1705
+ left: 0;
1706
+ z-index: 50;
1707
+ background: hsl(var(--tn-bg-dark));
1708
+ border: 1px solid hsl(var(--tn-fg-gutter));
1709
+ box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
1710
+ padding: 0.5rem;
1711
+ font-size: 10px;
1712
+ white-space: nowrap;
1713
+ pointer-events: none;
1714
+ min-width: 220px;
1715
+ }
1716
+
1717
+ .fw-dev-tooltip--above {
1718
+ bottom: 100%;
1719
+ margin-bottom: 0.25rem;
1720
+ }
1721
+
1722
+ .fw-dev-tooltip--below {
1723
+ top: 100%;
1724
+ margin-top: 0.25rem;
1725
+ }
1726
+
1727
+ .fw-dev-tooltip-header {
1728
+ margin-bottom: 0.5rem;
1729
+ padding-bottom: 0.5rem;
1730
+ border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.5);
1731
+ }
1732
+
1733
+ .fw-dev-tooltip-title {
1734
+ font-weight: 700;
1735
+ color: hsl(var(--tn-fg-bright));
1736
+ }
1737
+
1738
+ .fw-dev-tooltip-subtitle {
1739
+ color: hsl(var(--tn-cyan));
1740
+ }
1741
+
1742
+ .fw-dev-tooltip-info {
1743
+ color: hsl(var(--tn-fg-dark));
1744
+ margin-top: 0.25rem;
1745
+ }
1746
+
1747
+ .fw-dev-tooltip-score {
1748
+ font-weight: 700;
1749
+ color: hsl(var(--tn-fg-bright));
1750
+ margin-bottom: 0.25rem;
1751
+ }
1752
+
1753
+ .fw-dev-tooltip-breakdown {
1754
+ color: hsl(var(--tn-fg-dark));
1755
+ }
1756
+
1757
+ .fw-dev-tooltip-breakdown-value {
1758
+ color: hsl(var(--tn-fg));
1759
+ }
1760
+
1761
+ .fw-dev-tooltip-breakdown-weight {
1762
+ color: hsl(var(--tn-fg-gutter));
1763
+ }
1764
+
1765
+ .fw-dev-tooltip-error {
1766
+ color: hsl(var(--tn-red));
1767
+ }
1768
+
1769
+ /* Stats panel content */
1770
+ .fw-dev-stats-hero {
1771
+ padding: 0.75rem;
1772
+ border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
1773
+ }
1774
+
1775
+ .fw-dev-stats-rate {
1776
+ font-size: 1.5rem;
1777
+ font-weight: 700;
1778
+ font-variant-numeric: tabular-nums;
1779
+ }
1780
+
1781
+ .fw-dev-stats-rate--good {
1782
+ color: hsl(var(--tn-green));
1783
+ }
1784
+
1785
+ .fw-dev-stats-rate--catching {
1786
+ color: hsl(var(--tn-blue));
1787
+ }
1788
+
1789
+ .fw-dev-stats-rate--slow {
1790
+ color: hsl(var(--tn-yellow));
1791
+ }
1792
+
1793
+ .fw-dev-stats-rate--stalling {
1794
+ color: hsl(var(--tn-red));
1795
+ }
1796
+
1797
+ .fw-dev-stats-label {
1798
+ font-size: 10px;
1799
+ color: hsl(var(--tn-fg-dark));
1800
+ }
1801
+
1802
+ .fw-dev-stats-metrics {
1803
+ display: flex;
1804
+ gap: 1rem;
1805
+ margin-top: 0.5rem;
1806
+ font-size: 10px;
1807
+ }
1808
+
1809
+ .fw-dev-stats-metric--good {
1810
+ color: hsl(var(--tn-green));
1811
+ }
1812
+
1813
+ .fw-dev-stats-metric--warning {
1814
+ color: hsl(var(--tn-yellow));
1815
+ }
1816
+
1817
+ .fw-dev-stats-metric--bad {
1818
+ color: hsl(var(--tn-red));
1819
+ }
1820
+
1821
+ /* Stats rows */
1822
+ .fw-dev-stats-rows {
1823
+ border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
1824
+ }
1825
+
1826
+ .fw-dev-stats-row {
1827
+ display: flex;
1828
+ justify-content: space-between;
1829
+ padding: 0.5rem 0.75rem;
1830
+ border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.2);
1831
+ }
1832
+
1833
+ .fw-dev-stats-row:last-child {
1834
+ border-bottom: none;
1835
+ }
1836
+
1837
+ .fw-dev-stats-key {
1838
+ color: hsl(var(--tn-fg-dark));
1839
+ }
1840
+
1841
+ .fw-dev-stats-value {
1842
+ color: hsl(var(--tn-fg-bright));
1843
+ }
1844
+
1845
+ .fw-dev-stats-value--cyan {
1846
+ color: hsl(var(--tn-cyan));
1847
+ }
1848
+
1849
+ .fw-dev-stats-value--warning {
1850
+ color: hsl(var(--tn-yellow));
1851
+ }
1852
+
1853
+ .fw-dev-stats-value--bad {
1854
+ color: hsl(var(--tn-red));
1855
+ }
1856
+
1857
+ .fw-dev-stats-value--good {
1858
+ color: hsl(var(--tn-green));
1859
+ }
1860
+
1861
+ /* Track badges */
1862
+ .fw-dev-track-badge {
1863
+ font-size: 10px;
1864
+ font-family: ui-monospace, monospace;
1865
+ text-transform: uppercase;
1866
+ padding: 0.125rem 0.375rem;
1867
+ }
1868
+
1869
+ .fw-dev-track-badge--video {
1870
+ background: hsl(var(--tn-blue) / 0.2);
1871
+ color: hsl(var(--tn-blue));
1872
+ }
1873
+
1874
+ .fw-dev-track-badge--audio {
1875
+ background: hsl(var(--tn-green) / 0.2);
1876
+ color: hsl(var(--tn-green));
1877
+ }
1878
+
1879
+ .fw-dev-track-badge--other {
1880
+ background: hsl(var(--tn-yellow) / 0.2);
1881
+ color: hsl(var(--tn-yellow));
1882
+ }
1883
+
1884
+ .fw-dev-track-info {
1885
+ display: flex;
1886
+ flex-wrap: wrap;
1887
+ gap: 0.5rem;
1888
+ font-size: 10px;
1889
+ color: hsl(var(--tn-fg-dark));
1890
+ }
1891
+
1892
+ /* Empty state */
1893
+ .fw-dev-empty,
1894
+ .fw-dev-list-empty {
1895
+ padding: 1rem 0.75rem;
1896
+ text-align: center;
1897
+ color: hsl(var(--tn-fg-dark));
1898
+ }
1899
+
1900
+ /* Chevron icon (expand/collapse toggle) */
1901
+ .fw-dev-chevron {
1902
+ transition: transform 0.15s;
1903
+ }
1904
+
1905
+ .fw-dev-chevron--open {
1906
+ transform: rotate(180deg);
1907
+ }
1908
+
1909
+ /* Combo wrapper (relative for tooltip positioning) */
1910
+ .fw-dev-combo {
1911
+ position: relative;
1912
+ }
1913
+
1914
+ /* Combo button (alias for fw-dev-combo-item) */
1915
+ .fw-dev-combo-btn {
1916
+ width: 100%;
1917
+ display: flex;
1918
+ align-items: center;
1919
+ gap: 0.5rem;
1920
+ padding: 0.5rem 0.75rem;
1921
+ text-align: left;
1922
+ background: transparent;
1923
+ border: none;
1924
+ border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.2);
1925
+ color: hsl(var(--tn-fg));
1926
+ cursor: pointer;
1927
+ transition: background-color 0.15s;
1928
+ }
1929
+
1930
+ .fw-dev-combo-btn:last-child {
1931
+ border-bottom: none;
1932
+ }
1933
+
1934
+ .fw-dev-combo-btn:hover {
1935
+ background: hsl(var(--tn-bg) / 0.5);
1936
+ }
1937
+
1938
+ .fw-dev-combo-btn--active {
1939
+ background: hsl(var(--tn-bg-visual));
1940
+ color: hsl(var(--tn-fg-bright));
1941
+ box-shadow: inset 0 0 0 1px hsl(var(--tn-blue));
1942
+ }
1943
+
1944
+ .fw-dev-combo-btn--disabled {
1945
+ opacity: 0.4;
1946
+ }
1947
+
1948
+ .fw-dev-combo-btn--codec-warn {
1949
+ background: hsl(var(--tn-yellow) / 0.05);
1950
+ }
1951
+
1952
+ .fw-dev-combo-btn--codec-warn:hover {
1953
+ background: hsl(var(--tn-yellow) / 0.1);
1954
+ }
1955
+
1956
+ /* Combo rank modifiers */
1957
+ .fw-dev-combo-rank--disabled {
1958
+ background: hsl(var(--tn-fg-gutter) / 0.3);
1959
+ color: hsl(var(--tn-fg-gutter));
1960
+ }
1961
+
1962
+ .fw-dev-combo-rank--warn {
1963
+ background: hsl(var(--tn-yellow) / 0.3);
1964
+ color: hsl(var(--tn-yellow));
1965
+ }
1966
+
1967
+ /* Combo type modifiers */
1968
+ .fw-dev-combo-type--disabled {
1969
+ color: hsl(var(--tn-fg-gutter));
1970
+ }
1971
+
1972
+ .fw-dev-combo-type--warn {
1973
+ color: hsl(var(--tn-yellow));
1974
+ }
1975
+
1976
+ /* Combo score modifiers (additional) */
1977
+ .fw-dev-combo-score--disabled {
1978
+ background: hsl(var(--tn-fg-gutter) / 0.2);
1979
+ color: hsl(var(--tn-fg-gutter));
1980
+ }
1981
+
1982
+ .fw-dev-combo-score--mid {
1983
+ background: hsl(var(--tn-blue) / 0.2);
1984
+ color: hsl(var(--tn-blue));
1985
+ }
1986
+
1987
+ /* Tooltip additional classes */
1988
+ .fw-dev-tooltip-tracks {
1989
+ color: hsl(var(--tn-fg-dark));
1990
+ margin-top: 0.25rem;
1991
+ }
1992
+
1993
+ .fw-dev-tooltip-value {
1994
+ color: hsl(var(--tn-fg));
1995
+ }
1996
+
1997
+ .fw-dev-tooltip-row {
1998
+ color: hsl(var(--tn-fg-dark));
1999
+ margin-bottom: 0.125rem;
2000
+ }
2001
+
2002
+ .fw-dev-tooltip-bonus {
2003
+ color: hsl(var(--tn-green));
2004
+ }
2005
+
2006
+ .fw-dev-tooltip-penalty {
2007
+ color: hsl(var(--tn-red));
2008
+ }
2009
+
2010
+ .fw-dev-tooltip-weight {
2011
+ color: hsl(var(--tn-fg-gutter));
2012
+ font-size: 9px;
2013
+ }
2014
+
2015
+ /* =====================================================
2016
+ DEV MODE STATS TAB
2017
+ ===================================================== */
2018
+
2019
+ /* Section header modifier */
2020
+ .fw-dev-section-header {
2021
+ margin-top: 0.5rem;
2022
+ }
2023
+
2024
+ /* Playback rate hero section */
2025
+ .fw-dev-rate {
2026
+ display: flex;
2027
+ align-items: baseline;
2028
+ gap: 0.5rem;
2029
+ margin-bottom: 0.5rem;
2030
+ }
2031
+
2032
+ .fw-dev-rate-value {
2033
+ font-size: 1.5rem;
2034
+ font-weight: 700;
2035
+ font-variant-numeric: tabular-nums;
2036
+ }
2037
+
2038
+ .fw-dev-rate-status {
2039
+ font-size: 10px;
2040
+ color: hsl(var(--tn-fg-dark));
2041
+ text-transform: uppercase;
2042
+ }
2043
+
2044
+ .fw-dev-rate-stats {
2045
+ display: flex;
2046
+ gap: 1rem;
2047
+ font-size: 10px;
2048
+ color: hsl(var(--tn-fg-dark));
2049
+ }
2050
+
2051
+ /* Stat value modifiers */
2052
+ .fw-dev-stat-value--good {
2053
+ color: hsl(var(--tn-green));
2054
+ }
2055
+
2056
+ .fw-dev-stat-value--accent {
2057
+ color: hsl(var(--tn-blue));
2058
+ }
2059
+
2060
+ .fw-dev-stat-value--warn {
2061
+ color: hsl(var(--tn-yellow));
2062
+ }
2063
+
2064
+ .fw-dev-stat-value--bad {
2065
+ color: hsl(var(--tn-red));
2066
+ }
2067
+
2068
+ /* Stats row (key-value pair) */
2069
+ .fw-dev-stat {
2070
+ display: flex;
2071
+ justify-content: space-between;
2072
+ padding: 0.5rem 0.75rem;
2073
+ border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.2);
2074
+ }
2075
+
2076
+ .fw-dev-stat:last-child {
2077
+ border-bottom: none;
2078
+ }
2079
+
2080
+ .fw-dev-stat-label {
2081
+ color: hsl(var(--tn-fg-dark));
2082
+ }
2083
+
2084
+ .fw-dev-stat-value {
2085
+ color: hsl(var(--tn-fg-bright));
2086
+ }
2087
+
2088
+ /* Track display */
2089
+ .fw-dev-track {
2090
+ padding: 0.5rem 0.75rem;
2091
+ border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.2);
2092
+ }
2093
+
2094
+ .fw-dev-track:last-child {
2095
+ border-bottom: none;
2096
+ }
2097
+
2098
+ .fw-dev-track-header {
2099
+ display: flex;
2100
+ align-items: center;
2101
+ gap: 0.5rem;
2102
+ margin-bottom: 0.25rem;
2103
+ }
2104
+
2105
+ .fw-dev-track-codec {
2106
+ font-size: 10px;
2107
+ color: hsl(var(--tn-fg));
2108
+ font-weight: 500;
2109
+ }
2110
+
2111
+ .fw-dev-track-id {
2112
+ font-size: 10px;
2113
+ color: hsl(var(--tn-fg-gutter));
2114
+ margin-left: auto;
2115
+ }
2116
+
2117
+ .fw-dev-track-meta {
2118
+ display: flex;
2119
+ flex-wrap: wrap;
2120
+ gap: 0.5rem;
2121
+ font-size: 10px;
2122
+ color: hsl(var(--tn-fg-dark));
2123
+ }
2124
+
2125
+ /* No tracks state */
2126
+ .fw-dev-no-tracks {
2127
+ padding: 0.75rem;
2128
+ text-align: center;
2129
+ }
2130
+
2131
+ .fw-dev-no-tracks-text {
2132
+ color: hsl(var(--tn-fg-dark));
2133
+ font-size: 10px;
2134
+ }
2135
+
2136
+ .fw-dev-no-tracks-type {
2137
+ color: hsl(var(--tn-fg-gutter));
2138
+ margin-left: 0.25rem;
2139
+ }
2140
+ }