privateboard 0.1.37 → 0.1.40

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 (76) hide show
  1. package/dist/boot.js +1415 -91
  2. package/dist/boot.js.map +1 -1
  3. package/dist/cli.js +1415 -91
  4. package/dist/cli.js.map +1 -1
  5. package/dist/server.js +1271 -81
  6. package/dist/server.js.map +1 -1
  7. package/dist/version.d.ts +1 -1
  8. package/dist/version.js +1 -1
  9. package/dist/version.js.map +1 -1
  10. package/package.json +1 -1
  11. package/public/__avatar3d_test.html +156 -0
  12. package/public/adjourn-overlay.css +2 -2
  13. package/public/agent-overlay.css +27 -15
  14. package/public/agent-overlay.js +3 -1
  15. package/public/agent-profile.css +331 -41
  16. package/public/agent-profile.js +499 -75
  17. package/public/app-updater.css +1 -1
  18. package/public/app.js +2090 -547
  19. package/public/avatar-3d-snap.js +205 -0
  20. package/public/avatar-3d.js +792 -0
  21. package/public/avatar-customizer.html +274 -0
  22. package/public/avatar3d-editor.css +240 -0
  23. package/public/avatar3d-editor.js +481 -0
  24. package/public/avatars/3d/chair.png +0 -0
  25. package/public/avatars/3d/first-principles.png +0 -0
  26. package/public/avatars/3d/historian.png +0 -0
  27. package/public/avatars/3d/long-horizon.png +0 -0
  28. package/public/avatars/3d/phenomenologist.png +0 -0
  29. package/public/avatars/3d/socrates.png +0 -0
  30. package/public/avatars/3d/user-empathy.png +0 -0
  31. package/public/avatars/3d/value-investor.png +0 -0
  32. package/public/core-avatars.js +86 -0
  33. package/public/home-3d-loader.js +15 -4
  34. package/public/home-3d-mock.js +18 -7
  35. package/public/home.html +80 -18
  36. package/public/i18n.js +279 -4
  37. package/public/icons/avatar_1779855104027.glb +0 -0
  38. package/public/icons/logo.png +0 -0
  39. package/public/icons/new-style.glb +0 -0
  40. package/public/icons/new-style2.glb +0 -0
  41. package/public/icons/new-style3.glb +0 -0
  42. package/public/icons/new-style4.glb +0 -0
  43. package/public/icons/new-style5.glb +0 -0
  44. package/public/icons/office.glb +0 -0
  45. package/public/icons/stuff.glb +0 -0
  46. package/public/index.html +203 -182
  47. package/public/mention-picker.js +1 -1
  48. package/public/new-agent.css +7 -7
  49. package/public/new-agent.js +46 -20
  50. package/public/office-viewer.html +340 -0
  51. package/public/onboarding.css +5 -5
  52. package/public/quote-cta.css +5 -4
  53. package/public/quote-cta.js +50 -5
  54. package/public/room-settings.css +24 -9
  55. package/public/stuff-viewer.html +330 -0
  56. package/public/thread.css +1211 -0
  57. package/public/user-settings.css +16 -19
  58. package/public/user-settings.js +86 -78
  59. package/public/vendor/BufferGeometryUtils.js +1434 -0
  60. package/public/vendor/DRACOLoader.js +739 -0
  61. package/public/vendor/GLTFLoader.js +4860 -0
  62. package/public/vendor/RoomEnvironment.js +185 -0
  63. package/public/vendor/SkeletonUtils.js +496 -0
  64. package/public/vendor/draco/draco_decoder.js +34 -0
  65. package/public/vendor/draco/draco_decoder.wasm +0 -0
  66. package/public/vendor/draco/draco_encoder.js +33 -0
  67. package/public/vendor/draco/draco_wasm_wrapper.js +117 -0
  68. package/public/vendor/meshopt_decoder.module.js +196 -0
  69. package/public/voice-3d-banner.js +12 -0
  70. package/public/voice-3d.js +1407 -432
  71. package/public/voice-clone.css +875 -0
  72. package/public/voice-clone.js +1351 -0
  73. package/public/voice-replay.css +3 -3
  74. package/public/voice-replay.js +21 -0
  75. package/public/avatar-skill.js +0 -629
  76. package/public/icons/folded-sidebar.png +0 -0
@@ -0,0 +1,875 @@
1
+ /* ═══════════════════════════════════════════════════════════════════
2
+ voice-clone.css · Director voice cloning overlay + minimized pill
3
+ ═══════════════════════════════════════════════════════════════════
4
+
5
+ Chrome (overlay shell, panel, head, classification strip, foot) is
6
+ a 1:1 mirror of `.room-settings-overlay` (see room-settings.css) so
7
+ the two modals read as the same product family — same backdrop
8
+ blur, same lime corner brackets on the panel, same mono kicker
9
+ strip up top, same ▸ lime arrow prefix on the title, same close
10
+ button geometry. Body / source-picker / progress / trim controls
11
+ keep their voice-clone-specific layout but inherit the shared
12
+ typography (mono font, lime accents).
13
+
14
+ Two surfaces share state:
15
+ 1. `.vc-overlay` — full-screen modal (z-index 950, same as room-
16
+ settings; both sit beneath higher-z modals like adjourn).
17
+ 2. `.vc-pill` — right-bottom floating chip while the modal is
18
+ minimized. z-index 940 (above main chat, below modals).
19
+ ═══════════════════════════════════════════════════════════════════ */
20
+
21
+ /* ── Overlay shell · `.room-settings-overlay` clone ──────────────── */
22
+ .vc-overlay {
23
+ position: fixed;
24
+ inset: 0;
25
+ background: rgba(0, 0, 0, 0.78);
26
+ -webkit-backdrop-filter: blur(4px);
27
+ backdrop-filter: blur(4px);
28
+ z-index: 950;
29
+ display: none;
30
+ align-items: center;
31
+ justify-content: center;
32
+ padding: 24px;
33
+ overflow: hidden;
34
+ font-family: var(--mono, "Inter", system-ui, sans-serif);
35
+ animation: vc-fade 0.14s ease-out;
36
+ }
37
+ .vc-overlay.is-open { display: flex; }
38
+ .vc-overlay.is-collapsed { display: none; }
39
+ /* Electron macOS drag-region carve-out · mirror room-settings'
40
+ no-drag claim so close / minimize buttons aren't eaten by the
41
+ OS-level drag handler. */
42
+ html.is-electron-mac .vc-overlay,
43
+ html.is-electron-mac .vc-overlay * { -webkit-app-region: no-drag; }
44
+ @keyframes vc-fade { from { opacity: 0; } to { opacity: 1; } }
45
+ @keyframes vc-rise { from { transform: translateY(10px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }
46
+
47
+ .vc-backdrop {
48
+ position: absolute;
49
+ inset: 0;
50
+ /* No frosted blur here — the overlay itself does the blur, like
51
+ room-settings. Backdrop is just the click-to-dismiss target. */
52
+ background: transparent;
53
+ }
54
+ .vc-panel {
55
+ position: relative;
56
+ z-index: 1;
57
+ width: 100%;
58
+ max-width: 560px;
59
+ max-height: calc(100vh - 48px);
60
+ background: var(--panel);
61
+ border: 0.5px solid var(--line-strong);
62
+ color: var(--text);
63
+ display: flex;
64
+ flex-direction: column;
65
+ min-height: 0;
66
+ animation: vc-rise 0.18s ease-out;
67
+ }
68
+ .vc-panel > .vc-classification,
69
+ .vc-panel > .vc-head,
70
+ .vc-panel > .vc-foot { flex: 0 0 auto; }
71
+ .vc-panel > .vc-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
72
+ /* Lime corner brackets · the room-settings signature. Drawn with
73
+ 1.5px borders on opposite corners so the L-shape reads "this is a
74
+ live boardroom modal" the moment the user sees it. */
75
+ .vc-panel::before,
76
+ .vc-panel::after {
77
+ content: "";
78
+ position: absolute;
79
+ width: 10px;
80
+ height: 10px;
81
+ border: 1.5px solid var(--lime);
82
+ pointer-events: none;
83
+ }
84
+ .vc-panel::before { top: -1px; left: -1px; border-right: none; border-bottom: none; }
85
+ .vc-panel::after { bottom: -1px; right: -1px; border-left: none; border-top: none; }
86
+
87
+ /* ── Classification strip · lime kicker + right meta ───────────── */
88
+ .vc-classification {
89
+ background: var(--strip-bg);
90
+ border-bottom: 0.5px solid var(--line-bright);
91
+ padding: 5px 14px;
92
+ font-size: 8px;
93
+ letter-spacing: 0.22em;
94
+ text-transform: uppercase;
95
+ color: var(--lime);
96
+ font-weight: 700;
97
+ display: flex;
98
+ justify-content: space-between;
99
+ align-items: center;
100
+ }
101
+ .vc-classification .right {
102
+ color: var(--text-faint);
103
+ letter-spacing: 0.12em;
104
+ }
105
+
106
+ /* ── Head · meta + title (▸ prefix) + controls ─────────────────── */
107
+ .vc-head {
108
+ display: grid;
109
+ grid-template-columns: 1fr auto;
110
+ gap: 12px;
111
+ align-items: start;
112
+ padding: 14px 16px 12px;
113
+ background: var(--strip-bg);
114
+ border-bottom: 0.5px dashed var(--line-bright);
115
+ }
116
+ .vc-head .meta {
117
+ font-family: var(--mono);
118
+ font-size: 9px;
119
+ color: var(--text-dim);
120
+ text-transform: uppercase;
121
+ letter-spacing: 0.18em;
122
+ margin-bottom: 4px;
123
+ font-weight: 700;
124
+ }
125
+ .vc-head .title {
126
+ font-size: 14px;
127
+ font-weight: 700;
128
+ color: var(--text);
129
+ letter-spacing: -0.01em;
130
+ line-height: 1.3;
131
+ }
132
+ .vc-head .title::before {
133
+ content: "▸ ";
134
+ color: var(--lime);
135
+ }
136
+ .vc-head-controls {
137
+ display: inline-flex;
138
+ align-items: center;
139
+ gap: 6px;
140
+ }
141
+ .vc-head-btn {
142
+ width: 24px;
143
+ height: 24px;
144
+ background: transparent;
145
+ border: 0.5px solid var(--line-bright);
146
+ color: var(--text-dim);
147
+ font-size: 12px;
148
+ cursor: pointer;
149
+ font-family: var(--mono);
150
+ border-radius: 3px;
151
+ transition: all 0.12s;
152
+ display: inline-flex;
153
+ align-items: center;
154
+ justify-content: center;
155
+ padding: 0;
156
+ }
157
+ .vc-head-btn::before {
158
+ content: "";
159
+ width: 12px;
160
+ height: 12px;
161
+ background-color: currentColor;
162
+ -webkit-mask-image: var(--icon, none);
163
+ mask-image: var(--icon, none);
164
+ -webkit-mask-repeat: no-repeat;
165
+ mask-repeat: no-repeat;
166
+ -webkit-mask-position: center;
167
+ mask-position: center;
168
+ -webkit-mask-size: 12px 12px;
169
+ mask-size: 12px 12px;
170
+ }
171
+ .vc-head-btn:hover {
172
+ border-color: var(--lime);
173
+ color: var(--lime);
174
+ }
175
+ [data-vc-minimize] {
176
+ --icon: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M5 12h14'/></svg>");
177
+ }
178
+ [data-vc-close] {
179
+ --icon: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M18 6 6 18'/><path d='m6 6 12 12'/></svg>");
180
+ }
181
+
182
+ /* ── Body / picker stage ──────────────────────────────────────────── */
183
+ .vc-body {
184
+ padding: 14px 16px 4px;
185
+ }
186
+ .vc-section-label {
187
+ font-family: var(--mono);
188
+ font-size: 10px;
189
+ font-weight: 700;
190
+ letter-spacing: 0.16em;
191
+ text-transform: uppercase;
192
+ color: var(--text-faint);
193
+ margin: 0 0 8px;
194
+ }
195
+ .vc-source-input {
196
+ margin-bottom: 14px;
197
+ }
198
+ .vc-source-input[hidden] { display: none; }
199
+ .vc-file-pick {
200
+ display: flex;
201
+ align-items: center;
202
+ gap: 10px;
203
+ padding: 12px 14px;
204
+ background: var(--panel-2);
205
+ border: 0.5px dashed var(--line-strong);
206
+ border-radius: 6px;
207
+ color: var(--text-soft);
208
+ cursor: pointer;
209
+ font-size: 14px;
210
+ }
211
+ .vc-file-pick:hover { border-color: var(--lime); color: var(--text); }
212
+ .vc-file-pick.has-file {
213
+ border-style: solid;
214
+ color: var(--text);
215
+ }
216
+ .vc-file-pick svg { width: 16px; height: 16px; flex-shrink: 0; }
217
+ .vc-file-name {
218
+ flex: 1;
219
+ min-width: 0;
220
+ overflow: hidden;
221
+ text-overflow: ellipsis;
222
+ white-space: nowrap;
223
+ }
224
+ /* ── File-trim panel · dual-handle range slider over a thin
225
+ track. The two `<input type=range>` overlap; CSS hides their
226
+ default fill and only paints the thumbs. The `.vc-trim-track-
227
+ fill` div renders the selected band between handles. */
228
+ .vc-trim {
229
+ margin-top: 12px;
230
+ }
231
+ .vc-trim-status {
232
+ font-family: var(--mono);
233
+ font-size: 11px;
234
+ color: var(--text-faint);
235
+ letter-spacing: 0.04em;
236
+ margin-bottom: 8px;
237
+ }
238
+ .vc-trim-track {
239
+ position: relative;
240
+ height: 28px;
241
+ margin: 4px 4px 6px;
242
+ }
243
+ .vc-trim-track::before {
244
+ content: "";
245
+ position: absolute;
246
+ left: 0;
247
+ right: 0;
248
+ top: 50%;
249
+ transform: translateY(-50%);
250
+ height: 4px;
251
+ background: var(--panel-3);
252
+ border-radius: 2px;
253
+ pointer-events: none;
254
+ }
255
+ .vc-trim-track-fill {
256
+ position: absolute;
257
+ top: 50%;
258
+ transform: translateY(-50%);
259
+ height: 4px;
260
+ background: var(--lime);
261
+ border-radius: 2px;
262
+ left: 0;
263
+ right: 0;
264
+ pointer-events: none;
265
+ }
266
+ .vc-trim-range {
267
+ position: absolute;
268
+ top: 0;
269
+ left: 0;
270
+ right: 0;
271
+ width: 100%;
272
+ height: 28px;
273
+ margin: 0;
274
+ background: transparent;
275
+ -webkit-appearance: none;
276
+ appearance: none;
277
+ pointer-events: none;
278
+ }
279
+ .vc-trim-range::-webkit-slider-runnable-track {
280
+ height: 28px;
281
+ background: transparent;
282
+ }
283
+ .vc-trim-range::-webkit-slider-thumb {
284
+ -webkit-appearance: none;
285
+ appearance: none;
286
+ width: 16px;
287
+ height: 16px;
288
+ border-radius: 50%;
289
+ background: var(--lime);
290
+ border: 2px solid var(--panel);
291
+ cursor: pointer;
292
+ pointer-events: auto;
293
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
294
+ margin-top: 6px;
295
+ }
296
+ .vc-trim-range::-moz-range-thumb {
297
+ width: 16px;
298
+ height: 16px;
299
+ border-radius: 50%;
300
+ background: var(--lime);
301
+ border: 2px solid var(--panel);
302
+ cursor: pointer;
303
+ pointer-events: auto;
304
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
305
+ }
306
+ .vc-trim-range::-moz-range-track {
307
+ background: transparent;
308
+ height: 28px;
309
+ }
310
+ .vc-trim-meta {
311
+ display: flex;
312
+ align-items: baseline;
313
+ gap: 8px;
314
+ font-family: var(--mono);
315
+ font-size: 11px;
316
+ color: var(--text);
317
+ letter-spacing: 0.04em;
318
+ }
319
+ .vc-trim-meta-sep { color: var(--text-faint); }
320
+ .vc-trim-meta-dur { color: var(--text-faint); margin-left: auto; font-size: 10px; }
321
+
322
+ /* ── Source mode tabs · "Upload file" / "Record voice" ────── */
323
+ .vc-source-modes {
324
+ display: grid;
325
+ grid-template-columns: 1fr 1fr;
326
+ gap: 8px;
327
+ margin-bottom: 12px;
328
+ }
329
+ .vc-source-mode {
330
+ display: inline-flex;
331
+ align-items: center;
332
+ justify-content: center;
333
+ gap: 8px;
334
+ padding: 9px 10px;
335
+ background: var(--panel-2);
336
+ border: 0.5px solid var(--line-bright);
337
+ border-radius: 4px;
338
+ color: var(--text-soft);
339
+ font-family: var(--mono);
340
+ font-size: 11px;
341
+ letter-spacing: 0.12em;
342
+ text-transform: uppercase;
343
+ cursor: pointer;
344
+ transition: background 0.14s, color 0.14s, border-color 0.14s;
345
+ }
346
+ .vc-source-mode svg { width: 14px; height: 14px; flex-shrink: 0; }
347
+ .vc-source-mode:hover { color: var(--text); border-color: color-mix(in srgb, var(--lime) 30%, var(--line-bright)); }
348
+ .vc-source-mode.is-active {
349
+ background: color-mix(in srgb, var(--lime) 12%, var(--panel-2));
350
+ border-color: var(--lime);
351
+ color: var(--lime);
352
+ }
353
+
354
+ /* ── Record-mode pane · script + circular rec button + level meter ── */
355
+ .vc-record-script {
356
+ margin: 0 0 6px;
357
+ font-family: var(--mono);
358
+ font-size: 11px;
359
+ color: var(--text-faint);
360
+ letter-spacing: 0.02em;
361
+ line-height: 1.55;
362
+ }
363
+ /* Reading-script block · differentiates via TYPOGRAPHY (serif italic
364
+ + larger size + lime mono kicker via ::before) plus a hairline
365
+ top rule. Per CLAUDE.md "No coloured `border-left` callouts" we
366
+ do NOT paint a vertical accent strip — typography + top rule is
367
+ the canonical callout vocabulary for this codebase. */
368
+ .vc-record-script-text {
369
+ margin: 0 0 14px;
370
+ padding: 12px 0 0;
371
+ border-top: 0.5px solid var(--line-bright);
372
+ font-family: var(--font-human, var(--serif, var(--sans)));
373
+ font-style: italic;
374
+ font-size: 16px;
375
+ line-height: 1.7;
376
+ color: var(--text);
377
+ }
378
+ .vc-record-script-text::before {
379
+ content: "// script";
380
+ display: block;
381
+ margin-bottom: 6px;
382
+ font-family: var(--mono);
383
+ font-style: normal;
384
+ font-size: 10px;
385
+ font-weight: 700;
386
+ letter-spacing: 0.18em;
387
+ text-transform: uppercase;
388
+ color: var(--lime);
389
+ }
390
+ .vc-record-stage {
391
+ display: grid;
392
+ grid-template-columns: auto 1fr;
393
+ grid-template-areas: "btn meta" "btn level";
394
+ column-gap: 16px;
395
+ row-gap: 8px;
396
+ align-items: center;
397
+ padding: 16px;
398
+ background: var(--panel-2);
399
+ border: 0.5px solid var(--line-bright);
400
+ border-radius: 6px;
401
+ }
402
+ .vc-record-btn {
403
+ grid-area: btn;
404
+ position: relative;
405
+ width: 56px;
406
+ height: 56px;
407
+ border-radius: 50%;
408
+ background: color-mix(in srgb, var(--lime) 16%, var(--panel-3));
409
+ border: 0.5px solid color-mix(in srgb, var(--lime) 45%, var(--line-bright));
410
+ color: var(--lime);
411
+ cursor: pointer;
412
+ display: inline-flex;
413
+ align-items: center;
414
+ justify-content: center;
415
+ transition: background 0.18s, border-color 0.18s, transform 0.12s;
416
+ }
417
+ .vc-record-btn:hover { transform: scale(1.04); border-color: var(--lime); }
418
+ .vc-record-glyph { font-size: 22px; line-height: 1; pointer-events: none; }
419
+ .vc-record-ring {
420
+ position: absolute;
421
+ inset: -6px;
422
+ border-radius: 50%;
423
+ border: 1px solid color-mix(in srgb, var(--red, #B5706A) 65%, transparent);
424
+ opacity: 0;
425
+ pointer-events: none;
426
+ }
427
+ .vc-record-btn.is-recording {
428
+ background: color-mix(in srgb, var(--red, #B5706A) 28%, var(--panel-3));
429
+ border-color: var(--red, #B5706A);
430
+ color: var(--red, #B5706A);
431
+ }
432
+ .vc-record-btn.is-recording .vc-record-ring {
433
+ opacity: 1;
434
+ animation: vc-record-pulse 1.2s ease-in-out infinite;
435
+ }
436
+ @keyframes vc-record-pulse {
437
+ 0%, 100% { transform: scale(1); opacity: 0.7; }
438
+ 50% { transform: scale(1.18); opacity: 0; }
439
+ }
440
+ .vc-record-meta {
441
+ grid-area: meta;
442
+ display: flex;
443
+ align-items: baseline;
444
+ gap: 10px;
445
+ }
446
+ .vc-record-time {
447
+ font-family: var(--mono);
448
+ font-size: 18px;
449
+ font-weight: 600;
450
+ color: var(--text);
451
+ font-variant-numeric: tabular-nums;
452
+ letter-spacing: 0.04em;
453
+ }
454
+ .vc-record-state {
455
+ font-family: var(--mono);
456
+ font-size: 11px;
457
+ color: var(--text-faint);
458
+ letter-spacing: 0.04em;
459
+ }
460
+ .vc-record-level {
461
+ grid-area: level;
462
+ display: flex;
463
+ align-items: flex-end;
464
+ justify-content: stretch;
465
+ gap: 3px;
466
+ height: 24px;
467
+ }
468
+ .vc-record-level i {
469
+ flex: 1;
470
+ display: block;
471
+ height: 12%;
472
+ background: color-mix(in srgb, var(--lime) 55%, var(--panel-3));
473
+ border-radius: 1px;
474
+ transition: height 0.06s linear, background 0.18s;
475
+ }
476
+ .vc-record-btn.is-recording ~ .vc-record-meta + .vc-record-level i,
477
+ .vc-record-stage:has(.vc-record-btn.is-recording) .vc-record-level i {
478
+ background: var(--lime);
479
+ }
480
+ .vc-record-actions {
481
+ display: flex;
482
+ gap: 8px;
483
+ margin-top: 10px;
484
+ }
485
+ .vc-record-actions .vc-btn { flex: 1; }
486
+
487
+ /* File-hint micro-row · sits under the file picker and explains
488
+ what we accept (audio + video containers). */
489
+ .vc-file-hint {
490
+ margin: 6px 2px 0;
491
+ font-family: var(--mono);
492
+ font-size: 10px;
493
+ color: var(--text-faint);
494
+ letter-spacing: 0.02em;
495
+ line-height: 1.55;
496
+ }
497
+
498
+ .vc-label-input {
499
+ margin-top: 6px;
500
+ }
501
+ .vc-label-input input {
502
+ width: 100%;
503
+ padding: 9px 12px;
504
+ background: var(--panel-2);
505
+ border: 0.5px solid var(--line-strong);
506
+ border-radius: 6px;
507
+ color: var(--text);
508
+ font-family: var(--sans);
509
+ font-size: 14px;
510
+ outline: none;
511
+ }
512
+ .vc-label-input input:focus { border-color: var(--lime); }
513
+ .vc-hint {
514
+ font-family: var(--mono);
515
+ font-size: 11px;
516
+ color: var(--text-faint);
517
+ margin-top: 8px;
518
+ line-height: 1.55;
519
+ }
520
+ .vc-hint strong { color: var(--text-soft); font-weight: 600; }
521
+
522
+ /* ── Progress stage (replaces .vc-body content during clone) ──── */
523
+ .vc-progress { padding: 20px; }
524
+ .vc-progress[hidden] { display: none; }
525
+ .vc-stage-row {
526
+ display: flex;
527
+ flex-direction: column;
528
+ gap: 16px;
529
+ margin-bottom: 18px;
530
+ }
531
+ .vc-step {
532
+ display: grid;
533
+ grid-template-columns: 24px 1fr auto;
534
+ align-items: center;
535
+ column-gap: 12px;
536
+ row-gap: 6px;
537
+ }
538
+ .vc-step-num {
539
+ width: 24px;
540
+ height: 24px;
541
+ border-radius: 50%;
542
+ display: inline-flex;
543
+ align-items: center;
544
+ justify-content: center;
545
+ background: var(--panel-2);
546
+ border: 0.5px solid var(--line-strong);
547
+ font-family: var(--mono);
548
+ font-size: 11px;
549
+ font-weight: 700;
550
+ color: var(--text-faint);
551
+ grid-row: 1 / span 2;
552
+ }
553
+ .vc-step-label {
554
+ font-family: var(--sans);
555
+ font-size: 14px;
556
+ color: var(--text-faint);
557
+ }
558
+ .vc-step-pct {
559
+ font-family: var(--mono);
560
+ font-size: 12px;
561
+ color: var(--text-faint);
562
+ font-weight: 600;
563
+ letter-spacing: 0.04em;
564
+ }
565
+ .vc-step-bar {
566
+ grid-column: 2 / span 2;
567
+ height: 4px;
568
+ background: var(--panel-2);
569
+ border-radius: 2px;
570
+ overflow: hidden;
571
+ position: relative;
572
+ }
573
+ .vc-step-bar-fill {
574
+ position: absolute;
575
+ inset: 0;
576
+ width: 0%;
577
+ background: var(--lime);
578
+ transition: width 0.18s ease;
579
+ }
580
+ .vc-step.is-active .vc-step-num {
581
+ background: var(--lime);
582
+ color: var(--bg);
583
+ border-color: var(--lime);
584
+ }
585
+ .vc-step.is-active .vc-step-label { color: var(--text); font-weight: 500; }
586
+ .vc-step.is-active .vc-step-pct { color: var(--lime); }
587
+ .vc-step.is-done .vc-step-num {
588
+ background: color-mix(in srgb, var(--lime) 26%, transparent);
589
+ border-color: var(--lime);
590
+ color: var(--lime);
591
+ }
592
+ .vc-step.is-done .vc-step-num::after {
593
+ content: "✓";
594
+ font-size: 12px;
595
+ font-weight: 700;
596
+ }
597
+ .vc-step.is-done .vc-step-num { font-size: 0; }
598
+ .vc-step.is-done .vc-step-label { color: var(--text); }
599
+ .vc-step.is-done .vc-step-bar-fill { width: 100% !important; }
600
+
601
+ .vc-stage-text {
602
+ font-family: var(--mono);
603
+ font-size: 11px;
604
+ color: var(--text-soft);
605
+ line-height: 1.55;
606
+ min-height: 16px;
607
+ /* Preserve newlines so the stderr tail appended to error messages
608
+ (see normaliseError in routes/voice-clone.ts) keeps yt-dlp's
609
+ diagnostic line breaks. */
610
+ white-space: pre-wrap;
611
+ word-break: break-word;
612
+ max-height: 200px;
613
+ overflow-y: auto;
614
+ }
615
+ .vc-stage-text.is-error { color: var(--red); }
616
+
617
+ /* ── Success / preview stage · replaces the progress view when
618
+ the clone job lands `done`. Reads as a celebration row plus a
619
+ big play button and an editable sample line. ─────────────── */
620
+ .vc-success {
621
+ padding: 24px 24px 20px;
622
+ display: flex;
623
+ flex-direction: column;
624
+ align-items: stretch;
625
+ gap: 16px;
626
+ }
627
+ .vc-success[hidden] { display: none; }
628
+ .vc-success-head {
629
+ display: flex;
630
+ flex-direction: column;
631
+ align-items: center;
632
+ gap: 6px;
633
+ text-align: center;
634
+ }
635
+ .vc-success-kicker {
636
+ font-family: var(--mono);
637
+ font-size: 10px;
638
+ font-weight: 700;
639
+ letter-spacing: 0.18em;
640
+ text-transform: uppercase;
641
+ color: var(--lime);
642
+ }
643
+ .vc-success-title {
644
+ font-family: var(--sans);
645
+ font-size: 18px;
646
+ font-weight: 600;
647
+ color: var(--text);
648
+ letter-spacing: -0.01em;
649
+ line-height: 1.3;
650
+ }
651
+
652
+ .vc-preview-btn {
653
+ position: relative;
654
+ align-self: center;
655
+ width: 72px;
656
+ height: 72px;
657
+ border-radius: 50%;
658
+ background: var(--lime);
659
+ border: 0;
660
+ color: var(--bg);
661
+ cursor: pointer;
662
+ display: inline-flex;
663
+ align-items: center;
664
+ justify-content: center;
665
+ font-size: 26px;
666
+ line-height: 1;
667
+ transition: transform 0.12s, box-shadow 0.18s, background 0.18s;
668
+ box-shadow: 0 6px 18px -8px color-mix(in srgb, var(--lime) 80%, transparent);
669
+ }
670
+ .vc-preview-btn:hover { transform: translateY(-1px); }
671
+ .vc-preview-btn:active { transform: translateY(0); }
672
+ .vc-preview-btn.is-loading { cursor: progress; background: color-mix(in srgb, var(--lime) 60%, var(--panel-2)); }
673
+ .vc-preview-btn.is-playing { background: color-mix(in srgb, var(--lime) 80%, white); }
674
+ .vc-preview-btn[disabled] { opacity: 0.5; cursor: not-allowed; }
675
+ .vc-preview-glyph { pointer-events: none; font-size: 24px; line-height: 1; }
676
+ .vc-preview-dots {
677
+ position: absolute;
678
+ inset: 0;
679
+ display: none;
680
+ align-items: center;
681
+ justify-content: center;
682
+ gap: 4px;
683
+ pointer-events: none;
684
+ }
685
+ .vc-preview-dots i {
686
+ width: 6px;
687
+ height: 6px;
688
+ border-radius: 50%;
689
+ background: var(--bg);
690
+ animation: vc-pulse 0.9s ease-in-out infinite;
691
+ }
692
+ .vc-preview-dots i:nth-child(2) { animation-delay: 0.15s; }
693
+ .vc-preview-dots i:nth-child(3) { animation-delay: 0.3s; }
694
+ .vc-preview-btn.is-loading .vc-preview-glyph { visibility: hidden; }
695
+ .vc-preview-btn.is-loading .vc-preview-dots { display: inline-flex; }
696
+ @keyframes vc-pulse {
697
+ 0%, 100% { opacity: 0.4; transform: scale(0.85); }
698
+ 50% { opacity: 1; transform: scale(1.1); }
699
+ }
700
+
701
+ .vc-preview-text {
702
+ width: 100%;
703
+ resize: vertical;
704
+ min-height: 72px;
705
+ max-height: 160px;
706
+ padding: 12px 14px;
707
+ background: var(--panel-2);
708
+ border: 0.5px solid var(--line-bright);
709
+ border-radius: 6px;
710
+ color: var(--text);
711
+ font-family: var(--sans);
712
+ font-size: 15px;
713
+ line-height: 1.55;
714
+ outline: none;
715
+ transition: border-color 0.12s;
716
+ }
717
+ .vc-preview-text:focus { border-color: var(--lime); }
718
+ .vc-preview-hint {
719
+ margin: 0;
720
+ font-family: var(--mono);
721
+ font-size: 11px;
722
+ color: var(--text-faint);
723
+ text-align: center;
724
+ line-height: 1.55;
725
+ }
726
+ /* Inline status note · used when preview fails (provider 4xx, voice
727
+ still propagating, audio decode error). Sits where an alert() used
728
+ to land so the user can read the error in context. */
729
+ .vc-preview-status {
730
+ display: none;
731
+ margin: 6px 0 0;
732
+ font-family: var(--mono);
733
+ font-size: 11px;
734
+ line-height: 1.55;
735
+ text-align: center;
736
+ color: var(--text-soft);
737
+ }
738
+ .vc-preview-status.is-error { color: var(--red, #B5706A); }
739
+
740
+ /* ── Foot · buttons row · mirrors room-settings' rs-foot. The
741
+ hairline border + strip-bg row ties it to the head strip
742
+ above so the panel reads as a sealed boardroom card. */
743
+ .vc-foot {
744
+ padding: 12px 16px;
745
+ display: flex;
746
+ gap: 8px;
747
+ justify-content: flex-end;
748
+ border-top: 0.5px solid var(--line-bright);
749
+ background: var(--strip-bg);
750
+ }
751
+ .vc-btn {
752
+ padding: 8px 14px;
753
+ background: transparent;
754
+ border: 0.5px solid var(--line-bright);
755
+ color: var(--text-soft);
756
+ font-family: var(--mono);
757
+ font-size: 11px;
758
+ letter-spacing: 0.14em;
759
+ text-transform: uppercase;
760
+ border-radius: 3px;
761
+ cursor: pointer;
762
+ transition: background 0.12s, color 0.12s, border-color 0.12s;
763
+ }
764
+ .vc-btn::before { content: "[ "; color: var(--text-faint); }
765
+ .vc-btn::after { content: " ]"; color: var(--text-faint); }
766
+ .vc-btn:hover {
767
+ border-color: var(--lime);
768
+ color: var(--lime);
769
+ }
770
+ .vc-btn:hover::before,
771
+ .vc-btn:hover::after { color: var(--lime); }
772
+ .vc-btn[disabled] {
773
+ opacity: 0.45;
774
+ cursor: not-allowed;
775
+ }
776
+ .vc-btn-primary {
777
+ border-color: var(--lime);
778
+ color: var(--lime);
779
+ }
780
+ .vc-btn-primary::before,
781
+ .vc-btn-primary::after { color: var(--lime); }
782
+ .vc-btn-primary:hover {
783
+ background: color-mix(in srgb, var(--lime) 12%, transparent);
784
+ }
785
+ .vc-btn-primary[disabled] {
786
+ border-color: var(--line-bright);
787
+ color: var(--text-faint);
788
+ }
789
+ .vc-btn-primary[disabled]::before,
790
+ .vc-btn-primary[disabled]::after { color: var(--text-faint); }
791
+ .vc-btn-danger {
792
+ color: var(--red);
793
+ border-color: color-mix(in srgb, var(--red) 50%, var(--line-bright));
794
+ }
795
+ .vc-btn-danger:hover {
796
+ background: color-mix(in srgb, var(--red) 10%, transparent);
797
+ color: var(--red);
798
+ border-color: var(--red);
799
+ }
800
+ .vc-btn-danger::before,
801
+ .vc-btn-danger::after { color: color-mix(in srgb, var(--red) 70%, var(--text-faint)); }
802
+
803
+ /* ── Minimized pill · floating bottom-right ────────────────────── */
804
+ .vc-pill {
805
+ position: fixed;
806
+ right: 24px;
807
+ bottom: 24px;
808
+ z-index: 8400;
809
+ display: inline-flex;
810
+ align-items: center;
811
+ gap: 10px;
812
+ padding: 8px 14px 8px 10px;
813
+ background: var(--panel);
814
+ border: 0.5px solid var(--line-strong);
815
+ border-radius: 999px;
816
+ box-shadow: 0 8px 24px -8px rgba(0, 0, 0, 0.45);
817
+ cursor: pointer;
818
+ font-family: var(--mono);
819
+ font-size: 11px;
820
+ color: var(--text);
821
+ letter-spacing: 0.04em;
822
+ transition: border-color 0.18s, box-shadow 0.18s, transform 0.12s;
823
+ overflow: hidden;
824
+ }
825
+ .vc-pill[hidden] { display: none; }
826
+ .vc-pill:hover { transform: translateY(-1px); }
827
+ .vc-pill-icon {
828
+ width: 22px;
829
+ height: 22px;
830
+ border-radius: 50%;
831
+ display: inline-flex;
832
+ align-items: center;
833
+ justify-content: center;
834
+ background: var(--lime);
835
+ color: var(--bg);
836
+ flex-shrink: 0;
837
+ }
838
+ .vc-pill-icon svg { width: 12px; height: 12px; }
839
+ .vc-pill-label {
840
+ white-space: nowrap;
841
+ font-size: 12px;
842
+ font-weight: 600;
843
+ color: var(--text);
844
+ }
845
+ .vc-pill-pct {
846
+ font-family: var(--mono);
847
+ font-size: 11px;
848
+ letter-spacing: 0.04em;
849
+ color: var(--lime);
850
+ font-weight: 700;
851
+ }
852
+ .vc-pill-progress {
853
+ position: absolute;
854
+ left: 0;
855
+ right: 0;
856
+ bottom: 0;
857
+ height: 2px;
858
+ background: color-mix(in srgb, var(--lime) 16%, transparent);
859
+ overflow: hidden;
860
+ border-radius: 0 0 999px 999px;
861
+ }
862
+ .vc-pill-progress-fill {
863
+ position: absolute;
864
+ inset: 0;
865
+ width: 0%;
866
+ background: var(--lime);
867
+ transition: width 0.18s ease;
868
+ }
869
+ .vc-pill.is-failed {
870
+ border-color: var(--red);
871
+ }
872
+ .vc-pill.is-failed .vc-pill-icon { background: var(--red); }
873
+ .vc-pill.is-failed .vc-pill-pct { color: var(--red); }
874
+ .vc-pill.is-failed .vc-pill-progress-fill { background: var(--red); }
875
+ .vc-pill.is-done .vc-pill-icon { background: var(--lime); }