@almadar/ui 5.121.3 → 5.122.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@almadar/ui",
3
- "version": "5.121.3",
3
+ "version": "5.122.0",
4
4
  "description": "React UI components, hooks, and providers for Almadar",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -117,7 +117,7 @@
117
117
  "access": "public"
118
118
  },
119
119
  "dependencies": {
120
- "@almadar/core": "^10.29.0",
120
+ "@almadar/core": "^10.30.0",
121
121
  "@almadar/evaluator": "^2.30.0",
122
122
  "@almadar/logger": "^1.9.0",
123
123
  "@almadar/runtime": "^6.33.0",
@@ -84,6 +84,13 @@ module.exports = {
84
84
  'ring-2',
85
85
  'ring-primary',
86
86
 
87
+ // Motion utilities (applied conditionally on open/close state)
88
+ 'animate-modal-in',
89
+ 'animate-modal-out',
90
+ 'animate-overlay-in',
91
+ 'animate-overlay-out',
92
+ 'animate-slide-up',
93
+
87
94
  // Auto-added by audit-tailwind-safelist.ts (2026-03-28)
88
95
  'active:scale-[var(--active-scale)]',
89
96
  'animate-[combat-flash_0.2s_ease-in-out_2]',
@@ -363,6 +370,153 @@ module.exports = {
363
370
  emphasized: 'var(--easing-emphasized, cubic-bezier(0.2, 0, 0, 1))',
364
371
  spring: 'var(--easing-spring, cubic-bezier(0.34, 1.56, 0.64, 1))',
365
372
  },
373
+ keyframes: {
374
+ 'modal-in': {
375
+ '0%': {
376
+ opacity: 'var(--motion-modal-enter-from-opacity, 0)',
377
+ transform: 'var(--motion-modal-enter-from-transform, scale(0.96) translateY(8px))',
378
+ },
379
+ '100%': {
380
+ opacity: 'var(--motion-modal-enter-to-opacity, 1)',
381
+ transform: 'var(--motion-modal-enter-to-transform, translateZ(0))',
382
+ },
383
+ },
384
+ 'modal-out': {
385
+ '0%': {
386
+ opacity: 'var(--motion-modal-exit-from-opacity, 1)',
387
+ transform: 'var(--motion-modal-exit-from-transform, translateZ(0))',
388
+ },
389
+ '100%': {
390
+ opacity: 'var(--motion-modal-exit-to-opacity, 0)',
391
+ transform: 'var(--motion-modal-exit-to-transform, scale(0.96) translateY(8px))',
392
+ },
393
+ },
394
+ 'overlay-in': {
395
+ '0%': { opacity: 'var(--motion-overlay-enter-from-opacity, var(--motion-overlay-from-opacity, 0))' },
396
+ '100%': { opacity: '1' },
397
+ },
398
+ 'overlay-out': {
399
+ '0%': { opacity: '1' },
400
+ '100%': { opacity: 'var(--motion-overlay-exit-to-opacity, var(--motion-overlay-from-opacity, 0))' },
401
+ },
402
+ 'slide-up': {
403
+ '0%': {
404
+ opacity: 'var(--motion-slide-up-from-opacity, 0)',
405
+ transform: 'var(--motion-slide-up-from-transform, translateY(16px))',
406
+ },
407
+ '100%': {
408
+ opacity: '1',
409
+ transform: 'translateZ(0)',
410
+ },
411
+ },
412
+ 'drawer-in': {
413
+ '0%': {
414
+ opacity: 'var(--motion-drawer-enter-from-opacity, 0)',
415
+ transform: 'var(--motion-drawer-enter-from-transform, translateX(100%))',
416
+ },
417
+ '100%': {
418
+ opacity: 'var(--motion-drawer-enter-to-opacity, 1)',
419
+ transform: 'var(--motion-drawer-enter-to-transform, translateZ(0))',
420
+ },
421
+ },
422
+ 'drawer-out': {
423
+ '0%': {
424
+ opacity: 'var(--motion-drawer-exit-from-opacity, 1)',
425
+ transform: 'var(--motion-drawer-exit-from-transform, translateZ(0))',
426
+ },
427
+ '100%': {
428
+ opacity: 'var(--motion-drawer-exit-to-opacity, 0)',
429
+ transform: 'var(--motion-drawer-exit-to-transform, translateX(100%))',
430
+ },
431
+ },
432
+ 'popover-in': {
433
+ '0%': {
434
+ opacity: 'var(--motion-popover-enter-from-opacity, 0)',
435
+ transform: 'var(--motion-popover-enter-from-transform, scale(0.95))',
436
+ },
437
+ '100%': {
438
+ opacity: 'var(--motion-popover-enter-to-opacity, 1)',
439
+ transform: 'var(--motion-popover-enter-to-transform, scale(1))',
440
+ },
441
+ },
442
+ 'popover-out': {
443
+ '0%': {
444
+ opacity: 'var(--motion-popover-exit-from-opacity, 1)',
445
+ transform: 'var(--motion-popover-exit-from-transform, scale(1))',
446
+ },
447
+ '100%': {
448
+ opacity: 'var(--motion-popover-exit-to-opacity, 0)',
449
+ transform: 'var(--motion-popover-exit-to-transform, scale(0.95))',
450
+ },
451
+ },
452
+ 'toast-in': {
453
+ '0%': {
454
+ opacity: 'var(--motion-toast-enter-from-opacity, 0)',
455
+ transform: 'var(--motion-toast-enter-from-transform, translateY(16px))',
456
+ },
457
+ '100%': {
458
+ opacity: 'var(--motion-toast-enter-to-opacity, 1)',
459
+ transform: 'var(--motion-toast-enter-to-transform, translateZ(0))',
460
+ },
461
+ },
462
+ 'toast-out': {
463
+ '0%': {
464
+ opacity: 'var(--motion-toast-exit-from-opacity, 1)',
465
+ transform: 'var(--motion-toast-exit-from-transform, translateZ(0))',
466
+ },
467
+ '100%': {
468
+ opacity: 'var(--motion-toast-exit-to-opacity, 0)',
469
+ transform: 'var(--motion-toast-exit-to-transform, translateY(16px))',
470
+ },
471
+ },
472
+ 'fade-in': {
473
+ '0%': { opacity: 'var(--motion-fade-enter-from-opacity, 0)' },
474
+ '100%': { opacity: 'var(--motion-fade-enter-to-opacity, 1)' },
475
+ },
476
+ 'fade-out': {
477
+ '0%': { opacity: 'var(--motion-fade-exit-from-opacity, 1)' },
478
+ '100%': { opacity: 'var(--motion-fade-exit-to-opacity, 0)' },
479
+ },
480
+ 'page-in': {
481
+ '0%': {
482
+ opacity: 'var(--motion-page-enter-from-opacity, 0)',
483
+ transform: 'var(--motion-page-enter-from-transform, translateY(8px))',
484
+ },
485
+ '100%': {
486
+ opacity: 'var(--motion-page-enter-to-opacity, 1)',
487
+ transform: 'var(--motion-page-enter-to-transform, translateZ(0))',
488
+ },
489
+ },
490
+ 'page-out': {
491
+ '0%': {
492
+ opacity: 'var(--motion-page-exit-from-opacity, 1)',
493
+ transform: 'var(--motion-page-exit-from-transform, translateZ(0))',
494
+ },
495
+ '100%': {
496
+ opacity: 'var(--motion-page-exit-to-opacity, 0)',
497
+ transform: 'var(--motion-page-exit-to-transform, translateY(-8px))',
498
+ },
499
+ },
500
+ },
501
+ animation: {
502
+ // Motion-token-driven durations/easings (fall back to legacy
503
+ // --transition-* so themes that haven't migrated keep working).
504
+ 'modal-in': 'modal-in var(--duration-normal, var(--transition-normal, 250ms)) var(--easing-standard, var(--transition-timing, cubic-bezier(0.4, 0, 0.2, 1)))',
505
+ 'modal-out': 'modal-out var(--duration-fast, var(--transition-fast, 150ms)) var(--easing-standard, var(--transition-timing, cubic-bezier(0.4, 0, 0.2, 1)))',
506
+ 'overlay-in': 'overlay-in var(--duration-normal, var(--transition-normal, 250ms)) var(--easing-standard, var(--transition-timing, cubic-bezier(0.4, 0, 0.2, 1)))',
507
+ 'overlay-out': 'overlay-out var(--duration-fast, var(--transition-fast, 150ms)) var(--easing-standard, var(--transition-timing, cubic-bezier(0.4, 0, 0.2, 1)))',
508
+ 'slide-up': 'slide-up var(--duration-slow, var(--transition-slow, 400ms)) var(--easing-emphasized, cubic-bezier(0.2, 0, 0, 1))',
509
+ 'drawer-in': 'drawer-in var(--duration-normal, var(--transition-normal, 250ms)) var(--easing-emphasized, cubic-bezier(0.2, 0, 0, 1))',
510
+ 'drawer-out': 'drawer-out var(--duration-normal, var(--transition-normal, 250ms)) var(--easing-emphasized, cubic-bezier(0.2, 0, 0, 1))',
511
+ 'popover-in': 'popover-in var(--duration-fast, var(--transition-fast, 150ms)) var(--easing-standard, var(--transition-timing, cubic-bezier(0.4, 0, 0.2, 1)))',
512
+ 'popover-out': 'popover-out var(--duration-fast, var(--transition-fast, 150ms)) var(--easing-standard, var(--transition-timing, cubic-bezier(0.4, 0, 0.2, 1)))',
513
+ 'toast-in': 'toast-in var(--duration-normal, var(--transition-normal, 250ms)) var(--easing-emphasized, cubic-bezier(0.2, 0, 0, 1))',
514
+ 'toast-out': 'toast-out var(--duration-normal, var(--transition-normal, 250ms)) var(--easing-standard, var(--transition-timing, cubic-bezier(0.4, 0, 0.2, 1)))',
515
+ 'fade-in': 'fade-in var(--duration-fast, var(--transition-fast, 150ms)) var(--easing-standard, var(--transition-timing, cubic-bezier(0.4, 0, 0.2, 1)))',
516
+ 'fade-out': 'fade-out var(--duration-fast, var(--transition-fast, 150ms)) var(--easing-standard, var(--transition-timing, cubic-bezier(0.4, 0, 0.2, 1)))',
517
+ 'page-in': 'page-in var(--duration-normal, var(--transition-normal, 250ms)) var(--easing-standard, var(--transition-timing, cubic-bezier(0.4, 0, 0.2, 1)))',
518
+ 'page-out': 'page-out var(--duration-fast, var(--transition-fast, 150ms)) var(--easing-standard, var(--transition-timing, cubic-bezier(0.4, 0, 0.2, 1)))',
519
+ },
366
520
  // Density-axis spacing scale. Tailwind's default `p-3` / `gap-4` resolve
367
521
  // here so existing classes keep working — but now read from tokens.
368
522
  // Defaults match Tailwind's default (12px / 16px / 24px for 3 / 4 / 6),
@@ -453,4 +607,25 @@ module.exports = {
453
607
  },
454
608
  },
455
609
  },
610
+ plugins: [
611
+ // Chrome-skin utilities: sprite-driven panel/button chrome, opt-in per theme.
612
+ // No-op (`none`) unless the active theme defines the backing --game-* vars —
613
+ // zero visual change for every theme that doesn't set them.
614
+ require('tailwindcss/plugin')(({ addUtilities }) => {
615
+ addUtilities({
616
+ '.chrome-panel': {
617
+ 'border-image-source': 'var(--game-panel-border-image, none)',
618
+ 'border-image-slice': 'var(--game-panel-border-image-slice, 30 fill)',
619
+ 'border-image-width': 'var(--game-panel-border-image-width, 30px)',
620
+ 'border-image-repeat': 'stretch',
621
+ },
622
+ '.chrome-button': {
623
+ 'background-image': 'var(--game-button-bg-image, none)',
624
+ 'background-size': 'var(--game-button-bg-size, 100% 100%)',
625
+ 'background-repeat': 'no-repeat',
626
+ 'background-position': 'center',
627
+ },
628
+ });
629
+ }),
630
+ ],
456
631
  };
package/themes/_base.css CHANGED
@@ -121,4 +121,115 @@
121
121
  --card-padding-lg: 40px;
122
122
  --dialog-padding: 40px;
123
123
  --section-gap: 64px;
124
+
125
+ /* ----------------------------------------------------------------
126
+ * Motion — duration & easing axis
127
+ * ----------------------------------------------------------------
128
+ * Base values for the motion axis. Per-theme blocks override these to
129
+ * restyle timing without touching keyframes. The legacy --transition-*
130
+ * aliases are kept as fallbacks for themes that haven't migrated.
131
+ * ---------------------------------------------------------------- */
132
+ --duration-instant: 0ms;
133
+ --duration-fast: var(--transition-fast, 150ms);
134
+ --duration-normal: var(--transition-normal, 250ms);
135
+ --duration-slow: var(--transition-slow, 400ms);
136
+ --duration-dramatic: 600ms;
137
+ --easing-linear: linear;
138
+ --easing-standard: var(--transition-timing, cubic-bezier(0.4, 0, 0.2, 1));
139
+ --easing-emphasized: cubic-bezier(0.2, 0, 0, 1);
140
+ --easing-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
141
+ /* Master on/off. Set `off` to collapse EVERY motion to instant without
142
+ * relying on prefers-reduced-motion (programmatic opt-out for themes). */
143
+ --motion-enable: on;
144
+
145
+ /* ----------------------------------------------------------------
146
+ * Motion — keyframe endpoints
147
+ * ----------------------------------------------------------------
148
+ * The "shape" of every built-in transition. Duration/easing come from
149
+ * --duration-* / --easing-*. Override any of these in a theme block to
150
+ * restyle the motion — e.g. a minimal theme sets
151
+ * --motion-modal-enter-from-transform: none for a pure fade. Each surface
152
+ * has enter + exit endpoints so <Presence> can animate both directions.
153
+ * ---------------------------------------------------------------- */
154
+ --motion-overlay-from-opacity: 0;
155
+ --motion-overlay-enter-from-opacity: var(--motion-overlay-from-opacity);
156
+ --motion-overlay-exit-to-opacity: var(--motion-overlay-from-opacity);
157
+
158
+ --motion-modal-enter-from-opacity: 0;
159
+ --motion-modal-enter-from-transform: scale(0.96) translateY(8px);
160
+ --motion-modal-enter-to-opacity: 1;
161
+ --motion-modal-enter-to-transform: translateZ(0);
162
+ --motion-modal-exit-from-opacity: 1;
163
+ --motion-modal-exit-from-transform: translateZ(0);
164
+ --motion-modal-exit-to-opacity: 0;
165
+ --motion-modal-exit-to-transform: scale(0.96) translateY(8px);
166
+
167
+ --motion-slide-up-from-opacity: 0;
168
+ --motion-slide-up-from-transform: translateY(16px);
169
+
170
+ /* Drawer / SidePanel — horizontal slide. transform sign is flipped per
171
+ * side by the component via a CSS var override (--motion-drawer-sign). */
172
+ --motion-drawer-sign: 1;
173
+ --motion-drawer-enter-from-opacity: 0;
174
+ --motion-drawer-enter-from-transform: translateX(calc(100% * var(--motion-drawer-sign)));
175
+ --motion-drawer-enter-to-opacity: 1;
176
+ --motion-drawer-enter-to-transform: translateZ(0);
177
+ --motion-drawer-exit-from-opacity: 1;
178
+ --motion-drawer-exit-from-transform: translateZ(0);
179
+ --motion-drawer-exit-to-opacity: 0;
180
+ --motion-drawer-exit-to-transform: translateX(calc(100% * var(--motion-drawer-sign)));
181
+
182
+ /* Popover — small scale + fade from the trigger origin. */
183
+ --motion-popover-enter-from-opacity: 0;
184
+ --motion-popover-enter-from-transform: scale(0.95);
185
+ --motion-popover-enter-to-opacity: 1;
186
+ --motion-popover-enter-to-transform: scale(1);
187
+ --motion-popover-exit-from-opacity: 1;
188
+ --motion-popover-exit-from-transform: scale(1);
189
+ --motion-popover-exit-to-opacity: 0;
190
+ --motion-popover-exit-to-transform: scale(0.95);
191
+
192
+ /* Toast — slide up + fade (bottom-origin notification). */
193
+ --motion-toast-enter-from-opacity: 0;
194
+ --motion-toast-enter-from-transform: translateY(16px);
195
+ --motion-toast-enter-to-opacity: 1;
196
+ --motion-toast-enter-to-transform: translateZ(0);
197
+ --motion-toast-exit-from-opacity: 1;
198
+ --motion-toast-exit-from-transform: translateZ(0);
199
+ --motion-toast-exit-to-opacity: 0;
200
+ --motion-toast-exit-to-transform: translateY(16px);
201
+
202
+ /* Generic fade — default for arbitrary component enter/leave (<Presence>). */
203
+ --motion-fade-enter-from-opacity: 0;
204
+ --motion-fade-enter-to-opacity: 1;
205
+ --motion-fade-exit-from-opacity: 1;
206
+ --motion-fade-exit-to-opacity: 0;
207
+
208
+ /* Page / route transition — the routed subtree animates on navigation. */
209
+ --motion-page-enter-from-opacity: 0;
210
+ --motion-page-enter-from-transform: translateY(8px);
211
+ --motion-page-enter-to-opacity: 1;
212
+ --motion-page-enter-to-transform: translateZ(0);
213
+ --motion-page-exit-from-opacity: 1;
214
+ --motion-page-exit-from-transform: translateZ(0);
215
+ --motion-page-exit-to-opacity: 0;
216
+ --motion-page-exit-to-transform: translateY(-8px);
217
+ }
218
+
219
+ /* ----------------------------------------------------------------
220
+ * Reduced motion — the canonical accessibility kill-switch.
221
+ * Collapses EVERY animation/transition to ~instant regardless of which
222
+ * surface or theme is active. Themes/apps must NOT re-raise durations
223
+ * under this media query. (Supersedes per-app overrides like kflow.css.)
224
+ * ---------------------------------------------------------------- */
225
+ @media (prefers-reduced-motion: reduce) {
226
+ :root {
227
+ --motion-enable: off;
228
+ }
229
+ *, *::before, *::after {
230
+ animation-duration: 0.01ms !important;
231
+ animation-iteration-count: 1 !important;
232
+ transition-duration: 0.01ms !important;
233
+ scroll-behavior: auto !important;
234
+ }
124
235
  }
@@ -158,6 +158,31 @@ Easing palette (existing `--transition-timing` aliases to `--easing-standard`):
158
158
  | `--easing-emphasized` | `cubic-bezier(0.2, 0, 0, 1)` |
159
159
  | `--easing-spring` | `cubic-bezier(0.34, 1.56, 0.64, 1)` |
160
160
 
161
+ **Master toggle:** `--motion-enable` (`on` \| `off`, default `on`). Set `off` to collapse every
162
+ motion to instant — the programmatic opt-out (independent of `prefers-reduced-motion`, which the
163
+ base layer also honours globally).
164
+
165
+ ### Motion axis — keyframe endpoints (surface shapes)
166
+
167
+ Every animated surface has enter + exit endpoints so `<Presence>` (the one enter/leave primitive)
168
+ can animate both directions. Override these in a theme block to restyle the *shape*; duration &
169
+ easing still come from the palettes above.
170
+
171
+ | Surface | Endpoints (enter/exit) | Default shape |
172
+ |---|---|---|
173
+ | overlay | `--motion-overlay-enter-from-opacity`, `--motion-overlay-exit-to-opacity` | fade |
174
+ | modal | `--motion-modal-{enter,exit}-{from,to}-{opacity,transform}` | scale .96 + translateY(8px) + fade |
175
+ | slide-up | `--motion-slide-up-from-{opacity,transform}` | translateY(16px) + fade |
176
+ | drawer | `--motion-drawer-{enter,exit}-{from,to}-{opacity,transform}` + `--motion-drawer-sign` (1 / -1) | horizontal slide by side |
177
+ | popover | `--motion-popover-{enter,exit}-{from,to}-{opacity,transform}` | scale .95 + fade |
178
+ | toast | `--motion-toast-{enter,exit}-{from,to}-{opacity,transform}` | translateY(16px) + fade |
179
+ | fade | `--motion-fade-{enter,exit}-{from,to}-opacity` | opacity only |
180
+ | page | `--motion-page-{enter,exit}-{from,to}-{opacity,transform}` | translateY(±8px) + fade |
181
+
182
+ These back the preset utilities `animate-<surface>-in` / `animate-<surface>-out`. A reduced-motion
183
+ user (`@media (prefers-reduced-motion: reduce)`, declared once in `_base.css`) gets every surface
184
+ instantly regardless of theme.
185
+
161
186
  ### Iconography axis
162
187
 
163
188
  | Variable | Default | Notes |
@@ -0,0 +1,230 @@
1
+ /**
2
+ * Game Adventure Theme — "Tavern Parchment"
3
+ *
4
+ * Kenney's fantasy-adventure UI pack (warm brown-framed parchment panels,
5
+ * hanging banners) as a real theme, same contract as every other theme.
6
+ * Dark tavern-brown background so the parchment panel chrome reads as a
7
+ * lit scroll/signboard.
8
+ *
9
+ * Chrome sprites: --game-panel-border-image is a CSS 9-slice (border-image)
10
+ * sourced from kenney-ui-pack-adventure's `panel_brownimg` sprite, consumed
11
+ * by the `.chrome-panel` utility (tailwind-preset.cjs) that Card/GameShell/
12
+ * GameHud/GameMenu already apply unconditionally. This pack's only button
13
+ * sprites are small icon-sized (close/confirm), not full-width chrome, so
14
+ * --game-button-bg-image is left undeclared — Button renders with flat
15
+ * theme-colored chrome instead (the `.chrome-button` utility's `none`
16
+ * fallback already handles this cleanly, same as game-sci-fi).
17
+ */
18
+ [data-theme="game-adventure-dark"] {
19
+ /* Shadows — warm tavern glow */
20
+ --shadow-main: 0 4px 14px rgba(224, 165, 44, 0.12), 0 1px 3px rgba(0, 0, 0, 0.4);
21
+ --shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.3);
22
+ --shadow-lg: 0 8px 30px rgba(224, 165, 44, 0.18), 0 4px 10px rgba(0, 0, 0, 0.35);
23
+ --shadow-inner: inset 0 1px 3px rgba(0, 0, 0, 0.4);
24
+ --shadow-none: none;
25
+ --shadow-hover: 0 12px 40px rgba(224, 165, 44, 0.22), 0 4px 12px rgba(0, 0, 0, 0.3);
26
+ --shadow-active: 0 2px 8px rgba(0, 0, 0, 0.35);
27
+
28
+ /* Border radius — carved wood/parchment, only lightly rounded */
29
+ --radius-none: 0px;
30
+ --radius-sm: 3px;
31
+ --radius-md: 5px;
32
+ --radius-lg: 8px;
33
+ --radius-xl: 12px;
34
+ --radius-full: 9999px;
35
+
36
+ /* Border width */
37
+ --border-width: 1px;
38
+ --border-width-thin: 1px;
39
+ --border-width-thick: 2px;
40
+
41
+ /* ======================================================================
42
+ * CORE COLORS
43
+ * ====================================================================== */
44
+
45
+ /* Primary — leather brown-orange */
46
+ --color-primary: #b5793a;
47
+ --color-primary-hover: #c98d4c;
48
+ --color-primary-foreground: #241608;
49
+
50
+ /* Secondary — dark wood */
51
+ --color-secondary: #3a2712;
52
+ --color-secondary-hover: #4a3319;
53
+ --color-secondary-foreground: #f3e3c4;
54
+
55
+ /* Accent — gold trim */
56
+ --color-accent: #e0a52c;
57
+ --color-accent-foreground: #241608;
58
+
59
+ /* Muted */
60
+ --color-muted: #4a3018;
61
+ --color-muted-foreground: #b3966e;
62
+
63
+ /* Surfaces */
64
+ --color-background: #241608;
65
+ --color-foreground: #f3e3c4;
66
+ --color-card: #fff1d2;
67
+ --color-card-foreground: #4a2f14;
68
+ --color-surface: #2e1d0d;
69
+
70
+ /* Borders & Inputs */
71
+ --color-border: #7a5228;
72
+ --color-input: #3a2712;
73
+ --color-ring: #e0a52c;
74
+
75
+ /* Semantic colors */
76
+ --color-error: #d9534f;
77
+ --color-error-foreground: #1a0808;
78
+ --color-success: #7ba05b;
79
+ --color-success-foreground: #0d1a08;
80
+ --color-warning: #e0a52c;
81
+ --color-warning-foreground: #241608;
82
+ --color-info: #b5793a;
83
+ --color-info-foreground: #241608;
84
+
85
+ /* ======================================================================
86
+ * CHROME — sprite-driven panel skin (kenney-ui-pack-adventure/panel_brownimg)
87
+ * ====================================================================== */
88
+ --game-panel-border-image: url('https://almadar-kflow-assets.web.app/shared/ui-game-shell/kenney-ui-pack-adventure/ui/panel_brownimg.png');
89
+ --game-panel-border-image-slice: 17 fill;
90
+ --game-panel-border-image-width: 14px;
91
+
92
+ /* ======================================================================
93
+ * TYPOGRAPHY
94
+ * ====================================================================== */
95
+ --font-family:
96
+ "Kenney Blocks", ui-sans-serif, system-ui, sans-serif;
97
+ --font-weight-normal: 400;
98
+ --font-weight-medium: 500;
99
+ --font-weight-bold: 700;
100
+ --letter-spacing: 0.02em;
101
+ --line-height: 1.5;
102
+
103
+ /* Icon styling — gold tint */
104
+ --icon-stroke-width: 1.75;
105
+ --icon-color: #e0a52c;
106
+
107
+ /* Transitions */
108
+ --transition-fast: 100ms;
109
+ --transition-normal: 200ms;
110
+ --transition-slow: 400ms;
111
+ --transition-timing: cubic-bezier(0.4, 0, 0.2, 1);
112
+
113
+ /* Hover/Active transforms */
114
+ --hover-scale: 1.01;
115
+ --hover-translate-y: -1px;
116
+ --hover-translate-x: 0;
117
+ --active-scale: 0.99;
118
+ --active-translate-y: 0;
119
+
120
+ /* Focus ring — gold */
121
+ --focus-ring-width: 2px;
122
+ --focus-ring-offset: 2px;
123
+ --focus-ring-color: #e0a52c;
124
+
125
+ /* Layer 1 — backfill (see themes/_contract.md) */
126
+ /* Density axis — spacing scale */
127
+ --space-0: 0px;
128
+ --space-1: 4px;
129
+ --space-2: 8px;
130
+ --space-3: 12px;
131
+ --space-4: 16px;
132
+ --space-5: 20px;
133
+ --space-6: 24px;
134
+ --space-7: 28px;
135
+ --space-8: 32px;
136
+ --space-9: 36px;
137
+ --space-10: 40px;
138
+ --space-11: 44px;
139
+ --space-12: 48px;
140
+ /* Density axis — per-element heights */
141
+ --button-height-sm: 28px;
142
+ --button-height-md: 36px;
143
+ --button-height-lg: 44px;
144
+ --input-height-sm: 28px;
145
+ --input-height-md: 36px;
146
+ --input-height-lg: 44px;
147
+ --row-height-compact: 32px;
148
+ --row-height-normal: 40px;
149
+ --row-height-spacious: 48px;
150
+ /* Density axis — per-element padding */
151
+ --card-padding-sm: 12px;
152
+ --card-padding-md: 16px;
153
+ --card-padding-lg: 24px;
154
+ --dialog-padding: 24px;
155
+ --section-gap: 32px;
156
+ /* Type axis — family triplet */
157
+ --font-family-display: var(--font-family);
158
+ --font-family-body: var(--font-family);
159
+ --font-family-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
160
+ /* Type axis — size scale */
161
+ --text-xs: 12px;
162
+ --leading-xs: 16px;
163
+ --text-sm: 14px;
164
+ --leading-sm: 20px;
165
+ --text-base: 16px;
166
+ --leading-base: 24px;
167
+ --text-lg: 18px;
168
+ --leading-lg: 28px;
169
+ --text-xl: 20px;
170
+ --leading-xl: 28px;
171
+ --text-2xl: 24px;
172
+ --leading-2xl: 32px;
173
+ --text-3xl: 30px;
174
+ --leading-3xl: 36px;
175
+ --text-4xl: 36px;
176
+ --leading-4xl: 40px;
177
+ --text-display-1: 48px;
178
+ --leading-display-1: 52px;
179
+ --text-display-2: 60px;
180
+ --leading-display-2: 64px;
181
+ /* Type axis — intent mapping */
182
+ --intent-heading-major-size: var(--text-2xl);
183
+ --intent-heading-major-weight: var(--font-weight-bold);
184
+ --intent-heading-major-leading: var(--leading-2xl);
185
+ --intent-heading-minor-size: var(--text-lg);
186
+ --intent-heading-minor-weight: var(--font-weight-bold);
187
+ --intent-heading-minor-leading: var(--leading-lg);
188
+ --intent-body-emphasis-size: var(--text-base);
189
+ --intent-body-emphasis-weight: var(--font-weight-medium);
190
+ --intent-body-emphasis-leading: var(--leading-base);
191
+ --intent-body-default-size: var(--text-sm);
192
+ --intent-body-default-weight: var(--font-weight-normal);
193
+ --intent-body-default-leading: var(--leading-sm);
194
+ --intent-body-quiet-size: var(--text-sm);
195
+ --intent-body-quiet-weight: var(--font-weight-normal);
196
+ --intent-body-quiet-leading: var(--leading-sm);
197
+ --intent-caption-size: var(--text-xs);
198
+ --intent-caption-weight: var(--font-weight-normal);
199
+ --intent-caption-leading: var(--leading-xs);
200
+ --intent-numeric-size: var(--text-sm);
201
+ --intent-numeric-weight: var(--font-weight-medium);
202
+ --intent-numeric-leading: var(--leading-sm);
203
+ /* Motion axis — duration palette */
204
+ --duration-instant: 0ms;
205
+ --duration-fast: var(--transition-fast);
206
+ --duration-normal: var(--transition-normal);
207
+ --duration-slow: var(--transition-slow);
208
+ --duration-dramatic: 600ms;
209
+ /* Motion axis — easing palette */
210
+ --easing-linear: linear;
211
+ --easing-standard: var(--transition-timing);
212
+ --easing-emphasized: cubic-bezier(0.2, 0, 0, 1);
213
+ --easing-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
214
+ /* Iconography axis */
215
+ --icon-family: lucide;
216
+ --icon-default-size: 16px;
217
+ /* Elevation axis — per-layer mapping */
218
+ --elevation-card: var(--shadow-sm);
219
+ --elevation-popover: var(--shadow-main);
220
+ --elevation-dialog: var(--shadow-lg);
221
+ --elevation-toast: var(--shadow-main);
222
+ /* Geometry axis — radius rhythm with intent */
223
+ --radius-container: var(--radius-md);
224
+ --radius-interactive: var(--radius-md);
225
+ --radius-pill: var(--radius-full);
226
+ /* Geometry axis — border-width rhythm */
227
+ --border-hairline: 1px;
228
+ --border-standard: var(--border-width);
229
+ --border-heavy: var(--border-width-thick);
230
+ }