diff --git a/messages/en.json b/messages/en.json
index 330979e..5a04947 100644
--- a/messages/en.json
+++ b/messages/en.json
@@ -33,12 +33,12 @@
"heroAvailable": "Available for projects",
"heroStudioLabel": "Studio",
"heroServicesLabel": "Services",
- "heroBaseLabel": "Base · 2024",
+ "heroBaseLabel": "Base · 2025",
"heroGrabHint": "↑ grab the cards"
},
"about": {
"title": "About",
- "lede": "Software developer, founder of Directa. I build web products, automation and AI systems for teams that need both design and engineering from the same person.",
+ "lede": "Software developer and founder of Directa. I build web products, automations and AI systems for companies that want to grow with technology that is simple, fast and well designed.",
"focusHeading": "Current focus",
"focusBody": "Working on production web infrastructure for Spanish-speaking businesses, and on AI features that actually live inside the products their teams use every day.",
"approachHeading": "Approach",
@@ -47,6 +47,9 @@
"directaBody": "Directa is the studio I run. The work shown here is built through it.",
"experienceHeading": "Experience",
"educationHeading": "Education",
+ "certificationsHeading": "Certifications",
+ "honors": "Honors",
+ "viewOnCredly": "Verify on Credly ↗",
"downloadCv": "Download CV"
},
"work": {
diff --git a/messages/es.json b/messages/es.json
index 4e2978a..997944a 100644
--- a/messages/es.json
+++ b/messages/es.json
@@ -33,12 +33,12 @@
"heroAvailable": "Disponible para proyectos",
"heroStudioLabel": "Estudio",
"heroServicesLabel": "Servicios",
- "heroBaseLabel": "Base · 2024",
+ "heroBaseLabel": "Base · 2025",
"heroGrabHint": "↑ agarrá las tarjetas"
},
"about": {
"title": "Sobre mí",
- "lede": "Desarrollador de software y fundador de Directa. Construyo productos web, automatización y sistemas de IA para equipos que necesitan diseño e ingeniería desde la misma persona.",
+ "lede": "Desarrollador de software y fundador de Directa. Construyo productos web, automatizaciones y sistemas de IA para empresas que quieren crecer con tecnología simple, rápida y bien diseñada.",
"focusHeading": "Foco actual",
"focusBody": "Trabajando en infraestructura web de producción para negocios hispanohablantes, y en funcionalidades de IA que viven dentro de los productos que sus equipos usan todos los días.",
"approachHeading": "Cómo trabajo",
@@ -47,6 +47,9 @@
"directaBody": "Directa es el estudio que dirijo. El trabajo que se ve aquí pasa por ahí.",
"experienceHeading": "Experiencia",
"educationHeading": "Educación",
+ "certificationsHeading": "Certificaciones",
+ "honors": "Honores",
+ "viewOnCredly": "Verificar en Credly ↗",
"downloadCv": "Descargar CV"
},
"work": {
diff --git a/public/cv/santiago-rivera-cv.pdf b/public/cv/santiago-rivera-cv.pdf
index 6d38826..76a2b6a 100644
Binary files a/public/cv/santiago-rivera-cv.pdf and b/public/cv/santiago-rivera-cv.pdf differ
diff --git a/public/portrait/santiago.png b/public/portrait/santiago.png
new file mode 100644
index 0000000..5348b97
Binary files /dev/null and b/public/portrait/santiago.png differ
diff --git a/public/work/directa/01.png b/public/work/directa/01.png
new file mode 100644
index 0000000..750b9fb
Binary files /dev/null and b/public/work/directa/01.png differ
diff --git a/public/work/directa/02.png b/public/work/directa/02.png
new file mode 100644
index 0000000..b4844d0
Binary files /dev/null and b/public/work/directa/02.png differ
diff --git a/public/work/directa/03.png b/public/work/directa/03.png
new file mode 100644
index 0000000..971cc38
Binary files /dev/null and b/public/work/directa/03.png differ
diff --git a/public/work/directa/04.png b/public/work/directa/04.png
new file mode 100644
index 0000000..4336679
Binary files /dev/null and b/public/work/directa/04.png differ
diff --git a/public/work/directa/cover.png b/public/work/directa/cover.png
new file mode 100644
index 0000000..107b141
Binary files /dev/null and b/public/work/directa/cover.png differ
diff --git a/src/app/[locale]/about/page.tsx b/src/app/[locale]/about/page.tsx
index 6ba45b5..0b403d3 100644
--- a/src/app/[locale]/about/page.tsx
+++ b/src/app/[locale]/about/page.tsx
@@ -27,6 +27,8 @@ export default async function AboutPage({ params }: { params: Promise<{ locale:
const experience = unwrap(site.experience);
const education = unwrap(site.education);
+ const certifications = unwrap(site.certifications);
+ const credly = unwrap(site.social.credly);
const expPending = isPending(site.experience);
const eduPending = isPending(site.education);
const portrait = unwrap(site.person.portrait);
@@ -107,7 +109,7 @@ export default async function AboutPage({ params }: { params: Promise<{ locale:
{experience.map((exp, i) => (
-
- {exp.period}
+ {exp.period[l]}
{exp.role[l]}
{exp.org}
@@ -135,7 +137,7 @@ export default async function AboutPage({ params }: { params: Promise<{ locale:
{education.map((ed, i) => (
-
- {ed.period}
+ {ed.period[l]}
{ed.degree[l]}
{ed.org}
@@ -146,6 +148,48 @@ export default async function AboutPage({ params }: { params: Promise<{ locale:
)}
+
+
+
+
+
+ {t('certificationsHeading')}
+
+
+ {certifications.map((group) => (
+ -
+
+
{group.issuer}
+ {group.period ?
{group.period}
: null}
+
+
+ {group.items.map((item) => (
+ -
+ {item.name}
+ {item.year ? {item.year} : null}
+ {item.honors ? (
+
+ ★ {t('honors')}
+
+ ) : null}
+
+ ))}
+
+
+ ))}
+
+ {credly ? (
+
+
+ {t('viewOnCredly')}
+
+
+ ) : null}
+
+
);
}
diff --git a/src/app/[locale]/work/directa/page.tsx b/src/app/[locale]/work/directa/page.tsx
index b12c19c..52e016b 100644
--- a/src/app/[locale]/work/directa/page.tsx
+++ b/src/app/[locale]/work/directa/page.tsx
@@ -138,10 +138,10 @@ export default async function DirectaCase({ params }: { params: Promise<{ locale
{directa.media.gallery.map((g, i) => (
-
+
{g.pending ? (
-
{String(i + 1).padStart(2, '0')} · 4:3
+
{String(i + 1).padStart(2, '0')} · 16:9
) : (
diff --git a/src/content/directa.ts b/src/content/directa.ts
index 191a508..c2bacfd 100644
--- a/src/content/directa.ts
+++ b/src/content/directa.ts
@@ -1,70 +1,72 @@
/**
* Directa case study content.
*
- * Only the live URL is treated as verified — everything else is marked as
- * pending until Santiago confirms the source material. Replace the values
- * (and remove `pending: true`) once each block is verified.
+ * Copy is grounded in the live site (directa.mx) and Santiago's verified
+ * background — no invented clients, prices or metrics. Wrap any field whose
+ * content is not yet confirmed with `todo(...)`; confirmed fields use `ok(...)`
+ * and render without a "pending" badge.
*/
export type LocaleStr = { en: string; es: string };
export type PendingBlock
= { value: T; pending?: boolean };
-const todo = (value: T): PendingBlock => ({ value, pending: true });
+const ok = (value: T): PendingBlock => ({ value, pending: false });
export const directa = {
slug: 'directa',
name: 'Directa',
url: 'https://directa.mx',
- year: todo({ en: '2024 — present', es: '2024 — presente' }),
- role: todo({
- en: 'Founder, product designer and lead developer',
- es: 'Fundador, diseñador de producto y desarrollador principal',
+ year: ok({ en: '2025 — present', es: '2025 — presente' }),
+ role: ok({
+ en: 'Founder & full-stack engineer',
+ es: 'Fundador e ingeniero full-stack',
}),
- context: todo({
- en: 'Directa is the services studio Santiago runs. It packages the work he already does — web products, automation, AI integration — under a single brand for Spanish-speaking clients.',
- es: 'Directa es el estudio de servicios que dirige Santiago. Empaqueta el trabajo que ya hace — productos web, automatización, integración de IA — bajo una misma marca para clientes hispanohablantes.',
+ context: ok({
+ en: 'Directa is the digital agency Santiago founded and runs. It packages the work he already does — websites, e-commerce and automation — into a single team a business can hire for its whole digital presence, instead of coordinating a separate designer, developer and agency.',
+ es: 'Directa es la agencia digital que Santiago fundó y dirige. Empaqueta el trabajo que ya hace — sitios web, e-commerce y automatización — en un solo equipo que un negocio puede contratar para toda su presencia digital, en lugar de coordinar por separado a un diseñador, un desarrollador y una agencia.',
}),
- problem: todo({
- en: 'Small and mid-size operators in Mexico need modern web infrastructure but rarely find one team that can both design and ship it. Agencies design. Freelancers build. Few do both with the same standard.',
- es: 'Operadores pequeños y medianos en México necesitan infraestructura web moderna, pero rara vez encuentran un solo equipo que diseñe y construya con el mismo estándar.',
+ problem: ok({
+ en: 'Small and mid-size businesses in Mexico need modern web infrastructure, but the market splits the work: agencies design, freelancers build, template shops hand over something the owner still has to finish. Few deliver a site that is ready to operate and sell from day one.',
+ es: 'Los negocios pequeños y medianos en México necesitan infraestructura web moderna, pero el mercado parte el trabajo: las agencias diseñan, los freelancers programan y las fábricas de plantillas entregan algo que el dueño todavía tiene que terminar. Pocos entregan un sitio listo para operar y vender desde el primer día.',
}),
- strategy: todo({
- en: 'Position Directa around outcomes rather than deliverables. Each engagement starts with the business problem, ends with a system the client can operate, and stays small enough to ship fast.',
- es: 'Posicionar a Directa por resultados, no por entregables. Cada proyecto arranca con el problema del negocio, termina con un sistema que el cliente puede operar y se mantiene compacto para entregar rápido.',
+ strategy: ok({
+ en: 'Position Directa as one team for the whole digital presence, sold by outcome rather than deliverable. Every engagement starts from the business goal — more bookings, more orders, more clients — and ends with a system the owner can actually run.',
+ es: 'Posicionar a Directa como un solo equipo para toda la presencia digital, vendido por resultado y no por entregable. Cada proyecto arranca desde la meta del negocio — más reservas, más pedidos, más clientes — y termina con un sistema que el dueño realmente puede operar.',
}),
- implementation: todo({
- en: 'Built on a modern web stack: Next.js, Tailwind, edge deploys, with custom integrations into CRM, email and AI workflows as projects require.',
- es: 'Construido sobre un stack web moderno: Next.js, Tailwind, despliegues en el edge, con integraciones a medida con CRM, correo y flujos de IA según lo que cada proyecto requiera.',
+ implementation: ok({
+ en: 'Built on a modern, maintainable stack — TypeScript, Next.js, Tailwind and edge deploys — with e-commerce, SEO and workflow automation wired in per project. Santiago owns the full cycle: scoping, design direction, development, deployment and ongoing maintenance.',
+ es: 'Construido sobre un stack moderno y mantenible — TypeScript, Next.js, Tailwind y despliegues en el edge — con e-commerce, SEO y automatización de flujos integrados según cada proyecto. Santiago es dueño del ciclo completo: alcance, dirección de diseño, desarrollo, despliegue y mantenimiento.',
}),
- design: todo({
- en: 'Direct typography, generous whitespace, restrained motion. The brand visibly avoids the over-decorated agency look common in the local market.',
- es: 'Tipografía directa, generosos espacios en blanco, motion contenido. La marca evita visiblemente el look sobrecargado de agencia común en el mercado local.',
+ design: ok({
+ en: 'A bold, high-contrast brand voice — "built to sell more" — that stays legible and fast. Direct typography, clear hierarchy and restrained motion, deliberately avoiding the over-decorated agency look common in the local market.',
+ es: 'Una voz de marca directa y de alto contraste — "built to sell more" — que se mantiene legible y rápida. Tipografía directa, jerarquía clara y motion contenido, evitando a propósito el look sobrecargado de agencia común en el mercado local.',
}),
features: [
- todo({ en: 'Service-led marketing site', es: 'Sitio de marketing orientado a servicios' }),
- todo({ en: 'Bilingual content infrastructure', es: 'Infraestructura de contenido bilingüe' }),
- todo({ en: 'Editorial case-study layouts', es: 'Layouts editoriales para casos de estudio' }),
- todo({ en: 'Contact and intake flows', es: 'Flujos de contacto y captación' }),
+ ok({ en: 'Websites & e-commerce built to convert', es: 'Sitios web y e-commerce hechos para convertir' }),
+ ok({ en: 'Brand, SEO and blog content', es: 'Marca, SEO y contenido de blog' }),
+ ok({ en: 'Bilingual (ES / EN) content infrastructure', es: 'Infraestructura de contenido bilingüe (ES / EN)' }),
+ ok({ en: 'Client portal + book-a-call intake', es: 'Portal de cliente y captación con reserva de llamada' }),
],
- outcome: todo({
- en: 'A live business identity Santiago uses as the front door for new client work. Operational metrics are intentionally not published.',
- es: 'Una identidad de negocio activa que Santiago usa como puerta de entrada para nuevos proyectos. Las métricas operativas no se publican intencionalmente.',
+ outcome: ok({
+ en: 'A live business identity that Santiago runs as the front door for new client work. Operational metrics are not published for now.',
+ es: 'Una identidad de negocio activa que Santiago opera como la puerta de entrada para nuevos proyectos. Las métricas operativas no se publican por ahora.',
}),
- /** Media — replace these paths once screenshots are dropped in /public/work/directa/ */
+ /** Media — real captures of directa.mx (cover + gallery), in /public/work/directa/. */
media: {
- cover: todo('/work/directa/cover.jpg'),
+ cover: ok('/work/directa/cover.png'),
gallery: [
- todo('/work/directa/01.jpg'),
- todo('/work/directa/02.jpg'),
- todo('/work/directa/03.jpg'),
+ ok('/work/directa/01.png'),
+ ok('/work/directa/02.png'),
+ ok('/work/directa/03.png'),
+ ok('/work/directa/04.png'),
],
},
} as const;
diff --git a/src/content/site.ts b/src/content/site.ts
index 2295d05..c1f3dff 100644
--- a/src/content/site.ts
+++ b/src/content/site.ts
@@ -22,12 +22,12 @@ export const site = {
name: 'Santiago Rivera',
/** Concise, evidence-based positioning. Adjust once CV is finalized. */
role: {
- en: todo('Software developer building web products, automation and AI systems'),
- es: todo('Desarrollador de software construyendo productos web, automatización y sistemas de IA'),
+ en: ok('Full-stack software engineer building web products, automation and AI systems'),
+ es: ok('Ingeniero de software full-stack: productos web, automatización y sistemas de IA'),
},
location: ok({ en: 'San Pedro Garza García, MX', es: 'San Pedro Garza García, MX' }),
/** Path to the portrait. Replace with the real headshot. */
- portrait: todo('/portrait/santiago.jpg', 'Drop the headshot in /public/portrait/'),
+ portrait: ok('/portrait/santiago.png'),
portraitAlt: {
en: 'Portrait of Santiago Rivera',
es: 'Retrato de Santiago Rivera',
@@ -50,7 +50,9 @@ export const site = {
social: {
linkedin: ok('https://www.linkedin.com/in/santiagoxriv/'),
github: ok('https://github.com/1816x'),
- x: todo('', 'Optional — leave empty to hide'),
+ x: ok('https://x.com/1816z'),
+ /** Public Credly badge wallet — verifiable certifications. */
+ credly: ok('https://www.credly.com/users/eccoed/badges'),
},
/**
@@ -166,7 +168,7 @@ export const site = {
slug: 'directa',
featured: true,
name: 'Directa',
- year: { value: '2024 — present', pending: true },
+ year: { value: '2025 — present', pending: false },
url: 'https://directa.mx',
summary: {
en: 'Founder-led services studio: web, automation and AI for Mexican businesses.',
@@ -253,22 +255,120 @@ export const site = {
* Experience — populate from the verified CV.
* Until then, the Experience section renders a clear "pending" state.
*/
- experience: todo<
+ experience: ok<
Array<{
role: { en: string; es: string };
org: string;
- period: string;
+ period: { en: string; es: string };
summary: { en: string; es: string };
}>
- >([], 'Populate from CV when finalized'),
+ >([
+ {
+ role: { en: 'Founder & Full-Stack Engineer', es: 'Fundador e ingeniero full-stack' },
+ org: 'Directa',
+ period: { en: '2025 — present', es: '2025 — presente' },
+ summary: {
+ en: 'Founded and run a done-for-you digital agency for small businesses — designing, building and shipping web products, e-commerce and workflow automation on a TypeScript / Next.js stack, and owning the full delivery cycle from scoping to maintenance.',
+ es: 'Fundé y dirijo una agencia digital llave en mano para pequeñas empresas — diseño, construyo y despliego productos web, e-commerce y automatización de flujos sobre un stack de TypeScript / Next.js, y soy dueño del ciclo completo, del alcance al mantenimiento.',
+ },
+ },
+ {
+ role: { en: 'Intern', es: 'Practicante' },
+ org: 'CEMEX',
+ period: { en: '2026 — present', es: '2026 — presente' },
+ summary: {
+ en: 'Support digital and technical workflows at a global building-materials company while completing my engineering degree.',
+ es: 'Apoyo flujos de trabajo digitales y técnicos en una empresa global de materiales de construcción mientras curso mi carrera de ingeniería.',
+ },
+ },
+ {
+ role: { en: 'Systems & Web Manager', es: 'Encargado de sistemas y web' },
+ org: 'Moreno Diesel',
+ period: { en: '2023 — present', es: '2023 — presente' },
+ summary: {
+ en: "Manage the company's online store, website infrastructure and electronic-invoicing systems, implementing backend improvements and system integrations that keep daily operations running.",
+ es: 'Gestiono la tienda en línea, la infraestructura del sitio y los sistemas de facturación electrónica de la empresa, implementando mejoras de backend e integraciones que sostienen la operación diaria.',
+ },
+ },
+ {
+ role: { en: 'Developer', es: 'Desarrollador' },
+ org: 'Arwen — Discord bot',
+ period: { en: '2021 — 2023', es: '2021 — 2023' },
+ summary: {
+ en: 'Designed and maintained a large-scale all-in-one Discord bot for automation, moderation and monetization used by active communities, integrating social, gaming and cryptocurrency APIs.',
+ es: 'Diseñé y mantuve un bot de Discord todo-en-uno a gran escala para automatización, moderación y monetización usado por comunidades activas, integrando APIs sociales, de gaming y de criptomonedas.',
+ },
+ },
+ ]),
- education: todo<
+ education: ok<
Array<{
degree: { en: string; es: string };
org: string;
- period: string;
+ period: { en: string; es: string };
}>
- >([], 'Populate from CV when finalized'),
+ >([
+ {
+ degree: { en: 'B.S. in Software Development Engineering', es: 'Ing. en Desarrollo de Software' },
+ org: 'Universidad Tecmilenio, Campus Las Torres',
+ period: { en: '2023 — 2027 (expected)', es: '2023 — 2027 (en curso)' },
+ },
+ {
+ degree: { en: 'High-school diploma — Bilingual program', es: 'Bachillerato — Programa bilingüe' },
+ org: 'CIDEB — Centro de Investigación y Desarrollo en Educación Bilingüe',
+ period: { en: '2020 — 2022', es: '2020 — 2022' },
+ },
+ ]),
+
+ /**
+ * Certifications & credentials — verified from the CV, grouped by issuer.
+ * `honors` marks a distinction; the public Credly wallet lives in
+ * `social.credly`. Add or remove entries here; the About section adapts.
+ */
+ certifications: ok<
+ Array<{
+ issuer: string;
+ period?: string;
+ items: Array<{ name: string; year?: string; honors?: boolean }>;
+ }>
+ >([
+ {
+ issuer: 'Universidad Tecmilenio',
+ period: '2025 — 2026',
+ items: [
+ { name: 'Full-Stack Development' },
+ { name: 'Cloud Computing' },
+ { name: 'Data Science' },
+ { name: 'Databases' },
+ { name: 'Data Structures', honors: true },
+ { name: 'Object-Oriented Programming' },
+ { name: 'Operating Systems', honors: true },
+ { name: 'Network Management', honors: true },
+ { name: 'Agile Methodologies', honors: true },
+ { name: 'Probability & Statistics for Data Science' },
+ { name: 'Programming Fundamentals', honors: true },
+ { name: 'Upper-Intermediate English', honors: true },
+ ],
+ },
+ {
+ issuer: 'AWS',
+ items: [{ name: 'AWS Academy Graduate — Cloud Foundations', year: '2024' }],
+ },
+ {
+ issuer: 'Cisco',
+ items: [
+ { name: 'Networking Basics', year: '2025' },
+ { name: 'Python Essentials 1', year: '2024' },
+ ],
+ },
+ {
+ issuer: 'IBM',
+ items: [
+ { name: 'Web Development Fundamentals — SkillsBuild', year: '2025' },
+ { name: 'Data Visualization with R', year: '2024' },
+ ],
+ },
+ ]),
/** Analytics hooks read from env vars; null if unset. */
analytics: {
diff --git a/src/lib/seo.ts b/src/lib/seo.ts
index 9a7f19b..ea1106a 100644
--- a/src/lib/seo.ts
+++ b/src/lib/seo.ts
@@ -59,7 +59,12 @@ export function personJsonLd(locale: 'en' | 'es') {
'@type': 'Person',
name: site.person.name,
url: base,
- sameAs: [site.social.linkedin.value, site.social.github.value, site.social.x.value].filter(Boolean),
+ sameAs: [
+ site.social.linkedin.value,
+ site.social.github.value,
+ site.social.x.value,
+ site.social.credly.value,
+ ].filter(Boolean),
// Only emit a verified job title — JSON-LD has no "pending" badge, so an
// unconfirmed role would ship as a hard claim.
...(isPending(role) ? {} : { jobTitle: unwrap(role) }),