@higinio/design-mcp 0.1.1 → 0.2.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.
@@ -1,93 +1,64 @@
1
- /* ============================================================================
2
- NOCTURNA ELÉCTRICA — Framework CSS v2.0
3
- Sistema de diseño dark-first de Higinio Sosa.
1
+ /* ============================================================
2
+ Nocturna Eléctrica — Design Tokens v2.0
3
+ Sistema de diseño de Higinio Sosa · dark-first
4
4
  Fuente de verdad: DESIGN-nocturna-electrica.md
5
- Uso: enlazar este archivo antes de cualquier estilo propio.
6
- Tema: dark por defecto; <html data-theme="light"> activa la variante clara.
7
- Los componentes consumen SOLO aliases semánticos — nunca hex directos.
8
- ========================================================================== */
5
+ Uso: importar ANTES de cualquier estilo propio.
6
+ Los componentes consumen SOLO aliases semánticos, nunca hex.
7
+ ============================================================ */
9
8
 
10
9
  @import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@500;600;700&family=Manrope:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap');
11
10
 
12
- /* ============================================================
13
- 1. TOKENS — primitivos + aliases (dark por defecto)
14
- ============================================================ */
15
11
  :root {
16
- /* -- Primitivos: acento -- */
12
+ /* == Primitivos: acento (Celeste Eléctrico) ==
13
+ accent-600 sobre blanco da 4.01:1 — vale como UI (≥3) pero NO como texto (<4.5).
14
+ Para texto sobre claro se usa accent-700 (5.95:1) y accent-800 (8.74:1). */
17
15
  --accent-100: #d9f0fb; --accent-200: #a8ddf6; --accent-300: #7dccf1;
18
16
  --accent-400: #52baed; --accent-500: #2fa3dd; --accent-600: #1f87bb; --accent-700: #186a94;
19
- --accent-800: #12506f; /* solo light: texto de acción en hover */
17
+ --accent-800: #12506f;
20
18
 
21
- /* -- Primitivos: índigo -- */
19
+ /* == Primitivos: índigo (Índigo Profundo) — nunca texto sobre negro == */
22
20
  --indigo-300: #7a83c4; --indigo-400: #565fa3; --indigo-500: #353d7c;
23
21
  --indigo-600: #2b3264; --indigo-700: #22284f; --indigo-800: #1a1e3b;
24
22
 
25
- /* -- Primitivos: escala nocturna -- */
23
+ /* == Primitivos: escala nocturna == */
26
24
  --night-950: #010203; --night-900: #0b0d12; --night-800: #14171f;
27
25
  --night-700: #1e222d; --night-600: #2a2f3d; --night-500: #3d4354;
28
- --night-450: #5f6575; /* texto atenuado en light */
26
+ --night-450: #5f6575; /* texto atenuado en light: night-400 caía a 4.27:1 sobre --surface-3 */
29
27
  --night-400: #6b7183; --night-300: #9aa0b0; --night-200: #c5c9d4;
30
28
  --night-100: #e8eaef; --night-50: #f6f7f9;
31
29
 
32
- /* -- Primitivos: semánticos -- */
30
+ /* == Primitivos: semánticos (calibrados para dark) == */
33
31
  --success-400: #4ade80; --success-bg: #0a2617;
34
32
  --warning-400: #fbbf24; --warning-bg: #2a2005;
35
33
  --danger-400: #f87171; --danger-bg: #2c0f0f;
36
34
  --info-400: #52baed; --info-bg: #0d1f2c;
37
35
 
38
- /* -- Aliases semánticos (los componentes consumen SOLO esto) -- */
36
+ /* == Aliases semánticos (los componentes consumen SOLO esto) == */
39
37
  --bg: var(--night-950);
40
38
  --surface-1: var(--night-900);
41
39
  --surface-2: var(--night-800);
42
40
  --surface-3: var(--night-700);
43
41
  --border: var(--night-500);
44
- --border-strong: var(--night-600);
42
+ /* night-400, no night-600: en dark, oscurecer un borde lo DEBILITA (principio 2:
43
+ la elevación se comunica aclarando). n600 daba 1.46:1 vs los 1.97:1 del borde
44
+ normal — el énfasis se veía más flojo. n400 = 3.99:1 sobre surface-1. */
45
+ --border-strong: var(--night-400);
45
46
  --text-primary: var(--night-100);
46
47
  --text-secondary: var(--night-200);
47
48
  --text-muted: var(--night-300);
48
49
  --text-disabled: var(--night-400);
49
50
  --text-heading: var(--night-50);
50
- --text-on-accent: var(--night-950);
51
- --success: var(--success-400);
52
- --warning: var(--warning-400);
53
- --danger: var(--danger-400);
54
- --info: var(--info-400);
51
+ --text-on-accent: var(--night-950); /* texto de CTA oscuro, nunca blanco */
52
+ --success: var(--success-400); --warning: var(--warning-400);
53
+ --danger: var(--danger-400); --info: var(--info-400);
54
+ --link: var(--accent-400);
55
+ --link-hover: var(--accent-200);
55
56
  --accent-ghost-hover: rgba(82,186,237,.08);
56
57
  --accent-ghost-active: rgba(82,186,237,.14);
57
58
  --accent-selected: rgba(82,186,237,.12);
58
59
  --overlay: rgba(1,2,3,.7);
59
60
 
60
- /* -- Aliases de componentes (derivados; cambian con el tema) -- */
61
- --link: var(--accent-400);
62
- --link-hover: var(--accent-200);
63
- --focus-color: var(--accent-400);
64
- --border-hover: var(--night-400);
65
- --outline-hover-bg: var(--surface-2);
66
- --outline-active-bg: var(--surface-3);
67
- --text-on-danger: var(--night-950);
68
- --data-color: var(--accent-400); /* cifras/datos clave */
69
- --elevated-border: var(--night-600); /* borde de toasts, modales, menús */
70
- --card-border: var(--night-600);
71
- --code-color: var(--accent-300);
72
- --code-border: var(--night-700);
73
- --table-header-bg: var(--night-900);
74
- --row-border: var(--night-800);
75
- --row-hover: var(--night-700);
76
- --navbar-bg: rgba(1,2,3,.8);
77
- --nav-active: var(--accent-400);
78
- --sidebar-bg: var(--night-900);
79
- --sidebar-active-bg: var(--indigo-800);
80
- --sidebar-active-text: var(--night-50);
81
- --sidebar-active-icon: var(--accent-400);
82
- --switch-off: var(--night-600);
83
- --tooltip-bg: var(--night-700);
84
- --tooltip-border: var(--night-600);
85
- --tooltip-text: var(--night-100);
86
- --skeleton-base: var(--night-800);
87
- --skeleton-sheen: var(--night-700);
88
- --empty-icon: var(--indigo-400);
89
-
90
- /* -- Tipografía -- */
61
+ /* == Tipografía == */
91
62
  --font-display: "Space Grotesk", system-ui, sans-serif;
92
63
  --font-body: "Manrope", system-ui, sans-serif;
93
64
  --font-mono: "JetBrains Mono", monospace;
@@ -99,7 +70,7 @@
99
70
  --tracking-display: -0.02em; --tracking-overline: 0.08em;
100
71
  --measure: 65ch;
101
72
 
102
- /* -- Espaciado -- */
73
+ /* == Espaciado (escala base 4px) == */
103
74
  --space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px;
104
75
  --space-5: 20px; --space-6: 24px; --space-8: 32px; --space-10: 40px;
105
76
  --space-12: 48px; --space-16: 64px; --space-20: 80px; --space-24: 96px;
@@ -108,68 +79,148 @@
108
79
  --button-padding: 12px 20px; --touch-target: 44px;
109
80
  --grid-gutter: 24px; --grid-gutter-mobile: 16px;
110
81
 
111
- /* -- Radios y bordes -- */
82
+ /* == Radios y bordes == */
112
83
  --radius-sm: 8px; --radius-md: 12px; --radius-lg: 16px;
113
84
  --radius-xl: 24px; --radius-full: 9999px;
114
85
  --border-default: 1px solid var(--night-500);
115
- --border-emphasis: 1px solid var(--night-600);
116
- --focus-ring: 2px solid var(--accent-400);
86
+ --border-emphasis: 1px solid var(--night-400); /* ver --border-strong */
87
+ --focus-ring: 2px solid var(--focus-color);
117
88
  --focus-ring-offset: 2px;
118
89
 
119
- /* -- Elevación -- */
90
+ /* == Elevación (superficie + sombra + borde) == */
120
91
  --shadow-1: 0 1px 2px rgba(0,0,0,.4);
121
92
  --shadow-2: 0 4px 12px rgba(0,0,0,.5);
122
93
  --shadow-3: 0 12px 32px rgba(0,0,0,.6);
123
- --glow-accent: 0 0 0 1px rgba(82,186,237,.35), 0 4px 20px rgba(82,186,237,.18);
94
+ --glow-accent: 0 0 0 1px rgba(82,186,237,.35), 0 4px 20px rgba(82,186,237,.18); /* máx. uno por pantalla */
124
95
  --ring: 0 0 0 3px rgba(82,186,237,.2);
125
96
  --ring-danger: 0 0 0 3px rgba(248,113,113,.2);
126
97
 
127
- /* -- Motion -- */
98
+ /* == Motion == */
128
99
  --duration-instant: 100ms; --duration-fast: 150ms; --duration-base: 220ms;
129
100
  --duration-slow: 250ms; --duration-page: 350ms;
130
101
  --ease-out: cubic-bezier(0.2, 0, 0, 1);
131
102
  --ease-in: cubic-bezier(0.4, 0, 1, 1);
132
103
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1); /* moverse de A a B en pantalla */
133
104
  --ease-spring: cubic-bezier(0.34, 1.4, 0.64, 1);
105
+ --stagger-step: 30ms;
134
106
 
135
107
  color-scheme: dark;
136
- }
137
108
 
138
- /* ============================================================
139
- 2. VARIANTE LIGHT — <html data-theme="light">
140
- El CTA primario NO cambia (fondo #52baed, texto #010203).
141
- ============================================================ */
109
+ --focus-color: var(--accent-400);
110
+ --border-hover: var(--night-400);
111
+ --outline-hover-bg: var(--surface-2);
112
+ --outline-active-bg: var(--surface-3);
113
+ --text-on-danger: var(--night-950);
114
+ --data-color: var(--accent-400);
115
+ --elevated-border: var(--night-600);
116
+ --card-border: var(--night-600);
117
+ --code-color: var(--accent-300);
118
+ --code-border: var(--night-700);
119
+ --table-header-bg: var(--night-900);
120
+ --row-border: var(--night-800);
121
+ --row-hover: var(--night-700);
122
+ --navbar-bg: rgba(1,2,3,.8);
123
+ --nav-active: var(--accent-400);
124
+ --sidebar-bg: var(--night-900);
125
+ --sidebar-active-bg: var(--indigo-800);
126
+ --sidebar-active-text: var(--night-50);
127
+ --sidebar-active-icon: var(--accent-400);
128
+ --switch-off: var(--night-600);
129
+ --tooltip-bg: var(--night-700);
130
+ --tooltip-border: var(--night-600);
131
+ --tooltip-text: var(--night-100);
132
+ --skeleton-base: var(--night-800);
133
+ --skeleton-sheen: var(--night-700);
134
+ --empty-icon: var(--indigo-400);
135
+
136
+ --action-bg: var(--accent);
137
+ --action-hover: var(--accent-hover);
138
+ --action-active: var(--accent-active);
139
+ --text-on-action: var(--text-on-accent);
140
+ --control-border: var(--border-strong);
141
+ --placeholder: var(--text-muted);
142
+ --selection-bg: var(--action-bg);
143
+ --selection-text: var(--text-on-action);
144
+ --font-ui-heading: var(--font-body);
145
+ --text-ui-title: 2rem;
146
+ --control-height-md: 44px;
147
+ --button-padding-md: 12px 20px;
148
+ --surface: var(--surface-1);
149
+ --surface-alt: var(--surface-2);
150
+ --accent: var(--accent-400);
151
+ --accent-hover: var(--accent-300);
152
+ --accent-active: var(--accent-500);
153
+ --text-display-fluid: clamp(2.5rem, 1.5rem + 3.4vw, 5rem);
154
+ --text-on-primary-solid: var(--link);
155
+ --weight-regular: 400;
156
+ --weight-medium: 500;
157
+ --weight-semibold: 600;
158
+ --weight-bold: 700;
159
+ --leading-base: 1.6;
160
+ --leading-lg: 1.55;
161
+ --gutter: 24px;
162
+ --gutter-mobile: 16px;
163
+
164
+ /* Series categóricas: orden estable; acompañar con etiqueta o forma. */
165
+ --chart-1: #52baed;
166
+ --chart-2: #a0587c;
167
+ --chart-3: #2aa79e;
168
+ --chart-4: #aa5849;
169
+ --chart-5: #67a369;
170
+ --chart-6: #946a1c;
171
+ --chart-7: #877cc3;
172
+ /* Ritmo de contenido y formularios. */
173
+ --field-gap: 6px;
174
+ --heading-gap: var(--space-2);
175
+ --content-gap: var(--space-4);
176
+ --card-content-gap: var(--space-4);
177
+ --card-group-gap: var(--space-6);
178
+
179
+ }
180
+
181
+ /* == Modo claro (variante) — alternar data-theme en <html> == */
142
182
  [data-theme="light"] {
143
183
  --bg: #f5f8fb;
144
184
  --surface-1: #ffffff;
145
185
  --surface-2: #ffffff;
146
186
  --surface-3: #eaf1f7;
147
187
  --border: #d8dfe8;
148
- --border-strong: #b9c3d1;
188
+ /* border-strong NO se redefine en claro: night-400 (:root) cumple 1.4.11 en
189
+ AMBOS temas (3.26-3.99:1 en dark, 4.27-4.87:1 en light). #b9c3d1 daba
190
+ 1.53-1.78:1, bajo el mínimo de 3:1 para UI. */
149
191
  --text-primary: #14171f;
150
192
  --text-secondary: #3d4354;
151
- --text-muted: #5f6575; /* night-400 caía a 4.27:1 sobre --surface-3 */
193
+ --text-muted: var(--night-450); /* no night-400: ese caía a 4.27:1 sobre --surface-3 */
152
194
  --text-disabled: #9aa0b0;
153
- --text-heading: #0b0d12;
154
- --text-on-accent: #010203; /* el CTA no cambia */
155
- /* Los semánticos son texto sobre su propio --*-bg tintado: se calibran contra ese fondo. */
156
- --success: #166534; --success-bg: #e3f5ea;
157
- --warning: #854d0e; --warning-bg: #fbf3dd;
158
- --danger: #b02c2c; --danger-bg: #fbe7e7;
159
- --info: #186a94; --info-bg: #e3f2fb;
195
+ --text-heading: #14171f;
196
+ --link: #186a94; /* accent-700 · 5.95:1 sobre blanco — accent-600 solo daba 4.01:1 */
197
+ --link-hover: #12506f; /* accent-800 · en light el hover OSCURECE (en dark aclara) */
198
+ --text-on-accent: #010203; /* CTA no cambia entre modos */
199
+ /* Semánticos de light: son a la vez relleno y TEXTO sobre su propio --*-bg tintado,
200
+ así que se calibran contra ese fondo, no contra blanco. */
201
+ --success: #166534;
202
+ --warning: #854d0e;
203
+ --danger: #b02c2c;
204
+ --info: #186a94;
205
+ --success-bg: #e3f5ea; --warning-bg: #fbf3dd;
206
+ --danger-bg: #fbe7e7; --info-bg: #e3f2fb;
160
207
  --accent-ghost-hover: rgba(31,135,187,.08);
161
208
  --accent-ghost-active: rgba(31,135,187,.14);
162
209
  --accent-selected: rgba(82,186,237,.12);
163
210
  --overlay: rgba(20,23,31,.45);
211
+ --ring: 0 0 0 3px rgba(31,135,187,.25);
212
+ --shadow-1: 0 1px 3px rgba(53,61,124,.08);
213
+ --shadow-2: 0 4px 10px rgba(53,61,124,.10);
214
+ --shadow-3: 0 12px 28px rgba(53,61,124,.14);
215
+ --glow-accent: 0 0 0 1px rgba(82,186,237,.4), 0 4px 16px rgba(82,186,237,.25);
216
+ color-scheme: light;
164
217
 
165
- --link: #186a94; /* accent-700 · 5.95:1 — accent-600 solo daba 4.01:1 */
166
- --link-hover: #12506f; /* accent-800 · en light el hover oscurece */
167
- --focus-color: #1f87bb; /* accent-600 se queda: es UI (4.01 ≥ 3), no texto */
168
- --border-hover: #b9c3d1;
218
+ --focus-color: #186a94;
219
+ --border-hover: #5f6575;
169
220
  --outline-hover-bg: #eaf1f7;
170
221
  --outline-active-bg: #d8dfe8;
171
222
  --text-on-danger: #ffffff;
172
- --data-color: #186a94; /* pinta cifras y .progress__value: es TEXTO, necesita 4.5 */
223
+ --data-color: #186a94;
173
224
  --elevated-border: #d8dfe8;
174
225
  --card-border: #d8dfe8;
175
226
  --code-color: #186a94;
@@ -183,38 +234,36 @@
183
234
  --sidebar-active-bg: #e3f2fb;
184
235
  --sidebar-active-text: #14171f;
185
236
  --sidebar-active-icon: #1f87bb;
186
- --switch-off: #b9c3d1;
237
+ --switch-off: var(--control-border);
187
238
  --tooltip-bg: #14171f;
188
239
  --tooltip-border: #14171f;
189
240
  --tooltip-text: #f6f7f9;
190
241
  --skeleton-base: #eaf1f7;
191
242
  --skeleton-sheen: #f5f8fb;
192
-
193
- --ring: 0 0 0 3px rgba(31,135,187,.25);
194
243
  --ring-danger: 0 0 0 3px rgba(192,53,53,.2);
195
- --shadow-1: 0 1px 3px rgba(53,61,124,.08);
196
- --shadow-2: 0 4px 10px rgba(53,61,124,.10);
197
- --shadow-3: 0 12px 28px rgba(53,61,124,.14);
198
- --glow-accent: 0 0 0 1px rgba(82,186,237,.4), 0 4px 16px rgba(82,186,237,.25);
199
244
 
200
- color-scheme: light;
245
+ --selection-bg: var(--action-bg);
246
+ --selection-text: var(--text-on-action);
247
+
248
+ /* Series categóricas: orden estable; acompañar con etiqueta o forma. */
249
+ --chart-1: #186a94;
250
+ --chart-2: #853f63;
251
+ --chart-3: #129a92;
252
+ --chart-4: #8e4031;
253
+ --chart-5: #5b975e;
254
+ --chart-6: #8a600a;
255
+ --chart-7: #857bc1;
201
256
  }
202
257
 
203
- /* ============================================================
204
- 3. ACCESIBILIDAD DEL MOVIMIENTO
205
- ============================================================ */
258
+ /* == Accesibilidad del movimiento == */
259
+
206
260
  @media (prefers-reduced-motion: reduce) {
207
- *, *::before, *::after {
208
- animation-duration: 0.01ms !important;
209
- animation-iteration-count: 1 !important;
210
- transition-duration: 0.01ms !important;
211
- scroll-behavior: auto !important;
261
+ :root {
262
+ --duration-instant: 0ms; --duration-fast: 0ms; --duration-base: 0ms;
263
+ --duration-slow: 0ms; --duration-page: 0ms; --stagger-step: 0ms;
212
264
  }
213
265
  }
214
266
 
215
- /* ============================================================
216
- 4. BASE
217
- ============================================================ */
218
267
  *, *::before, *::after { box-sizing: border-box; }
219
268
 
220
269
  body {
@@ -245,7 +294,7 @@ p { max-width: var(--measure); margin: 0 0 var(--space-4); }
245
294
  a { color: var(--link); text-decoration: none; transition: color var(--duration-fast) var(--ease-out); }
246
295
  a:hover { color: var(--link-hover); }
247
296
 
248
- ::placeholder { color: var(--text-disabled); }
297
+ ::placeholder { color: var(--placeholder); }
249
298
  ::selection { background: rgba(82,186,237,.3); }
250
299
 
251
300
  :focus-visible {
@@ -299,12 +348,12 @@ a:hover { color: var(--link-hover); }
299
348
  /* Gating de hover (§11.5): los hover que mueven/elevan (transform, box-shadow, glow)
300
349
  van dentro de @media (hover: hover) and (pointer: fine) — en touch el tap dispara
301
350
  hovers falsos que se quedan pegados. Los hover de solo color/fondo quedan fuera. */
302
- .btn--primary { background: var(--accent-400); color: var(--text-on-accent); }
303
- .btn--primary:hover:not(:disabled) { background: var(--accent-500); }
351
+ .btn--primary { background: var(--action-bg); color: var(--text-on-action); }
352
+ .btn--primary:hover:not(:disabled) { background: var(--action-hover); }
304
353
  @media (hover: hover) and (pointer: fine) {
305
354
  .btn--primary:hover:not(:disabled) { box-shadow: var(--glow-accent); }
306
355
  }
307
- .btn--primary:active:not(:disabled) { background: var(--accent-600); box-shadow: none; }
356
+ .btn--primary:active:not(:disabled) { background: var(--action-active); box-shadow: none; }
308
357
  .btn--primary:disabled { background: var(--surface-3); color: var(--text-disabled); }
309
358
 
310
359
  /* Secundario — índigo (idéntico en ambos temas) */
@@ -390,7 +439,7 @@ a:hover { color: var(--link-hover); }
390
439
 
391
440
  .input, .textarea, .select {
392
441
  width: 100%; background: var(--surface-2);
393
- border: 1px solid var(--border); border-radius: var(--radius-sm);
442
+ border: 1px solid var(--control-border); border-radius: var(--radius-sm);
394
443
  color: var(--text-primary); font-family: var(--font-body); font-size: var(--text-base);
395
444
  transition: border-color var(--duration-fast) var(--ease-out),
396
445
  box-shadow var(--duration-fast) var(--ease-out);
@@ -416,7 +465,7 @@ a:hover { color: var(--link-hover); }
416
465
  .input-wrap .input { padding-left: 40px; }
417
466
 
418
467
  .textarea { min-height: 96px; padding: 12px 14px; resize: vertical; line-height: var(--leading-body); }
419
- .field__count { font-size: var(--text-xs); color: var(--text-disabled); text-align: right; margin-top: 4px; }
468
+ .field__count { font-size: var(--text-xs); color: var(--text-muted); text-align: right; margin-top: 4px; }
420
469
 
421
470
  /* Select nativo estilizado (el menú custom usa .menu) */
422
471
  .select-wrap { position: relative; }
@@ -466,7 +515,7 @@ a:hover { color: var(--link-hover); }
466
515
  }
467
516
  .segmented--sm label { padding: 0 12px; font-size: var(--text-xs); }
468
517
  .segmented input:checked + label {
469
- background: var(--surface-3); color: var(--accent-300);
518
+ background: var(--surface-3); color: var(--link);
470
519
  font-weight: 600; box-shadow: var(--shadow-1);
471
520
  }
472
521
  .segmented input:focus-visible + label { outline: var(--focus-ring); outline-offset: 2px; }
@@ -496,8 +545,8 @@ a:hover { color: var(--link-hover); }
496
545
  }
497
546
  .slider:active::-moz-range-thumb { transform: scale(1.15); }
498
547
  @media (prefers-reduced-motion: reduce) {
499
- .slider::-webkit-slider-thumb { transition-duration: 0.01ms !important; }
500
- .slider::-moz-range-thumb { transition-duration: 0.01ms !important; }
548
+ .slider::-webkit-slider-thumb { transition: none; }
549
+ .slider::-moz-range-thumb { transition: none; }
501
550
  }
502
551
 
503
552
  /* ============================================================
@@ -657,6 +706,32 @@ a:hover { color: var(--link-hover); }
657
706
  .stat__delta--down { color: var(--danger); }
658
707
  .stat__delta--neutral { color: var(--text-muted); }
659
708
 
709
+ /* Chip de métrica: rótulo y valor en una pastilla de una línea */
710
+ .metric-chips { display: flex; flex-wrap: wrap; gap: var(--space-2); }
711
+ .metric-chip {
712
+ display: inline-flex; align-items: center; gap: var(--space-2);
713
+ padding: var(--space-1) var(--space-3);
714
+ border: 1px solid var(--border); border-radius: var(--radius-full);
715
+ background: var(--surface-1);
716
+ font-family: var(--font-body); font-size: var(--text-sm); line-height: var(--leading-snug);
717
+ color: var(--text-secondary); white-space: nowrap;
718
+ }
719
+ .metric-chip svg { width: 16px; height: 16px; flex: none; color: var(--text-muted); }
720
+ .metric-chip__value {
721
+ font-family: var(--font-mono); font-weight: 500; color: var(--link);
722
+ font-variant-numeric: tabular-nums;
723
+ }
724
+ button.metric-chip {
725
+ min-height: var(--control-height-md); padding-inline: var(--space-4);
726
+ cursor: pointer; text-align: left;
727
+ transition: background-color var(--duration-fast) var(--ease-out),
728
+ border-color var(--duration-fast) var(--ease-out),
729
+ transform var(--duration-instant) var(--ease-out);
730
+ }
731
+ button.metric-chip:hover { background: var(--outline-hover-bg); border-color: var(--border-hover); }
732
+ button.metric-chip:active { background: var(--outline-active-bg); transform: scale(.98); }
733
+ button.metric-chip:focus-visible { outline: var(--focus-ring); outline-offset: var(--focus-ring-offset); }
734
+
660
735
  /* ============================================================
661
736
  13B. PAGINATION — .pagination (activa: fondo acento + peso, no solo color)
662
737
  ============================================================ */
@@ -739,7 +814,7 @@ a:hover { color: var(--link-hover); }
739
814
  }
740
815
  .alert > svg { width: 20px; height: 20px; flex: none; margin-top: 1px; }
741
816
  .alert__title { font-weight: 600; margin: 0 0 2px; color: inherit; }
742
- .alert__body { margin: 0; color: inherit; opacity: .88; }
817
+ .alert__body { margin: 0; color: inherit; }
743
818
  .alert__close {
744
819
  margin-left: auto; flex: none; display: grid; place-items: center;
745
820
  width: 36px; height: 36px; border: 0; border-radius: var(--radius-sm);
@@ -901,6 +976,70 @@ a:hover { color: var(--link-hover); }
901
976
  .sidebar__item svg { width: 18px; height: 18px; color: var(--text-muted); flex: none; }
902
977
  .sidebar__item.is-active svg { color: var(--sidebar-active-icon); }
903
978
 
979
+ /* Grupos con rótulo dentro del sidebar */
980
+ .sidebar__group { display: flex; flex-direction: column; gap: 2px; }
981
+ .sidebar__group + .sidebar__group { margin-top: var(--space-4); }
982
+ .sidebar__group-label {
983
+ padding: 0 12px var(--space-1);
984
+ font-size: var(--text-xs); font-weight: 500; text-transform: uppercase;
985
+ letter-spacing: var(--tracking-overline); color: var(--text-muted); white-space: nowrap;
986
+ }
987
+ .sidebar__label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
988
+
989
+ /* Sidebar contraíble: expandido 260 px, riel de iconos de 64 px.
990
+ El ancho cambia sin transición; solo los rótulos se funden con opacity y transform. */
991
+ .sidebar--collapsible .sidebar__label {
992
+ transition: opacity var(--duration-fast) var(--ease-out),
993
+ transform var(--duration-fast) var(--ease-out);
994
+ }
995
+ .sidebar__toggle {
996
+ margin-top: auto; width: 100%; border: 0; background: transparent;
997
+ font-family: var(--font-body); text-align: left; cursor: pointer;
998
+ }
999
+ .sidebar.is-collapsed { width: 64px; }
1000
+ .sidebar.is-collapsed .sidebar__item { position: relative; justify-content: center; padding-inline: 0; }
1001
+ .sidebar.is-collapsed .sidebar__toggle svg { transform: rotate(180deg); }
1002
+ .sidebar.is-collapsed .sidebar__group + .sidebar__group { margin-top: var(--space-2); }
1003
+ /* En el riel, el rótulo de grupo es un separador de 1 px; su texto sigue en el árbol de accesibilidad */
1004
+ .sidebar.is-collapsed .sidebar__group-label {
1005
+ height: 1px; padding: 0; margin: 0 var(--space-2) var(--space-2);
1006
+ overflow: hidden; color: transparent; background: var(--border);
1007
+ }
1008
+ /* Oculto a la vista pero accesible (patrón de .visually-hidden) */
1009
+ .sidebar.is-collapsed .sidebar__label,
1010
+ .sidebar.is-collapsed .sidebar__group:first-child > .sidebar__group-label {
1011
+ position: absolute; width: 1px; height: 1px; margin: -1px;
1012
+ clip-path: inset(50%); overflow: hidden; white-space: nowrap;
1013
+ }
1014
+ .sidebar.is-collapsed .sidebar__label {
1015
+ background: var(--tooltip-bg); border: 1px solid var(--tooltip-border); border-radius: var(--radius-sm);
1016
+ color: var(--tooltip-text); font-size: var(--text-xs); line-height: 16px;
1017
+ pointer-events: none; z-index: 70;
1018
+ opacity: 0; transform: translateX(-4px);
1019
+ }
1020
+ /* Tooltip del riel: el propio rótulo aparece a la derecha del ítem, también con teclado */
1021
+ .sidebar.is-collapsed .sidebar__item:focus-visible .sidebar__label {
1022
+ left: calc(100% + 8px); top: calc(50% - 15px); width: auto; height: auto;
1023
+ margin: 0; padding: 6px 10px; clip-path: none; overflow: visible;
1024
+ opacity: 1; transform: none; transition-delay: 400ms;
1025
+ }
1026
+ /* Por hover solo con puntero real — en touch quedaría pegado tras el tap */
1027
+ @media (hover: hover) and (pointer: fine) {
1028
+ .sidebar.is-collapsed .sidebar__item:hover .sidebar__label {
1029
+ left: calc(100% + 8px); top: calc(50% - 15px); width: auto; height: auto;
1030
+ margin: 0; padding: 6px 10px; clip-path: none; overflow: visible;
1031
+ opacity: 1; transform: none; transition-delay: 400ms;
1032
+ }
1033
+ }
1034
+ /* Por debajo de 768 px la navegación vive en un Drawer --left, que abre el botón ☰
1035
+ .sidebar-trigger (combinado con .icon-btn); desde 768 px el sidebar es fijo y el botón sobra */
1036
+ @media (max-width: 767px) {
1037
+ .sidebar--collapsible { display: none; }
1038
+ }
1039
+ @media (min-width: 768px) {
1040
+ .sidebar-trigger { display: none; }
1041
+ }
1042
+
904
1043
  .tabs { position: relative; display: flex; gap: var(--space-2); border-bottom: 1px solid var(--border-strong); }
905
1044
  .tabs__btn {
906
1045
  padding: 10px 14px; border: 0; background: transparent; cursor: pointer;
@@ -982,7 +1121,7 @@ a:hover { color: var(--link-hover); }
982
1121
  font-weight: 600; color: var(--data-color); font-variant-numeric: tabular-nums;
983
1122
  }
984
1123
  @media (prefers-reduced-motion: reduce) {
985
- .progress-ring__bar { transition-duration: 0.01ms !important; }
1124
+ .progress-ring__bar { transition: none; }
986
1125
  }
987
1126
 
988
1127
  /* ============================================================
@@ -1149,13 +1288,13 @@ a:hover { color: var(--link-hover); }
1149
1288
  transition: opacity var(--duration-base) var(--ease-out),
1150
1289
  transform var(--duration-base) var(--ease-out),
1151
1290
  visibility 0s linear var(--duration-base);
1152
- }
1291
+ display: none; }
1153
1292
  .popover.is-open .popover__panel {
1154
1293
  opacity: 1; transform: none; visibility: visible;
1155
1294
  transition: opacity var(--duration-base) var(--ease-out),
1156
1295
  transform var(--duration-base) var(--ease-out),
1157
1296
  visibility 0s;
1158
- }
1297
+ display: block; }
1159
1298
  /* Alineado a la derecha del disparador */
1160
1299
  .popover--right .popover__panel { left: auto; right: 0; transform-origin: top right; }
1161
1300
 
@@ -1208,7 +1347,7 @@ a:hover { color: var(--link-hover); }
1208
1347
  .calendar__day--today { border-color: var(--accent-400); color: var(--accent-300); }
1209
1348
  /* Seleccionado: fondo de acento + texto oscuro + peso — no depende solo del color */
1210
1349
  .calendar__day--selected { background: var(--accent-400); border-color: var(--accent-400); color: var(--text-on-accent); font-weight: 600; }
1211
- .calendar__day--outside { color: var(--text-disabled); }
1350
+ .calendar__day--outside { color: var(--text-muted); }
1212
1351
  .calendar__day[disabled] { color: var(--text-disabled); opacity: .5; cursor: not-allowed; }
1213
1352
 
1214
1353
  /* ============================================================
@@ -1227,7 +1366,7 @@ a:hover { color: var(--link-hover); }
1227
1366
  }
1228
1367
  .file-upload__label > svg { width: 28px; height: 28px; color: var(--text-muted); }
1229
1368
  .file-upload__button { color: var(--link); font-weight: 600; }
1230
- .file-upload__hint { font-size: var(--text-xs); color: var(--text-disabled); }
1369
+ .file-upload__hint { font-size: var(--text-xs); color: var(--text-muted); }
1231
1370
  @media (hover: hover) and (pointer: fine) {
1232
1371
  .file-upload__label:hover { border-color: var(--border-hover); background: var(--surface-2); color: var(--text-secondary); }
1233
1372
  }
@@ -1309,3 +1448,168 @@ a:hover { color: var(--link-hover); }
1309
1448
  color: var(--text-secondary); background: var(--surface-2);
1310
1449
  border: 1px solid var(--border-strong); border-radius: 6px; box-shadow: var(--shadow-1);
1311
1450
  }
1451
+
1452
+ /* CONTRATOS COMPARTIDOS GENERADOS */
1453
+ /* Contratos comunes de interacción; la identidad y los valores viven en tokens.css. */
1454
+ .grid { grid-template-columns: repeat(12, minmax(0, 1fr)); }
1455
+ .grid > *, .field, .card { min-inline-size: 0; }
1456
+ .stack, .card__content, .form-stack, .text-group { display: flex; flex-direction: column; align-items: stretch; }
1457
+ .stack, .card__content { gap: var(--content-gap); }
1458
+ .stack--tight, .text-group { gap: var(--heading-gap); }
1459
+ .stack--cards { gap: var(--card-group-gap); }
1460
+ .form-stack { gap: var(--form-gap); }
1461
+ .cluster { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); }
1462
+ .card { display: flex; flex-direction: column; gap: var(--card-content-gap); }
1463
+ .field { gap: var(--field-gap); }
1464
+ .field__label, .field__help, .field__error, .field > :is(label, p, span, div) { margin-block: 0; }
1465
+ :where(.field + .field, .field + .btn, .field + .cluster) { margin-block-start: var(--form-gap); }
1466
+ :where(.card + .card) { margin-block-start: var(--card-group-gap); }
1467
+ .stack > *, .card__content > *, .form-stack > *, .text-group > *, .grid > .card,
1468
+ .card > :is(h1, h2, h3, h4, h5, h6, p), .text-group > :is(h1, h2, h3, h4, h5, h6, p),
1469
+ .stack > :is(h1, h2, h3, h4, h5, h6, p) { margin-block: 0; }
1470
+ .form-stack > .field { margin-block: 0; }
1471
+ .btn {
1472
+ block-size: auto;
1473
+ min-block-size: var(--control-height-md);
1474
+ max-inline-size: 100%;
1475
+ white-space: normal;
1476
+ line-height: 1.35;
1477
+ text-align: center;
1478
+ }
1479
+ .btn--sm { min-block-size: var(--control-height-sm, 36px); }
1480
+ .btn--lg { min-block-size: var(--control-height-lg, 52px); }
1481
+ .btn--primary, .icon-btn--primary, .calendar__day--selected, .pagination__btn.is-active {
1482
+ background: var(--action-bg);
1483
+ color: var(--text-on-action);
1484
+ }
1485
+ .btn--primary:hover:not(:disabled), .icon-btn--primary:hover:not(:disabled) { background: var(--action-hover); color: var(--text-on-action); }
1486
+ .btn--primary:active:not(:disabled), .icon-btn--primary:active:not(:disabled) { background: var(--action-active); color: var(--text-on-action); }
1487
+ .input, .textarea, .select, .combobox__input, .search__input, .datepicker__input {
1488
+ max-inline-size: 100%;
1489
+ caret-color: var(--focus-color);
1490
+ }
1491
+ .input, .select, .combobox__input, .search__input, .datepicker__input {
1492
+ block-size: auto; min-block-size: var(--control-height-md);
1493
+ padding-block: var(--space-2); line-height: 1.5;
1494
+ }
1495
+ :is(.input, .textarea, .select, .combobox__input, .search__input, .datepicker__input):not(.input--error, .input--success, .textarea--error, [aria-invalid="true"]) { border-color: var(--control-border); }
1496
+ .input:hover:not(:disabled):not(:focus):not(.input--error, .input--success, [aria-invalid="true"]), .textarea:hover:not(:disabled):not(:focus):not(.textarea--error, [aria-invalid="true"]),
1497
+ .select:hover:not(:disabled):not(:focus), .combobox__input:hover:not(:disabled):not(:focus),
1498
+ .search__input:hover:not(:focus), .datepicker__input:hover:not(:disabled):not(:focus) { border-color: var(--border-hover); }
1499
+ .input:focus-visible, .textarea:focus-visible, .select:focus-visible,
1500
+ .combobox__input:focus-visible, .search__input:focus-visible, .datepicker__input:focus-visible {
1501
+ outline: 2px solid var(--focus-color);
1502
+ outline-offset: 2px;
1503
+ }
1504
+ .input[aria-invalid="true"], .textarea[aria-invalid="true"] { border-color: var(--danger-text, var(--danger)); }
1505
+ .input.input--error, .textarea.textarea--error,
1506
+ .input.input--error:hover:not(:disabled):not(:focus), .textarea.textarea--error:hover:not(:disabled):not(:focus),
1507
+ :is(.input, .textarea, .select, .combobox__input, .search__input, .datepicker__input)[aria-invalid="true"]:hover:not(:disabled):not(:focus) {
1508
+ border-color: var(--danger-text, var(--danger));
1509
+ }
1510
+ .input.input--success, .input.input--success:hover:not(:disabled):not(:focus) { border-color: var(--success-text, var(--success)); }
1511
+ .pagination { max-inline-size: 100%; flex-wrap: wrap; row-gap: var(--space-2); }
1512
+ .pagination__ellipsis { color: var(--text-muted); }
1513
+ .step--current .step__marker { color: var(--link); border-color: var(--focus-color); }
1514
+ .checkbox input:focus-visible ~ .checkbox__box,
1515
+ .radio input:focus-visible ~ .radio__dot,
1516
+ .switch input:focus-visible ~ .switch__track {
1517
+ outline: 2px solid var(--focus-color);
1518
+ outline-offset: 2px;
1519
+ }
1520
+ input.checkbox, input.radio { border-color: var(--control-border); }
1521
+ input.checkbox:hover, input.radio:hover { border-color: var(--focus-color); }
1522
+ input.checkbox:checked, input.checkbox:indeterminate, input.radio:checked { border-color: var(--focus-color); }
1523
+ .switch__track { background: var(--control-border); }
1524
+ .tabs { max-inline-size: 100%; overflow-x: auto; }
1525
+ .tabs__tab { flex-shrink: 0; }
1526
+ .segmented { max-inline-size: 100%; block-size: auto; min-block-size: var(--control-height-md); overflow-x: auto; }
1527
+ .segmented label { padding-block: var(--space-2); }
1528
+ .stat__row, .breadcrumbs, .check-row { flex-wrap: wrap; row-gap: var(--space-2); }
1529
+ .progress-row, .slider-row { flex-wrap: wrap; gap: var(--space-2); }
1530
+ .stat__value { max-inline-size: 100%; white-space: normal; overflow-wrap: anywhere; }
1531
+ .file-upload, .file-upload__label { min-inline-size: 0; max-inline-size: 100%; }
1532
+ .file-upload__button { max-inline-size: 100%; white-space: normal; text-align: center; }
1533
+ .file-upload__hint { overflow-wrap: anywhere; }
1534
+ .table-scroll { max-inline-size: 100%; overflow-x: auto; overscroll-behavior-inline: contain; }
1535
+ .popover__panel, .datepicker__calendar, .combobox__list {
1536
+ max-inline-size: calc(100vw - 2rem);
1537
+ max-block-size: min(32rem, 70dvh);
1538
+ overflow: auto;
1539
+ }
1540
+ .file-upload__input:focus-visible + .file-upload__label { outline: 2px solid var(--focus-color); outline-offset: 2px; }
1541
+ .calendar { max-inline-size: calc(100vw - 2rem); }
1542
+ .ui-title, :is(h1, h2, h3, h4, h5, h6).ui-title, [data-mode="operate"] :is(h1, h2, h3) {
1543
+ font-family: var(--font-ui-heading);
1544
+ letter-spacing: 0;
1545
+ line-height: 1.3;
1546
+ font-weight: var(--weight-semibold);
1547
+ }
1548
+ .ui-title, :is(h1, h2, h3, h4, h5, h6).ui-title, [data-mode="operate"] h1 { font-size: var(--text-ui-title); }
1549
+ [data-mode="operate"] h2 { font-size: var(--text-xl); }
1550
+ [data-mode="operate"] h3 { font-size: var(--text-lg); }
1551
+ .display, .reading-title, :is(h1, h2, h3, h4, h5, h6):is(.display, .reading-title) {
1552
+ font-family: var(--font-display);
1553
+ font-size: var(--text-display-fluid);
1554
+ line-height: 1.08;
1555
+ letter-spacing: var(--tracking-display);
1556
+ text-wrap: balance;
1557
+ }
1558
+ :where(.celeste-reset, .salvia-reset, .bruma-reset, .nocturna-reset, body[data-system]) {
1559
+ scrollbar-color: var(--control-border) var(--bg);
1560
+ }
1561
+ ::selection,
1562
+ .celeste-reset ::selection, .salvia-reset ::selection, .bruma-reset ::selection {
1563
+ background: var(--selection-bg); color: var(--selection-text);
1564
+ }
1565
+ ::placeholder { color: var(--placeholder); opacity: 1; }
1566
+ :where(a) { text-underline-offset: .2em; text-decoration-thickness: .08em; }
1567
+ .skip-link {
1568
+ position: fixed; inset-block-start: var(--space-2); inset-inline-start: var(--space-2);
1569
+ z-index: 1000; padding: var(--space-3) var(--space-4);
1570
+ background: var(--action-bg); color: var(--text-on-action);
1571
+ border-radius: var(--radius-sm); transform: translateY(-180%);
1572
+ }
1573
+ .skip-link:focus { transform: none; outline: 2px solid var(--focus-color); outline-offset: 2px; }
1574
+ [data-tooltip]::before, [data-tooltip]::after { display: none; }
1575
+ [data-tooltip]:focus-visible::before, [data-tooltip]:focus-visible::after { display: block; }
1576
+ .has-tooltip .tooltip { display: none; }
1577
+ .has-tooltip:has(:focus-visible) .tooltip { display: block; opacity: 1; transform: translate(-50%, 0); }
1578
+ @media (hover: hover) and (pointer: fine) {
1579
+ [data-tooltip]:hover::before, [data-tooltip]:hover::after { display: block; }
1580
+ .has-tooltip:hover .tooltip { display: block; }
1581
+ }
1582
+ @media (pointer: coarse) {
1583
+ .btn, .icon-btn, .segmented label, .pagination button,
1584
+ .tag__close, .search__clear, .toast__close, .check-row,
1585
+ .checkbox:not(input), .radio:not(input), .switch:not(input) {
1586
+ min-block-size: var(--touch-target);
1587
+ min-inline-size: var(--touch-target);
1588
+ }
1589
+ }
1590
+ @media (max-width: 479px) {
1591
+ .btn-group { flex-wrap: wrap; }
1592
+ .modal { max-inline-size: calc(100vw - 2rem); }
1593
+ .stepper { flex-direction: column; align-items: stretch; gap: var(--space-4); }
1594
+ .step { flex-direction: row; align-items: center; gap: var(--space-3); text-align: start; min-inline-size: 0; }
1595
+ .step__label { overflow-wrap: anywhere; }
1596
+ .step__connector { display: none; }
1597
+ }
1598
+ @media (prefers-reduced-motion: reduce) {
1599
+ html, html:focus-within { scroll-behavior: auto; }
1600
+ .spinner, .skeleton, .skeleton::after,
1601
+ .progress--indeterminate .progress__bar, .alert.is-leaving, .toast.is-leaving { animation: none; }
1602
+ .progress--indeterminate .progress__bar { transform: none; }
1603
+ .btn, .icon-btn, .card--interactive, .slider::-webkit-slider-thumb { transform: none; }
1604
+ .btn:active:not(:disabled), .icon-btn:active:not(:disabled), .card--interactive:hover { transform: none; }
1605
+ .btn, .icon-btn { transition: background-color 80ms linear, color 80ms linear; }
1606
+ .menu, .popover__panel, .datepicker__calendar, .link::after { transition: none; }
1607
+ .progress__bar, .progress-ring__value, .accordion__content, .accordion__body { transition: none; }
1608
+ }
1609
+ @media (forced-colors: active) {
1610
+ input.switch, .switch__track { border: 1px solid CanvasText; background: Canvas; }
1611
+ input.switch::after, .switch__track::after { background: CanvasText; }
1612
+ input.switch:checked, .switch input:checked ~ .switch__track { border-color: Highlight; }
1613
+ input.switch:checked::after, .switch input:checked ~ .switch__track::after { background: Highlight; }
1614
+ .checkbox__box, .radio__dot { border-color: CanvasText; }
1615
+ }