@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,2126 @@
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
+
799
+ /* --- Volume Control --- */
800
+ .fw-volume-group {
801
+ display: flex;
802
+ align-items: center;
803
+ border-radius: 0.25rem;
804
+ transition: background-color 0.2s, width 0.2s;
805
+ cursor: pointer;
806
+ }
807
+
808
+ .fw-volume-group--expanded {
809
+ background: rgb(255 255 255 / 0.1);
810
+ }
811
+
812
+ .fw-volume-group:hover {
813
+ background: rgb(255 255 255 / 0.05);
814
+ }
815
+
816
+ .fw-volume-group--expanded:hover {
817
+ background: rgb(255 255 255 / 0.1);
818
+ }
819
+
820
+ .fw-volume-group--disabled {
821
+ opacity: 0.4;
822
+ cursor: not-allowed;
823
+ }
824
+
825
+ .fw-volume-btn {
826
+ display: flex;
827
+ align-items: center;
828
+ justify-content: center;
829
+ width: 2rem;
830
+ height: 2rem;
831
+ color: rgb(255 255 255 / 0.8);
832
+ background: transparent;
833
+ border: none;
834
+ cursor: pointer;
835
+ transition: color 0.15s;
836
+ }
837
+
838
+ .fw-volume-btn:hover {
839
+ color: white;
840
+ }
841
+
842
+ .fw-volume-slider-wrapper {
843
+ display: flex;
844
+ align-items: center;
845
+ overflow: hidden;
846
+ transition: width 0.2s ease-out, opacity 0.2s ease-out;
847
+ }
848
+
849
+ .fw-volume-slider-wrapper--collapsed {
850
+ width: 0;
851
+ opacity: 0;
852
+ }
853
+
854
+ .fw-volume-slider-wrapper--expanded {
855
+ width: 7rem;
856
+ padding-right: 0.5rem;
857
+ opacity: 1;
858
+ }
859
+
860
+ /* --- Time Display --- */
861
+ .fw-time-display {
862
+ font-family: ui-monospace, monospace;
863
+ font-size: 11px;
864
+ line-height: 1;
865
+ color: hsl(var(--tn-fg-dark));
866
+ white-space: nowrap;
867
+ padding: 0 0.5rem;
868
+ }
869
+
870
+ /* --- Controls Wrapper --- */
871
+ .fw-controls-wrapper {
872
+ position: absolute;
873
+ left: 0;
874
+ right: 0;
875
+ bottom: 0;
876
+ display: flex;
877
+ flex-direction: column;
878
+ justify-content: flex-end;
879
+ transition: opacity 0.2s;
880
+ pointer-events: none;
881
+ }
882
+
883
+ .fw-controls-wrapper--visible {
884
+ opacity: 1;
885
+ }
886
+
887
+ .fw-controls-wrapper--hidden {
888
+ opacity: 0;
889
+ }
890
+
891
+ .fw-controls-row {
892
+ width: 100%;
893
+ display: flex;
894
+ align-items: center;
895
+ justify-content: space-between;
896
+ }
897
+
898
+ .fw-controls-left {
899
+ display: flex;
900
+ align-items: center;
901
+ min-width: 0;
902
+ flex-shrink: 1;
903
+ }
904
+
905
+ .fw-controls-right {
906
+ display: flex;
907
+ align-items: center;
908
+ }
909
+
910
+ .fw-seek-wrapper {
911
+ width: 100%;
912
+ padding: 0 0.5rem;
913
+ margin-bottom: -0.25rem;
914
+ }
915
+
916
+ /* --- SeekBar Component --- */
917
+ .fw-seek-track {
918
+ position: absolute;
919
+ left: 0;
920
+ right: 0;
921
+ height: 4px;
922
+ border-radius: 9999px;
923
+ background: hsl(var(--tn-fg-gutter) / 0.4);
924
+ transition: height 0.15s ease;
925
+ }
926
+
927
+ .fw-seek-wrapper:hover .fw-seek-track,
928
+ .fw-seek-track--active {
929
+ height: 6px;
930
+ }
931
+
932
+ .fw-seek-buffered {
933
+ position: absolute;
934
+ height: 100%;
935
+ border-radius: 9999px;
936
+ background: hsl(var(--tn-fg) / 0.3);
937
+ transition: all 0.2s ease;
938
+ }
939
+
940
+ .fw-seek-progress {
941
+ position: absolute;
942
+ height: 100%;
943
+ border-radius: 9999px;
944
+ background: hsl(var(--tn-blue));
945
+ transition: width 0.1s linear;
946
+ }
947
+
948
+ .fw-seek-thumb {
949
+ position: absolute;
950
+ top: 50%;
951
+ width: 16px;
952
+ height: 16px;
953
+ border-radius: 50%;
954
+ background: hsl(var(--tn-blue));
955
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
956
+ transform: translate(-50%, -50%);
957
+ transition: opacity 0.15s ease, transform 0.15s ease;
958
+ opacity: 0;
959
+ pointer-events: none;
960
+ }
961
+
962
+ .fw-seek-wrapper:hover .fw-seek-thumb,
963
+ .fw-seek-thumb--active {
964
+ opacity: 1;
965
+ transform: translate(-50%, -50%) scale(1);
966
+ }
967
+
968
+ .fw-seek-thumb--hidden {
969
+ opacity: 0;
970
+ transform: translate(-50%, -50%) scale(0.75);
971
+ }
972
+
973
+ .fw-seek-tooltip {
974
+ position: absolute;
975
+ bottom: 100%;
976
+ margin-bottom: 8px;
977
+ padding: 4px 8px;
978
+ font-family: ui-monospace, monospace;
979
+ font-size: 12px;
980
+ background: hsl(var(--tn-bg-dark) / 0.95);
981
+ color: hsl(var(--tn-fg));
982
+ border-radius: 4px;
983
+ white-space: nowrap;
984
+ pointer-events: none;
985
+ transform: translateX(-50%);
986
+ }
987
+
988
+ /* --- Stats Panel --- */
989
+ .fw-stats-panel {
990
+ position: absolute;
991
+ top: 0.5rem;
992
+ right: 0.5rem;
993
+ width: 18rem;
994
+ max-height: 80%;
995
+ overflow-y: auto;
996
+ background: hsl(var(--tn-bg-dark) / 0.85);
997
+ backdrop-filter: blur(4px);
998
+ border: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
999
+ font-family: ui-monospace, monospace;
1000
+ font-size: 0.75rem;
1001
+ z-index: 30;
1002
+ }
1003
+
1004
+ .fw-stats-header {
1005
+ display: flex;
1006
+ align-items: center;
1007
+ justify-content: space-between;
1008
+ padding: 0.5rem;
1009
+ border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
1010
+ font-size: 10px;
1011
+ font-weight: 600;
1012
+ text-transform: uppercase;
1013
+ letter-spacing: 0.05em;
1014
+ color: hsl(var(--tn-fg-dark));
1015
+ }
1016
+
1017
+ .fw-stats-section {
1018
+ padding: 0.5rem;
1019
+ border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
1020
+ }
1021
+
1022
+ .fw-stats-section:last-child {
1023
+ border-bottom: none;
1024
+ }
1025
+
1026
+ .fw-stats-row {
1027
+ display: flex;
1028
+ justify-content: space-between;
1029
+ padding: 0.125rem 0;
1030
+ color: hsl(var(--tn-fg-dark));
1031
+ }
1032
+
1033
+ .fw-stats-value {
1034
+ color: hsl(var(--tn-fg));
1035
+ }
1036
+
1037
+ /* --- Loading Screen --- */
1038
+ .fw-loading-screen {
1039
+ position: absolute;
1040
+ inset: 0;
1041
+ display: flex;
1042
+ flex-direction: column;
1043
+ align-items: center;
1044
+ justify-content: center;
1045
+ background: hsl(var(--tn-bg-dark));
1046
+ z-index: 20;
1047
+ }
1048
+
1049
+ .fw-loading-spinner {
1050
+ width: 2rem;
1051
+ height: 2rem;
1052
+ border: 3px solid hsl(var(--tn-fg-gutter) / 0.3);
1053
+ border-top-color: hsl(var(--tn-blue));
1054
+ border-radius: 50%;
1055
+ animation: spin 1s linear infinite;
1056
+ }
1057
+
1058
+ .fw-loading-text {
1059
+ margin-top: 1rem;
1060
+ font-size: 0.875rem;
1061
+ color: hsl(var(--tn-fg-dark));
1062
+ }
1063
+
1064
+ /* --- Idle Screen --- */
1065
+ .fw-idle-screen {
1066
+ position: absolute;
1067
+ inset: 0;
1068
+ display: flex;
1069
+ flex-direction: column;
1070
+ align-items: center;
1071
+ justify-content: center;
1072
+ background: hsl(var(--tn-bg-dark));
1073
+ z-index: 20;
1074
+ }
1075
+
1076
+ .fw-idle-message {
1077
+ font-size: 0.875rem;
1078
+ color: hsl(var(--tn-fg-dark));
1079
+ }
1080
+
1081
+ /* --- Stream State Overlay --- */
1082
+ .fw-stream-state-overlay {
1083
+ position: absolute;
1084
+ inset: 0;
1085
+ display: flex;
1086
+ flex-direction: column;
1087
+ align-items: center;
1088
+ justify-content: center;
1089
+ background: hsl(var(--tn-bg-dark) / 0.9);
1090
+ gap: 1rem;
1091
+ z-index: 20;
1092
+ }
1093
+
1094
+ .fw-stream-state-icon {
1095
+ color: hsl(var(--tn-fg-dark));
1096
+ }
1097
+
1098
+ .fw-stream-state-text {
1099
+ font-size: 0.875rem;
1100
+ color: hsl(var(--tn-fg-dark));
1101
+ }
1102
+
1103
+ /* --- Title Overlay --- */
1104
+ .fw-title-overlay {
1105
+ padding: 1rem;
1106
+ background: linear-gradient(to bottom, hsl(var(--tn-bg-dark) / 0.8), transparent);
1107
+ }
1108
+
1109
+ /* --- Thumbnail Overlay --- */
1110
+ .fw-player-thumbnail {
1111
+ position: relative;
1112
+ display: flex;
1113
+ height: 100%;
1114
+ min-height: 280px;
1115
+ width: 100%;
1116
+ cursor: pointer;
1117
+ align-items: center;
1118
+ justify-content: center;
1119
+ overflow: hidden;
1120
+ background: hsl(var(--tn-bg-dark));
1121
+ }
1122
+
1123
+ /* --- Speed Indicator --- */
1124
+ .fw-speed-indicator {
1125
+ pointer-events: none;
1126
+ background: none;
1127
+ backdrop-filter: none;
1128
+ }
1129
+
1130
+ /* --- Skip Indicator --- */
1131
+ .fw-skip-indicator {
1132
+ display: flex;
1133
+ align-items: center;
1134
+ justify-content: center;
1135
+ }
1136
+
1137
+ /* --- Error Display --- */
1138
+ .fw-player-error {
1139
+ display: flex;
1140
+ flex-direction: column;
1141
+ align-items: center;
1142
+ justify-content: center;
1143
+ gap: 1rem;
1144
+ padding: 1.5rem;
1145
+ min-height: 280px;
1146
+ background: hsl(var(--tn-bg-dark));
1147
+ color: hsl(var(--tn-fg));
1148
+ text-align: center;
1149
+ }
1150
+
1151
+ /* --- Error Popup Overlay --- */
1152
+ .fw-error-overlay {
1153
+ position: absolute;
1154
+ z-index: 20;
1155
+ }
1156
+
1157
+ .fw-error-overlay--fullscreen {
1158
+ inset: 0;
1159
+ display: flex;
1160
+ align-items: center;
1161
+ justify-content: center;
1162
+ background: hsl(0 0% 0% / 0.6);
1163
+ backdrop-filter: blur(4px);
1164
+ }
1165
+
1166
+ .fw-error-overlay--passive {
1167
+ top: 0.75rem;
1168
+ left: 0.75rem;
1169
+ right: 0.75rem;
1170
+ }
1171
+
1172
+ .fw-error-popup {
1173
+ background: hsl(var(--tn-bg-dark));
1174
+ border: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
1175
+ box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.3);
1176
+ overflow: hidden;
1177
+ }
1178
+
1179
+ .fw-error-popup--fullscreen {
1180
+ min-width: 280px;
1181
+ max-width: 320px;
1182
+ }
1183
+
1184
+ .fw-error-popup--passive {
1185
+ max-width: 28rem;
1186
+ }
1187
+
1188
+ .fw-error-header {
1189
+ display: flex;
1190
+ align-items: center;
1191
+ justify-content: space-between;
1192
+ padding: 0.5rem 0.75rem;
1193
+ border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
1194
+ }
1195
+
1196
+ .fw-error-header--error {
1197
+ background: hsl(0 70% 50% / 0.1);
1198
+ }
1199
+
1200
+ .fw-error-header--warning {
1201
+ background: hsl(45 100% 50% / 0.1);
1202
+ }
1203
+
1204
+ .fw-error-title {
1205
+ font-size: 11px;
1206
+ font-weight: 600;
1207
+ text-transform: uppercase;
1208
+ letter-spacing: 0.05em;
1209
+ }
1210
+
1211
+ .fw-error-title--error {
1212
+ color: hsl(0 70% 60%);
1213
+ }
1214
+
1215
+ .fw-error-title--warning {
1216
+ color: hsl(45 100% 50%);
1217
+ }
1218
+
1219
+ .fw-error-close {
1220
+ width: 24px;
1221
+ height: 24px;
1222
+ display: flex;
1223
+ align-items: center;
1224
+ justify-content: center;
1225
+ border-radius: 4px;
1226
+ background: transparent;
1227
+ border: none;
1228
+ color: hsl(var(--tn-fg-dark));
1229
+ cursor: pointer;
1230
+ transition: background-color 0.15s ease, color 0.15s ease;
1231
+ }
1232
+
1233
+ .fw-error-close:hover {
1234
+ background: hsl(var(--tn-fg) / 0.1);
1235
+ color: hsl(var(--tn-fg));
1236
+ }
1237
+
1238
+ .fw-error-body {
1239
+ padding: 0.75rem;
1240
+ }
1241
+
1242
+ .fw-error-message {
1243
+ font-size: 14px;
1244
+ color: hsl(var(--tn-fg));
1245
+ }
1246
+
1247
+ .fw-error-actions {
1248
+ display: flex;
1249
+ border-top: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
1250
+ }
1251
+
1252
+ .fw-error-btn {
1253
+ flex: 1;
1254
+ padding: 0.5rem 1rem;
1255
+ font-size: 12px;
1256
+ font-weight: 500;
1257
+ color: hsl(var(--tn-fg));
1258
+ background: transparent;
1259
+ border: none;
1260
+ cursor: pointer;
1261
+ transition: background-color 0.15s ease, color 0.15s ease;
1262
+ }
1263
+
1264
+ .fw-error-btn:hover {
1265
+ background: hsl(var(--tn-fg) / 0.05);
1266
+ color: hsl(var(--tn-fg-bright));
1267
+ }
1268
+
1269
+ /* --- Context Menu Animations (for bits-ui) --- */
1270
+ @keyframes fw-context-menu-in {
1271
+ from {
1272
+ opacity: 0;
1273
+ transform: scale(0.95);
1274
+ }
1275
+ to {
1276
+ opacity: 1;
1277
+ transform: scale(1);
1278
+ }
1279
+ }
1280
+
1281
+ @keyframes fw-context-menu-out {
1282
+ from {
1283
+ opacity: 1;
1284
+ transform: scale(1);
1285
+ }
1286
+ to {
1287
+ opacity: 0;
1288
+ transform: scale(0.95);
1289
+ }
1290
+ }
1291
+
1292
+ .fw-context-menu[data-state="open"] {
1293
+ animation: fw-context-menu-in 150ms cubic-bezier(0.4, 0, 0.2, 1);
1294
+ }
1295
+
1296
+ .fw-context-menu[data-state="closed"] {
1297
+ animation: fw-context-menu-out 150ms cubic-bezier(0.4, 0, 0.2, 1);
1298
+ }
1299
+
1300
+ /* Slide animations for different sides */
1301
+ .fw-context-menu[data-side="top"] {
1302
+ transform-origin: bottom center;
1303
+ }
1304
+
1305
+ .fw-context-menu[data-side="bottom"] {
1306
+ transform-origin: top center;
1307
+ }
1308
+
1309
+ .fw-context-menu[data-side="left"] {
1310
+ transform-origin: right center;
1311
+ }
1312
+
1313
+ .fw-context-menu[data-side="right"] {
1314
+ transform-origin: left center;
1315
+ }
1316
+
1317
+ /* =====================================================
1318
+ DEV MODE PANEL (Advanced settings sidebar)
1319
+ ===================================================== */
1320
+
1321
+ /* Toggle button - small icon button in corner */
1322
+ .fw-dev-toggle {
1323
+ position: absolute;
1324
+ bottom: 3.5rem;
1325
+ right: 0.5rem;
1326
+ z-index: 40;
1327
+ width: 2rem;
1328
+ height: 2rem;
1329
+ display: flex;
1330
+ align-items: center;
1331
+ justify-content: center;
1332
+ padding: 0;
1333
+ border-radius: 0.375rem;
1334
+ background: hsl(var(--tn-bg-dark));
1335
+ border: 1px solid hsl(var(--tn-fg-gutter) / 0.5);
1336
+ color: hsl(var(--tn-fg-dark));
1337
+ cursor: pointer;
1338
+ transition: background-color 0.15s, color 0.15s, border-color 0.15s, opacity 0.2s;
1339
+ }
1340
+
1341
+ .fw-dev-toggle:hover {
1342
+ background: hsl(var(--tn-bg-highlight));
1343
+ border-color: hsl(var(--tn-fg-gutter));
1344
+ color: hsl(var(--tn-fg));
1345
+ }
1346
+
1347
+ .fw-dev-toggle--visible {
1348
+ opacity: 1;
1349
+ pointer-events: auto;
1350
+ }
1351
+
1352
+ .fw-dev-toggle--hidden {
1353
+ opacity: 0;
1354
+ pointer-events: none;
1355
+ }
1356
+
1357
+ /* Main panel - side panel container */
1358
+ .fw-dev-panel {
1359
+ z-index: 40;
1360
+ pointer-events: auto;
1361
+ background: hsl(var(--tn-bg-dark));
1362
+ border-left: 1px solid hsl(var(--tn-fg-gutter) / 0.5);
1363
+ color: hsl(var(--tn-fg));
1364
+ font-family: ui-monospace, monospace;
1365
+ font-size: 0.75rem;
1366
+ width: 280px;
1367
+ display: flex;
1368
+ flex-direction: column;
1369
+ height: 100%;
1370
+ flex-shrink: 0;
1371
+ }
1372
+
1373
+ /* Header with tabs */
1374
+ .fw-dev-header {
1375
+ display: flex;
1376
+ align-items: center;
1377
+ border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
1378
+ background: hsl(var(--tn-bg) / 0.5);
1379
+ }
1380
+
1381
+ /* Tab buttons */
1382
+ .fw-dev-tab {
1383
+ padding: 0.5rem 0.75rem;
1384
+ font-size: 10px;
1385
+ text-transform: uppercase;
1386
+ letter-spacing: 0.05em;
1387
+ font-weight: 600;
1388
+ background: transparent;
1389
+ border: none;
1390
+ border-right: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
1391
+ color: hsl(var(--tn-fg-dark));
1392
+ cursor: pointer;
1393
+ transition: background-color 0.15s, color 0.15s;
1394
+ }
1395
+
1396
+ .fw-dev-tab:hover {
1397
+ background: hsl(var(--tn-bg-dark) / 0.5);
1398
+ color: hsl(var(--tn-fg));
1399
+ }
1400
+
1401
+ .fw-dev-tab--active {
1402
+ background: hsl(var(--tn-bg-dark));
1403
+ color: hsl(var(--tn-fg-bright));
1404
+ }
1405
+
1406
+ .fw-dev-close {
1407
+ padding: 0.5rem;
1408
+ width: 2rem;
1409
+ height: 2rem;
1410
+ display: flex;
1411
+ align-items: center;
1412
+ justify-content: center;
1413
+ background: transparent;
1414
+ border: none;
1415
+ color: hsl(var(--tn-fg-dark));
1416
+ cursor: pointer;
1417
+ transition: color 0.15s;
1418
+ margin-left: auto;
1419
+ }
1420
+
1421
+ .fw-dev-close:hover {
1422
+ color: hsl(var(--tn-fg));
1423
+ }
1424
+
1425
+ /* Spacer - pushes close button to right */
1426
+ .fw-dev-spacer {
1427
+ flex: 1;
1428
+ }
1429
+
1430
+ /* Content area (body) */
1431
+ .fw-dev-content,
1432
+ .fw-dev-body {
1433
+ flex: 1;
1434
+ overflow-y: auto;
1435
+ }
1436
+
1437
+ /* Section with label */
1438
+ .fw-dev-section {
1439
+ padding: 0.75rem;
1440
+ border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
1441
+ }
1442
+
1443
+ .fw-dev-section-label,
1444
+ .fw-dev-label {
1445
+ font-size: 10px;
1446
+ text-transform: uppercase;
1447
+ letter-spacing: 0.05em;
1448
+ font-weight: 600;
1449
+ color: hsl(var(--tn-fg-dark));
1450
+ margin-bottom: 0.25rem;
1451
+ }
1452
+
1453
+ .fw-dev-section-value,
1454
+ .fw-dev-value {
1455
+ font-size: 0.875rem;
1456
+ color: hsl(var(--tn-fg-bright));
1457
+ }
1458
+
1459
+ .fw-dev-value-arrow {
1460
+ color: hsl(var(--tn-fg-dark));
1461
+ }
1462
+
1463
+ .fw-dev-value-muted {
1464
+ font-size: 10px;
1465
+ color: hsl(var(--tn-fg-dark));
1466
+ margin-top: 0.25rem;
1467
+ }
1468
+
1469
+ .fw-dev-section-sub {
1470
+ font-size: 10px;
1471
+ color: hsl(var(--tn-fg-dark));
1472
+ margin-top: 0.25rem;
1473
+ }
1474
+
1475
+ /* Mode selector buttons */
1476
+ .fw-dev-mode-options,
1477
+ .fw-dev-mode-group {
1478
+ display: flex;
1479
+ gap: 0.25rem;
1480
+ margin-top: 0.5rem;
1481
+ }
1482
+
1483
+ .fw-dev-mode-desc {
1484
+ font-size: 10px;
1485
+ color: hsl(var(--tn-fg-dark));
1486
+ margin-top: 0.5rem;
1487
+ font-style: italic;
1488
+ }
1489
+
1490
+ .fw-dev-mode-btn {
1491
+ flex: 1;
1492
+ padding: 0.375rem 0.5rem;
1493
+ font-size: 10px;
1494
+ font-weight: 500;
1495
+ border-radius: 0.25rem;
1496
+ background: hsl(var(--tn-bg-highlight));
1497
+ border: none;
1498
+ color: hsl(var(--tn-fg-dark));
1499
+ cursor: pointer;
1500
+ transition: background-color 0.15s, color 0.15s;
1501
+ }
1502
+
1503
+ .fw-dev-mode-btn:hover {
1504
+ color: hsl(var(--tn-fg));
1505
+ background: hsl(var(--tn-bg-visual));
1506
+ }
1507
+
1508
+ .fw-dev-mode-btn--active {
1509
+ background: hsl(var(--tn-blue));
1510
+ color: hsl(var(--tn-bg-dark));
1511
+ }
1512
+
1513
+ /* Action buttons row */
1514
+ .fw-dev-actions {
1515
+ display: flex;
1516
+ border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
1517
+ }
1518
+
1519
+ .fw-dev-action-btn {
1520
+ flex: 1;
1521
+ padding: 0.5rem 0.75rem;
1522
+ background: hsl(var(--tn-bg-highlight));
1523
+ border: none;
1524
+ border-right: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
1525
+ color: hsl(var(--tn-fg));
1526
+ font-size: 0.75rem;
1527
+ cursor: pointer;
1528
+ transition: background-color 0.15s, color 0.15s;
1529
+ }
1530
+
1531
+ .fw-dev-action-btn:last-child {
1532
+ border-right: none;
1533
+ }
1534
+
1535
+ .fw-dev-action-btn:hover {
1536
+ background: hsl(var(--tn-bg-visual));
1537
+ color: hsl(var(--tn-fg-bright));
1538
+ }
1539
+
1540
+ /* Combo list header */
1541
+ .fw-dev-list-header {
1542
+ display: flex;
1543
+ align-items: center;
1544
+ justify-content: space-between;
1545
+ padding: 0.5rem 0.75rem;
1546
+ background: hsl(var(--tn-bg) / 0.5);
1547
+ }
1548
+
1549
+ .fw-dev-list-title {
1550
+ font-size: 10px;
1551
+ text-transform: uppercase;
1552
+ letter-spacing: 0.05em;
1553
+ font-weight: 600;
1554
+ color: hsl(var(--tn-fg-dark));
1555
+ }
1556
+
1557
+ .fw-dev-list-toggle {
1558
+ font-size: 10px;
1559
+ color: hsl(var(--tn-fg-dark));
1560
+ background: transparent;
1561
+ border: none;
1562
+ cursor: pointer;
1563
+ display: flex;
1564
+ align-items: center;
1565
+ gap: 0.25rem;
1566
+ padding: 0.25rem;
1567
+ transition: color 0.15s;
1568
+ }
1569
+
1570
+ .fw-dev-list-toggle:hover {
1571
+ color: hsl(var(--tn-fg));
1572
+ }
1573
+
1574
+ /* Combo list items */
1575
+ .fw-dev-combo-list {
1576
+ border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
1577
+ }
1578
+
1579
+ .fw-dev-combo-item {
1580
+ width: 100%;
1581
+ display: flex;
1582
+ align-items: center;
1583
+ gap: 0.5rem;
1584
+ padding: 0.5rem 0.75rem;
1585
+ text-align: left;
1586
+ background: transparent;
1587
+ border: none;
1588
+ border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.2);
1589
+ color: hsl(var(--tn-fg));
1590
+ cursor: pointer;
1591
+ transition: background-color 0.15s;
1592
+ }
1593
+
1594
+ .fw-dev-combo-item:last-child {
1595
+ border-bottom: none;
1596
+ }
1597
+
1598
+ .fw-dev-combo-item:hover {
1599
+ background: hsl(var(--tn-bg) / 0.5);
1600
+ }
1601
+
1602
+ .fw-dev-combo-item--active {
1603
+ background: hsl(var(--tn-bg-visual));
1604
+ color: hsl(var(--tn-fg-bright));
1605
+ box-shadow: inset 0 0 0 1px hsl(var(--tn-blue));
1606
+ }
1607
+
1608
+ .fw-dev-combo-item--disabled {
1609
+ opacity: 0.4;
1610
+ }
1611
+
1612
+ .fw-dev-combo-item--warning {
1613
+ background: hsl(var(--tn-yellow) / 0.05);
1614
+ }
1615
+
1616
+ .fw-dev-combo-item--warning:hover {
1617
+ background: hsl(var(--tn-yellow) / 0.1);
1618
+ }
1619
+
1620
+ .fw-dev-combo-rank {
1621
+ width: 1.25rem;
1622
+ height: 1.25rem;
1623
+ display: flex;
1624
+ align-items: center;
1625
+ justify-content: center;
1626
+ font-size: 10px;
1627
+ font-weight: 700;
1628
+ border-radius: 0.125rem;
1629
+ background: hsl(var(--tn-fg-gutter) / 0.5);
1630
+ color: hsl(var(--tn-fg-dark));
1631
+ flex-shrink: 0;
1632
+ }
1633
+
1634
+ .fw-dev-combo-rank--active {
1635
+ background: hsl(var(--tn-blue));
1636
+ color: hsl(var(--tn-bg-dark));
1637
+ }
1638
+
1639
+ .fw-dev-combo-rank--warning {
1640
+ background: hsl(var(--tn-yellow) / 0.3);
1641
+ color: hsl(var(--tn-yellow));
1642
+ }
1643
+
1644
+ .fw-dev-combo-name {
1645
+ flex: 1;
1646
+ font-size: 0.75rem;
1647
+ overflow: hidden;
1648
+ text-overflow: ellipsis;
1649
+ white-space: nowrap;
1650
+ }
1651
+
1652
+ .fw-dev-combo-arrow {
1653
+ color: hsl(var(--tn-fg-dark));
1654
+ }
1655
+
1656
+ .fw-dev-combo-type {
1657
+ color: hsl(var(--tn-cyan));
1658
+ }
1659
+
1660
+ .fw-dev-combo-type--warning {
1661
+ color: hsl(var(--tn-yellow));
1662
+ }
1663
+
1664
+ .fw-dev-combo-score {
1665
+ font-size: 10px;
1666
+ font-family: ui-monospace, monospace;
1667
+ font-variant-numeric: tabular-nums;
1668
+ padding: 0.125rem 0.375rem;
1669
+ border-radius: 0.125rem;
1670
+ flex-shrink: 0;
1671
+ }
1672
+
1673
+ .fw-dev-combo-score--high {
1674
+ background: hsl(var(--tn-green) / 0.2);
1675
+ color: hsl(var(--tn-green));
1676
+ }
1677
+
1678
+ .fw-dev-combo-score--medium {
1679
+ background: hsl(var(--tn-blue) / 0.2);
1680
+ color: hsl(var(--tn-blue));
1681
+ }
1682
+
1683
+ .fw-dev-combo-score--low {
1684
+ background: hsl(var(--tn-yellow) / 0.2);
1685
+ color: hsl(var(--tn-yellow));
1686
+ }
1687
+
1688
+ /* Tooltip for combo details */
1689
+ .fw-dev-tooltip {
1690
+ position: absolute;
1691
+ left: 0;
1692
+ z-index: 50;
1693
+ background: hsl(var(--tn-bg-dark));
1694
+ border: 1px solid hsl(var(--tn-fg-gutter));
1695
+ box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
1696
+ padding: 0.5rem;
1697
+ font-size: 10px;
1698
+ white-space: nowrap;
1699
+ pointer-events: none;
1700
+ min-width: 220px;
1701
+ }
1702
+
1703
+ .fw-dev-tooltip--above {
1704
+ bottom: 100%;
1705
+ margin-bottom: 0.25rem;
1706
+ }
1707
+
1708
+ .fw-dev-tooltip--below {
1709
+ top: 100%;
1710
+ margin-top: 0.25rem;
1711
+ }
1712
+
1713
+ .fw-dev-tooltip-header {
1714
+ margin-bottom: 0.5rem;
1715
+ padding-bottom: 0.5rem;
1716
+ border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.5);
1717
+ }
1718
+
1719
+ .fw-dev-tooltip-title {
1720
+ font-weight: 700;
1721
+ color: hsl(var(--tn-fg-bright));
1722
+ }
1723
+
1724
+ .fw-dev-tooltip-subtitle {
1725
+ color: hsl(var(--tn-cyan));
1726
+ }
1727
+
1728
+ .fw-dev-tooltip-info {
1729
+ color: hsl(var(--tn-fg-dark));
1730
+ margin-top: 0.25rem;
1731
+ }
1732
+
1733
+ .fw-dev-tooltip-score {
1734
+ font-weight: 700;
1735
+ color: hsl(var(--tn-fg-bright));
1736
+ margin-bottom: 0.25rem;
1737
+ }
1738
+
1739
+ .fw-dev-tooltip-breakdown {
1740
+ color: hsl(var(--tn-fg-dark));
1741
+ }
1742
+
1743
+ .fw-dev-tooltip-breakdown-value {
1744
+ color: hsl(var(--tn-fg));
1745
+ }
1746
+
1747
+ .fw-dev-tooltip-breakdown-weight {
1748
+ color: hsl(var(--tn-fg-gutter));
1749
+ }
1750
+
1751
+ .fw-dev-tooltip-error {
1752
+ color: hsl(var(--tn-red));
1753
+ }
1754
+
1755
+ /* Stats panel content */
1756
+ .fw-dev-stats-hero {
1757
+ padding: 0.75rem;
1758
+ border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
1759
+ }
1760
+
1761
+ .fw-dev-stats-rate {
1762
+ font-size: 1.5rem;
1763
+ font-weight: 700;
1764
+ font-variant-numeric: tabular-nums;
1765
+ }
1766
+
1767
+ .fw-dev-stats-rate--good {
1768
+ color: hsl(var(--tn-green));
1769
+ }
1770
+
1771
+ .fw-dev-stats-rate--catching {
1772
+ color: hsl(var(--tn-blue));
1773
+ }
1774
+
1775
+ .fw-dev-stats-rate--slow {
1776
+ color: hsl(var(--tn-yellow));
1777
+ }
1778
+
1779
+ .fw-dev-stats-rate--stalling {
1780
+ color: hsl(var(--tn-red));
1781
+ }
1782
+
1783
+ .fw-dev-stats-label {
1784
+ font-size: 10px;
1785
+ color: hsl(var(--tn-fg-dark));
1786
+ }
1787
+
1788
+ .fw-dev-stats-metrics {
1789
+ display: flex;
1790
+ gap: 1rem;
1791
+ margin-top: 0.5rem;
1792
+ font-size: 10px;
1793
+ }
1794
+
1795
+ .fw-dev-stats-metric--good {
1796
+ color: hsl(var(--tn-green));
1797
+ }
1798
+
1799
+ .fw-dev-stats-metric--warning {
1800
+ color: hsl(var(--tn-yellow));
1801
+ }
1802
+
1803
+ .fw-dev-stats-metric--bad {
1804
+ color: hsl(var(--tn-red));
1805
+ }
1806
+
1807
+ /* Stats rows */
1808
+ .fw-dev-stats-rows {
1809
+ border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
1810
+ }
1811
+
1812
+ .fw-dev-stats-row {
1813
+ display: flex;
1814
+ justify-content: space-between;
1815
+ padding: 0.5rem 0.75rem;
1816
+ border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.2);
1817
+ }
1818
+
1819
+ .fw-dev-stats-row:last-child {
1820
+ border-bottom: none;
1821
+ }
1822
+
1823
+ .fw-dev-stats-key {
1824
+ color: hsl(var(--tn-fg-dark));
1825
+ }
1826
+
1827
+ .fw-dev-stats-value {
1828
+ color: hsl(var(--tn-fg-bright));
1829
+ }
1830
+
1831
+ .fw-dev-stats-value--cyan {
1832
+ color: hsl(var(--tn-cyan));
1833
+ }
1834
+
1835
+ .fw-dev-stats-value--warning {
1836
+ color: hsl(var(--tn-yellow));
1837
+ }
1838
+
1839
+ .fw-dev-stats-value--bad {
1840
+ color: hsl(var(--tn-red));
1841
+ }
1842
+
1843
+ .fw-dev-stats-value--good {
1844
+ color: hsl(var(--tn-green));
1845
+ }
1846
+
1847
+ /* Track badges */
1848
+ .fw-dev-track-badge {
1849
+ font-size: 10px;
1850
+ font-family: ui-monospace, monospace;
1851
+ text-transform: uppercase;
1852
+ padding: 0.125rem 0.375rem;
1853
+ }
1854
+
1855
+ .fw-dev-track-badge--video {
1856
+ background: hsl(var(--tn-blue) / 0.2);
1857
+ color: hsl(var(--tn-blue));
1858
+ }
1859
+
1860
+ .fw-dev-track-badge--audio {
1861
+ background: hsl(var(--tn-green) / 0.2);
1862
+ color: hsl(var(--tn-green));
1863
+ }
1864
+
1865
+ .fw-dev-track-badge--other {
1866
+ background: hsl(var(--tn-yellow) / 0.2);
1867
+ color: hsl(var(--tn-yellow));
1868
+ }
1869
+
1870
+ .fw-dev-track-info {
1871
+ display: flex;
1872
+ flex-wrap: wrap;
1873
+ gap: 0.5rem;
1874
+ font-size: 10px;
1875
+ color: hsl(var(--tn-fg-dark));
1876
+ }
1877
+
1878
+ /* Empty state */
1879
+ .fw-dev-empty,
1880
+ .fw-dev-list-empty {
1881
+ padding: 1rem 0.75rem;
1882
+ text-align: center;
1883
+ color: hsl(var(--tn-fg-dark));
1884
+ }
1885
+
1886
+ /* Chevron icon (expand/collapse toggle) */
1887
+ .fw-dev-chevron {
1888
+ transition: transform 0.15s;
1889
+ }
1890
+
1891
+ .fw-dev-chevron--open {
1892
+ transform: rotate(180deg);
1893
+ }
1894
+
1895
+ /* Combo wrapper (relative for tooltip positioning) */
1896
+ .fw-dev-combo {
1897
+ position: relative;
1898
+ }
1899
+
1900
+ /* Combo button (alias for fw-dev-combo-item) */
1901
+ .fw-dev-combo-btn {
1902
+ width: 100%;
1903
+ display: flex;
1904
+ align-items: center;
1905
+ gap: 0.5rem;
1906
+ padding: 0.5rem 0.75rem;
1907
+ text-align: left;
1908
+ background: transparent;
1909
+ border: none;
1910
+ border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.2);
1911
+ color: hsl(var(--tn-fg));
1912
+ cursor: pointer;
1913
+ transition: background-color 0.15s;
1914
+ }
1915
+
1916
+ .fw-dev-combo-btn:last-child {
1917
+ border-bottom: none;
1918
+ }
1919
+
1920
+ .fw-dev-combo-btn:hover {
1921
+ background: hsl(var(--tn-bg) / 0.5);
1922
+ }
1923
+
1924
+ .fw-dev-combo-btn--active {
1925
+ background: hsl(var(--tn-bg-visual));
1926
+ color: hsl(var(--tn-fg-bright));
1927
+ box-shadow: inset 0 0 0 1px hsl(var(--tn-blue));
1928
+ }
1929
+
1930
+ .fw-dev-combo-btn--disabled {
1931
+ opacity: 0.4;
1932
+ }
1933
+
1934
+ .fw-dev-combo-btn--codec-warn {
1935
+ background: hsl(var(--tn-yellow) / 0.05);
1936
+ }
1937
+
1938
+ .fw-dev-combo-btn--codec-warn:hover {
1939
+ background: hsl(var(--tn-yellow) / 0.1);
1940
+ }
1941
+
1942
+ /* Combo rank modifiers */
1943
+ .fw-dev-combo-rank--disabled {
1944
+ background: hsl(var(--tn-fg-gutter) / 0.3);
1945
+ color: hsl(var(--tn-fg-gutter));
1946
+ }
1947
+
1948
+ .fw-dev-combo-rank--warn {
1949
+ background: hsl(var(--tn-yellow) / 0.3);
1950
+ color: hsl(var(--tn-yellow));
1951
+ }
1952
+
1953
+ /* Combo type modifiers */
1954
+ .fw-dev-combo-type--disabled {
1955
+ color: hsl(var(--tn-fg-gutter));
1956
+ }
1957
+
1958
+ .fw-dev-combo-type--warn {
1959
+ color: hsl(var(--tn-yellow));
1960
+ }
1961
+
1962
+ /* Combo score modifiers (additional) */
1963
+ .fw-dev-combo-score--disabled {
1964
+ background: hsl(var(--tn-fg-gutter) / 0.2);
1965
+ color: hsl(var(--tn-fg-gutter));
1966
+ }
1967
+
1968
+ .fw-dev-combo-score--mid {
1969
+ background: hsl(var(--tn-blue) / 0.2);
1970
+ color: hsl(var(--tn-blue));
1971
+ }
1972
+
1973
+ /* Tooltip additional classes */
1974
+ .fw-dev-tooltip-tracks {
1975
+ color: hsl(var(--tn-fg-dark));
1976
+ margin-top: 0.25rem;
1977
+ }
1978
+
1979
+ .fw-dev-tooltip-value {
1980
+ color: hsl(var(--tn-fg));
1981
+ }
1982
+
1983
+ .fw-dev-tooltip-row {
1984
+ color: hsl(var(--tn-fg-dark));
1985
+ margin-bottom: 0.125rem;
1986
+ }
1987
+
1988
+ .fw-dev-tooltip-bonus {
1989
+ color: hsl(var(--tn-green));
1990
+ }
1991
+
1992
+ .fw-dev-tooltip-penalty {
1993
+ color: hsl(var(--tn-red));
1994
+ }
1995
+
1996
+ .fw-dev-tooltip-weight {
1997
+ color: hsl(var(--tn-fg-gutter));
1998
+ font-size: 9px;
1999
+ }
2000
+
2001
+ /* =====================================================
2002
+ DEV MODE STATS TAB
2003
+ ===================================================== */
2004
+
2005
+ /* Section header modifier */
2006
+ .fw-dev-section-header {
2007
+ margin-top: 0.5rem;
2008
+ }
2009
+
2010
+ /* Playback rate hero section */
2011
+ .fw-dev-rate {
2012
+ display: flex;
2013
+ align-items: baseline;
2014
+ gap: 0.5rem;
2015
+ margin-bottom: 0.5rem;
2016
+ }
2017
+
2018
+ .fw-dev-rate-value {
2019
+ font-size: 1.5rem;
2020
+ font-weight: 700;
2021
+ font-variant-numeric: tabular-nums;
2022
+ }
2023
+
2024
+ .fw-dev-rate-status {
2025
+ font-size: 10px;
2026
+ color: hsl(var(--tn-fg-dark));
2027
+ text-transform: uppercase;
2028
+ }
2029
+
2030
+ .fw-dev-rate-stats {
2031
+ display: flex;
2032
+ gap: 1rem;
2033
+ font-size: 10px;
2034
+ color: hsl(var(--tn-fg-dark));
2035
+ }
2036
+
2037
+ /* Stat value modifiers */
2038
+ .fw-dev-stat-value--good {
2039
+ color: hsl(var(--tn-green));
2040
+ }
2041
+
2042
+ .fw-dev-stat-value--accent {
2043
+ color: hsl(var(--tn-blue));
2044
+ }
2045
+
2046
+ .fw-dev-stat-value--warn {
2047
+ color: hsl(var(--tn-yellow));
2048
+ }
2049
+
2050
+ .fw-dev-stat-value--bad {
2051
+ color: hsl(var(--tn-red));
2052
+ }
2053
+
2054
+ /* Stats row (key-value pair) */
2055
+ .fw-dev-stat {
2056
+ display: flex;
2057
+ justify-content: space-between;
2058
+ padding: 0.5rem 0.75rem;
2059
+ border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.2);
2060
+ }
2061
+
2062
+ .fw-dev-stat:last-child {
2063
+ border-bottom: none;
2064
+ }
2065
+
2066
+ .fw-dev-stat-label {
2067
+ color: hsl(var(--tn-fg-dark));
2068
+ }
2069
+
2070
+ .fw-dev-stat-value {
2071
+ color: hsl(var(--tn-fg-bright));
2072
+ }
2073
+
2074
+ /* Track display */
2075
+ .fw-dev-track {
2076
+ padding: 0.5rem 0.75rem;
2077
+ border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.2);
2078
+ }
2079
+
2080
+ .fw-dev-track:last-child {
2081
+ border-bottom: none;
2082
+ }
2083
+
2084
+ .fw-dev-track-header {
2085
+ display: flex;
2086
+ align-items: center;
2087
+ gap: 0.5rem;
2088
+ margin-bottom: 0.25rem;
2089
+ }
2090
+
2091
+ .fw-dev-track-codec {
2092
+ font-size: 10px;
2093
+ color: hsl(var(--tn-fg));
2094
+ font-weight: 500;
2095
+ }
2096
+
2097
+ .fw-dev-track-id {
2098
+ font-size: 10px;
2099
+ color: hsl(var(--tn-fg-gutter));
2100
+ margin-left: auto;
2101
+ }
2102
+
2103
+ .fw-dev-track-meta {
2104
+ display: flex;
2105
+ flex-wrap: wrap;
2106
+ gap: 0.5rem;
2107
+ font-size: 10px;
2108
+ color: hsl(var(--tn-fg-dark));
2109
+ }
2110
+
2111
+ /* No tracks state */
2112
+ .fw-dev-no-tracks {
2113
+ padding: 0.75rem;
2114
+ text-align: center;
2115
+ }
2116
+
2117
+ .fw-dev-no-tracks-text {
2118
+ color: hsl(var(--tn-fg-dark));
2119
+ font-size: 10px;
2120
+ }
2121
+
2122
+ .fw-dev-no-tracks-type {
2123
+ color: hsl(var(--tn-fg-gutter));
2124
+ margin-left: 0.25rem;
2125
+ }
2126
+ }