Compare commits
24
Commits
d219e66637
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e9cf9a160a | ||
|
|
1c1da7df73 | ||
|
|
92b21eab3c | ||
|
|
b4ff3a0aca | ||
|
|
b6a2b049bb | ||
|
|
7597ecc83e | ||
|
|
0313f9dbd5 | ||
|
|
f0cc636536 | ||
|
|
09686f9a15 | ||
|
|
dd5515c040 | ||
|
|
cdc160ec9e | ||
|
|
ed53a37ea5 | ||
|
|
54a0c584e6 | ||
|
|
41f0549be7 | ||
|
|
f548d8d324 | ||
|
|
1f49d33fbf | ||
|
|
cf8b3a9efd | ||
|
|
d5d6c904f5 | ||
|
|
f4808f5518 | ||
|
|
5d4756657c | ||
|
|
ac0c6b983a | ||
|
|
425028511d | ||
|
|
3ca89de80e | ||
|
|
3b94a9ca8a |
@@ -1,3 +1,98 @@
|
||||
/* ==========================================================================
|
||||
Self-Hosted Fonts & Icons
|
||||
========================================================================== */
|
||||
|
||||
/* Space Grotesk */
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: 'Space Grotesk';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: url('../fonts/space/space-grotesk-v22-latin-300.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: 'Space Grotesk';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url('../fonts/space/space-grotesk-v22-latin-regular.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: 'Space Grotesk';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: url('../fonts/space/space-grotesk-v22-latin-500.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: 'Space Grotesk';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: url('../fonts/space/space-grotesk-v22-latin-600.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: 'Space Grotesk';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: url('../fonts/space/space-grotesk-v22-latin-700.woff2') format('woff2');
|
||||
}
|
||||
|
||||
/* Instrument Serif */
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: 'Instrument Serif';
|
||||
font-style: normal;
|
||||
font-weight: 300 400;
|
||||
src: url('../fonts/instrument/instrument-serif-v5-latin-regular.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: 'Instrument Serif';
|
||||
font-style: italic;
|
||||
font-weight: 300 400;
|
||||
src: url('../fonts/instrument/instrument-serif-v5-latin-italic.woff2') format('woff2');
|
||||
}
|
||||
|
||||
/* Material Symbols Outlined */
|
||||
@font-face {
|
||||
font-display: block;
|
||||
font-family: 'Material Symbols Outlined';
|
||||
font-style: normal;
|
||||
font-weight: 100 700;
|
||||
src: url('../fonts/material-symbols-outlined.woff2') format('woff2');
|
||||
}
|
||||
|
||||
.material-symbols-outlined {
|
||||
font-family: 'Material Symbols Outlined';
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-size: 24px;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
overflow: hidden;
|
||||
line-height: 1;
|
||||
letter-spacing: normal;
|
||||
text-transform: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
white-space: nowrap;
|
||||
word-wrap: normal;
|
||||
direction: ltr;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
text-rendering: optimizeLegibility;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
font-feature-settings: 'liga';
|
||||
-webkit-font-feature-settings: 'liga';
|
||||
}
|
||||
|
||||
/* Glass Morphism Effect */
|
||||
.glass {
|
||||
background: rgba(255, 255, 255, 0.4);
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+338
@@ -0,0 +1,338 @@
|
||||
<!doctype html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
|
||||
<title>Huella Digital y Decisiones Técnicas | Itziar ZG</title>
|
||||
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
|
||||
<script id="tailwind-config">
|
||||
tailwind.config = {
|
||||
darkMode: "class",
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: "#8c2bee",
|
||||
"background-light": "#f7f6f8",
|
||||
"background-dark": "#191022",
|
||||
},
|
||||
fontFamily: {
|
||||
display: ["Space Grotesk", "sans-serif"],
|
||||
serif: ["Instrument Serif", "serif"],
|
||||
},
|
||||
borderRadius: {
|
||||
DEFAULT: "0.5rem",
|
||||
lg: "1rem",
|
||||
xl: "1.5rem",
|
||||
full: "9999px",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<link rel="preload" href="fonts/material-symbols-outlined.woff2" as="font" type="font/woff2" crossorigin />
|
||||
<link rel="preload" href="fonts/space/space-grotesk-v22-latin-regular.woff2" as="font" type="font/woff2" crossorigin />
|
||||
<link rel="stylesheet" href="css/styles.css?v=1.1" />
|
||||
</head>
|
||||
<body
|
||||
class="bg-background-light dark:bg-background-dark font-display text-slate-900 dark:text-slate-100 selection:bg-primary/30 transition-colors duration-300"
|
||||
>
|
||||
<div class="relative min-h-screen overflow-hidden iridescent-bg flex flex-col justify-between">
|
||||
<!-- Floating Fluid Shapes -->
|
||||
<div
|
||||
class="absolute -top-24 -left-24 w-96 h-96 bg-primary/10 fluid-shape blur-3xl opacity-50 pointer-events-none"
|
||||
></div>
|
||||
<div
|
||||
class="absolute top-1/2 -right-24 w-80 h-80 bg-pink-300/10 fluid-shape blur-3xl opacity-40 pointer-events-none"
|
||||
></div>
|
||||
|
||||
<div class="layout-container flex h-full grow flex-col relative z-10">
|
||||
<!-- Navigation -->
|
||||
<header
|
||||
class="mx-auto w-full max-w-6xl px-6 py-6 flex items-center justify-between"
|
||||
>
|
||||
<a href="index.html" class="flex items-center gap-2 group cursor-pointer">
|
||||
<div
|
||||
class="size-10 bg-primary rounded-full flex items-center justify-center text-white shadow-lg shadow-primary/20"
|
||||
>
|
||||
<span class="material-symbols-outlined text-2xl">
|
||||
auto_awesome
|
||||
</span>
|
||||
</div>
|
||||
<h2
|
||||
class="text-xl font-bold tracking-tight font-serif italic text-slate-900 dark:text-white"
|
||||
>
|
||||
Itziar ZG
|
||||
</h2>
|
||||
</a>
|
||||
<nav
|
||||
class="hidden md:flex items-center gap-10 glass px-8 py-3 rounded-full"
|
||||
>
|
||||
<a
|
||||
class="text-sm font-medium hover:text-primary transition-colors text-slate-700 dark:text-slate-300"
|
||||
href="index.html"
|
||||
>Inicio</a
|
||||
>
|
||||
<a
|
||||
class="text-sm font-medium hover:text-primary transition-colors text-slate-700 dark:text-slate-300"
|
||||
href="index.html#manifiesto"
|
||||
>Manifiesto</a
|
||||
>
|
||||
<a
|
||||
class="text-sm font-medium hover:text-primary transition-colors text-slate-700 dark:text-slate-300"
|
||||
href="index.html#laboratorio"
|
||||
>Laboratorio</a
|
||||
>
|
||||
<a
|
||||
class="text-sm font-medium text-primary hover:text-primary transition-colors"
|
||||
href="huella.html"
|
||||
>Huella</a
|
||||
>
|
||||
<a
|
||||
class="text-sm font-medium hover:text-primary transition-colors text-slate-700 dark:text-slate-300"
|
||||
href="#contact"
|
||||
>Contacto</a
|
||||
>
|
||||
</nav>
|
||||
<div class="flex items-center gap-3">
|
||||
<!-- Language Switcher -->
|
||||
<div class="flex items-center gap-1.5 glass px-3 py-1.5 rounded-full text-xs font-bold">
|
||||
<span class="text-primary">ES</span>
|
||||
<span class="text-slate-300 dark:text-slate-700">|</span>
|
||||
<a href="index-ca.html" class="text-slate-600 dark:text-slate-400 hover:text-primary transition-colors" title="Català">CA</a>
|
||||
<span class="text-slate-300 dark:text-slate-700">|</span>
|
||||
<a href="index-en.html" class="text-slate-600 dark:text-slate-400 hover:text-primary transition-colors" title="English">EN</a>
|
||||
</div>
|
||||
<button
|
||||
class="size-10 flex items-center justify-center rounded-full glass hover:bg-white/60 dark:hover:bg-white/10 transition-colors"
|
||||
id="theme-toggle"
|
||||
onclick="toggleTheme()"
|
||||
aria-label="Toggle theme"
|
||||
>
|
||||
<span class="material-symbols-outlined text-xl dark:hidden">
|
||||
dark_mode
|
||||
</span>
|
||||
<span
|
||||
class="material-symbols-outlined text-xl hidden dark:block text-yellow-200"
|
||||
>
|
||||
light_mode
|
||||
</span>
|
||||
</button>
|
||||
<a
|
||||
class="bg-primary hover:bg-primary/90 text-white px-6 py-2.5 rounded-full text-sm font-bold transition-all shadow-md shadow-primary/25 flex items-center justify-center"
|
||||
href="index.html#contact"
|
||||
>
|
||||
Hablemos
|
||||
</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="mx-auto max-w-4xl px-6 py-12 md:py-20 grow">
|
||||
|
||||
<!-- Back Link -->
|
||||
<div class="mb-8">
|
||||
<a
|
||||
href="index.html"
|
||||
class="inline-flex items-center gap-2 text-sm text-slate-500 dark:text-slate-400 hover:text-primary transition-colors"
|
||||
>
|
||||
<span class="material-symbols-outlined text-sm">arrow_back</span>
|
||||
Volver al inicio
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Page Title -->
|
||||
<div class="mb-16">
|
||||
<h1 class="text-4xl md:text-6xl font-serif italic mb-6 text-slate-900 dark:text-white leading-tight">
|
||||
Huella ecológica &<br />
|
||||
<span class="text-primary not-italic font-display font-black tracking-tighter">Decisiones</span> técnicas
|
||||
</h1>
|
||||
<p class="text-lg md:text-xl text-slate-600 dark:text-slate-400 leading-relaxed max-w-2xl">
|
||||
Medición del impacto ambiental de este sitio web y desglose de los compromisos pragmáticos adoptados durante su desarrollo.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Impact Metrics Grid -->
|
||||
<div class="grid md:grid-cols-3 gap-6 mb-16">
|
||||
<div class="p-6 glass rounded-2xl border border-white/20 flex flex-col justify-between">
|
||||
<div>
|
||||
<span class="material-symbols-outlined text-primary text-3xl mb-3 block">weight</span>
|
||||
<h3 class="font-bold mb-2 dark:text-white">Peso de la página</h3>
|
||||
<p class="text-3xl font-bold text-primary mb-2">~245 KB</p>
|
||||
<p class="text-xs text-slate-500 dark:text-slate-400 leading-relaxed">
|
||||
Total de datos transferidos en la primera carga completa del sitio. La media web mundial actual supera los 2.5 MB.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-6 glass rounded-2xl border border-white/20 flex flex-col justify-between">
|
||||
<div>
|
||||
<span class="material-symbols-outlined text-primary text-3xl mb-3 block">co2</span>
|
||||
<h3 class="font-bold mb-2 dark:text-white">Emisiones estimadas</h3>
|
||||
<p class="text-3xl font-bold text-primary mb-2">0.05 g</p>
|
||||
<p class="text-xs text-slate-500 dark:text-slate-400 leading-relaxed">
|
||||
CO2 generado por visita, medido con la fórmula estandarizada de Sustainable Web Design. El promedio web es de 0.5g.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-6 glass rounded-2xl border border-white/20 flex flex-col justify-between">
|
||||
<div>
|
||||
<span class="material-symbols-outlined text-primary text-3xl mb-3 block">nest_eco_leaf</span>
|
||||
<h3 class="font-bold mb-2 dark:text-white">Servidor ecológico</h3>
|
||||
<p class="text-3xl font-bold text-emerald-500 mb-2">100%</p>
|
||||
<p class="text-xs text-slate-500 dark:text-slate-400 leading-relaxed">
|
||||
La web está alojada en Hetzner (Helsinki), un centro de datos alimentado por energía libre de carbono (eólica e hidráulica).
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Anti-Purismo / Compromisos Imperfectos -->
|
||||
<section class="space-y-8 mb-20">
|
||||
<div class="border-l-4 border-primary pl-6">
|
||||
<h2 class="text-2xl md:text-3xl font-serif italic dark:text-white">El recordatorio anti-purismo</h2>
|
||||
<p class="text-sm text-slate-500 dark:text-slate-400 mt-1">El pragmatismo frente a la teoría absoluta</p>
|
||||
</div>
|
||||
|
||||
<p class="text-slate-700 dark:text-slate-300 leading-relaxed">
|
||||
En el desarrollo de software responsable, es fácil caer en el purismo de querer reducir la transferencia a cero absoluto o rechazar cualquier recurso externo. Sin embargo, para que el código sea sostenible también debe ser mantenible y útil. Aquí documento las decisiones imperfectas pero justificadas tomadas en esta web:
|
||||
</p>
|
||||
|
||||
<div class="space-y-6">
|
||||
<!-- Compromiso 1: Tailwind CSS por CDN -->
|
||||
<div class="p-6 bg-white/20 dark:bg-white/5 border border-white/10 rounded-2xl">
|
||||
<h3 class="font-bold text-lg dark:text-white mb-2 flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-amber-500">warning</span>
|
||||
Tailwind CSS mediante CDN de desarrollo
|
||||
</h3>
|
||||
<p class="text-sm text-slate-600 dark:text-slate-400 leading-relaxed mb-3">
|
||||
<strong>El problema:</strong> El script de Tailwind en línea pesa en torno a los 100 KB transferidos y compila las clases en el navegador del usuario en tiempo real. Compilar y empaquetar CSS en producción reduciría este peso a unos 10-15 KB.
|
||||
</p>
|
||||
<p class="text-sm text-slate-700 dark:text-slate-300 leading-relaxed">
|
||||
<strong>El compromiso:</strong> Elegimos mantener este sitio sin pasos de construcción (no-build setup) para garantizar que cualquiera pueda descargar el código, hacer doble clic en <code>index.html</code> y ver el sitio funcionando al instante sin instalar dependencias de Node.js. Esto prolonga la vida útil del código (impide la obsolescencia de dependencias de build) a cambio de un ligero impacto de descarga.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Compromiso 2: Tipografías e Iconos Autoalojados (Self-Hosted) -->
|
||||
<div class="p-6 bg-emerald-500/10 border border-emerald-500/20 rounded-2xl">
|
||||
<h3 class="font-bold text-lg dark:text-white mb-2 flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-emerald-500">verified</span>
|
||||
Tipografías e Iconos 100% Autoalojados (Zero Third-Party Requests)
|
||||
</h3>
|
||||
<p class="text-sm text-slate-600 dark:text-slate-400 leading-relaxed mb-3">
|
||||
<strong>La optimización:</strong> Hemos migrado tanto las fuentes (<em>Space Grotesk</em> e <em>Instrument Serif</em>) como la iconografía (<em>Material Symbols Outlined</em>) a nuestra propia infraestructura (`/fonts`).
|
||||
</p>
|
||||
<p class="text-sm text-slate-700 dark:text-slate-300 leading-relaxed">
|
||||
<strong>El impacto:</strong> Eliminamos por completo el rastreo y las llamadas DNS externas a Google Fonts, garantizando soberanía de datos, estricto cumplimiento del RGPD y menor latencia en el renderizado inicial.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Certificación Green Software Foundation -->
|
||||
<div class="p-6 bg-emerald-500/10 border border-emerald-500/20 rounded-2xl flex flex-col md:flex-row items-start md:items-center justify-between gap-4">
|
||||
<div>
|
||||
<div class="flex items-center gap-2 text-xs font-bold uppercase tracking-wider text-emerald-600 dark:text-emerald-400 mb-1">
|
||||
<span class="material-symbols-outlined text-sm">workspace_premium</span>
|
||||
Acreditación Oficial Green Software Foundation
|
||||
</div>
|
||||
<h3 class="font-bold text-lg dark:text-white mb-1">
|
||||
Green Software Practitioner
|
||||
</h3>
|
||||
<p class="text-xs text-slate-600 dark:text-slate-400 leading-relaxed">
|
||||
Credential ID: <code>b80fdaac-96a2-48e1-9170-7a84c0297295</code> — Certificación en patrones de diseño sostenible y reducción de emisiones de software.
|
||||
</p>
|
||||
</div>
|
||||
<a
|
||||
href="https://badges.greensoftware.foundation/awards/b80fdaac-96a2-48e1-9170-7a84c0297295"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="inline-flex items-center gap-2 px-4 py-2.5 bg-emerald-600 text-white rounded-xl text-xs font-bold hover:bg-emerald-700 transition-colors shrink-0 shadow-md shadow-emerald-600/20"
|
||||
>
|
||||
<span class="material-symbols-outlined text-sm">verified</span>
|
||||
Verificar Credencial
|
||||
<span class="material-symbols-outlined text-xs">open_in_new</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="pb-12" id="contact">
|
||||
<div class="mx-auto max-w-6xl px-6">
|
||||
<div
|
||||
class="glass rounded-[3rem] p-12 md:p-24 text-center relative overflow-hidden"
|
||||
>
|
||||
<div class="absolute top-0 right-0 p-8 pointer-events-none">
|
||||
<span
|
||||
class="material-symbols-outlined text-primary/20 dark:text-primary/10 text-9xl"
|
||||
>alternate_email</span
|
||||
>
|
||||
</div>
|
||||
<h2
|
||||
class="text-5xl md:text-7xl font-serif italic mb-8 relative z-10 text-slate-900 dark:text-white"
|
||||
>
|
||||
¿Hablamos?
|
||||
</h2>
|
||||
<p
|
||||
class="text-xl text-slate-600 dark:text-slate-400 mb-12 max-w-xl mx-auto relative z-10"
|
||||
>
|
||||
Si buscas tecnología ligera, accesible y con los pies en la tierra — o simplemente quieres hablar de soberanía digital — escríbeme.
|
||||
</p>
|
||||
<div
|
||||
class="flex flex-col md:flex-row justify-center gap-6 relative z-10"
|
||||
>
|
||||
<a
|
||||
class="bg-slate-900 dark:bg-slate-800 text-white px-10 py-5 rounded-2xl font-bold flex items-center justify-center gap-3 hover:bg-slate-800 dark:hover:bg-slate-700 transition-colors shadow-2xl"
|
||||
href="mailto:itziar.zameza.garcia@gmail.com"
|
||||
>
|
||||
<span class="material-symbols-outlined">mail</span>
|
||||
Enviar un Email
|
||||
</a>
|
||||
<div class="flex gap-4 justify-center">
|
||||
<a
|
||||
class="size-16 glass rounded-full flex items-center justify-center hover:text-primary transition-colors"
|
||||
href="https://www.linkedin.com/in/itziarzamezagarcia"
|
||||
title="LinkedIn"
|
||||
>
|
||||
<span class="material-symbols-outlined text-2xl">link</span>
|
||||
</a>
|
||||
<a
|
||||
class="size-16 glass rounded-full flex items-center justify-center hover:text-primary transition-colors"
|
||||
href="https://github.com/itziarZG"
|
||||
title="GitHub"
|
||||
>
|
||||
<span class="material-symbols-outlined text-2xl"
|
||||
>share</span
|
||||
>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mt-16 flex flex-col md:flex-row justify-between items-center gap-6 text-slate-400 dark:text-slate-500 text-sm"
|
||||
>
|
||||
<p>© 2026 Itziar ZG.</p>
|
||||
<div class="flex gap-8">
|
||||
<a
|
||||
class="hover:text-primary transition-colors text-slate-600 dark:text-slate-400"
|
||||
href="https://www.linkedin.com/in/itziarzamezagarcia"
|
||||
>LinkedIn</a
|
||||
>
|
||||
<a
|
||||
class="hover:text-primary transition-colors text-slate-600 dark:text-slate-400"
|
||||
href="https://github.com/itziarZG"
|
||||
>GitHub</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Scripts -->
|
||||
<script src="js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
+1320
File diff suppressed because it is too large
Load Diff
+1320
File diff suppressed because it is too large
Load Diff
+749
-354
File diff suppressed because it is too large
Load Diff
+64
-3
@@ -58,20 +58,37 @@ function scrollToContact() {
|
||||
// Modal Management
|
||||
// ============================================
|
||||
|
||||
/** Element that had focus before a modal was opened */
|
||||
let _modalPreviousFocus = null;
|
||||
|
||||
/**
|
||||
* Open modal by project ID
|
||||
* Open modal by project ID.
|
||||
* Saves the currently focused element and moves focus into the modal.
|
||||
* @param {string} projectId - The project identifier
|
||||
*/
|
||||
function openModal(projectId) {
|
||||
const modal = document.getElementById(projectId + "-modal");
|
||||
if (modal) {
|
||||
_modalPreviousFocus = document.activeElement;
|
||||
modal.classList.remove("hidden");
|
||||
document.body.style.overflow = "hidden";
|
||||
|
||||
// Move focus to the first focusable element inside the modal
|
||||
const focusable = modal.querySelectorAll(
|
||||
'button, [href], input, select, textarea, [contenteditable="true"], [tabindex]:not([tabindex="-1"])'
|
||||
);
|
||||
if (focusable.length > 0) {
|
||||
focusable[0].focus();
|
||||
} else {
|
||||
modal.setAttribute("tabindex", "-1");
|
||||
modal.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close modal by project ID
|
||||
* Close modal by project ID.
|
||||
* Restores focus to the element that was focused before the modal opened.
|
||||
* @param {string} projectId - The project identifier
|
||||
*/
|
||||
function closeModal(projectId) {
|
||||
@@ -79,9 +96,52 @@ function closeModal(projectId) {
|
||||
if (modal) {
|
||||
modal.classList.add("hidden");
|
||||
document.body.style.overflow = "auto";
|
||||
|
||||
// Return focus to the element that triggered the modal
|
||||
if (_modalPreviousFocus) {
|
||||
_modalPreviousFocus.focus();
|
||||
_modalPreviousFocus = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Laboratorio Tabs Management
|
||||
// ============================================
|
||||
|
||||
/**
|
||||
* Initialize tabs behavior for the Laboratorio section
|
||||
*/
|
||||
function initTabs() {
|
||||
const tabButtons = document.querySelectorAll(".tab-btn");
|
||||
const tabPanes = document.querySelectorAll(".tab-pane");
|
||||
|
||||
tabButtons.forEach((btn) => {
|
||||
btn.addEventListener("click", () => {
|
||||
const targetTab = btn.getAttribute("data-tab");
|
||||
|
||||
// Update button styles to reflect active/inactive state
|
||||
tabButtons.forEach((b) => {
|
||||
b.classList.remove("bg-primary", "text-white", "shadow-md", "shadow-primary/25");
|
||||
b.classList.add("text-slate-600", "dark:text-slate-400", "hover:bg-slate-100", "dark:hover:bg-white/5");
|
||||
});
|
||||
btn.classList.add("bg-primary", "text-white", "shadow-md", "shadow-primary/25");
|
||||
btn.classList.remove("text-slate-600", "dark:text-slate-400", "hover:bg-slate-100", "dark:hover:bg-white/5");
|
||||
|
||||
// Show the selected tab content and hide the others
|
||||
tabPanes.forEach((pane) => {
|
||||
if (pane.id === `tab-${targetTab}`) {
|
||||
pane.classList.remove("hidden");
|
||||
pane.classList.add("block");
|
||||
} else {
|
||||
pane.classList.remove("block");
|
||||
pane.classList.add("hidden");
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Event Listeners
|
||||
// ============================================
|
||||
@@ -102,6 +162,7 @@ document.addEventListener("keydown", function (event) {
|
||||
});
|
||||
|
||||
/**
|
||||
* Initialize theme as early as possible
|
||||
* Initialize theme and tabs
|
||||
*/
|
||||
initTheme();
|
||||
initTabs();
|
||||
|
||||
-674
@@ -1,674 +0,0 @@
|
||||
/* Reset and Base Styles */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* CSS Custom Properties for Theming */
|
||||
:root {
|
||||
--primary: #3b82f6;
|
||||
--primary-light: #60a5fa;
|
||||
--background: #0a0a0a;
|
||||
--surface: #171717;
|
||||
--text-primary: #ffffff;
|
||||
--text-secondary: #94a3b8;
|
||||
--border: rgba(255, 255, 255, 0.1);
|
||||
--accent: #8b5cf6;
|
||||
--glass-bg: rgba(255, 255, 255, 0.03);
|
||||
--glass-border: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
/* Dark mode - when html has class="dark" */
|
||||
html.dark {
|
||||
--background: #ffffff;
|
||||
--surface: #f8fafc;
|
||||
--text-primary: #0a0a0a;
|
||||
--text-secondary: #64748b;
|
||||
--border: rgba(0, 0, 0, 0.1);
|
||||
--glass-bg: rgba(0, 0, 0, 0.03);
|
||||
--glass-border: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* Typography */
|
||||
body {
|
||||
font-family:
|
||||
"Inter",
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
"Segoe UI",
|
||||
Roboto,
|
||||
sans-serif;
|
||||
line-height: 1.6;
|
||||
background-color: var(--background);
|
||||
color: var(--text-primary);
|
||||
transition:
|
||||
background-color 0.3s ease,
|
||||
color 0.3s ease;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
/* Layout Classes */
|
||||
.container {
|
||||
max-width: 72rem;
|
||||
margin: 0 auto;
|
||||
padding: 0 1.5rem;
|
||||
}
|
||||
|
||||
/* Navigation */
|
||||
.nav {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
z-index: 50;
|
||||
border-bottom: 1px solid var(--border);
|
||||
background-color: var(--background);
|
||||
backdrop-filter: blur(12px);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.nav-content {
|
||||
max-width: 72rem;
|
||||
margin: 0 auto;
|
||||
padding: 0 1.5rem;
|
||||
height: 4rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.nav-logo {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.025em;
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
display: none;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.nav-links a {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
color: var(--text-secondary);
|
||||
text-decoration: none;
|
||||
transition: color 0.15s ease;
|
||||
}
|
||||
|
||||
.nav-links a:hover {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.nav-contact {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 9999px;
|
||||
border: 1px solid var(--border);
|
||||
text-decoration: none;
|
||||
color: var(--text-primary);
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
.nav-contact:hover {
|
||||
background-color: var(--text-primary);
|
||||
color: var(--background);
|
||||
}
|
||||
|
||||
/* Hero Section */
|
||||
.hero {
|
||||
position: relative;
|
||||
padding-top: 8rem;
|
||||
padding-bottom: 5rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.hero-bg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.hero-glow-1 {
|
||||
position: absolute;
|
||||
top: -10%;
|
||||
left: -10%;
|
||||
width: 40%;
|
||||
height: 40%;
|
||||
background-color: rgba(59, 130, 246, 0.2);
|
||||
border-radius: 50%;
|
||||
filter: blur(120px);
|
||||
}
|
||||
|
||||
.hero-glow-2 {
|
||||
position: absolute;
|
||||
bottom: 10%;
|
||||
right: -10%;
|
||||
width: 30%;
|
||||
height: 30%;
|
||||
background-color: rgba(139, 92, 246, 0.2);
|
||||
border-radius: 50%;
|
||||
filter: blur(120px);
|
||||
}
|
||||
|
||||
.hero-content {
|
||||
max-width: 72rem;
|
||||
margin: 0 auto;
|
||||
padding: 0 1.5rem;
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.hero-title {
|
||||
font-size: 3.75rem;
|
||||
font-weight: 800;
|
||||
letter-spacing: -0.025em;
|
||||
margin-bottom: 1.5rem;
|
||||
background: linear-gradient(to right, #ffffff, #94a3b8, #ffffff);
|
||||
background-size: 200% 200%;
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
color: transparent;
|
||||
animation: subtle-gradient 8s ease infinite;
|
||||
}
|
||||
|
||||
.hero-subtitle {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 500;
|
||||
color: var(--primary);
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.hero-description {
|
||||
max-width: 42rem;
|
||||
margin: 0 auto 3rem;
|
||||
font-size: 1.125rem;
|
||||
line-height: 1.625;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.hero-actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
padding: 1rem 2rem;
|
||||
background-color: #ffffff;
|
||||
color: var(--background);
|
||||
font-weight: 600;
|
||||
border-radius: 0.5rem;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
padding: 1rem 2rem;
|
||||
background-color: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
color: var(--text-primary);
|
||||
font-weight: 600;
|
||||
border-radius: 0.5rem;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
.btn-tertiary {
|
||||
padding: 1rem 2rem;
|
||||
background-color: transparent;
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: color 0.15s ease;
|
||||
}
|
||||
|
||||
.btn-tertiary:hover {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
/* Section Styles */
|
||||
.section {
|
||||
padding: 6rem 0;
|
||||
}
|
||||
|
||||
.section-dark {
|
||||
background-color: var(--surface);
|
||||
}
|
||||
|
||||
.section-header {
|
||||
text-align: center;
|
||||
margin-bottom: 4rem;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 1.875rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.section-subtitle {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
/* About Section */
|
||||
.about-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 4rem;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.about-content h2 {
|
||||
font-size: 1.875rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.about-content p {
|
||||
color: var(--text-secondary);
|
||||
font-size: 1.125rem;
|
||||
line-height: 1.625;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.about-quote {
|
||||
padding: 1.5rem;
|
||||
background: var(--glass-bg);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 1rem;
|
||||
border-left: 4px solid var(--primary);
|
||||
}
|
||||
|
||||
.about-quote p {
|
||||
font-style: italic;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.capabilities-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.capability-card {
|
||||
padding: 1rem;
|
||||
background: var(--glass-bg);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 0.75rem;
|
||||
}
|
||||
|
||||
.capability-icon {
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
background-color: rgba(59, 130, 246, 0.1);
|
||||
border-radius: 0.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.capability-icon.purple {
|
||||
background-color: rgba(139, 92, 246, 0.1);
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.capability-card h3 {
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.capability-card p {
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
/* Projects Section */
|
||||
.projects-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.project-card {
|
||||
position: relative;
|
||||
background-color: var(--surface);
|
||||
border-radius: 1rem;
|
||||
border: 1px solid var(--border);
|
||||
overflow: hidden;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
.project-card:hover {
|
||||
border-color: rgba(59, 130, 246, 0.3);
|
||||
}
|
||||
|
||||
.project-image {
|
||||
height: 12rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 4rem;
|
||||
}
|
||||
|
||||
.project-image-1 {
|
||||
background: linear-gradient(
|
||||
to bottom right,
|
||||
rgba(37, 99, 235, 0.2),
|
||||
rgba(147, 51, 234, 0.2)
|
||||
);
|
||||
}
|
||||
|
||||
.project-image-2 {
|
||||
background: linear-gradient(
|
||||
to bottom right,
|
||||
rgba(16, 185, 129, 0.2),
|
||||
rgba(37, 99, 235, 0.2)
|
||||
);
|
||||
}
|
||||
|
||||
.project-image-3 {
|
||||
background: linear-gradient(
|
||||
to bottom right,
|
||||
rgba(147, 51, 234, 0.2),
|
||||
rgba(219, 39, 119, 0.2)
|
||||
);
|
||||
}
|
||||
|
||||
.project-content {
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.project-tags {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.project-tag {
|
||||
font-size: 10px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
font-weight: 700;
|
||||
padding: 0.25rem 0.5rem;
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
border-radius: 0.25rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.project-card h3 {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 0.75rem;
|
||||
transition: color 0.15s ease;
|
||||
}
|
||||
|
||||
.project-card:hover h3 {
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.project-card p {
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.625;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.project-link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
color: var(--primary);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.project-link svg {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
/* Tech Stack Section */
|
||||
.tech-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.tech-item {
|
||||
padding: 1.5rem;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
.tech-item:hover {
|
||||
border-color: rgba(59, 130, 246, 0.5);
|
||||
background-color: rgba(59, 130, 246, 0.05);
|
||||
}
|
||||
|
||||
.tech-icon {
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 1rem;
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.tech-icon.emerald {
|
||||
color: #10b981;
|
||||
}
|
||||
|
||||
.tech-name {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
.footer {
|
||||
padding: 6rem 0;
|
||||
border-top: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.footer-content {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.footer h2 {
|
||||
font-size: 2.25rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.footer p {
|
||||
color: var(--text-secondary);
|
||||
font-size: 1.125rem;
|
||||
margin-bottom: 3rem;
|
||||
max-width: 36rem;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.footer-links {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.footer-link {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
text-decoration: none;
|
||||
transition: color 0.15s ease;
|
||||
}
|
||||
|
||||
.footer-link:hover {
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.footer-divider {
|
||||
display: none;
|
||||
width: 1px;
|
||||
height: 1.5rem;
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.footer-copyright {
|
||||
margin-top: 6rem;
|
||||
font-size: 0.875rem;
|
||||
color: var(--text-secondary);
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
/* Custom Animations */
|
||||
@keyframes subtle-gradient {
|
||||
0% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
50% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
100% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
}
|
||||
|
||||
/* Custom scrollbar */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: var(--background);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: var(--primary);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--primary-light);
|
||||
}
|
||||
|
||||
/* Media Queries */
|
||||
@media (min-width: 768px) {
|
||||
.hero {
|
||||
padding-top: 12rem;
|
||||
padding-bottom: 8rem;
|
||||
}
|
||||
|
||||
.hero-title {
|
||||
font-size: 6rem;
|
||||
}
|
||||
|
||||
.hero-subtitle {
|
||||
font-size: 1.875rem;
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.hero-actions {
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.about-grid {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
|
||||
.capabilities-grid {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
|
||||
.projects-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.footer-links {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.footer-divider {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.projects-grid {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
|
||||
.tech-grid {
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
/* Theme Toggle Button */
|
||||
.theme-toggle {
|
||||
position: fixed;
|
||||
bottom: 2rem;
|
||||
right: 2rem;
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
background-color: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s ease;
|
||||
z-index: 50;
|
||||
}
|
||||
|
||||
.theme-toggle:hover {
|
||||
background-color: var(--primary);
|
||||
color: white;
|
||||
}
|
||||
Reference in New Issue
Block a user