@eduardoalvarez/arrecife 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/README.md +706 -470
  3. package/dist/brand/index.cjs +112 -95
  4. package/dist/brand/index.d.cts +40 -39
  5. package/dist/brand/index.d.ts +40 -39
  6. package/dist/brand/index.js +5 -4
  7. package/dist/catalog-D13txprv.d.cts +78 -0
  8. package/dist/catalog-D13txprv.d.ts +78 -0
  9. package/dist/chart/index.cjs +100 -83
  10. package/dist/chart/index.d.cts +66 -66
  11. package/dist/chart/index.d.ts +66 -66
  12. package/dist/chart/index.js +14 -12
  13. package/dist/chunk-25YNFCIF.js +141 -0
  14. package/dist/{chunk-YZ2SDOVZ.js → chunk-6O3KWB6P.js} +30 -30
  15. package/dist/chunk-CKRSQPTX.js +36 -0
  16. package/dist/{chunk-ZEOQKRQ7.js → chunk-DKCN7BAL.js} +1 -1
  17. package/dist/chunk-GCRII2KQ.js +86 -0
  18. package/dist/chunk-JMOOFZ3B.js +42 -0
  19. package/dist/chunk-O4TAH7YJ.js +276 -0
  20. package/dist/chunk-ODBFN44D.js +45 -0
  21. package/dist/{chunk-VPT32GPG.js → chunk-PMN7NR3G.js} +2 -2
  22. package/dist/chunk-XKYHTOUJ.js +27 -0
  23. package/dist/form/index.cjs +109 -92
  24. package/dist/form/index.d.cts +43 -42
  25. package/dist/form/index.d.ts +43 -42
  26. package/dist/form/index.js +25 -23
  27. package/dist/index.cjs +1068 -929
  28. package/dist/index.d.cts +770 -773
  29. package/dist/index.d.ts +770 -773
  30. package/dist/index.js +629 -675
  31. package/dist/{label-DuTvJGxD.d.ts → label-MgHFKnFy.d.cts} +3 -3
  32. package/dist/{label-DuTvJGxD.d.cts → label-MgHFKnFy.d.ts} +3 -3
  33. package/dist/og/index.cjs +130 -130
  34. package/dist/og/index.d.cts +93 -89
  35. package/dist/og/index.d.ts +93 -89
  36. package/dist/og/index.js +106 -106
  37. package/dist/shiki/index.cjs +28 -30
  38. package/dist/shiki/index.d.cts +4 -4
  39. package/dist/shiki/index.d.ts +4 -4
  40. package/dist/shiki/index.js +12 -12
  41. package/dist/theme/index.cjs +97 -0
  42. package/dist/theme/index.d.cts +144 -0
  43. package/dist/theme/index.d.ts +144 -0
  44. package/dist/theme/index.js +2 -0
  45. package/dist/tokens/index.cjs +133 -86
  46. package/dist/tokens/index.d.cts +246 -161
  47. package/dist/tokens/index.d.ts +246 -161
  48. package/dist/tokens/index.js +2 -2
  49. package/dist/tokens/theme.css +133 -98
  50. package/dist/variants/index.cjs +192 -0
  51. package/dist/variants/index.d.cts +192 -0
  52. package/dist/variants/index.d.ts +192 -0
  53. package/dist/variants/index.js +3 -0
  54. package/llms.txt +810 -744
  55. package/package.json +20 -11
  56. package/dist/catalogo-Du5ID-Hi.d.cts +0 -77
  57. package/dist/catalogo-Du5ID-Hi.d.ts +0 -77
  58. package/dist/chunk-E3OMP2DL.js +0 -36
  59. package/dist/chunk-KPZNNMV5.js +0 -83
  60. package/dist/chunk-NHS7ETKJ.js +0 -27
  61. package/dist/chunk-TSPJOM6K.js +0 -229
  62. package/dist/chunk-UOWIDFCB.js +0 -81
  63. package/dist/tema/index.cjs +0 -94
  64. package/dist/tema/index.d.cts +0 -110
  65. package/dist/tema/index.d.ts +0 -110
  66. package/dist/tema/index.js +0 -2
package/dist/index.cjs CHANGED
@@ -1,14 +1,15 @@
1
+ 'use client';
1
2
  'use strict';
2
3
 
4
+ var classVarianceAuthority = require('class-variance-authority');
3
5
  var AccordionPrimitive = require('@radix-ui/react-accordion');
4
6
  var clsx = require('clsx');
5
7
  var tailwindMerge = require('tailwind-merge');
6
8
  var jsxRuntime = require('react/jsx-runtime');
7
- var classVarianceAuthority = require('class-variance-authority');
8
9
  var AlertDialogPrimitive = require('@radix-ui/react-alert-dialog');
9
- var reactSlot = require('@radix-ui/react-slot');
10
10
  var AvatarPrimitive = require('@radix-ui/react-avatar');
11
11
  var react = require('react');
12
+ var reactSlot = require('@radix-ui/react-slot');
12
13
  var locale = require('date-fns/locale');
13
14
  var reactDayPicker = require('react-day-picker');
14
15
  var CheckboxPrimitive = require('@radix-ui/react-checkbox');
@@ -70,144 +71,175 @@ var __export = (target, all) => {
70
71
  // src/tokens/tokens.ts
71
72
  var dark = {
72
73
  background: "#091319",
73
- // abismo · fondo de página
74
+ // abyss · page background
74
75
  surface: "#10202B",
75
- // fosa · tarjetas y paneles
76
+ // trench · cards and panels
76
77
  surfaceRaised: "#17303E",
77
- // corriente · menús, tabs activos
78
+ // current · menus, active tabs
78
79
  border: "#22414F",
79
- // bordes de control
80
+ // control borders
80
81
  hairline: "#1E3441",
81
- // divisiones sutiles
82
+ // subtle dividers
82
83
  hairlineHover: "#2C4D5D",
83
- // hairline en hover de tarjeta
84
+ // hairline on card hover
84
85
  textPrimary: "#EDF4F3",
85
- // espuma 16.84:1
86
+ // foam 16.84:1
86
87
  textSecondary: "#A7BCC4",
87
- // bruma 9.50:1
88
+ // haze 9.50:1
88
89
  textMuted: "#71919C",
89
- // plancton 5.57:1 nunca bajo 13px
90
+ // plankton 5.57:1 never under 13px
90
91
  accent: "#35D6C0",
91
- // bioluz 10.31:1 interactivo
92
+ // biolume 10.31:1 interactive
92
93
  accentHover: "#5FE3D1",
93
94
  accentOn: "#06171A",
94
- // tinta sobre bioluz
95
+ // ink over biolume
95
96
  warm: "#F2A65A",
96
- // arena 9.28:1 humano y conversión
97
+ // sand 9.28:1 human and conversion
97
98
  warmHover: "#F7BB7D",
98
99
  warmOn: "#2A1605",
99
- // tinta sobre arena
100
+ // ink over sand
100
101
  success: "#4FB477",
101
102
  warning: "#E8A33D",
102
- error: "#E15757"
103
- // 4.51:1 sobre surface
103
+ error: "#E15757",
104
+ // 4.51:1 over surface
105
+ /**
106
+ * The destructive fill. It is NOT `error`, and the difference is the role.
107
+ *
108
+ * `error` is a TEXT color: it has to read against a dark surface, so it sits
109
+ * mid-red. `danger` is a FILL: what has to read is the ink ON TOP of it, so it
110
+ * goes lighter. Same reasoning as `accent` and `accentOn` — one is measured
111
+ * against the page, the other against what it carries.
112
+ *
113
+ * Measured: ink 6.53:1 over the fill and 7.92:1 over the hover; the fill
114
+ * itself 6.71:1 over abyss, 5.94 over surface and 4.91 over surfaceRaised, so
115
+ * the outline variant reads as text on all three.
116
+ */
117
+ danger: "#F4736B",
118
+ dangerHover: "#F78D86",
119
+ dangerOn: "#2B0A08"
120
+ // ink over danger 6.53:1
104
121
  };
105
122
  var light = {
106
123
  background: "#F6F2EA",
107
- // papel
124
+ // paper
108
125
  surface: "#FFFFFF",
109
126
  surfaceRaised: "#EFE9DE",
110
127
  border: "#E6DFD2",
111
128
  hairline: "#EBE6DC",
112
129
  hairlineHover: "#D3C8B2",
113
- // hairline en hover de tarjeta
130
+ // hairline on card hover
114
131
  textPrimary: "#0B1524",
115
132
  // 16.40:1
116
133
  textSecondary: "#3D4B58",
117
134
  textMuted: "#626A75",
118
- // 4.53:1 sobre surfaceRaised
135
+ // 4.53:1 over surfaceRaised
119
136
  accent: "#0D7C6F",
120
- // bioluz oscura 4.55:1
137
+ // dark biolume 4.55:1
121
138
  accentHover: "#0C7466",
122
139
  accentOn: "#FFFFFF",
123
140
  warm: "#A65B27",
124
- // arena oscura 4.54:1
141
+ // dark sand 4.54:1
125
142
  warmHover: "#96511F",
126
143
  warmOn: "#FFF7EE",
127
144
  success: "#0F6B52",
128
145
  warning: "#8D6111",
129
- // 4.51:1 sobre surfaceRaised
130
- error: "#C0392B"
146
+ // 4.51:1 over surfaceRaised
147
+ error: "#C0392B",
148
+ /**
149
+ * In light mode `danger` and `error` land on the same hex, and that is not an
150
+ * oversight: over paper a red dark enough to carry white ink is also the red
151
+ * that reads as text. In dark they part company because the fill has to be
152
+ * lighter than the text color, not darker.
153
+ *
154
+ * Measured: ink 5.11:1 over the fill and 6.61:1 over the hover; the fill
155
+ * itself 4.87:1 over paper, 5.44 over surface and 4.50 over surfaceRaised —
156
+ * the last one is exactly on the AA line, which is where every light semantic
157
+ * in this palette sits.
158
+ */
159
+ danger: "#C0392B",
160
+ dangerHover: "#A32F22",
161
+ dangerOn: "#FFF6F4"
162
+ // 5.11:1 over danger
131
163
  };
132
164
  var colors = { dark, light };
133
165
  var brand = {
134
- /** Cuerpo de la mascota. 4.22:1 → SOLO relleno, NUNCA texto. */
166
+ /** The mascot's body. 4.22:1 → FILL ONLY, NEVER text. */
135
167
  body: "#3E7CB1",
136
- /** Patrón de manchas. */
168
+ /** Spot pattern. */
137
169
  spots: "#C2D7E7",
138
- /** Casco · contorno y fondo de bloques de código. */
170
+ /** Hull · outline, and the background of code blocks. */
139
171
  hull: "#0B1524"
140
172
  };
141
173
  var fonts = {
142
- /** SOLO titulares y números grandes. Nunca cuerpo. */
174
+ /** Headlines and large numbers ONLY. Never body copy. */
143
175
  display: '"Bricolage Grotesque", ui-sans-serif, system-ui, sans-serif',
144
- /** Cuerpo e interfaz. */
176
+ /** Body and interface. */
145
177
  sans: '"Geist", ui-sans-serif, system-ui, sans-serif',
146
- /** Código, rutas, etiquetas, metadatos, firma CLI. */
178
+ /** Code, paths, labels, metadata, the CLI signature. */
147
179
  mono: '"JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace'
148
180
  };
149
181
  var typeScale = {
150
182
  display: { family: "display", size: 76, lineHeight: 0.96, weight: 800, tracking: "-0.035em" },
151
- /** Métricas grandes. Números, no prosa: interlineado 1 y sin descendentes. */
183
+ /** Large metrics. Numbers, not prose: line height 1 and no descenders. */
152
184
  stat: { family: "display", size: 46, lineHeight: 1, weight: 800, tracking: "-0.035em" },
153
185
  h1: { family: "display", size: 44, lineHeight: 1.05, weight: 700, tracking: "-0.03em" },
154
186
  h2: { family: "display", size: 30, lineHeight: 1.1, weight: 600, tracking: "-0.02em" },
155
187
  h3: { family: "display", size: 25, lineHeight: 1.15, weight: 600, tracking: "-0.02em" },
156
188
  body: { family: "sans", size: 18, lineHeight: 1.75, weight: 400 },
157
189
  /**
158
- * 17px. El botón grande y la bajada de las páginas internas.
190
+ * 17px. The large button, and the deck on interior pages.
159
191
  *
160
- * Entra como escalón propio porque el documento lo usa DOS veces y en piezas
161
- * distintas: «lg 15px 30px r12» con texto de 17, y «h1 44/700 · bajada 17px ·
162
- * párrafo de contexto 15px». Un solo uso no habría justificado partir la
163
- * distancia entre `ui` (15) y `body` (18); dos sí.
192
+ * It earns its own step because the identity document uses it TWICE, in two
193
+ * different pieces: «lg 15/30 r12» with 17px text, and «h1 44/700 · deck 17px
194
+ * · context paragraph 15px». One use would not have justified splitting the
195
+ * gap between `ui` (15) and `body` (18); two did.
164
196
  */
165
197
  lead: { family: "sans", size: 17, lineHeight: 1.5, weight: 400 },
166
198
  ui: { family: "sans", size: 15, lineHeight: 1.6, weight: 400 },
167
199
  label: { family: "sans", size: 13, lineHeight: 1.5, weight: 500 },
168
200
  /**
169
- * La escala de las etiquetas de estado: sans 12.5/500, cuadrada.
201
+ * The status-badge scale: sans 12.5/500, squared off.
170
202
  *
171
- * Es medio píxel por debajo de `label`, y esta vez el medio píxel sí importa:
172
- * una etiqueta de estado va dentro de una tabla o al lado de un título, y a 13
173
- * competía con el texto que acompaña. El documento la da en 12.5 y se ve.
203
+ * That is half a pixel below `label`, and this time the half pixel matters: a
204
+ * status badge sits inside a table or next to a title, and at 13 it competed
205
+ * with the text it accompanies. The document specifies 12.5 and it shows.
174
206
  */
175
207
  tag: { family: "sans", size: 12.5, lineHeight: 1.4, weight: 500 },
176
208
  /**
177
- * La escala de categoría y métrica: mono 11.5.
209
+ * The category and metric scale: mono 11.5.
178
210
  *
179
- * Rompe el suelo de `limits.minScreenPx`, y es a propósito. Ese suelo protege
180
- * al TEXTO —lo que se lee en una frase—, y una píldora de una palabra no es
181
- * texto corrido: es una marca. El contraste sigue medido y pasa AA (plancton
182
- * 5.57:1 sobre abismo), que es la parte que no se negocia.
211
+ * It breaks the floor set by `limits.minScreenPx`, deliberately. That floor
212
+ * protects TEXT — what you read in a sentence — and a one-word pill is not
213
+ * running text: it is a mark. Contrast is still measured and still passes AA
214
+ * (plankton 5.57:1 over abyss), which is the part that is not negotiable.
183
215
  *
184
- * A 13 las tres familias de etiqueta salían del tamaño de un botón pequeño y
185
- * pesaban más que el título que acompañan. Ver `docs/decisiones.md`.
216
+ * At 13 the three badge families grew past the size of a small button and
217
+ * outweighed the title they accompany. See `docs/decisions.md`.
186
218
  */
187
219
  chip: { family: "mono", size: 11.5, lineHeight: 1.4, weight: 400 },
188
220
  /**
189
- * Mono SIN transformar: fechas, rutas, versiones, nombres de archivo, la firma
190
- * del footer y las métricas de etiqueta. Es la mayoría del mono del sistema.
221
+ * Mono with NO transform: dates, paths, versions, file names, the footer
222
+ * signature and metric labels. It is most of the mono in the system.
191
223
  *
192
- * Existe porque `eyebrow` lleva `uppercase` de fábrica y no es una preferencia
193
- * que se pueda apagar: es la escala del eyebrow. Un `18 ago 2026` en versalitas
194
- * o un `pose-laptop-coffee.png` en mayúsculas están mal, y sin este escalón la
195
- * única salida era un `normal-case` en cada sitio de uso.
224
+ * It exists because `eyebrow` ships `uppercase` and that is not a preference
225
+ * you can switch off: it IS the eyebrow scale. An `18 Aug 2026` in small caps
226
+ * or a `pose-laptop-coffee.png` in all caps are wrong, and without this step
227
+ * the only way out was a `normal-case` at every call site.
196
228
  *
197
- * El documento dice 12.5. Son 13 por la misma razón por la que `textMuted` se
198
- * corrigió: `limits.minScreenPx` es 13 y plancton «nunca bajo 13px». Esta
199
- * escala es justo donde se escriben las metas en muted, así que ponerla en
200
- * 12.5 habría dejado ilegible su uso más común por medio píxel.
229
+ * The document says 12.5. This is 13 for the same reason `textMuted` was
230
+ * corrected: `limits.minScreenPx` is 13 and plankton is «never under 13px».
231
+ * This scale is exactly where muted metadata gets written, so putting it at
232
+ * 12.5 would have made its most common use illegible by half a pixel.
201
233
  */
202
234
  meta: { family: "mono", size: 13, lineHeight: 1.6, weight: 400 },
203
235
  eyebrow: { family: "mono", size: 12, tracking: "0.12em", transform: "uppercase" }
204
236
  };
205
237
  var limits = {
206
- /** Mínimo absoluto en pantalla. */
238
+ /** Absolute minimum on screen. */
207
239
  minScreenPx: 13,
208
- /** Mínimo absoluto impreso. */
240
+ /** Absolute minimum in print. */
209
241
  minPrintPt: 12,
210
- /** Medida máxima de cuerpo. */
242
+ /** Maximum body measure. */
211
243
  measure: "68ch"
212
244
  };
213
245
  var radius = {
@@ -221,8 +253,24 @@ var control = {
221
253
  sm: 14,
222
254
  md: 22,
223
255
  lg: 30,
224
- /** Botón de icono: cuadrado, sin texto. */
225
- icon: 42
256
+ /** Icon button: square, no text. */
257
+ icon: 42,
258
+ /**
259
+ * The dense icon button, 32×32.
260
+ *
261
+ * 42 is the right measure for a control you hit with a thumb, and the four
262
+ * reading sites are where that fits. `cursos` is the odd one out: it is the
263
+ * only admin app of the set, with three actions per table row, and at 42 the
264
+ * row grows with them — the original used 24 and 28.
265
+ *
266
+ * It is 32 and not 28 because 32 is `sm`'s height. A dense icon button lines
267
+ * up with a small text button, so a toolbar mixing the two stays on one
268
+ * baseline; 28 would have been a fifth height that matches nothing.
269
+ *
270
+ * It does not replace `icon`. A page's primary action stays at 42; this is for
271
+ * a row of a table. See `docs/decisions.md` § 22.
272
+ */
273
+ iconSm: 32
226
274
  };
227
275
  var spacing = {
228
276
  stepXs: 8,
@@ -233,42 +281,42 @@ var spacing = {
233
281
  section: 96
234
282
  };
235
283
  var size = {
236
- /** Alto de la barra de navegación. */
284
+ /** Navigation bar height. */
237
285
  nav: 64,
238
- /** Ancho de columna de lectura. */
286
+ /** Reading column width. */
239
287
  content: 760,
240
- /** Ancho máximo de página. */
288
+ /** Maximum page width. */
241
289
  wide: 1180
242
290
  };
243
- var sintaxis = {
244
- /** El casco. Es el fondo del bloque en los dos modos. */
245
- fondo: brand.hull,
246
- /** Identificadores, funciones, tipos, variables. */
247
- identificador: dark.textPrimary,
248
- /** Cadenas, números, booleanos, null. */
291
+ var syntax = {
292
+ /** The hull. It is the block background in both modes. */
293
+ background: brand.hull,
294
+ /** Identifiers, functions, types, variables. */
295
+ identifier: dark.textPrimary,
296
+ /** Strings, numbers, booleans, null. */
249
297
  literal: dark.accent,
250
- /** Palabras clave, control de flujo, `import`, `this`. */
251
- palabraClave: dark.warm,
252
- /** Comentarios y puntuación. */
253
- comentario: dark.textMuted,
298
+ /** Keywords, control flow, `import`, `this`. */
299
+ keyword: dark.warm,
300
+ /** Comments and punctuation. */
301
+ comment: dark.textMuted,
254
302
  /** `markup.deleted`, `invalid`. */
255
- invalido: dark.error
303
+ invalid: dark.error
256
304
  };
257
305
  var series = {
258
306
  dark: [dark.accent, dark.warm, brand.body, dark.textMuted],
259
307
  light: [light.accent, light.warm, brand.body, light.textMuted]
260
308
  };
261
- var profundo = "#0D2129";
309
+ var deep = "#0D2129";
262
310
  var gradient = {
263
311
  dark: {
264
- hero: `linear-gradient(160deg, ${dark.background} 60%, ${profundo} 100%)`,
265
- seccion: `linear-gradient(150deg, ${dark.surface} 0%, ${profundo} 100%)`,
266
- /** El de la plantilla OG de artículo. 145°, del documento. */
267
- og: `linear-gradient(145deg, ${dark.background} 55%, ${profundo} 100%)`
312
+ hero: `linear-gradient(160deg, ${dark.background} 60%, ${deep} 100%)`,
313
+ section: `linear-gradient(150deg, ${dark.surface} 0%, ${deep} 100%)`,
314
+ /** The one used by the article OG template. 145°, from the document. */
315
+ og: `linear-gradient(145deg, ${dark.background} 55%, ${deep} 100%)`
268
316
  },
269
317
  light: {
270
318
  hero: `linear-gradient(160deg, ${light.background} 60%, ${light.surfaceRaised} 100%)`,
271
- seccion: `linear-gradient(150deg, ${light.surface} 0%, ${light.surfaceRaised} 100%)`,
319
+ section: `linear-gradient(150deg, ${light.surface} 0%, ${light.surfaceRaised} 100%)`,
272
320
  og: `linear-gradient(145deg, ${light.background} 55%, ${light.surfaceRaised} 100%)`
273
321
  }
274
322
  };
@@ -282,16 +330,16 @@ var motion = {
282
330
  };
283
331
  var tagline = {
284
332
  /** Hero. */
285
- largo: "Ayudo a equipos de ingenier\xEDa a escalar con criterio",
286
- /** Header, una línea. */
287
- corto: "Ayudo a equipos a escalar con criterio",
333
+ long: "Ayudo a equipos de ingenier\xEDa a escalar con criterio",
334
+ /** Header, one line. */
335
+ short: "Ayudo a equipos a escalar con criterio",
288
336
  /** LinkedIn. */
289
337
  en: "Helping engineering teams scale with judgment"
290
338
  };
291
339
  var naming = {
292
340
  wordmark: "Eduardo \xC1lvarez",
293
341
  mascot: "Tiburonc\xEDn",
294
- /** El dominio, para la firma CLI del footer: `$ cd ~/eduardoalvarez.dev/2026`. */
342
+ /** The domain, for the footer CLI signature: `$ cd ~/eduardoalvarez.dev/2026`. */
295
343
  domain: "eduardoalvarez.dev"
296
344
  };
297
345
 
@@ -307,7 +355,7 @@ var tokens = {
307
355
  spacing,
308
356
  size,
309
357
  gradient,
310
- sintaxis,
358
+ syntax,
311
359
  series,
312
360
  shadow,
313
361
  motion,
@@ -315,102 +363,281 @@ var tokens = {
315
363
  naming
316
364
  };
317
365
 
318
- // src/tema/index.ts
319
- var TEMA_CLAVE = "arrecife-tema";
320
- var TEMA_ATRIBUTO = "data-theme";
321
- var TEMA_EVENTO = "arrecife:tema";
322
- var esTema = (valor) => valor === "dark" || valor === "light";
323
- function temaActual() {
366
+ // src/theme/index.ts
367
+ var THEME_KEY = "arrecife-theme";
368
+ var THEME_ATTRIBUTE = "data-theme";
369
+ var THEME_EVENT = "arrecife:theme";
370
+ var isTheme = (value) => value === "dark" || value === "light";
371
+ function currentTheme() {
324
372
  if (typeof document === "undefined") return "dark";
325
- const puesto = document.documentElement.getAttribute(TEMA_ATRIBUTO);
326
- return esTema(puesto) ? puesto : "dark";
373
+ const set = document.documentElement.getAttribute(THEME_ATTRIBUTE);
374
+ return isTheme(set) ? set : "dark";
327
375
  }
328
- function temaGuardado() {
376
+ function storedTheme() {
329
377
  if (typeof localStorage === "undefined") return null;
330
378
  try {
331
- const valor = localStorage.getItem(TEMA_CLAVE);
332
- return esTema(valor) ? valor : null;
379
+ const value = localStorage.getItem(THEME_KEY);
380
+ return isTheme(value) ? value : null;
333
381
  } catch {
334
382
  return null;
335
383
  }
336
384
  }
337
- function temaPreferido() {
338
- const guardado = temaGuardado();
339
- if (guardado) return guardado;
385
+ function preferredTheme({ base } = {}) {
386
+ const stored = storedTheme();
387
+ if (stored) return stored;
388
+ if (base) return base;
340
389
  if (typeof matchMedia === "undefined") return "dark";
341
390
  return matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark";
342
391
  }
343
- function aplicarTema(tema, persistir = true) {
392
+ function applyTheme(theme, persist = true) {
344
393
  if (typeof document === "undefined") return;
345
- document.documentElement.setAttribute(TEMA_ATRIBUTO, tema);
346
- document.documentElement.style.colorScheme = tema;
347
- if (persistir) {
394
+ document.documentElement.setAttribute(THEME_ATTRIBUTE, theme);
395
+ document.documentElement.style.colorScheme = theme;
396
+ if (persist) {
348
397
  try {
349
- localStorage.setItem(TEMA_CLAVE, tema);
398
+ localStorage.setItem(THEME_KEY, theme);
350
399
  } catch {
351
400
  }
352
401
  }
353
- dispatchEvent(new CustomEvent(TEMA_EVENTO, { detail: tema }));
402
+ dispatchEvent(new CustomEvent(THEME_EVENT, { detail: theme }));
354
403
  }
355
- function alternarTema() {
356
- const siguiente = temaActual() === "dark" ? "light" : "dark";
357
- aplicarTema(siguiente);
358
- return siguiente;
404
+ function toggleTheme() {
405
+ const next = currentTheme() === "dark" ? "light" : "dark";
406
+ applyTheme(next);
407
+ return next;
359
408
  }
360
- function escucharTema(alCambiar) {
409
+ function watchTheme(onChange, options = {}) {
361
410
  if (typeof window === "undefined") return () => {
362
411
  };
363
- const propio = (evento) => {
364
- const detalle = evento.detail;
365
- if (esTema(detalle)) alCambiar(detalle);
412
+ const own = (event) => {
413
+ const detail = event.detail;
414
+ if (isTheme(detail)) onChange(detail);
366
415
  };
367
- const otraPestana = (evento) => {
368
- if (evento.key !== TEMA_CLAVE) return;
369
- const tema = esTema(evento.newValue) ? evento.newValue : temaPreferido();
370
- aplicarTema(tema, false);
371
- alCambiar(tema);
416
+ const otherTab = (event) => {
417
+ if (event.key !== THEME_KEY) return;
418
+ const theme = isTheme(event.newValue) ? event.newValue : preferredTheme(options);
419
+ applyTheme(theme, false);
420
+ onChange(theme);
372
421
  };
373
- addEventListener(TEMA_EVENTO, propio);
374
- addEventListener("storage", otraPestana);
422
+ addEventListener(THEME_EVENT, own);
423
+ addEventListener("storage", otherTab);
375
424
  return () => {
376
- removeEventListener(TEMA_EVENTO, propio);
377
- removeEventListener("storage", otraPestana);
425
+ removeEventListener(THEME_EVENT, own);
426
+ removeEventListener("storage", otherTab);
378
427
  };
379
428
  }
380
- var scriptTema = [
381
- "(function () {",
382
- " var CLAVE = " + JSON.stringify(TEMA_CLAVE) + ";",
383
- " function resolver() {",
384
- " try {",
385
- " var guardado = localStorage.getItem(CLAVE);",
386
- ' if (guardado === "dark" || guardado === "light") return guardado;',
387
- " } catch (e) {}",
388
- ' return matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark";',
389
- " }",
390
- " function aplicar() {",
391
- " var tema = resolver();",
392
- " document.documentElement.setAttribute(" + JSON.stringify(TEMA_ATRIBUTO) + ", tema);",
393
- " document.documentElement.style.colorScheme = tema;",
394
- " }",
395
- " aplicar();",
396
- ' document.addEventListener("astro:after-swap", aplicar);',
397
- "})();"
398
- ].join("\n");
399
- var kebab = (nombre) => nombre.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`);
400
- var escalas = Object.keys(typeScale);
401
- var radios = Object.keys(radius);
402
- var espacios = Object.keys(spacing).map(kebab);
403
- var controles = Object.keys(control).map((nombre) => `control-${kebab(nombre)}`);
429
+ function themeScript({ base } = {}) {
430
+ return [
431
+ "(function () {",
432
+ " var KEY = " + JSON.stringify(THEME_KEY) + ";",
433
+ " function resolve() {",
434
+ " try {",
435
+ " var stored = localStorage.getItem(KEY);",
436
+ ' if (stored === "dark" || stored === "light") return stored;',
437
+ " } catch (e) {}",
438
+ base ? " return " + JSON.stringify(base) + ";" : ' return matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark";',
439
+ " }",
440
+ " function apply() {",
441
+ " var theme = resolve();",
442
+ " document.documentElement.setAttribute(" + JSON.stringify(THEME_ATTRIBUTE) + ", theme);",
443
+ " document.documentElement.style.colorScheme = theme;",
444
+ " }",
445
+ " apply();",
446
+ ' document.addEventListener("astro:after-swap", apply);',
447
+ "})();"
448
+ ].join("\n");
449
+ }
450
+ var button = classVarianceAuthority.cva(
451
+ [
452
+ "inline-flex cursor-pointer items-center justify-center gap-step-xs whitespace-nowrap select-none",
453
+ "rounded-control font-sans font-medium",
454
+ "transition-standard",
455
+ "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent",
456
+ "disabled:pointer-events-none disabled:opacity-50",
457
+ "[&_svg]:shrink-0"
458
+ ],
459
+ {
460
+ variants: {
461
+ variant: {
462
+ primary: [
463
+ "bg-accent text-accent-on hover:bg-accent-hover",
464
+ "light:bg-brand-hull light:text-accent-on light:hover:bg-text-secondary"
465
+ ],
466
+ conversion: "bg-warm text-warm-on hover:bg-warm-hover",
467
+ secondary: [
468
+ "border-hairline-hover border bg-transparent text-text-primary",
469
+ "hover:border-accent hover:text-accent"
470
+ ],
471
+ tertiary: [
472
+ "bg-transparent font-mono font-normal text-text-secondary",
473
+ "hover:text-accent hover:underline hover:underline-offset-4"
474
+ ],
475
+ /** Irreversible only. Never for «cancel» on a form. */
476
+ destructive: "bg-danger text-danger-on hover:bg-danger-hover",
477
+ destructiveOutline: [
478
+ "border-danger text-danger border bg-transparent",
479
+ "hover:bg-danger hover:text-danger-on"
480
+ ]
481
+ },
482
+ size: {
483
+ sm: "h-8 px-control-sm text-label",
484
+ md: "h-10 px-control-md text-ui",
485
+ lg: "h-12 px-control-lg text-lead",
486
+ /** A 42×42 square, no text. `aria-label` is mandatory on it. */
487
+ icon: "size-control-icon p-0",
488
+ /** 32×32, for a table row. Same height as `sm`, and same mandatory `aria-label`. */
489
+ "icon-sm": "size-control-icon-sm p-0"
490
+ }
491
+ },
492
+ compoundVariants: [
493
+ /**
494
+ * The tertiary has no box: neither horizontal padding nor control height.
495
+ * It goes here rather than as `px-0` on the variant so it beats the size's
496
+ * `px-*` without depending on the order cva concatenates classes in.
497
+ */
498
+ { variant: "tertiary", size: ["sm", "md", "lg"], class: "h-auto px-0" }
499
+ ],
500
+ defaultVariants: { variant: "primary", size: "md" }
501
+ }
502
+ );
503
+ var badge = classVarianceAuthority.cva(
504
+ ["inline-flex items-center gap-step-xs", "rounded-chip px-step-xs py-0.5", "font-sans text-tag", "transition-standard"],
505
+ {
506
+ variants: {
507
+ variant: {
508
+ neutral: "bg-surface-raised text-text-secondary",
509
+ accent: "bg-accent/8 text-text-primary",
510
+ warm: "bg-warm/8 text-text-primary",
511
+ success: "bg-success/8 text-text-primary",
512
+ warning: "bg-warning/8 text-text-primary",
513
+ error: "bg-error/8 text-text-primary"
514
+ }
515
+ },
516
+ defaultVariants: { variant: "neutral" }
517
+ }
518
+ );
519
+ var category = classVarianceAuthority.cva(
520
+ [
521
+ "inline-flex items-center gap-step-xs",
522
+ "rounded-pill px-step-sm py-0.5",
523
+ "font-mono text-chip",
524
+ "border transition-standard"
525
+ ],
526
+ {
527
+ variants: {
528
+ active: {
529
+ false: "border-warm/28 bg-transparent text-warm",
530
+ true: "border-warm bg-warm text-warm-on"
531
+ }
532
+ },
533
+ defaultVariants: { active: false }
534
+ }
535
+ );
536
+ var metric = ["inline-flex items-center gap-step-xs", "rounded-pill", "font-mono text-chip text-text-muted"];
537
+ var text = classVarianceAuthority.cva("", {
538
+ variants: {
539
+ variant: {
540
+ display: "text-display font-display",
541
+ // Large metrics: 46px of display. It is the only scale designed for a
542
+ // number, which is why its line height is 1.
543
+ stat: "text-stat font-display",
544
+ h1: "text-h1 font-display",
545
+ h2: "text-h2 font-display",
546
+ h3: "text-h3 font-display",
547
+ body: "text-body font-sans",
548
+ // 17px: the deck on interior pages and the large button.
549
+ lead: "text-lead font-sans",
550
+ ui: "text-ui font-sans",
551
+ label: "text-label font-sans",
552
+ // The two badge scales. `tag` is status (sans 12.5/500) and `chip` is
553
+ // category and metric (mono 11.5). Neither transforms the text.
554
+ tag: "text-tag font-sans",
555
+ chip: "text-chip font-mono",
556
+ // Mono with NO transform: dates, paths, versions, file names and the
557
+ // footer signature. It is the step that was missing, and the reason every
558
+ // call site ended up writing `variant="eyebrow" className="normal-case"`.
559
+ meta: "text-meta font-mono",
560
+ // `uppercase` does not fit in a size token: text-transform is not a
561
+ // modifier of --text-*. Which is why the variant sets it, not theme.css.
562
+ eyebrow: "text-eyebrow font-mono uppercase"
563
+ },
564
+ tone: {
565
+ primary: "text-text-primary",
566
+ secondary: "text-text-secondary",
567
+ muted: "text-text-muted",
568
+ accent: "text-accent",
569
+ warm: "text-warm",
570
+ success: "text-success",
571
+ warning: "text-warning",
572
+ error: "text-error"
573
+ }
574
+ },
575
+ defaultVariants: { variant: "body", tone: "primary" }
576
+ });
577
+ var alert = classVarianceAuthority.cva("w-full rounded-card border p-step-md font-sans text-ui", {
578
+ variants: {
579
+ variant: {
580
+ accent: "text-text-secondary",
581
+ success: "text-text-secondary",
582
+ warning: "text-text-secondary",
583
+ error: "text-text-secondary"
584
+ },
585
+ emphasis: {
586
+ subtle: "",
587
+ strong: ""
588
+ }
589
+ },
590
+ compoundVariants: [
591
+ { variant: "accent", emphasis: "subtle", class: "border-accent/22 bg-accent/8" },
592
+ { variant: "success", emphasis: "subtle", class: "border-success/22 bg-success/8" },
593
+ { variant: "warning", emphasis: "subtle", class: "border-warning/22 bg-warning/8" },
594
+ { variant: "error", emphasis: "subtle", class: "border-error/22 bg-error/8" },
595
+ { variant: "accent", emphasis: "strong", class: "border-accent bg-accent/10" },
596
+ { variant: "success", emphasis: "strong", class: "border-success bg-success/10" },
597
+ { variant: "warning", emphasis: "strong", class: "border-warning bg-warning/10" },
598
+ { variant: "error", emphasis: "strong", class: "border-error bg-error/10" }
599
+ ],
600
+ defaultVariants: { variant: "accent", emphasis: "subtle" }
601
+ });
602
+ var avatar = classVarianceAuthority.cva(
603
+ "relative flex shrink-0 overflow-hidden rounded-pill bg-surface-raised border border-hairline",
604
+ {
605
+ variants: {
606
+ size: {
607
+ sm: "size-8",
608
+ md: "size-10",
609
+ lg: "size-14",
610
+ xl: "size-24"
611
+ }
612
+ },
613
+ defaultVariants: { size: "md" }
614
+ }
615
+ );
616
+
617
+ // src/variants/card.ts
618
+ var CARD_SURFACE = "rounded-card border-hairline bg-surface border";
619
+ var CARD_HOVER = "transition-standard hover:border-hairline-hover";
620
+ var CARD = [
621
+ "group block cursor-pointer",
622
+ CARD_SURFACE,
623
+ CARD_HOVER,
624
+ "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent"
625
+ ];
626
+ var kebab = (name) => name.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`);
627
+ var scales = Object.keys(typeScale);
628
+ var radii = Object.keys(radius);
629
+ var namespaces = Object.keys(spacing).map(kebab);
630
+ var controls = Object.keys(control).map((name) => `control-${kebab(name)}`);
404
631
  var twMerge = tailwindMerge.extendTailwindMerge({
405
632
  extend: {
406
633
  classGroups: {
407
- "font-size": [{ text: escalas }],
408
- rounded: [{ rounded: radios }],
409
- p: [{ p: espacios }],
410
- px: [{ px: [...espacios, ...controles] }],
411
- py: [{ py: espacios }],
412
- gap: [{ gap: espacios }],
413
- size: [{ size: controles }]
634
+ "font-size": [{ text: scales }],
635
+ rounded: [{ rounded: radii }],
636
+ p: [{ p: namespaces }],
637
+ px: [{ px: [...namespaces, ...controls] }],
638
+ py: [{ py: namespaces }],
639
+ gap: [{ gap: namespaces }],
640
+ size: [{ size: controls }]
414
641
  }
415
642
  }
416
643
  });
@@ -469,16 +696,16 @@ var Spinner = (props) => /* @__PURE__ */ jsxRuntime.jsxs(
469
696
  ]
470
697
  }
471
698
  );
472
- var Camara = (props) => /* @__PURE__ */ jsxRuntime.jsxs(Glyph, { ...props, children: [
699
+ var Camera = (props) => /* @__PURE__ */ jsxRuntime.jsxs(Glyph, { ...props, children: [
473
700
  /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M2 5.5h2.6l1-1.5h4.8l1 1.5H14v7.5H2z" }),
474
701
  /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "8", cy: "9", r: "2.4" })
475
702
  ] });
476
- var Sol = (props) => /* @__PURE__ */ jsxRuntime.jsxs(Glyph, { ...props, children: [
703
+ var Sun = (props) => /* @__PURE__ */ jsxRuntime.jsxs(Glyph, { ...props, children: [
477
704
  /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "8", cy: "8", r: "3" }),
478
705
  /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 1.5v1.2M8 13.3v1.2M14.5 8h-1.2M2.7 8H1.5M12.6 3.4l-.85.85M4.25 11.75l-.85.85M12.6 12.6l-.85-.85M4.25 4.25l-.85-.85" })
479
706
  ] });
480
- var Luna = (props) => /* @__PURE__ */ jsxRuntime.jsx(Glyph, { ...props, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M13.5 9.4A5.8 5.8 0 0 1 6.6 2.5a5.8 5.8 0 1 0 6.9 6.9Z" }) });
481
- function GlifoMedia({ children, ...props }) {
707
+ var Moon = (props) => /* @__PURE__ */ jsxRuntime.jsx(Glyph, { ...props, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M13.5 9.4A5.8 5.8 0 0 1 6.6 2.5a5.8 5.8 0 1 0 6.9 6.9Z" }) });
708
+ function MediaGlyph({ children, ...props }) {
482
709
  return /* @__PURE__ */ jsxRuntime.jsx(
483
710
  "svg",
484
711
  {
@@ -492,10 +719,10 @@ function GlifoMedia({ children, ...props }) {
492
719
  }
493
720
  );
494
721
  }
495
- var Play = (props) => /* @__PURE__ */ jsxRuntime.jsx(GlifoMedia, { fill: "currentColor", ...props, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 5v14l11-7z" }) });
496
- var Pause = (props) => /* @__PURE__ */ jsxRuntime.jsx(GlifoMedia, { fill: "currentColor", ...props, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M6 4h4v16H6V4zm8 0h4v16h-4V4z" }) });
722
+ var Play = (props) => /* @__PURE__ */ jsxRuntime.jsx(MediaGlyph, { fill: "currentColor", ...props, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 5v14l11-7z" }) });
723
+ var Pause = (props) => /* @__PURE__ */ jsxRuntime.jsx(MediaGlyph, { fill: "currentColor", ...props, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M6 4h4v16H6V4zm8 0h4v16h-4V4z" }) });
497
724
  var Retry = (props) => /* @__PURE__ */ jsxRuntime.jsx(
498
- GlifoMedia,
725
+ MediaGlyph,
499
726
  {
500
727
  stroke: "currentColor",
501
728
  strokeWidth: 2,
@@ -505,7 +732,7 @@ var Retry = (props) => /* @__PURE__ */ jsxRuntime.jsx(
505
732
  children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" })
506
733
  }
507
734
  );
508
- var GoBackSeconds = (props) => /* @__PURE__ */ jsxRuntime.jsxs(GlifoMedia, { stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", ...props, children: [
735
+ var GoBackSeconds = (props) => /* @__PURE__ */ jsxRuntime.jsxs(MediaGlyph, { stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", ...props, children: [
509
736
  /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M16.5 8.5H13.8604C13.6452 8.5 13.4541 8.63772 13.386 8.84189L12.7194 10.8419C12.6114 11.1657 12.8524 11.5 13.1937 11.5H14.5C15.6046 11.5 16.5 12.3954 16.5 13.5C16.5 14.6046 15.6046 15.5 14.5 15.5H12.5" }),
510
737
  /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M7.5 10.5L10 8.5V15.5", strokeLinejoin: "round" }),
511
738
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -516,7 +743,7 @@ var GoBackSeconds = (props) => /* @__PURE__ */ jsxRuntime.jsxs(GlifoMedia, { str
516
743
  }
517
744
  )
518
745
  ] });
519
- var AdvanceSeconds = (props) => /* @__PURE__ */ jsxRuntime.jsxs(GlifoMedia, { stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", ...props, children: [
746
+ var AdvanceSeconds = (props) => /* @__PURE__ */ jsxRuntime.jsxs(MediaGlyph, { stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", ...props, children: [
520
747
  /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M16.5 8.5H13.8604C13.6452 8.5 13.4541 8.63772 13.386 8.84189L12.7194 10.8419C12.6114 11.1657 12.8524 11.5 13.1937 11.5H14.5C15.6046 11.5 16.5 12.3954 16.5 13.5C16.5 14.6046 15.6046 15.5 14.5 15.5H12.5" }),
521
748
  /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M7.5 10.5L10 8.5V15.5", strokeLinejoin: "round" }),
522
749
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -527,8 +754,8 @@ var AdvanceSeconds = (props) => /* @__PURE__ */ jsxRuntime.jsxs(GlifoMedia, { st
527
754
  }
528
755
  )
529
756
  ] });
530
- var VolumeOn = (props) => /* @__PURE__ */ jsxRuntime.jsx(GlifoMedia, { fill: "currentColor", ...props, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z" }) });
531
- var VolumeMuted = (props) => /* @__PURE__ */ jsxRuntime.jsx(GlifoMedia, { fill: "currentColor", ...props, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M16.5 12c0-1.77-1.02-3.29-2.5-4.03v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51C20.63 14.91 21 13.5 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06c1.38-.31 2.63-.95 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4L9.91 6.09 12 8.18V4z" }) });
757
+ var VolumeOn = (props) => /* @__PURE__ */ jsxRuntime.jsx(MediaGlyph, { fill: "currentColor", ...props, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z" }) });
758
+ var VolumeMuted = (props) => /* @__PURE__ */ jsxRuntime.jsx(MediaGlyph, { fill: "currentColor", ...props, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M16.5 12c0-1.77-1.02-3.29-2.5-4.03v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51C20.63 14.91 21 13.5 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06c1.38-.31 2.63-.95 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4L9.91 6.09 12 8.18V4z" }) });
532
759
  function Accordion({ className, ...props }) {
533
760
  return /* @__PURE__ */ jsxRuntime.jsx(
534
761
  AccordionPrimitive__namespace.Root,
@@ -550,8 +777,8 @@ function AccordionTrigger({
550
777
  headingLevel = 3,
551
778
  ...props
552
779
  }) {
553
- const Encabezado = `h${headingLevel}`;
554
- return /* @__PURE__ */ jsxRuntime.jsx(AccordionPrimitive__namespace.Header, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(Encabezado, { children: /* @__PURE__ */ jsxRuntime.jsxs(
780
+ const Heading = `h${headingLevel}`;
781
+ return /* @__PURE__ */ jsxRuntime.jsx(AccordionPrimitive__namespace.Header, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(Heading, { children: /* @__PURE__ */ jsxRuntime.jsxs(
555
782
  AccordionPrimitive__namespace.Trigger,
556
783
  {
557
784
  className: cn(
@@ -582,45 +809,20 @@ function AccordionContent({
582
809
  {
583
810
  className: cn(
584
811
  "overflow-hidden",
585
- "motion-safe:data-[state=open]:desplegar motion-safe:data-[state=closed]:replegar"
812
+ "motion-safe:data-[state=open]:expand motion-safe:data-[state=closed]:collapse"
586
813
  ),
587
814
  ...props,
588
815
  children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("pb-step-md font-sans text-ui text-text-secondary max-w-measure", className), children })
589
816
  }
590
817
  );
591
818
  }
592
- var alert = classVarianceAuthority.cva("w-full rounded-card border p-step-md font-sans text-ui", {
593
- variants: {
594
- variant: {
595
- accent: "text-text-secondary",
596
- success: "text-text-secondary",
597
- warning: "text-text-secondary",
598
- error: "text-text-secondary"
599
- },
600
- enfasis: {
601
- sutil: "",
602
- fuerte: ""
603
- }
604
- },
605
- compoundVariants: [
606
- { variant: "accent", enfasis: "sutil", class: "border-accent/22 bg-accent/8" },
607
- { variant: "success", enfasis: "sutil", class: "border-success/22 bg-success/8" },
608
- { variant: "warning", enfasis: "sutil", class: "border-warning/22 bg-warning/8" },
609
- { variant: "error", enfasis: "sutil", class: "border-error/22 bg-error/8" },
610
- { variant: "accent", enfasis: "fuerte", class: "border-accent bg-accent/10" },
611
- { variant: "success", enfasis: "fuerte", class: "border-success bg-success/10" },
612
- { variant: "warning", enfasis: "fuerte", class: "border-warning bg-warning/10" },
613
- { variant: "error", enfasis: "fuerte", class: "border-error bg-error/10" }
614
- ],
615
- defaultVariants: { variant: "accent", enfasis: "sutil" }
616
- });
617
- var glifoColor = {
819
+ var colorGlyph = {
618
820
  accent: "text-accent",
619
821
  success: "text-success",
620
822
  warning: "text-warning",
621
823
  error: "text-error"
622
824
  };
623
- var GLIFO = {
825
+ var GLYPH = {
624
826
  accent: "\u2726",
625
827
  success: "\u2713",
626
828
  warning: "!",
@@ -629,26 +831,26 @@ var GLIFO = {
629
831
  function Alert({
630
832
  className,
631
833
  variant,
632
- enfasis,
834
+ emphasis,
633
835
  title,
634
836
  icon,
635
837
  children,
636
838
  ...props
637
839
  }) {
638
- const tono = variant ?? "accent";
840
+ const tone = variant ?? "accent";
639
841
  return /* @__PURE__ */ jsxRuntime.jsx(
640
842
  "div",
641
843
  {
642
- role: tono === "error" ? "alert" : "status",
643
- className: cn(alert({ variant, enfasis }), className),
844
+ role: tone === "error" ? "alert" : "status",
845
+ className: cn(alert({ variant, emphasis }), className),
644
846
  ...props,
645
847
  children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-sm flex items-start", children: [
646
848
  /* @__PURE__ */ jsxRuntime.jsx(
647
849
  "span",
648
850
  {
649
851
  "aria-hidden": "true",
650
- className: cn("font-mono text-ui leading-normal select-none", glifoColor[tono]),
651
- children: icon ?? GLIFO[tono]
852
+ className: cn("font-mono text-ui leading-normal select-none", colorGlyph[tone]),
853
+ children: icon ?? GLYPH[tone]
652
854
  }
653
855
  ),
654
856
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
@@ -659,79 +861,6 @@ function Alert({
659
861
  }
660
862
  );
661
863
  }
662
- var button = classVarianceAuthority.cva(
663
- [
664
- "inline-flex cursor-pointer items-center justify-center gap-step-xs whitespace-nowrap select-none",
665
- "rounded-control font-sans font-medium",
666
- "transition-standard",
667
- "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent",
668
- "disabled:pointer-events-none disabled:opacity-50",
669
- "[&_svg]:shrink-0"
670
- ],
671
- {
672
- variants: {
673
- variant: {
674
- primary: [
675
- "bg-accent text-accent-on hover:bg-accent-hover",
676
- "light:bg-brand-hull light:text-accent-on light:hover:bg-text-secondary"
677
- ],
678
- conversion: "bg-warm text-warm-on hover:bg-warm-hover",
679
- secondary: [
680
- "border-hairline-hover border bg-transparent text-text-primary",
681
- "hover:border-accent hover:text-accent"
682
- ],
683
- tertiary: [
684
- "bg-transparent font-mono font-normal text-text-secondary",
685
- "hover:text-accent hover:underline hover:underline-offset-4"
686
- ]
687
- },
688
- size: {
689
- sm: "h-8 px-control-sm text-label",
690
- md: "h-10 px-control-md text-ui",
691
- lg: "h-12 px-control-lg text-lead",
692
- /** Cuadrado 42×42, sin texto. Lleva `aria-label` obligatorio. */
693
- icon: "size-control-icon p-0"
694
- }
695
- },
696
- compoundVariants: [
697
- /**
698
- * El terciario no tiene caja: ni padding horizontal ni alto de control.
699
- * Va aquí y no como `px-0` en la variante para que gane al `px-*` del
700
- * tamaño sin depender del orden en que cva concatena las clases.
701
- */
702
- { variant: "tertiary", size: ["sm", "md", "lg"], class: "h-auto px-0" }
703
- ],
704
- defaultVariants: { variant: "primary", size: "md" }
705
- }
706
- );
707
- function Button({
708
- className,
709
- variant,
710
- size: size2,
711
- asChild = false,
712
- loading = false,
713
- icon,
714
- children,
715
- disabled,
716
- ...props
717
- }) {
718
- if (asChild) {
719
- return /* @__PURE__ */ jsxRuntime.jsx(reactSlot.Slot, { className: cn(button({ variant, size: size2 }), className), ...props, children });
720
- }
721
- return /* @__PURE__ */ jsxRuntime.jsxs(
722
- "button",
723
- {
724
- className: cn(button({ variant, size: size2 }), className),
725
- disabled: disabled === true || loading,
726
- "aria-busy": loading || void 0,
727
- ...props,
728
- children: [
729
- loading ? /* @__PURE__ */ jsxRuntime.jsx(Spinner, {}) : icon,
730
- children
731
- ]
732
- }
733
- );
734
- }
735
864
  var AlertDialog = AlertDialogPrimitive__namespace.Root;
736
865
  var AlertDialogTrigger = AlertDialogPrimitive__namespace.Trigger;
737
866
  function AlertDialogOverlay({
@@ -829,20 +958,6 @@ function AlertDialogAction({
829
958
  }
830
959
  );
831
960
  }
832
- var avatar = classVarianceAuthority.cva(
833
- "relative flex shrink-0 overflow-hidden rounded-pill bg-surface-raised border border-hairline",
834
- {
835
- variants: {
836
- size: {
837
- sm: "size-8",
838
- md: "size-10",
839
- lg: "size-14",
840
- xl: "size-24"
841
- }
842
- },
843
- defaultVariants: { size: "md" }
844
- }
845
- );
846
961
  function Avatar({ className, size: size2, ...props }) {
847
962
  return /* @__PURE__ */ jsxRuntime.jsx(AvatarPrimitive__namespace.Root, { className: cn(avatar({ size: size2 }), className), ...props });
848
963
  }
@@ -879,22 +994,22 @@ function AvatarUpload({
879
994
  ...props
880
995
  }) {
881
996
  const id = react.useId();
882
- const [previa, setPrevia] = react.useState(null);
997
+ const [preview, setPreview] = react.useState(null);
883
998
  react.useEffect(() => {
884
- if (!previa) return;
885
- return () => URL.revokeObjectURL(previa);
886
- }, [previa]);
887
- function elegir(evento) {
888
- const archivo = evento.target.files?.[0];
889
- if (!archivo) return;
890
- setPrevia(URL.createObjectURL(archivo));
891
- onSelectFile?.(archivo);
892
- evento.target.value = "";
999
+ if (!preview) return;
1000
+ return () => URL.revokeObjectURL(preview);
1001
+ }, [preview]);
1002
+ function pick(event) {
1003
+ const file = event.target.files?.[0];
1004
+ if (!file) return;
1005
+ setPreview(URL.createObjectURL(file));
1006
+ onSelectFile?.(file);
1007
+ event.target.value = "";
893
1008
  }
894
- const imagen = previa ?? src;
1009
+ const image = preview ?? src;
895
1010
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("relative inline-flex", className), ...props, children: [
896
1011
  /* @__PURE__ */ jsxRuntime.jsxs(Avatar, { size: size2, children: [
897
- imagen ? /* @__PURE__ */ jsxRuntime.jsx(AvatarImage, { src: imagen, alt: "" }) : null,
1012
+ image ? /* @__PURE__ */ jsxRuntime.jsx(AvatarImage, { src: image, alt: "" }) : null,
898
1013
  /* @__PURE__ */ jsxRuntime.jsx(AvatarFallback, { children: fallback })
899
1014
  ] }),
900
1015
  /* @__PURE__ */ jsxRuntime.jsxs(
@@ -905,13 +1020,13 @@ function AvatarUpload({
905
1020
  "rounded-pill absolute -right-1 -bottom-1 flex size-6 cursor-pointer items-center justify-center",
906
1021
  "border-hairline bg-surface-raised text-text-secondary border",
907
1022
  "transition-standard hover:text-accent hover:border-accent",
908
- // El foco vive en el input, que está oculto: sin esto, tabular hasta
909
- // el control no se ve por ninguna parte.
1023
+ // Focus lives on the input, which is hidden: without this, tabbing to
1024
+ // the control shows up nowhere.
910
1025
  "has-[:focus-visible]:outline-2 has-[:focus-visible]:outline-offset-2 has-[:focus-visible]:outline-accent",
911
1026
  disabled && "pointer-events-none opacity-50"
912
1027
  ),
913
1028
  children: [
914
- /* @__PURE__ */ jsxRuntime.jsx(Camara, { "aria-hidden": "true" }),
1029
+ /* @__PURE__ */ jsxRuntime.jsx(Camera, { "aria-hidden": "true" }),
915
1030
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: label }),
916
1031
  /* @__PURE__ */ jsxRuntime.jsx(
917
1032
  "input",
@@ -920,7 +1035,7 @@ function AvatarUpload({
920
1035
  type: "file",
921
1036
  accept,
922
1037
  disabled,
923
- onChange: elegir,
1038
+ onChange: pick,
924
1039
  className: "sr-only"
925
1040
  }
926
1041
  )
@@ -929,62 +1044,56 @@ function AvatarUpload({
929
1044
  )
930
1045
  ] });
931
1046
  }
932
- var badge = classVarianceAuthority.cva(
933
- ["inline-flex items-center gap-step-xs", "rounded-chip px-step-xs py-0.5", "font-sans text-tag", "transition-standard"],
934
- {
935
- variants: {
936
- variant: {
937
- neutral: "bg-surface-raised text-text-secondary",
938
- accent: "bg-accent/8 text-text-primary",
939
- warm: "bg-warm/8 text-text-primary",
940
- success: "bg-success/8 text-text-primary",
941
- warning: "bg-warning/8 text-text-primary",
942
- error: "bg-error/8 text-text-primary"
943
- }
944
- },
945
- defaultVariants: { variant: "neutral" }
946
- }
947
- );
948
1047
  function Badge({ className, variant, ...props }) {
949
1048
  return /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn(badge({ variant }), className), ...props });
950
1049
  }
951
- var categoria = classVarianceAuthority.cva(
952
- [
953
- "inline-flex items-center gap-step-xs",
954
- "rounded-pill px-step-sm py-0.5",
955
- "font-mono text-chip",
956
- "border transition-standard"
957
- ],
958
- {
959
- variants: {
960
- active: {
961
- false: "border-warm/28 bg-transparent text-warm",
962
- true: "border-warm bg-warm text-warm-on"
963
- }
964
- },
965
- defaultVariants: { active: false }
966
- }
967
- );
968
1050
  function CategoryBadge({ className, active = false, ...props }) {
969
1051
  return /* @__PURE__ */ jsxRuntime.jsx(
970
1052
  "span",
971
1053
  {
972
1054
  "data-active": active || void 0,
973
- className: cn(categoria({ active }), className),
1055
+ className: cn(category({ active }), className),
974
1056
  ...props
975
1057
  }
976
1058
  );
977
1059
  }
978
- var metrica = ["inline-flex items-center gap-step-xs", "rounded-pill", "font-mono text-chip text-text-muted"];
979
1060
  function MetricBadge({ className, boxed = false, ...props }) {
980
1061
  return /* @__PURE__ */ jsxRuntime.jsx(
981
1062
  "span",
982
1063
  {
983
- className: cn(metrica, boxed && "border-hairline px-step-sm py-0.5 border", className),
1064
+ className: cn(metric, boxed && "border-hairline px-step-sm py-0.5 border", className),
984
1065
  ...props
985
1066
  }
986
1067
  );
987
1068
  }
1069
+ function Button({
1070
+ className,
1071
+ variant,
1072
+ size: size2,
1073
+ asChild = false,
1074
+ loading = false,
1075
+ icon,
1076
+ children,
1077
+ disabled,
1078
+ ...props
1079
+ }) {
1080
+ if (asChild) {
1081
+ return /* @__PURE__ */ jsxRuntime.jsx(reactSlot.Slot, { className: cn(button({ variant, size: size2 }), className), ...props, children });
1082
+ }
1083
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1084
+ "button",
1085
+ {
1086
+ className: cn(button({ variant, size: size2 }), className),
1087
+ disabled: disabled === true || loading,
1088
+ "aria-busy": loading || void 0,
1089
+ ...props,
1090
+ children: [
1091
+ loading ? /* @__PURE__ */ jsxRuntime.jsx(Spinner, {}) : icon,
1092
+ children
1093
+ ]
1094
+ }
1095
+ );
1096
+ }
988
1097
  function Calendar({
989
1098
  className,
990
1099
  classNames,
@@ -992,12 +1101,12 @@ function Calendar({
992
1101
  fullWidth = false,
993
1102
  ...props
994
1103
  }) {
995
- const dia = [
1104
+ const day = [
996
1105
  "relative p-0 text-center",
997
1106
  fullWidth ? "h-9 flex-1" : "size-9",
998
1107
  "text-ui font-sans text-text-primary"
999
1108
  ].join(" ");
1000
- const botonDia = [
1109
+ const dayButton = [
1001
1110
  fullWidth ? "h-9 w-full" : "size-9",
1002
1111
  "rounded-chip inline-flex items-center justify-center",
1003
1112
  "transition-standard cursor-pointer",
@@ -1005,7 +1114,7 @@ function Calendar({
1005
1114
  "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent",
1006
1115
  "disabled:pointer-events-none disabled:opacity-50"
1007
1116
  ].join(" ");
1008
- const navegacion = [
1117
+ const navigation = [
1009
1118
  "size-8 rounded-chip inline-flex items-center justify-center",
1010
1119
  "text-text-secondary transition-standard cursor-pointer",
1011
1120
  "hover:bg-surface-raised hover:text-text-primary",
@@ -1019,32 +1128,32 @@ function Calendar({
1019
1128
  showOutsideDays,
1020
1129
  className: cn("font-sans", className),
1021
1130
  classNames: {
1022
- // `relative` obligatorio: la navegación se posiciona en absoluto contra
1023
- // la raíz, y sin esto se ancla al primer ancestro posicionado —o al
1024
- // viewport— y las flechas acaban en los bordes de la página.
1131
+ // `relative` is mandatory: the navigation is absolutely positioned
1132
+ // against the root, and without this it anchors to the first positioned
1133
+ // ancestor — or to the viewport — and the arrows end up at the page edges.
1025
1134
  root: cn("relative", fullWidth ? "w-full" : "w-fit"),
1026
1135
  months: cn("gap-step-md flex flex-col sm:flex-row", fullWidth && "w-full"),
1027
1136
  month: cn("gap-step-sm flex flex-col", fullWidth && "min-w-0 flex-1"),
1028
1137
  month_caption: "h-8 flex items-center justify-center",
1029
1138
  caption_label: "text-ui font-sans font-medium text-text-primary capitalize",
1030
1139
  nav: "absolute inset-x-0 top-0 flex items-center justify-between",
1031
- button_previous: navegacion,
1032
- button_next: navegacion,
1140
+ button_previous: navigation,
1141
+ button_next: navigation,
1033
1142
  month_grid: "w-full border-collapse",
1034
1143
  weekdays: cn("flex", fullWidth && "w-full"),
1035
- // `textMuted` no va sobre `surfaceRaised` —4.07:1—, y un calendario dentro de
1036
- // un Popover vive justo ahí.
1144
+ // `textMuted` does not go over `surfaceRaised` — 4.07:1 — and a calendar
1145
+ // inside a Popover lives exactly there.
1037
1146
  weekday: cn(
1038
1147
  "h-9 text-eyebrow font-mono text-text-secondary uppercase flex items-center justify-center",
1039
1148
  fullWidth ? "flex-1" : "w-9"
1040
1149
  ),
1041
1150
  week: "flex w-full",
1042
- day: dia,
1043
- day_button: botonDia,
1151
+ day,
1152
+ day_button: dayButton,
1044
1153
  today: "text-accent font-medium",
1045
- // Los días de los meses vecinos se atenúan, pero no por debajo de AA:
1046
- // `textSecondary` al 85 % da 5.09:1 en el peor caso, que es sobre
1047
- // `surfaceRaised`. Al 75 % ya no pasa en modo claro.
1154
+ // Days from neighbouring months are dimmed, but not below AA:
1155
+ // `textSecondary` at 85 % gives 5.09:1 in the worst case, which is over
1156
+ // `surfaceRaised`. At 75 % it no longer passes in light mode.
1048
1157
  outside: "text-text-secondary opacity-85",
1049
1158
  disabled: "opacity-50",
1050
1159
  hidden: "invisible",
@@ -1062,7 +1171,7 @@ function Calendar({
1062
1171
  }
1063
1172
  );
1064
1173
  }
1065
- var ETIQUETA = {
1174
+ var LABEL = {
1066
1175
  display: "h1",
1067
1176
  stat: "p",
1068
1177
  h1: "h1",
@@ -1077,46 +1186,6 @@ var ETIQUETA = {
1077
1186
  chip: "span",
1078
1187
  eyebrow: "p"
1079
1188
  };
1080
- var texto = classVarianceAuthority.cva("", {
1081
- variants: {
1082
- variant: {
1083
- display: "text-display font-display",
1084
- // Métricas grandes: 46px de display. Es la única escala pensada para un
1085
- // número, y por eso el interlineado es 1.
1086
- stat: "text-stat font-display",
1087
- h1: "text-h1 font-display",
1088
- h2: "text-h2 font-display",
1089
- h3: "text-h3 font-display",
1090
- body: "text-body font-sans",
1091
- // 17px: la bajada de las páginas internas y el botón grande.
1092
- lead: "text-lead font-sans",
1093
- ui: "text-ui font-sans",
1094
- label: "text-label font-sans",
1095
- // Las dos escalas de etiqueta. `tag` es el estado (sans 12.5/500) y `chip`
1096
- // la categoría y la métrica (mono 11.5). Ninguna transforma el texto.
1097
- tag: "text-tag font-sans",
1098
- chip: "text-chip font-mono",
1099
- // Mono SIN transformar: fechas, rutas, versiones, nombres de archivo y la
1100
- // firma del footer. Es el escalón que faltaba, y por el que cada sitio de
1101
- // uso terminaba escribiendo `variant="eyebrow" className="normal-case"`.
1102
- meta: "text-meta font-mono",
1103
- // `uppercase` no cabe en un token de tamaño: text-transform no es un
1104
- // modificador de --text-*. Por eso lo pone la variante y no theme.css.
1105
- eyebrow: "text-eyebrow font-mono uppercase"
1106
- },
1107
- tone: {
1108
- primary: "text-text-primary",
1109
- secondary: "text-text-secondary",
1110
- muted: "text-text-muted",
1111
- accent: "text-accent",
1112
- warm: "text-warm",
1113
- success: "text-success",
1114
- warning: "text-warning",
1115
- error: "text-error"
1116
- }
1117
- },
1118
- defaultVariants: { variant: "body", tone: "primary" }
1119
- });
1120
1189
  function Text({
1121
1190
  className,
1122
1191
  variant,
@@ -1127,19 +1196,17 @@ function Text({
1127
1196
  children,
1128
1197
  ...props
1129
1198
  }) {
1130
- const escala = variant ?? "body";
1131
- const limitar = measure ?? escala === "body";
1132
- const clases = cn(texto({ variant, tone }), limitar && "max-w-measure", className);
1199
+ const scale = variant ?? "body";
1200
+ const clamp = measure ?? scale === "body";
1201
+ const classes = cn(text({ variant, tone }), clamp && "max-w-measure", className);
1133
1202
  if (asChild) {
1134
- return /* @__PURE__ */ jsxRuntime.jsx(reactSlot.Slot, { className: clases, ...props, children });
1203
+ return /* @__PURE__ */ jsxRuntime.jsx(reactSlot.Slot, { className: classes, ...props, children });
1135
1204
  }
1136
- const Etiqueta = as ?? ETIQUETA[escala];
1137
- return /* @__PURE__ */ jsxRuntime.jsx(Etiqueta, { className: clases, ...props, children });
1205
+ const Label4 = as ?? LABEL[scale];
1206
+ return /* @__PURE__ */ jsxRuntime.jsx(Label4, { className: classes, ...props, children });
1138
1207
  }
1139
- var SUPERFICIE_TARJETA = "rounded-card border-hairline bg-surface border";
1140
- var HOVER_TARJETA = "transition-standard hover:border-hairline-hover";
1141
1208
  function Card({ className, ...props }) {
1142
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn(SUPERFICIE_TARJETA, className), ...props });
1209
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn(CARD_SURFACE, className), ...props });
1143
1210
  }
1144
1211
  function CardHeader({ className, ...props }) {
1145
1212
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("gap-step-xs p-step-lg flex flex-col", className), ...props });
@@ -1216,7 +1283,7 @@ function DateField({
1216
1283
  "focus-visible:border-accent focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent",
1217
1284
  "disabled:pointer-events-none disabled:opacity-50",
1218
1285
  "data-[invalid]:border-error data-[invalid]:focus-visible:outline-error",
1219
- // El indicador nativo hereda el esquema del tema activo.
1286
+ // The native indicator inherits the active theme's scheme.
1220
1287
  "scheme-light dark:scheme-dark [&::-webkit-calendar-picker-indicator]:cursor-pointer",
1221
1288
  className
1222
1289
  ),
@@ -1481,7 +1548,7 @@ function PaginationPrevious({ className, ...props }) {
1481
1548
  ] });
1482
1549
  }
1483
1550
  function PaginationNext({ className, ...props }) {
1484
- return /* @__PURE__ */ jsxRuntime.jsxs(PaginationLink, { "aria-label": "P\xE1gina siguiente", className: cn("gap-step-xs", className), ...props, children: [
1551
+ return /* @__PURE__ */ jsxRuntime.jsxs(PaginationLink, { "aria-label": "P\xE1gina next", className: cn("gap-step-xs", className), ...props, children: [
1485
1552
  "Siguiente",
1486
1553
  /* @__PURE__ */ jsxRuntime.jsx(ChevronRight, {})
1487
1554
  ] });
@@ -1692,23 +1759,23 @@ var panel2 = classVarianceAuthority.cva(
1692
1759
  side: {
1693
1760
  right: [
1694
1761
  "inset-y-0 right-0 h-full w-3/4 max-w-content border-l",
1695
- "motion-safe:data-[state=open]:deslizar-entra-derecha",
1696
- "motion-safe:data-[state=closed]:deslizar-sale-derecha"
1762
+ "motion-safe:data-[state=open]:slide-in-right",
1763
+ "motion-safe:data-[state=closed]:slide-out-right"
1697
1764
  ],
1698
1765
  left: [
1699
1766
  "inset-y-0 left-0 h-full w-3/4 max-w-content border-r",
1700
- "motion-safe:data-[state=open]:deslizar-entra-izquierda",
1701
- "motion-safe:data-[state=closed]:deslizar-sale-izquierda"
1767
+ "motion-safe:data-[state=open]:slide-in-left",
1768
+ "motion-safe:data-[state=closed]:slide-out-left"
1702
1769
  ],
1703
1770
  top: [
1704
1771
  "inset-x-0 top-0 border-b",
1705
- "motion-safe:data-[state=open]:deslizar-entra-arriba",
1706
- "motion-safe:data-[state=closed]:deslizar-sale-arriba"
1772
+ "motion-safe:data-[state=open]:slide-in-top",
1773
+ "motion-safe:data-[state=closed]:slide-out-top"
1707
1774
  ],
1708
1775
  bottom: [
1709
1776
  "inset-x-0 bottom-0 border-t",
1710
- "motion-safe:data-[state=open]:deslizar-entra-abajo",
1711
- "motion-safe:data-[state=closed]:deslizar-sale-abajo"
1777
+ "motion-safe:data-[state=open]:slide-in-bottom",
1778
+ "motion-safe:data-[state=closed]:slide-out-bottom"
1712
1779
  ]
1713
1780
  }
1714
1781
  },
@@ -2020,62 +2087,62 @@ function ToastDescription({
2020
2087
  }) {
2021
2088
  return /* @__PURE__ */ jsxRuntime.jsx(ToastPrimitive__namespace.Description, { className: cn("max-w-measure", className), ...props });
2022
2089
  }
2023
- var avisos = [];
2024
- var suscriptores = /* @__PURE__ */ new Set();
2025
- var contador = 0;
2026
- function emitir(siguientes) {
2027
- avisos = siguientes;
2028
- for (const avisar of suscriptores) avisar();
2090
+ var warnings = [];
2091
+ var subscribers = /* @__PURE__ */ new Set();
2092
+ var counter = 0;
2093
+ function emit(next) {
2094
+ warnings = next;
2095
+ for (const warn of subscribers) warn();
2029
2096
  }
2030
- var suscribirse = (avisar) => {
2031
- suscriptores.add(avisar);
2097
+ var subscribe = (warn) => {
2098
+ subscribers.add(warn);
2032
2099
  return () => {
2033
- suscriptores.delete(avisar);
2100
+ subscribers.delete(warn);
2034
2101
  };
2035
2102
  };
2036
- var leer = () => avisos;
2037
- var VACIO = [];
2038
- var enServidor = () => VACIO;
2039
- function crear(mensaje, opciones = {}) {
2040
- contador += 1;
2041
- const id = `arrecife-toast-${contador}`;
2042
- const { description, ...resto } = opciones;
2043
- emitir([
2044
- ...avisos,
2045
- {
2046
- ...resto,
2047
- // El primer argumento es la línea principal. Si además llega `title`, el
2048
- // mensaje pasa a ser la descripción: así `toast('Guardado')` y
2049
- // `toast('No se pudo guardar', { title: 'Error' })` se leen igual de bien.
2050
- ...opciones.title ? { description: description ?? mensaje } : { title: mensaje, ...description === void 0 ? {} : { description } },
2103
+ var read = () => warnings;
2104
+ var EMPTY = [];
2105
+ var onServer = () => EMPTY;
2106
+ function create(message, options = {}) {
2107
+ counter += 1;
2108
+ const id = `arrecife-toast-${counter}`;
2109
+ const { description, ...rest } = options;
2110
+ emit([
2111
+ ...warnings,
2112
+ {
2113
+ ...rest,
2114
+ // The first argument is the main line. If a `title` also arrives, the
2115
+ // message becomes the description: that way `toast('Guardado')` and
2116
+ // `toast('No se pudo guardar', { title: 'Error' })` both read well.
2117
+ ...options.title ? { description: description ?? message } : { title: message, ...description === void 0 ? {} : { description } },
2051
2118
  id,
2052
- abierto: true
2119
+ open: true
2053
2120
  }
2054
2121
  ]);
2055
2122
  return id;
2056
2123
  }
2057
- function descartar(id) {
2058
- emitir(avisos.map((a) => id === void 0 || a.id === id ? { ...a, abierto: false } : a));
2124
+ function discard(id) {
2125
+ emit(warnings.map((a) => id === void 0 || a.id === id ? { ...a, open: false } : a));
2059
2126
  }
2060
- function retirar(id) {
2061
- emitir(avisos.filter((a) => a.id !== id));
2127
+ function remove(id) {
2128
+ emit(warnings.filter((a) => a.id !== id));
2062
2129
  }
2063
- var toast2 = Object.assign(crear, {
2064
- success: (mensaje, opciones = {}) => crear(mensaje, { ...opciones, variant: "success" }),
2065
- error: (mensaje, opciones = {}) => crear(mensaje, { ...opciones, variant: "error" }),
2066
- dismiss: descartar
2130
+ var toast2 = Object.assign(create, {
2131
+ success: (message, options = {}) => create(message, { ...options, variant: "success" }),
2132
+ error: (message, options = {}) => create(message, { ...options, variant: "error" }),
2133
+ dismiss: discard
2067
2134
  });
2068
2135
  function Toaster({ duration = 5e3, label = "Avisos" }) {
2069
- const lista = react.useSyncExternalStore(suscribirse, leer, enServidor);
2136
+ const list = react.useSyncExternalStore(subscribe, read, onServer);
2070
2137
  return /* @__PURE__ */ jsxRuntime.jsxs(ToastProvider, { duration, label, children: [
2071
- lista.map(({ id, title, description, variant, duration: propio, action, abierto }) => /* @__PURE__ */ jsxRuntime.jsxs(
2138
+ list.map(({ id, title, description, variant, duration: own, action, open }) => /* @__PURE__ */ jsxRuntime.jsxs(
2072
2139
  Toast,
2073
2140
  {
2074
- open: abierto,
2141
+ open,
2075
2142
  variant: variant ?? "neutral",
2076
- ...propio === void 0 ? {} : { duration: propio },
2077
- onOpenChange: (sigue) => {
2078
- if (!sigue) retirar(id);
2143
+ ...own === void 0 ? {} : { duration: own },
2144
+ onOpenChange: (stillOpen) => {
2145
+ if (!stillOpen) remove(id);
2079
2146
  },
2080
2147
  children: [
2081
2148
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
@@ -2112,15 +2179,9 @@ function TooltipContent({
2112
2179
  }
2113
2180
  ) });
2114
2181
  }
2115
- var TARJETA = [
2116
- "group block cursor-pointer",
2117
- SUPERFICIE_TARJETA,
2118
- HOVER_TARJETA,
2119
- "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent"
2120
- ];
2121
- function Tarjeta({ asChild = false, className, children, ...props }) {
2122
- const Raiz = asChild ? reactSlot.Slot : "a";
2123
- return /* @__PURE__ */ jsxRuntime.jsx(Raiz, { className: cn(TARJETA, className), ...props, children });
2182
+ function CardShell({ asChild = false, className, children, ...props }) {
2183
+ const Root18 = asChild ? reactSlot.Slot : "a";
2184
+ return /* @__PURE__ */ jsxRuntime.jsx(Root18, { className: cn(CARD, className), ...props, children });
2124
2185
  }
2125
2186
  function ArticleCard({
2126
2187
  title,
@@ -2130,17 +2191,18 @@ function ArticleCard({
2130
2191
  readingMinutes,
2131
2192
  tags,
2132
2193
  headingLevel = 3,
2194
+ tagAsChild,
2133
2195
  className,
2134
2196
  ...props
2135
2197
  }) {
2136
2198
  const meta = [
2137
- date ? /* @__PURE__ */ jsxRuntime.jsx("time", { dateTime, children: date }, "fecha") : null,
2199
+ date ? /* @__PURE__ */ jsxRuntime.jsx("time", { dateTime, children: date }, "date") : null,
2138
2200
  readingMinutes ? /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
2139
2201
  readingMinutes,
2140
2202
  " min de lectura"
2141
2203
  ] }, "lectura") : null
2142
2204
  ].filter(Boolean);
2143
- return /* @__PURE__ */ jsxRuntime.jsx(Tarjeta, { className: cn("p-step-lg", className), ...props, children: /* @__PURE__ */ jsxRuntime.jsxs("article", { className: "gap-step-sm flex h-full flex-col", children: [
2205
+ return /* @__PURE__ */ jsxRuntime.jsx(CardShell, { className: cn("p-step-lg", className), ...props, children: /* @__PURE__ */ jsxRuntime.jsxs("article", { className: "gap-step-sm flex h-full flex-col", children: [
2144
2206
  meta.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "meta", tone: "muted", as: "p", className: "gap-step-xs flex items-center", children: meta.map((m, i) => /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "gap-step-xs flex items-center", children: [
2145
2207
  i > 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: "\xB7" }) : null,
2146
2208
  m
@@ -2155,18 +2217,20 @@ function ArticleCard({
2155
2217
  }
2156
2218
  ),
2157
2219
  excerpt ? /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "ui", tone: "secondary", className: "line-clamp-2", children: excerpt }) : null,
2158
- tags && tags.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "gap-step-xs mt-auto flex flex-wrap pt-step-sm", children: tags.map((t) => /* @__PURE__ */ jsxRuntime.jsx(CategoryBadge, { children: t }, t)) }) : null
2220
+ tags && tags.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "gap-step-xs mt-auto flex flex-wrap pt-step-sm", children: tags.map(
2221
+ (t) => tagAsChild ? /* @__PURE__ */ jsxRuntime.jsx(reactSlot.Slot, { className: category({ active: false }), children: tagAsChild({ tag: t, children: t }) }, t) : /* @__PURE__ */ jsxRuntime.jsx(CategoryBadge, { children: t }, t)
2222
+ ) }) : null
2159
2223
  ] }) });
2160
2224
  }
2161
2225
  var AVATAR = "size-[52px]";
2162
- function iniciales(nombre) {
2163
- return nombre.split(/\s+/).slice(0, 2).map((p) => p[0] ?? "").join("").toUpperCase();
2226
+ function initials(name) {
2227
+ return name.split(/\s+/).slice(0, 2).map((p) => p[0] ?? "").join("").toUpperCase();
2164
2228
  }
2165
2229
  function AuthorCard({ name, role, src, bio, action, className, ...props }) {
2166
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn(SUPERFICIE_TARJETA, "p-step-lg gap-step-md flex items-start", className), ...props, children: [
2230
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn(CARD_SURFACE, "p-step-lg gap-step-md flex items-start", className), ...props, children: [
2167
2231
  /* @__PURE__ */ jsxRuntime.jsxs(Avatar, { className: cn(AVATAR, "shrink-0"), children: [
2168
2232
  src ? /* @__PURE__ */ jsxRuntime.jsx(AvatarImage, { src, alt: "" }) : null,
2169
- /* @__PURE__ */ jsxRuntime.jsx(AvatarFallback, { children: iniciales(name) })
2233
+ /* @__PURE__ */ jsxRuntime.jsx(AvatarFallback, { children: initials(name) })
2170
2234
  ] }),
2171
2235
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-xs flex min-w-0 flex-1 flex-col", children: [
2172
2236
  /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "ui", as: "p", className: "font-medium", children: name }),
@@ -2184,45 +2248,45 @@ function formatTime(seconds) {
2184
2248
  }
2185
2249
  var WAVEFORM_HEIGHTS = [35, 65, 100, 65, 35];
2186
2250
  var RATES = [1, 1.25, 1.5, 1.75, 2];
2187
- function Onda({ grosor, alto, sonando }) {
2188
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("flex items-end gap-[2px]", alto), "aria-hidden": "true", children: WAVEFORM_HEIGHTS.map((h, i) => /* @__PURE__ */ jsxRuntime.jsx(
2251
+ function Wave({ strokeWidth, height, playing }) {
2252
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("flex items-end gap-[2px]", height), "aria-hidden": "true", children: WAVEFORM_HEIGHTS.map((h, i) => /* @__PURE__ */ jsxRuntime.jsx(
2189
2253
  "span",
2190
2254
  {
2191
- className: cn("rounded-pill bg-accent transition-standard", grosor),
2192
- style: { height: `${h}%`, opacity: sonando ? 1 : 0.3 + h / 100 * 0.4 }
2255
+ className: cn("rounded-pill bg-accent transition-standard", strokeWidth),
2256
+ style: { height: `${h}%`, opacity: playing ? 1 : 0.3 + h / 100 * 0.4 }
2193
2257
  },
2194
2258
  i
2195
2259
  )) });
2196
2260
  }
2197
- function BarraProgreso({
2198
- alto,
2199
- perilla,
2200
- progreso,
2261
+ function ProgressBar({
2262
+ height,
2263
+ knob,
2264
+ progress,
2201
2265
  redondeada = true,
2202
2266
  tabIndex = 0,
2203
- ...puntero
2267
+ ...pointer
2204
2268
  }) {
2205
2269
  return /* @__PURE__ */ jsxRuntime.jsxs(
2206
2270
  "div",
2207
2271
  {
2208
2272
  className: cn(
2209
2273
  "group bg-surface-raised relative cursor-pointer",
2210
- alto,
2274
+ height,
2211
2275
  redondeada && "rounded-pill"
2212
2276
  ),
2213
2277
  role: "slider",
2214
2278
  "aria-label": "Progreso del audio",
2215
2279
  "aria-valuemin": 0,
2216
2280
  "aria-valuemax": 100,
2217
- "aria-valuenow": Math.round(progreso),
2281
+ "aria-valuenow": Math.round(progress),
2218
2282
  tabIndex,
2219
- ...puntero,
2283
+ ...pointer,
2220
2284
  children: [
2221
2285
  /* @__PURE__ */ jsxRuntime.jsx(
2222
2286
  "div",
2223
2287
  {
2224
2288
  className: cn("bg-accent absolute h-full", redondeada && "rounded-pill"),
2225
- style: { width: `${progreso}%` }
2289
+ style: { width: `${progress}%` }
2226
2290
  }
2227
2291
  ),
2228
2292
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -2230,9 +2294,9 @@ function BarraProgreso({
2230
2294
  {
2231
2295
  className: "rounded-pill bg-text-primary absolute top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100",
2232
2296
  style: {
2233
- width: perilla,
2234
- height: perilla,
2235
- left: `calc(${progreso}% - ${perilla / 2}px)`
2297
+ width: knob,
2298
+ height: knob,
2299
+ left: `calc(${progress}% - ${knob / 2}px)`
2236
2300
  }
2237
2301
  }
2238
2302
  )
@@ -2241,10 +2305,10 @@ function BarraProgreso({
2241
2305
  );
2242
2306
  }
2243
2307
  var CONTROL = "rounded-chip transition-standard cursor-pointer focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent";
2244
- function BotonSalto({
2308
+ function SkipButton({
2245
2309
  segundos,
2246
2310
  className,
2247
- tamano,
2311
+ size: size2,
2248
2312
  onSaltar
2249
2313
  }) {
2250
2314
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -2254,52 +2318,52 @@ function BotonSalto({
2254
2318
  onClick: () => onSaltar(segundos),
2255
2319
  className: cn("text-text-muted hover:text-text-primary", CONTROL, className),
2256
2320
  "aria-label": segundos < 0 ? "Retroceder 15 segundos" : "Adelantar 15 segundos",
2257
- children: segundos < 0 ? /* @__PURE__ */ jsxRuntime.jsx(GoBackSeconds, { className: tamano }) : /* @__PURE__ */ jsxRuntime.jsx(AdvanceSeconds, { className: tamano })
2321
+ children: segundos < 0 ? /* @__PURE__ */ jsxRuntime.jsx(GoBackSeconds, { className: size2 }) : /* @__PURE__ */ jsxRuntime.jsx(AdvanceSeconds, { className: size2 })
2258
2322
  }
2259
2323
  );
2260
2324
  }
2261
- function BotonVelocidad({
2325
+ function SpeedButton({
2262
2326
  className,
2263
- velocidad,
2264
- onCambiar
2327
+ speed,
2328
+ onChange
2265
2329
  }) {
2266
2330
  return /* @__PURE__ */ jsxRuntime.jsxs(
2267
2331
  "button",
2268
2332
  {
2269
2333
  type: "button",
2270
- onClick: onCambiar,
2334
+ onClick: onChange,
2271
2335
  className: cn(
2272
- // `textMuted` sobre `surfaceRaised` da 4.07:1 en oscuro y no pasa AA.
2273
- // El original del portafolio arrastra ese mismo fallo.
2336
+ // `textMuted` over `surfaceRaised` gives 4.07:1 in dark and fails AA.
2337
+ // The portfolio original carries that same failure.
2274
2338
  "text-text-secondary hover:text-text-primary bg-surface-raised",
2275
2339
  CONTROL,
2276
2340
  className
2277
2341
  ),
2278
2342
  "aria-label": "Cambiar velocidad de reproducci\xF3n",
2279
2343
  children: [
2280
- velocidad,
2344
+ speed,
2281
2345
  "x"
2282
2346
  ]
2283
2347
  }
2284
2348
  );
2285
2349
  }
2286
- function Volumen({
2287
- tamano,
2288
- ancho,
2350
+ function Volume({
2351
+ size: size2,
2352
+ width,
2289
2353
  silenciado,
2290
2354
  volumen,
2291
- onSilenciar,
2292
- onCambiarVolumen
2355
+ onMute,
2356
+ onVolumeChange
2293
2357
  }) {
2294
2358
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-xs hidden items-center sm:flex", children: [
2295
2359
  /* @__PURE__ */ jsxRuntime.jsx(
2296
2360
  "button",
2297
2361
  {
2298
2362
  type: "button",
2299
- onClick: onSilenciar,
2363
+ onClick: onMute,
2300
2364
  className: cn("text-text-muted hover:text-text-primary p-1", CONTROL),
2301
2365
  "aria-label": silenciado ? "Activar sonido" : "Silenciar",
2302
- children: silenciado || volumen === 0 ? /* @__PURE__ */ jsxRuntime.jsx(VolumeMuted, { className: tamano }) : /* @__PURE__ */ jsxRuntime.jsx(VolumeOn, { className: tamano })
2366
+ children: silenciado || volumen === 0 ? /* @__PURE__ */ jsxRuntime.jsx(VolumeMuted, { className: size2 }) : /* @__PURE__ */ jsxRuntime.jsx(VolumeOn, { className: size2 })
2303
2367
  }
2304
2368
  ),
2305
2369
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -2310,46 +2374,46 @@ function Volumen({
2310
2374
  max: "1",
2311
2375
  step: "0.1",
2312
2376
  value: silenciado ? 0 : volumen,
2313
- onChange: onCambiarVolumen,
2377
+ onChange: onVolumeChange,
2314
2378
  className: cn(
2315
2379
  "bg-surface-raised rounded-pill h-1 cursor-pointer appearance-none",
2316
2380
  "[accent-color:var(--color-accent)]",
2317
- ancho
2381
+ width
2318
2382
  ),
2319
- "aria-label": "Volumen"
2383
+ "aria-label": "Volume"
2320
2384
  }
2321
2385
  )
2322
2386
  ] });
2323
2387
  }
2324
- function IconoPlay({ className, cargando, error, sonando }) {
2388
+ function PlayIcon({ className, cargando, error, playing }) {
2325
2389
  if (cargando) return /* @__PURE__ */ jsxRuntime.jsx(Spinner, { className: cn("motion-safe:animate-spin", className) });
2326
2390
  if (error) return /* @__PURE__ */ jsxRuntime.jsx(Retry, { className });
2327
- if (sonando) return /* @__PURE__ */ jsxRuntime.jsx(Pause, { className });
2391
+ if (playing) return /* @__PURE__ */ jsxRuntime.jsx(Pause, { className });
2328
2392
  return /* @__PURE__ */ jsxRuntime.jsx(Play, { className });
2329
2393
  }
2330
- function BotonPlay({
2394
+ function PlayButton({
2331
2395
  className,
2332
- tamano,
2333
- etiqueta,
2334
- texto: texto2,
2335
- onAlternar,
2336
- ...estado
2396
+ size: size2,
2397
+ label,
2398
+ text: text2,
2399
+ onToggle,
2400
+ ...state
2337
2401
  }) {
2338
2402
  return /* @__PURE__ */ jsxRuntime.jsxs(
2339
2403
  "button",
2340
2404
  {
2341
2405
  type: "button",
2342
- onClick: onAlternar,
2406
+ onClick: onToggle,
2343
2407
  className: cn(
2344
2408
  "text-accent-on transition-standard cursor-pointer",
2345
2409
  "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent",
2346
- estado.error ? "bg-error hover:bg-error/80" : "bg-accent hover:bg-accent-hover",
2410
+ state.error ? "bg-error hover:bg-error/80" : "bg-accent hover:bg-accent-hover",
2347
2411
  className
2348
2412
  ),
2349
- "aria-label": etiqueta,
2413
+ "aria-label": label,
2350
2414
  children: [
2351
- /* @__PURE__ */ jsxRuntime.jsx(IconoPlay, { className: tamano, ...estado }),
2352
- texto2 ? /* @__PURE__ */ jsxRuntime.jsx("span", { children: texto2 }) : null
2415
+ /* @__PURE__ */ jsxRuntime.jsx(PlayIcon, { className: size2, ...state }),
2416
+ text2 ? /* @__PURE__ */ jsxRuntime.jsx("span", { children: text2 }) : null
2353
2417
  ]
2354
2418
  }
2355
2419
  );
@@ -2368,7 +2432,7 @@ function AudioPlayer({ src, title, mode = "full", onFirstPlay }) {
2368
2432
  const [isMuted, setIsMuted] = react.useState(false);
2369
2433
  const [playbackRate, setPlaybackRate] = react.useState(1);
2370
2434
  const [isStaticVisible, setIsStaticVisible] = react.useState(true);
2371
- const flotante = mode === "banner" || mode === "compact";
2435
+ const floating = mode === "banner" || mode === "compact";
2372
2436
  react.useEffect(() => {
2373
2437
  const audio = audioRef.current;
2374
2438
  if (!audio) return;
@@ -2411,7 +2475,7 @@ function AudioPlayer({ src, title, mode = "full", onFirstPlay }) {
2411
2475
  };
2412
2476
  }, []);
2413
2477
  react.useEffect(() => {
2414
- if (!flotante || !staticRef.current) return;
2478
+ if (!floating || !staticRef.current) return;
2415
2479
  const observer = new IntersectionObserver(
2416
2480
  (entries) => {
2417
2481
  const entry = entries[0];
@@ -2421,7 +2485,7 @@ function AudioPlayer({ src, title, mode = "full", onFirstPlay }) {
2421
2485
  );
2422
2486
  observer.observe(staticRef.current);
2423
2487
  return () => observer.disconnect();
2424
- }, [flotante]);
2488
+ }, [floating]);
2425
2489
  const togglePlay = react.useCallback(async () => {
2426
2490
  const audio = audioRef.current;
2427
2491
  if (!audio) return;
@@ -2510,37 +2574,37 @@ function AudioPlayer({ src, title, mode = "full", onFirstPlay }) {
2510
2574
  [duration]
2511
2575
  );
2512
2576
  const progress = duration > 0 ? currentTime / duration * 100 : 0;
2513
- const etiquetaPlay = hasError ? "Reintentar" : isPlaying ? "Pausar" : "Reproducir";
2514
- const estadoPlay = { cargando: isLoading, error: hasError, sonando: isPlaying };
2515
- const puntero = {
2577
+ const playLabel = hasError ? "Reintentar" : isPlaying ? "Pausar" : "Reproducir";
2578
+ const playState = { cargando: isLoading, error: hasError, playing: isPlaying };
2579
+ const pointer = {
2516
2580
  onPointerDown: handlePointerDown,
2517
2581
  onPointerMove: handlePointerMove,
2518
2582
  onPointerUp: handlePointerUp
2519
2583
  };
2520
- const flotanteJsx = /* @__PURE__ */ jsxRuntime.jsxs(
2584
+ const floatingJsx = /* @__PURE__ */ jsxRuntime.jsxs(
2521
2585
  "div",
2522
2586
  {
2523
2587
  "aria-hidden": isStaticVisible,
2524
2588
  className: cn(
2525
2589
  "fixed inset-x-0 bottom-0 z-40 xl:hidden",
2526
- // Aparece y desaparece: el sistema no anima desplazamiento.
2590
+ // It appears and disappears: the system does not animate displacement.
2527
2591
  isStaticVisible && "hidden"
2528
2592
  ),
2529
2593
  children: [
2530
2594
  /* @__PURE__ */ jsxRuntime.jsx(
2531
- BarraProgreso,
2595
+ ProgressBar,
2532
2596
  {
2533
- alto: "h-1",
2534
- perilla: 10,
2535
- progreso: progress,
2597
+ height: "h-1",
2598
+ knob: 10,
2599
+ progress,
2536
2600
  redondeada: false,
2537
2601
  tabIndex: -1,
2538
- ...puntero
2602
+ ...pointer
2539
2603
  }
2540
2604
  ),
2541
2605
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-surface/95 border-accent/25 border-t backdrop-blur-md", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-sm px-step-md flex items-center py-5", children: [
2542
2606
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-xs flex shrink-0 items-center", children: [
2543
- /* @__PURE__ */ jsxRuntime.jsx(Onda, { grosor: "w-[2px]", alto: "h-4", sonando: isPlaying }),
2607
+ /* @__PURE__ */ jsxRuntime.jsx(Wave, { strokeWidth: "w-[2px]", height: "h-4", playing: isPlaying }),
2544
2608
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-eyebrow font-mono text-accent hidden uppercase sm:block", children: "Narraci\xF3n" })
2545
2609
  ] }),
2546
2610
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
@@ -2552,24 +2616,24 @@ function AudioPlayer({ src, title, mode = "full", onFirstPlay }) {
2552
2616
  ] })
2553
2617
  ] }),
2554
2618
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [
2555
- /* @__PURE__ */ jsxRuntime.jsx(BotonSalto, { segundos: -15, className: "p-1.5", tamano: "w-5 h-5", onSaltar: skip }),
2619
+ /* @__PURE__ */ jsxRuntime.jsx(SkipButton, { segundos: -15, className: "p-1.5", size: "w-5 h-5", onSaltar: skip }),
2556
2620
  /* @__PURE__ */ jsxRuntime.jsx(
2557
- BotonPlay,
2621
+ PlayButton,
2558
2622
  {
2559
2623
  className: "rounded-pill px-step-sm gap-step-xs flex items-center py-1.5",
2560
- tamano: "w-4 h-4",
2561
- etiqueta: etiquetaPlay,
2562
- onAlternar: togglePlay,
2563
- ...estadoPlay
2624
+ size: "w-4 h-4",
2625
+ label: playLabel,
2626
+ onToggle: togglePlay,
2627
+ ...playState
2564
2628
  }
2565
2629
  ),
2566
- /* @__PURE__ */ jsxRuntime.jsx(BotonSalto, { segundos: 15, className: "p-1.5", tamano: "w-5 h-5", onSaltar: skip }),
2630
+ /* @__PURE__ */ jsxRuntime.jsx(SkipButton, { segundos: 15, className: "p-1.5", size: "w-5 h-5", onSaltar: skip }),
2567
2631
  /* @__PURE__ */ jsxRuntime.jsx(
2568
- BotonVelocidad,
2632
+ SpeedButton,
2569
2633
  {
2570
2634
  className: "text-label ml-0.5 px-1.5 py-0.5",
2571
- velocidad: playbackRate,
2572
- onCambiar: handlePlaybackRateChange
2635
+ speed: playbackRate,
2636
+ onChange: handlePlaybackRateChange
2573
2637
  }
2574
2638
  )
2575
2639
  ] })
@@ -2582,7 +2646,7 @@ function AudioPlayer({ src, title, mode = "full", onFirstPlay }) {
2582
2646
  /* @__PURE__ */ jsxRuntime.jsx("audio", { ref: audioRef, src, preload: "metadata" }),
2583
2647
  /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: staticRef, children: [
2584
2648
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-sm mb-step-md flex items-center", children: [
2585
- /* @__PURE__ */ jsxRuntime.jsx(Onda, { grosor: "w-[3px]", alto: "h-5", sonando: isPlaying }),
2649
+ /* @__PURE__ */ jsxRuntime.jsx(Wave, { strokeWidth: "w-[3px]", height: "h-5", playing: isPlaying }),
2586
2650
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-eyebrow font-mono text-accent uppercase", children: "Narraci\xF3n de audio" }),
2587
2651
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-chip font-mono text-text-muted ml-auto tabular-nums", children: [
2588
2652
  formatTime(currentTime),
@@ -2590,125 +2654,125 @@ function AudioPlayer({ src, title, mode = "full", onFirstPlay }) {
2590
2654
  formatTime(duration)
2591
2655
  ] })
2592
2656
  ] }),
2593
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-step-md", children: /* @__PURE__ */ jsxRuntime.jsx(BarraProgreso, { alto: "h-1.5", perilla: 14, progreso: progress, ...puntero }) }),
2657
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-step-md", children: /* @__PURE__ */ jsxRuntime.jsx(ProgressBar, { height: "h-1.5", knob: 14, progress, ...pointer }) }),
2594
2658
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-sm flex items-center", children: [
2595
- /* @__PURE__ */ jsxRuntime.jsx(BotonSalto, { segundos: -15, className: "p-2", tamano: "w-6 h-6", onSaltar: skip }),
2659
+ /* @__PURE__ */ jsxRuntime.jsx(SkipButton, { segundos: -15, className: "p-2", size: "w-6 h-6", onSaltar: skip }),
2596
2660
  /* @__PURE__ */ jsxRuntime.jsx(
2597
- BotonPlay,
2661
+ PlayButton,
2598
2662
  {
2599
2663
  className: "rounded-pill px-step-lg gap-step-xs text-ui flex items-center py-2 font-medium",
2600
- tamano: "w-4 h-4",
2601
- etiqueta: etiquetaPlay,
2602
- texto: etiquetaPlay,
2603
- onAlternar: togglePlay,
2604
- ...estadoPlay
2664
+ size: "w-4 h-4",
2665
+ label: playLabel,
2666
+ text: playLabel,
2667
+ onToggle: togglePlay,
2668
+ ...playState
2605
2669
  }
2606
2670
  ),
2607
- /* @__PURE__ */ jsxRuntime.jsx(BotonSalto, { segundos: 15, className: "p-2", tamano: "w-6 h-6", onSaltar: skip }),
2671
+ /* @__PURE__ */ jsxRuntime.jsx(SkipButton, { segundos: 15, className: "p-2", size: "w-6 h-6", onSaltar: skip }),
2608
2672
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-sm ml-auto flex items-center", children: [
2609
2673
  /* @__PURE__ */ jsxRuntime.jsx(
2610
- BotonVelocidad,
2674
+ SpeedButton,
2611
2675
  {
2612
2676
  className: "text-label px-2 py-1",
2613
- velocidad: playbackRate,
2614
- onCambiar: handlePlaybackRateChange
2677
+ speed: playbackRate,
2678
+ onChange: handlePlaybackRateChange
2615
2679
  }
2616
2680
  ),
2617
2681
  /* @__PURE__ */ jsxRuntime.jsx(
2618
- Volumen,
2682
+ Volume,
2619
2683
  {
2620
- tamano: "w-4 h-4",
2621
- ancho: "w-16",
2684
+ size: "w-4 h-4",
2685
+ width: "w-16",
2622
2686
  silenciado: isMuted,
2623
2687
  volumen: volume,
2624
- onSilenciar: toggleMute,
2625
- onCambiarVolumen: handleVolumeChange
2688
+ onMute: toggleMute,
2689
+ onVolumeChange: handleVolumeChange
2626
2690
  }
2627
2691
  )
2628
2692
  ] })
2629
2693
  ] })
2630
2694
  ] }),
2631
- flotanteJsx
2695
+ floatingJsx
2632
2696
  ] });
2633
2697
  }
2634
2698
  if (mode === "compact") {
2635
2699
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2636
2700
  /* @__PURE__ */ jsxRuntime.jsx("audio", { ref: audioRef, src, preload: "metadata" }),
2637
2701
  /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: staticRef, className: "w-full", children: [
2638
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-step-xs", children: /* @__PURE__ */ jsxRuntime.jsx(BarraProgreso, { alto: "h-1.5", perilla: 12, progreso: progress, ...puntero }) }),
2702
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-step-xs", children: /* @__PURE__ */ jsxRuntime.jsx(ProgressBar, { height: "h-1.5", knob: 12, progress, ...pointer }) }),
2639
2703
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-chip font-mono text-text-muted mb-step-sm flex justify-between", children: [
2640
2704
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: formatTime(currentTime) }),
2641
2705
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: formatTime(duration) })
2642
2706
  ] }),
2643
2707
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
2644
2708
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
2645
- /* @__PURE__ */ jsxRuntime.jsx(BotonSalto, { segundos: -15, className: "p-1.5", tamano: "w-5 h-5", onSaltar: skip }),
2709
+ /* @__PURE__ */ jsxRuntime.jsx(SkipButton, { segundos: -15, className: "p-1.5", size: "w-5 h-5", onSaltar: skip }),
2646
2710
  /* @__PURE__ */ jsxRuntime.jsx(
2647
- BotonPlay,
2711
+ PlayButton,
2648
2712
  {
2649
2713
  className: "rounded-pill flex size-[34px] items-center justify-center",
2650
- tamano: "w-4 h-4",
2651
- etiqueta: etiquetaPlay,
2652
- onAlternar: togglePlay,
2653
- ...estadoPlay
2714
+ size: "w-4 h-4",
2715
+ label: playLabel,
2716
+ onToggle: togglePlay,
2717
+ ...playState
2654
2718
  }
2655
2719
  ),
2656
- /* @__PURE__ */ jsxRuntime.jsx(BotonSalto, { segundos: 15, className: "p-1.5", tamano: "w-5 h-5", onSaltar: skip })
2720
+ /* @__PURE__ */ jsxRuntime.jsx(SkipButton, { segundos: 15, className: "p-1.5", size: "w-5 h-5", onSaltar: skip })
2657
2721
  ] }),
2658
2722
  /* @__PURE__ */ jsxRuntime.jsx(
2659
- BotonVelocidad,
2723
+ SpeedButton,
2660
2724
  {
2661
2725
  className: "text-label px-1.5 py-0.5",
2662
- velocidad: playbackRate,
2663
- onCambiar: handlePlaybackRateChange
2726
+ speed: playbackRate,
2727
+ onChange: handlePlaybackRateChange
2664
2728
  }
2665
2729
  )
2666
2730
  ] })
2667
2731
  ] }),
2668
- flotanteJsx
2732
+ floatingJsx
2669
2733
  ] });
2670
2734
  }
2671
2735
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-surface rounded-control border-border p-step-md w-full border", children: [
2672
2736
  /* @__PURE__ */ jsxRuntime.jsx("audio", { ref: audioRef, src, preload: "metadata" }),
2673
2737
  title ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-ui font-sans text-text-secondary mb-step-sm truncate", children: title }) : null,
2674
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-step-sm", children: /* @__PURE__ */ jsxRuntime.jsx(BarraProgreso, { alto: "h-2", perilla: 16, progreso: progress, ...puntero }) }),
2738
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-step-sm", children: /* @__PURE__ */ jsxRuntime.jsx(ProgressBar, { height: "h-2", knob: 16, progress, ...pointer }) }),
2675
2739
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-chip font-mono text-text-muted mb-step-sm flex justify-between", children: [
2676
2740
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: formatTime(currentTime) }),
2677
2741
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: formatTime(duration) })
2678
2742
  ] }),
2679
2743
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
2680
2744
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-xs flex items-center", children: [
2681
- /* @__PURE__ */ jsxRuntime.jsx(BotonSalto, { segundos: -15, className: "p-2", tamano: "w-5 h-5", onSaltar: skip }),
2745
+ /* @__PURE__ */ jsxRuntime.jsx(SkipButton, { segundos: -15, className: "p-2", size: "w-5 h-5", onSaltar: skip }),
2682
2746
  /* @__PURE__ */ jsxRuntime.jsx(
2683
- BotonPlay,
2747
+ PlayButton,
2684
2748
  {
2685
2749
  className: "rounded-pill flex size-12 items-center justify-center",
2686
- tamano: "w-6 h-6",
2687
- etiqueta: etiquetaPlay,
2688
- onAlternar: togglePlay,
2689
- ...estadoPlay
2750
+ size: "w-6 h-6",
2751
+ label: playLabel,
2752
+ onToggle: togglePlay,
2753
+ ...playState
2690
2754
  }
2691
2755
  ),
2692
- /* @__PURE__ */ jsxRuntime.jsx(BotonSalto, { segundos: 15, className: "p-2", tamano: "w-5 h-5", onSaltar: skip })
2756
+ /* @__PURE__ */ jsxRuntime.jsx(SkipButton, { segundos: 15, className: "p-2", size: "w-5 h-5", onSaltar: skip })
2693
2757
  ] }),
2694
2758
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-sm flex items-center", children: [
2695
2759
  /* @__PURE__ */ jsxRuntime.jsx(
2696
- BotonVelocidad,
2760
+ SpeedButton,
2697
2761
  {
2698
2762
  className: "text-label px-2 py-1",
2699
- velocidad: playbackRate,
2700
- onCambiar: handlePlaybackRateChange
2763
+ speed: playbackRate,
2764
+ onChange: handlePlaybackRateChange
2701
2765
  }
2702
2766
  ),
2703
2767
  /* @__PURE__ */ jsxRuntime.jsx(
2704
- Volumen,
2768
+ Volume,
2705
2769
  {
2706
- tamano: "w-5 h-5",
2707
- ancho: "w-20",
2770
+ size: "w-5 h-5",
2771
+ width: "w-20",
2708
2772
  silenciado: isMuted,
2709
2773
  volumen: volume,
2710
- onSilenciar: toggleMute,
2711
- onCambiarVolumen: handleVolumeChange
2774
+ onMute: toggleMute,
2775
+ onVolumeChange: handleVolumeChange
2712
2776
  }
2713
2777
  )
2714
2778
  ] })
@@ -2732,7 +2796,7 @@ function Blockquote({ children, author, source, className, ...props }) {
2732
2796
  }
2733
2797
  );
2734
2798
  }
2735
- var ENLACE = "transition-standard hover:text-accent focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent rounded-chip cursor-pointer";
2799
+ var LINK = "transition-standard hover:text-accent focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent rounded-chip cursor-pointer";
2736
2800
  function Breadcrumb({
2737
2801
  items,
2738
2802
  homeHref = "/",
@@ -2741,28 +2805,28 @@ function Breadcrumb({
2741
2805
  className,
2742
2806
  ...props
2743
2807
  }) {
2744
- const enlace = (href, contenido) => linkAsChild ? /* @__PURE__ */ jsxRuntime.jsx(reactSlot.Slot, { className: ENLACE, children: linkAsChild({ href, children: contenido }) }) : /* @__PURE__ */ jsxRuntime.jsx("a", { href, className: ENLACE, children: contenido });
2808
+ const link = (href, content) => linkAsChild ? /* @__PURE__ */ jsxRuntime.jsx(reactSlot.Slot, { className: LINK, children: linkAsChild({ href, children: content }) }) : /* @__PURE__ */ jsxRuntime.jsx("a", { href, className: LINK, children: content });
2745
2809
  return /* @__PURE__ */ jsxRuntime.jsx("nav", { "aria-label": "Ruta", className: cn("font-mono text-meta text-text-muted", className), ...props, children: /* @__PURE__ */ jsxRuntime.jsxs("ol", { className: "gap-step-xs flex flex-wrap items-center", children: [
2746
- /* @__PURE__ */ jsxRuntime.jsx("li", { children: enlace(homeHref, /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-label": homeLabel, children: "~" })) }),
2810
+ /* @__PURE__ */ jsxRuntime.jsx("li", { children: link(homeHref, /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-label": homeLabel, children: "~" })) }),
2747
2811
  items.map((item2, i) => {
2748
- const ultimo = i === items.length - 1;
2812
+ const last = i === items.length - 1;
2749
2813
  return /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "gap-step-xs flex items-center", children: [
2750
2814
  /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", className: "text-border", children: "/" }),
2751
- item2.href && !ultimo ? enlace(item2.href, item2.label) : /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-current": ultimo ? "page" : void 0, className: "text-text-secondary", children: item2.label })
2815
+ item2.href && !last ? link(item2.href, item2.label) : /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-current": last ? "page" : void 0, className: "text-text-secondary", children: item2.label })
2752
2816
  ] }, i);
2753
2817
  })
2754
2818
  ] }) });
2755
2819
  }
2756
2820
  function CodeBlock({ children, language, copyText, className, ...props }) {
2757
2821
  const [copiado, setCopiado] = react.useState(false);
2758
- const temporizador = react.useRef(null);
2759
- const copiar = react.useCallback(async () => {
2822
+ const timer = react.useRef(null);
2823
+ const copy = react.useCallback(async () => {
2760
2824
  if (!copyText) return;
2761
2825
  try {
2762
2826
  await navigator.clipboard.writeText(copyText);
2763
2827
  setCopiado(true);
2764
- if (temporizador.current) clearTimeout(temporizador.current);
2765
- temporizador.current = setTimeout(() => setCopiado(false), 2e3);
2828
+ if (timer.current) clearTimeout(timer.current);
2829
+ timer.current = setTimeout(() => setCopiado(false), 2e3);
2766
2830
  } catch {
2767
2831
  }
2768
2832
  }, [copyText]);
@@ -2786,7 +2850,7 @@ function CodeBlock({ children, language, copyText, className, ...props }) {
2786
2850
  "button",
2787
2851
  {
2788
2852
  type: "button",
2789
- onClick: copiar,
2853
+ onClick: copy,
2790
2854
  "aria-label": copiado ? "C\xF3digo copiado" : "Copiar c\xF3digo",
2791
2855
  "aria-live": "polite",
2792
2856
  className: cn(
@@ -2816,7 +2880,7 @@ function CourseCard({
2816
2880
  className,
2817
2881
  ...props
2818
2882
  }) {
2819
- return /* @__PURE__ */ jsxRuntime.jsx(Tarjeta, { className: cn("p-step-lg", className), ...props, children: /* @__PURE__ */ jsxRuntime.jsxs("article", { className: "gap-step-sm flex h-full flex-col", children: [
2883
+ return /* @__PURE__ */ jsxRuntime.jsx(CardShell, { className: cn("p-step-lg", className), ...props, children: /* @__PURE__ */ jsxRuntime.jsxs("article", { className: "gap-step-sm flex h-full flex-col", children: [
2820
2884
  status ? /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "warm", children: status }) }) : null,
2821
2885
  /* @__PURE__ */ jsxRuntime.jsx(Text, { as: "h3", variant: "h3", className: "group-hover:text-accent transition-standard", children: title }),
2822
2886
  summary ? /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "ui", tone: "secondary", className: "line-clamp-3", children: summary }) : null,
@@ -2834,8 +2898,8 @@ function CourseCard({
2834
2898
  ] }) });
2835
2899
  }
2836
2900
 
2837
- // src/brand/catalogo.ts
2838
- var caras = {
2901
+ // src/brand/catalog.ts
2902
+ var faces = {
2839
2903
  annoyed: "face-annoyed.png",
2840
2904
  confused: "face-confused.png",
2841
2905
  hearts: "face-hearts.png",
@@ -2850,27 +2914,27 @@ var poses = {
2850
2914
  peek: "pose-peek.png",
2851
2915
  surf: "pose-surf.png"
2852
2916
  };
2853
- var aletas = {
2854
- /** Dos azules. Para fondos claros. */
2917
+ var fins = {
2918
+ /** Two blues. For light backgrounds. */
2855
2919
  color: "fin.png",
2856
- /** Silueta a una tinta. Para fondos oscuros. */
2857
- espuma: "fin-foam.png"
2920
+ /** Single-ink silhouette. For dark backgrounds. */
2921
+ foam: "fin-foam.png"
2858
2922
  };
2859
- var usoDeCara = {
2860
- wink: "Formularios, nota al pie amable",
2861
- waiting: "Sin resultados, estado de espera",
2862
- laughing: "\xC9xito, suscripci\xF3n confirmada",
2863
- shades: "M\xF3dulo o curso completado",
2864
- hearts: "Agradecimiento, OG de art\xEDculo",
2865
- confused: "404, p\xE1gina no encontrada",
2866
- annoyed: "Error del servidor, fallo de build"
2923
+ var faceUsage = {
2924
+ wink: "Forms, a friendly footnote",
2925
+ waiting: "No results, waiting state",
2926
+ laughing: "Success, subscription confirmed",
2927
+ shades: "Module or course completed",
2928
+ hearts: "Gratitude, article OG",
2929
+ confused: "404, page not found",
2930
+ annoyed: "Server error, build failure"
2867
2931
  };
2868
- var RUTA_ASSETS = "/brand";
2869
- var listaCaras = Object.keys(caras);
2870
- var listaPoses = Object.keys(poses);
2871
- function Mascota({
2932
+ var ASSETS_PATH = "/brand";
2933
+ var faceList = Object.keys(faces);
2934
+ var poseList = Object.keys(poses);
2935
+ function Mascot({
2872
2936
  pose,
2873
- basePath = RUTA_ASSETS,
2937
+ basePath = ASSETS_PATH,
2874
2938
  alt = "",
2875
2939
  className,
2876
2940
  ...props
@@ -2885,9 +2949,9 @@ function Mascota({
2885
2949
  }
2886
2950
  );
2887
2951
  }
2888
- function CaraDeMascota({
2889
- expresion,
2890
- basePath = RUTA_ASSETS,
2952
+ function MascotFace({
2953
+ expression,
2954
+ basePath = ASSETS_PATH,
2891
2955
  alt = "",
2892
2956
  className,
2893
2957
  ...props
@@ -2895,16 +2959,16 @@ function CaraDeMascota({
2895
2959
  return /* @__PURE__ */ jsxRuntime.jsx(
2896
2960
  "img",
2897
2961
  {
2898
- src: `${basePath}/${caras[expresion]}`,
2962
+ src: `${basePath}/${faces[expression]}`,
2899
2963
  alt,
2900
2964
  className: cn("h-auto w-full max-w-24 select-none", className),
2901
2965
  ...props
2902
2966
  }
2903
2967
  );
2904
2968
  }
2905
- var CARA = "w-[66px] max-w-none";
2969
+ var FACE = "w-[66px] max-w-none";
2906
2970
  function EmptyState({
2907
- expresion,
2971
+ expression,
2908
2972
  title,
2909
2973
  description,
2910
2974
  action,
@@ -2915,10 +2979,10 @@ function EmptyState({
2915
2979
  return /* @__PURE__ */ jsxRuntime.jsxs(
2916
2980
  "div",
2917
2981
  {
2918
- className: cn(SUPERFICIE_TARJETA, "p-step-lg gap-step-sm flex flex-col items-center text-center", className),
2982
+ className: cn(CARD_SURFACE, "p-step-lg gap-step-sm flex flex-col items-center text-center", className),
2919
2983
  ...props,
2920
2984
  children: [
2921
- /* @__PURE__ */ jsxRuntime.jsx(CaraDeMascota, { expresion, className: CARA, basePath }),
2985
+ /* @__PURE__ */ jsxRuntime.jsx(MascotFace, { expression, className: FACE, basePath }),
2922
2986
  /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "ui", as: "p", className: "font-medium", children: title }),
2923
2987
  description ? /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "label", tone: "muted", as: "p", className: "max-w-measure font-normal", children: description }) : null,
2924
2988
  action ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-step-xs", children: action }) : null
@@ -2926,12 +2990,12 @@ function EmptyState({
2926
2990
  }
2927
2991
  );
2928
2992
  }
2929
- var mismoDia = (a, b) => a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
2930
- function aValorDeCampo(fecha) {
2931
- const dos = (n) => String(n).padStart(2, "0");
2932
- return `${fecha.getFullYear()}-${dos(fecha.getMonth() + 1)}-${dos(fecha.getDate())}T${dos(
2933
- fecha.getHours()
2934
- )}:${dos(fecha.getMinutes())}`;
2993
+ var sameDay = (a, b) => a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
2994
+ function toFieldValue(date) {
2995
+ const two = (n) => String(n).padStart(2, "0");
2996
+ return `${date.getFullYear()}-${two(date.getMonth() + 1)}-${two(date.getDate())}T${two(
2997
+ date.getHours()
2998
+ )}:${two(date.getMinutes())}`;
2935
2999
  }
2936
3000
  function EventCalendar({
2937
3001
  events,
@@ -2940,68 +3004,68 @@ function EventCalendar({
2940
3004
  onDeleteEvent,
2941
3005
  selected,
2942
3006
  onSelectDay,
2943
- formatDay = (dia) => dateFns.format(dia, "EEEE d 'de' MMMM", { locale: locale.es }),
2944
- formatTime: formatTime2 = (fecha) => dateFns.format(fecha, "HH:mm", { locale: locale.es }),
3007
+ formatDay = (day) => dateFns.format(day, "EEEE d 'de' MMMM", { locale: locale.es }),
3008
+ formatTime: formatTime2 = (date) => dateFns.format(date, "HH:mm", { locale: locale.es }),
2945
3009
  emptyMessage = "Nada en este d\xEDa.",
2946
3010
  className,
2947
3011
  ...props
2948
3012
  }) {
2949
3013
  const id = react.useId();
2950
3014
  const [diaInterno, setDiaInterno] = react.useState(() => selected ?? /* @__PURE__ */ new Date());
2951
- const dia = selected ?? diaInterno;
2952
- const [mes, setMes] = react.useState(() => dia);
2953
- const [editando, setEditando] = react.useState(null);
2954
- const [titulo, setTitulo] = react.useState("");
2955
- const [cuando, setCuando] = react.useState("");
3015
+ const day = selected ?? diaInterno;
3016
+ const [month, setMonth] = react.useState(() => day);
3017
+ const [editing, setEditing] = react.useState(null);
3018
+ const [title, setTitle] = react.useState("");
3019
+ const [when, setWhen] = react.useState("");
2956
3020
  const editable = Boolean(onCreateEvent ?? onUpdateEvent);
2957
- const delDia = events.filter((e) => mismoDia(e.start, dia)).sort((a, b) => a.start.getTime() - b.start.getTime());
2958
- function elegirDia(nuevo) {
3021
+ const ofTheDay = events.filter((e) => sameDay(e.start, day)).sort((a, b) => a.start.getTime() - b.start.getTime());
3022
+ function pickDay(nuevo) {
2959
3023
  if (!nuevo) return;
2960
3024
  setDiaInterno(nuevo);
2961
- setMes(nuevo);
3025
+ setMonth(nuevo);
2962
3026
  onSelectDay?.(nuevo);
2963
- cancelar();
3027
+ cancel();
2964
3028
  }
2965
- function cancelar() {
2966
- setEditando(null);
2967
- setTitulo("");
2968
- setCuando("");
3029
+ function cancel() {
3030
+ setEditing(null);
3031
+ setTitle("");
3032
+ setWhen("");
2969
3033
  }
2970
- function editar(evento) {
2971
- setEditando(evento.id);
2972
- setTitulo(evento.title);
2973
- setCuando(aValorDeCampo(evento.start));
3034
+ function edit(event) {
3035
+ setEditing(event.id);
3036
+ setTitle(event.title);
3037
+ setWhen(toFieldValue(event.start));
2974
3038
  }
2975
- function enviar(e) {
3039
+ function submit(e) {
2976
3040
  e.preventDefault();
2977
- if (!titulo.trim() || !cuando) return;
2978
- const start = new Date(cuando);
3041
+ if (!title.trim() || !when) return;
3042
+ const start = new Date(when);
2979
3043
  if (Number.isNaN(start.getTime())) return;
2980
- if (editando) {
2981
- const previo = events.find((ev) => ev.id === editando);
2982
- if (previo) onUpdateEvent?.({ ...previo, title: titulo.trim(), start });
3044
+ if (editing) {
3045
+ const previous = events.find((ev) => ev.id === editing);
3046
+ if (previous) onUpdateEvent?.({ ...previous, title: title.trim(), start });
2983
3047
  } else {
2984
- onCreateEvent?.({ title: titulo.trim(), start });
3048
+ onCreateEvent?.({ title: title.trim(), start });
2985
3049
  }
2986
- cancelar();
3050
+ cancel();
2987
3051
  }
2988
- const conEvento = events.map((e) => e.start);
3052
+ const withEvent = events.map((e) => e.start);
2989
3053
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("gap-step-lg flex flex-col md:flex-row md:items-start", className), ...props, children: [
2990
3054
  /* @__PURE__ */ jsxRuntime.jsx(
2991
3055
  Calendar,
2992
3056
  {
2993
3057
  mode: "single",
2994
- selected: dia,
2995
- onSelect: elegirDia,
2996
- month: mes,
2997
- onMonthChange: setMes,
2998
- modifiers: { conEvento },
3058
+ selected: day,
3059
+ onSelect: pickDay,
3060
+ month,
3061
+ onMonthChange: setMonth,
3062
+ modifiers: { withEvent },
2999
3063
  modifiersClassNames: {
3000
- // El punto va bajo el número, no encima del fondo del día: sobre el
3001
- // seleccionado —que es bioluz sólido— un punto de acento desaparece.
3002
- // `after:` en Tailwind v4 ya trae `content: ""`, así que no hace falta
3003
- // declararlo con un valor arbitrario.
3004
- conEvento: cn(
3064
+ // The dot goes under the number, not on top of the day's background:
3065
+ // over the selected one — which is solid biolume — an accent dot
3066
+ // disappears. `after:` in Tailwind v4 already ships `content: ""`, so
3067
+ // there is no need to declare it with an arbitrary value.
3068
+ withEvent: cn(
3005
3069
  "after:bg-accent after:absolute after:bottom-1 after:left-1/2",
3006
3070
  "after:size-1 after:-translate-x-1/2 after:rounded-pill",
3007
3071
  "[&[data-selected=true]]:after:bg-accent-on"
@@ -3016,8 +3080,8 @@ function EventCalendar({
3016
3080
  "aria-labelledby": `${id}-dia`,
3017
3081
  className: "border-hairline rounded-card p-step-md gap-step-md flex min-w-0 flex-1 flex-col border",
3018
3082
  children: [
3019
- /* @__PURE__ */ jsxRuntime.jsx(Text, { as: "h3", variant: "h3", id: `${id}-dia`, className: "first-letter:uppercase", children: formatDay(dia) }),
3020
- delDia.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "gap-step-xs flex flex-col", children: delDia.map((evento) => /* @__PURE__ */ jsxRuntime.jsxs(
3083
+ /* @__PURE__ */ jsxRuntime.jsx(Text, { as: "h3", variant: "h3", id: `${id}-dia`, className: "first-letter:uppercase", children: formatDay(day) }),
3084
+ ofTheDay.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "gap-step-xs flex flex-col", children: ofTheDay.map((event) => /* @__PURE__ */ jsxRuntime.jsxs(
3021
3085
  "li",
3022
3086
  {
3023
3087
  className: "border-hairline gap-step-sm py-step-xs flex items-center border-b last:border-b-0",
@@ -3027,85 +3091,85 @@ function EventCalendar({
3027
3091
  {
3028
3092
  as: "span",
3029
3093
  variant: "meta",
3030
- tone: evento.tone === "warm" ? "warm" : "accent",
3094
+ tone: event.tone === "warm" ? "warm" : "accent",
3031
3095
  className: "shrink-0 tabular-nums",
3032
- children: /* @__PURE__ */ jsxRuntime.jsx("time", { dateTime: evento.start.toISOString(), children: formatTime2(evento.start) })
3096
+ children: /* @__PURE__ */ jsxRuntime.jsx("time", { dateTime: event.start.toISOString(), children: formatTime2(event.start) })
3033
3097
  }
3034
3098
  ),
3035
- /* @__PURE__ */ jsxRuntime.jsx(Text, { as: "span", variant: "ui", className: "min-w-0 flex-1 truncate", children: evento.title }),
3099
+ /* @__PURE__ */ jsxRuntime.jsx(Text, { as: "span", variant: "ui", className: "min-w-0 flex-1 truncate", children: event.title }),
3036
3100
  onUpdateEvent ? /* @__PURE__ */ jsxRuntime.jsx(
3037
3101
  Button,
3038
3102
  {
3039
3103
  variant: "tertiary",
3040
3104
  size: "sm",
3041
- onClick: () => editar(evento),
3042
- "aria-label": `Editar \xAB${evento.title}\xBB`,
3043
- children: "./editar"
3105
+ onClick: () => edit(event),
3106
+ "aria-label": `Editar \xAB${event.title}\xBB`,
3107
+ children: "./edit"
3044
3108
  }
3045
3109
  ) : null,
3046
3110
  onDeleteEvent ? /* @__PURE__ */ jsxRuntime.jsxs(AlertDialog, { children: [
3047
- /* @__PURE__ */ jsxRuntime.jsx(AlertDialogTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "tertiary", size: "sm", "aria-label": `Borrar \xAB${evento.title}\xBB`, children: "./borrar" }) }),
3111
+ /* @__PURE__ */ jsxRuntime.jsx(AlertDialogTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "tertiary", size: "sm", "aria-label": `Borrar \xAB${event.title}\xBB`, children: "./borrar" }) }),
3048
3112
  /* @__PURE__ */ jsxRuntime.jsxs(AlertDialogContent, { children: [
3049
3113
  /* @__PURE__ */ jsxRuntime.jsxs(AlertDialogHeader, { children: [
3050
3114
  /* @__PURE__ */ jsxRuntime.jsxs(AlertDialogTitle, { children: [
3051
3115
  "Borrar \xAB",
3052
- evento.title,
3116
+ event.title,
3053
3117
  "\xBB"
3054
3118
  ] }),
3055
3119
  /* @__PURE__ */ jsxRuntime.jsxs(AlertDialogDescription, { children: [
3056
3120
  "Se borra del ",
3057
- formatDay(evento.start),
3121
+ formatDay(event.start),
3058
3122
  " a las ",
3059
- formatTime2(evento.start),
3123
+ formatTime2(event.start),
3060
3124
  ". No se puede deshacer."
3061
3125
  ] })
3062
3126
  ] }),
3063
3127
  /* @__PURE__ */ jsxRuntime.jsxs(AlertDialogFooter, { children: [
3064
3128
  /* @__PURE__ */ jsxRuntime.jsx(AlertDialogCancel, { children: "Mejor no" }),
3065
- /* @__PURE__ */ jsxRuntime.jsx(AlertDialogAction, { onClick: () => onDeleteEvent(evento.id), children: "Borrar el evento" })
3129
+ /* @__PURE__ */ jsxRuntime.jsx(AlertDialogAction, { onClick: () => onDeleteEvent(event.id), children: "Borrar el event" })
3066
3130
  ] })
3067
3131
  ] })
3068
3132
  ] }) : null
3069
3133
  ]
3070
3134
  },
3071
- evento.id
3135
+ event.id
3072
3136
  )) }) : (
3073
- // `EmptyState` no: lleva una cara de la mascota, y la mascota tiene
3074
- // siete sitios contados donde puede aparecer. Un día vacío de una
3075
- // agenda no es uno de ellos — y en un mes de treinta días, la mitad lo
3076
- // serían.
3137
+ // `EmptyState` is not used: it carries a mascot face, and the mascot
3138
+ // has seven counted places where it may appear. An empty day in a
3139
+ // schedule is not one of them — and in a thirty-day month, half of them
3140
+ // would be.
3077
3141
  /* @__PURE__ */ jsxRuntime.jsx(Text, { as: "p", variant: "ui", tone: "muted", children: emptyMessage })
3078
3142
  ),
3079
- editable ? /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: enviar, className: "gap-step-sm border-hairline pt-step-md flex flex-col border-t", children: [
3143
+ editable ? /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: submit, className: "gap-step-sm border-hairline pt-step-md flex flex-col border-t", children: [
3080
3144
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-xs flex flex-col", children: [
3081
- /* @__PURE__ */ jsxRuntime.jsx(Label2, { htmlFor: `${id}-titulo`, children: "T\xEDtulo" }),
3145
+ /* @__PURE__ */ jsxRuntime.jsx(Label2, { htmlFor: `${id}-title`, children: "T\xEDtulo" }),
3082
3146
  /* @__PURE__ */ jsxRuntime.jsx(
3083
3147
  Input,
3084
3148
  {
3085
- id: `${id}-titulo`,
3086
- value: titulo,
3087
- onChange: (e) => setTitulo(e.target.value),
3149
+ id: `${id}-title`,
3150
+ value: title,
3151
+ onChange: (e) => setTitle(e.target.value),
3088
3152
  placeholder: "Publicar el art\xEDculo",
3089
3153
  required: true
3090
3154
  }
3091
3155
  )
3092
3156
  ] }),
3093
3157
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-xs flex flex-col", children: [
3094
- /* @__PURE__ */ jsxRuntime.jsx(Label2, { htmlFor: `${id}-cuando`, children: "Cu\xE1ndo" }),
3158
+ /* @__PURE__ */ jsxRuntime.jsx(Label2, { htmlFor: `${id}-when`, children: "Cu\xE1ndo" }),
3095
3159
  /* @__PURE__ */ jsxRuntime.jsx(
3096
3160
  DateField,
3097
3161
  {
3098
- id: `${id}-cuando`,
3162
+ id: `${id}-when`,
3099
3163
  withTime: true,
3100
- value: cuando,
3101
- onChange: (e) => setCuando(e.target.value),
3164
+ value: when,
3165
+ onChange: (e) => setWhen(e.target.value),
3102
3166
  required: true
3103
3167
  }
3104
3168
  )
3105
3169
  ] }),
3106
3170
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-sm flex flex-wrap items-center", children: [
3107
- /* @__PURE__ */ jsxRuntime.jsx(Button, { type: "submit", size: "sm", children: editando ? "Guardar" : "A\xF1adir" }),
3108
- editando ? /* @__PURE__ */ jsxRuntime.jsx(Button, { type: "button", variant: "tertiary", size: "sm", onClick: cancelar, children: "./cancelar" }) : null
3171
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { type: "submit", size: "sm", children: editing ? "Guardar" : "A\xF1adir" }),
3172
+ editing ? /* @__PURE__ */ jsxRuntime.jsx(Button, { type: "button", variant: "tertiary", size: "sm", onClick: cancel, children: "./cancel" }) : null
3109
3173
  ] })
3110
3174
  ] }) : null
3111
3175
  ]
@@ -3121,43 +3185,56 @@ function Footer({
3121
3185
  className,
3122
3186
  ...props
3123
3187
  }) {
3188
+ const signature = /* @__PURE__ */ jsxRuntime.jsxs(Text, { variant: "meta", tone: "muted", as: "p", className: "ml-auto shrink-0", children: [
3189
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { "aria-hidden": "true", className: "text-accent", children: [
3190
+ "$",
3191
+ " "
3192
+ ] }),
3193
+ "cd ~/",
3194
+ naming.domain,
3195
+ "/",
3196
+ year,
3197
+ /* @__PURE__ */ jsxRuntime.jsx(
3198
+ "span",
3199
+ {
3200
+ "aria-hidden": "true",
3201
+ className: "bg-accent motion-safe:caret ml-[0.2em] inline-block h-[1em] w-[0.5em] translate-y-[0.15em]"
3202
+ }
3203
+ )
3204
+ ] });
3205
+ const rows = [
3206
+ brand2 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center", children: brand2 }, "marca") : null,
3207
+ children ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "gap-step-md flex flex-wrap items-center", children }, "enlaces") : null,
3208
+ social && social.length > 0 ? (
3209
+ // 18px of separation, from the document. It is not a `spacing` step: it is
3210
+ // the rhythm of a row of icons, not that of a page.
3211
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "flex items-center gap-[18px]", children: social.map((socialLink) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(
3212
+ "a",
3213
+ {
3214
+ href: socialLink.href,
3215
+ "aria-label": socialLink.label,
3216
+ className: cn(
3217
+ "text-text-muted hover:text-accent transition-standard block cursor-pointer text-[19px]",
3218
+ "rounded-chip focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent"
3219
+ ),
3220
+ children: socialLink.icon
3221
+ }
3222
+ ) }, socialLink.href)) }, "socialLinks")
3223
+ ) : null
3224
+ ].filter(Boolean);
3225
+ const [first, ...rest] = rows;
3124
3226
  return /* @__PURE__ */ jsxRuntime.jsx("footer", { className: cn("border-hairline w-full border-t", className), ...props, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-wide px-step-md py-step-xl gap-step-lg mx-auto flex flex-col", children: [
3125
- brand2 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center", children: brand2 }) : null,
3126
- children ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "gap-step-md flex flex-wrap items-center", children }) : null,
3127
3227
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-md flex flex-wrap items-center", children: [
3128
- social && social.length > 0 ? (
3129
- // 18px de separación, del documento. No es un escalón de `spacing`:
3130
- // es el ritmo de una fila de iconos, no el de una página.
3131
- /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "flex items-center gap-[18px]", children: social.map((red) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(
3132
- "a",
3133
- {
3134
- href: red.href,
3135
- "aria-label": red.label,
3136
- className: cn(
3137
- "text-text-muted hover:text-accent transition-standard block cursor-pointer text-[19px]",
3138
- "rounded-chip focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent"
3139
- ),
3140
- children: red.icon
3141
- }
3142
- ) }, red.href)) })
3143
- ) : null,
3144
- /* @__PURE__ */ jsxRuntime.jsxs(Text, { variant: "meta", tone: "muted", as: "p", className: "ml-auto", children: [
3145
- /* @__PURE__ */ jsxRuntime.jsxs("span", { "aria-hidden": "true", className: "text-accent", children: [
3146
- "$",
3147
- " "
3148
- ] }),
3149
- "cd ~/",
3150
- naming.domain,
3151
- "/",
3152
- year
3153
- ] })
3154
- ] })
3228
+ first ?? null,
3229
+ signature
3230
+ ] }),
3231
+ rest
3155
3232
  ] }) });
3156
3233
  }
3157
3234
  function FooterLink({ asChild = false, className, ...props }) {
3158
- const Raiz = asChild ? reactSlot.Slot : "a";
3235
+ const Root18 = asChild ? reactSlot.Slot : "a";
3159
3236
  return /* @__PURE__ */ jsxRuntime.jsx(
3160
- Raiz,
3237
+ Root18,
3161
3238
  {
3162
3239
  className: cn(
3163
3240
  "font-mono text-meta text-text-secondary hover:text-accent transition-standard cursor-pointer",
@@ -3175,24 +3252,24 @@ function Hero({
3175
3252
  action,
3176
3253
  pose,
3177
3254
  basePath,
3178
- variant = "cabecera",
3255
+ variant = "header",
3179
3256
  className,
3180
3257
  ...props
3181
3258
  }) {
3182
- const centrado = variant === "centrado";
3259
+ const centered = variant === "centered";
3183
3260
  return /* @__PURE__ */ jsxRuntime.jsxs(
3184
3261
  "section",
3185
3262
  {
3186
3263
  className: cn(
3187
- "degradado-hero rounded-panel border-hairline relative overflow-hidden border",
3188
- // 44 arriba, 40 a los lados y abajo. Del documento.
3264
+ "gradient-hero rounded-panel border-hairline relative overflow-hidden border",
3265
+ // 44 top, 40 on the sides and bottom. From the document.
3189
3266
  "px-step-xl pt-[44px] pb-step-xl",
3190
3267
  className
3191
3268
  ),
3192
3269
  ...props,
3193
3270
  children: [
3194
- centrado && pose ? /* @__PURE__ */ jsxRuntime.jsx(
3195
- Mascota,
3271
+ centered && pose ? /* @__PURE__ */ jsxRuntime.jsx(
3272
+ Mascot,
3196
3273
  {
3197
3274
  pose,
3198
3275
  basePath,
@@ -3204,7 +3281,7 @@ function Hero({
3204
3281
  {
3205
3282
  className: cn(
3206
3283
  "gap-step-md flex flex-col",
3207
- centrado ? "items-center text-center" : "md:max-w-[62%]"
3284
+ centered ? "items-center text-center" : "md:max-w-[62%]"
3208
3285
  ),
3209
3286
  children: [
3210
3287
  eyebrow ? /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "eyebrow", tone: "accent", as: "p", children: eyebrow }) : null,
@@ -3215,7 +3292,7 @@ function Hero({
3215
3292
  {
3216
3293
  className: cn(
3217
3294
  "gap-step-sm mt-step-xs flex flex-wrap items-center",
3218
- centrado && "justify-center"
3295
+ centered && "justify-center"
3219
3296
  ),
3220
3297
  children: action
3221
3298
  }
@@ -3223,14 +3300,14 @@ function Hero({
3223
3300
  ]
3224
3301
  }
3225
3302
  ),
3226
- !centrado && pose ? /* @__PURE__ */ jsxRuntime.jsx(
3227
- Mascota,
3303
+ !centered && pose ? /* @__PURE__ */ jsxRuntime.jsx(
3304
+ Mascot,
3228
3305
  {
3229
3306
  pose,
3230
3307
  basePath,
3231
3308
  className: cn(
3232
- // En móvil, en el flujo y bajo los botones. Desde md, sangrando por
3233
- // la esquina inferior derecha.
3309
+ // On mobile, in the flow and below the buttons. From md up, bleeding
3310
+ // off the bottom-right corner.
3234
3311
  "mt-step-lg mx-auto w-40",
3235
3312
  "md:mt-0 md:absolute md:right-0 md:-bottom-4 md:mx-0 md:w-64"
3236
3313
  )
@@ -3251,7 +3328,7 @@ function LinkRow({
3251
3328
  const rel = external ? "noopener noreferrer" : props.rel;
3252
3329
  const target = external ? "_blank" : props.target;
3253
3330
  return /* @__PURE__ */ jsxRuntime.jsx(
3254
- Tarjeta,
3331
+ CardShell,
3255
3332
  {
3256
3333
  className: cn("px-step-md py-step-sm", className),
3257
3334
  ...props,
@@ -3280,8 +3357,8 @@ function Nav({ brand: brand2, children, actions, className, ...props }) {
3280
3357
  {
3281
3358
  className: cn(
3282
3359
  "sticky top-0 z-50 w-full",
3283
- // abismo al 86 % con 14px de desenfoque detrás. El alfa va sobre el
3284
- // token, así que sigue el modo: en claro es papel al 86 %.
3360
+ // Abyss at 86 % with 14px of blur behind. The alpha rides on the token,
3361
+ // so it follows the mode: in light it is paper at 86 %.
3285
3362
  "bg-background/86 backdrop-blur-[14px]",
3286
3363
  "border-hairline border-b",
3287
3364
  className
@@ -3299,9 +3376,9 @@ function Nav({ brand: brand2, children, actions, className, ...props }) {
3299
3376
  );
3300
3377
  }
3301
3378
  function NavItem({ active = false, asChild = false, className, children, ...props }) {
3302
- const Raiz = asChild ? reactSlot.Slot : "a";
3379
+ const Root18 = asChild ? reactSlot.Slot : "a";
3303
3380
  return /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsxs(
3304
- Raiz,
3381
+ Root18,
3305
3382
  {
3306
3383
  "aria-current": active ? "page" : void 0,
3307
3384
  className: cn(
@@ -3323,39 +3400,49 @@ function NavItem({ active = false, asChild = false, className, children, ...prop
3323
3400
  function NewsletterForm({
3324
3401
  title,
3325
3402
  description,
3326
- state = "reposo",
3403
+ state = "idle",
3327
3404
  onSubmitEmail,
3328
- successMessage = "Ya est\xE1s dentro. Te llega un correo cada dos semanas, y nada m\xE1s.",
3329
- errorMessage = "No se pudo suscribir ese correo. Rev\xEDsalo y vuelve a intentar.",
3405
+ successMessage = "Ya est\xE1s dentro. Te llega un email cada dos semanas, y nada m\xE1s.",
3406
+ errorMessage = "No se pudo suscribir ese email. Rev\xEDsalo y vuelve a intentar.",
3330
3407
  disclaimer,
3331
- expresion,
3408
+ expression,
3332
3409
  basePath,
3333
3410
  submitLabel = "Suscribirme",
3334
- placeholder = "tu@correo.dev",
3335
- fieldLabel = "Correo electr\xF3nico",
3411
+ placeholder = "tu@email.dev",
3412
+ fieldLabel = "Email electr\xF3nico",
3336
3413
  nameField = false,
3337
3414
  nameLabel = "Nombre",
3338
3415
  namePlaceholder = "C\xF3mo te llamas",
3339
3416
  nameInputProps,
3417
+ aside,
3418
+ resetOnSuccess = true,
3419
+ onFieldChange,
3420
+ fieldErrors,
3340
3421
  className,
3341
3422
  ...props
3342
3423
  }) {
3343
3424
  const id = react.useId();
3344
- const enviando = state === "enviando";
3425
+ const form = react.useRef(null);
3426
+ const sending = state === "sending";
3345
3427
  const error = state === "error";
3346
- function enviar(evento) {
3347
- evento.preventDefault();
3348
- const datos = new FormData(evento.currentTarget);
3349
- const correo = String(datos.get("email") ?? "");
3350
- const nombre = nameField ? String(datos.get("name") ?? "") : void 0;
3351
- onSubmitEmail?.(correo, nombre);
3428
+ const nameError = fieldErrors?.name;
3429
+ const emailError = fieldErrors?.email;
3430
+ react.useEffect(() => {
3431
+ if (state === "success" && resetOnSuccess) form.current?.reset();
3432
+ }, [state, resetOnSuccess]);
3433
+ function submit(event) {
3434
+ event.preventDefault();
3435
+ const data = new FormData(event.currentTarget);
3436
+ const email = String(data.get("email") ?? "");
3437
+ const name = nameField ? String(data.get("name") ?? "") : void 0;
3438
+ onSubmitEmail?.(email, name);
3352
3439
  }
3353
- const boton = /* @__PURE__ */ jsxRuntime.jsx(
3440
+ const button2 = /* @__PURE__ */ jsxRuntime.jsx(
3354
3441
  Button,
3355
3442
  {
3356
3443
  type: "submit",
3357
3444
  variant: "conversion",
3358
- loading: enviando,
3445
+ loading: sending,
3359
3446
  className: cn("disabled:opacity-60", nameField && "sm:self-start"),
3360
3447
  children: submitLabel
3361
3448
  }
@@ -3364,65 +3451,79 @@ function NewsletterForm({
3364
3451
  "section",
3365
3452
  {
3366
3453
  className: cn(
3367
- "degradado-seccion rounded-panel border-hairline p-step-lg gap-step-md flex flex-col border",
3454
+ "gradient-section rounded-panel border-hairline p-step-lg border",
3455
+ // Two columns from `md` up when there is an illustration, one otherwise.
3456
+ // `items-center` centres the pose against the block of text and form
3457
+ // rather than against the panel, which is what the blog was doing by
3458
+ // hand with an absolute position and a reserved `pr`.
3459
+ aside ? "gap-step-lg flex flex-col md:flex-row md:items-center" : "gap-step-md flex flex-col",
3368
3460
  className
3369
3461
  ),
3370
3462
  ...props,
3371
3463
  children: [
3372
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-xs flex flex-col", children: [
3373
- /* @__PURE__ */ jsxRuntime.jsx(Text, { as: "h2", variant: "h3", children: title }),
3374
- description ? /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "ui", tone: "secondary", children: description }) : null
3375
- ] }),
3376
- /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: enviar, className: "gap-step-sm flex flex-col", noValidate: true, children: [
3377
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-sm flex flex-col sm:flex-row sm:items-end", children: [
3378
- nameField ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-xs flex flex-1 flex-col", children: [
3379
- /* @__PURE__ */ jsxRuntime.jsx(Label2, { htmlFor: `${id}-nombre`, children: nameLabel }),
3380
- /* @__PURE__ */ jsxRuntime.jsx(
3381
- Input,
3382
- {
3383
- id: `${id}-nombre`,
3384
- name: "name",
3385
- type: "text",
3386
- autoComplete: "name",
3387
- required: true,
3388
- placeholder: namePlaceholder,
3389
- disabled: enviando,
3390
- ...nameInputProps
3391
- }
3392
- )
3393
- ] }) : null,
3394
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-xs flex flex-1 flex-col", children: [
3395
- /* @__PURE__ */ jsxRuntime.jsx(Label2, { htmlFor: id, children: fieldLabel }),
3396
- /* @__PURE__ */ jsxRuntime.jsx(
3397
- Input,
3398
- {
3399
- id,
3400
- name: "email",
3401
- type: "email",
3402
- autoComplete: "email",
3403
- required: true,
3404
- placeholder,
3405
- disabled: enviando,
3406
- invalid: error,
3407
- "aria-describedby": state === "exito" || error ? `${id}-aviso` : void 0
3408
- }
3409
- )
3464
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("gap-step-md flex min-w-0 flex-1 flex-col"), children: [
3465
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-xs flex flex-col", children: [
3466
+ /* @__PURE__ */ jsxRuntime.jsx(Text, { as: "h2", variant: "h3", children: title }),
3467
+ description ? /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "ui", tone: "secondary", children: description }) : null
3468
+ ] }),
3469
+ /* @__PURE__ */ jsxRuntime.jsxs("form", { ref: form, onSubmit: submit, className: "gap-step-sm flex flex-col", noValidate: true, children: [
3470
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-sm flex flex-col sm:flex-row sm:items-start", children: [
3471
+ nameField ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-xs flex flex-1 flex-col", children: [
3472
+ /* @__PURE__ */ jsxRuntime.jsx(Label2, { htmlFor: `${id}-name`, children: nameLabel }),
3473
+ /* @__PURE__ */ jsxRuntime.jsx(
3474
+ Input,
3475
+ {
3476
+ id: `${id}-name`,
3477
+ name: "name",
3478
+ type: "text",
3479
+ autoComplete: "name",
3480
+ required: true,
3481
+ placeholder: namePlaceholder,
3482
+ disabled: sending,
3483
+ invalid: Boolean(nameError),
3484
+ "aria-describedby": nameError ? `${id}-name-error` : void 0,
3485
+ onChange: (e) => onFieldChange?.("name", e.currentTarget.value),
3486
+ ...nameInputProps
3487
+ }
3488
+ ),
3489
+ nameError ? /* @__PURE__ */ jsxRuntime.jsx(Text, { id: `${id}-name-error`, variant: "label", tone: "error", as: "p", role: "alert", children: nameError }) : null
3490
+ ] }) : null,
3491
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-xs flex flex-1 flex-col", children: [
3492
+ /* @__PURE__ */ jsxRuntime.jsx(Label2, { htmlFor: id, children: fieldLabel }),
3493
+ /* @__PURE__ */ jsxRuntime.jsx(
3494
+ Input,
3495
+ {
3496
+ id,
3497
+ name: "email",
3498
+ type: "email",
3499
+ autoComplete: "email",
3500
+ required: true,
3501
+ placeholder,
3502
+ disabled: sending,
3503
+ invalid: error || Boolean(emailError),
3504
+ onChange: (e) => onFieldChange?.("email", e.currentTarget.value),
3505
+ "aria-describedby": emailError ? `${id}-email-error` : state === "success" || error ? `${id}-notice` : void 0
3506
+ }
3507
+ ),
3508
+ emailError ? /* @__PURE__ */ jsxRuntime.jsx(Text, { id: `${id}-email-error`, variant: "label", tone: "error", as: "p", role: "alert", children: emailError }) : null
3509
+ ] }),
3510
+ nameField ? null : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sm:mt-[26px]", children: button2 })
3410
3511
  ] }),
3411
- nameField ? null : boton
3512
+ nameField ? button2 : null
3412
3513
  ] }),
3413
- nameField ? boton : null
3514
+ state === "success" ? /* @__PURE__ */ jsxRuntime.jsx(Alert, { id: `${id}-notice`, variant: "success", emphasis: "strong", children: successMessage }) : null,
3515
+ error ? /* @__PURE__ */ jsxRuntime.jsx(Alert, { id: `${id}-notice`, variant: "error", emphasis: "strong", children: errorMessage }) : null,
3516
+ disclaimer ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-sm flex items-center", children: [
3517
+ expression ? /* @__PURE__ */ jsxRuntime.jsx(MascotFace, { expression, basePath, className: "w-10 max-w-none" }) : null,
3518
+ /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "label", tone: "muted", as: "p", className: "font-normal", children: disclaimer })
3519
+ ] }) : null
3414
3520
  ] }),
3415
- state === "exito" ? /* @__PURE__ */ jsxRuntime.jsx(Alert, { id: `${id}-aviso`, variant: "success", enfasis: "fuerte", children: successMessage }) : null,
3416
- error ? /* @__PURE__ */ jsxRuntime.jsx(Alert, { id: `${id}-aviso`, variant: "error", enfasis: "fuerte", children: errorMessage }) : null,
3417
- disclaimer ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-sm flex items-center", children: [
3418
- expresion ? /* @__PURE__ */ jsxRuntime.jsx(CaraDeMascota, { expresion, basePath, className: "w-10 max-w-none" }) : null,
3419
- /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "label", tone: "muted", as: "p", className: "font-normal", children: disclaimer })
3420
- ] }) : null
3521
+ aside ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "shrink-0 md:max-w-[42%]", children: aside }) : null
3421
3522
  ]
3422
3523
  }
3423
3524
  );
3424
3525
  }
3425
- var cabecera = classVarianceAuthority.cva("gap-step-sm flex flex-col", {
3526
+ var header = classVarianceAuthority.cva("gap-step-sm flex flex-col", {
3426
3527
  variants: {
3427
3528
  size: {
3428
3529
  display: "py-section",
@@ -3441,7 +3542,7 @@ function PageHeader({
3441
3542
  className,
3442
3543
  ...props
3443
3544
  }) {
3444
- return /* @__PURE__ */ jsxRuntime.jsxs("header", { className: cn(cabecera({ size: size2 }), className), ...props, children: [
3545
+ return /* @__PURE__ */ jsxRuntime.jsxs("header", { className: cn(header({ size: size2 }), className), ...props, children: [
3445
3546
  eyebrow ? /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "eyebrow", tone: "accent", as: "p", children: eyebrow }) : null,
3446
3547
  /* @__PURE__ */ jsxRuntime.jsx(Text, { as, variant: size2 === "display" ? "display" : "h1", className: "max-w-measure", children: title }),
3447
3548
  description ? /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "body", tone: "secondary", children: description }) : null,
@@ -3455,33 +3556,33 @@ function ScrollingProgressBar({
3455
3556
  className,
3456
3557
  ...props
3457
3558
  }) {
3458
- const [avance, setAvance] = react.useState(0);
3459
- const pendiente = react.useRef(0);
3559
+ const [seek, setAvance] = react.useState(0);
3560
+ const pendingOne = react.useRef(0);
3460
3561
  react.useEffect(() => {
3461
- const medir = () => {
3462
- pendiente.current = 0;
3463
- const elemento = target?.current;
3464
- const inicio = elemento ? elemento.getBoundingClientRect().top + scrollY : 0;
3465
- const alto = elemento ? elemento.offsetHeight : document.documentElement.scrollHeight;
3466
- const recorrible = alto - innerHeight;
3467
- if (recorrible <= 0) {
3468
- setAvance(scrollY >= inicio ? 100 : 0);
3562
+ const measure = () => {
3563
+ pendingOne.current = 0;
3564
+ const element = target?.current;
3565
+ const start = element ? element.getBoundingClientRect().top + scrollY : 0;
3566
+ const height = element ? element.offsetHeight : document.documentElement.scrollHeight;
3567
+ const walkable = height - innerHeight;
3568
+ if (walkable <= 0) {
3569
+ setAvance(scrollY >= start ? 100 : 0);
3469
3570
  return;
3470
3571
  }
3471
- const recorrido = scrollY - inicio;
3472
- setAvance(Math.min(100, Math.max(0, recorrido / recorrible * 100)));
3572
+ const walk = scrollY - start;
3573
+ setAvance(Math.min(100, Math.max(0, walk / walkable * 100)));
3473
3574
  };
3474
- const alMoverse = () => {
3475
- if (pendiente.current) return;
3476
- pendiente.current = requestAnimationFrame(medir);
3575
+ const onMove = () => {
3576
+ if (pendingOne.current) return;
3577
+ pendingOne.current = requestAnimationFrame(measure);
3477
3578
  };
3478
- medir();
3479
- addEventListener("scroll", alMoverse, { passive: true });
3480
- addEventListener("resize", alMoverse);
3579
+ measure();
3580
+ addEventListener("scroll", onMove, { passive: true });
3581
+ addEventListener("resize", onMove);
3481
3582
  return () => {
3482
- if (pendiente.current) cancelAnimationFrame(pendiente.current);
3483
- removeEventListener("scroll", alMoverse);
3484
- removeEventListener("resize", alMoverse);
3583
+ if (pendingOne.current) cancelAnimationFrame(pendingOne.current);
3584
+ removeEventListener("scroll", onMove);
3585
+ removeEventListener("resize", onMove);
3485
3586
  };
3486
3587
  }, [target]);
3487
3588
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -3498,7 +3599,7 @@ function ScrollingProgressBar({
3498
3599
  "div",
3499
3600
  {
3500
3601
  className: cn("h-full", tone === "warm" ? "bg-warm" : "bg-accent"),
3501
- style: { width: `${avance}%` }
3602
+ style: { width: `${seek}%` }
3502
3603
  }
3503
3604
  )
3504
3605
  }
@@ -3512,20 +3613,21 @@ function SidebarItem({
3512
3613
  children,
3513
3614
  ...props
3514
3615
  }) {
3515
- const Raiz = asChild ? reactSlot.Slot : "a";
3616
+ const Root18 = asChild ? reactSlot.Slot : "a";
3516
3617
  return /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsxs(
3517
- Raiz,
3618
+ Root18,
3518
3619
  {
3519
3620
  "aria-current": active ? "page" : void 0,
3520
3621
  className: cn(
3521
3622
  "rounded-chip px-step-sm gap-step-xs flex cursor-pointer items-center py-1.5",
3522
3623
  "font-mono text-meta transition-standard",
3523
3624
  "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent",
3524
- // El activo es fondo `surfaceRaised` y tinta primaria, igual que
3525
- // `TabsTrigger`. NO bioluz: `tokens.ts` lo dice explícito — acento
3526
- // claro pasa sobre background y sobre surface, pero sobre
3527
- // surfaceRaised mide 4.2 y no es color de texto ahí. El acento se
3528
- // queda en el `▸`, que es decorativo y le aplica el umbral de 3:1.
3625
+ // The active item is `surfaceRaised` with primary ink, same as
3626
+ // `TabsTrigger`. NOT biolume: `tokens.ts` says so explicitly — light
3627
+ // accent passes over background and over surface, but over
3628
+ // surfaceRaised it measures 4.2 and is not a text color there. The
3629
+ // accent stays on the `▸`, which is decorative and gets the 3:1
3630
+ // threshold.
3529
3631
  active ? "bg-surface-raised text-text-primary" : "text-text-secondary hover:text-text-primary hover:bg-surface-raised",
3530
3632
  className
3531
3633
  ),
@@ -3546,7 +3648,7 @@ function SidebarNav({
3546
3648
  className,
3547
3649
  ...props
3548
3650
  }) {
3549
- const pie = [version, branch].filter(Boolean);
3651
+ const footer = [version, branch].filter(Boolean);
3550
3652
  return /* @__PURE__ */ jsxRuntime.jsxs(
3551
3653
  "nav",
3552
3654
  {
@@ -3559,7 +3661,7 @@ function SidebarNav({
3559
3661
  children: [
3560
3662
  title ? /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "eyebrow", tone: "muted", as: "p", className: "px-step-sm", children: title }) : null,
3561
3663
  /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "gap-0.5 flex flex-1 flex-col", children }),
3562
- pie.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "meta", tone: "muted", as: "p", className: "border-hairline px-step-sm pt-step-sm border-t", children: pie.map((p, i) => /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
3664
+ footer.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "meta", tone: "muted", as: "p", className: "border-hairline px-step-sm pt-step-sm border-t", children: footer.map((p, i) => /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
3563
3665
  i > 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: " \xB7 " }) : null,
3564
3666
  p
3565
3667
  ] }, i)) }) : null
@@ -3577,12 +3679,12 @@ function Stat({
3577
3679
  className,
3578
3680
  ...props
3579
3681
  }) {
3580
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn(SUPERFICIE_TARJETA, "p-step-lg gap-step-xs flex flex-col", className), ...props, children: [
3682
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn(CARD_SURFACE, "p-step-lg gap-step-xs flex flex-col", className), ...props, children: [
3581
3683
  /* @__PURE__ */ jsxRuntime.jsxs(Text, { variant: "eyebrow", tone: "muted", as: "p", className: "gap-step-xs flex items-center", children: [
3582
3684
  icon ? (
3583
- // `aria-hidden` no hace falta: los glifos del sistema ya lo traen
3584
- // puesto. Lo que sí hace falta es que no encoja al lado de un título
3585
- // largo, porque un icono aplastado se lee como otro icono.
3685
+ // `aria-hidden` is not needed: the system's glyphs already carry it.
3686
+ // What IS needed is that it does not shrink next to a long title,
3687
+ // because a squashed icon reads as a different icon.
3586
3688
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0", children: icon })
3587
3689
  ) : null,
3588
3690
  label
@@ -3608,28 +3710,58 @@ function TalkCard({
3608
3710
  location,
3609
3711
  status,
3610
3712
  description,
3713
+ resources,
3611
3714
  className,
3612
3715
  ...props
3613
3716
  }) {
3614
- const pie = [
3615
- date ? /* @__PURE__ */ jsxRuntime.jsx("time", { dateTime, children: date }, "fecha") : null,
3616
- location ? /* @__PURE__ */ jsxRuntime.jsx("span", { children: location }, "lugar") : null
3717
+ const footer = [
3718
+ date ? /* @__PURE__ */ jsxRuntime.jsx("time", { dateTime, children: date }, "date") : null,
3719
+ location ? /* @__PURE__ */ jsxRuntime.jsx("span", { children: location }, "location") : null
3617
3720
  ].filter(Boolean);
3618
- return /* @__PURE__ */ jsxRuntime.jsx(Tarjeta, { className: cn("p-step-lg", className), ...props, children: /* @__PURE__ */ jsxRuntime.jsxs("article", { className: "gap-step-sm flex h-full flex-col", children: [
3721
+ const body = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3619
3722
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-sm flex items-start justify-between", children: [
3620
3723
  /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "eyebrow", tone: "accent", as: "p", children: event }),
3621
3724
  status ? /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "neutral", children: status }) : null
3622
3725
  ] }),
3623
- /* @__PURE__ */ jsxRuntime.jsx(Text, { as: "h3", variant: "h3", className: "group-hover:text-accent transition-standard", children: title }),
3726
+ /* @__PURE__ */ jsxRuntime.jsx(
3727
+ Text,
3728
+ {
3729
+ as: "h3",
3730
+ variant: "h3",
3731
+ className: cn(!resources && "group-hover:text-accent transition-standard"),
3732
+ children: title
3733
+ }
3734
+ ),
3624
3735
  description ? /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "ui", tone: "secondary", className: "line-clamp-2", children: description }) : null,
3625
- pie.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "label", tone: "muted", as: "p", className: "gap-step-xs mt-auto flex items-center pt-step-sm", children: pie.map((m, i) => /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "gap-step-xs flex items-center", children: [
3626
- i > 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: "\xB7" }) : null,
3627
- m
3628
- ] }, i)) }) : null
3629
- ] }) });
3736
+ footer.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
3737
+ Text,
3738
+ {
3739
+ variant: "label",
3740
+ tone: "muted",
3741
+ as: "p",
3742
+ className: cn("gap-step-xs flex items-center pt-step-sm", !resources && "mt-auto"),
3743
+ children: footer.map((m, i) => /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "gap-step-xs flex items-center", children: [
3744
+ i > 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: "\xB7" }) : null,
3745
+ m
3746
+ ] }, i))
3747
+ }
3748
+ ) : null,
3749
+ resources ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "gap-step-md mt-auto flex flex-wrap items-center pt-step-sm", children: resources }) : null
3750
+ ] });
3751
+ if (resources) {
3752
+ return /* @__PURE__ */ jsxRuntime.jsx(
3753
+ "article",
3754
+ {
3755
+ className: cn(CARD_SURFACE, "p-step-lg gap-step-sm flex h-full flex-col", className),
3756
+ ...props,
3757
+ children: body
3758
+ }
3759
+ );
3760
+ }
3761
+ return /* @__PURE__ */ jsxRuntime.jsx(CardShell, { className: cn("p-step-lg", className), ...props, children: /* @__PURE__ */ jsxRuntime.jsx("article", { className: "gap-step-sm flex h-full flex-col", children: body }) });
3630
3762
  }
3631
3763
  function ThemeToggle({
3632
- label = "Cambiar de tema",
3764
+ label = "Cambiar de theme",
3633
3765
  onThemeChange,
3634
3766
  variant = "secondary",
3635
3767
  size: size2 = "icon",
@@ -3644,20 +3776,20 @@ function ThemeToggle({
3644
3776
  size: size2,
3645
3777
  "aria-label": label,
3646
3778
  className: cn("[&_svg]:size-[19px]", className),
3647
- onClick: () => onThemeChange?.(alternarTema()),
3779
+ onClick: () => onThemeChange?.(toggleTheme()),
3648
3780
  ...props,
3649
3781
  children: [
3650
- /* @__PURE__ */ jsxRuntime.jsx(Sol, { className: "light:hidden" }),
3651
- /* @__PURE__ */ jsxRuntime.jsx(Luna, { className: "hidden light:block" })
3782
+ /* @__PURE__ */ jsxRuntime.jsx(Sun, { className: "light:hidden" }),
3783
+ /* @__PURE__ */ jsxRuntime.jsx(Moon, { className: "hidden light:block" })
3652
3784
  ]
3653
3785
  }
3654
3786
  );
3655
3787
  }
3656
- function useTema() {
3657
- return react.useSyncExternalStore(suscribirse2, temaActual, servidor);
3788
+ function useTheme() {
3789
+ return react.useSyncExternalStore(subscribe2, currentTheme, server);
3658
3790
  }
3659
- var suscribirse2 = (avisar) => escucharTema(() => avisar());
3660
- var servidor = () => "dark";
3791
+ var subscribe2 = (warn) => watchTheme(() => warn());
3792
+ var server = () => "dark";
3661
3793
  function TableOfContents({
3662
3794
  items,
3663
3795
  title = "En esta p\xE1gina",
@@ -3669,35 +3801,35 @@ function TableOfContents({
3669
3801
  return /* @__PURE__ */ jsxRuntime.jsxs("nav", { "aria-label": typeof title === "string" ? title : "En esta p\xE1gina", className: cn("gap-step-sm flex flex-col", className), ...props, children: [
3670
3802
  /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "eyebrow", tone: "muted", as: "p", children: title }),
3671
3803
  /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "border-hairline gap-step-xs flex flex-col border-l", children: items.map((item2) => {
3672
- const activo = item2.href === activeHref;
3673
- const clases = cn(
3804
+ const active = item2.href === activeHref;
3805
+ const classes = cn(
3674
3806
  "px-step-sm -ml-px block border-l",
3675
3807
  "font-sans text-label transition-standard",
3676
3808
  "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent",
3677
3809
  "cursor-pointer",
3678
3810
  item2.nested && "pl-step-lg",
3679
3811
  "border-transparent text-text-secondary hover:text-text-primary",
3680
- // El hover del activo se declara con las dos variantes juntas para
3681
- // que gane al `hover:` de arriba por especificidad y no por el orden
3682
- // en que Tailwind decida emitir las reglas.
3812
+ // The active hover is declared with both variants together so it
3813
+ // beats the `hover:` above by specificity and not by the order
3814
+ // Tailwind happens to emit the rules in.
3683
3815
  "aria-[current]:border-accent aria-[current]:text-accent aria-[current]:hover:text-accent"
3684
3816
  );
3685
- return /* @__PURE__ */ jsxRuntime.jsx("li", { children: linkAsChild ? /* @__PURE__ */ jsxRuntime.jsx(reactSlot.Slot, { className: clases, "aria-current": activo ? "location" : void 0, children: linkAsChild({ href: item2.href, children: item2.label }) }) : /* @__PURE__ */ jsxRuntime.jsx("a", { href: item2.href, className: clases, "aria-current": activo ? "location" : void 0, children: item2.label }) }, item2.href);
3817
+ return /* @__PURE__ */ jsxRuntime.jsx("li", { children: linkAsChild ? /* @__PURE__ */ jsxRuntime.jsx(reactSlot.Slot, { className: classes, "aria-current": active ? "location" : void 0, children: linkAsChild({ href: item2.href, children: item2.label }) }) : /* @__PURE__ */ jsxRuntime.jsx("a", { href: item2.href, className: classes, "aria-current": active ? "location" : void 0, children: item2.label }) }, item2.href);
3686
3818
  }) })
3687
3819
  ] });
3688
3820
  }
3689
- function Isotipo({
3690
- sobre = "oscuro",
3691
- basePath = RUTA_ASSETS,
3821
+ function Isotype({
3822
+ background = "dark",
3823
+ basePath = ASSETS_PATH,
3692
3824
  alt = "",
3693
3825
  className,
3694
3826
  ...props
3695
3827
  }) {
3696
- const archivo = sobre === "oscuro" ? aletas.espuma : aletas.color;
3828
+ const file = background === "dark" ? fins.foam : fins.color;
3697
3829
  return /* @__PURE__ */ jsxRuntime.jsx(
3698
3830
  "img",
3699
3831
  {
3700
- src: `${basePath}/${archivo}`,
3832
+ src: `${basePath}/${file}`,
3701
3833
  alt,
3702
3834
  width: 147,
3703
3835
  height: 111,
@@ -3707,38 +3839,38 @@ function Isotipo({
3707
3839
  );
3708
3840
  }
3709
3841
  function Logo({
3710
- sobre = "oscuro",
3711
- basePath = RUTA_ASSETS,
3712
- soloIsotipo = false,
3713
- conLema = false,
3842
+ background = "dark",
3843
+ basePath = ASSETS_PATH,
3844
+ isotypeOnly = false,
3845
+ withTagline = false,
3714
3846
  className,
3715
3847
  ...props
3716
3848
  }) {
3717
- const conAleta = /* @__PURE__ */ jsxRuntime.jsx(
3718
- Isotipo,
3849
+ const withFin = /* @__PURE__ */ jsxRuntime.jsx(
3850
+ Isotype,
3719
3851
  {
3720
- sobre,
3852
+ background,
3721
3853
  basePath,
3722
- alt: soloIsotipo ? naming.wordmark : "",
3723
- className: conLema ? "h-9 w-auto" : "h-7 w-auto"
3854
+ alt: isotypeOnly ? naming.wordmark : "",
3855
+ className: withTagline ? "h-9 w-auto" : "h-7 w-auto"
3724
3856
  }
3725
3857
  );
3726
3858
  const wordmark = /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-ui font-display text-text-primary font-bold tracking-[-0.02em]", children: naming.wordmark });
3727
- if (soloIsotipo) {
3728
- return /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("gap-step-xs inline-flex items-center", className), ...props, children: conAleta });
3859
+ if (isotypeOnly) {
3860
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("gap-step-xs inline-flex items-center", className), ...props, children: withFin });
3729
3861
  }
3730
- if (conLema) {
3862
+ if (withTagline) {
3731
3863
  return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: cn("gap-step-sm inline-flex items-center", className), ...props, children: [
3732
- conAleta,
3864
+ withFin,
3733
3865
  /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", className: "bg-hairline h-8 w-px shrink-0" }),
3734
3866
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 flex-col", children: [
3735
3867
  wordmark,
3736
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-eyebrow font-mono text-text-muted uppercase", children: tagline.corto })
3868
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-eyebrow font-mono text-text-muted uppercase", children: tagline.short })
3737
3869
  ] })
3738
3870
  ] });
3739
3871
  }
3740
3872
  return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: cn("gap-step-xs inline-flex items-center", className), ...props, children: [
3741
- conAleta,
3873
+ withFin,
3742
3874
  wordmark
3743
3875
  ] });
3744
3876
  }
@@ -3746,16 +3878,17 @@ function Logo({
3746
3878
  // src/lib/social.tsx
3747
3879
  var social_exports = {};
3748
3880
  __export(social_exports, {
3749
- Correo: () => Correo,
3750
3881
  Discord: () => Discord,
3882
+ Email: () => Email,
3751
3883
  GitHub: () => GitHub,
3752
3884
  Instagram: () => Instagram,
3753
3885
  LinkedIn: () => LinkedIn,
3886
+ Newsletter: () => Newsletter,
3754
3887
  Rss: () => Rss,
3755
3888
  X: () => X,
3756
3889
  YouTube: () => YouTube
3757
3890
  });
3758
- function Marca({ children, ...props }) {
3891
+ function Brand({ children, ...props }) {
3759
3892
  return /* @__PURE__ */ jsxRuntime.jsx(
3760
3893
  "svg",
3761
3894
  {
@@ -3770,7 +3903,7 @@ function Marca({ children, ...props }) {
3770
3903
  }
3771
3904
  );
3772
3905
  }
3773
- function Funcional({ children, ...props }) {
3906
+ function Functional({ children, ...props }) {
3774
3907
  return /* @__PURE__ */ jsxRuntime.jsx(
3775
3908
  "svg",
3776
3909
  {
@@ -3789,21 +3922,26 @@ function Funcional({ children, ...props }) {
3789
3922
  }
3790
3923
  );
3791
3924
  }
3792
- var GitHub = (props) => /* @__PURE__ */ jsxRuntime.jsx(Marca, { ...props, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 .5C5.73.5.5 5.73.5 12a11.5 11.5 0 0 0 7.86 10.91c.58.1.79-.25.79-.55 0-.27-.01-1.18-.02-2.14-3.2.7-3.88-1.36-3.88-1.36-.52-1.33-1.28-1.68-1.28-1.68-1.05-.72.08-.7.08-.7 1.16.08 1.77 1.19 1.77 1.19 1.03 1.77 2.7 1.26 3.36.96.1-.75.4-1.26.73-1.55-2.55-.29-5.24-1.28-5.24-5.69 0-1.26.45-2.29 1.19-3.09-.12-.29-.52-1.46.11-3.05 0 0 .97-.31 3.18 1.18a11 11 0 0 1 5.79 0c2.2-1.49 3.17-1.18 3.17-1.18.63 1.59.23 2.76.12 3.05.74.8 1.18 1.83 1.18 3.09 0 4.42-2.69 5.39-5.25 5.68.41.36.78 1.06.78 2.14 0 1.55-.01 2.79-.01 3.17 0 .3.2.66.79.55A11.5 11.5 0 0 0 23.5 12C23.5 5.73 18.27.5 12 .5Z" }) });
3793
- var LinkedIn = (props) => /* @__PURE__ */ jsxRuntime.jsx(Marca, { ...props, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M4.98 3.5a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5ZM3 9h4v12H3V9Zm7 0h3.8v1.71h.05a4.17 4.17 0 0 1 3.75-2.06c4.01 0 4.75 2.64 4.75 6.07V21h-4v-5.4c0-1.29-.02-2.95-1.8-2.95-1.8 0-2.08 1.4-2.08 2.85V21h-3.97V9Z" }) });
3794
- var X = (props) => /* @__PURE__ */ jsxRuntime.jsx(Marca, { ...props, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M17.53 3h3.06l-6.69 7.64L21.75 21h-6.16l-4.83-6.3L5.25 21H2.19l7.15-8.17L2.5 3h6.32l4.36 5.77L17.53 3Zm-1.07 16.17h1.7L7.62 4.74H5.8l10.66 14.43Z" }) });
3795
- var Instagram = (props) => /* @__PURE__ */ jsxRuntime.jsx(Marca, { ...props, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 2.16c3.2 0 3.58.01 4.85.07 1.17.05 1.8.25 2.23.41.56.22.96.48 1.38.9.42.42.68.82.9 1.38.16.42.36 1.06.41 2.23.06 1.27.07 1.65.07 4.85s-.01 3.58-.07 4.85c-.05 1.17-.25 1.8-.41 2.23-.22.56-.48.96-.9 1.38-.42.42-.82.68-1.38.9-.42.16-1.06.36-2.23.41-1.27.06-1.65.07-4.85.07s-3.58-.01-4.85-.07c-1.17-.05-1.8-.25-2.23-.41-.56-.22-.96-.48-1.38-.9-.42-.42-.68-.82-.9-1.38-.16-.42-.36-1.06-.41-2.23C2.17 15.58 2.16 15.2 2.16 12s.01-3.58.07-4.85c.05-1.17.25-1.8.41-2.23.22-.56.48-.96.9-1.38.42-.42.82-.68 1.38-.9.42-.16 1.06-.36 2.23-.41C8.42 2.17 8.8 2.16 12 2.16Zm0 3.68a6.16 6.16 0 1 0 0 12.32 6.16 6.16 0 0 0 0-12.32Zm0 10.16a4 4 0 1 1 0-8 4 4 0 0 1 0 8Zm7.85-10.4a1.44 1.44 0 1 1-2.88 0 1.44 1.44 0 0 1 2.88 0Z" }) });
3796
- var Discord = (props) => /* @__PURE__ */ jsxRuntime.jsx(Marca, { ...props, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M20.32 5.56A18.5 18.5 0 0 0 15.7 4.1a.07.07 0 0 0-.07.04c-.2.36-.42.82-.58 1.19a17 17 0 0 0-5.1 0c-.16-.38-.39-.83-.59-1.19a.07.07 0 0 0-.07-.04c-1.6.28-3.15.77-4.62 1.46a.06.06 0 0 0-.03.03C1.66 10 .9 14.28 1.28 18.5a.08.08 0 0 0 .03.05 18.6 18.6 0 0 0 5.6 2.84.07.07 0 0 0 .08-.03c.43-.59.81-1.21 1.14-1.86a.07.07 0 0 0-.04-.1c-.6-.23-1.18-.51-1.74-.83a.07.07 0 0 1-.01-.12l.35-.27a.07.07 0 0 1 .07-.01 13.3 13.3 0 0 0 11.3 0 .07.07 0 0 1 .08.01l.34.27c.04.04.04.1-.01.12-.55.33-1.13.6-1.74.83a.07.07 0 0 0-.04.1c.34.65.72 1.27 1.14 1.86a.07.07 0 0 0 .08.03 18.5 18.5 0 0 0 5.6-2.84.07.07 0 0 0 .04-.05c.45-4.88-.75-9.12-3.18-12.88a.06.06 0 0 0-.03-.03ZM8.4 15.93c-1.1 0-2.01-1.01-2.01-2.25 0-1.24.89-2.25 2.01-2.25 1.13 0 2.03 1.02 2.02 2.25 0 1.24-.9 2.25-2.02 2.25Zm7.22 0c-1.1 0-2.01-1.01-2.01-2.25 0-1.24.89-2.25 2.01-2.25 1.13 0 2.03 1.02 2.02 2.25 0 1.24-.89 2.25-2.02 2.25Z" }) });
3797
- var YouTube = (props) => /* @__PURE__ */ jsxRuntime.jsx(Marca, { ...props, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M23.5 6.9a3 3 0 0 0-2.12-2.12C19.5 4.27 12 4.27 12 4.27s-7.5 0-9.38.51A3 3 0 0 0 .5 6.9 31.3 31.3 0 0 0 0 12a31.3 31.3 0 0 0 .5 5.1 3 3 0 0 0 2.12 2.12c1.88.51 9.38.51 9.38.51s7.5 0 9.38-.51a3 3 0 0 0 2.12-2.12A31.3 31.3 0 0 0 24 12a31.3 31.3 0 0 0-.5-5.1ZM9.6 15.6V8.4l6.24 3.6-6.24 3.6Z" }) });
3798
- var Rss = (props) => /* @__PURE__ */ jsxRuntime.jsxs(Funcional, { ...props, children: [
3925
+ var GitHub = (props) => /* @__PURE__ */ jsxRuntime.jsx(Brand, { ...props, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 .5C5.73.5.5 5.73.5 12a11.5 11.5 0 0 0 7.86 10.91c.58.1.79-.25.79-.55 0-.27-.01-1.18-.02-2.14-3.2.7-3.88-1.36-3.88-1.36-.52-1.33-1.28-1.68-1.28-1.68-1.05-.72.08-.7.08-.7 1.16.08 1.77 1.19 1.77 1.19 1.03 1.77 2.7 1.26 3.36.96.1-.75.4-1.26.73-1.55-2.55-.29-5.24-1.28-5.24-5.69 0-1.26.45-2.29 1.19-3.09-.12-.29-.52-1.46.11-3.05 0 0 .97-.31 3.18 1.18a11 11 0 0 1 5.79 0c2.2-1.49 3.17-1.18 3.17-1.18.63 1.59.23 2.76.12 3.05.74.8 1.18 1.83 1.18 3.09 0 4.42-2.69 5.39-5.25 5.68.41.36.78 1.06.78 2.14 0 1.55-.01 2.79-.01 3.17 0 .3.2.66.79.55A11.5 11.5 0 0 0 23.5 12C23.5 5.73 18.27.5 12 .5Z" }) });
3926
+ var LinkedIn = (props) => /* @__PURE__ */ jsxRuntime.jsx(Brand, { ...props, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M4.98 3.5a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5ZM3 9h4v12H3V9Zm7 0h3.8v1.71h.05a4.17 4.17 0 0 1 3.75-2.06c4.01 0 4.75 2.64 4.75 6.07V21h-4v-5.4c0-1.29-.02-2.95-1.8-2.95-1.8 0-2.08 1.4-2.08 2.85V21h-3.97V9Z" }) });
3927
+ var X = (props) => /* @__PURE__ */ jsxRuntime.jsx(Brand, { ...props, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M17.53 3h3.06l-6.69 7.64L21.75 21h-6.16l-4.83-6.3L5.25 21H2.19l7.15-8.17L2.5 3h6.32l4.36 5.77L17.53 3Zm-1.07 16.17h1.7L7.62 4.74H5.8l10.66 14.43Z" }) });
3928
+ var Instagram = (props) => /* @__PURE__ */ jsxRuntime.jsx(Brand, { ...props, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 2.16c3.2 0 3.58.01 4.85.07 1.17.05 1.8.25 2.23.41.56.22.96.48 1.38.9.42.42.68.82.9 1.38.16.42.36 1.06.41 2.23.06 1.27.07 1.65.07 4.85s-.01 3.58-.07 4.85c-.05 1.17-.25 1.8-.41 2.23-.22.56-.48.96-.9 1.38-.42.42-.82.68-1.38.9-.42.16-1.06.36-2.23.41-1.27.06-1.65.07-4.85.07s-3.58-.01-4.85-.07c-1.17-.05-1.8-.25-2.23-.41-.56-.22-.96-.48-1.38-.9-.42-.42-.68-.82-.9-1.38-.16-.42-.36-1.06-.41-2.23C2.17 15.58 2.16 15.2 2.16 12s.01-3.58.07-4.85c.05-1.17.25-1.8.41-2.23.22-.56.48-.96.9-1.38.42-.42.82-.68 1.38-.9.42-.16 1.06-.36 2.23-.41C8.42 2.17 8.8 2.16 12 2.16Zm0 3.68a6.16 6.16 0 1 0 0 12.32 6.16 6.16 0 0 0 0-12.32Zm0 10.16a4 4 0 1 1 0-8 4 4 0 0 1 0 8Zm7.85-10.4a1.44 1.44 0 1 1-2.88 0 1.44 1.44 0 0 1 2.88 0Z" }) });
3929
+ var Discord = (props) => /* @__PURE__ */ jsxRuntime.jsx(Brand, { ...props, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M20.32 5.56A18.5 18.5 0 0 0 15.7 4.1a.07.07 0 0 0-.07.04c-.2.36-.42.82-.58 1.19a17 17 0 0 0-5.1 0c-.16-.38-.39-.83-.59-1.19a.07.07 0 0 0-.07-.04c-1.6.28-3.15.77-4.62 1.46a.06.06 0 0 0-.03.03C1.66 10 .9 14.28 1.28 18.5a.08.08 0 0 0 .03.05 18.6 18.6 0 0 0 5.6 2.84.07.07 0 0 0 .08-.03c.43-.59.81-1.21 1.14-1.86a.07.07 0 0 0-.04-.1c-.6-.23-1.18-.51-1.74-.83a.07.07 0 0 1-.01-.12l.35-.27a.07.07 0 0 1 .07-.01 13.3 13.3 0 0 0 11.3 0 .07.07 0 0 1 .08.01l.34.27c.04.04.04.1-.01.12-.55.33-1.13.6-1.74.83a.07.07 0 0 0-.04.1c.34.65.72 1.27 1.14 1.86a.07.07 0 0 0 .08.03 18.5 18.5 0 0 0 5.6-2.84.07.07 0 0 0 .04-.05c.45-4.88-.75-9.12-3.18-12.88a.06.06 0 0 0-.03-.03ZM8.4 15.93c-1.1 0-2.01-1.01-2.01-2.25 0-1.24.89-2.25 2.01-2.25 1.13 0 2.03 1.02 2.02 2.25 0 1.24-.9 2.25-2.02 2.25Zm7.22 0c-1.1 0-2.01-1.01-2.01-2.25 0-1.24.89-2.25 2.01-2.25 1.13 0 2.03 1.02 2.02 2.25 0 1.24-.89 2.25-2.02 2.25Z" }) });
3930
+ var YouTube = (props) => /* @__PURE__ */ jsxRuntime.jsx(Brand, { ...props, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M23.5 6.9a3 3 0 0 0-2.12-2.12C19.5 4.27 12 4.27 12 4.27s-7.5 0-9.38.51A3 3 0 0 0 .5 6.9 31.3 31.3 0 0 0 0 12a31.3 31.3 0 0 0 .5 5.1 3 3 0 0 0 2.12 2.12c1.88.51 9.38.51 9.38.51s7.5 0 9.38-.51a3 3 0 0 0 2.12-2.12A31.3 31.3 0 0 0 24 12a31.3 31.3 0 0 0-.5-5.1ZM9.6 15.6V8.4l6.24 3.6-6.24 3.6Z" }) });
3931
+ var Rss = (props) => /* @__PURE__ */ jsxRuntime.jsxs(Functional, { ...props, children: [
3799
3932
  /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M4 11a9 9 0 0 1 9 9M4 4a16 16 0 0 1 16 16" }),
3800
3933
  /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "5", cy: "19", r: "1.6", fill: "currentColor", stroke: "none" })
3801
3934
  ] });
3802
- var Correo = (props) => /* @__PURE__ */ jsxRuntime.jsxs(Funcional, { ...props, children: [
3935
+ var Email = (props) => /* @__PURE__ */ jsxRuntime.jsxs(Functional, { ...props, children: [
3803
3936
  /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "2.75", y: "4.75", width: "18.5", height: "14.5", rx: "2" }),
3804
3937
  /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m3.5 7.5 7.4 5.2a2 2 0 0 0 2.2 0l7.4-5.2" })
3805
3938
  ] });
3939
+ var Newsletter = (props) => /* @__PURE__ */ jsxRuntime.jsxs(Functional, { ...props, children: [
3940
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 8.5a6 6 0 1 0-12 0c0 5-2 6.5-2 6.5h16s-2-1.5-2-6.5" }),
3941
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M13.7 19a2 2 0 0 1-3.4 0" })
3942
+ ] });
3806
3943
 
3944
+ exports.ASSETS_PATH = ASSETS_PATH;
3807
3945
  exports.Accordion = Accordion;
3808
3946
  exports.AccordionContent = AccordionContent;
3809
3947
  exports.AccordionItem = AccordionItem;
@@ -3830,8 +3968,10 @@ exports.Badge = Badge;
3830
3968
  exports.Blockquote = Blockquote;
3831
3969
  exports.Breadcrumb = Breadcrumb;
3832
3970
  exports.Button = Button;
3971
+ exports.CARD = CARD;
3972
+ exports.CARD_HOVER = CARD_HOVER;
3973
+ exports.CARD_SURFACE = CARD_SURFACE;
3833
3974
  exports.Calendar = Calendar;
3834
- exports.CaraDeMascota = CaraDeMascota;
3835
3975
  exports.Card = Card;
3836
3976
  exports.CardContent = CardContent;
3837
3977
  exports.CardDescription = CardDescription;
@@ -3870,14 +4010,14 @@ exports.EmptyState = EmptyState;
3870
4010
  exports.EventCalendar = EventCalendar;
3871
4011
  exports.Footer = Footer;
3872
4012
  exports.FooterLink = FooterLink;
3873
- exports.HOVER_TARJETA = HOVER_TARJETA;
3874
4013
  exports.Hero = Hero;
3875
4014
  exports.Input = Input;
3876
- exports.Isotipo = Isotipo;
4015
+ exports.Isotype = Isotype;
3877
4016
  exports.Label = Label2;
3878
4017
  exports.LinkRow = LinkRow;
3879
4018
  exports.Logo = Logo;
3880
- exports.Mascota = Mascota;
4019
+ exports.Mascot = Mascot;
4020
+ exports.MascotFace = MascotFace;
3881
4021
  exports.MetricBadge = MetricBadge;
3882
4022
  exports.Nav = Nav;
3883
4023
  exports.NavItem = NavItem;
@@ -3895,10 +4035,8 @@ exports.PopoverAnchor = PopoverAnchor;
3895
4035
  exports.PopoverContent = PopoverContent;
3896
4036
  exports.PopoverTrigger = PopoverTrigger;
3897
4037
  exports.Progress = Progress;
3898
- exports.RUTA_ASSETS = RUTA_ASSETS;
3899
4038
  exports.RadioGroup = RadioGroup2;
3900
4039
  exports.RadioGroupItem = RadioGroupItem;
3901
- exports.SUPERFICIE_TARJETA = SUPERFICIE_TARJETA;
3902
4040
  exports.ScrollingProgressBar = ScrollingProgressBar;
3903
4041
  exports.Select = Select;
3904
4042
  exports.SelectContent = SelectContent;
@@ -3923,9 +4061,9 @@ exports.SidebarNav = SidebarNav;
3923
4061
  exports.Skeleton = Skeleton;
3924
4062
  exports.Stat = Stat;
3925
4063
  exports.Switch = Switch;
3926
- exports.TEMA_ATRIBUTO = TEMA_ATRIBUTO;
3927
- exports.TEMA_CLAVE = TEMA_CLAVE;
3928
- exports.TEMA_EVENTO = TEMA_EVENTO;
4064
+ exports.THEME_ATTRIBUTE = THEME_ATTRIBUTE;
4065
+ exports.THEME_EVENT = THEME_EVENT;
4066
+ exports.THEME_KEY = THEME_KEY;
3929
4067
  exports.Table = Table;
3930
4068
  exports.TableBody = TableBody;
3931
4069
  exports.TableCaption = TableCaption;
@@ -3950,44 +4088,45 @@ exports.TooltipContent = TooltipContent;
3950
4088
  exports.TooltipProvider = TooltipProvider;
3951
4089
  exports.TooltipTrigger = TooltipTrigger;
3952
4090
  exports.alertVariants = alert;
3953
- exports.aletas = aletas;
3954
- exports.alternarTema = alternarTema;
3955
- exports.aplicarTema = aplicarTema;
4091
+ exports.applyTheme = applyTheme;
3956
4092
  exports.avatarVariants = avatar;
3957
4093
  exports.badgeVariants = badge;
3958
4094
  exports.brand = brand;
3959
4095
  exports.buttonVariants = button;
3960
- exports.caras = caras;
3961
- exports.categoryBadgeVariants = categoria;
4096
+ exports.categoryBadgeVariants = category;
3962
4097
  exports.cn = cn;
3963
4098
  exports.colors = colors;
3964
4099
  exports.control = control;
4100
+ exports.currentTheme = currentTheme;
3965
4101
  exports.dark = dark;
3966
- exports.escucharTema = escucharTema;
4102
+ exports.faceList = faceList;
4103
+ exports.faceUsage = faceUsage;
4104
+ exports.faces = faces;
4105
+ exports.fins = fins;
3967
4106
  exports.fonts = fonts;
3968
4107
  exports.gradient = gradient;
3969
4108
  exports.light = light;
3970
4109
  exports.limits = limits;
3971
- exports.listaCaras = listaCaras;
3972
- exports.listaPoses = listaPoses;
4110
+ exports.metricBadgeVariants = metric;
3973
4111
  exports.motion = motion;
3974
4112
  exports.naming = naming;
4113
+ exports.poseList = poseList;
3975
4114
  exports.poses = poses;
4115
+ exports.preferredTheme = preferredTheme;
3976
4116
  exports.radius = radius;
3977
- exports.scriptTema = scriptTema;
3978
4117
  exports.series = series;
3979
4118
  exports.shadow = shadow;
3980
- exports.sintaxis = sintaxis;
3981
4119
  exports.size = size;
3982
4120
  exports.social = social_exports;
3983
4121
  exports.spacing = spacing;
4122
+ exports.storedTheme = storedTheme;
4123
+ exports.syntax = syntax;
3984
4124
  exports.tagline = tagline;
3985
- exports.temaActual = temaActual;
3986
- exports.temaGuardado = temaGuardado;
3987
- exports.temaPreferido = temaPreferido;
3988
- exports.textVariants = texto;
4125
+ exports.textVariants = text;
4126
+ exports.themeScript = themeScript;
3989
4127
  exports.toast = toast2;
4128
+ exports.toggleTheme = toggleTheme;
3990
4129
  exports.tokens = tokens;
3991
4130
  exports.typeScale = typeScale;
3992
- exports.useTema = useTema;
3993
- exports.usoDeCara = usoDeCara;
4131
+ exports.useTheme = useTheme;
4132
+ exports.watchTheme = watchTheme;