@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,305 +1,314 @@
1
- /* ============================================================
2
- PRIMITIVES — Avatar · Money · Stat · Divider · Pill
3
- Atoms used across every product surface. All values from tokens.
4
- (Tabs moved to components/tabs.css — it's a stateful nav component.)
5
- ============================================================ */
6
-
7
- /* =================== AVATAR =================== */
8
- .av {
9
- display: inline-flex;
10
- align-items: center;
11
- justify-content: center;
12
- flex-shrink: 0;
13
- width: var(--av-size, 36px);
14
- height: var(--av-size, 36px);
15
- border-radius: var(--radius-pill);
16
- font-family: var(--font-mono);
17
- font-size: var(--av-fs, var(--fs-xs));
18
- font-weight: var(--w-bold);
19
- letter-spacing: var(--ls-caps);
20
- text-transform: uppercase;
21
- user-select: none;
22
- overflow: hidden;
23
- position: relative;
24
- }
25
- .av img { width: 100%; height: 100%; object-fit: cover; display: block; }
26
-
27
- .av-xs { --av-size: 20px; --av-fs: 9px; }
28
- .av-sm { --av-size: 28px; --av-fs: var(--fs-xxs); }
29
- .av-md { --av-size: 36px; --av-fs: var(--fs-xs); }
30
- .av-lg { --av-size: 48px; --av-fs: var(--fs-sm); }
31
- .av-xl { --av-size: 64px; --av-fs: var(--fs-md); }
32
-
33
- /* Tone — accent-on-tinted-bg uses the same background+foreground pair as feedback pills,
34
- so an Avatar reads as the same "object" as a status pill that references it. */
35
- .av-brand { background: var(--color-brand-secondary-50); color: var(--color-brand-secondary-700); }
36
- .av-success { background: var(--color-feedback-success-background); color: var(--color-feedback-success-foreground); }
37
- .av-warning { background: var(--color-feedback-warning-background); color: var(--color-feedback-warning-foreground); }
38
- .av-danger { background: var(--color-feedback-danger-background); color: var(--color-feedback-danger-foreground); }
39
- .av-info { background: var(--color-feedback-info-background); color: var(--color-feedback-info-foreground); }
40
- .av-neutral { background: var(--color-background-subtle); color: var(--color-foreground-muted); }
41
-
42
- /* Status dot — overlay for presence/online state */
43
- .av-dot {
44
- position: absolute;
45
- bottom: 0;
46
- right: 0;
47
- width: 30%;
48
- height: 30%;
49
- min-width: 8px;
50
- min-height: 8px;
51
- border-radius: var(--radius-pill);
52
- background: var(--color-success-500);
53
- box-shadow: 0 0 0 2px var(--color-background-surface);
54
- }
55
- .av-dot-busy { background: var(--color-feedback-danger-foreground); }
56
- .av-dot-away { background: var(--color-feedback-warning-foreground); }
57
- .av-dot-off { background: var(--color-foreground-muted); }
58
-
59
- /* Avatar group — overlapping stack */
60
- .av-group {
61
- display: inline-flex;
62
- align-items: center;
63
- isolation: isolate;
64
- }
65
- .av-group .av {
66
- margin-left: -8px;
67
- border: 2px solid var(--color-background-surface);
68
- }
69
- .av-group .av:first-child { margin-left: 0; }
70
- .av-group .av-more {
71
- background: var(--color-background-surface);
72
- color: var(--color-foreground-secondary);
73
- border: 1px dashed var(--color-border-default);
74
- }
75
-
76
- /* =================== MONEY =================== */
77
- .money {
78
- display: inline-flex;
79
- align-items: baseline;
80
- gap: var(--space-1);
81
- font-family: var(--font-mono);
82
- font-variant-numeric: tabular-nums;
83
- font-size: var(--money-fs, var(--fs-sm));
84
- font-weight: var(--w-semibold);
85
- color: var(--color-foreground-default);
86
- white-space: nowrap;
87
- }
88
- .money-cur {
89
- font-size: 0.78em;
90
- font-weight: var(--w-semibold);
91
- letter-spacing: var(--ls-caps);
92
- text-transform: uppercase;
93
- color: var(--color-foreground-muted);
94
- }
95
- .money-frac { color: var(--color-foreground-secondary); font-weight: var(--w-medium); }
96
-
97
- .money-sm { --money-fs: var(--fs-xs); }
98
- .money-md { --money-fs: var(--fs-sm); }
99
- .money-lg { --money-fs: var(--fs-md); font-weight: var(--w-bold); }
100
- .money-xl { --money-fs: var(--fs-2xl); font-weight: var(--w-bold); letter-spacing: var(--ls-tight); }
101
- .money-display { --money-fs: var(--fs-4xl); font-weight: var(--w-bold); letter-spacing: var(--ls-display); }
102
-
103
- .money-pos { color: var(--color-feedback-success-foreground); }
104
- .money-neg { color: var(--color-feedback-danger-foreground); }
105
- .money-muted { color: var(--color-foreground-muted); font-weight: var(--w-medium); }
106
-
107
- /* =================== METRIC (hero display number) ===================
108
- Big, display-weight figure for hero/KPI moments. Distinct from .money
109
- (mono, inline): .metric is SANS display, tabular, with a dimmed currency
110
- prefix and dimmed fraction. Colour INHERITS (works on light + inverse);
111
- dimming uses opacity so it reads on any surface. Tone via inline color
112
- or a utility (e.g. success). Size via .metric-{sm,md,lg}. */
113
- .metric {
114
- font-family: var(--font-display);
115
- font-weight: var(--w-bold);
116
- letter-spacing: var(--ls-display);
117
- line-height: 1;
118
- font-variant-numeric: tabular-nums;
119
- color: inherit;
120
- white-space: nowrap;
121
- font-size: var(--metric-fs, 38px);
122
- }
123
- .metric-cur { font-size: 0.58em; font-weight: var(--w-semibold); opacity: 0.55; margin-right: 0.32em; }
124
- .metric-frac { font-size: 0.68em; opacity: 0.55; }
125
- .metric-sm { --metric-fs: 30px; }
126
- .metric-md { --metric-fs: 38px; }
127
- .metric-lg { --metric-fs: var(--fs-5xl); }
128
- .metric-pos { color: var(--color-success-700); }
129
- .metric-neg { color: var(--color-danger-700); }
130
-
131
- /* =================== STAT =================== */
132
- .stat-row {
133
- display: grid;
134
- grid-template-columns: repeat(var(--stat-cols, 3), 1fr);
135
- gap: var(--space-3);
136
- }
137
- .stat {
138
- display: flex;
139
- flex-direction: column;
140
- gap: var(--space-2);
141
- padding: var(--space-5);
142
- background: var(--color-background-surface);
143
- border: 1px solid var(--color-border-default);
144
- border-radius: var(--radius-md);
145
- }
146
- .stat-label {
147
- font-family: var(--font-mono);
148
- font-size: var(--fs-xxs);
149
- font-weight: var(--w-semibold);
150
- letter-spacing: var(--ls-caps);
151
- text-transform: uppercase;
152
- color: var(--color-foreground-muted);
153
- }
154
- .stat-value {
155
- font-size: var(--fs-3xl);
156
- font-weight: var(--w-bold);
157
- letter-spacing: var(--ls-tight);
158
- color: var(--color-foreground-default);
159
- font-variant-numeric: tabular-nums;
160
- line-height: 1.1;
161
- }
162
- .stat-delta {
163
- display: inline-flex;
164
- align-items: center;
165
- gap: var(--space-1);
166
- font-family: var(--font-mono);
167
- font-size: var(--fs-xs);
168
- font-weight: var(--w-semibold);
169
- }
170
- .stat-delta i, .stat-delta svg { width: 14px; height: 14px; stroke-width: 2.25; }
171
- .stat-delta-up { color: var(--color-feedback-success-foreground); }
172
- .stat-delta-down { color: var(--color-feedback-danger-foreground); }
173
- .stat-delta-flat { color: var(--color-foreground-muted); }
174
-
175
- .stat-foot {
176
- font-size: var(--fs-xs);
177
- color: var(--color-foreground-secondary);
178
- margin-top: var(--space-1);
179
- }
180
-
181
- /* Compact variant — for sidebars / dense headers */
182
- .stat-compact {
183
- padding: var(--space-3) var(--space-4);
184
- background: transparent;
185
- border: 0;
186
- }
187
- .stat-compact .stat-value { font-size: var(--fs-xl); }
188
-
189
- /* =================== DIVIDER =================== */
190
- .divider {
191
- border: 0;
192
- border-top: 1px solid var(--color-border-default);
193
- margin: var(--space-4) 0;
194
- }
195
- .divider-subtle { border-top-color: var(--color-border-subtle); }
196
- .divider-strong { border-top-color: var(--color-border-strong); }
197
- .divider-dashed { border-top-style: dashed; }
198
-
199
- .divider-vertical {
200
- width: 1px;
201
- height: 1em;
202
- border-top: 0;
203
- border-left: 1px solid var(--color-border-default);
204
- display: inline-block;
205
- margin: 0 var(--space-3);
206
- vertical-align: middle;
207
- }
208
-
209
- /* Labelled divider — splits content with a midline label */
210
- .divider-labelled {
211
- display: flex;
212
- align-items: center;
213
- gap: var(--space-3);
214
- margin: var(--space-5) 0;
215
- font-family: var(--font-mono);
216
- font-size: var(--fs-xxs);
217
- font-weight: var(--w-semibold);
218
- letter-spacing: var(--ls-caps);
219
- text-transform: uppercase;
220
- color: var(--color-foreground-muted);
221
- }
222
- .divider-labelled::before,
223
- .divider-labelled::after {
224
- content: '';
225
- flex: 1;
226
- height: 1px;
227
- background: var(--color-border-default);
228
- }
229
-
230
- /* =================== PILL =================== */
231
- /* Canonical pill lives in components/status-pills.css (single source:
232
- Lato, chained tones .pill.success / sizes .pill.sm). This file no
233
- longer re-defines .pill — primitives.html loads status-pills.css. */
234
-
235
- /* =================== EYEBROW =================== */
236
- /* Uppercase micro-label. size = xs|sm|md, tone maps to a foreground role.
237
- When an icon sits inline, add .has-icon (the JS wrapper does this). */
238
- .eyebrow {
239
- display: block;
240
- font-size: var(--fs-xs);
241
- font-weight: var(--w-semibold);
242
- letter-spacing: var(--ls-caps);
243
- text-transform: uppercase;
244
- color: var(--color-foreground-muted);
245
- }
246
- .eyebrow.has-icon { display: inline-flex; align-items: center; gap: var(--space-2-5); }
247
- .eyebrow-xs { font-size: var(--fs-xxs); font-weight: var(--w-bold); }
248
- .eyebrow-xs.has-icon { gap: var(--space-2); }
249
- .eyebrow-sm { font-size: var(--fs-xxs); font-weight: var(--w-semibold); }
250
- .eyebrow-md { font-size: var(--fs-xs); font-weight: var(--w-semibold); }
251
- .eyebrow-muted { color: var(--color-foreground-muted); }
252
- .eyebrow-secondary { color: var(--color-foreground-secondary); }
253
- .eyebrow-default { color: var(--color-foreground-default); }
254
- .eyebrow-brand { color: var(--color-brand-secondary-700); }
255
- .eyebrow-inverse { color: var(--color-foreground-inverse-muted); }
256
-
257
- /* =================== FILTER CHIP =================== */
258
- /* Pill-button used in filter rows. Control height matches .input / .btn (40px). */
259
- .filter-chip {
260
- display: inline-flex;
261
- align-items: center;
262
- gap: var(--space-2-5);
263
- height: 40px;
264
- padding: 0 var(--space-4);
265
- border: 1px solid var(--color-border-default);
266
- border-radius: var(--radius-md);
267
- background: var(--color-background-surface);
268
- font-family: inherit;
269
- font-size: var(--fs-sm);
270
- font-weight: var(--w-medium);
271
- color: var(--color-foreground-secondary);
272
- cursor: pointer;
273
- white-space: nowrap;
274
- transition: background var(--transition-fast), border-color var(--transition-fast), color var(--transition-fast);
275
- }
276
- .filter-chip:hover { background: var(--color-background-hover); border-color: var(--color-border-strong); }
277
- .filter-chip:active { background: var(--color-background-press); }
278
- .filter-chip .chip-chevron { opacity: .55; }
279
-
280
- /* =================== PROGRESS BAR =================== */
281
- /* Slim track + fill. Width of fill is data-driven (set inline). */
282
- .progress {
283
- height: var(--progress-h, 6px);
284
- background: var(--progress-track, var(--color-neutral-100));
285
- border-radius: var(--radius-pill);
286
- position: relative;
287
- overflow: hidden;
288
- }
289
- .progress-fill {
290
- position: absolute;
291
- inset: 0;
292
- background: var(--progress-fill, var(--grad-brand-soft));
293
- border-radius: var(--radius-pill);
294
- }
295
-
296
- /* =================== DESC TERM =================== */
297
- /* Labelled term-definition pair (receipts, detail drawers). */
298
- .desc-term-label {
299
- font-size: var(--fs-xxs);
300
- font-weight: var(--w-bold);
301
- letter-spacing: var(--ls-caps);
302
- text-transform: uppercase;
303
- color: var(--color-foreground-muted);
304
- margin-bottom: var(--space-2-5);
305
- }
1
+ /* ============================================================
2
+ PRIMITIVES — Avatar · Money · Stat · Divider · Pill
3
+ Atoms used across every product surface. All values from tokens.
4
+ (Tabs moved to components/tabs.css — it's a stateful nav component.)
5
+ ============================================================ */
6
+
7
+ /* =================== AVATAR =================== */
8
+ .av {
9
+ display: inline-flex;
10
+ align-items: center;
11
+ justify-content: center;
12
+ flex-shrink: 0;
13
+ width: var(--av-size, 36px);
14
+ height: var(--av-size, 36px);
15
+ border-radius: var(--radius-pill);
16
+ font-family: var(--font-mono);
17
+ font-size: var(--av-fs, var(--fs-xs));
18
+ font-weight: var(--w-bold);
19
+ letter-spacing: var(--ls-caps);
20
+ text-transform: uppercase;
21
+ user-select: none;
22
+ overflow: hidden;
23
+ position: relative;
24
+ }
25
+ .av img { width: 100%; height: 100%; object-fit: cover; display: block; }
26
+
27
+ .av-xs { --av-size: 20px; --av-fs: 9px; }
28
+ .av-sm { --av-size: 28px; --av-fs: var(--fs-xxs); }
29
+ .av-md { --av-size: 36px; --av-fs: var(--fs-xs); }
30
+ .av-lg { --av-size: 48px; --av-fs: var(--fs-sm); }
31
+ .av-xl { --av-size: 64px; --av-fs: var(--fs-md); }
32
+
33
+ /* Tone — accent-on-tinted-bg uses the same background+foreground pair as feedback pills,
34
+ so an Avatar reads as the same "object" as a status pill that references it. */
35
+ .av-brand { background: var(--color-brand-secondary-50); color: var(--color-brand-secondary-700); }
36
+ .av-success { background: var(--color-feedback-success-background); color: var(--color-feedback-success-foreground); }
37
+ .av-warning { background: var(--color-feedback-warning-background); color: var(--color-feedback-warning-foreground); }
38
+ .av-danger { background: var(--color-feedback-danger-background); color: var(--color-feedback-danger-foreground); }
39
+ .av-info { background: var(--color-feedback-info-background); color: var(--color-feedback-info-foreground); }
40
+ .av-neutral { background: var(--color-background-subtle); color: var(--color-foreground-muted); }
41
+
42
+ /* Status dot — overlay for presence/online state */
43
+ .av-dot {
44
+ position: absolute;
45
+ bottom: 0;
46
+ right: 0;
47
+ width: 30%;
48
+ height: 30%;
49
+ min-width: 8px;
50
+ min-height: 8px;
51
+ border-radius: var(--radius-pill);
52
+ background: var(--color-success-500);
53
+ box-shadow: 0 0 0 2px var(--color-background-surface);
54
+ }
55
+ .av-dot-busy { background: var(--color-feedback-danger-foreground); }
56
+ .av-dot-away { background: var(--color-feedback-warning-foreground); }
57
+ .av-dot-off { background: var(--color-foreground-muted); }
58
+
59
+ /* Avatar group — overlapping stack */
60
+ .av-group {
61
+ display: inline-flex;
62
+ align-items: center;
63
+ isolation: isolate;
64
+ }
65
+ .av-group .av {
66
+ margin-left: -8px;
67
+ border: 2px solid var(--color-background-surface);
68
+ }
69
+ .av-group .av:first-child { margin-left: 0; }
70
+ .av-group .av-more {
71
+ background: var(--color-background-surface);
72
+ color: var(--color-foreground-secondary);
73
+ border: 1px dashed var(--color-border-default);
74
+ }
75
+
76
+ /* =================== MONEY =================== */
77
+ .money {
78
+ display: inline-flex;
79
+ align-items: baseline;
80
+ gap: var(--space-1);
81
+ font-family: var(--font-mono);
82
+ font-variant-numeric: tabular-nums;
83
+ font-size: var(--money-fs, var(--fs-sm));
84
+ font-weight: var(--w-semibold);
85
+ color: var(--color-foreground-default);
86
+ white-space: nowrap;
87
+ }
88
+ .money-cur {
89
+ font-size: 0.78em;
90
+ font-weight: var(--w-semibold);
91
+ letter-spacing: var(--ls-caps);
92
+ text-transform: uppercase;
93
+ color: var(--color-foreground-muted);
94
+ }
95
+ .money-frac { color: var(--color-foreground-secondary); font-weight: var(--w-medium); }
96
+
97
+ .money-sm { --money-fs: var(--fs-xs); }
98
+ .money-md { --money-fs: var(--fs-sm); }
99
+ .money-lg { --money-fs: var(--fs-md); font-weight: var(--w-bold); }
100
+ .money-xl { --money-fs: var(--fs-2xl); font-weight: var(--w-bold); letter-spacing: var(--ls-tight); }
101
+ .money-display { --money-fs: var(--fs-4xl); font-weight: var(--w-bold); letter-spacing: var(--ls-display); }
102
+
103
+ .money-pos { color: var(--color-feedback-success-foreground); }
104
+ .money-neg { color: var(--color-feedback-danger-foreground); }
105
+ .money-muted { color: var(--color-foreground-muted); font-weight: var(--w-medium); }
106
+
107
+ /* =================== METRIC (hero display number) ===================
108
+ Big, display-weight figure for hero/KPI moments. Distinct from .money
109
+ (mono, inline): .metric is SANS display, tabular, with a dimmed currency
110
+ prefix and dimmed fraction. Colour INHERITS (works on light + inverse);
111
+ dimming uses opacity so it reads on any surface. Tone via inline color
112
+ or a utility (e.g. success). Size via .metric-{sm,md,lg}. */
113
+ .metric {
114
+ font-family: var(--font-display);
115
+ font-weight: var(--w-bold);
116
+ letter-spacing: var(--ls-display);
117
+ line-height: 1;
118
+ font-variant-numeric: tabular-nums;
119
+ color: inherit;
120
+ white-space: nowrap;
121
+ font-size: var(--metric-fs, 38px);
122
+ }
123
+ .metric-cur { font-size: 0.58em; font-weight: var(--w-semibold); opacity: 0.55; margin-right: 0.32em; }
124
+ .metric-frac { font-size: 0.68em; opacity: 0.55; }
125
+ .metric-sm { --metric-fs: 30px; }
126
+ .metric-md { --metric-fs: 38px; }
127
+ .metric-lg { --metric-fs: var(--fs-5xl); }
128
+ .metric-pos { color: var(--color-success-700); }
129
+ .metric-neg { color: var(--color-danger-700); }
130
+
131
+ /* =================== STAT =================== */
132
+ .stat-row {
133
+ display: grid;
134
+ grid-template-columns: repeat(var(--stat-cols, 3), 1fr);
135
+ gap: var(--space-3);
136
+ }
137
+ .stat {
138
+ display: flex;
139
+ flex-direction: column;
140
+ gap: var(--space-2);
141
+ padding: var(--space-5);
142
+ background: var(--color-background-surface);
143
+ border: 1px solid var(--color-border-default);
144
+ border-radius: var(--radius-md);
145
+ }
146
+ .stat-label {
147
+ font-family: var(--font-mono);
148
+ font-size: var(--fs-xxs);
149
+ font-weight: var(--w-semibold);
150
+ letter-spacing: var(--ls-caps);
151
+ text-transform: uppercase;
152
+ color: var(--color-foreground-muted);
153
+ }
154
+ .stat-value {
155
+ font-size: var(--fs-3xl);
156
+ font-weight: var(--w-bold);
157
+ letter-spacing: var(--ls-tight);
158
+ color: var(--color-foreground-default);
159
+ font-variant-numeric: tabular-nums;
160
+ line-height: 1.1;
161
+ }
162
+ .stat-delta {
163
+ display: inline-flex;
164
+ align-items: center;
165
+ gap: var(--space-1);
166
+ font-family: var(--font-mono);
167
+ font-size: var(--fs-xs);
168
+ font-weight: var(--w-semibold);
169
+ }
170
+ .stat-delta i, .stat-delta svg { width: 14px; height: 14px; stroke-width: 2.25; }
171
+ .stat-delta-up { color: var(--color-feedback-success-foreground); }
172
+ .stat-delta-down { color: var(--color-feedback-danger-foreground); }
173
+ .stat-delta-flat { color: var(--color-foreground-muted); }
174
+
175
+ .stat-foot {
176
+ font-size: var(--fs-xs);
177
+ color: var(--color-foreground-secondary);
178
+ margin-top: var(--space-1);
179
+ }
180
+ /* Bare stat — figure block without tile chrome, for use inside a Card */
181
+ .stat.is-bare { background: none; border: 0; padding: 0; border-radius: 0; }
182
+
183
+ /* Compact variant — for sidebars / dense headers */
184
+ .stat-compact {
185
+ padding: var(--space-3) var(--space-4);
186
+ background: transparent;
187
+ border: 0;
188
+ }
189
+ .stat-compact .stat-value { font-size: var(--fs-xl); }
190
+
191
+ /* =================== DIVIDER =================== */
192
+ .divider {
193
+ border: 0;
194
+ border-top: 1px solid var(--color-border-default);
195
+ margin: var(--space-4) 0;
196
+ }
197
+ .divider-subtle { border-top-color: var(--color-border-subtle); }
198
+ .divider-strong { border-top-color: var(--color-border-strong); }
199
+ .divider-dashed { border-top-style: dashed; }
200
+
201
+ .divider-vertical {
202
+ width: 1px;
203
+ height: 1em;
204
+ border-top: 0;
205
+ border-left: 1px solid var(--color-border-default);
206
+ display: inline-block;
207
+ margin: 0 var(--space-3);
208
+ vertical-align: middle;
209
+ }
210
+
211
+ /* Labelled divider — splits content with a midline label */
212
+ .divider-labelled {
213
+ display: flex;
214
+ align-items: center;
215
+ gap: var(--space-3);
216
+ margin: var(--space-5) 0;
217
+ font-family: var(--font-mono);
218
+ font-size: var(--fs-xxs);
219
+ font-weight: var(--w-semibold);
220
+ letter-spacing: var(--ls-caps);
221
+ text-transform: uppercase;
222
+ color: var(--color-foreground-muted);
223
+ }
224
+ .divider-labelled::before,
225
+ .divider-labelled::after {
226
+ content: '';
227
+ flex: 1;
228
+ height: 1px;
229
+ background: var(--color-border-default);
230
+ }
231
+
232
+ /* =================== PILL =================== */
233
+ /* Canonical pill lives in components/status-pills.css (single source:
234
+ Lato, chained tones .pill.success / sizes .pill.sm). This file no
235
+ longer re-defines .pill — primitives.html loads status-pills.css. */
236
+
237
+ /* =================== EYEBROW =================== */
238
+ /* Uppercase micro-label. size = xs|sm|md, tone maps to a foreground role.
239
+ When an icon sits inline, add .has-icon (the JS wrapper does this). */
240
+ .eyebrow {
241
+ display: block;
242
+ font-size: var(--fs-xs);
243
+ font-weight: var(--w-semibold);
244
+ letter-spacing: var(--ls-caps);
245
+ text-transform: uppercase;
246
+ color: var(--color-foreground-muted);
247
+ }
248
+ .eyebrow.has-icon { display: inline-flex; align-items: center; gap: var(--space-2-5); }
249
+ .eyebrow-xs { font-size: var(--fs-xxs); font-weight: var(--w-bold); }
250
+ .eyebrow-xs.has-icon { gap: var(--space-2); }
251
+ .eyebrow-sm { font-size: var(--fs-xxs); font-weight: var(--w-semibold); }
252
+ .eyebrow-md { font-size: var(--fs-xs); font-weight: var(--w-semibold); }
253
+ .eyebrow-muted { color: var(--color-foreground-muted); }
254
+ .eyebrow-secondary { color: var(--color-foreground-secondary); }
255
+ .eyebrow-default { color: var(--color-foreground-default); }
256
+ .eyebrow-brand { color: var(--color-brand-secondary-700); }
257
+ .eyebrow-inverse { color: var(--color-foreground-inverse-muted); }
258
+
259
+ /* =================== FILTER CHIP =================== */
260
+ /* Pill-button used in filter rows. Control height matches .input / .btn (40px). */
261
+ .filter-chip {
262
+ display: inline-flex;
263
+ align-items: center;
264
+ gap: var(--space-2-5);
265
+ height: 40px;
266
+ padding: 0 var(--space-4);
267
+ border: 1px solid var(--color-border-default);
268
+ border-radius: var(--radius-md);
269
+ background: var(--color-background-surface);
270
+ font-family: inherit;
271
+ font-size: var(--fs-sm);
272
+ font-weight: var(--w-medium);
273
+ color: var(--color-foreground-secondary);
274
+ cursor: pointer;
275
+ white-space: nowrap;
276
+ transition: background var(--transition-fast), border-color var(--transition-fast), color var(--transition-fast);
277
+ }
278
+ .filter-chip:hover { background: var(--color-background-hover); border-color: var(--color-border-strong); }
279
+ .filter-chip:active { background: var(--color-background-press); }
280
+ /* Selected (aria-pressed via FilterChip TSX) — same active treatment as combobox filter chips */
281
+ .filter-chip[aria-pressed="true"] {
282
+ background: var(--color-background-selected);
283
+ border-color: var(--color-brand-secondary-200);
284
+ color: var(--color-foreground-brand);
285
+ font-weight: var(--w-semibold);
286
+ }
287
+ .filter-chip .chip-chevron { opacity: .55; }
288
+
289
+ /* =================== PROGRESS BAR =================== */
290
+ /* Slim track + fill. Width of fill is data-driven (set inline). */
291
+ .progress {
292
+ height: var(--progress-h, 6px);
293
+ background: var(--progress-track, var(--color-border-default));
294
+ border-radius: var(--radius-pill);
295
+ position: relative;
296
+ overflow: hidden;
297
+ }
298
+ .progress-fill {
299
+ position: absolute;
300
+ inset: 0;
301
+ background: var(--progress-fill, var(--grad-brand-soft));
302
+ border-radius: var(--radius-pill);
303
+ }
304
+
305
+ /* =================== DESC TERM =================== */
306
+ /* Labelled term-definition pair (receipts, detail drawers). */
307
+ .desc-term-label {
308
+ font-size: var(--fs-xxs);
309
+ font-weight: var(--w-bold);
310
+ letter-spacing: var(--ls-caps);
311
+ text-transform: uppercase;
312
+ color: var(--color-foreground-muted);
313
+ margin-bottom: var(--space-2-5);
314
+ }