@attrus-ds/core 0.1.1 → 0.3.1

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 (82) hide show
  1. package/LICENSE +27 -27
  2. package/README.md +63 -9
  3. package/dist/assets/flags/Brazil-rounded.svg +15 -15
  4. package/dist/assets/flags/Brazil-square.svg +16 -16
  5. package/dist/assets/flags/Chile-rounded.svg +10 -10
  6. package/dist/assets/flags/Chile-square.svg +10 -10
  7. package/dist/assets/flags/Colombia-rounded.svg +9 -9
  8. package/dist/assets/flags/Colombia-square.svg +9 -9
  9. package/dist/assets/flags/EUA-rounded.svg +10 -10
  10. package/dist/assets/flags/EUA-square.svg +10 -10
  11. package/dist/assets/flags/EUR-rounded.svg +8 -8
  12. package/dist/assets/flags/EUR-square.svg +8 -8
  13. package/dist/assets/flags/Mexico-rounded.svg +18 -18
  14. package/dist/assets/flags/Mexico-square.svg +18 -18
  15. package/dist/assets/icon-attrus-dark.svg +2 -2
  16. package/dist/assets/icon-attrus-light.svg +2 -2
  17. package/dist/assets/logo-attrus-dark.svg +7 -7
  18. package/dist/assets/logo-attrus-light.svg +7 -7
  19. package/dist/css/components/buttons.css +301 -301
  20. package/dist/css/components/callout.css +195 -195
  21. package/dist/css/components/cards.css +272 -272
  22. package/dist/css/components/combobox.css +319 -303
  23. package/dist/css/components/empty-states.css +61 -61
  24. package/dist/css/components/inputs.css +975 -1027
  25. package/dist/css/components/number-input.css +138 -138
  26. package/dist/css/components/overlays.css +374 -405
  27. package/dist/css/components/primitives.css +314 -305
  28. package/dist/css/components/sidebar.css +179 -179
  29. package/dist/css/components/smart-search.css +91 -0
  30. package/dist/css/components/status-pills.css +207 -207
  31. package/dist/css/components/stepper.css +309 -309
  32. package/dist/css/components/table.css +602 -600
  33. package/dist/css/components/tabs.css +319 -319
  34. package/dist/css/components/toast.css +243 -239
  35. package/dist/css/components/tooltip.css +231 -226
  36. package/dist/css/components/topbar.css +148 -148
  37. package/dist/css/index.css +1 -0
  38. package/dist/css/tokens.css +666 -666
  39. package/dist/react/AppSidebar/AppSidebar.d.ts +50 -0
  40. package/dist/react/AppSidebar/AppSidebar.tsx +197 -0
  41. package/dist/react/AppTopbar/AppTopbar.d.ts +58 -0
  42. package/dist/react/AppTopbar/AppTopbar.tsx +203 -0
  43. package/dist/react/Atoms/Atoms.d.ts +149 -0
  44. package/dist/react/Atoms/Atoms.tsx +407 -0
  45. package/dist/react/Button/Button.d.ts +25 -0
  46. package/dist/react/Button/Button.tsx +83 -0
  47. package/dist/react/Callout/Callout.d.ts +29 -0
  48. package/dist/react/Callout/Callout.tsx +98 -0
  49. package/dist/react/Card/Card.d.ts +38 -0
  50. package/dist/react/Card/Card.tsx +93 -0
  51. package/dist/react/Combobox/Combobox.d.ts +52 -0
  52. package/dist/react/Combobox/Combobox.tsx +254 -0
  53. package/dist/react/DataTable/DataTable.d.ts +81 -0
  54. package/dist/react/DataTable/DataTable.tsx +308 -0
  55. package/dist/react/Drawer/Drawer.d.ts +35 -0
  56. package/dist/react/Drawer/Drawer.tsx +226 -0
  57. package/dist/react/Input/Input.d.ts +155 -0
  58. package/dist/react/Input/Input.tsx +478 -0
  59. package/dist/react/Modal/Modal.d.ts +27 -0
  60. package/dist/react/Modal/Modal.tsx +173 -0
  61. package/dist/react/NumberInput/NumberInput.d.ts +25 -0
  62. package/dist/react/NumberInput/NumberInput.tsx +117 -0
  63. package/dist/react/Pill/Pill.d.ts +31 -0
  64. package/dist/react/Pill/Pill.tsx +104 -0
  65. package/dist/react/SearchPill/SearchPill.d.ts +14 -0
  66. package/dist/react/SearchPill/SearchPill.tsx +40 -0
  67. package/dist/react/SmartSearch/SmartSearch.d.ts +65 -0
  68. package/dist/react/SmartSearch/SmartSearch.tsx +271 -0
  69. package/dist/react/Stepper/Stepper.d.ts +25 -0
  70. package/dist/react/Stepper/Stepper.tsx +94 -0
  71. package/dist/react/Tabs/Tabs.d.ts +32 -0
  72. package/dist/react/Tabs/Tabs.tsx +147 -0
  73. package/dist/react/Toast/Toast.d.ts +30 -0
  74. package/dist/react/Toast/Toast.tsx +94 -0
  75. package/dist/react/Tooltip/Tooltip.d.ts +23 -0
  76. package/dist/react/Tooltip/Tooltip.tsx +80 -0
  77. package/dist/react/ZeroState/ZeroState.d.ts +17 -0
  78. package/dist/react/ZeroState/ZeroState.tsx +45 -0
  79. package/dist/react/index.ts +35 -0
  80. package/dist/tokens/attrus-tokens.flat.json +1234 -1234
  81. package/dist/tokens/attrus-tokens.json +1089 -1089
  82. package/package.json +64 -54
@@ -1,1027 +1,975 @@
1
- /* ============================================================
2
- INPUTS & FORM CONTROLS — ATTRUS Design System
3
- Component CSS — pure tokens, no hardcoded values.
4
-
5
- Anatomy (vertical stack):
6
- [label] .field > label
7
- [field row] .field > .input | .group | .affix
8
- [helper / error] .field > .help
9
-
10
- Field variants:
11
- .input base text input (size: md = 40px)
12
- .input.is-error danger border + danger focus ring
13
- .input.is-success success border + success focus ring
14
- .input[disabled] opacity .5 + bg-subtle
15
- .input.is-readonly no border, only value text
16
-
17
- Sizes (apply to .input or wrapper):
18
- .input-sm (32h, fs-sm)
19
- default (40h, fs-md)
20
- .input-lg (48h, fs-lg)
21
-
22
- Wrappers (compose with .input):
23
- .affix .lead | .trail leading / trailing icon
24
- .group .fix.l | .fix.r prefix / suffix attached
25
- .pill-search ⌘K-style search bar
26
- .underline ghost / quiet input
27
- .chips multi-select with embedded text input
28
- .otp one-time-password digit cells
29
-
30
- Other controls (siblings, not .input):
31
- .select wrapper around <select>
32
- .switch toggle
33
- .check custom checkbox
34
- .radio custom radio
35
- .number-input number input with +/- buttons
36
- .file-drop dropzone
37
- .slider range with track + thumb
38
- .date-trigger trigger button for date/time picker
39
-
40
- Inverse:
41
- .input.on-inverse dark-aware text input
42
- surfaces with .dark inherit white-on-dark text
43
- ============================================================ */
44
-
45
-
46
- /* ----------------------------------------------------------------
47
- 1. FIELD WRAPPER
48
- ---------------------------------------------------------------- */
49
- .field {
50
- display: flex;
51
- flex-direction: column;
52
- gap: var(--space-2);
53
- min-width: 0;
54
- }
55
- .field > label {
56
- font-family: var(--font-sans);
57
- font-size: var(--fs-sm);
58
- font-weight: var(--w-semibold);
59
- color: var(--color-foreground-default);
60
- letter-spacing: var(--ls-body);
61
- }
62
- .field > label .req {
63
- color: var(--color-feedback-danger-foreground-muted);
64
- margin-left: 2px;
65
- }
66
- .field > label .opt {
67
- color: var(--color-foreground-muted);
68
- font-weight: var(--w-regular);
69
- margin-left: var(--space-2);
70
- font-size: var(--fs-xs);
71
- }
72
- .field > .help {
73
- font-size: var(--fs-xs);
74
- color: var(--color-foreground-muted);
75
- display: flex;
76
- gap: var(--space-2);
77
- align-items: flex-start;
78
- line-height: var(--lh-snug, 1.4);
79
- }
80
- .field > .help i, .field > .help svg,
81
- .field > .help svg {
82
- width: var(--icon-xs);
83
- height: var(--icon-xs);
84
- stroke-width: var(--icon-stroke);
85
- flex-shrink: 0;
86
- margin-top: 1px;
87
- }
88
- .field > .help.is-error { color: var(--color-feedback-danger-foreground); }
89
- .field > .help.is-error i, .field > .help.is-error svg { color: var(--color-feedback-danger-foreground-muted); }
90
- .field > .help.is-success { color: var(--color-feedback-success-foreground); }
91
- .field > .help.is-success i, .field > .help.is-success svg{ color: var(--color-feedback-success-foreground-muted); }
92
- .field > .help.is-warning { color: var(--color-feedback-warning-foreground); }
93
- .field > .help.is-warning i, .field > .help.is-warning svg{ color: var(--color-feedback-warning-foreground-muted); }
94
-
95
-
96
- /* ----------------------------------------------------------------
97
- 2. BASE INPUT
98
- ---------------------------------------------------------------- */
99
- .input {
100
- /* Box — default size = md (40px tall) */
101
- width: 100%;
102
- box-sizing: border-box;
103
- height: 40px;
104
- padding: 0 var(--space-4);
105
- border: 1px solid var(--color-border-default);
106
- border-radius: var(--radius-sm);
107
- background: var(--color-background-surface);
108
- color: var(--color-foreground-default);
109
-
110
- /* Type */
111
- font-family: var(--font-sans);
112
- font-size: var(--fs-md);
113
- font-weight: var(--w-regular);
114
- line-height: 1;
115
- letter-spacing: var(--ls-body);
116
-
117
- /* Behavior */
118
- outline: none;
119
- transition:
120
- background var(--transition-fast),
121
- border-color var(--transition-fast),
122
- color var(--transition-fast),
123
- box-shadow var(--transition-fast);
124
- }
125
- .input::placeholder { color: var(--color-foreground-placeholder); }
126
-
127
- .input:hover:not(:focus):not([disabled]):not(.is-error):not(.is-success) {
128
- border-color: var(--color-border-strong);
129
- }
130
- .input:focus,
131
- .input:focus-visible {
132
- border-color: var(--color-border-focus);
133
- box-shadow: var(--shadow-focus);
134
- }
135
- .input[disabled],
136
- .input.is-disabled {
137
- background: var(--color-background-subtle);
138
- color: var(--color-foreground-placeholder);
139
- cursor: not-allowed;
140
- opacity: 0.7;
141
- }
142
- .input[readonly],
143
- .input.is-readonly {
144
- background: transparent;
145
- border-color: transparent;
146
- padding-left: 0;
147
- padding-right: 0;
148
- cursor: default;
149
- }
150
-
151
- /* Validation states */
152
- .input.is-error { border-color: var(--color-border-danger); }
153
- .input.is-error:focus { box-shadow: var(--shadow-focus-danger); }
154
- .input.is-success { border-color: var(--color-border-success); }
155
- .input.is-success:focus { box-shadow: var(--shadow-focus-success); }
156
- .input.is-warning { border-color: var(--color-border-warning); }
157
- .input.is-warning:focus { box-shadow: var(--shadow-focus-warning); }
158
-
159
- /* Textarea */
160
- textarea.input {
161
- height: auto;
162
- min-height: 88px;
163
- padding: var(--space-3) var(--space-4);
164
- resize: vertical;
165
- line-height: var(--lh-normal, 1.5);
166
- }
167
-
168
-
169
- /* ----------------------------------------------------------------
170
- 3. SIZES
171
- ---------------------------------------------------------------- */
172
- .input-sm {
173
- height: 32px;
174
- padding: 0 var(--space-3);
175
- font-size: var(--fs-sm);
176
- border-radius: var(--radius-xs);
177
- }
178
- .input-lg {
179
- height: 48px;
180
- padding: 0 var(--space-5);
181
- font-size: var(--fs-lg);
182
- border-radius: var(--radius-md);
183
- }
184
- textarea.input.input-sm { height: auto; min-height: 64px; padding: var(--space-2) var(--space-3); }
185
- textarea.input.input-lg { height: auto; min-height: 120px; padding: var(--space-4) var(--space-5); }
186
-
187
-
188
- /* ----------------------------------------------------------------
189
- 4. AFFIX — leading / trailing icon
190
- ---------------------------------------------------------------- */
191
- .affix { position: relative; display: block; }
192
- .affix .lead,
193
- .affix .trail {
194
- position: absolute;
195
- top: 50%;
196
- transform: translateY(-50%);
197
- width: var(--icon-sm);
198
- height: var(--icon-sm);
199
- stroke-width: var(--icon-stroke);
200
- color: var(--color-foreground-muted);
201
- pointer-events: none;
202
- flex-shrink: 0;
203
- }
204
- .affix .lead { left: var(--space-4); }
205
- .affix .trail { right: var(--space-4); }
206
-
207
- /* Pad the input to make room for the icon */
208
- .affix.lead-pad > .input { padding-left: calc(var(--space-4) + var(--icon-sm) + var(--space-3)); }
209
- .affix.trail-pad > .input { padding-right: calc(var(--space-4) + var(--icon-sm) + var(--space-3)); }
210
-
211
- /* Trailing button (e.g. password reveal, clear) — clickable */
212
- .affix .trail-btn {
213
- position: absolute;
214
- top: 50%;
215
- right: var(--space-2);
216
- transform: translateY(-50%);
217
- width: 28px;
218
- height: 28px;
219
- display: inline-flex;
220
- align-items: center;
221
- justify-content: center;
222
- border: 0;
223
- border-radius: var(--radius-xs);
224
- background: transparent;
225
- color: var(--color-foreground-muted);
226
- cursor: pointer;
227
- transition: background var(--transition-fast), color var(--transition-fast);
228
- }
229
- .affix .trail-btn:hover { background: var(--color-background-hover); color: var(--color-foreground-default); }
230
- .affix .trail-btn:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
231
- .affix .trail-btn > i, .affix .trail-btn > svg,
232
- .affix .trail-btn > svg { width: var(--icon-xs); height: var(--icon-xs); stroke-width: var(--icon-stroke); }
233
-
234
-
235
- /* ----------------------------------------------------------------
236
- 5. PREFIX / SUFFIX GROUP
237
- ---------------------------------------------------------------- */
238
- .group {
239
- display: flex;
240
- align-items: stretch;
241
- width: 100%;
242
- height: 40px;
243
- border: 1px solid var(--color-border-default);
244
- border-radius: var(--radius-sm);
245
- background: var(--color-background-surface);
246
- overflow: hidden;
247
- transition:
248
- border-color var(--transition-fast),
249
- box-shadow var(--transition-fast);
250
- }
251
- .group:focus-within {
252
- border-color: var(--color-border-focus);
253
- box-shadow: var(--shadow-focus);
254
- }
255
- .group .fix {
256
- display: flex;
257
- align-items: center;
258
- gap: var(--space-2);
259
- padding: 0 var(--space-4);
260
- background: var(--color-background-subtle);
261
- color: var(--color-foreground-muted);
262
- font-family: var(--font-mono);
263
- font-size: var(--fs-sm);
264
- flex-shrink: 0;
265
- }
266
- .group .fix.l { border-right: 1px solid var(--color-border-default); }
267
- .group .fix.r { border-left: 1px solid var(--color-border-default); }
268
- .group > input {
269
- flex: 1;
270
- min-width: 0;
271
- border: 0;
272
- outline: none;
273
- padding: 0 var(--space-4);
274
- background: transparent;
275
- color: var(--color-foreground-default);
276
- font-family: var(--font-sans);
277
- font-size: var(--fs-md);
278
- }
279
- .group > input::placeholder { color: var(--color-foreground-placeholder); }
280
- .group.is-error { border-color: var(--color-border-danger); }
281
- .group.is-error:focus-within { box-shadow: var(--shadow-focus-danger); }
282
-
283
-
284
- /* ----------------------------------------------------------------
285
- 6. PILL SEARCH (⌘K-style)
286
- ---------------------------------------------------------------- */
287
- .pill-search {
288
- display: flex;
289
- align-items: center;
290
- gap: var(--space-3);
291
- height: 40px;
292
- padding: 0 var(--space-4);
293
- border: 1px solid var(--color-border-default);
294
- border-radius: var(--radius-pill);
295
- background: var(--color-background-surface);
296
- transition:
297
- border-color var(--transition-fast),
298
- box-shadow var(--transition-fast),
299
- background var(--transition-fast);
300
- }
301
- .pill-search:hover { border-color: var(--color-border-strong); }
302
- .pill-search:focus-within {
303
- border-color: var(--color-border-focus);
304
- box-shadow: var(--shadow-focus);
305
- }
306
- .pill-search > i, .pill-search > svg,
307
- .pill-search > svg {
308
- width: var(--icon-sm);
309
- height: var(--icon-sm);
310
- stroke-width: var(--icon-stroke);
311
- color: var(--color-foreground-muted);
312
- flex-shrink: 0;
313
- }
314
- .pill-search > input {
315
- flex: 1;
316
- min-width: 0;
317
- border: 0;
318
- outline: none;
319
- background: transparent;
320
- font-family: var(--font-sans);
321
- font-size: var(--fs-md);
322
- color: var(--color-foreground-default);
323
- }
324
- .pill-search > input::placeholder { color: var(--color-foreground-placeholder); }
325
- .kbd {
326
- display: inline-flex;
327
- align-items: center;
328
- font-family: var(--font-mono);
329
- font-size: var(--fs-xxs);
330
- font-weight: var(--w-semibold);
331
- color: var(--color-foreground-muted);
332
- background: var(--color-background-subtle);
333
- border: 1px solid var(--color-border-default);
334
- border-radius: var(--radius-2xs);
335
- padding: 1px var(--space-2);
336
- letter-spacing: var(--ls-caps);
337
- }
338
-
339
-
340
- /* ----------------------------------------------------------------
341
- 7. UNDERLINE — quiet / ghost input
342
- ---------------------------------------------------------------- */
343
- .input.underline {
344
- border: 0;
345
- border-bottom: 1px solid var(--color-border-default);
346
- border-radius: 0;
347
- background: transparent;
348
- padding: var(--space-2) 0;
349
- }
350
- .input.underline:focus,
351
- .input.underline:focus-visible {
352
- border-bottom-color: var(--color-border-focus);
353
- box-shadow: none;
354
- }
355
- .input.underline.is-error { border-bottom-color: var(--color-border-danger); }
356
-
357
-
358
- /* ----------------------------------------------------------------
359
- 8. CHIPS — multi-select / token input
360
- ---------------------------------------------------------------- */
361
- .chips {
362
- display: flex;
363
- flex-wrap: wrap;
364
- gap: 6px;
365
- align-items: center;
366
- min-height: 40px;
367
- padding: 5px 8px;
368
- border: 1px solid var(--color-border-default);
369
- border-radius: var(--radius-sm);
370
- background: var(--color-background-surface);
371
- transition:
372
- border-color var(--transition-fast),
373
- box-shadow var(--transition-fast);
374
- }
375
- .chips:hover { border-color: var(--color-border-strong); }
376
- .chips:focus-within {
377
- border-color: var(--color-border-focus);
378
- box-shadow: var(--shadow-focus);
379
- }
380
- .chip {
381
- display: inline-flex;
382
- align-items: center;
383
- gap: 4px;
384
- height: 26px;
385
- padding: 0 4px 0 10px;
386
- background: var(--color-background-subtle);
387
- color: var(--color-foreground-default);
388
- border: 1px solid var(--color-border-default);
389
- border-radius: var(--radius-xs);
390
- font-family: var(--font-sans);
391
- font-size: var(--fs-xs);
392
- font-weight: var(--w-medium);
393
- line-height: 1;
394
- white-space: nowrap;
395
- flex-shrink: 0;
396
- transition: background var(--transition-fast), border-color var(--transition-fast);
397
- }
398
- .chip:hover { background: var(--color-background-canvas); border-color: var(--color-border-strong); }
399
- .chip-x {
400
- display: inline-flex;
401
- align-items: center;
402
- justify-content: center;
403
- width: 18px;
404
- height: 18px;
405
- margin-left: 2px;
406
- border: 0;
407
- padding: 0;
408
- background: transparent;
409
- cursor: pointer;
410
- border-radius: var(--radius-xs);
411
- color: var(--color-foreground-muted);
412
- transition: background var(--transition-fast), color var(--transition-fast);
413
- }
414
- .chip-x svg, .chip-x i { width: 11px; height: 11px; stroke-width: 2.25; }
415
- .chip-x:hover { background: var(--color-border-default); color: var(--color-foreground-default); }
416
- .chips > input {
417
- flex: 1;
418
- min-width: 80px;
419
- height: 26px;
420
- border: 0;
421
- outline: none;
422
- background: transparent;
423
- font-family: var(--font-sans);
424
- font-size: var(--fs-sm);
425
- padding: 0 6px;
426
- color: var(--color-foreground-default);
427
- }
428
- .chips > input::placeholder { color: var(--color-foreground-placeholder); }
429
-
430
-
431
- /* ----------------------------------------------------------------
432
- 9. SELECT (native)
433
- ---------------------------------------------------------------- */
434
- .select {
435
- position: relative;
436
- display: block;
437
- }
438
- .select > select.input {
439
- appearance: none;
440
- -webkit-appearance: none;
441
- cursor: pointer;
442
- padding-right: calc(var(--space-4) + var(--icon-sm) + var(--space-3));
443
- background-image: none;
444
- }
445
- .select > .chev {
446
- position: absolute;
447
- top: 50%;
448
- right: var(--space-4);
449
- transform: translateY(-50%);
450
- width: var(--icon-sm);
451
- height: var(--icon-sm);
452
- stroke-width: var(--icon-stroke);
453
- color: var(--color-foreground-muted);
454
- pointer-events: none;
455
- }
456
-
457
-
458
- /* ----------------------------------------------------------------
459
- 10. NUMBER INPUT (with +/- buttons)
460
- ---------------------------------------------------------------- */
461
- .number-input {
462
- display: inline-flex;
463
- align-items: stretch;
464
- height: 40px;
465
- border: 1px solid var(--color-border-default);
466
- border-radius: var(--radius-sm);
467
- background: var(--color-background-surface);
468
- overflow: hidden;
469
- transition:
470
- border-color var(--transition-fast),
471
- box-shadow var(--transition-fast);
472
- }
473
- .number-input:focus-within {
474
- border-color: var(--color-border-focus);
475
- box-shadow: var(--shadow-focus);
476
- }
477
- .number-input button {
478
- width: 36px;
479
- border: 0;
480
- background: var(--color-background-subtle);
481
- color: var(--color-foreground-default);
482
- cursor: pointer;
483
- display: inline-flex;
484
- align-items: center;
485
- justify-content: center;
486
- font-family: var(--font-sans);
487
- font-size: var(--fs-md);
488
- font-weight: var(--w-semibold);
489
- transition: background var(--transition-fast);
490
- }
491
- .number-input button:hover { background: var(--color-background-hover); }
492
- .number-input button:active { background: var(--color-background-press); }
493
- .number-input button:disabled { opacity: 0.4; cursor: not-allowed; }
494
- .number-input button.minus { border-right: 1px solid var(--color-border-default); }
495
- .number-input button.plus { border-left: 1px solid var(--color-border-default); }
496
- .number-input > input {
497
- width: 64px;
498
- border: 0;
499
- outline: none;
500
- text-align: center;
501
- font-family: var(--font-mono);
502
- font-size: var(--fs-md);
503
- font-weight: var(--w-semibold);
504
- background: transparent;
505
- color: var(--color-foreground-default);
506
- -moz-appearance: textfield;
507
- }
508
- .number-input > input::-webkit-outer-spin-button,
509
- .number-input > input::-webkit-inner-spin-button { appearance: none; margin: 0; }
510
-
511
-
512
- /* ----------------------------------------------------------------
513
- 11. OTP — one-time password digit cells
514
- ---------------------------------------------------------------- */
515
- .otp { display: inline-flex; gap: var(--space-2); }
516
- .otp > input {
517
- width: 40px;
518
- height: 48px;
519
- text-align: center;
520
- font-family: var(--font-mono);
521
- font-size: var(--fs-xl);
522
- font-weight: var(--w-semibold);
523
- border: 1px solid var(--color-border-default);
524
- border-radius: var(--radius-sm);
525
- outline: none;
526
- background: var(--color-background-surface);
527
- color: var(--color-foreground-default);
528
- transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
529
- }
530
- .otp > input:focus { border-color: var(--color-border-focus); box-shadow: var(--shadow-focus); }
531
- .otp > input.is-error { border-color: var(--color-border-danger); }
532
-
533
-
534
- /* ----------------------------------------------------------------
535
- 12. SWITCH (toggle)
536
- ---------------------------------------------------------------- */
537
- .switch {
538
- display: inline-flex;
539
- align-items: center;
540
- gap: var(--space-3);
541
- cursor: pointer;
542
- user-select: none;
543
- }
544
- .switch-track {
545
- position: relative;
546
- width: 36px;
547
- height: 20px;
548
- border-radius: var(--radius-pill);
549
- background: var(--color-neutral-300);
550
- transition: background var(--transition-base);
551
- flex-shrink: 0;
552
- }
553
- .switch-thumb {
554
- position: absolute;
555
- top: 2px;
556
- left: 2px;
557
- width: 16px;
558
- height: 16px;
559
- border-radius: var(--radius-pill);
560
- background: var(--color-pure-white);
561
- box-shadow: 0 1px 2px color-mix(in srgb, var(--color-pure-black) 18%, transparent);
562
- transition: transform var(--transition-base);
563
- }
564
- .switch input { position: absolute; opacity: 0; pointer-events: none; }
565
- .switch input:checked ~ .switch-track { background: var(--color-brand-secondary-500); }
566
- .switch input:checked ~ .switch-track .switch-thumb { transform: translateX(16px); }
567
- .switch input:focus-visible ~ .switch-track { box-shadow: var(--shadow-focus); }
568
- .switch input:disabled ~ .switch-track { opacity: 0.5; cursor: not-allowed; }
569
- .switch-label {
570
- font-family: var(--font-sans);
571
- font-size: var(--fs-sm);
572
- color: var(--color-foreground-default);
573
- }
574
-
575
-
576
- /* ----------------------------------------------------------------
577
- 13. CHECKBOX
578
- ---------------------------------------------------------------- */
579
- .check {
580
- display: inline-flex;
581
- align-items: center;
582
- gap: var(--space-3);
583
- cursor: pointer;
584
- user-select: none;
585
- font-family: var(--font-sans);
586
- font-size: var(--fs-sm);
587
- color: var(--color-foreground-default);
588
- }
589
- .check input { position: absolute; opacity: 0; pointer-events: none; }
590
- .check-box {
591
- width: 18px;
592
- height: 18px;
593
- border: 1px solid var(--color-border-strong);
594
- border-radius: var(--radius-xs);
595
- background: var(--color-background-surface);
596
- display: inline-flex;
597
- align-items: center;
598
- justify-content: center;
599
- flex-shrink: 0;
600
- color: var(--color-pure-white);
601
- transition:
602
- background var(--transition-fast),
603
- border-color var(--transition-fast),
604
- box-shadow var(--transition-fast);
605
- }
606
- .check-box svg { width: 14px; height: 14px; stroke-width: 3; opacity: 0; transform: scale(0.8); transition: opacity var(--transition-fast), transform var(--transition-fast); }
607
- .check input:checked ~ .check-box {
608
- background: var(--color-brand-secondary-500);
609
- border-color: var(--color-brand-secondary-500);
610
- }
611
- .check input:checked ~ .check-box svg { opacity: 1; transform: scale(1); }
612
- .check input:indeterminate ~ .check-box {
613
- background: var(--color-brand-secondary-500);
614
- border-color: var(--color-brand-secondary-500);
615
- }
616
- .check input:indeterminate ~ .check-box::after {
617
- content: '';
618
- width: 10px;
619
- height: 2px;
620
- background: var(--color-pure-white);
621
- border-radius: 1px;
622
- }
623
- .check input:indeterminate ~ .check-box svg { display: none; }
624
- .check input:focus-visible ~ .check-box { box-shadow: var(--shadow-focus); }
625
- .check input:disabled ~ .check-box { opacity: 0.5; cursor: not-allowed; }
626
- .check.is-error .check-box { border-color: var(--color-border-danger); }
627
-
628
-
629
- /* ----------------------------------------------------------------
630
- 14. RADIO
631
- ---------------------------------------------------------------- */
632
- .radio {
633
- display: inline-flex;
634
- align-items: center;
635
- gap: var(--space-3);
636
- cursor: pointer;
637
- user-select: none;
638
- font-family: var(--font-sans);
639
- font-size: var(--fs-sm);
640
- color: var(--color-foreground-default);
641
- }
642
- .radio input { position: absolute; opacity: 0; pointer-events: none; }
643
- .radio-dot {
644
- width: 18px;
645
- height: 18px;
646
- border: 1px solid var(--color-border-strong);
647
- border-radius: var(--radius-pill);
648
- background: var(--color-background-surface);
649
- display: inline-flex;
650
- align-items: center;
651
- justify-content: center;
652
- flex-shrink: 0;
653
- transition:
654
- border-color var(--transition-fast),
655
- box-shadow var(--transition-fast);
656
- }
657
- .radio-dot::after {
658
- content: '';
659
- width: 8px;
660
- height: 8px;
661
- border-radius: var(--radius-pill);
662
- background: var(--color-brand-secondary-500);
663
- transform: scale(0);
664
- transition: transform var(--transition-fast);
665
- }
666
- .radio input:checked ~ .radio-dot { border-color: var(--color-brand-secondary-500); }
667
- .radio input:checked ~ .radio-dot::after { transform: scale(1); }
668
- .radio input:focus-visible ~ .radio-dot { box-shadow: var(--shadow-focus); }
669
- .radio input:disabled ~ .radio-dot { opacity: 0.5; cursor: not-allowed; }
670
-
671
-
672
- /* ----------------------------------------------------------------
673
- 15. DATE / TIME TRIGGER
674
- ---------------------------------------------------------------- */
675
- .date-trigger {
676
- display: inline-flex;
677
- align-items: center;
678
- justify-content: space-between;
679
- gap: var(--space-3);
680
- width: 100%;
681
- height: 40px;
682
- padding: 0 var(--space-4);
683
- border: 1px solid var(--color-border-default);
684
- border-radius: var(--radius-sm);
685
- background: var(--color-background-surface);
686
- color: var(--color-foreground-default);
687
- font-family: var(--font-sans);
688
- font-size: var(--fs-md);
689
- cursor: pointer;
690
- transition:
691
- border-color var(--transition-fast),
692
- box-shadow var(--transition-fast),
693
- background var(--transition-fast);
694
- }
695
- .date-trigger:hover { border-color: var(--color-border-strong); }
696
- .date-trigger:focus,
697
- .date-trigger:focus-visible {
698
- outline: none;
699
- border-color: var(--color-border-focus);
700
- box-shadow: var(--shadow-focus);
701
- }
702
- .date-trigger.is-error { border-color: var(--color-border-danger); }
703
- .date-trigger.is-error:focus,
704
- .date-trigger.is-error:focus-visible { box-shadow: var(--shadow-focus-danger); }
705
- .date-trigger .placeholder { color: var(--color-foreground-placeholder); }
706
- .date-trigger > i, .date-trigger > svg,
707
- .date-trigger > svg {
708
- width: var(--icon-sm);
709
- height: var(--icon-sm);
710
- stroke-width: var(--icon-stroke);
711
- color: var(--color-foreground-muted);
712
- flex-shrink: 0;
713
- }
714
-
715
-
716
- /* ----------------------------------------------------------------
717
- 16. FILE DROPZONE
718
- ---------------------------------------------------------------- */
719
- .file-drop {
720
- display: flex;
721
- flex-direction: column;
722
- align-items: center;
723
- justify-content: center;
724
- gap: var(--space-3);
725
- padding: var(--space-7) var(--space-5);
726
- border: 1.5px dashed var(--color-border-default);
727
- border-radius: var(--radius-md);
728
- background: var(--color-background-canvas);
729
- color: var(--color-foreground-secondary);
730
- text-align: center;
731
- cursor: pointer;
732
- transition:
733
- border-color var(--transition-fast),
734
- background var(--transition-fast);
735
- }
736
- .file-drop:hover {
737
- border-color: var(--color-border-focus);
738
- background: var(--color-background-selected);
739
- }
740
- .file-drop.is-dragging {
741
- border-color: var(--color-border-focus);
742
- background: var(--color-background-selected);
743
- border-style: solid;
744
- }
745
- .file-drop > i, .file-drop > svg,
746
- .file-drop > svg {
747
- width: 28px;
748
- height: 28px;
749
- stroke-width: var(--icon-stroke);
750
- color: var(--color-foreground-muted);
751
- }
752
- .file-drop .ttl { font-size: var(--fs-md); font-weight: var(--w-semibold); color: var(--color-foreground-default); }
753
- .file-drop .sub { font-size: var(--fs-xs); color: var(--color-foreground-muted); }
754
- .file-drop .anchor { color: var(--color-foreground-highlight); font-weight: var(--w-semibold); }
755
-
756
- /* ----------------------------------------------------------------
757
- 16b. FILE LIST (post-upload state)
758
- ---------------------------------------------------------------- */
759
- .file-list {
760
- width: 100%;
761
- background: var(--color-background-surface);
762
- border: 1px solid var(--color-border-default);
763
- border-radius: var(--radius-md);
764
- overflow: hidden;
765
- }
766
- .file-list-head {
767
- display: flex;
768
- align-items: center;
769
- justify-content: space-between;
770
- padding: var(--space-3) var(--space-5);
771
- background: var(--color-background-subtle);
772
- border-bottom: 1px solid var(--color-border-subtle);
773
- font-family: var(--font-mono);
774
- font-size: var(--fs-xxs);
775
- color: var(--color-foreground-muted);
776
- text-transform: uppercase;
777
- letter-spacing: var(--ls-caps);
778
- }
779
- .file-list-action {
780
- display: inline-flex;
781
- align-items: center;
782
- gap: var(--space-2);
783
- background: transparent;
784
- border: 0;
785
- padding: 0;
786
- font-family: var(--font-mono);
787
- font-size: var(--fs-xxs);
788
- color: var(--color-foreground-highlight);
789
- text-transform: uppercase;
790
- letter-spacing: var(--ls-caps);
791
- cursor: pointer;
792
- transition: color var(--transition-fast);
793
- }
794
- .file-list-action:hover { color: var(--color-brand-secondary-700); }
795
-
796
- .file-rows {
797
- list-style: none;
798
- margin: 0;
799
- padding: 0;
800
- }
801
- .file-row {
802
- display: grid;
803
- grid-template-columns: auto 1fr auto auto;
804
- align-items: center;
805
- gap: var(--space-4);
806
- padding: var(--space-4) var(--space-5);
807
- border-bottom: 1px solid var(--color-border-subtle);
808
- transition: background var(--transition-fast);
809
- }
810
- .file-row:last-child { border-bottom: 0; }
811
- .file-row:hover { background: var(--color-background-canvas); }
812
-
813
- .file-icon {
814
- position: relative;
815
- width: 40px;
816
- height: 48px;
817
- display: flex;
818
- align-items: center;
819
- justify-content: center;
820
- flex-shrink: 0;
821
- border-radius: var(--radius-xs);
822
- background: var(--color-background-subtle);
823
- color: var(--color-foreground-muted);
824
- }
825
- .file-icon[data-kind="pdf"] { background: color-mix(in srgb, var(--color-danger-500) 8%, var(--color-background-surface)); color: var(--color-feedback-danger-foreground); }
826
- .file-icon[data-kind="img"] { background: color-mix(in srgb, var(--color-brand-secondary-500) 8%, var(--color-background-surface)); color: var(--color-brand-secondary-700); }
827
- .file-icon[data-kind="csv"] { background: color-mix(in srgb, var(--color-success-500) 8%, var(--color-background-surface)); color: var(--color-feedback-success-foreground); }
828
- .file-icon[data-kind="doc"] { background: color-mix(in srgb, var(--color-brand-secondary-500) 10%, var(--color-background-surface)); color: var(--color-brand-secondary-700); }
829
-
830
- .file-icon-tag {
831
- position: absolute;
832
- bottom: 4px;
833
- left: 50%;
834
- transform: translateX(-50%);
835
- font-family: var(--font-mono);
836
- font-size: 8px;
837
- font-weight: var(--w-bold);
838
- letter-spacing: 0.04em;
839
- line-height: 1;
840
- color: currentColor;
841
- background: var(--color-background-surface);
842
- padding: 1px 3px;
843
- border-radius: 2px;
844
- }
845
-
846
- .file-meta {
847
- min-width: 0;
848
- display: flex;
849
- flex-direction: column;
850
- gap: 2px;
851
- }
852
- .file-name {
853
- font-family: var(--font-sans);
854
- font-size: var(--fs-sm);
855
- font-weight: var(--w-semibold);
856
- color: var(--color-foreground-default);
857
- white-space: nowrap;
858
- overflow: hidden;
859
- text-overflow: ellipsis;
860
- }
861
- .file-sub {
862
- font-family: var(--font-mono);
863
- font-size: var(--fs-xxs);
864
- color: var(--color-foreground-muted);
865
- }
866
- .file-sub.err { color: var(--color-feedback-danger-foreground); }
867
-
868
- .file-progress {
869
- margin-top: 6px;
870
- height: 3px;
871
- background: var(--color-background-subtle);
872
- border-radius: var(--radius-pill);
873
- overflow: hidden;
874
- }
875
- .file-progress > span {
876
- display: block;
877
- height: 100%;
878
- background: var(--color-brand-secondary-500);
879
- border-radius: inherit;
880
- transition: width var(--transition-fast);
881
- }
882
-
883
- .file-status {
884
- display: inline-flex;
885
- align-items: center;
886
- gap: 4px;
887
- padding: 3px var(--space-2);
888
- border-radius: var(--radius-pill);
889
- font-family: var(--font-mono);
890
- font-size: var(--fs-xxs);
891
- font-weight: var(--w-semibold);
892
- text-transform: uppercase;
893
- letter-spacing: var(--ls-caps);
894
- white-space: nowrap;
895
- }
896
- .file-status.ok { color: var(--color-feedback-success-foreground); background: var(--color-feedback-success-background); }
897
- .file-status.pending { color: var(--color-brand-secondary-700); background: color-mix(in srgb, var(--color-brand-secondary-500) 10%, transparent); }
898
- .file-status.err { color: var(--color-feedback-danger-foreground); background: var(--color-feedback-danger-background); cursor: pointer; }
899
-
900
- .file-remove {
901
- width: 28px;
902
- height: 28px;
903
- display: inline-flex;
904
- align-items: center;
905
- justify-content: center;
906
- background: transparent;
907
- border: 0;
908
- border-radius: var(--radius-xs);
909
- color: var(--color-foreground-muted);
910
- cursor: pointer;
911
- transition: background var(--transition-fast), color var(--transition-fast);
912
- }
913
- .file-remove:hover { background: var(--color-background-subtle); color: var(--color-foreground-default); }
914
- .file-remove > svg, .file-remove > i { width: 14px; height: 14px; }
915
-
916
-
917
- /* ----------------------------------------------------------------
918
- 17. SLIDER (range)
919
- ---------------------------------------------------------------- */
920
- .slider {
921
- -webkit-appearance: none;
922
- appearance: none;
923
- width: 100%;
924
- height: 24px;
925
- background: transparent;
926
- cursor: pointer;
927
- }
928
- .slider::-webkit-slider-runnable-track {
929
- height: 4px;
930
- background: var(--color-neutral-150);
931
- border-radius: var(--radius-pill);
932
- }
933
- .slider::-moz-range-track {
934
- height: 4px;
935
- background: var(--color-neutral-150);
936
- border-radius: var(--radius-pill);
937
- }
938
- .slider::-webkit-slider-thumb {
939
- -webkit-appearance: none;
940
- appearance: none;
941
- width: 18px;
942
- height: 18px;
943
- border-radius: var(--radius-pill);
944
- background: var(--color-brand-secondary-500);
945
- border: 2px solid var(--color-pure-white);
946
- box-shadow: 0 1px 4px color-mix(in srgb, var(--color-shadow-tint) 22%, transparent);
947
- margin-top: -7px;
948
- transition: transform var(--transition-fast);
949
- }
950
- .slider::-moz-range-thumb {
951
- width: 18px;
952
- height: 18px;
953
- border-radius: var(--radius-pill);
954
- background: var(--color-brand-secondary-500);
955
- border: 2px solid var(--color-pure-white);
956
- box-shadow: 0 1px 4px color-mix(in srgb, var(--color-shadow-tint) 22%, transparent);
957
- }
958
- .slider:focus-visible::-webkit-slider-thumb {
959
- box-shadow: var(--shadow-focus);
960
- }
961
- .slider::-webkit-slider-thumb:hover { transform: scale(1.1); }
962
-
963
-
964
- /* ----------------------------------------------------------------
965
- 18. ON INVERSE — dark surfaces
966
- ---------------------------------------------------------------- */
967
- .input.on-inverse,
968
- .dark .input,
969
- .dark .group,
970
- .dark .pill-search,
971
- .dark .chips,
972
- .dark .number-input,
973
- .dark .date-trigger {
974
- background: color-mix(in srgb, var(--color-pure-white) 6%, transparent);
975
- border-color: var(--color-border-inverse);
976
- color: var(--color-foreground-inverse-default);
977
- }
978
- .input.on-inverse::placeholder,
979
- .dark .input::placeholder,
980
- .dark .group > input::placeholder,
981
- .dark .pill-search > input::placeholder,
982
- .dark .chips > input::placeholder {
983
- color: var(--color-foreground-inverse-placeholder);
984
- }
985
- .dark .group > input,
986
- .dark .pill-search > input,
987
- .dark .chips > input,
988
- .dark .number-input > input,
989
- .dark .date-trigger { color: var(--color-foreground-inverse-default); }
990
- .dark .group .fix {
991
- background: color-mix(in srgb, var(--color-pure-white) 4%, transparent);
992
- color: var(--color-foreground-inverse-secondary);
993
- border-color: var(--color-border-inverse);
994
- }
995
- .dark .number-input button {
996
- background: color-mix(in srgb, var(--color-pure-white) 4%, transparent);
997
- color: var(--color-foreground-inverse-default);
998
- border-color: var(--color-border-inverse);
999
- }
1000
- .dark .number-input button:hover { background: color-mix(in srgb, var(--color-pure-white) 10%, transparent); }
1001
- .dark .input:focus,
1002
- .dark .group:focus-within,
1003
- .dark .pill-search:focus-within,
1004
- .dark .chips:focus-within,
1005
- .dark .number-input:focus-within,
1006
- .dark .date-trigger:focus-visible {
1007
- border-color: var(--color-brand-secondary-300);
1008
- box-shadow: var(--shadow-focus-sky);
1009
- }
1010
- .dark .field > label { color: var(--color-foreground-inverse-default); }
1011
- .dark .field > .help { color: var(--color-foreground-inverse-muted); }
1012
- .dark .kbd {
1013
- background: color-mix(in srgb, var(--color-pure-white) 8%, transparent);
1014
- color: var(--color-foreground-inverse-secondary);
1015
- border-color: var(--color-border-inverse);
1016
- }
1017
- .dark .file-drop {
1018
- background: color-mix(in srgb, var(--color-pure-white) 4%, transparent);
1019
- border-color: var(--color-border-inverse);
1020
- color: var(--color-foreground-inverse-muted);
1021
- }
1022
- .dark .file-drop .ttl { color: var(--color-foreground-inverse-default); }
1023
- .dark .check-box,
1024
- .dark .radio-dot {
1025
- background: color-mix(in srgb, var(--color-pure-white) 6%, transparent);
1026
- border-color: var(--color-border-inverse);
1027
- }
1
+ /* ============================================================
2
+ INPUTS & FORM CONTROLS — ATTRUS Design System
3
+ Component CSS — pure tokens, no hardcoded values.
4
+
5
+ Anatomy (vertical stack):
6
+ [label] .field > label
7
+ [field row] .field > .input | .group | .affix
8
+ [helper / error] .field > .help
9
+
10
+ Field variants:
11
+ .input base text input (size: md = 40px)
12
+ .input.is-error danger border + danger focus ring
13
+ .input.is-success success border + success focus ring
14
+ .input[disabled] opacity .5 + bg-subtle
15
+ .input.is-readonly no border, only value text
16
+
17
+ Sizes (apply to .input or wrapper):
18
+ .input-sm (32h, fs-sm)
19
+ default (40h, fs-md)
20
+ .input-lg (48h, fs-lg)
21
+
22
+ Wrappers (compose with .input):
23
+ .affix .lead | .trail leading / trailing icon
24
+ .group .fix.l | .fix.r prefix / suffix attached
25
+ .pill-search ⌘K-style search bar
26
+ .underline ghost / quiet input
27
+ .chips multi-select with embedded text input
28
+ .otp one-time-password digit cells
29
+
30
+ Other controls (siblings, not .input):
31
+ .select wrapper around <select>
32
+ .switch toggle
33
+ .check custom checkbox
34
+ .radio custom radio
35
+ .number-input number input with +/- buttons
36
+ .file-drop dropzone
37
+ .slider range with track + thumb
38
+ .date-trigger trigger button for date/time picker
39
+
40
+ Inverse:
41
+ .input.on-inverse dark-aware text input
42
+ surfaces with .dark inherit white-on-dark text
43
+ ============================================================ */
44
+
45
+
46
+ /* ----------------------------------------------------------------
47
+ 1. FIELD WRAPPER
48
+ ---------------------------------------------------------------- */
49
+ .field {
50
+ display: flex;
51
+ flex-direction: column;
52
+ gap: var(--space-2);
53
+ min-width: 0;
54
+ }
55
+ .field > label {
56
+ font-family: var(--font-sans);
57
+ font-size: var(--fs-sm);
58
+ font-weight: var(--w-semibold);
59
+ color: var(--color-foreground-default);
60
+ letter-spacing: var(--ls-body);
61
+ }
62
+ .field > label .req {
63
+ color: var(--color-feedback-danger-foreground-muted);
64
+ margin-left: 2px;
65
+ }
66
+ .field > label .opt {
67
+ color: var(--color-foreground-muted);
68
+ font-weight: var(--w-regular);
69
+ margin-left: var(--space-2);
70
+ font-size: var(--fs-xs);
71
+ }
72
+ .field > .help {
73
+ font-size: var(--fs-xs);
74
+ color: var(--color-foreground-muted);
75
+ display: flex;
76
+ gap: var(--space-2);
77
+ align-items: flex-start;
78
+ line-height: var(--lh-snug, 1.4);
79
+ }
80
+ .field > .help i, .field > .help svg,
81
+ .field > .help svg {
82
+ width: var(--icon-xs);
83
+ height: var(--icon-xs);
84
+ stroke-width: var(--icon-stroke);
85
+ flex-shrink: 0;
86
+ margin-top: 1px;
87
+ }
88
+ .field > .help.is-error { color: var(--color-feedback-danger-foreground); }
89
+ .field > .help.is-error i, .field > .help.is-error svg { color: var(--color-feedback-danger-foreground-muted); }
90
+ .field > .help.is-success { color: var(--color-feedback-success-foreground); }
91
+ .field > .help.is-success i, .field > .help.is-success svg{ color: var(--color-feedback-success-foreground-muted); }
92
+ .field > .help.is-warning { color: var(--color-feedback-warning-foreground); }
93
+ .field > .help.is-warning i, .field > .help.is-warning svg{ color: var(--color-feedback-warning-foreground-muted); }
94
+
95
+
96
+ /* ----------------------------------------------------------------
97
+ 2. BASE INPUT
98
+ ---------------------------------------------------------------- */
99
+ .input {
100
+ /* Box — default size = md (40px tall) */
101
+ width: 100%;
102
+ box-sizing: border-box;
103
+ height: 40px;
104
+ padding: 0 var(--space-4);
105
+ border: 1px solid var(--color-border-default);
106
+ border-radius: var(--radius-sm);
107
+ background: var(--color-background-surface);
108
+ color: var(--color-foreground-default);
109
+
110
+ /* Type */
111
+ font-family: var(--font-sans);
112
+ font-size: var(--fs-md);
113
+ font-weight: var(--w-regular);
114
+ line-height: 1;
115
+ letter-spacing: var(--ls-body);
116
+
117
+ /* Behavior */
118
+ outline: none;
119
+ transition:
120
+ background var(--transition-fast),
121
+ border-color var(--transition-fast),
122
+ color var(--transition-fast),
123
+ box-shadow var(--transition-fast);
124
+ }
125
+ .input::placeholder { color: var(--color-foreground-placeholder); }
126
+
127
+ .input:hover:not(:focus):not([disabled]):not(.is-error):not(.is-success) {
128
+ border-color: var(--color-border-strong);
129
+ }
130
+ .input:focus,
131
+ .input:focus-visible {
132
+ border-color: var(--color-border-focus);
133
+ box-shadow: var(--shadow-focus);
134
+ }
135
+ .input[disabled],
136
+ .input.is-disabled {
137
+ background: var(--color-background-subtle);
138
+ color: var(--color-foreground-placeholder);
139
+ cursor: not-allowed;
140
+ opacity: 0.7;
141
+ }
142
+ .input[readonly],
143
+ .input.is-readonly {
144
+ background: transparent;
145
+ border-color: transparent;
146
+ padding-left: 0;
147
+ padding-right: 0;
148
+ cursor: default;
149
+ }
150
+
151
+ /* Validation states */
152
+ .input.is-error { border-color: var(--color-border-danger); }
153
+ .input.is-error:focus { box-shadow: var(--shadow-focus-danger); }
154
+ .input.is-success { border-color: var(--color-border-success); }
155
+ .input.is-success:focus { box-shadow: var(--shadow-focus-success); }
156
+ .input.is-warning { border-color: var(--color-border-warning); }
157
+ .input.is-warning:focus { box-shadow: var(--shadow-focus-warning); }
158
+
159
+ /* Textarea */
160
+ textarea.input {
161
+ height: auto;
162
+ min-height: 88px;
163
+ padding: var(--space-3) var(--space-4);
164
+ resize: vertical;
165
+ line-height: var(--lh-normal, 1.5);
166
+ }
167
+
168
+
169
+ /* ----------------------------------------------------------------
170
+ 3. SIZES
171
+ ---------------------------------------------------------------- */
172
+ .input-sm {
173
+ height: 32px;
174
+ padding: 0 var(--space-3);
175
+ font-size: var(--fs-sm);
176
+ border-radius: var(--radius-xs);
177
+ }
178
+ .input-lg {
179
+ height: 48px;
180
+ padding: 0 var(--space-5);
181
+ font-size: var(--fs-lg);
182
+ border-radius: var(--radius-md);
183
+ }
184
+ textarea.input.input-sm { height: auto; min-height: 64px; padding: var(--space-2) var(--space-3); }
185
+ textarea.input.input-lg { height: auto; min-height: 120px; padding: var(--space-4) var(--space-5); }
186
+
187
+
188
+ /* ----------------------------------------------------------------
189
+ 4. AFFIX — leading / trailing icon
190
+ ---------------------------------------------------------------- */
191
+ .affix { position: relative; display: block; }
192
+ .affix .lead,
193
+ .affix .trail {
194
+ position: absolute;
195
+ top: 50%;
196
+ transform: translateY(-50%);
197
+ width: var(--icon-sm);
198
+ height: var(--icon-sm);
199
+ stroke-width: var(--icon-stroke);
200
+ color: var(--color-foreground-muted);
201
+ pointer-events: none;
202
+ flex-shrink: 0;
203
+ }
204
+ .affix .lead { left: var(--space-4); }
205
+ .affix .trail { right: var(--space-4); }
206
+
207
+ /* Pad the input to make room for the icon */
208
+ .affix.lead-pad > .input { padding-left: calc(var(--space-4) + var(--icon-sm) + var(--space-3)); }
209
+ .affix.trail-pad > .input { padding-right: calc(var(--space-4) + var(--icon-sm) + var(--space-3)); }
210
+
211
+ /* Trailing button (e.g. password reveal, clear) — clickable */
212
+ .affix .trail-btn {
213
+ position: absolute;
214
+ top: 50%;
215
+ right: var(--space-2);
216
+ transform: translateY(-50%);
217
+ width: 28px;
218
+ height: 28px;
219
+ display: inline-flex;
220
+ align-items: center;
221
+ justify-content: center;
222
+ border: 0;
223
+ border-radius: var(--radius-xs);
224
+ background: transparent;
225
+ color: var(--color-foreground-muted);
226
+ cursor: pointer;
227
+ transition: background var(--transition-fast), color var(--transition-fast);
228
+ }
229
+ .affix .trail-btn:hover { background: var(--color-background-hover); color: var(--color-foreground-default); }
230
+ .affix .trail-btn:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
231
+ .affix .trail-btn > i, .affix .trail-btn > svg,
232
+ .affix .trail-btn > svg { width: var(--icon-xs); height: var(--icon-xs); stroke-width: var(--icon-stroke); }
233
+
234
+
235
+ /* ----------------------------------------------------------------
236
+ 5. PREFIX / SUFFIX GROUP
237
+ ---------------------------------------------------------------- */
238
+ .group {
239
+ display: flex;
240
+ align-items: stretch;
241
+ width: 100%;
242
+ height: 40px;
243
+ border: 1px solid var(--color-border-default);
244
+ border-radius: var(--radius-sm);
245
+ background: var(--color-background-surface);
246
+ overflow: hidden;
247
+ transition:
248
+ border-color var(--transition-fast),
249
+ box-shadow var(--transition-fast);
250
+ }
251
+ .group:focus-within {
252
+ border-color: var(--color-border-focus);
253
+ box-shadow: var(--shadow-focus);
254
+ }
255
+ .group .fix {
256
+ display: flex;
257
+ align-items: center;
258
+ gap: var(--space-2);
259
+ padding: 0 var(--space-4);
260
+ background: var(--color-background-subtle);
261
+ color: var(--color-foreground-muted);
262
+ font-family: var(--font-mono);
263
+ font-size: var(--fs-sm);
264
+ flex-shrink: 0;
265
+ }
266
+ .group .fix.l { border-right: 1px solid var(--color-border-default); }
267
+ .group .fix.r { border-left: 1px solid var(--color-border-default); }
268
+ .group > input {
269
+ flex: 1;
270
+ min-width: 0;
271
+ border: 0;
272
+ outline: none;
273
+ padding: 0 var(--space-4);
274
+ background: transparent;
275
+ color: var(--color-foreground-default);
276
+ font-family: var(--font-sans);
277
+ font-size: var(--fs-md);
278
+ }
279
+ .group > input::placeholder { color: var(--color-foreground-placeholder); }
280
+ .group.is-error { border-color: var(--color-border-danger); }
281
+ .group.is-error:focus-within { box-shadow: var(--shadow-focus-danger); }
282
+
283
+
284
+ /* ----------------------------------------------------------------
285
+ 6. PILL SEARCH (⌘K-style)
286
+ ---------------------------------------------------------------- */
287
+ .pill-search {
288
+ display: flex;
289
+ align-items: center;
290
+ gap: var(--space-3);
291
+ height: 40px;
292
+ padding: 0 var(--space-4);
293
+ border: 1px solid var(--color-border-default);
294
+ border-radius: var(--radius-pill);
295
+ background: var(--color-background-surface);
296
+ transition:
297
+ border-color var(--transition-fast),
298
+ box-shadow var(--transition-fast),
299
+ background var(--transition-fast);
300
+ }
301
+ .pill-search:hover { border-color: var(--color-border-strong); }
302
+ .pill-search:focus-within {
303
+ border-color: var(--color-border-focus);
304
+ box-shadow: var(--shadow-focus);
305
+ }
306
+ .pill-search > i, .pill-search > svg,
307
+ .pill-search > svg {
308
+ width: var(--icon-sm);
309
+ height: var(--icon-sm);
310
+ stroke-width: var(--icon-stroke);
311
+ color: var(--color-foreground-muted);
312
+ flex-shrink: 0;
313
+ }
314
+ .pill-search > input {
315
+ flex: 1;
316
+ min-width: 0;
317
+ border: 0;
318
+ outline: none;
319
+ background: transparent;
320
+ font-family: var(--font-sans);
321
+ font-size: var(--fs-md);
322
+ color: var(--color-foreground-default);
323
+ }
324
+ .pill-search > input::placeholder { color: var(--color-foreground-placeholder); }
325
+ .kbd {
326
+ display: inline-flex;
327
+ align-items: center;
328
+ font-family: var(--font-mono);
329
+ font-size: var(--fs-xxs);
330
+ font-weight: var(--w-semibold);
331
+ color: var(--color-foreground-muted);
332
+ background: var(--color-background-subtle);
333
+ border: 1px solid var(--color-border-default);
334
+ border-radius: var(--radius-2xs);
335
+ padding: 1px var(--space-2);
336
+ letter-spacing: var(--ls-caps);
337
+ }
338
+
339
+
340
+ /* ----------------------------------------------------------------
341
+ 7. UNDERLINE — quiet / ghost input
342
+ ---------------------------------------------------------------- */
343
+ .input.underline {
344
+ border: 0;
345
+ border-bottom: 1px solid var(--color-border-default);
346
+ border-radius: 0;
347
+ background: transparent;
348
+ padding: var(--space-2) 0;
349
+ }
350
+ .input.underline:focus,
351
+ .input.underline:focus-visible {
352
+ border-bottom-color: var(--color-border-focus);
353
+ box-shadow: none;
354
+ }
355
+ .input.underline.is-error { border-bottom-color: var(--color-border-danger); }
356
+
357
+
358
+ /* ----------------------------------------------------------------
359
+ 8. CHIPS — multi-select / token input
360
+ ---------------------------------------------------------------- */
361
+ .chips {
362
+ display: flex;
363
+ flex-wrap: wrap;
364
+ gap: 6px;
365
+ align-items: center;
366
+ min-height: 40px;
367
+ padding: 5px 8px;
368
+ border: 1px solid var(--color-border-default);
369
+ border-radius: var(--radius-sm);
370
+ background: var(--color-background-surface);
371
+ transition:
372
+ border-color var(--transition-fast),
373
+ box-shadow var(--transition-fast);
374
+ }
375
+ .chips:hover { border-color: var(--color-border-strong); }
376
+ .chips:focus-within {
377
+ border-color: var(--color-border-focus);
378
+ box-shadow: var(--shadow-focus);
379
+ }
380
+ .chip {
381
+ display: inline-flex;
382
+ align-items: center;
383
+ gap: 4px;
384
+ height: 26px;
385
+ padding: 0 4px 0 10px;
386
+ background: var(--color-background-subtle);
387
+ color: var(--color-foreground-default);
388
+ border: 1px solid var(--color-border-default);
389
+ border-radius: var(--radius-xs);
390
+ font-family: var(--font-sans);
391
+ font-size: var(--fs-xs);
392
+ font-weight: var(--w-medium);
393
+ line-height: 1;
394
+ white-space: nowrap;
395
+ flex-shrink: 0;
396
+ transition: background var(--transition-fast), border-color var(--transition-fast);
397
+ }
398
+ .chip:hover { background: var(--color-background-canvas); border-color: var(--color-border-strong); }
399
+ .chip-x {
400
+ display: inline-flex;
401
+ align-items: center;
402
+ justify-content: center;
403
+ width: 18px;
404
+ height: 18px;
405
+ margin-left: 2px;
406
+ border: 0;
407
+ padding: 0;
408
+ background: transparent;
409
+ cursor: pointer;
410
+ border-radius: var(--radius-xs);
411
+ color: var(--color-foreground-muted);
412
+ transition: background var(--transition-fast), color var(--transition-fast);
413
+ }
414
+ .chip-x svg, .chip-x i { width: 11px; height: 11px; stroke-width: 2.25; }
415
+ .chip-x:hover { background: var(--color-border-default); color: var(--color-foreground-default); }
416
+ .chips > input {
417
+ flex: 1;
418
+ min-width: 80px;
419
+ height: 26px;
420
+ border: 0;
421
+ outline: none;
422
+ background: transparent;
423
+ font-family: var(--font-sans);
424
+ font-size: var(--fs-sm);
425
+ padding: 0 6px;
426
+ color: var(--color-foreground-default);
427
+ }
428
+ .chips > input::placeholder { color: var(--color-foreground-placeholder); }
429
+
430
+
431
+ /* ----------------------------------------------------------------
432
+ 9. SELECT (native)
433
+ ---------------------------------------------------------------- */
434
+ .select {
435
+ position: relative;
436
+ display: block;
437
+ }
438
+ .select > select.input {
439
+ appearance: none;
440
+ -webkit-appearance: none;
441
+ cursor: pointer;
442
+ padding-right: calc(var(--space-4) + var(--icon-sm) + var(--space-3));
443
+ background-image: none;
444
+ }
445
+ .select > .chev {
446
+ position: absolute;
447
+ top: 50%;
448
+ right: var(--space-4);
449
+ transform: translateY(-50%);
450
+ width: var(--icon-sm);
451
+ height: var(--icon-sm);
452
+ stroke-width: var(--icon-stroke);
453
+ color: var(--color-foreground-muted);
454
+ pointer-events: none;
455
+ }
456
+
457
+
458
+ /* (NUMBER INPUT moved to number-input.css — canonical single source) */
459
+
460
+ /* ----------------------------------------------------------------
461
+ 11. OTP — one-time password digit cells
462
+ ---------------------------------------------------------------- */
463
+ .otp { display: inline-flex; gap: var(--space-2); }
464
+ .otp > input {
465
+ width: 40px;
466
+ height: 48px;
467
+ text-align: center;
468
+ font-family: var(--font-mono);
469
+ font-size: var(--fs-xl);
470
+ font-weight: var(--w-semibold);
471
+ border: 1px solid var(--color-border-default);
472
+ border-radius: var(--radius-sm);
473
+ outline: none;
474
+ background: var(--color-background-surface);
475
+ color: var(--color-foreground-default);
476
+ transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
477
+ }
478
+ .otp > input:focus { border-color: var(--color-border-focus); box-shadow: var(--shadow-focus); }
479
+ .otp > input.is-error { border-color: var(--color-border-danger); }
480
+
481
+
482
+ /* ----------------------------------------------------------------
483
+ 12. SWITCH (toggle)
484
+ ---------------------------------------------------------------- */
485
+ .switch {
486
+ display: inline-flex;
487
+ align-items: center;
488
+ gap: var(--space-3);
489
+ cursor: pointer;
490
+ user-select: none;
491
+ }
492
+ .switch-track {
493
+ position: relative;
494
+ width: 36px;
495
+ height: 20px;
496
+ border-radius: var(--radius-pill);
497
+ background: var(--color-neutral-300);
498
+ transition: background var(--transition-base);
499
+ flex-shrink: 0;
500
+ }
501
+ .switch-thumb {
502
+ position: absolute;
503
+ top: 2px;
504
+ left: 2px;
505
+ width: 16px;
506
+ height: 16px;
507
+ border-radius: var(--radius-pill);
508
+ background: var(--color-pure-white);
509
+ box-shadow: 0 1px 2px color-mix(in srgb, var(--color-pure-black) 18%, transparent);
510
+ transition: transform var(--transition-base);
511
+ }
512
+ .switch input { position: absolute; opacity: 0; pointer-events: none; }
513
+ .switch input:checked ~ .switch-track { background: var(--color-brand-secondary-500); }
514
+ .switch input:checked ~ .switch-track .switch-thumb { transform: translateX(16px); }
515
+ .switch input:focus-visible ~ .switch-track { box-shadow: var(--shadow-focus); }
516
+ .switch input:disabled ~ .switch-track { opacity: 0.5; cursor: not-allowed; }
517
+ .switch-label {
518
+ font-family: var(--font-sans);
519
+ font-size: var(--fs-sm);
520
+ color: var(--color-foreground-default);
521
+ }
522
+
523
+
524
+ /* ----------------------------------------------------------------
525
+ 13. CHECKBOX
526
+ ---------------------------------------------------------------- */
527
+ .check {
528
+ display: inline-flex;
529
+ align-items: center;
530
+ gap: var(--space-3);
531
+ cursor: pointer;
532
+ user-select: none;
533
+ font-family: var(--font-sans);
534
+ font-size: var(--fs-sm);
535
+ color: var(--color-foreground-default);
536
+ }
537
+ .check input { position: absolute; opacity: 0; pointer-events: none; }
538
+ .check-box {
539
+ width: 18px;
540
+ height: 18px;
541
+ border: 1px solid var(--color-border-strong);
542
+ border-radius: var(--radius-xs);
543
+ background: var(--color-background-surface);
544
+ display: inline-flex;
545
+ align-items: center;
546
+ justify-content: center;
547
+ flex-shrink: 0;
548
+ color: var(--color-pure-white);
549
+ transition:
550
+ background var(--transition-fast),
551
+ border-color var(--transition-fast),
552
+ box-shadow var(--transition-fast);
553
+ }
554
+ .check-box svg { width: 14px; height: 14px; stroke-width: 3; opacity: 0; transform: scale(0.8); transition: opacity var(--transition-fast), transform var(--transition-fast); }
555
+ .check input:checked ~ .check-box {
556
+ background: var(--color-brand-secondary-500);
557
+ border-color: var(--color-brand-secondary-500);
558
+ }
559
+ .check input:checked ~ .check-box svg { opacity: 1; transform: scale(1); }
560
+ .check input:indeterminate ~ .check-box {
561
+ background: var(--color-brand-secondary-500);
562
+ border-color: var(--color-brand-secondary-500);
563
+ }
564
+ .check input:indeterminate ~ .check-box::after {
565
+ content: '';
566
+ width: 10px;
567
+ height: 2px;
568
+ background: var(--color-pure-white);
569
+ border-radius: 1px;
570
+ }
571
+ .check input:indeterminate ~ .check-box svg { display: none; }
572
+ .check input:focus-visible ~ .check-box { box-shadow: var(--shadow-focus); }
573
+ .check input:disabled ~ .check-box { opacity: 0.5; cursor: not-allowed; }
574
+ .check.is-error .check-box { border-color: var(--color-border-danger); }
575
+
576
+
577
+ /* ----------------------------------------------------------------
578
+ 14. RADIO
579
+ ---------------------------------------------------------------- */
580
+ .radio {
581
+ display: inline-flex;
582
+ align-items: center;
583
+ gap: var(--space-3);
584
+ cursor: pointer;
585
+ user-select: none;
586
+ font-family: var(--font-sans);
587
+ font-size: var(--fs-sm);
588
+ color: var(--color-foreground-default);
589
+ }
590
+ .radio input { position: absolute; opacity: 0; pointer-events: none; }
591
+ .radio-dot {
592
+ width: 18px;
593
+ height: 18px;
594
+ border: 1px solid var(--color-border-strong);
595
+ border-radius: var(--radius-pill);
596
+ background: var(--color-background-surface);
597
+ display: inline-flex;
598
+ align-items: center;
599
+ justify-content: center;
600
+ flex-shrink: 0;
601
+ transition:
602
+ border-color var(--transition-fast),
603
+ box-shadow var(--transition-fast);
604
+ }
605
+ .radio-dot::after {
606
+ content: '';
607
+ width: 8px;
608
+ height: 8px;
609
+ border-radius: var(--radius-pill);
610
+ background: var(--color-brand-secondary-500);
611
+ transform: scale(0);
612
+ transition: transform var(--transition-fast);
613
+ }
614
+ .radio input:checked ~ .radio-dot { border-color: var(--color-brand-secondary-500); }
615
+ .radio input:checked ~ .radio-dot::after { transform: scale(1); }
616
+ .radio input:focus-visible ~ .radio-dot { box-shadow: var(--shadow-focus); }
617
+ .radio input:disabled ~ .radio-dot { opacity: 0.5; cursor: not-allowed; }
618
+
619
+
620
+ /* ----------------------------------------------------------------
621
+ 15. DATE / TIME TRIGGER
622
+ ---------------------------------------------------------------- */
623
+ .date-trigger {
624
+ display: inline-flex;
625
+ align-items: center;
626
+ justify-content: space-between;
627
+ gap: var(--space-3);
628
+ width: 100%;
629
+ height: 40px;
630
+ padding: 0 var(--space-4);
631
+ border: 1px solid var(--color-border-default);
632
+ border-radius: var(--radius-sm);
633
+ background: var(--color-background-surface);
634
+ color: var(--color-foreground-default);
635
+ font-family: var(--font-sans);
636
+ font-size: var(--fs-md);
637
+ cursor: pointer;
638
+ transition:
639
+ border-color var(--transition-fast),
640
+ box-shadow var(--transition-fast),
641
+ background var(--transition-fast);
642
+ }
643
+ .date-trigger:hover { border-color: var(--color-border-strong); }
644
+ .date-trigger:focus,
645
+ .date-trigger:focus-visible {
646
+ outline: none;
647
+ border-color: var(--color-border-focus);
648
+ box-shadow: var(--shadow-focus);
649
+ }
650
+ .date-trigger.is-error { border-color: var(--color-border-danger); }
651
+ .date-trigger.is-error:focus,
652
+ .date-trigger.is-error:focus-visible { box-shadow: var(--shadow-focus-danger); }
653
+ .date-trigger .placeholder { color: var(--color-foreground-placeholder); }
654
+ .date-trigger > i, .date-trigger > svg,
655
+ .date-trigger > svg {
656
+ width: var(--icon-sm);
657
+ height: var(--icon-sm);
658
+ stroke-width: var(--icon-stroke);
659
+ color: var(--color-foreground-muted);
660
+ flex-shrink: 0;
661
+ }
662
+
663
+
664
+ /* ----------------------------------------------------------------
665
+ 16. FILE DROPZONE
666
+ ---------------------------------------------------------------- */
667
+ .file-drop {
668
+ display: flex;
669
+ flex-direction: column;
670
+ align-items: center;
671
+ justify-content: center;
672
+ gap: var(--space-3);
673
+ padding: var(--space-7) var(--space-5);
674
+ border: 1.5px dashed var(--color-border-default);
675
+ border-radius: var(--radius-md);
676
+ background: var(--color-background-canvas);
677
+ color: var(--color-foreground-secondary);
678
+ text-align: center;
679
+ cursor: pointer;
680
+ transition:
681
+ border-color var(--transition-fast),
682
+ background var(--transition-fast);
683
+ }
684
+ .file-drop:hover {
685
+ border-color: var(--color-border-focus);
686
+ background: var(--color-background-selected);
687
+ }
688
+ .file-drop.is-dragging {
689
+ border-color: var(--color-border-focus);
690
+ background: var(--color-background-selected);
691
+ border-style: solid;
692
+ }
693
+ .file-drop > i, .file-drop > svg,
694
+ .file-drop > svg {
695
+ width: 28px;
696
+ height: 28px;
697
+ stroke-width: var(--icon-stroke);
698
+ color: var(--color-foreground-muted);
699
+ }
700
+ .file-drop .ttl { font-size: var(--fs-md); font-weight: var(--w-semibold); color: var(--color-foreground-default); }
701
+ .file-drop .sub { font-size: var(--fs-xs); color: var(--color-foreground-muted); }
702
+ .file-drop .anchor { color: var(--color-foreground-highlight); font-weight: var(--w-semibold); }
703
+
704
+ /* ----------------------------------------------------------------
705
+ 16b. FILE LIST (post-upload state)
706
+ ---------------------------------------------------------------- */
707
+ .file-list {
708
+ width: 100%;
709
+ background: var(--color-background-surface);
710
+ border: 1px solid var(--color-border-default);
711
+ border-radius: var(--radius-md);
712
+ overflow: hidden;
713
+ }
714
+ .file-list-head {
715
+ display: flex;
716
+ align-items: center;
717
+ justify-content: space-between;
718
+ padding: var(--space-3) var(--space-5);
719
+ background: var(--color-background-subtle);
720
+ border-bottom: 1px solid var(--color-border-subtle);
721
+ font-family: var(--font-mono);
722
+ font-size: var(--fs-xxs);
723
+ color: var(--color-foreground-muted);
724
+ text-transform: uppercase;
725
+ letter-spacing: var(--ls-caps);
726
+ }
727
+ .file-list-action {
728
+ display: inline-flex;
729
+ align-items: center;
730
+ gap: var(--space-2);
731
+ background: transparent;
732
+ border: 0;
733
+ padding: 0;
734
+ font-family: var(--font-mono);
735
+ font-size: var(--fs-xxs);
736
+ color: var(--color-foreground-highlight);
737
+ text-transform: uppercase;
738
+ letter-spacing: var(--ls-caps);
739
+ cursor: pointer;
740
+ transition: color var(--transition-fast);
741
+ }
742
+ .file-list-action:hover { color: var(--color-brand-secondary-700); }
743
+
744
+ .file-rows {
745
+ list-style: none;
746
+ margin: 0;
747
+ padding: 0;
748
+ }
749
+ .file-row {
750
+ display: grid;
751
+ grid-template-columns: auto 1fr auto auto;
752
+ align-items: center;
753
+ gap: var(--space-4);
754
+ padding: var(--space-4) var(--space-5);
755
+ border-bottom: 1px solid var(--color-border-subtle);
756
+ transition: background var(--transition-fast);
757
+ }
758
+ .file-row:last-child { border-bottom: 0; }
759
+ .file-row:hover { background: var(--color-background-canvas); }
760
+
761
+ .file-icon {
762
+ position: relative;
763
+ width: 40px;
764
+ height: 48px;
765
+ display: flex;
766
+ align-items: center;
767
+ justify-content: center;
768
+ flex-shrink: 0;
769
+ border-radius: var(--radius-xs);
770
+ background: var(--color-background-subtle);
771
+ color: var(--color-foreground-muted);
772
+ }
773
+ .file-icon[data-kind="pdf"] { background: color-mix(in srgb, var(--color-danger-500) 8%, var(--color-background-surface)); color: var(--color-feedback-danger-foreground); }
774
+ .file-icon[data-kind="img"] { background: color-mix(in srgb, var(--color-brand-secondary-500) 8%, var(--color-background-surface)); color: var(--color-brand-secondary-700); }
775
+ .file-icon[data-kind="csv"] { background: color-mix(in srgb, var(--color-success-500) 8%, var(--color-background-surface)); color: var(--color-feedback-success-foreground); }
776
+ .file-icon[data-kind="doc"] { background: color-mix(in srgb, var(--color-brand-secondary-500) 10%, var(--color-background-surface)); color: var(--color-brand-secondary-700); }
777
+
778
+ .file-icon-tag {
779
+ position: absolute;
780
+ bottom: 4px;
781
+ left: 50%;
782
+ transform: translateX(-50%);
783
+ font-family: var(--font-mono);
784
+ font-size: 8px;
785
+ font-weight: var(--w-bold);
786
+ letter-spacing: 0.04em;
787
+ line-height: 1;
788
+ color: currentColor;
789
+ background: var(--color-background-surface);
790
+ padding: 1px 3px;
791
+ border-radius: 2px;
792
+ }
793
+
794
+ .file-meta {
795
+ min-width: 0;
796
+ display: flex;
797
+ flex-direction: column;
798
+ gap: 2px;
799
+ }
800
+ .file-name {
801
+ font-family: var(--font-sans);
802
+ font-size: var(--fs-sm);
803
+ font-weight: var(--w-semibold);
804
+ color: var(--color-foreground-default);
805
+ white-space: nowrap;
806
+ overflow: hidden;
807
+ text-overflow: ellipsis;
808
+ }
809
+ .file-sub {
810
+ font-family: var(--font-mono);
811
+ font-size: var(--fs-xxs);
812
+ color: var(--color-foreground-muted);
813
+ }
814
+ .file-sub.err { color: var(--color-feedback-danger-foreground); }
815
+
816
+ .file-progress {
817
+ margin-top: 6px;
818
+ height: 3px;
819
+ background: var(--color-background-subtle);
820
+ border-radius: var(--radius-pill);
821
+ overflow: hidden;
822
+ }
823
+ .file-progress > span {
824
+ display: block;
825
+ height: 100%;
826
+ background: var(--color-brand-secondary-500);
827
+ border-radius: inherit;
828
+ transition: width var(--transition-fast);
829
+ }
830
+
831
+ .file-status {
832
+ display: inline-flex;
833
+ align-items: center;
834
+ gap: 4px;
835
+ padding: 3px var(--space-2);
836
+ border-radius: var(--radius-pill);
837
+ font-family: var(--font-mono);
838
+ font-size: var(--fs-xxs);
839
+ font-weight: var(--w-semibold);
840
+ text-transform: uppercase;
841
+ letter-spacing: var(--ls-caps);
842
+ white-space: nowrap;
843
+ }
844
+ .file-status.ok { color: var(--color-feedback-success-foreground); background: var(--color-feedback-success-background); }
845
+ .file-status.pending { color: var(--color-brand-secondary-700); background: color-mix(in srgb, var(--color-brand-secondary-500) 10%, transparent); }
846
+ .file-status.err { color: var(--color-feedback-danger-foreground); background: var(--color-feedback-danger-background); cursor: pointer; }
847
+
848
+ .file-remove {
849
+ width: 28px;
850
+ height: 28px;
851
+ display: inline-flex;
852
+ align-items: center;
853
+ justify-content: center;
854
+ background: transparent;
855
+ border: 0;
856
+ border-radius: var(--radius-xs);
857
+ color: var(--color-foreground-muted);
858
+ cursor: pointer;
859
+ transition: background var(--transition-fast), color var(--transition-fast);
860
+ }
861
+ .file-remove:hover { background: var(--color-background-subtle); color: var(--color-foreground-default); }
862
+ .file-remove > svg, .file-remove > i { width: 14px; height: 14px; }
863
+
864
+
865
+ /* ----------------------------------------------------------------
866
+ 17. SLIDER (range)
867
+ ---------------------------------------------------------------- */
868
+ .slider {
869
+ -webkit-appearance: none;
870
+ appearance: none;
871
+ width: 100%;
872
+ height: 24px;
873
+ background: transparent;
874
+ cursor: pointer;
875
+ }
876
+ .slider::-webkit-slider-runnable-track {
877
+ height: 4px;
878
+ background: var(--color-border-default);
879
+ border-radius: var(--radius-pill);
880
+ }
881
+ .slider::-moz-range-track {
882
+ height: 4px;
883
+ background: var(--color-border-default);
884
+ border-radius: var(--radius-pill);
885
+ }
886
+ .slider::-webkit-slider-thumb {
887
+ -webkit-appearance: none;
888
+ appearance: none;
889
+ width: 18px;
890
+ height: 18px;
891
+ border-radius: var(--radius-pill);
892
+ background: var(--color-brand-secondary-500);
893
+ border: 2px solid var(--color-pure-white);
894
+ box-shadow: 0 1px 4px color-mix(in srgb, var(--color-shadow-tint) 22%, transparent);
895
+ margin-top: -7px;
896
+ transition: transform var(--transition-fast);
897
+ }
898
+ .slider::-moz-range-thumb {
899
+ width: 18px;
900
+ height: 18px;
901
+ border-radius: var(--radius-pill);
902
+ background: var(--color-brand-secondary-500);
903
+ border: 2px solid var(--color-pure-white);
904
+ box-shadow: 0 1px 4px color-mix(in srgb, var(--color-shadow-tint) 22%, transparent);
905
+ }
906
+ .slider:focus-visible::-webkit-slider-thumb {
907
+ box-shadow: var(--shadow-focus);
908
+ }
909
+ .slider::-webkit-slider-thumb:hover { transform: scale(1.1); }
910
+
911
+
912
+ /* ----------------------------------------------------------------
913
+ 18. ON INVERSE — dark surfaces
914
+ ---------------------------------------------------------------- */
915
+ .input.on-inverse,
916
+ .dark .input,
917
+ .dark .group,
918
+ .dark .pill-search,
919
+ .dark .chips,
920
+ .dark .number-input,
921
+ .dark .date-trigger {
922
+ background: color-mix(in srgb, var(--color-pure-white) 6%, transparent);
923
+ border-color: var(--color-border-inverse);
924
+ color: var(--color-foreground-inverse-default);
925
+ }
926
+ .input.on-inverse::placeholder,
927
+ .dark .input::placeholder,
928
+ .dark .group > input::placeholder,
929
+ .dark .pill-search > input::placeholder,
930
+ .dark .chips > input::placeholder {
931
+ color: var(--color-foreground-inverse-placeholder);
932
+ }
933
+ .dark .group > input,
934
+ .dark .pill-search > input,
935
+ .dark .chips > input,
936
+ .dark .number-input > input,
937
+ .dark .date-trigger { color: var(--color-foreground-inverse-default); }
938
+ .dark .group .fix {
939
+ background: color-mix(in srgb, var(--color-pure-white) 4%, transparent);
940
+ color: var(--color-foreground-inverse-secondary);
941
+ border-color: var(--color-border-inverse);
942
+ }
943
+ .dark .number-input button {
944
+ background: color-mix(in srgb, var(--color-pure-white) 4%, transparent);
945
+ color: var(--color-foreground-inverse-default);
946
+ border-color: var(--color-border-inverse);
947
+ }
948
+ .dark .number-input button:hover { background: color-mix(in srgb, var(--color-pure-white) 10%, transparent); }
949
+ .dark .input:focus,
950
+ .dark .group:focus-within,
951
+ .dark .pill-search:focus-within,
952
+ .dark .chips:focus-within,
953
+ .dark .number-input:focus-within,
954
+ .dark .date-trigger:focus-visible {
955
+ border-color: var(--color-brand-secondary-300);
956
+ box-shadow: var(--shadow-focus-sky);
957
+ }
958
+ .dark .field > label { color: var(--color-foreground-inverse-default); }
959
+ .dark .field > .help { color: var(--color-foreground-inverse-muted); }
960
+ .dark .kbd {
961
+ background: color-mix(in srgb, var(--color-pure-white) 8%, transparent);
962
+ color: var(--color-foreground-inverse-secondary);
963
+ border-color: var(--color-border-inverse);
964
+ }
965
+ .dark .file-drop {
966
+ background: color-mix(in srgb, var(--color-pure-white) 4%, transparent);
967
+ border-color: var(--color-border-inverse);
968
+ color: var(--color-foreground-inverse-muted);
969
+ }
970
+ .dark .file-drop .ttl { color: var(--color-foreground-inverse-default); }
971
+ .dark .check-box,
972
+ .dark .radio-dot {
973
+ background: color-mix(in srgb, var(--color-pure-white) 6%, transparent);
974
+ border-color: var(--color-border-inverse);
975
+ }