@higinio/design-mcp 0.2.0 → 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.
@@ -8,44 +8,42 @@
8
8
  <link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' rx='8' fill='%23010203'/%3E%3Ctext x='16' y='22' font-family='system-ui,sans-serif' font-size='18' font-weight='700' fill='%2352baed' text-anchor='middle'%3Eh%3C/text%3E%3C/svg%3E">
9
9
  <script>(function(){var t=null;try{t=localStorage.getItem('ne-theme');}catch(e){}if(t==='light'||t==='dark')document.documentElement.dataset.theme=t;})();</script>
10
10
  <style>
11
- /* ============================================================================
12
- NOCTURNA ELÉCTRICA — Framework CSS v2.0
13
- Sistema de diseño dark-first de Higinio Sosa.
11
+ /* ============================================================
12
+ Nocturna Eléctrica — Design Tokens v2.0
13
+ Sistema de diseño de Higinio Sosa · dark-first
14
14
  Fuente de verdad: DESIGN-nocturna-electrica.md
15
- Uso: enlazar este archivo antes de cualquier estilo propio.
16
- Tema: dark por defecto; <html data-theme="light"> activa la variante clara.
17
- Los componentes consumen SOLO aliases semánticos — nunca hex directos.
18
- ========================================================================== */
15
+ Uso: importar ANTES de cualquier estilo propio.
16
+ Los componentes consumen SOLO aliases semánticos, nunca hex.
17
+ ============================================================ */
19
18
 
20
19
  @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');
21
20
 
22
- /* ============================================================
23
- 1. TOKENS — primitivos + aliases (dark por defecto)
24
- ============================================================ */
25
21
  :root {
26
- /* -- Primitivos: acento -- */
22
+ /* == Primitivos: acento (Celeste Eléctrico) ==
23
+ accent-600 sobre blanco da 4.01:1 — vale como UI (≥3) pero NO como texto (<4.5).
24
+ Para texto sobre claro se usa accent-700 (5.95:1) y accent-800 (8.74:1). */
27
25
  --accent-100: #d9f0fb; --accent-200: #a8ddf6; --accent-300: #7dccf1;
28
26
  --accent-400: #52baed; --accent-500: #2fa3dd; --accent-600: #1f87bb; --accent-700: #186a94;
29
27
  --accent-800: #12506f;
30
28
 
31
- /* -- Primitivos: índigo -- */
29
+ /* == Primitivos: índigo (Índigo Profundo) — nunca texto sobre negro == */
32
30
  --indigo-300: #7a83c4; --indigo-400: #565fa3; --indigo-500: #353d7c;
33
31
  --indigo-600: #2b3264; --indigo-700: #22284f; --indigo-800: #1a1e3b;
34
32
 
35
- /* -- Primitivos: escala nocturna -- */
33
+ /* == Primitivos: escala nocturna == */
36
34
  --night-950: #010203; --night-900: #0b0d12; --night-800: #14171f;
37
35
  --night-700: #1e222d; --night-600: #2a2f3d; --night-500: #3d4354;
38
- --night-450: #5f6575; /* texto atenuado en light */
36
+ --night-450: #5f6575; /* texto atenuado en light: night-400 caía a 4.27:1 sobre --surface-3 */
39
37
  --night-400: #6b7183; --night-300: #9aa0b0; --night-200: #c5c9d4;
40
38
  --night-100: #e8eaef; --night-50: #f6f7f9;
41
39
 
42
- /* -- Primitivos: semánticos -- */
40
+ /* == Primitivos: semánticos (calibrados para dark) == */
43
41
  --success-400: #4ade80; --success-bg: #0a2617;
44
42
  --warning-400: #fbbf24; --warning-bg: #2a2005;
45
43
  --danger-400: #f87171; --danger-bg: #2c0f0f;
46
44
  --info-400: #52baed; --info-bg: #0d1f2c;
47
45
 
48
- /* -- Aliases semánticos (los componentes consumen SOLO esto) -- */
46
+ /* == Aliases semánticos (los componentes consumen SOLO esto) == */
49
47
  --bg: var(--night-950);
50
48
  --surface-1: var(--night-900);
51
49
  --surface-2: var(--night-800);
@@ -60,47 +58,17 @@
60
58
  --text-muted: var(--night-300);
61
59
  --text-disabled: var(--night-400);
62
60
  --text-heading: var(--night-50);
63
- --text-on-accent: var(--night-950);
64
- --success: var(--success-400);
65
- --warning: var(--warning-400);
66
- --danger: var(--danger-400);
67
- --info: var(--info-400);
61
+ --text-on-accent: var(--night-950); /* texto de CTA oscuro, nunca blanco */
62
+ --success: var(--success-400); --warning: var(--warning-400);
63
+ --danger: var(--danger-400); --info: var(--info-400);
64
+ --link: var(--accent-400);
65
+ --link-hover: var(--accent-200);
68
66
  --accent-ghost-hover: rgba(82,186,237,.08);
69
67
  --accent-ghost-active: rgba(82,186,237,.14);
70
68
  --accent-selected: rgba(82,186,237,.12);
71
69
  --overlay: rgba(1,2,3,.7);
72
70
 
73
- /* -- Aliases de componentes (derivados; cambian con el tema) -- */
74
- --link: var(--accent-400);
75
- --link-hover: var(--accent-200);
76
- --focus-color: var(--accent-400);
77
- --border-hover: var(--night-400);
78
- --outline-hover-bg: var(--surface-2);
79
- --outline-active-bg: var(--surface-3);
80
- --text-on-danger: var(--night-950);
81
- --data-color: var(--accent-400); /* cifras/datos clave */
82
- --elevated-border: var(--night-600); /* borde de toasts, modales, menús */
83
- --card-border: var(--night-600);
84
- --code-color: var(--accent-300);
85
- --code-border: var(--night-700);
86
- --table-header-bg: var(--night-900);
87
- --row-border: var(--night-800);
88
- --row-hover: var(--night-700);
89
- --navbar-bg: rgba(1,2,3,.8);
90
- --nav-active: var(--accent-400);
91
- --sidebar-bg: var(--night-900);
92
- --sidebar-active-bg: var(--indigo-800);
93
- --sidebar-active-text: var(--night-50);
94
- --sidebar-active-icon: var(--accent-400);
95
- --switch-off: var(--night-600);
96
- --tooltip-bg: var(--night-700);
97
- --tooltip-border: var(--night-600);
98
- --tooltip-text: var(--night-100);
99
- --skeleton-base: var(--night-800);
100
- --skeleton-sheen: var(--night-700);
101
- --empty-icon: var(--indigo-400);
102
-
103
- /* -- Tipografía -- */
71
+ /* == Tipografía == */
104
72
  --font-display: "Space Grotesk", system-ui, sans-serif;
105
73
  --font-body: "Manrope", system-ui, sans-serif;
106
74
  --font-mono: "JetBrains Mono", monospace;
@@ -112,7 +80,7 @@
112
80
  --tracking-display: -0.02em; --tracking-overline: 0.08em;
113
81
  --measure: 65ch;
114
82
 
115
- /* -- Espaciado -- */
83
+ /* == Espaciado (escala base 4px) == */
116
84
  --space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px;
117
85
  --space-5: 20px; --space-6: 24px; --space-8: 32px; --space-10: 40px;
118
86
  --space-12: 48px; --space-16: 64px; --space-20: 80px; --space-24: 96px;
@@ -121,37 +89,106 @@
121
89
  --button-padding: 12px 20px; --touch-target: 44px;
122
90
  --grid-gutter: 24px; --grid-gutter-mobile: 16px;
123
91
 
124
- /* -- Radios y bordes -- */
92
+ /* == Radios y bordes == */
125
93
  --radius-sm: 8px; --radius-md: 12px; --radius-lg: 16px;
126
94
  --radius-xl: 24px; --radius-full: 9999px;
127
95
  --border-default: 1px solid var(--night-500);
128
96
  --border-emphasis: 1px solid var(--night-400); /* ver --border-strong */
129
- --focus-ring: 2px solid var(--accent-400);
97
+ --focus-ring: 2px solid var(--focus-color);
130
98
  --focus-ring-offset: 2px;
131
99
 
132
- /* -- Elevación -- */
100
+ /* == Elevación (superficie + sombra + borde) == */
133
101
  --shadow-1: 0 1px 2px rgba(0,0,0,.4);
134
102
  --shadow-2: 0 4px 12px rgba(0,0,0,.5);
135
103
  --shadow-3: 0 12px 32px rgba(0,0,0,.6);
136
- --glow-accent: 0 0 0 1px rgba(82,186,237,.35), 0 4px 20px rgba(82,186,237,.18);
104
+ --glow-accent: 0 0 0 1px rgba(82,186,237,.35), 0 4px 20px rgba(82,186,237,.18); /* máx. uno por pantalla */
137
105
  --ring: 0 0 0 3px rgba(82,186,237,.2);
138
106
  --ring-danger: 0 0 0 3px rgba(248,113,113,.2);
139
107
 
140
- /* -- Motion -- */
108
+ /* == Motion == */
141
109
  --duration-instant: 100ms; --duration-fast: 150ms; --duration-base: 220ms;
142
110
  --duration-slow: 250ms; --duration-page: 350ms;
143
111
  --ease-out: cubic-bezier(0.2, 0, 0, 1);
144
112
  --ease-in: cubic-bezier(0.4, 0, 1, 1);
145
113
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1); /* moverse de A a B en pantalla */
146
114
  --ease-spring: cubic-bezier(0.34, 1.4, 0.64, 1);
115
+ --stagger-step: 30ms;
147
116
 
148
117
  color-scheme: dark;
149
- }
150
118
 
151
- /* ============================================================
152
- 2. VARIANTE LIGHT — <html data-theme="light">
153
- El CTA primario NO cambia (fondo #52baed, texto #010203).
154
- ============================================================ */
119
+ --focus-color: var(--accent-400);
120
+ --border-hover: var(--night-400);
121
+ --outline-hover-bg: var(--surface-2);
122
+ --outline-active-bg: var(--surface-3);
123
+ --text-on-danger: var(--night-950);
124
+ --data-color: var(--accent-400);
125
+ --elevated-border: var(--night-600);
126
+ --card-border: var(--night-600);
127
+ --code-color: var(--accent-300);
128
+ --code-border: var(--night-700);
129
+ --table-header-bg: var(--night-900);
130
+ --row-border: var(--night-800);
131
+ --row-hover: var(--night-700);
132
+ --navbar-bg: rgba(1,2,3,.8);
133
+ --nav-active: var(--accent-400);
134
+ --sidebar-bg: var(--night-900);
135
+ --sidebar-active-bg: var(--indigo-800);
136
+ --sidebar-active-text: var(--night-50);
137
+ --sidebar-active-icon: var(--accent-400);
138
+ --switch-off: var(--night-600);
139
+ --tooltip-bg: var(--night-700);
140
+ --tooltip-border: var(--night-600);
141
+ --tooltip-text: var(--night-100);
142
+ --skeleton-base: var(--night-800);
143
+ --skeleton-sheen: var(--night-700);
144
+ --empty-icon: var(--indigo-400);
145
+
146
+ --action-bg: var(--accent);
147
+ --action-hover: var(--accent-hover);
148
+ --action-active: var(--accent-active);
149
+ --text-on-action: var(--text-on-accent);
150
+ --control-border: var(--border-strong);
151
+ --placeholder: var(--text-muted);
152
+ --selection-bg: var(--action-bg);
153
+ --selection-text: var(--text-on-action);
154
+ --font-ui-heading: var(--font-body);
155
+ --text-ui-title: 2rem;
156
+ --control-height-md: 44px;
157
+ --button-padding-md: 12px 20px;
158
+ --surface: var(--surface-1);
159
+ --surface-alt: var(--surface-2);
160
+ --accent: var(--accent-400);
161
+ --accent-hover: var(--accent-300);
162
+ --accent-active: var(--accent-500);
163
+ --text-display-fluid: clamp(2.5rem, 1.5rem + 3.4vw, 5rem);
164
+ --text-on-primary-solid: var(--link);
165
+ --weight-regular: 400;
166
+ --weight-medium: 500;
167
+ --weight-semibold: 600;
168
+ --weight-bold: 700;
169
+ --leading-base: 1.6;
170
+ --leading-lg: 1.55;
171
+ --gutter: 24px;
172
+ --gutter-mobile: 16px;
173
+
174
+ /* Series categóricas: orden estable; acompañar con etiqueta o forma. */
175
+ --chart-1: #52baed;
176
+ --chart-2: #a0587c;
177
+ --chart-3: #2aa79e;
178
+ --chart-4: #aa5849;
179
+ --chart-5: #67a369;
180
+ --chart-6: #946a1c;
181
+ --chart-7: #877cc3;
182
+ /* Ritmo de contenido y formularios. */
183
+ --field-gap: 6px;
184
+ --heading-gap: var(--space-2);
185
+ --content-gap: var(--space-4);
186
+ --card-content-gap: var(--space-4);
187
+ --card-group-gap: var(--space-6);
188
+
189
+ }
190
+
191
+ /* == Modo claro (variante) — alternar data-theme en <html> == */
155
192
  [data-theme="light"] {
156
193
  --bg: #f5f8fb;
157
194
  --surface-1: #ffffff;
@@ -163,27 +200,37 @@
163
200
  1.53-1.78:1, bajo el mínimo de 3:1 para UI. */
164
201
  --text-primary: #14171f;
165
202
  --text-secondary: #3d4354;
166
- --text-muted: #5f6575;
203
+ --text-muted: var(--night-450); /* no night-400: ese caía a 4.27:1 sobre --surface-3 */
167
204
  --text-disabled: #9aa0b0;
168
- --text-heading: #0b0d12;
169
- --text-on-accent: #010203; /* el CTA no cambia */
170
- --success: #166534; --success-bg: #e3f5ea;
171
- --warning: #854d0e; --warning-bg: #fbf3dd;
172
- --danger: #b02c2c; --danger-bg: #fbe7e7;
173
- --info: #186a94; --info-bg: #e3f2fb;
205
+ --text-heading: #14171f;
206
+ --link: #186a94; /* accent-700 · 5.95:1 sobre blanco — accent-600 solo daba 4.01:1 */
207
+ --link-hover: #12506f; /* accent-800 · en light el hover OSCURECE (en dark aclara) */
208
+ --text-on-accent: #010203; /* CTA no cambia entre modos */
209
+ /* Semánticos de light: son a la vez relleno y TEXTO sobre su propio --*-bg tintado,
210
+ así que se calibran contra ese fondo, no contra blanco. */
211
+ --success: #166534;
212
+ --warning: #854d0e;
213
+ --danger: #b02c2c;
214
+ --info: #186a94;
215
+ --success-bg: #e3f5ea; --warning-bg: #fbf3dd;
216
+ --danger-bg: #fbe7e7; --info-bg: #e3f2fb;
174
217
  --accent-ghost-hover: rgba(31,135,187,.08);
175
218
  --accent-ghost-active: rgba(31,135,187,.14);
176
219
  --accent-selected: rgba(82,186,237,.12);
177
220
  --overlay: rgba(20,23,31,.45);
221
+ --ring: 0 0 0 3px rgba(31,135,187,.25);
222
+ --shadow-1: 0 1px 3px rgba(53,61,124,.08);
223
+ --shadow-2: 0 4px 10px rgba(53,61,124,.10);
224
+ --shadow-3: 0 12px 28px rgba(53,61,124,.14);
225
+ --glow-accent: 0 0 0 1px rgba(82,186,237,.4), 0 4px 16px rgba(82,186,237,.25);
226
+ color-scheme: light;
178
227
 
179
- --link: #186a94;
180
- --link-hover: #12506f;
181
- --focus-color: #1f87bb; /* UI (4.01:1 ≥ 3), no texto */
182
- --border-hover: #b9c3d1;
228
+ --focus-color: #186a94;
229
+ --border-hover: #5f6575;
183
230
  --outline-hover-bg: #eaf1f7;
184
231
  --outline-active-bg: #d8dfe8;
185
232
  --text-on-danger: #ffffff;
186
- --data-color: #186a94; /* cifras y .progress__value: es texto */
233
+ --data-color: #186a94;
187
234
  --elevated-border: #d8dfe8;
188
235
  --card-border: #d8dfe8;
189
236
  --code-color: #186a94;
@@ -197,40 +244,37 @@
197
244
  --sidebar-active-bg: #e3f2fb;
198
245
  --sidebar-active-text: #14171f;
199
246
  --sidebar-active-icon: #1f87bb;
200
- --switch-off: #b9c3d1;
247
+ --switch-off: var(--control-border);
201
248
  --tooltip-bg: #14171f;
202
249
  --tooltip-border: #14171f;
203
250
  --tooltip-text: #f6f7f9;
204
251
  --skeleton-base: #eaf1f7;
205
252
  --skeleton-sheen: #f5f8fb;
206
-
207
- --ring: 0 0 0 3px rgba(31,135,187,.25);
208
253
  --ring-danger: 0 0 0 3px rgba(192,53,53,.2);
209
- --shadow-1: 0 1px 3px rgba(53,61,124,.08);
210
- --shadow-2: 0 4px 10px rgba(53,61,124,.10);
211
- --shadow-3: 0 12px 28px rgba(53,61,124,.14);
212
- --glow-accent: 0 0 0 1px rgba(82,186,237,.4), 0 4px 16px rgba(82,186,237,.25);
213
254
 
214
- color-scheme: light;
255
+ --selection-bg: var(--action-bg);
256
+ --selection-text: var(--text-on-action);
257
+
258
+ /* Series categóricas: orden estable; acompañar con etiqueta o forma. */
259
+ --chart-1: #186a94;
260
+ --chart-2: #853f63;
261
+ --chart-3: #129a92;
262
+ --chart-4: #8e4031;
263
+ --chart-5: #5b975e;
264
+ --chart-6: #8a600a;
265
+ --chart-7: #857bc1;
215
266
  }
216
267
 
217
- /* ============================================================
218
- 3. ACCESIBILIDAD DEL MOVIMIENTO
219
- ============================================================ */
268
+ /* == Accesibilidad del movimiento == */
269
+
220
270
  @media (prefers-reduced-motion: reduce) {
221
- *, *::before, *::after {
222
- animation-duration: 0.01ms !important;
223
- animation-iteration-count: 1 !important;
224
- transition-duration: 0.01ms !important;
225
- scroll-behavior: auto !important;
271
+ :root {
272
+ --duration-instant: 0ms; --duration-fast: 0ms; --duration-base: 0ms;
273
+ --duration-slow: 0ms; --duration-page: 0ms; --stagger-step: 0ms;
226
274
  }
227
275
  }
228
276
 
229
- /* ============================================================
230
- 4. BASE
231
- ============================================================ */
232
277
  *, *::before, *::after { box-sizing: border-box; }
233
- html, body { overflow-x: clip; }
234
278
 
235
279
  body {
236
280
  margin: 0;
@@ -260,7 +304,7 @@ p { max-width: var(--measure); margin: 0 0 var(--space-4); }
260
304
  a { color: var(--link); text-decoration: none; transition: color var(--duration-fast) var(--ease-out); }
261
305
  a:hover { color: var(--link-hover); }
262
306
 
263
- ::placeholder { color: var(--text-disabled); }
307
+ ::placeholder { color: var(--placeholder); }
264
308
  ::selection { background: rgba(82,186,237,.3); }
265
309
 
266
310
  :focus-visible {
@@ -311,15 +355,15 @@ a:hover { color: var(--link-hover); }
311
355
  .btn--lg { height: 52px; padding: 14px 24px; font-size: var(--text-lg); }
312
356
 
313
357
  /* Primario — texto oscuro sobre acento, nunca blanco */
314
- /* Gating de hover (§11.5): los hover que mueven/elevan van dentro de
315
- @media (hover: hover) and (pointer: fine) — en touch el tap deja el hover pegado.
316
- Los hover de solo color/fondo quedan fuera. */
317
- .btn--primary { background: var(--accent-400); color: var(--text-on-accent); }
318
- .btn--primary:hover:not(:disabled) { background: var(--accent-500); }
358
+ /* Gating de hover (§11.5): los hover que mueven/elevan (transform, box-shadow, glow)
359
+ van dentro de @media (hover: hover) and (pointer: fine) — en touch el tap dispara
360
+ hovers falsos que se quedan pegados. Los hover de solo color/fondo quedan fuera. */
361
+ .btn--primary { background: var(--action-bg); color: var(--text-on-action); }
362
+ .btn--primary:hover:not(:disabled) { background: var(--action-hover); }
319
363
  @media (hover: hover) and (pointer: fine) {
320
364
  .btn--primary:hover:not(:disabled) { box-shadow: var(--glow-accent); }
321
365
  }
322
- .btn--primary:active:not(:disabled) { background: var(--accent-600); box-shadow: none; }
366
+ .btn--primary:active:not(:disabled) { background: var(--action-active); box-shadow: none; }
323
367
  .btn--primary:disabled { background: var(--surface-3); color: var(--text-disabled); }
324
368
 
325
369
  /* Secundario — índigo (idéntico en ambos temas) */
@@ -405,7 +449,7 @@ a:hover { color: var(--link-hover); }
405
449
 
406
450
  .input, .textarea, .select {
407
451
  width: 100%; background: var(--surface-2);
408
- border: 1px solid var(--border); border-radius: var(--radius-sm);
452
+ border: 1px solid var(--control-border); border-radius: var(--radius-sm);
409
453
  color: var(--text-primary); font-family: var(--font-body); font-size: var(--text-base);
410
454
  transition: border-color var(--duration-fast) var(--ease-out),
411
455
  box-shadow var(--duration-fast) var(--ease-out);
@@ -481,7 +525,7 @@ a:hover { color: var(--link-hover); }
481
525
  }
482
526
  .segmented--sm label { padding: 0 12px; font-size: var(--text-xs); }
483
527
  .segmented input:checked + label {
484
- background: var(--surface-3); color: var(--accent-300);
528
+ background: var(--surface-3); color: var(--link);
485
529
  font-weight: 600; box-shadow: var(--shadow-1);
486
530
  }
487
531
  .segmented input:focus-visible + label { outline: var(--focus-ring); outline-offset: 2px; }
@@ -511,8 +555,8 @@ a:hover { color: var(--link-hover); }
511
555
  }
512
556
  .slider:active::-moz-range-thumb { transform: scale(1.15); }
513
557
  @media (prefers-reduced-motion: reduce) {
514
- .slider::-webkit-slider-thumb { transition-duration: 0.01ms !important; }
515
- .slider::-moz-range-thumb { transition-duration: 0.01ms !important; }
558
+ .slider::-webkit-slider-thumb { transition: none; }
559
+ .slider::-moz-range-thumb { transition: none; }
516
560
  }
517
561
 
518
562
  /* ============================================================
@@ -575,9 +619,7 @@ a:hover { color: var(--link-hover); }
575
619
  display: inline-flex; align-items: center; gap: 4px;
576
620
  padding: 2px 8px; border-radius: var(--radius-full);
577
621
  font-size: var(--text-xs); font-weight: 500; line-height: var(--leading-snug);
578
- background: var(--surface-2); color: var(--text-secondary); border: 1px solid var(--border);
579
622
  }
580
- .badge svg { width: 14px; height: 14px; flex: none; }
581
623
  .badge--success { background: var(--success-bg); color: var(--success); border: 1px solid color-mix(in srgb, var(--success) 30%, transparent); }
582
624
  .badge--warning { background: var(--warning-bg); color: var(--warning); border: 1px solid color-mix(in srgb, var(--warning) 30%, transparent); }
583
625
  .badge--danger { background: var(--danger-bg); color: var(--danger); border: 1px solid color-mix(in srgb, var(--danger) 30%, transparent); }
@@ -690,7 +732,7 @@ a:hover { color: var(--link-hover); }
690
732
  font-variant-numeric: tabular-nums;
691
733
  }
692
734
  button.metric-chip {
693
- min-height: var(--touch-target); padding-inline: var(--space-4);
735
+ min-height: var(--control-height-md); padding-inline: var(--space-4);
694
736
  cursor: pointer; text-align: left;
695
737
  transition: background-color var(--duration-fast) var(--ease-out),
696
738
  border-color var(--duration-fast) var(--ease-out),
@@ -1089,10 +1131,7 @@ button.metric-chip:focus-visible { outline: var(--focus-ring); outline-offset: v
1089
1131
  font-weight: 600; color: var(--data-color); font-variant-numeric: tabular-nums;
1090
1132
  }
1091
1133
  @media (prefers-reduced-motion: reduce) {
1092
- /* La regla global (§3) ya cubre el <circle>, pero se deja explícito
1093
- como en .slider::-webkit-slider-thumb: el anillo es la única animación
1094
- nueva de esta sección y no debe depender solo de la regla universal. */
1095
- .progress-ring__bar { transition-duration: 0.01ms !important; }
1134
+ .progress-ring__bar { transition: none; }
1096
1135
  }
1097
1136
 
1098
1137
  /* ============================================================
@@ -1259,13 +1298,13 @@ button.metric-chip:focus-visible { outline: var(--focus-ring); outline-offset: v
1259
1298
  transition: opacity var(--duration-base) var(--ease-out),
1260
1299
  transform var(--duration-base) var(--ease-out),
1261
1300
  visibility 0s linear var(--duration-base);
1262
- }
1301
+ display: none; }
1263
1302
  .popover.is-open .popover__panel {
1264
1303
  opacity: 1; transform: none; visibility: visible;
1265
1304
  transition: opacity var(--duration-base) var(--ease-out),
1266
1305
  transform var(--duration-base) var(--ease-out),
1267
1306
  visibility 0s;
1268
- }
1307
+ display: block; }
1269
1308
  /* Alineado a la derecha del disparador */
1270
1309
  .popover--right .popover__panel { left: auto; right: 0; transform-origin: top right; }
1271
1310
 
@@ -1420,6 +1459,171 @@ button.metric-chip:focus-visible { outline: var(--focus-ring); outline-offset: v
1420
1459
  border: 1px solid var(--border-strong); border-radius: 6px; box-shadow: var(--shadow-1);
1421
1460
  }
1422
1461
 
1462
+ /* CONTRATOS COMPARTIDOS GENERADOS */
1463
+ /* Contratos comunes de interacción; la identidad y los valores viven en tokens.css. */
1464
+ .grid { grid-template-columns: repeat(12, minmax(0, 1fr)); }
1465
+ .grid > *, .field, .card { min-inline-size: 0; }
1466
+ .stack, .card__content, .form-stack, .text-group { display: flex; flex-direction: column; align-items: stretch; }
1467
+ .stack, .card__content { gap: var(--content-gap); }
1468
+ .stack--tight, .text-group { gap: var(--heading-gap); }
1469
+ .stack--cards { gap: var(--card-group-gap); }
1470
+ .form-stack { gap: var(--form-gap); }
1471
+ .cluster { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); }
1472
+ .card { display: flex; flex-direction: column; gap: var(--card-content-gap); }
1473
+ .field { gap: var(--field-gap); }
1474
+ .field__label, .field__help, .field__error, .field > :is(label, p, span, div) { margin-block: 0; }
1475
+ :where(.field + .field, .field + .btn, .field + .cluster) { margin-block-start: var(--form-gap); }
1476
+ :where(.card + .card) { margin-block-start: var(--card-group-gap); }
1477
+ .stack > *, .card__content > *, .form-stack > *, .text-group > *, .grid > .card,
1478
+ .card > :is(h1, h2, h3, h4, h5, h6, p), .text-group > :is(h1, h2, h3, h4, h5, h6, p),
1479
+ .stack > :is(h1, h2, h3, h4, h5, h6, p) { margin-block: 0; }
1480
+ .form-stack > .field { margin-block: 0; }
1481
+ .btn {
1482
+ block-size: auto;
1483
+ min-block-size: var(--control-height-md);
1484
+ max-inline-size: 100%;
1485
+ white-space: normal;
1486
+ line-height: 1.35;
1487
+ text-align: center;
1488
+ }
1489
+ .btn--sm { min-block-size: var(--control-height-sm, 36px); }
1490
+ .btn--lg { min-block-size: var(--control-height-lg, 52px); }
1491
+ .btn--primary, .icon-btn--primary, .calendar__day--selected, .pagination__btn.is-active {
1492
+ background: var(--action-bg);
1493
+ color: var(--text-on-action);
1494
+ }
1495
+ .btn--primary:hover:not(:disabled), .icon-btn--primary:hover:not(:disabled) { background: var(--action-hover); color: var(--text-on-action); }
1496
+ .btn--primary:active:not(:disabled), .icon-btn--primary:active:not(:disabled) { background: var(--action-active); color: var(--text-on-action); }
1497
+ .input, .textarea, .select, .combobox__input, .search__input, .datepicker__input {
1498
+ max-inline-size: 100%;
1499
+ caret-color: var(--focus-color);
1500
+ }
1501
+ .input, .select, .combobox__input, .search__input, .datepicker__input {
1502
+ block-size: auto; min-block-size: var(--control-height-md);
1503
+ padding-block: var(--space-2); line-height: 1.5;
1504
+ }
1505
+ :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); }
1506
+ .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"]),
1507
+ .select:hover:not(:disabled):not(:focus), .combobox__input:hover:not(:disabled):not(:focus),
1508
+ .search__input:hover:not(:focus), .datepicker__input:hover:not(:disabled):not(:focus) { border-color: var(--border-hover); }
1509
+ .input:focus-visible, .textarea:focus-visible, .select:focus-visible,
1510
+ .combobox__input:focus-visible, .search__input:focus-visible, .datepicker__input:focus-visible {
1511
+ outline: 2px solid var(--focus-color);
1512
+ outline-offset: 2px;
1513
+ }
1514
+ .input[aria-invalid="true"], .textarea[aria-invalid="true"] { border-color: var(--danger-text, var(--danger)); }
1515
+ .input.input--error, .textarea.textarea--error,
1516
+ .input.input--error:hover:not(:disabled):not(:focus), .textarea.textarea--error:hover:not(:disabled):not(:focus),
1517
+ :is(.input, .textarea, .select, .combobox__input, .search__input, .datepicker__input)[aria-invalid="true"]:hover:not(:disabled):not(:focus) {
1518
+ border-color: var(--danger-text, var(--danger));
1519
+ }
1520
+ .input.input--success, .input.input--success:hover:not(:disabled):not(:focus) { border-color: var(--success-text, var(--success)); }
1521
+ .pagination { max-inline-size: 100%; flex-wrap: wrap; row-gap: var(--space-2); }
1522
+ .pagination__ellipsis { color: var(--text-muted); }
1523
+ .step--current .step__marker { color: var(--link); border-color: var(--focus-color); }
1524
+ .checkbox input:focus-visible ~ .checkbox__box,
1525
+ .radio input:focus-visible ~ .radio__dot,
1526
+ .switch input:focus-visible ~ .switch__track {
1527
+ outline: 2px solid var(--focus-color);
1528
+ outline-offset: 2px;
1529
+ }
1530
+ input.checkbox, input.radio { border-color: var(--control-border); }
1531
+ input.checkbox:hover, input.radio:hover { border-color: var(--focus-color); }
1532
+ input.checkbox:checked, input.checkbox:indeterminate, input.radio:checked { border-color: var(--focus-color); }
1533
+ .switch__track { background: var(--control-border); }
1534
+ .tabs { max-inline-size: 100%; overflow-x: auto; }
1535
+ .tabs__tab { flex-shrink: 0; }
1536
+ .segmented { max-inline-size: 100%; block-size: auto; min-block-size: var(--control-height-md); overflow-x: auto; }
1537
+ .segmented label { padding-block: var(--space-2); }
1538
+ .stat__row, .breadcrumbs, .check-row { flex-wrap: wrap; row-gap: var(--space-2); }
1539
+ .progress-row, .slider-row { flex-wrap: wrap; gap: var(--space-2); }
1540
+ .stat__value { max-inline-size: 100%; white-space: normal; overflow-wrap: anywhere; }
1541
+ .file-upload, .file-upload__label { min-inline-size: 0; max-inline-size: 100%; }
1542
+ .file-upload__button { max-inline-size: 100%; white-space: normal; text-align: center; }
1543
+ .file-upload__hint { overflow-wrap: anywhere; }
1544
+ .table-scroll { max-inline-size: 100%; overflow-x: auto; overscroll-behavior-inline: contain; }
1545
+ .popover__panel, .datepicker__calendar, .combobox__list {
1546
+ max-inline-size: calc(100vw - 2rem);
1547
+ max-block-size: min(32rem, 70dvh);
1548
+ overflow: auto;
1549
+ }
1550
+ .file-upload__input:focus-visible + .file-upload__label { outline: 2px solid var(--focus-color); outline-offset: 2px; }
1551
+ .calendar { max-inline-size: calc(100vw - 2rem); }
1552
+ .ui-title, :is(h1, h2, h3, h4, h5, h6).ui-title, [data-mode="operate"] :is(h1, h2, h3) {
1553
+ font-family: var(--font-ui-heading);
1554
+ letter-spacing: 0;
1555
+ line-height: 1.3;
1556
+ font-weight: var(--weight-semibold);
1557
+ }
1558
+ .ui-title, :is(h1, h2, h3, h4, h5, h6).ui-title, [data-mode="operate"] h1 { font-size: var(--text-ui-title); }
1559
+ [data-mode="operate"] h2 { font-size: var(--text-xl); }
1560
+ [data-mode="operate"] h3 { font-size: var(--text-lg); }
1561
+ .display, .reading-title, :is(h1, h2, h3, h4, h5, h6):is(.display, .reading-title) {
1562
+ font-family: var(--font-display);
1563
+ font-size: var(--text-display-fluid);
1564
+ line-height: 1.08;
1565
+ letter-spacing: var(--tracking-display);
1566
+ text-wrap: balance;
1567
+ }
1568
+ :where(.celeste-reset, .salvia-reset, .bruma-reset, .nocturna-reset, body[data-system]) {
1569
+ scrollbar-color: var(--control-border) var(--bg);
1570
+ }
1571
+ ::selection,
1572
+ .celeste-reset ::selection, .salvia-reset ::selection, .bruma-reset ::selection {
1573
+ background: var(--selection-bg); color: var(--selection-text);
1574
+ }
1575
+ ::placeholder { color: var(--placeholder); opacity: 1; }
1576
+ :where(a) { text-underline-offset: .2em; text-decoration-thickness: .08em; }
1577
+ .skip-link {
1578
+ position: fixed; inset-block-start: var(--space-2); inset-inline-start: var(--space-2);
1579
+ z-index: 1000; padding: var(--space-3) var(--space-4);
1580
+ background: var(--action-bg); color: var(--text-on-action);
1581
+ border-radius: var(--radius-sm); transform: translateY(-180%);
1582
+ }
1583
+ .skip-link:focus { transform: none; outline: 2px solid var(--focus-color); outline-offset: 2px; }
1584
+ [data-tooltip]::before, [data-tooltip]::after { display: none; }
1585
+ [data-tooltip]:focus-visible::before, [data-tooltip]:focus-visible::after { display: block; }
1586
+ .has-tooltip .tooltip { display: none; }
1587
+ .has-tooltip:has(:focus-visible) .tooltip { display: block; opacity: 1; transform: translate(-50%, 0); }
1588
+ @media (hover: hover) and (pointer: fine) {
1589
+ [data-tooltip]:hover::before, [data-tooltip]:hover::after { display: block; }
1590
+ .has-tooltip:hover .tooltip { display: block; }
1591
+ }
1592
+ @media (pointer: coarse) {
1593
+ .btn, .icon-btn, .segmented label, .pagination button,
1594
+ .tag__close, .search__clear, .toast__close, .check-row,
1595
+ .checkbox:not(input), .radio:not(input), .switch:not(input) {
1596
+ min-block-size: var(--touch-target);
1597
+ min-inline-size: var(--touch-target);
1598
+ }
1599
+ }
1600
+ @media (max-width: 479px) {
1601
+ .btn-group { flex-wrap: wrap; }
1602
+ .modal { max-inline-size: calc(100vw - 2rem); }
1603
+ .stepper { flex-direction: column; align-items: stretch; gap: var(--space-4); }
1604
+ .step { flex-direction: row; align-items: center; gap: var(--space-3); text-align: start; min-inline-size: 0; }
1605
+ .step__label { overflow-wrap: anywhere; }
1606
+ .step__connector { display: none; }
1607
+ }
1608
+ @media (prefers-reduced-motion: reduce) {
1609
+ html, html:focus-within { scroll-behavior: auto; }
1610
+ .spinner, .skeleton, .skeleton::after,
1611
+ .progress--indeterminate .progress__bar, .alert.is-leaving, .toast.is-leaving { animation: none; }
1612
+ .progress--indeterminate .progress__bar { transform: none; }
1613
+ .btn, .icon-btn, .card--interactive, .slider::-webkit-slider-thumb { transform: none; }
1614
+ .btn:active:not(:disabled), .icon-btn:active:not(:disabled), .card--interactive:hover { transform: none; }
1615
+ .btn, .icon-btn { transition: background-color 80ms linear, color 80ms linear; }
1616
+ .menu, .popover__panel, .datepicker__calendar, .link::after { transition: none; }
1617
+ .progress__bar, .progress-ring__value, .accordion__content, .accordion__body { transition: none; }
1618
+ }
1619
+ @media (forced-colors: active) {
1620
+ input.switch, .switch__track { border: 1px solid CanvasText; background: Canvas; }
1621
+ input.switch::after, .switch__track::after { background: CanvasText; }
1622
+ input.switch:checked, .switch input:checked ~ .switch__track { border-color: Highlight; }
1623
+ input.switch:checked::after, .switch input:checked ~ .switch__track::after { background: Highlight; }
1624
+ .checkbox__box, .radio__dot { border-color: CanvasText; }
1625
+ }
1626
+
1423
1627
 
1424
1628
  /* ============================================================
1425
1629
  ESTILOS DE LA PÁGINA DE DOCS (.ds-*)
@@ -1588,9 +1792,118 @@ html { scroll-behavior: smooth; }
1588
1792
  .ds-main { margin-left: 0; padding-inline: var(--space-5); }
1589
1793
  .ds-hamburger { display: inline-flex; }
1590
1794
  }
1795
+ /* ESTILOS COMPARTIDOS GENERADOS */
1796
+ /* La documentación comparte navegación y deja que cada marca determine el ritmo. */
1797
+ .ds-topbar { gap: var(--space-3); }
1798
+ .ds-brand { min-inline-size: 0; }
1799
+ .ds-brand, .ds-brand__name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1800
+ .ds-theme-toggle, .ds-hamburger { flex-shrink: 0; }
1801
+ .ds-main { padding-block-start: var(--space-12); }
1802
+ .ds-main > section { scroll-margin-block-start: 88px; }
1803
+ .ds-main > section > h2 { text-wrap: balance; }
1804
+ .ds-main :is(h1, h2, h3, h4, h5, h6) { overflow-wrap: anywhere; }
1805
+ .ds-hero {
1806
+ display: grid;
1807
+ grid-template-columns: minmax(0, 1.45fr) minmax(12rem, .65fr);
1808
+ column-gap: var(--space-12);
1809
+ row-gap: var(--space-6);
1810
+ padding-block-start: var(--space-6);
1811
+ }
1812
+ .ds-hero > h1 {
1813
+ grid-column: 1 / -1;
1814
+ font-size: var(--text-display-fluid);
1815
+ line-height: 1.08;
1816
+ letter-spacing: var(--tracking-display);
1817
+ text-wrap: balance;
1818
+ margin: 0;
1819
+ }
1820
+ .ds-hero > .ds-lead { grid-column: 1; margin: 0; max-inline-size: 55ch; }
1821
+ .ds-hero > .ds-actions { grid-column: 1; margin: 0; display: flex; flex-wrap: wrap; gap: var(--space-3); }
1822
+ .ds-hero__palette {
1823
+ grid-column: 2; grid-row: 2 / 4;
1824
+ display: grid; grid-template-columns: 2fr 1.25fr .75fr;
1825
+ gap: var(--space-1); margin: 0; min-block-size: 150px;
1826
+ align-self: stretch; overflow: hidden; border-radius: var(--radius-lg);
1827
+ }
1828
+ .ds-hero__chip { inline-size: auto; block-size: auto; border: 0; border-radius: 0; }
1829
+ .ds-principles {
1830
+ grid-column: 1 / -1; display: grid;
1831
+ grid-template-columns: repeat(2, minmax(0, 1fr));
1832
+ gap: var(--space-6) var(--space-10); margin-block: var(--space-4) 0;
1833
+ }
1834
+ .ds-principle { padding-block-start: var(--space-4); border-block-start: 1px solid var(--border); }
1835
+ .ds-principle h2 { font-family: var(--font-body); font-size: var(--text-base); line-height: 1.5; margin: 0 0 var(--space-2); }
1836
+ .ds-principle p { font-size: var(--text-sm); color: var(--text-secondary); margin: 0; max-inline-size: 48ch; }
1837
+ .ds-hero > .alert, .ds-hero > .ds-callout { grid-column: 1 / -1; margin: var(--space-3) 0 0; }
1838
+ .ds-callout { border: 1px solid var(--border); border-radius: var(--radius-md); }
1839
+ .ds-recipes { margin: 0; }
1840
+ .ds-recipes > div {
1841
+ display: grid; grid-template-columns: minmax(8rem, .4fr) minmax(0, 1fr);
1842
+ gap: var(--space-4); padding-block: var(--space-5); border-block-start: 1px solid var(--border);
1843
+ }
1844
+ .ds-recipes dt { font-weight: var(--weight-semibold); }
1845
+ .ds-recipes dd { margin: 0; color: var(--text-secondary); max-inline-size: 65ch; }
1846
+ .ds-chart-palette { list-style: none; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: var(--space-4); }
1847
+ .ds-chart-palette li { display: grid; gap: var(--space-2); }
1848
+ .ds-chart-palette__color { display: block; block-size: 56px; border-radius: var(--radius-sm); }
1849
+ .ds-chart-palette code { font-family: var(--font-mono); font-size: var(--text-sm); }
1850
+ .ds-chart-palette [data-chart-color] { color: var(--text-muted); font-size: var(--text-sm); }
1851
+ .ds-nav a { min-block-size: 36px; display: flex; align-items: center; }
1852
+ .ds-nav__sub a { font-size: var(--text-sm); }
1853
+ .ds-demo { min-inline-size: 0; max-inline-size: 100%; }
1854
+ .ds-demo > * { min-inline-size: 0; max-inline-size: 100%; }
1855
+ .ds-demo :has(> .divider--vertical) { flex-wrap: wrap; row-gap: var(--space-3); }
1856
+ .ds-wordmark-demo { display: inline-block; max-inline-size: 100%; overflow-x: auto; vertical-align: bottom; }
1857
+ .ds-main code:not(.ds-pre code), .ds-main .code, .ds-main .u-mono { overflow-wrap: anywhere; white-space: normal; }
1858
+ .ds-demo .popover__panel { max-inline-size: calc(100vw - var(--space-20)); }
1859
+ .ds-demo--grid, .ds-semantic-grid { grid-template-columns: repeat(auto-fit, minmax(min(100%, 200px), 1fr)); }
1860
+ .ds-copy { min-block-size: 36px; }
1861
+ .ds-code summary { padding-block: var(--space-3); }
1862
+ .ds-pre { max-inline-size: 100%; }
1863
+ .ds-type-row__sample { min-inline-size: 0; white-space: normal; overflow-wrap: anywhere; }
1864
+ .ds-main table { min-inline-size: 0; }
1865
+ .ds-theme-toggle[data-tooltip]::after { left: auto; right: 0; transform: translateY(4px); }
1866
+ .ds-theme-toggle[data-tooltip]:hover::after, .ds-theme-toggle[data-tooltip]:focus-visible::after { transform: translateY(0); }
1867
+ [data-system="nocturna"] .ds-wordmark-badge { color: var(--night-50); }
1868
+ .ds-main p { overflow-wrap: anywhere; }
1869
+ .ds-type-row { min-inline-size: 0; }
1870
+ [data-system="celeste"] .ds-hero { padding-block-start: var(--space-10); }
1871
+ [data-system="salvia"] .ds-principles { row-gap: var(--space-8); }
1872
+ [data-system="bruma"] .ds-principles { gap: var(--space-4) var(--space-8); }
1873
+ [data-system="nocturna"] .ds-main { padding-block-start: calc(64px + var(--space-10)); }
1874
+ @media (max-width: 1023px) {
1875
+ .ds-main { padding-inline: var(--space-5); }
1876
+ .ds-hero { column-gap: var(--space-6); }
1877
+ .ds-nav a { min-block-size: var(--touch-target); }
1878
+ .ds-sidenav:not(.is-open), .ds-sidebar:not(.is-open) { visibility: hidden; }
1879
+ }
1880
+ @media (max-width: 639px) {
1881
+ .ds-topbar { padding-inline: var(--space-4); }
1882
+ .ds-version { max-inline-size: 44vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1883
+ .ds-main { padding-inline: var(--space-4); padding-block-start: var(--space-8); }
1884
+ .ds-hero { grid-template-columns: minmax(0, 1fr); row-gap: var(--space-5); padding-block-start: var(--space-2); }
1885
+ .ds-hero > h1, .ds-hero > .ds-lead, .ds-hero > .ds-actions { grid-column: 1; }
1886
+ .ds-hero__palette { grid-column: 1; grid-row: auto; min-block-size: 64px; }
1887
+ .ds-principles { grid-template-columns: minmax(0, 1fr); }
1888
+ .ds-demo { padding: var(--space-4); }
1889
+ .ds-type-row { display: grid; gap: var(--space-2); }
1890
+ .ds-type-row__meta { inline-size: auto; }
1891
+ .ds-space-item { min-inline-size: 0; }
1892
+ .ds-space-item .u-mono { inline-size: 96px; flex: none; }
1893
+ .ds-space-bar { max-inline-size: calc(100vw - 164px); }
1894
+ .ds-recipes > div { grid-template-columns: minmax(0, 1fr); gap: var(--space-2); }
1895
+ .ds-actions > .btn { flex: 1 1 12rem; }
1896
+ .ds-copy { min-block-size: var(--touch-target); }
1897
+ }
1898
+ @media (prefers-reduced-motion: reduce) {
1899
+ .ds-sidenav, .ds-sidebar, .ds-swatch, .ds-ease-chip { transition: none; }
1900
+ .ds-swatch:hover, .ds-ease-chip:hover { transform: none; }
1901
+ }
1902
+
1591
1903
  </style>
1592
1904
  </head>
1593
- <body>
1905
+ <body data-system="nocturna" data-mode="read">
1906
+ <a class="skip-link" href="#contenido">Saltar al contenido</a>
1594
1907
 
1595
1908
  <header class="ds-topbar">
1596
1909
  <button class="icon-btn icon-btn--ghost ds-hamburger" id="dsHamburger" aria-label="Abrir navegación">
@@ -1609,10 +1922,13 @@ html { scroll-behavior: smooth; }
1609
1922
  <div class="ds-nav">
1610
1923
  <div class="overline ds-group">Fundamentos</div>
1611
1924
  <a href="#introduccion">Introducción</a>
1925
+ <a href="#datos">Color para datos</a>
1926
+ <a href="#criterio">Criterio de uso</a>
1612
1927
  <a href="#voz">Voz y contenido</a>
1613
1928
  <a href="#colores">Colores</a>
1614
1929
  <a href="#tipografia">Tipografía</a>
1615
1930
  <a href="#espaciado">Espaciado</a>
1931
+ <a href="#ritmo">Ritmo de contenido</a>
1616
1932
  <a href="#bordes">Bordes, radios y focus</a>
1617
1933
  <a href="#elevacion">Elevación</a>
1618
1934
  <a href="#iconografia">Iconografía</a>
@@ -1664,29 +1980,18 @@ html { scroll-behavior: smooth; }
1664
1980
  </div>
1665
1981
  </nav>
1666
1982
 
1667
- <main class="ds-main">
1983
+ <main class="ds-main" id="contenido" tabindex="-1">
1668
1984
 
1669
1985
  <!-- ================= INTRODUCCIÓN ================= -->
1670
1986
  <section id="introduccion" class="ds-hero">
1671
1987
  <h1>Nocturna Eléctrica</h1>
1672
1988
  <p class="ds-lead">Sistema de diseño <strong>dark-first</strong>, enérgico y de alto contraste. El fondo casi negro es el escenario; el celeste eléctrico es el reflector.</p>
1673
- <div class="ds-palette-chips">
1674
- <span class="code">#52baed · celeste eléctrico</span>
1675
- <span class="code">#353d7c · índigo profundo</span>
1676
- <span class="code">#010203 · negro nocturno</span>
1677
- </div>
1678
- <div style="display:flex;gap:16px;flex-wrap:wrap;margin:24px 0;">
1679
- <button class="btn btn--primary ds-hero-cta" id="heroCtaBuild">Empezar a construir</button>
1989
+ <div class="ds-hero__palette" aria-hidden="true"><div class="ds-hero__chip" style="background:var(--accent-400)"></div><div class="ds-hero__chip" style="background:var(--indigo-500)"></div><div class="ds-hero__chip" style="background:var(--night-950)"></div></div>
1990
+ <div class="ds-actions">
1991
+ <button class="btn btn--primary" id="heroCtaBuild">Empezar a construir</button>
1680
1992
  <button class="btn btn--outline" id="heroCtaTokens">Ver tokens</button>
1681
1993
  </div>
1682
- <h3>Principios</h3>
1683
- <ol style="color:var(--text-secondary);line-height:1.8;max-width:65ch;padding-left:20px;">
1684
- <li><strong style="color:var(--text-primary)">Contraste con propósito</strong> — el acento solo aparece donde hay acción o dato clave.</li>
1685
- <li><strong style="color:var(--text-primary)">Profundidad por luz</strong> — la elevación aclara superficies, no oscurece bordes.</li>
1686
- <li><strong style="color:var(--text-primary)">Energía contenida</strong> — paleta vibrante, espaciado disciplinado: nunca ruidosa.</li>
1687
- <li><strong style="color:var(--text-primary)">Datos protagonistas</strong> — cifras y gráficas brillan; el chrome se mantiene discreto.</li>
1688
- <li><strong style="color:var(--text-primary)">Movimiento con propósito</strong> — la animación explica cambios de estado, nunca decora sola.</li>
1689
- </ol>
1994
+ <div class="ds-principles"><div class="ds-principle"><h2>Contraste con propósito</h2><p>el acento solo aparece donde hay acción o dato clave.</p></div><div class="ds-principle"><h2>Profundidad por luz</h2><p>la elevación aclara superficies, no oscurece bordes.</p></div><div class="ds-principle"><h2>Energía contenida</h2><p>paleta vibrante, espaciado disciplinado: nunca ruidosa.</p></div><div class="ds-principle"><h2>Datos protagonistas</h2><p>cifras y gráficas brillan; el chrome se mantiene discreto.</p></div><div class="ds-principle"><h2>Movimiento con propósito</h2><p>la animación explica cambios de estado, nunca decora sola.</p></div></div>
1690
1995
  <div class="ds-callout ds-callout--info">
1691
1996
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4m0-4h.01"/></svg>
1692
1997
  <div><strong>Regla de oro:</strong> los componentes consumen solo tokens semánticos (<span class="code">--surface-1</span>, <span class="code">--text-primary</span>…), nunca hex directos. El cambio de tema es un cambio de atributo, no un refactor. Pruébalo con el botón de tema de la esquina superior.</div>
@@ -1694,6 +1999,42 @@ html { scroll-behavior: smooth; }
1694
1999
  </section>
1695
2000
 
1696
2001
  <!-- ================= VOZ ================= -->
2002
+ <section id="datos">
2003
+ <h2>Color para datos</h2>
2004
+ <p>Serie categórica de orden estable, con pasos propios en cada tema. El color identifica; etiquetas, formas o patrones conservan el significado. Los labels usan tinta, no el color de la serie.</p>
2005
+ <ul class="ds-chart-palette"><li><span class="ds-chart-palette__color" style="background:var(--chart-1)" aria-hidden="true"></span><code>--chart-1</code><span data-chart-color="--chart-1"></span></li><li><span class="ds-chart-palette__color" style="background:var(--chart-2)" aria-hidden="true"></span><code>--chart-2</code><span data-chart-color="--chart-2"></span></li><li><span class="ds-chart-palette__color" style="background:var(--chart-3)" aria-hidden="true"></span><code>--chart-3</code><span data-chart-color="--chart-3"></span></li><li><span class="ds-chart-palette__color" style="background:var(--chart-4)" aria-hidden="true"></span><code>--chart-4</code><span data-chart-color="--chart-4"></span></li><li><span class="ds-chart-palette__color" style="background:var(--chart-5)" aria-hidden="true"></span><code>--chart-5</code><span data-chart-color="--chart-5"></span></li><li><span class="ds-chart-palette__color" style="background:var(--chart-6)" aria-hidden="true"></span><code>--chart-6</code><span data-chart-color="--chart-6"></span></li><li><span class="ds-chart-palette__color" style="background:var(--chart-7)" aria-hidden="true"></span><code>--chart-7</code><span data-chart-color="--chart-7"></span></li></ul>
2006
+ <p class="u-muted">Las pruebas comprueban contraste con la superficie y separación perceptual entre colores adyacentes. Ofrece una tabla equivalente; más de siete series necesitan agrupación u otra composición.</p>
2007
+ </section>
2008
+
2009
+ <section id="ritmo">
2010
+ <h2>Ritmo de contenido</h2>
2011
+ <p>El espacio sigue a la relación: 6px dentro de un campo, 8px entre título y descripción, 16px entre bloques, 20px entre campos y 24px entre tarjetas. Las secciones se separan 64px.</p>
2012
+ <div class="stack stack--cards" data-spacing-example data-mode="operate">
2013
+ <section class="card">
2014
+ <header class="text-group"><h3>Datos de contacto</h3><p>Ejemplo de composición. El título y su descripción forman un grupo.</p></header>
2015
+ <form class="form-stack" onsubmit="return false">
2016
+ <div class="field"><label class="field__label" for="rh-name">Nombre</label><input class="input" id="rh-name" autocomplete="name" placeholder="Tu nombre"><p class="field__help">Escribe el nombre que quieres mostrar.</p></div>
2017
+ <div class="field"><label class="field__label" for="rh-email">Correo</label><input class="input" id="rh-email" type="email" autocomplete="email" placeholder="nombre@ejemplo.com"><p class="field__help">La ayuda puede ocupar varias líneas sin juntarse con el siguiente control.</p></div>
2018
+ <div class="cluster"><button class="btn btn--primary" type="button">Acción de ejemplo</button><button class="btn btn--secondary" type="button">Cancelar</button></div>
2019
+ </form>
2020
+ </section>
2021
+ <section class="card">
2022
+ <header class="text-group"><h3>Contenido relacionado</h3><p>Una tarjeta distinta conserva su propia distancia.</p></header>
2023
+ <div class="stack"><p>Los párrafos y los divs no necesitan una colección de márgenes manuales.</p><div>Este bloque mantiene 16px respecto al párrafo anterior.</div></div>
2024
+ </section>
2025
+ </div>
2026
+ </section>
2027
+
2028
+ <section id="criterio">
2029
+ <h2>Diseñar con Nocturna Eléctrica</h2>
2030
+ <dl class="ds-recipes">
2031
+ <div><dt>Operar</dt><dd>Una tarea clara por región. Títulos en la familia de UI, datos estables y una acción dominante por contexto. Usa <code>data-mode="operate"</code> en formularios, paneles y tablas.</dd></div>
2032
+ <div><dt>Leer</dt><dd>Display para abrir la lectura; cuerpo de 65–75 caracteres por línea. Agrupa lo relacionado y separa las secciones con espacio, sin convertir cada párrafo en una tarjeta.</dd></div>
2033
+ <div><dt>Decidir</dt><dd>La composición sigue a la decisión: contexto, evidencia y acción. Conserva la identidad de marca y evita trasladar la densidad de un dashboard a una landing.</dd></div>
2034
+ <div><dt>Interactuar</dt><dd>Los botones sólidos usan <code>--action-bg</code> y <code>--text-on-action</code>. Foco, hover y error deben seguir siendo reconocibles en ambos temas, con teclado y con movimiento reducido.</dd></div>
2035
+ </dl>
2036
+ </section>
2037
+
1697
2038
  <section id="voz">
1698
2039
  <h2>Voz y contenido</h2>
1699
2040
  <div class="ds-demo ds-demo--col" style="gap:12px;">
@@ -1701,7 +2042,7 @@ html { scroll-behavior: smooth; }
1701
2042
  <div><span class="overline">Casing</span><p style="margin:6px 0 0;">Sentence case en headings y botones: «Crear proyecto», no «Crear Proyecto». Overlines en uppercase con tracking <span class="code">+0.08em</span>.</p></div>
1702
2043
  <div><span class="overline">Botones</span><p style="margin:6px 0 0;">Verbo + objeto, corto: «Guardar cambios», «Ver informe».</p></div>
1703
2044
  <div><span class="overline">Errores</span><p style="margin:6px 0 0;">Concretos y accionables: dicen qué pasó <em>y qué hacer</em>.</p></div>
1704
- <div><span class="overline">Datos</span><p style="margin:6px 0 0;">Siempre en JetBrains Mono o <span class="code">tabular-nums</span>; grandes en dashboards, con count-up de 350ms al montar.</p></div>
2045
+ <div><span class="overline">Datos</span><p style="margin:6px 0 0;">Siempre en JetBrains Mono o <span class="code">tabular-nums</span>; grandes en dashboards, con valores finales listos para leer.</p></div>
1705
2046
  <div><span class="overline">Emoji</span><p style="margin:6px 0 0;">No se usan. El estado se comunica con color + icono + texto.</p></div>
1706
2047
  </div>
1707
2048
  </section>
@@ -1769,7 +2110,7 @@ html { scroll-behavior: smooth; }
1769
2110
  <div class="ds-radius-demo" style="border-radius:var(--radius-md);">md · 12</div>
1770
2111
  <div class="ds-radius-demo" style="border-radius:var(--radius-lg);">lg · 16</div>
1771
2112
  <div class="ds-radius-demo" style="border-radius:var(--radius-xl);">xl · 24</div>
1772
- <div class="ds-radius-demo" style="border-radius:var(--radius-full);width:72px;">full</div>
2113
+ <div class="ds-radius-demo" style="border-radius:var(--radius-full);width:min(100%, 72px);">full</div>
1773
2114
  </div>
1774
2115
  <p style="margin-top:16px;">Borde por defecto <span class="code">1px night-500</span>; énfasis <span class="code">night-600</span>. Focus ring global de teclado: <span class="code">2px accent-400</span> con offset 2px — pruébalo navegando con <kbd class="code">Tab</kbd>.</p>
1775
2116
  </section>
@@ -1818,7 +2159,7 @@ html { scroll-behavior: smooth; }
1818
2159
 
1819
2160
  <!-- ================= COMPONENTES (overview) ================= -->
1820
2161
  <section id="componentes">
1821
- <span class="overline">Biblioteca</span>
2162
+
1822
2163
  <h2>Componentes</h2>
1823
2164
  <p class="ds-lead">Los 35 componentes del sistema, renderizados en vivo con las clases del framework <span class="code">nocturna-electrica.css</span>. Cada bloque incluye su código de uso.</p>
1824
2165
  </section>
@@ -1826,7 +2167,7 @@ html { scroll-behavior: smooth; }
1826
2167
  <!-- ================= BOTONES ================= -->
1827
2168
  <section id="comp-botones">
1828
2169
  <h2>Botones</h2>
1829
- <p>Alturas 36 / 44 / 52px. El primario lleva <strong>texto oscuro</strong> (contraste 9.50:1) — nunca blanco. Un solo primario por pantalla.</p>
2170
+ <p>Alturas 36 / 44 / 52px. El primario lleva <strong>texto oscuro</strong> (contraste 9.50:1) — nunca blanco. Una acción dominante por contexto de decisión.</p>
1830
2171
  <div class="ds-demo">
1831
2172
  <button class="btn btn--primary">Guardar cambios</button>
1832
2173
  <button class="btn btn--secondary">Ver informe</button>
@@ -1875,8 +2216,8 @@ html { scroll-behavior: smooth; }
1875
2216
  <p>Trazo 2px, rotación 0.8s. Si la carga dura menos de 300ms, no mostrar spinner.</p>
1876
2217
  <div class="ds-demo">
1877
2218
  <span class="spinner"></span>
1878
- <span class="spinner" style="width:24px;height:24px;"></span>
1879
- <span class="spinner" style="width:32px;height:32px;color:var(--text-muted);"></span>
2219
+ <span class="spinner" style="width:min(100%, 24px);height:24px;"></span>
2220
+ <span class="spinner" style="width:min(100%, 32px);height:32px;color:var(--text-muted);"></span>
1880
2221
  </div>
1881
2222
  <div class="ds-code-wrap"><details class="ds-code"><summary>Ver código</summary><pre><code>&lt;span class="spinner"&gt;&lt;/span&gt;
1882
2223
  &lt;!-- Hereda currentColor; default accent-400 --&gt;</code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
@@ -2390,21 +2731,21 @@ html { scroll-behavior: smooth; }
2390
2731
  <!-- ================= STAT ================= -->
2391
2732
  <section id="comp-stat">
2392
2733
  <h2>Stat</h2>
2393
- <p>Cifra de dashboard con count-up de 350ms al entrar en pantalla (respeta <span class="code">prefers-reduced-motion</span>).</p>
2734
+ <p>Cifra de dashboard estable desde la carga. El movimiento no retrasa la lectura del dato.</p>
2394
2735
  <div class="ds-demo ds-demo--grid">
2395
2736
  <div class="stat">
2396
2737
  <span class="stat__label">Ingresos del mes</span>
2397
- <span class="stat__value" data-countup="128450" data-prefix="$">$0</span>
2738
+ <span class="stat__value">$128,450</span>
2398
2739
  <span class="stat__delta stat__delta--up">▲ +12,4 %</span>
2399
2740
  </div>
2400
2741
  <div class="stat">
2401
2742
  <span class="stat__label">Usuarios activos</span>
2402
- <span class="stat__value" data-countup="8342">0</span>
2743
+ <span class="stat__value">8,342</span>
2403
2744
  <span class="stat__delta stat__delta--down">▼ −3,1 %</span>
2404
2745
  </div>
2405
2746
  <div class="stat">
2406
2747
  <span class="stat__label">Uptime</span>
2407
- <span class="stat__value" data-countup="99.98" data-decimals="2" data-suffix=" %">0 %</span>
2748
+ <span class="stat__value">99.98 %</span>
2408
2749
  <span class="stat__delta stat__delta--neutral">— sin cambios</span>
2409
2750
  </div>
2410
2751
  </div>
@@ -2561,7 +2902,7 @@ html { scroll-behavior: smooth; }
2561
2902
  <h2>Skeleton y EmptyState</h2>
2562
2903
  <div class="ds-demo ds-demo--grid">
2563
2904
  <div style="display:flex;gap:12px;align-items:center;">
2564
- <span class="skeleton skeleton--circle" style="width:40px;height:40px;"></span>
2905
+ <span class="skeleton skeleton--circle" style="width:min(100%, 40px);height:40px;"></span>
2565
2906
  <div style="flex:1;display:flex;flex-direction:column;gap:8px;">
2566
2907
  <span class="skeleton" style="height:12px;width:70%;"></span>
2567
2908
  <span class="skeleton" style="height:12px;width:45%;"></span>
@@ -2575,7 +2916,7 @@ html { scroll-behavior: smooth; }
2575
2916
  </div>
2576
2917
  </div>
2577
2918
  <div class="ds-code-wrap"><details class="ds-code"><summary>Ver código</summary><pre><code>&lt;span class="skeleton" style="height:12px;width:70%"&gt;&lt;/span&gt;
2578
- &lt;span class="skeleton skeleton--circle" style="width:40px;height:40px"&gt;&lt;/span&gt;
2919
+ &lt;span class="skeleton skeleton--circle" style="width:min(100%, 40px);height:40px"&gt;&lt;/span&gt;
2579
2920
 
2580
2921
  &lt;div class="empty-state"&gt;
2581
2922
  &lt;svg&gt;…&lt;/svg&gt;
@@ -3000,7 +3341,7 @@ Guarda con &lt;kbd class="kbd"&gt;Ctrl&lt;/kbd&gt; + &lt;kbd class="kbd"&gt;S&lt
3000
3341
  <div class="ds-motion-box" id="mBox2">fade + glow</div>
3001
3342
  <button class="btn btn--outline btn--sm" id="mReplay">Reproducir</button>
3002
3343
  </div>
3003
- <p style="font-size:var(--text-sm);color:var(--text-muted);">Firma de marca: los elementos con acento entran con fade + glow — solo en el elemento hero/CTA. <span class="code">prefers-reduced-motion</span> colapsa todo a 0.01ms.</p>
3344
+ <p style="font-size:var(--text-sm);color:var(--text-muted);">Firma de marca: los elementos con acento entran con fade + glow — solo en el elemento hero/CTA. <span class="code">prefers-reduced-motion</span> conserva estados finales sin desplazamientos.</p>
3004
3345
  </section>
3005
3346
 
3006
3347
  <!-- ================= ACCESIBILIDAD ================= -->
@@ -3038,44 +3379,270 @@ Guarda con &lt;kbd class="kbd"&gt;Ctrl&lt;/kbd&gt; + &lt;kbd class="kbd"&gt;S&lt
3038
3379
  <p>El sistema se distribuye en <span class="code">dist/nocturna-electrica/</span>: <strong>nocturna-electrica.css</strong> (framework completo), <strong>tokens.css</strong> (solo variables), <strong>tailwind.css</strong> (bloque <span class="code">@theme</span> para Tailwind v4) y <strong>tokens.json</strong> (formato estándar W3C DTCG 2025.10, interoperable con Figma / Tokens Studio / Style Dictionary).</p>
3039
3380
 
3040
3381
  <h3>Variables CSS (núcleo)</h3>
3041
- <div class="ds-code-wrap"><details class="ds-code" open><summary>tokens :root (aliases semánticos)</summary><pre><code>:root {
3042
- /* Superficies */
3043
- --bg: #010203;
3044
- --surface-1: #0b0d12; --surface-2: #14171f; --surface-3: #1e222d;
3045
- --border: #3d4354; --border-strong: #6b7183;
3046
-
3047
- /* Texto */
3048
- --text-primary: #e8eaef; --text-secondary: #c5c9d4;
3049
- --text-muted: #9aa0b0; --text-disabled: #6b7183;
3050
- --text-heading: #f6f7f9; --text-on-accent: #010203;
3051
-
3052
- /* Acento e índigo */
3053
- --accent-400: #52baed; --accent-500: #2fa3dd; --accent-600: #1f87bb;
3054
- --indigo-500: #353d7c; --indigo-700: #22284f; --indigo-800: #1a1e3b;
3382
+ <div class="ds-code-wrap"><details class="ds-code" open><summary>tokens :root (aliases semánticos)</summary><pre><code>/* ============================================================
3383
+ Nocturna Eléctrica — Design Tokens v2.0
3384
+ Sistema de diseño de Higinio Sosa · dark-first
3385
+ Fuente de verdad: DESIGN-nocturna-electrica.md
3386
+ Uso: importar ANTES de cualquier estilo propio.
3387
+ Los componentes consumen SOLO aliases semánticos, nunca hex.
3388
+ ============================================================ */
3389
+
3390
+ @import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@500;600;700&amp;family=Manrope:wght@400;500;600;700&amp;family=JetBrains+Mono:wght@400;500&amp;display=swap');
3391
+
3392
+ :root {
3393
+ /* == Primitivos: acento (Celeste Eléctrico) ==
3394
+ accent-600 sobre blanco da 4.01:1 — vale como UI (≥3) pero NO como texto (&lt;4.5).
3395
+ Para texto sobre claro se usa accent-700 (5.95:1) y accent-800 (8.74:1). */
3396
+ --accent-100: #d9f0fb; --accent-200: #a8ddf6; --accent-300: #7dccf1;
3397
+ --accent-400: #52baed; --accent-500: #2fa3dd; --accent-600: #1f87bb; --accent-700: #186a94;
3398
+ --accent-800: #12506f;
3399
+
3400
+ /* == Primitivos: índigo (Índigo Profundo) — nunca texto sobre negro == */
3401
+ --indigo-300: #7a83c4; --indigo-400: #565fa3; --indigo-500: #353d7c;
3402
+ --indigo-600: #2b3264; --indigo-700: #22284f; --indigo-800: #1a1e3b;
3403
+
3404
+ /* == Primitivos: escala nocturna == */
3405
+ --night-950: #010203; --night-900: #0b0d12; --night-800: #14171f;
3406
+ --night-700: #1e222d; --night-600: #2a2f3d; --night-500: #3d4354;
3407
+ --night-450: #5f6575; /* texto atenuado en light: night-400 caía a 4.27:1 sobre --surface-3 */
3408
+ --night-400: #6b7183; --night-300: #9aa0b0; --night-200: #c5c9d4;
3409
+ --night-100: #e8eaef; --night-50: #f6f7f9;
3410
+
3411
+ /* == Primitivos: semánticos (calibrados para dark) == */
3412
+ --success-400: #4ade80; --success-bg: #0a2617;
3413
+ --warning-400: #fbbf24; --warning-bg: #2a2005;
3414
+ --danger-400: #f87171; --danger-bg: #2c0f0f;
3415
+ --info-400: #52baed; --info-bg: #0d1f2c;
3416
+
3417
+ /* == Aliases semánticos (los componentes consumen SOLO esto) == */
3418
+ --bg: var(--night-950);
3419
+ --surface-1: var(--night-900);
3420
+ --surface-2: var(--night-800);
3421
+ --surface-3: var(--night-700);
3422
+ --border: var(--night-500);
3423
+ /* night-400, no night-600: en dark, oscurecer un borde lo DEBILITA (principio 2:
3424
+ la elevación se comunica aclarando). n600 daba 1.46:1 vs los 1.97:1 del borde
3425
+ normal — el énfasis se veía más flojo. n400 = 3.99:1 sobre surface-1. */
3426
+ --border-strong: var(--night-400);
3427
+ --text-primary: var(--night-100);
3428
+ --text-secondary: var(--night-200);
3429
+ --text-muted: var(--night-300);
3430
+ --text-disabled: var(--night-400);
3431
+ --text-heading: var(--night-50);
3432
+ --text-on-accent: var(--night-950); /* texto de CTA oscuro, nunca blanco */
3433
+ --success: var(--success-400); --warning: var(--warning-400);
3434
+ --danger: var(--danger-400); --info: var(--info-400);
3435
+ --link: var(--accent-400);
3436
+ --link-hover: var(--accent-200);
3055
3437
  --accent-ghost-hover: rgba(82,186,237,.08);
3438
+ --accent-ghost-active: rgba(82,186,237,.14);
3056
3439
  --accent-selected: rgba(82,186,237,.12);
3440
+ --overlay: rgba(1,2,3,.7);
3057
3441
 
3058
- /* Semánticos */
3059
- --success: #4ade80; --warning: #fbbf24; --danger: #f87171; --info: #52baed;
3060
-
3061
- /* Tipografía */
3442
+ /* == Tipografía == */
3062
3443
  --font-display: "Space Grotesk", system-ui, sans-serif;
3063
3444
  --font-body: "Manrope", system-ui, sans-serif;
3064
3445
  --font-mono: "JetBrains Mono", monospace;
3446
+ --text-xs: .75rem; --text-sm: .875rem; --text-base: 1rem; --text-lg: 1.125rem;
3447
+ --text-xl: 1.25rem; --text-2xl: 1.5625rem; --text-3xl: 1.9375rem;
3448
+ --text-4xl: 2.4375rem; --text-5xl: 3.0625rem;
3449
+ --leading-tight: 1.05; --leading-heading: 1.15; --leading-h2: 1.2;
3450
+ --leading-h3: 1.3; --leading-h4: 1.4; --leading-snug: 1.5; --leading-body: 1.6;
3451
+ --tracking-display: -0.02em; --tracking-overline: 0.08em;
3452
+ --measure: 65ch;
3453
+
3454
+ /* == Espaciado (escala base 4px) == */
3455
+ --space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px;
3456
+ --space-5: 20px; --space-6: 24px; --space-8: 32px; --space-10: 40px;
3457
+ --space-12: 48px; --space-16: 64px; --space-20: 80px; --space-24: 96px;
3458
+ --card-padding: 24px; --card-padding-mobile: 16px;
3459
+ --section-gap: 64px; --form-gap: 20px;
3460
+ --button-padding: 12px 20px; --touch-target: 44px;
3461
+ --grid-gutter: 24px; --grid-gutter-mobile: 16px;
3462
+
3463
+ /* == Radios y bordes == */
3464
+ --radius-sm: 8px; --radius-md: 12px; --radius-lg: 16px;
3465
+ --radius-xl: 24px; --radius-full: 9999px;
3466
+ --border-default: 1px solid var(--night-500);
3467
+ --border-emphasis: 1px solid var(--night-400); /* ver --border-strong */
3468
+ --focus-ring: 2px solid var(--focus-color);
3469
+ --focus-ring-offset: 2px;
3065
3470
 
3066
- /* Radios y elevación */
3067
- --radius-sm: 8px; --radius-md: 12px; --radius-lg: 16px; --radius-full: 9999px;
3471
+ /* == Elevación (superficie + sombra + borde) == */
3068
3472
  --shadow-1: 0 1px 2px rgba(0,0,0,.4);
3069
3473
  --shadow-2: 0 4px 12px rgba(0,0,0,.5);
3070
3474
  --shadow-3: 0 12px 32px rgba(0,0,0,.6);
3071
- --glow-accent: 0 0 0 1px rgba(82,186,237,.35), 0 4px 20px rgba(82,186,237,.18);
3475
+ --glow-accent: 0 0 0 1px rgba(82,186,237,.35), 0 4px 20px rgba(82,186,237,.18); /* máx. uno por pantalla */
3072
3476
  --ring: 0 0 0 3px rgba(82,186,237,.2);
3477
+ --ring-danger: 0 0 0 3px rgba(248,113,113,.2);
3073
3478
 
3074
- /* Motion */
3075
- --duration-fast: 150ms; --duration-base: 220ms; --duration-slow: 250ms;
3479
+ /* == Motion == */
3480
+ --duration-instant: 100ms; --duration-fast: 150ms; --duration-base: 220ms;
3481
+ --duration-slow: 250ms; --duration-page: 350ms;
3076
3482
  --ease-out: cubic-bezier(0.2, 0, 0, 1);
3077
- --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
3483
+ --ease-in: cubic-bezier(0.4, 0, 1, 1);
3484
+ --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1); /* moverse de A a B en pantalla */
3078
3485
  --ease-spring: cubic-bezier(0.34, 1.4, 0.64, 1);
3486
+ --stagger-step: 30ms;
3487
+
3488
+ color-scheme: dark;
3489
+
3490
+ --focus-color: var(--accent-400);
3491
+ --border-hover: var(--night-400);
3492
+ --outline-hover-bg: var(--surface-2);
3493
+ --outline-active-bg: var(--surface-3);
3494
+ --text-on-danger: var(--night-950);
3495
+ --data-color: var(--accent-400);
3496
+ --elevated-border: var(--night-600);
3497
+ --card-border: var(--night-600);
3498
+ --code-color: var(--accent-300);
3499
+ --code-border: var(--night-700);
3500
+ --table-header-bg: var(--night-900);
3501
+ --row-border: var(--night-800);
3502
+ --row-hover: var(--night-700);
3503
+ --navbar-bg: rgba(1,2,3,.8);
3504
+ --nav-active: var(--accent-400);
3505
+ --sidebar-bg: var(--night-900);
3506
+ --sidebar-active-bg: var(--indigo-800);
3507
+ --sidebar-active-text: var(--night-50);
3508
+ --sidebar-active-icon: var(--accent-400);
3509
+ --switch-off: var(--night-600);
3510
+ --tooltip-bg: var(--night-700);
3511
+ --tooltip-border: var(--night-600);
3512
+ --tooltip-text: var(--night-100);
3513
+ --skeleton-base: var(--night-800);
3514
+ --skeleton-sheen: var(--night-700);
3515
+ --empty-icon: var(--indigo-400);
3516
+
3517
+ --action-bg: var(--accent);
3518
+ --action-hover: var(--accent-hover);
3519
+ --action-active: var(--accent-active);
3520
+ --text-on-action: var(--text-on-accent);
3521
+ --control-border: var(--border-strong);
3522
+ --placeholder: var(--text-muted);
3523
+ --selection-bg: var(--action-bg);
3524
+ --selection-text: var(--text-on-action);
3525
+ --font-ui-heading: var(--font-body);
3526
+ --text-ui-title: 2rem;
3527
+ --control-height-md: 44px;
3528
+ --button-padding-md: 12px 20px;
3529
+ --surface: var(--surface-1);
3530
+ --surface-alt: var(--surface-2);
3531
+ --accent: var(--accent-400);
3532
+ --accent-hover: var(--accent-300);
3533
+ --accent-active: var(--accent-500);
3534
+ --text-display-fluid: clamp(2.5rem, 1.5rem + 3.4vw, 5rem);
3535
+ --text-on-primary-solid: var(--link);
3536
+ --weight-regular: 400;
3537
+ --weight-medium: 500;
3538
+ --weight-semibold: 600;
3539
+ --weight-bold: 700;
3540
+ --leading-base: 1.6;
3541
+ --leading-lg: 1.55;
3542
+ --gutter: 24px;
3543
+ --gutter-mobile: 16px;
3544
+
3545
+ /* Series categóricas: orden estable; acompañar con etiqueta o forma. */
3546
+ --chart-1: #52baed;
3547
+ --chart-2: #a0587c;
3548
+ --chart-3: #2aa79e;
3549
+ --chart-4: #aa5849;
3550
+ --chart-5: #67a369;
3551
+ --chart-6: #946a1c;
3552
+ --chart-7: #877cc3;
3553
+ /* Ritmo de contenido y formularios. */
3554
+ --field-gap: 6px;
3555
+ --heading-gap: var(--space-2);
3556
+ --content-gap: var(--space-4);
3557
+ --card-content-gap: var(--space-4);
3558
+ --card-group-gap: var(--space-6);
3559
+
3560
+ }
3561
+
3562
+ /* == Modo claro (variante) — alternar data-theme en &lt;html&gt; == */
3563
+ [data-theme="light"] {
3564
+ --bg: #f5f8fb;
3565
+ --surface-1: #ffffff;
3566
+ --surface-2: #ffffff;
3567
+ --surface-3: #eaf1f7;
3568
+ --border: #d8dfe8;
3569
+ /* border-strong NO se redefine en claro: night-400 (:root) cumple 1.4.11 en
3570
+ AMBOS temas (3.26-3.99:1 en dark, 4.27-4.87:1 en light). #b9c3d1 daba
3571
+ 1.53-1.78:1, bajo el mínimo de 3:1 para UI. */
3572
+ --text-primary: #14171f;
3573
+ --text-secondary: #3d4354;
3574
+ --text-muted: var(--night-450); /* no night-400: ese caía a 4.27:1 sobre --surface-3 */
3575
+ --text-disabled: #9aa0b0;
3576
+ --text-heading: #14171f;
3577
+ --link: #186a94; /* accent-700 · 5.95:1 sobre blanco — accent-600 solo daba 4.01:1 */
3578
+ --link-hover: #12506f; /* accent-800 · en light el hover OSCURECE (en dark aclara) */
3579
+ --text-on-accent: #010203; /* CTA no cambia entre modos */
3580
+ /* Semánticos de light: son a la vez relleno y TEXTO sobre su propio --*-bg tintado,
3581
+ así que se calibran contra ese fondo, no contra blanco. */
3582
+ --success: #166534;
3583
+ --warning: #854d0e;
3584
+ --danger: #b02c2c;
3585
+ --info: #186a94;
3586
+ --success-bg: #e3f5ea; --warning-bg: #fbf3dd;
3587
+ --danger-bg: #fbe7e7; --info-bg: #e3f2fb;
3588
+ --accent-ghost-hover: rgba(31,135,187,.08);
3589
+ --accent-ghost-active: rgba(31,135,187,.14);
3590
+ --accent-selected: rgba(82,186,237,.12);
3591
+ --overlay: rgba(20,23,31,.45);
3592
+ --ring: 0 0 0 3px rgba(31,135,187,.25);
3593
+ --shadow-1: 0 1px 3px rgba(53,61,124,.08);
3594
+ --shadow-2: 0 4px 10px rgba(53,61,124,.10);
3595
+ --shadow-3: 0 12px 28px rgba(53,61,124,.14);
3596
+ --glow-accent: 0 0 0 1px rgba(82,186,237,.4), 0 4px 16px rgba(82,186,237,.25);
3597
+ color-scheme: light;
3598
+
3599
+ --focus-color: #186a94;
3600
+ --border-hover: #5f6575;
3601
+ --outline-hover-bg: #eaf1f7;
3602
+ --outline-active-bg: #d8dfe8;
3603
+ --text-on-danger: #ffffff;
3604
+ --data-color: #186a94;
3605
+ --elevated-border: #d8dfe8;
3606
+ --card-border: #d8dfe8;
3607
+ --code-color: #186a94;
3608
+ --code-border: #d8dfe8;
3609
+ --table-header-bg: #eaf1f7;
3610
+ --row-border: #eaf1f7;
3611
+ --row-hover: #f5f8fb;
3612
+ --navbar-bg: rgba(255,255,255,.8);
3613
+ --nav-active: #186a94;
3614
+ --sidebar-bg: #ffffff;
3615
+ --sidebar-active-bg: #e3f2fb;
3616
+ --sidebar-active-text: #14171f;
3617
+ --sidebar-active-icon: #1f87bb;
3618
+ --switch-off: var(--control-border);
3619
+ --tooltip-bg: #14171f;
3620
+ --tooltip-border: #14171f;
3621
+ --tooltip-text: #f6f7f9;
3622
+ --skeleton-base: #eaf1f7;
3623
+ --skeleton-sheen: #f5f8fb;
3624
+ --ring-danger: 0 0 0 3px rgba(192,53,53,.2);
3625
+
3626
+ --selection-bg: var(--action-bg);
3627
+ --selection-text: var(--text-on-action);
3628
+
3629
+ /* Series categóricas: orden estable; acompañar con etiqueta o forma. */
3630
+ --chart-1: #186a94;
3631
+ --chart-2: #853f63;
3632
+ --chart-3: #129a92;
3633
+ --chart-4: #8e4031;
3634
+ --chart-5: #5b975e;
3635
+ --chart-6: #8a600a;
3636
+ --chart-7: #857bc1;
3637
+ }
3638
+
3639
+ /* == Accesibilidad del movimiento == */
3640
+
3641
+ @media (prefers-reduced-motion: reduce) {
3642
+ :root {
3643
+ --duration-instant: 0ms; --duration-fast: 0ms; --duration-base: 0ms;
3644
+ --duration-slow: 0ms; --duration-page: 0ms; --stagger-step: 0ms;
3645
+ }
3079
3646
  }</code></pre></details><button class="ds-copy" type="button">Copiar</button></div>
3080
3647
 
3081
3648
  <h3 style="margin-top:32px;">Tailwind CSS v4 — <span class="code">@theme</span></h3>
@@ -3292,36 +3859,19 @@ if (themeBtn) {
3292
3859
  });
3293
3860
  }
3294
3861
 
3295
- /* ---------- Sidebar móvil ---------- */
3296
- var sidebar = document.getElementById('dsSidebar');
3297
- var hamburger = document.getElementById('dsHamburger');
3298
- if (hamburger && sidebar) {
3299
- hamburger.addEventListener('click', function(){
3300
- sidebar.classList.toggle('is-open');
3301
- });
3302
- sidebar.addEventListener('click', function(e){
3303
- if (e.target.tagName === 'A') sidebar.classList.remove('is-open');
3304
- });
3305
- document.addEventListener('click', function(e){
3306
- if (sidebar.classList.contains('is-open') && !sidebar.contains(e.target) && e.target !== hamburger) {
3307
- sidebar.classList.remove('is-open');
3308
- }
3309
- });
3310
- }
3311
-
3312
3862
  /* ---------- Hero CTAs ---------- */
3313
3863
  var heroCtaBuild = document.getElementById('heroCtaBuild');
3314
3864
  if (heroCtaBuild) {
3315
3865
  heroCtaBuild.addEventListener('click', function(){
3316
3866
  var target = document.getElementById('componentes');
3317
- if (target) target.scrollIntoView({ behavior: 'smooth' });
3867
+ if (target) target.scrollIntoView({ behavior: reduced ? 'auto' : 'smooth' });
3318
3868
  });
3319
3869
  }
3320
3870
  var heroCtaTokens = document.getElementById('heroCtaTokens');
3321
3871
  if (heroCtaTokens) {
3322
3872
  heroCtaTokens.addEventListener('click', function(){
3323
3873
  var target = document.getElementById('tokens');
3324
- if (target) target.scrollIntoView({ behavior: 'smooth' });
3874
+ if (target) target.scrollIntoView({ behavior: reduced ? 'auto' : 'smooth' });
3325
3875
  });
3326
3876
  }
3327
3877
 
@@ -3353,7 +3903,7 @@ function swatches(el, items, darkText){
3353
3903
  d.addEventListener('click', function(){
3354
3904
  copyText('var(--' + it[0] + ')', function(){
3355
3905
  var n = d.querySelector('.ds-swatch__name'), old = n.textContent;
3356
- n.textContent = '¡Copiado!'; n.style.color = 'var(--success)';
3906
+ n.textContent = 'Copiado'; n.style.color = 'var(--success)';
3357
3907
  setTimeout(function(){ n.textContent = old; n.style.color = ''; }, 1200);
3358
3908
  });
3359
3909
  });
@@ -3411,7 +3961,7 @@ document.querySelectorAll('.ds-copy').forEach(function(btn){
3411
3961
  var code = box ? box.querySelector('code') : null;
3412
3962
  if (code) {
3413
3963
  copyText(code.textContent, function(){
3414
- btn.textContent = '¡Copiado!'; btn.classList.add('is-done');
3964
+ btn.textContent = 'Copiado'; btn.classList.add('is-done');
3415
3965
  setTimeout(function(){ btn.textContent = 'Copiar'; btn.classList.remove('is-done'); }, 1500);
3416
3966
  });
3417
3967
  }
@@ -3889,31 +4439,6 @@ if (demoSlider && demoSliderValue) {
3889
4439
  });
3890
4440
  }
3891
4441
 
3892
- /* ---------- Count-up ---------- */
3893
- var fmt = {};
3894
- function fmtFor(dec){ return fmt[dec] || (fmt[dec] = new Intl.NumberFormat('es-MX', { minimumFractionDigits: dec, maximumFractionDigits: dec })); }
3895
- function countUp(el){
3896
- var target = parseFloat(el.dataset.countup);
3897
- var dec = parseInt(el.dataset.decimals || '0', 10);
3898
- var prefix = el.dataset.prefix || '', suffix = el.dataset.suffix || '';
3899
- if (reduced) { el.textContent = prefix + fmtFor(dec).format(target) + suffix; return; }
3900
- var t0 = performance.now();
3901
- function frame(t){
3902
- var p = Math.min((t - t0) / 350, 1);
3903
- var e = 1 - Math.pow(1 - p, 3);
3904
- el.textContent = prefix + fmtFor(dec).format(target * e) + suffix;
3905
- if (p < 1) requestAnimationFrame(frame);
3906
- }
3907
- requestAnimationFrame(frame);
3908
- }
3909
- var seen = new WeakSet();
3910
- var cuObs = new IntersectionObserver(function(entries){
3911
- entries.forEach(function(en){
3912
- if (en.isIntersecting && !seen.has(en.target)) { seen.add(en.target); countUp(en.target); }
3913
- });
3914
- }, { threshold: .4 });
3915
- document.querySelectorAll('[data-countup]').forEach(function(el){ cuObs.observe(el); });
3916
-
3917
4442
  /* ---------- Pagination (demo funcional) ---------- */
3918
4443
  var paginationEl = document.getElementById('paginationDemo');
3919
4444
  if (paginationEl) {
@@ -4093,6 +4618,75 @@ if (mReplayBtn) {
4093
4618
  }
4094
4619
  })();
4095
4620
  })();
4621
+ </script>
4622
+ <!-- INTERACCIONES COMPARTIDAS GENERADAS -->
4623
+ <script>
4624
+ (function () {
4625
+ "use strict";
4626
+ var trigger = document.getElementById("dsHamburger");
4627
+ var panel = document.getElementById("dsSidenav") || document.getElementById("dsSidebar");
4628
+ var mobile = window.matchMedia("(max-width: 1023px)");
4629
+
4630
+ /**
4631
+ * Actualiza la navegación como disclosure, sin crear una trampa de foco.
4632
+ * @param {boolean} open - Si la navegación móvil debe estar disponible.
4633
+ * @param {boolean} restore - Si el cierre debe devolver foco al disparador.
4634
+ * @returns {void}
4635
+ */
4636
+ function setNavigation(open, restore) {
4637
+ if (!trigger || !panel) return;
4638
+ panel.classList.toggle("is-open", open);
4639
+ panel.inert = mobile.matches && !open;
4640
+ trigger.setAttribute("aria-expanded", String(open));
4641
+ trigger.setAttribute("aria-label", open ? "Cerrar navegación" : "Abrir navegación");
4642
+ if (restore) trigger.focus();
4643
+ }
4644
+
4645
+ if (trigger && panel) {
4646
+ trigger.setAttribute("aria-controls", panel.id);
4647
+ setNavigation(false, false);
4648
+ trigger.addEventListener("click", function () { setNavigation(!panel.classList.contains("is-open"), false); });
4649
+ panel.addEventListener("click", function (event) {
4650
+ var link = event.target.closest("a[href^='#']");
4651
+ if (!link || !mobile.matches) return;
4652
+ var target = document.getElementById(link.hash.slice(1));
4653
+ setNavigation(false, false);
4654
+ if (target) {
4655
+ target.setAttribute("tabindex", "-1");
4656
+ target.focus({ preventScroll: true });
4657
+ }
4658
+ });
4659
+ document.addEventListener("keydown", function (event) {
4660
+ if (event.key === "Escape" && mobile.matches && panel.classList.contains("is-open")) setNavigation(false, true);
4661
+ });
4662
+ document.addEventListener("click", function (event) {
4663
+ if (mobile.matches && !panel.contains(event.target) && !trigger.contains(event.target)) setNavigation(false, false);
4664
+ });
4665
+ mobile.addEventListener("change", function () { setNavigation(false, false); });
4666
+ }
4667
+
4668
+ var themeButton = document.getElementById("dsThemeToggle") || document.getElementById("dsTheme");
4669
+ /**
4670
+ * Nombra la acción de tema y mantiene semántica de ubicación en el índice.
4671
+ * @returns {void}
4672
+ */
4673
+ function updateLabels() {
4674
+ if (themeButton) themeButton.setAttribute("aria-label", document.documentElement.dataset.theme === "dark" ? "Usar tema claro" : "Usar tema oscuro");
4675
+ document.querySelectorAll(".ds-nav a").forEach(function (link) {
4676
+ if (link.classList.contains("is-active")) link.setAttribute("aria-current", "location");
4677
+ else link.removeAttribute("aria-current");
4678
+ });
4679
+ var style = getComputedStyle(document.documentElement);
4680
+ document.querySelectorAll("[data-chart-color]").forEach(function (label) {
4681
+ label.textContent = style.getPropertyValue(label.getAttribute("data-chart-color")).trim();
4682
+ });
4683
+ }
4684
+ updateLabels();
4685
+ new MutationObserver(updateLabels).observe(document.documentElement, { attributes: true, attributeFilter: ["data-theme"] });
4686
+ var nav = document.querySelector(".ds-nav");
4687
+ if (nav) new MutationObserver(updateLabels).observe(nav, { subtree: true, attributes: true, attributeFilter: ["class"] });
4688
+ })();
4689
+
4096
4690
  </script>
4097
4691
  </body>
4098
4692
  </html>