@xenide-io/the-old-ui-theme 0.2.9 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/README.md +1 -1
  2. package/dist/{chunk-SHF57J7U.mjs → chunk-FDMD3IIN.mjs} +1573 -652
  3. package/dist/index-D1RTT2Ap.d.mts +1044 -0
  4. package/dist/index-D1RTT2Ap.d.ts +1044 -0
  5. package/dist/index.d.mts +10 -7
  6. package/dist/index.d.ts +10 -7
  7. package/dist/index.js +1705 -744
  8. package/dist/index.mjs +61 -1
  9. package/dist/tailwind-preset.js +7 -0
  10. package/dist/tailwind-preset.mjs +7 -0
  11. package/dist/ui.d.mts +3 -2
  12. package/dist/ui.d.ts +3 -2
  13. package/dist/ui.js +1584 -666
  14. package/dist/ui.mjs +31 -1
  15. package/package.json +18 -4
  16. package/src/app/globals.css +1005 -227
  17. package/src/components/icons/IconBase.tsx +20 -12
  18. package/src/components/icons/createIconBase.tsx +12 -7
  19. package/src/components/icons/icons.tsx +208 -18
  20. package/src/components/sections/AlertShowcase.tsx +2 -1
  21. package/src/components/sections/BadgeShowcase.tsx +1 -1
  22. package/src/components/sections/ButtonShowcase.tsx +6 -10
  23. package/src/components/sections/CardShowcase.tsx +22 -6
  24. package/src/components/sections/DropdownShowcase.tsx +4 -6
  25. package/src/components/sections/FilterChipsShowcase.tsx +74 -26
  26. package/src/components/sections/FormShowcase.tsx +2 -1
  27. package/src/components/sections/IconShowcase.tsx +86 -206
  28. package/src/components/sections/KbdShowcase.tsx +47 -21
  29. package/src/components/sections/ModalShowcase.tsx +7 -6
  30. package/src/components/sections/ProductLayoutsShowcase.tsx +138 -0
  31. package/src/components/sections/ProgressShowcase.tsx +50 -53
  32. package/src/components/sections/SwapShowcase.tsx +13 -5
  33. package/src/components/ui/Alert.tsx +28 -19
  34. package/src/components/ui/AuthLayout.tsx +58 -0
  35. package/src/components/ui/Badge.tsx +10 -5
  36. package/src/components/ui/Button.tsx +22 -12
  37. package/src/components/ui/ButtonChrome.tsx +1 -1
  38. package/src/components/ui/Calendar.tsx +6 -7
  39. package/src/components/ui/Card.tsx +21 -11
  40. package/src/components/ui/CollapsibleSection.tsx +11 -11
  41. package/src/components/ui/DropdownMenu.tsx +189 -65
  42. package/src/components/ui/FilterBar.tsx +165 -0
  43. package/src/components/ui/FilterChips.tsx +65 -35
  44. package/src/components/ui/FilterControls.tsx +30 -0
  45. package/src/components/ui/FormField.tsx +69 -0
  46. package/src/components/ui/Input.tsx +166 -140
  47. package/src/components/ui/Kbd.tsx +88 -13
  48. package/src/components/ui/Loader.tsx +96 -0
  49. package/src/components/ui/Modal.tsx +66 -36
  50. package/src/components/ui/SettingsLayout.tsx +94 -0
  51. package/src/components/ui/ShowcaseWrapper.tsx +4 -16
  52. package/src/components/ui/Stepper.tsx +5 -6
  53. package/src/components/ui/first-slice.test.tsx +98 -0
  54. package/src/components/ui/index.ts +62 -3
  55. package/src/components/ui/interaction-primitives.test.tsx +77 -0
  56. package/src/components/ui/kbd-icons.test.tsx +90 -0
  57. package/src/components/ui/product-patterns.test.tsx +77 -0
  58. package/src/styles/themes.css +411 -1
  59. package/tailwind-preset.ts +7 -0
  60. package/dist/index-CmS6hcUk.d.mts +0 -753
  61. package/dist/index-CmS6hcUk.d.ts +0 -753
@@ -36,21 +36,71 @@
36
36
  }
37
37
 
38
38
  /* Structural tokens — shared across colour themes */
39
- :root {
39
+ :where(:root) {
40
40
  --ph-shadow-elevation-3000: 0 3px 0 var(--ph-border-3000);
41
41
  --ph-chrome-depth: 0.1875rem;
42
42
  --ph-chrome-inset-solid-top: inset 0 1px 0 hsl(0deg 0% 100% / 0.26);
43
43
  --ph-chrome-inset-solid-bottom: inset 0 -1px 0 hsl(0deg 0% 0% / 0.14);
44
44
  --ph-radius-app: 0.375rem;
45
+ --ph-radius-control: var(--ph-radius-app);
46
+ --ph-radius-surface: 0.5rem;
47
+ --ph-radius-overlay: 0.75rem;
45
48
  --ph-border-control: color-mix(in oklab, var(--ph-border) 68%, var(--ph-border-strong));
46
49
  /** Tailwind `ph.brand` maps to `--ph-accent`; plain CSS controls used only this name, so it must exist. */
47
50
  --ph-brand: var(--ph-accent);
51
+ /** Compatibility fallbacks let existing custom themes adopt the expanded semantic contract incrementally. */
52
+ --ph-surface-inset: var(--ph-muted);
53
+ --ph-surface-raised: var(--ph-surface);
54
+ --ph-surface-overlay: var(--ph-surface);
55
+ --ph-surface-hover: color-mix(in oklab, var(--ph-text-primary) 7%, var(--ph-surface-raised));
56
+ --ph-border-subtle: color-mix(in oklab, var(--ph-border) 62%, var(--ph-surface));
57
+ --ph-on-accent: #ffffff;
58
+ --ph-on-violet: #ffffff;
59
+ --ph-on-success: #ffffff;
60
+ --ph-on-warning: #1f1808;
61
+ --ph-on-danger: #ffffff;
62
+ --ph-on-info: #ffffff;
63
+ --ph-focus-ring: var(--ph-accent);
64
+ --ph-selected-bg: color-mix(in oklab, var(--ph-accent) 12%, var(--ph-surface));
65
+ --ph-selected-text: color-mix(in oklab, var(--ph-accent) 72%, var(--ph-text-primary));
66
+ --ph-selected-border: color-mix(in oklab, var(--ph-accent) 58%, var(--ph-border));
67
+ --ph-info-bg: color-mix(in oklab, var(--ph-info) 12%, var(--ph-surface));
68
+ --ph-info-border: color-mix(in oklab, var(--ph-info) 45%, var(--ph-border));
69
+ --ph-info-text: color-mix(in oklab, var(--ph-info) 76%, var(--ph-text-primary));
70
+ --ph-success-bg: color-mix(in oklab, var(--ph-success) 12%, var(--ph-surface));
71
+ --ph-success-border: color-mix(in oklab, var(--ph-success) 45%, var(--ph-border));
72
+ --ph-success-text: color-mix(in oklab, var(--ph-success) 76%, var(--ph-text-primary));
73
+ --ph-warning-bg: color-mix(in oklab, var(--ph-warning) 14%, var(--ph-surface));
74
+ --ph-warning-border: color-mix(in oklab, var(--ph-warning) 48%, var(--ph-border));
75
+ --ph-warning-text: color-mix(in oklab, var(--ph-warning) 72%, var(--ph-text-primary));
76
+ --ph-danger-bg: color-mix(in oklab, var(--ph-danger) 12%, var(--ph-surface));
77
+ --ph-danger-border: color-mix(in oklab, var(--ph-danger) 45%, var(--ph-border));
78
+ --ph-danger-text: color-mix(in oklab, var(--ph-danger) 76%, var(--ph-text-primary));
79
+ --ph-shadow-raised: 0 1px 2px hsl(0deg 0% 0% / 0.08), 0 3px 8px hsl(0deg 0% 0% / 0.06);
80
+ --ph-shadow-floating: 0 18px 48px hsl(0deg 0% 0% / 0.22), 0 2px 8px hsl(0deg 0% 0% / 0.12);
81
+ --ph-motion-fast: 120ms;
82
+ --ph-motion-standard: 180ms;
48
83
  }
49
84
 
50
85
  html {
51
86
  scroll-behavior: smooth;
52
87
  }
53
88
 
89
+ @media (prefers-reduced-motion: reduce) {
90
+ html {
91
+ scroll-behavior: auto;
92
+ }
93
+
94
+ *,
95
+ *::before,
96
+ *::after {
97
+ animation-duration: 0.01ms !important;
98
+ animation-iteration-count: 1 !important;
99
+ scroll-behavior: auto !important;
100
+ transition-duration: 0.01ms !important;
101
+ }
102
+ }
103
+
54
104
  body {
55
105
  @apply bg-ph-canvas text-ph-ink antialiased;
56
106
  font-size: 15px;
@@ -62,33 +112,44 @@ body {
62
112
  * PostHog LemonButton — from frontend/src/lib/lemon-ui/LemonButton/LemonButton.scss
63
113
  */
64
114
  .ph-btn {
115
+ --ph-btn-control-size: 2.3125rem;
65
116
  @apply inline-flex shrink-0 cursor-pointer items-center justify-center gap-2 whitespace-nowrap font-medium;
66
- border-radius: var(--ph-radius-app);
117
+ border-radius: var(--ph-radius-control);
67
118
  min-height: 2.3125rem;
68
119
  padding: 0.25rem 0.875rem;
69
120
  font-size: 0.875rem;
70
121
  line-height: 1.5rem;
71
122
  transition:
72
- transform 200ms ease,
73
- opacity 200ms ease,
74
- background-color 200ms ease,
75
- color 200ms ease;
123
+ transform var(--ph-motion-fast) ease,
124
+ opacity var(--ph-motion-fast) ease,
125
+ background-color var(--ph-motion-fast) ease,
126
+ border-color var(--ph-motion-fast) ease,
127
+ color var(--ph-motion-fast) ease;
128
+ touch-action: manipulation;
76
129
  @apply disabled:pointer-events-none disabled:opacity-[0.65];
77
130
  }
78
131
 
132
+ .ph-btn:focus-visible {
133
+ outline: 3px solid color-mix(in oklab, var(--ph-focus-ring) 38%, transparent);
134
+ outline-offset: 2px;
135
+ }
136
+
79
137
  .ph-btn-sm {
138
+ --ph-btn-control-size: 2.0625rem;
80
139
  min-height: 2.0625rem;
81
140
  padding: 0.25rem 0.625rem;
82
141
  font-size: 0.8125rem;
83
142
  }
84
143
 
85
144
  .ph-btn-xs {
145
+ --ph-btn-control-size: 1.625rem;
86
146
  min-height: 1.625rem;
87
147
  padding: 0.125rem 0.5rem;
88
148
  font-size: 0.6875rem;
89
149
  }
90
150
 
91
151
  .ph-btn-lg {
152
+ --ph-btn-control-size: 3.0625rem;
92
153
  min-height: 3.0625rem;
93
154
  padding: 0.375rem 1.25rem;
94
155
  font-size: 1rem;
@@ -100,32 +161,44 @@ body {
100
161
 
101
162
  .ph-btn-circle {
102
163
  @apply aspect-square rounded-full p-0;
103
- min-width: 2.3125rem;
164
+ min-width: var(--ph-btn-control-size);
104
165
  }
105
166
 
106
167
  .ph-btn-square {
107
168
  @apply aspect-square p-0;
108
- min-width: 2.3125rem;
109
- }
110
-
111
- /* Lemon chrome primary & secondary (LemonButton.scss ::before / ::after on __chrome) */
112
- .ph-btn-primary,
113
- .ph-btn-secondary {
169
+ min-width: var(--ph-btn-control-size);
170
+ }
171
+
172
+ /* Shared tactile chrome for every filled button. */
173
+ :is(
174
+ .ph-btn-raised,
175
+ .ph-btn-primary,
176
+ .ph-btn-secondary,
177
+ .ph-btn-signal,
178
+ .ph-btn-accent,
179
+ .ph-btn-neutral,
180
+ .ph-btn-info,
181
+ .ph-btn-success,
182
+ .ph-btn-warning,
183
+ .ph-btn-danger
184
+ ) {
114
185
  --ph-btn-depth: 0px;
115
186
  --ph-btn-chrome-depth: var(--ph-chrome-depth);
187
+ --ph-btn-border-width: 1.5px;
116
188
  --ph-btn-face: var(--ph-secondary-button-bg);
117
189
  --ph-btn-border: var(--ph-secondary-button-border);
118
190
  --ph-btn-border-hover: var(--ph-secondary-button-border-hover);
119
191
  --ph-btn-frame: var(--ph-secondary-frame-bg);
120
192
  --ph-btn-ink: var(--ph-text-secondary);
193
+ --ph-btn-outline-ink: var(--ph-text-primary);
121
194
 
122
195
  padding: 0;
123
196
  min-height: unset;
124
197
  border: none;
125
198
  background: transparent;
126
199
  transition:
127
- opacity 200ms ease,
128
- color 200ms ease;
200
+ opacity var(--ph-motion-fast) ease,
201
+ color var(--ph-motion-fast) ease;
129
202
  }
130
203
 
131
204
  .ph-btn-primary {
@@ -134,11 +207,95 @@ body {
134
207
  --ph-btn-border-hover: var(--ph-primary-button-border-hover);
135
208
  --ph-btn-frame: var(--ph-primary-frame-bg);
136
209
  --ph-btn-ink: var(--ph-text-3000);
210
+ --ph-btn-outline-ink: var(--ph-text-primary);
211
+ }
212
+
213
+ .ph-btn-signal {
214
+ --ph-btn-face: var(--ph-accent);
215
+ --ph-btn-border: color-mix(in oklab, var(--ph-accent) 78%, black);
216
+ --ph-btn-border-hover: color-mix(in oklab, var(--ph-accent) 64%, black);
217
+ --ph-btn-frame: color-mix(in oklab, var(--ph-accent) 70%, black);
218
+ --ph-btn-ink: var(--ph-on-accent);
219
+ --ph-btn-outline-ink: var(--ph-accent);
220
+ }
221
+
222
+ .ph-btn-accent {
223
+ --ph-btn-face: var(--ph-violet);
224
+ --ph-btn-border: color-mix(in oklab, var(--ph-violet) 78%, black);
225
+ --ph-btn-border-hover: color-mix(in oklab, var(--ph-violet) 64%, black);
226
+ --ph-btn-frame: color-mix(in oklab, var(--ph-violet) 70%, black);
227
+ --ph-btn-ink: var(--ph-on-violet);
228
+ --ph-btn-outline-ink: var(--ph-violet);
229
+ }
230
+
231
+ .ph-btn-neutral {
232
+ --ph-btn-face: var(--ph-toolbar);
233
+ --ph-btn-border: var(--ph-border-strong);
234
+ --ph-btn-border-hover: var(--ph-text-tertiary);
235
+ --ph-btn-frame: var(--ph-border-strong);
236
+ --ph-btn-ink: var(--ph-text-primary);
237
+ --ph-btn-outline-ink: var(--ph-text-primary);
238
+ }
239
+
240
+ .ph-btn-info {
241
+ --ph-btn-face: var(--ph-info);
242
+ --ph-btn-border: color-mix(in oklab, var(--ph-info) 78%, black);
243
+ --ph-btn-border-hover: color-mix(in oklab, var(--ph-info) 64%, black);
244
+ --ph-btn-frame: color-mix(in oklab, var(--ph-info) 70%, black);
245
+ --ph-btn-ink: var(--ph-on-info);
246
+ --ph-btn-outline-ink: var(--ph-info-text);
247
+ }
248
+
249
+ .ph-btn-success {
250
+ --ph-btn-face: var(--ph-success);
251
+ --ph-btn-border: color-mix(in oklab, var(--ph-success) 78%, black);
252
+ --ph-btn-border-hover: color-mix(in oklab, var(--ph-success) 64%, black);
253
+ --ph-btn-frame: color-mix(in oklab, var(--ph-success) 70%, black);
254
+ --ph-btn-ink: var(--ph-on-success);
255
+ --ph-btn-outline-ink: var(--ph-success-text);
256
+ }
257
+
258
+ .ph-btn-warning {
259
+ --ph-btn-face: var(--ph-warning);
260
+ --ph-btn-border: color-mix(in oklab, var(--ph-warning) 78%, black);
261
+ --ph-btn-border-hover: color-mix(in oklab, var(--ph-warning) 64%, black);
262
+ --ph-btn-frame: color-mix(in oklab, var(--ph-warning) 70%, black);
263
+ --ph-btn-ink: var(--ph-on-warning);
264
+ --ph-btn-outline-ink: var(--ph-warning-text);
137
265
  }
138
266
 
139
- .ph-btn-chrome,
140
- .ph-btn-primary:not(:has(.ph-btn-chrome)),
141
- .ph-btn-secondary:not(:has(.ph-btn-chrome)) {
267
+ .ph-btn-danger {
268
+ --ph-btn-face: var(--ph-danger);
269
+ --ph-btn-border: color-mix(in oklab, var(--ph-danger) 78%, black);
270
+ --ph-btn-border-hover: color-mix(in oklab, var(--ph-danger) 64%, black);
271
+ --ph-btn-frame: color-mix(in oklab, var(--ph-danger) 70%, black);
272
+ --ph-btn-ink: var(--ph-on-danger);
273
+ --ph-btn-outline-ink: var(--ph-danger-text);
274
+ }
275
+
276
+ :is(
277
+ .ph-btn-raised,
278
+ .ph-btn-primary,
279
+ .ph-btn-secondary,
280
+ .ph-btn-signal,
281
+ .ph-btn-accent,
282
+ .ph-btn-neutral,
283
+ .ph-btn-info,
284
+ .ph-btn-success,
285
+ .ph-btn-warning,
286
+ .ph-btn-danger
287
+ ) .ph-btn-chrome,
288
+ :is(
289
+ .ph-btn-primary,
290
+ .ph-btn-secondary,
291
+ .ph-btn-signal,
292
+ .ph-btn-accent,
293
+ .ph-btn-neutral,
294
+ .ph-btn-info,
295
+ .ph-btn-success,
296
+ .ph-btn-warning,
297
+ .ph-btn-danger
298
+ ):not(:has(.ph-btn-chrome)) {
142
299
  position: relative;
143
300
  isolation: isolate;
144
301
  display: inline-flex;
@@ -155,19 +312,29 @@ body {
155
312
  color: var(--ph-btn-ink);
156
313
  background: transparent;
157
314
  border: none;
315
+ transition:
316
+ color var(--ph-motion-fast) ease,
317
+ transform 50ms ease-out;
158
318
  }
159
319
 
160
320
  .ph-btn-chrome > *,
161
- .ph-btn-primary:not(:has(.ph-btn-chrome)) > *,
162
- .ph-btn-secondary:not(:has(.ph-btn-chrome)) > * {
321
+ :is(
322
+ .ph-btn-primary,
323
+ .ph-btn-secondary,
324
+ .ph-btn-signal,
325
+ .ph-btn-accent,
326
+ .ph-btn-neutral,
327
+ .ph-btn-info,
328
+ .ph-btn-success,
329
+ .ph-btn-warning,
330
+ .ph-btn-danger
331
+ ):not(:has(.ph-btn-chrome)) > * {
163
332
  position: relative;
164
333
  z-index: 1;
165
334
  }
166
335
 
167
- .ph-btn-sm.ph-btn-primary .ph-btn-chrome,
168
- .ph-btn-sm.ph-btn-secondary .ph-btn-chrome,
169
- .ph-btn-sm.ph-btn-primary:not(:has(.ph-btn-chrome)),
170
- .ph-btn-sm.ph-btn-secondary:not(:has(.ph-btn-chrome)) {
336
+ .ph-btn-sm.ph-btn-raised .ph-btn-chrome,
337
+ .ph-btn-sm:is(.ph-btn-primary, .ph-btn-secondary, .ph-btn-signal, .ph-btn-accent, .ph-btn-neutral, .ph-btn-info, .ph-btn-success, .ph-btn-warning, .ph-btn-danger):not(:has(.ph-btn-chrome)) {
171
338
  min-height: 2.0625rem;
172
339
  padding:
173
340
  calc(0.25rem - var(--ph-btn-chrome-depth) * 0.5) 0.625rem
@@ -175,10 +342,8 @@ body {
175
342
  font-size: 0.8125rem;
176
343
  }
177
344
 
178
- .ph-btn-xs.ph-btn-primary .ph-btn-chrome,
179
- .ph-btn-xs.ph-btn-secondary .ph-btn-chrome,
180
- .ph-btn-xs.ph-btn-primary:not(:has(.ph-btn-chrome)),
181
- .ph-btn-xs.ph-btn-secondary:not(:has(.ph-btn-chrome)) {
345
+ .ph-btn-xs.ph-btn-raised .ph-btn-chrome,
346
+ .ph-btn-xs:is(.ph-btn-primary, .ph-btn-secondary, .ph-btn-signal, .ph-btn-accent, .ph-btn-neutral, .ph-btn-info, .ph-btn-success, .ph-btn-warning, .ph-btn-danger):not(:has(.ph-btn-chrome)) {
182
347
  min-height: 1.625rem;
183
348
  padding:
184
349
  calc(0.125rem - var(--ph-btn-chrome-depth) * 0.5) 0.5rem
@@ -186,10 +351,8 @@ body {
186
351
  font-size: 0.6875rem;
187
352
  }
188
353
 
189
- .ph-btn-lg.ph-btn-primary .ph-btn-chrome,
190
- .ph-btn-lg.ph-btn-secondary .ph-btn-chrome,
191
- .ph-btn-lg.ph-btn-primary:not(:has(.ph-btn-chrome)),
192
- .ph-btn-lg.ph-btn-secondary:not(:has(.ph-btn-chrome)) {
354
+ .ph-btn-lg.ph-btn-raised .ph-btn-chrome,
355
+ .ph-btn-lg:is(.ph-btn-primary, .ph-btn-secondary, .ph-btn-signal, .ph-btn-accent, .ph-btn-neutral, .ph-btn-info, .ph-btn-success, .ph-btn-warning, .ph-btn-danger):not(:has(.ph-btn-chrome)) {
193
356
  min-height: 3.0625rem;
194
357
  padding:
195
358
  calc(0.375rem - var(--ph-btn-chrome-depth) * 0.5) 1.25rem
@@ -197,81 +360,72 @@ body {
197
360
  font-size: 1rem;
198
361
  }
199
362
 
200
- .ph-btn-chrome::before,
201
- .ph-btn-primary:not(:has(.ph-btn-chrome))::before,
202
- .ph-btn-secondary:not(:has(.ph-btn-chrome))::before {
363
+ .ph-btn-raised .ph-btn-chrome::before,
364
+ :is(.ph-btn-primary, .ph-btn-secondary, .ph-btn-signal, .ph-btn-accent, .ph-btn-neutral, .ph-btn-info, .ph-btn-success, .ph-btn-warning, .ph-btn-danger):not(:has(.ph-btn-chrome))::before {
203
365
  content: "";
204
366
  position: absolute;
205
- inset: -1px;
206
- bottom: calc(var(--ph-btn-depth) - 1px);
367
+ inset: calc(-1 * var(--ph-btn-border-width));
368
+ bottom: calc(var(--ph-btn-depth) - var(--ph-btn-border-width));
207
369
  z-index: -1;
208
- border: 1px solid var(--ph-btn-border);
209
- border-radius: var(--ph-radius-app);
370
+ border: var(--ph-btn-border-width) solid var(--ph-btn-border);
371
+ border-radius: var(--ph-radius-control);
210
372
  pointer-events: none;
211
373
  }
212
374
 
213
- .ph-btn-chrome::after,
214
- .ph-btn-primary:not(:has(.ph-btn-chrome))::after,
215
- .ph-btn-secondary:not(:has(.ph-btn-chrome))::after {
375
+ .ph-btn-raised .ph-btn-chrome::after,
376
+ :is(.ph-btn-primary, .ph-btn-secondary, .ph-btn-signal, .ph-btn-accent, .ph-btn-neutral, .ph-btn-info, .ph-btn-success, .ph-btn-warning, .ph-btn-danger):not(:has(.ph-btn-chrome))::after {
216
377
  content: "";
217
378
  position: absolute;
218
- inset: -1px -1px calc(var(--ph-btn-chrome-depth) - 1px);
379
+ inset: calc(-1 * var(--ph-btn-border-width));
380
+ bottom: calc(var(--ph-btn-chrome-depth) - var(--ph-btn-border-width));
219
381
  z-index: -1;
220
382
  background: var(--ph-btn-face);
221
- border: 1px solid var(--ph-btn-border);
222
- border-radius: var(--ph-radius-app);
383
+ border: var(--ph-btn-border-width) solid var(--ph-btn-border);
384
+ border-radius: var(--ph-radius-control);
223
385
  box-shadow: 0 var(--ph-btn-chrome-depth) 0 -1px var(--ph-btn-frame);
224
386
  pointer-events: none;
387
+ transition: border-color 50ms linear;
225
388
  }
226
389
 
227
- .ph-btn-primary:hover:not(:disabled),
228
- .ph-btn-secondary:hover:not(:disabled) {
229
- --ph-btn-depth: -0.03125rem;
390
+ .ph-btn-raised:hover:not(:disabled),
391
+ :is(.ph-btn-primary, .ph-btn-secondary, .ph-btn-signal, .ph-btn-accent, .ph-btn-neutral, .ph-btn-info, .ph-btn-success, .ph-btn-warning, .ph-btn-danger):not(:has(.ph-btn-chrome)):hover:not(:disabled) {
392
+ --ph-btn-depth: -0.0625rem;
230
393
  }
231
394
 
232
395
  .ph-btn-secondary:hover:not(:disabled) {
233
396
  --ph-btn-ink: var(--ph-text-primary);
234
397
  }
235
398
 
236
- .ph-btn-primary:hover:not(:disabled) .ph-btn-chrome,
237
- .ph-btn-secondary:hover:not(:disabled) .ph-btn-chrome,
238
- .ph-btn-primary:not(:has(.ph-btn-chrome)):hover:not(:disabled),
239
- .ph-btn-secondary:not(:has(.ph-btn-chrome)):hover:not(:disabled) {
399
+ .ph-btn-raised:hover:not(:disabled) .ph-btn-chrome,
400
+ :is(.ph-btn-primary, .ph-btn-secondary, .ph-btn-signal, .ph-btn-accent, .ph-btn-neutral, .ph-btn-info, .ph-btn-success, .ph-btn-warning, .ph-btn-danger):not(:has(.ph-btn-chrome)):hover:not(:disabled) {
240
401
  transform: translateY(var(--ph-btn-depth));
241
402
  color: var(--ph-btn-ink);
242
403
  }
243
404
 
244
- .ph-btn-primary:hover:not(:disabled) .ph-btn-chrome::before,
245
- .ph-btn-secondary:hover:not(:disabled) .ph-btn-chrome::before,
246
- .ph-btn-primary:not(:has(.ph-btn-chrome)):hover:not(:disabled)::before,
247
- .ph-btn-secondary:not(:has(.ph-btn-chrome)):hover:not(:disabled)::before {
405
+ .ph-btn-raised:hover:not(:disabled) .ph-btn-chrome::before,
406
+ :is(.ph-btn-primary, .ph-btn-secondary, .ph-btn-signal, .ph-btn-accent, .ph-btn-neutral, .ph-btn-info, .ph-btn-success, .ph-btn-warning, .ph-btn-danger):not(:has(.ph-btn-chrome)):hover:not(:disabled)::before {
248
407
  border-color: var(--ph-btn-border-hover);
249
408
  }
250
409
 
251
- .ph-btn-primary:hover:not(:disabled) .ph-btn-chrome::after,
252
- .ph-btn-secondary:hover:not(:disabled) .ph-btn-chrome::after,
253
- .ph-btn-primary:not(:has(.ph-btn-chrome)):hover:not(:disabled)::after,
254
- .ph-btn-secondary:not(:has(.ph-btn-chrome)):hover:not(:disabled)::after {
410
+ .ph-btn-raised:hover:not(:disabled) .ph-btn-chrome::after,
411
+ :is(.ph-btn-primary, .ph-btn-secondary, .ph-btn-signal, .ph-btn-accent, .ph-btn-neutral, .ph-btn-info, .ph-btn-success, .ph-btn-warning, .ph-btn-danger):not(:has(.ph-btn-chrome)):hover:not(:disabled)::after {
255
412
  border-color: var(--ph-btn-border-hover);
256
413
  box-shadow: 0 calc(var(--ph-btn-chrome-depth) - var(--ph-btn-depth)) 0 -1px var(--ph-btn-frame);
257
414
  }
258
415
 
259
- .ph-btn-primary:active:not(:disabled),
260
- .ph-btn-secondary:active:not(:disabled) {
261
- --ph-btn-depth: 0.03125rem;
416
+ .ph-btn-raised:active:not(:disabled),
417
+ :is(.ph-btn-primary, .ph-btn-secondary, .ph-btn-signal, .ph-btn-accent, .ph-btn-neutral, .ph-btn-info, .ph-btn-success, .ph-btn-warning, .ph-btn-danger):not(:has(.ph-btn-chrome)):active:not(:disabled) {
418
+ --ph-btn-depth: var(--ph-btn-chrome-depth);
262
419
  }
263
420
 
264
- .ph-btn-primary:active:not(:disabled) .ph-btn-chrome,
265
- .ph-btn-secondary:active:not(:disabled) .ph-btn-chrome,
266
- .ph-btn-primary:not(:has(.ph-btn-chrome)):active:not(:disabled),
267
- .ph-btn-secondary:not(:has(.ph-btn-chrome)):active:not(:disabled) {
421
+ .ph-btn-raised:active:not(:disabled) .ph-btn-chrome,
422
+ :is(.ph-btn-primary, .ph-btn-secondary, .ph-btn-signal, .ph-btn-accent, .ph-btn-neutral, .ph-btn-info, .ph-btn-success, .ph-btn-warning, .ph-btn-danger):not(:has(.ph-btn-chrome)):active:not(:disabled) {
268
423
  transform: translateY(var(--ph-btn-depth));
424
+ transition-duration: 0ms;
269
425
  }
270
426
 
271
- .ph-btn-primary:active:not(:disabled) .ph-btn-chrome::after,
272
- .ph-btn-secondary:active:not(:disabled) .ph-btn-chrome::after,
273
- .ph-btn-primary:not(:has(.ph-btn-chrome)):active:not(:disabled)::after,
274
- .ph-btn-secondary:not(:has(.ph-btn-chrome)):active:not(:disabled)::after {
427
+ .ph-btn-raised:active:not(:disabled) .ph-btn-chrome::after,
428
+ :is(.ph-btn-primary, .ph-btn-secondary, .ph-btn-signal, .ph-btn-accent, .ph-btn-neutral, .ph-btn-info, .ph-btn-success, .ph-btn-warning, .ph-btn-danger):not(:has(.ph-btn-chrome)):active:not(:disabled)::after {
275
429
  border-color: var(--ph-btn-border-hover);
276
430
  box-shadow: 0 calc(var(--ph-btn-chrome-depth) - var(--ph-btn-depth)) 0 -1px var(--ph-btn-frame);
277
431
  }
@@ -287,10 +441,8 @@ body {
287
441
  vertical-align: unset;
288
442
  }
289
443
 
290
- .ph-btn-primary:hover:not(:disabled) .ph-btn-icon,
291
- .ph-btn-secondary:hover:not(:disabled) .ph-btn-icon,
292
- .ph-btn-primary:hover:not(:disabled) .ph-btn-side-icon,
293
- .ph-btn-secondary:hover:not(:disabled) .ph-btn-side-icon {
444
+ .ph-btn-raised:hover:not(:disabled) .ph-btn-icon,
445
+ .ph-btn-raised:hover:not(:disabled) .ph-btn-side-icon {
294
446
  opacity: 0.75;
295
447
  }
296
448
 
@@ -302,68 +454,19 @@ body {
302
454
  opacity: 0.28;
303
455
  }
304
456
 
305
- .ph-btn-signal {
306
- border: 1px solid color-mix(in oklab, var(--ph-accent) 88%, black);
307
- background-color: var(--ph-accent);
308
- color: #fff;
309
- font-weight: 500;
310
- box-shadow: 0 var(--ph-chrome-depth) 0 -1px color-mix(in oklab, var(--ph-primary-frame-bg) 75%, var(--ph-accent));
311
- }
312
-
313
- .ph-btn-signal:hover:not(:disabled) {
314
- transform: translateY(-0.03125rem);
315
- }
316
-
317
- .ph-btn-signal:active:not(:disabled) {
318
- transform: translateY(0.03125rem);
319
- }
320
-
321
- .ph-btn-accent {
322
- @apply border-violet-800 bg-violet-600 text-white font-medium;
323
- box-shadow: 0 var(--ph-chrome-depth) 0 -1px #5b21b6;
324
- }
325
-
326
- .ph-btn-neutral {
327
- @apply border-neutral-900 bg-neutral-900 text-neutral-100 font-medium;
328
- box-shadow: 0 var(--ph-chrome-depth) 0 -1px rgb(55 55 62);
329
- }
330
-
331
- .ph-btn-info {
332
- @apply border-ph-info bg-ph-info text-white font-medium;
333
- box-shadow: 0 var(--ph-chrome-depth) 0 -1px color-mix(in oklab, var(--ph-info) 80%, black);
334
- }
335
-
336
- .ph-btn-success {
337
- @apply border-ph-success bg-ph-success text-white font-medium;
338
- box-shadow: 0 var(--ph-chrome-depth) 0 -1px color-mix(in oklab, var(--ph-success) 80%, black);
339
- }
340
-
341
- .ph-btn-warning {
342
- @apply border-ph-warning bg-ph-warning text-white font-medium;
343
- box-shadow: 0 var(--ph-chrome-depth) 0 -1px color-mix(in oklab, var(--ph-warning) 80%, black);
344
- }
345
-
346
- .ph-btn-danger {
347
- @apply border-ph-danger bg-ph-danger text-white font-medium;
348
- box-shadow: 0 var(--ph-chrome-depth) 0 -1px color-mix(in oklab, var(--ph-danger) 80%, black);
457
+ .ph-btn-wide.ph-btn-raised .ph-btn-chrome {
458
+ padding-inline: 2.5rem;
349
459
  }
350
460
 
351
- .ph-btn-accent:hover:not(:disabled),
352
- .ph-btn-neutral:hover:not(:disabled),
353
- .ph-btn-info:hover:not(:disabled),
354
- .ph-btn-success:hover:not(:disabled),
355
- .ph-btn-warning:hover:not(:disabled),
356
- .ph-btn-danger:hover:not(:disabled) {
357
- transform: translateY(-0.03125rem);
461
+ :is(.ph-btn-circle, .ph-btn-square).ph-btn-raised .ph-btn-chrome {
462
+ aspect-ratio: 1;
463
+ min-width: var(--ph-btn-control-size);
464
+ padding-inline: 0;
358
465
  }
359
466
 
360
- .ph-btn-accent:active:not(:disabled),
361
- .ph-btn-neutral:active:not(:disabled),
362
- .ph-btn-info:active:not(:disabled),
363
- .ph-btn-success:active:not(:disabled),
364
- .ph-btn-warning:active:not(:disabled),
365
- .ph-btn-danger:active:not(:disabled) {
366
- transform: translateY(0.03125rem);
467
+ .ph-btn-circle.ph-btn-raised .ph-btn-chrome::before,
468
+ .ph-btn-circle.ph-btn-raised .ph-btn-chrome::after {
469
+ border-radius: 9999px;
367
470
  }
368
471
 
369
472
  .ph-btn-tertiary,
@@ -387,17 +490,34 @@ body {
387
490
  text-decoration: underline;
388
491
  }
389
492
 
390
- .ph-btn-outline.ph-btn-primary .ph-btn-chrome::after,
391
- .ph-btn-outline.ph-btn-secondary .ph-btn-chrome::after,
392
- .ph-btn-outline.ph-btn-primary:not(:has(.ph-btn-chrome))::after,
393
- .ph-btn-outline.ph-btn-secondary:not(:has(.ph-btn-chrome))::after {
493
+ .ph-btn-outline:is(.ph-btn-raised, .ph-btn-primary, .ph-btn-secondary, .ph-btn-signal, .ph-btn-accent, .ph-btn-neutral, .ph-btn-info, .ph-btn-success, .ph-btn-warning, .ph-btn-danger) {
494
+ --ph-btn-ink: var(--ph-btn-outline-ink);
495
+ }
496
+
497
+ .ph-btn-outline.ph-btn-raised .ph-btn-chrome::after,
498
+ .ph-btn-outline:is(.ph-btn-primary, .ph-btn-secondary, .ph-btn-signal, .ph-btn-accent, .ph-btn-neutral, .ph-btn-info, .ph-btn-success, .ph-btn-warning, .ph-btn-danger):not(:has(.ph-btn-chrome))::after {
394
499
  background: transparent;
395
500
  }
396
501
 
502
+ .rounded-full:is(.ph-btn-primary, .ph-btn-secondary, .ph-btn-signal, .ph-btn-accent, .ph-btn-neutral, .ph-btn-info, .ph-btn-success, .ph-btn-warning, .ph-btn-danger):not(:has(.ph-btn-chrome))::before,
503
+ .rounded-full:is(.ph-btn-primary, .ph-btn-secondary, .ph-btn-signal, .ph-btn-accent, .ph-btn-neutral, .ph-btn-info, .ph-btn-success, .ph-btn-warning, .ph-btn-danger):not(:has(.ph-btn-chrome))::after {
504
+ border-radius: 9999px;
505
+ }
506
+
507
+ .ph-btn-spinner {
508
+ width: 1em;
509
+ height: 1em;
510
+ flex: none;
511
+ border: 0.125rem solid color-mix(in oklab, currentColor 28%, transparent);
512
+ border-top-color: currentColor;
513
+ border-radius: 9999px;
514
+ animation: ph-spin 0.65s linear infinite;
515
+ }
516
+
397
517
  /* Surfaces — crisp edge + faint top lip beside elevation */
398
518
  .ph-panel {
399
519
  @apply border bg-ph-muted p-6;
400
- border-radius: var(--ph-radius-app);
520
+ border-radius: var(--ph-radius-surface);
401
521
  border-color: var(--ph-border-control);
402
522
  box-shadow:
403
523
  var(--ph-chrome-inset-face-top),
@@ -405,25 +525,39 @@ body {
405
525
  }
406
526
 
407
527
  .ph-card {
408
- @apply flex flex-col overflow-hidden border bg-ph-surface;
409
- border-radius: var(--ph-radius-app);
410
- border-color: var(--ph-border-control);
411
- box-shadow:
412
- var(--ph-chrome-inset-face-top),
413
- var(--ph-chrome-inset-face-bottom),
414
- var(--ph-shadow-elevation-3000);
528
+ @apply flex flex-col overflow-hidden border;
529
+ background: var(--ph-surface-raised);
530
+ border-radius: var(--ph-radius-surface);
531
+ border-color: var(--ph-border-subtle);
415
532
  transition:
416
- border-color 0.15s ease,
417
- box-shadow 0.15s ease;
533
+ border-color var(--ph-motion-fast) ease,
534
+ box-shadow var(--ph-motion-fast) ease,
535
+ transform var(--ph-motion-fast) ease;
536
+ }
537
+
538
+ .ph-card-elevated {
539
+ border-color: var(--ph-border-control);
540
+ box-shadow: var(--ph-shadow-raised);
541
+ }
542
+
543
+ .ph-card-highlighted {
544
+ border-color: var(--ph-selected-border);
545
+ background: var(--ph-selected-bg);
546
+ }
547
+
548
+ .ph-card-interactive {
549
+ position: relative;
418
550
  }
419
551
 
420
- .ph-card:hover {
552
+ .ph-card-interactive:hover {
421
553
  border-color: var(--ph-border-strong);
422
- box-shadow:
423
- var(--ph-chrome-inset-face-top),
424
- var(--ph-chrome-inset-face-bottom),
425
- var(--ph-shadow-elevation-3000),
426
- 0 4px 14px hsl(0deg 0% 0% / 0.06);
554
+ box-shadow: var(--ph-shadow-raised);
555
+ transform: translateY(-1px);
556
+ }
557
+
558
+ .ph-card-interactive:focus-within {
559
+ outline: 3px solid color-mix(in oklab, var(--ph-focus-ring) 30%, transparent);
560
+ outline-offset: 2px;
427
561
  }
428
562
 
429
563
  /* Dashboard / inset insight shells */
@@ -460,6 +594,11 @@ body {
460
594
  @apply flex flex-1 flex-col gap-3 p-5;
461
595
  }
462
596
 
597
+ .ph-card-media {
598
+ overflow: hidden;
599
+ border-bottom: 1px solid var(--ph-border-subtle);
600
+ }
601
+
463
602
  .ph-card-title {
464
603
  @apply flex items-center gap-2 text-lg font-semibold text-ph-ink;
465
604
  }
@@ -483,42 +622,41 @@ body {
483
622
 
484
623
  /* Forms — control outline + Lemon inset lip + bottom elevation */
485
624
  .ph-input-base {
486
- @apply w-full bg-ph-surface px-3 py-2 text-sm text-ph-ink transition box-border;
487
- border-radius: var(--ph-radius-app);
625
+ @apply w-full px-3 py-2 text-sm text-ph-ink transition box-border;
626
+ background: var(--ph-surface-raised);
627
+ border-radius: var(--ph-radius-control);
488
628
  border: 1px solid var(--ph-border-control);
489
629
  min-height: 2.3125rem;
490
630
  line-height: 1.5rem;
491
631
  font-family: inherit;
492
632
  box-shadow:
493
633
  var(--ph-chrome-inset-face-top),
494
- var(--ph-chrome-inset-face-bottom),
495
- var(--ph-shadow-elevation-3000);
634
+ var(--ph-chrome-inset-face-bottom);
496
635
  }
497
636
 
498
- .ph-input:focus,
499
- .ph-select:focus,
500
- .ph-textarea:focus {
637
+ .ph-input:focus-visible,
638
+ .ph-select:focus-visible,
639
+ .ph-textarea:focus-visible {
501
640
  outline: none;
502
641
  box-shadow:
503
- 0 0 0 3px color-mix(in oklab, var(--ph-accent) 18%, transparent),
642
+ 0 0 0 3px color-mix(in oklab, var(--ph-focus-ring) 24%, transparent),
504
643
  var(--ph-chrome-inset-face-top),
505
- var(--ph-chrome-inset-face-bottom),
506
- var(--ph-shadow-elevation-3000);
507
- border-color: color-mix(in oklab, var(--ph-accent) 45%, var(--ph-border));
644
+ var(--ph-chrome-inset-face-bottom);
645
+ border-color: color-mix(in oklab, var(--ph-focus-ring) 58%, var(--ph-border));
508
646
  }
509
647
 
510
648
  .ph-input {
511
649
  @apply ph-input-base;
512
650
  }
513
651
 
514
- .ph-input-error:focus,
515
- .ph-select.ph-input-error:focus,
516
- .ph-textarea.ph-input-error:focus {
652
+ .ph-input-error:focus-visible,
653
+ .ph-select.ph-input-error:focus-visible,
654
+ .ph-textarea.ph-input-error:focus-visible {
655
+ border-color: var(--ph-danger);
517
656
  box-shadow:
518
657
  0 0 0 3px color-mix(in oklab, var(--ph-danger) 20%, transparent),
519
658
  var(--ph-chrome-inset-face-top),
520
- var(--ph-chrome-inset-face-bottom),
521
- var(--ph-shadow-elevation-3000);
659
+ var(--ph-chrome-inset-face-bottom);
522
660
  }
523
661
 
524
662
  .ph-input-error {
@@ -530,6 +668,18 @@ body {
530
668
  box-shadow: none;
531
669
  }
532
670
 
671
+ .ph-input-sm {
672
+ min-height: 1.875rem;
673
+ padding-block: 0.125rem;
674
+ font-size: 0.8125rem;
675
+ }
676
+
677
+ .ph-input-lg {
678
+ min-height: 2.75rem;
679
+ padding-block: 0.5rem;
680
+ font-size: 1rem;
681
+ }
682
+
533
683
  .ph-input-group-field {
534
684
  @apply rounded-none border-0 shadow-none focus:ring-0;
535
685
  box-shadow: none;
@@ -624,13 +774,23 @@ body {
624
774
  border-radius: 0.25rem;
625
775
  }
626
776
 
777
+ .ph-checkbox-wrap {
778
+ @apply relative inline-flex h-4 w-4 shrink-0;
779
+ }
780
+
781
+ .ph-checkbox-icon {
782
+ @apply pointer-events-none absolute inset-0 h-4 w-4 scale-75 opacity-0;
783
+ color: var(--ph-on-accent);
784
+ transition: opacity var(--ph-motion-fast) ease;
785
+ }
786
+
627
787
  .ph-checkbox:checked {
628
788
  border-color: var(--ph-brand);
629
789
  background-color: var(--ph-brand);
630
- background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e");
631
- background-size: 0.75rem 0.75rem;
632
- background-position: center;
633
- background-repeat: no-repeat;
790
+ }
791
+
792
+ .ph-checkbox:checked + .ph-checkbox-icon {
793
+ opacity: 1;
634
794
  }
635
795
 
636
796
  .ph-checkbox:focus-visible,
@@ -692,7 +852,27 @@ body {
692
852
  }
693
853
 
694
854
  .ph-control-row {
695
- @apply flex cursor-pointer items-center gap-3 text-sm text-ph-subtle;
855
+ @apply flex cursor-pointer items-start gap-3 text-sm text-ph-subtle;
856
+ }
857
+
858
+ .ph-control-copy {
859
+ @apply flex min-w-0 flex-1 flex-col gap-0.5;
860
+ }
861
+
862
+ .ph-file-upload {
863
+ @apply flex cursor-pointer items-center gap-3 border border-dashed px-4 py-6 text-sm text-ph-subtle transition;
864
+ border-color: var(--ph-border-control);
865
+ border-radius: var(--ph-radius-surface);
866
+ background: var(--ph-surface-raised);
867
+ }
868
+
869
+ .ph-file-upload:hover {
870
+ background: var(--ph-surface-inset);
871
+ }
872
+
873
+ .ph-file-upload:focus-within {
874
+ border-color: color-mix(in oklab, var(--ph-focus-ring) 58%, var(--ph-border));
875
+ box-shadow: 0 0 0 3px color-mix(in oklab, var(--ph-focus-ring) 24%, transparent);
696
876
  }
697
877
 
698
878
  .ph-control-row-between {
@@ -856,6 +1036,19 @@ body {
856
1036
  }
857
1037
  }
858
1038
 
1039
+ @keyframes ph-loading-pulse {
1040
+ 0%,
1041
+ 100% {
1042
+ opacity: 0.35;
1043
+ transform: scale(0.72);
1044
+ }
1045
+
1046
+ 50% {
1047
+ opacity: 1;
1048
+ transform: scale(1);
1049
+ }
1050
+ }
1051
+
859
1052
  .ph-skeleton {
860
1053
  border-radius: 0.25rem;
861
1054
  background: linear-gradient(
@@ -888,6 +1081,14 @@ body {
888
1081
  @apply rounded-lg border border-ph-border bg-ph-surface p-4 shadow-ph;
889
1082
  }
890
1083
 
1084
+ .ph-skeleton-button {
1085
+ @apply h-9 w-28 rounded-[var(--ph-radius-control)];
1086
+ }
1087
+
1088
+ .ph-skeleton-block {
1089
+ @apply block h-28 w-full rounded-[var(--ph-radius-surface)];
1090
+ }
1091
+
891
1092
  .ph-loading {
892
1093
  @apply inline-block h-6 w-6 rounded-full;
893
1094
  border: 2px solid var(--ph-border);
@@ -917,38 +1118,106 @@ body {
917
1118
  animation-delay: 0.3s;
918
1119
  }
919
1120
 
1121
+ .ph-loader {
1122
+ @apply inline-flex shrink-0 items-center justify-center;
1123
+ color: var(--ph-accent);
1124
+ }
1125
+
1126
+ .ph-loader-sm .ph-loading {
1127
+ width: 1rem;
1128
+ height: 1rem;
1129
+ border-width: 1.5px;
1130
+ }
1131
+
1132
+ .ph-loader-md .ph-loading {
1133
+ width: 1.5rem;
1134
+ height: 1.5rem;
1135
+ }
1136
+
1137
+ .ph-loader-lg .ph-loading {
1138
+ width: 2rem;
1139
+ height: 2rem;
1140
+ border-width: 2.5px;
1141
+ }
1142
+
1143
+ .ph-loader-sm .ph-loading-dots > span {
1144
+ width: 0.375rem;
1145
+ height: 0.375rem;
1146
+ }
1147
+
1148
+ .ph-loader-lg .ph-loading-dots > span {
1149
+ width: 0.625rem;
1150
+ height: 0.625rem;
1151
+ }
1152
+
1153
+ .ph-loading-pulse {
1154
+ width: 0.875rem;
1155
+ height: 0.875rem;
1156
+ border-radius: 9999px;
1157
+ background: currentColor;
1158
+ animation: ph-loading-pulse 1s ease-in-out infinite;
1159
+ }
1160
+
1161
+ .ph-loader-sm .ph-loading-pulse {
1162
+ width: 0.625rem;
1163
+ height: 0.625rem;
1164
+ }
1165
+
1166
+ .ph-loader-lg .ph-loading-pulse {
1167
+ width: 1.125rem;
1168
+ height: 1.125rem;
1169
+ }
1170
+
1171
+ .ph-loading-state {
1172
+ @apply flex min-h-40 flex-col items-center justify-center gap-4 px-6 py-8 text-center;
1173
+ }
1174
+
1175
+ .ph-loading-state__copy {
1176
+ @apply flex max-w-sm flex-col gap-1 text-sm;
1177
+ }
1178
+
1179
+ .ph-loading-state__copy strong {
1180
+ color: var(--ph-text-primary);
1181
+ }
1182
+
1183
+ .ph-loading-state__copy span {
1184
+ color: var(--ph-text-secondary);
1185
+ }
1186
+
920
1187
  /* Badges — tight pills */
921
1188
  .ph-badge {
922
1189
  @apply inline-flex items-center rounded-full px-2 py-0.5 text-[11px] font-semibold uppercase tracking-wide;
923
1190
  }
924
1191
 
925
1192
  .ph-badge-neutral {
926
- @apply bg-ph-toolbar text-ph-subtle;
1193
+ background: var(--ph-surface-inset);
1194
+ color: var(--ph-text-secondary);
1195
+ box-shadow: inset 0 0 0 1px var(--ph-border-subtle);
927
1196
  }
928
1197
  .ph-badge-brand {
929
- background: color-mix(in oklab, var(--ph-accent) 18%, var(--ph-surface));
930
- color: color-mix(in oklab, var(--ph-accent) 72%, var(--ph-text-primary));
931
- box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--ph-accent) 38%, transparent);
1198
+ background: var(--ph-selected-bg);
1199
+ color: var(--ph-selected-text);
1200
+ box-shadow: inset 0 0 0 1px var(--ph-selected-border);
932
1201
  }
933
1202
  .ph-badge-success {
934
- background: color-mix(in oklab, var(--ph-success) 18%, var(--ph-surface));
935
- color: color-mix(in oklab, var(--ph-success) 72%, var(--ph-text-primary));
936
- box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--ph-success) 38%, transparent);
1203
+ background: var(--ph-success-bg);
1204
+ color: var(--ph-success-text);
1205
+ box-shadow: inset 0 0 0 1px var(--ph-success-border);
937
1206
  }
938
1207
  .ph-badge-warning {
939
- background: color-mix(in oklab, var(--ph-warning) 20%, var(--ph-surface));
940
- color: color-mix(in oklab, var(--ph-warning) 75%, var(--ph-text-primary));
941
- box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--ph-warning) 40%, transparent);
1208
+ background: var(--ph-warning-bg);
1209
+ color: var(--ph-warning-text);
1210
+ box-shadow: inset 0 0 0 1px var(--ph-warning-border);
942
1211
  }
943
1212
  .ph-badge-danger {
944
- background: color-mix(in oklab, var(--ph-danger) 18%, var(--ph-surface));
945
- color: color-mix(in oklab, var(--ph-danger) 72%, var(--ph-text-primary));
946
- box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--ph-danger) 38%, transparent);
1213
+ background: var(--ph-danger-bg);
1214
+ color: var(--ph-danger-text);
1215
+ box-shadow: inset 0 0 0 1px var(--ph-danger-border);
947
1216
  }
948
1217
  .ph-badge-info {
949
- background: color-mix(in oklab, var(--ph-info) 18%, var(--ph-surface));
950
- color: color-mix(in oklab, var(--ph-info) 72%, var(--ph-text-primary));
951
- box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--ph-info) 38%, transparent);
1218
+ background: var(--ph-info-bg);
1219
+ color: var(--ph-info-text);
1220
+ box-shadow: inset 0 0 0 1px var(--ph-info-border);
952
1221
  }
953
1222
  .ph-badge-outline {
954
1223
  @apply border border-ph-border bg-transparent text-ph-subtle;
@@ -956,33 +1225,66 @@ body {
956
1225
 
957
1226
  /* Alerts */
958
1227
  .ph-alert {
959
- @apply flex items-start gap-3 rounded-[var(--ph-radius-app)] px-4 py-3 text-sm;
960
- border: 1px solid var(--ph-border-control);
1228
+ @apply flex items-start gap-3 px-4 py-3 text-sm;
1229
+ border: 1px solid var(--ph-alert-border);
961
1230
  border-left-width: 4px;
962
- box-shadow:
963
- var(--ph-chrome-inset-face-top),
964
- var(--ph-shadow-elevation-3000);
1231
+ border-radius: var(--ph-radius-surface);
1232
+ background: var(--ph-alert-bg);
1233
+ color: var(--ph-text-primary);
965
1234
  }
966
1235
 
967
1236
  .ph-alert-info {
968
- border-left-color: var(--ph-info);
969
- background: color-mix(in oklab, var(--ph-info) 16%, var(--ph-muted));
970
- color: var(--ph-text-primary);
1237
+ --ph-alert-accent: var(--ph-info);
1238
+ --ph-alert-bg: var(--ph-info-bg);
1239
+ --ph-alert-border: var(--ph-info-border);
1240
+ --ph-alert-text: var(--ph-info-text);
971
1241
  }
972
1242
  .ph-alert-success {
973
- border-left-color: var(--ph-success);
974
- background: color-mix(in oklab, var(--ph-success) 16%, var(--ph-muted));
975
- color: var(--ph-text-primary);
1243
+ --ph-alert-accent: var(--ph-success);
1244
+ --ph-alert-bg: var(--ph-success-bg);
1245
+ --ph-alert-border: var(--ph-success-border);
1246
+ --ph-alert-text: var(--ph-success-text);
976
1247
  }
977
1248
  .ph-alert-warning {
978
- border-left-color: var(--ph-warning);
979
- background: color-mix(in oklab, var(--ph-warning) 18%, var(--ph-muted));
980
- color: var(--ph-text-primary);
1249
+ --ph-alert-accent: var(--ph-warning);
1250
+ --ph-alert-bg: var(--ph-warning-bg);
1251
+ --ph-alert-border: var(--ph-warning-border);
1252
+ --ph-alert-text: var(--ph-warning-text);
981
1253
  }
982
1254
  .ph-alert-danger {
983
- border-left-color: var(--ph-danger);
984
- background: color-mix(in oklab, var(--ph-danger) 16%, var(--ph-muted));
985
- color: var(--ph-text-primary);
1255
+ --ph-alert-accent: var(--ph-danger);
1256
+ --ph-alert-bg: var(--ph-danger-bg);
1257
+ --ph-alert-border: var(--ph-danger-border);
1258
+ --ph-alert-text: var(--ph-danger-text);
1259
+ }
1260
+
1261
+ .ph-alert-info,
1262
+ .ph-alert-success,
1263
+ .ph-alert-warning,
1264
+ .ph-alert-danger {
1265
+ border-left-color: var(--ph-alert-accent);
1266
+ }
1267
+
1268
+ .ph-alert-icon {
1269
+ color: var(--ph-alert-text);
1270
+ }
1271
+
1272
+ .ph-alert-dismiss,
1273
+ .ph-modal-close {
1274
+ @apply shrink-0 p-1 text-ph-subtle hover:text-ph-ink;
1275
+ border-radius: var(--ph-radius-control);
1276
+ touch-action: manipulation;
1277
+ }
1278
+
1279
+ .ph-alert-dismiss:hover,
1280
+ .ph-modal-close:hover {
1281
+ background: color-mix(in oklab, var(--ph-text-primary) 7%, transparent);
1282
+ }
1283
+
1284
+ .ph-alert-dismiss:focus-visible,
1285
+ .ph-modal-close:focus-visible {
1286
+ outline: 3px solid color-mix(in oklab, var(--ph-focus-ring) 32%, transparent);
1287
+ outline-offset: 1px;
986
1288
  }
987
1289
 
988
1290
  /* Toasts — same semantic tints as alerts */
@@ -1013,32 +1315,473 @@ body {
1013
1315
 
1014
1316
  /* Modal */
1015
1317
  .ph-modal-overlay {
1016
- @apply fixed inset-0 z-[100] flex items-center justify-center bg-black/40 backdrop-blur-sm p-4;
1318
+ @apply fixed inset-0 z-[100] bg-black/45 backdrop-blur-sm;
1319
+ animation: ph-modal-overlay-in var(--ph-motion-standard) ease-out;
1017
1320
  }
1018
1321
 
1019
1322
  .ph-modal-panel {
1020
- @apply w-full max-w-lg bg-ph-surface p-6 text-ph-ink;
1021
- border-radius: var(--ph-radius-app);
1323
+ @apply fixed left-1/2 top-1/2 z-[101] flex w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 flex-col p-6 text-ph-ink;
1324
+ max-height: min(42rem, calc(100dvh - 2rem));
1325
+ overflow: hidden;
1326
+ overscroll-behavior: contain;
1327
+ background: var(--ph-surface-overlay);
1328
+ border-radius: var(--ph-radius-overlay);
1329
+ border: 1px solid var(--ph-border-control);
1330
+ box-shadow: var(--ph-shadow-floating);
1331
+ animation: ph-modal-panel-in var(--ph-motion-standard) ease-out;
1332
+ }
1333
+
1334
+ .ph-modal-header {
1335
+ @apply flex shrink-0 items-start justify-between gap-4;
1336
+ }
1337
+
1338
+ .ph-modal-body {
1339
+ @apply mt-4 min-h-0 overflow-y-auto;
1340
+ overscroll-behavior: contain;
1341
+ }
1342
+
1343
+ .ph-modal-footer {
1344
+ @apply mt-6 flex shrink-0 flex-wrap justify-end gap-2 border-t pt-5;
1345
+ border-color: var(--ph-border-subtle);
1346
+ }
1347
+
1348
+ @keyframes ph-modal-overlay-in {
1349
+ from {
1350
+ opacity: 0;
1351
+ }
1352
+ }
1353
+
1354
+ @keyframes ph-modal-panel-in {
1355
+ from {
1356
+ opacity: 0;
1357
+ transform: translate(-50%, calc(-50% + 0.5rem)) scale(0.98);
1358
+ }
1359
+ }
1360
+
1361
+ @media (max-width: 40rem) {
1362
+ .ph-modal-panel {
1363
+ width: calc(100% - 1rem);
1364
+ max-height: calc(100dvh - 1rem - env(safe-area-inset-top) - env(safe-area-inset-bottom));
1365
+ padding: 1.25rem;
1366
+ }
1367
+ }
1368
+
1369
+ /* Adaptive dropdowns: Radix resolves side/alignment against viewport collisions. */
1370
+ .ph-dropdown {
1371
+ display: inline-flex;
1372
+ min-width: 0;
1373
+ }
1374
+
1375
+ .ph-dropdown-trigger {
1376
+ @apply inline-flex cursor-pointer items-center justify-center border-0 bg-transparent p-0 text-inherit;
1377
+ border-radius: var(--ph-radius-control);
1378
+ }
1379
+
1380
+ .ph-dropdown-trigger:focus-visible {
1381
+ outline: 3px solid color-mix(in oklab, var(--ph-focus-ring) 38%, transparent);
1382
+ outline-offset: 2px;
1383
+ }
1384
+
1385
+ .ph-dropdown-trigger:disabled {
1386
+ @apply pointer-events-none opacity-[0.65];
1387
+ }
1388
+
1389
+ .ph-dropdown-button[data-state="open"] .ph-btn-side-icon {
1390
+ transform: translateY(var(--ph-btn-face-offset)) rotate(180deg);
1391
+ }
1392
+
1393
+ .ph-dropdown-panel {
1394
+ @apply z-50 min-w-[12rem] overflow-y-auto bg-ph-surface p-1;
1395
+ max-width: min(22rem, calc(100vw - 1rem));
1396
+ max-height: var(--radix-dropdown-menu-content-available-height);
1397
+ border-radius: var(--ph-radius-overlay);
1022
1398
  border: 1px solid var(--ph-border-control);
1023
1399
  box-shadow:
1024
1400
  var(--ph-chrome-inset-face-top),
1025
- var(--ph-chrome-inset-face-bottom),
1026
- var(--ph-shadow-elevation-3000),
1027
- 0px 16px 16px -16px rgb(0 0 0 / 35%);
1401
+ var(--ph-shadow-floating);
1402
+ transform-origin: var(--radix-dropdown-menu-content-transform-origin);
1403
+ animation: ph-dropdown-in 60ms cubic-bezier(0.22, 1, 0.36, 1);
1404
+ will-change: transform, opacity;
1028
1405
  }
1029
1406
 
1030
- /* Dropdown */
1031
- .ph-dropdown-panel {
1032
- @apply z-50 mt-2 min-w-[12rem] bg-ph-surface p-1;
1033
- border-radius: var(--ph-radius-app);
1407
+ .ph-dropdown-panel[data-side="top"] {
1408
+ --ph-dropdown-entry-y: 0.25rem;
1409
+ }
1410
+
1411
+ .ph-dropdown-panel[data-side="bottom"] {
1412
+ --ph-dropdown-entry-y: -0.25rem;
1413
+ }
1414
+
1415
+ .ph-dropdown-panel[data-side="left"] {
1416
+ --ph-dropdown-entry-x: 0.25rem;
1417
+ }
1418
+
1419
+ .ph-dropdown-panel[data-side="right"] {
1420
+ --ph-dropdown-entry-x: -0.25rem;
1421
+ }
1422
+
1423
+ .ph-dropdown-arrow {
1424
+ fill: var(--ph-surface-raised);
1425
+ stroke: var(--ph-border-control);
1426
+ stroke-width: 1px;
1427
+ }
1428
+
1429
+ .ph-dropdown-item {
1430
+ @apply flex min-h-9 cursor-pointer select-none items-center gap-2 px-3 py-2 text-sm text-ph-subtle outline-none;
1431
+ border-radius: var(--ph-radius-control);
1432
+ transition:
1433
+ color var(--ph-motion-fast) ease,
1434
+ background-color var(--ph-motion-fast) ease;
1435
+ }
1436
+
1437
+ .ph-dropdown-item[data-highlighted] {
1438
+ color: var(--ph-text-primary);
1439
+ background: var(--ph-surface-hover);
1440
+ }
1441
+
1442
+ .ph-dropdown-item[data-disabled] {
1443
+ @apply pointer-events-none opacity-50;
1444
+ }
1445
+
1446
+ .ph-dropdown-radio-item {
1447
+ position: relative;
1448
+ padding-left: 2rem;
1449
+ }
1450
+
1451
+ .ph-dropdown-item-indicator {
1452
+ @apply absolute left-2 inline-flex h-4 w-4 items-center justify-center;
1453
+ color: var(--ph-accent);
1454
+ }
1455
+
1456
+ @keyframes ph-dropdown-in {
1457
+ from {
1458
+ opacity: 0;
1459
+ transform: translate(
1460
+ var(--ph-dropdown-entry-x, 0),
1461
+ var(--ph-dropdown-entry-y, 0)
1462
+ )
1463
+ scale(0.98);
1464
+ }
1465
+ }
1466
+
1467
+ /* Composable filter/sort bar and applied chips. */
1468
+ .ph-filter-bar {
1469
+ @apply flex flex-wrap items-center justify-between gap-3 px-3 py-2;
1470
+ background: var(--ph-surface-raised);
1034
1471
  border: 1px solid var(--ph-border-control);
1472
+ border-radius: var(--ph-radius-app);
1035
1473
  box-shadow:
1036
1474
  var(--ph-chrome-inset-face-top),
1037
1475
  var(--ph-shadow-elevation-3000);
1038
1476
  }
1039
1477
 
1040
- .ph-dropdown-item {
1041
- @apply flex cursor-pointer items-center gap-2 rounded-lg px-3 py-2 text-sm text-ph-subtle transition hover:bg-ph-muted;
1478
+ .ph-filter-bar__controls,
1479
+ .ph-filter-chips {
1480
+ @apply flex min-w-0 flex-wrap items-center gap-2;
1481
+ }
1482
+
1483
+ .ph-filter-bar__clear {
1484
+ @apply min-h-8 rounded-md px-2 text-xs font-medium text-ph-mutedtext underline-offset-4;
1485
+ }
1486
+
1487
+ .ph-filter-bar__clear:hover {
1488
+ @apply text-ph-ink underline;
1489
+ }
1490
+
1491
+ .ph-filter-bar__clear:focus-visible,
1492
+ .ph-filter-chip__main:focus-visible,
1493
+ .ph-filter-chip__remove:focus-visible {
1494
+ outline: 3px solid color-mix(in oklab, var(--ph-focus-ring) 38%, transparent);
1495
+ outline-offset: 2px;
1496
+ }
1497
+
1498
+ .ph-filter-bar__results {
1499
+ @apply whitespace-nowrap text-xs tabular-nums text-ph-mutedtext;
1500
+ }
1501
+
1502
+ .ph-filter-menu-trigger {
1503
+ @apply inline-flex min-h-8 max-w-full items-center gap-1.5 whitespace-nowrap px-2.5 text-xs font-medium text-ph-subtle;
1504
+ border: 1px solid var(--ph-border-control);
1505
+ border-radius: 9999px;
1506
+ background: var(--ph-surface-inset);
1507
+ transition:
1508
+ border-color var(--ph-motion-fast) ease,
1509
+ background-color var(--ph-motion-fast) ease,
1510
+ color var(--ph-motion-fast) ease;
1511
+ }
1512
+
1513
+ .ph-dropdown-trigger:hover .ph-filter-menu-trigger,
1514
+ .ph-dropdown-trigger[data-state="open"] .ph-filter-menu-trigger {
1515
+ color: var(--ph-text-primary);
1516
+ border-color: var(--ph-border-strong);
1517
+ background: var(--ph-surface-hover);
1518
+ }
1519
+
1520
+ .ph-filter-menu-trigger--active {
1521
+ border-color: color-mix(in oklab, var(--ph-accent) 45%, var(--ph-border-control));
1522
+ background: color-mix(in oklab, var(--ph-accent) 10%, var(--ph-surface-raised));
1523
+ }
1524
+
1525
+ .ph-filter-menu-trigger__label {
1526
+ color: var(--ph-text-tertiary);
1527
+ }
1528
+
1529
+ .ph-filter-menu-trigger__value {
1530
+ @apply max-w-40 overflow-hidden text-ellipsis;
1531
+ color: var(--ph-text-primary);
1532
+ }
1533
+
1534
+ .ph-sort-menu-trigger {
1535
+ border-radius: var(--ph-radius-control);
1536
+ }
1537
+
1538
+ .ph-filter-chip {
1539
+ @apply inline-flex min-h-8 max-w-full items-stretch overflow-hidden;
1540
+ color: var(--ph-text-secondary);
1541
+ border: 1px solid var(--ph-border-control);
1542
+ border-radius: 9999px;
1543
+ background: var(--ph-surface-inset);
1544
+ }
1545
+
1546
+ .ph-filter-chip--active {
1547
+ color: var(--ph-text-primary);
1548
+ border-color: color-mix(in oklab, var(--ph-accent) 45%, var(--ph-border-control));
1549
+ background: color-mix(in oklab, var(--ph-accent) 10%, var(--ph-surface-raised));
1550
+ }
1551
+
1552
+ .ph-filter-chip__main {
1553
+ @apply inline-flex min-w-0 items-center gap-1.5 rounded-l-full px-2.5 text-xs font-medium;
1554
+ }
1555
+
1556
+ button.ph-filter-chip__main:hover {
1557
+ background: var(--ph-surface-hover);
1558
+ }
1559
+
1560
+ .ph-filter-chip__label {
1561
+ color: var(--ph-text-tertiary);
1562
+ }
1563
+
1564
+ .ph-filter-chip__value {
1565
+ @apply max-w-40 overflow-hidden text-ellipsis whitespace-nowrap;
1566
+ }
1567
+
1568
+ .ph-filter-chip__remove {
1569
+ @apply inline-flex min-h-7 min-w-7 items-center justify-center rounded-r-full;
1570
+ border-left: 1px solid color-mix(in oklab, currentColor 16%, transparent);
1571
+ }
1572
+
1573
+ .ph-filter-chip__remove:hover {
1574
+ color: var(--ph-danger-text);
1575
+ background: var(--ph-danger-bg);
1576
+ }
1577
+
1578
+ .ph-filter-controls {
1579
+ @apply space-y-3;
1580
+ }
1581
+
1582
+ /* Authentication shells keep product forms focused without owning auth logic. */
1583
+ .ph-auth-layout {
1584
+ @apply grid min-h-[32rem] overflow-hidden;
1585
+ color: var(--ph-text-primary);
1586
+ background:
1587
+ radial-gradient(circle at 15% 10%, color-mix(in oklab, var(--ph-accent) 10%, transparent), transparent 34%),
1588
+ var(--ph-canvas);
1589
+ border: 1px solid var(--ph-border-control);
1590
+ border-radius: var(--ph-radius-overlay);
1591
+ }
1592
+
1593
+ .ph-auth-layout--split {
1594
+ grid-template-columns: minmax(15rem, 0.85fr) minmax(20rem, 1.15fr);
1595
+ }
1596
+
1597
+ .ph-auth-layout__aside {
1598
+ @apply relative flex flex-col justify-between overflow-hidden p-8;
1599
+ color: var(--ph-on-accent);
1600
+ background: var(--ph-accent);
1601
+ }
1602
+
1603
+ .ph-auth-layout__main {
1604
+ @apply flex min-w-0 items-center justify-center overflow-y-auto px-4 py-8 sm:px-8;
1605
+ }
1606
+
1607
+ .ph-auth-layout__column {
1608
+ @apply w-full max-w-[27rem];
1609
+ }
1610
+
1611
+ .ph-auth-layout__brand {
1612
+ @apply mb-5 text-center text-lg font-bold tracking-tight;
1613
+ }
1614
+
1615
+ .ph-auth-layout__footer {
1616
+ @apply mt-5 text-center text-xs text-ph-mutedtext;
1617
+ }
1618
+
1619
+ .ph-auth-card {
1620
+ @apply overflow-hidden;
1621
+ background: var(--ph-surface-raised);
1622
+ border: 1px solid var(--ph-border-control);
1623
+ border-radius: var(--ph-radius-surface);
1624
+ box-shadow:
1625
+ var(--ph-chrome-inset-face-top),
1626
+ var(--ph-shadow-raised);
1627
+ }
1628
+
1629
+ .ph-auth-card__header {
1630
+ @apply px-5 pb-1 pt-5 sm:px-8 sm:pt-8;
1631
+ }
1632
+
1633
+ .ph-auth-card__header h1 {
1634
+ @apply text-xl font-bold tracking-tight;
1635
+ }
1636
+
1637
+ .ph-auth-card__header p {
1638
+ @apply mt-1 text-sm text-ph-subtle;
1639
+ }
1640
+
1641
+ .ph-auth-card__body {
1642
+ @apply space-y-4 p-5 sm:p-8;
1643
+ }
1644
+
1645
+ .ph-auth-card__footer {
1646
+ @apply border-t px-5 py-4 text-center text-xs text-ph-subtle sm:px-8;
1647
+ border-color: var(--ph-border-subtle);
1648
+ background: var(--ph-surface-inset);
1649
+ }
1650
+
1651
+ .ph-auth-divider {
1652
+ @apply relative flex items-center justify-center text-[11px] text-ph-mutedtext;
1653
+ }
1654
+
1655
+ .ph-auth-divider::before {
1656
+ content: "";
1657
+ position: absolute;
1658
+ inset-inline: 0;
1659
+ top: 50%;
1660
+ height: 1px;
1661
+ background: var(--ph-border-subtle);
1662
+ }
1663
+
1664
+ .ph-auth-divider > span {
1665
+ @apply relative px-3;
1666
+ background: var(--ph-surface-raised);
1667
+ }
1668
+
1669
+ /* Route-friendly settings shell with mobile disclosure navigation. */
1670
+ .ph-settings-layout {
1671
+ @apply overflow-hidden;
1672
+ background: var(--ph-surface-raised);
1673
+ border: 1px solid var(--ph-border-control);
1674
+ border-radius: var(--ph-radius-overlay);
1675
+ }
1676
+
1677
+ .ph-settings-layout__header {
1678
+ @apply flex flex-wrap items-start justify-between gap-4 border-b px-5 py-5 sm:px-6;
1679
+ border-color: var(--ph-border-subtle);
1680
+ }
1681
+
1682
+ .ph-settings-layout__header h2 {
1683
+ @apply text-xl font-bold tracking-tight;
1684
+ }
1685
+
1686
+ .ph-settings-layout__header p {
1687
+ @apply mt-1 text-sm text-ph-subtle;
1688
+ }
1689
+
1690
+ .ph-settings-layout__actions {
1691
+ @apply flex flex-wrap items-center gap-2;
1692
+ }
1693
+
1694
+ .ph-settings-layout__grid {
1695
+ display: grid;
1696
+ grid-template-columns: 16rem minmax(0, 1fr);
1697
+ min-height: 30rem;
1698
+ }
1699
+
1700
+ .ph-settings-layout__sidebar {
1701
+ @apply border-r p-3;
1702
+ border-color: var(--ph-border-subtle);
1703
+ background: var(--ph-surface-inset);
1704
+ }
1705
+
1706
+ .ph-settings-layout__content {
1707
+ @apply min-w-0 space-y-5 p-5 sm:p-6;
1708
+ }
1709
+
1710
+ .ph-settings-layout__mobile-nav {
1711
+ display: none;
1712
+ }
1713
+
1714
+ .ph-settings-nav {
1715
+ @apply space-y-5;
1716
+ }
1717
+
1718
+ .ph-settings-nav__group h3 {
1719
+ @apply mb-1 px-2 text-[10px] font-semibold uppercase tracking-wider text-ph-mutedtext;
1720
+ }
1721
+
1722
+ .ph-settings-nav__items {
1723
+ @apply space-y-0.5;
1724
+ }
1725
+
1726
+ .ph-settings-nav__item {
1727
+ @apply flex min-h-9 items-center gap-2 rounded-[var(--ph-radius-control)] px-2.5 py-2 text-sm text-ph-subtle;
1728
+ }
1729
+
1730
+ .ph-settings-nav__item:hover {
1731
+ color: var(--ph-text-primary);
1732
+ background: var(--ph-surface-hover);
1733
+ }
1734
+
1735
+ .ph-settings-nav__item--active {
1736
+ color: var(--ph-selected-text);
1737
+ background: var(--ph-selected-bg);
1738
+ box-shadow: inset 3px 0 0 var(--ph-accent);
1739
+ }
1740
+
1741
+ .ph-settings-nav__icon {
1742
+ @apply inline-flex h-4 w-4 items-center justify-center;
1743
+ }
1744
+
1745
+ .ph-settings-nav__badge {
1746
+ @apply ml-auto text-[10px] text-ph-mutedtext;
1747
+ }
1748
+
1749
+ @media (max-width: 47.999rem) {
1750
+ .ph-auth-layout--split {
1751
+ grid-template-columns: 1fr;
1752
+ }
1753
+
1754
+ .ph-auth-layout__aside {
1755
+ display: none;
1756
+ }
1757
+
1758
+ .ph-settings-layout__mobile-nav {
1759
+ display: block;
1760
+ border-bottom: 1px solid var(--ph-border-subtle);
1761
+ }
1762
+
1763
+ .ph-settings-layout__mobile-nav > summary {
1764
+ @apply flex cursor-pointer list-none items-center gap-2 px-5 py-3 text-sm font-medium;
1765
+ }
1766
+
1767
+ .ph-settings-layout__mobile-nav > summary::-webkit-details-marker {
1768
+ display: none;
1769
+ }
1770
+
1771
+ .ph-settings-layout__mobile-nav > nav {
1772
+ @apply max-h-72 overflow-y-auto border-t p-3;
1773
+ border-color: var(--ph-border-subtle);
1774
+ background: var(--ph-surface-inset);
1775
+ }
1776
+
1777
+ .ph-settings-layout__grid {
1778
+ display: block;
1779
+ min-height: 0;
1780
+ }
1781
+
1782
+ .ph-settings-layout__sidebar {
1783
+ display: none;
1784
+ }
1042
1785
  }
1043
1786
 
1044
1787
  /* Tabs */
@@ -1159,28 +1902,63 @@ body {
1159
1902
  @apply relative flex aspect-square flex-col items-center justify-center rounded-[var(--ph-radius-app)] border text-[10px] font-bold uppercase tracking-wide shadow-sm ring-4 ring-ph-canvas transition hover:brightness-[1.02];
1160
1903
  }
1161
1904
 
1162
- /** Token / HogQL snippets — monospace. */
1905
+ .ph-product-icon-tile {
1906
+ border-color: color-mix(in oklab, var(--ph-icon-tile-color) 42%, var(--ph-border));
1907
+ background: color-mix(in oklab, var(--ph-icon-tile-color) 13%, var(--ph-surface-raised));
1908
+ color: var(--ph-text-primary);
1909
+ }
1910
+
1911
+ .ph-product-icon-tile .ph-icon {
1912
+ color: color-mix(in oklab, var(--ph-icon-tile-color) 78%, var(--ph-text-primary));
1913
+ }
1914
+
1915
+ /** Inline code / HogQL tokens are flat and distinct from physical keys. */
1916
+ .ph-code-token,
1163
1917
  .ph-kbd {
1164
- @apply rounded border border-ph-border bg-ph-muted px-1.5 py-0.5 text-[11px] text-ph-subtle shadow-sm;
1918
+ @apply inline rounded border px-1.5 py-0.5 text-[11px] text-ph-subtle;
1919
+ border-color: var(--ph-border-subtle);
1920
+ background: var(--ph-surface-inset);
1165
1921
  font-family: var(--ph-font-monospace-stack);
1166
1922
  font-variant-ligatures: none;
1167
1923
  font-feature-settings: "liga" off;
1168
1924
  font-synthesis-weight: none;
1169
1925
  }
1170
1926
 
1171
- /** Keyboard shortcuts ⌘ ⌥ ⇧ — system fonts so modifier glyphs resolve. */
1927
+ .ph-shortcut-group {
1928
+ @apply inline-flex items-center gap-0.5 align-middle;
1929
+ }
1930
+
1931
+ /** PostHog KeyboardShortcut: one compact cap per chord, with a crisp 2px lower edge. */
1932
+ .ph-keycap,
1172
1933
  .ph-shortcut {
1173
- @apply rounded border border-ph-border bg-ph-muted px-1.5 py-0.5 text-[11px] text-ph-subtle shadow-sm;
1174
- font-family: var(--ph-font-shortcut-stack);
1934
+ @apply inline-flex min-w-5 items-center justify-center whitespace-nowrap px-1 py-0.5 text-xs font-medium text-ph-ink;
1935
+ height: 1.25rem;
1936
+ border: 1px solid var(--ph-border-strong);
1937
+ border-bottom-width: 2px;
1938
+ border-radius: 0.25rem;
1939
+ background: var(--ph-surface-raised);
1940
+ box-shadow: none;
1941
+ font-family: var(--ph-font-monospace-stack);
1175
1942
  font-variant-numeric: normal;
1176
- letter-spacing: 0.02em;
1943
+ line-height: 1;
1944
+ user-select: none;
1945
+ }
1946
+
1947
+ .ph-shortcut-key {
1948
+ display: inline-flex;
1949
+ align-items: center;
1950
+ justify-content: center;
1951
+ }
1952
+
1953
+ .ph-shortcut-separator {
1954
+ @apply select-none text-[10px] font-medium text-ph-mutedtext;
1177
1955
  }
1178
1956
 
1179
1957
  /* LemonIcon parity — bespoke filled glyphs */
1180
1958
  .ph-icon {
1181
1959
  display: inline-block;
1182
- width: 1em;
1183
- height: 1em;
1960
+ width: var(--ph-icon-size, 1em);
1961
+ height: var(--ph-icon-size, 1em);
1184
1962
  vertical-align: -0.15em;
1185
1963
  flex-shrink: 0;
1186
1964
  }