@odori/cli 0.0.2

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 (70) hide show
  1. package/LICENSE +22 -0
  2. package/bin/odori.mjs +39 -0
  3. package/dist/chunk-7XJL2BYO.js +3552 -0
  4. package/dist/cli.d.ts +10 -0
  5. package/dist/cli.js +10 -0
  6. package/dist/index.d.ts +622 -0
  7. package/dist/index.js +156 -0
  8. package/dist/registry-snapshot-NIH2JMQ6.js +3559 -0
  9. package/package.json +50 -0
  10. package/src/audio-mix.ts +133 -0
  11. package/src/binaries.ts +241 -0
  12. package/src/brand-file.ts +94 -0
  13. package/src/chunk-cache.ts +85 -0
  14. package/src/chunks.ts +78 -0
  15. package/src/cli.ts +319 -0
  16. package/src/commands/add.ts +151 -0
  17. package/src/commands/dev.ts +160 -0
  18. package/src/commands/doctor.ts +162 -0
  19. package/src/commands/exportVideo.ts +198 -0
  20. package/src/commands/init.ts +56 -0
  21. package/src/commands/inspect.ts +72 -0
  22. package/src/commands/list.ts +22 -0
  23. package/src/commands/new.ts +126 -0
  24. package/src/commands/shared.ts +96 -0
  25. package/src/commands/still.ts +40 -0
  26. package/src/commands/test.ts +265 -0
  27. package/src/commands/update.ts +183 -0
  28. package/src/config.ts +84 -0
  29. package/src/contracts.ts +159 -0
  30. package/src/cues.ts +141 -0
  31. package/src/determinism.ts +82 -0
  32. package/src/diff.ts +71 -0
  33. package/src/discovery.ts +216 -0
  34. package/src/formats.ts +119 -0
  35. package/src/index.ts +58 -0
  36. package/src/integrity.ts +101 -0
  37. package/src/jobs.ts +151 -0
  38. package/src/log.ts +17 -0
  39. package/src/open.ts +32 -0
  40. package/src/paths.ts +12 -0
  41. package/src/prepare-cache.ts +58 -0
  42. package/src/project.ts +196 -0
  43. package/src/registry-snapshot.json +3431 -0
  44. package/src/registry-source.ts +269 -0
  45. package/src/render.ts +627 -0
  46. package/src/server.ts +307 -0
  47. package/studio/index.html +41 -0
  48. package/studio/src/Studio.tsx +192 -0
  49. package/studio/src/components/AudioClip.tsx +64 -0
  50. package/studio/src/components/CanvasStage.tsx +79 -0
  51. package/studio/src/components/CommandPalette.tsx +129 -0
  52. package/studio/src/components/Diagnostics.tsx +93 -0
  53. package/studio/src/components/ExportPanel.tsx +234 -0
  54. package/studio/src/components/InputControls.tsx +110 -0
  55. package/studio/src/components/Thumbnail.tsx +71 -0
  56. package/studio/src/components/Transport.tsx +237 -0
  57. package/studio/src/components/Waveform.tsx +114 -0
  58. package/studio/src/components/Wordmark.tsx +449 -0
  59. package/studio/src/components/ui.tsx +138 -0
  60. package/studio/src/lib/mix-loudness.ts +52 -0
  61. package/studio/src/main.tsx +34 -0
  62. package/studio/src/shortcuts.ts +27 -0
  63. package/studio/src/studio.css +1232 -0
  64. package/studio/src/theme.ts +61 -0
  65. package/studio/src/views/AssetsView.tsx +111 -0
  66. package/studio/src/views/BrandsView.tsx +139 -0
  67. package/studio/src/views/ComponentsView.tsx +285 -0
  68. package/studio/src/views/HomeView.tsx +122 -0
  69. package/studio/src/views/VideosView.tsx +343 -0
  70. package/studio/src/virtual.d.ts +25 -0
@@ -0,0 +1,1232 @@
1
+ /*
2
+ * Studio chrome follows the viewer's theme. A composition never does: it always
3
+ * renders its brand's own colors, because the video is the product.
4
+ *
5
+ * Light is the base palette. The dark palette is applied by [data-theme="dark"],
6
+ * which an inline script in index.html sets before the first paint.
7
+ */
8
+ :root {
9
+ color-scheme: light;
10
+
11
+ /* Surfaces */
12
+ --background: #ffffff;
13
+ --panel: #fafafa;
14
+ --elevated: #ffffff;
15
+ --border: #e6e6e6;
16
+ --border-strong: #d0d0d0;
17
+
18
+ /* Text */
19
+ --foreground: #0a0a0a;
20
+ --muted: #5c5c5c;
21
+ --subtle: #8f8f8f;
22
+
23
+ /* Interaction */
24
+ --hover: #f2f2f2;
25
+ --active: #ebebeb;
26
+ --field: #ffffff;
27
+ --overlay: rgba(0, 0, 0, 0.35);
28
+ --scrollbar: #dcdcdc;
29
+ --scrollbar-hover: #c4c4c4;
30
+ --shadow: 0 24px 60px rgba(0, 0, 0, 0.14);
31
+ --viewport: #f4f4f4;
32
+ --viewport-glow: #ffffff;
33
+ --contrast: #ffffff;
34
+
35
+ /* Signals */
36
+ --accent: #0a0a0a;
37
+ --success: #2f7d3f;
38
+ --warning: #9a6400;
39
+ --danger: #c23b3b;
40
+
41
+ --radius: 8px;
42
+ --radius-sm: 6px;
43
+ --radius-lg: 12px;
44
+
45
+ --font-sans: "Geist Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
46
+ --font-mono: "Geist Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
47
+
48
+ font-family: var(--font-sans);
49
+ font-feature-settings: "cv11", "ss01";
50
+ -webkit-font-smoothing: antialiased;
51
+ }
52
+
53
+ :root[data-theme="dark"] {
54
+ color-scheme: dark;
55
+
56
+ --background: #000000;
57
+ --panel: #0a0a0a;
58
+ --elevated: #101010;
59
+ --border: #1f1f1f;
60
+ --border-strong: #2e2e2e;
61
+
62
+ --foreground: #ededed;
63
+ --muted: #a1a1a1;
64
+ --subtle: #6f6f6f;
65
+
66
+ --hover: #141414;
67
+ --active: #161616;
68
+ --field: #0c0c0c;
69
+ --overlay: rgba(0, 0, 0, 0.6);
70
+ --scrollbar: #1f1f1f;
71
+ --scrollbar-hover: #2e2e2e;
72
+ --shadow: 0 30px 80px rgba(0, 0, 0, 0.6);
73
+ --viewport: #050505;
74
+ --viewport-glow: #0c0c0c;
75
+ --contrast: #000000;
76
+
77
+ --accent: #ededed;
78
+ --success: #62c073;
79
+ --warning: #f5a623;
80
+ --danger: #f76d6d;
81
+ }
82
+
83
+ /* Before the inline script runs, follow the operating system. */
84
+ @media (prefers-color-scheme: dark) {
85
+ :root:not([data-theme="light"]) {
86
+ color-scheme: dark;
87
+
88
+ --background: #000000;
89
+ --panel: #0a0a0a;
90
+ --elevated: #101010;
91
+ --border: #1f1f1f;
92
+ --border-strong: #2e2e2e;
93
+
94
+ --foreground: #ededed;
95
+ --muted: #a1a1a1;
96
+ --subtle: #6f6f6f;
97
+
98
+ --hover: #141414;
99
+ --active: #161616;
100
+ --field: #0c0c0c;
101
+ --overlay: rgba(0, 0, 0, 0.6);
102
+ --scrollbar: #1f1f1f;
103
+ --scrollbar-hover: #2e2e2e;
104
+ --shadow: 0 30px 80px rgba(0, 0, 0, 0.6);
105
+ --viewport: #050505;
106
+ --viewport-glow: #0c0c0c;
107
+ --contrast: #000000;
108
+
109
+ --accent: #ededed;
110
+ --success: #62c073;
111
+ --warning: #f5a623;
112
+ --danger: #f76d6d;
113
+ }
114
+ }
115
+
116
+ * {
117
+ box-sizing: border-box;
118
+ }
119
+
120
+ html {
121
+ background: var(--background);
122
+ }
123
+
124
+ body {
125
+ margin: 0;
126
+ background: var(--background);
127
+ color: var(--foreground);
128
+ font-size: 14px;
129
+ overflow: hidden;
130
+ }
131
+
132
+ body.render-mode {
133
+ overflow: hidden;
134
+ }
135
+
136
+ ::selection {
137
+ background: var(--border-strong);
138
+ }
139
+
140
+ /* Scrollbars stay out of the way, like a code editor. */
141
+ ::-webkit-scrollbar {
142
+ width: 10px;
143
+ height: 10px;
144
+ }
145
+ ::-webkit-scrollbar-thumb {
146
+ background: var(--scrollbar);
147
+ border: 3px solid transparent;
148
+ background-clip: content-box;
149
+ border-radius: 999px;
150
+ }
151
+ ::-webkit-scrollbar-thumb:hover {
152
+ background: var(--scrollbar-hover);
153
+ background-clip: content-box;
154
+ }
155
+
156
+ /* ---------- primitives ---------- */
157
+
158
+ .button {
159
+ align-items: center;
160
+ background: transparent;
161
+ border: 1px solid transparent;
162
+ border-radius: var(--radius-sm);
163
+ color: var(--muted);
164
+ cursor: pointer;
165
+ display: inline-flex;
166
+ font: inherit;
167
+ font-size: 13px;
168
+ gap: 6px;
169
+ height: 30px;
170
+ justify-content: center;
171
+ padding: 0 10px;
172
+ transition: background 120ms ease, color 120ms ease, border-color 120ms ease;
173
+ white-space: nowrap;
174
+ }
175
+ .button:hover {
176
+ background: var(--hover);
177
+ color: var(--foreground);
178
+ }
179
+
180
+ /* Links styled as buttons, for destinations rather than actions. */
181
+ a.button {
182
+ text-decoration: none;
183
+ }
184
+ .button:focus-visible {
185
+ outline: 2px solid var(--border-strong);
186
+ outline-offset: 1px;
187
+ }
188
+ .button[data-variant="outline"] {
189
+ border-color: var(--border);
190
+ color: var(--foreground);
191
+ }
192
+ .button[data-variant="outline"]:hover {
193
+ border-color: var(--border-strong);
194
+ background: var(--panel);
195
+ }
196
+ .button[data-variant="primary"] {
197
+ background: var(--foreground);
198
+ color: var(--contrast);
199
+ font-weight: 500;
200
+ }
201
+ .button[data-variant="primary"]:hover {
202
+ background: color-mix(in srgb, var(--foreground) 88%, var(--background));
203
+ }
204
+ .button[data-active="true"] {
205
+ background: var(--active);
206
+ color: var(--foreground);
207
+ }
208
+ .button:disabled {
209
+ cursor: not-allowed;
210
+ opacity: 0.45;
211
+ }
212
+ .button[data-icon="true"] {
213
+ padding: 0;
214
+ width: 30px;
215
+ }
216
+
217
+ .split {
218
+ display: inline-flex;
219
+ position: relative;
220
+ }
221
+ .split .button[data-variant="primary"]:first-child {
222
+ border-bottom-right-radius: 0;
223
+ border-top-right-radius: 0;
224
+ }
225
+ .split .button[data-variant="primary"]:nth-child(2) {
226
+ border-bottom-left-radius: 0;
227
+ border-top-left-radius: 0;
228
+ position: relative;
229
+ width: 28px;
230
+ }
231
+ /* The divider sits on a light button, so it is drawn from the button's own
232
+ ink rather than from --border, which is a line for dark surfaces. Inset
233
+ top and bottom, it reads as one control divided instead of two buttons
234
+ pushed together. */
235
+ .split .button[data-variant="primary"]:nth-child(2)::before {
236
+ background: currentColor;
237
+ bottom: 8px;
238
+ content: "";
239
+ left: 0;
240
+ opacity: 0.18;
241
+ position: absolute;
242
+ top: 8px;
243
+ width: 1px;
244
+ }
245
+ /* Both halves are one button, so they light together. */
246
+ .split:hover .button[data-variant="primary"]:not(:disabled) {
247
+ background: #ffffff;
248
+ }
249
+
250
+ .menu {
251
+ background: var(--elevated);
252
+ border: 1px solid var(--border);
253
+ border-radius: var(--radius);
254
+ box-shadow: var(--shadow);
255
+ display: flex;
256
+ flex-direction: column;
257
+ left: 0;
258
+ margin-top: 6px;
259
+ min-width: 232px;
260
+ padding: 4px;
261
+ position: absolute;
262
+ top: 100%;
263
+ z-index: 40;
264
+ }
265
+ .menu[data-placement="up"] {
266
+ bottom: 100%;
267
+ margin-bottom: 6px;
268
+ margin-top: 0;
269
+ top: auto;
270
+ }
271
+ .menu-item {
272
+ background: transparent;
273
+ border: 0;
274
+ border-radius: var(--radius-sm);
275
+ color: var(--foreground);
276
+ cursor: pointer;
277
+ display: flex;
278
+ flex-direction: column;
279
+ font: inherit;
280
+ font-size: 13px;
281
+ gap: 2px;
282
+ padding: 7px 9px;
283
+ text-align: left;
284
+ }
285
+ .menu-item:hover {
286
+ background: var(--active);
287
+ }
288
+ .menu-item[data-active="true"] {
289
+ background: var(--active);
290
+ }
291
+ .menu-item[data-active="true"] > span:first-child::after {
292
+ color: var(--subtle);
293
+ content: " default";
294
+ font-size: 11px;
295
+ }
296
+
297
+ /* Matches the transport's icon buttons rather than the host select. */
298
+ .rate {
299
+ appearance: none;
300
+ background: transparent;
301
+ border: 1px solid transparent;
302
+ border-radius: var(--radius-sm);
303
+ color: var(--muted);
304
+ cursor: pointer;
305
+ font: inherit;
306
+ font-family: var(--font-mono);
307
+ font-size: 12px;
308
+ height: 30px;
309
+ padding: 0 6px;
310
+ }
311
+ .rate:hover {
312
+ background: var(--hover);
313
+ color: var(--foreground);
314
+ }
315
+ .rate:focus-visible {
316
+ outline: 2px solid var(--border-strong);
317
+ outline-offset: 1px;
318
+ }
319
+
320
+ .badge {
321
+ align-items: center;
322
+ border: 1px solid var(--border);
323
+ border-radius: 999px;
324
+ color: var(--muted);
325
+ display: inline-flex;
326
+ font-family: var(--font-mono);
327
+ font-size: 11px;
328
+ gap: 5px;
329
+ height: 20px;
330
+ letter-spacing: 0.02em;
331
+ padding: 0 8px;
332
+ }
333
+ .badge[data-tone="success"] { border-color: color-mix(in srgb, var(--success) 40%, var(--border)); color: var(--success); }
334
+ .badge[data-tone="warning"] { border-color: color-mix(in srgb, var(--warning) 40%, var(--border)); color: var(--warning); }
335
+ .badge[data-tone="danger"] { border-color: color-mix(in srgb, var(--danger) 40%, var(--border)); color: var(--danger); }
336
+
337
+ .kbd {
338
+ background: var(--hover);
339
+ border: 1px solid var(--border);
340
+ border-bottom-width: 2px;
341
+ border-radius: 5px;
342
+ color: var(--subtle);
343
+ font-family: var(--font-mono);
344
+ font-size: 10px;
345
+ line-height: 1;
346
+ padding: 3px 5px;
347
+ }
348
+
349
+ .input {
350
+ background: var(--field);
351
+ border: 1px solid var(--border);
352
+ border-radius: var(--radius-sm);
353
+ color: var(--foreground);
354
+ font: inherit;
355
+ font-size: 13px;
356
+ height: 30px;
357
+ padding: 0 10px;
358
+ width: 100%;
359
+ }
360
+ .input:focus {
361
+ border-color: var(--border-strong);
362
+ outline: none;
363
+ }
364
+ textarea.input {
365
+ font-family: var(--font-mono);
366
+ font-size: 12px;
367
+ height: auto;
368
+ line-height: 1.5;
369
+ padding: 8px 10px;
370
+ resize: vertical;
371
+ }
372
+ select.input {
373
+ appearance: none;
374
+ background-image: linear-gradient(45deg, transparent 50%, var(--subtle) 50%),
375
+ linear-gradient(135deg, var(--subtle) 50%, transparent 50%);
376
+ background-position: calc(100% - 14px) 13px, calc(100% - 9px) 13px;
377
+ background-repeat: no-repeat;
378
+ background-size: 5px 5px, 5px 5px;
379
+ }
380
+
381
+ .separator {
382
+ background: var(--border);
383
+ height: 1px;
384
+ margin: 14px 0;
385
+ }
386
+
387
+ /* ---------- shell ---------- */
388
+
389
+ .studio {
390
+ display: grid;
391
+ grid-template-rows: 48px 1fr 28px;
392
+ height: 100vh;
393
+ }
394
+
395
+ .topbar {
396
+ align-items: center;
397
+ background: var(--panel);
398
+ border-bottom: 1px solid var(--border);
399
+ display: flex;
400
+ gap: 14px;
401
+ padding: 0 12px;
402
+ }
403
+
404
+ /* The wordmark opens the overview, so it is a control, not a label. */
405
+ .wordmark {
406
+ align-items: center;
407
+ background: transparent;
408
+ border: 0;
409
+ color: var(--foreground);
410
+ cursor: pointer;
411
+ display: flex;
412
+ font: inherit;
413
+ font-size: 15px;
414
+ font-weight: 560;
415
+ gap: 8px;
416
+ letter-spacing: -0.03em;
417
+ padding: 0 0 0 4px;
418
+ }
419
+ .wordmark:focus-visible {
420
+ outline: 2px solid var(--border-strong);
421
+ outline-offset: 3px;
422
+ }
423
+ /* The mark and the wordmark are one drawing, so the topbar sets a height and
424
+ lets the artwork keep its own proportions. */
425
+ .wordmark svg {
426
+ height: 17px;
427
+ width: auto;
428
+ }
429
+
430
+ .tabs {
431
+ background: var(--field);
432
+ border: 1px solid var(--border);
433
+ border-radius: var(--radius);
434
+ display: flex;
435
+ gap: 2px;
436
+ padding: 2px;
437
+ }
438
+ .tabs .button {
439
+ height: 26px;
440
+ text-transform: capitalize;
441
+ }
442
+
443
+ /* Navigation type, measured off shadcn/ui: 14px at 500, spaced by padding
444
+ rather than gaps, and colored by state alone. */
445
+ .views {
446
+ display: flex;
447
+ gap: 2px;
448
+ }
449
+ .views .button {
450
+ border-radius: var(--radius-sm);
451
+ font-size: 14px;
452
+ font-weight: 500;
453
+ height: 32px;
454
+ letter-spacing: -0.006em;
455
+ padding: 0 10px;
456
+ text-transform: capitalize;
457
+ transition: color 120ms ease;
458
+ }
459
+ .views .button,
460
+ .views .button:hover,
461
+ .views .button[data-active="true"] {
462
+ background: transparent;
463
+ }
464
+ .views .button:hover,
465
+ .views .button[data-active="true"] {
466
+ color: var(--foreground);
467
+ }
468
+
469
+ .topbar-right {
470
+ align-items: center;
471
+ display: flex;
472
+ gap: 8px;
473
+ margin-left: auto;
474
+ }
475
+
476
+ .search {
477
+ position: relative;
478
+ width: 210px;
479
+ }
480
+ .search .kbd {
481
+ position: absolute;
482
+ right: 7px;
483
+ top: 7px;
484
+ }
485
+
486
+ .main {
487
+ display: grid;
488
+ grid-template-columns: 260px minmax(0, 1fr) 320px;
489
+ overflow: hidden;
490
+ }
491
+ .main[data-single="true"] {
492
+ grid-template-columns: 1fr;
493
+ }
494
+
495
+ .sidebar {
496
+ background: var(--panel);
497
+ border-right: 1px solid var(--border);
498
+ display: flex;
499
+ flex-direction: column;
500
+ overflow: hidden;
501
+ }
502
+ .sidebar-scroll {
503
+ overflow-y: auto;
504
+ padding: 10px;
505
+ }
506
+
507
+ .inspector {
508
+ background: var(--panel);
509
+ border-left: 1px solid var(--border);
510
+ overflow-y: auto;
511
+ padding: 16px;
512
+ }
513
+
514
+ .section-title {
515
+ color: var(--subtle);
516
+ font-size: 11px;
517
+ font-weight: 500;
518
+ letter-spacing: 0.08em;
519
+ margin: 0 0 10px;
520
+ text-transform: uppercase;
521
+ }
522
+
523
+ .list {
524
+ display: grid;
525
+ gap: 2px;
526
+ list-style: none;
527
+ margin: 0 0 14px;
528
+ padding: 0;
529
+ }
530
+ .list-item {
531
+ align-items: center;
532
+ background: transparent;
533
+ border: 1px solid transparent;
534
+ border-radius: var(--radius-sm);
535
+ cursor: pointer;
536
+ display: grid;
537
+ gap: 2px;
538
+ padding: 8px 10px;
539
+ text-align: left;
540
+ width: 100%;
541
+ }
542
+ .list-item:hover {
543
+ background: var(--panel);
544
+ }
545
+ .list-item[data-active="true"] {
546
+ background: var(--hover);
547
+ border-color: var(--border);
548
+ }
549
+ .list-item strong {
550
+ color: var(--foreground);
551
+ font-size: 13px;
552
+ font-weight: 500;
553
+ letter-spacing: -0.01em;
554
+ }
555
+ .list-item span {
556
+ color: var(--subtle);
557
+ font-family: var(--font-mono);
558
+ font-size: 11px;
559
+ }
560
+
561
+ /* ---------- canvas ---------- */
562
+
563
+ .stage {
564
+ display: grid;
565
+ grid-template-rows: minmax(0, 1fr) auto;
566
+ min-width: 0;
567
+ overflow: hidden;
568
+ }
569
+
570
+ .viewport {
571
+ background:
572
+ radial-gradient(ellipse 70% 60% at 50% 40%, var(--viewport-glow), transparent 72%),
573
+ var(--viewport);
574
+ min-height: 0;
575
+ overflow: hidden;
576
+ position: relative;
577
+ }
578
+
579
+ .viewport-inner {
580
+ align-items: center;
581
+ display: flex;
582
+ inset: 24px;
583
+ justify-content: center;
584
+ position: absolute;
585
+ }
586
+
587
+ .frame {
588
+ border: 1px solid var(--border);
589
+ border-radius: var(--radius-lg);
590
+ box-shadow: var(--shadow);
591
+ overflow: hidden;
592
+ position: relative;
593
+ }
594
+ .frame[data-safe="true"]::after {
595
+ border: 1px dashed color-mix(in srgb, var(--foreground) 22%, transparent);
596
+ content: "";
597
+ inset: var(--safe-y) var(--safe-x);
598
+ pointer-events: none;
599
+ position: absolute;
600
+ }
601
+
602
+ .transport {
603
+ background: var(--panel);
604
+ border-top: 1px solid var(--border);
605
+ display: grid;
606
+ gap: 10px;
607
+ padding: 10px 14px 14px;
608
+ }
609
+
610
+ .transport-row {
611
+ align-items: center;
612
+ display: flex;
613
+ gap: 8px;
614
+ }
615
+
616
+ .timecode {
617
+ color: var(--muted);
618
+ font-family: var(--font-mono);
619
+ font-size: 12px;
620
+ font-variant-numeric: tabular-nums;
621
+ }
622
+ .timecode b {
623
+ color: var(--foreground);
624
+ font-weight: 500;
625
+ }
626
+
627
+ .timeline {
628
+ cursor: pointer;
629
+ display: grid;
630
+ gap: 4px;
631
+ position: relative;
632
+ user-select: none;
633
+ }
634
+ .ruler {
635
+ color: var(--subtle);
636
+ display: flex;
637
+ font-family: var(--font-mono);
638
+ font-size: 10px;
639
+ height: 12px;
640
+ justify-content: space-between;
641
+ }
642
+ .track {
643
+ background: var(--field);
644
+ border: 1px solid var(--border);
645
+ border-radius: var(--radius-sm);
646
+ display: flex;
647
+ gap: 2px;
648
+ height: 34px;
649
+ overflow: hidden;
650
+ padding: 2px;
651
+ position: relative;
652
+ }
653
+ .segment {
654
+ align-items: center;
655
+ background: var(--hover);
656
+ border: 1px solid transparent;
657
+ border-radius: 4px;
658
+ color: var(--subtle);
659
+ display: flex;
660
+ font-size: 11px;
661
+ min-width: 0;
662
+ overflow: hidden;
663
+ padding: 0 8px;
664
+ white-space: nowrap;
665
+ }
666
+ .segment[data-active="true"] {
667
+ background: var(--active);
668
+ border-color: var(--border-strong);
669
+ color: var(--foreground);
670
+ }
671
+ .playhead {
672
+ background: var(--foreground);
673
+ bottom: 0;
674
+ pointer-events: none;
675
+ position: absolute;
676
+ top: 0;
677
+ width: 1.5px;
678
+ }
679
+ .audio-track {
680
+ height: 20px;
681
+ position: relative;
682
+ }
683
+ .audio-track .cue {
684
+ align-items: center;
685
+ background: var(--hover);
686
+ border: 1px solid var(--border);
687
+ border-radius: 4px;
688
+ bottom: 0;
689
+ color: var(--subtle);
690
+ display: flex;
691
+ font-family: var(--font-mono);
692
+ font-size: 10px;
693
+ overflow: hidden;
694
+ padding: 0 6px;
695
+ position: absolute;
696
+ top: 0;
697
+ white-space: nowrap;
698
+ }
699
+ .audio-track .cue[data-active="true"] {
700
+ border-color: var(--border-strong);
701
+ color: var(--muted);
702
+ }
703
+ .audio-track .cue {
704
+ cursor: pointer;
705
+ }
706
+ /* Narrow cues keep their bar and hand the name to the space beside it. */
707
+ .audio-track .cue[data-label] {
708
+ overflow: visible;
709
+ padding: 0;
710
+ }
711
+ .audio-track .cue[data-label] > span {
712
+ position: absolute;
713
+ top: 50%;
714
+ transform: translateY(-50%);
715
+ }
716
+ .audio-track .cue[data-label="after"] > span {
717
+ left: calc(100% + 6px);
718
+ }
719
+ .audio-track .cue[data-label="before"] > span {
720
+ right: calc(100% + 6px);
721
+ }
722
+ .audio-track .cue[data-solo="off"] {
723
+ opacity: 0.4;
724
+ }
725
+ .audio-track .cue > span {
726
+ position: relative;
727
+ z-index: 1;
728
+ }
729
+
730
+ /* The waveform sits behind the label, filling the cue it belongs to. */
731
+ .waveform {
732
+ bottom: 0;
733
+ color: var(--subtle);
734
+ height: 100%;
735
+ left: 0;
736
+ position: absolute;
737
+ width: 100%;
738
+ }
739
+ .audio-track .cue[data-active="true"] .waveform {
740
+ color: var(--subtle);
741
+ }
742
+
743
+ .playhead::before {
744
+ background: var(--foreground);
745
+ border-radius: 2px;
746
+ content: "";
747
+ height: 6px;
748
+ left: -3px;
749
+ position: absolute;
750
+ top: -1px;
751
+ width: 7.5px;
752
+ }
753
+
754
+ /* ---------- inspector ---------- */
755
+
756
+ .facts {
757
+ display: grid;
758
+ gap: 6px;
759
+ margin: 0 0 6px;
760
+ }
761
+ .fact {
762
+ align-items: baseline;
763
+ display: grid;
764
+ gap: 10px;
765
+ grid-template-columns: 84px minmax(0, 1fr);
766
+ font-size: 12px;
767
+ }
768
+ .fact dt {
769
+ color: var(--subtle);
770
+ }
771
+ .fact dd {
772
+ color: var(--foreground);
773
+ font-family: var(--font-mono);
774
+ margin: 0;
775
+ overflow-wrap: anywhere;
776
+ }
777
+
778
+ .field {
779
+ display: grid;
780
+ gap: 5px;
781
+ margin-bottom: 10px;
782
+ }
783
+ .field label {
784
+ align-items: center;
785
+ color: var(--muted);
786
+ display: flex;
787
+ font-size: 12px;
788
+ gap: 6px;
789
+ justify-content: space-between;
790
+ }
791
+ .field .hint {
792
+ color: var(--subtle);
793
+ font-family: var(--font-mono);
794
+ font-size: 10px;
795
+ }
796
+
797
+ .issues {
798
+ display: grid;
799
+ gap: 6px;
800
+ list-style: none;
801
+ margin: 0;
802
+ padding: 0;
803
+ }
804
+ .issue {
805
+ border: 1px solid var(--border);
806
+ border-left-width: 2px;
807
+ border-radius: var(--radius-sm);
808
+ color: var(--muted);
809
+ font-size: 12px;
810
+ line-height: 1.5;
811
+ padding: 8px 10px;
812
+ }
813
+ .issue[data-level="error"] { border-left-color: var(--danger); }
814
+ .issue[data-level="warning"] { border-left-color: var(--warning); }
815
+
816
+ .hint {
817
+ color: var(--subtle);
818
+ font-size: 12px;
819
+ line-height: 1.6;
820
+ }
821
+ .hint code {
822
+ background: var(--panel);
823
+ border: 1px solid var(--border);
824
+ border-radius: 4px;
825
+ color: var(--muted);
826
+ font-family: var(--font-mono);
827
+ font-size: 11px;
828
+ padding: 1px 5px;
829
+ }
830
+
831
+ .status {
832
+ align-items: center;
833
+ border: 1px solid var(--border);
834
+ border-radius: var(--radius-sm);
835
+ display: flex;
836
+ font-family: var(--font-mono);
837
+ font-size: 11px;
838
+ gap: 8px;
839
+ justify-content: space-between;
840
+ margin-top: 10px;
841
+ padding: 8px 10px;
842
+ }
843
+ .progress {
844
+ background: var(--border);
845
+ border-radius: 999px;
846
+ height: 3px;
847
+ margin-top: 8px;
848
+ overflow: hidden;
849
+ }
850
+ .progress span {
851
+ background: var(--foreground);
852
+ display: block;
853
+ height: 100%;
854
+ transition: width 200ms ease;
855
+ }
856
+
857
+ /* ---------- gallery, brands, assets ---------- */
858
+
859
+ .gallery {
860
+ align-content: start;
861
+ display: grid;
862
+ gap: 16px;
863
+ grid-auto-rows: max-content;
864
+ grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
865
+ overflow-y: auto;
866
+ padding: 20px;
867
+ }
868
+ .card {
869
+ background: var(--panel);
870
+ border: 1px solid var(--border);
871
+ border-radius: var(--radius-lg);
872
+ cursor: pointer;
873
+ overflow: hidden;
874
+ transition: background 140ms ease, border-color 140ms ease;
875
+ }
876
+ /* Hover changes the surface, never the position. Moving the card transforms
877
+ it, which promotes the playing composition inside to its own compositing
878
+ layer and drops the rounded clip, so the thumbnail spills past the corners. */
879
+ .card:hover {
880
+ background: var(--hover);
881
+ border-color: var(--border-strong);
882
+ }
883
+ .card[data-active="true"] {
884
+ border-color: var(--border-strong);
885
+ }
886
+ .card-canvas {
887
+ aspect-ratio: 16 / 9;
888
+ background: #000;
889
+ border-bottom: 1px solid var(--border);
890
+ /* The composition inside is transformed, which promotes it to its own
891
+ compositing layer; a rounded clip on an ancestor alone stops applying to
892
+ it, so the canvas carries the top radii and its own paint containment. */
893
+ border-radius: calc(var(--radius-lg) - 1px) calc(var(--radius-lg) - 1px) 0 0;
894
+ contain: paint;
895
+ overflow: hidden;
896
+ position: relative;
897
+ }
898
+ .card-meta {
899
+ display: grid;
900
+ gap: 3px;
901
+ padding: 10px 12px 12px;
902
+ }
903
+ .card-meta strong {
904
+ font-size: 13px;
905
+ font-weight: 500;
906
+ letter-spacing: -0.01em;
907
+ }
908
+ .card-meta span {
909
+ color: var(--subtle);
910
+ font-family: var(--font-mono);
911
+ font-size: 11px;
912
+ }
913
+
914
+ .swatches {
915
+ display: grid;
916
+ gap: 6px;
917
+ margin-bottom: 14px;
918
+ }
919
+ .swatches .swatch {
920
+ align-items: center;
921
+ display: grid;
922
+ font-size: 12px;
923
+ gap: 10px;
924
+ grid-template-columns: 18px 84px 1fr;
925
+ }
926
+ .swatches .swatch .chip {
927
+ border: 1px solid var(--border);
928
+ border-radius: 5px;
929
+ height: 18px;
930
+ width: 18px;
931
+ }
932
+ .swatches .swatch code {
933
+ color: var(--subtle);
934
+ font-family: var(--font-mono);
935
+ font-size: 11px;
936
+ }
937
+
938
+ .assets-view {
939
+ display: grid;
940
+ gap: 26px;
941
+ overflow-y: auto;
942
+ padding: 24px;
943
+ }
944
+ .asset-grid {
945
+ display: grid;
946
+ gap: 14px;
947
+ grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
948
+ list-style: none;
949
+ margin: 0;
950
+ padding: 0;
951
+ }
952
+ .asset-grid li,
953
+ .asset-list li {
954
+ display: grid;
955
+ gap: 3px;
956
+ font-size: 12px;
957
+ }
958
+ /* The reference a cue would use, beside the file it resolves to. */
959
+ .asset-list .reference {
960
+ color: var(--subtle);
961
+ font-family: var(--font-mono);
962
+ font-size: 11px;
963
+ font-weight: 400;
964
+ margin-left: 8px;
965
+ }
966
+
967
+ /* A section that is usually closed: the summary reads like a section title. */
968
+ .input.filter {
969
+ margin: 6px 0 10px;
970
+ width: 100%;
971
+ }
972
+
973
+ .disclosure {
974
+ margin-top: 14px;
975
+ }
976
+ .disclosure > summary {
977
+ align-items: center;
978
+ color: var(--subtle);
979
+ cursor: pointer;
980
+ display: flex;
981
+ font-size: 11px;
982
+ gap: 6px;
983
+ letter-spacing: 0.08em;
984
+ list-style: none;
985
+ text-transform: uppercase;
986
+ user-select: none;
987
+ }
988
+ .disclosure > summary::-webkit-details-marker {
989
+ display: none;
990
+ }
991
+ .disclosure > summary:hover {
992
+ color: var(--muted);
993
+ }
994
+ .disclosure > summary svg {
995
+ transition: transform 120ms ease;
996
+ }
997
+ .disclosure[open] > summary svg {
998
+ transform: rotate(180deg);
999
+ }
1000
+ .disclosure-meta {
1001
+ font-family: var(--font-mono);
1002
+ letter-spacing: 0;
1003
+ margin-left: auto;
1004
+ text-transform: none;
1005
+ }
1006
+ .disclosure > .issues {
1007
+ margin-top: 8px;
1008
+ }
1009
+
1010
+ .clip {
1011
+ align-items: center;
1012
+ display: flex;
1013
+ gap: 8px;
1014
+ width: 100%;
1015
+ }
1016
+ .clip-shape {
1017
+ flex: 1;
1018
+ height: 26px;
1019
+ min-width: 60px;
1020
+ position: relative;
1021
+ }
1022
+ .clip-length {
1023
+ color: var(--subtle);
1024
+ font-family: var(--font-mono);
1025
+ font-size: 11px;
1026
+ }
1027
+
1028
+ /* A row that leads with a control keeps the control beside its label. */
1029
+ .asset-list li:has(> .button),
1030
+ .asset-list li:has(> .clip) {
1031
+ align-items: center;
1032
+ grid-template-columns: auto 1fr;
1033
+ gap: 10px;
1034
+ }
1035
+ .asset-file {
1036
+ align-items: center;
1037
+ display: flex;
1038
+ justify-content: center;
1039
+ }
1040
+ .asset-grid img,
1041
+ .asset-file {
1042
+ aspect-ratio: 16 / 9;
1043
+ background: var(--panel);
1044
+ border: 1px solid var(--border);
1045
+ border-radius: var(--radius);
1046
+ object-fit: contain;
1047
+ width: 100%;
1048
+ }
1049
+ .asset-list {
1050
+ display: grid;
1051
+ gap: 8px;
1052
+ list-style: none;
1053
+ margin: 0;
1054
+ padding: 0;
1055
+ }
1056
+ .asset-list code,
1057
+ .asset-grid code {
1058
+ color: var(--subtle);
1059
+ font-family: var(--font-mono);
1060
+ font-size: 11px;
1061
+ }
1062
+
1063
+ .empty {
1064
+ align-content: center;
1065
+ display: grid;
1066
+ gap: 10px;
1067
+ grid-column: 1 / -1;
1068
+ justify-items: center;
1069
+ padding: 60px;
1070
+ text-align: center;
1071
+ }
1072
+ .empty h2 {
1073
+ font-size: 15px;
1074
+ font-weight: 500;
1075
+ letter-spacing: -0.02em;
1076
+ margin: 0;
1077
+ text-transform: none;
1078
+ color: var(--foreground);
1079
+ }
1080
+
1081
+ /* ---------- home ---------- */
1082
+
1083
+ .home {
1084
+ display: grid;
1085
+ gap: 32px;
1086
+ overflow-y: auto;
1087
+ /* The scroll container spans the window so its scrollbar sits at the window
1088
+ edge; the content is centered by padding rather than by a narrower box. */
1089
+ padding: 22px max(24px, calc((100% - 1100px) / 2)) 44px;
1090
+ width: 100%;
1091
+ }
1092
+ .home .gallery {
1093
+ padding: 0;
1094
+ }
1095
+ .home-swatches {
1096
+ display: grid;
1097
+ gap: 12px;
1098
+ grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
1099
+ }
1100
+ .brand-card {
1101
+ background: transparent;
1102
+ border: 1px solid var(--border);
1103
+ border-radius: var(--radius);
1104
+ cursor: pointer;
1105
+ display: grid;
1106
+ gap: 10px;
1107
+ grid-template-columns: minmax(0, 1fr);
1108
+ overflow: hidden;
1109
+ padding: 0 0 10px;
1110
+ text-align: left;
1111
+ }
1112
+ .brand-card:hover {
1113
+ border-color: var(--border-strong);
1114
+ }
1115
+ .swatch-colors {
1116
+ display: flex;
1117
+ height: 56px;
1118
+ }
1119
+ .swatch-colors span {
1120
+ flex: 1;
1121
+ }
1122
+ .brand-card .card-meta {
1123
+ padding: 0 12px;
1124
+ }
1125
+ .home-more {
1126
+ background: transparent;
1127
+ border: 1px solid var(--border);
1128
+ border-radius: var(--radius);
1129
+ color: var(--muted);
1130
+ cursor: pointer;
1131
+ font-family: var(--font-mono);
1132
+ font-size: 11px;
1133
+ margin-top: 10px;
1134
+ padding: 9px 12px;
1135
+ text-align: left;
1136
+ width: 100%;
1137
+ }
1138
+ .home-more:hover {
1139
+ border-color: var(--border-strong);
1140
+ color: var(--foreground);
1141
+ }
1142
+
1143
+ .statusbar {
1144
+ align-items: center;
1145
+ background: var(--panel);
1146
+ border-top: 1px solid var(--border);
1147
+ color: var(--subtle);
1148
+ display: flex;
1149
+ font-family: var(--font-mono);
1150
+ font-size: 11px;
1151
+ gap: 16px;
1152
+ padding: 0 14px;
1153
+ }
1154
+ .statusbar .spacer {
1155
+ margin-left: auto;
1156
+ }
1157
+ .statusbar-link {
1158
+ align-items: center;
1159
+ color: var(--subtle);
1160
+ display: inline-flex;
1161
+ gap: 5px;
1162
+ text-decoration: none;
1163
+ }
1164
+ .statusbar-link:hover {
1165
+ color: var(--foreground);
1166
+ }
1167
+ .statusbar-link svg {
1168
+ height: 11px;
1169
+ width: 11px;
1170
+ }
1171
+
1172
+ /* ---------- command palette ---------- */
1173
+
1174
+ .overlay {
1175
+ align-items: flex-start;
1176
+ background: var(--overlay);
1177
+ display: flex;
1178
+ inset: 0;
1179
+ justify-content: center;
1180
+ padding-top: 14vh;
1181
+ position: fixed;
1182
+ z-index: 50;
1183
+ }
1184
+ .palette {
1185
+ background: var(--elevated);
1186
+ border: 1px solid var(--border-strong);
1187
+ border-radius: var(--radius-lg);
1188
+ box-shadow: var(--shadow);
1189
+ overflow: hidden;
1190
+ width: min(560px, 92vw);
1191
+ }
1192
+ .palette input {
1193
+ background: transparent;
1194
+ border: none;
1195
+ border-bottom: 1px solid var(--border);
1196
+ border-radius: 0;
1197
+ height: 46px;
1198
+ padding: 0 16px;
1199
+ }
1200
+ .palette ul {
1201
+ list-style: none;
1202
+ margin: 0;
1203
+ max-height: 320px;
1204
+ overflow-y: auto;
1205
+ padding: 6px;
1206
+ }
1207
+ .palette li button {
1208
+ align-items: center;
1209
+ background: transparent;
1210
+ border: none;
1211
+ border-radius: var(--radius-sm);
1212
+ color: var(--muted);
1213
+ cursor: pointer;
1214
+ display: flex;
1215
+ font: inherit;
1216
+ font-size: 13px;
1217
+ gap: 10px;
1218
+ padding: 9px 10px;
1219
+ text-align: left;
1220
+ width: 100%;
1221
+ }
1222
+ .palette li button[data-active="true"] {
1223
+ background: var(--active);
1224
+ color: var(--foreground);
1225
+ }
1226
+ .palette .type {
1227
+ color: var(--subtle);
1228
+ font-family: var(--font-mono);
1229
+ font-size: 10px;
1230
+ margin-left: auto;
1231
+ text-transform: uppercase;
1232
+ }