/* ========================================================================== ECOSISTEMA IDE — Cambio in-place entre tarjetas y paneles de unidad ========================================================================== */ (function () { 'use strict'; const section = document.getElementById('ecosistema-ide'); if (!section) return; const stateEcosystem = section.querySelector('[data-eco-state="ecosystem"]'); const stateUnit = section.querySelector('[data-eco-state="unit"]'); const panels = Array.from(section.querySelectorAll('[data-eco-panel]')); const triggers = Array.from(section.querySelectorAll('[data-eco-open]')); const backBtn = section.querySelector('[data-eco-close]'); const pills = Array.from(section.querySelectorAll('.eco-pill')); let lastFocusedTrigger = null; /* --- Mostrar panel de una unidad --- */ function openUnit(id) { const panel = panels.find(p => p.dataset.ecoPanel === id); if (!panel) return; // Guardamos el trigger que originó el cambio (para devolver foco) const origin = triggers.find(t => t.dataset.ecoOpen === id); if (origin) lastFocusedTrigger = origin; // Ocultar ecosistema, mostrar estado unidad stateEcosystem.hidden = true; stateUnit.hidden = false; // Alternar paneles panels.forEach(p => { const isActive = p.dataset.ecoPanel === id; p.hidden = !isActive; if (isActive) { // Re-trigger de la animación de entrada p.style.animation = 'none'; // Forzar reflow void p.offsetWidth; p.style.animation = ''; } }); // Aria expandido en tarjetas triggers.forEach(t => { const isThis = t.dataset.ecoOpen === id; t.setAttribute('aria-expanded', isThis ? 'true' : 'false'); }); // Pills activas pills.forEach(pill => { pill.classList.toggle('is-active', pill.dataset.ecoOpen === id); }); // Foco accesible al título del panel const title = panel.querySelector('.eco-panel__title'); if (title) { title.setAttribute('tabindex', '-1'); // Pequeño timeout para asegurar visibilidad tras el cambio de estado requestAnimationFrame(() => title.focus({ preventScroll: false })); } // Scroll suave al inicio de la sección (solo si quedó fuera de vista) const rect = section.getBoundingClientRect(); if (rect.top < -80) { section.scrollIntoView({ behavior: 'smooth', block: 'start' }); } } /* --- Volver al estado ecosistema --- */ function closeUnit() { stateUnit.hidden = true; stateEcosystem.hidden = false; triggers.forEach(t => t.setAttribute('aria-expanded', 'false')); pills.forEach(p => p.classList.remove('is-active')); // Devolver foco a la tarjeta origen if (lastFocusedTrigger && typeof lastFocusedTrigger.focus === 'function') { lastFocusedTrigger.focus({ preventScroll: false }); } else { // Fallback: primer botón del grid const first = triggers[0]; if (first) first.focus({ preventScroll: false }); } } /* --- Listeners --- */ triggers.forEach(trigger => { trigger.addEventListener('click', (e) => { const id = trigger.dataset.ecoOpen; if (!id) return; // Si es un real (academy), dejamos que navegue if (trigger.tagName === 'A' && trigger.getAttribute('href')) return; e.preventDefault(); openUnit(id); }); // Soporte teclado para tarjetas no-button (por si acaso) trigger.addEventListener('keydown', (e) => { if (e.key === 'Enter' || e.key === ' ') { const id = trigger.dataset.ecoOpen; if (!id) return; if (trigger.tagName === 'A') return; e.preventDefault(); openUnit(id); } }); }); if (backBtn) backBtn.addEventListener('click', closeUnit); // Escape cierra el panel expandido section.addEventListener('keydown', (e) => { if (e.key === 'Escape' && !stateUnit.hidden) { closeUnit(); } }); /* --- Deep link opcional por hash (#eco-projects) sin recargar --- */ function readHash() { const hash = window.location.hash.replace('#eco-', ''); if (['excellence', 'projects', 'digital'].includes(hash)) { openUnit(hash); } } window.addEventListener('hashchange', readHash); readHash(); })();