repoimage 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.
Files changed (71) hide show
  1. package/.claude/settings.local.json +8 -0
  2. package/AGENTS.md +28 -0
  3. package/PROJECT-AGENTS.md +55 -0
  4. package/README.md +153 -0
  5. package/TODO.md +132 -0
  6. package/client/index.html +12 -0
  7. package/client/package.json +23 -0
  8. package/client/src/App.tsx +599 -0
  9. package/client/src/components/FsBrowser.tsx +210 -0
  10. package/client/src/components/Settings.tsx +81 -0
  11. package/client/src/index.css +797 -0
  12. package/client/src/lib/api.ts +69 -0
  13. package/client/src/lib/collect.ts +204 -0
  14. package/client/src/lib/format.ts +96 -0
  15. package/client/src/lib/session.ts +58 -0
  16. package/client/src/main.tsx +10 -0
  17. package/client/src/vite-env.d.ts +1 -0
  18. package/client/tsconfig.json +18 -0
  19. package/client/vite.config.ts +27 -0
  20. package/docs/README.md +28 -0
  21. package/docs/api/overview.md +65 -0
  22. package/docs/api/scan.md +188 -0
  23. package/docs/architecture.md +155 -0
  24. package/docs/design/invariants.md +19 -0
  25. package/docs/design/role-system.md +50 -0
  26. package/docs/development/README.md +94 -0
  27. package/docs/features/README.md +21 -0
  28. package/docs/features/compression-score.md +75 -0
  29. package/docs/features/exclusions.md +63 -0
  30. package/docs/features/session.md +64 -0
  31. package/package.json +37 -0
  32. package/server/dist/cli.d.ts +3 -0
  33. package/server/dist/cli.d.ts.map +1 -0
  34. package/server/dist/cli.js +54 -0
  35. package/server/dist/cli.js.map +1 -0
  36. package/server/dist/fs-list.d.ts +3 -0
  37. package/server/dist/fs-list.d.ts.map +1 -0
  38. package/server/dist/fs-list.js +73 -0
  39. package/server/dist/fs-list.js.map +1 -0
  40. package/server/dist/paths.d.ts +3 -0
  41. package/server/dist/paths.d.ts.map +1 -0
  42. package/server/dist/paths.js +12 -0
  43. package/server/dist/paths.js.map +1 -0
  44. package/server/dist/scan.d.ts +16 -0
  45. package/server/dist/scan.d.ts.map +1 -0
  46. package/server/dist/scan.js +158 -0
  47. package/server/dist/scan.js.map +1 -0
  48. package/server/dist/server.d.ts +6 -0
  49. package/server/dist/server.d.ts.map +1 -0
  50. package/server/dist/server.js +313 -0
  51. package/server/dist/server.js.map +1 -0
  52. package/server/package.json +22 -0
  53. package/server/src/cli.ts +63 -0
  54. package/server/src/fs-list.ts +70 -0
  55. package/server/src/paths.ts +6 -0
  56. package/server/src/scan.ts +203 -0
  57. package/server/src/server.ts +356 -0
  58. package/server/tsconfig.json +9 -0
  59. package/shared/package.json +10 -0
  60. package/shared/src/constants.ts +37 -0
  61. package/shared/src/index.ts +4 -0
  62. package/shared/src/role-guess.ts +103 -0
  63. package/shared/src/schema.ts +18 -0
  64. package/shared/src/types.ts +36 -0
  65. package/shared/tsconfig.json +9 -0
  66. package/test/cli.test.js +56 -0
  67. package/test/fs-list.test.js +39 -0
  68. package/test/role-guess.test.js +50 -0
  69. package/test/scan.test.js +150 -0
  70. package/test/server.test.js +308 -0
  71. package/tsconfig.base.json +14 -0
@@ -0,0 +1,797 @@
1
+ /* ===== Reset & base ===== */
2
+ *,
3
+ *::before,
4
+ *::after {
5
+ box-sizing: border-box;
6
+ margin: 0;
7
+ padding: 0;
8
+ }
9
+
10
+ :root {
11
+ --clr-bg: #0e1117;
12
+ --clr-surface: #161b22;
13
+ --clr-surface-raised: #1c2129;
14
+ --clr-border: #30363d;
15
+ --clr-text: #e6edf3;
16
+ --clr-text-muted: #8b949e;
17
+ --clr-accent: #58a6ff;
18
+ --clr-accent-hover: #79b8ff;
19
+ --clr-danger: #f85149;
20
+ --clr-warning: #d29922;
21
+ --clr-success: #3fb950;
22
+ --clr-score-bad: #f85149;
23
+ --clr-score-mid: #d29922;
24
+ --clr-score-good: #3fb950;
25
+ --radius: 6px;
26
+ --font-mono: "SF Mono", "Cascadia Code", "Fira Code", "Consolas", monospace;
27
+ --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
28
+ }
29
+
30
+ html {
31
+ font-size: 14px;
32
+ }
33
+
34
+ body {
35
+ font-family: var(--font-sans);
36
+ background: var(--clr-bg);
37
+ color: var(--clr-text);
38
+ line-height: 1.5;
39
+ min-height: 100vh;
40
+ display: flex;
41
+ flex-direction: column;
42
+ }
43
+
44
+ .file-protocol-banner {
45
+ margin: 0;
46
+ padding: 0.75rem 2rem;
47
+ background: rgba(210, 153, 34, 0.12);
48
+ border-bottom: 1px solid var(--clr-warning);
49
+ color: var(--clr-text);
50
+ font-size: 0.95rem;
51
+ }
52
+
53
+ /* ===== Top bar ===== */
54
+ .top-bar {
55
+ background: var(--clr-surface);
56
+ border-bottom: 1px solid var(--clr-border);
57
+ padding: 1rem 2rem;
58
+ display: flex;
59
+ align-items: baseline;
60
+ gap: 1rem;
61
+ }
62
+
63
+ .top-bar__title {
64
+ font-size: 1.25rem;
65
+ font-weight: 700;
66
+ color: var(--clr-accent);
67
+ }
68
+
69
+ .top-bar__subtitle {
70
+ font-size: 0.85rem;
71
+ color: var(--clr-text-muted);
72
+ }
73
+
74
+ /* ===== Main ===== */
75
+ .main {
76
+ flex: 1;
77
+ padding: 1.5rem 2rem;
78
+ max-width: 1400px;
79
+ width: 100%;
80
+ margin: 0 auto;
81
+ }
82
+
83
+ /* ===== Controls ===== */
84
+ .controls {
85
+ background: var(--clr-surface);
86
+ border: 1px solid var(--clr-border);
87
+ border-radius: var(--radius);
88
+ padding: 1.25rem 1.5rem;
89
+ margin-bottom: 1rem;
90
+ display: flex;
91
+ flex-direction: column;
92
+ gap: 1rem;
93
+ }
94
+
95
+ .controls__group--row {
96
+ display: flex;
97
+ flex-wrap: wrap;
98
+ gap: 1rem;
99
+ align-items: flex-end;
100
+ }
101
+
102
+ .controls__label {
103
+ display: block;
104
+ font-size: 0.8rem;
105
+ font-weight: 600;
106
+ color: var(--clr-text-muted);
107
+ text-transform: uppercase;
108
+ letter-spacing: 0.04em;
109
+ margin-bottom: 0.35rem;
110
+ }
111
+
112
+ .controls__input-row {
113
+ display: flex;
114
+ gap: 0.5rem;
115
+ align-items: center;
116
+ }
117
+
118
+ .controls__input-row--wrap {
119
+ flex-wrap: wrap;
120
+ }
121
+
122
+ .visually-hidden {
123
+ position: absolute;
124
+ width: 1px;
125
+ height: 1px;
126
+ padding: 0;
127
+ margin: -1px;
128
+ overflow: hidden;
129
+ clip: rect(0, 0, 0, 0);
130
+ white-space: nowrap;
131
+ border: 0;
132
+ }
133
+
134
+ .controls__text-input {
135
+ flex: 1;
136
+ background: var(--clr-bg);
137
+ border: 1px solid var(--clr-border);
138
+ border-radius: var(--radius);
139
+ padding: 0.5rem 0.75rem;
140
+ color: var(--clr-text);
141
+ font-family: var(--font-mono);
142
+ font-size: 0.9rem;
143
+ }
144
+
145
+ .controls__text-input::placeholder {
146
+ color: var(--clr-text-muted);
147
+ opacity: 0.38;
148
+ }
149
+
150
+ .controls__text-input:focus::placeholder {
151
+ opacity: 0.22;
152
+ }
153
+
154
+ .controls__text-input:focus,
155
+ .controls__select:focus {
156
+ outline: none;
157
+ border-color: var(--clr-accent);
158
+ box-shadow: 0 0 0 2px rgba(88, 166, 255, 0.2);
159
+ }
160
+
161
+ .controls__text-input--short {
162
+ max-width: 200px;
163
+ }
164
+
165
+ .controls__text-input--tiny {
166
+ max-width: 90px;
167
+ flex: none;
168
+ }
169
+
170
+ .controls__select {
171
+ background: var(--clr-bg);
172
+ border: 1px solid var(--clr-border);
173
+ border-radius: var(--radius);
174
+ padding: 0.5rem 0.75rem;
175
+ color: var(--clr-text);
176
+ font-size: 0.9rem;
177
+ cursor: pointer;
178
+ }
179
+
180
+ /* ===== Buttons ===== */
181
+ .btn {
182
+ border: none;
183
+ border-radius: var(--radius);
184
+ padding: 0.5rem 1.25rem;
185
+ font-size: 0.9rem;
186
+ font-weight: 600;
187
+ cursor: pointer;
188
+ transition: background 0.15s, transform 0.1s;
189
+ white-space: nowrap;
190
+ }
191
+
192
+ .btn:active {
193
+ transform: scale(0.97);
194
+ }
195
+
196
+ .btn--primary {
197
+ background: var(--clr-accent);
198
+ color: #000;
199
+ }
200
+
201
+ .btn--primary:hover {
202
+ background: var(--clr-accent-hover);
203
+ }
204
+
205
+ .btn--secondary {
206
+ background: var(--clr-surface-raised);
207
+ color: var(--clr-text);
208
+ border: 1px solid var(--clr-border);
209
+ }
210
+
211
+ .btn--secondary:hover {
212
+ background: var(--clr-border);
213
+ }
214
+
215
+ .scan-progress {
216
+ margin-top: 0.65rem;
217
+ height: 3px;
218
+ background: var(--clr-border);
219
+ border-radius: 2px;
220
+ overflow: hidden;
221
+ flex-shrink: 0;
222
+ }
223
+
224
+ .scan-progress__bar {
225
+ height: 100%;
226
+ width: 35%;
227
+ border-radius: 2px;
228
+ background: linear-gradient(90deg, var(--clr-accent), var(--clr-accent-hover));
229
+ opacity: 0;
230
+ }
231
+
232
+ .scan-progress--active .scan-progress__bar {
233
+ opacity: 1;
234
+ will-change: transform, opacity;
235
+ animation: scan-progress-slide 1s ease-in-out infinite;
236
+ }
237
+
238
+ .scan-progress--active {
239
+ background: var(--clr-surface-raised);
240
+ }
241
+
242
+ @keyframes scan-progress-slide {
243
+ 0% {
244
+ transform: translateX(-120%);
245
+ }
246
+ 100% {
247
+ transform: translateX(420%);
248
+ }
249
+ }
250
+
251
+ .btn--working {
252
+ opacity: 0.85;
253
+ pointer-events: none;
254
+ }
255
+
256
+ /* ===== Server folder browser (modal) ===== */
257
+ .fs-browser {
258
+ position: fixed;
259
+ inset: 0;
260
+ z-index: 11000;
261
+ display: flex;
262
+ align-items: center;
263
+ justify-content: center;
264
+ padding: 1rem;
265
+ }
266
+
267
+ .fs-browser.hidden {
268
+ display: none;
269
+ }
270
+
271
+ .fs-browser__backdrop {
272
+ position: absolute;
273
+ inset: 0;
274
+ background: rgba(0, 0, 0, 0.55);
275
+ }
276
+
277
+ .fs-browser__panel {
278
+ position: relative;
279
+ z-index: 1;
280
+ width: 100%;
281
+ max-width: min(36rem, 96vw);
282
+ max-height: min(28rem, 88vh);
283
+ display: flex;
284
+ flex-direction: column;
285
+ background: var(--clr-surface);
286
+ border: 1px solid var(--clr-border);
287
+ border-radius: var(--radius);
288
+ box-shadow: 0 16px 48px rgba(0, 0, 0, 0.5);
289
+ }
290
+
291
+ .fs-browser__head {
292
+ display: flex;
293
+ align-items: center;
294
+ justify-content: space-between;
295
+ padding: 0.65rem 1rem;
296
+ border-bottom: 1px solid var(--clr-border);
297
+ }
298
+
299
+ .fs-browser__title {
300
+ font-size: 1rem;
301
+ font-weight: 600;
302
+ color: var(--clr-text);
303
+ }
304
+
305
+ .fs-browser__close {
306
+ border: none;
307
+ background: transparent;
308
+ color: var(--clr-text-muted);
309
+ font-size: 1.5rem;
310
+ line-height: 1;
311
+ cursor: pointer;
312
+ padding: 0.15rem 0.45rem;
313
+ border-radius: var(--radius);
314
+ }
315
+
316
+ .fs-browser__close:hover {
317
+ color: var(--clr-text);
318
+ background: var(--clr-surface-raised);
319
+ }
320
+
321
+ .fs-browser__path-row {
322
+ display: flex;
323
+ gap: 0.5rem;
324
+ padding: 0.65rem 1rem;
325
+ align-items: center;
326
+ }
327
+
328
+ .fs-browser__path-input {
329
+ flex: 1;
330
+ min-width: 0;
331
+ }
332
+
333
+ .fs-browser__opts {
334
+ padding: 0 1rem 0.5rem;
335
+ user-select: none;
336
+ }
337
+
338
+ .fs-browser__hidden-label {
339
+ display: inline-flex;
340
+ align-items: center;
341
+ gap: 0.4rem;
342
+ cursor: pointer;
343
+ font-size: 0.85rem;
344
+ color: var(--clr-text-muted);
345
+ }
346
+
347
+ .fs-browser__hidden-label:hover {
348
+ color: var(--clr-text);
349
+ }
350
+
351
+ .fs-browser__error {
352
+ padding: 0 1rem 0.35rem;
353
+ color: var(--clr-danger);
354
+ font-size: 0.85rem;
355
+ }
356
+
357
+ .fs-browser__error.hidden {
358
+ display: none;
359
+ }
360
+
361
+ .fs-browser__list {
362
+ list-style: none;
363
+ margin: 0;
364
+ padding: 0.25rem 0;
365
+ overflow-y: auto;
366
+ flex: 1;
367
+ min-height: 7rem;
368
+ max-height: 14rem;
369
+ border-top: 1px solid var(--clr-border);
370
+ border-bottom: 1px solid var(--clr-border);
371
+ }
372
+
373
+ .fs-browser__list li {
374
+ padding: 0.35rem 1rem;
375
+ cursor: default;
376
+ font-family: var(--font-mono);
377
+ font-size: 0.85rem;
378
+ color: var(--clr-text-muted);
379
+ }
380
+
381
+ .fs-browser__list li[data-type="directory"] {
382
+ cursor: pointer;
383
+ color: var(--clr-accent);
384
+ }
385
+
386
+ .fs-browser__list li[data-type="directory"]:hover {
387
+ background: var(--clr-surface-raised);
388
+ }
389
+
390
+ .fs-browser__list .fs-browser__loading,
391
+ .fs-browser__list .fs-browser__empty {
392
+ color: var(--clr-text-muted);
393
+ font-style: italic;
394
+ cursor: default;
395
+ }
396
+
397
+ .fs-browser__actions {
398
+ display: flex;
399
+ gap: 0.5rem;
400
+ padding: 0.65rem 1rem;
401
+ flex-wrap: wrap;
402
+ }
403
+
404
+ .fs-browser__alt {
405
+ padding: 0 1rem 0.85rem;
406
+ font-size: 0.8rem;
407
+ color: var(--clr-text-muted);
408
+ }
409
+
410
+ .fs-browser__linkbtn {
411
+ border: none;
412
+ background: none;
413
+ color: var(--clr-accent);
414
+ cursor: pointer;
415
+ text-decoration: underline;
416
+ padding: 0;
417
+ font: inherit;
418
+ }
419
+
420
+ .fs-browser__linkbtn:hover {
421
+ color: var(--clr-accent-hover);
422
+ }
423
+
424
+ /* ===== Folder flow popup (non-blocking; sits under OS file dialog) ===== */
425
+ .folder-flow-pop {
426
+ position: fixed;
427
+ inset: 0;
428
+ z-index: 10000;
429
+ display: flex;
430
+ align-items: center;
431
+ justify-content: center;
432
+ padding: 1rem;
433
+ pointer-events: none;
434
+ }
435
+
436
+ .folder-flow-pop.hidden {
437
+ display: none;
438
+ }
439
+
440
+ .folder-flow-pop__backdrop {
441
+ position: absolute;
442
+ inset: 0;
443
+ background: rgba(0, 0, 0, 0.5);
444
+ pointer-events: none;
445
+ }
446
+
447
+ .folder-flow-pop__panel {
448
+ position: relative;
449
+ width: 100%;
450
+ max-width: 22rem;
451
+ padding: 1.25rem 1.35rem;
452
+ background: var(--clr-surface);
453
+ border: 1px solid var(--clr-border);
454
+ border-radius: var(--radius);
455
+ box-shadow: 0 12px 40px rgba(0, 0, 0, 0.45);
456
+ pointer-events: none;
457
+ }
458
+
459
+ .folder-flow-pop__title {
460
+ font-size: 0.72rem;
461
+ font-weight: 600;
462
+ text-transform: uppercase;
463
+ letter-spacing: 0.06em;
464
+ color: var(--clr-text-muted);
465
+ margin-bottom: 0.45rem;
466
+ }
467
+
468
+ .folder-flow-pop__msg {
469
+ font-size: 0.95rem;
470
+ color: var(--clr-text);
471
+ line-height: 1.45;
472
+ min-height: 2.8em;
473
+ }
474
+
475
+ .folder-flow-pop__track {
476
+ margin-top: 1rem;
477
+ height: 3px;
478
+ background: var(--clr-border);
479
+ border-radius: 2px;
480
+ overflow: hidden;
481
+ }
482
+
483
+ .folder-flow-pop__bar {
484
+ height: 100%;
485
+ width: 35%;
486
+ border-radius: 2px;
487
+ background: linear-gradient(90deg, var(--clr-accent), var(--clr-accent-hover));
488
+ animation: scan-progress-slide 1s ease-in-out infinite;
489
+ }
490
+
491
+ /* ===== Summary ===== */
492
+ .summary {
493
+ display: flex;
494
+ gap: 2rem;
495
+ padding: 0.75rem 1.5rem;
496
+ background: var(--clr-surface);
497
+ border: 1px solid var(--clr-border);
498
+ border-radius: var(--radius);
499
+ margin-bottom: 1rem;
500
+ }
501
+
502
+ .summary__stat {
503
+ font-size: 0.9rem;
504
+ color: var(--clr-text-muted);
505
+ }
506
+
507
+ .summary__stat strong {
508
+ color: var(--clr-text);
509
+ }
510
+
511
+ /* ===== Status ===== */
512
+ .status {
513
+ text-align: center;
514
+ padding: 4rem 2rem;
515
+ color: var(--clr-text-muted);
516
+ }
517
+
518
+ .status__message {
519
+ font-size: 1rem;
520
+ }
521
+
522
+ /* ===== Results table ===== */
523
+ .results-container {
524
+ overflow-x: auto;
525
+ }
526
+
527
+ .results-table {
528
+ width: 100%;
529
+ border-collapse: collapse;
530
+ font-size: 0.85rem;
531
+ }
532
+
533
+ .results-table__th {
534
+ text-align: left;
535
+ padding: 0.6rem 0.75rem;
536
+ font-size: 0.75rem;
537
+ font-weight: 600;
538
+ text-transform: uppercase;
539
+ letter-spacing: 0.04em;
540
+ color: var(--clr-text-muted);
541
+ border-bottom: 2px solid var(--clr-border);
542
+ position: sticky;
543
+ top: 0;
544
+ background: var(--clr-bg);
545
+ white-space: nowrap;
546
+ }
547
+
548
+ .results-table__th--num {
549
+ width: 3rem;
550
+ text-align: right;
551
+ }
552
+
553
+ .results-table__th--size,
554
+ .results-table__th--dims,
555
+ .results-table__th--uncomp,
556
+ .results-table__th--score {
557
+ text-align: right;
558
+ width: 8rem;
559
+ }
560
+
561
+ .results-table__th--bar {
562
+ width: 10rem;
563
+ }
564
+
565
+ .results-table td {
566
+ padding: 0.5rem 0.75rem;
567
+ border-bottom: 1px solid var(--clr-border);
568
+ vertical-align: middle;
569
+ }
570
+
571
+ .results-table tbody tr:hover {
572
+ background: var(--clr-surface-raised);
573
+ }
574
+
575
+ .cell--num {
576
+ text-align: right;
577
+ color: var(--clr-text-muted);
578
+ font-variant-numeric: tabular-nums;
579
+ }
580
+
581
+ .cell--path {
582
+ font-family: var(--font-mono);
583
+ font-size: 0.8rem;
584
+ word-break: break-all;
585
+ max-width: 400px;
586
+ }
587
+
588
+ .cell--path__dir {
589
+ color: var(--clr-text-muted);
590
+ }
591
+
592
+ .cell--path__file {
593
+ color: var(--clr-text);
594
+ font-weight: 600;
595
+ }
596
+
597
+ .cell--path__link {
598
+ text-decoration: none;
599
+ }
600
+
601
+ .cell--path__link:hover {
602
+ text-decoration: underline;
603
+ color: var(--clr-accent);
604
+ }
605
+
606
+ .cell--size,
607
+ .cell--dims,
608
+ .cell--uncomp {
609
+ text-align: right;
610
+ font-variant-numeric: tabular-nums;
611
+ white-space: nowrap;
612
+ }
613
+
614
+ .cell--score {
615
+ text-align: right;
616
+ font-weight: 700;
617
+ font-variant-numeric: tabular-nums;
618
+ }
619
+
620
+ .cell--bar {
621
+ padding-right: 1rem;
622
+ }
623
+
624
+ /* Score colour classes */
625
+ .score--good {
626
+ color: var(--clr-score-good);
627
+ }
628
+
629
+ .score--mid {
630
+ color: var(--clr-score-mid);
631
+ }
632
+
633
+ .score--bad {
634
+ color: var(--clr-score-bad);
635
+ }
636
+
637
+ .score--na {
638
+ color: var(--clr-text-muted);
639
+ font-weight: 400;
640
+ }
641
+
642
+ /* Compression bar */
643
+ .bar {
644
+ height: 8px;
645
+ border-radius: 4px;
646
+ background: var(--clr-border);
647
+ overflow: hidden;
648
+ position: relative;
649
+ }
650
+
651
+ .bar__fill {
652
+ height: 100%;
653
+ border-radius: 4px;
654
+ transition: width 0.3s ease;
655
+ }
656
+
657
+ .bar__fill--good {
658
+ background: var(--clr-score-good);
659
+ }
660
+
661
+ .bar__fill--mid {
662
+ background: var(--clr-score-mid);
663
+ }
664
+
665
+ .bar__fill--bad {
666
+ background: var(--clr-score-bad);
667
+ }
668
+
669
+ /* ===== Footer ===== */
670
+ .footer {
671
+ text-align: center;
672
+ padding: 1rem 2rem;
673
+ font-size: 0.75rem;
674
+ color: var(--clr-text-muted);
675
+ border-top: 1px solid var(--clr-border);
676
+ }
677
+
678
+ /* ===== Utility ===== */
679
+ .hidden {
680
+ display: none !important;
681
+ }
682
+
683
+ /* ===== Settings dropdown ===== */
684
+ .top-bar {
685
+ justify-content: space-between;
686
+ }
687
+
688
+ .top-bar__left {
689
+ display: flex;
690
+ flex-direction: column;
691
+ gap: 0.25rem;
692
+ }
693
+
694
+ .settings-container {
695
+ position: relative;
696
+ display: flex;
697
+ align-items: center;
698
+ }
699
+
700
+ .settings-button {
701
+ background: none;
702
+ border: none;
703
+ color: var(--clr-text-muted);
704
+ cursor: pointer;
705
+ font-size: 1.2rem;
706
+ padding: 0.5rem;
707
+ border-radius: var(--radius);
708
+ transition: all 0.2s ease;
709
+ }
710
+
711
+ .settings-button:hover {
712
+ color: var(--clr-accent);
713
+ background: rgba(88, 166, 255, 0.1);
714
+ }
715
+
716
+ .settings-dropdown {
717
+ position: absolute;
718
+ top: 100%;
719
+ right: 0;
720
+ margin-top: 0.5rem;
721
+ background: var(--clr-surface-raised);
722
+ border: 1px solid var(--clr-border);
723
+ border-radius: var(--radius);
724
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
725
+ min-width: 200px;
726
+ z-index: 1000;
727
+ }
728
+
729
+ .settings-item {
730
+ padding: 0.75rem 1rem;
731
+ }
732
+
733
+ .settings-label {
734
+ display: flex;
735
+ align-items: center;
736
+ gap: 0.5rem;
737
+ cursor: pointer;
738
+ font-size: 0.95rem;
739
+ user-select: none;
740
+ }
741
+
742
+ .settings-checkbox {
743
+ cursor: pointer;
744
+ width: 16px;
745
+ height: 16px;
746
+ accent-color: var(--clr-accent);
747
+ }
748
+
749
+ .settings-hint {
750
+ font-size: 0.8rem;
751
+ color: var(--clr-text-muted);
752
+ margin: 0.5rem 0 0 1.5rem;
753
+ margin-top: 0.35rem;
754
+ }
755
+
756
+ .settings-divider {
757
+ border: none;
758
+ border-top: 1px solid var(--clr-border);
759
+ margin: 0;
760
+ }
761
+
762
+ .settings-action {
763
+ display: block;
764
+ width: 100%;
765
+ padding: 0.75rem 1rem;
766
+ background: none;
767
+ border: none;
768
+ color: var(--clr-danger);
769
+ cursor: pointer;
770
+ text-align: left;
771
+ font-size: 0.95rem;
772
+ transition: background 0.2s ease;
773
+ }
774
+
775
+ .settings-action:hover {
776
+ background: rgba(248, 81, 73, 0.1);
777
+ }
778
+
779
+ /* ===== Responsive ===== */
780
+ @media (max-width: 768px) {
781
+ .main {
782
+ padding: 1rem;
783
+ }
784
+
785
+ .controls__group--row {
786
+ flex-direction: column;
787
+ }
788
+
789
+ .controls__text-input--short {
790
+ max-width: none;
791
+ }
792
+
793
+ .summary {
794
+ flex-direction: column;
795
+ gap: 0.5rem;
796
+ }
797
+ }