PageSourceSearch

https://vicentemiguel.com/

html vicentemiguel.com collected 2026-09-29 02:23:34 UTC 51,879 bytes, 871 lines download raw bytes

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>&copy; 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.