@axzydev/axzy_ui_system 1.3.5 → 1.4.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 (34) hide show
  1. package/dist/index.cjs +562 -506
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.css +1 -1
  4. package/dist/index.d.cts +107 -3
  5. package/dist/index.d.ts +107 -3
  6. package/dist/index.js +433 -378
  7. package/dist/index.js.map +1 -1
  8. package/package.json +1 -1
  9. package/src/App.tsx +6 -0
  10. package/src/components/atoms/input/input.props.ts +5 -0
  11. package/src/components/atoms/input/input.tsx +24 -17
  12. package/src/components/atoms/input-number/input-number.doc.mdx +74 -0
  13. package/src/components/atoms/input-number/input-number.props.ts +66 -0
  14. package/src/components/atoms/input-number/input-number.stories.tsx +111 -0
  15. package/src/components/atoms/input-number/input-number.tsx +86 -0
  16. package/src/components/molecules/date-picker/datePicker.tsx +1 -0
  17. package/src/components/molecules/dropdown-menu/dropdown-menu.tsx +1 -0
  18. package/src/components/molecules/multi-select/multi-select.tsx +1 -0
  19. package/src/components/molecules/search-select/search-select.tsx +1 -0
  20. package/src/components/molecules/time-picker/timePicker.tsx +1 -0
  21. package/src/components/organisms/dialog/dialog.stories.tsx +48 -0
  22. package/src/components/organisms/sidebar/sidebar.doc.mdx +12 -12
  23. package/src/hooks/useClickOutside.ts +6 -0
  24. package/src/hooks/useFloatingPanel.ts +4 -1
  25. package/src/index.ts +4 -2
  26. package/src/sandbox/navigation.ts +2 -0
  27. package/src/showcases/FeedbackShowcases.tsx +16 -1
  28. package/src/showcases/FormShowcases.tsx +287 -1
  29. package/src/showcases/HomeShowcase.tsx +552 -331
  30. package/src/showcases/ProductoDetalle.tsx +181 -0
  31. package/src/showcases/ProductoIcono.tsx +25 -0
  32. package/src/showcases/StructureShowcases.tsx +2 -2
  33. package/src/showcases/productos.ts +179 -0
  34. package/src/showcases/techIcons.ts +67 -0
@@ -1,67 +1,301 @@
1
- import { useState } from "react";
2
- import { FaArrowRight, FaCheck, FaCloud, FaCode, FaDatabase, FaDownload, FaExternalLinkAlt, FaGithub, FaLayerGroup, FaLinkedin, FaMapMarkerAlt, FaMobileAlt, FaSearch, FaServer, FaMedium } from "react-icons/fa";
3
- import { SiReact, SiNodedotjs } from "react-icons/si";
1
+ import { useState, type ReactNode } from "react";
2
+ import {
3
+ FaBoxOpen,
4
+ FaCloud,
5
+ FaCode,
6
+ FaDatabase,
7
+ FaDownload,
8
+ FaExternalLinkAlt,
9
+ FaGithub,
10
+ FaLayerGroup,
11
+ FaLinkedin,
12
+ FaMapMarkerAlt,
13
+ FaMedium,
14
+ FaMobileAlt,
15
+ FaSearch,
16
+ FaServer,
17
+ } from "react-icons/fa";
4
18
  import ITBadget from "../components/atoms/badget/badget";
5
19
  import ITButton from "../components/atoms/button/button";
6
- import ITCard from "../components/molecules/card/card";
7
20
  import ITDivider from "../components/atoms/divider/divider";
8
21
  import ITFlex from "../components/atoms/flex/flex";
9
22
  import ITGrid from "../components/atoms/grid/grid";
10
23
  import ITStack from "../components/atoms/stack/stack";
11
24
  import ITText from "../components/atoms/text/text";
25
+ import { techIcon, type TechIconEntry } from "./techIcons";
26
+ import { PRODUCTOS, type Producto } from "./productos";
27
+ import ProductoDetalle from "./ProductoDetalle";
28
+ import ProductoIcono from "./ProductoIcono";
29
+
30
+ const CV_URL = "/Martin_Amaro_CV_ES_2026.pdf";
31
+ const GITHUB_URL = "https://github.com/maag070208";
32
+ const LINKEDIN_URL = "https://www.linkedin.com/in/maag070208/";
33
+ const MEDIUM_URL = "https://medium.com/@axzydev";
34
+
35
+ /**
36
+ * Degradados literales para los íconos de las métricas. Tailwind v4 no ve las
37
+ * clases construidas en runtime, así que van escritas completas y se eligen por
38
+ * posición.
39
+ */
40
+ const ACCENTS = [
41
+ "from-primary-500 to-info-500",
42
+ "from-purple-500 to-primary-500",
43
+ "from-primary-600 to-purple-600",
44
+ "from-info-500 to-purple-500",
45
+ ];
46
+
47
+ /**
48
+ * Encabezado de sección: eyebrow en mayúsculas, título y subtítulo opcional.
49
+ * Mismo patrón que el landing del sistema (`#ui-system`).
50
+ */
51
+ const SectionHeading = ({
52
+ eyebrow,
53
+ title,
54
+ subtitle,
55
+ className = "",
56
+ }: {
57
+ /** Texto chico en mayúsculas sobre el título. */
58
+ eyebrow: string;
59
+ /** Título de la sección. */
60
+ title: string;
61
+ /** Frase de apoyo debajo del título. */
62
+ subtitle?: string;
63
+ /** Clases extra del contenedor (márgenes). */
64
+ className?: string;
65
+ }) => (
66
+ <ITStack spacing={1} className={className}>
67
+ <ITText as="p" className="!text-[11px] !font-bold uppercase !tracking-wider text-primary-600 dark:text-primary-400">
68
+ {eyebrow}
69
+ </ITText>
70
+ <ITText as="h2" className="!text-xl !font-bold !tracking-tight text-slate-900 dark:text-white">
71
+ {title}
72
+ </ITText>
73
+ {subtitle && (
74
+ <ITText as="p" className="!text-sm text-slate-500 dark:text-slate-400">
75
+ {subtitle}
76
+ </ITText>
77
+ )}
78
+ </ITStack>
79
+ );
80
+
81
+ /**
82
+ * Ícono de tecnología local, invertido en modo oscuro cuando la marca es
83
+ * monocromática (GitHub, Express).
84
+ */
85
+ const TechIconImg = ({
86
+ icon,
87
+ alt,
88
+ size = "w-7 h-7",
89
+ }: {
90
+ /** Ícono resuelto por `techIcons`. */
91
+ icon: TechIconEntry;
92
+ /** Texto alternativo. */
93
+ alt: string;
94
+ /** Clases de tamaño. @default "w-7 h-7" */
95
+ size?: string;
96
+ }) => (
97
+ <img
98
+ src={icon.src}
99
+ alt={alt}
100
+ className={`${size} object-contain ${icon.mono ? "dark:invert" : ""}`}
101
+ loading="lazy"
102
+ />
103
+ );
104
+
105
+ /** Tarjeta de una tecnología: ícono en caja blanca + nombre. */
106
+ const TechTile = ({ icon, label }: { icon: TechIconEntry; label: string }) => (
107
+ <div className="group flex h-full flex-col items-center justify-center gap-2 rounded-xl border border-slate-100 bg-slate-50/70 p-3 transition-all duration-200 hover:-translate-y-0.5 hover:border-primary-300 hover:bg-white hover:shadow-md dark:border-slate-800 dark:bg-slate-950/40 dark:hover:border-primary-700 dark:hover:bg-slate-900">
108
+ <div className="flex h-11 w-11 items-center justify-center rounded-lg bg-white shadow-sm dark:bg-slate-900">
109
+ <TechIconImg icon={icon} alt={label} size="w-7 h-7" />
110
+ </div>
111
+ <ITText as="span" className="w-full truncate text-center text-[11px] font-semibold text-slate-600 dark:text-slate-300">
112
+ {label}
113
+ </ITText>
114
+ </div>
115
+ );
116
+
117
+ /** Métrica del hero: valor grande, ícono con degradado y texto de apoyo. */
118
+ const StatCard = ({ stat, index }: { stat: { label: string; value: string; hint: string; icon: ReactNode }; index: number }) => (
119
+ <div className="flex h-full flex-col gap-4 rounded-2xl border border-slate-200 bg-white p-5 shadow-sm transition-all duration-300 hover:-translate-y-0.5 hover:border-primary-300 hover:shadow-lg dark:border-slate-800 dark:bg-slate-900 dark:hover:border-primary-700">
120
+ <div className="flex items-start justify-between gap-3">
121
+ <ITText as="p" className="!text-3xl !font-extrabold !leading-none !tracking-tight tabular-nums text-slate-900 dark:text-white">
122
+ {stat.value}
123
+ </ITText>
124
+ <span className={`flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-xl bg-gradient-to-br text-[13px] text-white shadow-sm ${ACCENTS[index % ACCENTS.length]}`}>
125
+ {stat.icon}
126
+ </span>
127
+ </div>
128
+
129
+ <div className="mt-auto">
130
+ <ITText as="p" className="!text-sm !font-semibold text-primary-600 dark:text-primary-400">
131
+ {stat.label}
132
+ </ITText>
133
+ <ITText as="p" className="!mt-0.5 !text-[11px] text-slate-400 dark:text-slate-500">
134
+ {stat.hint}
135
+ </ITText>
136
+ </div>
137
+ </div>
138
+ );
139
+
140
+ /**
141
+ * Tarjeta de producto, compacta: portada (captura real o degradado), nombre,
142
+ * descripción corta, stack y acceso al detalle.
143
+ */
144
+ const TarjetaProducto = ({
145
+ producto,
146
+ onVerDetalle,
147
+ }: {
148
+ producto: Producto;
149
+ onVerDetalle: (producto: Producto) => void;
150
+ }) => {
151
+ const capturas = producto.capturas ?? [];
152
+ const portada = capturas[0];
153
+ const tieneDetalle = capturas.length > 0;
154
+ const enlaceInterno = producto.enlace?.startsWith("#");
155
+
156
+ return (
157
+ <div className="group flex h-full flex-col gap-4 rounded-2xl border border-slate-200 bg-white p-4 shadow-sm transition-all duration-300 hover:-translate-y-1 hover:border-primary-300 hover:shadow-xl dark:border-slate-800 dark:bg-slate-900 dark:hover:border-primary-700">
158
+ {/* Portada */}
159
+ <button
160
+ type="button"
161
+ disabled={!tieneDetalle}
162
+ onClick={() => onVerDetalle(producto)}
163
+ aria-label={tieneDetalle ? `Ver detalle de ${producto.nombre}` : producto.nombre}
164
+ className="relative block aspect-[16/10] w-full overflow-hidden rounded-xl border border-slate-200 bg-slate-50 dark:border-slate-800 dark:bg-slate-950/60"
165
+ >
166
+ {portada ? (
167
+ <img
168
+ src={portada.src}
169
+ alt={`${producto.nombre}: ${portada.titulo}`}
170
+ className="h-full w-full object-cover object-top transition-transform duration-300 group-hover:scale-[1.02]"
171
+ loading="lazy"
172
+ />
173
+ ) : (
174
+ <div className={`flex h-full w-full items-center justify-center bg-gradient-to-br ${producto.gradiente} text-white`}>
175
+ <ProductoIcono icono={producto.icono} size={44} />
176
+ </div>
177
+ )}
178
+
179
+ {capturas.length > 1 && (
180
+ <span className="absolute bottom-2 right-2 rounded-full bg-slate-900/70 px-2.5 py-1 text-[10px] font-semibold text-white backdrop-blur-sm">
181
+ {capturas.length} capturas
182
+ </span>
183
+ )}
184
+ </button>
185
+
186
+ {/* Encabezado */}
187
+ <ITFlex align="center" justify="between" gap={3}>
188
+ <ITFlex align="center" gap={2.5}>
189
+ <div className={`flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-gradient-to-br ${producto.gradiente} text-white shadow-sm`}>
190
+ <ProductoIcono icono={producto.icono} size={16} />
191
+ </div>
192
+ <ITText as="h3" className="!text-base !font-bold !tracking-tight text-slate-900 dark:text-white">
193
+ {producto.nombre}
194
+ </ITText>
195
+ </ITFlex>
196
+ <ITBadget label={producto.tipo} color="primary" variant="outlined" />
197
+ </ITFlex>
198
+
199
+ <ITText as="p" className="!text-sm !leading-relaxed text-slate-600 dark:text-slate-300">
200
+ {producto.descripcion}
201
+ </ITText>
202
+
203
+ <ITFlex gap={1.5} wrap="wrap">
204
+ {producto.stack.map((tecnologia) => (
205
+ <span
206
+ key={tecnologia}
207
+ className="rounded-md bg-slate-100 px-2 py-0.5 text-[10px] font-medium text-slate-500 dark:bg-slate-800 dark:text-slate-400"
208
+ >
209
+ {tecnologia}
210
+ </span>
211
+ ))}
212
+ </ITFlex>
213
+
214
+ {/* Acciones */}
215
+ <ITFlex align="center" justify="between" gap={3} wrap="wrap" className="mt-auto pt-2">
216
+ {tieneDetalle ? (
217
+ <ITButton variant="filled" color="primary" size="sm" onClick={() => onVerDetalle(producto)}>
218
+ Ver detalle
219
+ </ITButton>
220
+ ) : (
221
+ <ITText as="span" />
222
+ )}
223
+
224
+ {producto.enlace && (
225
+ <a
226
+ href={producto.enlace}
227
+ {...(enlaceInterno ? {} : { target: "_blank", rel: "noopener noreferrer" })}
228
+ className="!text-xs !font-semibold !text-primary-600 dark:!text-primary-400 inline-flex items-center gap-1.5"
229
+ >
230
+ {!enlaceInterno && <FaExternalLinkAlt size={11} />}
231
+ {producto.enlaceTexto ?? "Ver el repositorio"}
232
+ </a>
233
+ )}
234
+ </ITFlex>
235
+ </div>
236
+ );
237
+ };
12
238
 
13
239
  export const HomeShowcase = () => {
14
240
  const [techSearch, setTechSearch] = useState("");
15
241
  const [selectedCategory, setSelectedCategory] = useState("all");
242
+ const [detalle, setDetalle] = useState<Producto | null>(null);
16
243
 
17
244
  const LANGUAGES = [
18
- { name: "TypeScript", icon: "https://icongr.am/devicon/typescript-original.svg?size=128&color=currentColor" },
19
- { name: "JavaScript", icon: "https://icongr.am/devicon/javascript-original.svg?size=128&color=currentColor" },
20
- { name: "C#", icon: "https://icongr.am/devicon/csharp-original.svg?size=128&color=currentColor" },
21
- { name: "Python", icon: "https://icongr.am/devicon/python-original.svg?size=128&color=currentColor" },
22
- { name: "Kotlin", icon: "https://devicons.railway.app/i/kotlin.svg" },
23
- { name: "Dart", icon: "https://cdn.jsdelivr.net/gh/devicons/devicon/icons/dart/dart-original.svg" },
245
+ { name: "TypeScript", icon: techIcon("typescript") },
246
+ { name: "JavaScript", icon: techIcon("javascript") },
247
+ { name: "C#", icon: techIcon("csharp") },
248
+ { name: "Python", icon: techIcon("python") },
249
+ { name: "Kotlin", icon: techIcon("kotlin") },
250
+ { name: "Dart", icon: techIcon("dart") },
24
251
  ];
25
252
 
26
253
  const FRAMEWORKS = [
27
- { name: "Angular", icon: "https://icongr.am/devicon/angularjs-original.svg?size=128&color=currentColor" },
28
- { name: "React", icon: "https://icongr.am/devicon/react-original.svg?size=128&color=currentColor" },
29
- { name: "Ionic", icon: "https://icongr.am/devicon/ionic-original.svg?size=128&color=currentColor" },
30
- { name: ".NET", icon: "https://icongr.am/devicon/dot-net-original.svg?size=128&color=currentColor" },
31
- { name: "Node.js", icon: "https://icongr.am/devicon/nodejs-original.svg?size=128&color=currentColor" },
32
- { name: "Flutter", icon: "https://cdn.jsdelivr.net/gh/devicons/devicon/icons/flutter/flutter-original.svg" },
33
- { name: "Express", icon: "https://icongr.am/devicon/express-original-wordmark.svg?size=128&color=currentColor" },
34
- { name: "Electron", icon: "https://icongr.am/devicon/electron-original.svg?size=128&color=currentColor" },
254
+ { name: "Angular", icon: techIcon("angular") },
255
+ { name: "React", icon: techIcon("react") },
256
+ { name: "Ionic", icon: techIcon("ionic") },
257
+ { name: ".NET", icon: techIcon("dotnet") },
258
+ { name: "Node.js", icon: techIcon("nodejs") },
259
+ { name: "Flutter", icon: techIcon("flutter") },
260
+ { name: "Express", icon: techIcon("express") },
261
+ { name: "Electron", icon: techIcon("electron") },
35
262
  ];
36
263
 
37
264
  const TECH_STACK = [
38
- { name: "TypeScript", icon: "https://icongr.am/devicon/typescript-original.svg?size=128&color=currentColor", category: "frontend" },
39
- { name: "JavaScript", icon: "https://icongr.am/devicon/javascript-original.svg?size=128&color=currentColor", category: "frontend" },
40
- { name: "Angular", icon: "https://icongr.am/devicon/angularjs-original.svg?size=128&color=currentColor", category: "frontend" },
41
- { name: "React", icon: "https://icongr.am/devicon/react-original.svg?size=128&color=currentColor", category: "frontend" },
42
- { name: "Redux", icon: "https://devicons.railway.app/i/redux.svg", category: "frontend" },
43
- { name: "HTML5", icon: "https://icongr.am/devicon/html5-original.svg?size=128&color=currentColor", category: "frontend" },
44
- { name: "CSS3", icon: "https://icongr.am/devicon/css3-original.svg?size=128&color=currentColor", category: "frontend" },
45
- { name: "Sass", icon: "https://icongr.am/devicon/sass-original.svg?size=128&color=currentColor", category: "frontend" },
46
- { name: "C#", icon: "https://icongr.am/devicon/csharp-original.svg?size=128&color=currentColor", category: "backend" },
47
- { name: "Python", icon: "https://icongr.am/devicon/python-original.svg?size=128&color=currentColor", category: "backend" },
48
- { name: "Node.js", icon: "https://icongr.am/devicon/nodejs-original.svg?size=128&color=currentColor", category: "backend" },
49
- { name: "Express", icon: "https://icongr.am/devicon/express-original-wordmark.svg?size=128&color=currentColor", category: "backend" },
50
- { name: ".NET Core", icon: "https://icongr.am/devicon/dot-net-original.svg?size=128&color=currentColor", category: "backend" },
51
- { name: "MongoDB", icon: "https://icongr.am/devicon/mongodb-original.svg?size=128&color=currentColor", category: "database" },
52
- { name: "MySQL", icon: "https://icongr.am/devicon/mysql-original-wordmark.svg?size=128&color=currentColor", category: "database" },
53
- { name: "Flutter", icon: "https://cdn.jsdelivr.net/gh/devicons/devicon/icons/flutter/flutter-original.svg", category: "mobile" },
54
- { name: "Kotlin", icon: "https://devicons.railway.app/i/kotlin.svg", category: "mobile" },
55
- { name: "Android", icon: "https://icongr.am/devicon/android-original.svg?size=128&color=currentColor", category: "mobile" },
56
- { name: "Ionic", icon: "https://icongr.am/devicon/ionic-original.svg?size=128&color=currentColor", category: "mobile" },
57
- { name: "Docker", icon: "https://icongr.am/devicon/docker-original.svg?size=128&color=currentColor", category: "devops" },
58
- { name: "Git", icon: "https://icongr.am/devicon/git-original.svg?size=128&color=currentColor", category: "devops" },
59
- { name: "GitHub", icon: "https://icongr.am/devicon/github-original.svg?size=128&color=currentColor", category: "devops" },
60
- { name: "GitLab", icon: "https://icongr.am/devicon/gitlab-original.svg?size=128&color=currentColor", category: "devops" },
61
- { name: "npm", icon: "https://devicons.railway.app/i/npm.svg", category: "devops" },
62
- { name: "Electron", icon: "https://icongr.am/devicon/electron-original.svg?size=128&color=currentColor", category: "desktop" },
63
- { name: "Visual Studio", icon: "https://icongr.am/devicon/visualstudio-plain.svg?size=128&color=currentColor", category: "tools" },
64
- { name: "Jest", icon: "https://cdn.jsdelivr.net/gh/devicons/devicon/icons/jest/jest-plain.svg", category: "testing" },
265
+ { name: "TypeScript", icon: techIcon("typescript"), category: "frontend" },
266
+ { name: "JavaScript", icon: techIcon("javascript"), category: "frontend" },
267
+ { name: "Angular", icon: techIcon("angular"), category: "frontend" },
268
+ { name: "React", icon: techIcon("react"), category: "frontend" },
269
+ { name: "Redux", icon: techIcon("redux"), category: "frontend" },
270
+ { name: "HTML5", icon: techIcon("html5"), category: "frontend" },
271
+ { name: "CSS3", icon: techIcon("css3"), category: "frontend" },
272
+ { name: "Sass", icon: techIcon("sass"), category: "frontend" },
273
+ { name: "C#", icon: techIcon("csharp"), category: "backend" },
274
+ { name: "Python", icon: techIcon("python"), category: "backend" },
275
+ { name: "Node.js", icon: techIcon("nodejs"), category: "backend" },
276
+ { name: "Express", icon: techIcon("express"), category: "backend" },
277
+ { name: ".NET Core", icon: techIcon("dotnet"), category: "backend" },
278
+ { name: "MongoDB", icon: techIcon("mongodb"), category: "database" },
279
+ { name: "MySQL", icon: techIcon("mysql"), category: "database" },
280
+ { name: "Flutter", icon: techIcon("flutter"), category: "mobile" },
281
+ { name: "Kotlin", icon: techIcon("kotlin"), category: "mobile" },
282
+ { name: "Android", icon: techIcon("android"), category: "mobile" },
283
+ { name: "Ionic", icon: techIcon("ionic"), category: "mobile" },
284
+ { name: "Docker", icon: techIcon("docker"), category: "devops" },
285
+ { name: "Git", icon: techIcon("git"), category: "devops" },
286
+ { name: "GitHub", icon: techIcon("github"), category: "devops" },
287
+ { name: "GitLab", icon: techIcon("gitlab"), category: "devops" },
288
+ { name: "npm", icon: techIcon("npm"), category: "devops" },
289
+ { name: "Electron", icon: techIcon("electron"), category: "desktop" },
290
+ { name: "Visual Studio", icon: techIcon("visualstudio"), category: "tools" },
291
+ { name: "Jest", icon: techIcon("jest"), category: "testing" },
292
+ ];
293
+
294
+ const STATS = [
295
+ { label: "Experiencia", value: "5+", hint: "años como fullstack", icon: <FaCode /> },
296
+ { label: "Tecnologías", value: `${TECH_STACK.length}`, hint: "en el stack", icon: <FaLayerGroup /> },
297
+ { label: "Productos", value: `${PRODUCTOS.length}`, hint: "propios y en uso", icon: <FaBoxOpen /> },
298
+ { label: "Modalidad", value: "Remoto", hint: "disponible ya", icon: <FaMapMarkerAlt /> },
65
299
  ];
66
300
 
67
301
  const categoriesList = [
@@ -73,324 +307,311 @@ export const HomeShowcase = () => {
73
307
  { id: "devops", name: "DevOps", icon: <FaCloud size={10} /> },
74
308
  ];
75
309
 
76
- const filteredTech = TECH_STACK.filter(tech => {
310
+ const filteredTech = TECH_STACK.filter((tech) => {
77
311
  const matchesSearch = tech.name.toLowerCase().includes(techSearch.toLowerCase());
78
312
  const matchesCategory = selectedCategory === "all" || tech.category === selectedCategory;
79
313
  return matchesSearch && matchesCategory;
80
314
  });
81
315
 
82
316
  return (
83
- <ITStack spacing={10}>
317
+ <ITStack spacing={10} className="animate-fadeIn overflow-x-clip pb-5">
84
318
  {/* ─── HERO ─── */}
85
- <ITCard className="overflow-hidden border-0 bg-gradient-to-br from-slate-50 via-white to-primary-50/20 dark:from-slate-950 dark:via-slate-900 dark:to-primary-950/10 shadow-sm">
86
- <div className="relative">
87
- <div className="absolute -top-40 -right-40 w-[500px] h-[500px] bg-primary-500/5 rounded-full blur-[120px] pointer-events-none" />
88
- <div className="absolute -bottom-40 -left-40 w-[500px] h-[500px] bg-purple-500/5 rounded-full blur-[120px] pointer-events-none" />
89
-
90
- <ITGrid container spacing={8} className="items-center relative z-10">
91
- {/* Photo */}
92
- <ITGrid item xs={12} md={5} className="flex justify-center md:justify-start">
93
- <ITStack spacing={4} alignItems="center">
94
- <div className="relative">
95
- <div className="w-56 h-56 md:w-64 md:h-64 rounded-full shadow-xl overflow-hidden ring-2 ring-slate-200 dark:ring-slate-700">
96
- <img
97
- src="/personalFoto.jpg"
98
- alt="Asael Amaro"
99
- className="w-full h-full object-cover"
100
- />
101
- </div>
319
+ <section
320
+ aria-labelledby="home-hero-title"
321
+ className="relative overflow-hidden rounded-3xl bg-gradient-to-br from-primary-50 via-white to-purple-50 px-8 py-14 dark:from-primary-950/30 dark:via-slate-900 dark:to-purple-950/30 sm:px-14"
322
+ >
323
+ <div aria-hidden className="it-landing-float pointer-events-none absolute -top-24 -right-16 h-72 w-72 rounded-full bg-primary-400/20 blur-3xl" />
324
+ <div
325
+ aria-hidden
326
+ className="it-landing-float pointer-events-none absolute -bottom-24 -left-16 h-72 w-72 rounded-full bg-purple-400/20 blur-3xl"
327
+ style={{ animationDelay: "1.5s" }}
328
+ />
329
+ <div aria-hidden className="it-landing-grid pointer-events-none absolute inset-0 text-slate-400/50 dark:text-slate-600/40" />
102
330
 
103
- </div>
104
- </ITStack>
105
- </ITGrid>
331
+ <div className="relative z-10 flex flex-col gap-12 xl:flex-row xl:items-center xl:gap-14">
332
+ <div className="min-w-0 max-w-3xl flex-1 xl:max-w-2xl">
333
+ <div className="it-landing-rise">
334
+ <ITBadget label="Portafolio · 2026" color="primary" variant="outlined" className="w-fit" />
335
+ </div>
336
+
337
+ <ITText
338
+ as="h1"
339
+ id="home-hero-title"
340
+ className="it-landing-rise !mt-5 !text-4xl !font-extrabold !tracking-tight text-slate-900 dark:text-white sm:!text-5xl"
341
+ style={{ animationDelay: "70ms" }}
342
+ >
343
+ Hola, soy{" "}
344
+ <span className="bg-gradient-to-r from-primary-600 via-primary-500 to-purple-500 bg-clip-text text-transparent">
345
+ Asael Amaro
346
+ </span>
347
+ </ITText>
348
+
349
+ <ITText
350
+ as="p"
351
+ className="it-landing-rise !mt-4 !text-base !leading-relaxed max-w-xl text-slate-600 dark:text-slate-300"
352
+ style={{ animationDelay: "140ms" }}
353
+ >
354
+ Fullstack Developer con 4+ años creando aplicaciones web modernas.
355
+ Especializado en TypeScript, Angular y React, con foco en arquitectura
356
+ limpia y en productos que la gente usa todos los días.
357
+ </ITText>
358
+
359
+ <ITFlex
360
+ gap={3}
361
+ wrap="wrap"
362
+ align="center"
363
+ className="it-landing-rise !mt-7"
364
+ style={{ animationDelay: "210ms" }}
365
+ >
366
+ <a href={CV_URL} download>
367
+ <ITButton variant="filled" color="primary" size="lg" icon={<FaDownload size={13} />}>
368
+ Descargar CV
369
+ </ITButton>
370
+ </a>
371
+ <a href={GITHUB_URL} target="_blank" rel="noopener noreferrer">
372
+ <ITButton variant="outlined" color="secondary" size="lg" icon={<FaGithub size={13} />}>
373
+ GitHub
374
+ </ITButton>
375
+ </a>
376
+ </ITFlex>
106
377
 
107
- {/* Text */}
108
- <ITGrid item xs={12} md={7}>
109
- <ITStack spacing={5}>
110
- <ITStack spacing={2}>
111
- <ITBadget label="Portafolio Personal" color="primary" variant="outlined" className="w-fit" />
112
- <ITText as="h1" className="text-4xl md:text-5xl font-bold tracking-tight text-slate-900 dark:text-white">
113
- Hola, soy <span className="text-primary-600 dark:text-primary-400">Asael Amaro</span>
378
+ <div
379
+ className="it-landing-rise mt-7 inline-flex flex-wrap items-center gap-x-3 gap-y-2 rounded-2xl border border-primary-200 bg-gradient-to-r from-primary-50 to-purple-50 px-4 py-2 shadow-sm dark:border-primary-800/50 dark:from-primary-950/30 dark:to-purple-950/30"
380
+ style={{ animationDelay: "280ms" }}
381
+ >
382
+ <span className="relative flex h-2 w-2 shrink-0">
383
+ <span className="absolute inline-flex h-full w-full rounded-full bg-success-400 opacity-75" />
384
+ <span className="relative inline-flex h-2 w-2 rounded-full bg-success-500" />
385
+ </span>
386
+ <code className="text-sm font-medium text-slate-800 dark:text-slate-200">
387
+ Disponible para proyectos fullstack
388
+ </code>
389
+ <a
390
+ href={LINKEDIN_URL}
391
+ target="_blank"
392
+ rel="noopener noreferrer"
393
+ className="inline-flex items-center gap-1.5 text-xs font-semibold text-primary-600 dark:text-primary-400"
394
+ >
395
+ <FaLinkedin size={12} />
396
+ LinkedIn
397
+ </a>
398
+ </div>
399
+ </div>
400
+
401
+ {/* Foto en tarjeta flotante */}
402
+ <div className="flex w-full shrink-0 justify-center xl:w-[300px] xl:justify-end">
403
+ <div
404
+ className="it-landing-float w-full max-w-[300px] overflow-hidden rounded-2xl border border-slate-200/80 bg-white/85 shadow-xl backdrop-blur-sm dark:border-slate-700/70 dark:bg-slate-900/85"
405
+ style={{ animationDelay: "0.8s" }}
406
+ >
407
+ <img
408
+ src="/personalFoto.jpg"
409
+ alt="Asael Amaro"
410
+ className="aspect-[4/5] w-full object-cover"
411
+ />
412
+ <div className="flex items-center justify-between gap-3 border-t border-slate-200/80 px-4 py-3.5 dark:border-slate-700/70">
413
+ <div>
414
+ <ITText as="p" className="!text-xs !font-bold text-slate-900 dark:text-white">
415
+ Fullstack Developer
114
416
  </ITText>
115
- <ITText as="p" muted className="text-base leading-relaxed max-w-xl">
116
- Fullstack Developer con 4+ años de experiencia creando aplicaciones web modernas.
117
- Especializado en TypeScript, Angular y React. Apasionado por la arquitectura limpia,
118
- el código escalable y las tecnologías emergentes.
417
+ <ITText as="p" className="!text-[11px] text-slate-400 dark:text-slate-500">
418
+ Remoto · México
119
419
  </ITText>
120
- </ITStack>
121
-
122
- {/* Stats */}
123
- <ITFlex gap={6} wrap="wrap">
124
- <ITFlex align="center" gap={3}>
125
- <div className="w-10 h-10 rounded-xl bg-primary-100 dark:bg-primary-950/40 flex items-center justify-center text-primary-600 dark:text-primary-400">
126
- <FaCode size={16} />
127
- </div>
128
- <div>
129
- <ITText as="div" className="text-lg font-bold text-slate-800 dark:text-white">4+</ITText>
130
- <ITText as="div" muted className="text-xs font-medium">Años exp.</ITText>
131
- </div>
132
- </ITFlex>
133
- <ITFlex align="center" gap={3}>
134
- <div className="w-10 h-10 rounded-xl bg-purple-100 dark:bg-purple-950/40 flex items-center justify-center text-purple-600 dark:text-purple-400">
135
- <FaMapMarkerAlt size={16} />
136
- </div>
137
- <div>
138
- <ITText as="div" className="text-lg font-bold text-slate-800 dark:text-white">Remoto</ITText>
139
- <ITText as="div" muted className="text-xs font-medium">Disponible</ITText>
140
- </div>
141
- </ITFlex>
142
- </ITFlex>
143
-
144
- {/* CTAs */}
145
- <ITFlex gap={2.5} wrap="wrap" align="center">
146
- <a href="/Martin_Amaro_CV_ES_2026.pdf" download>
147
- <ITButton variant="filled" color="primary" size="sm">
148
- <ITFlex align="center" gap={2}>
149
- <FaDownload size={12} />
150
- Descargar CV
151
- </ITFlex>
152
- </ITButton>
153
- </a>
154
- <ITText as="span" muted className="text-xs">|</ITText>
155
- <a href="https://github.com/maag070208" target="_blank" rel="noopener noreferrer">
156
- <ITButton variant="text" color="gray" size="sm">
157
- <ITFlex align="center" gap={1.5}>
158
- <FaGithub size={14} />
159
- GitHub
160
- </ITFlex>
161
- </ITButton>
162
- </a>
163
- <a href="https://www.linkedin.com/in/maag070208/" target="_blank" rel="noopener noreferrer">
164
- <ITButton variant="text" color="gray" size="sm">
165
- <ITFlex align="center" gap={1.5}>
166
- <FaLinkedin size={14} />
167
- LinkedIn
168
- </ITFlex>
169
- </ITButton>
170
- </a>
171
- <a href="https://medium.com/@axzydev" target="_blank" rel="noopener noreferrer">
172
- <ITButton variant="text" color="gray" size="sm">
173
- <ITFlex align="center" gap={1.5}>
174
- <FaMedium size={14} />
175
- Medium
176
- </ITFlex>
177
- </ITButton>
178
- </a>
179
- </ITFlex>
180
- </ITStack>
181
- </ITGrid>
182
- </ITGrid>
420
+ </div>
421
+ <span className="inline-flex shrink-0 items-center gap-1.5 rounded-full border border-success-500/40 px-2.5 py-1 text-[10px] font-semibold text-success-600 dark:text-success-400">
422
+ <span className="h-1.5 w-1.5 rounded-full bg-success-500" />
423
+ Disponible
424
+ </span>
425
+ </div>
426
+ </div>
427
+ </div>
183
428
  </div>
184
- </ITCard>
185
-
186
- {/* ─── SKILLS ─── */}
187
- <ITGrid container spacing={6}>
188
- <ITGrid item xs={12} md={6}>
189
- <ITCard title="Lenguajes" className="border-slate-200/60 dark:border-slate-800/60 shadow-sm">
190
- <ITStack spacing={4}>
191
- <ITGrid container spacing={3}>
429
+ </section>
430
+
431
+ {/* ─── MÉTRICAS ─── */}
432
+ <section aria-label="Métricas del perfil">
433
+ <ITGrid container spacing={4}>
434
+ {STATS.map((stat, index) => (
435
+ <ITGrid item xs={6} lg={3} key={stat.label}>
436
+ <StatCard stat={stat} index={index} />
437
+ </ITGrid>
438
+ ))}
439
+ </ITGrid>
440
+ </section>
441
+
442
+ {/* ─── LENGUAJES Y FRAMEWORKS ─── */}
443
+ <section>
444
+ <SectionHeading
445
+ eyebrow="Base técnica"
446
+ title="Lenguajes y frameworks"
447
+ subtitle="Con los que trabajo todos los días."
448
+ className="!mb-5"
449
+ />
450
+
451
+ <ITGrid container spacing={5}>
452
+ <ITGrid item xs={12} lg={6}>
453
+ <div className="h-full rounded-2xl border border-slate-200 bg-white p-5 shadow-sm dark:border-slate-800 dark:bg-slate-900">
454
+ <ITText as="h3" className="!text-[11px] !font-bold uppercase !tracking-wider text-slate-400 dark:text-slate-500">
455
+ Lenguajes
456
+ </ITText>
457
+ <div className="mt-4 grid grid-cols-3 gap-3 sm:grid-cols-6 lg:grid-cols-3">
192
458
  {LANGUAGES.map((lang) => (
193
- <ITGrid item xs={4} sm={4} key={lang.name}>
194
- <div className="group flex flex-col items-center gap-2.5 p-3.5 rounded-xl bg-white/80 dark:bg-slate-900/60 border border-slate-100 dark:border-slate-800 hover:border-primary-400/40 hover:shadow-md hover:-translate-y-0.5 transition-all duration-200">
195
- <div className="w-10 h-10 flex items-center justify-center rounded-lg bg-slate-50 dark:bg-slate-950/40 border border-slate-100 dark:border-slate-800/80 group-hover:scale-110 transition-transform duration-200">
196
- <img src={lang.icon} alt={lang.name} className="w-7 h-7 object-contain" />
197
- </div>
198
- <ITText as="span" className="text-[11px] font-semibold text-slate-600 dark:text-slate-300 text-center">
199
- {lang.name}
200
- </ITText>
201
- </div>
202
- </ITGrid>
459
+ <TechTile key={lang.name} icon={lang.icon} label={lang.name} />
203
460
  ))}
204
- </ITGrid>
205
- </ITStack>
206
- </ITCard>
207
- </ITGrid>
461
+ </div>
462
+ </div>
463
+ </ITGrid>
208
464
 
209
- <ITGrid item xs={12} md={6}>
210
- <ITCard title="Frameworks" className="border-slate-200/60 dark:border-slate-800/60 shadow-sm">
211
- <ITStack spacing={4}>
212
- <ITGrid container spacing={3}>
465
+ <ITGrid item xs={12} lg={6}>
466
+ <div className="h-full rounded-2xl border border-slate-200 bg-white p-5 shadow-sm dark:border-slate-800 dark:bg-slate-900">
467
+ <ITText as="h3" className="!text-[11px] !font-bold uppercase !tracking-wider text-slate-400 dark:text-slate-500">
468
+ Frameworks
469
+ </ITText>
470
+ <div className="mt-4 grid grid-cols-3 gap-3 sm:grid-cols-4 lg:grid-cols-4">
213
471
  {FRAMEWORKS.map((fw) => (
214
- <ITGrid item xs={4} sm={4} key={fw.name}>
215
- <div className="group flex flex-col items-center gap-2.5 p-3.5 rounded-xl bg-white/80 dark:bg-slate-900/60 border border-slate-100 dark:border-slate-800 hover:border-primary-400/40 hover:shadow-md hover:-translate-y-0.5 transition-all duration-200">
216
- <div className="w-10 h-10 flex items-center justify-center rounded-lg bg-slate-50 dark:bg-slate-950/40 border border-slate-100 dark:border-slate-800/80 group-hover:scale-110 transition-transform duration-200">
217
- <img src={fw.icon} alt={fw.name} className="w-7 h-7 object-contain" />
218
- </div>
219
- <ITText as="span" className="text-[11px] font-semibold text-slate-600 dark:text-slate-300 text-center">
220
- {fw.name}
221
- </ITText>
222
- </div>
223
- </ITGrid>
472
+ <TechTile key={fw.name} icon={fw.icon} label={fw.name} />
224
473
  ))}
225
- </ITGrid>
226
- </ITStack>
227
- </ITCard>
474
+ </div>
475
+ </div>
476
+ </ITGrid>
228
477
  </ITGrid>
229
- </ITGrid>
478
+ </section>
230
479
 
231
- {/* ─── TECH STACK ─── */}
232
- <ITCard
233
- title="Stack Tecnológico"
234
- className="border-slate-200/60 dark:border-slate-800/60 shadow-sm"
235
- >
236
- <ITStack spacing={5}>
237
- {/* Filters */}
238
- <ITFlex align="center" justify="between" wrap="wrap" gap={4}>
239
- <ITFlex gap={2} wrap="wrap">
240
- {categoriesList.map((cat) => (
241
- <button
242
- key={cat.id}
243
- onClick={() => setSelectedCategory(cat.id)}
244
- className={`inline-flex items-center gap-1.5 px-3.5 py-1.5 rounded-lg text-xs font-semibold transition-all ${
245
- selectedCategory === cat.id
246
- ? "bg-primary-100 dark:bg-primary-900/40 text-primary-700 dark:text-primary-300 shadow-sm border border-primary-200 dark:border-primary-800"
247
- : "bg-slate-100 dark:bg-slate-800 hover:bg-slate-200 dark:hover:bg-slate-700 text-slate-500 dark:text-slate-400 border border-transparent"
248
- }`}
249
- >
250
- {cat.icon}
251
- {cat.name}
252
- </button>
253
- ))}
254
- </ITFlex>
255
- <div className="relative w-full sm:w-56">
256
- <FaSearch className="absolute left-3 top-1/2 -translate-y-1/2 text-slate-400" size={12} />
257
- <input
258
- type="text"
259
- placeholder="Buscar tecnología..."
260
- value={techSearch}
261
- onChange={(e) => setTechSearch(e.target.value)}
262
- className="w-full bg-white dark:bg-slate-900/80 text-slate-800 dark:text-white pl-8 pr-4 py-2 text-xs rounded-xl outline-none focus:ring-2 focus:ring-primary-500/50 border border-slate-200 dark:border-slate-700 transition-all"
263
- />
264
- </div>
480
+ {/* ─── STACK TECNOLÓGICO ─── */}
481
+ <section>
482
+ <SectionHeading
483
+ eyebrow="Stack"
484
+ title="Tecnologías que uso"
485
+ subtitle="Filtra por área o busca por nombre."
486
+ className="!mb-5"
487
+ />
488
+
489
+ <ITFlex align="center" justify="between" wrap="wrap" gap={4} className="!mb-6">
490
+ <ITFlex gap={2} wrap="wrap">
491
+ {categoriesList.map((cat) => (
492
+ <button
493
+ key={cat.id}
494
+ type="button"
495
+ onClick={() => setSelectedCategory(cat.id)}
496
+ className={`inline-flex items-center gap-1.5 rounded-lg px-3.5 py-1.5 text-xs font-semibold transition-all ${
497
+ selectedCategory === cat.id
498
+ ? "bg-primary-500 text-white shadow-sm"
499
+ : "bg-slate-100 text-slate-500 hover:bg-slate-200 dark:bg-slate-800 dark:text-slate-400 dark:hover:bg-slate-700"
500
+ }`}
501
+ >
502
+ {cat.icon}
503
+ {cat.name}
504
+ </button>
505
+ ))}
265
506
  </ITFlex>
266
507
 
267
- {/* Divider */}
268
- <ITDivider />
269
-
270
- {/* Tech Grid */}
271
- {filteredTech.length > 0 ? (
272
- <ITGrid container spacing={2}>
273
- {filteredTech.map((tech, index) => (
274
- <ITGrid item xs={3} sm={2} md={2} lg={1} key={`${tech.name}-${index}`}>
275
- <div className="group flex flex-col items-center gap-2 p-3 rounded-xl bg-white/60 dark:bg-slate-900/40 border border-slate-100 dark:border-slate-800/50 hover:border-primary-400/30 hover:shadow-md hover:-translate-y-1 transition-all duration-200 cursor-pointer">
276
- <div className="w-10 h-10 flex items-center justify-center rounded-lg bg-white dark:bg-slate-900 border border-slate-100 dark:border-slate-800/60 group-hover:scale-110 group-hover:rotate-3 transition-all duration-200">
277
- <img
278
- src={tech.icon}
279
- alt={tech.name}
280
- className="w-7 h-7 object-contain"
281
- />
282
- </div>
283
- <ITText as="span" className="text-[9px] font-semibold text-slate-500 dark:text-slate-400 text-center truncate w-full group-hover:text-slate-700 dark:group-hover:text-slate-200 transition-colors">
284
- {tech.name}
285
- </ITText>
286
- </div>
287
- </ITGrid>
288
- ))}
289
- </ITGrid>
290
- ) : (
291
- <div className="text-center py-12">
292
- <ITText muted className="text-sm">No se encontraron tecnologías para "{techSearch}"</ITText>
293
- </div>
294
- )}
508
+ <div className="relative w-full sm:w-64">
509
+ <FaSearch className="pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-slate-400" size={12} />
510
+ <input
511
+ type="text"
512
+ placeholder="Buscar tecnología..."
513
+ value={techSearch}
514
+ onChange={(e) => setTechSearch(e.target.value)}
515
+ className="w-full rounded-xl border border-slate-200 bg-white py-2 pl-8 pr-4 text-xs text-slate-800 outline-none transition-all focus:border-primary-400 focus:ring-2 focus:ring-primary-500/30 dark:border-slate-700 dark:bg-slate-900 dark:text-white"
516
+ />
517
+ </div>
518
+ </ITFlex>
295
519
 
296
- {/* Footer count */}
297
- <ITDivider />
298
- <ITText as="div" muted className="text-xs text-center">
299
- {filteredTech.length} de {TECH_STACK.length} tecnologías
300
- </ITText>
301
- </ITStack>
302
- </ITCard>
520
+ {filteredTech.length > 0 ? (
521
+ <ITGrid container spacing={3}>
522
+ {filteredTech.map((tech, index) => (
523
+ <ITGrid item xs={4} sm={3} md={2} lg={2} key={`${tech.name}-${index}`}>
524
+ <TechTile icon={tech.icon} label={tech.name} />
525
+ </ITGrid>
526
+ ))}
527
+ </ITGrid>
528
+ ) : (
529
+ <div className="rounded-2xl border border-slate-200 bg-white py-12 text-center shadow-sm dark:border-slate-800 dark:bg-slate-900">
530
+ <ITText as="p" className="!text-sm text-slate-500 dark:text-slate-400">
531
+ No se encontraron tecnologías para “{techSearch}”.
532
+ </ITText>
533
+ </div>
534
+ )}
303
535
 
304
- {/* ─── MIS PRODUCTOS ─── */}
305
- <ITCard
306
- title="Mis Productos"
307
- className="border-slate-200/60 dark:border-slate-800/60 shadow-sm"
308
- >
309
- <ITText as="p" muted className="!text-sm !mb-4">
310
- Productos destacados que he construido.
536
+ <ITDivider className="!my-6" />
537
+ <ITText as="p" className="!text-xs text-center text-slate-400 dark:text-slate-500">
538
+ {filteredTech.length} de {TECH_STACK.length} tecnologías
311
539
  </ITText>
540
+ </section>
541
+
542
+ {/* ─── PRODUCTOS ─── */}
543
+ <section>
544
+ <SectionHeading
545
+ eyebrow="Portafolio"
546
+ title="Productos que construí"
547
+ subtitle="Los que tienen capturas se abren en su pantalla de detalle."
548
+ className="!mb-5"
549
+ />
550
+
312
551
  <ITGrid container spacing={5}>
313
- {/* AXZY UI System */}
314
- <ITGrid item xs={12} md={6}>
552
+ {PRODUCTOS.map((producto) => (
553
+ <ITGrid item xs={12} md={6} key={producto.id}>
554
+ <TarjetaProducto producto={producto} onVerDetalle={setDetalle} />
555
+ </ITGrid>
556
+ ))}
557
+ </ITGrid>
558
+ </section>
559
+
560
+ {/* ─── CIERRE ─── */}
561
+ <section className="relative overflow-hidden rounded-3xl border border-primary-200 bg-gradient-to-br from-primary-50 via-white to-purple-50 p-6 shadow-sm dark:border-primary-900/40 dark:from-primary-950/30 dark:via-slate-900 dark:to-purple-950/30 sm:p-10">
562
+ <div aria-hidden className="it-landing-float pointer-events-none absolute -top-20 -right-16 h-56 w-56 rounded-full bg-primary-400/20 blur-3xl" />
563
+ <div aria-hidden className="it-landing-grid pointer-events-none absolute inset-0 text-slate-400/40 dark:text-slate-600/30" />
564
+
565
+ <div className="relative z-10">
566
+ <div className="flex flex-col gap-6 lg:flex-row lg:items-start lg:justify-between lg:gap-10">
567
+ <SectionHeading
568
+ eyebrow="Siguientes pasos"
569
+ title="¿Trabajamos juntos?"
570
+ subtitle="Estoy disponible para proyectos fullstack, de producto o de diseño de sistemas."
571
+ />
572
+
573
+ <ITFlex gap={3} wrap="wrap" align="center" className="shrink-0">
574
+ <a href={CV_URL} download>
575
+ <ITButton variant="filled" color="primary" icon={<FaDownload size={13} />}>
576
+ Descargar CV
577
+ </ITButton>
578
+ </a>
579
+ <a href={LINKEDIN_URL} target="_blank" rel="noopener noreferrer">
580
+ <ITButton variant="outlined" color="secondary" icon={<FaLinkedin size={13} />}>
581
+ LinkedIn
582
+ </ITButton>
583
+ </a>
584
+ </ITFlex>
585
+ </div>
586
+
587
+ <ITText as="p" className="!mt-6 !text-xs text-slate-500 dark:text-slate-400">
588
+ También escribo en{" "}
315
589
  <a
316
- href="#ui-system"
317
- className="group block h-full rounded-2xl border border-slate-200 dark:border-slate-800 bg-white dark:bg-slate-900/50 p-6 hover:border-primary-400/40 hover:shadow-lg hover:-translate-y-1 transition-all duration-200"
590
+ href={MEDIUM_URL}
591
+ target="_blank"
592
+ rel="noopener noreferrer"
593
+ className="inline-flex items-center gap-1 font-semibold text-primary-600 dark:text-primary-400"
318
594
  >
319
- <ITStack spacing={4} className="h-full" justifyContent="between">
320
- <ITStack spacing={3}>
321
- <ITFlex align="center" justify="between">
322
- <div className="w-12 h-12 rounded-xl bg-gradient-to-br from-primary-500 to-purple-500 text-white flex items-center justify-center shadow-md">
323
- <FaLayerGroup size={20} />
324
- </div>
325
- <ITBadget label="Propio" color="primary" variant="filled" />
326
- </ITFlex>
327
- <div>
328
- <ITText as="h3" className="!text-lg !font-bold flex items-center gap-2">
329
- AXZY UI System
330
- <FaArrowRight size={14} className="text-primary-500 opacity-0 -translate-x-1 group-hover:opacity-100 group-hover:translate-x-0 transition-all duration-200" />
331
- </ITText>
332
- <ITText as="p" muted className="!text-sm !leading-relaxed mt-1.5">
333
- Librería de componentes React con Tailwind CSS v4. Más de 50
334
- componentes enterprise con theming en runtime, TypeScript nativo y
335
- accesibilidad.
336
- </ITText>
337
- </div>
338
- <ITFlex gap={2} wrap="wrap">
339
- <ITBadget label="React" color="gray" variant="outlined" />
340
- <ITBadget label="TypeScript" color="gray" variant="outlined" />
341
- <ITBadget label="Tailwind" color="gray" variant="outlined" />
342
- <ITBadget label="npm" color="gray" variant="outlined" />
343
- </ITFlex>
344
- </ITStack>
345
- <ITText as="span" className="!text-xs !font-semibold !text-primary-600 dark:!text-primary-400 inline-flex items-center gap-1.5">
346
- <SiReact size={12} />
347
- Ver proyecto
348
- </ITText>
349
- </ITStack>
350
- </a>
351
- </ITGrid>
352
-
353
- {/* CheckApp */}
354
- <ITGrid item xs={12} md={6}>
595
+ <FaMedium size={11} />
596
+ Medium
597
+ </a>{" "}
598
+ y el código de mis productos está en{" "}
355
599
  <a
356
- href="https://checkapp.axzy.dev/"
600
+ href={GITHUB_URL}
357
601
  target="_blank"
358
602
  rel="noopener noreferrer"
359
- className="group block h-full rounded-2xl border border-slate-200 dark:border-slate-800 bg-white dark:bg-slate-900/50 p-6 hover:border-emerald-400/40 hover:shadow-lg hover:-translate-y-1 transition-all duration-200"
603
+ className="inline-flex items-center gap-1 font-semibold text-primary-600 dark:text-primary-400"
360
604
  >
361
- <ITStack spacing={4} className="h-full" justifyContent="between">
362
- <ITStack spacing={3}>
363
- <ITFlex align="center" justify="between">
364
- <div className="w-12 h-12 rounded-xl bg-gradient-to-br from-emerald-500 to-cyan-500 text-white flex items-center justify-center shadow-md">
365
- <FaCheck size={20} />
366
- </div>
367
- <ITBadget label="En vivo" color="gray" variant="outlined" />
368
- </ITFlex>
369
- <div>
370
- <ITText as="h3" className="!text-lg !font-bold flex items-center gap-2">
371
- CheckApp
372
- <FaExternalLinkAlt size={12} className="text-slate-400 opacity-0 -translate-x-1 group-hover:opacity-100 group-hover:translate-x-0 transition-all duration-200" />
373
- </ITText>
374
- <ITText as="p" muted className="!text-sm !leading-relaxed mt-1.5">
375
- Aplicación web para consultar información de manera rápida y
376
- sencilla. Desplegada y disponible en línea.
377
- </ITText>
378
- </div>
379
- <ITFlex gap={2} wrap="wrap">
380
- <ITBadget label="Node.js" color="gray" variant="outlined" />
381
- <ITBadget label="React" color="gray" variant="outlined" />
382
- <ITBadget label="Deploy" color="gray" variant="outlined" />
383
- </ITFlex>
384
- </ITStack>
385
- <ITText as="span" className="!text-xs !font-semibold !text-emerald-600 dark:!text-emerald-400 inline-flex items-center gap-1.5">
386
- <SiNodedotjs size={12} />
387
- Abrir CheckApp
388
- </ITText>
389
- </ITStack>
605
+ <FaGithub size={11} />
606
+ GitHub
390
607
  </a>
391
- </ITGrid>
392
- </ITGrid>
393
- </ITCard>
608
+ .
609
+ </ITText>
610
+ </div>
611
+ </section>
612
+
613
+ {/* Pantalla de detalle del producto elegido */}
614
+ {detalle && <ProductoDetalle producto={detalle} onClose={() => setDetalle(null)} />}
394
615
  </ITStack>
395
616
  );
396
617
  };