commit 9f708cc9837ced426c929bbabced924cce1896b8 Author: DevLab Bot Date: Thu Jun 11 17:55:13 2026 -0400 feat: landing page inicial para devlab.lat - Hero con animación de partículas y efecto typewriter - Sección de servicios con animación scroll-reveal - Estadísticas animadas con contadores progresivos - Galería de proyectos con overlay hover - Sección de contacto con enlaces - Footer completo - Diseño responsive mobile-first - Soporte prefers-reduced-motion diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8e88989 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +# OS +.DS_Store +Thumbs.db + +# Editor +*.swp +*.swo +*~ +.vscode/ +.idea/ + +# Env +.env +.env.local diff --git a/index.html b/index.html new file mode 100644 index 0000000..23a4dd5 --- /dev/null +++ b/index.html @@ -0,0 +1,218 @@ + + + + + + DevLab.lat — Laboratorio de Desarrollo + + + + + + + + + + +
+ +
+
+

DevLab.lat

+

+

Creamos experiencias digitales innovadoras con tecnología de punta.

+ +
+ +
+ +
+
+

Servicios

+

Transformamos ideas en productos digitales

+
+
+
+
+ +
+

Desarrollo Web

+

Aplicaciones web modernas con las mejores tecnologías del ecosistema frontend y backend.

+
+
+
+ +
+

UI/UX Design

+

Interfaces intuitivas y experiencias de usuario memorables centradas en las personas.

+
+
+
+ +
+

Optimización

+

Rendimiento, SEO y accesibilidad para que tu producto llegue más lejos y más rápido.

+
+
+
+ +
+
+

En números

+

Nuestra trayectoria en datos

+
+
+
+ 0 + Proyectos entregados +
+
+ 0 + Clientes satisfechos +
+
+ 0 + Años de experiencia +
+
+ 0 + Satisfacción +
+
+
+ +
+
+

Proyectos

+

Algunos de nuestros trabajos recientes

+
+
+
+
+
+ +
+
+

Nebula Dashboard

+

Panel de análisis en tiempo real

+
+
+
+
+
+
+ +
+
+

Chronos App

+

Gestión de tiempo inteligente

+
+
+
+
+
+
+ +
+
+

Void Platform

+

Infraestructura cloud escalable

+
+
+
+
+
+ +
+
+

¿Listo para construir algo increíble?

+

Trabajemos juntos para llevar tu idea al siguiente nivel.

+ +
+
+ + + + + + diff --git a/main.js b/main.js new file mode 100644 index 0000000..7729105 --- /dev/null +++ b/main.js @@ -0,0 +1,311 @@ +'use strict'; + +document.addEventListener('DOMContentLoaded', () => { + + const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; + + // ─── Particles ─────────────────────────────────────────── + if (!prefersReducedMotion) { + const canvas = document.getElementById('particles-canvas'); + const ctx = canvas.getContext('2d'); + let particles = []; + let animId; + let mouseX = -1000; + let mouseY = -1000; + + function resizeCanvas() { + canvas.width = window.innerWidth; + canvas.height = window.innerHeight; + } + + function createParticles(count) { + particles = []; + for (let i = 0; i < count; i++) { + particles.push({ + x: Math.random() * canvas.width, + y: Math.random() * canvas.height, + vx: (Math.random() - 0.5) * 0.8, + vy: (Math.random() - 0.5) * 0.8, + size: Math.random() * 2.5 + 0.5, + opacity: Math.random() * 0.5 + 0.2, + }); + } + } + + function drawParticles() { + ctx.clearRect(0, 0, canvas.width, canvas.height); + + for (const p of particles) { + p.x += p.vx; + p.y += p.vy; + + if (p.x < 0 || p.x > canvas.width) p.vx *= -1; + if (p.y < 0 || p.y > canvas.height) p.vy *= -1; + + const dx = p.x - mouseX; + const dy = p.y - mouseY; + const dist = Math.sqrt(dx * dx + dy * dy); + if (dist < 120) { + p.vx += dx * 0.0005; + p.vy += dy * 0.0005; + const maxSpeed = 2; + const speed = Math.sqrt(p.vx * p.vx + p.vy * p.vy); + if (speed > maxSpeed) { + p.vx = (p.vx / speed) * maxSpeed; + p.vy = (p.vy / speed) * maxSpeed; + } + } + + ctx.beginPath(); + ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2); + ctx.fillStyle = `rgba(124, 58, 237, ${p.opacity})`; + ctx.fill(); + } + + // draw connections + for (let i = 0; i < particles.length; i++) { + for (let j = i + 1; j < particles.length; j++) { + const dx = particles[i].x - particles[j].x; + const dy = particles[i].y - particles[j].y; + const dist = Math.sqrt(dx * dx + dy * dy); + if (dist < 150) { + ctx.beginPath(); + ctx.moveTo(particles[i].x, particles[i].y); + ctx.lineTo(particles[j].x, particles[j].y); + ctx.strokeStyle = `rgba(6, 182, 212, ${0.12 * (1 - dist / 150)})`; + ctx.lineWidth = 0.8; + ctx.stroke(); + } + } + } + + animId = requestAnimationFrame(drawParticles); + } + + function initParticles() { + resizeCanvas(); + const count = Math.min(Math.floor((canvas.width * canvas.height) / 12000), 120); + createParticles(count); + drawParticles(); + } + + window.addEventListener('resize', () => { + resizeCanvas(); + createParticles(Math.min(Math.floor((canvas.width * canvas.height) / 12000), 120)); + }); + + document.addEventListener('mousemove', (e) => { + mouseX = e.clientX; + mouseY = e.clientY; + }); + + document.addEventListener('touchmove', (e) => { + const touch = e.touches[0]; + mouseX = touch.clientX; + mouseY = touch.clientY; + }); + + initParticles(); + } + + // ─── Typewriter ───────────────────────────────────────── + const typewriterEl = document.querySelector('.typewriter'); + if (typewriterEl) { + const text = typewriterEl.dataset.text || ''; + const speed = 60; + let charIndex = 0; + let isDeleting = false; + const cursor = document.createElement('span'); + cursor.className = 'cursor'; + cursor.setAttribute('aria-hidden', 'true'); + typewriterEl.appendChild(cursor); + + // clear initial content + typewriterEl.childNodes.forEach((node, i) => { + if (node.nodeType === Node.TEXT_NODE) node.textContent = ''; + }); + + function typeStep() { + if (!isDeleting) { + if (charIndex < text.length) { + typewriterEl.textContent = text.slice(0, charIndex + 1); + typewriterEl.appendChild(cursor); + charIndex++; + setTimeout(typeStep, speed); + } else { + if (!prefersReducedMotion) { + setTimeout(() => { isDeleting = true; typeStep(); }, 3000); + } + } + } else { + if (charIndex > 0) { + charIndex--; + typewriterEl.textContent = text.slice(0, charIndex); + typewriterEl.appendChild(cursor); + setTimeout(typeStep, speed * 0.6); + } else { + isDeleting = false; + setTimeout(typeStep, 1000); + } + } + } + + typeStep(); + } + + // ─── Navbar scroll effect ─────────────────────────────── + const navbar = document.querySelector('.navbar'); + let lastScroll = 0; + + function handleNavScroll() { + const currentScroll = window.scrollY; + if (currentScroll > 50) { + navbar.classList.add('scrolled'); + } else { + navbar.classList.remove('scrolled'); + } + lastScroll = currentScroll; + } + + window.addEventListener('scroll', handleNavScroll, { passive: true }); + + // ─── Hamburger menu ───────────────────────────────────── + const hamburger = document.querySelector('.hamburger'); + const navLinks = document.querySelector('.nav-links'); + + function toggleMenu() { + const isOpen = navLinks.classList.toggle('open'); + hamburger.classList.toggle('active'); + hamburger.setAttribute('aria-expanded', isOpen); + document.body.style.overflow = isOpen ? 'hidden' : ''; + } + + if (hamburger) { + hamburger.addEventListener('click', toggleMenu); + } + + // close menu on link click + document.querySelectorAll('.nav-links a').forEach(link => { + link.addEventListener('click', () => { + if (navLinks.classList.contains('open')) { + toggleMenu(); + } + }); + }); + + // close menu on escape + document.addEventListener('keydown', (e) => { + if (e.key === 'Escape' && navLinks.classList.contains('open')) { + toggleMenu(); + } + }); + + // ─── Active nav link ──────────────────────────────────── + const sections = document.querySelectorAll('section[id]'); + const navAnchors = document.querySelectorAll('.nav-links a'); + + function updateActiveLink() { + let current = ''; + sections.forEach(section => { + const top = section.offsetTop - 120; + if (window.scrollY >= top) { + current = section.getAttribute('id'); + } + }); + navAnchors.forEach(a => { + a.classList.toggle('active', a.getAttribute('href') === `#${current}`); + }); + } + + window.addEventListener('scroll', updateActiveLink, { passive: true }); + + // ─── Scroll Reveal (IntersectionObserver) ─────────────── + if (!prefersReducedMotion) { + const revealObserver = new IntersectionObserver((entries) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + entry.target.classList.add('visible'); + } + }); + }, { threshold: 0.15, rootMargin: '0px 0px -50px 0px' }); + + document.querySelectorAll('.reveal').forEach(el => revealObserver.observe(el)); + } else { + document.querySelectorAll('.reveal').forEach(el => el.classList.add('visible')); + } + + // ─── Counter Animation ────────────────────────────────── + const counters = document.querySelectorAll('.stat-number'); + + function animateCounter(el) { + const target = parseInt(el.dataset.target, 10); + const suffix = el.dataset.suffix || ''; + const duration = 2000; + const startTime = performance.now(); + + function update(currentTime) { + const elapsed = currentTime - startTime; + const progress = Math.min(elapsed / duration, 1); + const eased = 1 - Math.pow(1 - progress, 3); + const current = Math.floor(eased * target); + el.textContent = current + suffix; + if (progress < 1) { + requestAnimationFrame(update); + } else { + el.textContent = target + suffix; + } + } + + requestAnimationFrame(update); + } + + if (!prefersReducedMotion) { + const counterObserver = new IntersectionObserver((entries) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + const counter = entry.target; + animateCounter(counter); + counterObserver.unobserve(counter); + } + }); + }, { threshold: 0.5 }); + + counters.forEach(c => counterObserver.observe(c)); + } else { + counters.forEach(c => { + c.textContent = c.dataset.target + (c.dataset.suffix || ''); + }); + } + + // ─── Service cards staggered reveal ───────────────────── + if (!prefersReducedMotion) { + const cardObserver = new IntersectionObserver((entries) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + const delay = parseInt(entry.target.dataset.delay || '0', 10); + setTimeout(() => { + entry.target.classList.add('visible'); + }, delay); + cardObserver.unobserve(entry.target); + } + }); + }, { threshold: 0.2 }); + + document.querySelectorAll('.service-card.reveal, .stat-item.reveal, .proyecto-card.reveal').forEach( + el => cardObserver.observe(el) + ); + } + + // ─── Smooth scroll for anchor links (fallback) ────────── + document.querySelectorAll('a[href^="#"]').forEach(anchor => { + anchor.addEventListener('click', (e) => { + const targetId = anchor.getAttribute('href'); + if (targetId === '#') return; + const target = document.querySelector(targetId); + if (target) { + e.preventDefault(); + target.scrollIntoView({ behavior: 'smooth', block: 'start' }); + } + }); + }); +}); diff --git a/styles.css b/styles.css new file mode 100644 index 0000000..227de86 --- /dev/null +++ b/styles.css @@ -0,0 +1,838 @@ +:root { + --bg-primary: #0a0e27; + --bg-secondary: #111539; + --bg-card: rgba(17, 21, 57, 0.8); + --purple: #7c3aed; + --purple-glow: rgba(124, 58, 237, 0.4); + --cyan: #06b6d4; + --cyan-glow: rgba(6, 182, 212, 0.4); + --text-primary: #f1f5f9; + --text-secondary: #94a3b8; + --text-muted: #64748b; + --gradient-1: linear-gradient(135deg, var(--purple), var(--cyan)); + --gradient-2: linear-gradient(135deg, rgba(124, 58, 237, 0.15), rgba(6, 182, 212, 0.15)); + --radius: 16px; + --radius-sm: 8px; + --transition: 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94); + --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif; + --font-mono: 'JetBrains Mono', 'Fira Code', monospace; + --max-width: 1200px; + --nav-height: 70px; +} + +*, +*::before, +*::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +html { + scroll-behavior: smooth; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +body { + font-family: var(--font-sans); + background: var(--bg-primary); + color: var(--text-primary); + line-height: 1.6; + min-height: 100vh; + overflow-x: hidden; +} + +::selection { + background: var(--purple); + color: white; +} + +:focus-visible { + outline: 2px solid var(--cyan); + outline-offset: 2px; +} + +/* ===== NAVBAR ===== */ +.navbar { + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 1000; + height: var(--nav-height); + transition: background var(--transition), backdrop-filter var(--transition); +} + +.navbar.scrolled { + background: rgba(10, 14, 39, 0.85); + backdrop-filter: blur(20px) saturate(1.8); + -webkit-backdrop-filter: blur(20px) saturate(1.8); + border-bottom: 1px solid rgba(124, 58, 237, 0.15); +} + +.nav-container { + max-width: var(--max-width); + margin: 0 auto; + padding: 0 24px; + display: flex; + align-items: center; + justify-content: space-between; + height: 100%; +} + +.logo { + font-family: var(--font-mono); + font-size: 1.5rem; + font-weight: 700; + color: var(--text-primary); + text-decoration: none; + letter-spacing: -0.5px; +} + +.logo span { + color: var(--cyan); +} + +.nav-links { + display: flex; + list-style: none; + gap: 32px; +} + +.nav-links a { + color: var(--text-secondary); + text-decoration: none; + font-size: 0.9rem; + font-weight: 500; + transition: color var(--transition); + position: relative; +} + +.nav-links a::after { + content: ''; + position: absolute; + bottom: -4px; + left: 0; + width: 0; + height: 2px; + background: var(--gradient-1); + transition: width var(--transition); + border-radius: 1px; +} + +.nav-links a:hover, +.nav-links a.active { + color: var(--text-primary); +} + +.nav-links a:hover::after, +.nav-links a.active::after { + width: 100%; +} + +.hamburger { + display: none; + flex-direction: column; + gap: 5px; + background: none; + border: none; + cursor: pointer; + padding: 8px; + z-index: 1001; +} + +.hamburger span { + display: block; + width: 24px; + height: 2px; + background: var(--text-primary); + border-radius: 2px; + transition: all var(--transition); + transform-origin: center; +} + +.hamburger.active span:nth-child(1) { + transform: translateY(7px) rotate(45deg); +} + +.hamburger.active span:nth-child(2) { + opacity: 0; + transform: scaleX(0); +} + +.hamburger.active span:nth-child(3) { + transform: translateY(-7px) rotate(-45deg); +} + +/* ===== HERO ===== */ +.hero { + position: relative; + min-height: 100vh; + display: flex; + align-items: center; + justify-content: center; + overflow: hidden; + padding: 0 24px; +} + +#particles-canvas { + position: absolute; + inset: 0; + z-index: 1; +} + +.hero-overlay { + position: absolute; + inset: 0; + z-index: 2; + background: + radial-gradient(ellipse at 20% 50%, var(--purple-glow) 0%, transparent 50%), + radial-gradient(ellipse at 80% 50%, var(--cyan-glow) 0%, transparent 50%); +} + +.hero-content { + position: relative; + z-index: 3; + text-align: center; + max-width: 800px; + animation: fadeInUp 1s ease-out; +} + +@keyframes fadeInUp { + from { opacity: 0; transform: translateY(40px); } + to { opacity: 1; transform: translateY(0); } +} + +.hero-title { + font-size: clamp(2.5rem, 8vw, 5rem); + font-weight: 800; + line-height: 1.1; + margin-bottom: 16px; + background: var(--gradient-1); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + letter-spacing: -2px; +} + +.hero-title span { + -webkit-text-fill-color: var(--cyan); + background: none; +} + +.typewriter { + font-family: var(--font-mono); + font-size: clamp(1rem, 2.5vw, 1.3rem); + color: var(--text-secondary); + min-height: 1.6em; + margin-bottom: 16px; +} + +.typewriter .cursor { + display: inline-block; + width: 2px; + height: 1.2em; + background: var(--cyan); + margin-left: 2px; + vertical-align: text-bottom; + animation: blink 1s step-end infinite; +} + +@keyframes blink { + 50% { opacity: 0; } +} + +.hero-desc { + font-size: clamp(1rem, 2vw, 1.15rem); + color: var(--text-muted); + margin-bottom: 40px; + max-width: 600px; + margin-left: auto; + margin-right: auto; +} + +.hero-actions { + display: flex; + gap: 16px; + justify-content: center; + flex-wrap: wrap; +} + +/* ===== BUTTONS ===== */ +.btn { + display: inline-flex; + align-items: center; + gap: 10px; + padding: 14px 32px; + border-radius: 50px; + font-family: var(--font-sans); + font-size: 0.95rem; + font-weight: 600; + text-decoration: none; + cursor: pointer; + border: none; + transition: all var(--transition); + position: relative; + overflow: hidden; +} + +.btn svg { + width: 20px; + height: 20px; + flex-shrink: 0; +} + +.btn-primary { + background: var(--gradient-1); + color: white; + box-shadow: 0 4px 20px var(--purple-glow); +} + +.btn-primary:hover { + transform: translateY(-3px); + box-shadow: 0 8px 30px var(--purple-glow); +} + +.btn-secondary { + background: transparent; + color: var(--text-primary); + border: 1.5px solid rgba(124, 58, 237, 0.4); +} + +.btn-secondary:hover { + border-color: var(--purple); + background: rgba(124, 58, 237, 0.1); + transform: translateY(-3px); +} + +.scroll-indicator { + position: absolute; + bottom: 40px; + left: 50%; + transform: translateX(-50%); + z-index: 3; + animation: bounceDown 2s ease-in-out infinite; +} + +.scroll-indicator span { + display: block; + width: 24px; + height: 40px; + border: 2px solid var(--text-muted); + border-radius: 12px; + position: relative; +} + +.scroll-indicator span::before { + content: ''; + position: absolute; + top: 8px; + left: 50%; + transform: translateX(-50%); + width: 4px; + height: 8px; + background: var(--cyan); + border-radius: 2px; + animation: scrollDot 2s ease-in-out infinite; +} + +@keyframes scrollDot { + 0%, 100% { transform: translateX(-50%) translateY(0); opacity: 1; } + 50% { transform: translateX(-50%) translateY(12px); opacity: 0.3; } +} + +@keyframes bounceDown { + 0%, 100% { transform: translateX(-50%) translateY(0); } + 50% { transform: translateX(-50%) translateY(8px); } +} + +/* ===== SECTIONS ===== */ +.section { + padding: 100px 24px; + max-width: var(--max-width); + margin: 0 auto; +} + +.section-header { + text-align: center; + margin-bottom: 60px; +} + +.section-header h2 { + font-size: clamp(2rem, 4vw, 2.8rem); + font-weight: 700; + margin-bottom: 12px; + background: var(--gradient-1); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +.section-sub { + color: var(--text-muted); + font-size: 1.1rem; +} + +/* ===== REVEAL ANIMATION ===== */ +.reveal { + opacity: 0; + transform: translateY(40px); + transition: opacity 0.8s ease-out, transform 0.8s ease-out; +} + +.reveal.visible { + opacity: 1; + transform: translateY(0); +} + +/* ===== SERVICIOS ===== */ +.servicios { + padding-top: 40px; +} + +.services-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 24px; +} + +@container (max-width: 700px) { + .services-grid { grid-template-columns: 1fr; } +} + +.service-card { + background: var(--bg-card); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); + border: 1px solid rgba(124, 58, 237, 0.12); + border-radius: var(--radius); + padding: 36px 28px; + transition: all var(--transition); + position: relative; + overflow: hidden; +} + +.service-card::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 3px; + background: var(--gradient-1); + transform: scaleX(0); + transform-origin: left; + transition: transform var(--transition); +} + +.service-card:hover { + transform: translateY(-8px); + border-color: rgba(124, 58, 237, 0.3); + box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); +} + +.service-card:hover::before { + transform: scaleX(1); +} + +.card-icon { + width: 52px; + height: 52px; + border-radius: 14px; + background: var(--gradient-2); + border: 1px solid rgba(124, 58, 237, 0.2); + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 20px; + color: var(--cyan); + transition: all var(--transition); +} + +.service-card:hover .card-icon { + background: var(--gradient-1); + color: white; + transform: scale(1.1) rotate(-5deg); +} + +.card-icon svg { + width: 26px; + height: 26px; +} + +.service-card h3 { + font-size: 1.25rem; + font-weight: 600; + margin-bottom: 12px; +} + +.service-card p { + color: var(--text-secondary); + font-size: 0.95rem; + line-height: 1.7; +} + +/* ===== STATS ===== */ +.stats { + background: var(--gradient-2); + border-radius: var(--radius); + margin: 0 24px; + max-width: calc(var(--max-width) - 48px); + padding: 80px 48px; + border: 1px solid rgba(124, 58, 237, 0.1); +} + +.stats-grid { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 32px; + text-align: center; +} + +.stat-item { + padding: 20px; +} + +.stat-number { + display: block; + font-family: var(--font-mono); + font-size: clamp(2.5rem, 4vw, 3.5rem); + font-weight: 800; + background: var(--gradient-1); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + line-height: 1.2; + margin-bottom: 8px; +} + +.stat-label { + color: var(--text-muted); + font-size: 0.95rem; + font-weight: 500; + text-transform: uppercase; + letter-spacing: 1px; +} + +/* ===== PROYECTOS ===== */ +.proyectos { + padding-top: 40px; +} + +.proyectos-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 24px; +} + +@container (max-width: 700px) { + .proyectos-grid { grid-template-columns: 1fr; } +} + +.proyecto-card { + border-radius: var(--radius); + overflow: hidden; + position: relative; + cursor: pointer; +} + +.proyecto-img { + position: relative; + aspect-ratio: 4 / 3; + overflow: hidden; + border-radius: var(--radius); + border: 1px solid rgba(124, 58, 237, 0.12); + background: var(--bg-card); +} + +.proyecto-placeholder { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + color: var(--text-muted); + transition: all 0.6s ease-out; +} + +.proyecto-placeholder svg { + width: 60px; + height: 60px; + opacity: 0.3; + transition: all 0.6s ease-out; +} + +.proyecto-card:hover .proyecto-placeholder { + transform: scale(1.1); +} + +.proyecto-card:hover .proyecto-placeholder svg { + opacity: 0.6; + color: var(--cyan); +} + +.proyecto-overlay { + position: absolute; + inset: 0; + background: linear-gradient(to top, rgba(10, 14, 39, 0.95), transparent); + display: flex; + flex-direction: column; + justify-content: flex-end; + padding: 28px; + opacity: 0; + transform: translateY(20px); + transition: all var(--transition); +} + +.proyecto-card:hover .proyecto-overlay { + opacity: 1; + transform: translateY(0); +} + +.proyecto-overlay h3 { + font-size: 1.2rem; + font-weight: 600; + margin-bottom: 6px; +} + +.proyecto-overlay p { + color: var(--text-secondary); + font-size: 0.9rem; +} + +/* ===== CONTACTO ===== */ +.contacto { + text-align: center; + padding-bottom: 60px; +} + +.contacto-content h2 { + font-size: clamp(1.8rem, 3.5vw, 2.5rem); + font-weight: 700; + margin-bottom: 16px; +} + +.contacto-content p { + color: var(--text-muted); + font-size: 1.1rem; + margin-bottom: 40px; +} + +.contacto-links { + display: flex; + gap: 16px; + justify-content: center; + flex-wrap: wrap; +} + +.contact-btn { + padding: 14px 28px; +} + +/* ===== FOOTER ===== */ +.footer { + border-top: 1px solid rgba(124, 58, 237, 0.1); + padding: 60px 24px 30px; +} + +.footer-content { + max-width: var(--max-width); + margin: 0 auto; + display: grid; + grid-template-columns: 2fr 1fr 1fr; + gap: 48px; + margin-bottom: 40px; +} + +.footer-brand .logo { + display: inline-block; + margin-bottom: 12px; +} + +.footer-brand p { + color: var(--text-muted); + font-size: 0.9rem; + max-width: 280px; +} + +.footer-links h4, +.footer-tech h4 { + font-size: 0.85rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 1.5px; + color: var(--text-muted); + margin-bottom: 16px; +} + +.footer-links ul, +.footer-tech ul { + list-style: none; +} + +.footer-links li, +.footer-tech li { + margin-bottom: 10px; +} + +.footer-links a { + color: var(--text-secondary); + text-decoration: none; + font-size: 0.9rem; + transition: color var(--transition); +} + +.footer-links a:hover { + color: var(--cyan); +} + +.footer-tech li { + color: var(--text-secondary); + font-family: var(--font-mono); + font-size: 0.8rem; +} + +.footer-bottom { + max-width: var(--max-width); + margin: 0 auto; + padding-top: 24px; + border-top: 1px solid rgba(124, 58, 237, 0.08); + text-align: center; +} + +.footer-bottom p { + color: var(--text-muted); + font-size: 0.85rem; +} + +/* ===== RESPONSIVE ===== */ +@media (max-width: 1024px) { + .services-grid, + .proyectos-grid { + grid-template-columns: repeat(2, 1fr); + } + + .stats-grid { + grid-template-columns: repeat(2, 1fr); + } + + .footer-content { + grid-template-columns: 1fr 1fr; + } +} + +@media (max-width: 768px) { + .nav-links { + position: fixed; + top: 0; + right: -100%; + width: 280px; + height: 100vh; + background: rgba(10, 14, 39, 0.98); + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); + flex-direction: column; + justify-content: center; + align-items: center; + gap: 0; + transition: right var(--transition); + border-left: 1px solid rgba(124, 58, 237, 0.15); + } + + .nav-links.open { + right: 0; + } + + .nav-links li { + width: 100%; + text-align: center; + } + + .nav-links a { + display: block; + padding: 18px 24px; + font-size: 1.1rem; + } + + .nav-links a::after { + display: none; + } + + .hamburger { + display: flex; + } + + .services-grid, + .proyectos-grid { + grid-template-columns: 1fr; + } + + .stats-grid { + grid-template-columns: repeat(2, 1fr); + gap: 20px; + } + + .stats { + padding: 60px 24px; + margin: 0 16px; + } + + .footer-content { + grid-template-columns: 1fr; + text-align: center; + } + + .footer-brand p { + margin: 0 auto; + } + + .section { + padding: 60px 16px; + } +} + +@media (max-width: 480px) { + .hero-actions { + flex-direction: column; + align-items: center; + } + + .btn { + width: 100%; + justify-content: center; + } + + .contacto-links { + flex-direction: column; + align-items: center; + } + + .contact-btn { + width: 100%; + justify-content: center; + } + + .stats-grid { + grid-template-columns: 1fr; + } +} + +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + } + + html { + scroll-behavior: auto; + } + + .reveal { + opacity: 1; + transform: none; + } +}