PageSourceSearch

https://www.ciudaddelaluz.com/pages/WhyCVPage.js

js ciudaddelaluz.com collected 2026-10-02 12:56:41 UTC 62,375 bytes, 1,010 lines download raw bytes

1// pages/WhyCVPage.js
2
3import { contentService } from "../services/ContentService.js";
4import { filmographyService } from "../services/FilmographyService.js";
5import { i18n } from "../services/I18nService.js";
6import { seoService } from "../services/SeoService.js";
7import { incentivesService } from "../services/IncentivesService.js";
8import { mediaService } from "../services/MediaService.js";
9import { leadService } from "../services/LeadService.js";
10import { pressService } from "../services/PressService.js";
11import { AppConfig } from "../config/AppConfig.js";
12import { initCarousel, renderCarousel, renderPopup } from '../components/Carousel.js';
13
14//VARIABLES PARA ABREVIAR I18n Y LLAMADA GLOBAL
15const t = (key) => i18n.t(key);
16const overview = 'why-cv.overview';
17const data = 'why-cv.incentives';
18const filmography = 'why-cv.filmography';
19const guide = 'why-cv.production_guide';
20const connectivity = 'why-cv.connectivity';
21const investors = 'why-cv.investors';
22
23// --- 1. VISTA GENERAL DE LA SECCIÓN ---
24function renderWhyCVOverview() {
25
26    setTimeout(async () => {
27        const response = await contentService.getPageContent('why-cv');
28        const videoUrl = mediaService.getImageUrl(response?.conectivity_video?.url || '');
29        const videoPoster = mediaService.getImageUrl(response?.conectivity_video?.poster || '');
30
31        const titleContainer = document.getElementById('title-container');
32        if(titleContainer) {
33            titleContainer.innerHTML = `<h1 class="text-5xl md:text-7xl font-black text-cdl-blue mb-6 uppercase tracking-tighter italic leading-none">
34                ${response.title_1 || t(`${overview}.title_1`)} <br><span class="text-cdl-gold bg-clip-text bg-gradient-to-r from-cdl-gold to-yellow-600">${response.title_2 || t(`${overview}.title_2`)}</span>
35            </h1>
36            <p class="text-lg text-slate-500 max-w-3xl mx-auto leading-relaxed">
37                ${response.subtitle || t(`${overview}.subtitle`)}
38            </p>`
39        }
40
41        const content = document.getElementById('content');
42        if(content && response) {
43            content.innerHTML = `<div class="relative h-[500px] rounded-[2.5rem] overflow-hidden shadow-2xl mb-16 border-4 border-slate-100 group">
44                <video controls class="absolute inset-0 w-full h-full object-cover" poster="${videoPoster}">
45                    <source src="${videoUrl}" type="video/mp4">
46                    ${t('why-cv.connectivity.video_error')}
47                </video>
48
49                <div class="absolute inset-0 bg-gradient-to-br from-slate-800 to-black opacity-30 group-hover:opacity-10 transition-opacity duration-500 pointer-events-none"></div>
50            </div>
51
52            <div class="grid grid-cols-1 md:grid-cols-4 gap-8 mt-8 text-center">
53                
54                <div class="bg-white p-8 rounded-[2rem] border border-slate-100 shadow-lg hover:shadow-xl hover:-translate-y-1 transition duration-300 group">
55                    <div class="relative z-10 flex flex-col items-center h-full">
56                        <div class="w-16 h-16 bg-slate-50 rounded-2xl flex items-center justify-center mb-8 border border-slate-100 group-hover:scale-110 transition-transform duration-500 shadow-sm">
57                            <i data-lucide="${response.stats_card.icon || 'sun'}" class="w-8 h-8 text-amber-500"></i> 
58                        </div>
59                        
60                        <div class="flex items-baseline justify-center gap-1 mb-4">
61                            <span class="text-6xl md:text-7xl font-black text-cdl-blue tracking-tighter">${response.stats_card.value || '300'}</span>
62                            <span class="text-4xl font-bold text-cdl-gold">${response.stats_card.suffix || '+'}</span>
63                        </div>
64                        
65                        <div class="w-8 h-1 bg-slate-200 rounded-full mb-6 group-hover:w-16 group-hover:bg-cdl-gold transition-all duration-300"></div>
66                        
67                        <h3 class="text-slate-600 font-bold uppercase tracking-widest text-xs leading-relaxed">${response.stats_card.label || 'Días de sol al año'}</h3>
68                    </div>
69                </div>
70
71                ${response.conectivity_cards.map(c => `<div class="bg-white p-8 rounded-[2rem] border border-slate-100 shadow-lg hover:shadow-xl hover:-translate-y-1 transition duration-300 group">
72                    <div class="w-16 h-16 bg-blue-50 rounded-2xl flex items-center justify-center mx-auto mb-6 group-hover:bg-cdl-blue transition-colors">
73                        <i data-lucide="${c.lucide}" class="w-8 h-8 text-cdl-blue group-hover:text-white transition-colors"></i>
74                    </div>
75                    <h3 class="font-black text-xl text-slate-900 mb-2 uppercase tracking-tight">${c.title}</h3>
76                    <p class="text-slate-500 text-sm leading-relaxed">${c.desc}</p>
77                </div>`).join('')}
78            </div>`
79        }
80
81        lucide.createIcons();
82    }, 0)
83
84    return `
85    <div class="w-full bg-white">
86        
87        <div class="pt-32 pb-12 px-6 max-w-7xl mx-auto text-center">
88            <nav class="flex justify-center text-xs text-slate-400 mb-6 uppercase tracking-widest font-bold" aria-label="Breadcrumb">
89                <ol class="list-none p-0 inline-flex items-center gap-2">
90                    <li><a href="/" class="hover:text-cdl-blue transition">${t('actions.home')}</a></li>
91                    <li><span class="text-cdl-gold">/</span></li>
92                    <li><span class="text-cdl-blue">${t('why-cv.nav')}</span></li>
93                </ol>
94            </nav>
95            <div id="title-container">
96            
97            </div>
98        </div>
99
100        <div class="container mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 pb-24">
101
102            <div id="content">
103            
104            </div>
105
106            <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 mt-8">
107                <a href="/why-cv/production-guide" class="group !relative bg-blue-900 p-8 rounded-[2.5rem] shadow-xl hover:shadow-2xl hover:shadow-blue-900/30 transition-all duration-300 hover:-translate-y-2 flex flex-col overflow-hidden">
108                    <div class="absolute top-0 right-0 w-32 h-32 bg-white/5 rounded-full blur-3xl -translate-y-1/2 translate-x-1/2"></div>
109                    <div class="w-16 h-16 bg-white/10 backdrop-blur-sm rounded-2xl flex items-center justify-center mb-6 border border-white/20">
110                        <i data-lucide="book-open" class="w-8 h-8 text-white"></i>
111                    </div>
112                    <h3 class="text-2xl font-black text-white mb-2 uppercase tracking-tight">${t(`${guide}.title_1`)} ${t(`${guide}.title_2`)}</h3>
113                    <p class="text-blue-200 text-sm leading-relaxed mb-6 flex-grow">${t(`${guide}.subtitle`)}</p>
114                    <span class="text-xs font-bold text-white uppercase tracking-widest flex items-center gap-2">${t(`${guide}.action`)} <i data-lucide="arrow-right" class="w-4 h-4"></i></span>
115                </a>
116                
117                <a href="/why-cv/incentives" class="group bg-white p-8 rounded-[2.5rem] border border-slate-100 shadow-lg hover:shadow-2xl transition-all duration-300 hover:-translate-y-2 flex flex-col">
118                    <div class="w-16 h-16 bg-blue-50 rounded-2xl flex items-center justify-center mb-6 group-hover:bg-cdl-blue transition-colors">
119                        <i data-lucide="bar-chart-2" class="w-8 h-8 text-cdl-blue group-hover:text-white transition-colors"></i>
120                    </div>
121                    <h3 class="text-2xl font-black text-slate-800 mb-2 group-hover:text-cdl-blue uppercase tracking-tight">${t(`${data}.title_1`)} ${t(`${data}.title_2`)}</h3>
122                    <p class="text-slate-500 text-sm leading-relaxed mb-6 flex-grow">${t(`${data}.subtitle`)}</p>
123                    <span class="text-xs font-bold text-cdl-gold uppercase tracking-widest flex items-center gap-2">${t(`${data}.action`)} <i data-lucide="arrow-right" class="w-4 h-4"></i></span>
124                </a>
125
126
127                <a href="/why-cv/investors" class="group bg-slate-50 p-8 rounded-[2.5rem] border border-slate-200 shadow-lg hover:shadow-2xl transition-all duration-300 hover:-translate-y-2 flex flex-col">
128                    <div class="w-16 h-16 bg-white rounded-2xl flex items-center justify-center mb-6 shadow-sm">
129                        <i data-lucide="trending-up" class="w-8 h-8 text-slate-800"></i>
130                    </div>
131                    <h3 class="text-2xl font-black text-slate-800 mb-2 uppercase tracking-tight">${t(`${investors}.title`)}</h3>
132                    <p class="text-slate-500 text-sm leading-relaxed mb-6 flex-grow">${t(`${investors}.subtitle`)}</p>
133                    <span class="text-xs font-bold text-slate-800 uppercase tracking-widest flex items-center gap-2">${t(`${investors}.action`)} <i data-lucide="arrow-right" class="w-4 h-4"></i></span>
134                </a>
135            </div>
136
137        </div>
138    </div>`;
139}
140
141// --- 2. SUBPÁGINA 4.1: DATOS Y CIFRAS ---
142// --- 2. SUBPÁGINA 4.2: DATOS Y CIFRAS (PREMIUM DASHBOARD) ---
143function renderDataCard(data, index){
144    // Asignación de iconos dinámicos por si no vienen del backend
145    const icons = ['film', 'sun', 'trending-up', 'landmark', 'pie-chart', 'award'];
146    const icon = data.icon || icons[index % icons.length];
147
148    return `
149    <div class="relative bg-white p-10 rounded-[3rem] text-center border border-slate-100 shadow-xl hover:shadow-2xl hover:-translate-y-2 transition-all duration-500 overflow-hidden group">
150        <div class="absolute -top-10 -right-10 w-32 h-32 bg-blue-50 rounded-full blur-3xl group-hover:bg-blue-100 transition-colors duration-500 pointer-events-none"></div>
151        
152        <div class="relative z-10 flex flex-col items-center h-full">
153            <div class="w-16 h-16 bg-slate-50 rounded-2xl flex items-center justify-center mb-8 border border-slate-100 group-hover:scale-110 transition-transform duration-500 shadow-sm">
154                <i data-lucide="${icon}" class="w-8 h-8 text-cdl-gold"></i>
155            </div>
156            
157            <div class="flex items-baseline justify-center gap-1 mb-4">
158                <span class="text-6xl md:text-7xl font-black text-cdl-blue tracking-tighter">${data.value}</span>
159                <span class="text-4xl font-bold text-cdl-gold">${data.suffix || ''}</span>
160            </div>
161            
162            <div class="w-8 h-1 bg-slate-200 rounded-full mb-6 group-hover:w-16 group-hover:bg-cdl-gold transition-all duration-300"></div>
163            
164            <h3 class="text-slate-600 font-bold uppercase tracking-widest text-xs leading-relaxed">${data.label}</h3>
165        </div>
166    </div>`;
167}
168
169function renderDataPage() {
170    const t = (key) => i18n.t(key);
171    const dataKey = 'why-cv.data_figures';
172
173    setTimeout(async () => {
174        const dataSection = document.getElementById('data-section');
175        try{
176            const pageData = await contentService.getPageContent('why-cv');
177            const incentives = await incentivesService.getCurrentIncentives();
178            const greenBook = await pressService.getGreenBook();
179            
180            // Se mapea un nuevo objeto data buscando los objetos que comparten datos con incentives.
181            const newData = pageData.stats.map((d, index) => {
182                if (d.incentives) {
183                    d.value = incentives[d.incentives]?.percentage || d.value;
184                }
185                return renderDataCard(d, index);
186            });
187            
188            if (dataSection) dataSection.innerHTML = newData.join('');
189
190            const greenBookBtn = document.getElementById('green-book-btn');
191            if (greenBookBtn) {
192                if (greenBook && greenBook.url) {
193                    greenBookBtn.href = greenBook.url;
194                    greenBookBtn.innerHTML = `<i data-lucide="${greenBook.icon}" class="w-5 h-5"></i> ${t('home.green_book_cta')}`;
195                    greenBookBtn.classList.remove('hidden');
196                }
197            }
198
199            if (window.lucide) lucide.createIcons();
200            
201        } catch(error) {
202            console.error("Error al devolver datos: ", error);
203            if (dataSection) {
204                dataSection.innerHTML = `<div class="col-span-full text-center text-red-500 font-bold py-10">${t('actions.error_loading') || 'Error al cargar las estadísticas.'}</div>`;
205            }
206        }
207    }, 0);
208    
209    return `
210    <div class="w-full bg-slate-50/30 min-h-screen">
211        
212        <div class="relative pt-32 pb-20 px-6 max-w-7xl mx-auto text-center overflow-hidden bg-white border border-slate-100 shadow-sm rounded-[3rem] md:rounded-[4rem] mb-16 mt-6">
213            <div class="absolute top-0 left-1/2 -translate-x-1/2 w-[800px] h-[400px] bg-cdl-blue/5 rounded-full blur-[100px] pointer-events-none"></div>
214            
215            <div class="relative z-10">
216                <a href="/why-cv" class="inline-flex items-center text-slate-400 hover:text-cdl-blue mb-8 text-xs font-bold uppercase tracking-widest transition">
217                    <i data-lucide="arrow-left" class="w-4 h-4 mr-2"></i> ${t('actions.nav_back')}
218                </a>
219                <br>
220                <div class="inline-flex items-center gap-2 px-4 py-2 rounded-full bg-blue-50 text-cdl-blue text-[10px] font-black uppercase tracking-widest mb-6 border border-blue-100 shadow-sm">
221                    <i data-lucide="bar-chart-2" class="w-4 h-4"></i> ${t(`${dataKey}.title_1`)} ${t(`${dataKey}.title_2`)}
222                </div>
223
224                <h1 class="text-5xl md:text-7xl font-black text-cdl-blue mb-6 uppercase tracking-tighter italic leading-none">
225                    ${t(`${dataKey}.title_1`)} <span class="text-cdl-gold bg-clip-text bg-gradient-to-r from-cdl-gold to-yellow-600">${t(`${dataKey}.title_2`)}</span>
226                </h1>
227                <p class="text-lg text-slate-500 max-w-2xl mx-auto leading-relaxed font-medium">
228                    ${t(`${dataKey}.desc`)}
229                </p>
230            </div>
231        </div>
232
233        <div class="container mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 pb-32">
234            <div id="data-section" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
235                <div class="col-span-full flex flex-col items-center justify-center py-24 space-y-4">
236                    <div class="animate-spin rounded-full h-12 w-12 border-4 border-slate-200 border-t-cdl-gold"></div>
237                    <p class="text-slate-400 font-medium animate-pulse">${t('actions.loader')}</p>
238                </div>
239            </div>
240
241            <div class="mt-24 bg-cdl-blue rounded-[3rem] p-12 md:p-16 text-center relative overflow-hidden shadow-2xl">
242                <div class="absolute top-0 right-0 w-64 h-64 bg-cdl-gold/20 rounded-full blur-3xl -translate-y-32 translate-x-32 pointer-events-none"></div>
243                <div class="absolute bottom-0 left-0 w-64 h-64 bg-cyan-500/20 rounded-full blur-3xl translate-y-32 -translate-x-32 pointer-events-none"></div>
244                
245                <div class="relative z-10 max-w-3xl mx-auto">
246                    <h2 class="text-3xl md:text-5xl font-black text-white mb-6 uppercase italic tracking-tight">${t(`${dataKey}.info_title`)}</h2>
247                    <p class="text-blue-100 text-lg mb-10 leading-relaxed">${t(`${dataKey}.info_subtitle`)}</p>
248                    <div class="flex flex-wrap justify-center gap-4">
249                        <a href="/contact" class="inline-flex items-center gap-3 bg-cdl-gold hover:bg-yellow-500 text-white font-black py-4 px-10 rounded-full shadow-xl shadow-amber-900/20 transition-all transform hover:scale-105 uppercase text-xs tracking-widest">
250                            <i data-lucide="download" class="w-5 h-5"></i> ${t(`${dataKey}.info_btn`)}
251                        </a>
252                        <a id="green-book-btn" href="#" target="_blank" rel="noopener noreferrer" download
253                           class="hidden inline-flex items-center gap-3 bg-white text-cdl-blue font-black py-4 px-10 rounded-full transition-all transform hover:scale-105 hover:bg-cdl-gold hover:text-white uppercase text-xs tracking-widest shadow-xl">
254                            
255                        </a>
256                    </div>
257                </div>
258            </div>
259        </div>
260    </div>`;
261}
262
263// --- 3. SUBPÁGINA 4.2: FILMOGRAFÍA ---
264function renderSuccesStoriesCard(data){
265    const t = (key) => i18n.t(key);
266    
267    // Pasamos la imagen por el mediaService. 
268    // Si tu servicio acepta un parámetro de tamaño, podrías usar: mediaService.getImageUrl(data.image, 'small')
269    const imageUrl = mediaService.getImageUrl(data.image);
270
271    return `<div onclick="window.navigateTo('/filmography/${data.slug || data.id}')" class="group relative aspect-[2/3] rounded-xl overflow-hidden bg-slate-200 shadow-md hover:shadow-2xl transition-all duration-500 hover:-translate-y-2 cursor-pointer">
272                <div class="absolute inset-0 flex items-center justify-center bg-slate-300 text-slate-400 font-bold text-center p-2 text-xs uppercase tracking-widest">
273                    <img class="w-full h-full object-cover object-center" src="${imageUrl}" alt="${data.title || 'Producción'}">
274                    <div class="absolute inset-0 bg-cdl-blue/90 opacity-0 group-hover:opacity-100 transition-opacity duration-300 flex flex-col items-center justify-center p-4 text-center">
275                        <h3 class="text-white font-bold text-sm mb-2">${data.title}</h3>
276                        <span class="text-cdl-gold text-[10px] font-bold uppercase tracking-wider border border-cdl-gold px-2 py-1 rounded">${t('why-cv.filmography.card_btn') || 'Ver Ficha'}</span>
277                    </div>
278                </div>
279            </div>`
280}
281
282function renderFilmographyDetailPage(id) {
283    const t = (key) => i18n.t(key);
284
285    setTimeout(async () => {
286        const container = document.getElementById('filmography-detail-content');
287        try {
288            const prod = await filmographyService.getFilmographyById(id);
289
290            seoService.update({
291                seo: prod?.seo,
292                fallback: { title: prod?.title, description: prod?.description, image: prod?.image?.url },
293                path: `/filmography/${prod?.slug || id}`,
294            });
295
296            // --- CONFIGURACIÓN DEL CARRUSEL ---
297            if(prod.gallery.length > 0) {
298                const carousel = document.getElementById('carousel');
299                const popupCarousel = document.getElementById('carousel-popup');
300                if(carousel) carousel.innerHTML = renderCarousel(prod.gallery) // Se renderiza primero el carousel
301                if(popupCarousel) popupCarousel.innerHTML = renderPopup(); // Luego se renderiza el popup de las imágenes
302                initCarousel(prod.gallery); // Por último se inicia la funcionalidad
303            }
304            
305            if (!prod) throw new Error("Not found");
306
307            //RENDER DATOS FILMOGRAFÍA
308            const poster = document.getElementById('poster-container');
309            poster.innerHTML = `<img src="${prod.image.url}" class="w-full h-full object-cover object-center" alt="${prod.image.alt}" onerror="this.onerror=null; this.src='${mediaService.fallbackImage}'">`;
310
311            const title = document.getElementById('title-container');
312            title.innerHTML += `<h1 class="text-5xl md:text-7xl font-black text-cdl-blue uppercase italic tracking-tighter leading-none">${prod.title}</h1>
313                                <p class="text-2xl text-slate-400 font-light mt-2 italic">${prod.subtitle}</p>`;
314            
315            const synopsis = document.getElementById('synopsis-container');
316            synopsis.innerHTML += `<p>${prod.description || t('why-cv.filmography.detail.desc_placeholder')}</p>`;
317
318            const locations = document.getElementById('locations-container');
319            locations.innerHTML = prod?.locations_used.length > 0 ? `<div class="p-6 bg-slate-50 rounded-3xl border border-slate-100">
320                                    <span class="block text-[10px] font-black text-slate-400 uppercase tracking-widest mb-2">${t('why-cv.filmography.detail.locations_used') || 'Instalaciones utilizadas'}</span>
321                                    <ul class="space-y-2">
322                                    ${prod.locations_used.map(l => `<li class="flex items-center gap-2 text-cdl-blue font-bold text-sm">
323                                            <i data-lucide="check-circle" class="w-4 h-4 text-cdl-gold"></i> ${l}
324                                        </li>`).join('')}
325                                    </ul>
326                                </div>` : '';
327
328            const trailer = document.getElementById('film-trailer-grid');
329            const trailerPanel = document.getElementById('panel-trailer');
330            const trailerBtn = document.getElementById('tab-trailer');
331            if(!prod.trailer && trailerPanel && trailerBtn) {
332                trailerPanel.remove();
333                trailerBtn.remove();
334            }
335            const videoUrl = mediaService.getImageUrl(prod.trailer.video || '');
336            const posterUrl = mediaService.getImageUrl(prod.trailer.poster || '');
337            const embedUrl = mediaService.getEmbedUrl(prod.trailer.url || '');
338            trailer.innerHTML = prod.trailer.is_external ? `<div class="flex flex-col items-center space-y-4">
339                                                                <div class="aspect-video rounded-2xl overflow-hidden border border-slate-100 shadow-sm hover:shadow-md hover:border-cdl-blue transition cursor-pointer group bg-slate-200  w-full h-full">
340                                                                    <iframe class="w-full h-full" src="${embedUrl}" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture;
340 web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
341                                                                </div>
342                                                                <div class="w-full text-slate-900 mt-3 px-1 flex items-center gap-3 justify-center">
343                                                                    <p class="font-bold uppercase text-lg tracking-widest">
344                                                                        ${prod?.trailer?.caption || ''}
345                                                                    </p>
346                                                                </div>
347                                                            </div>` :
348                                                            `<div class="flex flex-col items-center space-y-4">
349                                                                <div class="aspect-video w-full h-full rounded-2xl overflow-hidden border border-slate-100 shadow-sm hover:shadow-md hover:border-cdl-blue transition cursor-pointer group bg-slate-200">
350                                                                    <video controls poster="${posterUrl}" class="w-full h-full">
351                                                                        <source src="${videoUrl}" type="video/mp4">
352                                                                    </video>
353                                                                </div>
354                                                                <div class="w-full text-slate-900 mt-3 px-1 flex items-center gap-3 justify-center">
355                                                                    <p class="font-bold text-lg">
356                                                                        ${prod?.trailer?.video?.caption || ''}
357                                                                    </p>
358                                                                </div>
359                                                            </div>`
360
361            const galleryPanel = document.getElementById('panel-gallery');
362            const galleryBtn = document.getElementById('tab-gallery');
363            if(!prod.gallery || prod.gallery.length == 0 && galleryPanel && galleryBtn) {
364                galleryPanel.remove();
365                galleryBtn.remove();
366            }
367
368            const videos = document.getElementById('film-videos-list');
369            const videosPanel = document.getElementById('panel-videos');
370            const videosBtn = document.getElementById('tab-videos');
371            if(!prod.videos || prod.videos.length == 0 && videosPanel && videosBtn) {
372                videosPanel.remove();
373                videosBtn.remove();
374            }
375            prod.videos.forEach(video => {
376                const videoUrl = mediaService.getImageUrl(video.video || '');
377                const posterUrl = mediaService.getImageUrl(video.poster || '');
378                const embedUrl = mediaService.getEmbedUrl(video.url || '');
379                if(!embedUrl.includes('youtube') && video.is_external) return;
380                    
381                videos.innerHTML += video.is_external ? `<div class="flex flex-col items-center space-y-4">
382                                                                <div class="aspect-video w-full h-full rounded-2xl overflow-hidden border border-slate-100 shadow-sm hover:shadow-md hover:border-cdl-blue transition cursor-pointer group bg-slate-200">
383                                                                    <iframe class="w-full h-full" src="${embedUrl}" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
384                                                                </div>
385                                                                <div class="w-full text-slate-900 mt-3 px-1 flex items-center gap-3">
386                                                                    <p class="font-bold text-sm">
387                                                                        ${video?.caption || ''}
388                                                                    </p>
389                                                                </div>
390                                                            </div>` :
391                                                            `<div class="flex flex-col items-center space-y-4">
392                                                                <div class="aspect-video rounded-2xl overflow-hidden border border-slate-100 shadow-sm hover:shadow-md hover:border-cdl-blue transition cursor-pointer group bg-slate-200 w-full h-full">
393                                                                    <video controls poster="${posterUrl}" class="w-full h-full">
394                                                                        <source src="${videoUrl}" type="video/mp4">
395                                                                    </video>
396                                                                </div>
397                                                                <div class="w-full text-slate-900 mt-3 px-1 flex items-center gap-3 justify-start">
398                                                                    <p class="font-bold text-sm">
399                                                                        ${video?.video?.caption || ''}
400                                                                    </p>
401                                                                </div>
402                                                            </div>`
403            })
404            
405            //PRIMER ELEMENTO ACTIVO DEL TAB-PANEL
406            const tabList = document.querySelectorAll('button[role="tab"]');
407            const panelList = document.querySelectorAll('div[role="tabpanel"]');
408            if(tabList.length > 0 && panelList.length > 0) {
409                tabList[0].classList.add('active');
410                tabList[0].classList.remove('hidden');
411                panelList[0].classList.remove('hidden');
412            }
413
414            //TRAS ESPERAR A QUE SE PINTEN SOLO LOS TABS QUE TENGAN CONTENIDO SE ELIMINA LA CLASE HIDDEN DEL CONTENEDOR
415            const tabsContainer = document.querySelector('div[data-tabs-container]');
416            tabsContainer.classList.remove('hidden')
417
418            if (window.lucide) lucide.createIcons();
419        } catch (error) {
420            container.innerHTML = `<div class="text-center py-24"><h2 class="text-2xl font-bold text-slate-400">${t('actions.not_found')}</h2></div>`;
421        }
422    }, 0);
423
424    return `
425    <div class="w-full bg-white">
426        <div class="pt-32 pb-12 px-6 max-w-7xl mx-auto">
427            <a href="/filmography" class="inline-flex items-center text-slate-400 hover:text-cdl-blue mb-12 text-xs font-bold uppercase tracking-widest transition">
428                <i data-lucide="arrow-left" class="w-4 h-4 mr-2"></i> ${t('actions.nav_back')}
429            </a>
430            
431            <div id="filmography-detail-content">
432                    <div class="grid col-span-12 grid-cols-1 lg:grid-cols-12 gap-12 items-start">
433                        <div class="lg:col-span-4">
434                            <div id="poster-container" class="aspect-[2/3] rounded-[2rem] overflow-hidden shadow-2xl border-4 border-white">
435                                
436                            </div>
437                        </div>
438
439                        <div class="lg:col-span-8 space-y-8">
440                            <div id="title-container">
441                                <span class="inline-block bg-cdl-gold/10 text-cdl-gold text-[10px] font-black uppercase tracking-[0.2em] px-3 py-1 rounded-md mb-4 border border-cdl-gold/20">${t('why-cv.filmography.detail.production_label') || 'Producción Destacada'}</span>
442                                
443                            </div>
444
445                            <div id="synopsis-container" class="prose prose-lg text-slate-600 max-w-none">
446                                <h3 class="text-slate-900 font-bold uppercase text-sm tracking-widest border-b border-slate-100 pb-2 mb-4">${t('why-cv.filmography.detail.synopsis') || 'Sobre la producción'}</h3>
447                                
448                            </div>
449
450                            <div class="grid grid-cols-1 md:grid-cols-2 gap-6 pt-6">
451                                <div id="locations-container">
452                                
453                                </div>
454                                
455                                <div class="p-6 bg-slate-50 rounded-3xl border border-slate-100 flex flex-col justify-center">
456                                    <span class="block text-xs font-bold text-slate-500 mb-4">${t('why-cv.filmography.detail.cta_text') || '¿Quieres rodar tu próximo proyecto con nosotros?'}</span>
457                                    <a href="/contact" class="bg-cdl-blue text-white text-center py-3 rounded-xl font-bold uppercase text-xs tracking-widest hover:bg-slate-900 transition shadow-lg">${t('actions.contact')}</a>
458                                </div>
459                            </div>
460                            
461                        </div>
462                    </div>
463                    <div>
464                        <div data-tabs-container class="hidden bg-white py-8 md:py-12 rounded-[3rem] border border-slate-100">
465                            <div class="flex border-b border-slate-100 mb-12 overflow-x-auto">
466                                <nav role="tablist" class="flex space-x-12 min-w-max px-4">
467                                    <button role="tab" id="tab-trailer" aria-controls="panel-trailer" aria-selected="true" class="tab-button active py-6 font-black uppercase tracking-widest text-xs">${'Trailer'}</button>
468                                    <button role="tab" id="tab-videos" aria-controls="panel-videos" aria-selected="false" class="tab-button py-6 font-black uppercase tracking-widest text-xs">${'Videos de Producción'}</button>
469                                    <button role="tab" id="tab-gallery" aria-controls="panel-gallery" aria-selected="false" class="tab-button py-6 font-black uppercase tracking-widest text-xs">${t('plato_detail.tab_gallery') || 'Galería'}</button>
470                                </nav>
471                            </div>
472                            <div>
473                                <div role="tabpanel" id="panel-trailer" class="animate-fade-in-up">
474                                    <div id="film-trailer-grid" class="grid grid-cols-1 gap-4">
475                                    
476                                    </div>
477                                </div>
478
479                                
480                                <div role="tabpanel" id="panel-videos" class="hidden animate-fade-in-up">
481                                    <div id="film-videos-list" class="grid grid-cols-1 md:grid-cols-3 gap-4">
482                                    
483                                    </div>
484                                </div>
485
486                                <div role="tabpanel" id="panel-gallery" class="hidden animate-fade-in-up space-y-12">
487                                    <div class="space-y-6">
488                                        <div id="carousel"></div>
489                                        <div id="film-gallery-grid" class="grid grid-cols-2 md:grid-cols-4 gap-4">
490                                            
491                                        </div>
492                                    </div>
493                                </div>
494                            </div>
495                        </div>
496                    </div>
497            </div>
498        </div>
499        <div id="carousel-popup"></div>
500    </div>`;
501}
502
503// pages/WhyCVPage.js
504
505// ... (imports y variables existentes)
506
507/**
508 * Nueva función auxiliar para renderizar los controles de paginación
509 */
510function renderPagination(pagination, baseUrl) {
511    if (!pagination || pagination.pageCount <= 1) return '';
512
513    const { page, pageCount } = pagination;
514    let buttons = '';
515
516    // Botón Anterior
517    buttons += `
518        <button ${page > 1 ? `onclick="window.navigateTo('${baseUrl}/${page - 1}')"` : 'disabled'}
519            class="w-10 h-10 rounded-full border border-slate-200 flex items-center justify-center text-slate-400 hover:bg-slate-50 transition disabled:opacity-30">
520            <i data-lucide="chevron-left" class="w-4 h-4"></i>
521        </button>
522    `;
523
524    // Números de página
525    for (let i = 1; i <= pageCount; i++) {
526        buttons += `
527            <button onclick="window.navigateTo('${baseUrl}/${i}')"
528                class="w-10 h-10 rounded-full ${i === page ? 'bg-cdl-blue text-white font-bold shadow-md' : 'border border-slate-200 text-slate-600 hover:bg-slate-50'} transition">
529                ${i}
530            </button>
531        `;
532    }
533
534    // Botón Siguiente
535    buttons += `
536        <button ${page < pageCount ? `onclick="window.navigateTo('${baseUrl}/${page + 1}')"` : 'disabled'}
537            class="w-10 h-10 rounded-full border border-slate-200 flex items-center justify-center text-slate-400 hover:bg-slate-50 transition disabled:opacity-30">
538            <i data-lucide="chevron-right" class="w-4 h-4"></i>
539        </button>
540    `;
541
542    return `
543        <div class="flex justify-center items-center space-x-2 mt-12 pt-8 border-t border-slate-100">
544            ${buttons}
545        </div>
546    `;
547}
548
549/**
550 * Versión actualizada con soporte para paginación
551 */
552function renderSuccessStoriesPage() {
553    const t = (key) => i18n.t(key);
554
555    setTimeout(async () => {
556        const pageData = await contentService.getContent('filmography');
557        if (pageData?.title_1) document.getElementById('page-title')?.replaceChildren(pageData.title_1);
558        if (pageData?.title_2) document.getElementById('page-title-highlight')?.replaceChildren(pageData.title_2);
559        if (pageData?.desc) document.getElementById('page-desc')?.replaceChildren(pageData.desc);
560    }, 0);
561
562    setTimeout(async () => {
563        const container = document.getElementById('view-list-container');
564        const paginationContainer = document.getElementById('pagination-container');
565        
566        try {
567            // 1. Pedimos más resultados y los ordenamos por año descendente.
568            // Ajusta 'year:desc' si tu campo en la base de datos se llama distinto (ej. releaseDate:desc)
569            const query = `pagination[pageSize]=100&sort[0]=year:desc`;
570            const response = await filmographyService.getAllProductions(query);
571            
572            const productions = response.data || response;
573
574            // 2. Agrupar las producciones por año
575            const groupedByYear = productions.reduce((acc, prod) => {
576                const year = prod.year || 'N/A'; // Cambia prod.year por el nombre de tu variable de año
577                if (!acc[year]) acc[year] = [];
578                acc[year].push(prod);
579                return acc;
580            }, {});
581
582            // 3. Obtener el año actual para saber qué pestaña abrir
583            const currentYear = new Date().getFullYear().toString();
584
585            // 4. Ordenar las claves (años) de mayor a menor
586            const sortedYears = Object.keys(groupedByYear).sort((a, b) => {
587                if (a === 'N/A') return 1;
588                if (b === 'N/A') return -1;
589                return parseInt(b) - parseInt(a);
590            });
591
592            // 5. Generar el HTML de los desplegables (Acordeón nativo de HTML5)
593            let html = '';
594            sortedYears.forEach(year => {
595                // Si el año coincide con el actual, le añadimos el atributo 'open'
596                const isOpen = year === currentYear ? 'open' : '';
597                
598                html += `
599                <details class="group mb-4 bg-white rounded-[2rem] border border-slate-100 shadow-sm overflow-hidden" ${isOpen}>
600                    <summary class="cursor-pointer p-6 px-8 bg-slate-50 hover:bg-slate-100 transition-colors font-black text-2xl text-cdl-blue uppercase tracking-widest flex justify-between items-center list-none outline-none [&::-webkit-details-marker]:hidden select-none">
601                        <span class="flex items-center gap-3">
602                            ${year} 
603                            <span class="text-xs font-bold text-slate-400 bg-white px-3 py-1 rounded-full border border-slate-200 shadow-sm">${groupedByYear[year].length}</span>
604                        </span>
605                        <span class="text-slate-400 group-open:rotate-180 transition-transform duration-300">
606                            <i data-lucide="chevron-down" class="w-6 h-6"></i>
607                        </span>
608                    </summary>
609                    <div class="p-8 grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-6 bg-white border-t border-slate-100">
610                        ${groupedByYear[year].map(p => renderSuccesStoriesCard(p)).join('')}
611                    </div>
612                </details>`;
613            });
614
615            container.innerHTML = html;
616            
617            // Ocultamos el contenedor de paginación ya que ahora mostramos todo en acordeones
618            if (paginationContainer) {
619                paginationContainer.innerHTML = ''; 
620            }
621
622            if (window.lucide) lucide.createIcons();
623        } catch (error) {
624            console.error("Error cargando filmografía agrupada:", error);
625            container.innerHTML = `<p class="col-span-full text-center text-slate-400">${t('actions.error_loading') || 'Error al cargar datos'}</p>`;
626        }
627    }, 0);
628
629    return `
630    <div class="w-full bg-white">
631        <div class="pt-32 pb-12 px-6 max-w-7xl mx-auto text-center">
632            <a href="/" class="inline-flex items-center text-slate-400 hover:text-cdl-blue mb-8 text-xs font-bold uppercase tracking-widest transition">
633                <i data-lucide="arrow-left" class="w-4 h-4 mr-2"></i> ${t('actions.nav_back')}
634            </a>
635            <h1 class="text-5xl md:text-6xl font-black text-cdl-blue mb-6 uppercase tracking-tighter italic leading-none">
636                <span id="page-title">${t('why-cv.filmography.title')}</span><br><span id="page-title-highlight" class="text-cdl-gold bg-clip-text bg-gradient-to-r from-cdl-gold to-yellow-600"></span>
637            </h1>
638            <p id="page-desc" class="text-lg text-slate-500 max-w-3xl mx-auto leading-relaxed">
639                ${t('why-cv.filmography.desc')}
640            </p>
641        </div>
642
643        <div class="container mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 pb-24">
644            <div id="view-list-container" class="flex flex-col">
645                <div class="flex justify-center py-24"><div class="animate-spin rounded-full h-12 w-12 border-4 border-slate-200 border-t-cdl-gold"></div></div>
646            </div>
647            
648            <div id="pagination-container"></div>
649        </div>
650    </div>
651    `;
652}
653// --- 4. SUBPÁGINA 4.3: GUÍA DE PRODUCCIÓN ---
654function renderProductionGuidePage() {
655    setTimeout(async() => {
656        const response = await contentService.getPageContent('why-cv');
657        const content = document.getElementById('content');
658        if(response && content){
659            const stepCtas = [
660                {
661                    href: '/contact',
662                    label: t(`${guide}.cta_contact`),
663                    icon: 'message-circle'
664                },
665                {
666                    href: '/locations',
667                    label: t(`${guide}.cta_locations`),
668                    icon: 'search'
669                },
670                {
671                    href: '/hub/film-offices',
672                    label: t(`${guide}.cta_film_offices`),
673                    icon: 'building-2'
674                },
675                {
676                    href: '/hub',
677                    label: t(`${guide}.cta_talent_companies`),
678                    icon: 'users'
679                }
680            ];
681
682            const stepCards = response.production_guide_4steps.map((step, index) => {
683                const card = {
684                    ...step,
685                    ...(index === 2 ? {
686                        title: t(`${guide}.film_offices_title`),
687                        desc: t(`${guide}.film_offices_desc`),
688                        lucide: 'building-2'
689                    } : {})
690                };
691                const cta = stepCtas[index];
692
693                return `<div class="relative p-6 rounded-2xl bg-white border border-slate-100 shadow-sm text-center group hover:shadow-lg transition flex flex-col">
694                        <div class="text-6xl font-black text-slate-100 absolute top-2 right-4 group-hover:text-blue-50 transition-colors">${card.step}</div>
695                        <div class="relative z-10 flex h-full flex-col">
696                            <div class="w-12 h-12 bg-blue-50 rounded-full flex items-center justify-center mx-auto mb-4 text-cdl-blue font-bold"><i data-lucide="${card.lucide}" class="w-6 h-6"></i></div>
697                            <h3 class="text-xl font-bold text-slate-900 mb-2">${card.title}</h3>
698                            <p class="text-sm text-slate-500 mb-6 flex-1">${card.desc}</p>
699                            <a href="${cta.href}" class="mt-auto inline-flex items-center justify-center gap-2 bg-cdl-blue hover:bg-slate-900 text-white font-bold py-3 px-5 rounded-xl transition shadow-lg text-xs uppercase tracking-widest">
700                                <i data-lucide="${cta.icon}" class="w-4 h-4"></i>${cta.label}
701                            </a>
702                        </div>
703                    </div>`;
704            }).join('');
705
706            content.innerHTML = `<section class="mb-16 bg-blue-50 px-8 py-10 rounded-[2.5rem] border border-blue-100 text-center">
707                <p class="text-lg text-blue-800 max-w-3xl mx-auto leading-relaxed">${response.production_guide_desc}</p>
708            </section>
709
710            <section class="mb-20">
711                <div class="grid grid-cols-1 md:grid-cols-4 gap-8">
712                    ${stepCards}
713
714                </div>
715            </section>
716                        
717            ${
718                response.downloadable_resources.filter(r => r.url).length != 0 ? `<section>
719                <h2 class="text-2xl font-black text-slate-900 mb-8 uppercase tracking-tight">${t(`${guide}.downloadable_resources`)}</h2>
720                <div class="grid grid-cols-1 md:grid-cols-3 gap-6">
721                ${response.downloadable_resources.filter(r => r.url).map(r => `<a href="${AppConfig.API_BASE_URL}${r.url}" target="_blank" class="flex items-center p-6 bg-slate-50 rounded-2xl border border-slate-200 hover:bg-white hover:shadow-md transition group">
722                        <div class="mr-4 text-${r.icon_color.includes('cdl') ? r.icon_color : r.icon_color.concat("","-500")}"><i data-lucide="${r.icon}" class="w-8 h-8"></i></div>
723                        <div>
724                            <h3 class="font-bold text-slate-900 group-hover:text-cdl-blue transition">${r.filename}</h3>
725                            <span class="text-xs font-bold text-slate-400 uppercase tracking-wider">${r.extension} • ${r.size}</span>
726                        </div>
727                        <i data-lucide="download" class="w-5 h-5 ml-auto text-slate-300 group-hover:text-cdl-blue transition"></i>
728                    </a>`).join('')}
729                </div>
730            </section>`
731                :
732            ''}`
733        }
734
735        lucide.createIcons();
736    }, 0)
737    
738    return `
739    <div class="w-full bg-white">
740        
741        <div class="pt-32 pb-12 px-6 max-w-7xl mx-auto text-center">
742            <a href="/why-cv" class="inline-flex items-center text-slate-400 hover:text-cdl-blue mb-8 text-xs font-bold uppercase tracking-widest transition">
743                <i data-lucide="arrow-left" class="w-4 h-4 mr-2"></i> ${t('actions.nav_back')}
744            </a>
745            <h1 class="text-5xl md:text-6xl font-black text-cdl-blue mb-6 uppercase tracking-tighter italic leading-none">
746                ${t(`${guide}.title_1`)} <span class="text-cdl-gold bg-clip-text bg-gradient-to-r from-cdl-gold">${t(`${guide}.title_2`)}</span>
747            </h1>
748            <p class="text-lg text-slate-500 max-w-3xl mx-auto leading-relaxed">
749                ${t(`${guide}.subtitle`)}
750            </p>
751        </div>
752
753        <div id="content" class="container mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 pb-24">
754            
755        </div>
756    </div>
757    `;
758}
759
760// --- 5. SUBPÁGINA 4.4: CONECTIVIDAD ---
761function renderConnectivityPage() {
762    const t = (key) => i18n.t(key);
763    setTimeout(async () => {
764        const response = await contentService.getPageContent('why-cv');
765        const videoUrl = mediaService.getImageUrl(response?.conectivity_video?.url || '');
766        const videoPoster = mediaService.getImageUrl(response?.conectivity_video?.poster || '');
767
768        const content = document.getElementById('content');
769        if(content && response) {
770            content.innerHTML = `<div class="relative h-[500px] rounded-[2.5rem] overflow-hidden shadow-2xl mb-16 border-4 border-slate-100 group">
771                <video controls class="absolute inset-0 w-full h-full object-cover" poster="${videoPoster}">
772                    <source src="${videoUrl}" type="video/mp4">
773                    ${t('why-cv.connectivity.video_error')}
774                </video>
775
776                <div class="absolute inset-0 bg-gradient-to-br from-slate-800 to-black opacity-30 group-hover:opacity-10 transition-opacity duration-500 pointer-events-none"></div>
777            </div>
778
779            <div class="grid grid-cols-1 md:grid-cols-3 gap-8 mt-8 text-center">
780                
781                ${response.conectivity_cards.map(c => `<div class="bg-white p-8 rounded-[2rem] border border-slate-100 shadow-lg hover:shadow-xl hover:-translate-y-1 transition duration-300 group">
782                    <div class="w-16 h-16 bg-blue-50 rounded-2xl flex items-center justify-center mx-auto mb-6 group-hover:bg-cdl-blue transition-colors">
783                        <i data-lucide="${c.lucide}" class="w-8 h-8 text-cdl-blue group-hover:text-white transition-colors"></i>
784                    </div>
785                    <h3 class="font-black text-xl text-slate-900 mb-2 uppercase tracking-tight">${c.title}</h3>
786                    <p class="text-slate-500 text-sm leading-relaxed">${c.desc}</p>
787                </div>`).join('')}
788            </div>`
789        }
790
791        lucide.createIcons();
792    }, 0)
793    return `
794    <div class="w-full bg-white">
795        
796        <div class="pt-32 pb-12 px-6 max-w-7xl mx-auto text-center">
797            <a href="/why-cv" class="inline-flex items-center text-slate-400 hover:text-cdl-blue mb-8 text-xs font-bold uppercase tracking-widest transition">
798                <i data-lucide="arrow-left" class="w-4 h-4 mr-2"></i> ${t('actions.nav_back')}
799            </a>
800            <h1 class="text-5xl md:text-6xl font-black text-cdl-blue mb-6 uppercase tracking-tighter italic leading-none">
801                ${t(`${connectivity}.title_1`)} <span class="text-cdl-gold bg-clip-text bg-gradient-to-r from-cdl-gold to-yellow-600">${t(`${connectivity}.title_2`)}</span>
802            </h1>
803            <p class="text-lg text-slate-500 max-w-3xl mx-auto leading-relaxed">
804                ${t(`${connectivity}.subtitle_1`)}
805            </p>
806        </div>
807        
808        <div id="content" class="container mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 pb-24">
809            
810        </div>
811    </div>`;
812}
813
814// --- 6. SUBPÁGINA 4.5: CONTACTO INVERSORES ---
815function renderInvestorsContactPage() {
816
817    // Inicializamos los formularios una vez inyectado el HTML
818    setTimeout(() => {
819        leadService.initLeadForm('investors-form', 'investors', 'success-investors');
820        if (window.lucide) lucide.createIcons();
821    }, 0);
822
823    return `
824    <div class="w-full bg-white">
825        
826        <div class="pt-32 pb-12 px-6 max-w-3xl mx-auto text-center">
827            <a href="/why-cv" class="inline-flex items-center text-slate-400 hover:text-cdl-blue mb-8 text-xs font-bold uppercase tracking-widest transition">
828                <i data-lucide="arrow-left" class="w-4 h-4 mr-2"></i> ${t('actions.nav_back')}
829            </a>
830            <h1 class="text-4xl md:text-5xl font-black text-cdl-blue mb-6 uppercase tracking-tighter italic leading-none">
831                ${t(`${investors}.title`)}
832            </h1>
833        </div>
834
835        <div class="container mx-auto max-w-3xl px-4 sm:px-6 lg:px-8 pb-24">
836            <div class="bg-white p-10 rounded-[2.5rem] shadow-2xl border border-slate-100">
837                <form id="investors-form" class="space-y-6">
838                    <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
839                        <div>
840                            <label class="block text-xs font-bold text-slate-500 uppercase mb-2 ml-1">${t('forms.name_label')} <span class="text-red-500">*</span></label>
841                            <input name="name" type="text" required class="w-full p-4 bg-slate-50 border border-slate-200 rounded-xl focus:outline-none focus:border-cdl-blue transition">
842                        </div>
843                        <div>
844                            <label class="block text-xs font-bold text-slate-500 uppercase mb-2 ml-1">${t(`${investors}.form_company_label`)}</label>
845                            <input name="company" type="text" class="w-full p-4 bg-slate-50 border border-slate-200 rounded-xl focus:outline-none focus:border-cdl-blue transition">
846                        </div>
847                    </div>
848                    
849                    <div>
850                        <label class="block text-xs font-bold text-slate-500 uppercase mb-2 ml-1">${t(`${investors}.form_email_label`)} <span class="text-red-500">*</span></label>
851                        <input name="email" type="email" required class="w-full p-4 bg-slate-50 border border-slate-200 rounded-xl focus:outline-none focus:border-cdl-blue transition">
852                    </div>
853                    
854                    <div>
855                        <label class="block text-xs font-bold text-slate-500 uppercase mb-2 ml-1">${t(`${investors}.form_interest_label`)} <span class="text-red-500">*</span></label>
856                        <textarea name="message" required rows="4" class="w-full p-4 bg-slate-50 border border-slate-200 rounded-xl focus:outline-none focus:border-cdl-blue transition"></textarea>
857                    </div>
858                    
859                    <div class="pt-4">
860                        <button type="submit" class="w-full bg-slate-900 hover:bg-cdl-blue text-white font-bold py-4 px-8 rounded-xl shadow-lg transition duration-300 flex items-center justify-center gap-2">
861                            <i data-lucide="send" class="w-4 h-4"></i> ${t(`${investors}.submit_btn`)}
862                        </button>
863                    </div>
864                </form>
865
866                <div id="success-investors" class="hidden text-center py-8">
867                <div class="w-16 h-16 bg-green-100 rounded-full flex items-center justify-center mx-auto mb-4">
868                    <i data-lucide="check" class="w-8 h-8 text-green-600"></i>
869                </div>
870                <h3 class="text-xl font-bold text-slate-800 mb-2">${t(`${investors}.success_title`)}</h3>
871                <p class="text-slate-500">${t(`${investors}.success_msg`)}</p>
872            </div>
873            </div>
874        </div>
875    </div>`;
876}
877
878// --- 7. SUBPÁGINA 4.6: ATRATIVOS INCENTIVOS ---
879export function renderIncentivesPage() {
880    const t = (key) => i18n.t(key);
881    
882    setTimeout(async () => {
883        const container = document.getElementById('incentives-container');
884        try {
885            const data = await incentivesService.getCurrentIncentives();
886            
887            if (container && data) {
888                const defaultDesc = `<p>${t('why-cv.incentives.default_text') || 'La Comunitat Valenciana ofrece uno de los marcos fiscales más atractivos de Europa.'}</p>`;
889                const descriptionHtml = data.description ? data.description : defaultDesc;
890
891                container.innerHTML = `
892                    <div class="grid grid-cols-1 md:grid-cols-2 gap-8 mb-16">
893                        
894                        <div class="bg-gradient-to-br from-slate-900 to-cdl-blue p-10 md:p-12 rounded-[3rem] shadow-2xl text-white relative overflow-hidden group hover:scale-[1.02] transition-transform duration-500">
895                            <div class="absolute top-0 right-0 w-48 h-48 bg-cdl-gold/20 rounded-full blur-3xl -translate-y-16 translate-x-16"></div>
896                            <div class="relative z-10 flex flex-col h-full">
897                                <div class="flex justify-between items-start mb-8">
898                                    <div class="w-14 h-14 bg-white/10 backdrop-blur-md rounded-2xl flex items-center justify-center border border-white/20 shadow-sm">
899                                        <i data-lucide="globe" class="w-7 h-7 text-cdl-gold"></i>
900                                    </div>
901                                    <span class="bg-cdl-gold/20 text-cdl-gold text-[10px] font-black uppercase tracking-[0.2em] px-4 py-2 rounded-full border border-cdl-gold/30">
902                                        ${data.national?.title || t('why-cv.incentives.national')}
903                                    </span>
904                                </div>
905                                <div class="mt-auto">
906                                    <div class="text-8xl md:text-9xl font-black text-white mb-2 tracking-tighter leading-none text-shadow-sm">
907                                        ${data.national?.percentage || '0'}%
908                                    </div>
909                                    <h3 class="text-xl font-bold text-slate-500 uppercase tracking-widest">
910                                        ${data.national?.label || t('why-cv.incentives.tax_rebate')}
911                                    </h3>
912                                </div>
913                            </div>
914                        </div>
915
916                        <div class="bg-white p-10 md:p-12 rounded-[3rem] shadow-xl border border-slate-100 relative overflow-hidden group hover:scale-[1.02] transition-transform duration-500">
917                            <div class="absolute top-0 right-0 w-48 h-48 bg-cyan-500/10 rounded-full blur-3xl -translate-y-16 translate-x-16"></div>
918                            <div class="relative z-10 flex flex-col h-full">
919                                <div class="flex justify-between items-start mb-8">
920                                    <div class="w-14 h-14 bg-blue-50 rounded-2xl flex items-center justify-center border border-blue-100 shadow-sm">
921                                        <i data-lucide="map" class="w-7 h-7 text-cdl-blue"></i>
922                                    </div>
923                                    <span class="bg-blue-50 text-cdl-blue text-[10px] font-black uppercase tracking-[0.2em] px-4 py-2 rounded-full border border-blue-100">
924                                        ${data.regional?.title || t('why-cv.incentives.regional')}
925                                    </span>
926                                </div>
927                                <div class="mt-auto">
928                                    <div class="text-8xl md:text-9xl font-black text-cdl-blue mb-2 tracking-tighter leading-none">
929                                        ${data.regional?.percentage || '0'}%
930                                    </div>
931                                    <h3 class="text-xl font-bold text-slate-500 uppercase tracking-widest">
932                                        ${data.regional?.label || t('why-cv.incentives.grant')}
933                                    </h3>
934                                </div>
935                            </div>
936                        </div>
937
938                    </div>
939
940                    <div class="bg-slate-50 p-10 md:p-16 rounded-[3rem] border border-slate-100 shadow-inner">
941                        <div class="flex items-center gap-4 mb-8 border-b border-slate-200 pb-6">
942                            <div class="w-10 h-10 bg-white rounded-full flex items-center justify-center shadow-sm">
943                                <i data-lucide="info" class="w-5 h-5 text-cdl-gold"></i>
944                            </div>
945                            <h2 class="text-2xl font-black text-slate-800 uppercase tracking-tight">
946                                ${t('why-cv.incentives.details_title') || 'Condiciones y Detalles'}
947                            </h2>
948                        </div>
949                        <div class="prose prose-lg prose-slate text-slate-600 max-w-none">
950                            ${descriptionHtml}
951                        </div>
952                    </div>
953
954                    <div class="mt-16 text-center">
955                        <a href="/contact" class="inline-flex items-center gap-3 bg-cdl-gold hover:bg-yellow-500 text-white font-black py-5 px-10 rounded-full shadow-xl shadow-amber-900/20 transition-all transform hover:scale-105 uppercase text-sm tracking-widest">
956                            <i data-lucide="calculator" class="w-5 h-5"></i> ${t('why-cv.incentives.cta') || 'Consultar mi Proyecto'}
957                        </a>
958                        ${data.lastUpdated ? `
959                            <p class="text-[10px] text-slate-400 font-bold uppercase tracking-[0.2em] mt-6">
960                                ${t('why-cv.incentives.last_update') || 'Última actualización'}: ${data.lastUpdated}
961                            </p>` : ''}
962                    </div>
963                `;
964            }
965            if (window.lucide) lucide.createIcons();
966        } catch (e) { 
967            console.error("Error al renderizar incentivos:", e); 
968            if (container) container.innerHTML = `<p class="text-center text-red-500 font-bold">${t('why-cv.incentives.not_found')}</p>`;
969        }
970    }, 0);
971
972    // ... [El HTML de retorno de Incentivos se mantiene igual]
973    return `
974    <div class="w-full bg-white">
975        <div class="pt-32 pb-12 px-6 max-w-7xl mx-auto text-center">
976            <nav class="flex justify-center text-xs text-slate-400 mb-6 uppercase tracking-widest font-bold" aria-label="Breadcrumb">
977                <ol class="list-none p-0 inline-flex items-center gap-2">
978                    <li><a href="/" class="hover:text-cdl-blue transition">${t('actions.home')}</a></li>
979                    <li><span class="text-cdl-gold">/</span></li>
980                    <li><a href="/why-cv" class="hover:text-cdl-blue transition">${t('nav.why')}</a></li>
981                    <li><span class="text-cdl-gold">/</span></li>
982                    <li><span class="text-cdl-blue">${t('why-cv.incentives.title')}</span></li>
983                </ol>
984            </nav>
985            <h1 class="text-5xl md:text-7xl font-black text-cdl-blue mb-6 uppercase tracking-tighter italic leading-none">
986                ${t('why-cv.incentives.title_1') || 'Incentivos'} <span class="text-cdl-gold bg-clip-text bg-gradient-to-r from-cdl-gold to-yellow-600">${t('why-cv.incentives.title_2') || 'Fiscales'}</span>
987            </h1>
988            <p class="text-lg text-slate-500 max-w-3xl mx-auto leading-relaxed">
989                ${t('why-cv.incentives.desc')}
990            </p>
991        </div>
992        
993        <div id="incentives-container" class="container mx-auto max-w-5xl px-4 sm:px-6 lg:px-8 pb-32">
994            <div class="flex flex-col items-center justify-center py-24 space-y-4">
995                <div class="animate-spin rounded-full h-12 w-12 border-4 border-slate-200 border-t-cdl-gold"></div>
996                <p class="text-slate-400 font-medium animate-pulse">${t('actions.loader')}</p>
997            </div>
998        </div>
999    </div>`;
1000}
1001
1002export { 
1003    renderWhyCVOverview,
1004    renderDataPage,
1005    renderSuccessStoriesPage, 
1006    renderConnectivityPage,
1007    renderInvestorsContactPage,
1008    renderProductionGuidePage,
1009    renderFilmographyDetailPage
1010};

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.