1<!DOCTYPE html> 2<html lang="en"> 3<head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <!-- TÃtulo --> 7 <title>Vicente Miguel | Animation</title> 8 9 <!-- Tailwind CSS --> 10
10<script src="https://cdn.tailwindcss.com"></script>
10 11 12 <!-- Library for Metadata (EXIF/IPTC/XMP) --> 13
13<script src="https://cdn.jsdelivr.net/npm/exifr/dist/full.umd.js"></script>
13 14 15 <!-- Fonts --> 16 <link rel="preconnect" href="https://fonts.googleapis.com"> 17 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> 18 <!-- Fonts: Libre Baskerville, Cormorant Garamond, Playfair Display, Inter --> 19 <link href="https://fonts.googleapis.com/css2?family=Libre+Baskerville:ital,wght@0,400;0,700;1,400&family=Cormorant+Garamond:ital,wght@0,300;0,400;0,500;0,600;1,300;1,400&family=Playfair+Display:ital,wght@0,400;0,600;1,400&family=Inter:wght@300;400;500&display=swap" rel="stylesheet"> 20 21 <!-- Icons --> 22
22<script src="https://unpkg.com/lucide@latest"></script>
22 23 24
24<script> 25 tailwind.config = { 26 theme: { 27 extend: { 28 fontFamily: { 29 sans: ['Inter', 'sans-serif'], 30 serif: ['Playfair Display', 'serif'], 31 cormorant: ['Cormorant Garamond', 'serif'], 32 classic: ['Libre Baskerville', 'serif'], 33 }, 34 colors: { 35 brand: { 36 black: '#1a1a1a', 37 gray: '#64748b', 38 light: '#f1f5f9', 39 white: '#ffffff', 40 accent: '#0d9488', 41 ice: '#045d6b', /* CAMBIO: Nuevo Teal RGB(4, 93, 107) -> #045d6b */ 42 darker: '#0f172a', 43 deep: '#020617', 44 } 45 }, 46 animation: { 47 'fade-in': 'fadeIn 1s ease-out forwards', 48 'slide-up': 'slideUp 1s cubic-bezier(0.16, 1, 0.3, 1) forwards', 49 'bounce-slow': 'bounceSlow 1.5s infinite', 50 }, 51 keyframes: { 52 fadeIn: { 53 '0%': { opacity: '0' }, 54 '100%': { opacity: '1' }, 55 }, 56 slideUp: { 57 '0%': { opacity: '0', transform: 'translateY(30px)' }, 58 '100%': { opacity: '1', transform: 'translateY(0)' }, 59 }, 60 bounceSlow: { 61 '0%, 100%': { transform: 'translateY(0)', animationTimingFunction: 'cubic-bezier(0.8, 0, 1, 1)' }, 62 '50%': { transform: 'translateY(25%)', animationTimingFunction: 'cubic-bezier(0, 0, 0.2, 1)' }, 63 } 64 } 65 } 66 } 67 } 68 </script>
68 69 <style> 70 html { scroll-behavior: auto; } 71 /* Fondo body unificado */ 72 body { overflow-x: hidden; padding-bottom: 0; background-color: #045d6b; color: #ffffff; } 73 74 ::-webkit-scrollbar { width: 8px; } 75 ::-webkit-scrollbar-track { background: #064e5a; } 76 ::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 4px; } 77 ::-webkit-scrollbar-thumb:hover { background: #94a3b8; } 78 79 ::selection { background: #ffffff; color: #045d6b; } 80 81 /* --- NAV STYLES --- */ 82 .nav-item { 83 position: relative; 84 padding-bottom: 4px; 85 transition: all 0.3s ease; 86 color: #ffffff; 87 font-weight: 400; 88 } 89 .nav-line { 90 position: absolute; 91 bottom: 0; 92 left: 0; 93 width: 0; 94 height: 2px; 95 background-color: #ffffff; 96 transition: width 0.4s cubic-bezier(0.22, 1, 0.36, 1); 97 } 98 .nav-item.active { font-weight: 700; } 99 .nav-item.active .nav-line { width: 100%; } 100 .nav-item:not(.active):hover { opacity: 0.7; } 101 102 /* --- MOBILE MENU --- */ 103 #mobile-menu { 104 transition: max-height 0.4s cubic-bezier(0.4, 0, 0.2, 1); 105 max-height: 0; 106 overflow: hidden; 107 } 108 #mobile-menu.open { max-height: 400px; } 109 110 /* --- PARALLAX LINES --- */ 111 .parallax-line { position: absolute; top: -20%; bottom: -20%; width: 1px; background: linear-gradient(to bottom, transparent, rgba(255,255,255,0.1), transparent); z-index: 0; pointer-events: none; } 112 113 /* --- CAROUSEL ANIMATION --- */ 114 @keyframes scroll { 115 0% { transform: translateX(0); } 116 100% { transform: translateX(-50%); } 117 } 118 .carousel-track { 119 display: flex; 120 width: max-content; 121 animation: scroll 60s linear infinite; 122 } 123 124 .card-description { 125 font-size: 1.05rem; 126 line-height: 1.5; 127 } 128 129 /* Lightbox: móvil apaisado â ficha más ancha, video proporcional */ 130 @media (orientation: landscape) and (max-height: 520px) { 131 #lightbox-container:not(.lightbox-compact) { 132 flex-direction: row !important; 133 height: 92dvh !important; 134 max-height: 92dvh; 135 } 136 .lightbox-video-panel { 137 width: 68% !important; 138 height: 100% !important; 139 aspect-ratio: auto !important; 140 } 141 .lightbox-info-panel { 142 width: 32% !important; 143 height: 100% !important; 144 min-width: 130px; 145 padding: 1rem 1.1rem !important; 146 } 147 .lightbox-info-panel h2 { 148 font-size: 0.95rem !important; 149 } 150 .lightbox-info-panel .lightbox-desc { 151 font-size: 0.7rem !important; 152 line-height: 1.45 !important; 153 } 154 } 155 156 /* Lightbox compacto (3D IA): escritorio â ficha 1/3 y misma altura que el video */ 157 @media (min-width: 768px) { 158 .lightbox-compact { 159 align-items: stretch !important; 160 } 161 .lightbox-compact .lightbox-video-panel, 162 .lightbox-compact .lightbox-info-panel { 163 height: 100% !important; 164 aspect-ratio: auto !important; 165 align-self: stretch !important; 166 } 167 } 168 </style> 169</head> 170<body class="bg-brand-ice text-white font-sans antialiased relative"> 171 172 <!-- PARALLAX LINES --> 173 <div class="fixed inset-0 pointer-events-none z-0 overflow-hidden"> 174 <div class="parallax-line left-[20%] translate-y-0 transition-transform duration-100 ease-out" id="line1"></div> 175 <div class="parallax-line left-[50%] translate-y-0 transition-transform duration-100 ease-out" id="line2"></div> 176 <div class="parallax-line left-[80%] translate-y-0 transition-transform duration-100 ease-out" id="line3"></div> 177 </div> 178 179 <!-- Navigation --> 180 <nav id="navbar" class="fixed w-full z-50 transition-all duration-500 py-3.5 px-6 lg:px-12"> 181 <div class="absolute inset-0 -z-10 bg-brand-ice/90 backdrop-blur-md shadow-sm"></div> 182 183 <div class="max-w-7xl mx-auto flex justify-between items-center relative z-50"> 184 <!-- LOGO AREA --> 185 <div class="flex flex-col items-start group drop-shadow-none"> 186 <a href="#home" class="font-classic text-lg tracking-widest hover:opacity-80 transition-opacity nav-trigger leading-none text-white block font-bold"> 187 vicentemiguel.com 188 </a>
189 <span class="text-[0.65rem] text-white/60 tracking-[0.15em] uppercase mt-1 font-medium opacity-80 leading-none"> 190 Animation 191 </span> 192 </div> 193 194 <!-- LINKS --> 195 <div class="hidden md:flex gap-12 text-[11px] font-light tracking-[0.2em] uppercase text-white"> 196 <a href="#home" class="nav-item nav-trigger active" id="link-home">Home<span class="nav-line"></span></a> 197 <a href="#development" class="nav-item nav-trigger" id="link-development">Development<span class="nav-line"></span></a> 198 <a href="#reels" class="nav-item nav-trigger" id="link-reels">Reels<span class="nav-line"></span></a> 199 <a href="#3d-animation" class="nav-item nav-trigger" id="link-3d-animation">3D Animation<span class="nav-line"></span></a> 200 <a href="#contact" class="nav-item nav-trigger" id="link-contact">Contact<span class="nav-line"></span></a> 201 </div> 202 203 <!-- CONTACT ICONS (Desktop) --> 204 <div class="hidden md:flex items-center gap-4 text-white"> 205 <a href="mailto:[email protected]" title="Email" class="hover:text-brand-accent transition-colors hover:scale-110 transform duration-300"> 206 <i data-lucide="mail" class="w-5 h-5"></i> 207 </a> 208 <!-- LinkedIn Icon Added --> 209 <a href="https://www.linkedin.com/in/v-miguel-66a410170/" target="_blank" title="LinkedIn" class="hover:text-brand-accent transition-colors hover:scale-110 transform duration-300"> 210 <i data-lucide="linkedin" class="w-5 h-5"></i> 211 </a> 212 </div> 213 214 <!-- Mobile Menu Button --> 215 <button class="md:hidden text-white p-2 focus:outline-none" onclick="toggleMobileMenu()"> 216 <i data-lucide="menu" class="w-6 h-6"></i> 217 </button> 218 </div> 219 220 <!-- Mobile Menu --> 221 <div id="mobile-menu" class="absolute top-full left-0 w-full flex flex-col items-center z-40 bg-brand-ice shadow-2xl border-t border-white/10"> 222 <div class="w-full flex flex-col"> 223 <a href="#home" class="text-white/70 hover:text-white hover:bg-white/10 text-sm font-medium tracking-widest uppercase nav-trigger py-4 w-full text-center transition-colors border-b border-white/10" onclick="toggleMobileMenu()">Home</a> 224 <a href="#development" class="text-white/70 hover:text-white hover:bg-white/10 text-sm font-medium tracking-widest uppercase nav-trigger py-4 w-full text-center transition-colors border-b border-white/10" onclick="toggleMobileMenu()">Development</a> 225 <a href="#reels" class="text-white/70 hover:text-white hover:bg-white/10 text-sm font-medium tracking-widest uppercase nav-trigger py-4 w-full text-center transition-colors border-b border-white/10" onclick="toggleMobileMenu()">Reels</a> 226 <a href="#3d-animation" class="text-white/70 hover:text-white hover:bg-white/10 text-sm font-medium tracking-widest uppercase nav-trigger py-4 w-full text-center transition-colors border-b border-white/10" onclick="toggleMobileMenu()">3D Animation</a> 227 <a href="#contact" class="text-white/70 hover:text-white hover:bg-white/10 font-medium text-sm tracking-widest uppercase nav-trigger py-4 w-full text-center transition-colors border-b border-white/10" onclick="toggleMobileMenu()">Contact</a> 228 229 <!-- Mobile Social Icons --> 230 <div class="flex items-center justify-center gap-6 py-6 text-white"> 231 <a href="mailto:[email protected]" class="hover:text-brand-accent transition-colors"> 232 <i data-lucide="mail" class="w-6 h-6"></i> 233 </a> 234 <!-- LinkedIn Icon Mobile --> 235 <a href="https://www.linkedin.com/in/v-miguel-66a410170/" target="_blank" class="hover:text-brand-accent transition-colors"> 236 <i data-lucide="linkedin" class="w-6 h-6"></i> 237 </a> 238 </div> 239 </div> 240 </div> 241 </nav> 242 243 <!-- HERO SECTION --> 244 <header id="home" class="relative h-[100svh] flex flex-col items-center bg-brand-ice overflow-hidden z-10 section-spy"> 245 <!-- BACKGROUND VIDEO --> 246 <div class="absolute top-0 left-0 right-0 bottom-[250px] md:bottom-[340p
246x] z-0 overflow-hidden"> 247 <video autoplay muted loop playsinline class="w-full h-full object-cover opacity-100 relative z-0"> 248 <source src="cut_BB.mov" type="video/quicktime"> 249 <source src="cut_BB.mov" type="video/mp4"> 250 </video> 251 252 <!-- CAMBIO: Tinte ligero añadido de nuevo (usando el color de la marca para integrar) --> 253 <div class="absolute inset-0 bg-brand-ice/40 z-1 pointer-events-none mix-blend-multiply"></div> 254 255 <div class="absolute inset-x-0 bottom-0 h-48 bg-gradient-to-t from-brand-ice to-transparent z-10"></div> 256 </div> 257 258 <!-- TITLE --> 259 <div class="relative w-full max-w-7xl mx-auto text-center flex flex-col items-center justify-end flex-grow z-10 px-6 h-full pb-[270px] md:pb-[370px] pointer-events-none"> 260 <!-- TITULO BLANCO (Contraste sobre fondo oscuro) --> 261 <h2 class="font-serif text-[4vw] md:text-5xl text-white italic tracking-tight opacity-0 animate-slide-up relative z-10 drop-shadow-2xl leading-tight pointer-events-auto group flex flex-wrap justify-center items-end gap-x-3 md:gap-x-5" style="animation-delay: 0.2s"> 262 <span>Let's create something together?</span> 263 <!-- BOTÃN BLANCO, FLECHA MARCA --> 264 <span id="hero-scroll-btn" class="inline-flex items-center justify-center w-6 h-6 md:w-9 md:h-9 bg-white rounded-full cursor-pointer hover:scale-110 transition-all duration-300 nav-trigger group/btn translate-y-[-2px]"> 265 <i data-lucide="arrow-down" class="w-3 h-3 md:w-4 md:h-4 text-brand-ice animate-bounce-slow stroke-[2.5]"></i> 266 </span> 267 </h2> 268 </div> 269 270 <!-- CAROUSEL WRAPPER --> 271 <div class="absolute bottom-[10px] w-full flex flex-col items-center z-10 opacity-0 animate-slide-up pb-0 pointer-events-auto" style="animation-delay: 0.3s"> 272 <div class="w-full overflow-hidden mb-0 relative z-10"> 273 <div class="carousel-track gap-4 md:gap-6 px-4" id="hero-carousel"> 274 <!-- Images via JS --> 275 </div> 276 </div> 277 </div> 278 </header> 279 280 <!-- GLOBAL SCROLL ARROW --> 281 <div id="global-scroll-arrow" class="fixed bottom-20 right-6 z-50 transition-all duration-500 cursor-pointer opacity-0 bg-gray-900/10 backdrop-blur-md rounded-full p-2 shadow-2xl hover:bg-gray-900/20 animate-bounce pointer-events-none"> 282 <i data-lucide="chevron-down" class="w-6 h-6 text-white drop-shadow-md"></i> 283 </div> 284 285 <!-- DEVELOPMENT SECTION --> 286 <section id="development" class="w-full mx-auto px-6 pt-4 pb-12 bg-brand-ice scroll-mt-24 section-spy z-10 relative border-t border-white/10 md:border-none"> 287 <div class="max-w-4xl mx-auto text-center mb-16"> 288 <!-- TÃtulo Principal (BLANCO) --> 289 <span class="text-white text-4xl md:text-6xl font-bold tracking-tight uppercase block opacity-90 mt-[45px] mb-2">DEVELOPMENT</span> 290 291 <!-- SubtÃtulo (GRIS CLARO) --> 292 <h2 class="font-serif text-2xl md:text-3xl text-gray-300 italic font-light">Animation Direction</h2> 293 </div> 294 <!-- Grid: 4 columns, gap-1 (4px), dense flow for asymmetry --> 295 <div id="development-grid" class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-1 w-full grid-flow-dense"></div> 296 </section> 297 298 <!-- REELS SECTION --> 299 <section id="reels" class="w-full mx-auto px-6 pt-24 pb-24 bg-brand-ice scroll-mt-40 section-spy z-10 relative border-t border-white/10 md:border-none"> 300 <div class="max-w-4xl mx-auto text-center mb-16"> 301 <!-- TÃtulo Principal (BLANCO) --> 302 <span class="text-white text-4xl md:text-6xl font-bold tracking-tight uppercase block opacity-90 mb-2">REELS</span> 303 304 <!-- SubtÃtulo (GRIS CLARO) --> 305 <h2 class="font-serif text-2xl md:text-3xl text-gray-300 italic font-light">Lead Animator</h2> 306 </div> 307 <!-- Grid: 4 columns, gap-1 (4px), dense flow for asymmetry --> 308 <div id="reels-grid" class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-1 w-full grid-flow-dense"></div> 309 </section> 310 311 <!-- 3D ANIMATION SECTION --> 312 <section id="3d-animation" class="w-full mx-auto pt-24 pb-24 bg-brand-ice scroll-mt-40 section-spy z-10 relative border-t border-white/10 md:border-none"> 313 <div class="max-w-4xl mx-auto text-center mb-16 px-6">
314 <span class="text-white text-4xl md:text-6xl font-bold tracking-tight uppercase block opacity-90 mb-2">3D ANIMATION</span> 315 </div> 316 <div id="3d-animation-grid" class="w-full flex flex-col gap-8 md:gap-10"></div> 317 </section> 318 319 <!-- CONTACT SECTION --> 320 <!-- CAMBIO: Fondo unificado con el resto (bg-brand-ice) --> 321 <section id="contact" class="py-32 px-6 text-center bg-brand-ice relative scroll-mt-32 section-spy z-10 overflow-hidden border-t border-white/10 md:border-none"> 322 <div class="max-w-4xl mx-auto relative z-10"> 323 <!-- Texto cambiado a blanco/gris claro --> 324 <span class="text-white text-4xl md:text-6xl font-bold tracking-tight uppercase mb-6 block opacity-90">CONTACT</span> 325 <h2 class="font-serif text-2xl md:text-3xl text-gray-300 italic font-light mb-12">Let's create something together?</h2> 326 327 <div class="flex flex-col md:flex-row items-center justify-center gap-8 mb-12"> 328 <!-- Email Item (Dark Mode) --> 329 <a href="mailto:[email protected]" class="group flex items-center gap-4 hover:bg-white/10 p-4 rounded-lg transition-all duration-500 hover:shadow-lg border border-transparent hover:border-white/20"> 330 <div class="w-12 h-12 rounded-full border border-gray-400 flex items-center justify-center group-hover:bg-white group-hover:border-transparent transition-colors duration-500"> 331 <!-- Icono se invierte a color marca al hover --> 332 <i data-lucide="mail" class="w-5 h-5 text-gray-200 group-hover:text-brand-ice"></i> 333 </div> 334 <div class="flex flex-col items-start"> 335 <span class="text-[10px] uppercase tracking-widest text-gray-400 opacity-80 mb-1">Email</span> 336 <span class="font-sans text-lg md:text-xl font-light text-gray-100 transition-colors relative"> 337 [email protected] 338 <span class="absolute bottom-0 left-0 w-full h-[1px] bg-white scale-x-0 group-hover:scale-x-100 transition-transform origin-left duration-500"></span> 339 </span> 340 </div> 341 </a> 342 343 </div> 344 345 <div class="mt-4"> 346 <p class="text-[11px] font-bold tracking-[0.2em] uppercase bg-white text-brand-ice inline-block px-3 py-1 font-bold">Remote Work</p> 347 </div> 348 349 <footer class="mt-32 text-[10px] text-gray-400 tracking-[0.2em] uppercase border-t border-white/10 pt-8 w-1/2 mx-auto"> 350 <p>© 2025 vicentemiguel.com</p> 351 </footer> 352 </div> 353 </section> 354 355 <!-- Cookie Banner --> 356 <div id="cookie-banner" class="fixed bottom-8 right-4 md:bottom-8 md:right-8 max-w-sm w-[90%] bg-white border border-gray-200 p-5 shadow-2xl rounded-sm z-[1000] transform translate-y-20 opacity-0 transition-all duration-500 flex flex-col gap-4"> 357 <div class="text-[11px] text-gray-600 leading-relaxed font-light"> 358 <p>Utilizamos cookies para mejorar tu experiencia. Al continuar navegando, aceptas su uso.</p> 359 </div> 360 <div class="flex justify-end gap-3"> 361 <button id="reject-cookies" class="px-4 py-2 bg-gray-200 text-gray-600 text-[9px] font-bold tracking-widest uppercase rounded-sm hover:bg-gray-300 transition-all">Rechazar</button> 362 <button id="accept-cookies" class="px-4 py-2 bg-brand-ice text-white text-[9px] font-bold tracking-widest uppercase rounded-sm hover:bg-[#034650] transition-all hover:shadow-sm">Aceptar</button> 363 </div> 364 </div> 365 366 <!-- Lightbox --> 367 <div id="lightbox" class="fixed inset-0 z-[100] bg-black/80 hidden backdrop-blur-sm opacity-0 transition-opacity duration-500 flex items-center justify-center p-4 md:p-12"> 368 <!-- Contenedor del Lightbox --> 369 <div id="lightbox-container" class="flex flex-col md:flex-row bg-white shadow-2xl rounded-lg overflow-hidden h-auto md:h-[85vh] w-[95vw] max-w-[95vw] relative z-[110]"> 370 <!-- Contenido inyectado por JS --> 371 </div> 372 </div> 373 374
374<script> 375 document.addEventListener('DOMContentLoaded', () => { 376 document.body.style.overflow = 'auto'; 377 lucide.createIcons(); 378 document.addEventListener('contextmenu', (event) => event.preventDefault()); 379 380 // --- 1. DATA DEFINITIONS --- 381 382 // Files Arrays 383 // 1. Defined Reels Content manually to include specific titles and descriptions 384 const reelsContent = [ 385 { 386 file: "1_Black is Beltza-Lead_Animator.mov", 387 thumb: "1_CAPTURA-Black_is_Beltza_Lead_Animator.jpg", 388 title: "Lead Animator, Feature Film", 389 desc: "Animation Lead and Assistant Director on \"Black is Beltza\".<br>Quirino Awards (2019) 2018, winner of Best Sound Design and Original Music.<br>Gaudà Awards (2019) 2018, nominee for Best Animated Film." 390 }, 391 { 392 file: "2_Games_Animation.mov", 393 thumb: "2-CAPTURA-Games_Animation.jpg", 394 title: "Video Game Animation", 395 desc: "Animation for the video game \"Death or Treat\"" 396 }, 397 { 398 file: "3_FX_Animation.mov", 399 thumb: "3-CAPTURA-FX_Animation.jpg", 400 title: "2D FX Animation, Feature Film", 401 desc: "Animation for \"Space Jam 2\": 2D FX, Highlights, and Elmer's Shadow designs" 402 }, 403 { 404 file: "4_Animation_Direction.mov", 405 thumb: "4_CAPTURA-Animation_Direction.jpg", 406 title: "Animation Direction", 407 desc: "Nominated for Best Animated Short Film at the 2019 Lola Gaos Awards" 408 }, 409 { 410 file: "5_Feature_Lead_Animation.mov", 411 thumb: "5-CAPTURA-Feature_Lead_Animation.jpg", 412 title: "Animation Lead, Feature Film", 413 desc: "Goya Awards (2019) 2018, nominee for Best Animated Film<br>Gaudà Awards (2019) 2018, winner for Best Animated Film<br>Platino Awards for Ibero-American Cinema, nominee for Best Animated Film<br>Málaga Film Festival 2018 - Official Selection, nominee for Best Film - Golden Biznaga<br>Cinema Writers Circle Medals, nominee for Best Animated Film" 414 }, 415 { 416 file: "6_Totte_and_Lotte-Senior_Animator.mov", 417 thumb: "6-CAPTURA-Totte_and_Lotte-Senior_Animator.jpg", 418 title: "Senior Animator, Feature Film", 419 desc: "Lotte & Totte 2025" 420 }, 421 { 422 file: "7_Mocap_New_York_Short.mov", 423 thumb: "7-CAPTURA-Mocap_New _York _Short.jpg", 424 title: "Lead Animator", 425 desc: "2024 Short Film Mocap, New York" 426 }, 427 { 428 file: "8_Cartoon_Animation.mov", 429 thumb: "8-CAPTURA-Cartoon_Animation.jpg", 430 title: "Cartoon Animation", 431 desc: "I have a soft spot for the cartoon style." 432 }, 433 { 434 file: "9_Artificial_Intelligence_Animation.mov", 435 thumb: "9-CAPTURAArtificial_Intelligence_Animation.jpg", 436 title: "Artificial Intelligence Animation", 437 desc: "We can now develop 2D, 3D, or fiction animation projects with AI at very low costs." 438 }, 439 { 440 file: "10_Lead_Animation_Credits.mov", 441 thumb: "10-CAPTURALead_Animation_Credits.jpg", 442 title: "Credits", 443 desc: "Lead Animator of a team of several dozen professionals on \"Black is Beltza\".<br>Quirino Awards (2019) 2018, winner of Best Sound Design and Original Music.<br>Gaudà Awards (2019) 2018, nominee for Best Animated Film." 444 } 445 ]; 446 447 const developmentContent = [ 448 { 449 file: "1_Art_Direction.mov", 450 thumb: "1_CAPTURA-Art-Direction.jpg",
451 title: "Art Direction", 452 desc: "Nominated for Best Animated Short Film at the 2019 Lola Gaos Awards, I created all the layouts, including character development, color, composition, and style, as well as directing the animation team." 453 }, 454 { 455 file: "2_Artificial_Intelligence _Development.mov", 456 thumb: "2_CAPTURA-Artificial_Intelligence_Development.jpg", 457 title: "Development with Artificial Intelligence", 458 desc: "Project for UPV University 2025, where I used AI to clean up recordings of passersby, generating characters and animations with AI in layers that were assembled in After Effects." 459 }, 460 { 461 file: "3_Artificial_Intelligence_Animation.mov", 462 thumb: "3_CAPTURA-Artificial_Intelligence_Animation.jpg", 463 title: "Animation with Artificial Intelligence", 464 desc: "Study of textured characters in illustration and watercolor styles, generated and animated with AI for an institutional short film." 465 }, 466 { 467 file: "4_Animatic_Shorts_Development.mov", 468 thumb: "4_CAPTURA-Animatic_Shorts_Development.jpg", 469 title: "Animatics", 470 desc: "2018 animatic for a Short Film." 471 }, 472 { 473 file: "5_Diddle_Animatic.mov", 474 thumb: "5-CAPTURA-Diddle_Animatic.jpg", 475 title: "Animatic for Diddl", 476 desc: "Animatic for the Thomas Goletz Series in 2020." 477 }, 478 { 479 file: "6_Lead_Layouts_and_Direction_Assintance.mov", 480 thumb: "6-CAPTURA-Lead_Layouts_and_Direction_Assintance.jpg", 481 title: "Lead Layout Designer and Assistant Director \"Black is Beltza\" Feature Film", 482 desc: "Quirino Awards (2019) 2018, winner of Best Sound Design and Original Music. Gaudà Awards (2019) 2018, nominated for Best Animated Film. I carried out a large part of the layout design and oversaw the layouts as Lead Layout Designer and Assistant Director on \"Black is Beltza\"." 483 }, 484 { 485 file: "7_Shorts_Development.mov", 486 thumb: "7-CAPTURA-Shorts_Development.jpg", 487 title: "Shorts Development", 488 desc: "Extensive experience in Character Design, Layouts, Animation, and Compositing for numerous projects, mostly Spanish. These include Kukuxumusu and TV teasers." 489 }, 490 { 491 file: "8_The_Cid_Posing_Layoutman.mov", 492 thumb: "8-CAPTURA-The_Cid_Posing_Layoutman.jpg", 493 title: "Layout Artist on \"The Cid, the Legend, Feature Film\"", 494 desc: "Goya Awards (2004) 2003, winner of Best Animated Film. Annecy Film Festival (2004) Cristal Award nominee for Best Feature Film. I created posing layouts for the main characters." 495 } 496 ]; 497 498 // Helper Functions (Not strictly needed for titles anymore but good for fallback) 499 function cleanTitle(filename) { 500 let name = filename.replace(/\.[^/.]+$/, ""); 501 name = name.replace(/^\d+[-_.\s]+/, ''); 502 name = name.replace(/[-_]/g, ' '); 503 return name.replace(/\s+/g, ' ').trim(); 504 } 505 506 // 2. PROCESS DATA ITEMS 507 const reelsItems = reelsContent.map((item, index) => ({ 508 id: 100 + index, // IDs 100-199 509 title: item.title, 510 longDescription: item.desc, 511 videoSrc: `reels/Lead_Animation/${item.file}`, 512 thumbSrc: `reels/Lead_Animation/${item.thumb}` 513 })); 514 515 const developmentItems = developmentContent.map((item, index) => ({ 516 id: 200 + index, // IDs 200-299 517 title: item.title, 518 longDescription: item.desc, 519 videoSrc: `development/artdirection/${item.file}`, 520 thumbSrc: `development/artdirection/${item.thumb}` 521 })); 522 523 const animation3dItems = [{ 524 id: 300, 525 title: "3D Animation Show Reel", 526 longDescription: "3D animation reel created in Maya, featuring Layout assembly, lighting, and rendering in Arnold. I have also worked with motion capture; my Mocap Reel is available upon request via email.", 527 videoSrc: "3danimation/3D.mov", 528 thumbSrc: "3danimation/reel_cap.jpg" 529 }, { 530 id: 301, 531 title: "3D Animation with AI", 532 longDescription: "3D animation produced with AI, built from a custom layout of my own creationâcombining original art direction with AI-assisted workflow.", 533 videoSrc: "3danimation/3d-ia.mp4", 534 thumbSrc: "3danimation/bg_ai.jpg",
535 compactPlayer: true 536 }]; 537 538 // Variables de estado para Lightbox 539 let currentList = []; 540 let currentIndex = 0; 541 let currentSectionTitle = ""; 542 543 // 3. DOM ELEMENTS & RENDER LOGIC 544 const reelsGrid = document.getElementById('reels-grid'); 545 const devGrid = document.getElementById('development-grid'); 546 const animation3dGrid = document.getElementById('3d-animation-grid'); 547 const lightbox = document.getElementById('lightbox'); 548 const lightboxContainer = document.getElementById('lightbox-container'); 549 550 function renderCard(item, index) { 551 // CAMBIO: Lógica de Grid perfecta 552 // Reels (ID < 200): 10 items. Ãndices 0 y 5 grandes. 553 // Development (ID >= 200): 8 items. Todos iguales (col-span-1) para que sea un cuadro perfecto de 4x2. 554 555 let gridClasses = 'md:col-span-1 md:row-span-1'; 556 557 if (item.id < 200) { 558 // REELS (10 items) 559 // Hacer 0 y 5 grandes crea un patrón que rellena 4 columnas x 4 filas exactamente (16 slots) 560 if (index === 0 || index === 5) { 561 gridClasses = 'md:col-span-2 md:row-span-2'; 562 } 563 } else { 564 // DEVELOPMENT (8 items) 565 // Mantener todos iguales (1x1) rellena 4 columnas x 2 filas exactamente (8 slots) 566 gridClasses = 'md:col-span-1 md:row-span-1'; 567 } 568 569 const titleSize = gridClasses.includes('col-span-2') ? 'text-xl' : 'text-sm'; 570 const descSize = gridClasses.includes('col-span-2') ? 'text-base' : 'text-xs'; 571 572 // Usamos un div temporal para limpiar tags HTML de la descripcion en la tarjeta (hover) 573 const tempDiv = document.createElement("div"); 574 tempDiv.innerHTML = item.longDescription; 575 const cleanDesc = tempDiv.textContent || tempDiv.innerText || ""; 576 577 return ` 578 <div class="group relative cursor-pointer overflow-hidden w-full aspect-video ${gridClasses}" onclick="window.openProject(${item.id})"> 579 <!-- Image --> 580 <img src="${item.thumbSrc}" alt="${item.title}" class="w-full h-full object-cover transition-transform duration-700 group-hover:scale-105 brightness-95 group-hover:brightness-100"> 581 582 <!-- Play Icon (Subtle) --> 583 <div class="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-300 z-10 pointer-events-none"> 584 <div class="w-14 h-14 rounded-full bg-black/30 backdrop-blur-sm flex items-center justify-center border border-white/40 shadow-xl"> 585 <i data-lucide="play" class="w-6 h-6 ml-1 text-white fill-white"></i> 586 </div> 587 </div> 588 589 <!-- Hover Overlay --> 590 <div class="absolute bottom-0 left-0 right-0 p-6 opacity-0 group-hover:opacity-100 transition-all duration-500 bg-white/50 backdrop-blur-md z-20 translate-y-full group-hover:translate-y-0"> 591 <div> 592 <h3 class="${titleSize} font-bold text-gray-900 uppercase tracking-wide mb-1 leading-tight">${item.title}</h3> 593 <p class="${descSize} text-gray-800 font-serif leading-relaxed card-description line-clamp-2"> 594 ${cleanDesc} 595 </p> 596 </div> 597 </div> 598 </div> 599 `; 600 } 601 602 function renderReels() { 603 reelsGrid.innerHTML = reelsItems.map((item, index) => renderCard(item, index)).join(''); 604 } 605 606 function renderDevelopment() { 607 devGrid.innerHTML = developmentItems.map((item, index) => renderCard(item, index)).join(''); 608 } 609 610 function render3dAnimation() { 611 const previewWidths = ['w-full md:w-[70%]', 'w-[92%] md:w-[45%]']; 612 const titleSizes = ['text-xl md:text-2xl', 'text-lg md:text-xl']; 613 const descSizes = ['text-sm md:text-base', 'text-xs md:text-sm']; 614 615 animation3dGrid.innerHTML = animation3dItems.map((item, index) => ` 616 <div class="w-full bg-black"> 617 <div class="group relative cursor-pointer overflow-hidden mx-auto ${previewWidths[index]}" onclick="window.openProject(${item.id})"> 618 <img src="${item.thumbSrc}" alt="${item.title}" class="block w-full h-auto object-cover transition-transform duration-700 group-hover:scale-105"> 619 620 <div class="absolute inset-0 bg-gradient-to-t from-black/60 via-black/10 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div> 621 622 <div class="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-300 z-10 pointer-events-none">
623 <div class="w-14 h-14 rounded-full bg-black/30 backdrop-blur-sm flex items-center justify-center border border-white/40 shadow-xl"> 624 <i data-lucide="play" class="w-6 h-6 ml-1 text-white fill-white"></i> 625 </div> 626 </div> 627 628 <div class="absolute bottom-0 left-0 right-0 p-4 md:p-6 opacity-0 group-hover:opacity-100 transition-all duration-500 bg-white/50 backdrop-blur-md z-20 translate-y-full group-hover:translate-y-0"> 629 <h3 class="${titleSizes[index]} font-bold text-gray-900 uppercase tracking-wide leading-tight mb-1">${item.title}</h3> 630 <p class="${descSizes[index]} text-gray-800 font-serif leading-relaxed">${item.longDescription}</p> 631 </div> 632 </div> 633 </div> 634 `).join(''); 635 } 636 637 // 4. EXECUTE RENDER 638 renderReels(); 639 renderDevelopment(); 640 render3dAnimation(); 641 lucide.createIcons(); 642 643 // 5. CAROUSEL & COOKIES 644 const carouselTrack = document.getElementById('hero-carousel'); 645 const workImages = [ 646 "1-Lotte_&_Totte.jpg", "2-Death_or_Treat.jpg", "3-PobreDiablo.jpg", 647 "4-Jimmy_and_the_Magic_Key.jpg", "5-Descendants_The_Royal_Wedding.jpg", 648 "6-Space_Jam.jpg", "7-Obsolescence.jpg", "8-Buñuel_en_el_laberinto_de_las_tortugas.jpg", 649 "9-Black_is_Beltza.jpg", "10-Memorias_de_un_hombre_en _pijama.jpg", "11-El_Cid.jpg", 650 "12-BugWatch.jpg", "13-Tales_of_Little_Grey_Rabbit_Serie_TV.jpg", "14-Captain_Pugwash.jpg", 651 "15-Club_Babala.jpg", "16-Ogu_y_Mampato.jpg", "17-Pumby_Serie_TV.jpg" 652 ]; 653 654 const carouselContent = [...workImages, ...workImages].map(img => { 655 const safeName = encodeURIComponent(img.trim()).replace(/%20/g, ' '); 656 const srcPath = `trabajos/${safeName}`; 657 return ` 658 <div class="flex-none w-[160px] md:w-[220px] aspect-[2/3] relative rounded-[7px] overflow-hidden bg-transparent"> 659 <img src="${srcPath}" alt="" class="w-full h-full object-cover" loading="lazy"> 660 </div> 661 `; 662 }).join(''); 663 carouselTrack.innerHTML = carouselContent; 664 665 const cookieBanner = document.getElementById('cookie-banner'); 666 const acceptBtn = document.getElementById('accept-cookies'); 667 const rejectBtn = document.getElementById('reject-cookies'); 668 669 if (!localStorage.getItem('cookiePreference')) { 670 setTimeout(() => { cookieBanner.classList.remove('translate-y-20', 'opacity-0'); }, 2000); 671 } 672 673 acceptBtn.addEventListener('click', () => { localStorage.setItem('cookiePreference', 'accepted'); hideBanner(); }); 674 rejectBtn.addEventListener('click', () => { localStorage.setItem('cookiePreference', 'rejected'); hideBanner(); }); 675 function hideBanner() { cookieBanner.classList.add('translate-y-20', 'opacity-0'); setTimeout(() => cookieBanner.remove(), 500); } 676 677 // 6. LIGHTBOX & SCROLL EVENTS 678 679 // Función para actualizar el contenido del lightbox según el Ãndice actual 680 function updateLightboxContent() { 681 const item = currentList[currentIndex]; 682 if (!item) return; 683 684 const isCompactPlayer = !!item.compactPlayer; 685 const videoPanelWidth = isCompactPlayer ? 'md:w-2/3' : 'md:w-5/6'; 686 const infoPanelWidth = isCompactPlayer ? 'md:w-1/3' : 'md:w-1/6'; 687 const videoAspect = isCompactPlayer ? '' : ' aspect-video'; 688 689 lightboxContainer.className = isCompactPlayer 690 ? 'lightbox-compact flex flex-col md:flex-row bg-white shadow-2xl rounded-lg overflow-hidden h-auto md:h-[45vh] w-[95vw] max-w-[95vw] md:max-w-[62vw] relative z-[110]' 691 : 'flex flex-col md:flex-row bg-white shadow-2xl rounded-lg overflow-hidden h-auto md:h-[85vh] w-[95vw] max-w-[95vw] relative z-[110]'; 692 693 let contentHtml = ` 694 <div class="lightbox-video-panel w-full ${videoPanelWidth} h-[35vh] md:h-full${videoAspect} bg-black relative flex-shrink-0 flex items-center justify-center"> 695 ${item.videoSrc ? ` 696 <video controls controlsList="nodownload" disablePictureInPicture autoplay class="w-full h-full object-contain${isCompactPlayer ? ' md:max-w-[90%] md:max-h-[90%]' : ''}"> 697 <source src="${item.videoSrc}" type="video/mp4"> 698 <source src="${item.videoSrc}" type="video/quicktime"> 699 Your browser does not support the video tag. 700 </video>` : `<div class="w-full h-full flex items-center justify-center text-white">No Content</div>`} 701 </div> 702 703 <div class="lightbox-info-panel w-full ${infoPanelWidth} h-auto md:h-full bg-white p-5 md:p-6 overflow-y-auto border-l border-gray-100 flex flex-col relative min-w-0"> 704 705 <button id="close-lightbox-btn" onclick="document.getElementById('close-lightbox').click()" class="absolute top-3 right-3 text-gray-400 hover:bg-red-50 hover:text-red-600 rounded-full p-1.5 transition-colors z-10" title="Close"> 706 <i data-lucide="x" class="w-4 h-4 stroke-[2]"></i> 707 </button> 708 709 <div class="flex flex-col gap-3 mb-5 pr-6">
710 <span class="text-[8px] font-bold uppercase tracking-widest text-black leading-tight">${currentSectionTitle}</span> 711 <div class="flex items-center gap-0.5"> 712 <button onclick="navigateProject(-1)" class="text-brand-darker hover:bg-gray-100 rounded-full p-1.5 transition-colors" title="Previous"> 713 <i data-lucide="chevron-left" class="w-3.5 h-3.5 stroke-[2]"></i> 714 </button> 715 <button onclick="navigateProject(1)" class="text-brand-darker hover:bg-gray-100 rounded-full p-1.5 transition-colors" title="Next"> 716 <i data-lucide="chevron-right" class="w-3.5 h-3.5 stroke-[2]"></i> 717 </button> 718 </div> 719 </div> 720 721 <div class="mt-0"> 722 <div class="mb-4 pb-4 border-b border-gray-100"> 723 <span class="text-[8px] uppercase tracking-widest text-gray-400 mb-2 block">Project</span> 724 <h2 class="text-base md:text-lg font-serif text-brand-darker leading-snug">${item.title}</h2> 725 </div> 726 727 <div class="lightbox-desc text-xs text-gray-600 font-light leading-relaxed"> 728 <p>${item.longDescription}</p> 729 </div> 730 </div> 731 </div> 732 `; 733 lightboxContainer.innerHTML = contentHtml; 734 lucide.createIcons(); 735 } 736 737 window.openProject = (id) => { 738 // Determinar a qué lista pertenece el ID 739 if (id >= 300) { 740 currentList = animation3dItems; 741 currentSectionTitle = "3D Animation"; 742 currentIndex = animation3dItems.findIndex(i => i.id === id); 743 } else if (id >= 200) { 744 currentList = developmentItems; 745 // TÃtulo de sección acortado 746 currentSectionTitle = "Animation Direction"; 747 currentIndex = developmentItems.findIndex(i => i.id === id); 748 } else { 749 currentList = reelsItems; 750 // TÃtulo de sección acortado 751 currentSectionTitle = "Lead Animator"; 752 currentIndex = reelsItems.findIndex(i => i.id === id); 753 } 754 755 if (currentIndex !== -1) { 756 updateLightboxContent(); 757 lightbox.classList.remove('hidden'); 758 void lightbox.offsetWidth; 759 lightbox.classList.remove('opacity-0'); 760 document.body.style.overflow = 'hidden'; 761 } 762 }; 763 764 window.navigateProject = (direction) => { 765 currentIndex += direction; 766 767 // Bucle infinito dentro de la lista actual 768 if (currentIndex < 0) currentIndex = currentList.length - 1; 769 if (currentIndex >= currentList.length) currentIndex = 0; 770 771 updateLightboxContent(); 772 }; 773 774 const closeLightbox = () => { 775 lightbox.classList.add('opacity-0'); 776 setTimeout(() => { 777 lightbox.classList.add('hidden'); 778 lightboxContainer.innerHTML = ''; 779 document.body.style.overflow = 'auto'; 780 }, 500); 781 }; 782 783 document.getElementById('lightbox').addEventListener('click', (e) => { 784 if (e.target.id === 'lightbox') closeLightbox(); 785 }); 786 787 const dummyClose = document.createElement('button'); 788 dummyClose.id = 'close-lightbox'; 789 dummyClose.style.display = 'none'; 790 dummyClose.addEventListener('click', closeLightbox); 791 document.body.appendChild(dummyClose); 792 793 window.toggleMobileMenu = () => { document.getElementById('mobile-menu').classList.toggle('open'); }; 794 795 // --- DEPURACIÃN SCROLL SUAVE (Usando requestAnimationFrame) --- 796 function easeInOutCubic(t) { return t < .5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1; } 797 798 function smoothScrollToPos(to, duration) { 799 const element = document.documentElement || document.body; 800 const start = element.scrollTop; 801 const change = to - start; 802 const startDate = +new Date(); 803 804 // Animación fluida usando requestAnimationFrame 805 function animateScroll() { 806 const currentDate = +new Date(); 807 const currentTime = currentDate - startDate; 808 809 if (currentTime < duration) { 810 const val = easeInOutCubic(currentTime / duration); 811 element.scrollTop = start + change * val; 812 requestAnimationFrame(animateScroll); 813 } else { 814 element.scrollTop = to; 815 } 816 } 817 animateScroll(); 818 } 819 820 function smoothScrollTo(target, duration) { 821 const targetEl = target === '#home' ? document.body : document.getElementById(target.slice(1)); 822 if (!targetEl) return; 823 const headerOffset = target === '#home' ? 0 : (window.innerWidth < 768 ? 20 : 40); 824 const targetPos = target === '#home' ? 0 : (targetEl.getBoundingClientRect().top + window.scrollY - headerOffset); 825 smoothScrollToPos(targetPos, duration); 826 } 827 828 document.querySelectorAll('.nav-trigger').forEach(link => { link.addEventListener('click', (e) => { const href = link.getAttribute('href'); if (href && href.startsWith('#')) { e.preventDefault(); smoothScrollTo(href, 1000); } }); }); 829 document.getElementById('hero-scroll-btn')?.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); smoothScrollTo('#development', 1000); }); 830 document.getElementById('global-scroll-arrow')?.addEventListener('click', () => { smoothScrollToPos(window.scrollY + window.innerHeight, 1000); }); 831 832 // Scroll Events 833 const line1 = document.getElementById('line1'); const line2 = document.getElementById('line2'); const line3 = document.getElementById('line3'); 834 const globalArrow = document.getElementById('global-scroll-arrow'); 835 window.addEventListener('scroll', () => { 836 const scrollY = window.scrollY; 837 if(line1) line1.style.transform = `translateY(${scrollY * 0.2}px)`; 838 if(line2) line2.style.transform = `translateY(${scrollY * 0.5}px)`; 839 if(line3) line3.style.transform = `translateY(${scrollY * 0.3}px)`; 840 841 const windowHeight = window.innerHeight; const documentHeight = document.body.offsetHeight; 842 if (windowHeight + scrollY < documentHeight - 100) { 843 if (scrollY > windowHeight * 0.5) { globalArrow.classList.remove('opacity-0', 'pointer-events-none'); globalArrow.classList.add('pointer-events-auto'); } 844 else { globalArrow.classList.add('opacity-0', 'pointer-events-none'); globalArrow.classList.remove('pointer-events-auto'); } 845 } else { globalArrow.classList.add('opacity-0', 'pointer-events-none'); globalArrow.classList.remove('pointer-events-auto'); } 846 847 if (window.scrollY < 100) { document.querySelectorAll('.nav-item').forEach(nav => nav.classList.remove('active')); document.getElementById('link-home').classList.add('active'); } 848 }); 849 850 // Intersection Observer 851 const observer = new IntersectionObserver((entries) => { 852 if (window.scrollY < 100) return;
853 entries.forEach(entry => { 854 if (entry.isIntersecting) { 855 document.querySelectorAll('.nav-item').forEach(nav => nav.classList.remove('active')); 856 const id = entry.target.getAttribute('id'); 857 if (id === 'home') document.getElementById('link-home')?.classList.add('active'); 858 if (id === 'reels') document.getElementById('link-reels')?.classList.add('active'); 859 if (id === '3d-animation') document.getElementById('link-3d-animation')?.classList.add('active'); 860 if (id === 'development') document.getElementById('link-development')?.classList.add('active'); 861 if (id === 'contact') document.getElementById('link-contact')?.classList.add('active'); 862 } 863 }); 864 }, { rootMargin: '-50% 0px -50% 0px', threshold: 0 }); 865 866 document.querySelectorAll('.section-spy').forEach(section => observer.observe(section)); 867 if (window.scrollY < 100) { document.querySelectorAll('.nav-item').forEach(nav => nav.classList.remove('active')); document.getElementById('link-home').classList.add('active'); } 868 }); 869 </script>
869 870</body> 871</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.