diff --git a/public/favicon.ico.txt b/public/favicon.ico.txt
new file mode 100644
index 0000000..bc25832
--- /dev/null
+++ b/public/favicon.ico.txt
@@ -0,0 +1 @@
+Placeholder for favicon - use favicon.io or other generator to create a comprehensive favicon set.
\ No newline at end of file
diff --git a/public/images/avatar.jpg.txt b/public/images/avatar.jpg.txt
new file mode 100644
index 0000000..7489769
--- /dev/null
+++ b/public/images/avatar.jpg.txt
@@ -0,0 +1 @@
+Placeholder for avatar - replace with real photo
\ No newline at end of file
diff --git a/public/images/og-default.jpg.txt b/public/images/og-default.jpg.txt
new file mode 100644
index 0000000..c897c6a
--- /dev/null
+++ b/public/images/og-default.jpg.txt
@@ -0,0 +1 @@
+Placeholder for OG image - replace with a 1200x630px image
\ No newline at end of file
diff --git a/public/robots.txt b/public/robots.txt
new file mode 100644
index 0000000..707fce5
--- /dev/null
+++ b/public/robots.txt
@@ -0,0 +1,8 @@
+User-agent: *
+Allow: /
+
+Sitemap: https://davidaragon.impresion3d.pro/sitemap-index.xml
+
+# Disallow admin/private areas (if any)
+Disallow: /admin/
+Disallow: /api/
diff --git a/public/site.webmanifest b/public/site.webmanifest
new file mode 100644
index 0000000..0c111eb
--- /dev/null
+++ b/public/site.webmanifest
@@ -0,0 +1,21 @@
+{
+ "name": "David Aragón - Indie Builder",
+ "short_name": "DA Portfolio",
+ "description": "Portfolio personal y blog de indie builder español",
+ "start_url": "/",
+ "display": "standalone",
+ "background_color": "#0a0e27",
+ "theme_color": "#60a5fa",
+ "icons": [
+ {
+ "src": "/android-chrome-192x192.png",
+ "sizes": "192x192",
+ "type": "image/png"
+ },
+ {
+ "src": "/android-chrome-512x512.png",
+ "sizes": "512x512",
+ "type": "image/png"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/components/layout/Footer.astro b/src/components/layout/Footer.astro
new file mode 100644
index 0000000..49ca1e1
--- /dev/null
+++ b/src/components/layout/Footer.astro
@@ -0,0 +1,39 @@
+---
+const currentYear = new Date().getFullYear();
+const socialLinks = [
+ { href: 'https://twitter.com/davidaragon', label: 'X', icon: 'X' },
+ { href: 'https://linkedin.com/in/davidaragon', label: 'LinkedIn', icon: 'in' },
+ { href: 'https://github.com/davidaragon', label: 'GitHub', icon: 'GH' },
+];
+---
+
+
+
+
+
+ © {currentYear} David Aragón. Construyendo en público.
+
+
+
+
+
+ RSS Feed
+
+
+
+
\ No newline at end of file
diff --git a/src/components/layout/Header.astro b/src/components/layout/Header.astro
new file mode 100644
index 0000000..00f6b12
--- /dev/null
+++ b/src/components/layout/Header.astro
@@ -0,0 +1,14 @@
+---
+import Navigation from './Navigation.astro';
+---
+
+
\ No newline at end of file
diff --git a/src/components/layout/Navigation.astro b/src/components/layout/Navigation.astro
new file mode 100644
index 0000000..507f75c
--- /dev/null
+++ b/src/components/layout/Navigation.astro
@@ -0,0 +1,24 @@
+---
+const navItems = [
+ { href: '/about', label: 'Sobre mí' },
+ { href: '/projects', label: 'Proyectos' },
+ { href: '/blog', label: 'Blog' },
+];
+
+const currentPath = Astro.url.pathname;
+---
+
+
+ {navItems.map(item => (
+
+ {item.label}
+
+ ))}
+
\ No newline at end of file
diff --git a/src/components/ui/Card.astro b/src/components/ui/Card.astro
new file mode 100644
index 0000000..b88514b
--- /dev/null
+++ b/src/components/ui/Card.astro
@@ -0,0 +1,17 @@
+---
+export interface Props {
+ href?: string;
+ class?: string;
+}
+
+const { href, class: className } = Astro.props;
+const Component = href ? 'a' : 'div';
+---
+
+
+
+
diff --git a/src/components/ui/Tag.astro b/src/components/ui/Tag.astro
new file mode 100644
index 0000000..54db062
--- /dev/null
+++ b/src/components/ui/Tag.astro
@@ -0,0 +1,18 @@
+---
+export interface Props {
+ label: string;
+ variant?: 'primary' | 'secondary' | 'neutral';
+}
+
+const { label, variant = 'neutral' } = Astro.props;
+
+const variants = {
+ primary: 'bg-primary/10 text-primary border-primary/20',
+ secondary: 'bg-secondary/10 text-secondary border-secondary/20',
+ neutral: 'bg-text-tertiary/10 text-text-secondary border-text-tertiary/20',
+};
+---
+
+
+ {label}
+
diff --git a/src/layouts/BaseLayout.astro b/src/layouts/BaseLayout.astro
new file mode 100644
index 0000000..21a58c2
--- /dev/null
+++ b/src/layouts/BaseLayout.astro
@@ -0,0 +1,49 @@
+---
+import '../styles/global.css';
+
+export interface Props {
+ title: string;
+ description: string;
+ image?: string;
+}
+
+const { title, description, image } = Astro.props;
+const canonicalURL = new URL(Astro.url.pathname, Astro.site);
+const socialImage = image ? new URL(image, Astro.site) : new URL('/images/og-default.jpg', Astro.site);
+---
+
+
+
+
+
+
+ {title} | David Aragón - Indie Builder
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/layouts/BlogLayout.astro b/src/layouts/BlogLayout.astro
new file mode 100644
index 0000000..56944f6
--- /dev/null
+++ b/src/layouts/BlogLayout.astro
@@ -0,0 +1,34 @@
+---
+const blogPostJsonLd = {
+ "@context": "https://schema.org",
+ "@type": "BlogPosting",
+ "headline": "{title}",
+ "description": "{description}",
+ "image": "{socialImage}",
+ "datePublished": "{publishDate}",
+ "author": {
+ "@type": "Person",
+ "name": "David Aragón",
+ "url": "https://davidaragon.impresion3d.pro"
+ },
+ "publisher": {
+ "@type": "Person",
+ "name": "David Aragón",
+ "url": "https://davidaragon.impresion3d.pro"
+ },
+ "keywords": "{keywords}",
+ "articleSection": "{category}",
+ "inLanguage": "es-ES"
+};
+---
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/layouts/ProjectLayout.astro b/src/layouts/ProjectLayout.astro
new file mode 100644
index 0000000..9e42bf5
--- /dev/null
+++ b/src/layouts/ProjectLayout.astro
@@ -0,0 +1,95 @@
+---
+import BaseLayout from './BaseLayout.astro';
+import Header from '../components/layout/Header.astro';
+import Footer from '../components/layout/Footer.astro';
+import Tag from '../components/ui/Tag.astro';
+import { formatDate } from '../utils/dateFormat';
+
+export interface Props {
+ title: string;
+ description: string;
+ url: string;
+ github?: string;
+ status: string;
+ tags: string[];
+ startDate: Date;
+ image?: string;
+}
+
+const { title, description, url, github, status, tags, startDate, image } = Astro.props;
+
+const statusColors = {
+ active: 'text-accent-green',
+ development: 'text-accent-yellow',
+ completed: 'text-text-tertiary',
+};
+---
+
+
+
+
+
+
+
+ {image && (
+
+
+
+ )}
+
+
+
+
+
+
+ Tecnologías
+
+ {tags.map(tag => (
+
+ ))}
+
+
+
+
+
+
diff --git a/src/pages/about.astro b/src/pages/about.astro
new file mode 100644
index 0000000..d2ecb76
--- /dev/null
+++ b/src/pages/about.astro
@@ -0,0 +1,168 @@
+---
+import BaseLayout from '../layouts/BaseLayout.astro';
+import Header from '../components/layout/Header.astro';
+import Footer from '../components/layout/Footer.astro';
+---
+
+
+
+
+
+ Sobre mí
+
+
+
+
+
+
+
Hola 👋
+
+ Soy David, desarrollador e indie builder español. Estoy construyendo
+ un portfolio de productos SaaS enfocados en el mercado español y europeo,
+ con el objetivo de alcanzar €4M ARR.
+
+
+
+
+
+ Mi Historia
+
+ Después de años trabajando como desarrollador, decidí dar el salto al emprendimiento
+ tecnológico. Vi una oportunidad en el mercado español: muchas soluciones B2B están
+ dominadas por empresas extranjeras que no entienden las particularidades del mercado
+ local, especialmente en temas de cumplimiento normativo.
+
+
+ Mi enfoque es construir en público, compartiendo tanto los éxitos como los fracasos.
+ Creo en la transparencia y en la comunidad indie, y quiero contribuir al ecosistema
+ de emprendedores españoles compartiendo mi viaje.
+
+
+
+
+ Filosofía de Construcción
+
+
+ →
+
+ Spanish-first: Productos diseñados
+ para el mercado español y europeo
+
+
+
+ →
+
+ Build-in-public: Transparencia
+ total en métricas, aprendizajes y decisiones
+
+
+
+ →
+
+ AI-assisted: Aprovechando herramientas
+ de IA para acelerar desarrollo (79% AI-assisted en WarrantyHub)
+
+
+
+ →
+
+ Compliance-focused: Ventaja competitiva
+ en regulaciones españolas y europeas
+
+
+
+
+
+
+ Stack Técnico
+
+
React
+
TypeScript
+
Node.js
+
Astro
+
Tailwind CSS
+
Docker
+
PostgreSQL
+
Python
+
FastAPI
+
+
+
+
+ En Qué Estoy Trabajando
+
+
Portfolio SaaS €4M ARR
+
+ Construyendo un portfolio de productos SaaS en 3 fases:
+
+
+
+ Fase 1 (0-8 meses): WarrantyHub,
+ Verifactu compliance, análisis de contratos → €825K ARR
+
+
+ Fase 2 (8-18 meses): Plataforma
+ de auditoría, integraciones → €1.8M ARR
+
+
+ Fase 3 (18+ meses): Expansión
+ europea, liderazgo → €4M+ ARR
+
+
+
+
+
+
+ Conecta Conmigo
+
+ Siempre estoy abierto a conversar con otros builders, inversores, o simplemente
+ personas interesadas en emprendimiento tecnológico español.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/pages/projects/[slug].astro b/src/pages/projects/[slug].astro
new file mode 100644
index 0000000..fba593b
--- /dev/null
+++ b/src/pages/projects/[slug].astro
@@ -0,0 +1,21 @@
+---
+import { getCollection } from 'astro:content';
+import ProjectLayout from '../../layouts/ProjectLayout.astro';
+
+export async function getStaticPaths() {
+ const projects = await getCollection('projects');
+ return projects
+ .filter(p => !p.data.draft)
+ .map(project => ({
+ params: { slug: project.slug },
+ props: { project },
+ }));
+}
+
+const { project } = Astro.props;
+const { Content } = await project.render();
+---
+
+
+
+
diff --git a/src/pages/projects/index.astro b/src/pages/projects/index.astro
new file mode 100644
index 0000000..8e8c692
--- /dev/null
+++ b/src/pages/projects/index.astro
@@ -0,0 +1,127 @@
+---
+import BaseLayout from '../../layouts/BaseLayout.astro';
+import Header from '../../components/layout/Header.astro';
+import Footer from '../../components/layout/Footer.astro';
+import Card from '../../components/ui/Card.astro';
+import Tag from '../../components/ui/Tag.astro';
+import { getCollection } from 'astro:content';
+
+const allProjects = (await getCollection('projects'))
+ .filter(p => !p.data.draft)
+ .sort((a, b) => b.data.startDate.getTime() - a.data.startDate.getTime());
+
+const featured = allProjects.filter(p => p.data.featured);
+const active = allProjects.filter(p => p.data.status === 'active' && !p.data.featured);
+const others = allProjects.filter(p => p.data.status !== 'active' && !p.data.featured);
+---
+
+
+
+
+
+
+ {featured.length > 0 && (
+
+ 🌟 Proyectos Destacados
+
+ {featured.map(project => (
+
+ {project.data.image && (
+
+ )}
+
+ {project.data.title}
+
+
+ {project.data.description}
+
+
+ {project.data.tags.slice(0, 4).map(tag => (
+
+ ))}
+
+
+
+ {project.data.status}
+
+
+ Ver proyecto →
+
+
+
+ ))}
+
+
+ )}
+
+ {active.length > 0 && (
+
+ Proyectos Activos
+
+ {active.map(project => (
+
+
+ {project.data.title}
+
+
+ {project.data.description}
+
+
+ {project.data.tags.slice(0, 3).map(tag => (
+
+ ))}
+
+
+ Activo
+ Ver →
+
+
+ ))}
+
+
+ )}
+
+ {others.length > 0 && (
+
+ Otros Proyectos
+
+ {others.map(project => (
+
+
+ {project.data.title}
+
+
+ {project.data.description}
+
+
+ {project.data.tags.slice(0, 3).map(tag => (
+
+ ))}
+
+
+
+ {project.data.status}
+
+ Ver →
+
+
+ ))}
+
+
+ )}
+
+
+