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
This commit is contained in:
14
.gitignore
vendored
Normal file
14
.gitignore
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Editor
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# Env
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
218
index.html
Normal file
218
index.html
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="es">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>DevLab.lat — Laboratorio de Desarrollo</title>
|
||||||
|
<meta name="description" content="DevLab.lat — Laboratorio de desarrollo web moderno. Creamos experiencias digitales innovadoras con tecnología de punta.">
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;600&display=swap" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="styles.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<nav class="navbar" role="navigation" aria-label="Navegación principal">
|
||||||
|
<div class="nav-container">
|
||||||
|
<a href="#" class="logo" aria-label="DevLab.lat inicio">DevLab<span>.lat</span></a>
|
||||||
|
<button class="hamburger" aria-label="Abrir menú" aria-expanded="false">
|
||||||
|
<span></span><span></span><span></span>
|
||||||
|
</button>
|
||||||
|
<ul class="nav-links" role="menubar">
|
||||||
|
<li role="none"><a href="#hero" role="menuitem">Inicio</a></li>
|
||||||
|
<li role="none"><a href="#servicios" role="menuitem">Servicios</a></li>
|
||||||
|
<li role="none"><a href="#stats" role="menuitem">Estadísticas</a></li>
|
||||||
|
<li role="none"><a href="#proyectos" role="menuitem">Proyectos</a></li>
|
||||||
|
<li role="none"><a href="#contacto" role="menuitem">Contacto</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<section id="hero" class="hero">
|
||||||
|
<canvas id="particles-canvas" aria-hidden="true"></canvas>
|
||||||
|
<div class="hero-overlay"></div>
|
||||||
|
<div class="hero-content">
|
||||||
|
<h1 class="hero-title">DevLab<span>.lat</span></h1>
|
||||||
|
<p class="typewriter" data-text="Laboratorio de desarrollo web moderno" aria-label="Laboratorio de desarrollo web moderno"></p>
|
||||||
|
<p class="hero-desc">Creamos experiencias digitales innovadoras con tecnología de punta.</p>
|
||||||
|
<div class="hero-actions">
|
||||||
|
<a href="#servicios" class="btn btn-primary">Descubrir</a>
|
||||||
|
<a href="#contacto" class="btn btn-secondary">Contactar</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="scroll-indicator" aria-hidden="true">
|
||||||
|
<span></span>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="servicios" class="section servicios">
|
||||||
|
<div class="section-header reveal">
|
||||||
|
<h2>Servicios</h2>
|
||||||
|
<p class="section-sub">Transformamos ideas en productos digitales</p>
|
||||||
|
</div>
|
||||||
|
<div class="services-grid">
|
||||||
|
<article class="service-card reveal" data-delay="0">
|
||||||
|
<div class="card-icon">
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true">
|
||||||
|
<path d="M12 6v12M6 12h12"/><rect x="2" y="2" width="20" height="20" rx="3"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<h3>Desarrollo Web</h3>
|
||||||
|
<p>Aplicaciones web modernas con las mejores tecnologías del ecosistema frontend y backend.</p>
|
||||||
|
</article>
|
||||||
|
<article class="service-card reveal" data-delay="100">
|
||||||
|
<div class="card-icon">
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true">
|
||||||
|
<path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<h3>UI/UX Design</h3>
|
||||||
|
<p>Interfaces intuitivas y experiencias de usuario memorables centradas en las personas.</p>
|
||||||
|
</article>
|
||||||
|
<article class="service-card reveal" data-delay="200">
|
||||||
|
<div class="card-icon">
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true">
|
||||||
|
<circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<h3>Optimización</h3>
|
||||||
|
<p>Rendimiento, SEO y accesibilidad para que tu producto llegue más lejos y más rápido.</p>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="stats" class="section stats">
|
||||||
|
<div class="section-header reveal">
|
||||||
|
<h2>En números</h2>
|
||||||
|
<p class="section-sub">Nuestra trayectoria en datos</p>
|
||||||
|
</div>
|
||||||
|
<div class="stats-grid">
|
||||||
|
<div class="stat-item reveal" data-delay="0">
|
||||||
|
<span class="stat-number" data-target="150" data-suffix="+">0</span>
|
||||||
|
<span class="stat-label">Proyectos entregados</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat-item reveal" data-delay="100">
|
||||||
|
<span class="stat-number" data-target="48" data-suffix="">0</span>
|
||||||
|
<span class="stat-label">Clientes satisfechos</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat-item reveal" data-delay="200">
|
||||||
|
<span class="stat-number" data-target="12" data-suffix="">0</span>
|
||||||
|
<span class="stat-label">Años de experiencia</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat-item reveal" data-delay="300">
|
||||||
|
<span class="stat-number" data-target="99" data-suffix="%">0</span>
|
||||||
|
<span class="stat-label">Satisfacción</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="proyectos" class="section proyectos">
|
||||||
|
<div class="section-header reveal">
|
||||||
|
<h2>Proyectos</h2>
|
||||||
|
<p class="section-sub">Algunos de nuestros trabajos recientes</p>
|
||||||
|
</div>
|
||||||
|
<div class="proyectos-grid">
|
||||||
|
<article class="proyecto-card reveal" data-delay="0">
|
||||||
|
<div class="proyecto-img">
|
||||||
|
<div class="proyecto-placeholder">
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true">
|
||||||
|
<path d="M3 3h18v18H3zM9 3v18M3 9h18"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="proyecto-overlay">
|
||||||
|
<h3>Nebula Dashboard</h3>
|
||||||
|
<p>Panel de análisis en tiempo real</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
<article class="proyecto-card reveal" data-delay="100">
|
||||||
|
<div class="proyecto-img">
|
||||||
|
<div class="proyecto-placeholder">
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true">
|
||||||
|
<circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="proyecto-overlay">
|
||||||
|
<h3>Chronos App</h3>
|
||||||
|
<p>Gestión de tiempo inteligente</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
<article class="proyecto-card reveal" data-delay="200">
|
||||||
|
<div class="proyecto-img">
|
||||||
|
<div class="proyecto-placeholder">
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true">
|
||||||
|
<path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="proyecto-overlay">
|
||||||
|
<h3>Void Platform</h3>
|
||||||
|
<p>Infraestructura cloud escalable</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="contacto" class="section contacto">
|
||||||
|
<div class="contacto-content reveal">
|
||||||
|
<h2>¿Listo para construir algo increíble?</h2>
|
||||||
|
<p>Trabajemos juntos para llevar tu idea al siguiente nivel.</p>
|
||||||
|
<div class="contacto-links">
|
||||||
|
<a href="mailto:hola@devlab.lat" class="btn btn-primary contact-btn" aria-label="Enviar correo">
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
|
||||||
|
<rect x="2" y="4" width="20" height="16" rx="2"/><path d="m22 7-10 7L2 7"/>
|
||||||
|
</svg>
|
||||||
|
hola@devlab.lat
|
||||||
|
</a>
|
||||||
|
<a href="https://github.com/devlab" target="_blank" rel="noopener noreferrer" class="btn btn-secondary contact-btn" aria-label="GitHub">
|
||||||
|
<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
||||||
|
<path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0 0 24 12c0-6.63-5.37-12-12-12z"/>
|
||||||
|
</svg>
|
||||||
|
GitHub
|
||||||
|
</a>
|
||||||
|
<a href="https://linkedin.com/company/devlab" target="_blank" rel="noopener noreferrer" class="btn btn-secondary contact-btn" aria-label="LinkedIn">
|
||||||
|
<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
||||||
|
<path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 0 1-2.063-2.065 2.064 2.064 0 1 1 2.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/>
|
||||||
|
</svg>
|
||||||
|
LinkedIn
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<footer class="footer">
|
||||||
|
<div class="footer-content">
|
||||||
|
<div class="footer-brand">
|
||||||
|
<a href="#" class="logo">DevLab<span>.lat</span></a>
|
||||||
|
<p>Laboratorio de desarrollo web moderno.</p>
|
||||||
|
</div>
|
||||||
|
<div class="footer-links">
|
||||||
|
<h4>Navegación</h4>
|
||||||
|
<ul>
|
||||||
|
<li><a href="#hero">Inicio</a></li>
|
||||||
|
<li><a href="#servicios">Servicios</a></li>
|
||||||
|
<li><a href="#stats">Estadísticas</a></li>
|
||||||
|
<li><a href="#proyectos">Proyectos</a></li>
|
||||||
|
<li><a href="#contacto">Contacto</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="footer-tech">
|
||||||
|
<h4>Stack</h4>
|
||||||
|
<ul>
|
||||||
|
<li>HTML5 & CSS3</li>
|
||||||
|
<li>JavaScript ES2024</li>
|
||||||
|
<li>IntersectionObserver</li>
|
||||||
|
<li>Canvas API</li>
|
||||||
|
<li>CSS Container Queries</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="footer-bottom">
|
||||||
|
<p>© 2026 DevLab.lat — Todos los derechos reservados.</p>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<script src="main.js" defer></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
311
main.js
Normal file
311
main.js
Normal file
@@ -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' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
838
styles.css
Normal file
838
styles.css
Normal file
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user