@higinio/design-mcp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3060 @@
1
+ <!DOCTYPE html>
2
+ <html lang="es" data-theme="light">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Celeste Clásica — Sistema de Diseño</title>
7
+ <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='%237192ba'/%3E%3Ctext x='16' y='22' font-family='Georgia,serif' font-size='18' font-weight='700' fill='white' text-anchor='middle'%3EH%3C/text%3E%3C/svg%3E">
8
+ <meta name="description" content="Sistema de diseño Celeste Clásica de Higinio Sosa: tokens, componentes y guías de uso. Light-first, sereno y confiable.">
9
+ <script>
10
+ /* Aplica el tema guardado antes del primer render para evitar parpadeo */
11
+ (function () {
12
+ var t = null;
13
+ try { t = localStorage.getItem("cc-theme"); } catch (e) {}
14
+ if (t === "dark" || t === "light") document.documentElement.dataset.theme = t;
15
+ })();
16
+ </script>
17
+ <style>
18
+ /* ==========================================================================
19
+ CELESTE CLÁSICA — Framework CSS v2.0 (embebido)
20
+ Idéntico a dist/celeste-clasica/celeste-clasica.css
21
+ ========================================================================== */
22
+
23
+ @import url("https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,600;0,9..144,700;1,9..144,600&family=Public+Sans:ital,wght@0,400;0,500;0,600;0,700;1,400&family=IBM+Plex+Mono:wght@400;500&display=swap");
24
+
25
+ :root {
26
+ --primary-50: #f2f6fa; --primary-100: #dfe9f2; --primary-200: #c3d5e6;
27
+ --primary-300: #a0c0d4; --primary-400: #87a8c7; --primary-500: #7192ba;
28
+ --primary-600: #5d7ba3; --primary-700: #4d6588; --primary-800: #40536e;
29
+ --primary-900: #31343e;
30
+
31
+ --neutral-0: #ffffff; --neutral-50: #f7f8fa; --neutral-100: #eef0f3;
32
+ --neutral-200: #dcdfe5; --neutral-300: #b8bdc7; --neutral-400: #8a8f9c;
33
+ --neutral-500: #666b78; --neutral-600: #4d5160; --neutral-700: #3d414e;
34
+ --neutral-800: #31343e; --neutral-900: #22242b;
35
+
36
+ --success-500: #3d9970; --success-100: #e2f3ec; --success-text: #2b6e50;
37
+ --warning-500: #c9922e; --warning-100: #faf1de; --warning-text: #8a651c;
38
+ --danger-500: #c04d4d; --danger-100: #f9e7e7; --danger-text: #8f3232;
39
+ --info-500: #7192ba; --info-100: #dfe9f2; --info-text: #4d6588;
40
+
41
+ --bg: var(--neutral-50);
42
+ --surface: var(--neutral-0);
43
+ --surface-alt: var(--neutral-100);
44
+ --border: var(--neutral-200);
45
+ --border-strong: var(--neutral-300);
46
+ --text-primary: var(--neutral-800);
47
+ --text-secondary: var(--neutral-600);
48
+ --text-muted: var(--neutral-500);
49
+ --text-disabled: var(--neutral-400);
50
+ --text-on-primary: #ffffff;
51
+ --text-on-primary-solid: var(--primary-700);
52
+ --accent: var(--primary-500);
53
+ --accent-hover: var(--primary-600);
54
+ --accent-active: var(--primary-700);
55
+ --accent-soft: var(--primary-50);
56
+ --accent-soft-strong: var(--primary-100);
57
+ --link: var(--primary-700);
58
+ --overlay: rgba(49, 52, 62, .5);
59
+ --tooltip-bg: var(--neutral-900);
60
+ --tooltip-text: #ffffff;
61
+
62
+ --font-display: "Fraunces", Georgia, serif;
63
+ --font-body: "Public Sans", system-ui, -apple-system, sans-serif;
64
+ --font-mono: "IBM Plex Mono", ui-monospace, monospace;
65
+ --text-xs: .75rem; --text-sm: .875rem; --text-base: 1rem; --text-lg: 1.125rem;
66
+ --text-xl: 1.25rem; --text-2xl: 1.5625rem; --text-3xl: 1.9375rem;
67
+ --text-4xl: 2.4375rem; --text-5xl: 3.0625rem;
68
+ --leading-xs: 1.5; --leading-sm: 1.5; --leading-base: 1.6; --leading-lg: 1.55;
69
+ --leading-xl: 1.4; --leading-2xl: 1.3; --leading-3xl: 1.25;
70
+ --leading-4xl: 1.15; --leading-5xl: 1.1;
71
+ --tracking-display: -0.02em; --tracking-body: 0; --tracking-label: 0.04em;
72
+ --weight-regular: 400; --weight-medium: 500; --weight-semibold: 600; --weight-bold: 700;
73
+ --measure: 65ch;
74
+
75
+ --space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px;
76
+ --space-5: 20px; --space-6: 24px; --space-8: 32px; --space-10: 40px;
77
+ --space-12: 48px; --space-16: 64px; --space-20: 80px; --space-24: 96px;
78
+ --card-padding: var(--space-6); --card-padding-mobile: var(--space-4);
79
+ --section-gap: var(--space-16); --form-gap: var(--space-5);
80
+ --button-padding-md: 12px 20px; --button-padding-sm: 8px 14px;
81
+ --control-height-sm: 36px; --control-height-md: 44px; --control-height-lg: 52px;
82
+ --gutter: 24px; --gutter-mobile: 16px;
83
+
84
+ --radius-sm: 6px; --radius-md: 10px; --radius-lg: 14px;
85
+ --radius-xl: 20px; --radius-full: 9999px;
86
+ --border-width: 1px;
87
+ --shadow-xs: 0 1px 2px rgba(49,52,62,.06);
88
+ --shadow-sm: 0 1px 3px rgba(49,52,62,.08), 0 1px 2px rgba(49,52,62,.05);
89
+ --shadow-md: 0 4px 8px rgba(49,52,62,.09), 0 2px 4px rgba(49,52,62,.05);
90
+ --shadow-lg: 0 12px 24px rgba(49,52,62,.12), 0 4px 8px rgba(49,52,62,.06);
91
+ --shadow-xl: 0 24px 48px rgba(49,52,62,.16);
92
+
93
+ --ring: 0 0 0 3px rgba(113,146,186,.25);
94
+ --ring-color: rgba(113,146,186,.25);
95
+ --ring-danger: 0 0 0 3px rgba(192,77,77,.22);
96
+
97
+ --duration-instant: 100ms; --duration-fast: 150ms; --duration-base: 200ms;
98
+ --duration-slow: 250ms; --duration-page: 320ms;
99
+ --ease-out: cubic-bezier(0.2, 0, 0, 1);
100
+ --ease-in: cubic-bezier(0.4, 0, 1, 1);
101
+ --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1); /* moverse de A a B en pantalla */
102
+ --ease-spring: cubic-bezier(0.34, 1.4, 0.64, 1);
103
+
104
+ color-scheme: light;
105
+ }
106
+
107
+ [data-theme="dark"] {
108
+ --bg: #16181e;
109
+ --surface: #1d2028;
110
+ --surface-alt: #252935;
111
+ --border: #3a3f4d;
112
+ --border-strong: #4a5060;
113
+ --text-primary: #e6e9ee;
114
+ --text-secondary: #b9bfcb;
115
+ --text-muted: #8b91a0;
116
+ --text-disabled: #6f7585;
117
+ --accent: #87a8c7;
118
+ --accent-hover: #a0c0d4;
119
+ --accent-active: #7192ba;
120
+ --accent-soft: rgba(113,146,186,.12);
121
+ --accent-soft-strong: rgba(113,146,186,.18);
122
+ --text-on-primary: #16181e;
123
+ --text-on-primary-solid: #a0c0d4;
124
+ --link: #a0c0d4;
125
+ --overlay: rgba(0, 0, 0, .6);
126
+ --tooltip-bg: #e6e9ee;
127
+ --tooltip-text: #31343e;
128
+ --success-500: #5cbf93; --success-100: #12291f; --success-text: #7fd4ae;
129
+ --warning-500: #e0ad4d; --warning-100: #2b2210; --warning-text: #ecc57e;
130
+ --danger-500: #e07777; --danger-100: #2e1515; --danger-text: #eb9e9e;
131
+ --info-500: #87a8c7; --info-100: #1a2430; --info-text: #a0c0d4;
132
+ --ring: 0 0 0 3px rgba(160,192,212,.3);
133
+ --ring-danger: 0 0 0 3px rgba(224,119,119,.25);
134
+ --shadow-sm: 0 1px 2px rgba(0,0,0,.35);
135
+ --shadow-md: 0 4px 12px rgba(0,0,0,.45);
136
+ --shadow-lg: 0 12px 32px rgba(0,0,0,.55);
137
+ color-scheme: dark;
138
+ }
139
+
140
+ *, *::before, *::after { box-sizing: border-box; }
141
+ html, body { overflow-x: clip; }
142
+
143
+ :where(a, button, [role="button"], input, select, textarea, summary, [tabindex]):focus-visible {
144
+ outline: 2px solid var(--primary-500);
145
+ outline-offset: 2px;
146
+ border-radius: 2px;
147
+ }
148
+
149
+ .celeste-reset {
150
+ margin: 0;
151
+ background: var(--bg);
152
+ color: var(--text-primary);
153
+ font-family: var(--font-body);
154
+ font-size: var(--text-base);
155
+ line-height: var(--leading-base);
156
+ letter-spacing: var(--tracking-body);
157
+ -webkit-font-smoothing: antialiased;
158
+ text-rendering: optimizeLegibility;
159
+ }
160
+ .celeste-reset h1, .celeste-reset h2 {
161
+ font-family: var(--font-display);
162
+ font-weight: var(--weight-bold);
163
+ letter-spacing: var(--tracking-display);
164
+ color: var(--text-primary);
165
+ margin: 0 0 .5em;
166
+ }
167
+ .celeste-reset h1 { font-size: var(--text-4xl); line-height: var(--leading-4xl); }
168
+ .celeste-reset h2 { font-size: var(--text-3xl); line-height: var(--leading-3xl); font-weight: var(--weight-semibold); }
169
+ .celeste-reset h3, .celeste-reset h4, .celeste-reset h5, .celeste-reset h6 {
170
+ font-family: var(--font-body);
171
+ font-weight: var(--weight-semibold);
172
+ color: var(--text-primary);
173
+ margin: 0 0 .5em;
174
+ }
175
+ .celeste-reset h3 { font-size: var(--text-2xl); line-height: var(--leading-2xl); }
176
+ .celeste-reset h4 { font-size: var(--text-xl); line-height: var(--leading-xl); }
177
+ .celeste-reset h5 { font-size: var(--text-lg); line-height: var(--leading-lg); }
178
+ .celeste-reset h6 { font-size: var(--text-base); line-height: var(--leading-base); }
179
+ .celeste-reset p { max-width: var(--measure); margin: 0 0 1em; }
180
+ .celeste-reset table { font-variant-numeric: tabular-nums; }
181
+ .celeste-reset ::placeholder { color: var(--text-disabled); }
182
+ .celeste-reset ::selection { background: rgba(113,146,186,.25); }
183
+
184
+ @media (prefers-reduced-motion: reduce) {
185
+ *, *::before, *::after {
186
+ animation-duration: 0.01ms !important;
187
+ animation-iteration-count: 1 !important;
188
+ transition-duration: 0.01ms !important;
189
+ scroll-behavior: auto !important;
190
+ }
191
+ }
192
+
193
+ .container { width: 100%; margin-inline: auto; padding-inline: var(--gutter-mobile); }
194
+ @media (min-width: 640px) { .container { padding-inline: var(--space-4); } }
195
+ @media (min-width: 768px) { .container { max-width: 720px; } }
196
+ @media (min-width: 1024px) { .container { max-width: 960px; } }
197
+ @media (min-width: 1280px) { .container { max-width: 1200px; } }
198
+
199
+ .grid { display: grid; grid-template-columns: repeat(12, 1fr); gap: var(--gutter-mobile); }
200
+ @media (min-width: 768px) { .grid { gap: var(--gutter); } }
201
+ .col-1{grid-column:span 1}.col-2{grid-column:span 2}.col-3{grid-column:span 3}
202
+ .col-4{grid-column:span 4}.col-5{grid-column:span 5}.col-6{grid-column:span 6}
203
+ .col-7{grid-column:span 7}.col-8{grid-column:span 8}.col-9{grid-column:span 9}
204
+ .col-10{grid-column:span 10}.col-11{grid-column:span 11}.col-12{grid-column:span 12}
205
+ @media (min-width: 768px) {
206
+ .col-md-3{grid-column:span 3}.col-md-4{grid-column:span 4}
207
+ .col-md-6{grid-column:span 6}.col-md-8{grid-column:span 8}.col-md-12{grid-column:span 12}
208
+ }
209
+ @media (min-width: 1024px) {
210
+ .col-lg-3{grid-column:span 3}.col-lg-4{grid-column:span 4}
211
+ .col-lg-6{grid-column:span 6}.col-lg-8{grid-column:span 8}.col-lg-12{grid-column:span 12}
212
+ }
213
+
214
+ .btn {
215
+ display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
216
+ height: var(--control-height-md); padding: var(--button-padding-md);
217
+ border: var(--border-width) solid transparent; border-radius: var(--radius-md);
218
+ font-family: var(--font-body); font-size: var(--text-base); font-weight: var(--weight-semibold);
219
+ line-height: 1; color: var(--text-primary); background: transparent;
220
+ cursor: pointer; text-decoration: none; white-space: nowrap; user-select: none;
221
+ transition: background-color var(--duration-fast) var(--ease-out),
222
+ border-color var(--duration-fast) var(--ease-out),
223
+ box-shadow var(--duration-fast) var(--ease-out),
224
+ transform var(--duration-instant) var(--ease-out);
225
+ }
226
+ .btn:active:not(:disabled) { transform: scale(.98); }
227
+ .btn:disabled { cursor: not-allowed; }
228
+ .btn svg { width: 20px; height: 20px; flex: none; }
229
+
230
+ /* Gating de hover (§11.5): los hover que mueven/elevan van dentro de
231
+ @media (hover: hover) and (pointer: fine) — en touch el tap deja el hover pegado.
232
+ Los hover de solo color/fondo quedan fuera. */
233
+ .btn--primary { background: var(--accent); color: var(--text-on-primary); }
234
+ .btn--primary:hover:not(:disabled) { background: var(--accent-hover); }
235
+ @media (hover: hover) and (pointer: fine) {
236
+ .btn--primary:hover:not(:disabled) { box-shadow: var(--shadow-sm); }
237
+ }
238
+ .btn--primary:active:not(:disabled) { background: var(--accent-active); box-shadow: none; }
239
+ .btn--primary:disabled { background: var(--border); color: var(--text-disabled); box-shadow: none; }
240
+
241
+ .btn--secondary { background: var(--surface); border-color: var(--border-strong); color: var(--text-primary); }
242
+ .btn--secondary:hover:not(:disabled) { background: var(--bg); }
243
+ .btn--secondary:active:not(:disabled) { background: var(--surface-alt); }
244
+ .btn--secondary:disabled { opacity: .5; }
245
+
246
+ .btn--outline { background: transparent; border-color: var(--accent); color: var(--link); }
247
+ .btn--outline:hover:not(:disabled) { background: var(--accent-soft); border-color: var(--accent-hover); }
248
+ .btn--outline:active:not(:disabled) { background: var(--accent-soft-strong); }
249
+ .btn--outline:disabled { opacity: .5; }
250
+
251
+ .btn--ghost { color: var(--link); }
252
+ .btn--ghost:hover:not(:disabled) { background: var(--accent-soft); }
253
+ .btn--ghost:active:not(:disabled) { background: var(--accent-soft-strong); }
254
+ .btn--ghost:disabled { background: var(--border); color: var(--text-disabled); }
255
+
256
+ .btn--danger { background: var(--danger-500); color: #fff; }
257
+ .btn--danger:hover:not(:disabled) { background: color-mix(in srgb, var(--danger-500) 92%, #000); }
258
+ .btn--danger:active:not(:disabled) { background: color-mix(in srgb, var(--danger-500) 86%, #000); }
259
+ .btn--danger:disabled { opacity: .5; }
260
+
261
+ .btn--sm { height: var(--control-height-sm); padding: var(--button-padding-sm); font-size: var(--text-sm); }
262
+ .btn--lg { height: var(--control-height-lg); padding: 14px 24px; font-size: var(--text-lg); }
263
+
264
+ .icon-btn {
265
+ display: inline-grid; place-items: center;
266
+ width: var(--control-height-md); height: var(--control-height-md); padding: 0;
267
+ border: var(--border-width) solid transparent; border-radius: var(--radius-md);
268
+ background: transparent; color: var(--text-secondary); cursor: pointer;
269
+ transition: background-color var(--duration-fast) var(--ease-out),
270
+ border-color var(--duration-fast) var(--ease-out),
271
+ transform var(--duration-instant) var(--ease-out);
272
+ }
273
+ .icon-btn:active:not(:disabled) { transform: scale(.98); }
274
+ .icon-btn:disabled { opacity: .5; cursor: not-allowed; }
275
+ .icon-btn svg { width: 18px; height: 18px; }
276
+ .icon-btn--sm { width: var(--control-height-sm); height: var(--control-height-sm); } .icon-btn--sm svg { width: 16px; height: 16px; }
277
+ .icon-btn--lg { width: 48px; height: 48px; } .icon-btn--lg svg { width: 20px; height: 20px; }
278
+ .icon-btn--ghost:hover:not(:disabled) { background: var(--surface-alt); color: var(--text-primary); }
279
+ .icon-btn--primary { background: var(--accent); color: var(--text-on-primary); }
280
+ .icon-btn--primary:hover:not(:disabled) { background: var(--accent-hover); }
281
+ .icon-btn--secondary { background: var(--surface); border-color: var(--border-strong); }
282
+ .icon-btn--secondary:hover:not(:disabled) { background: var(--bg); }
283
+
284
+ .spinner { width: 16px; height: 16px; animation: cc-spin .8s linear infinite; color: var(--accent); }
285
+ .spinner circle { opacity: .25; }
286
+ .spinner path { opacity: 1; }
287
+ @keyframes cc-spin { to { transform: rotate(360deg); } }
288
+
289
+ .field { display: flex; flex-direction: column; gap: 6px; }
290
+ .field__label { font-size: var(--text-sm); font-weight: var(--weight-medium); color: var(--text-secondary); }
291
+ .field__help { font-size: var(--text-sm); color: var(--text-muted); }
292
+ .field__error { font-size: var(--text-sm); color: var(--danger-500); display: inline-flex; align-items: center; gap: 6px; }
293
+ .field__error svg { width: 14px; height: 14px; flex: none; }
294
+
295
+ .input, .textarea, .select {
296
+ width: 100%; font-family: var(--font-body); font-size: var(--text-base);
297
+ color: var(--text-primary); background: var(--surface);
298
+ border: var(--border-width) solid var(--border-strong); border-radius: var(--radius-sm);
299
+ transition: border-color var(--duration-fast) var(--ease-out),
300
+ box-shadow var(--duration-fast) var(--ease-out);
301
+ }
302
+ .input, .select { height: var(--control-height-md); padding: 0 14px; }
303
+ .input:hover:not(:disabled):not(:focus),
304
+ .textarea:hover:not(:disabled):not(:focus),
305
+ .select:hover:not(:disabled):not(:focus) { border-color: var(--text-disabled); }
306
+ .input:focus, .textarea:focus, .select:focus {
307
+ outline: none; border-color: var(--accent); box-shadow: var(--ring);
308
+ }
309
+ .input:disabled, .textarea:disabled, .select:disabled {
310
+ background: var(--surface-alt); color: var(--text-disabled); cursor: not-allowed;
311
+ }
312
+ .input--error, .input--error:focus, .textarea--error { border-color: var(--danger-500); box-shadow: var(--ring-danger); }
313
+ .input--success { border-color: var(--success-500); }
314
+
315
+ .input-icon { position: relative; }
316
+ .input-icon > svg {
317
+ position: absolute; left: 12px; top: 50%; transform: translateY(-50%);
318
+ width: 18px; height: 18px; color: var(--text-disabled); pointer-events: none;
319
+ }
320
+ .input-icon .input { padding-left: 40px; }
321
+
322
+ .textarea { min-height: 96px; padding: 12px 14px; resize: vertical; line-height: var(--leading-base); }
323
+ .textarea-counter { font-size: var(--text-xs); color: var(--text-disabled); text-align: right; }
324
+
325
+ .select-wrap { position: relative; }
326
+ .select { appearance: none; padding-right: 38px; cursor: pointer; }
327
+ .select-wrap::after {
328
+ content: ""; position: absolute; right: 14px; top: 50%;
329
+ width: 16px; height: 16px; transform: translateY(-50%);
330
+ background: currentColor; color: var(--text-muted); pointer-events: none;
331
+ -webkit-mask: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg>') center / contain no-repeat;
332
+ mask: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg>') center / contain no-repeat;
333
+ transition: transform var(--duration-base) var(--ease-out);
334
+ }
335
+
336
+ .segmented {
337
+ display: inline-flex; align-items: stretch;
338
+ background: var(--surface-alt); border-radius: var(--radius-md); padding: 3px;
339
+ height: 40px;
340
+ }
341
+ .segmented--sm { height: 32px; }
342
+ .segmented input { position: absolute; opacity: 0; pointer-events: none; }
343
+ .segmented label {
344
+ display: inline-flex; align-items: center; padding: 0 16px;
345
+ border-radius: calc(var(--radius-md) - 3px);
346
+ font-size: var(--text-sm); font-weight: var(--weight-medium); color: var(--text-secondary);
347
+ cursor: pointer; user-select: none; white-space: nowrap;
348
+ transition: background-color var(--duration-base) var(--ease-out),
349
+ color var(--duration-fast) var(--ease-out),
350
+ box-shadow var(--duration-base) var(--ease-out);
351
+ }
352
+ .segmented input:checked + label {
353
+ background: var(--surface); color: var(--text-primary);
354
+ font-weight: var(--weight-semibold); box-shadow: var(--shadow-xs);
355
+ }
356
+ .segmented input:focus-visible + label { outline: 2px solid var(--primary-500); outline-offset: 2px; }
357
+
358
+ .slider-row { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 10px; }
359
+ .slider-row .slider-label { font-size: var(--text-sm); color: var(--text-secondary); }
360
+ .slider-row .slider-value { font-family: var(--font-mono); font-size: var(--text-sm); color: var(--text-muted); }
361
+ .slider {
362
+ -webkit-appearance: none; appearance: none;
363
+ width: 100%; height: 6px; border-radius: var(--radius-full);
364
+ background: linear-gradient(to right, var(--accent) var(--slider-fill, 50%), var(--border) var(--slider-fill, 50%));
365
+ cursor: pointer;
366
+ }
367
+ .slider:disabled { opacity: .5; cursor: not-allowed; }
368
+ .slider::-webkit-slider-thumb {
369
+ -webkit-appearance: none; width: 18px; height: 18px; border-radius: 50%;
370
+ background: var(--surface); border: 2px solid var(--accent); box-shadow: var(--shadow-sm);
371
+ transition: transform var(--duration-instant) var(--ease-out);
372
+ }
373
+ .slider:active::-webkit-slider-thumb { transform: scale(1.15); }
374
+ .slider::-moz-range-thumb {
375
+ width: 14px; height: 14px; border-radius: 50%;
376
+ background: var(--surface); border: 2px solid var(--accent); box-shadow: var(--shadow-sm);
377
+ transition: transform var(--duration-instant) var(--ease-out);
378
+ }
379
+ .slider:active::-moz-range-thumb { transform: scale(1.15); }
380
+
381
+ .checkbox, .radio {
382
+ display: inline-flex; align-items: center; gap: 10px;
383
+ font-size: var(--text-base); color: var(--text-primary); cursor: pointer; position: relative;
384
+ }
385
+ .checkbox input, .radio input, .switch input {
386
+ position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0;
387
+ }
388
+ .checkbox__box {
389
+ width: 20px; height: 20px; flex: none; display: grid; place-items: center;
390
+ background: var(--surface); border: 2px solid var(--border-strong); border-radius: 4px;
391
+ transition: background-color var(--duration-fast) var(--ease-out),
392
+ border-color var(--duration-fast) var(--ease-out);
393
+ }
394
+ .checkbox__box svg {
395
+ width: 12px; height: 12px; fill: none; stroke: var(--text-on-primary);
396
+ stroke-width: 3; stroke-linecap: round; stroke-linejoin: round;
397
+ stroke-dasharray: 20; stroke-dashoffset: 20;
398
+ transition: stroke-dashoffset var(--duration-base) var(--ease-spring);
399
+ }
400
+ .checkbox:hover .checkbox__box { border-color: var(--primary-400); }
401
+ .checkbox input:checked ~ .checkbox__box { background: var(--accent); border-color: var(--accent); }
402
+ .checkbox input:checked ~ .checkbox__box svg { stroke-dashoffset: 0; }
403
+ .checkbox input:focus-visible ~ .checkbox__box { box-shadow: var(--ring); }
404
+ .checkbox input:disabled ~ * { opacity: .5; cursor: not-allowed; }
405
+
406
+ .radio__dot {
407
+ width: 20px; height: 20px; flex: none; display: grid; place-items: center;
408
+ background: var(--surface); border: 2px solid var(--border-strong); border-radius: var(--radius-full);
409
+ transition: background-color var(--duration-fast) var(--ease-out),
410
+ border-color var(--duration-fast) var(--ease-out);
411
+ }
412
+ .radio__dot::after {
413
+ content: ""; width: 8px; height: 8px; border-radius: 50%;
414
+ background: var(--text-on-primary); transform: scale(0);
415
+ transition: transform var(--duration-base) var(--ease-spring);
416
+ }
417
+ .radio:hover .radio__dot { border-color: var(--primary-400); }
418
+ .radio input:checked ~ .radio__dot { background: var(--accent); border-color: var(--accent); }
419
+ .radio input:checked ~ .radio__dot::after { transform: scale(1); }
420
+ .radio input:focus-visible ~ .radio__dot { box-shadow: var(--ring); }
421
+
422
+ .switch { display: inline-flex; align-items: center; gap: 10px; cursor: pointer; position: relative; font-size: var(--text-base); }
423
+ .switch__track {
424
+ width: 44px; height: 24px; flex: none; border-radius: var(--radius-full);
425
+ background: var(--border-strong); position: relative;
426
+ transition: background-color var(--duration-base) var(--ease-out);
427
+ }
428
+ .switch__track::after {
429
+ content: ""; position: absolute; top: 2px; left: 2px;
430
+ width: 20px; height: 20px; border-radius: 50%;
431
+ background: var(--neutral-0); box-shadow: var(--shadow-xs);
432
+ transition: transform var(--duration-base) var(--ease-spring);
433
+ }
434
+ .switch input:checked ~ .switch__track { background: var(--accent); }
435
+ .switch input:checked ~ .switch__track::after { transform: translateX(20px); }
436
+ .switch input:focus-visible ~ .switch__track { box-shadow: var(--ring); }
437
+ .switch input:disabled ~ * { opacity: .5; cursor: not-allowed; }
438
+
439
+ .card {
440
+ background: var(--surface); border: var(--border-width) solid var(--border);
441
+ border-radius: var(--radius-lg); padding: var(--card-padding);
442
+ box-shadow: var(--shadow-sm);
443
+ }
444
+ @media (max-width: 639px) { .card { padding: var(--card-padding-mobile); } }
445
+ .card--interactive {
446
+ cursor: pointer;
447
+ transition: transform var(--duration-base) var(--ease-out),
448
+ box-shadow var(--duration-base) var(--ease-out);
449
+ }
450
+ @media (hover: hover) and (pointer: fine) {
451
+ .card--interactive:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); }
452
+ }
453
+
454
+ .badge {
455
+ display: inline-flex; align-items: center; gap: 4px;
456
+ font-size: var(--text-xs); font-weight: var(--weight-medium);
457
+ padding: 2px 8px; border-radius: var(--radius-full);
458
+ background: var(--surface-alt); color: var(--text-secondary);
459
+ }
460
+ .badge svg { width: 12px; height: 12px; }
461
+ .badge--success { background: var(--success-100); color: var(--success-text); }
462
+ .badge--warning { background: var(--warning-100); color: var(--warning-text); }
463
+ .badge--danger { background: var(--danger-100); color: var(--danger-text); }
464
+ .badge--info { background: var(--info-100); color: var(--info-text); }
465
+
466
+ .tag {
467
+ display: inline-flex; align-items: center; gap: 6px;
468
+ font-size: var(--text-xs); font-weight: var(--weight-medium);
469
+ padding: 3px 6px 3px 10px; border-radius: var(--radius-full);
470
+ background: var(--surface-alt); color: var(--text-secondary);
471
+ border: var(--border-width) solid var(--border);
472
+ }
473
+ .tag__close {
474
+ display: grid; place-items: center; width: 16px; height: 16px;
475
+ border: 0; border-radius: 50%; background: transparent; color: inherit; cursor: pointer;
476
+ transition: background-color var(--duration-fast) var(--ease-out);
477
+ }
478
+ .tag__close:hover { background: var(--border); }
479
+ .tag__close svg { width: 10px; height: 10px; }
480
+
481
+ .avatar {
482
+ position: relative; display: inline-flex; flex: none;
483
+ width: 36px; height: 36px; font-size: 14px;
484
+ }
485
+ .avatar--sm { width: 28px; height: 28px; font-size: 12px; }
486
+ .avatar--lg { width: 48px; height: 48px; font-size: 18px; }
487
+ .avatar--xl { width: 64px; height: 64px; font-size: 24px; }
488
+ .avatar__img, .avatar__initials {
489
+ width: 100%; height: 100%; border-radius: var(--radius-full);
490
+ }
491
+ .avatar__img { object-fit: cover; }
492
+ .avatar__initials {
493
+ display: grid; place-items: center;
494
+ background: var(--accent-soft-strong); color: var(--text-on-primary-solid);
495
+ font-weight: var(--weight-semibold); text-transform: uppercase;
496
+ }
497
+ .avatar__dot {
498
+ position: absolute; right: 0; bottom: 0;
499
+ width: 28%; height: 28%; min-width: 8px; min-height: 8px;
500
+ border-radius: 50%; box-shadow: 0 0 0 2px var(--surface);
501
+ background: var(--neutral-300);
502
+ }
503
+ .avatar__dot--online { background: var(--success-500); }
504
+ .avatar__dot--busy { background: var(--danger-500); }
505
+ .avatar__dot--away { background: var(--warning-500); }
506
+
507
+ .divider { border: 0; border-top: var(--border-width) solid var(--border); margin: var(--space-4) 0; }
508
+ .divider--label {
509
+ display: flex; align-items: center; gap: 12px; border: 0; margin: var(--space-4) 0;
510
+ color: var(--text-muted); font-size: var(--text-xs); font-weight: var(--weight-medium);
511
+ text-transform: uppercase; letter-spacing: var(--tracking-label);
512
+ }
513
+ .divider--label::before, .divider--label::after {
514
+ content: ""; flex: 1; border-top: var(--border-width) solid var(--border);
515
+ }
516
+ .divider--vertical {
517
+ display: inline-block; align-self: stretch; width: var(--border-width);
518
+ background: var(--border); border: 0; margin: 0 var(--space-3);
519
+ }
520
+
521
+ .accordion {
522
+ border: var(--border-width) solid var(--border); border-radius: var(--radius-lg);
523
+ background: var(--surface); overflow: hidden;
524
+ }
525
+ .accordion__item + .accordion__item { border-top: var(--border-width) solid var(--border); }
526
+ .accordion__header {
527
+ display: flex; align-items: center; justify-content: space-between; gap: 12px;
528
+ width: 100%; padding: 16px 20px; border: 0; background: transparent;
529
+ font-family: var(--font-body); font-size: var(--text-base); font-weight: var(--weight-medium);
530
+ color: var(--text-primary); text-align: left; cursor: pointer;
531
+ transition: background-color var(--duration-fast) var(--ease-out);
532
+ }
533
+ .accordion__header:hover { background: var(--bg); }
534
+ .accordion__header svg {
535
+ width: 18px; height: 18px; flex: none; color: var(--text-muted);
536
+ transition: transform var(--duration-base) var(--ease-out);
537
+ }
538
+ .accordion__item.is-open .accordion__header svg { transform: rotate(180deg); }
539
+ .accordion__panel {
540
+ display: grid; grid-template-rows: 0fr;
541
+ transition: grid-template-rows var(--duration-slow) var(--ease-out);
542
+ }
543
+ .accordion__item.is-open .accordion__panel { grid-template-rows: 1fr; }
544
+ .accordion__panel > div { overflow: hidden; }
545
+ .accordion__content {
546
+ padding: 0 20px 16px; font-size: var(--text-sm); color: var(--text-secondary);
547
+ opacity: 0; transition: opacity var(--duration-slow) var(--ease-out);
548
+ }
549
+ .accordion__item.is-open .accordion__content { opacity: 1; }
550
+
551
+ [data-tooltip] { position: relative; }
552
+ [data-tooltip]::after {
553
+ content: attr(data-tooltip);
554
+ position: absolute; bottom: calc(100% + 8px); left: 50%;
555
+ transform: translate(-50%, 4px);
556
+ background: var(--tooltip-bg); color: var(--tooltip-text);
557
+ font-size: var(--text-xs); line-height: 1.4; white-space: nowrap;
558
+ padding: 6px 10px; border-radius: var(--radius-sm);
559
+ opacity: 0; pointer-events: none; z-index: 70;
560
+ transition: opacity var(--duration-fast) var(--ease-out) 0s,
561
+ transform var(--duration-fast) var(--ease-out) 0s;
562
+ }
563
+ [data-tooltip]::before {
564
+ content: ""; position: absolute; bottom: calc(100% + 2px); left: 50%;
565
+ transform: translate(-50%, 4px);
566
+ border: 6px solid transparent; border-top-color: var(--tooltip-bg); border-bottom: 0;
567
+ opacity: 0; pointer-events: none; z-index: 70;
568
+ transition: opacity var(--duration-fast) var(--ease-out) 0s,
569
+ transform var(--duration-fast) var(--ease-out) 0s;
570
+ }
571
+ [data-tooltip]:focus-visible::after, [data-tooltip]:focus-visible::before {
572
+ opacity: 1; transform: translate(-50%, 0); transition-delay: 400ms;
573
+ }
574
+ /* Tooltip por hover solo con puntero real — en touch quedaría pegado tras el tap */
575
+ @media (hover: hover) and (pointer: fine) {
576
+ [data-tooltip]:hover::after, [data-tooltip]:hover::before {
577
+ opacity: 1; transform: translate(-50%, 0); transition-delay: 400ms;
578
+ }
579
+ }
580
+
581
+ .alert {
582
+ display: flex; gap: 12px; align-items: flex-start;
583
+ padding: 14px 16px; border-radius: var(--radius-md); font-size: var(--text-sm);
584
+ }
585
+ .alert > svg { width: 20px; height: 20px; flex: none; margin-top: 1px; }
586
+ .alert__title { font-weight: var(--weight-semibold); margin: 0 0 2px; }
587
+ .alert__body { opacity: .92; margin: 0; }
588
+ .alert__close {
589
+ margin-left: auto; flex: none; display: grid; place-items: center;
590
+ width: 36px; height: 36px; border: 0; border-radius: var(--radius-sm);
591
+ background: transparent; color: inherit; cursor: pointer; opacity: .7;
592
+ }
593
+ .alert__close:hover { opacity: 1; }
594
+ .alert__close svg { width: 18px; height: 18px; }
595
+ .alert--info { background: var(--info-100); color: var(--info-text); }
596
+ .alert--info > svg { color: var(--info-500); }
597
+ .alert--success { background: var(--success-100); color: var(--success-text); }
598
+ .alert--success > svg { color: var(--success-500); }
599
+ .alert--warning { background: var(--warning-100); color: var(--warning-text); }
600
+ .alert--warning > svg { color: var(--warning-500); }
601
+ .alert--danger { background: var(--danger-100); color: var(--danger-text); }
602
+ .alert--danger > svg { color: var(--danger-500); }
603
+
604
+ .toast-stack {
605
+ position: fixed; top: 16px; right: 16px; z-index: 90;
606
+ display: flex; flex-direction: column; gap: 12px; align-items: flex-end;
607
+ }
608
+ @media (max-width: 639px) {
609
+ .toast-stack { right: 50%; transform: translateX(50%); align-items: center; }
610
+ }
611
+ .toast {
612
+ display: flex; gap: 12px; align-items: flex-start;
613
+ width: 360px; max-width: calc(100vw - 32px);
614
+ background: var(--surface); color: var(--text-primary);
615
+ border-left: 3px solid var(--info-500); border-radius: var(--radius-md);
616
+ box-shadow: var(--shadow-lg); padding: 14px 16px; font-size: var(--text-sm);
617
+ /* Entrada interrumpible (§11.2): transition + @starting-style, no @keyframes —
618
+ un keyframe no puede invertirse si el toast se apila o se descarta a mitad. */
619
+ opacity: 1; transform: none;
620
+ transition: opacity var(--duration-slow) var(--ease-out),
621
+ transform var(--duration-slow) var(--ease-out);
622
+ }
623
+ @starting-style {
624
+ .toast { opacity: 0; transform: translateX(24px); }
625
+ }
626
+ .toast.is-leaving { animation: cc-toast-out var(--duration-base) var(--ease-in) forwards; }
627
+ .toast > svg { width: 20px; height: 20px; flex: none; }
628
+ .toast--success { border-left-color: var(--success-500); } .toast--success > svg { color: var(--success-500); }
629
+ .toast--warning { border-left-color: var(--warning-500); } .toast--warning > svg { color: var(--warning-500); }
630
+ .toast--danger { border-left-color: var(--danger-500); } .toast--danger > svg { color: var(--danger-500); }
631
+ .toast--info { border-left-color: var(--info-500); } .toast--info > svg { color: var(--info-500); }
632
+ .toast__title { font-weight: var(--weight-semibold); margin: 0; }
633
+ .toast__body { color: var(--text-secondary); margin: 0; }
634
+ @keyframes cc-toast-out { to { opacity: 0; transform: translateX(24px); } }
635
+
636
+ .progress-row { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 8px; }
637
+ .progress-row .progress-label { font-size: var(--text-sm); color: var(--text-secondary); }
638
+ .progress-row .progress-value { font-family: var(--font-mono); font-size: var(--text-sm); color: var(--text-muted); }
639
+ .progress {
640
+ height: 8px; border-radius: var(--radius-full);
641
+ background: var(--surface-alt); overflow: hidden;
642
+ }
643
+ .progress__bar {
644
+ height: 100%; width: 0; border-radius: var(--radius-full);
645
+ background: var(--accent);
646
+ transition: width var(--duration-slow) var(--ease-out);
647
+ }
648
+ .progress__bar--success { background: var(--success-500); }
649
+ .progress__bar--warning { background: var(--warning-500); }
650
+ .progress__bar--danger { background: var(--danger-500); }
651
+
652
+ .progress-ring { display: inline-grid; place-items: center; position: relative; }
653
+ .progress-ring svg { transform: rotate(-90deg); }
654
+ .progress-ring__track { fill: none; stroke: var(--surface-alt); }
655
+ .progress-ring__bar {
656
+ fill: none; stroke: var(--accent); stroke-linecap: round;
657
+ transition: stroke-dashoffset var(--duration-slow) var(--ease-out);
658
+ }
659
+ .progress-ring__value {
660
+ position: absolute; font-family: var(--font-mono); color: var(--text-primary);
661
+ font-weight: var(--weight-medium);
662
+ }
663
+
664
+ .skeleton {
665
+ position: relative; overflow: hidden;
666
+ background: var(--surface-alt); border-radius: var(--radius-sm);
667
+ min-height: 12px;
668
+ }
669
+ .skeleton::after {
670
+ content: ""; position: absolute; inset: 0;
671
+ background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--surface) 80%, transparent), transparent);
672
+ transform: translateX(-100%);
673
+ animation: cc-shimmer 1.4s var(--ease-out) infinite;
674
+ }
675
+ @keyframes cc-shimmer { to { transform: translateX(100%); } }
676
+ .skeleton--circle { border-radius: var(--radius-full); }
677
+
678
+ .empty-state {
679
+ display: flex; flex-direction: column; align-items: center; text-align: center;
680
+ gap: var(--space-3); padding: var(--space-10) var(--space-6);
681
+ }
682
+ .empty-state > svg { width: 40px; height: 40px; color: var(--primary-300); }
683
+ .empty-state__title { font-size: var(--text-lg); font-weight: var(--weight-semibold); color: var(--text-primary); margin: 0; }
684
+ .empty-state__desc { font-size: var(--text-sm); color: var(--text-muted); max-width: 40ch; margin: 0; }
685
+
686
+ .modal {
687
+ position: fixed; inset: 0; z-index: 100;
688
+ display: grid; place-items: center; padding: var(--space-4);
689
+ visibility: hidden;
690
+ }
691
+ .modal.is-open { visibility: visible; }
692
+ .modal__overlay {
693
+ position: absolute; inset: 0; background: var(--overlay);
694
+ opacity: 0; transition: opacity var(--duration-base) var(--ease-out);
695
+ }
696
+ .modal.is-open .modal__overlay { opacity: 1; }
697
+ .modal__panel {
698
+ position: relative; width: min(480px, 100%);
699
+ background: var(--surface); border-radius: var(--radius-lg);
700
+ box-shadow: var(--shadow-xl); padding: var(--card-padding);
701
+ opacity: 0; transform: scale(.96);
702
+ transition: opacity var(--duration-slow) var(--ease-out),
703
+ transform var(--duration-slow) var(--ease-out);
704
+ }
705
+ .modal.is-open .modal__panel { opacity: 1; transform: scale(1); }
706
+ .modal__panel--lg { width: min(640px, 100%); }
707
+ .modal__header {
708
+ display: flex; align-items: center; justify-content: space-between;
709
+ gap: var(--space-4); margin-bottom: var(--space-4);
710
+ }
711
+ .modal__title { font-size: var(--text-xl); font-weight: var(--weight-semibold); margin: 0; }
712
+ .modal__footer {
713
+ display: flex; justify-content: flex-end; gap: var(--space-3);
714
+ margin-top: var(--space-6);
715
+ }
716
+
717
+ .menu-wrap { position: relative; display: inline-block; }
718
+ .menu {
719
+ position: absolute; top: calc(100% + 6px); left: 0; z-index: 50;
720
+ min-width: 190px; padding: 6px;
721
+ background: var(--surface); border: var(--border-width) solid var(--border);
722
+ border-radius: var(--radius-md); box-shadow: var(--shadow-lg);
723
+ opacity: 0; transform: translateY(-4px); pointer-events: none;
724
+ transition: opacity 160ms var(--ease-out), transform 160ms var(--ease-out);
725
+ }
726
+ .menu--right { left: auto; right: 0; }
727
+ .menu.is-open { opacity: 1; transform: none; pointer-events: auto; }
728
+ .menu__item {
729
+ display: flex; align-items: center; gap: 10px; width: 100%;
730
+ padding: 9px 12px; border: 0; border-radius: var(--radius-sm);
731
+ background: transparent; font-family: var(--font-body); font-size: var(--text-sm);
732
+ color: var(--text-primary); text-align: left; cursor: pointer;
733
+ transition: background-color var(--duration-fast) var(--ease-out);
734
+ }
735
+ .menu__item:hover { background: var(--bg); }
736
+ .menu__item svg { width: 16px; height: 16px; color: var(--text-muted); flex: none; }
737
+ .menu__item--danger, .menu__item--danger svg { color: var(--danger-500); }
738
+ .menu__item--danger:hover { background: var(--danger-100); }
739
+ .menu__sep { height: var(--border-width); background: var(--border); margin: 6px 4px; }
740
+
741
+ .table { width: 100%; border-collapse: collapse; font-size: var(--text-sm); font-variant-numeric: tabular-nums; }
742
+ .table thead th {
743
+ height: 40px; padding: 0 16px; text-align: left;
744
+ background: var(--bg); color: var(--text-muted);
745
+ font-size: var(--text-xs); font-weight: var(--weight-medium);
746
+ text-transform: uppercase; letter-spacing: var(--tracking-label);
747
+ border-bottom: var(--border-width) solid var(--border);
748
+ }
749
+ .table tbody td {
750
+ height: 52px; padding: 0 16px; color: var(--text-primary);
751
+ border-bottom: var(--border-width) solid var(--surface-alt);
752
+ }
753
+ .table tbody tr { transition: background-color var(--duration-fast) var(--ease-out); }
754
+ .table tbody tr:hover { background: var(--bg); }
755
+ .table tbody tr.is-selected { background: var(--accent-soft); }
756
+ .table .num, .table td.num, .table th.num { text-align: right; font-family: var(--font-mono); }
757
+
758
+ .stat__label {
759
+ display: inline-flex; align-items: center; gap: 8px; margin-bottom: 8px;
760
+ font-size: var(--text-sm); color: var(--text-muted);
761
+ }
762
+ .stat__label svg { width: 16px; height: 16px; }
763
+ .stat__row { display: flex; align-items: baseline; gap: 10px; }
764
+ .stat__value {
765
+ font-family: var(--font-mono); font-size: var(--text-3xl);
766
+ font-weight: var(--weight-semibold); color: var(--text-primary);
767
+ letter-spacing: -0.01em; line-height: 1;
768
+ }
769
+ .stat__delta {
770
+ display: inline-flex; align-items: center; gap: 4px;
771
+ font-size: var(--text-sm); font-weight: var(--weight-semibold);
772
+ color: var(--text-muted);
773
+ }
774
+ .stat__delta svg { width: 14px; height: 14px; }
775
+ .stat__delta--up { color: var(--success-text); }
776
+ .stat__delta--down { color: var(--danger-text); }
777
+
778
+ .pagination { display: flex; align-items: center; gap: 4px; list-style: none; padding: 0; margin: 0; }
779
+ .pagination__btn {
780
+ display: inline-grid; place-items: center;
781
+ min-width: 36px; height: 36px; padding: 0 8px;
782
+ border: 0; border-radius: var(--radius-sm); background: transparent;
783
+ font-family: var(--font-body); font-size: var(--text-sm); font-weight: var(--weight-medium);
784
+ color: var(--text-secondary); cursor: pointer;
785
+ transition: background-color var(--duration-fast) var(--ease-out);
786
+ }
787
+ .pagination__btn:hover:not(:disabled):not(.is-active) { background: var(--surface-alt); }
788
+ .pagination__btn.is-active {
789
+ background: var(--accent); color: var(--text-on-primary); font-weight: var(--weight-semibold);
790
+ }
791
+ .pagination__btn:disabled { color: var(--text-disabled); cursor: not-allowed; }
792
+ .pagination__btn svg { width: 16px; height: 16px; }
793
+ .pagination__ellipsis { color: var(--text-disabled); padding: 0 4px; user-select: none; }
794
+
795
+ .navbar {
796
+ display: flex; align-items: center; gap: var(--space-8);
797
+ height: 64px; padding: 0 var(--space-6);
798
+ background: var(--surface); border-bottom: var(--border-width) solid var(--border);
799
+ }
800
+ .navbar__brand { font-family: var(--font-display); font-weight: var(--weight-bold); font-size: var(--text-lg); color: var(--text-primary); text-decoration: none; }
801
+ .navbar__nav { display: flex; align-items: stretch; gap: var(--space-6); height: 100%; }
802
+ .navbar__link {
803
+ position: relative; display: inline-flex; align-items: center;
804
+ font-size: var(--text-sm); font-weight: var(--weight-medium);
805
+ color: var(--text-secondary); text-decoration: none;
806
+ transition: color var(--duration-fast) var(--ease-out);
807
+ }
808
+ .navbar__link:hover { color: var(--text-primary); }
809
+ .navbar__link.is-active { color: var(--accent-active); }
810
+ .navbar__link.is-active::after {
811
+ content: ""; position: absolute; left: 0; right: 0; bottom: 0;
812
+ height: 2px; background: var(--accent);
813
+ }
814
+
815
+ .sidebar {
816
+ width: 260px; padding: var(--space-4);
817
+ background: var(--bg); border-right: var(--border-width) solid var(--border);
818
+ display: flex; flex-direction: column; gap: 2px;
819
+ }
820
+ .sidebar__item {
821
+ display: flex; align-items: center; gap: 10px;
822
+ padding: 10px 12px; border-radius: var(--radius-md);
823
+ font-size: var(--text-sm); font-weight: var(--weight-medium);
824
+ color: var(--text-secondary); text-decoration: none; border: 0; background: transparent;
825
+ cursor: pointer; text-align: left; width: 100%;
826
+ transition: background-color var(--duration-fast) var(--ease-out), color var(--duration-fast) var(--ease-out);
827
+ }
828
+ .sidebar__item svg { width: 18px; height: 18px; flex: none; }
829
+ .sidebar__item:hover { background: var(--surface-alt); color: var(--text-primary); }
830
+ .sidebar__item.is-active { background: var(--accent-soft-strong); color: var(--link); }
831
+ .sidebar__item.is-active svg { color: var(--accent); }
832
+
833
+ .tabs { display: flex; gap: var(--space-6); border-bottom: var(--border-width) solid var(--border); }
834
+ .tabs__tab {
835
+ padding: 12px 4px; margin-bottom: -1px;
836
+ border: 0; border-bottom: 2px solid transparent; background: transparent;
837
+ font-family: var(--font-body); font-size: var(--text-sm); font-weight: var(--weight-medium);
838
+ color: var(--text-secondary); cursor: pointer;
839
+ transition: color var(--duration-fast) var(--ease-out),
840
+ border-color var(--duration-slow) var(--ease-out);
841
+ }
842
+ .tabs__tab:hover { color: var(--text-primary); }
843
+ .tabs__tab.is-active { color: var(--accent-active); border-bottom-color: var(--accent); }
844
+
845
+ .breadcrumbs { display: flex; align-items: center; gap: var(--space-2); font-size: var(--text-sm); color: var(--text-muted); }
846
+ .breadcrumbs a { color: var(--text-muted); text-decoration: none; transition: color var(--duration-fast) var(--ease-out); }
847
+ .breadcrumbs a:hover { color: var(--link); }
848
+ .breadcrumbs__sep { user-select: none; }
849
+ .breadcrumbs__current { color: var(--text-primary); }
850
+
851
+ .link {
852
+ color: var(--link); text-decoration: none;
853
+ background-image: linear-gradient(currentColor, currentColor);
854
+ background-size: 0% 1px; background-repeat: no-repeat; background-position: 0 100%;
855
+ transition: color var(--duration-fast) var(--ease-out),
856
+ background-size var(--duration-base) var(--ease-out);
857
+ }
858
+ .link:hover { background-size: 100% 1px; }
859
+
860
+ .code {
861
+ font-family: var(--font-mono); font-size: .9em;
862
+ background: var(--surface-alt); color: var(--text-primary);
863
+ border: var(--border-width) solid var(--border);
864
+ border-radius: var(--radius-sm); padding: 1px 6px;
865
+ }
866
+
867
+ .u-visually-hidden {
868
+ position: absolute; width: 1px; height: 1px; margin: -1px;
869
+ clip-path: inset(50%); overflow: hidden; white-space: nowrap;
870
+ }
871
+ .u-mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
872
+ .u-muted { color: var(--text-muted); }
873
+ .u-measure { max-width: var(--measure); }
874
+
875
+ /* ==========================================================================
876
+ ESTILOS DE LA PÁGINA DE DOCUMENTACIÓN (.ds-*)
877
+ ========================================================================== */
878
+ .ds-topbar {
879
+ position: sticky; top: 0; z-index: 60;
880
+ display: flex; align-items: center; gap: var(--space-4);
881
+ height: 64px; padding: 0 var(--space-6);
882
+ background: var(--surface); border-bottom: var(--border-width) solid var(--border);
883
+ }
884
+ .ds-brand { display: flex; align-items: baseline; gap: 10px; text-decoration: none; color: var(--text-primary); }
885
+ .ds-brand__name { font-family: var(--font-display); font-weight: var(--weight-bold); font-size: var(--text-xl); letter-spacing: var(--tracking-display); }
886
+ .ds-topbar__spacer { flex: 1; }
887
+ .ds-version {
888
+ font-family: var(--font-mono); font-size: var(--text-xs); color: var(--text-muted);
889
+ border: var(--border-width) solid var(--border); border-radius: var(--radius-full); padding: 2px 10px;
890
+ }
891
+
892
+ .ds-theme-toggle .ds-icon-moon { display: none; }
893
+ [data-theme="dark"] .ds-theme-toggle .ds-icon-sun { display: none; }
894
+ [data-theme="dark"] .ds-theme-toggle .ds-icon-moon { display: block; }
895
+
896
+ .ds-hamburger { display: none; }
897
+
898
+ .ds-sidenav {
899
+ position: fixed; top: 64px; bottom: 0; left: 0; z-index: 50;
900
+ width: 260px; overflow-y: auto; padding: var(--space-5) var(--space-3) var(--space-10);
901
+ background: var(--bg); border-right: var(--border-width) solid var(--border);
902
+ }
903
+ .ds-nav { display: flex; flex-direction: column; gap: 1px; }
904
+ .ds-nav a {
905
+ display: block; padding: 6px 10px; border-radius: var(--radius-sm);
906
+ color: var(--text-secondary); font-size: var(--text-sm); text-decoration: none;
907
+ transition: background-color var(--duration-fast) var(--ease-out), color var(--duration-fast) var(--ease-out);
908
+ }
909
+ .ds-nav a:hover { background: var(--surface-alt); color: var(--text-primary); }
910
+ .ds-nav a.is-active { background: var(--accent-soft-strong); color: var(--link); font-weight: var(--weight-semibold); }
911
+ .ds-nav__group {
912
+ margin: var(--space-4) 0 4px; padding: 0 10px;
913
+ font-size: var(--text-xs); font-weight: var(--weight-medium);
914
+ text-transform: uppercase; letter-spacing: var(--tracking-label); color: var(--text-muted);
915
+ }
916
+ .ds-nav__sub a { padding-left: 22px; font-size: 13px; }
917
+
918
+ .ds-main { margin-left: 260px; padding: var(--space-12) var(--space-8) var(--space-24); }
919
+ .ds-main > section { max-width: 920px; margin: 0 auto var(--section-gap); scroll-margin-top: 88px; }
920
+
921
+ .ds-eyebrow {
922
+ font-size: var(--text-xs); font-weight: var(--weight-semibold);
923
+ text-transform: uppercase; letter-spacing: var(--tracking-label);
924
+ color: var(--accent-active); margin: 0 0 6px;
925
+ }
926
+ .ds-lead { font-size: var(--text-lg); line-height: var(--leading-lg); color: var(--text-secondary); }
927
+
928
+ .ds-hero { padding: var(--space-10) 0 0; }
929
+ .ds-hero h1 { font-size: var(--text-5xl); line-height: var(--leading-5xl); margin-bottom: .3em; }
930
+ .ds-hero__palette { display: flex; gap: 10px; margin-top: var(--space-6); }
931
+ .ds-hero__chip { width: 56px; height: 56px; border-radius: var(--radius-md); border: var(--border-width) solid var(--border); }
932
+
933
+ .ds-principles { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: var(--space-4); margin-top: var(--space-6); }
934
+ .ds-principles .card h4 { font-size: var(--text-base); margin-bottom: 4px; }
935
+ .ds-principles .card p { font-size: var(--text-sm); color: var(--text-secondary); margin: 0; max-width: none; }
936
+ .ds-principles .card .ds-num { font-family: var(--font-mono); font-size: var(--text-xs); color: var(--accent); }
937
+
938
+ .ds-demo {
939
+ display: flex; flex-wrap: wrap; gap: var(--space-4); align-items: center;
940
+ background: var(--surface); border: var(--border-width) solid var(--border);
941
+ border-radius: var(--radius-lg); padding: var(--space-8);
942
+ margin: var(--space-4) 0 var(--space-2);
943
+ }
944
+ .ds-demo--col { flex-direction: column; align-items: stretch; }
945
+ .ds-demo--center { justify-content: center; }
946
+ .ds-demo--bg { background: var(--bg); }
947
+ .ds-demo--grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); align-items: stretch; }
948
+ .ds-demo:has(table) { overflow-x: auto; }
949
+
950
+ .ds-code { margin: var(--space-2) 0 0; }
951
+ .ds-code summary {
952
+ cursor: pointer; user-select: none; font-size: var(--text-sm);
953
+ color: var(--text-muted); padding: 4px 0;
954
+ }
955
+ .ds-code summary:hover { color: var(--link); }
956
+ .ds-code-box { position: relative; margin-top: var(--space-2); }
957
+ .ds-pre {
958
+ background: var(--surface-alt); color: var(--text-primary);
959
+ border: var(--border-width) solid var(--border);
960
+ font-family: var(--font-mono); font-size: 13px; line-height: 1.6;
961
+ padding: var(--space-4) 18px; border-radius: var(--radius-md);
962
+ overflow-x: auto; margin: 0; tab-size: 2;
963
+ }
964
+ .ds-copy {
965
+ position: absolute; top: 8px; right: 8px;
966
+ height: 28px; padding: 0 10px;
967
+ border: var(--border-width) solid var(--border); border-radius: var(--radius-sm);
968
+ background: var(--surface); color: var(--text-secondary);
969
+ font-family: var(--font-body); font-size: var(--text-xs); font-weight: var(--weight-medium);
970
+ cursor: pointer; transition: background-color var(--duration-fast) var(--ease-out), color var(--duration-fast) var(--ease-out);
971
+ }
972
+ .ds-copy:hover { background: var(--surface-alt); color: var(--text-primary); }
973
+ /* Resaltado de sintaxis de los ejemplos (tokens semánticos, sigue el tema) */
974
+ .ds-pre .tok-com { color: var(--text-muted); font-style: italic; }
975
+ .ds-pre .tok-str { color: var(--success-text); }
976
+ .ds-pre .tok-tag { color: var(--link); }
977
+ .ds-pre .tok-attr { color: var(--warning-text); }
978
+ .ds-pre .tok-var { color: var(--link); }
979
+ .ds-pre .tok-punct { color: var(--text-muted); }
980
+
981
+ .ds-swatches { display: grid; grid-template-columns: repeat(auto-fill, minmax(148px, 1fr)); gap: var(--space-3); margin: var(--space-4) 0; }
982
+ .ds-swatch {
983
+ border: var(--border-width) solid var(--border); border-radius: var(--radius-md);
984
+ overflow: hidden; background: var(--surface); cursor: pointer; padding: 0; text-align: left;
985
+ font-family: var(--font-body);
986
+ transition: transform var(--duration-base) var(--ease-out), box-shadow var(--duration-base) var(--ease-out);
987
+ }
988
+ @media (hover: hover) and (pointer: fine) {
989
+ .ds-swatch:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); }
990
+ }
991
+ .ds-swatch__chip { height: 52px; }
992
+ .ds-swatch__meta { padding: 8px 10px; display: flex; flex-direction: column; gap: 1px; }
993
+ .ds-swatch__name { font-size: 13px; font-weight: var(--weight-semibold); color: var(--text-primary); }
994
+ .ds-swatch__hex { font-family: var(--font-mono); font-size: var(--text-xs); color: var(--text-muted); }
995
+
996
+ .ds-semantic-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: var(--space-4); margin: var(--space-4) 0; }
997
+
998
+ .ds-ratio { display: flex; height: 44px; border-radius: var(--radius-md); overflow: hidden; border: var(--border-width) solid var(--border); margin: var(--space-4) 0; }
999
+ .ds-ratio > div { display: grid; place-items: center; font-size: var(--text-xs); font-weight: var(--weight-semibold); }
1000
+
1001
+ .ds-space-list { display: flex; flex-direction: column; gap: 10px; margin: var(--space-4) 0; }
1002
+ .ds-space-item { display: flex; align-items: center; gap: var(--space-4); }
1003
+ .ds-space-item .u-mono { width: 120px; font-size: var(--text-xs); color: var(--text-muted); }
1004
+ .ds-space-bar { height: 14px; background: var(--primary-300); border-radius: 4px; }
1005
+
1006
+ .ds-type-row {
1007
+ display: flex; align-items: baseline; gap: var(--space-6);
1008
+ padding: var(--space-4) 0; border-bottom: 1px dashed var(--border);
1009
+ }
1010
+ .ds-type-row__meta { width: 190px; flex: none; font-family: var(--font-mono); font-size: var(--text-xs); color: var(--text-muted); }
1011
+ .ds-type-row__sample { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1012
+
1013
+ .ds-font-card h4 { margin-bottom: 2px; }
1014
+ .ds-font-card .ds-font-sample { font-size: var(--text-3xl); line-height: 1.2; margin: 6px 0; }
1015
+ .ds-font-card p { font-size: var(--text-sm); color: var(--text-muted); margin: 0; max-width: none; }
1016
+
1017
+ .ds-radius-box {
1018
+ width: 104px; height: 72px; display: grid; place-items: center;
1019
+ background: var(--accent-soft-strong); border: 2px solid var(--accent);
1020
+ font-family: var(--font-mono); font-size: var(--text-xs); color: var(--text-on-primary-solid);
1021
+ }
1022
+ .ds-shadow-box {
1023
+ width: 128px; height: 88px; display: grid; place-items: center;
1024
+ background: var(--surface); border-radius: var(--radius-md);
1025
+ font-family: var(--font-mono); font-size: var(--text-xs); color: var(--text-muted);
1026
+ }
1027
+ .ds-elev-box {
1028
+ min-height: 96px; display: grid; place-content: center; text-align: center; gap: 4px;
1029
+ border-radius: var(--radius-lg); padding: var(--space-4);
1030
+ font-family: var(--font-mono); font-size: var(--text-xs); color: var(--text-muted);
1031
+ }
1032
+ .ds-wordmark-demo {
1033
+ font-family: var(--font-display); font-weight: var(--weight-bold);
1034
+ font-size: var(--text-3xl); letter-spacing: var(--tracking-display); color: var(--text-primary);
1035
+ }
1036
+ .ds-wordmark-badge {
1037
+ display: inline-block; padding: 14px 22px;
1038
+ background: var(--surface); border: var(--border-width) solid var(--border);
1039
+ border-radius: var(--radius-md); box-shadow: var(--shadow-sm);
1040
+ }
1041
+
1042
+ .ds-ease-chip {
1043
+ display: inline-flex; align-items: center; justify-content: center;
1044
+ padding: 10px 18px; border-radius: var(--radius-md);
1045
+ background: var(--accent-soft-strong); color: var(--text-on-primary-solid);
1046
+ font-family: var(--font-mono); font-size: var(--text-xs); cursor: default;
1047
+ }
1048
+ @media (hover: hover) and (pointer: fine) {
1049
+ .ds-ease-chip:hover { transform: translateX(24px); }
1050
+ }
1051
+ .ds-ease-chip--out { transition: transform 400ms var(--ease-out); }
1052
+ .ds-ease-chip--in { transition: transform 400ms var(--ease-in); }
1053
+ .ds-ease-chip--inout { transition: transform 400ms var(--ease-in-out); }
1054
+ .ds-ease-chip--spring{ transition: transform 400ms var(--ease-spring); }
1055
+
1056
+ .ds-icon-grid { display: flex; flex-wrap: wrap; gap: var(--space-4); }
1057
+ .ds-icon-cell {
1058
+ display: flex; flex-direction: column; align-items: center; gap: 6px;
1059
+ width: 88px; padding: var(--space-3) 0;
1060
+ border: var(--border-width) solid var(--border); border-radius: var(--radius-md);
1061
+ background: var(--surface);
1062
+ }
1063
+ .ds-icon-cell svg { width: 20px; height: 20px; color: var(--text-secondary); }
1064
+ .ds-icon-cell span { font-family: var(--font-mono); font-size: 11px; color: var(--text-muted); }
1065
+
1066
+ .ds-component { margin-top: var(--space-12); }
1067
+ .ds-component > h3 { display: flex; align-items: baseline; gap: 10px; }
1068
+ .ds-component .ds-spec { font-size: var(--text-sm); color: var(--text-muted); max-width: var(--measure); }
1069
+
1070
+ .ds-footer {
1071
+ margin-left: 260px; padding: var(--space-8);
1072
+ border-top: var(--border-width) solid var(--border);
1073
+ color: var(--text-muted); font-size: var(--text-sm); text-align: center;
1074
+ }
1075
+
1076
+ @media (max-width: 1023px) {
1077
+ .ds-hamburger { display: inline-grid; }
1078
+ .ds-sidenav { transform: translateX(-100%); transition: transform var(--duration-slow) var(--ease-out); box-shadow: none; }
1079
+ .ds-sidenav.is-open { transform: translateX(0); box-shadow: var(--shadow-lg); }
1080
+ .ds-main, .ds-footer { margin-left: 0; }
1081
+ .ds-main { padding: var(--space-8) var(--space-4) var(--space-16); }
1082
+ }
1083
+ </style>
1084
+ </head>
1085
+ <body class="celeste-reset">
1086
+
1087
+ <!-- Iconos (estilo Lucide, trazo 2px) -->
1088
+ <svg xmlns="http://www.w3.org/2000/svg" style="display:none" aria-hidden="true">
1089
+ <defs>
1090
+ <symbol id="i-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></symbol>
1091
+ <symbol id="i-x" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></symbol>
1092
+ <symbol id="i-chevron-down" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></symbol>
1093
+ <symbol id="i-chevron-left" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m15 18-6-6 6-6"/></symbol>
1094
+ <symbol id="i-chevron-right" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></symbol>
1095
+ <symbol id="i-search" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></symbol>
1096
+ <symbol id="i-plus" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14"/><path d="M12 5v14"/></symbol>
1097
+ <symbol id="i-info" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></symbol>
1098
+ <symbol id="i-circle-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="m9 12 2 2 4-4"/></symbol>
1099
+ <symbol id="i-triangle-alert" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21.73 18-8-14a2 2 0 0 0-3.46 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"/><path d="M12 9v4"/><path d="M12 17h.01"/></symbol>
1100
+ <symbol id="i-circle-alert" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 8v4"/><path d="M12 16h.01"/></symbol>
1101
+ <symbol id="i-bell" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9"/><path d="M10.3 21a1.94 1.94 0 0 0 3.4 0"/></symbol>
1102
+ <symbol id="i-home" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><path d="M9 22V12h6v10"/></symbol>
1103
+ <symbol id="i-user" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></symbol>
1104
+ <symbol id="i-folder" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"/></symbol>
1105
+ <symbol id="i-copy" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></symbol>
1106
+ <symbol id="i-pencil" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"/></symbol>
1107
+ <symbol id="i-trash" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/></symbol>
1108
+ <symbol id="i-trending-up" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 7 13.5 15.5 8.5 10.5 2 17"/><path d="M16 7h6v6"/></symbol>
1109
+ <symbol id="i-trending-down" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m22 17-8.5-8.5-5 5L2 7"/><path d="M16 17h6v-6"/></symbol>
1110
+ <symbol id="i-minus" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14"/></symbol>
1111
+ <symbol id="i-more-horizontal" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="1"/><circle cx="19" cy="12" r="1"/><circle cx="5" cy="12" r="1"/></symbol>
1112
+ <symbol id="i-chart" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 20V10"/><path d="M18 20V4"/><path d="M6 20v-6"/></symbol>
1113
+ <symbol id="i-inbox" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="22 12 16 12 14 15 10 15 8 12 2 12"/><path d="M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z"/></symbol>
1114
+ <symbol id="i-sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4"/><path d="M12 2v2"/><path d="M12 20v2"/><path d="m4.93 4.93 1.41 1.41"/><path d="m17.66 17.66 1.41 1.41"/><path d="M2 12h2"/><path d="M20 12h2"/><path d="m6.34 17.66-1.41 1.41"/><path d="m19.07 4.93-1.41 1.41"/></symbol>
1115
+ <symbol id="i-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"/></symbol>
1116
+ <symbol id="i-menu" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 6h16"/><path d="M4 12h16"/><path d="M4 18h16"/></symbol>
1117
+ <symbol id="i-download" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="m7 10 5 5 5-5"/><path d="M12 15V3"/></symbol>
1118
+ </defs>
1119
+ </svg>
1120
+
1121
+ <header class="ds-topbar">
1122
+ <button class="icon-btn icon-btn--ghost ds-hamburger" id="dsHamburger" aria-label="Abrir navegación">
1123
+ <svg><use href="#i-menu"/></svg>
1124
+ </button>
1125
+ <a class="ds-brand" href="#introduccion">
1126
+ <span class="ds-brand__name">Higinio</span>
1127
+ </a>
1128
+ <span class="ds-version">Celeste Clásica · v2.0</span>
1129
+ <div class="ds-topbar__spacer"></div>
1130
+ <button class="icon-btn icon-btn--secondary ds-theme-toggle" id="dsThemeToggle" aria-label="Cambiar tema claro/oscuro" data-tooltip="Cambiar tema">
1131
+ <svg class="ds-icon-sun"><use href="#i-sun"/></svg>
1132
+ <svg class="ds-icon-moon"><use href="#i-moon"/></svg>
1133
+ </button>
1134
+ </header>
1135
+
1136
+ <aside class="ds-sidenav" id="dsSidenav">
1137
+ <nav class="ds-nav" id="dsNav">
1138
+ <div class="ds-nav__group">Fundamentos</div>
1139
+ <a href="#introduccion">Introducción</a>
1140
+ <a href="#voz">Voz y contenido</a>
1141
+ <a href="#colores">Colores</a>
1142
+ <a href="#tipografia">Tipografía</a>
1143
+ <a href="#espaciado">Espaciado</a>
1144
+ <a href="#bordes">Bordes, radios y focus</a>
1145
+ <a href="#elevacion">Elevación</a>
1146
+ <a href="#iconografia">Iconografía</a>
1147
+ <a href="#marca">Marca / wordmark</a>
1148
+ <div class="ds-nav__group">Componentes</div>
1149
+ <a href="#componentes">Todos los componentes</a>
1150
+ <div class="ds-nav__sub">
1151
+ <a href="#comp-botones">Botones</a>
1152
+ <a href="#comp-iconbutton">IconButton</a>
1153
+ <a href="#comp-spinner">Spinner</a>
1154
+ <a href="#comp-input">Input</a>
1155
+ <a href="#comp-textarea">Textarea</a>
1156
+ <a href="#comp-select">Select</a>
1157
+ <a href="#comp-segmented">SegmentedControl</a>
1158
+ <a href="#comp-slider">Slider</a>
1159
+ <a href="#comp-checkbox-radio">Checkbox y Radio</a>
1160
+ <a href="#comp-switch">Switch</a>
1161
+ <a href="#comp-badge-tag">Badge y Tag</a>
1162
+ <a href="#comp-avatar">Avatar</a>
1163
+ <a href="#comp-divider">Divider</a>
1164
+ <a href="#comp-accordion">Accordion</a>
1165
+ <a href="#comp-tooltip">Tooltip</a>
1166
+ <a href="#comp-alert">Alert</a>
1167
+ <a href="#comp-toast">Toast</a>
1168
+ <a href="#comp-progress">Progress y ProgressRing</a>
1169
+ <a href="#comp-modal">Modal</a>
1170
+ <a href="#comp-menu">Menu</a>
1171
+ <a href="#comp-tabla">Tabla</a>
1172
+ <a href="#comp-stat">Stat</a>
1173
+ <a href="#comp-pagination">Pagination</a>
1174
+ <a href="#comp-navegacion">Navegación</a>
1175
+ <a href="#comp-skeleton">Skeleton y EmptyState</a>
1176
+ <a href="#comp-card">Card, Link y Code</a>
1177
+ </div>
1178
+ <div class="ds-nav__group">Recursos</div>
1179
+ <a href="#motion">Motion</a>
1180
+ <a href="#accesibilidad">Accesibilidad</a>
1181
+ <a href="#tokens">Tokens y descarga</a>
1182
+ <a href="#instalacion">Instalación</a>
1183
+ <a href="#mcp">MCP</a>
1184
+ </nav>
1185
+ </aside>
1186
+
1187
+ <main class="ds-main">
1188
+
1189
+ <!-- ============================== INTRODUCCIÓN ============================== -->
1190
+ <section id="introduccion" class="ds-hero">
1191
+ <p class="ds-eyebrow">Sistema de diseño · Higinio Sosa</p>
1192
+ <h1>Celeste Clásica</h1>
1193
+ <p class="ds-lead u-measure">
1194
+ Sistema de marca <strong>light-first</strong>, sereno y confiable, para todos los productos de
1195
+ software de Higinio Sosa: web apps, dashboards, landing pages y móvil. La elegancia viene del
1196
+ orden y el aire, no de la decoración.
1197
+ </p>
1198
+ <div class="ds-hero__palette" aria-hidden="true">
1199
+ <div class="ds-hero__chip" style="background:#a0c0d4"></div>
1200
+ <div class="ds-hero__chip" style="background:#7192ba"></div>
1201
+ <div class="ds-hero__chip" style="background:#31343e"></div>
1202
+ </div>
1203
+
1204
+ <div style="display:flex;gap:16px;flex-wrap:wrap;margin:24px 0;">
1205
+ <button class="btn btn--primary" id="dsHeroStart">Empezar a construir</button>
1206
+ <button class="btn btn--outline" id="dsHeroTokens">Ver tokens</button>
1207
+ </div>
1208
+
1209
+ <div class="ds-principles">
1210
+ <div class="card"><span class="ds-num">01</span><h4>Claridad primero</h4><p>El contenido siempre por encima de la decoración.</p></div>
1211
+ <div class="card"><span class="ds-num">02</span><h4>Calma visual</h4><p>Superficies claras, whitespace generoso, contraste solo donde importa.</p></div>
1212
+ <div class="card"><span class="ds-num">03</span><h4>Un acento a la vez</h4><p>El azul acero guía la acción; nunca compite consigo mismo.</p></div>
1213
+ <div class="card"><span class="ds-num">04</span><h4>Consistencia sobre novedad</h4><p>Mismos tokens en todos los productos; cero valores mágicos.</p></div>
1214
+ <div class="card"><span class="ds-num">05</span><h4>Movimiento con propósito</h4><p>La animación explica — aparece, cambia, desaparece — nunca decora sola.</p></div>
1215
+ </div>
1216
+
1217
+ <div class="alert alert--info" style="margin-top:24px">
1218
+ <svg><use href="#i-info"/></svg>
1219
+ <div>
1220
+ <p class="alert__title">Regla de oro</p>
1221
+ <p class="alert__body">Los componentes consumen solo tokens semánticos (<span class="u-mono">--surface</span>, <span class="u-mono">--accent</span>, <span class="u-mono">--link</span>…), nunca hex directos. El cambio de tema es un atributo, no un refactor.</p>
1222
+ </div>
1223
+ </div>
1224
+ </section>
1225
+
1226
+ <!-- ============================== VOZ Y CONTENIDO ============================== -->
1227
+ <section id="voz">
1228
+ <p class="ds-eyebrow">Fundamentos</p>
1229
+ <h2>Voz y contenido</h2>
1230
+ <p class="ds-lead">Una consultora seria pero cálida — "banca privada boutique", no "startup ruidosa".</p>
1231
+ <div class="ds-demo ds-demo--col">
1232
+ <table class="table">
1233
+ <thead><tr><th>Regla</th><th>Sí</th><th>No</th></tr></thead>
1234
+ <tbody>
1235
+ <tr><td>Idioma: español LatAm neutro, de "tú"</td><td>Guarda tus cambios</td><td>Guarde usted los cambios</td></tr>
1236
+ <tr><td>Sentence case en todo</td><td>Eliminar cuenta</td><td>Eliminar Cuenta / ELIMINAR</td></tr>
1237
+ <tr><td>Botones imperativos de 1–3 palabras</td><td>Guardar</td><td>Guardando los cambios ahora</td></tr>
1238
+ <tr><td>Errores accionables</td><td>El correo no tiene un formato válido. Revísalo.</td><td>Error</td></tr>
1239
+ <tr><td>Sin exclamaciones ni emoji</td><td>Cuenta creada</td><td>¡Cuenta creada! 🎉</td></tr>
1240
+ <tr><td>Voz en segunda persona</td><td>Tus proyectos</td><td>Nuestros proyectos para ti</td></tr>
1241
+ </tbody>
1242
+ </table>
1243
+ </div>
1244
+ </section>
1245
+
1246
+ <!-- ============================== COLORES ============================== -->
1247
+ <section id="colores">
1248
+ <p class="ds-eyebrow">Fundamentos</p>
1249
+ <h2>Colores</h2>
1250
+ <p class="ds-lead">Un solo acento — azul acero <span class="u-mono">#7192ba</span> — sobre neutrales grisáceo-azulados derivados de la tinta <span class="u-mono">#31343e</span>. Haz clic en cualquier muestra para copiar su variable.</p>
1251
+
1252
+ <h3>Primario — Azul acero</h3>
1253
+ <div class="ds-swatches" id="swatchesPrimary"></div>
1254
+
1255
+ <h3>Neutrales — Gris azulado</h3>
1256
+ <div class="ds-swatches" id="swatchesNeutral"></div>
1257
+
1258
+ <h3>Semánticos — ramp de 3 tonos</h3>
1259
+ <p>Cada estado tiene el sólido (<span class="u-mono">-500</span>), el fondo suave (<span class="u-mono">-100</span>) y el tono de texto AA sobre ese fondo (<span class="u-mono">-text</span>).</p>
1260
+ <div class="ds-semantic-grid" id="swatchesSemantic"></div>
1261
+
1262
+ <h3>Aliases semánticos</h3>
1263
+ <p>Los componentes consumen <strong>solo esta capa</strong> — así el cambio de tema es un cambio de atributo.</p>
1264
+ <div class="ds-demo ds-demo--col" style="padding:16px">
1265
+ <table class="table" id="aliasTable">
1266
+ <thead><tr><th style="width:52px"></th><th>Alias</th><th>Valor (light)</th><th>Rol</th></tr></thead>
1267
+ <tbody></tbody>
1268
+ </table>
1269
+ </div>
1270
+
1271
+ <div class="alert alert--warning" style="margin-top:16px">
1272
+ <svg><use href="#i-triangle-alert"/></svg>
1273
+ <div>
1274
+ <p class="alert__title">Regla AA del acento</p>
1275
+ <p class="alert__body"><span class="u-mono">primary-500</span> sobre blanco da ~3:1 — válido solo para componentes UI (rellenos, bordes, focus). Para texto (links, botones ghost) usa <span class="u-mono">--link</span> = <span class="u-mono">primary-700</span> (~6:1). <span class="u-mono">primary-300</span> jamás como texto.</p>
1276
+ </div>
1277
+ </div>
1278
+
1279
+ <h3>Proporción 60-30-10</h3>
1280
+ <div class="ds-ratio">
1281
+ <div style="flex:6;background:var(--bg);color:var(--text-secondary)">60% neutrales claros</div>
1282
+ <div style="flex:3;background:var(--neutral-600);color:#fff">30% tinta y estructura</div>
1283
+ <div style="flex:1;background:var(--accent);color:var(--text-on-primary)">10%</div>
1284
+ </div>
1285
+ <p><strong>Un solo botón primario visible por pantalla</strong>; el resto secundarios o ghost. Fondos siempre planos: sin gradientes, texturas ni patrones. El acento pierde poder si supera el 10&nbsp;%.</p>
1286
+ </section>
1287
+
1288
+ <!-- ============================== TIPOGRAFÍA ============================== -->
1289
+ <section id="tipografia">
1290
+ <p class="ds-eyebrow">Fundamentos</p>
1291
+ <h2>Tipografía</h2>
1292
+ <p class="ds-lead">Tres familias con roles estrictos. Escala 1.25 (tercera mayor) desde 16&nbsp;px.</p>
1293
+
1294
+ <div class="grid" style="margin-bottom:24px">
1295
+ <div class="card col-12 col-md-4 ds-font-card">
1296
+ <h4>Fraunces</h4>
1297
+ <div class="ds-font-sample" style="font-family:var(--font-display);font-weight:700">Ag Serena</div>
1298
+ <p>Display 600–700 · solo h1–h2, cifras hero y marketing. Nunca UI ni párrafos.</p>
1299
+ </div>
1300
+ <div class="card col-12 col-md-4 ds-font-card">
1301
+ <h4>Public Sans</h4>
1302
+ <div class="ds-font-sample" style="font-family:var(--font-body);font-weight:500">Ag Clara</div>
1303
+ <p>Body/UI 400–600 · párrafos, botones, formularios, h3–h6. Todo lo demás.</p>
1304
+ </div>
1305
+ <div class="card col-12 col-md-4 ds-font-card">
1306
+ <h4>IBM Plex Mono</h4>
1307
+ <div class="ds-font-sample" style="font-family:var(--font-mono);font-weight:500">0123.45</div>
1308
+ <p>Mono 400–500 · datos numéricos, código, IDs, montos en tablas.</p>
1309
+ </div>
1310
+ </div>
1311
+
1312
+ <h3>Escala</h3>
1313
+ <div id="typeScale"></div>
1314
+ <p style="margin-top:16px">Tracking: <span class="u-mono">-0.02em</span> en display ≥31px · <span class="u-mono">0</span> en body · <span class="u-mono">+0.04em</span> en labels uppercase. Longitud de línea máx. <span class="u-mono">65ch</span>. Números en tablas con <span class="u-mono">tabular-nums</span>. Mínimo absoluto 12&nbsp;px.</p>
1315
+ </section>
1316
+
1317
+ <!-- ============================== ESPACIADO ============================== -->
1318
+ <section id="espaciado">
1319
+ <p class="ds-eyebrow">Fundamentos</p>
1320
+ <h2>Espaciado</h2>
1321
+ <p class="ds-lead">Escala base 4&nbsp;px — cero valores mágicos. El número del token = px ÷ 4.</p>
1322
+ <div class="ds-space-list" id="spaceScale"></div>
1323
+ <h3>Aliases de uso</h3>
1324
+ <div class="ds-demo ds-demo--col" style="padding:16px">
1325
+ <table class="table">
1326
+ <thead><tr><th>Alias</th><th class="num">Valor</th><th>Uso</th></tr></thead>
1327
+ <tbody>
1328
+ <tr><td class="u-mono">--card-padding</td><td class="num">24px</td><td>Padding de tarjetas (16px en móvil)</td></tr>
1329
+ <tr><td class="u-mono">--section-gap</td><td class="num">64px</td><td>Separación entre secciones</td></tr>
1330
+ <tr><td class="u-mono">--form-gap</td><td class="num">20px</td><td>Gap entre campos de formulario</td></tr>
1331
+ <tr><td class="u-mono">--button-padding-md</td><td class="num">12px 20px</td><td>Botón md (sm: 8px 14px)</td></tr>
1332
+ <tr><td class="u-mono">--control-height-*</td><td class="num">36 / 44 / 52</td><td>Alturas de controles sm / md / lg</td></tr>
1333
+ <tr><td class="u-mono">--gutter</td><td class="num">24px</td><td>Gutter del grid de 12 columnas (16px móvil)</td></tr>
1334
+ </tbody>
1335
+ </table>
1336
+ </div>
1337
+ </section>
1338
+
1339
+ <!-- ============================== BORDES, RADIOS Y FOCUS ============================== -->
1340
+ <section id="bordes">
1341
+ <p class="ds-eyebrow">Fundamentos</p>
1342
+ <h2>Bordes, radios y focus</h2>
1343
+ <p class="ds-lead">Bordes de 1&nbsp;px, radios generosos y un focus siempre visible — la geometría que sostiene la calma de la marca.</p>
1344
+ <h3>Radios</h3>
1345
+ <div class="ds-demo">
1346
+ <div class="ds-radius-box" style="border-radius:var(--radius-sm)">sm · 6</div>
1347
+ <div class="ds-radius-box" style="border-radius:var(--radius-md)">md · 10</div>
1348
+ <div class="ds-radius-box" style="border-radius:var(--radius-lg)">lg · 14</div>
1349
+ <div class="ds-radius-box" style="border-radius:var(--radius-xl)">xl · 20</div>
1350
+ <div class="ds-radius-box" style="border-radius:var(--radius-full);width:72px">full</div>
1351
+ </div>
1352
+ <p>Borde por defecto <span class="u-mono">1px solid var(--border)</span>. Focus ring global de teclado: <span class="u-mono">2px var(--primary-500)</span> con offset 2px vía <span class="u-mono">:focus-visible</span> — el click de ratón no lo dispara. Único uso de transparencia: overlay de modal y ring de focus. <strong>No hay blur ni glassmorphism en Celeste Clásica.</strong></p>
1353
+ </section>
1354
+
1355
+ <!-- ============================== ELEVACIÓN ============================== -->
1356
+ <section id="elevacion">
1357
+ <p class="ds-eyebrow">Fundamentos</p>
1358
+ <h2>Elevación</h2>
1359
+ <p class="ds-lead">Sombras frías teñidas con la tinta <span class="u-mono">rgba(49,52,62,…)</span>, nunca negro puro. Máximo 3 niveles de elevación visibles por pantalla. Sin resplandor de acento: el <span class="u-mono">glow</span> es exclusivo de Nocturna Eléctrica.</p>
1360
+ <h3>Niveles</h3>
1361
+ <div class="ds-demo ds-demo--grid ds-demo--bg">
1362
+ <div class="ds-elev-box" style="background:var(--bg);border:1px dashed var(--border)">Nivel 0<br>reposo · sin sombra</div>
1363
+ <div class="ds-elev-box" style="background:var(--surface);box-shadow:var(--shadow-xs);border:var(--border-width) solid var(--border)">Nivel 1<br>shadow-xs/sm · inputs, tarjetas</div>
1364
+ <div class="ds-elev-box" style="background:var(--surface);box-shadow:var(--shadow-md);border:var(--border-width) solid var(--border)">Nivel 2<br>shadow-md · hover, dropdowns</div>
1365
+ <div class="ds-elev-box" style="background:var(--surface);box-shadow:var(--shadow-lg);border:var(--border-width) solid var(--border-strong)">Nivel 3<br>shadow-lg/xl · modales, diálogos</div>
1366
+ </div>
1367
+ <h3>Sombras</h3>
1368
+ <div class="ds-demo ds-demo--bg">
1369
+ <div class="ds-shadow-box" style="box-shadow:var(--shadow-xs)">xs</div>
1370
+ <div class="ds-shadow-box" style="box-shadow:var(--shadow-sm)">sm</div>
1371
+ <div class="ds-shadow-box" style="box-shadow:var(--shadow-md)">md</div>
1372
+ <div class="ds-shadow-box" style="box-shadow:var(--shadow-lg)">lg</div>
1373
+ <div class="ds-shadow-box" style="box-shadow:var(--shadow-xl)">xl</div>
1374
+ </div>
1375
+ </section>
1376
+
1377
+ <!-- ============================== ICONOGRAFÍA ============================== -->
1378
+ <section id="iconografia">
1379
+ <p class="ds-eyebrow">Fundamentos</p>
1380
+ <h2>Iconografía</h2>
1381
+ <p class="ds-lead">Set sustituto: <a class="link" href="https://lucide.dev" target="_blank" rel="noopener noreferrer">Lucide</a> — trazo 1.5–2&nbsp;px, esquinas redondeadas, geométrico. Si existe un set propio de marca, reemplazar.</p>
1382
+ <div class="ds-icon-grid">
1383
+ <div class="ds-icon-cell"><svg><use href="#i-home"/></svg><span>home</span></div>
1384
+ <div class="ds-icon-cell"><svg><use href="#i-search"/></svg><span>search</span></div>
1385
+ <div class="ds-icon-cell"><svg><use href="#i-bell"/></svg><span>bell</span></div>
1386
+ <div class="ds-icon-cell"><svg><use href="#i-user"/></svg><span>user</span></div>
1387
+ <div class="ds-icon-cell"><svg><use href="#i-folder"/></svg><span>folder</span></div>
1388
+ <div class="ds-icon-cell"><svg><use href="#i-chart"/></svg><span>chart</span></div>
1389
+ </div>
1390
+ <ul style="max-width:var(--measure)">
1391
+ <li>Los iconos se insertan <strong>inline como SVG</strong> para heredar <span class="u-mono">currentColor</span>.</li>
1392
+ <li>Tamaños: 14&nbsp;px inline con texto pequeño · 16–18&nbsp;px inputs y chevrons · 20&nbsp;px botones y toolbars · 40&nbsp;px empty states (en <span class="u-mono">primary-300</span> decorativo).</li>
1393
+ <li>Sin emoji; sin Unicode-como-icono (excepto el <span class="u-mono">/</span> tipográfico de breadcrumbs).</li>
1394
+ </ul>
1395
+ </section>
1396
+
1397
+ <!-- ============================== MARCA / WORDMARK ============================== -->
1398
+ <section id="marca">
1399
+ <p class="ds-eyebrow">Fundamentos</p>
1400
+ <h2>Marca / wordmark</h2>
1401
+ <p class="ds-lead">No hay logo ni imágenes de marca. Donde un mockup necesite logotipo se renderiza <strong>«Higinio»</strong> (o el nombre del producto) en <span class="u-mono">--font-display</span> (Fraunces) — nunca se dibuja un logo aproximado.</p>
1402
+ <div class="ds-demo" style="gap:32px">
1403
+ <span class="ds-wordmark-demo">Higinio</span>
1404
+ <span class="ds-wordmark-demo ds-wordmark-badge">Higinio</span>
1405
+ </div>
1406
+ <ul style="max-width:var(--measure)">
1407
+ <li>El wordmark es texto, no un icono: <span class="u-mono">--font-display</span> (Fraunces 600–700), nunca en UI ni párrafos.</li>
1408
+ <li>Sentence case siempre: «Higinio», nunca «HIGINIO» ni «higinio».</li>
1409
+ <li>Color <span class="u-mono">--text-primary</span> (tinta); <span class="u-mono">primary-300</span> jamás como texto, tampoco en el wordmark.</li>
1410
+ <li>No se genera ilustración ni isotipo para no inventar assets: se usan placeholders y se piden los materiales reales.</li>
1411
+ </ul>
1412
+ </section>
1413
+
1414
+ <!-- ============================== COMPONENTES ============================== -->
1415
+ <section id="componentes">
1416
+ <p class="ds-eyebrow">Biblioteca</p>
1417
+ <h2>Componentes</h2>
1418
+ <p class="ds-lead">Los 26 componentes del sistema, renderizados en vivo con las clases del framework <span class="u-mono">celeste-clasica.css</span>. Cada bloque incluye su código de uso.</p>
1419
+
1420
+ <!-- Botones -->
1421
+ <div class="ds-component" id="comp-botones">
1422
+ <h3>Botones</h3>
1423
+ <p class="ds-spec">Alturas 36 / 44 / 52 px · radius md · peso 600 · un solo primario visible por pantalla. Ghost y outline usan <span class="u-mono">--link</span> (AA), no <span class="u-mono">primary-500</span>.</p>
1424
+ <div class="ds-demo">
1425
+ <button class="btn btn--primary">Guardar cambios</button>
1426
+ <button class="btn btn--secondary">Cancelar</button>
1427
+ <button class="btn btn--outline">Exportar</button>
1428
+ <button class="btn btn--ghost">Ver detalles</button>
1429
+ <button class="btn btn--danger">Eliminar cuenta</button>
1430
+ </div>
1431
+ <div class="ds-demo">
1432
+ <button class="btn btn--primary btn--sm">Pequeño</button>
1433
+ <button class="btn btn--primary">Mediano</button>
1434
+ <button class="btn btn--primary btn--lg">Grande</button>
1435
+ <button class="btn btn--primary" disabled>Deshabilitado</button>
1436
+ <button class="btn btn--secondary" style="min-width:150px">
1437
+ <svg class="spinner" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="9" stroke="currentColor" stroke-width="2.5"/><path d="M21 12a9 9 0 0 0-9-9" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"/></svg>
1438
+ Guardando…
1439
+ </button>
1440
+ <button class="btn btn--primary"><svg><use href="#i-plus"/></svg>Crear proyecto</button>
1441
+ </div>
1442
+ <details class="ds-code"><summary>Ver código</summary>
1443
+ <div class="ds-code-box"><button class="ds-copy">Copiar</button>
1444
+ <pre class="ds-pre"><code>&lt;button class="btn btn--primary"&gt;Guardar cambios&lt;/button&gt;
1445
+ &lt;button class="btn btn--secondary"&gt;Cancelar&lt;/button&gt;
1446
+ &lt;button class="btn btn--outline"&gt;Exportar&lt;/button&gt;
1447
+ &lt;button class="btn btn--ghost"&gt;Ver detalles&lt;/button&gt;
1448
+ &lt;button class="btn btn--danger"&gt;Eliminar cuenta&lt;/button&gt;
1449
+
1450
+ &lt;!-- Tamaños: btn--sm · (md por defecto) · btn--lg --&gt;
1451
+ &lt;button class="btn btn--primary btn--lg"&gt;Grande&lt;/button&gt;</code></pre>
1452
+ </div>
1453
+ </details>
1454
+ </div>
1455
+
1456
+ <!-- IconButton -->
1457
+ <div class="ds-component" id="comp-iconbutton">
1458
+ <h3>IconButton</h3>
1459
+ <p class="ds-spec">Botón cuadrado solo-icono para toolbars y chrome. Default <span class="u-mono">ghost</span>. Siempre con <span class="u-mono">aria-label</span>.</p>
1460
+ <div class="ds-demo">
1461
+ <button class="icon-btn icon-btn--ghost" aria-label="Buscar" data-tooltip="Buscar"><svg><use href="#i-search"/></svg></button>
1462
+ <button class="icon-btn icon-btn--secondary" aria-label="Notificaciones" data-tooltip="Notificaciones"><svg><use href="#i-bell"/></svg></button>
1463
+ <button class="icon-btn icon-btn--primary" aria-label="Crear" data-tooltip="Crear"><svg><use href="#i-plus"/></svg></button>
1464
+ <button class="icon-btn icon-btn--ghost icon-btn--sm" aria-label="Copiar"><svg><use href="#i-copy"/></svg></button>
1465
+ <button class="icon-btn icon-btn--ghost icon-btn--lg" aria-label="Editar"><svg><use href="#i-pencil"/></svg></button>
1466
+ <button class="icon-btn icon-btn--ghost" aria-label="Eliminar" disabled><svg><use href="#i-trash"/></svg></button>
1467
+ </div>
1468
+ <details class="ds-code"><summary>Ver código</summary>
1469
+ <div class="ds-code-box"><button class="ds-copy">Copiar</button>
1470
+ <pre class="ds-pre"><code>&lt;button class="icon-btn icon-btn--ghost" aria-label="Buscar"&gt;
1471
+ &lt;svg&gt;…icono 18px…&lt;/svg&gt;
1472
+ &lt;/button&gt;
1473
+ &lt;!-- Variantes: --ghost (default) · --secondary · --primary
1474
+ Tamaños: --sm 32px · (md 40px) · --lg 48px --&gt;</code></pre>
1475
+ </div>
1476
+ </details>
1477
+ </div>
1478
+
1479
+ <!-- Spinner -->
1480
+ <div class="ds-component" id="comp-spinner">
1481
+ <h3>Spinner</h3>
1482
+ <p class="ds-spec">Trazo 2.5, rotación 0.8 s linear. Si la carga dura &lt;300 ms, no mostrar spinner. Blanco dentro de botones primarios.</p>
1483
+ <div class="ds-demo">
1484
+ <svg class="spinner" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="9" stroke="currentColor" stroke-width="2.5"/><path d="M21 12a9 9 0 0 0-9-9" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"/></svg>
1485
+ <svg class="spinner" style="width:24px;height:24px" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="9" stroke="currentColor" stroke-width="2.5"/><path d="M21 12a9 9 0 0 0-9-9" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"/></svg>
1486
+ <svg class="spinner" style="width:32px;height:32px;color:var(--text-muted)" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="9" stroke="currentColor" stroke-width="2.5"/><path d="M21 12a9 9 0 0 0-9-9" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"/></svg>
1487
+ </div>
1488
+ <details class="ds-code"><summary>Ver código</summary>
1489
+ <div class="ds-code-box"><button class="ds-copy">Copiar</button>
1490
+ <pre class="ds-pre"><code>&lt;svg class="spinner" viewBox="0 0 24 24" fill="none"&gt;
1491
+ &lt;circle cx="12" cy="12" r="9" stroke="currentColor" stroke-width="2.5"/&gt;
1492
+ &lt;path d="M21 12a9 9 0 0 0-9-9" stroke="currentColor"
1493
+ stroke-width="2.5" stroke-linecap="round"/&gt;
1494
+ &lt;/svg&gt;</code></pre>
1495
+ </div>
1496
+ </details>
1497
+ </div>
1498
+
1499
+ <!-- Input -->
1500
+ <div class="ds-component" id="comp-input">
1501
+ <h3>Input</h3>
1502
+ <p class="ds-spec">Altura 44 px · label siempre visible (nunca placeholder como label) · focus con borde <span class="u-mono">--accent</span> + ring.</p>
1503
+ <div class="ds-demo ds-demo--col" style="max-width:420px">
1504
+ <div class="field">
1505
+ <label class="field__label" for="demo-input-1">Correo electrónico</label>
1506
+ <input class="input" id="demo-input-1" type="email" placeholder="tu@correo.com">
1507
+ <span class="field__help">Nunca compartiremos tu correo.</span>
1508
+ </div>
1509
+ <div class="field">
1510
+ <label class="field__label" for="demo-input-2">Buscar</label>
1511
+ <div class="input-icon">
1512
+ <svg><use href="#i-search"/></svg>
1513
+ <input class="input" id="demo-input-2" type="text" placeholder="Buscar proyectos…">
1514
+ </div>
1515
+ </div>
1516
+ <div class="field">
1517
+ <label class="field__label" for="demo-input-3">Contraseña</label>
1518
+ <input class="input input--error" id="demo-input-3" type="password" value="123">
1519
+ <span class="field__error"><svg><use href="#i-circle-alert"/></svg>La contraseña necesita al menos 8 caracteres. Agrega más.</span>
1520
+ </div>
1521
+ <div class="field">
1522
+ <label class="field__label" for="demo-input-4">Usuario</label>
1523
+ <input class="input input--success" id="demo-input-4" type="text" value="higinio.sosa">
1524
+ </div>
1525
+ <div class="field">
1526
+ <label class="field__label" for="demo-input-5">Plan</label>
1527
+ <input class="input" id="demo-input-5" type="text" value="Profesional" disabled>
1528
+ </div>
1529
+ </div>
1530
+ <details class="ds-code"><summary>Ver código</summary>
1531
+ <div class="ds-code-box"><button class="ds-copy">Copiar</button>
1532
+ <pre class="ds-pre"><code>&lt;div class="field"&gt;
1533
+ &lt;label class="field__label" for="correo"&gt;Correo electrónico&lt;/label&gt;
1534
+ &lt;input class="input" id="correo" type="email" placeholder="tu@correo.com"&gt;
1535
+ &lt;span class="field__help"&gt;Nunca compartiremos tu correo.&lt;/span&gt;
1536
+ &lt;/div&gt;
1537
+
1538
+ &lt;!-- Estados: input--error (+ .field__error) · input--success · disabled
1539
+ Icono leading: envolver en .input-icon --&gt;</code></pre>
1540
+ </div>
1541
+ </details>
1542
+ </div>
1543
+
1544
+ <!-- Textarea -->
1545
+ <div class="ds-component" id="comp-textarea">
1546
+ <h3>Textarea</h3>
1547
+ <p class="ds-spec">Min-height 96 px · <span class="u-mono">resize: vertical</span> · contador opcional alineado a la derecha.</p>
1548
+ <div class="ds-demo ds-demo--col" style="max-width:420px">
1549
+ <div class="field">
1550
+ <label class="field__label" for="demo-ta">Descripción</label>
1551
+ <textarea class="textarea" id="demo-ta" maxlength="200" placeholder="Describe tu proyecto…"></textarea>
1552
+ <span class="textarea-counter" id="demo-ta-counter">0 / 200</span>
1553
+ </div>
1554
+ </div>
1555
+ <details class="ds-code"><summary>Ver código</summary>
1556
+ <div class="ds-code-box"><button class="ds-copy">Copiar</button>
1557
+ <pre class="ds-pre"><code>&lt;div class="field"&gt;
1558
+ &lt;label class="field__label" for="desc"&gt;Descripción&lt;/label&gt;
1559
+ &lt;textarea class="textarea" id="desc" maxlength="200"&gt;&lt;/textarea&gt;
1560
+ &lt;span class="textarea-counter"&gt;0 / 200&lt;/span&gt;
1561
+ &lt;/div&gt;</code></pre>
1562
+ </div>
1563
+ </details>
1564
+ </div>
1565
+
1566
+ <!-- Select -->
1567
+ <div class="ds-component" id="comp-select">
1568
+ <h3>Select</h3>
1569
+ <p class="ds-spec">Trigger igual que input + chevron 16 px que rota al abrir. Menú con <span class="u-mono">shadow-lg</span> y opción seleccionada en <span class="u-mono">--accent-soft</span>.</p>
1570
+ <div class="ds-demo ds-demo--col" style="max-width:420px">
1571
+ <div class="field">
1572
+ <label class="field__label" for="demo-select">Zona horaria</label>
1573
+ <div class="select-wrap">
1574
+ <select class="select" id="demo-select">
1575
+ <option>Ciudad de México (GMT-6)</option>
1576
+ <option>Bogotá (GMT-5)</option>
1577
+ <option>Buenos Aires (GMT-3)</option>
1578
+ <option>Madrid (GMT+1)</option>
1579
+ </select>
1580
+ </div>
1581
+ </div>
1582
+ </div>
1583
+ <details class="ds-code"><summary>Ver código</summary>
1584
+ <div class="ds-code-box"><button class="ds-copy">Copiar</button>
1585
+ <pre class="ds-pre"><code>&lt;div class="select-wrap"&gt;
1586
+ &lt;select class="select"&gt;
1587
+ &lt;option&gt;Ciudad de México (GMT-6)&lt;/option&gt;
1588
+ &lt;option&gt;Bogotá (GMT-5)&lt;/option&gt;
1589
+ &lt;/select&gt;
1590
+ &lt;/div&gt;</code></pre>
1591
+ </div>
1592
+ </details>
1593
+ </div>
1594
+
1595
+ <!-- SegmentedControl -->
1596
+ <div class="ds-component" id="comp-segmented">
1597
+ <h3>SegmentedControl</h3>
1598
+ <p class="ds-spec">2–4 opciones mutuamente excluyentes. Indicador blanco con <span class="u-mono">shadow-xs</span>. Para más de 4 opciones, usar Select.</p>
1599
+ <div class="ds-demo">
1600
+ <div class="segmented" role="radiogroup" aria-label="Vista">
1601
+ <input type="radio" name="seg-vista" id="seg-lista" checked><label for="seg-lista">Lista</label>
1602
+ <input type="radio" name="seg-vista" id="seg-tablero"><label for="seg-tablero">Tablero</label>
1603
+ <input type="radio" name="seg-vista" id="seg-cal"><label for="seg-cal">Calendario</label>
1604
+ </div>
1605
+ <div class="segmented segmented--sm" role="radiogroup" aria-label="Periodo">
1606
+ <input type="radio" name="seg-per" id="seg-7d" checked><label for="seg-7d">7 d</label>
1607
+ <input type="radio" name="seg-per" id="seg-30d"><label for="seg-30d">30 d</label>
1608
+ <input type="radio" name="seg-per" id="seg-12m"><label for="seg-12m">12 m</label>
1609
+ </div>
1610
+ </div>
1611
+ <details class="ds-code"><summary>Ver código</summary>
1612
+ <div class="ds-code-box"><button class="ds-copy">Copiar</button>
1613
+ <pre class="ds-pre"><code>&lt;div class="segmented" role="radiogroup" aria-label="Vista"&gt;
1614
+ &lt;input type="radio" name="vista" id="lista" checked&gt;&lt;label for="lista"&gt;Lista&lt;/label&gt;
1615
+ &lt;input type="radio" name="vista" id="tablero"&gt;&lt;label for="tablero"&gt;Tablero&lt;/label&gt;
1616
+ &lt;/div&gt;
1617
+ &lt;!-- Compacto: añadir segmented--sm --&gt;</code></pre>
1618
+ </div>
1619
+ </details>
1620
+ </div>
1621
+
1622
+ <!-- Slider -->
1623
+ <div class="ds-component" id="comp-slider">
1624
+ <h3>Slider</h3>
1625
+ <p class="ds-spec">Track 6 px con porción rellena en <span class="u-mono">--accent</span> · thumb blanco 18 px con borde de acento que escala 1.15 al presionar.</p>
1626
+ <div class="ds-demo ds-demo--col" style="max-width:420px">
1627
+ <div>
1628
+ <div class="slider-row">
1629
+ <span class="slider-label">Límite de gasto</span>
1630
+ <span class="slider-value" id="demo-slider-value">$ 350</span>
1631
+ </div>
1632
+ <input class="slider js-slider" id="demo-slider" type="range" min="0" max="1000" step="10" value="350" aria-label="Límite de gasto">
1633
+ </div>
1634
+ <input class="slider js-slider" type="range" min="0" max="100" value="70" disabled aria-label="Slider deshabilitado">
1635
+ </div>
1636
+ <details class="ds-code"><summary>Ver código</summary>
1637
+ <div class="ds-code-box"><button class="ds-copy">Copiar</button>
1638
+ <pre class="ds-pre"><code>&lt;div class="slider-row"&gt;
1639
+ &lt;span class="slider-label"&gt;Límite de gasto&lt;/span&gt;
1640
+ &lt;span class="slider-value"&gt;$ 350&lt;/span&gt;
1641
+ &lt;/div&gt;
1642
+ &lt;input class="slider" type="range" min="0" max="1000" value="350"&gt;
1643
+ &lt;!-- JS: actualizar --slider-fill = ((v-min)/(max-min))*100 + '%' --&gt;</code></pre>
1644
+ </div>
1645
+ </details>
1646
+ </div>
1647
+
1648
+ <!-- Checkbox y Radio -->
1649
+ <div class="ds-component" id="comp-checkbox-radio">
1650
+ <h3>Checkbox y Radio</h3>
1651
+ <p class="ds-spec">20 px · borde 2 px · check que se dibuja con <span class="u-mono">ease-spring</span> · label clicable con gap 10 px.</p>
1652
+ <div class="ds-demo">
1653
+ <label class="checkbox"><input type="checkbox" checked><span class="checkbox__box"><svg viewBox="0 0 24 24"><path d="M20 6 9 17l-5-5"/></svg></span>Recibir novedades</label>
1654
+ <label class="checkbox"><input type="checkbox"><span class="checkbox__box"><svg viewBox="0 0 24 24"><path d="M20 6 9 17l-5-5"/></svg></span>Recordarme</label>
1655
+ <label class="checkbox"><input type="checkbox" disabled><span class="checkbox__box"><svg viewBox="0 0 24 24"><path d="M20 6 9 17l-5-5"/></svg></span>Deshabilitado</label>
1656
+ </div>
1657
+ <div class="ds-demo" role="radiogroup" aria-label="Plan">
1658
+ <label class="radio"><input type="radio" name="demo-plan" checked><span class="radio__dot"></span>Plan mensual</label>
1659
+ <label class="radio"><input type="radio" name="demo-plan"><span class="radio__dot"></span>Plan anual</label>
1660
+ <label class="radio"><input type="radio" name="demo-plan" disabled><span class="radio__dot"></span>Deshabilitado</label>
1661
+ </div>
1662
+ <details class="ds-code"><summary>Ver código</summary>
1663
+ <div class="ds-code-box"><button class="ds-copy">Copiar</button>
1664
+ <pre class="ds-pre"><code>&lt;label class="checkbox"&gt;
1665
+ &lt;input type="checkbox" checked&gt;
1666
+ &lt;span class="checkbox__box"&gt;
1667
+ &lt;svg viewBox="0 0 24 24"&gt;&lt;path d="M20 6 9 17l-5-5"/&gt;&lt;/svg&gt;
1668
+ &lt;/span&gt;
1669
+ Recibir novedades
1670
+ &lt;/label&gt;
1671
+
1672
+ &lt;label class="radio"&gt;
1673
+ &lt;input type="radio" name="plan" checked&gt;
1674
+ &lt;span class="radio__dot"&gt;&lt;/span&gt;
1675
+ Plan mensual
1676
+ &lt;/label&gt;</code></pre>
1677
+ </div>
1678
+ </details>
1679
+ </div>
1680
+
1681
+ <!-- Switch -->
1682
+ <div class="ds-component" id="comp-switch">
1683
+ <h3>Switch</h3>
1684
+ <p class="ds-spec">Track 44×24 px · thumb 20 px que desliza 20 px en 200 ms <span class="u-mono">ease-spring</span>.</p>
1685
+ <div class="ds-demo">
1686
+ <label class="switch"><input type="checkbox" checked><span class="switch__track"></span>Notificaciones</label>
1687
+ <label class="switch"><input type="checkbox"><span class="switch__track"></span>Modo compacto</label>
1688
+ <label class="switch"><input type="checkbox" disabled><span class="switch__track"></span>Deshabilitado</label>
1689
+ </div>
1690
+ <details class="ds-code"><summary>Ver código</summary>
1691
+ <div class="ds-code-box"><button class="ds-copy">Copiar</button>
1692
+ <pre class="ds-pre"><code>&lt;label class="switch"&gt;
1693
+ &lt;input type="checkbox" checked&gt;
1694
+ &lt;span class="switch__track"&gt;&lt;/span&gt;
1695
+ Notificaciones
1696
+ &lt;/label&gt;</code></pre>
1697
+ </div>
1698
+ </details>
1699
+ </div>
1700
+
1701
+ <!-- Badge y Tag -->
1702
+ <div class="ds-component" id="comp-badge-tag">
1703
+ <h3>Badge y Tag</h3>
1704
+ <p class="ds-spec">Fondo suave semántico (<span class="u-mono">*-100</span>) + texto <span class="u-mono">--*-text</span> (AA garantizado). Estado nunca solo con color: color + icono + texto.</p>
1705
+ <div class="ds-demo">
1706
+ <span class="badge">Borrador</span>
1707
+ <span class="badge badge--success"><svg><use href="#i-circle-check"/></svg>Activo</span>
1708
+ <span class="badge badge--warning"><svg><use href="#i-triangle-alert"/></svg>Pendiente</span>
1709
+ <span class="badge badge--danger"><svg><use href="#i-circle-alert"/></svg>Vencido</span>
1710
+ <span class="badge badge--info"><svg><use href="#i-info"/></svg>Beta</span>
1711
+ </div>
1712
+ <div class="ds-demo">
1713
+ <span class="tag">diseño<button class="tag__close" aria-label="Quitar etiqueta diseño"><svg><use href="#i-x"/></svg></button></span>
1714
+ <span class="tag">frontend<button class="tag__close" aria-label="Quitar etiqueta frontend"><svg><use href="#i-x"/></svg></button></span>
1715
+ <span class="tag">urgente<button class="tag__close" aria-label="Quitar etiqueta urgente"><svg><use href="#i-x"/></svg></button></span>
1716
+ </div>
1717
+ <details class="ds-code"><summary>Ver código</summary>
1718
+ <div class="ds-code-box"><button class="ds-copy">Copiar</button>
1719
+ <pre class="ds-pre"><code>&lt;span class="badge badge--success"&gt;
1720
+ &lt;svg&gt;…icono…&lt;/svg&gt;Activo
1721
+ &lt;/span&gt;
1722
+ &lt;!-- Tonos: (neutral) · --success · --warning · --danger · --info --&gt;
1723
+
1724
+ &lt;span class="tag"&gt;diseño
1725
+ &lt;button class="tag__close" aria-label="Quitar etiqueta"&gt;
1726
+ &lt;svg&gt;…x…&lt;/svg&gt;
1727
+ &lt;/button&gt;
1728
+ &lt;/span&gt;</code></pre>
1729
+ </div>
1730
+ </details>
1731
+ </div>
1732
+
1733
+ <!-- Avatar -->
1734
+ <div class="ds-component" id="comp-avatar">
1735
+ <h3>Avatar</h3>
1736
+ <p class="ds-spec">Fallback automático a iniciales sobre chip <span class="u-mono">primary-100</span>. Dot de presencia: 28 % del diámetro con anillo de superficie.</p>
1737
+ <div class="ds-demo">
1738
+ <span class="avatar avatar--sm"><span class="avatar__initials">HS</span></span>
1739
+ <span class="avatar"><span class="avatar__initials">HS</span></span>
1740
+ <span class="avatar avatar--lg"><span class="avatar__initials">MG</span></span>
1741
+ <span class="avatar avatar--xl"><span class="avatar__initials">AL</span></span>
1742
+ <span class="avatar avatar--lg"><span class="avatar__initials">HS</span><i class="avatar__dot avatar__dot--online"></i></span>
1743
+ <span class="avatar avatar--lg"><span class="avatar__initials">MG</span><i class="avatar__dot avatar__dot--busy"></i></span>
1744
+ <span class="avatar avatar--lg"><span class="avatar__initials">AL</span><i class="avatar__dot avatar__dot--away"></i></span>
1745
+ <span class="avatar avatar--lg"><span class="avatar__initials">RD</span><i class="avatar__dot"></i></span>
1746
+ </div>
1747
+ <details class="ds-code"><summary>Ver código</summary>
1748
+ <div class="ds-code-box"><button class="ds-copy">Copiar</button>
1749
+ <pre class="ds-pre"><code>&lt;span class="avatar avatar--lg"&gt;
1750
+ &lt;span class="avatar__initials"&gt;HS&lt;/span&gt;
1751
+ &lt;i class="avatar__dot avatar__dot--online"&gt;&lt;/i&gt;
1752
+ &lt;/span&gt;
1753
+ &lt;!-- Tamaños: --sm 28 · (md 36) · --lg 48 · --xl 64
1754
+ Presencia: --online · --busy · --away · (offline) --&gt;</code></pre>
1755
+ </div>
1756
+ </details>
1757
+ </div>
1758
+
1759
+ <!-- Divider -->
1760
+ <div class="ds-component" id="comp-divider">
1761
+ <h3>Divider</h3>
1762
+ <p class="ds-spec">Regla hairline 1 px <span class="u-mono">--border</span> · variante con label centrado (patrón "o continúa con") · vertical para flex.</p>
1763
+ <div class="ds-demo ds-demo--col" style="max-width:420px">
1764
+ <hr class="divider">
1765
+ <div class="divider--label" role="separator">o continúa con</div>
1766
+ <div style="display:flex;align-items:center;gap:0">
1767
+ <span>Filtros</span><span class="divider--vertical" role="separator"></span><span>Ordenar</span><span class="divider--vertical" role="separator"></span><span>Exportar</span>
1768
+ </div>
1769
+ </div>
1770
+ <details class="ds-code"><summary>Ver código</summary>
1771
+ <div class="ds-code-box"><button class="ds-copy">Copiar</button>
1772
+ <pre class="ds-pre"><code>&lt;hr class="divider"&gt;
1773
+ &lt;div class="divider--label" role="separator"&gt;o continúa con&lt;/div&gt;
1774
+ &lt;span class="divider--vertical" role="separator"&gt;&lt;/span&gt;</code></pre>
1775
+ </div>
1776
+ </details>
1777
+ </div>
1778
+
1779
+ <!-- Accordion -->
1780
+ <div class="ds-component" id="comp-accordion">
1781
+ <h3>Accordion</h3>
1782
+ <p class="ds-spec">Altura animada con <span class="u-mono">grid-template-rows</span> 0fr→1fr (sin reflow) · chevron rota 180° · <span class="u-mono">aria-expanded</span>.</p>
1783
+ <div class="ds-demo ds-demo--col" style="max-width:560px">
1784
+ <div class="accordion">
1785
+ <div class="accordion__item is-open">
1786
+ <button class="accordion__header js-accordion" aria-expanded="true">¿Puedo cambiar de plan en cualquier momento?<svg><use href="#i-chevron-down"/></svg></button>
1787
+ <div class="accordion__panel"><div><div class="accordion__content">Sí. El cambio se aplica de inmediato y el cobro se prorratea según los días restantes del ciclo.</div></div></div>
1788
+ </div>
1789
+ <div class="accordion__item">
1790
+ <button class="accordion__header js-accordion" aria-expanded="false">¿Cómo exporto mis datos?<svg><use href="#i-chevron-down"/></svg></button>
1791
+ <div class="accordion__panel"><div><div class="accordion__content">Desde Ajustes → Datos puedes descargar todo en CSV o JSON. El archivo llega a tu correo en unos minutos.</div></div></div>
1792
+ </div>
1793
+ <div class="accordion__item">
1794
+ <button class="accordion__header js-accordion" aria-expanded="false">¿Qué pasa al cancelar mi cuenta?<svg><use href="#i-chevron-down"/></svg></button>
1795
+ <div class="accordion__panel"><div><div class="accordion__content">Tu cuenta queda en pausa 30 días por si cambias de opinión. Después, los datos se eliminan de forma permanente.</div></div></div>
1796
+ </div>
1797
+ </div>
1798
+ </div>
1799
+ <details class="ds-code"><summary>Ver código</summary>
1800
+ <div class="ds-code-box"><button class="ds-copy">Copiar</button>
1801
+ <pre class="ds-pre"><code>&lt;div class="accordion"&gt;
1802
+ &lt;div class="accordion__item is-open"&gt;
1803
+ &lt;button class="accordion__header" aria-expanded="true"&gt;
1804
+ Pregunta &lt;svg&gt;…chevron…&lt;/svg&gt;
1805
+ &lt;/button&gt;
1806
+ &lt;div class="accordion__panel"&gt;&lt;div&gt;
1807
+ &lt;div class="accordion__content"&gt;Respuesta&lt;/div&gt;
1808
+ &lt;/div&gt;&lt;/div&gt;
1809
+ &lt;/div&gt;
1810
+ &lt;/div&gt;
1811
+ &lt;!-- JS: alternar .is-open en .accordion__item + aria-expanded --&gt;</code></pre>
1812
+ </div>
1813
+ </details>
1814
+ </div>
1815
+
1816
+ <!-- Tooltip -->
1817
+ <div class="ds-component" id="comp-tooltip">
1818
+ <h3>Tooltip</h3>
1819
+ <p class="ds-spec">Delay 400 ms · fade + translateY(4→0) · máx. 1 línea; si necesita más, usar popover. Pasa el cursor por los botones.</p>
1820
+ <div class="ds-demo">
1821
+ <button class="btn btn--secondary" data-tooltip="Duplica el proyecto">Duplicar</button>
1822
+ <button class="btn btn--secondary" data-tooltip="Exporta a CSV">Exportar</button>
1823
+ <button class="icon-btn icon-btn--secondary" aria-label="Más acciones" data-tooltip="Más acciones"><svg><use href="#i-more-horizontal"/></svg></button>
1824
+ </div>
1825
+ <details class="ds-code"><summary>Ver código</summary>
1826
+ <div class="ds-code-box"><button class="ds-copy">Copiar</button>
1827
+ <pre class="ds-pre"><code>&lt;button class="btn btn--secondary" data-tooltip="Duplica el proyecto"&gt;
1828
+ Duplicar
1829
+ &lt;/button&gt;
1830
+ &lt;!-- Solo CSS: atributo data-tooltip. Fondo se invierte en dark. --&gt;</code></pre>
1831
+ </div>
1832
+ </details>
1833
+ </div>
1834
+
1835
+ <!-- Alert -->
1836
+ <div class="ds-component" id="comp-alert">
1837
+ <h3>Alert</h3>
1838
+ <p class="ds-spec">Banner inline y <strong>persistente</strong> (vs Toast, transitorio). Icono <span class="u-mono">*-500</span> + texto <span class="u-mono">--*-text</span> sobre fondo <span class="u-mono">*-100</span>. <span class="u-mono">role="status"</span>.</p>
1839
+ <div class="ds-demo ds-demo--col" style="max-width:560px">
1840
+ <div class="alert alert--info" role="status">
1841
+ <svg><use href="#i-info"/></svg>
1842
+ <div><p class="alert__title">Nueva versión disponible</p><p class="alert__body">La versión 2.4 incluye exportación a PDF. Actualiza cuando quieras.</p></div>
1843
+ </div>
1844
+ <div class="alert alert--success" role="status">
1845
+ <svg><use href="#i-circle-check"/></svg>
1846
+ <div><p class="alert__title">Pago confirmado</p><p class="alert__body">El recibo se envió a tu correo.</p></div>
1847
+ </div>
1848
+ <div class="alert alert--warning" role="status">
1849
+ <svg><use href="#i-triangle-alert"/></svg>
1850
+ <div><p class="alert__title">Tu plan expira en 5 días</p><p class="alert__body">Renueva antes del 12 de julio para no perder acceso.</p></div>
1851
+ </div>
1852
+ <div class="alert alert--danger" role="status">
1853
+ <svg><use href="#i-circle-alert"/></svg>
1854
+ <div><p class="alert__title">No se pudo guardar el formulario</p><p class="alert__body">Revisa los campos marcados en rojo e inténtalo de nuevo.</p></div>
1855
+ <button class="alert__close js-alert-close" aria-label="Cerrar aviso"><svg><use href="#i-x"/></svg></button>
1856
+ </div>
1857
+ </div>
1858
+ <details class="ds-code"><summary>Ver código</summary>
1859
+ <div class="ds-code-box"><button class="ds-copy">Copiar</button>
1860
+ <pre class="ds-pre"><code>&lt;div class="alert alert--warning" role="status"&gt;
1861
+ &lt;svg&gt;…icono…&lt;/svg&gt;
1862
+ &lt;div&gt;
1863
+ &lt;p class="alert__title"&gt;Tu plan expira en 5 días&lt;/p&gt;
1864
+ &lt;p class="alert__body"&gt;Renueva antes del 12 de julio.&lt;/p&gt;
1865
+ &lt;/div&gt;
1866
+ &lt;button class="alert__close" aria-label="Cerrar aviso"&gt;…x…&lt;/button&gt;
1867
+ &lt;/div&gt;
1868
+ &lt;!-- Tonos: --info · --success · --warning · --danger --&gt;</code></pre>
1869
+ </div>
1870
+ </details>
1871
+ </div>
1872
+
1873
+ <!-- Toast -->
1874
+ <div class="ds-component" id="comp-toast">
1875
+ <h3>Toast</h3>
1876
+ <p class="ds-spec">Top-right (desktop) · 360 px · borde izquierdo 3 px semántico · auto-dismiss 5 s pausable con hover · máx. 3 apilados.</p>
1877
+ <div class="ds-demo">
1878
+ <button class="btn btn--secondary" data-toast="success">Toast de éxito</button>
1879
+ <button class="btn btn--secondary" data-toast="info">Toast informativo</button>
1880
+ <button class="btn btn--secondary" data-toast="warning">Toast de aviso</button>
1881
+ <button class="btn btn--secondary" data-toast="danger">Toast de error</button>
1882
+ </div>
1883
+ <details class="ds-code"><summary>Ver código</summary>
1884
+ <div class="ds-code-box"><button class="ds-copy">Copiar</button>
1885
+ <pre class="ds-pre"><code>&lt;div class="toast-stack" role="status" aria-live="polite"&gt;
1886
+ &lt;div class="toast toast--success"&gt;
1887
+ &lt;svg&gt;…icono…&lt;/svg&gt;
1888
+ &lt;div&gt;
1889
+ &lt;p class="toast__title"&gt;Cambios guardados&lt;/p&gt;
1890
+ &lt;p class="toast__body"&gt;El proyecto se actualizó.&lt;/p&gt;
1891
+ &lt;/div&gt;
1892
+ &lt;button class="icon-btn icon-btn--ghost icon-btn--sm" aria-label="Cerrar"&gt;…x…&lt;/button&gt;
1893
+ &lt;/div&gt;
1894
+ &lt;/div&gt;</code></pre>
1895
+ </div>
1896
+ </details>
1897
+ </div>
1898
+
1899
+ <!-- Progress -->
1900
+ <div class="ds-component" id="comp-progress">
1901
+ <h3>Progress y ProgressRing</h3>
1902
+ <p class="ds-spec">Solo progreso <strong>determinado</strong> — para carga indeterminada usar Skeleton. Width / dashoffset animan 250 ms.</p>
1903
+ <div class="ds-demo ds-demo--col" style="max-width:420px">
1904
+ <div>
1905
+ <div class="progress-row"><span class="progress-label">Almacenamiento</span><span class="progress-value js-progress-value">68%</span></div>
1906
+ <div class="progress"><div class="progress__bar js-progress-bar" style="width:68%" role="progressbar" aria-valuenow="68" aria-valuemin="0" aria-valuemax="100"></div></div>
1907
+ </div>
1908
+ <div>
1909
+ <div class="progress-row"><span class="progress-label">Tareas completadas</span><span class="progress-value">24/30</span></div>
1910
+ <div class="progress"><div class="progress__bar progress__bar--success" style="width:80%" role="progressbar" aria-valuenow="80" aria-valuemin="0" aria-valuemax="100"></div></div>
1911
+ </div>
1912
+ <div style="display:flex;align-items:center;gap:24px">
1913
+ <div class="progress-ring js-ring" data-value="68">
1914
+ <svg width="56" height="56"><circle class="progress-ring__track" cx="28" cy="28" r="24" stroke-width="5"/><circle class="progress-ring__bar" cx="28" cy="28" r="24" stroke-width="5"/></svg>
1915
+ <span class="progress-ring__value" style="font-size:13px">68%</span>
1916
+ </div>
1917
+ <button class="btn btn--ghost js-progress-demo">Simular progreso</button>
1918
+ </div>
1919
+ </div>
1920
+ <details class="ds-code"><summary>Ver código</summary>
1921
+ <div class="ds-code-box"><button class="ds-copy">Copiar</button>
1922
+ <pre class="ds-pre"><code>&lt;div class="progress-row"&gt;
1923
+ &lt;span class="progress-label"&gt;Almacenamiento&lt;/span&gt;
1924
+ &lt;span class="progress-value"&gt;68%&lt;/span&gt;
1925
+ &lt;/div&gt;
1926
+ &lt;div class="progress"&gt;
1927
+ &lt;div class="progress__bar" style="width:68%"
1928
+ role="progressbar" aria-valuenow="68"
1929
+ aria-valuemin="0" aria-valuemax="100"&gt;&lt;/div&gt;
1930
+ &lt;/div&gt;
1931
+ &lt;!-- Tonos de barra: (accent) · --success · --warning · --danger --&gt;</code></pre>
1932
+ </div>
1933
+ </details>
1934
+ </div>
1935
+
1936
+ <!-- Modal -->
1937
+ <div class="ds-component" id="comp-modal">
1938
+ <h3>Modal</h3>
1939
+ <p class="ds-spec">Overlay <span class="u-mono">--overlay</span> · panel 480 px (confirmación) o 640 px (formulario) · fade + scale(0.96→1) · Esc y click en overlay cierran.</p>
1940
+ <div class="ds-demo">
1941
+ <button class="btn btn--secondary" data-modal-open="demoModal">Abrir confirmación</button>
1942
+ <button class="btn btn--secondary" data-modal-open="demoModalLg">Abrir formulario</button>
1943
+ </div>
1944
+ <details class="ds-code"><summary>Ver código</summary>
1945
+ <div class="ds-code-box"><button class="ds-copy">Copiar</button>
1946
+ <pre class="ds-pre"><code>&lt;div class="modal" id="miModal" role="dialog" aria-modal="true" aria-labelledby="miModalTitulo"&gt;
1947
+ &lt;div class="modal__overlay" data-modal-close&gt;&lt;/div&gt;
1948
+ &lt;div class="modal__panel"&gt;
1949
+ &lt;div class="modal__header"&gt;
1950
+ &lt;h4 class="modal__title" id="miModalTitulo"&gt;¿Eliminar proyecto?&lt;/h4&gt;
1951
+ &lt;button class="icon-btn icon-btn--ghost" data-modal-close aria-label="Cerrar"&gt;…x…&lt;/button&gt;
1952
+ &lt;/div&gt;
1953
+ &lt;p&gt;Esta acción no se puede deshacer.&lt;/p&gt;
1954
+ &lt;div class="modal__footer"&gt;
1955
+ &lt;button class="btn btn--secondary" data-modal-close&gt;Cancelar&lt;/button&gt;
1956
+ &lt;button class="btn btn--danger"&gt;Eliminar&lt;/button&gt;
1957
+ &lt;/div&gt;
1958
+ &lt;/div&gt;
1959
+ &lt;/div&gt;
1960
+ &lt;!-- Panel ancho: modal__panel--lg (640px) --&gt;</code></pre>
1961
+ </div>
1962
+ </details>
1963
+ </div>
1964
+
1965
+ <!-- Menu -->
1966
+ <div class="ds-component" id="comp-menu">
1967
+ <h3>Menu</h3>
1968
+ <p class="ds-spec">Panel 190 px mín. con <span class="u-mono">shadow-lg</span> · entrada fade + translateY(-4→0) 160 ms · ítem danger con hover <span class="u-mono">danger-100</span>.</p>
1969
+ <div class="ds-demo" style="padding-bottom:180px">
1970
+ <div class="menu-wrap">
1971
+ <button class="btn btn--secondary js-menu-toggle">Acciones<svg style="width:16px;height:16px"><use href="#i-chevron-down"/></svg></button>
1972
+ <div class="menu" role="menu">
1973
+ <button class="menu__item" role="menuitem"><svg><use href="#i-pencil"/></svg>Editar</button>
1974
+ <button class="menu__item" role="menuitem"><svg><use href="#i-copy"/></svg>Duplicar</button>
1975
+ <button class="menu__item" role="menuitem"><svg><use href="#i-download"/></svg>Exportar</button>
1976
+ <div class="menu__sep" role="separator"></div>
1977
+ <button class="menu__item menu__item--danger" role="menuitem"><svg><use href="#i-trash"/></svg>Eliminar</button>
1978
+ </div>
1979
+ </div>
1980
+ </div>
1981
+ <details class="ds-code"><summary>Ver código</summary>
1982
+ <div class="ds-code-box"><button class="ds-copy">Copiar</button>
1983
+ <pre class="ds-pre"><code>&lt;div class="menu-wrap"&gt;
1984
+ &lt;button class="btn btn--secondary"&gt;Acciones&lt;/button&gt;
1985
+ &lt;div class="menu" role="menu"&gt;
1986
+ &lt;button class="menu__item" role="menuitem"&gt;…Editar&lt;/button&gt;
1987
+ &lt;div class="menu__sep" role="separator"&gt;&lt;/div&gt;
1988
+ &lt;button class="menu__item menu__item--danger" role="menuitem"&gt;…Eliminar&lt;/button&gt;
1989
+ &lt;/div&gt;
1990
+ &lt;/div&gt;
1991
+ &lt;!-- JS: alternar .is-open; cerrar con click fuera, Esc o al seleccionar --&gt;</code></pre>
1992
+ </div>
1993
+ </details>
1994
+ </div>
1995
+
1996
+ <!-- Tabla -->
1997
+ <div class="ds-component" id="comp-tabla">
1998
+ <h3>Tabla</h3>
1999
+ <p class="ds-spec">Header uppercase <span class="u-mono">+0.04em</span> · filas 52 px · numéricos a la derecha con <span class="u-mono">tabular-nums</span> · seleccionada en <span class="u-mono">--accent-soft</span>.</p>
2000
+ <div class="ds-demo ds-demo--col" style="padding:0;overflow-x:auto">
2001
+ <table class="table">
2002
+ <thead><tr><th>Cliente</th><th>Estado</th><th class="num">Facturas</th><th class="num">Monto</th></tr></thead>
2003
+ <tbody>
2004
+ <tr><td>Constructora Delta</td><td><span class="badge badge--success"><svg><use href="#i-circle-check"/></svg>Activo</span></td><td class="num">12</td><td class="num">$ 48,200.00</td></tr>
2005
+ <tr class="is-selected"><td>Grupo Andrade</td><td><span class="badge badge--warning"><svg><use href="#i-triangle-alert"/></svg>Pendiente</span></td><td class="num">3</td><td class="num">$ 9,850.00</td></tr>
2006
+ <tr><td>Studio Nébula</td><td><span class="badge badge--success"><svg><use href="#i-circle-check"/></svg>Activo</span></td><td class="num">27</td><td class="num">$ 132,400.00</td></tr>
2007
+ <tr><td>Farmacias Luna</td><td><span class="badge badge--danger"><svg><use href="#i-circle-alert"/></svg>Vencido</span></td><td class="num">1</td><td class="num">$ 1,120.00</td></tr>
2008
+ </tbody>
2009
+ </table>
2010
+ </div>
2011
+ <details class="ds-code"><summary>Ver código</summary>
2012
+ <div class="ds-code-box"><button class="ds-copy">Copiar</button>
2013
+ <pre class="ds-pre"><code>&lt;table class="table"&gt;
2014
+ &lt;thead&gt;&lt;tr&gt;&lt;th&gt;Cliente&lt;/th&gt;&lt;th class="num"&gt;Monto&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;
2015
+ &lt;tbody&gt;
2016
+ &lt;tr&gt;&lt;td&gt;Constructora Delta&lt;/td&gt;&lt;td class="num"&gt;$ 48,200.00&lt;/td&gt;&lt;/tr&gt;
2017
+ &lt;tr class="is-selected"&gt;&lt;td&gt;Grupo Andrade&lt;/td&gt;&lt;td class="num"&gt;$ 9,850.00&lt;/td&gt;&lt;/tr&gt;
2018
+ &lt;/tbody&gt;
2019
+ &lt;/table&gt;</code></pre>
2020
+ </div>
2021
+ </details>
2022
+ </div>
2023
+
2024
+ <!-- Stat -->
2025
+ <div class="ds-component" id="comp-stat">
2026
+ <h3>Stat</h3>
2027
+ <p class="ds-spec">KPI único, componer dentro de Card. Valor en mono <span class="u-mono">text-3xl</span> · delta con flecha en tonos <span class="u-mono">--*-text</span> · count-up al entrar en viewport.</p>
2028
+ <div class="grid">
2029
+ <div class="card col-12 col-md-4">
2030
+ <div class="stat">
2031
+ <span class="stat__label"><svg><use href="#i-chart"/></svg>Ingresos del mes</span>
2032
+ <div class="stat__row">
2033
+ <span class="stat__value js-countup" data-target="128400" data-prefix="$ ">$ 0</span>
2034
+ <span class="stat__delta stat__delta--up"><svg><use href="#i-trending-up"/></svg>+12.4%</span>
2035
+ </div>
2036
+ </div>
2037
+ </div>
2038
+ <div class="card col-12 col-md-4">
2039
+ <div class="stat">
2040
+ <span class="stat__label"><svg><use href="#i-user"/></svg>Clientes activos</span>
2041
+ <div class="stat__row">
2042
+ <span class="stat__value js-countup" data-target="1284">0</span>
2043
+ <span class="stat__delta stat__delta--down"><svg><use href="#i-trending-down"/></svg>-2.1%</span>
2044
+ </div>
2045
+ </div>
2046
+ </div>
2047
+ <div class="card col-12 col-md-4">
2048
+ <div class="stat">
2049
+ <span class="stat__label"><svg><use href="#i-folder"/></svg>Proyectos abiertos</span>
2050
+ <div class="stat__row">
2051
+ <span class="stat__value js-countup" data-target="37">0</span>
2052
+ <span class="stat__delta"><svg><use href="#i-minus"/></svg>0%</span>
2053
+ </div>
2054
+ </div>
2055
+ </div>
2056
+ </div>
2057
+ <details class="ds-code"><summary>Ver código</summary>
2058
+ <div class="ds-code-box"><button class="ds-copy">Copiar</button>
2059
+ <pre class="ds-pre"><code>&lt;div class="card"&gt;
2060
+ &lt;div class="stat"&gt;
2061
+ &lt;span class="stat__label"&gt;&lt;svg&gt;…&lt;/svg&gt;Ingresos del mes&lt;/span&gt;
2062
+ &lt;div class="stat__row"&gt;
2063
+ &lt;span class="stat__value"&gt;$ 128,400&lt;/span&gt;
2064
+ &lt;span class="stat__delta stat__delta--up"&gt;&lt;svg&gt;…&lt;/svg&gt;+12.4%&lt;/span&gt;
2065
+ &lt;/div&gt;
2066
+ &lt;/div&gt;
2067
+ &lt;/div&gt;
2068
+ &lt;!-- Delta: --up (success-text) · --down (danger-text) · neutro (text-muted) --&gt;</code></pre>
2069
+ </div>
2070
+ </details>
2071
+ </div>
2072
+
2073
+ <!-- Pagination -->
2074
+ <div class="ds-component" id="comp-pagination">
2075
+ <h3>Pagination</h3>
2076
+ <p class="ds-spec">1-indexado, bajo una tabla · trunca con elipsis a partir de 7 páginas · página activa como chip sólido con <span class="u-mono">aria-current="page"</span>.</p>
2077
+ <div class="ds-demo">
2078
+ <ul class="pagination js-pagination" aria-label="Paginación"></ul>
2079
+ </div>
2080
+ <details class="ds-code"><summary>Ver código</summary>
2081
+ <div class="ds-code-box"><button class="ds-copy">Copiar</button>
2082
+ <pre class="ds-pre"><code>&lt;ul class="pagination" aria-label="Paginación"&gt;
2083
+ &lt;li&gt;&lt;button class="pagination__btn" aria-label="Anterior"&gt;…‹…&lt;/button&gt;&lt;/li&gt;
2084
+ &lt;li&gt;&lt;button class="pagination__btn"&gt;1&lt;/button&gt;&lt;/li&gt;
2085
+ &lt;li&gt;&lt;span class="pagination__ellipsis"&gt;…&lt;/span&gt;&lt;/li&gt;
2086
+ &lt;li&gt;&lt;button class="pagination__btn is-active" aria-current="page"&gt;5&lt;/button&gt;&lt;/li&gt;
2087
+ &lt;li&gt;&lt;button class="pagination__btn"&gt;6&lt;/button&gt;&lt;/li&gt;
2088
+ &lt;li&gt;&lt;button class="pagination__btn" aria-label="Siguiente"&gt;…›…&lt;/button&gt;&lt;/li&gt;
2089
+ &lt;/ul&gt;</code></pre>
2090
+ </div>
2091
+ </details>
2092
+ </div>
2093
+
2094
+ <!-- Navegación -->
2095
+ <div class="ds-component" id="comp-navegacion">
2096
+ <h3>Navegación</h3>
2097
+ <p class="ds-spec">Navbar 64 px · sidebar 260 px · tabs con indicador de 2 px · breadcrumbs con separador <span class="u-mono">/</span> tipográfico.</p>
2098
+ <div class="ds-demo ds-demo--col" style="padding:0;overflow-x:auto">
2099
+ <nav class="navbar">
2100
+ <a class="navbar__brand" href="#comp-navegacion">Higinio</a>
2101
+ <div class="navbar__nav">
2102
+ <a class="navbar__link is-active" href="#comp-navegacion">Resumen</a>
2103
+ <a class="navbar__link" href="#comp-navegacion">Proyectos</a>
2104
+ <a class="navbar__link" href="#comp-navegacion">Informes</a>
2105
+ <a class="navbar__link" href="#comp-navegacion">Ajustes</a>
2106
+ </div>
2107
+ </nav>
2108
+ </div>
2109
+ <div class="ds-demo" style="align-items:stretch">
2110
+ <nav class="sidebar" style="border:1px solid var(--border);border-radius:var(--radius-lg)">
2111
+ <button class="sidebar__item is-active"><svg><use href="#i-home"/></svg>Inicio</button>
2112
+ <button class="sidebar__item"><svg><use href="#i-folder"/></svg>Proyectos</button>
2113
+ <button class="sidebar__item"><svg><use href="#i-chart"/></svg>Informes</button>
2114
+ <button class="sidebar__item"><svg><use href="#i-user"/></svg>Equipo</button>
2115
+ </nav>
2116
+ <div style="flex:1;min-width:260px;display:flex;flex-direction:column;gap:20px">
2117
+ <nav class="breadcrumbs" aria-label="Ruta">
2118
+ <a href="#comp-navegacion">Inicio</a><span class="breadcrumbs__sep">/</span>
2119
+ <a href="#comp-navegacion">Proyectos</a><span class="breadcrumbs__sep">/</span>
2120
+ <span class="breadcrumbs__current">Panel Higinio</span>
2121
+ </nav>
2122
+ <div>
2123
+ <div class="tabs" role="tablist">
2124
+ <button class="tabs__tab is-active js-tab" role="tab" data-tab="t1" aria-selected="true">Resumen</button>
2125
+ <button class="tabs__tab js-tab" role="tab" data-tab="t2" aria-selected="false">Actividad</button>
2126
+ <button class="tabs__tab js-tab" role="tab" data-tab="t3" aria-selected="false">Ajustes</button>
2127
+ </div>
2128
+ <div class="ds-tabpanel" id="tab-t1" style="padding:16px 4px;font-size:var(--text-sm);color:var(--text-secondary)">Vista general del proyecto con sus métricas clave.</div>
2129
+ <div class="ds-tabpanel" id="tab-t2" hidden style="padding:16px 4px;font-size:var(--text-sm);color:var(--text-secondary)">Historial de cambios y comentarios del equipo.</div>
2130
+ <div class="ds-tabpanel" id="tab-t3" hidden style="padding:16px 4px;font-size:var(--text-sm);color:var(--text-secondary)">Preferencias, permisos e integraciones.</div>
2131
+ </div>
2132
+ </div>
2133
+ </div>
2134
+ <details class="ds-code"><summary>Ver código</summary>
2135
+ <div class="ds-code-box"><button class="ds-copy">Copiar</button>
2136
+ <pre class="ds-pre"><code>&lt;nav class="navbar"&gt;
2137
+ &lt;a class="navbar__brand" href="/"&gt;Higinio&lt;/a&gt;
2138
+ &lt;div class="navbar__nav"&gt;
2139
+ &lt;a class="navbar__link is-active" href="#"&gt;Resumen&lt;/a&gt;
2140
+ &lt;a class="navbar__link" href="#"&gt;Proyectos&lt;/a&gt;
2141
+ &lt;/div&gt;
2142
+ &lt;/nav&gt;
2143
+
2144
+ &lt;nav class="sidebar"&gt;
2145
+ &lt;button class="sidebar__item is-active"&gt;&lt;svg&gt;…&lt;/svg&gt;Inicio&lt;/button&gt;
2146
+ &lt;/nav&gt;
2147
+
2148
+ &lt;div class="tabs" role="tablist"&gt;
2149
+ &lt;button class="tabs__tab is-active" role="tab"&gt;Resumen&lt;/button&gt;
2150
+ &lt;/div&gt;
2151
+
2152
+ &lt;nav class="breadcrumbs"&gt;
2153
+ &lt;a href="#"&gt;Inicio&lt;/a&gt;&lt;span class="breadcrumbs__sep"&gt;/&lt;/span&gt;
2154
+ &lt;span class="breadcrumbs__current"&gt;Panel&lt;/span&gt;
2155
+ &lt;/nav&gt;</code></pre>
2156
+ </div>
2157
+ </details>
2158
+ </div>
2159
+
2160
+ <!-- Skeleton y EmptyState -->
2161
+ <div class="ds-component" id="comp-skeleton">
2162
+ <h3>Skeleton y EmptyState</h3>
2163
+ <p class="ds-spec">Shimmer 1.4 s en loop, radius igual al contenido que reemplaza. Un estado vacío siempre invita a actuar.</p>
2164
+ <div class="grid">
2165
+ <div class="card col-12 col-md-6">
2166
+ <div style="display:flex;gap:12px;align-items:center;margin-bottom:16px">
2167
+ <div class="skeleton skeleton--circle" style="width:40px;height:40px"></div>
2168
+ <div style="flex:1;display:flex;flex-direction:column;gap:8px">
2169
+ <div class="skeleton" style="height:12px;width:60%"></div>
2170
+ <div class="skeleton" style="height:10px;width:40%"></div>
2171
+ </div>
2172
+ </div>
2173
+ <div class="skeleton" style="height:10px;margin-bottom:8px"></div>
2174
+ <div class="skeleton" style="height:10px;width:85%;margin-bottom:8px"></div>
2175
+ <div class="skeleton" style="height:10px;width:70%"></div>
2176
+ </div>
2177
+ <div class="card col-12 col-md-6">
2178
+ <div class="empty-state">
2179
+ <svg><use href="#i-inbox"/></svg>
2180
+ <p class="empty-state__title">Aún no tienes proyectos</p>
2181
+ <p class="empty-state__desc">Crea tu primer proyecto para empezar a organizar tu trabajo.</p>
2182
+ <button class="btn btn--primary"><svg><use href="#i-plus"/></svg>Crear proyecto</button>
2183
+ </div>
2184
+ </div>
2185
+ </div>
2186
+ <details class="ds-code"><summary>Ver código</summary>
2187
+ <div class="ds-code-box"><button class="ds-copy">Copiar</button>
2188
+ <pre class="ds-pre"><code>&lt;div class="skeleton" style="height:10px;width:85%"&gt;&lt;/div&gt;
2189
+ &lt;div class="skeleton skeleton--circle" style="width:40px;height:40px"&gt;&lt;/div&gt;
2190
+
2191
+ &lt;div class="empty-state"&gt;
2192
+ &lt;svg&gt;…icono 40px…&lt;/svg&gt;
2193
+ &lt;p class="empty-state__title"&gt;Aún no tienes proyectos&lt;/p&gt;
2194
+ &lt;p class="empty-state__desc"&gt;Crea tu primer proyecto para empezar.&lt;/p&gt;
2195
+ &lt;button class="btn btn--primary"&gt;Crear proyecto&lt;/button&gt;
2196
+ &lt;/div&gt;</code></pre>
2197
+ </div>
2198
+ </details>
2199
+ </div>
2200
+
2201
+ <!-- Card, Link y Code -->
2202
+ <div class="ds-component" id="comp-card">
2203
+ <h3>Card, Link y Code</h3>
2204
+ <p class="ds-spec">Tarjeta con padding 24 px y <span class="u-mono">shadow-sm</span>; la interactiva sube 2 px al hover. Link con subrayado que crece. Code inline en mono.</p>
2205
+ <div class="grid">
2206
+ <div class="card col-12 col-md-6">
2207
+ <h4>Tarjeta estática</h4>
2208
+ <p style="font-size:var(--text-sm);color:var(--text-secondary);margin:0">Contenido en reposo con borde <span class="code">--border</span> y <span class="code">shadow-sm</span>.</p>
2209
+ </div>
2210
+ <div class="card card--interactive col-12 col-md-6" tabindex="0">
2211
+ <h4>Tarjeta interactiva</h4>
2212
+ <p style="font-size:var(--text-sm);color:var(--text-secondary);margin:0">Pasa el cursor: sube 2&nbsp;px con <span class="code">shadow-md</span>. Consulta la <a class="link" href="#motion">guía de motion</a>.</p>
2213
+ </div>
2214
+ </div>
2215
+ <details class="ds-code"><summary>Ver código</summary>
2216
+ <div class="ds-code-box"><button class="ds-copy">Copiar</button>
2217
+ <pre class="ds-pre"><code>&lt;div class="card"&gt;…&lt;/div&gt;
2218
+ &lt;div class="card card--interactive"&gt;…&lt;/div&gt;
2219
+
2220
+ &lt;a class="link" href="#"&gt;Enlace con subrayado animado&lt;/a&gt;
2221
+ &lt;span class="code"&gt;--accent&lt;/span&gt;</code></pre>
2222
+ </div>
2223
+ </details>
2224
+ </div>
2225
+ </section>
2226
+
2227
+ <!-- ============================== MOTION ============================== -->
2228
+ <section id="motion">
2229
+ <p class="ds-eyebrow">Sistema</p>
2230
+ <h2>Motion</h2>
2231
+ <p class="ds-lead">Solo <span class="u-mono">transform</span> y <span class="u-mono">opacity</span>. La salida siempre ~20 % más corta que la entrada. Distancias de 4–8 px: nada "vuela" por la pantalla.</p>
2232
+ <div class="ds-demo ds-demo--col" style="padding:16px">
2233
+ <table class="table">
2234
+ <thead><tr><th>Token</th><th class="num">Valor</th><th>Uso</th></tr></thead>
2235
+ <tbody>
2236
+ <tr><td class="u-mono">--duration-instant</td><td class="num">100ms</td><td>Press, toggle</td></tr>
2237
+ <tr><td class="u-mono">--duration-fast</td><td class="num">150ms</td><td>Hover, focus, color</td></tr>
2238
+ <tr><td class="u-mono">--duration-base</td><td class="num">200ms</td><td>Dropdowns, tooltips, switches</td></tr>
2239
+ <tr><td class="u-mono">--duration-slow</td><td class="num">250ms</td><td>Modales, toasts</td></tr>
2240
+ <tr><td class="u-mono">--duration-page</td><td class="num">320ms</td><td>Transiciones de página</td></tr>
2241
+ </tbody>
2242
+ </table>
2243
+ </div>
2244
+ <h3>Easings <span style="font-size:var(--text-sm);color:var(--text-muted);font-weight:400">(pasa el cursor)</span></h3>
2245
+ <div class="ds-demo ds-demo--col">
2246
+ <span class="ds-ease-chip ds-ease-chip--out">--ease-out · entradas</span>
2247
+ <span class="ds-ease-chip ds-ease-chip--in">--ease-in · salidas</span>
2248
+ <span class="ds-ease-chip ds-ease-chip--inout">--ease-in-out · moverse de A a B</span>
2249
+ <span class="ds-ease-chip ds-ease-chip--spring">--ease-spring · toggles y checks</span>
2250
+ </div>
2251
+ <ul style="max-width:var(--measure)">
2252
+ <li>Stagger en listas: 30 ms por ítem, máximo 6 ítems.</li>
2253
+ <li>Si la carga dura &lt;300 ms, no mostrar spinner (evita parpadeo).</li>
2254
+ <li><span class="u-mono">prefers-reduced-motion</span> colapsa toda animación a 0.01 ms.</li>
2255
+ </ul>
2256
+ </section>
2257
+
2258
+ <!-- ============================== ACCESIBILIDAD ============================== -->
2259
+ <section id="accesibilidad">
2260
+ <p class="ds-eyebrow">Sistema</p>
2261
+ <h2>Accesibilidad</h2>
2262
+ <p class="ds-lead">WCAG AA mínimo: 4.5:1 texto normal, 3:1 texto grande y componentes UI. Touch targets de 44×44 px. Estado nunca comunicado solo con color.</p>
2263
+ <div class="ds-demo ds-demo--col" style="padding:16px">
2264
+ <table class="table">
2265
+ <thead><tr><th>Combinación</th><th class="num">Contraste</th><th>Veredicto</th></tr></thead>
2266
+ <tbody>
2267
+ <tr><td><span class="u-mono">neutral-800</span> sobre blanco</td><td class="num">≈ 10.4:1</td><td><span class="badge badge--success"><svg><use href="#i-circle-check"/></svg>AAA</span></td></tr>
2268
+ <tr><td>Blanco sobre <span class="u-mono">primary-500</span></td><td class="num">≈ 3.4:1</td><td><span class="badge badge--warning"><svg><use href="#i-triangle-alert"/></svg>Solo texto ≥18px o UI</span></td></tr>
2269
+ <tr><td><span class="u-mono">--link</span> (primary-700) sobre blanco</td><td class="num">≈ 6:1</td><td><span class="badge badge--success"><svg><use href="#i-circle-check"/></svg>AA texto normal</span></td></tr>
2270
+ <tr><td><span class="u-mono">primary-300</span> sobre blanco</td><td class="num">≈ 1.8:1</td><td><span class="badge badge--danger"><svg><use href="#i-circle-alert"/></svg>Nunca como texto</span></td></tr>
2271
+ <tr><td>Tonos <span class="u-mono">--*-text</span> sobre <span class="u-mono">*-100</span></td><td class="num">≥ 4.5:1</td><td><span class="badge badge--success"><svg><use href="#i-circle-check"/></svg>AA garantizado</span></td></tr>
2272
+ </tbody>
2273
+ </table>
2274
+ </div>
2275
+ <ul style="max-width:var(--measure)">
2276
+ <li>Focus visible siempre: ring global de teclado vía <span class="u-mono">:focus-visible</span> con especificidad cero (<span class="u-mono">:where()</span>).</li>
2277
+ <li>IconButton siempre con <span class="u-mono">aria-label</span> · Progress con <span class="u-mono">role="progressbar"</span> · Alert con <span class="u-mono">role="status"</span> · Pagination con <span class="u-mono">aria-current="page"</span>.</li>
2278
+ <li>Labels de formulario siempre visibles — el placeholder nunca sustituye al label.</li>
2279
+ </ul>
2280
+ </section>
2281
+
2282
+ <!-- ============================== TOKENS Y DESCARGA ============================== -->
2283
+ <section id="tokens">
2284
+ <p class="ds-eyebrow">Sistema</p>
2285
+ <h2>Tokens y descarga</h2>
2286
+ <p class="ds-lead">El sistema completo vive en <span class="u-mono">dist/celeste-clasica/</span>: <span class="u-mono">celeste-clasica.css</span> (framework completo), <span class="u-mono">tokens.css</span> (solo variables), <span class="u-mono">tailwind.css</span> (@theme para Tailwind v4) y <span class="u-mono">tokens.json</span> (W3C DTCG 2025.10, interoperable con Figma / Tokens Studio / Style Dictionary).</p>
2287
+
2288
+ <h3>Variables CSS</h3>
2289
+ <div class="ds-code-box"><button class="ds-copy">Copiar</button>
2290
+ <pre class="ds-pre"><code>:root {
2291
+ /* Primario */
2292
+ --primary-50: #f2f6fa; --primary-100: #dfe9f2; --primary-200: #c3d5e6;
2293
+ --primary-300: #a0c0d4; --primary-400: #87a8c7; --primary-500: #7192ba;
2294
+ --primary-600: #5d7ba3; --primary-700: #4d6588; --primary-800: #40536e;
2295
+ --primary-900: #31343e;
2296
+
2297
+ /* Neutrales */
2298
+ --neutral-0: #ffffff; --neutral-50: #f7f8fa; --neutral-100: #eef0f3;
2299
+ --neutral-200: #dcdfe5; --neutral-300: #b8bdc7; --neutral-400: #8a8f9c;
2300
+ --neutral-500: #666b78; --neutral-600: #4d5160; --neutral-700: #3d414e;
2301
+ --neutral-800: #31343e; --neutral-900: #22242b;
2302
+
2303
+ /* Semánticos (ramp de 3) */
2304
+ --success-500: #3d9970; --success-100: #e2f3ec; --success-text: #2b6e50;
2305
+ --warning-500: #c9922e; --warning-100: #faf1de; --warning-text: #8a651c;
2306
+ --danger-500: #c04d4d; --danger-100: #f9e7e7; --danger-text: #8f3232;
2307
+ --info-500: #7192ba; --info-100: #dfe9f2; --info-text: #4d6588;
2308
+
2309
+ /* Aliases (los componentes consumen SOLO esto) */
2310
+ --bg: var(--neutral-50); --surface: var(--neutral-0); --surface-alt: var(--neutral-100);
2311
+ --border: var(--neutral-200); --border-strong: var(--neutral-300);
2312
+ --text-primary: var(--neutral-800); --text-secondary: var(--neutral-600);
2313
+ --text-muted: var(--neutral-500); --text-disabled: var(--neutral-400);
2314
+ --text-on-primary: #ffffff; --text-on-primary-solid: var(--primary-700);
2315
+ --accent: var(--primary-500); --accent-hover: var(--primary-600);
2316
+ --accent-active: var(--primary-700); --accent-soft: var(--primary-50);
2317
+ --accent-soft-strong: var(--primary-100); --link: var(--primary-700);
2318
+
2319
+ /* Tipografía */
2320
+ --font-display: "Fraunces", Georgia, serif;
2321
+ --font-body: "Public Sans", system-ui, -apple-system, sans-serif;
2322
+ --font-mono: "IBM Plex Mono", ui-monospace, monospace;
2323
+ --text-xs: .75rem; --text-sm: .875rem; --text-base: 1rem; --text-lg: 1.125rem;
2324
+ --text-xl: 1.25rem; --text-2xl: 1.5625rem; --text-3xl: 1.9375rem;
2325
+ --text-4xl: 2.4375rem; --text-5xl: 3.0625rem;
2326
+ --tracking-display: -0.02em; --tracking-label: 0.04em; --measure: 65ch;
2327
+
2328
+ /* Espaciado (base 4px) */
2329
+ --space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px;
2330
+ --space-5: 20px; --space-6: 24px; --space-8: 32px; --space-10: 40px;
2331
+ --space-12: 48px; --space-16: 64px; --space-20: 80px; --space-24: 96px;
2332
+ --control-height-sm: 36px; --control-height-md: 44px; --control-height-lg: 52px;
2333
+
2334
+ /* Radios y sombras */
2335
+ --radius-sm: 6px; --radius-md: 10px; --radius-lg: 14px;
2336
+ --radius-xl: 20px; --radius-full: 9999px;
2337
+ --shadow-xs: 0 1px 2px rgba(49,52,62,.06);
2338
+ --shadow-sm: 0 1px 3px rgba(49,52,62,.08), 0 1px 2px rgba(49,52,62,.05);
2339
+ --shadow-md: 0 4px 8px rgba(49,52,62,.09), 0 2px 4px rgba(49,52,62,.05);
2340
+ --shadow-lg: 0 12px 24px rgba(49,52,62,.12), 0 4px 8px rgba(49,52,62,.06);
2341
+ --shadow-xl: 0 24px 48px rgba(49,52,62,.16);
2342
+ --ring: 0 0 0 3px rgba(113,146,186,.25);
2343
+
2344
+ /* Motion */
2345
+ --duration-instant: 100ms; --duration-fast: 150ms; --duration-base: 200ms;
2346
+ --duration-slow: 250ms; --duration-page: 320ms;
2347
+ --ease-out: cubic-bezier(0.2, 0, 0, 1);
2348
+ --ease-in: cubic-bezier(0.4, 0, 1, 1);
2349
+ --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
2350
+ --ease-spring: cubic-bezier(0.34, 1.4, 0.64, 1);
2351
+ }</code></pre>
2352
+ </div>
2353
+
2354
+ <h3 style="margin-top:32px">Tailwind CSS v4 — bloque <span class="u-mono">@theme</span></h3>
2355
+ <div class="ds-code-box"><button class="ds-copy">Copiar</button>
2356
+ <pre class="ds-pre"><code>@import "tailwindcss";
2357
+
2358
+ @theme {
2359
+ /* Colores → genera bg-primary-500, text-neutral-800, border-primary-200… */
2360
+ --color-primary-50: #f2f6fa; --color-primary-100: #dfe9f2;
2361
+ --color-primary-200: #c3d5e6; --color-primary-300: #a0c0d4;
2362
+ --color-primary-400: #87a8c7; --color-primary-500: #7192ba;
2363
+ --color-primary-600: #5d7ba3; --color-primary-700: #4d6588;
2364
+ --color-primary-800: #40536e; --color-primary-900: #31343e;
2365
+
2366
+ --color-neutral-50: #f7f8fa; --color-neutral-100: #eef0f3;
2367
+ --color-neutral-200: #dcdfe5; --color-neutral-300: #b8bdc7;
2368
+ --color-neutral-400: #8a8f9c; --color-neutral-500: #666b78;
2369
+ --color-neutral-600: #4d5160; --color-neutral-700: #3d414e;
2370
+ --color-neutral-800: #31343e; --color-neutral-900: #22242b;
2371
+
2372
+ --color-success: #3d9970; --color-success-soft: #e2f3ec; --color-success-strong: #2b6e50;
2373
+ --color-warning: #c9922e; --color-warning-soft: #faf1de; --color-warning-strong: #8a651c;
2374
+ --color-danger: #c04d4d; --color-danger-soft: #f9e7e7; --color-danger-strong: #8f3232;
2375
+ --color-info: #7192ba; --color-info-soft: #dfe9f2; --color-info-strong: #4d6588;
2376
+
2377
+ /* Tipografía → font-display, font-body, font-mono */
2378
+ --font-display: "Fraunces", Georgia, serif;
2379
+ --font-body: "Public Sans", system-ui, sans-serif;
2380
+ --font-mono: "IBM Plex Mono", ui-monospace, monospace;
2381
+
2382
+ /* Radios → rounded-sm/md/lg/xl */
2383
+ --radius-sm: 6px; --radius-md: 10px; --radius-lg: 14px; --radius-xl: 20px;
2384
+
2385
+ /* Sombras → shadow-xs…shadow-xl (teñidas de tinta) */
2386
+ --shadow-xs: 0 1px 2px rgba(49,52,62,.06);
2387
+ --shadow-sm: 0 1px 3px rgba(49,52,62,.08), 0 1px 2px rgba(49,52,62,.05);
2388
+ --shadow-md: 0 4px 8px rgba(49,52,62,.09), 0 2px 4px rgba(49,52,62,.05);
2389
+ --shadow-lg: 0 12px 24px rgba(49,52,62,.12), 0 4px 8px rgba(49,52,62,.06);
2390
+ --shadow-xl: 0 24px 48px rgba(49,52,62,.16);
2391
+
2392
+ /* Easings → ease-out-cc, ease-in-cc, ease-in-out-cc, ease-spring */
2393
+ --ease-out-cc: cubic-bezier(0.2, 0, 0, 1);
2394
+ --ease-in-cc: cubic-bezier(0.4, 0, 1, 1);
2395
+ --ease-in-out-cc: cubic-bezier(0.65, 0, 0.35, 1);
2396
+ --ease-spring: cubic-bezier(0.34, 1.4, 0.64, 1);
2397
+ }</code></pre>
2398
+ </div>
2399
+
2400
+ <div class="alert alert--info" style="margin-top:24px">
2401
+ <svg><use href="#i-info"/></svg>
2402
+ <div>
2403
+ <p class="alert__title">Cómo consumirlo en un proyecto nuevo</p>
2404
+ <p class="alert__body">1) Copia <span class="u-mono">dist/celeste-clasica/celeste-clasica.css</span> (o solo <span class="u-mono">tokens.css</span>) e impórtalo antes de tus estilos, o pega el bloque <span class="u-mono">@theme</span> en tu CSS de Tailwind v4. 2) Aplica <span class="u-mono">.celeste-reset</span> al body. 3) Consume solo aliases semánticos. 4) Alterna <span class="u-mono">data-theme</span> en <span class="u-mono">&lt;html&gt;</span> para light/dark.</p>
2405
+ </div>
2406
+ </div>
2407
+ </section>
2408
+
2409
+ <!-- ============================== INSTALACIÓN ============================== -->
2410
+ <section id="instalacion">
2411
+ <p class="ds-eyebrow">Sistema</p>
2412
+ <h2>Instalación</h2>
2413
+ <p class="ds-lead">El sistema se distribuye como archivos en <span class="u-mono">dist/celeste-clasica/</span> — cópialos a tu proyecto (aún no hay paquete npm). Elige la vía según tu stack. En todas: <strong>light por defecto</strong>; <span class="u-mono">&lt;html data-theme="dark"&gt;</span> activa el modo oscuro, y los componentes consumen <strong>solo aliases semánticos</strong>, nunca hex.</p>
2414
+
2415
+ <h3>A · CSS drop-in (framework completo)</h3>
2416
+ <p>Para cualquier proyecto — HTML plano o cualquier framework. Trae variables + clases de componentes listas (tipo Bootstrap) y ya importa las fuentes. Aplica <span class="u-mono">.celeste-reset</span> al <span class="u-mono">&lt;body&gt;</span>.</p>
2417
+ <div class="ds-code-box"><button class="ds-copy">Copiar</button>
2418
+ <pre class="ds-pre"><code>&lt;link rel="stylesheet" href="/estilos/celeste-clasica.css"&gt;
2419
+
2420
+ &lt;body class="celeste-reset"&gt;
2421
+ &lt;button class="btn btn--primary"&gt;Guardar&lt;/button&gt;
2422
+ &lt;div class="card"&gt;…&lt;/div&gt;
2423
+ &lt;/body&gt;</code></pre>
2424
+ </div>
2425
+
2426
+ <h3 style="margin-top:32px">B · Solo tokens</h3>
2427
+ <p>Si diseñas tus propios componentes: solo las variables, sin clases.</p>
2428
+ <div class="ds-code-box"><button class="ds-copy">Copiar</button>
2429
+ <pre class="ds-pre"><code>@import "./tokens.css";
2430
+
2431
+ .mi-panel {
2432
+ background: var(--surface);
2433
+ color: var(--text-primary);
2434
+ border: 1px solid var(--border);
2435
+ }</code></pre>
2436
+ </div>
2437
+
2438
+ <h3 style="margin-top:32px">C · Tailwind v4</h3>
2439
+ <p>Tailwind v4 se configura por CSS. El <span class="u-mono">tailwind.css</span> ya incluye <span class="u-mono">@import "tailwindcss"</span>, los tokens y el bloque <span class="u-mono">@theme</span> — úsalo como tu hoja de entrada (mantén <span class="u-mono">tokens.css</span> a su lado, que es lo que importa).</p>
2440
+ <div class="ds-code-box"><button class="ds-copy">Copiar</button>
2441
+ <pre class="ds-pre"><code># 1) instala Tailwind v4
2442
+ npm i tailwindcss @tailwindcss/postcss
2443
+
2444
+ # 2) copia tailwind.css + tokens.css a tu carpeta de CSS
2445
+ # y usa tailwind.css como entrada. Ya tienes utilidades:
2446
+ &lt;div class="bg-primary-500 text-neutral-0"&gt;…&lt;/div&gt;</code></pre>
2447
+ </div>
2448
+
2449
+ <h3 style="margin-top:32px">D · Pipelines de tokens</h3>
2450
+ <p>Para Figma, Tokens Studio o Style Dictionary: importa <span class="u-mono">tokens.json</span> (formato estándar W3C DTCG 2025.10).</p>
2451
+
2452
+ <div class="alert alert--info" style="margin-top:24px">
2453
+ <svg><use href="#i-info"/></svg>
2454
+ <div>
2455
+ <p class="alert__title">Con Claude no copias nada</p>
2456
+ <p class="alert__body">Invoca la skill <span class="u-mono">celeste-clasica-design</span> o conecta el MCP (sección siguiente) y construye consumiendo tokens, componentes y reglas del sistema, con validación automática.</p>
2457
+ </div>
2458
+ </div>
2459
+ </section>
2460
+
2461
+ <!-- ============================== MCP ============================== -->
2462
+ <section id="mcp">
2463
+ <p class="ds-eyebrow">Sistema</p>
2464
+ <h2>MCP</h2>
2465
+ <p class="ds-lead">El paquete <span class="code">@higinio/design-mcp</span> expone ambos sistemas de diseño — Celeste Clásica y Nocturna Eléctrica — a cualquier IDE o cliente compatible con Model Context Protocol: tokens, componentes, reglas duras y validación de código. Complementa a las skills de Claude, no las reemplaza: misma fuente de verdad, accesible también fuera del entorno de Claude.</p>
2466
+
2467
+ <h3>Las 8 tools</h3>
2468
+ <div class="ds-demo ds-demo--col" style="padding:0;overflow-x:auto">
2469
+ <table class="table">
2470
+ <thead><tr><th>Tool</th><th>Qué hace</th></tr></thead>
2471
+ <tbody>
2472
+ <tr><td class="u-mono">list_design_systems</td><td>Lista los sistemas de diseño disponibles.</td></tr>
2473
+ <tr><td class="u-mono">get_token</td><td>Devuelve el valor resuelto de un token o alias de un sistema.</td></tr>
2474
+ <tr><td class="u-mono">search_tokens</td><td>Busca tokens por nombre, categoría o valor.</td></tr>
2475
+ <tr><td class="u-mono">list_components</td><td>Lista los componentes documentados de un sistema.</td></tr>
2476
+ <tr><td class="u-mono">get_component_spec</td><td>Devuelve la especificación completa de un componente: estados, medidas, variantes.</td></tr>
2477
+ <tr><td class="u-mono">get_rules</td><td>Devuelve las reglas duras (no negociables) del sistema.</td></tr>
2478
+ <tr><td class="u-mono">validate_code</td><td>Audita CSS/HTML contra las reglas del sistema — hex directos, contraste, aliases.</td></tr>
2479
+ <tr><td class="u-mono">generate_snippet</td><td>Genera un snippet de componente listo para usar, con los aliases correctos.</td></tr>
2480
+ </tbody>
2481
+ </table>
2482
+ </div>
2483
+
2484
+ <h3 style="margin-top:32px">Conectar (remoto)</h3>
2485
+ <p>Añade el servidor a tu <span class="code">.mcp.json</span>:</p>
2486
+ <div class="ds-code-box"><button class="ds-copy">Copiar</button>
2487
+ <pre class="ds-pre"><code>{
2488
+ "mcpServers": {
2489
+ "design": {
2490
+ "type": "http",
2491
+ "url": "https://design-mcp.higiniolab.tech/mcp",
2492
+ "headers": { "Authorization": "Bearer &lt;tu-token&gt;" }
2493
+ }
2494
+ }
2495
+ }</code></pre>
2496
+ </div>
2497
+
2498
+ <h3 style="margin-top:32px">Ejemplo</h3>
2499
+ <p>Consultar un token de este sistema:</p>
2500
+ <div class="ds-code-box"><button class="ds-copy">Copiar</button>
2501
+ <pre class="ds-pre"><code>get_token(system="celeste", name="--accent")
2502
+ → "#7192ba"</code></pre>
2503
+ </div>
2504
+ <p style="margin-top:16px">Auditar código antes de integrarlo:</p>
2505
+ <div class="ds-code-box"><button class="ds-copy">Copiar</button>
2506
+ <pre class="ds-pre"><code>validate_code(system="celeste", code="…")
2507
+ → { ok: false, issues: ["hex directo #7192ba en línea 12 — usa var(--accent)"] }</code></pre>
2508
+ </div>
2509
+
2510
+ <div class="alert alert--info" style="margin-top:24px">
2511
+ <svg><use href="#i-info"/></svg>
2512
+ <div>
2513
+ <p class="alert__title">MCP y skills, misma fuente de verdad</p>
2514
+ <p class="alert__body">Usa las skills dentro de Claude; el MCP para IDEs y clientes MCP externos. Ambos leen <span class="u-mono">DESIGN-celeste-clasica.md</span>.</p>
2515
+ </div>
2516
+ </div>
2517
+ </section>
2518
+
2519
+ </main>
2520
+
2521
+ <footer class="ds-footer">
2522
+ Celeste Clásica v2.0 — Sistema de diseño de Higinio Sosa · Fuente de verdad: <span class="u-mono">DESIGN-celeste-clasica.md</span>
2523
+ </footer>
2524
+
2525
+ <!-- Modales de demostración -->
2526
+ <div class="modal" id="demoModal" role="dialog" aria-modal="true" aria-labelledby="demoModalTitle">
2527
+ <div class="modal__overlay" data-modal-close></div>
2528
+ <div class="modal__panel">
2529
+ <div class="modal__header">
2530
+ <h4 class="modal__title" id="demoModalTitle">¿Eliminar proyecto?</h4>
2531
+ <button class="icon-btn icon-btn--ghost" data-modal-close aria-label="Cerrar"><svg><use href="#i-x"/></svg></button>
2532
+ </div>
2533
+ <p style="font-size:var(--text-sm);color:var(--text-secondary)">Se eliminará "Panel Higinio" con sus 12 tableros. Esta acción no se puede deshacer.</p>
2534
+ <div class="modal__footer">
2535
+ <button class="btn btn--secondary" data-modal-close>Cancelar</button>
2536
+ <button class="btn btn--danger" data-modal-close>Eliminar proyecto</button>
2537
+ </div>
2538
+ </div>
2539
+ </div>
2540
+
2541
+ <div class="modal" id="demoModalLg" role="dialog" aria-modal="true" aria-labelledby="demoModalLgTitle">
2542
+ <div class="modal__overlay" data-modal-close></div>
2543
+ <div class="modal__panel modal__panel--lg">
2544
+ <div class="modal__header">
2545
+ <h4 class="modal__title" id="demoModalLgTitle">Invitar al equipo</h4>
2546
+ <button class="icon-btn icon-btn--ghost" data-modal-close aria-label="Cerrar"><svg><use href="#i-x"/></svg></button>
2547
+ </div>
2548
+ <div style="display:flex;flex-direction:column;gap:var(--form-gap)">
2549
+ <div class="field">
2550
+ <label class="field__label" for="modal-correo">Correo electrónico</label>
2551
+ <input class="input" id="modal-correo" type="email" placeholder="colega@correo.com">
2552
+ </div>
2553
+ <div class="field">
2554
+ <label class="field__label" for="modal-rol">Rol</label>
2555
+ <div class="select-wrap">
2556
+ <select class="select" id="modal-rol">
2557
+ <option>Editor</option>
2558
+ <option>Lector</option>
2559
+ <option>Administrador</option>
2560
+ </select>
2561
+ </div>
2562
+ <span class="field__help">Podrás cambiar el rol más adelante.</span>
2563
+ </div>
2564
+ </div>
2565
+ <div class="modal__footer">
2566
+ <button class="btn btn--secondary" data-modal-close>Cancelar</button>
2567
+ <button class="btn btn--primary" data-modal-close>Enviar invitación</button>
2568
+ </div>
2569
+ </div>
2570
+ </div>
2571
+
2572
+ <!-- Contenedor de toasts -->
2573
+ <div class="toast-stack" id="toastStack" role="status" aria-live="polite"></div>
2574
+
2575
+ <script>
2576
+ (function () {
2577
+ "use strict";
2578
+ var d = document;
2579
+ var reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
2580
+
2581
+ /* ---------- Tema claro/oscuro ---------- */
2582
+ var themeBtn = d.getElementById("dsThemeToggle");
2583
+ if (themeBtn) {
2584
+ themeBtn.addEventListener("click", function () {
2585
+ var root = d.documentElement;
2586
+ var next = root.dataset.theme === "dark" ? "light" : "dark";
2587
+ root.dataset.theme = next;
2588
+ try { localStorage.setItem("cc-theme", next); } catch (e) {}
2589
+ });
2590
+ }
2591
+
2592
+ /* ---------- CTA del hero ---------- */
2593
+ var heroStart = d.getElementById("dsHeroStart");
2594
+ var heroTokens = d.getElementById("dsHeroTokens");
2595
+ function scrollToId(id) {
2596
+ var target = d.getElementById(id);
2597
+ if (target) target.scrollIntoView({ behavior: reduceMotion ? "auto" : "smooth", block: "start" });
2598
+ }
2599
+ if (heroStart) heroStart.addEventListener("click", function () { scrollToId("componentes"); });
2600
+ if (heroTokens) heroTokens.addEventListener("click", function () { scrollToId("tokens"); });
2601
+
2602
+ /* ---------- Menú móvil ---------- */
2603
+ var burger = d.getElementById("dsHamburger");
2604
+ var sidenav = d.getElementById("dsSidenav");
2605
+ if (burger && sidenav) {
2606
+ burger.addEventListener("click", function (e) {
2607
+ e.stopPropagation();
2608
+ sidenav.classList.toggle("is-open");
2609
+ });
2610
+ d.addEventListener("click", function (e) {
2611
+ if (sidenav.classList.contains("is-open") && !sidenav.contains(e.target) && e.target !== burger) {
2612
+ sidenav.classList.remove("is-open");
2613
+ }
2614
+ });
2615
+ sidenav.addEventListener("click", function (e) {
2616
+ if (e.target.closest("a")) sidenav.classList.remove("is-open");
2617
+ });
2618
+ }
2619
+
2620
+ /* ---------- Copiar al portapapeles ---------- */
2621
+ function copyText(text, feedbackFn) {
2622
+ function done() { if (feedbackFn) feedbackFn(); }
2623
+ if (navigator.clipboard && navigator.clipboard.writeText) {
2624
+ navigator.clipboard.writeText(text).then(done, function () { fallback(); });
2625
+ } else { fallback(); }
2626
+ function fallback() {
2627
+ var ta = d.createElement("textarea");
2628
+ ta.value = text; ta.style.position = "fixed"; ta.style.opacity = "0";
2629
+ d.body.appendChild(ta); ta.select();
2630
+ try { d.execCommand("copy"); done(); } catch (e) {}
2631
+ d.body.removeChild(ta);
2632
+ }
2633
+ }
2634
+
2635
+ d.addEventListener("click", function (e) {
2636
+ var btn = e.target.closest(".ds-copy");
2637
+ if (!btn) return;
2638
+ var box = btn.closest(".ds-code-box");
2639
+ var code = box ? box.querySelector("code") : null;
2640
+ if (!code) return;
2641
+ copyText(code.textContent, function () {
2642
+ var prev = btn.textContent;
2643
+ btn.textContent = "¡Copiado!";
2644
+ setTimeout(function () { btn.textContent = prev; }, 1600);
2645
+ });
2646
+ });
2647
+
2648
+ /* ---------- Resaltado de sintaxis (autocontenido, sin librerías) ---------- */
2649
+ (function () {
2650
+ function esc(s) { return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;"); }
2651
+ var RE = /(&lt;!--[\s\S]*?--&gt;)|(\/\*[\s\S]*?\*\/)|("[^"]*"|'[^']*')|(&lt;\/?)([a-zA-Z][\w:-]*)|(\/?&gt;)|([A-Za-z_][\w-]*)(=)(?=")|(--[\w-]+)/g;
2652
+ function hl(s) {
2653
+ return s.replace(RE, function (m, com, css, str, lt, tag, gt, attr, eq, cssvar) {
2654
+ if (com || css) return '<span class="tok-com">' + (com || css) + "</span>";
2655
+ if (str) return '<span class="tok-str">' + str + "</span>";
2656
+ if (lt) return '<span class="tok-punct">' + lt + '</span><span class="tok-tag">' + tag + "</span>";
2657
+ if (gt) return '<span class="tok-punct">' + gt + "</span>";
2658
+ if (attr) return '<span class="tok-attr">' + attr + "</span>" + eq;
2659
+ if (cssvar) return '<span class="tok-var">' + cssvar + "</span>";
2660
+ return m;
2661
+ });
2662
+ }
2663
+ var blocks = d.querySelectorAll(".ds-code code, .ds-code-box code");
2664
+ for (var i = 0; i < blocks.length; i++) { blocks[i].innerHTML = hl(esc(blocks[i].textContent)); }
2665
+ })();
2666
+
2667
+ /* ---------- Swatches de color (generados) ---------- */
2668
+ var PRIMARY = [
2669
+ ["primary-50", "#f2f6fa"], ["primary-100", "#dfe9f2"], ["primary-200", "#c3d5e6"],
2670
+ ["primary-300", "#a0c0d4"], ["primary-400", "#87a8c7"], ["primary-500", "#7192ba"],
2671
+ ["primary-600", "#5d7ba3"], ["primary-700", "#4d6588"], ["primary-800", "#40536e"],
2672
+ ["primary-900", "#31343e"]
2673
+ ];
2674
+ var NEUTRAL = [
2675
+ ["neutral-0", "#ffffff"], ["neutral-50", "#f7f8fa"], ["neutral-100", "#eef0f3"],
2676
+ ["neutral-200", "#dcdfe5"], ["neutral-300", "#b8bdc7"], ["neutral-400", "#8a8f9c"],
2677
+ ["neutral-500", "#666b78"], ["neutral-600", "#4d5160"], ["neutral-700", "#3d414e"],
2678
+ ["neutral-800", "#31343e"], ["neutral-900", "#22242b"]
2679
+ ];
2680
+ var SEMANTIC = [
2681
+ ["success", "#3d9970", "#e2f3ec", "#2b6e50", "Confirmaciones, estados positivos"],
2682
+ ["warning", "#c9922e", "#faf1de", "#8a651c", "Advertencias, pendientes"],
2683
+ ["danger", "#c04d4d", "#f9e7e7", "#8f3232", "Errores, acciones destructivas"],
2684
+ ["info", "#7192ba", "#dfe9f2", "#4d6588", "Informativos"]
2685
+ ];
2686
+ var ALIASES = [
2687
+ ["--bg", "neutral-50", "Fondo de la app"],
2688
+ ["--surface", "neutral-0", "Tarjetas, paneles"],
2689
+ ["--surface-alt", "neutral-100", "Fondos alternos, zebra, tracks"],
2690
+ ["--border", "neutral-200", "Borde por defecto"],
2691
+ ["--border-strong", "neutral-300", "Bordes de inputs y botones secundarios"],
2692
+ ["--text-primary", "neutral-800", "Texto principal"],
2693
+ ["--text-secondary", "neutral-600", "Texto secundario"],
2694
+ ["--text-muted", "neutral-500", "Terciario, captions"],
2695
+ ["--text-disabled", "neutral-400", "Deshabilitado, placeholder"],
2696
+ ["--text-on-primary", "#ffffff", "Texto sobre botón primario"],
2697
+ ["--accent", "primary-500", "Rellenos de acción, bordes activos, focus"],
2698
+ ["--accent-hover", "primary-600", "Hover del primario"],
2699
+ ["--accent-active", "primary-700", "Pressed"],
2700
+ ["--accent-soft", "primary-50", "Hover de ghost, fondos sutiles"],
2701
+ ["--accent-soft-strong", "primary-100", "Seleccionados suaves"],
2702
+ ["--link", "primary-700", "Texto de links y botones ghost (AA ~6:1)"]
2703
+ ];
2704
+
2705
+ function swatchButton(name, hex) {
2706
+ var b = d.createElement("button");
2707
+ b.className = "ds-swatch";
2708
+ b.type = "button";
2709
+ b.setAttribute("data-copy", "var(--" + name + ")");
2710
+ b.setAttribute("aria-label", "Copiar var(--" + name + ")");
2711
+ b.innerHTML =
2712
+ '<span class="ds-swatch__chip" style="display:block;background:' + hex + '"></span>' +
2713
+ '<span class="ds-swatch__meta"><span class="ds-swatch__name">' + name + '</span>' +
2714
+ '<span class="ds-swatch__hex">' + hex + "</span></span>";
2715
+ return b;
2716
+ }
2717
+ var swP = d.getElementById("swatchesPrimary");
2718
+ var swN = d.getElementById("swatchesNeutral");
2719
+ if (swP) PRIMARY.forEach(function (c) { swP.appendChild(swatchButton(c[0], c[1])); });
2720
+ if (swN) NEUTRAL.forEach(function (c) { swN.appendChild(swatchButton(c[0], c[1])); });
2721
+
2722
+ var swS = d.getElementById("swatchesSemantic");
2723
+ if (swS) SEMANTIC.forEach(function (t) {
2724
+ var card = d.createElement("div");
2725
+ card.className = "card";
2726
+ card.style.padding = "16px";
2727
+ card.innerHTML =
2728
+ '<h4 style="font-size:var(--text-base);margin-bottom:10px">' + t[0] + "</h4>" +
2729
+ '<div style="display:flex;gap:8px">' +
2730
+ ['500|' + t[1], '100|' + t[2], 'text|' + t[3]].map(function (pair) {
2731
+ var p = pair.split("|");
2732
+ return '<button type="button" class="ds-swatch" style="flex:1" data-copy="var(--' + t[0] + "-" + p[0] + ')" aria-label="Copiar var(--' + t[0] + "-" + p[0] + ')">' +
2733
+ '<span class="ds-swatch__chip" style="display:block;height:36px;background:' + p[1] + '"></span>' +
2734
+ '<span class="ds-swatch__meta"><span class="ds-swatch__hex">-' + p[0] + "</span></span></button>";
2735
+ }).join("") +
2736
+ "</div>" +
2737
+ '<p style="font-size:var(--text-xs);color:var(--text-muted);margin:10px 0 0;max-width:none">' + t[4] + "</p>";
2738
+ swS.appendChild(card);
2739
+ });
2740
+
2741
+ var aliasBody = d.querySelector("#aliasTable tbody");
2742
+ if (aliasBody) ALIASES.forEach(function (a) {
2743
+ var tr = d.createElement("tr");
2744
+ var chipColor = a[1].indexOf("#") === 0 ? a[1] : "var(--" + a[1] + ")";
2745
+ tr.innerHTML =
2746
+ '<td><span style="display:inline-block;width:24px;height:24px;border-radius:6px;border:1px solid var(--border);background:' + chipColor + '"></span></td>' +
2747
+ '<td class="u-mono">' + a[0] + "</td>" +
2748
+ '<td class="u-mono" style="color:var(--text-muted)">' + a[1] + "</td>" +
2749
+ "<td>" + a[2] + "</td>";
2750
+ aliasBody.appendChild(tr);
2751
+ });
2752
+
2753
+ d.addEventListener("click", function (e) {
2754
+ var sw = e.target.closest("[data-copy]");
2755
+ if (!sw || e.target.closest(".ds-copy")) return;
2756
+ copyText(sw.getAttribute("data-copy"), function () {
2757
+ var nameEl = sw.querySelector(".ds-swatch__name") || sw.querySelector(".ds-swatch__hex");
2758
+ if (!nameEl) return;
2759
+ var prev = nameEl.textContent;
2760
+ nameEl.textContent = "¡Copiado!";
2761
+ setTimeout(function () { nameEl.textContent = prev; }, 1200);
2762
+ });
2763
+ });
2764
+
2765
+ /* ---------- Escala tipográfica (generada) ---------- */
2766
+ var TYPE = [
2767
+ ["text-xs", "12px · 1.5", "Captions, labels", ".75rem", "var(--font-body)", "400"],
2768
+ ["text-sm", "14px · 1.5", "Texto secundario, tablas", ".875rem", "var(--font-body)", "400"],
2769
+ ["text-base", "16px · 1.6", "Párrafos, inputs", "1rem", "var(--font-body)", "400"],
2770
+ ["text-lg", "18px · 1.55", "Lead, subtítulos", "1.125rem", "var(--font-body)", "500"],
2771
+ ["text-xl", "20px · 1.4", "h4", "1.25rem", "var(--font-body)", "600"],
2772
+ ["text-2xl", "25px · 1.3", "h3", "1.5625rem", "var(--font-body)", "600"],
2773
+ ["text-3xl", "31px · 1.25", "h2 (Fraunces)", "1.9375rem", "var(--font-display)", "600"],
2774
+ ["text-4xl", "39px · 1.15", "h1 (Fraunces)", "2.4375rem", "var(--font-display)", "700"],
2775
+ ["text-5xl", "49px · 1.1", "Display/hero", "3.0625rem", "var(--font-display)", "700"]
2776
+ ];
2777
+ var typeWrap = d.getElementById("typeScale");
2778
+ if (typeWrap) TYPE.forEach(function (t) {
2779
+ var row = d.createElement("div");
2780
+ row.className = "ds-type-row";
2781
+ var tracking = parseFloat(t[3]) >= 1.9 ? "letter-spacing:-0.02em;" : "";
2782
+ row.innerHTML =
2783
+ '<span class="ds-type-row__meta">' + t[0] + " · " + t[1] + "<br>" + t[2] + "</span>" +
2784
+ '<span class="ds-type-row__sample" style="font-size:' + t[3] + ";font-family:" + t[4] + ";font-weight:" + t[5] + ";" + tracking + '">Calma y precisión</span>';
2785
+ typeWrap.appendChild(row);
2786
+ });
2787
+
2788
+ /* ---------- Escala de espaciado (generada) ---------- */
2789
+ var SPACES = [1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 24];
2790
+ var spaceWrap = d.getElementById("spaceScale");
2791
+ if (spaceWrap) SPACES.forEach(function (n) {
2792
+ var px = n * 4;
2793
+ var item = d.createElement("div");
2794
+ item.className = "ds-space-item";
2795
+ item.innerHTML =
2796
+ '<span class="u-mono">--space-' + n + " · " + px + "px</span>" +
2797
+ '<span class="ds-space-bar" style="width:' + px * 3 + 'px"></span>';
2798
+ spaceWrap.appendChild(item);
2799
+ });
2800
+
2801
+ /* ---------- Scrollspy ---------- */
2802
+ var navLinks = Array.prototype.slice.call(d.querySelectorAll("#dsNav a"));
2803
+ var targets = navLinks
2804
+ .map(function (a) { return d.getElementById(a.getAttribute("href").slice(1)); })
2805
+ .filter(Boolean);
2806
+ if ("IntersectionObserver" in window && targets.length) {
2807
+ var activeId = null;
2808
+ var spy = new IntersectionObserver(function (entries) {
2809
+ entries.forEach(function (entry) {
2810
+ if (entry.isIntersecting) activeId = entry.target.id;
2811
+ });
2812
+ if (!activeId) return;
2813
+ navLinks.forEach(function (a) {
2814
+ a.classList.toggle("is-active", a.getAttribute("href") === "#" + activeId);
2815
+ });
2816
+ }, { rootMargin: "-25% 0px -65% 0px" });
2817
+ targets.forEach(function (t) { spy.observe(t); });
2818
+ }
2819
+
2820
+ /* ---------- Contador de textarea ---------- */
2821
+ var ta = d.getElementById("demo-ta");
2822
+ var taCounter = d.getElementById("demo-ta-counter");
2823
+ if (ta && taCounter) {
2824
+ ta.addEventListener("input", function () {
2825
+ taCounter.textContent = ta.value.length + " / 200";
2826
+ });
2827
+ }
2828
+
2829
+ /* ---------- Sliders: porción rellena ---------- */
2830
+ function paintSlider(s) {
2831
+ var min = parseFloat(s.min || 0), max = parseFloat(s.max || 100);
2832
+ var pct = ((parseFloat(s.value) - min) / (max - min)) * 100;
2833
+ s.style.setProperty("--slider-fill", pct + "%");
2834
+ }
2835
+ Array.prototype.forEach.call(d.querySelectorAll(".js-slider"), function (s) {
2836
+ paintSlider(s);
2837
+ s.addEventListener("input", function () { paintSlider(s); });
2838
+ });
2839
+ var demoSlider = d.getElementById("demo-slider");
2840
+ var demoSliderValue = d.getElementById("demo-slider-value");
2841
+ if (demoSlider && demoSliderValue) {
2842
+ demoSlider.addEventListener("input", function () {
2843
+ demoSliderValue.textContent = "$ " + Number(demoSlider.value).toLocaleString("es-MX");
2844
+ });
2845
+ }
2846
+
2847
+ /* ---------- Accordion ---------- */
2848
+ Array.prototype.forEach.call(d.querySelectorAll(".js-accordion"), function (btn) {
2849
+ btn.addEventListener("click", function () {
2850
+ var item = btn.closest(".accordion__item");
2851
+ var open = item.classList.toggle("is-open");
2852
+ btn.setAttribute("aria-expanded", open ? "true" : "false");
2853
+ });
2854
+ });
2855
+
2856
+ /* ---------- Alert dismiss ---------- */
2857
+ Array.prototype.forEach.call(d.querySelectorAll(".js-alert-close"), function (btn) {
2858
+ btn.addEventListener("click", function () {
2859
+ var alert = btn.closest(".alert");
2860
+ alert.style.opacity = "0";
2861
+ alert.style.transition = "opacity 200ms var(--ease-in)";
2862
+ setTimeout(function () { alert.remove(); }, 220);
2863
+ });
2864
+ });
2865
+
2866
+ /* ---------- Modal ---------- */
2867
+ var openModalBtns = d.querySelectorAll("[data-modal-open]");
2868
+ Array.prototype.forEach.call(openModalBtns, function (btn) {
2869
+ btn.addEventListener("click", function () {
2870
+ var modal = d.getElementById(btn.getAttribute("data-modal-open"));
2871
+ if (modal) modal.classList.add("is-open");
2872
+ });
2873
+ });
2874
+ d.addEventListener("click", function (e) {
2875
+ if (e.target.closest("[data-modal-close]")) {
2876
+ var modal = e.target.closest(".modal");
2877
+ if (modal) modal.classList.remove("is-open");
2878
+ }
2879
+ });
2880
+ d.addEventListener("keydown", function (e) {
2881
+ if (e.key !== "Escape") return;
2882
+ Array.prototype.forEach.call(d.querySelectorAll(".modal.is-open"), function (m) {
2883
+ m.classList.remove("is-open");
2884
+ });
2885
+ Array.prototype.forEach.call(d.querySelectorAll(".menu.is-open"), function (m) {
2886
+ m.classList.remove("is-open");
2887
+ });
2888
+ });
2889
+
2890
+ /* ---------- Menu ---------- */
2891
+ Array.prototype.forEach.call(d.querySelectorAll(".js-menu-toggle"), function (btn) {
2892
+ btn.addEventListener("click", function (e) {
2893
+ e.stopPropagation();
2894
+ var menu = btn.parentElement.querySelector(".menu");
2895
+ if (menu) menu.classList.toggle("is-open");
2896
+ });
2897
+ });
2898
+ d.addEventListener("click", function (e) {
2899
+ Array.prototype.forEach.call(d.querySelectorAll(".menu.is-open"), function (m) {
2900
+ if (!m.parentElement.contains(e.target) || e.target.closest(".menu__item")) {
2901
+ m.classList.remove("is-open");
2902
+ }
2903
+ });
2904
+ });
2905
+
2906
+ /* ---------- Tabs ---------- */
2907
+ Array.prototype.forEach.call(d.querySelectorAll(".js-tab"), function (tab) {
2908
+ tab.addEventListener("click", function () {
2909
+ var group = tab.closest(".tabs");
2910
+ Array.prototype.forEach.call(group.querySelectorAll(".js-tab"), function (t) {
2911
+ t.classList.toggle("is-active", t === tab);
2912
+ t.setAttribute("aria-selected", t === tab ? "true" : "false");
2913
+ var panel = d.getElementById("tab-" + t.getAttribute("data-tab"));
2914
+ if (panel) panel.hidden = t !== tab;
2915
+ });
2916
+ });
2917
+ });
2918
+
2919
+ /* ---------- Toast ---------- */
2920
+ var TOAST_DATA = {
2921
+ success: ["i-circle-check", "Cambios guardados", "El proyecto se actualizó correctamente."],
2922
+ info: ["i-info", "Sincronización lista", "Tus datos están al día."],
2923
+ warning: ["i-triangle-alert", "Conexión inestable", "Los cambios se guardarán al reconectar."],
2924
+ danger: ["i-circle-alert", "No se pudo guardar", "Revisa tu conexión e inténtalo de nuevo."]
2925
+ };
2926
+ var stack = d.getElementById("toastStack");
2927
+ function showToast(tone) {
2928
+ if (!stack) return;
2929
+ while (stack.children.length >= 3) stack.removeChild(stack.firstChild);
2930
+ var t = TOAST_DATA[tone] || TOAST_DATA.info;
2931
+ var toast = d.createElement("div");
2932
+ toast.className = "toast toast--" + tone;
2933
+ toast.setAttribute("role", tone === "danger" ? "alert" : "status");
2934
+ toast.innerHTML =
2935
+ '<svg><use href="#' + t[0] + '"/></svg>' +
2936
+ "<div><p class=\"toast__title\">" + t[1] + "</p><p class=\"toast__body\">" + t[2] + "</p></div>" +
2937
+ '<button class="icon-btn icon-btn--ghost icon-btn--sm" aria-label="Cerrar notificación"><svg><use href="#i-x"/></svg></button>';
2938
+ stack.appendChild(toast);
2939
+ var timer = setTimeout(dismiss, 5000);
2940
+ toast.addEventListener("mouseenter", function () { clearTimeout(timer); });
2941
+ toast.addEventListener("mouseleave", function () { timer = setTimeout(dismiss, 2000); });
2942
+ toast.querySelector("button").addEventListener("click", function () {
2943
+ clearTimeout(timer); dismiss();
2944
+ });
2945
+ function dismiss() {
2946
+ if (!toast.parentElement) return;
2947
+ toast.classList.add("is-leaving");
2948
+ toast.addEventListener("animationend", function () { toast.remove(); });
2949
+ if (reduceMotion) toast.remove();
2950
+ }
2951
+ }
2952
+ Array.prototype.forEach.call(d.querySelectorAll("[data-toast]"), function (btn) {
2953
+ btn.addEventListener("click", function () { showToast(btn.getAttribute("data-toast")); });
2954
+ });
2955
+
2956
+ /* ---------- Progress demo ---------- */
2957
+ var progressBtn = d.querySelector(".js-progress-demo");
2958
+ if (progressBtn) {
2959
+ progressBtn.addEventListener("click", function () {
2960
+ var v = Math.round(Math.random() * 100);
2961
+ var bar = d.querySelector(".js-progress-bar");
2962
+ var value = d.querySelector(".js-progress-value");
2963
+ if (bar) { bar.style.width = v + "%"; bar.setAttribute("aria-valuenow", v); }
2964
+ if (value) value.textContent = v + "%";
2965
+ var ring = d.querySelector(".js-ring");
2966
+ if (ring) paintRing(ring, v);
2967
+ });
2968
+ }
2969
+ function paintRing(ring, value) {
2970
+ var bar = ring.querySelector(".progress-ring__bar");
2971
+ var label = ring.querySelector(".progress-ring__value");
2972
+ var r = parseFloat(bar.getAttribute("r"));
2973
+ var c = 2 * Math.PI * r;
2974
+ bar.style.strokeDasharray = c;
2975
+ bar.style.strokeDashoffset = c * (1 - value / 100);
2976
+ if (label) label.textContent = value + "%";
2977
+ }
2978
+ Array.prototype.forEach.call(d.querySelectorAll(".js-ring"), function (ring) {
2979
+ paintRing(ring, parseFloat(ring.getAttribute("data-value") || 0));
2980
+ });
2981
+
2982
+ /* ---------- Pagination (demo funcional) ---------- */
2983
+ var pagWrap = d.querySelector(".js-pagination");
2984
+ if (pagWrap) {
2985
+ var TOTAL_PAGES = 12;
2986
+ var current = 5;
2987
+ function renderPagination() {
2988
+ var pages = [];
2989
+ if (TOTAL_PAGES <= 7) {
2990
+ for (var i = 1; i <= TOTAL_PAGES; i++) pages.push(i);
2991
+ } else {
2992
+ pages.push(1);
2993
+ if (current > 3) pages.push("…");
2994
+ for (var p = Math.max(2, current - 1); p <= Math.min(TOTAL_PAGES - 1, current + 1); p++) pages.push(p);
2995
+ if (current < TOTAL_PAGES - 2) pages.push("…");
2996
+ pages.push(TOTAL_PAGES);
2997
+ }
2998
+ var html = '<li><button class="pagination__btn" data-page="prev" aria-label="Página anterior"' +
2999
+ (current === 1 ? " disabled" : "") + '><svg><use href="#i-chevron-left"/></svg></button></li>';
3000
+ pages.forEach(function (pg) {
3001
+ if (pg === "…") {
3002
+ html += '<li><span class="pagination__ellipsis">…</span></li>';
3003
+ } else {
3004
+ html += '<li><button class="pagination__btn' + (pg === current ? " is-active" : "") +
3005
+ '" data-page="' + pg + '"' + (pg === current ? ' aria-current="page"' : "") + ">" + pg + "</button></li>";
3006
+ }
3007
+ });
3008
+ html += '<li><button class="pagination__btn" data-page="next" aria-label="Página siguiente"' +
3009
+ (current === TOTAL_PAGES ? " disabled" : "") + '><svg><use href="#i-chevron-right"/></svg></button></li>';
3010
+ pagWrap.innerHTML = html;
3011
+ }
3012
+ pagWrap.addEventListener("click", function (e) {
3013
+ var btn = e.target.closest("[data-page]");
3014
+ if (!btn || btn.disabled) return;
3015
+ var pg = btn.getAttribute("data-page");
3016
+ if (pg === "prev") current = Math.max(1, current - 1);
3017
+ else if (pg === "next") current = Math.min(TOTAL_PAGES, current + 1);
3018
+ else current = parseInt(pg, 10);
3019
+ renderPagination();
3020
+ });
3021
+ renderPagination();
3022
+ }
3023
+
3024
+ /* ---------- Count-up de Stat ---------- */
3025
+ function countUp(el) {
3026
+ var target = parseFloat(el.getAttribute("data-target") || 0);
3027
+ var prefix = el.getAttribute("data-prefix") || "";
3028
+ var suffix = el.getAttribute("data-suffix") || "";
3029
+ function fmt(v) { return prefix + Math.round(v).toLocaleString("es-MX") + suffix; }
3030
+ if (reduceMotion) { el.textContent = fmt(target); return; }
3031
+ var start = null, DUR = 600;
3032
+ function tick(ts) {
3033
+ if (start === null) start = ts;
3034
+ var t = Math.min(1, (ts - start) / DUR);
3035
+ var eased = 1 - Math.pow(1 - t, 3);
3036
+ el.textContent = fmt(target * eased);
3037
+ if (t < 1) requestAnimationFrame(tick);
3038
+ }
3039
+ requestAnimationFrame(tick);
3040
+ }
3041
+ var countEls = d.querySelectorAll(".js-countup");
3042
+ if ("IntersectionObserver" in window) {
3043
+ var seen = new WeakSet();
3044
+ var counter = new IntersectionObserver(function (entries) {
3045
+ entries.forEach(function (entry) {
3046
+ if (entry.isIntersecting && !seen.has(entry.target)) {
3047
+ seen.add(entry.target);
3048
+ countUp(entry.target);
3049
+ counter.unobserve(entry.target);
3050
+ }
3051
+ });
3052
+ }, { threshold: 0.4 });
3053
+ Array.prototype.forEach.call(countEls, function (el) { counter.observe(el); });
3054
+ } else {
3055
+ Array.prototype.forEach.call(countEls, countUp);
3056
+ }
3057
+ })();
3058
+ </script>
3059
+ </body>
3060
+ </html>