@ibanzajoe/uploader 0.1.0 → 0.2.0

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.
package/dist/styles.css CHANGED
@@ -11,31 +11,65 @@
11
11
 
12
12
  /* ─── Design tokens ─────────────────────────────────────────────────────────── */
13
13
 
14
+ /*
15
+ * Three theming knobs are all that host apps need to override:
16
+ * --uploader-accent Primary brand color (default: Tideline teal #0CA187)
17
+ * --uploader-accent-soft Tinted background for icon swatches and hover fills
18
+ * --uploader-accent-ring Focus ring / drag-over glow (20-30% opacity of accent)
19
+ *
20
+ * Example — adapting to a purple host brand:
21
+ * .my-app { --uploader-accent:#7C3AED; --uploader-accent-soft:#F1ECFD; --uploader-accent-ring:rgba(124,58,237,.28); }
22
+ */
14
23
  :root {
15
- --uploader-accent: #0057ff;
16
- --uploader-accent-hover: #0041cc;
17
- --uploader-radius: 8px;
24
+ /* ── Accent (the only brand color the host app needs to set) ── */
25
+ --uploader-accent: #0CA187;
26
+ --uploader-accent-hover: #0A8270;
27
+ --uploader-accent-soft: #ECFBF7;
28
+ --uploader-accent-ring: rgba(12, 161, 135, 0.28);
29
+
30
+ /* ── Surface / structure ── */
31
+ --uploader-radius: 10px;
18
32
  --uploader-bg: #ffffff;
19
- --uploader-surface: #f5f7fa;
20
- --uploader-border: #d4d8e1;
21
- --uploader-text: #1a1d23;
22
- --uploader-text-muted: #6b7280;
23
- --uploader-error: #dc2626;
24
- --uploader-success: #16a34a;
25
- --uploader-shadow: 0 8px 32px rgba(0, 0, 0, 0.14);
33
+ --uploader-surface: #FAF9F7;
34
+ --uploader-surface-hover: #F4F2EE;
35
+ --uploader-border: #D5CFC5;
36
+ --uploader-border-subtle: #F0EDE8;
37
+
38
+ /* ── Typography ── */
39
+ --uploader-text: #211E1A;
40
+ --uploader-text-muted: #8E8678;
41
+ --uploader-text-xmuted: #A39A8C;
42
+
43
+ /* ── Semantic ── */
44
+ --uploader-error: #D8442F;
45
+ --uploader-error-soft: #FCF0EE;
46
+ --uploader-success: #0CA187;
47
+
48
+ /* ── Elevation ── */
49
+ --uploader-shadow-sm: 0 1px 4px rgba(31, 28, 25, 0.08);
50
+ --uploader-shadow: 0 8px 30px rgba(31, 28, 25, 0.12);
51
+ --uploader-shadow-lg: 0 24px 60px -16px rgba(31, 28, 25, 0.4);
52
+
53
+ /* ── Z-index ── */
26
54
  --uploader-z-backdrop: 1000;
27
55
  --uploader-z-panel: 1001;
56
+
57
+ /* ── Misc ── */
28
58
  --uploader-font: inherit;
29
59
  --uploader-transition: 0.15s ease;
30
60
  }
31
61
 
32
62
  [data-theme="dark"] {
33
- --uploader-bg: #1e2130;
34
- --uploader-surface: #282c3e;
35
- --uploader-border: #3b4060;
36
- --uploader-text: #e8eaf0;
37
- --uploader-text-muted: #9ba3b8;
38
- --uploader-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
63
+ --uploader-bg: #1C1A17;
64
+ --uploader-surface: #252219;
65
+ --uploader-surface-hover: #2E2A22;
66
+ --uploader-border: #3D3830;
67
+ --uploader-border-subtle: #2A2620;
68
+ --uploader-text: #F0EDE8;
69
+ --uploader-text-muted: #9A9185;
70
+ --uploader-text-xmuted: #6B6358;
71
+ --uploader-shadow: 0 8px 30px rgba(0, 0, 0, 0.35);
72
+ --uploader-shadow-lg: 0 24px 60px -16px rgba(0, 0, 0, 0.55);
39
73
  }
40
74
 
41
75
  /* ─── Backdrop ──────────────────────────────────────────────────────────────── */
@@ -55,10 +89,10 @@
55
89
 
56
90
  .uploader-picker-panel {
57
91
  background: var(--uploader-bg);
58
- border-radius: var(--uploader-radius);
59
- box-shadow: var(--uploader-shadow);
60
- width: min(560px, calc(100vw - 32px));
61
- max-height: calc(100dvh - 32px);
92
+ border-radius: 16px;
93
+ box-shadow: var(--uploader-shadow-lg);
94
+ width: min(480px, calc(100vw - 32px));
95
+ max-height: calc(100dvh - 48px);
62
96
  display: flex;
63
97
  flex-direction: column;
64
98
  overflow: hidden;
@@ -70,8 +104,8 @@
70
104
  display: flex;
71
105
  align-items: center;
72
106
  justify-content: space-between;
73
- padding: 16px 20px;
74
- border-bottom: 1px solid var(--uploader-border);
107
+ padding: 18px 20px;
108
+ border-bottom: 1px solid var(--uploader-border-subtle);
75
109
  flex-shrink: 0;
76
110
  }
77
111
 
@@ -83,44 +117,63 @@
83
117
  }
84
118
 
85
119
  .uploader-btn-close {
86
- background: none;
120
+ width: 26px;
121
+ height: 26px;
122
+ display: flex;
123
+ align-items: center;
124
+ justify-content: center;
125
+ background: var(--uploader-surface);
87
126
  border: none;
127
+ border-radius: 8px;
88
128
  cursor: pointer;
89
- font-size: 1.4rem;
129
+ font-size: 13px;
90
130
  line-height: 1;
91
131
  color: var(--uploader-text-muted);
92
- padding: 4px 6px;
93
- border-radius: 4px;
94
- transition: color var(--uploader-transition), background var(--uploader-transition);
132
+ transition: background var(--uploader-transition), color var(--uploader-transition);
133
+ flex-shrink: 0;
95
134
  }
96
135
 
97
136
  .uploader-btn-close:hover {
98
137
  color: var(--uploader-text);
99
- background: var(--uploader-surface);
138
+ background: var(--uploader-surface-hover);
100
139
  }
101
140
 
102
141
  .uploader-picker-footer {
103
142
  display: flex;
104
- justify-content: flex-end;
143
+ align-items: center;
144
+ justify-content: space-between;
105
145
  gap: 8px;
106
- padding: 12px 20px;
107
- border-top: 1px solid var(--uploader-border);
146
+ padding: 14px 20px;
147
+ border-top: 1px solid var(--uploader-border-subtle);
148
+ background: var(--uploader-surface);
108
149
  flex-shrink: 0;
109
150
  }
110
151
 
152
+ .uploader-picker-footer-esc {
153
+ font-family: var(--uploader-font-mono, 'JetBrains Mono', monospace);
154
+ font-size: 11px;
155
+ color: var(--uploader-text-xmuted);
156
+ }
157
+
158
+ .uploader-picker-footer-actions {
159
+ display: flex;
160
+ gap: 9px;
161
+ }
162
+
111
163
  /* ─── Drop zone (shared by PickerOverlay and DropPane) ──────────────────────── */
112
164
 
113
165
  .uploader-dropzone {
114
- margin: 16px 20px;
115
- border: 2px dashed var(--uploader-border);
116
- border-radius: var(--uploader-radius);
117
- background: var(--uploader-surface);
118
- padding: 32px 20px;
166
+ margin: 20px;
167
+ border: 1.5px dashed var(--uploader-border);
168
+ border-radius: 13px;
169
+ background: transparent;
170
+ padding: 38px 20px;
119
171
  display: flex;
120
172
  flex-direction: column;
121
173
  align-items: center;
122
174
  gap: 10px;
123
175
  cursor: pointer;
176
+ text-align: center;
124
177
  transition:
125
178
  border-color var(--uploader-transition),
126
179
  background var(--uploader-transition);
@@ -128,67 +181,178 @@
128
181
  }
129
182
 
130
183
  .uploader-dropzone:hover,
131
- .uploader-dropzone:focus-visible,
132
- .uploader-drag-over .uploader-dropzone {
184
+ .uploader-dropzone:focus-visible {
133
185
  border-color: var(--uploader-accent);
134
- background: color-mix(in srgb, var(--uploader-accent) 5%, var(--uploader-surface));
186
+ background: var(--uploader-accent-soft);
135
187
  outline: none;
136
188
  }
137
189
 
138
190
  .uploader-dropzone:focus-visible {
139
- outline: 2px solid var(--uploader-accent);
140
- outline-offset: 2px;
191
+ box-shadow: 0 0 0 3px var(--uploader-accent-ring);
192
+ }
193
+
194
+ /* Applied to the PickerOverlay panel when a drag is in progress */
195
+ .uploader-picker-panel[data-drag-over="true"] .uploader-dropzone,
196
+ .uploader-drop-pane[data-drag-over="true"] .uploader-dropzone {
197
+ border-color: var(--uploader-accent);
198
+ background: var(--uploader-accent-soft);
199
+ }
200
+
201
+ /* Upload icon swatch */
202
+ .uploader-dropzone-icon-wrap {
203
+ width: 50px;
204
+ height: 50px;
205
+ border-radius: 14px;
206
+ background: var(--uploader-accent-soft);
207
+ display: flex;
208
+ align-items: center;
209
+ justify-content: center;
210
+ margin-bottom: 6px;
211
+ flex-shrink: 0;
141
212
  }
142
213
 
143
214
  .uploader-dropzone-icon {
144
- font-size: 2.5rem;
145
- color: var(--uploader-accent);
215
+ /* SVG upload arrow rendered via CSS borders — no external icon dep */
216
+ position: relative;
217
+ width: 16px;
218
+ height: 14px;
219
+ border: 2.4px solid var(--uploader-accent);
220
+ border-radius: 0 0 5px 5px;
221
+ border-top: none;
222
+ }
223
+
224
+ .uploader-dropzone-icon::before {
225
+ content: '';
226
+ position: absolute;
227
+ left: 50%;
228
+ top: -10px;
229
+ transform: translateX(-50%);
230
+ width: 0;
231
+ height: 0;
232
+ border-left: 5px solid transparent;
233
+ border-right: 5px solid transparent;
234
+ border-bottom: 8px solid var(--uploader-accent);
235
+ }
236
+
237
+ .uploader-dropzone-icon::after {
238
+ content: '';
239
+ position: absolute;
240
+ left: 50%;
241
+ top: -2px;
242
+ transform: translateX(-50%);
243
+ width: 2.4px;
244
+ height: 9px;
245
+ background: var(--uploader-accent);
146
246
  }
147
247
 
148
248
  .uploader-dropzone-label {
149
249
  margin: 0;
250
+ font-size: 15px;
251
+ font-weight: 600;
252
+ color: var(--uploader-text);
253
+ }
254
+
255
+ .uploader-dropzone-sublabel {
256
+ margin: 0;
257
+ font-size: 13px;
150
258
  color: var(--uploader-text-muted);
151
- font-size: 0.9rem;
152
- text-align: center;
259
+ }
260
+
261
+ .uploader-dropzone-hint {
262
+ margin-top: 4px;
263
+ font-size: 11.5px;
264
+ color: var(--uploader-text-xmuted);
265
+ }
266
+
267
+ /* Source button row (Browse files + optional Take photo) */
268
+ .uploader-dropzone-actions {
269
+ display: flex;
270
+ align-items: center;
271
+ justify-content: center;
272
+ flex-wrap: wrap;
273
+ gap: 8px;
274
+ }
275
+
276
+ /* "Take photo" — secondary-styled source button with a camera glyph */
277
+ .uploader-btn-camera {
278
+ display: inline-flex;
279
+ align-items: center;
280
+ gap: 7px;
281
+ padding: 9px 16px;
282
+ border: 1px solid var(--uploader-border);
283
+ border-radius: 10px;
284
+ background: var(--uploader-bg);
285
+ color: var(--uploader-text);
286
+ font-family: var(--uploader-font);
287
+ font-weight: 600;
288
+ font-size: 14px;
289
+ cursor: pointer;
290
+ transition: border-color var(--uploader-transition), background var(--uploader-transition),
291
+ color var(--uploader-transition);
292
+ }
293
+
294
+ .uploader-btn-camera:hover {
295
+ border-color: var(--uploader-accent);
296
+ background: var(--uploader-accent-soft);
297
+ color: var(--uploader-accent);
298
+ }
299
+
300
+ .uploader-btn-camera:focus-visible {
301
+ outline: none;
302
+ border-color: var(--uploader-accent);
303
+ box-shadow: 0 0 0 3px var(--uploader-accent-ring);
304
+ }
305
+
306
+ .uploader-btn-camera-icon {
307
+ flex-shrink: 0;
153
308
  }
154
309
 
155
310
  /* ─── File list ─────────────────────────────────────────────────────────────── */
156
311
 
157
312
  .uploader-file-list {
158
313
  list-style: none;
159
- margin: 0 20px;
160
- padding: 0;
314
+ margin: 0;
315
+ padding: 0 20px;
161
316
  overflow-y: auto;
162
- max-height: 260px;
317
+ max-height: 220px;
163
318
  display: flex;
164
319
  flex-direction: column;
165
- gap: 6px;
320
+ gap: 12px;
166
321
  }
167
322
 
168
323
  .uploader-file-item {
169
324
  display: flex;
170
325
  align-items: center;
171
- gap: 10px;
172
- padding: 8px 10px;
173
- border-radius: calc(var(--uploader-radius) - 2px);
174
- background: var(--uploader-surface);
175
- border: 1px solid var(--uploader-border);
326
+ gap: 12px;
327
+ padding: 0;
328
+ border-radius: 0;
329
+ background: transparent;
330
+ border: none;
176
331
  }
177
332
 
178
- .uploader-file-item.uploader-file-error {
179
- border-color: var(--uploader-error);
180
- background: color-mix(in srgb, var(--uploader-error) 6%, var(--uploader-surface));
333
+ .uploader-file-item.uploader-file-error .uploader-file-name {
334
+ color: var(--uploader-error);
181
335
  }
182
336
 
183
- .uploader-file-item.uploader-file-done {
184
- border-color: var(--uploader-success);
337
+ .uploader-file-item.uploader-file-done .uploader-file-status {
338
+ color: var(--uploader-accent);
185
339
  }
186
340
 
187
341
  .uploader-file-thumb {
188
- width: 36px;
189
- height: 36px;
342
+ width: 38px;
343
+ height: 38px;
190
344
  object-fit: cover;
191
- border-radius: 4px;
345
+ border-radius: 9px;
346
+ flex-shrink: 0;
347
+ background: var(--uploader-surface);
348
+ }
349
+
350
+ /* Placeholder swatch when no preview is available */
351
+ .uploader-file-thumb-placeholder {
352
+ width: 38px;
353
+ height: 38px;
354
+ border-radius: 9px;
355
+ background: var(--uploader-surface);
192
356
  flex-shrink: 0;
193
357
  }
194
358
 
@@ -197,50 +361,68 @@
197
361
  min-width: 0;
198
362
  display: flex;
199
363
  flex-direction: column;
200
- gap: 2px;
364
+ gap: 5px;
365
+ }
366
+
367
+ .uploader-file-info-row {
368
+ display: flex;
369
+ align-items: center;
370
+ justify-content: space-between;
201
371
  }
202
372
 
203
373
  .uploader-file-name {
204
- font-size: 0.875rem;
374
+ font-size: 13px;
375
+ font-weight: 500;
205
376
  color: var(--uploader-text);
206
377
  white-space: nowrap;
207
378
  overflow: hidden;
208
379
  text-overflow: ellipsis;
209
380
  }
210
381
 
211
- .uploader-file-error {
212
- font-size: 0.75rem;
213
- color: var(--uploader-error);
382
+ .uploader-file-status {
383
+ font-size: 11.5px;
384
+ font-weight: 600;
385
+ color: var(--uploader-text-muted);
386
+ flex-shrink: 0;
214
387
  }
215
388
 
216
- .uploader-file-done {
217
- font-size: 0.75rem;
218
- color: var(--uploader-success);
389
+ .uploader-file-status[data-state="done"] {
390
+ color: var(--uploader-accent);
219
391
  }
220
392
 
221
- .uploader-file-progress {
222
- width: 80px;
223
- height: 6px;
224
- appearance: none;
225
- border-radius: 3px;
226
- overflow: hidden;
227
- flex-shrink: 0;
393
+ .uploader-file-status[data-state="error"] {
394
+ color: var(--uploader-error);
228
395
  }
229
396
 
230
- .uploader-file-progress::-webkit-progress-bar {
231
- background: var(--uploader-border);
232
- border-radius: 3px;
397
+ /* Thin progress track used inside FileQueueRow */
398
+ .uploader-file-progress-track {
399
+ height: 5px;
400
+ border-radius: 99px;
401
+ background: var(--uploader-border-subtle);
402
+ overflow: hidden;
233
403
  }
234
404
 
235
- .uploader-file-progress::-webkit-progress-value {
405
+ .uploader-file-progress-fill {
406
+ height: 100%;
236
407
  background: var(--uploader-accent);
237
- border-radius: 3px;
238
- transition: width var(--uploader-transition);
408
+ border-radius: 99px;
409
+ transition: width 0.2s ease;
239
410
  }
240
411
 
241
- .uploader-file-progress::-moz-progress-bar {
242
- background: var(--uploader-accent);
243
- border-radius: 3px;
412
+ /* Done checkmark badge */
413
+ .uploader-file-done-badge {
414
+ width: 18px;
415
+ height: 18px;
416
+ border-radius: 99px;
417
+ background: var(--uploader-accent-soft);
418
+ display: flex;
419
+ align-items: center;
420
+ justify-content: center;
421
+ flex-shrink: 0;
422
+ }
423
+
424
+ .uploader-file-done-badge svg {
425
+ color: var(--uploader-accent);
244
426
  }
245
427
 
246
428
  .uploader-file-actions {
@@ -255,86 +437,148 @@
255
437
  display: flex;
256
438
  align-items: center;
257
439
  gap: 10px;
258
- padding: 8px 20px;
440
+ padding: 6px 20px 12px;
259
441
  flex-shrink: 0;
260
442
  }
261
443
 
262
- .uploader-aggregate-progress progress {
263
- flex: 1;
264
- height: 8px;
265
- appearance: none;
266
- border-radius: 4px;
444
+ .uploader-aggregate-progress-label {
445
+ font-size: 12px;
446
+ color: var(--uploader-text-muted);
447
+ font-weight: 600;
448
+ white-space: nowrap;
449
+ }
450
+
451
+ .uploader-aggregate-progress-pct {
452
+ font-size: 12px;
453
+ color: var(--uploader-text-muted);
454
+ min-width: 3ch;
455
+ text-align: right;
456
+ }
457
+
458
+ /* ─── Standalone ProgressBar primitive ─────────────────────────────────────── */
459
+
460
+ /*
461
+ * .uploader-progress-bar — wraps the track+fill div pair.
462
+ * Height, radius, and color are set via CSS vars so they can be overridden
463
+ * at any ancestor (e.g. a taller bar for the aggregate slot).
464
+ */
465
+ .uploader-progress-bar {
466
+ --uploader-progress-height: 5px;
467
+ --uploader-progress-radius: 99px;
468
+ height: var(--uploader-progress-height);
469
+ border-radius: var(--uploader-progress-radius);
470
+ background: var(--uploader-border-subtle);
267
471
  overflow: hidden;
472
+ flex: 1;
268
473
  }
269
474
 
270
- .uploader-aggregate-progress progress::-webkit-progress-bar {
271
- background: var(--uploader-border);
272
- border-radius: 4px;
475
+ .uploader-progress-bar[data-size="lg"] {
476
+ --uploader-progress-height: 8px;
273
477
  }
274
478
 
275
- .uploader-aggregate-progress progress::-webkit-progress-value {
479
+ .uploader-progress-bar-fill {
480
+ height: 100%;
276
481
  background: var(--uploader-accent);
277
- border-radius: 4px;
482
+ border-radius: var(--uploader-progress-radius);
278
483
  transition: width 0.2s ease;
484
+ min-width: 4px; /* visible nub at 0% */
279
485
  }
280
486
 
281
- .uploader-aggregate-progress progress::-moz-progress-bar {
487
+ /* ─── Buttons ───────────────────────────────────────────────────────────────── */
488
+
489
+ .uploader-btn-primary {
490
+ padding: 8px 14px;
491
+ border: none;
492
+ border-radius: 9px;
282
493
  background: var(--uploader-accent);
494
+ color: #fff;
495
+ font-family: var(--uploader-font);
496
+ font-weight: 600;
497
+ font-size: 13px;
498
+ cursor: pointer;
499
+ transition: background var(--uploader-transition), opacity var(--uploader-transition);
283
500
  }
284
501
 
285
- .uploader-aggregate-progress span {
286
- font-size: 0.8rem;
287
- color: var(--uploader-text-muted);
288
- min-width: 3ch;
502
+ .uploader-btn-primary:hover {
503
+ background: var(--uploader-accent-hover);
289
504
  }
290
505
 
291
- /* ─── Buttons ───────────────────────────────────────────────────────────────── */
506
+ .uploader-btn-primary:disabled {
507
+ opacity: 0.4;
508
+ cursor: not-allowed;
509
+ }
292
510
 
293
- .uploader-btn-upload,
294
- .uploader-btn-browse,
295
- .uploader-btn-done {
511
+ /* Browse files button inside the dropzone gets a focus ring matching the accent */
512
+ .uploader-btn-browse {
513
+ padding: 9px 18px;
514
+ border: none;
515
+ border-radius: 10px;
296
516
  background: var(--uploader-accent);
297
517
  color: #fff;
298
- border: none;
299
- border-radius: calc(var(--uploader-radius) - 2px);
300
- padding: 8px 18px;
301
- font-size: 0.875rem;
302
- font-weight: 500;
518
+ font-family: var(--uploader-font);
519
+ font-weight: 600;
520
+ font-size: 14px;
303
521
  cursor: pointer;
522
+ box-shadow: 0 0 0 3px var(--uploader-accent-ring);
304
523
  transition: background var(--uploader-transition);
305
524
  }
306
525
 
307
- .uploader-btn-upload:hover,
308
- .uploader-btn-browse:hover,
309
- .uploader-btn-done:hover {
526
+ .uploader-btn-browse:hover {
310
527
  background: var(--uploader-accent-hover);
311
528
  }
312
529
 
313
- .uploader-btn-upload:disabled {
314
- opacity: 0.5;
315
- cursor: not-allowed;
530
+ .uploader-btn-browse:focus-visible {
531
+ outline: none;
532
+ box-shadow: 0 0 0 3px var(--uploader-accent-ring);
316
533
  }
317
534
 
318
- .uploader-btn-cancel {
319
- background: var(--uploader-surface);
320
- color: var(--uploader-text);
535
+ .uploader-btn-secondary {
536
+ padding: 8px 14px;
321
537
  border: 1px solid var(--uploader-border);
322
- border-radius: calc(var(--uploader-radius) - 2px);
323
- padding: 8px 18px;
324
- font-size: 0.875rem;
538
+ border-radius: 9px;
539
+ background: var(--uploader-bg);
540
+ color: var(--uploader-text);
541
+ font-family: var(--uploader-font);
542
+ font-weight: 600;
543
+ font-size: 13px;
325
544
  cursor: pointer;
326
545
  transition: background var(--uploader-transition);
327
546
  }
328
547
 
329
- .uploader-btn-cancel:hover {
330
- background: var(--uploader-border);
548
+ .uploader-btn-secondary:hover {
549
+ background: var(--uploader-surface);
331
550
  }
332
551
 
333
- .uploader-btn-cancel:disabled {
552
+ .uploader-btn-secondary:disabled {
334
553
  opacity: 0.5;
335
554
  cursor: not-allowed;
336
555
  }
337
556
 
557
+ /* Aliases used in existing markup — map to the new button styles */
558
+ .uploader-btn-upload { composes: uploader-btn-primary; }
559
+ .uploader-btn-done { composes: uploader-btn-primary; }
560
+ .uploader-btn-cancel { composes: uploader-btn-secondary; }
561
+
562
+ .uploader-btn-icon {
563
+ width: 18px;
564
+ height: 18px;
565
+ display: flex;
566
+ align-items: center;
567
+ justify-content: center;
568
+ background: none;
569
+ border: none;
570
+ padding: 0;
571
+ cursor: pointer;
572
+ color: var(--uploader-text-muted);
573
+ border-radius: 4px;
574
+ transition: color var(--uploader-transition);
575
+ flex-shrink: 0;
576
+ }
577
+
578
+ .uploader-btn-icon:hover {
579
+ color: var(--uploader-text);
580
+ }
581
+
338
582
  .uploader-btn-retry,
339
583
  .uploader-btn-remove {
340
584
  background: none;
@@ -371,9 +615,41 @@
371
615
  border: 0;
372
616
  }
373
617
 
618
+ /* ─── PickerOverlay done state ──────────────────────────────────────────────── */
619
+
620
+ .uploader-done-body {
621
+ display: flex;
622
+ flex-direction: column;
623
+ align-items: center;
624
+ text-align: center;
625
+ padding: 24px 20px;
626
+ gap: 0;
627
+ }
628
+
629
+ .uploader-done-subtitle {
630
+ font-size: 13.5px;
631
+ color: var(--uploader-text-muted);
632
+ margin: 0 0 24px;
633
+ }
634
+
635
+ .uploader-done-list {
636
+ width: 100%;
637
+ text-align: left;
638
+ margin: 0 0 24px !important;
639
+ padding: 0 !important;
640
+ max-height: 180px;
641
+ }
642
+
643
+ .uploader-btn-done-full {
644
+ width: 100%;
645
+ padding: 11px;
646
+ font-size: 14px;
647
+ }
648
+
374
649
  /* ─── DropPane root ─────────────────────────────────────────────────────────── */
375
650
 
376
651
  .uploader-drop-pane {
652
+ position: relative;
377
653
  display: flex;
378
654
  flex-direction: column;
379
655
  gap: 12px;
@@ -381,21 +657,51 @@
381
657
  color: var(--uploader-text);
382
658
  }
383
659
 
660
+ /* Dropzone inside DropPane has no side margin — fills the pane width */
384
661
  .uploader-drop-pane .uploader-dropzone {
385
662
  margin: 0;
386
663
  }
387
664
 
388
- .uploader-drop-pane .uploader-file-list {
665
+ .uploader-drop-pane-file-list {
389
666
  margin: 0;
667
+ padding: 0;
390
668
  max-height: 200px;
391
669
  }
392
670
 
393
- .uploader-drop-pane .uploader-actions {
671
+ /* Drag-over overlay — covers queued file list when user drags over a non-empty pane */
672
+ .uploader-drop-pane-drag-overlay {
673
+ position: absolute;
674
+ inset: 0;
675
+ border: 2px dashed var(--uploader-accent);
676
+ border-radius: 13px;
677
+ background: var(--uploader-accent-soft);
394
678
  display: flex;
679
+ align-items: center;
680
+ justify-content: center;
681
+ z-index: 10;
682
+ pointer-events: all;
683
+ }
684
+
685
+ .uploader-drop-pane-drag-label {
686
+ font-size: 14px;
687
+ font-weight: 700;
688
+ color: var(--uploader-accent);
689
+ }
690
+
691
+ /* Action bar at the bottom of the pane */
692
+ .uploader-drop-pane-actions {
693
+ display: flex;
694
+ align-items: center;
695
+ justify-content: flex-end;
395
696
  gap: 8px;
396
697
  }
397
698
 
398
- /* ─── Browse link (DropPane) ────────────────────────────────────────────────── */
699
+ /* Aggregate progress inside DropPane — no side padding (pane handles spacing) */
700
+ .uploader-drop-pane .uploader-aggregate-progress {
701
+ padding: 0;
702
+ }
703
+
704
+ /* ─── Browse link (DropPane inline variant) ─────────────────────────────────── */
399
705
 
400
706
  .uploader-browse-link {
401
707
  color: var(--uploader-accent);
@@ -587,3 +893,129 @@
587
893
  opacity: 0.4;
588
894
  cursor: not-allowed;
589
895
  }
896
+
897
+ /* ─── Camera capture (in-picker webcam) ─────────────────────────────────────── */
898
+
899
+ .uploader-camera {
900
+ display: flex;
901
+ flex-direction: column;
902
+ gap: 14px;
903
+ padding: 16px 20px 18px;
904
+ }
905
+
906
+ .uploader-camera-stage {
907
+ position: relative;
908
+ width: 100%;
909
+ aspect-ratio: 4 / 3;
910
+ max-height: 60vh;
911
+ background: #111418;
912
+ border-radius: var(--uploader-radius);
913
+ overflow: hidden;
914
+ display: flex;
915
+ align-items: center;
916
+ justify-content: center;
917
+ }
918
+
919
+ .uploader-camera-video,
920
+ .uploader-camera-still {
921
+ width: 100%;
922
+ height: 100%;
923
+ object-fit: contain;
924
+ display: block;
925
+ }
926
+
927
+ /* Front-camera preview/still are mirrored so the selfie matches the user. */
928
+ .uploader-camera-video[data-mirror='true'] {
929
+ transform: scaleX(-1);
930
+ }
931
+
932
+ .uploader-camera-status {
933
+ position: absolute;
934
+ inset: 0;
935
+ display: flex;
936
+ align-items: center;
937
+ justify-content: center;
938
+ color: #c9c4bc;
939
+ font-size: 13px;
940
+ pointer-events: none;
941
+ }
942
+
943
+ .uploader-camera-error {
944
+ padding: 24px 28px;
945
+ text-align: center;
946
+ color: #f0ede8;
947
+ font-size: 13.5px;
948
+ line-height: 1.5;
949
+ max-width: 320px;
950
+ }
951
+
952
+ .uploader-camera-hint {
953
+ margin: -4px 0 0;
954
+ font-size: 12px;
955
+ color: var(--uploader-text-muted);
956
+ text-align: center;
957
+ }
958
+
959
+ .uploader-camera-actions {
960
+ display: flex;
961
+ align-items: center;
962
+ justify-content: space-between;
963
+ gap: 10px;
964
+ }
965
+
966
+ /*
967
+ * Live (shutter) row: Back · shutter · spacer, spread with space-between. The
968
+ * spacer is an invisible twin of the Back button, so both sides of the row have
969
+ * identical width and the shutter sits perfectly centered. visibility:hidden
970
+ * keeps its layout box (and thus its width) while removing it visually.
971
+ */
972
+ .uploader-camera-actions-spacer {
973
+ visibility: hidden;
974
+ pointer-events: none;
975
+ }
976
+
977
+ /* Round shutter button with an inner ring */
978
+ .uploader-camera-shutter {
979
+ width: 62px;
980
+ height: 62px;
981
+ border-radius: 999px;
982
+ border: 3px solid var(--uploader-accent);
983
+ background: var(--uploader-bg);
984
+ cursor: pointer;
985
+ display: inline-flex;
986
+ align-items: center;
987
+ justify-content: center;
988
+ padding: 0;
989
+ transition: transform var(--uploader-transition), box-shadow var(--uploader-transition);
990
+ }
991
+
992
+ .uploader-camera-shutter-ring {
993
+ width: 46px;
994
+ height: 46px;
995
+ border-radius: 999px;
996
+ background: var(--uploader-accent);
997
+ transition: background var(--uploader-transition);
998
+ }
999
+
1000
+ .uploader-camera-shutter:hover:not(:disabled) {
1001
+ box-shadow: 0 0 0 4px var(--uploader-accent-ring);
1002
+ }
1003
+
1004
+ .uploader-camera-shutter:active:not(:disabled) {
1005
+ transform: scale(0.94);
1006
+ }
1007
+
1008
+ .uploader-camera-shutter:focus-visible {
1009
+ outline: none;
1010
+ box-shadow: 0 0 0 4px var(--uploader-accent-ring);
1011
+ }
1012
+
1013
+ .uploader-camera-shutter:disabled {
1014
+ opacity: 0.5;
1015
+ cursor: not-allowed;
1016
+ border-color: var(--uploader-border);
1017
+ }
1018
+
1019
+ .uploader-camera-shutter:disabled .uploader-camera-shutter-ring {
1020
+ background: var(--uploader-border);
1021
+ }