@eduardoalvarez/arrecife 0.5.1 → 0.7.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 (78) hide show
  1. package/CHANGELOG.md +114 -0
  2. package/README.md +868 -467
  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-2WPWEIMD.js +27 -0
  14. package/dist/chunk-45HVCTB7.js +70 -0
  15. package/dist/{chunk-ZEOQKRQ7.js → chunk-727HCBD4.js} +1 -1
  16. package/dist/chunk-CKRSQPTX.js +36 -0
  17. package/dist/chunk-E6KFUSKB.js +144 -0
  18. package/dist/chunk-GCRII2KQ.js +86 -0
  19. package/dist/{chunk-YZ2SDOVZ.js → chunk-JN3IS5OS.js} +30 -30
  20. package/dist/chunk-ODBFN44D.js +45 -0
  21. package/dist/chunk-OMKSESQB.js +300 -0
  22. package/dist/{chunk-VPT32GPG.js → chunk-TA7TLWW4.js} +2 -2
  23. package/dist/chunk-WGNIRIN7.js +42 -0
  24. package/dist/doctor.mjs +166 -0
  25. package/dist/form/index.cjs +109 -92
  26. package/dist/form/index.d.cts +43 -42
  27. package/dist/form/index.d.ts +43 -42
  28. package/dist/form/index.js +25 -23
  29. package/dist/icons/index.cjs +149 -0
  30. package/dist/icons/index.d.cts +94 -0
  31. package/dist/icons/index.d.ts +94 -0
  32. package/dist/icons/index.js +28 -0
  33. package/dist/index-DlAO2JZs.d.cts +47 -0
  34. package/dist/index-DlAO2JZs.d.ts +47 -0
  35. package/dist/index.cjs +1292 -983
  36. package/dist/index.d.cts +927 -806
  37. package/dist/index.d.ts +927 -806
  38. package/dist/index.js +809 -778
  39. package/dist/{label-DuTvJGxD.d.ts → label-MgHFKnFy.d.cts} +3 -3
  40. package/dist/{label-DuTvJGxD.d.cts → label-MgHFKnFy.d.ts} +3 -3
  41. package/dist/og/index.cjs +133 -132
  42. package/dist/og/index.d.cts +93 -89
  43. package/dist/og/index.d.ts +93 -89
  44. package/dist/og/index.js +106 -106
  45. package/dist/shiki/index.cjs +28 -30
  46. package/dist/shiki/index.d.cts +4 -4
  47. package/dist/shiki/index.d.ts +4 -4
  48. package/dist/shiki/index.js +12 -12
  49. package/dist/social/index.cjs +67 -0
  50. package/dist/social/index.d.cts +2 -0
  51. package/dist/social/index.d.ts +2 -0
  52. package/dist/social/index.js +2 -0
  53. package/dist/theme/index.cjs +97 -0
  54. package/dist/theme/index.d.cts +144 -0
  55. package/dist/theme/index.d.ts +144 -0
  56. package/dist/theme/index.js +2 -0
  57. package/dist/tokens/index.cjs +159 -88
  58. package/dist/tokens/index.d.cts +277 -165
  59. package/dist/tokens/index.d.ts +277 -165
  60. package/dist/tokens/index.js +2 -2
  61. package/dist/tokens/theme.css +165 -100
  62. package/dist/variants/index.cjs +195 -0
  63. package/dist/variants/index.d.cts +195 -0
  64. package/dist/variants/index.d.ts +195 -0
  65. package/dist/variants/index.js +3 -0
  66. package/llms.txt +1145 -746
  67. package/package.json +42 -11
  68. package/dist/catalogo-Du5ID-Hi.d.cts +0 -77
  69. package/dist/catalogo-Du5ID-Hi.d.ts +0 -77
  70. package/dist/chunk-E3OMP2DL.js +0 -36
  71. package/dist/chunk-KPZNNMV5.js +0 -83
  72. package/dist/chunk-NHS7ETKJ.js +0 -27
  73. package/dist/chunk-TSPJOM6K.js +0 -229
  74. package/dist/chunk-UOWIDFCB.js +0 -81
  75. package/dist/tema/index.cjs +0 -94
  76. package/dist/tema/index.d.cts +0 -110
  77. package/dist/tema/index.d.ts +0 -110
  78. 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,43 +281,67 @@ 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
+ /**
287
+ * The bar when it shares the screen with a sidebar.
288
+ *
289
+ * It is not «a smaller bar because it looks better». At 64 the header of an
290
+ * app shell competes with the rail beside it for the same corner, and the two
291
+ * together eat the top of the content area. 56 is what `cursos` had arrived at
292
+ * on its own, and it is the same reasoning as `control.iconSm`: the one admin
293
+ * app of the five is denser than the four reading sites. See
294
+ * `docs/decisions.md` § 30.
295
+ */
296
+ navCompact: 56,
297
+ /**
298
+ * The admin sidebar, and the rail it collapses to.
299
+ *
300
+ * They are tokens and not the consumer's `w-*` because a collapsible sidebar
301
+ * has to know BOTH: the layout beside it reserves one of the two, and two
302
+ * numbers written by hand in two files drift the first time one of them moves.
303
+ * A sidebar that never collapses is still just a box the layout sizes.
304
+ *
305
+ * 56 is the rail: `navCompact`'s height, so a panel's bar and its rail meet in
306
+ * a square corner instead of a step.
307
+ */
308
+ sidebar: 256,
309
+ sidebarRail: 56,
310
+ /** Reading column width. */
239
311
  content: 760,
240
- /** Ancho máximo de página. */
312
+ /** Maximum page width. */
241
313
  wide: 1180
242
314
  };
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. */
315
+ var syntax = {
316
+ /** The hull. It is the block background in both modes. */
317
+ background: brand.hull,
318
+ /** Identifiers, functions, types, variables. */
319
+ identifier: dark.textPrimary,
320
+ /** Strings, numbers, booleans, null. */
249
321
  literal: dark.accent,
250
- /** Palabras clave, control de flujo, `import`, `this`. */
251
- palabraClave: dark.warm,
252
- /** Comentarios y puntuación. */
253
- comentario: dark.textMuted,
322
+ /** Keywords, control flow, `import`, `this`. */
323
+ keyword: dark.warm,
324
+ /** Comments and punctuation. */
325
+ comment: dark.textMuted,
254
326
  /** `markup.deleted`, `invalid`. */
255
- invalido: dark.error
327
+ invalid: dark.error
256
328
  };
257
329
  var series = {
258
330
  dark: [dark.accent, dark.warm, brand.body, dark.textMuted],
259
331
  light: [light.accent, light.warm, brand.body, light.textMuted]
260
332
  };
261
- var profundo = "#0D2129";
333
+ var deep = "#0D2129";
262
334
  var gradient = {
263
335
  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%)`
336
+ hero: `linear-gradient(160deg, ${dark.background} 60%, ${deep} 100%)`,
337
+ section: `linear-gradient(150deg, ${dark.surface} 0%, ${deep} 100%)`,
338
+ /** The one used by the article OG template. 145°, from the document. */
339
+ og: `linear-gradient(145deg, ${dark.background} 55%, ${deep} 100%)`
268
340
  },
269
341
  light: {
270
- hero: `linear-gradient(160deg, ${light.background} 60%, ${light.surfaceRaised} 100%)`,
271
- seccion: `linear-gradient(150deg, ${light.surface} 0%, ${light.surfaceRaised} 100%)`,
272
- og: `linear-gradient(145deg, ${light.background} 55%, ${light.surfaceRaised} 100%)`
342
+ hero: `linear-gradient(160deg, ${light.background} 60%, ${light.surface} 100%)`,
343
+ section: `linear-gradient(150deg, ${light.surface} 0%, ${light.background} 100%)`,
344
+ og: `linear-gradient(145deg, ${light.background} 55%, ${light.surface} 100%)`
273
345
  }
274
346
  };
275
347
  var shadow = {
@@ -282,16 +354,16 @@ var motion = {
282
354
  };
283
355
  var tagline = {
284
356
  /** 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",
357
+ long: "Ayudo a equipos de ingenier\xEDa a escalar con criterio",
358
+ /** Header, one line. */
359
+ short: "Ayudo a equipos a escalar con criterio",
288
360
  /** LinkedIn. */
289
361
  en: "Helping engineering teams scale with judgment"
290
362
  };
291
363
  var naming = {
292
364
  wordmark: "Eduardo \xC1lvarez",
293
365
  mascot: "Tiburonc\xEDn",
294
- /** El dominio, para la firma CLI del footer: `$ cd ~/eduardoalvarez.dev/2026`. */
366
+ /** The domain, for the footer CLI signature: `$ cd ~/eduardoalvarez.dev/2026`. */
295
367
  domain: "eduardoalvarez.dev"
296
368
  };
297
369
 
@@ -307,7 +379,7 @@ var tokens = {
307
379
  spacing,
308
380
  size,
309
381
  gradient,
310
- sintaxis,
382
+ syntax,
311
383
  series,
312
384
  shadow,
313
385
  motion,
@@ -315,102 +387,284 @@ var tokens = {
315
387
  naming
316
388
  };
317
389
 
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() {
390
+ // src/theme/index.ts
391
+ var THEME_KEY = "arrecife-theme";
392
+ var THEME_ATTRIBUTE = "data-theme";
393
+ var THEME_EVENT = "arrecife:theme";
394
+ var isTheme = (value) => value === "dark" || value === "light";
395
+ function currentTheme() {
324
396
  if (typeof document === "undefined") return "dark";
325
- const puesto = document.documentElement.getAttribute(TEMA_ATRIBUTO);
326
- return esTema(puesto) ? puesto : "dark";
397
+ const set = document.documentElement.getAttribute(THEME_ATTRIBUTE);
398
+ return isTheme(set) ? set : "dark";
327
399
  }
328
- function temaGuardado() {
400
+ function storedTheme() {
329
401
  if (typeof localStorage === "undefined") return null;
330
402
  try {
331
- const valor = localStorage.getItem(TEMA_CLAVE);
332
- return esTema(valor) ? valor : null;
403
+ const value = localStorage.getItem(THEME_KEY);
404
+ return isTheme(value) ? value : null;
333
405
  } catch {
334
406
  return null;
335
407
  }
336
408
  }
337
- function temaPreferido() {
338
- const guardado = temaGuardado();
339
- if (guardado) return guardado;
409
+ function preferredTheme({ base } = {}) {
410
+ const stored = storedTheme();
411
+ if (stored) return stored;
412
+ if (base) return base;
340
413
  if (typeof matchMedia === "undefined") return "dark";
341
414
  return matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark";
342
415
  }
343
- function aplicarTema(tema, persistir = true) {
416
+ function applyTheme(theme, persist = true) {
344
417
  if (typeof document === "undefined") return;
345
- document.documentElement.setAttribute(TEMA_ATRIBUTO, tema);
346
- document.documentElement.style.colorScheme = tema;
347
- if (persistir) {
418
+ document.documentElement.setAttribute(THEME_ATTRIBUTE, theme);
419
+ document.documentElement.style.colorScheme = theme;
420
+ if (persist) {
348
421
  try {
349
- localStorage.setItem(TEMA_CLAVE, tema);
422
+ localStorage.setItem(THEME_KEY, theme);
350
423
  } catch {
351
424
  }
352
425
  }
353
- dispatchEvent(new CustomEvent(TEMA_EVENTO, { detail: tema }));
426
+ dispatchEvent(new CustomEvent(THEME_EVENT, { detail: theme }));
354
427
  }
355
- function alternarTema() {
356
- const siguiente = temaActual() === "dark" ? "light" : "dark";
357
- aplicarTema(siguiente);
358
- return siguiente;
428
+ function toggleTheme() {
429
+ const next = currentTheme() === "dark" ? "light" : "dark";
430
+ applyTheme(next);
431
+ return next;
359
432
  }
360
- function escucharTema(alCambiar) {
433
+ function watchTheme(onChange, options = {}) {
361
434
  if (typeof window === "undefined") return () => {
362
435
  };
363
- const propio = (evento) => {
364
- const detalle = evento.detail;
365
- if (esTema(detalle)) alCambiar(detalle);
436
+ const own = (event) => {
437
+ const detail = event.detail;
438
+ if (isTheme(detail)) onChange(detail);
366
439
  };
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);
440
+ const otherTab = (event) => {
441
+ if (event.key !== THEME_KEY) return;
442
+ const theme = isTheme(event.newValue) ? event.newValue : preferredTheme(options);
443
+ applyTheme(theme, false);
444
+ onChange(theme);
372
445
  };
373
- addEventListener(TEMA_EVENTO, propio);
374
- addEventListener("storage", otraPestana);
446
+ addEventListener(THEME_EVENT, own);
447
+ addEventListener("storage", otherTab);
375
448
  return () => {
376
- removeEventListener(TEMA_EVENTO, propio);
377
- removeEventListener("storage", otraPestana);
449
+ removeEventListener(THEME_EVENT, own);
450
+ removeEventListener("storage", otherTab);
378
451
  };
379
452
  }
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)}`);
453
+ function themeScript({ base } = {}) {
454
+ return [
455
+ "(function () {",
456
+ " var KEY = " + JSON.stringify(THEME_KEY) + ";",
457
+ " function resolve() {",
458
+ " try {",
459
+ " var stored = localStorage.getItem(KEY);",
460
+ ' if (stored === "dark" || stored === "light") return stored;',
461
+ " } catch (e) {}",
462
+ base ? " return " + JSON.stringify(base) + ";" : ' return matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark";',
463
+ " }",
464
+ " function apply() {",
465
+ " var theme = resolve();",
466
+ " document.documentElement.setAttribute(" + JSON.stringify(THEME_ATTRIBUTE) + ", theme);",
467
+ " document.documentElement.style.colorScheme = theme;",
468
+ " }",
469
+ " apply();",
470
+ ' document.addEventListener("astro:after-swap", apply);',
471
+ "})();"
472
+ ].join("\n");
473
+ }
474
+ var button = classVarianceAuthority.cva(
475
+ [
476
+ "inline-flex cursor-pointer items-center justify-center gap-step-xs whitespace-nowrap select-none",
477
+ "rounded-control font-sans font-medium",
478
+ "transition-standard",
479
+ "focus-ring",
480
+ "disabled:pointer-events-none disabled:opacity-50",
481
+ "[&_svg]:shrink-0"
482
+ ],
483
+ {
484
+ variants: {
485
+ variant: {
486
+ primary: [
487
+ "bg-accent text-accent-on hover:bg-accent-hover",
488
+ "light:bg-brand-hull light:text-accent-on light:hover:bg-text-secondary"
489
+ ],
490
+ // The one control whose focus ring is not biolume. It is the system's
491
+ // only sand fill, and a biolume ring three pixels off a sand button puts
492
+ // both of the brand's accents in the same glance. See `decisions.md` § 37.
493
+ conversion: "bg-warm text-warm-on hover:bg-warm-hover focus-ring-warm",
494
+ secondary: [
495
+ "border-hairline-hover border bg-transparent text-text-primary",
496
+ "hover:border-accent hover:text-accent"
497
+ ],
498
+ tertiary: [
499
+ "bg-transparent font-mono font-normal text-text-secondary",
500
+ "hover:text-accent hover:underline hover:underline-offset-4"
501
+ ],
502
+ /** Irreversible only. Never for «cancel» on a form. */
503
+ destructive: "bg-danger text-danger-on hover:bg-danger-hover",
504
+ destructiveOutline: [
505
+ "border-danger text-danger border bg-transparent",
506
+ "hover:bg-danger hover:text-danger-on"
507
+ ]
508
+ },
509
+ size: {
510
+ sm: "h-8 px-control-sm text-label",
511
+ md: "h-10 px-control-md text-ui",
512
+ lg: "h-12 px-control-lg text-lead",
513
+ /** A 42×42 square, no text. `aria-label` is mandatory on it. */
514
+ icon: "size-control-icon p-0",
515
+ /** 32×32, for a table row. Same height as `sm`, and same mandatory `aria-label`. */
516
+ "icon-sm": "size-control-icon-sm p-0"
517
+ }
518
+ },
519
+ compoundVariants: [
520
+ /**
521
+ * The tertiary has no box: neither horizontal padding nor control height.
522
+ * It goes here rather than as `px-0` on the variant so it beats the size's
523
+ * `px-*` without depending on the order cva concatenates classes in.
524
+ */
525
+ { variant: "tertiary", size: ["sm", "md", "lg"], class: "h-auto px-0" }
526
+ ],
527
+ defaultVariants: { variant: "primary", size: "md" }
528
+ }
529
+ );
530
+ var badge = classVarianceAuthority.cva(
531
+ ["inline-flex items-center gap-step-xs", "rounded-chip px-step-xs py-0.5", "font-sans text-tag", "transition-standard"],
532
+ {
533
+ variants: {
534
+ variant: {
535
+ neutral: "bg-surface-raised text-text-secondary",
536
+ accent: "bg-accent/8 text-text-primary",
537
+ warm: "bg-warm/8 text-text-primary",
538
+ success: "bg-success/8 text-text-primary",
539
+ warning: "bg-warning/8 text-text-primary",
540
+ error: "bg-error/8 text-text-primary"
541
+ }
542
+ },
543
+ defaultVariants: { variant: "neutral" }
544
+ }
545
+ );
546
+ var category = classVarianceAuthority.cva(
547
+ [
548
+ "inline-flex items-center gap-step-xs",
549
+ "rounded-pill px-step-sm py-0.5",
550
+ "font-mono text-chip",
551
+ "border transition-standard"
552
+ ],
553
+ {
554
+ variants: {
555
+ active: {
556
+ false: "border-warm/28 bg-transparent text-warm",
557
+ true: "border-warm bg-warm text-warm-on"
558
+ }
559
+ },
560
+ defaultVariants: { active: false }
561
+ }
562
+ );
563
+ var metric = ["inline-flex items-center gap-step-xs", "rounded-pill", "font-mono text-chip text-text-muted"];
564
+ var text = classVarianceAuthority.cva("", {
565
+ variants: {
566
+ variant: {
567
+ display: "text-display font-display",
568
+ // Large metrics: 46px of display. It is the only scale designed for a
569
+ // number, which is why its line height is 1.
570
+ stat: "text-stat font-display",
571
+ h1: "text-h1 font-display",
572
+ h2: "text-h2 font-display",
573
+ h3: "text-h3 font-display",
574
+ body: "text-body font-sans",
575
+ // 17px: the deck on interior pages and the large button.
576
+ lead: "text-lead font-sans",
577
+ ui: "text-ui font-sans",
578
+ label: "text-label font-sans",
579
+ // The two badge scales. `tag` is status (sans 12.5/500) and `chip` is
580
+ // category and metric (mono 11.5). Neither transforms the text.
581
+ tag: "text-tag font-sans",
582
+ chip: "text-chip font-mono",
583
+ // Mono with NO transform: dates, paths, versions, file names and the
584
+ // footer signature. It is the step that was missing, and the reason every
585
+ // call site ended up writing `variant="eyebrow" className="normal-case"`.
586
+ meta: "text-meta font-mono",
587
+ // `uppercase` does not fit in a size token: text-transform is not a
588
+ // modifier of --text-*. Which is why the variant sets it, not theme.css.
589
+ eyebrow: "text-eyebrow font-mono uppercase"
590
+ },
591
+ tone: {
592
+ primary: "text-text-primary",
593
+ secondary: "text-text-secondary",
594
+ muted: "text-text-muted",
595
+ accent: "text-accent",
596
+ warm: "text-warm",
597
+ success: "text-success",
598
+ warning: "text-warning",
599
+ error: "text-error"
600
+ }
601
+ },
602
+ defaultVariants: { variant: "body", tone: "primary" }
603
+ });
604
+ var alert = classVarianceAuthority.cva("w-full rounded-card border p-step-md font-sans text-ui", {
605
+ variants: {
606
+ variant: {
607
+ accent: "text-text-secondary",
608
+ success: "text-text-secondary",
609
+ warning: "text-text-secondary",
610
+ error: "text-text-secondary"
611
+ },
612
+ emphasis: {
613
+ subtle: "",
614
+ strong: ""
615
+ }
616
+ },
617
+ compoundVariants: [
618
+ { variant: "accent", emphasis: "subtle", class: "border-accent/22 bg-accent/8" },
619
+ { variant: "success", emphasis: "subtle", class: "border-success/22 bg-success/8" },
620
+ { variant: "warning", emphasis: "subtle", class: "border-warning/22 bg-warning/8" },
621
+ { variant: "error", emphasis: "subtle", class: "border-error/22 bg-error/8" },
622
+ { variant: "accent", emphasis: "strong", class: "border-accent bg-accent/10" },
623
+ { variant: "success", emphasis: "strong", class: "border-success bg-success/10" },
624
+ { variant: "warning", emphasis: "strong", class: "border-warning bg-warning/10" },
625
+ { variant: "error", emphasis: "strong", class: "border-error bg-error/10" }
626
+ ],
627
+ defaultVariants: { variant: "accent", emphasis: "subtle" }
628
+ });
629
+ var avatar = classVarianceAuthority.cva(
630
+ "relative flex shrink-0 overflow-hidden rounded-pill bg-surface-raised border border-hairline",
631
+ {
632
+ variants: {
633
+ size: {
634
+ sm: "size-8",
635
+ md: "size-10",
636
+ lg: "size-14",
637
+ xl: "size-24"
638
+ }
639
+ },
640
+ defaultVariants: { size: "md" }
641
+ }
642
+ );
643
+
644
+ // src/variants/card.ts
645
+ var CARD_SURFACE = "rounded-card border-hairline bg-surface border";
646
+ var CARD_HOVER = "transition-standard hover:border-hairline-hover";
647
+ var CARD = [
648
+ "group block cursor-pointer",
649
+ CARD_SURFACE,
650
+ CARD_HOVER,
651
+ "focus-ring"
652
+ ];
653
+ var kebab = (name) => name.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`);
654
+ var scales = Object.keys(typeScale);
655
+ var radii = Object.keys(radius);
656
+ var namespaces = Object.keys(spacing).map(kebab);
657
+ var controls = Object.keys(control).map((name) => `control-${kebab(name)}`);
404
658
  var twMerge = tailwindMerge.extendTailwindMerge({
405
659
  extend: {
406
660
  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 }]
661
+ "font-size": [{ text: scales }],
662
+ rounded: [{ rounded: radii }],
663
+ p: [{ p: namespaces }],
664
+ px: [{ px: [...namespaces, ...controls] }],
665
+ py: [{ py: namespaces }],
666
+ gap: [{ gap: namespaces }],
667
+ size: [{ size: controls }]
414
668
  }
415
669
  }
416
670
  });
@@ -469,16 +723,16 @@ var Spinner = (props) => /* @__PURE__ */ jsxRuntime.jsxs(
469
723
  ]
470
724
  }
471
725
  );
472
- var Camara = (props) => /* @__PURE__ */ jsxRuntime.jsxs(Glyph, { ...props, children: [
726
+ var Camera = (props) => /* @__PURE__ */ jsxRuntime.jsxs(Glyph, { ...props, children: [
473
727
  /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M2 5.5h2.6l1-1.5h4.8l1 1.5H14v7.5H2z" }),
474
728
  /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "8", cy: "9", r: "2.4" })
475
729
  ] });
476
- var Sol = (props) => /* @__PURE__ */ jsxRuntime.jsxs(Glyph, { ...props, children: [
730
+ var Sun = (props) => /* @__PURE__ */ jsxRuntime.jsxs(Glyph, { ...props, children: [
477
731
  /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "8", cy: "8", r: "3" }),
478
732
  /* @__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
733
  ] });
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 }) {
734
+ 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" }) });
735
+ function MediaGlyph({ children, ...props }) {
482
736
  return /* @__PURE__ */ jsxRuntime.jsx(
483
737
  "svg",
484
738
  {
@@ -492,10 +746,10 @@ function GlifoMedia({ children, ...props }) {
492
746
  }
493
747
  );
494
748
  }
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" }) });
749
+ var Play = (props) => /* @__PURE__ */ jsxRuntime.jsx(MediaGlyph, { fill: "currentColor", ...props, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 5v14l11-7z" }) });
750
+ var Pause = (props) => /* @__PURE__ */ jsxRuntime.jsx(MediaGlyph, { fill: "currentColor", ...props, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M6 4h4v16H6V4zm8 0h4v16h-4V4z" }) });
497
751
  var Retry = (props) => /* @__PURE__ */ jsxRuntime.jsx(
498
- GlifoMedia,
752
+ MediaGlyph,
499
753
  {
500
754
  stroke: "currentColor",
501
755
  strokeWidth: 2,
@@ -505,7 +759,7 @@ var Retry = (props) => /* @__PURE__ */ jsxRuntime.jsx(
505
759
  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
760
  }
507
761
  );
508
- var GoBackSeconds = (props) => /* @__PURE__ */ jsxRuntime.jsxs(GlifoMedia, { stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", ...props, children: [
762
+ var GoBackSeconds = (props) => /* @__PURE__ */ jsxRuntime.jsxs(MediaGlyph, { stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", ...props, children: [
509
763
  /* @__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
764
  /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M7.5 10.5L10 8.5V15.5", strokeLinejoin: "round" }),
511
765
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -516,7 +770,7 @@ var GoBackSeconds = (props) => /* @__PURE__ */ jsxRuntime.jsxs(GlifoMedia, { str
516
770
  }
517
771
  )
518
772
  ] });
519
- var AdvanceSeconds = (props) => /* @__PURE__ */ jsxRuntime.jsxs(GlifoMedia, { stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", ...props, children: [
773
+ var AdvanceSeconds = (props) => /* @__PURE__ */ jsxRuntime.jsxs(MediaGlyph, { stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", ...props, children: [
520
774
  /* @__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
775
  /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M7.5 10.5L10 8.5V15.5", strokeLinejoin: "round" }),
522
776
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -527,8 +781,8 @@ var AdvanceSeconds = (props) => /* @__PURE__ */ jsxRuntime.jsxs(GlifoMedia, { st
527
781
  }
528
782
  )
529
783
  ] });
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" }) });
784
+ 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" }) });
785
+ 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
786
  function Accordion({ className, ...props }) {
533
787
  return /* @__PURE__ */ jsxRuntime.jsx(
534
788
  AccordionPrimitive__namespace.Root,
@@ -550,8 +804,8 @@ function AccordionTrigger({
550
804
  headingLevel = 3,
551
805
  ...props
552
806
  }) {
553
- const Encabezado = `h${headingLevel}`;
554
- return /* @__PURE__ */ jsxRuntime.jsx(AccordionPrimitive__namespace.Header, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(Encabezado, { children: /* @__PURE__ */ jsxRuntime.jsxs(
807
+ const Heading = `h${headingLevel}`;
808
+ return /* @__PURE__ */ jsxRuntime.jsx(AccordionPrimitive__namespace.Header, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(Heading, { children: /* @__PURE__ */ jsxRuntime.jsxs(
555
809
  AccordionPrimitive__namespace.Trigger,
556
810
  {
557
811
  className: cn(
@@ -559,7 +813,7 @@ function AccordionTrigger({
559
813
  "font-sans text-lead font-medium text-text-primary",
560
814
  "transition-standard",
561
815
  "hover:text-accent",
562
- "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent",
816
+ "focus-ring",
563
817
  "disabled:pointer-events-none disabled:opacity-50",
564
818
  "[&[data-state=open]>svg]:rotate-180",
565
819
  className
@@ -582,45 +836,20 @@ function AccordionContent({
582
836
  {
583
837
  className: cn(
584
838
  "overflow-hidden",
585
- "motion-safe:data-[state=open]:desplegar motion-safe:data-[state=closed]:replegar"
839
+ "motion-safe:data-[state=open]:expand motion-safe:data-[state=closed]:collapse"
586
840
  ),
587
841
  ...props,
588
842
  children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("pb-step-md font-sans text-ui text-text-secondary max-w-measure", className), children })
589
843
  }
590
844
  );
591
845
  }
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 = {
846
+ var colorGlyph = {
618
847
  accent: "text-accent",
619
848
  success: "text-success",
620
849
  warning: "text-warning",
621
850
  error: "text-error"
622
851
  };
623
- var GLIFO = {
852
+ var GLYPH = {
624
853
  accent: "\u2726",
625
854
  success: "\u2713",
626
855
  warning: "!",
@@ -629,26 +858,26 @@ var GLIFO = {
629
858
  function Alert({
630
859
  className,
631
860
  variant,
632
- enfasis,
861
+ emphasis,
633
862
  title,
634
863
  icon,
635
864
  children,
636
865
  ...props
637
866
  }) {
638
- const tono = variant ?? "accent";
867
+ const tone = variant ?? "accent";
639
868
  return /* @__PURE__ */ jsxRuntime.jsx(
640
869
  "div",
641
870
  {
642
- role: tono === "error" ? "alert" : "status",
643
- className: cn(alert({ variant, enfasis }), className),
871
+ role: tone === "error" ? "alert" : "status",
872
+ className: cn(alert({ variant, emphasis }), className),
644
873
  ...props,
645
874
  children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-sm flex items-start", children: [
646
875
  /* @__PURE__ */ jsxRuntime.jsx(
647
876
  "span",
648
877
  {
649
878
  "aria-hidden": "true",
650
- className: cn("font-mono text-ui leading-normal select-none", glifoColor[tono]),
651
- children: icon ?? GLIFO[tono]
879
+ className: cn("font-mono text-ui leading-normal select-none", colorGlyph[tone]),
880
+ children: icon ?? GLYPH[tone]
652
881
  }
653
882
  ),
654
883
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
@@ -659,79 +888,6 @@ function Alert({
659
888
  }
660
889
  );
661
890
  }
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
891
  var AlertDialog = AlertDialogPrimitive__namespace.Root;
736
892
  var AlertDialogTrigger = AlertDialogPrimitive__namespace.Trigger;
737
893
  function AlertDialogOverlay({
@@ -829,20 +985,6 @@ function AlertDialogAction({
829
985
  }
830
986
  );
831
987
  }
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
988
  function Avatar({ className, size: size2, ...props }) {
847
989
  return /* @__PURE__ */ jsxRuntime.jsx(AvatarPrimitive__namespace.Root, { className: cn(avatar({ size: size2 }), className), ...props });
848
990
  }
@@ -879,22 +1021,22 @@ function AvatarUpload({
879
1021
  ...props
880
1022
  }) {
881
1023
  const id = react.useId();
882
- const [previa, setPrevia] = react.useState(null);
1024
+ const [preview, setPreview] = react.useState(null);
883
1025
  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 = "";
1026
+ if (!preview) return;
1027
+ return () => URL.revokeObjectURL(preview);
1028
+ }, [preview]);
1029
+ function pick(event) {
1030
+ const file = event.target.files?.[0];
1031
+ if (!file) return;
1032
+ setPreview(URL.createObjectURL(file));
1033
+ onSelectFile?.(file);
1034
+ event.target.value = "";
893
1035
  }
894
- const imagen = previa ?? src;
1036
+ const image = preview ?? src;
895
1037
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("relative inline-flex", className), ...props, children: [
896
1038
  /* @__PURE__ */ jsxRuntime.jsxs(Avatar, { size: size2, children: [
897
- imagen ? /* @__PURE__ */ jsxRuntime.jsx(AvatarImage, { src: imagen, alt: "" }) : null,
1039
+ image ? /* @__PURE__ */ jsxRuntime.jsx(AvatarImage, { src: image, alt: "" }) : null,
898
1040
  /* @__PURE__ */ jsxRuntime.jsx(AvatarFallback, { children: fallback })
899
1041
  ] }),
900
1042
  /* @__PURE__ */ jsxRuntime.jsxs(
@@ -905,13 +1047,13 @@ function AvatarUpload({
905
1047
  "rounded-pill absolute -right-1 -bottom-1 flex size-6 cursor-pointer items-center justify-center",
906
1048
  "border-hairline bg-surface-raised text-text-secondary border",
907
1049
  "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.
910
- "has-[:focus-visible]:outline-2 has-[:focus-visible]:outline-offset-2 has-[:focus-visible]:outline-accent",
1050
+ // Focus lives on the input, which is hidden: without this, tabbing to
1051
+ // the control shows up nowhere.
1052
+ "has-[:focus-visible]:outline-2 has-[:focus-visible]:outline-offset-3 has-[:focus-visible]:outline-accent",
911
1053
  disabled && "pointer-events-none opacity-50"
912
1054
  ),
913
1055
  children: [
914
- /* @__PURE__ */ jsxRuntime.jsx(Camara, { "aria-hidden": "true" }),
1056
+ /* @__PURE__ */ jsxRuntime.jsx(Camera, { "aria-hidden": "true" }),
915
1057
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: label }),
916
1058
  /* @__PURE__ */ jsxRuntime.jsx(
917
1059
  "input",
@@ -920,7 +1062,7 @@ function AvatarUpload({
920
1062
  type: "file",
921
1063
  accept,
922
1064
  disabled,
923
- onChange: elegir,
1065
+ onChange: pick,
924
1066
  className: "sr-only"
925
1067
  }
926
1068
  )
@@ -929,62 +1071,56 @@ function AvatarUpload({
929
1071
  )
930
1072
  ] });
931
1073
  }
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
1074
  function Badge({ className, variant, ...props }) {
949
1075
  return /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn(badge({ variant }), className), ...props });
950
1076
  }
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
1077
  function CategoryBadge({ className, active = false, ...props }) {
969
1078
  return /* @__PURE__ */ jsxRuntime.jsx(
970
1079
  "span",
971
1080
  {
972
1081
  "data-active": active || void 0,
973
- className: cn(categoria({ active }), className),
1082
+ className: cn(category({ active }), className),
974
1083
  ...props
975
1084
  }
976
1085
  );
977
1086
  }
978
- var metrica = ["inline-flex items-center gap-step-xs", "rounded-pill", "font-mono text-chip text-text-muted"];
979
1087
  function MetricBadge({ className, boxed = false, ...props }) {
980
1088
  return /* @__PURE__ */ jsxRuntime.jsx(
981
1089
  "span",
982
1090
  {
983
- className: cn(metrica, boxed && "border-hairline px-step-sm py-0.5 border", className),
1091
+ className: cn(metric, boxed && "border-hairline px-step-sm py-0.5 border", className),
984
1092
  ...props
985
1093
  }
986
1094
  );
987
1095
  }
1096
+ function Button({
1097
+ className,
1098
+ variant,
1099
+ size: size2,
1100
+ asChild = false,
1101
+ loading = false,
1102
+ icon,
1103
+ children,
1104
+ disabled,
1105
+ ...props
1106
+ }) {
1107
+ if (asChild) {
1108
+ return /* @__PURE__ */ jsxRuntime.jsx(reactSlot.Slot, { className: cn(button({ variant, size: size2 }), className), ...props, children });
1109
+ }
1110
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1111
+ "button",
1112
+ {
1113
+ className: cn(button({ variant, size: size2 }), className),
1114
+ disabled: disabled === true || loading,
1115
+ "aria-busy": loading || void 0,
1116
+ ...props,
1117
+ children: [
1118
+ loading ? /* @__PURE__ */ jsxRuntime.jsx(Spinner, {}) : icon,
1119
+ children
1120
+ ]
1121
+ }
1122
+ );
1123
+ }
988
1124
  function Calendar({
989
1125
  className,
990
1126
  classNames,
@@ -992,24 +1128,24 @@ function Calendar({
992
1128
  fullWidth = false,
993
1129
  ...props
994
1130
  }) {
995
- const dia = [
1131
+ const day = [
996
1132
  "relative p-0 text-center",
997
1133
  fullWidth ? "h-9 flex-1" : "size-9",
998
1134
  "text-ui font-sans text-text-primary"
999
1135
  ].join(" ");
1000
- const botonDia = [
1136
+ const dayButton = [
1001
1137
  fullWidth ? "h-9 w-full" : "size-9",
1002
1138
  "rounded-chip inline-flex items-center justify-center",
1003
1139
  "transition-standard cursor-pointer",
1004
1140
  "hover:bg-surface-raised",
1005
- "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent",
1141
+ "focus-ring",
1006
1142
  "disabled:pointer-events-none disabled:opacity-50"
1007
1143
  ].join(" ");
1008
- const navegacion = [
1144
+ const navigation = [
1009
1145
  "size-8 rounded-chip inline-flex items-center justify-center",
1010
1146
  "text-text-secondary transition-standard cursor-pointer",
1011
1147
  "hover:bg-surface-raised hover:text-text-primary",
1012
- "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent",
1148
+ "focus-ring",
1013
1149
  "disabled:pointer-events-none disabled:opacity-50"
1014
1150
  ].join(" ");
1015
1151
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -1019,32 +1155,32 @@ function Calendar({
1019
1155
  showOutsideDays,
1020
1156
  className: cn("font-sans", className),
1021
1157
  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.
1158
+ // `relative` is mandatory: the navigation is absolutely positioned
1159
+ // against the root, and without this it anchors to the first positioned
1160
+ // ancestor — or to the viewport — and the arrows end up at the page edges.
1025
1161
  root: cn("relative", fullWidth ? "w-full" : "w-fit"),
1026
1162
  months: cn("gap-step-md flex flex-col sm:flex-row", fullWidth && "w-full"),
1027
1163
  month: cn("gap-step-sm flex flex-col", fullWidth && "min-w-0 flex-1"),
1028
1164
  month_caption: "h-8 flex items-center justify-center",
1029
1165
  caption_label: "text-ui font-sans font-medium text-text-primary capitalize",
1030
1166
  nav: "absolute inset-x-0 top-0 flex items-center justify-between",
1031
- button_previous: navegacion,
1032
- button_next: navegacion,
1167
+ button_previous: navigation,
1168
+ button_next: navigation,
1033
1169
  month_grid: "w-full border-collapse",
1034
1170
  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í.
1171
+ // `textMuted` does not go over `surfaceRaised` — 4.07:1 — and a calendar
1172
+ // inside a Popover lives exactly there.
1037
1173
  weekday: cn(
1038
1174
  "h-9 text-eyebrow font-mono text-text-secondary uppercase flex items-center justify-center",
1039
1175
  fullWidth ? "flex-1" : "w-9"
1040
1176
  ),
1041
1177
  week: "flex w-full",
1042
- day: dia,
1043
- day_button: botonDia,
1178
+ day,
1179
+ day_button: dayButton,
1044
1180
  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.
1181
+ // Days from neighbouring months are dimmed, but not below AA:
1182
+ // `textSecondary` at 85 % gives 5.09:1 in the worst case, which is over
1183
+ // `surfaceRaised`. At 75 % it no longer passes in light mode.
1048
1184
  outside: "text-text-secondary opacity-85",
1049
1185
  disabled: "opacity-50",
1050
1186
  hidden: "invisible",
@@ -1062,7 +1198,7 @@ function Calendar({
1062
1198
  }
1063
1199
  );
1064
1200
  }
1065
- var ETIQUETA = {
1201
+ var LABEL = {
1066
1202
  display: "h1",
1067
1203
  stat: "p",
1068
1204
  h1: "h1",
@@ -1077,46 +1213,6 @@ var ETIQUETA = {
1077
1213
  chip: "span",
1078
1214
  eyebrow: "p"
1079
1215
  };
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
1216
  function Text({
1121
1217
  className,
1122
1218
  variant,
@@ -1127,19 +1223,17 @@ function Text({
1127
1223
  children,
1128
1224
  ...props
1129
1225
  }) {
1130
- const escala = variant ?? "body";
1131
- const limitar = measure ?? escala === "body";
1132
- const clases = cn(texto({ variant, tone }), limitar && "max-w-measure", className);
1226
+ const scale = variant ?? "body";
1227
+ const clamp = measure ?? scale === "body";
1228
+ const classes = cn(text({ variant, tone }), clamp && "max-w-measure", className);
1133
1229
  if (asChild) {
1134
- return /* @__PURE__ */ jsxRuntime.jsx(reactSlot.Slot, { className: clases, ...props, children });
1230
+ return /* @__PURE__ */ jsxRuntime.jsx(reactSlot.Slot, { className: classes, ...props, children });
1135
1231
  }
1136
- const Etiqueta = as ?? ETIQUETA[escala];
1137
- return /* @__PURE__ */ jsxRuntime.jsx(Etiqueta, { className: clases, ...props, children });
1232
+ const Label4 = as ?? LABEL[scale];
1233
+ return /* @__PURE__ */ jsxRuntime.jsx(Label4, { className: classes, ...props, children });
1138
1234
  }
1139
- var SUPERFICIE_TARJETA = "rounded-card border-hairline bg-surface border";
1140
- var HOVER_TARJETA = "transition-standard hover:border-hairline-hover";
1141
1235
  function Card({ className, ...props }) {
1142
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn(SUPERFICIE_TARJETA, className), ...props });
1236
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn(CARD_SURFACE, className), ...props });
1143
1237
  }
1144
1238
  function CardHeader({ className, ...props }) {
1145
1239
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("gap-step-xs p-step-lg flex flex-col", className), ...props });
@@ -1170,7 +1264,7 @@ function Checkbox({ className, ...props }) {
1170
1264
  "rounded-chip border-border bg-surface size-5 shrink-0 cursor-pointer border",
1171
1265
  "text-accent-on transition-standard",
1172
1266
  "hover:border-hairline-hover",
1173
- "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent",
1267
+ "focus-ring",
1174
1268
  "disabled:pointer-events-none disabled:opacity-50",
1175
1269
  "data-[state=checked]:border-accent data-[state=checked]:bg-accent",
1176
1270
  "data-[state=indeterminate]:border-accent data-[state=indeterminate]:bg-accent",
@@ -1213,10 +1307,10 @@ function DateField({
1213
1307
  "font-mono text-ui text-text-primary",
1214
1308
  "transition-standard",
1215
1309
  "hover:border-hairline-hover",
1216
- "focus-visible:border-accent focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent",
1310
+ "focus-visible:border-accent focus-ring",
1217
1311
  "disabled:pointer-events-none disabled:opacity-50",
1218
1312
  "data-[invalid]:border-error data-[invalid]:focus-visible:outline-error",
1219
- // El indicador nativo hereda el esquema del tema activo.
1313
+ // The native indicator inherits the active theme's scheme.
1220
1314
  "scheme-light dark:scheme-dark [&::-webkit-calendar-picker-indicator]:cursor-pointer",
1221
1315
  className
1222
1316
  ),
@@ -1267,7 +1361,7 @@ function DialogContent({
1267
1361
  "rounded-chip text-text-muted absolute top-4 right-4 flex size-8 cursor-pointer items-center justify-center",
1268
1362
  "transition-standard",
1269
1363
  "hover:bg-surface-raised hover:text-text-primary",
1270
- "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent"
1364
+ "focus-ring"
1271
1365
  ),
1272
1366
  children: /* @__PURE__ */ jsxRuntime.jsx(Close, {})
1273
1367
  }
@@ -1416,7 +1510,7 @@ function Input({ className, invalid = false, ...props }) {
1416
1510
  "font-sans text-ui text-text-primary placeholder:text-text-muted",
1417
1511
  "transition-standard",
1418
1512
  "hover:border-hairline-hover",
1419
- "focus-visible:border-accent focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent",
1513
+ "focus-visible:border-accent focus-ring",
1420
1514
  "disabled:pointer-events-none disabled:opacity-50",
1421
1515
  "data-[invalid]:border-error data-[invalid]:focus-visible:outline-error",
1422
1516
  "file:mr-step-sm file:border-0 file:bg-transparent file:font-sans file:text-label file:text-text-secondary",
@@ -1466,7 +1560,7 @@ function PaginationLink({ className, isActive = false, ...props }) {
1466
1560
  "font-sans text-ui text-text-secondary",
1467
1561
  "transition-standard",
1468
1562
  "hover:bg-surface hover:text-text-primary",
1469
- "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent",
1563
+ "focus-ring",
1470
1564
  isActive && "bg-surface-raised text-text-primary",
1471
1565
  className
1472
1566
  ),
@@ -1551,7 +1645,7 @@ function RadioGroupItem({ className, ...props }) {
1551
1645
  "rounded-pill border-border bg-surface size-5 shrink-0 cursor-pointer border",
1552
1646
  "transition-standard",
1553
1647
  "hover:border-hairline-hover",
1554
- "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent",
1648
+ "focus-ring",
1555
1649
  "disabled:pointer-events-none disabled:opacity-50",
1556
1650
  "data-[state=checked]:border-accent",
1557
1651
  className
@@ -1578,7 +1672,7 @@ function SelectTrigger({
1578
1672
  "font-sans text-ui text-text-primary",
1579
1673
  "transition-standard",
1580
1674
  "hover:border-hairline-hover",
1581
- "focus-visible:border-accent focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent",
1675
+ "focus-visible:border-accent focus-ring",
1582
1676
  "disabled:pointer-events-none disabled:opacity-50",
1583
1677
  "data-[placeholder]:text-text-muted",
1584
1678
  className
@@ -1692,23 +1786,23 @@ var panel2 = classVarianceAuthority.cva(
1692
1786
  side: {
1693
1787
  right: [
1694
1788
  "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"
1789
+ "motion-safe:data-[state=open]:slide-in-right",
1790
+ "motion-safe:data-[state=closed]:slide-out-right"
1697
1791
  ],
1698
1792
  left: [
1699
1793
  "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"
1794
+ "motion-safe:data-[state=open]:slide-in-left",
1795
+ "motion-safe:data-[state=closed]:slide-out-left"
1702
1796
  ],
1703
1797
  top: [
1704
1798
  "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"
1799
+ "motion-safe:data-[state=open]:slide-in-top",
1800
+ "motion-safe:data-[state=closed]:slide-out-top"
1707
1801
  ],
1708
1802
  bottom: [
1709
1803
  "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"
1804
+ "motion-safe:data-[state=open]:slide-in-bottom",
1805
+ "motion-safe:data-[state=closed]:slide-out-bottom"
1712
1806
  ]
1713
1807
  }
1714
1808
  },
@@ -1728,7 +1822,7 @@ function SheetContent({ className, side, children, ...props }) {
1728
1822
  "rounded-chip text-text-muted absolute top-4 right-4 flex size-8 cursor-pointer items-center justify-center",
1729
1823
  "transition-standard",
1730
1824
  "hover:bg-surface-raised hover:text-text-primary",
1731
- "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent"
1825
+ "focus-ring"
1732
1826
  ),
1733
1827
  children: /* @__PURE__ */ jsxRuntime.jsx(Close, {})
1734
1828
  }
@@ -1803,7 +1897,7 @@ function Switch({ className, ...props }) {
1803
1897
  "rounded-pill border-border bg-surface-raised inline-flex h-6 w-11 shrink-0 cursor-pointer items-center border",
1804
1898
  "transition-standard",
1805
1899
  "hover:border-hairline-hover",
1806
- "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent",
1900
+ "focus-ring",
1807
1901
  "disabled:pointer-events-none disabled:opacity-50",
1808
1902
  "data-[state=checked]:border-accent data-[state=checked]:bg-accent",
1809
1903
  className
@@ -1904,7 +1998,7 @@ function TabsTrigger({
1904
1998
  "font-sans text-label text-text-secondary",
1905
1999
  "transition-standard",
1906
2000
  "hover:text-text-primary",
1907
- "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent",
2001
+ "focus-ring",
1908
2002
  "disabled:pointer-events-none disabled:opacity-50",
1909
2003
  "data-[state=active]:bg-surface-raised data-[state=active]:text-text-primary",
1910
2004
  className
@@ -1921,7 +2015,7 @@ function TabsContent({
1921
2015
  TabsPrimitive__namespace.Content,
1922
2016
  {
1923
2017
  className: cn(
1924
- "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent",
2018
+ "focus-ring",
1925
2019
  className
1926
2020
  ),
1927
2021
  ...props
@@ -1940,7 +2034,7 @@ function Textarea({ className, invalid = false, ...props }) {
1940
2034
  "font-sans text-ui text-text-primary placeholder:text-text-muted",
1941
2035
  "transition-standard",
1942
2036
  "hover:border-hairline-hover",
1943
- "focus-visible:border-accent focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent",
2037
+ "focus-visible:border-accent focus-ring",
1944
2038
  "disabled:pointer-events-none disabled:opacity-50",
1945
2039
  "data-[invalid]:border-error data-[invalid]:focus-visible:outline-error",
1946
2040
  className
@@ -1995,7 +2089,7 @@ function Toast({ className, variant, children, ...props }) {
1995
2089
  "rounded-chip text-text-muted absolute top-3 right-3 flex size-7 cursor-pointer items-center justify-center",
1996
2090
  "transition-standard",
1997
2091
  "hover:bg-surface-raised hover:text-text-primary",
1998
- "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent"
2092
+ "focus-ring"
1999
2093
  ),
2000
2094
  children: /* @__PURE__ */ jsxRuntime.jsx(Close, {})
2001
2095
  }
@@ -2020,62 +2114,62 @@ function ToastDescription({
2020
2114
  }) {
2021
2115
  return /* @__PURE__ */ jsxRuntime.jsx(ToastPrimitive__namespace.Description, { className: cn("max-w-measure", className), ...props });
2022
2116
  }
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();
2117
+ var warnings = [];
2118
+ var subscribers = /* @__PURE__ */ new Set();
2119
+ var counter = 0;
2120
+ function emit(next) {
2121
+ warnings = next;
2122
+ for (const warn of subscribers) warn();
2029
2123
  }
2030
- var suscribirse = (avisar) => {
2031
- suscriptores.add(avisar);
2124
+ var subscribe = (warn) => {
2125
+ subscribers.add(warn);
2032
2126
  return () => {
2033
- suscriptores.delete(avisar);
2127
+ subscribers.delete(warn);
2034
2128
  };
2035
2129
  };
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 } },
2130
+ var read = () => warnings;
2131
+ var EMPTY = [];
2132
+ var onServer = () => EMPTY;
2133
+ function create(message, options = {}) {
2134
+ counter += 1;
2135
+ const id = `arrecife-toast-${counter}`;
2136
+ const { description, ...rest } = options;
2137
+ emit([
2138
+ ...warnings,
2139
+ {
2140
+ ...rest,
2141
+ // The first argument is the main line. If a `title` also arrives, the
2142
+ // message becomes the description: that way `toast('Guardado')` and
2143
+ // `toast('No se pudo guardar', { title: 'Error' })` both read well.
2144
+ ...options.title ? { description: description ?? message } : { title: message, ...description === void 0 ? {} : { description } },
2051
2145
  id,
2052
- abierto: true
2146
+ open: true
2053
2147
  }
2054
2148
  ]);
2055
2149
  return id;
2056
2150
  }
2057
- function descartar(id) {
2058
- emitir(avisos.map((a) => id === void 0 || a.id === id ? { ...a, abierto: false } : a));
2151
+ function discard(id) {
2152
+ emit(warnings.map((a) => id === void 0 || a.id === id ? { ...a, open: false } : a));
2059
2153
  }
2060
- function retirar(id) {
2061
- emitir(avisos.filter((a) => a.id !== id));
2154
+ function remove(id) {
2155
+ emit(warnings.filter((a) => a.id !== id));
2062
2156
  }
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
2157
+ var toast2 = Object.assign(create, {
2158
+ success: (message, options = {}) => create(message, { ...options, variant: "success" }),
2159
+ error: (message, options = {}) => create(message, { ...options, variant: "error" }),
2160
+ dismiss: discard
2067
2161
  });
2068
2162
  function Toaster({ duration = 5e3, label = "Avisos" }) {
2069
- const lista = react.useSyncExternalStore(suscribirse, leer, enServidor);
2163
+ const list = react.useSyncExternalStore(subscribe, read, onServer);
2070
2164
  return /* @__PURE__ */ jsxRuntime.jsxs(ToastProvider, { duration, label, children: [
2071
- lista.map(({ id, title, description, variant, duration: propio, action, abierto }) => /* @__PURE__ */ jsxRuntime.jsxs(
2165
+ list.map(({ id, title, description, variant, duration: own, action, open }) => /* @__PURE__ */ jsxRuntime.jsxs(
2072
2166
  Toast,
2073
2167
  {
2074
- open: abierto,
2168
+ open,
2075
2169
  variant: variant ?? "neutral",
2076
- ...propio === void 0 ? {} : { duration: propio },
2077
- onOpenChange: (sigue) => {
2078
- if (!sigue) retirar(id);
2170
+ ...own === void 0 ? {} : { duration: own },
2171
+ onOpenChange: (stillOpen) => {
2172
+ if (!stillOpen) remove(id);
2079
2173
  },
2080
2174
  children: [
2081
2175
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
@@ -2112,15 +2206,9 @@ function TooltipContent({
2112
2206
  }
2113
2207
  ) });
2114
2208
  }
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 });
2209
+ function CardShell({ asChild = false, className, children, ...props }) {
2210
+ const Root18 = asChild ? reactSlot.Slot : "a";
2211
+ return /* @__PURE__ */ jsxRuntime.jsx(Root18, { className: cn(CARD, className), ...props, children });
2124
2212
  }
2125
2213
  function ArticleCard({
2126
2214
  title,
@@ -2130,17 +2218,18 @@ function ArticleCard({
2130
2218
  readingMinutes,
2131
2219
  tags,
2132
2220
  headingLevel = 3,
2221
+ tagAsChild,
2133
2222
  className,
2134
2223
  ...props
2135
2224
  }) {
2136
2225
  const meta = [
2137
- date ? /* @__PURE__ */ jsxRuntime.jsx("time", { dateTime, children: date }, "fecha") : null,
2226
+ date ? /* @__PURE__ */ jsxRuntime.jsx("time", { dateTime, children: date }, "date") : null,
2138
2227
  readingMinutes ? /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
2139
2228
  readingMinutes,
2140
2229
  " min de lectura"
2141
2230
  ] }, "lectura") : null
2142
2231
  ].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: [
2232
+ 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
2233
  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
2234
  i > 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: "\xB7" }) : null,
2146
2235
  m
@@ -2155,18 +2244,20 @@ function ArticleCard({
2155
2244
  }
2156
2245
  ),
2157
2246
  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
2247
+ tags && tags.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "gap-step-xs mt-auto flex flex-wrap pt-step-sm", children: tags.map(
2248
+ (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)
2249
+ ) }) : null
2159
2250
  ] }) });
2160
2251
  }
2161
2252
  var AVATAR = "size-[52px]";
2162
- function iniciales(nombre) {
2163
- return nombre.split(/\s+/).slice(0, 2).map((p) => p[0] ?? "").join("").toUpperCase();
2253
+ function initials(name) {
2254
+ return name.split(/\s+/).slice(0, 2).map((p) => p[0] ?? "").join("").toUpperCase();
2164
2255
  }
2165
2256
  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: [
2257
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn(CARD_SURFACE, "p-step-lg gap-step-md flex items-start", className), ...props, children: [
2167
2258
  /* @__PURE__ */ jsxRuntime.jsxs(Avatar, { className: cn(AVATAR, "shrink-0"), children: [
2168
2259
  src ? /* @__PURE__ */ jsxRuntime.jsx(AvatarImage, { src, alt: "" }) : null,
2169
- /* @__PURE__ */ jsxRuntime.jsx(AvatarFallback, { children: iniciales(name) })
2260
+ /* @__PURE__ */ jsxRuntime.jsx(AvatarFallback, { children: initials(name) })
2170
2261
  ] }),
2171
2262
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-xs flex min-w-0 flex-1 flex-col", children: [
2172
2263
  /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "ui", as: "p", className: "font-medium", children: name }),
@@ -2184,45 +2275,45 @@ function formatTime(seconds) {
2184
2275
  }
2185
2276
  var WAVEFORM_HEIGHTS = [35, 65, 100, 65, 35];
2186
2277
  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(
2278
+ function Wave({ strokeWidth, height, playing }) {
2279
+ 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
2280
  "span",
2190
2281
  {
2191
- className: cn("rounded-pill bg-accent transition-standard", grosor),
2192
- style: { height: `${h}%`, opacity: sonando ? 1 : 0.3 + h / 100 * 0.4 }
2282
+ className: cn("rounded-pill bg-accent transition-standard", strokeWidth),
2283
+ style: { height: `${h}%`, opacity: playing ? 1 : 0.3 + h / 100 * 0.4 }
2193
2284
  },
2194
2285
  i
2195
2286
  )) });
2196
2287
  }
2197
- function BarraProgreso({
2198
- alto,
2199
- perilla,
2200
- progreso,
2288
+ function ProgressBar({
2289
+ height,
2290
+ knob,
2291
+ progress,
2201
2292
  redondeada = true,
2202
2293
  tabIndex = 0,
2203
- ...puntero
2294
+ ...pointer
2204
2295
  }) {
2205
2296
  return /* @__PURE__ */ jsxRuntime.jsxs(
2206
2297
  "div",
2207
2298
  {
2208
2299
  className: cn(
2209
2300
  "group bg-surface-raised relative cursor-pointer",
2210
- alto,
2301
+ height,
2211
2302
  redondeada && "rounded-pill"
2212
2303
  ),
2213
2304
  role: "slider",
2214
2305
  "aria-label": "Progreso del audio",
2215
2306
  "aria-valuemin": 0,
2216
2307
  "aria-valuemax": 100,
2217
- "aria-valuenow": Math.round(progreso),
2308
+ "aria-valuenow": Math.round(progress),
2218
2309
  tabIndex,
2219
- ...puntero,
2310
+ ...pointer,
2220
2311
  children: [
2221
2312
  /* @__PURE__ */ jsxRuntime.jsx(
2222
2313
  "div",
2223
2314
  {
2224
2315
  className: cn("bg-accent absolute h-full", redondeada && "rounded-pill"),
2225
- style: { width: `${progreso}%` }
2316
+ style: { width: `${progress}%` }
2226
2317
  }
2227
2318
  ),
2228
2319
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -2230,9 +2321,9 @@ function BarraProgreso({
2230
2321
  {
2231
2322
  className: "rounded-pill bg-text-primary absolute top-1/2 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100",
2232
2323
  style: {
2233
- width: perilla,
2234
- height: perilla,
2235
- left: `calc(${progreso}% - ${perilla / 2}px)`
2324
+ width: knob,
2325
+ height: knob,
2326
+ left: `calc(${progress}% - ${knob / 2}px)`
2236
2327
  }
2237
2328
  }
2238
2329
  )
@@ -2240,66 +2331,66 @@ function BarraProgreso({
2240
2331
  }
2241
2332
  );
2242
2333
  }
2243
- var CONTROL = "rounded-chip transition-standard cursor-pointer focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent";
2244
- function BotonSalto({
2245
- segundos,
2334
+ var CONTROL = "rounded-chip transition-standard cursor-pointer focus-ring";
2335
+ function SkipButton({
2336
+ seconds,
2246
2337
  className,
2247
- tamano,
2248
- onSaltar
2338
+ size: size2,
2339
+ onSkip
2249
2340
  }) {
2250
2341
  return /* @__PURE__ */ jsxRuntime.jsx(
2251
2342
  "button",
2252
2343
  {
2253
2344
  type: "button",
2254
- onClick: () => onSaltar(segundos),
2345
+ onClick: () => onSkip(seconds),
2255
2346
  className: cn("text-text-muted hover:text-text-primary", CONTROL, className),
2256
- "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 })
2347
+ "aria-label": seconds < 0 ? "Retroceder 15 segundos" : "Adelantar 15 segundos",
2348
+ children: seconds < 0 ? /* @__PURE__ */ jsxRuntime.jsx(GoBackSeconds, { className: size2 }) : /* @__PURE__ */ jsxRuntime.jsx(AdvanceSeconds, { className: size2 })
2258
2349
  }
2259
2350
  );
2260
2351
  }
2261
- function BotonVelocidad({
2352
+ function SpeedButton({
2262
2353
  className,
2263
- velocidad,
2264
- onCambiar
2354
+ speed,
2355
+ onChange
2265
2356
  }) {
2266
2357
  return /* @__PURE__ */ jsxRuntime.jsxs(
2267
2358
  "button",
2268
2359
  {
2269
2360
  type: "button",
2270
- onClick: onCambiar,
2361
+ onClick: onChange,
2271
2362
  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.
2363
+ // `textMuted` over `surfaceRaised` gives 4.07:1 in dark and fails AA.
2364
+ // The portfolio original carries that same failure.
2274
2365
  "text-text-secondary hover:text-text-primary bg-surface-raised",
2275
2366
  CONTROL,
2276
2367
  className
2277
2368
  ),
2278
2369
  "aria-label": "Cambiar velocidad de reproducci\xF3n",
2279
2370
  children: [
2280
- velocidad,
2371
+ speed,
2281
2372
  "x"
2282
2373
  ]
2283
2374
  }
2284
2375
  );
2285
2376
  }
2286
- function Volumen({
2287
- tamano,
2288
- ancho,
2289
- silenciado,
2290
- volumen,
2291
- onSilenciar,
2292
- onCambiarVolumen
2377
+ function Volume({
2378
+ size: size2,
2379
+ width,
2380
+ muted,
2381
+ volume,
2382
+ onMute,
2383
+ onVolumeChange
2293
2384
  }) {
2294
2385
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-xs hidden items-center sm:flex", children: [
2295
2386
  /* @__PURE__ */ jsxRuntime.jsx(
2296
2387
  "button",
2297
2388
  {
2298
2389
  type: "button",
2299
- onClick: onSilenciar,
2390
+ onClick: onMute,
2300
2391
  className: cn("text-text-muted hover:text-text-primary p-1", CONTROL),
2301
- "aria-label": silenciado ? "Activar sonido" : "Silenciar",
2302
- children: silenciado || volumen === 0 ? /* @__PURE__ */ jsxRuntime.jsx(VolumeMuted, { className: tamano }) : /* @__PURE__ */ jsxRuntime.jsx(VolumeOn, { className: tamano })
2392
+ "aria-label": muted ? "Activar sonido" : "Silenciar",
2393
+ children: muted || volume === 0 ? /* @__PURE__ */ jsxRuntime.jsx(VolumeMuted, { className: size2 }) : /* @__PURE__ */ jsxRuntime.jsx(VolumeOn, { className: size2 })
2303
2394
  }
2304
2395
  ),
2305
2396
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -2309,47 +2400,47 @@ function Volumen({
2309
2400
  min: "0",
2310
2401
  max: "1",
2311
2402
  step: "0.1",
2312
- value: silenciado ? 0 : volumen,
2313
- onChange: onCambiarVolumen,
2403
+ value: muted ? 0 : volume,
2404
+ onChange: onVolumeChange,
2314
2405
  className: cn(
2315
2406
  "bg-surface-raised rounded-pill h-1 cursor-pointer appearance-none",
2316
2407
  "[accent-color:var(--color-accent)]",
2317
- ancho
2408
+ width
2318
2409
  ),
2319
2410
  "aria-label": "Volumen"
2320
2411
  }
2321
2412
  )
2322
2413
  ] });
2323
2414
  }
2324
- function IconoPlay({ className, cargando, error, sonando }) {
2325
- if (cargando) return /* @__PURE__ */ jsxRuntime.jsx(Spinner, { className: cn("motion-safe:animate-spin", className) });
2415
+ function PlayIcon({ className, loading, error, playing }) {
2416
+ if (loading) return /* @__PURE__ */ jsxRuntime.jsx(Spinner, { className: cn("motion-safe:animate-spin", className) });
2326
2417
  if (error) return /* @__PURE__ */ jsxRuntime.jsx(Retry, { className });
2327
- if (sonando) return /* @__PURE__ */ jsxRuntime.jsx(Pause, { className });
2418
+ if (playing) return /* @__PURE__ */ jsxRuntime.jsx(Pause, { className });
2328
2419
  return /* @__PURE__ */ jsxRuntime.jsx(Play, { className });
2329
2420
  }
2330
- function BotonPlay({
2421
+ function PlayButton({
2331
2422
  className,
2332
- tamano,
2333
- etiqueta,
2334
- texto: texto2,
2335
- onAlternar,
2336
- ...estado
2423
+ size: size2,
2424
+ label,
2425
+ text: text2,
2426
+ onToggle,
2427
+ ...state
2337
2428
  }) {
2338
2429
  return /* @__PURE__ */ jsxRuntime.jsxs(
2339
2430
  "button",
2340
2431
  {
2341
2432
  type: "button",
2342
- onClick: onAlternar,
2433
+ onClick: onToggle,
2343
2434
  className: cn(
2344
2435
  "text-accent-on transition-standard cursor-pointer",
2345
- "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",
2436
+ "focus-ring",
2437
+ state.error ? "bg-error hover:bg-error/80" : "bg-accent hover:bg-accent-hover",
2347
2438
  className
2348
2439
  ),
2349
- "aria-label": etiqueta,
2440
+ "aria-label": label,
2350
2441
  children: [
2351
- /* @__PURE__ */ jsxRuntime.jsx(IconoPlay, { className: tamano, ...estado }),
2352
- texto2 ? /* @__PURE__ */ jsxRuntime.jsx("span", { children: texto2 }) : null
2442
+ /* @__PURE__ */ jsxRuntime.jsx(PlayIcon, { className: size2, ...state }),
2443
+ text2 ? /* @__PURE__ */ jsxRuntime.jsx("span", { children: text2 }) : null
2353
2444
  ]
2354
2445
  }
2355
2446
  );
@@ -2368,7 +2459,7 @@ function AudioPlayer({ src, title, mode = "full", onFirstPlay }) {
2368
2459
  const [isMuted, setIsMuted] = react.useState(false);
2369
2460
  const [playbackRate, setPlaybackRate] = react.useState(1);
2370
2461
  const [isStaticVisible, setIsStaticVisible] = react.useState(true);
2371
- const flotante = mode === "banner" || mode === "compact";
2462
+ const floating = mode === "banner" || mode === "compact";
2372
2463
  react.useEffect(() => {
2373
2464
  const audio = audioRef.current;
2374
2465
  if (!audio) return;
@@ -2411,7 +2502,7 @@ function AudioPlayer({ src, title, mode = "full", onFirstPlay }) {
2411
2502
  };
2412
2503
  }, []);
2413
2504
  react.useEffect(() => {
2414
- if (!flotante || !staticRef.current) return;
2505
+ if (!floating || !staticRef.current) return;
2415
2506
  const observer = new IntersectionObserver(
2416
2507
  (entries) => {
2417
2508
  const entry = entries[0];
@@ -2421,7 +2512,7 @@ function AudioPlayer({ src, title, mode = "full", onFirstPlay }) {
2421
2512
  );
2422
2513
  observer.observe(staticRef.current);
2423
2514
  return () => observer.disconnect();
2424
- }, [flotante]);
2515
+ }, [floating]);
2425
2516
  const togglePlay = react.useCallback(async () => {
2426
2517
  const audio = audioRef.current;
2427
2518
  if (!audio) return;
@@ -2510,37 +2601,37 @@ function AudioPlayer({ src, title, mode = "full", onFirstPlay }) {
2510
2601
  [duration]
2511
2602
  );
2512
2603
  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 = {
2604
+ const playLabel = hasError ? "Reintentar" : isPlaying ? "Pausar" : "Reproducir";
2605
+ const playState = { loading: isLoading, error: hasError, playing: isPlaying };
2606
+ const pointer = {
2516
2607
  onPointerDown: handlePointerDown,
2517
2608
  onPointerMove: handlePointerMove,
2518
2609
  onPointerUp: handlePointerUp
2519
2610
  };
2520
- const flotanteJsx = /* @__PURE__ */ jsxRuntime.jsxs(
2611
+ const floatingJsx = /* @__PURE__ */ jsxRuntime.jsxs(
2521
2612
  "div",
2522
2613
  {
2523
2614
  "aria-hidden": isStaticVisible,
2524
2615
  className: cn(
2525
2616
  "fixed inset-x-0 bottom-0 z-40 xl:hidden",
2526
- // Aparece y desaparece: el sistema no anima desplazamiento.
2617
+ // It appears and disappears: the system does not animate displacement.
2527
2618
  isStaticVisible && "hidden"
2528
2619
  ),
2529
2620
  children: [
2530
2621
  /* @__PURE__ */ jsxRuntime.jsx(
2531
- BarraProgreso,
2622
+ ProgressBar,
2532
2623
  {
2533
- alto: "h-1",
2534
- perilla: 10,
2535
- progreso: progress,
2624
+ height: "h-1",
2625
+ knob: 10,
2626
+ progress,
2536
2627
  redondeada: false,
2537
2628
  tabIndex: -1,
2538
- ...puntero
2629
+ ...pointer
2539
2630
  }
2540
2631
  ),
2541
2632
  /* @__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
2633
  /* @__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 }),
2634
+ /* @__PURE__ */ jsxRuntime.jsx(Wave, { strokeWidth: "w-[2px]", height: "h-4", playing: isPlaying }),
2544
2635
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-eyebrow font-mono text-accent hidden uppercase sm:block", children: "Narraci\xF3n" })
2545
2636
  ] }),
2546
2637
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
@@ -2552,24 +2643,24 @@ function AudioPlayer({ src, title, mode = "full", onFirstPlay }) {
2552
2643
  ] })
2553
2644
  ] }),
2554
2645
  /* @__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 }),
2646
+ /* @__PURE__ */ jsxRuntime.jsx(SkipButton, { seconds: -15, className: "p-1.5", size: "w-5 h-5", onSkip: skip }),
2556
2647
  /* @__PURE__ */ jsxRuntime.jsx(
2557
- BotonPlay,
2648
+ PlayButton,
2558
2649
  {
2559
2650
  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
2651
+ size: "w-4 h-4",
2652
+ label: playLabel,
2653
+ onToggle: togglePlay,
2654
+ ...playState
2564
2655
  }
2565
2656
  ),
2566
- /* @__PURE__ */ jsxRuntime.jsx(BotonSalto, { segundos: 15, className: "p-1.5", tamano: "w-5 h-5", onSaltar: skip }),
2657
+ /* @__PURE__ */ jsxRuntime.jsx(SkipButton, { seconds: 15, className: "p-1.5", size: "w-5 h-5", onSkip: skip }),
2567
2658
  /* @__PURE__ */ jsxRuntime.jsx(
2568
- BotonVelocidad,
2659
+ SpeedButton,
2569
2660
  {
2570
2661
  className: "text-label ml-0.5 px-1.5 py-0.5",
2571
- velocidad: playbackRate,
2572
- onCambiar: handlePlaybackRateChange
2662
+ speed: playbackRate,
2663
+ onChange: handlePlaybackRateChange
2573
2664
  }
2574
2665
  )
2575
2666
  ] })
@@ -2582,7 +2673,7 @@ function AudioPlayer({ src, title, mode = "full", onFirstPlay }) {
2582
2673
  /* @__PURE__ */ jsxRuntime.jsx("audio", { ref: audioRef, src, preload: "metadata" }),
2583
2674
  /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: staticRef, children: [
2584
2675
  /* @__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 }),
2676
+ /* @__PURE__ */ jsxRuntime.jsx(Wave, { strokeWidth: "w-[3px]", height: "h-5", playing: isPlaying }),
2586
2677
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-eyebrow font-mono text-accent uppercase", children: "Narraci\xF3n de audio" }),
2587
2678
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-chip font-mono text-text-muted ml-auto tabular-nums", children: [
2588
2679
  formatTime(currentTime),
@@ -2590,125 +2681,125 @@ function AudioPlayer({ src, title, mode = "full", onFirstPlay }) {
2590
2681
  formatTime(duration)
2591
2682
  ] })
2592
2683
  ] }),
2593
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-step-md", children: /* @__PURE__ */ jsxRuntime.jsx(BarraProgreso, { alto: "h-1.5", perilla: 14, progreso: progress, ...puntero }) }),
2684
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-step-md", children: /* @__PURE__ */ jsxRuntime.jsx(ProgressBar, { height: "h-1.5", knob: 14, progress, ...pointer }) }),
2594
2685
  /* @__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 }),
2686
+ /* @__PURE__ */ jsxRuntime.jsx(SkipButton, { seconds: -15, className: "p-2", size: "w-6 h-6", onSkip: skip }),
2596
2687
  /* @__PURE__ */ jsxRuntime.jsx(
2597
- BotonPlay,
2688
+ PlayButton,
2598
2689
  {
2599
2690
  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
2691
+ size: "w-4 h-4",
2692
+ label: playLabel,
2693
+ text: playLabel,
2694
+ onToggle: togglePlay,
2695
+ ...playState
2605
2696
  }
2606
2697
  ),
2607
- /* @__PURE__ */ jsxRuntime.jsx(BotonSalto, { segundos: 15, className: "p-2", tamano: "w-6 h-6", onSaltar: skip }),
2698
+ /* @__PURE__ */ jsxRuntime.jsx(SkipButton, { seconds: 15, className: "p-2", size: "w-6 h-6", onSkip: skip }),
2608
2699
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-sm ml-auto flex items-center", children: [
2609
2700
  /* @__PURE__ */ jsxRuntime.jsx(
2610
- BotonVelocidad,
2701
+ SpeedButton,
2611
2702
  {
2612
2703
  className: "text-label px-2 py-1",
2613
- velocidad: playbackRate,
2614
- onCambiar: handlePlaybackRateChange
2704
+ speed: playbackRate,
2705
+ onChange: handlePlaybackRateChange
2615
2706
  }
2616
2707
  ),
2617
2708
  /* @__PURE__ */ jsxRuntime.jsx(
2618
- Volumen,
2709
+ Volume,
2619
2710
  {
2620
- tamano: "w-4 h-4",
2621
- ancho: "w-16",
2622
- silenciado: isMuted,
2623
- volumen: volume,
2624
- onSilenciar: toggleMute,
2625
- onCambiarVolumen: handleVolumeChange
2711
+ size: "w-4 h-4",
2712
+ width: "w-16",
2713
+ muted: isMuted,
2714
+ volume,
2715
+ onMute: toggleMute,
2716
+ onVolumeChange: handleVolumeChange
2626
2717
  }
2627
2718
  )
2628
2719
  ] })
2629
2720
  ] })
2630
2721
  ] }),
2631
- flotanteJsx
2722
+ floatingJsx
2632
2723
  ] });
2633
2724
  }
2634
2725
  if (mode === "compact") {
2635
2726
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2636
2727
  /* @__PURE__ */ jsxRuntime.jsx("audio", { ref: audioRef, src, preload: "metadata" }),
2637
2728
  /* @__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 }) }),
2729
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-step-xs", children: /* @__PURE__ */ jsxRuntime.jsx(ProgressBar, { height: "h-1.5", knob: 12, progress, ...pointer }) }),
2639
2730
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-chip font-mono text-text-muted mb-step-sm flex justify-between", children: [
2640
2731
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: formatTime(currentTime) }),
2641
2732
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: formatTime(duration) })
2642
2733
  ] }),
2643
2734
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
2644
2735
  /* @__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 }),
2736
+ /* @__PURE__ */ jsxRuntime.jsx(SkipButton, { seconds: -15, className: "p-1.5", size: "w-5 h-5", onSkip: skip }),
2646
2737
  /* @__PURE__ */ jsxRuntime.jsx(
2647
- BotonPlay,
2738
+ PlayButton,
2648
2739
  {
2649
2740
  className: "rounded-pill flex size-[34px] items-center justify-center",
2650
- tamano: "w-4 h-4",
2651
- etiqueta: etiquetaPlay,
2652
- onAlternar: togglePlay,
2653
- ...estadoPlay
2741
+ size: "w-4 h-4",
2742
+ label: playLabel,
2743
+ onToggle: togglePlay,
2744
+ ...playState
2654
2745
  }
2655
2746
  ),
2656
- /* @__PURE__ */ jsxRuntime.jsx(BotonSalto, { segundos: 15, className: "p-1.5", tamano: "w-5 h-5", onSaltar: skip })
2747
+ /* @__PURE__ */ jsxRuntime.jsx(SkipButton, { seconds: 15, className: "p-1.5", size: "w-5 h-5", onSkip: skip })
2657
2748
  ] }),
2658
2749
  /* @__PURE__ */ jsxRuntime.jsx(
2659
- BotonVelocidad,
2750
+ SpeedButton,
2660
2751
  {
2661
2752
  className: "text-label px-1.5 py-0.5",
2662
- velocidad: playbackRate,
2663
- onCambiar: handlePlaybackRateChange
2753
+ speed: playbackRate,
2754
+ onChange: handlePlaybackRateChange
2664
2755
  }
2665
2756
  )
2666
2757
  ] })
2667
2758
  ] }),
2668
- flotanteJsx
2759
+ floatingJsx
2669
2760
  ] });
2670
2761
  }
2671
2762
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-surface rounded-control border-border p-step-md w-full border", children: [
2672
2763
  /* @__PURE__ */ jsxRuntime.jsx("audio", { ref: audioRef, src, preload: "metadata" }),
2673
2764
  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 }) }),
2765
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-step-sm", children: /* @__PURE__ */ jsxRuntime.jsx(ProgressBar, { height: "h-2", knob: 16, progress, ...pointer }) }),
2675
2766
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-chip font-mono text-text-muted mb-step-sm flex justify-between", children: [
2676
2767
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: formatTime(currentTime) }),
2677
2768
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: formatTime(duration) })
2678
2769
  ] }),
2679
2770
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
2680
2771
  /* @__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 }),
2772
+ /* @__PURE__ */ jsxRuntime.jsx(SkipButton, { seconds: -15, className: "p-2", size: "w-5 h-5", onSkip: skip }),
2682
2773
  /* @__PURE__ */ jsxRuntime.jsx(
2683
- BotonPlay,
2774
+ PlayButton,
2684
2775
  {
2685
2776
  className: "rounded-pill flex size-12 items-center justify-center",
2686
- tamano: "w-6 h-6",
2687
- etiqueta: etiquetaPlay,
2688
- onAlternar: togglePlay,
2689
- ...estadoPlay
2777
+ size: "w-6 h-6",
2778
+ label: playLabel,
2779
+ onToggle: togglePlay,
2780
+ ...playState
2690
2781
  }
2691
2782
  ),
2692
- /* @__PURE__ */ jsxRuntime.jsx(BotonSalto, { segundos: 15, className: "p-2", tamano: "w-5 h-5", onSaltar: skip })
2783
+ /* @__PURE__ */ jsxRuntime.jsx(SkipButton, { seconds: 15, className: "p-2", size: "w-5 h-5", onSkip: skip })
2693
2784
  ] }),
2694
2785
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-sm flex items-center", children: [
2695
2786
  /* @__PURE__ */ jsxRuntime.jsx(
2696
- BotonVelocidad,
2787
+ SpeedButton,
2697
2788
  {
2698
2789
  className: "text-label px-2 py-1",
2699
- velocidad: playbackRate,
2700
- onCambiar: handlePlaybackRateChange
2790
+ speed: playbackRate,
2791
+ onChange: handlePlaybackRateChange
2701
2792
  }
2702
2793
  ),
2703
2794
  /* @__PURE__ */ jsxRuntime.jsx(
2704
- Volumen,
2795
+ Volume,
2705
2796
  {
2706
- tamano: "w-5 h-5",
2707
- ancho: "w-20",
2708
- silenciado: isMuted,
2709
- volumen: volume,
2710
- onSilenciar: toggleMute,
2711
- onCambiarVolumen: handleVolumeChange
2797
+ size: "w-5 h-5",
2798
+ width: "w-20",
2799
+ muted: isMuted,
2800
+ volume,
2801
+ onMute: toggleMute,
2802
+ onVolumeChange: handleVolumeChange
2712
2803
  }
2713
2804
  )
2714
2805
  ] })
@@ -2732,7 +2823,7 @@ function Blockquote({ children, author, source, className, ...props }) {
2732
2823
  }
2733
2824
  );
2734
2825
  }
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";
2826
+ var LINK = "transition-standard hover:text-accent focus-ring rounded-chip cursor-pointer";
2736
2827
  function Breadcrumb({
2737
2828
  items,
2738
2829
  homeHref = "/",
@@ -2741,28 +2832,28 @@ function Breadcrumb({
2741
2832
  className,
2742
2833
  ...props
2743
2834
  }) {
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 });
2835
+ 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
2836
  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: "~" })) }),
2837
+ /* @__PURE__ */ jsxRuntime.jsx("li", { children: link(homeHref, /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-label": homeLabel, children: "~" })) }),
2747
2838
  items.map((item2, i) => {
2748
- const ultimo = i === items.length - 1;
2839
+ const last = i === items.length - 1;
2749
2840
  return /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "gap-step-xs flex items-center", children: [
2750
2841
  /* @__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 })
2842
+ 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
2843
  ] }, i);
2753
2844
  })
2754
2845
  ] }) });
2755
2846
  }
2756
2847
  function CodeBlock({ children, language, copyText, className, ...props }) {
2757
2848
  const [copiado, setCopiado] = react.useState(false);
2758
- const temporizador = react.useRef(null);
2759
- const copiar = react.useCallback(async () => {
2849
+ const timer = react.useRef(null);
2850
+ const copy = react.useCallback(async () => {
2760
2851
  if (!copyText) return;
2761
2852
  try {
2762
2853
  await navigator.clipboard.writeText(copyText);
2763
2854
  setCopiado(true);
2764
- if (temporizador.current) clearTimeout(temporizador.current);
2765
- temporizador.current = setTimeout(() => setCopiado(false), 2e3);
2855
+ if (timer.current) clearTimeout(timer.current);
2856
+ timer.current = setTimeout(() => setCopiado(false), 2e3);
2766
2857
  } catch {
2767
2858
  }
2768
2859
  }, [copyText]);
@@ -2786,14 +2877,14 @@ function CodeBlock({ children, language, copyText, className, ...props }) {
2786
2877
  "button",
2787
2878
  {
2788
2879
  type: "button",
2789
- onClick: copiar,
2880
+ onClick: copy,
2790
2881
  "aria-label": copiado ? "C\xF3digo copiado" : "Copiar c\xF3digo",
2791
2882
  "aria-live": "polite",
2792
2883
  className: cn(
2793
2884
  "gap-step-xs rounded-chip flex items-center px-2 py-1",
2794
2885
  "text-label font-mono text-text-muted cursor-pointer",
2795
2886
  "transition-standard hover:bg-surface hover:text-text-primary",
2796
- "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent"
2887
+ "focus-ring"
2797
2888
  ),
2798
2889
  children: [
2799
2890
  copiado ? /* @__PURE__ */ jsxRuntime.jsx(Check, { className: "text-accent" }) : /* @__PURE__ */ jsxRuntime.jsx(Copy, {}),
@@ -2816,7 +2907,7 @@ function CourseCard({
2816
2907
  className,
2817
2908
  ...props
2818
2909
  }) {
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: [
2910
+ 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
2911
  status ? /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "warm", children: status }) }) : null,
2821
2912
  /* @__PURE__ */ jsxRuntime.jsx(Text, { as: "h3", variant: "h3", className: "group-hover:text-accent transition-standard", children: title }),
2822
2913
  summary ? /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "ui", tone: "secondary", className: "line-clamp-3", children: summary }) : null,
@@ -2834,8 +2925,8 @@ function CourseCard({
2834
2925
  ] }) });
2835
2926
  }
2836
2927
 
2837
- // src/brand/catalogo.ts
2838
- var caras = {
2928
+ // src/brand/catalog.ts
2929
+ var faces = {
2839
2930
  annoyed: "face-annoyed.png",
2840
2931
  confused: "face-confused.png",
2841
2932
  hearts: "face-hearts.png",
@@ -2850,27 +2941,27 @@ var poses = {
2850
2941
  peek: "pose-peek.png",
2851
2942
  surf: "pose-surf.png"
2852
2943
  };
2853
- var aletas = {
2854
- /** Dos azules. Para fondos claros. */
2944
+ var fins = {
2945
+ /** Two blues. For light backgrounds. */
2855
2946
  color: "fin.png",
2856
- /** Silueta a una tinta. Para fondos oscuros. */
2857
- espuma: "fin-foam.png"
2947
+ /** Single-ink silhouette. For dark backgrounds. */
2948
+ foam: "fin-foam.png"
2858
2949
  };
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"
2950
+ var faceUsage = {
2951
+ wink: "Forms, a friendly footnote",
2952
+ waiting: "No results, waiting state",
2953
+ laughing: "Success, subscription confirmed",
2954
+ shades: "Module or course completed",
2955
+ hearts: "Gratitude, article OG",
2956
+ confused: "404, page not found",
2957
+ annoyed: "Server error, build failure"
2867
2958
  };
2868
- var RUTA_ASSETS = "/brand";
2869
- var listaCaras = Object.keys(caras);
2870
- var listaPoses = Object.keys(poses);
2871
- function Mascota({
2959
+ var ASSETS_PATH = "/brand";
2960
+ var faceList = Object.keys(faces);
2961
+ var poseList = Object.keys(poses);
2962
+ function Mascot({
2872
2963
  pose,
2873
- basePath = RUTA_ASSETS,
2964
+ basePath = ASSETS_PATH,
2874
2965
  alt = "",
2875
2966
  className,
2876
2967
  ...props
@@ -2885,9 +2976,9 @@ function Mascota({
2885
2976
  }
2886
2977
  );
2887
2978
  }
2888
- function CaraDeMascota({
2889
- expresion,
2890
- basePath = RUTA_ASSETS,
2979
+ function MascotFace({
2980
+ expression,
2981
+ basePath = ASSETS_PATH,
2891
2982
  alt = "",
2892
2983
  className,
2893
2984
  ...props
@@ -2895,30 +2986,42 @@ function CaraDeMascota({
2895
2986
  return /* @__PURE__ */ jsxRuntime.jsx(
2896
2987
  "img",
2897
2988
  {
2898
- src: `${basePath}/${caras[expresion]}`,
2989
+ src: `${basePath}/${faces[expression]}`,
2899
2990
  alt,
2900
2991
  className: cn("h-auto w-full max-w-24 select-none", className),
2901
2992
  ...props
2902
2993
  }
2903
2994
  );
2904
2995
  }
2905
- var CARA = "w-[66px] max-w-none";
2906
- function EmptyState({
2907
- expresion,
2908
- title,
2909
- description,
2910
- action,
2911
- basePath,
2912
- className,
2913
- ...props
2914
- }) {
2996
+ var FACE = "w-[66px] max-w-none";
2997
+ function EmptyState(props) {
2998
+ if (props.variant === "inline") {
2999
+ const { variant: variant2, title: title2, description: description2, action: action2, icon, className: className2, ...rest2 } = props;
3000
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3001
+ "div",
3002
+ {
3003
+ className: cn(
3004
+ "py-step-lg px-step-md gap-step-xs flex flex-col items-center text-center",
3005
+ className2
3006
+ ),
3007
+ ...rest2,
3008
+ children: [
3009
+ icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-text-muted shrink-0", children: icon }) : null,
3010
+ /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "ui", tone: "muted", as: "p", className: "max-w-measure", children: title2 }),
3011
+ description2 ? /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "label", tone: "muted", as: "p", className: "max-w-measure font-normal", children: description2 }) : null,
3012
+ action2 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-step-xs", children: action2 }) : null
3013
+ ]
3014
+ }
3015
+ );
3016
+ }
3017
+ const { variant, title, description, action, expression, basePath, className, ...rest } = props;
2915
3018
  return /* @__PURE__ */ jsxRuntime.jsxs(
2916
3019
  "div",
2917
3020
  {
2918
- className: cn(SUPERFICIE_TARJETA, "p-step-lg gap-step-sm flex flex-col items-center text-center", className),
2919
- ...props,
3021
+ className: cn(CARD_SURFACE, "p-step-lg gap-step-sm flex flex-col items-center text-center", className),
3022
+ ...rest,
2920
3023
  children: [
2921
- /* @__PURE__ */ jsxRuntime.jsx(CaraDeMascota, { expresion, className: CARA, basePath }),
3024
+ /* @__PURE__ */ jsxRuntime.jsx(MascotFace, { expression, className: FACE, basePath }),
2922
3025
  /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "ui", as: "p", className: "font-medium", children: title }),
2923
3026
  description ? /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "label", tone: "muted", as: "p", className: "max-w-measure font-normal", children: description }) : null,
2924
3027
  action ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-step-xs", children: action }) : null
@@ -2926,12 +3029,12 @@ function EmptyState({
2926
3029
  }
2927
3030
  );
2928
3031
  }
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())}`;
3032
+ var sameDay = (a, b) => a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
3033
+ function toFieldValue(date) {
3034
+ const two = (n) => String(n).padStart(2, "0");
3035
+ return `${date.getFullYear()}-${two(date.getMonth() + 1)}-${two(date.getDate())}T${two(
3036
+ date.getHours()
3037
+ )}:${two(date.getMinutes())}`;
2935
3038
  }
2936
3039
  function EventCalendar({
2937
3040
  events,
@@ -2940,68 +3043,68 @@ function EventCalendar({
2940
3043
  onDeleteEvent,
2941
3044
  selected,
2942
3045
  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 }),
3046
+ formatDay = (day) => dateFns.format(day, "EEEE d 'de' MMMM", { locale: locale.es }),
3047
+ formatTime: formatTime2 = (date) => dateFns.format(date, "HH:mm", { locale: locale.es }),
2945
3048
  emptyMessage = "Nada en este d\xEDa.",
2946
3049
  className,
2947
3050
  ...props
2948
3051
  }) {
2949
3052
  const id = react.useId();
2950
3053
  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("");
3054
+ const day = selected ?? diaInterno;
3055
+ const [month, setMonth] = react.useState(() => day);
3056
+ const [editing, setEditing] = react.useState(null);
3057
+ const [title, setTitle] = react.useState("");
3058
+ const [when, setWhen] = react.useState("");
2956
3059
  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) {
3060
+ const ofTheDay = events.filter((e) => sameDay(e.start, day)).sort((a, b) => a.start.getTime() - b.start.getTime());
3061
+ function pickDay(nuevo) {
2959
3062
  if (!nuevo) return;
2960
3063
  setDiaInterno(nuevo);
2961
- setMes(nuevo);
3064
+ setMonth(nuevo);
2962
3065
  onSelectDay?.(nuevo);
2963
- cancelar();
3066
+ cancel();
2964
3067
  }
2965
- function cancelar() {
2966
- setEditando(null);
2967
- setTitulo("");
2968
- setCuando("");
3068
+ function cancel() {
3069
+ setEditing(null);
3070
+ setTitle("");
3071
+ setWhen("");
2969
3072
  }
2970
- function editar(evento) {
2971
- setEditando(evento.id);
2972
- setTitulo(evento.title);
2973
- setCuando(aValorDeCampo(evento.start));
3073
+ function edit(event) {
3074
+ setEditing(event.id);
3075
+ setTitle(event.title);
3076
+ setWhen(toFieldValue(event.start));
2974
3077
  }
2975
- function enviar(e) {
3078
+ function submit(e) {
2976
3079
  e.preventDefault();
2977
- if (!titulo.trim() || !cuando) return;
2978
- const start = new Date(cuando);
3080
+ if (!title.trim() || !when) return;
3081
+ const start = new Date(when);
2979
3082
  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 });
3083
+ if (editing) {
3084
+ const previous = events.find((ev) => ev.id === editing);
3085
+ if (previous) onUpdateEvent?.({ ...previous, title: title.trim(), start });
2983
3086
  } else {
2984
- onCreateEvent?.({ title: titulo.trim(), start });
3087
+ onCreateEvent?.({ title: title.trim(), start });
2985
3088
  }
2986
- cancelar();
3089
+ cancel();
2987
3090
  }
2988
- const conEvento = events.map((e) => e.start);
3091
+ const withEvent = events.map((e) => e.start);
2989
3092
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("gap-step-lg flex flex-col md:flex-row md:items-start", className), ...props, children: [
2990
3093
  /* @__PURE__ */ jsxRuntime.jsx(
2991
3094
  Calendar,
2992
3095
  {
2993
3096
  mode: "single",
2994
- selected: dia,
2995
- onSelect: elegirDia,
2996
- month: mes,
2997
- onMonthChange: setMes,
2998
- modifiers: { conEvento },
3097
+ selected: day,
3098
+ onSelect: pickDay,
3099
+ month,
3100
+ onMonthChange: setMonth,
3101
+ modifiers: { withEvent },
2999
3102
  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(
3103
+ // The dot goes under the number, not on top of the day's background:
3104
+ // over the selected one — which is solid biolume — an accent dot
3105
+ // disappears. `after:` in Tailwind v4 already ships `content: ""`, so
3106
+ // there is no need to declare it with an arbitrary value.
3107
+ withEvent: cn(
3005
3108
  "after:bg-accent after:absolute after:bottom-1 after:left-1/2",
3006
3109
  "after:size-1 after:-translate-x-1/2 after:rounded-pill",
3007
3110
  "[&[data-selected=true]]:after:bg-accent-on"
@@ -3016,8 +3119,8 @@ function EventCalendar({
3016
3119
  "aria-labelledby": `${id}-dia`,
3017
3120
  className: "border-hairline rounded-card p-step-md gap-step-md flex min-w-0 flex-1 flex-col border",
3018
3121
  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(
3122
+ /* @__PURE__ */ jsxRuntime.jsx(Text, { as: "h3", variant: "h3", id: `${id}-dia`, className: "first-letter:uppercase", children: formatDay(day) }),
3123
+ ofTheDay.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "gap-step-xs flex flex-col", children: ofTheDay.map((event) => /* @__PURE__ */ jsxRuntime.jsxs(
3021
3124
  "li",
3022
3125
  {
3023
3126
  className: "border-hairline gap-step-sm py-step-xs flex items-center border-b last:border-b-0",
@@ -3027,85 +3130,85 @@ function EventCalendar({
3027
3130
  {
3028
3131
  as: "span",
3029
3132
  variant: "meta",
3030
- tone: evento.tone === "warm" ? "warm" : "accent",
3133
+ tone: event.tone === "warm" ? "warm" : "accent",
3031
3134
  className: "shrink-0 tabular-nums",
3032
- children: /* @__PURE__ */ jsxRuntime.jsx("time", { dateTime: evento.start.toISOString(), children: formatTime2(evento.start) })
3135
+ children: /* @__PURE__ */ jsxRuntime.jsx("time", { dateTime: event.start.toISOString(), children: formatTime2(event.start) })
3033
3136
  }
3034
3137
  ),
3035
- /* @__PURE__ */ jsxRuntime.jsx(Text, { as: "span", variant: "ui", className: "min-w-0 flex-1 truncate", children: evento.title }),
3138
+ /* @__PURE__ */ jsxRuntime.jsx(Text, { as: "span", variant: "ui", className: "min-w-0 flex-1 truncate", children: event.title }),
3036
3139
  onUpdateEvent ? /* @__PURE__ */ jsxRuntime.jsx(
3037
3140
  Button,
3038
3141
  {
3039
3142
  variant: "tertiary",
3040
3143
  size: "sm",
3041
- onClick: () => editar(evento),
3042
- "aria-label": `Editar \xAB${evento.title}\xBB`,
3043
- children: "./editar"
3144
+ onClick: () => edit(event),
3145
+ "aria-label": `Editar \xAB${event.title}\xBB`,
3146
+ children: "./edit"
3044
3147
  }
3045
3148
  ) : null,
3046
3149
  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" }) }),
3150
+ /* @__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
3151
  /* @__PURE__ */ jsxRuntime.jsxs(AlertDialogContent, { children: [
3049
3152
  /* @__PURE__ */ jsxRuntime.jsxs(AlertDialogHeader, { children: [
3050
3153
  /* @__PURE__ */ jsxRuntime.jsxs(AlertDialogTitle, { children: [
3051
3154
  "Borrar \xAB",
3052
- evento.title,
3155
+ event.title,
3053
3156
  "\xBB"
3054
3157
  ] }),
3055
3158
  /* @__PURE__ */ jsxRuntime.jsxs(AlertDialogDescription, { children: [
3056
3159
  "Se borra del ",
3057
- formatDay(evento.start),
3160
+ formatDay(event.start),
3058
3161
  " a las ",
3059
- formatTime2(evento.start),
3162
+ formatTime2(event.start),
3060
3163
  ". No se puede deshacer."
3061
3164
  ] })
3062
3165
  ] }),
3063
3166
  /* @__PURE__ */ jsxRuntime.jsxs(AlertDialogFooter, { children: [
3064
3167
  /* @__PURE__ */ jsxRuntime.jsx(AlertDialogCancel, { children: "Mejor no" }),
3065
- /* @__PURE__ */ jsxRuntime.jsx(AlertDialogAction, { onClick: () => onDeleteEvent(evento.id), children: "Borrar el evento" })
3168
+ /* @__PURE__ */ jsxRuntime.jsx(AlertDialogAction, { onClick: () => onDeleteEvent(event.id), children: "Borrar el event" })
3066
3169
  ] })
3067
3170
  ] })
3068
3171
  ] }) : null
3069
3172
  ]
3070
3173
  },
3071
- evento.id
3174
+ event.id
3072
3175
  )) }) : (
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.
3176
+ // `EmptyState` is not used: it carries a mascot face, and the mascot
3177
+ // has seven counted places where it may appear. An empty day in a
3178
+ // schedule is not one of them — and in a thirty-day month, half of them
3179
+ // would be.
3077
3180
  /* @__PURE__ */ jsxRuntime.jsx(Text, { as: "p", variant: "ui", tone: "muted", children: emptyMessage })
3078
3181
  ),
3079
- editable ? /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: enviar, className: "gap-step-sm border-hairline pt-step-md flex flex-col border-t", children: [
3182
+ editable ? /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: submit, className: "gap-step-sm border-hairline pt-step-md flex flex-col border-t", children: [
3080
3183
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-xs flex flex-col", children: [
3081
- /* @__PURE__ */ jsxRuntime.jsx(Label2, { htmlFor: `${id}-titulo`, children: "T\xEDtulo" }),
3184
+ /* @__PURE__ */ jsxRuntime.jsx(Label2, { htmlFor: `${id}-title`, children: "T\xEDtulo" }),
3082
3185
  /* @__PURE__ */ jsxRuntime.jsx(
3083
3186
  Input,
3084
3187
  {
3085
- id: `${id}-titulo`,
3086
- value: titulo,
3087
- onChange: (e) => setTitulo(e.target.value),
3188
+ id: `${id}-title`,
3189
+ value: title,
3190
+ onChange: (e) => setTitle(e.target.value),
3088
3191
  placeholder: "Publicar el art\xEDculo",
3089
3192
  required: true
3090
3193
  }
3091
3194
  )
3092
3195
  ] }),
3093
3196
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-xs flex flex-col", children: [
3094
- /* @__PURE__ */ jsxRuntime.jsx(Label2, { htmlFor: `${id}-cuando`, children: "Cu\xE1ndo" }),
3197
+ /* @__PURE__ */ jsxRuntime.jsx(Label2, { htmlFor: `${id}-when`, children: "Cu\xE1ndo" }),
3095
3198
  /* @__PURE__ */ jsxRuntime.jsx(
3096
3199
  DateField,
3097
3200
  {
3098
- id: `${id}-cuando`,
3201
+ id: `${id}-when`,
3099
3202
  withTime: true,
3100
- value: cuando,
3101
- onChange: (e) => setCuando(e.target.value),
3203
+ value: when,
3204
+ onChange: (e) => setWhen(e.target.value),
3102
3205
  required: true
3103
3206
  }
3104
3207
  )
3105
3208
  ] }),
3106
3209
  /* @__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
3210
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { type: "submit", size: "sm", children: editing ? "Guardar" : "A\xF1adir" }),
3211
+ editing ? /* @__PURE__ */ jsxRuntime.jsx(Button, { type: "button", variant: "tertiary", size: "sm", onClick: cancel, children: "./cancelar" }) : null
3109
3212
  ] })
3110
3213
  ] }) : null
3111
3214
  ]
@@ -3121,7 +3224,7 @@ function Footer({
3121
3224
  className,
3122
3225
  ...props
3123
3226
  }) {
3124
- const firma = /* @__PURE__ */ jsxRuntime.jsxs(Text, { variant: "meta", tone: "muted", as: "p", className: "ml-auto shrink-0", children: [
3227
+ const signature = /* @__PURE__ */ jsxRuntime.jsxs(Text, { variant: "meta", tone: "muted", as: "p", className: "ml-auto shrink-0", children: [
3125
3228
  /* @__PURE__ */ jsxRuntime.jsxs("span", { "aria-hidden": "true", className: "text-accent", children: [
3126
3229
  "$",
3127
3230
  " "
@@ -3129,45 +3232,52 @@ function Footer({
3129
3232
  "cd ~/",
3130
3233
  naming.domain,
3131
3234
  "/",
3132
- year
3235
+ year,
3236
+ /* @__PURE__ */ jsxRuntime.jsx(
3237
+ "span",
3238
+ {
3239
+ "aria-hidden": "true",
3240
+ className: "bg-accent motion-safe:caret ml-[0.2em] inline-block h-[1em] w-[0.5em] translate-y-[0.15em]"
3241
+ }
3242
+ )
3133
3243
  ] });
3134
- const filas = [
3244
+ const rows = [
3135
3245
  brand2 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center", children: brand2 }, "marca") : null,
3136
3246
  children ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "gap-step-md flex flex-wrap items-center", children }, "enlaces") : null,
3137
3247
  social && social.length > 0 ? (
3138
- // 18px de separación, del documento. No es un escalón de `spacing`:
3139
- // es el ritmo de una fila de iconos, no el de una página.
3140
- /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "flex items-center gap-[18px]", children: social.map((red) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(
3248
+ // 18px of separation, from the document. It is not a `spacing` step: it is
3249
+ // the rhythm of a row of icons, not that of a page.
3250
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "flex items-center gap-[18px]", children: social.map((socialLink) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(
3141
3251
  "a",
3142
3252
  {
3143
- href: red.href,
3144
- "aria-label": red.label,
3253
+ href: socialLink.href,
3254
+ "aria-label": socialLink.label,
3145
3255
  className: cn(
3146
3256
  "text-text-muted hover:text-accent transition-standard block cursor-pointer text-[19px]",
3147
- "rounded-chip focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent"
3257
+ "rounded-chip focus-ring"
3148
3258
  ),
3149
- children: red.icon
3259
+ children: socialLink.icon
3150
3260
  }
3151
- ) }, red.href)) }, "redes")
3261
+ ) }, socialLink.href)) }, "socialLinks")
3152
3262
  ) : null
3153
3263
  ].filter(Boolean);
3154
- const [primera, ...resto] = filas;
3264
+ const [first, ...rest] = rows;
3155
3265
  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: [
3156
3266
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-md flex flex-wrap items-center", children: [
3157
- primera ?? null,
3158
- firma
3267
+ first ?? null,
3268
+ signature
3159
3269
  ] }),
3160
- resto
3270
+ rest
3161
3271
  ] }) });
3162
3272
  }
3163
3273
  function FooterLink({ asChild = false, className, ...props }) {
3164
- const Raiz = asChild ? reactSlot.Slot : "a";
3274
+ const Root18 = asChild ? reactSlot.Slot : "a";
3165
3275
  return /* @__PURE__ */ jsxRuntime.jsx(
3166
- Raiz,
3276
+ Root18,
3167
3277
  {
3168
3278
  className: cn(
3169
3279
  "font-mono text-meta text-text-secondary hover:text-accent transition-standard cursor-pointer",
3170
- "rounded-chip focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent",
3280
+ "rounded-chip focus-ring",
3171
3281
  className
3172
3282
  ),
3173
3283
  ...props
@@ -3181,24 +3291,24 @@ function Hero({
3181
3291
  action,
3182
3292
  pose,
3183
3293
  basePath,
3184
- variant = "cabecera",
3294
+ variant = "header",
3185
3295
  className,
3186
3296
  ...props
3187
3297
  }) {
3188
- const centrado = variant === "centrado";
3298
+ const centered = variant === "centered";
3189
3299
  return /* @__PURE__ */ jsxRuntime.jsxs(
3190
3300
  "section",
3191
3301
  {
3192
3302
  className: cn(
3193
- "degradado-hero rounded-panel border-hairline relative overflow-hidden border",
3194
- // 44 arriba, 40 a los lados y abajo. Del documento.
3303
+ "gradient-hero rounded-panel border-hairline relative overflow-hidden border",
3304
+ // 44 top, 40 on the sides and bottom. From the document.
3195
3305
  "px-step-xl pt-[44px] pb-step-xl",
3196
3306
  className
3197
3307
  ),
3198
3308
  ...props,
3199
3309
  children: [
3200
- centrado && pose ? /* @__PURE__ */ jsxRuntime.jsx(
3201
- Mascota,
3310
+ centered && pose ? /* @__PURE__ */ jsxRuntime.jsx(
3311
+ Mascot,
3202
3312
  {
3203
3313
  pose,
3204
3314
  basePath,
@@ -3210,7 +3320,7 @@ function Hero({
3210
3320
  {
3211
3321
  className: cn(
3212
3322
  "gap-step-md flex flex-col",
3213
- centrado ? "items-center text-center" : "md:max-w-[62%]"
3323
+ centered ? "items-center text-center" : "md:max-w-[62%]"
3214
3324
  ),
3215
3325
  children: [
3216
3326
  eyebrow ? /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "eyebrow", tone: "accent", as: "p", children: eyebrow }) : null,
@@ -3221,7 +3331,7 @@ function Hero({
3221
3331
  {
3222
3332
  className: cn(
3223
3333
  "gap-step-sm mt-step-xs flex flex-wrap items-center",
3224
- centrado && "justify-center"
3334
+ centered && "justify-center"
3225
3335
  ),
3226
3336
  children: action
3227
3337
  }
@@ -3229,14 +3339,14 @@ function Hero({
3229
3339
  ]
3230
3340
  }
3231
3341
  ),
3232
- !centrado && pose ? /* @__PURE__ */ jsxRuntime.jsx(
3233
- Mascota,
3342
+ !centered && pose ? /* @__PURE__ */ jsxRuntime.jsx(
3343
+ Mascot,
3234
3344
  {
3235
3345
  pose,
3236
3346
  basePath,
3237
3347
  className: cn(
3238
- // En móvil, en el flujo y bajo los botones. Desde md, sangrando por
3239
- // la esquina inferior derecha.
3348
+ // On mobile, in the flow and below the buttons. From md up, bleeding
3349
+ // off the bottom-right corner.
3240
3350
  "mt-step-lg mx-auto w-40",
3241
3351
  "md:mt-0 md:absolute md:right-0 md:-bottom-4 md:mx-0 md:w-64"
3242
3352
  )
@@ -3257,7 +3367,7 @@ function LinkRow({
3257
3367
  const rel = external ? "noopener noreferrer" : props.rel;
3258
3368
  const target = external ? "_blank" : props.target;
3259
3369
  return /* @__PURE__ */ jsxRuntime.jsx(
3260
- Tarjeta,
3370
+ CardShell,
3261
3371
  {
3262
3372
  className: cn("px-step-md py-step-sm", className),
3263
3373
  ...props,
@@ -3280,39 +3390,48 @@ function LinkRow({
3280
3390
  }
3281
3391
  );
3282
3392
  }
3283
- function Nav({ brand: brand2, children, actions, className, ...props }) {
3393
+ function Nav({ brand: brand2, children, actions, size: size2 = "default", className, ...props }) {
3284
3394
  return /* @__PURE__ */ jsxRuntime.jsx(
3285
3395
  "header",
3286
3396
  {
3287
3397
  className: cn(
3288
3398
  "sticky top-0 z-50 w-full",
3289
- // abismo al 86 % con 14px de desenfoque detrás. El alfa va sobre el
3290
- // token, así que sigue el modo: en claro es papel al 86 %.
3399
+ // Abyss at 86 % with 14px of blur behind. The alpha rides on the token,
3400
+ // so it follows the mode: in light it is paper at 86 %.
3291
3401
  "bg-background/86 backdrop-blur-[14px]",
3292
3402
  "border-hairline border-b",
3293
3403
  className
3294
3404
  ),
3295
3405
  ...props,
3296
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-wide px-step-md h-nav gap-step-lg mx-auto flex items-center", children: [
3297
- brand2 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "shrink-0", children: brand2 }) : null,
3298
- children ? /* @__PURE__ */ jsxRuntime.jsx("nav", { "aria-label": "Principal", className: "ml-auto min-w-0", children: /* @__PURE__ */ jsxRuntime.jsxs("ul", { className: "gap-step-md flex items-center", children: [
3299
- /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", className: "text-text-muted font-mono text-meta select-none", children: "~/" }),
3300
- children
3301
- ] }) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1" }),
3302
- actions ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "gap-step-sm flex shrink-0 items-center", children: actions }) : null
3303
- ] })
3406
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
3407
+ "div",
3408
+ {
3409
+ className: cn(
3410
+ "max-w-wide px-step-md gap-step-lg mx-auto flex items-center",
3411
+ size2 === "compact" ? "h-nav-compact" : "h-nav"
3412
+ ),
3413
+ children: [
3414
+ brand2 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "shrink-0", children: brand2 }) : null,
3415
+ children ? /* @__PURE__ */ jsxRuntime.jsx("nav", { "aria-label": "Principal", className: "ml-auto min-w-0", children: /* @__PURE__ */ jsxRuntime.jsxs("ul", { className: "gap-step-md flex items-center", children: [
3416
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", className: "text-text-muted font-mono text-meta select-none", children: "~/" }),
3417
+ children
3418
+ ] }) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1" }),
3419
+ actions ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "gap-step-sm flex shrink-0 items-center", children: actions }) : null
3420
+ ]
3421
+ }
3422
+ )
3304
3423
  }
3305
3424
  );
3306
3425
  }
3307
3426
  function NavItem({ active = false, asChild = false, className, children, ...props }) {
3308
- const Raiz = asChild ? reactSlot.Slot : "a";
3427
+ const Root18 = asChild ? reactSlot.Slot : "a";
3309
3428
  return /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsxs(
3310
- Raiz,
3429
+ Root18,
3311
3430
  {
3312
3431
  "aria-current": active ? "page" : void 0,
3313
3432
  className: cn(
3314
3433
  "font-mono text-meta transition-standard cursor-pointer",
3315
- "rounded-chip focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent",
3434
+ "rounded-chip focus-ring",
3316
3435
  active ? "text-accent underline decoration-1 underline-offset-4" : "text-text-secondary hover:text-accent",
3317
3436
  className
3318
3437
  ),
@@ -3329,39 +3448,49 @@ function NavItem({ active = false, asChild = false, className, children, ...prop
3329
3448
  function NewsletterForm({
3330
3449
  title,
3331
3450
  description,
3332
- state = "reposo",
3451
+ state = "idle",
3333
3452
  onSubmitEmail,
3334
3453
  successMessage = "Ya est\xE1s dentro. Te llega un correo cada dos semanas, y nada m\xE1s.",
3335
3454
  errorMessage = "No se pudo suscribir ese correo. Rev\xEDsalo y vuelve a intentar.",
3336
3455
  disclaimer,
3337
- expresion,
3456
+ expression,
3338
3457
  basePath,
3339
3458
  submitLabel = "Suscribirme",
3340
- placeholder = "tu@correo.dev",
3459
+ placeholder = "tu@email.dev",
3341
3460
  fieldLabel = "Correo electr\xF3nico",
3342
3461
  nameField = false,
3343
3462
  nameLabel = "Nombre",
3344
3463
  namePlaceholder = "C\xF3mo te llamas",
3345
3464
  nameInputProps,
3465
+ aside,
3466
+ resetOnSuccess = true,
3467
+ onFieldChange,
3468
+ fieldErrors,
3346
3469
  className,
3347
3470
  ...props
3348
3471
  }) {
3349
3472
  const id = react.useId();
3350
- const enviando = state === "enviando";
3473
+ const form = react.useRef(null);
3474
+ const sending = state === "sending";
3351
3475
  const error = state === "error";
3352
- function enviar(evento) {
3353
- evento.preventDefault();
3354
- const datos = new FormData(evento.currentTarget);
3355
- const correo = String(datos.get("email") ?? "");
3356
- const nombre = nameField ? String(datos.get("name") ?? "") : void 0;
3357
- onSubmitEmail?.(correo, nombre);
3476
+ const nameError = fieldErrors?.name;
3477
+ const emailError = fieldErrors?.email;
3478
+ react.useEffect(() => {
3479
+ if (state === "success" && resetOnSuccess) form.current?.reset();
3480
+ }, [state, resetOnSuccess]);
3481
+ function submit(event) {
3482
+ event.preventDefault();
3483
+ const data = new FormData(event.currentTarget);
3484
+ const email = String(data.get("email") ?? "");
3485
+ const name = nameField ? String(data.get("name") ?? "") : void 0;
3486
+ onSubmitEmail?.(email, name);
3358
3487
  }
3359
- const boton = /* @__PURE__ */ jsxRuntime.jsx(
3488
+ const button2 = /* @__PURE__ */ jsxRuntime.jsx(
3360
3489
  Button,
3361
3490
  {
3362
3491
  type: "submit",
3363
3492
  variant: "conversion",
3364
- loading: enviando,
3493
+ loading: sending,
3365
3494
  className: cn("disabled:opacity-60", nameField && "sm:self-start"),
3366
3495
  children: submitLabel
3367
3496
  }
@@ -3370,65 +3499,79 @@ function NewsletterForm({
3370
3499
  "section",
3371
3500
  {
3372
3501
  className: cn(
3373
- "degradado-seccion rounded-panel border-hairline p-step-lg gap-step-md flex flex-col border",
3502
+ "gradient-section rounded-panel border-hairline p-step-lg border",
3503
+ // Two columns from `md` up when there is an illustration, one otherwise.
3504
+ // `items-center` centres the pose against the block of text and form
3505
+ // rather than against the panel, which is what the blog was doing by
3506
+ // hand with an absolute position and a reserved `pr`.
3507
+ aside ? "gap-step-lg flex flex-col md:flex-row md:items-center" : "gap-step-md flex flex-col",
3374
3508
  className
3375
3509
  ),
3376
3510
  ...props,
3377
3511
  children: [
3378
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-xs flex flex-col", children: [
3379
- /* @__PURE__ */ jsxRuntime.jsx(Text, { as: "h2", variant: "h3", children: title }),
3380
- description ? /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "ui", tone: "secondary", children: description }) : null
3381
- ] }),
3382
- /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: enviar, className: "gap-step-sm flex flex-col", noValidate: true, children: [
3383
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-sm flex flex-col sm:flex-row sm:items-end", children: [
3384
- nameField ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-xs flex flex-1 flex-col", children: [
3385
- /* @__PURE__ */ jsxRuntime.jsx(Label2, { htmlFor: `${id}-nombre`, children: nameLabel }),
3386
- /* @__PURE__ */ jsxRuntime.jsx(
3387
- Input,
3388
- {
3389
- id: `${id}-nombre`,
3390
- name: "name",
3391
- type: "text",
3392
- autoComplete: "name",
3393
- required: true,
3394
- placeholder: namePlaceholder,
3395
- disabled: enviando,
3396
- ...nameInputProps
3397
- }
3398
- )
3399
- ] }) : null,
3400
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-xs flex flex-1 flex-col", children: [
3401
- /* @__PURE__ */ jsxRuntime.jsx(Label2, { htmlFor: id, children: fieldLabel }),
3402
- /* @__PURE__ */ jsxRuntime.jsx(
3403
- Input,
3404
- {
3405
- id,
3406
- name: "email",
3407
- type: "email",
3408
- autoComplete: "email",
3409
- required: true,
3410
- placeholder,
3411
- disabled: enviando,
3412
- invalid: error,
3413
- "aria-describedby": state === "exito" || error ? `${id}-aviso` : void 0
3414
- }
3415
- )
3512
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("gap-step-md flex min-w-0 flex-1 flex-col"), children: [
3513
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-xs flex flex-col", children: [
3514
+ /* @__PURE__ */ jsxRuntime.jsx(Text, { as: "h2", variant: "h3", children: title }),
3515
+ description ? /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "ui", tone: "secondary", children: description }) : null
3516
+ ] }),
3517
+ /* @__PURE__ */ jsxRuntime.jsxs("form", { ref: form, onSubmit: submit, className: "gap-step-sm flex flex-col", noValidate: true, children: [
3518
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-sm flex flex-col sm:flex-row sm:items-start", children: [
3519
+ nameField ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-xs flex flex-1 flex-col", children: [
3520
+ /* @__PURE__ */ jsxRuntime.jsx(Label2, { htmlFor: `${id}-name`, children: nameLabel }),
3521
+ /* @__PURE__ */ jsxRuntime.jsx(
3522
+ Input,
3523
+ {
3524
+ id: `${id}-name`,
3525
+ name: "name",
3526
+ type: "text",
3527
+ autoComplete: "name",
3528
+ required: true,
3529
+ placeholder: namePlaceholder,
3530
+ disabled: sending,
3531
+ invalid: Boolean(nameError),
3532
+ "aria-describedby": nameError ? `${id}-name-error` : void 0,
3533
+ onChange: (e) => onFieldChange?.("name", e.currentTarget.value),
3534
+ ...nameInputProps
3535
+ }
3536
+ ),
3537
+ nameError ? /* @__PURE__ */ jsxRuntime.jsx(Text, { id: `${id}-name-error`, variant: "label", tone: "error", as: "p", role: "alert", children: nameError }) : null
3538
+ ] }) : null,
3539
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-xs flex flex-1 flex-col", children: [
3540
+ /* @__PURE__ */ jsxRuntime.jsx(Label2, { htmlFor: id, children: fieldLabel }),
3541
+ /* @__PURE__ */ jsxRuntime.jsx(
3542
+ Input,
3543
+ {
3544
+ id,
3545
+ name: "email",
3546
+ type: "email",
3547
+ autoComplete: "email",
3548
+ required: true,
3549
+ placeholder,
3550
+ disabled: sending,
3551
+ invalid: error || Boolean(emailError),
3552
+ onChange: (e) => onFieldChange?.("email", e.currentTarget.value),
3553
+ "aria-describedby": emailError ? `${id}-email-error` : state === "success" || error ? `${id}-notice` : void 0
3554
+ }
3555
+ ),
3556
+ emailError ? /* @__PURE__ */ jsxRuntime.jsx(Text, { id: `${id}-email-error`, variant: "label", tone: "error", as: "p", role: "alert", children: emailError }) : null
3557
+ ] }),
3558
+ nameField ? null : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sm:mt-[26px]", children: button2 })
3416
3559
  ] }),
3417
- nameField ? null : boton
3560
+ nameField ? button2 : null
3418
3561
  ] }),
3419
- nameField ? boton : null
3562
+ state === "success" ? /* @__PURE__ */ jsxRuntime.jsx(Alert, { id: `${id}-notice`, variant: "success", emphasis: "strong", children: successMessage }) : null,
3563
+ error ? /* @__PURE__ */ jsxRuntime.jsx(Alert, { id: `${id}-notice`, variant: "error", emphasis: "strong", children: errorMessage }) : null,
3564
+ disclaimer ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-sm flex items-center", children: [
3565
+ expression ? /* @__PURE__ */ jsxRuntime.jsx(MascotFace, { expression, basePath, className: "w-10 max-w-none" }) : null,
3566
+ /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "label", tone: "muted", as: "p", className: "font-normal", children: disclaimer })
3567
+ ] }) : null
3420
3568
  ] }),
3421
- state === "exito" ? /* @__PURE__ */ jsxRuntime.jsx(Alert, { id: `${id}-aviso`, variant: "success", enfasis: "fuerte", children: successMessage }) : null,
3422
- error ? /* @__PURE__ */ jsxRuntime.jsx(Alert, { id: `${id}-aviso`, variant: "error", enfasis: "fuerte", children: errorMessage }) : null,
3423
- disclaimer ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-sm flex items-center", children: [
3424
- expresion ? /* @__PURE__ */ jsxRuntime.jsx(CaraDeMascota, { expresion, basePath, className: "w-10 max-w-none" }) : null,
3425
- /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "label", tone: "muted", as: "p", className: "font-normal", children: disclaimer })
3426
- ] }) : null
3569
+ aside ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "shrink-0 md:max-w-[42%]", children: aside }) : null
3427
3570
  ]
3428
3571
  }
3429
3572
  );
3430
3573
  }
3431
- var cabecera = classVarianceAuthority.cva("gap-step-sm flex flex-col", {
3574
+ var header = classVarianceAuthority.cva("gap-step-sm flex flex-col", {
3432
3575
  variants: {
3433
3576
  size: {
3434
3577
  display: "py-section",
@@ -3447,7 +3590,7 @@ function PageHeader({
3447
3590
  className,
3448
3591
  ...props
3449
3592
  }) {
3450
- return /* @__PURE__ */ jsxRuntime.jsxs("header", { className: cn(cabecera({ size: size2 }), className), ...props, children: [
3593
+ return /* @__PURE__ */ jsxRuntime.jsxs("header", { className: cn(header({ size: size2 }), className), ...props, children: [
3451
3594
  eyebrow ? /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "eyebrow", tone: "accent", as: "p", children: eyebrow }) : null,
3452
3595
  /* @__PURE__ */ jsxRuntime.jsx(Text, { as, variant: size2 === "display" ? "display" : "h1", className: "max-w-measure", children: title }),
3453
3596
  description ? /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "body", tone: "secondary", children: description }) : null,
@@ -3461,33 +3604,33 @@ function ScrollingProgressBar({
3461
3604
  className,
3462
3605
  ...props
3463
3606
  }) {
3464
- const [avance, setAvance] = react.useState(0);
3465
- const pendiente = react.useRef(0);
3607
+ const [seek, setAvance] = react.useState(0);
3608
+ const pendingOne = react.useRef(0);
3466
3609
  react.useEffect(() => {
3467
- const medir = () => {
3468
- pendiente.current = 0;
3469
- const elemento = target?.current;
3470
- const inicio = elemento ? elemento.getBoundingClientRect().top + scrollY : 0;
3471
- const alto = elemento ? elemento.offsetHeight : document.documentElement.scrollHeight;
3472
- const recorrible = alto - innerHeight;
3473
- if (recorrible <= 0) {
3474
- setAvance(scrollY >= inicio ? 100 : 0);
3610
+ const measure = () => {
3611
+ pendingOne.current = 0;
3612
+ const element = target?.current;
3613
+ const start = element ? element.getBoundingClientRect().top + scrollY : 0;
3614
+ const height = element ? element.offsetHeight : document.documentElement.scrollHeight;
3615
+ const walkable = height - innerHeight;
3616
+ if (walkable <= 0) {
3617
+ setAvance(scrollY >= start ? 100 : 0);
3475
3618
  return;
3476
3619
  }
3477
- const recorrido = scrollY - inicio;
3478
- setAvance(Math.min(100, Math.max(0, recorrido / recorrible * 100)));
3620
+ const walk = scrollY - start;
3621
+ setAvance(Math.min(100, Math.max(0, walk / walkable * 100)));
3479
3622
  };
3480
- const alMoverse = () => {
3481
- if (pendiente.current) return;
3482
- pendiente.current = requestAnimationFrame(medir);
3623
+ const onMove = () => {
3624
+ if (pendingOne.current) return;
3625
+ pendingOne.current = requestAnimationFrame(measure);
3483
3626
  };
3484
- medir();
3485
- addEventListener("scroll", alMoverse, { passive: true });
3486
- addEventListener("resize", alMoverse);
3627
+ measure();
3628
+ addEventListener("scroll", onMove, { passive: true });
3629
+ addEventListener("resize", onMove);
3487
3630
  return () => {
3488
- if (pendiente.current) cancelAnimationFrame(pendiente.current);
3489
- removeEventListener("scroll", alMoverse);
3490
- removeEventListener("resize", alMoverse);
3631
+ if (pendingOne.current) cancelAnimationFrame(pendingOne.current);
3632
+ removeEventListener("scroll", onMove);
3633
+ removeEventListener("resize", onMove);
3491
3634
  };
3492
3635
  }, [target]);
3493
3636
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -3504,75 +3647,181 @@ function ScrollingProgressBar({
3504
3647
  "div",
3505
3648
  {
3506
3649
  className: cn("h-full", tone === "warm" ? "bg-warm" : "bg-accent"),
3507
- style: { width: `${avance}%` }
3650
+ style: { width: `${seek}%` }
3508
3651
  }
3509
3652
  )
3510
3653
  }
3511
3654
  );
3512
3655
  }
3656
+ var Collapsed = react.createContext(false);
3513
3657
  function SidebarItem({
3514
3658
  active = false,
3515
3659
  asChild = false,
3516
3660
  badge: badge2,
3661
+ icon,
3517
3662
  className,
3518
3663
  children,
3519
3664
  ...props
3520
3665
  }) {
3521
- const Raiz = asChild ? reactSlot.Slot : "a";
3666
+ const Root18 = asChild ? reactSlot.Slot : "a";
3667
+ const collapsed = react.useContext(Collapsed);
3522
3668
  return /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsxs(
3523
- Raiz,
3669
+ Root18,
3524
3670
  {
3525
3671
  "aria-current": active ? "page" : void 0,
3526
3672
  className: cn(
3527
3673
  "rounded-chip px-step-sm gap-step-xs flex cursor-pointer items-center py-1.5",
3674
+ collapsed && "justify-center",
3528
3675
  "font-mono text-meta transition-standard",
3529
- "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent",
3530
- // El activo es fondo `surfaceRaised` y tinta primaria, igual que
3531
- // `TabsTrigger`. NO bioluz: `tokens.ts` lo dice explícito — acento
3532
- // claro pasa sobre background y sobre surface, pero sobre
3533
- // surfaceRaised mide 4.2 y no es color de texto ahí. El acento se
3534
- // queda en el `▸`, que es decorativo y le aplica el umbral de 3:1.
3676
+ "focus-ring",
3677
+ // The active item is `surfaceRaised` with primary ink, same as
3678
+ // `TabsTrigger`. NOT biolume: `tokens.ts` says so explicitly — light
3679
+ // accent passes over background and over surface, but over
3680
+ // surfaceRaised it measures 4.2 and is not a text color there. The
3681
+ // accent stays on the `▸`, which is decorative and gets the 3:1
3682
+ // threshold.
3535
3683
  active ? "bg-surface-raised text-text-primary" : "text-text-secondary hover:text-text-primary hover:bg-surface-raised",
3536
3684
  className
3537
3685
  ),
3538
3686
  ...props,
3539
3687
  children: [
3540
- /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", className: active ? "text-accent" : "text-text-muted", children: "\u25B8" }),
3541
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 truncate", children }),
3542
- badge2 ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-text-muted shrink-0", children: badge2 }) : null
3688
+ icon ? (
3689
+ // A shade bigger than the 13px mono label, which is what keeps it
3690
+ // reading as a glyph rather than as a character in the word.
3691
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", className: "text-body shrink-0", children: icon })
3692
+ ) : /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", className: active ? "text-accent" : "text-text-muted", children: "\u25B8" }),
3693
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: collapsed ? "sr-only" : "min-w-0 flex-1 truncate", children }),
3694
+ badge2 ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("text-text-muted", collapsed ? "sr-only" : "shrink-0"), children: badge2 }) : null
3543
3695
  ]
3544
3696
  }
3545
3697
  ) });
3546
3698
  }
3699
+ function SidebarGroup({ label, children, className, ...props }) {
3700
+ const id = react.useId();
3701
+ const collapsed = react.useContext(Collapsed);
3702
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3703
+ "li",
3704
+ {
3705
+ className: cn(
3706
+ "mt-step-md first:mt-0",
3707
+ // In a rail the label has nowhere to go, so the block becomes a rule.
3708
+ // The label itself stays in the accessibility tree naming the list.
3709
+ collapsed && "border-hairline pt-step-md mt-step-sm border-t first:border-t-0",
3710
+ className
3711
+ ),
3712
+ ...props,
3713
+ children: [
3714
+ /* @__PURE__ */ jsxRuntime.jsx(
3715
+ Text,
3716
+ {
3717
+ variant: "meta",
3718
+ tone: "muted",
3719
+ as: "p",
3720
+ id,
3721
+ className: collapsed ? "sr-only" : "px-step-sm pb-step-xs",
3722
+ children: label
3723
+ }
3724
+ ),
3725
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { "aria-labelledby": id, className: "gap-0.5 flex flex-col", children })
3726
+ ]
3727
+ }
3728
+ );
3729
+ }
3547
3730
  function SidebarNav({
3548
3731
  title,
3732
+ brand: brand2,
3733
+ mark,
3734
+ user,
3735
+ collapsed = false,
3736
+ onCollapsedChange,
3737
+ collapseLabel = "Plegar el panel",
3738
+ expandLabel = "Desplegar el panel",
3549
3739
  version,
3550
3740
  branch,
3551
3741
  children,
3552
3742
  className,
3553
3743
  ...props
3554
3744
  }) {
3555
- const pie = [version, branch].filter(Boolean);
3556
- return /* @__PURE__ */ jsxRuntime.jsxs(
3745
+ const footer = [version, branch].filter(Boolean);
3746
+ const top = collapsed ? mark : brand2;
3747
+ return /* @__PURE__ */ jsxRuntime.jsx(Collapsed.Provider, { value: collapsed, children: /* @__PURE__ */ jsxRuntime.jsxs(
3557
3748
  "nav",
3558
3749
  {
3559
3750
  "aria-label": typeof title === "string" ? title : "Administraci\xF3n",
3560
3751
  className: cn(
3561
3752
  "border-hairline bg-surface p-step-sm gap-step-sm flex h-full flex-col border-r",
3753
+ // The width is the component's only when it can collapse: the layout
3754
+ // beside it has to reserve one of the two, and it cannot know which.
3755
+ // It does NOT transition. The system's only transition animates colour
3756
+ // and border, and a rail that slides is the entrance animation this
3757
+ // library does not have — same call as the `Switch` knob, which changes
3758
+ // position without moving.
3759
+ onCollapsedChange && (collapsed ? "w-sidebar-rail" : "w-sidebar"),
3562
3760
  className
3563
3761
  ),
3564
3762
  ...props,
3565
3763
  children: [
3566
- title ? /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "eyebrow", tone: "muted", as: "p", className: "px-step-sm", children: title }) : null,
3764
+ top || onCollapsedChange ? /* @__PURE__ */ jsxRuntime.jsxs(
3765
+ "div",
3766
+ {
3767
+ className: cn(
3768
+ "border-hairline px-step-sm pb-step-sm mb-step-xs gap-step-xs flex items-center border-b",
3769
+ collapsed ? "justify-center" : "justify-between"
3770
+ ),
3771
+ children: [
3772
+ top ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0", children: top }) : null,
3773
+ onCollapsedChange ? /* @__PURE__ */ jsxRuntime.jsx(
3774
+ "button",
3775
+ {
3776
+ type: "button",
3777
+ onClick: () => onCollapsedChange(!collapsed),
3778
+ "aria-expanded": !collapsed,
3779
+ "aria-label": collapsed ? expandLabel : collapseLabel,
3780
+ className: cn(
3781
+ "rounded-chip transition-standard shrink-0 cursor-pointer p-1",
3782
+ "text-text-muted hover:text-text-primary hover:bg-surface-raised",
3783
+ "focus-ring"
3784
+ ),
3785
+ children: collapsed ? /* @__PURE__ */ jsxRuntime.jsx(ChevronRight, {}) : /* @__PURE__ */ jsxRuntime.jsx(ChevronLeft, {})
3786
+ }
3787
+ ) : null
3788
+ ]
3789
+ }
3790
+ ) : null,
3791
+ title && !collapsed ? /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "eyebrow", tone: "muted", as: "p", className: "px-step-sm", children: title }) : null,
3567
3792
  /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "gap-0.5 flex flex-1 flex-col", children }),
3568
- 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: [
3569
- i > 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: " \xB7 " }) : null,
3570
- p
3571
- ] }, i)) }) : null
3793
+ user ? /* @__PURE__ */ jsxRuntime.jsx(
3794
+ "div",
3795
+ {
3796
+ className: cn(
3797
+ "border-hairline px-step-sm pt-step-sm border-t",
3798
+ collapsed && "flex justify-center"
3799
+ ),
3800
+ children: user
3801
+ }
3802
+ ) : null,
3803
+ footer.length > 0 && !collapsed ? /* @__PURE__ */ jsxRuntime.jsx(
3804
+ Text,
3805
+ {
3806
+ variant: "meta",
3807
+ tone: "muted",
3808
+ as: "p",
3809
+ className: cn("px-step-sm", user ? "" : "border-hairline pt-step-sm border-t"),
3810
+ children: footer.map((p, i) => /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
3811
+ i > 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: " \xB7 " }) : null,
3812
+ p
3813
+ ] }, i))
3814
+ }
3815
+ ) : null
3572
3816
  ]
3573
3817
  }
3574
- );
3818
+ ) });
3575
3819
  }
3820
+ var DELTA = {
3821
+ up: { glyph: ChevronUp, label: "sube" },
3822
+ down: { glyph: ChevronDown, label: "baja" },
3823
+ flat: { glyph: Minus, label: "sin cambio" }
3824
+ };
3576
3825
  function Stat({
3577
3826
  value,
3578
3827
  label,
@@ -3580,30 +3829,52 @@ function Stat({
3580
3829
  progress,
3581
3830
  icon,
3582
3831
  description,
3832
+ delta,
3833
+ spark,
3583
3834
  className,
3584
3835
  ...props
3585
3836
  }) {
3586
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn(SUPERFICIE_TARJETA, "p-step-lg gap-step-xs flex flex-col", className), ...props, children: [
3587
- /* @__PURE__ */ jsxRuntime.jsxs(Text, { variant: "eyebrow", tone: "muted", as: "p", className: "gap-step-xs flex items-center", children: [
3837
+ const DeltaGlyph = delta ? DELTA[delta.direction].glyph : null;
3838
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn(CARD_SURFACE, "p-step-lg gap-step-xs flex flex-col", className), ...props, children: [
3839
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-sm flex items-start justify-between", children: [
3840
+ /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "eyebrow", tone: "muted", as: "p", className: "min-w-0", children: label }),
3588
3841
  icon ? (
3589
- // `aria-hidden` no hace falta: los glifos del sistema ya lo traen
3590
- // puesto. Lo que sí hace falta es que no encoja al lado de un título
3591
- // largo, porque un icono aplastado se lee como otro icono.
3592
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0", children: icon })
3593
- ) : null,
3594
- label
3842
+ // `aria-hidden` is not needed: the system's glyphs already carry it.
3843
+ // The circle is a SURFACE — the tint at 10 %, same as an alert's — and
3844
+ // the colour stays on the glyph, which is § 4b and also what keeps it
3845
+ // on the 3:1 graphical threshold instead of text's 4.5.
3846
+ /* @__PURE__ */ jsxRuntime.jsx(
3847
+ "span",
3848
+ {
3849
+ className: cn(
3850
+ "size-control-icon-sm text-ui flex shrink-0 items-center justify-center rounded-full",
3851
+ tone === "neutral" ? "bg-accent/10 text-accent" : "bg-warm/10 text-warm"
3852
+ ),
3853
+ children: icon
3854
+ }
3855
+ )
3856
+ ) : null
3595
3857
  ] }),
3596
- /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "stat", as: "p", tone: tone === "alerta" ? "warm" : "accent", children: value }),
3858
+ /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "stat", as: "p", tone: tone === "neutral" ? "primary" : "warm", children: value }),
3859
+ delta && DeltaGlyph ? /* @__PURE__ */ jsxRuntime.jsxs(Text, { variant: "label", tone: "secondary", as: "p", className: "gap-step-xs flex items-center", children: [
3860
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "sr-only", children: [
3861
+ DELTA[delta.direction].label,
3862
+ " "
3863
+ ] }),
3864
+ /* @__PURE__ */ jsxRuntime.jsx(DeltaGlyph, { className: "shrink-0" }),
3865
+ delta.value
3866
+ ] }) : null,
3597
3867
  description ? /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "ui", tone: "secondary", as: "p", children: description }) : null,
3598
3868
  typeof progress === "number" ? /* @__PURE__ */ jsxRuntime.jsx(
3599
3869
  Progress,
3600
3870
  {
3601
3871
  value: progress,
3602
- tone: tone === "alerta" ? "warm" : "accent",
3872
+ tone: tone === "neutral" ? "accent" : "warm",
3603
3873
  label: `${label}: ${progress}%`,
3604
3874
  className: "mt-step-sm"
3605
3875
  }
3606
- ) : null
3876
+ ) : null,
3877
+ spark ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-auto pt-step-md", children: spark }) : null
3607
3878
  ] });
3608
3879
  }
3609
3880
  function TalkCard({
@@ -3614,25 +3885,55 @@ function TalkCard({
3614
3885
  location,
3615
3886
  status,
3616
3887
  description,
3888
+ resources,
3617
3889
  className,
3618
3890
  ...props
3619
3891
  }) {
3620
- const pie = [
3621
- date ? /* @__PURE__ */ jsxRuntime.jsx("time", { dateTime, children: date }, "fecha") : null,
3622
- location ? /* @__PURE__ */ jsxRuntime.jsx("span", { children: location }, "lugar") : null
3892
+ const footer = [
3893
+ date ? /* @__PURE__ */ jsxRuntime.jsx("time", { dateTime, children: date }, "date") : null,
3894
+ location ? /* @__PURE__ */ jsxRuntime.jsx("span", { children: location }, "location") : null
3623
3895
  ].filter(Boolean);
3624
- 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: [
3896
+ const body = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3625
3897
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "gap-step-sm flex items-start justify-between", children: [
3626
3898
  /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "eyebrow", tone: "accent", as: "p", children: event }),
3627
3899
  status ? /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "neutral", children: status }) : null
3628
3900
  ] }),
3629
- /* @__PURE__ */ jsxRuntime.jsx(Text, { as: "h3", variant: "h3", className: "group-hover:text-accent transition-standard", children: title }),
3901
+ /* @__PURE__ */ jsxRuntime.jsx(
3902
+ Text,
3903
+ {
3904
+ as: "h3",
3905
+ variant: "h3",
3906
+ className: cn(!resources && "group-hover:text-accent transition-standard"),
3907
+ children: title
3908
+ }
3909
+ ),
3630
3910
  description ? /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "ui", tone: "secondary", className: "line-clamp-2", children: description }) : null,
3631
- 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: [
3632
- i > 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: "\xB7" }) : null,
3633
- m
3634
- ] }, i)) }) : null
3635
- ] }) });
3911
+ footer.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
3912
+ Text,
3913
+ {
3914
+ variant: "label",
3915
+ tone: "muted",
3916
+ as: "p",
3917
+ className: cn("gap-step-xs flex items-center pt-step-sm", !resources && "mt-auto"),
3918
+ children: footer.map((m, i) => /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "gap-step-xs flex items-center", children: [
3919
+ i > 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: "\xB7" }) : null,
3920
+ m
3921
+ ] }, i))
3922
+ }
3923
+ ) : null,
3924
+ resources ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "gap-step-md mt-auto flex flex-wrap items-center pt-step-sm", children: resources }) : null
3925
+ ] });
3926
+ if (resources) {
3927
+ return /* @__PURE__ */ jsxRuntime.jsx(
3928
+ "article",
3929
+ {
3930
+ className: cn(CARD_SURFACE, "p-step-lg gap-step-sm flex h-full flex-col", className),
3931
+ ...props,
3932
+ children: body
3933
+ }
3934
+ );
3935
+ }
3936
+ 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 }) });
3636
3937
  }
3637
3938
  function ThemeToggle({
3638
3939
  label = "Cambiar de tema",
@@ -3650,20 +3951,20 @@ function ThemeToggle({
3650
3951
  size: size2,
3651
3952
  "aria-label": label,
3652
3953
  className: cn("[&_svg]:size-[19px]", className),
3653
- onClick: () => onThemeChange?.(alternarTema()),
3954
+ onClick: () => onThemeChange?.(toggleTheme()),
3654
3955
  ...props,
3655
3956
  children: [
3656
- /* @__PURE__ */ jsxRuntime.jsx(Sol, { className: "light:hidden" }),
3657
- /* @__PURE__ */ jsxRuntime.jsx(Luna, { className: "hidden light:block" })
3957
+ /* @__PURE__ */ jsxRuntime.jsx(Sun, { className: "light:hidden" }),
3958
+ /* @__PURE__ */ jsxRuntime.jsx(Moon, { className: "hidden light:block" })
3658
3959
  ]
3659
3960
  }
3660
3961
  );
3661
3962
  }
3662
- function useTema() {
3663
- return react.useSyncExternalStore(suscribirse2, temaActual, servidor);
3963
+ function useTheme() {
3964
+ return react.useSyncExternalStore(subscribe2, currentTheme, server);
3664
3965
  }
3665
- var suscribirse2 = (avisar) => escucharTema(() => avisar());
3666
- var servidor = () => "dark";
3966
+ var subscribe2 = (warn) => watchTheme(() => warn());
3967
+ var server = () => "dark";
3667
3968
  function TableOfContents({
3668
3969
  items,
3669
3970
  title = "En esta p\xE1gina",
@@ -3675,35 +3976,35 @@ function TableOfContents({
3675
3976
  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: [
3676
3977
  /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "eyebrow", tone: "muted", as: "p", children: title }),
3677
3978
  /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "border-hairline gap-step-xs flex flex-col border-l", children: items.map((item2) => {
3678
- const activo = item2.href === activeHref;
3679
- const clases = cn(
3979
+ const active = item2.href === activeHref;
3980
+ const classes = cn(
3680
3981
  "px-step-sm -ml-px block border-l",
3681
3982
  "font-sans text-label transition-standard",
3682
- "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent",
3983
+ "focus-ring",
3683
3984
  "cursor-pointer",
3684
3985
  item2.nested && "pl-step-lg",
3685
3986
  "border-transparent text-text-secondary hover:text-text-primary",
3686
- // El hover del activo se declara con las dos variantes juntas para
3687
- // que gane al `hover:` de arriba por especificidad y no por el orden
3688
- // en que Tailwind decida emitir las reglas.
3987
+ // The active hover is declared with both variants together so it
3988
+ // beats the `hover:` above by specificity and not by the order
3989
+ // Tailwind happens to emit the rules in.
3689
3990
  "aria-[current]:border-accent aria-[current]:text-accent aria-[current]:hover:text-accent"
3690
3991
  );
3691
- 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);
3992
+ 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);
3692
3993
  }) })
3693
3994
  ] });
3694
3995
  }
3695
- function Isotipo({
3696
- sobre = "oscuro",
3697
- basePath = RUTA_ASSETS,
3996
+ function Isotype({
3997
+ background = "dark",
3998
+ basePath = ASSETS_PATH,
3698
3999
  alt = "",
3699
4000
  className,
3700
4001
  ...props
3701
4002
  }) {
3702
- const archivo = sobre === "oscuro" ? aletas.espuma : aletas.color;
4003
+ const file = background === "dark" ? fins.foam : fins.color;
3703
4004
  return /* @__PURE__ */ jsxRuntime.jsx(
3704
4005
  "img",
3705
4006
  {
3706
- src: `${basePath}/${archivo}`,
4007
+ src: `${basePath}/${file}`,
3707
4008
  alt,
3708
4009
  width: 147,
3709
4010
  height: 111,
@@ -3713,55 +4014,56 @@ function Isotipo({
3713
4014
  );
3714
4015
  }
3715
4016
  function Logo({
3716
- sobre = "oscuro",
3717
- basePath = RUTA_ASSETS,
3718
- soloIsotipo = false,
3719
- conLema = false,
4017
+ background = "dark",
4018
+ basePath = ASSETS_PATH,
4019
+ isotypeOnly = false,
4020
+ withTagline = false,
3720
4021
  className,
3721
4022
  ...props
3722
4023
  }) {
3723
- const conAleta = /* @__PURE__ */ jsxRuntime.jsx(
3724
- Isotipo,
4024
+ const withFin = /* @__PURE__ */ jsxRuntime.jsx(
4025
+ Isotype,
3725
4026
  {
3726
- sobre,
4027
+ background,
3727
4028
  basePath,
3728
- alt: soloIsotipo ? naming.wordmark : "",
3729
- className: conLema ? "h-9 w-auto" : "h-7 w-auto"
4029
+ alt: isotypeOnly ? naming.wordmark : "",
4030
+ className: withTagline ? "h-9 w-auto" : "h-7 w-auto"
3730
4031
  }
3731
4032
  );
3732
4033
  const wordmark = /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-ui font-display text-text-primary font-bold tracking-[-0.02em]", children: naming.wordmark });
3733
- if (soloIsotipo) {
3734
- return /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("gap-step-xs inline-flex items-center", className), ...props, children: conAleta });
4034
+ if (isotypeOnly) {
4035
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("gap-step-xs inline-flex items-center", className), ...props, children: withFin });
3735
4036
  }
3736
- if (conLema) {
4037
+ if (withTagline) {
3737
4038
  return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: cn("gap-step-sm inline-flex items-center", className), ...props, children: [
3738
- conAleta,
4039
+ withFin,
3739
4040
  /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", className: "bg-hairline h-8 w-px shrink-0" }),
3740
4041
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 flex-col", children: [
3741
4042
  wordmark,
3742
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-eyebrow font-mono text-text-muted uppercase", children: tagline.corto })
4043
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-eyebrow font-mono text-text-muted uppercase", children: tagline.short })
3743
4044
  ] })
3744
4045
  ] });
3745
4046
  }
3746
4047
  return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: cn("gap-step-xs inline-flex items-center", className), ...props, children: [
3747
- conAleta,
4048
+ withFin,
3748
4049
  wordmark
3749
4050
  ] });
3750
4051
  }
3751
4052
 
3752
- // src/lib/social.tsx
4053
+ // src/social/index.tsx
3753
4054
  var social_exports = {};
3754
4055
  __export(social_exports, {
3755
- Correo: () => Correo,
3756
4056
  Discord: () => Discord,
4057
+ Email: () => Email,
3757
4058
  GitHub: () => GitHub,
3758
4059
  Instagram: () => Instagram,
3759
4060
  LinkedIn: () => LinkedIn,
4061
+ Newsletter: () => Newsletter,
3760
4062
  Rss: () => Rss,
3761
4063
  X: () => X,
3762
4064
  YouTube: () => YouTube
3763
4065
  });
3764
- function Marca({ children, ...props }) {
4066
+ function Brand({ children, ...props }) {
3765
4067
  return /* @__PURE__ */ jsxRuntime.jsx(
3766
4068
  "svg",
3767
4069
  {
@@ -3776,7 +4078,7 @@ function Marca({ children, ...props }) {
3776
4078
  }
3777
4079
  );
3778
4080
  }
3779
- function Funcional({ children, ...props }) {
4081
+ function Functional({ children, ...props }) {
3780
4082
  return /* @__PURE__ */ jsxRuntime.jsx(
3781
4083
  "svg",
3782
4084
  {
@@ -3795,21 +4097,26 @@ function Funcional({ children, ...props }) {
3795
4097
  }
3796
4098
  );
3797
4099
  }
3798
- 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" }) });
3799
- 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" }) });
3800
- 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" }) });
3801
- 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" }) });
3802
- 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" }) });
3803
- 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" }) });
3804
- var Rss = (props) => /* @__PURE__ */ jsxRuntime.jsxs(Funcional, { ...props, children: [
4100
+ 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" }) });
4101
+ 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" }) });
4102
+ 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" }) });
4103
+ 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" }) });
4104
+ 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" }) });
4105
+ 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" }) });
4106
+ var Rss = (props) => /* @__PURE__ */ jsxRuntime.jsxs(Functional, { ...props, children: [
3805
4107
  /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M4 11a9 9 0 0 1 9 9M4 4a16 16 0 0 1 16 16" }),
3806
4108
  /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "5", cy: "19", r: "1.6", fill: "currentColor", stroke: "none" })
3807
4109
  ] });
3808
- var Correo = (props) => /* @__PURE__ */ jsxRuntime.jsxs(Funcional, { ...props, children: [
4110
+ var Email = (props) => /* @__PURE__ */ jsxRuntime.jsxs(Functional, { ...props, children: [
3809
4111
  /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "2.75", y: "4.75", width: "18.5", height: "14.5", rx: "2" }),
3810
4112
  /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m3.5 7.5 7.4 5.2a2 2 0 0 0 2.2 0l7.4-5.2" })
3811
4113
  ] });
4114
+ var Newsletter = (props) => /* @__PURE__ */ jsxRuntime.jsxs(Functional, { ...props, children: [
4115
+ /* @__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" }),
4116
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M13.7 19a2 2 0 0 1-3.4 0" })
4117
+ ] });
3812
4118
 
4119
+ exports.ASSETS_PATH = ASSETS_PATH;
3813
4120
  exports.Accordion = Accordion;
3814
4121
  exports.AccordionContent = AccordionContent;
3815
4122
  exports.AccordionItem = AccordionItem;
@@ -3836,8 +4143,10 @@ exports.Badge = Badge;
3836
4143
  exports.Blockquote = Blockquote;
3837
4144
  exports.Breadcrumb = Breadcrumb;
3838
4145
  exports.Button = Button;
4146
+ exports.CARD = CARD;
4147
+ exports.CARD_HOVER = CARD_HOVER;
4148
+ exports.CARD_SURFACE = CARD_SURFACE;
3839
4149
  exports.Calendar = Calendar;
3840
- exports.CaraDeMascota = CaraDeMascota;
3841
4150
  exports.Card = Card;
3842
4151
  exports.CardContent = CardContent;
3843
4152
  exports.CardDescription = CardDescription;
@@ -3876,14 +4185,14 @@ exports.EmptyState = EmptyState;
3876
4185
  exports.EventCalendar = EventCalendar;
3877
4186
  exports.Footer = Footer;
3878
4187
  exports.FooterLink = FooterLink;
3879
- exports.HOVER_TARJETA = HOVER_TARJETA;
3880
4188
  exports.Hero = Hero;
3881
4189
  exports.Input = Input;
3882
- exports.Isotipo = Isotipo;
4190
+ exports.Isotype = Isotype;
3883
4191
  exports.Label = Label2;
3884
4192
  exports.LinkRow = LinkRow;
3885
4193
  exports.Logo = Logo;
3886
- exports.Mascota = Mascota;
4194
+ exports.Mascot = Mascot;
4195
+ exports.MascotFace = MascotFace;
3887
4196
  exports.MetricBadge = MetricBadge;
3888
4197
  exports.Nav = Nav;
3889
4198
  exports.NavItem = NavItem;
@@ -3901,10 +4210,8 @@ exports.PopoverAnchor = PopoverAnchor;
3901
4210
  exports.PopoverContent = PopoverContent;
3902
4211
  exports.PopoverTrigger = PopoverTrigger;
3903
4212
  exports.Progress = Progress;
3904
- exports.RUTA_ASSETS = RUTA_ASSETS;
3905
4213
  exports.RadioGroup = RadioGroup2;
3906
4214
  exports.RadioGroupItem = RadioGroupItem;
3907
- exports.SUPERFICIE_TARJETA = SUPERFICIE_TARJETA;
3908
4215
  exports.ScrollingProgressBar = ScrollingProgressBar;
3909
4216
  exports.Select = Select;
3910
4217
  exports.SelectContent = SelectContent;
@@ -3924,14 +4231,15 @@ exports.SheetFooter = SheetFooter;
3924
4231
  exports.SheetHeader = SheetHeader;
3925
4232
  exports.SheetTitle = SheetTitle;
3926
4233
  exports.SheetTrigger = SheetTrigger;
4234
+ exports.SidebarGroup = SidebarGroup;
3927
4235
  exports.SidebarItem = SidebarItem;
3928
4236
  exports.SidebarNav = SidebarNav;
3929
4237
  exports.Skeleton = Skeleton;
3930
4238
  exports.Stat = Stat;
3931
4239
  exports.Switch = Switch;
3932
- exports.TEMA_ATRIBUTO = TEMA_ATRIBUTO;
3933
- exports.TEMA_CLAVE = TEMA_CLAVE;
3934
- exports.TEMA_EVENTO = TEMA_EVENTO;
4240
+ exports.THEME_ATTRIBUTE = THEME_ATTRIBUTE;
4241
+ exports.THEME_EVENT = THEME_EVENT;
4242
+ exports.THEME_KEY = THEME_KEY;
3935
4243
  exports.Table = Table;
3936
4244
  exports.TableBody = TableBody;
3937
4245
  exports.TableCaption = TableCaption;
@@ -3956,44 +4264,45 @@ exports.TooltipContent = TooltipContent;
3956
4264
  exports.TooltipProvider = TooltipProvider;
3957
4265
  exports.TooltipTrigger = TooltipTrigger;
3958
4266
  exports.alertVariants = alert;
3959
- exports.aletas = aletas;
3960
- exports.alternarTema = alternarTema;
3961
- exports.aplicarTema = aplicarTema;
4267
+ exports.applyTheme = applyTheme;
3962
4268
  exports.avatarVariants = avatar;
3963
4269
  exports.badgeVariants = badge;
3964
4270
  exports.brand = brand;
3965
4271
  exports.buttonVariants = button;
3966
- exports.caras = caras;
3967
- exports.categoryBadgeVariants = categoria;
4272
+ exports.categoryBadgeVariants = category;
3968
4273
  exports.cn = cn;
3969
4274
  exports.colors = colors;
3970
4275
  exports.control = control;
4276
+ exports.currentTheme = currentTheme;
3971
4277
  exports.dark = dark;
3972
- exports.escucharTema = escucharTema;
4278
+ exports.faceList = faceList;
4279
+ exports.faceUsage = faceUsage;
4280
+ exports.faces = faces;
4281
+ exports.fins = fins;
3973
4282
  exports.fonts = fonts;
3974
4283
  exports.gradient = gradient;
3975
4284
  exports.light = light;
3976
4285
  exports.limits = limits;
3977
- exports.listaCaras = listaCaras;
3978
- exports.listaPoses = listaPoses;
4286
+ exports.metricBadgeVariants = metric;
3979
4287
  exports.motion = motion;
3980
4288
  exports.naming = naming;
4289
+ exports.poseList = poseList;
3981
4290
  exports.poses = poses;
4291
+ exports.preferredTheme = preferredTheme;
3982
4292
  exports.radius = radius;
3983
- exports.scriptTema = scriptTema;
3984
4293
  exports.series = series;
3985
4294
  exports.shadow = shadow;
3986
- exports.sintaxis = sintaxis;
3987
4295
  exports.size = size;
3988
4296
  exports.social = social_exports;
3989
4297
  exports.spacing = spacing;
4298
+ exports.storedTheme = storedTheme;
4299
+ exports.syntax = syntax;
3990
4300
  exports.tagline = tagline;
3991
- exports.temaActual = temaActual;
3992
- exports.temaGuardado = temaGuardado;
3993
- exports.temaPreferido = temaPreferido;
3994
- exports.textVariants = texto;
4301
+ exports.textVariants = text;
4302
+ exports.themeScript = themeScript;
3995
4303
  exports.toast = toast2;
4304
+ exports.toggleTheme = toggleTheme;
3996
4305
  exports.tokens = tokens;
3997
4306
  exports.typeScale = typeScale;
3998
- exports.useTema = useTema;
3999
- exports.usoDeCara = usoDeCara;
4307
+ exports.useTheme = useTheme;
4308
+ exports.watchTheme = watchTheme;